View on GitHub

chaoshearing

🤐

Chaos Hearing

Erick Oduniyi & Myunghyun Oh

On Listening and Duets: Hearing Performance, Chaotic Dynamics, and Computational Auditory Scenery Analysis

This first project ("On Listening and Duets") and the entire Chaos Hearing project have been started because of a deep appreciation of mathematics, music, and sound. The Chaos Hearing project's goal is to facilitate the conversations between these entities (sound, music, and math. These conversations are between student and teacher, artist and artist, mathematician and mathematician, and artist and mathematician. During the conversations, expect discussions on related research, theories, art, design, and everything else from a formal and wonderfully artistic perspective.

Examples of Theory

We use latex to specify mathematical formulations:

% The normal form equation for the supercritical Hopf bifurication w/ AWGN: n_z(t)
\begin{align}
	\dfrac{dz(t)}{dt} = (\mu i\omega_0)z(t) - (a + i/3)|z(t)|^2z(t) + n_{z}(t)
\end{align}

Examples of Code

We use python, matlab, R, swift, and various other programming languages for implementations of theory.

Deep Learning in Swift (Iris ANN, The Hello World of DL)

The Iris genus entails about 300 species, but our program will only classify the following three:

  • Iris setosa
  • Iris virginica
  • Iris versicolor
Petal geometry compared for three iris species: Iris setosa, Iris virginica, and Iris versicolor
Figure 1. Iris setosa (by Radomil, CC BY-SA 3.0), Iris versicolor, (by Dlanglois, CC BY-SA 3.0), and Iris virginica (by Frank Mayfield, CC BY-SA 2.0).

🧠 Creating a neural network model to classify flowers

A diagram of the network architecture: Inputs, 2 hidden layers, and outputs
Figure 2. A fully-connected neural network consisting of an input layer (features), two hidden layers, and an output layer (predictions).
// Import Swift for TensorFlow Deep Learning Library
import Tensorflow
import Pythonkit
import Python

/** Define a neural network
  We'll define a neural network that is three layers:layer1 = features -> size(hidden_nodes)
  layer2 = size(hidden_nodes) -> size(hidden_nodes)
  layer3 = size(hidden_nodes) -> p(features) = {labels}
**/

// Set the # of artificial neurons
let hiddenSize: Int = 10

// Implement a model: fully-connected Neural Network (NN)
struct lightIrisModel: Layer {
    var layer1 = Dense<Float>(inputSize: 4, outputSize: hiddenSize, activation: relu)
    var layer2 = Dense<Float>(inputSize: hiddenSize, outputSize: hiddenSize, activation: relu)
    var layer3 = Dense<Float>(inputSize: hiddenSize, outputSize: 3)
		
    // Differentiable Programming! 
    @differentiable
    func callAsFunction(_ input: Tensor<Float>) -> Tensor<Float> {
        return input.sequenced(through: layer1, layer2, layer3)
    }
}

Reinforcement Learning in Swift (Cart-Pole, The Classic Control Example)

// Copyright 2019, Emmanouil Antonios Platanios. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.
import TensorFlow

@usableFromInline internal let gravity: Float = 9.8
@usableFromInline internal let cartMass: Float = 1.0
@usableFromInline internal let poleMass: Float = 0.1
@usableFromInline internal let length: Float = 0.5
@usableFromInline internal let forceMagnitude: Float = 10.0
@usableFromInline internal let secondCountBetweenUpdates: Float = 0.02
@usableFromInline internal let angleThreshold: Float = 12 * 2 * Float.pi / 360
@usableFromInline internal let positionThreshold: Float = 2.4
@usableFromInline internal let totalMass: Float = cartMass + poleMass
@usableFromInline internal let poleMassLength: Float = poleMass * length

public struct CartPoleEnvironment: RenderableEnvironment {
  public let batchSize: Int
  public let actionSpace: Discrete
  public var observationSpace: ObservationSpace

  @usableFromInline internal var step: Step<Observation, Tensor<Float>>
  @usableFromInline internal var needsReset: Tensor<Bool>
  @usableFromInline internal var renderer: CartPoleRenderer? = nil

  @inlinable public var currentStep: Step<Observation, Tensor<Float>> { step }

  @inlinable
  public init(batchSize: Int, renderer: CartPoleRenderer? = nil) {
    self.batchSize = batchSize
    self.actionSpace = Discrete(withSize: 2, batchSize: batchSize)
    self.observationSpace = ObservationSpace(batchSize: batchSize)
    self.step = Step(
      kind: StepKind.first(batchSize: batchSize),
      observation: observationSpace.sample(),
      reward: Tensor<Float>(ones: [batchSize]))
    self.needsReset = Tensor<Bool>(repeating: false, shape: [batchSize])
    self.renderer = renderer
  }

