Beispiel #1
0
def test_cont2discrete_zoh(dt, allclose):
    """test the function cont2discrete with zero-order hold"""
    taus = np.logspace(-np.log10(dt) - 1, np.log10(dt) + 3, 30)

    # test with lowpass filter, using analytic solution
    for tau in taus:
        num, den = [1], [tau, 1]
        d = -np.expm1(-dt / tau)
        num0, den0 = [0, d], [1, d - 1]
        num1, den1, _ = cont2discrete((num, den), dt)
        assert allclose(num0, num1)
        assert allclose(den0, den1)

    # test with alpha filter, using analytic solution
    for tau in taus:
        num, den = [1], [tau**2, 2 * tau, 1]
        a = dt / tau
        ea = np.exp(-a)
        num0 = [0, -a * ea + (1 - ea), ea * (a + ea - 1)]
        den0 = [1, -2 * ea, ea**2]
        num1, den1, _ = cont2discrete((num, den), dt)
        assert allclose(num0, num1)
        assert allclose(den0, den1)

    # test integrative filter, using analytic solution
    num, den = [1], [1, 0]
    num0, den0 = [0, dt], [1, -1]
    num1, den1, _ = cont2discrete((num, den), dt)
    assert allclose(num0, num1)
    assert allclose(den0, den1)
Beispiel #2
0
def test_cont2discrete_zoh(dt):
    taus = np.logspace(-np.log10(dt) - 1, np.log10(dt) + 3, 30)

    # test with lowpass filter, using analytic solution
    for tau in taus:
        num, den = [1], [tau, 1]
        d = -np.expm1(-dt / tau)
        num0, den0 = [0, d], [1, d - 1]
        num1, den1, _ = cont2discrete((num, den), dt)
        assert np.allclose(num0, num1)
        assert np.allclose(den0, den1)

    # test with alpha filter, using analytic solution
    for tau in taus:
        num, den = [1], [tau**2, 2*tau, 1]
        a = dt / tau
        ea = np.exp(-a)
        num0 = [0, -a*ea + (1 - ea), ea*(a + ea - 1)]
        den0 = [1, -2 * ea, ea**2]
        num1, den1, _ = cont2discrete((num, den), dt)
        assert np.allclose(num0, num1)
        assert np.allclose(den0, den1)

    # test integrative filter, using analytic solution
    num, den = [1], [1, 0]
    num0, den0 = [0, dt], [1, -1]
    num1, den1, _ = cont2discrete((num, den), dt)
    assert np.allclose(num0, num1)
    assert np.allclose(den0, den1)
Beispiel #3
0
def build_filter(model, synapse, owner, input_signal):
    num, den, _ = cont2discrete(
        (synapse.num, synapse.den), model.dt, method='zoh')
    num = num.flatten()
    num = num[1:] if num[0] == 0 else num
    den = den[1:]  # drop first element (equal to 1)
    build_discrete_filter(model, synapse, owner, input_signal, num, den)
Beispiel #4
0
    def make_step(self,
                  shape_in,
                  shape_out,
                  dt,
                  rng,
                  y0=None,
                  dtype=np.float64,
                  method='zoh'):
        """Returns a `.Step` instance that implements the linear filter."""
        assert shape_in == shape_out

        num, den = self.num, self.den
        if self.analog:
            num, den, _ = cont2discrete((num, den), dt, method=method)
            num = num.flatten()

        if den[0] != 1.:
            raise ValidationError("First element of the denominator must be 1",
                                  attr='den',
                                  obj=self)
        num = num[1:] if num[0] == 0 else num
        den = den[1:]  # drop first element (equal to 1)
        num, den = num.astype(dtype), den.astype(dtype)

        output = np.zeros(shape_out, dtype=dtype)
        if len(num) == 1 and len(den) == 0:
            return LinearFilter.NoDen(num, den, output)
        elif len(num) == 1 and len(den) == 1:
            return LinearFilter.Simple(num, den, output, y0=y0)
        return LinearFilter.General(num, den, output, y0=y0)