  /// Updates the environment according to the provided action.
  @inlinable
  @discardableResult
  public mutating func step(taking action: Tensor<Int32>) -> Step<Observation, Tensor<Float>> {
    // precondition(actionSpace.contains(action), "Invalid action provided.")
    var position = step.observation.position
    var positionDerivative = step.observation.positionDerivative
    var angle = step.observation.angle
    var angleDerivative = step.observation.angleDerivative

    // Calculate the updates to the pole position, angle, and their derivatives.
    let force = Tensor<Float>(2 * action - 1) * forceMagnitude
    let angleCosine = cos(angle)
    let angleSine = sin(angle)
    let temp = force + poleMassLength * angleDerivative * angleDerivative * angleSine
    let angleAccNominator = gravity * angleSine - temp * angleCosine / totalMass
    let angleAccDenominator = 4/3 - poleMass * angleCosine * angleCosine / totalMass
    let angleAcc = angleAccNominator / (length * angleAccDenominator)
    let positionAcc = (temp - poleMassLength * angleAcc * angleCosine) / totalMass
    position += secondCountBetweenUpdates * positionDerivative
    positionDerivative += secondCountBetweenUpdates * positionAcc
    angle += secondCountBetweenUpdates * angleDerivative
    angleDerivative += secondCountBetweenUpdates * angleAcc

    // Take into account the finished simulations in the batch.
    let sample = observationSpace.sample()
    step.observation.position = position.replacing(with: sample.position, where: needsReset)
    step.observation.positionDerivative = positionDerivative.replacing(
      with: sample.positionDerivative,
      where: needsReset)
    step.observation.angle = angle.replacing(with: sample.angle, where: needsReset)
    step.observation.angleDerivative = angleDerivative.replacing(
      with: sample.angleDerivative,
      where: needsReset)
    let newNeedsReset = (step.observation.position .< -positionThreshold)
      .elementsLogicalOr(step.observation.position .> positionThreshold)
      .elementsLogicalOr(step.observation.angle .< -angleThreshold)
      .elementsLogicalOr(step.observation.angle .> angleThreshold)
    step.kind.rawValue = Tensor(onesLike: step.kind.rawValue)
      .replacing(with: Tensor<Int32>(zeros: newNeedsReset.shape), where: needsReset)
      .replacing(with: 3 * Tensor<Int32>(ones: newNeedsReset.shape), where: newNeedsReset)
    // Rewards need not be updated because they are always equal to one.
    needsReset = newNeedsReset
    return step
  }

  /// Resets the environment.
  @inlinable
  @discardableResult
  public mutating func reset() -> Step<Observation, Tensor<Float>> {
    step.kind = StepKind.first(batchSize: batchSize)
    step.observation = observationSpace.sample()
    needsReset = Tensor<Bool>(repeating: false, shape: [batchSize])
    return step
  }

  /// Returns a copy of this environment that is reset before being returned.
  @inlinable
  public func copy() -> CartPoleEnvironment {
    CartPoleEnvironment(batchSize: batchSize, renderer: renderer)
  }

  @inlinable
  public mutating func render() {
    if renderer == nil { renderer = CartPoleRenderer() }
    renderer!.render(observation: step.observation)
  }
}

extension CartPoleEnvironment {
  public struct Observation: Differentiable, KeyPathIterable {
    public var position: Tensor<Float>
    public var positionDerivative: Tensor<Float>
    public var angle: Tensor<Float>
    public var angleDerivative: Tensor<Float>

    @inlinable
    public init(
      position: Tensor<Float>,
      positionDerivative: Tensor<Float>,
      angle: Tensor<Float>,
      angleDerivative: Tensor<Float>
    ) {
      self.position = position
      self.positionDerivative = positionDerivative
      self.angle = angle
      self.angleDerivative = angleDerivative
    }
  }

  public struct ObservationSpace: Space {
    public let distribution: ValueDistribution

    @inlinable
    public init(batchSize: Int) {
      self.distribution = ValueDistribution(batchSize: batchSize)
    }

    @inlinable
    public var description: String {
      "CartPoleObservation"
    }

    @inlinable
    public func contains(_ value: Observation) -> Bool {
      true
    }

    public struct ValueDistribution: DifferentiableDistribution, KeyPathIterable {
      @noDerivative public let batchSize: Int

      public var positionDistribution: Uniform<Float> { 
        Uniform<Float>(
          lowerBound: Tensor<Float>(repeating: -0.05, shape: [batchSize]),
          upperBound: Tensor<Float>(repeating: 0.05, shape: [batchSize]))
      }

      public var positionDerivativeDistribution: Uniform<Float> {
        Uniform<Float>(
          lowerBound: Tensor<Float>(repeating: -0.05, shape: [batchSize]),
          upperBound: Tensor<Float>(repeating: 0.05, shape: [batchSize]))
      }

      public var angleDistribution: Uniform<Float> {
        Uniform<Float>(
          lowerBound: Tensor<Float>(repeating: -0.05, shape: [batchSize]),
          upperBound: Tensor<Float>(repeating: 0.05, shape: [batchSize]))
      }

      public var angleDerivativeDistribution: Uniform<Float> {
        Uniform<Float>(
          lowerBound: Tensor<Float>(repeating: -0.05, shape: [batchSize]),
          upperBound: Tensor<Float>(repeating: 0.05, shape: [batchSize]))
      }

      @inlinable
      public init(batchSize: Int) {
        self.batchSize = batchSize
      }