Beispiel #5
0
def test_combined_delay(Simulator, allclose):
    # ensure that two sequential filters has the same output
    # as combining their individual discrete transfer functions
    nt = 50
    tau = 0.005
    dt = 0.001

    sys1 = nengo.Lowpass(tau)
    (num,), den, _ = cont2discrete((sys1.num, sys1.den), dt=dt)
    sys2 = nengo.LinearFilter(np.poly1d(num) ** 2, np.poly1d(den) ** 2, analog=False)

    with nengo.Network() as model:
        u = nengo.Node(1)
        x = nengo.Node(size_in=1)
        nengo.Connection(u, x, synapse=sys1)
        p1 = nengo.Probe(x, synapse=sys1)
        p2 = nengo.Probe(u, synapse=sys2)

    with Simulator(model, dt=dt) as sim:
        sim.run_steps(nt)

    assert allclose(sim.data[p1], sim.data[p2])

    # Both have two time-step delays:
    # for sys1, this comes from two levels of filtering
    # for sys2, this comes from one level of filtering + delay in sys2
    assert allclose(sim.data[p1][:2], 0)
    assert not allclose(sim.data[p1][2], 0, record_rmse=False)
Beispiel #6
0
    def _get_ss(self, dt):
        A, B, C, D = tf2ss(self.num, self.den)

        # discretize (if len(A) == 0, filter is stateless and already discrete)
        if self.analog and len(A) > 0:
            A, B, C, D, _ = cont2discrete((A, B, C, D), dt, method=self.method)

        return A, B, C, D
Beispiel #7
0
    def __init__(self,
                 vocab,
                 gain=5,
                 deriv_synapse=.1,
                 theta=.05,
                 order=30,
                 dt=.001,
                 **kwargs):

        super().__init__(**kwargs)

        self.vocab = vocab

        # parameters of LMU
        theta = theta  # length of window
        order = order  # number of Legendre polynomials
        dt = dt  # simulation timestep

        Q = np.arange(order, dtype=np.float64)
        R = (2 * Q + 1)[:, None] / theta
        j, i = np.meshgrid(Q, Q)

        A = np.where(i < j, -1, (-1.0)**(i - j + 1)) * R
        B = (-1.0)**Q[:, None] * R
        C = np.ones((1, order))
        D = np.zeros((1, ))

        A, B, C, D, _ = cont2discrete((A, B, C, D), dt=dt, method="zoh")

        with self:
            self.input = nengo.Node(size_in=1)

            self.deriv = nengo.Ensemble(100,
                                        1,
                                        intercepts=nengo.dists.Uniform(
                                            0.1, .9),
                                        encoders=[[1]] * 100)

            gain = 5
            nengo.Connection(self.input, self.deriv, transform=gain)
            nengo.Connection(self.input,
                             self.deriv,
                             transform=-gain,
                             synapse=deriv_synapse)

            self.lmu = nengo.Node(size_in=order)
            nengo.Connection(self.deriv, self.lmu, transform=B, synapse=None)
            nengo.Connection(self.lmu, self.lmu, transform=A, synapse=0)

            self.output = nengo.Node(size_in=2)
            nengo.Connection(self.deriv, self.output[0], synapse=None)
            nengo.Connection(self.lmu,
                             self.output[1],
                             transform=C,
                             synapse=None)

        self.declare_input(self.input, None)
        self.declare_output(self.output, None)
def test_cont2discrete():
    import scipy.signal

    dt = 1e-3
    tau = 0.03
    num, den = [1], [tau**2, 2 * tau, 1]
    num0, den0, _ = scipy.signal.cont2discrete((num, den), dt)
    num1, den1, _ = cont2discrete((num, den), dt)
    assert np.allclose(num0, num1)
    assert np.allclose(den0, den1)
Beispiel #9
0
def test_cont2discrete():
    import scipy.signal

    dt = 1e-3
    tau = 0.03
    num, den = [1], [tau**2, 2*tau, 1]
    num0, den0, _ = scipy.signal.cont2discrete((num, den), dt)
    num1, den1, _ = cont2discrete((num, den), dt)
    assert np.allclose(num0, num1)
    assert np.allclose(den0, den1)
Beispiel #10
0
    def pack_data(self, dt, buffer, offset=0):
        """Pack the struct describing the filter into the buffer."""
        # Compute the filter coefficients
        b, a, _ = cont2discrete((self.num, self.den), dt)
        b = b.flatten()

        # Strip out the first values
        # `a` is negated so that it can be used with a multiply-accumulate
        # instruction on chip.
        assert b[0] == 0.0  # Oops!
        ab = np.vstack((-a[1:], b[1:])).T.flatten()

        # Convert the values to fixpoint and write into a data buffer
        struct.pack_into("<I{}s".format(self.order * 2 * 4), buffer, offset,
                         self.order, tp.np_to_fix(ab).tostring())
    def pack_data(self, dt, buffer, offset=0):
        """Pack the struct describing the filter into the buffer."""
        # Compute the filter coefficients
        b, a, _ = cont2discrete((self.num, self.den), dt)
        b = b.flatten()

        # Strip out the first values
        # `a` is negated so that it can be used with a multiply-accumulate
        # instruction on chip.
        assert b[0] == 0.0  # Oops!
        ab = np.vstack((-a[1:], b[1:])).T.flatten()

        # Convert the values to fixpoint and write into a data buffer
        struct.pack_into("<I", buffer, offset, self.order)
        buffer[offset + 4:4+self.order*8] = tp.np_to_fix(ab).tostring()
Beispiel #12
0
    def make_step(self, dt, output, method='zoh'):
        num, den = self.num, self.den
        if self.analog:
            num, den, _ = cont2discrete((num, den), dt, method=method)
            num = num.flatten()

        if den[0] != 1.:
            raise ValueError("First element of the denominator must be 1")
        num = num[1:] if num[0] == 0 else num
        den = den[1:]  # drop first element (equal to 1)

        if len(num) == 1 and len(den) == 0:
            return LinearFilter.NoDen(num, den, output)
        elif len(num) == 1 and len(den) == 1:
            return LinearFilter.Simple(num, den, output)
        return LinearFilter.General(num, den, output)
Beispiel #13
0
    def make_step(self, dt, output, method='zoh'):
        num, den = self.num, self.den
        if self.analog:
            num, den, _ = cont2discrete((num, den), dt, method=method)
            num = num.flatten()

        if den[0] != 1.:
            raise ValueError("First element of the denominator must be 1")
        num = num[1:] if num[0] == 0 else num
        den = den[1:]  # drop first element (equal to 1)

        if len(num) == 1 and len(den) == 0:
            return LinearFilter.NoDen(num, den, output)
        elif len(num) == 1 and len(den) == 1:
            return LinearFilter.Simple(num, den, output)
        return LinearFilter.General(num, den, output)
Beispiel #14
0
    def make_step(self, dt, output, method='zoh'):
        num, den, _ = cont2discrete((self.num, self.den), dt, method=method)
        num = num.flatten()
        num = num[1:] if num[0] == 0 else num
        den = den[1:]  # drop first element (equal to 1)

        if len(num) == 1 and len(den) == 0:
            return functools.partial(
                LinearFilter.no_den_step, output=output, b=num[0])
        elif len(num) == 1 and len(den) == 1:
            return functools.partial(
                LinearFilter.simple_step, output=output, a=den[0], b=num[0])
        else:
            x = collections.deque(maxlen=len(num))
            y = collections.deque(maxlen=len(den))
            return functools.partial(LinearFilter.general_step,
                                     output=output, x=x, y=y, num=num, den=den)
Beispiel #15
0
    def make_step(self, dt, output, method='zoh'):
        num, den, _ = cont2discrete((self.num, self.den), dt, method=method)
        num = num.flatten()
        num = num[1:] if num[0] == 0 else num
        den = den[1:]  # drop first element (equal to 1)

        if len(num) == 1 and len(den) == 0:
            return functools.partial(
                LinearFilter.no_den_step, output=output, b=num[0])
        elif len(num) == 1 and len(den) == 1:
            return functools.partial(
                LinearFilter.simple_step, output=output, a=den[0], b=num[0])
        else:
            x = collections.deque(maxlen=len(num))
            y = collections.deque(maxlen=len(den))
            return functools.partial(LinearFilter.general_step,
                                     output=output, x=x, y=y, num=num, den=den)