      // TODO: @inlinable
      @differentiable(wrt: self)
      public func logProbability(of value: Observation) -> Tensor<Float> {
        positionDistribution.logProbability(of: value.position) +
          positionDerivativeDistribution.logProbability(of: value.positionDerivative) +
          angleDistribution.logProbability(of: value.angle) +
          angleDerivativeDistribution.logProbability(of: value.angleDerivative)
      }

      // TODO: @inlinable
      @differentiable(wrt: self)
      public func entropy() -> Tensor<Float> {
        positionDistribution.entropy() +
          positionDerivativeDistribution.entropy() +
          angleDistribution.entropy() +
          angleDerivativeDistribution.entropy()
      }

      @inlinable
      public func mode() -> Observation {
        Observation(
          position: positionDistribution.mode(),
          positionDerivative: positionDerivativeDistribution.mode(),
          angle: angleDistribution.mode(),
          angleDerivative: angleDerivativeDistribution.mode())
      }

      @inlinable
      public func sample() -> Observation {
        Observation(
          position: positionDistribution.sample(),
          positionDerivative: positionDerivativeDistribution.sample(),
          angle: angleDistribution.sample(),
          angleDerivative: angleDerivativeDistribution.sample())
      }
    }
  }
}

public struct CartPoleRenderer: GLFWScene {
  public let windowWidth: Int
  public let windowHeight: Int
  public let worldWidth: Float
  public let scale: Float
  public let cartTop: Float
  public let poleWidth: Float
  public let poleLength: Float
  public let cartWidth: Float
  public let cartHeight: Float

  @usableFromInline internal var window: GLFWWindow
  @usableFromInline internal var cart: GLFWGeometry
  @usableFromInline internal var pole: GLFWGeometry
  @usableFromInline internal var axle: GLFWGeometry
  @usableFromInline internal var track: GLFWGeometry
  @usableFromInline internal var cartTransform: GLFWTransform
  @usableFromInline internal var poleTransform: GLFWTransform

  @inlinable
  public init(
    windowWidth: Int = 600,
    windowHeight: Int = 400,
    positionThreshold: Float = 2.4,
    cartTop: Float = 100.0,
    poleWidth: Float = 10.0,
    cartWidth: Float = 50.0,
    cartHeight: Float = 30.0
  ) {
    self.windowWidth = windowWidth
    self.windowHeight = windowHeight
    self.worldWidth = positionThreshold * 2
    self.scale = Float(windowWidth) / worldWidth
    self.cartTop = cartTop
    self.poleWidth = poleWidth
    self.poleLength = scale
    self.cartWidth = cartWidth
    self.cartHeight = cartHeight

    // Create the GLFW window along with all the shapes.
    self.window = try! GLFWWindow(
        name: "CartPole Environment",
        width: windowWidth,
        height: windowHeight,
        framesPerSecond: 60)
    let (cl, cr, ct, cb) = (
      -cartWidth / 2, cartWidth / 2,
      cartHeight / 2, -cartHeight / 2)
    self.cart = GLFWPolygon(vertices: [(cl, cb), (cl, ct), (cr, ct), (cr, cb)])
    self.cartTransform = GLFWTransform()
    self.cart.attributes.append(cartTransform)
    let (pl, pr, pt, pb) = (
      -poleWidth / 2, poleWidth / 2,
      poleLength - poleWidth / 2, -poleWidth / 2)
    self.pole = GLFWPolygon(vertices: [(pl, pb), (pl, pt), (pr, pt), (pr, pb)])
    self.pole.attributes.append(GLFWColor(red: 0.8, green: 0.6, blue: 0.4))
    self.poleTransform = GLFWTransform(translation: (0.0, cartHeight / 4))
    self.pole.attributes.append(poleTransform)
    self.pole.attributes.append(cartTransform)
    let axleVertices = (0..<30).map { i -> (Float, Float) in
      let angle = 2 * Float.pi * Float(i) / Float(30)
      return (cos(angle) * poleWidth / 2, sin(angle) * poleWidth / 2)
    }
    self.axle = GLFWPolygon(vertices: axleVertices)
    self.axle.attributes.append(poleTransform)
    self.axle.attributes.append(cartTransform)
    self.axle.attributes.append(GLFWColor(red: 0.5, green: 0.5, blue: 0.8))
    self.track = GLFWLine(start: (0.0, cartTop), end: (Float(windowWidth), cartTop))
    self.track.attributes.append(GLFWColor(red: 0, green: 0, blue: 0))
  }

  @inlinable
  public func draw() {
    cart.renderWithAttributes()
    pole.renderWithAttributes()
    axle.renderWithAttributes()
    track.renderWithAttributes()
  }

  @inlinable
  public mutating func render(observation: CartPoleEnvironment.Observation) {
    // TODO: Support batched environments.
    let position = observation.position[0].scalarized()
    let angle = observation.angle[0].scalarized()
    cartTransform.translation = (position * scale + Float(windowWidth) / 2, cartTop)
    poleTransform.rotation = -angle
    render(in: window)
  }
}

. . . To be continued