Beispiel #16
0
def lti(u, system, state=lambda x: x, dt=0.001, method="zoh"):
    r"""Operator that solves \dot{x} = A.state(x) + B.u. where A, B = system.

    The state parameter can be any callable function that consumes a Stimulus operator
    and produces a Gyrus operator that consumes said operator as an input. For instance,
    nonlinear dynamical systems may be implemented by specifying a nonlinear function
    for the ``state``.
    """
    if not is_iterable(system) or len(system) != 2:
        raise NotImplementedError(
            f"lti currently only supports systems as two-tuples: (A, B); not {system}"
        )

    # Reshape and validate A, B matrices.
    A, B = system
    A = np.asarray(A)
    if A.ndim != 2 or A.shape[0] != A.shape[1]:
        raise ValueError(f"A ({A}) must be a square matrix")
    size_out = A.shape[0]
    B = np.asarray(B)
    if B.ndim == 1:
        B = B[:, None]
    if B.ndim != 2:
        raise ValueError(f"B ({B}) must be 1D or 2D, but is {B.ndim}")
    if B.shape[0] != size_out:
        raise ValueError(
            f"B ({B}) must be an array of length {size_out}, not {B.shape[0]}")
    C = np.zeros_like(B).T
    D = 0

    # Discretize the dynamical system, \dot{x} = Ax + Bu, to
    # x[t + dt] = Ax[t] + Bu[t], using some method (ZOH recommended for stability).
    Abar, Bbar, _, _, _ = cont2discrete((A, B, C, D), dt=dt, method=method)

    # Apply Voelker (2019) equation 5.30 with H(z) = dt / (z - 1).
    # This compensates for the discretized integrator such that the resulting
    # system is the one that was requested. In this particular case (with the
    # synapse being the discretized integrator) this reduces to the inverse
    # of Euler's method.
    Amap = (Abar - np.eye(len(Abar))) / dt
    Bmap = Bbar / dt

    # Finally express the Amap, Bmap system using vectorized Gyrus operators.
    return u.transform(Bmap).integrate(
        integrand=lambda x: state(x).transform(Amap))
    def write_spec(self, spec, dt, width):
        BasicFilterImpl.write_basic_spec(self, spec, width)
        """Pack the struct describing the filter into the buffer."""
        # Compute the filter coefficients
        b, a, _ = cont2discrete((self.num, self.den), dt)
        b = b.flatten()

        # Strip out the first values
        # `a` is negated so that it can be used with a multiply-accumulate
        # instruction on chip.
        assert b[0] == 0.0  # Oops!
        ab = numpy.vstack((-a[1:], b[1:])).T.flatten()

        # Convert the values to fixpoint and write into a data buffer
        print "other {}".format(self._other)
        spec.write_value(self._order)
        print "ab {}".format(ab)
        spec.write_array(helpful_functions.convert_numpy_array_to_s16_15(ab))
Beispiel #18
0
    def __init__(self, ops, signals, config):
        super().__init__(ops, signals, config)

        # the main difference between this and the general linearfilter
        # OneX implementation is that this version allows us to merge
        # synapses with different input dimensionality (by duplicating
        # the synapse parameters for every input, rather than using
        # broadcasting)

        self.input_data = signals.combine([op.input for op in ops])
        self.output_data = signals.combine([op.output for op in ops])

        nums = []
        dens = []
        for op in ops:
            if op.process.tau <= 0.03 * signals.dt_val:
                num = 1
                den = 0
            else:
                num, den, _ = cont2discrete((op.process.num, op.process.den),
                                            signals.dt_val,
                                            method="zoh")
                num = num.flatten()

                num = num[1:] if num[0] == 0 else num
                assert len(num) == 1
                num = num[0]

                assert len(den) == 2
                den = den[1]

            nums += [num] * op.input.shape[0]
            dens += [den] * op.input.shape[0]

        if self.input_data.minibatched:
            # add batch dimension for broadcasting
            nums = np.expand_dims(nums, 0)
            dens = np.expand_dims(dens, 0)

        # apply the negative here
        dens = -np.asarray(dens)

        self.nums = tf.constant(nums, dtype=self.output_data.dtype)
        self.dens = tf.constant(dens, dtype=self.output_data.dtype)
Beispiel #19
0
            def __init__(self, units, order, theta, **kwargs):
                super().__init__(**kwargs)

                self.units = units
                self.order = order
                self.theta = theta

                Q = np.arange(order, dtype=np.float64)
                R = (2 * Q + 1)[:, None] / theta
                j, i = np.meshgrid(Q, Q)

                A = np.where(i < j, -1, (-1.0) ** (i - j + 1)) * R
                B = (-1.0) ** Q[:, None] * R
                C = np.ones((1, order))
                D = np.zeros((1,))

                self._A, self._B, _, _, _ = cont2discrete(
                    (A, B, C, D), dt=1.0, method="zoh"
                )
Beispiel #20
0
    def __init__(self, ops, signals, config):
        super(LowpassBuilder, self).__init__(ops, signals, config)

        self.input_data = signals.combine([op.input for op in ops])
        self.output_data = signals.combine([op.output for op in ops])

        nums = []
        dens = []
        for op in ops:
            if op.process.tau <= 0.03 * signals.dt_val:
                num = 1
                den = 0
            else:
                num, den, _ = cont2discrete((op.process.num, op.process.den),
                                            signals.dt_val,
                                            method="zoh")
                num = num.flatten()

                num = num[1:] if num[0] == 0 else num
                assert len(num) == 1
                num = num[0]

                assert len(den) == 2
                den = den[1]

            nums += [num] * op.input.shape[0]
            dens += [den] * op.input.shape[0]

        nums = np.asarray(nums)
        while nums.ndim < len(self.input_data.full_shape):
            nums = np.expand_dims(nums, -1)

        # note: applying the negative here
        dens = -np.asarray(dens)
        while dens.ndim < len(self.input_data.full_shape):
            dens = np.expand_dims(dens, -1)

        # need to manually broadcast for scatter_mul
        # dens = np.tile(dens, (1, signals.minibatch_size))

        self.nums = signals.constant(nums, dtype=self.output_data.dtype)
        self.dens = signals.constant(dens, dtype=self.output_data.dtype)
    def __init__(self, ops, signals, config):
        super(LowpassBuilder, self).__init__(ops, signals, config)

        self.input_data = signals.combine([op.input for op in ops])
        self.output_data = signals.combine([op.output for op in ops])

        nums = []
        dens = []
        for op in ops:
            if op.process.tau <= 0.03 * signals.dt_val:
                num = 1
                den = 0
            else:
                num, den, _ = cont2discrete((op.process.num, op.process.den),
                                            signals.dt_val, method="zoh")
                num = num.flatten()

                num = num[1:] if num[0] == 0 else num
                assert len(num) == 1
                num = num[0]

                assert len(den) == 2
                den = den[1]

            nums += [num] * op.input.shape[0]
            dens += [den] * op.input.shape[0]

        nums = np.asarray(nums)
        while nums.ndim < len(self.input_data.full_shape):
            nums = np.expand_dims(nums, -1)

        # note: applying the negative here
        dens = -np.asarray(dens)
        while dens.ndim < len(self.input_data.full_shape):
            dens = np.expand_dims(dens, -1)

        # need to manually broadcast for scatter_mul
        # dens = np.tile(dens, (1, signals.minibatch_size))

        self.nums = signals.constant(nums, dtype=self.output_data.dtype)
        self.dens = signals.constant(dens, dtype=self.output_data.dtype)
Beispiel #22
0
    def make_step(self, shape_in, shape_out, dt, rng, y0=None, dtype=np.float64, method="zoh"):
        """Returns a `.Step` instance that implements the linear filter."""
        assert shape_in == shape_out

        num, den = self.num, self.den
        if self.analog:
            num, den, _ = cont2discrete((num, den), dt, method=method)
            num = num.flatten()

        if den[0] != 1.0:
            raise ValidationError("First element of the denominator must be 1", attr="den", obj=self)
        num = num[1:] if num[0] == 0 else num
        den = den[1:]  # drop first element (equal to 1)
        num, den = num.astype(dtype), den.astype(dtype)

        output = np.zeros(shape_out, dtype=dtype)
        if len(num) == 1 and len(den) == 0:
            return LinearFilter.NoDen(num, den, output)
        elif len(num) == 1 and len(den) == 1:
            return LinearFilter.Simple(num, den, output, y0=y0)
        return LinearFilter.General(num, den, output, y0=y0)
Beispiel #23
0
    def test_pack_data(self, num, den, dt, order):
        # Create the filter
        lf = LinearFilter(0, False, num, den)

        # Create a buffer to pack data into
        data = bytearray((order * 2 + 1) * 4)

        # Pack the parameters
        lf.pack_data(dt, data, 0)

        # Generate what we expect the data to look like
        numd, dend, _ = cont2discrete((num, den), dt)
        numd = numd.flatten()
        exp = list()
        for a, b in zip(dend[1:], numd[1:]):
            exp.append(-a)
            exp.append(b)
        expected_data = tp.np_to_fix(np.array(exp)).tostring()

        # Check that's what we get
        assert struct.unpack_from("<I", data, 0)[0] == order
        assert data[4:] == expected_data
Beispiel #24
0
    def __init__(self, ops, signals):
        # TODO: implement general linear filter (using queues?)

        self.input_data = (None if ops[0].input is None else signals.combine(
            [op.input for op in ops]))
        self.output_data = signals.combine([op.output for op in ops])

        nums = []
        dens = []
        for op in ops:
            if op.process.tau <= 0.03 * signals.dt_val:
                num = 1
                den = 0
            else:
                num, den, _ = cont2discrete((op.process.num, op.process.den),
                                            signals.dt_val,
                                            method="zoh")
                num = num.flatten()

                num = num[1:] if num[0] == 0 else num
                assert len(num) == 1
                num = num[0]

                assert len(den) == 2
                den = den[1]

            nums += [num] * op.input.shape[0]
            dens += [den] * op.input.shape[0]

        nums = np.asarray(nums)[:, None]

        # note: applying the negative here
        dens = -np.asarray(dens)[:, None]

        # need to manually broadcast for scatter_mul
        # dens = np.tile(dens, (1, signals.minibatch_size))

        self.nums = tf.constant(nums, dtype=self.output_data.dtype)
        self.dens = tf.constant(dens, dtype=self.output_data.dtype)
    def test_pack_data(self, num, den, dt, order):
        # Create the filter
        lf = LinearFilter(0, False, num, den)

        # Create a buffer to pack data into
        data = bytearray((order*2 + 1)*4)

        # Pack the parameters
        lf.pack_data(dt, data, 0)

        # Generate what we expect the data to look like
        numd, dend, _ = cont2discrete((num, den), dt)
        numd = numd.flatten()
        exp = list()
        for a, b in zip(dend[1:], numd[1:]):
            exp.append(-a)
            exp.append(b)
        expected_data = tp.np_to_fix(np.array(exp)).tostring()

        # Check that's what we get
        assert struct.unpack_from("<I", data, 0)[0] == order
        assert data[4:] == expected_data
Beispiel #26
0
def test_cont2discrete_other_methods():
    dt = 1e-3

    # test with len(sys) == 3
    assert (repr(cont2discrete(
        ([1], [1], [1]),
        dt)) == "(array([1.0010005]), array([1.0010005]), 1.0, 0.001)")

    # test with len(sys) == 5
    with pytest.raises(ValueError):
        cont2discrete(([1], [1], [1], [1], [1]), dt)

    # test method gbt and alpha None
    with pytest.raises(ValueError):
        cont2discrete(([1], [1], [1]), dt, method="gbt", alpha=None)

    # test method gbt and alpha invalid
    with pytest.raises(ValueError):
        cont2discrete(([1], [1], [1]), dt, method="gbt", alpha=2)

    # test method bilinear
    assert (repr(cont2discrete(([1], [1], [1]), dt, method="bilinear")) ==
            "(array([1.0010005]), array([1.0010005]), 1.0, 0.001)")

    # test method backward_diff
    assert (repr(cont2discrete(([1], [1], [1]), dt, method="backward_diff")) ==
            "(array([1.001001]), array([1.001001]), 1.0, 0.001)")

    # test bad method
    with pytest.raises(ValueError):
        cont2discrete(([1], [1], [1]), dt, method="not_a_method")
    def __init__(self, ops, signals, config):
        super(LinearFilterBuilder, self).__init__(ops, signals, config)

        self.input_data = signals.combine([op.input for op in ops])
        self.output_data = signals.combine([op.output for op in ops])

        self.n_ops = len(ops)
        self.signal_d = ops[0].input.shape[0]
        As = []
        Cs = []
        Ds = []
        # compute the A/C/D matrices for each operator
        for op in ops:
            A, B, C, D = tf2ss(op.process.num, op.process.den)

            if op.process.analog:
                # convert to discrete system
                A, B, C, D, _ = cont2discrete((A, B, C, D), signals.dt_val,
                                              method="zoh")

            # convert to controllable form
            num, den = ss2tf(A, B, C, D)

            if op.process.analog:
                # add shift
                num = np.concatenate((num, [[0]]), axis=1)

            with warnings.catch_warnings():
                # ignore the warning about B, since we aren't using it anyway
                warnings.simplefilter("ignore", BadCoefficients)
                A, _, C, D = tf2ss(num, den)

            As.append(A)
            Cs.append(C[0])
            Ds.append(D.item())

        self.state_d = sum(x.shape[0] for x in Cs)

        # build a sparse matrix containing the A matrices as blocks
        # along the diagonal
        sparse_indices = []
        corner = np.zeros(2, dtype=np.int64)
        for A in As:
            idxs = np.reshape(np.dstack(np.meshgrid(
                np.arange(A.shape[0]), np.arange(A.shape[1]),
                indexing="ij")), (-1, 2))
            idxs += corner
            corner += A.shape
            sparse_indices += [idxs]
        sparse_indices = np.concatenate(sparse_indices, axis=0)
        self.A = signals.constant(np.concatenate(As, axis=0).flatten(),
                                  dtype=signals.dtype)
        self.A_indices = signals.constant(sparse_indices, dtype=(
            tf.int32 if np.all(sparse_indices < np.iinfo(np.int32).max)
            else tf.int64))
        self.A_shape = tf.constant(corner, dtype=tf.int64)

        if np.allclose(Cs, 0):
            self.C = None
        else:
            # add empty dimension for broadcasting
            self.C = signals.constant(np.concatenate(Cs)[:, None],
                                      dtype=signals.dtype)

        if np.allclose(Ds, 0):
            self.D = None
        else:
            # add empty dimension for broadcasting
            self.D = signals.constant(np.asarray(Ds)[:, None],
                                      dtype=signals.dtype)

        self.offsets = tf.expand_dims(
            tf.range(0, len(ops) * As[0].shape[0], As[0].shape[0]),
            axis=1)

        # create a variable to represent the internal state of the filter
        self.state_sig = signals.make_internal(
            "state", (self.state_d, signals.minibatch_size * self.signal_d),
            minibatched=False)
Beispiel #28
0
    def __init__(self, ops, signals, config):
        super(LinearFilterBuilder, self).__init__(ops, signals, config)

        self.input_data = signals.combine([op.input for op in ops])
        self.output_data = signals.combine([op.output for op in ops])

        self.n_ops = len(ops)
        self.signal_d = ops[0].input.shape[0]
        As = []
        Cs = []
        Ds = []
        # compute the A/C/D matrices for each operator
        for op in ops:
            A, B, C, D = tf2ss(op.process.num, op.process.den)

            if op.process.analog:
                # convert to discrete system
                A, B, C, D, _ = cont2discrete((A, B, C, D),
                                              signals.dt_val,
                                              method="zoh")

            # convert to controllable form
            num, den = ss2tf(A, B, C, D)

            if op.process.analog:
                # add shift
                num = np.concatenate((num, [[0]]), axis=1)

            with warnings.catch_warnings():
                # ignore the warning about B, since we aren't using it anyway
                warnings.simplefilter("ignore", BadCoefficients)
                A, _, C, D = tf2ss(num, den)

            As.append(A)
            Cs.append(C[0])
            Ds.append(D.item())

        self.state_d = sum(x.shape[0] for x in Cs)

        # build a sparse matrix containing the A matrices as blocks
        # along the diagonal
        sparse_indices = []
        corner = np.zeros(2, dtype=np.int64)
        for A in As:
            idxs = np.reshape(
                np.dstack(
                    np.meshgrid(np.arange(A.shape[0]),
                                np.arange(A.shape[1]),
                                indexing="ij")), (-1, 2))
            idxs += corner
            corner += A.shape
            sparse_indices += [idxs]
        sparse_indices = np.concatenate(sparse_indices, axis=0)
        self.A = signals.constant(np.concatenate(As, axis=0).flatten(),
                                  dtype=signals.dtype)
        self.A_indices = signals.constant(
            sparse_indices,
            dtype=(tf.int32 if np.all(
                sparse_indices < np.iinfo(np.int32).max) else tf.int64))
        self.A_shape = tf.constant(corner, dtype=tf.int64)

        if np.allclose(Cs, 0):
            self.C = None
        else:
            # add empty dimension for broadcasting
            self.C = signals.constant(np.concatenate(Cs)[:, None],
                                      dtype=signals.dtype)

        if np.allclose(Ds, 0):
            self.D = None
        else:
            # add empty dimension for broadcasting
            self.D = signals.constant(np.asarray(Ds)[:, None],
                                      dtype=signals.dtype)

        self.offsets = tf.expand_dims(tf.range(0,
                                               len(ops) * As[0].shape[0],
                                               As[0].shape[0]),
                                      axis=1)

        # create a variable to represent the internal state of the filter
        self.state_sig = signals.make_internal(
            "state", (self.state_d, signals.minibatch_size * self.signal_d),
            minibatched=False)
Beispiel #29
0
            def __init__(self, units, order, theta, input_d, **kwargs):
                super().__init__(**kwargs)

                # compute the A and B matrices according to the LMU's mathematical
                # derivation (see the paper for details)
                Q = np.arange(order, dtype=np.float64)
                R = (2 * Q + 1)[:, None] / theta
                j, i = np.meshgrid(Q, Q)

                A = np.where(i < j, -1, (-1.0) ** (i - j + 1)) * R
                B = (-1.0) ** Q[:, None] * R
                C = np.ones((1, order))
                D = np.zeros((1,))

                A, B, _, _, _ = cont2discrete((A, B, C, D), dt=1.0, method="zoh")

                with self:
                    nengo_dl.configure_settings(trainable=None)

                    # create objects corresponding to the x/u/m/h variables in LMU
                    self.x = nengo.Node(size_in=input_d)
                    self.u = nengo.Node(size_in=1)
                    self.m = nengo.Node(size_in=order)
                    self.h = nengo_dl.TensorNode(
                        tf.nn.tanh, shape_in=(units,), pass_time=False
                    )

                    # compute u_t
                    # note that setting synapse=0 (versus synapse=None) adds a
                    # one-timestep delay, so we can think of any connections with
                    # synapse=0 as representing value_{t-1}
                    nengo.Connection(
                        self.x, self.u, transform=np.ones((1, input_d)), synapse=None
                    )
                    nengo.Connection(
                        self.h, self.u, transform=np.zeros((1, units)), synapse=0
                    )
                    nengo.Connection(
                        self.m, self.u, transform=np.zeros((1, order)), synapse=0
                    )

                    # compute m_t
                    # in this implementation we'll make A and B non-trainable, but they
                    # could also be optimized in the same way as the other parameters
                    conn = nengo.Connection(self.m, self.m, transform=A, synapse=0)
                    self.config[conn].trainable = False
                    conn = nengo.Connection(self.u, self.m, transform=B, synapse=None)
                    self.config[conn].trainable = False

                    # compute h_t
                    nengo.Connection(
                        self.x,
                        self.h,
                        transform=np.zeros((units, input_d)),
                        synapse=None,
                    )
                    nengo.Connection(
                        self.h, self.h, transform=np.zeros((units, units)), synapse=0
                    )
                    nengo.Connection(
                        self.m,
                        self.h,
                        transform=nengo_dl.dists.Glorot(distribution="normal"),
                        synapse=None,
                    )