def test_unsupported_synapse(): with pytest.raises(ValueError): ss2sim(sys=Lowpass(0.1), synapse=Alpha(0.1)) with pytest.raises(ValueError): ss2sim(sys=Lowpass(0.1), synapse=LinearFilter([1, 2], [2, 1]), dt=0.01) with pytest.raises(ValueError): ss2sim(sys=Lowpass(0.1), synapse=LinearFilter(1, 1)) with pytest.raises(ValueError): ss2sim(sys=Lowpass(0.1), synapse=LinearFilter([1, 0.01], [1])) with pytest.raises(ValueError): ss2sim(sys=Lowpass(0.1), synapse=LinearFilter([1], [2, 1, 1]))
def test_doubleexp_discrete(): sys = PadeDelay(0.1, order=5) tau1 = 0.05 tau2 = 0.02 dt = 0.002 syn = DoubleExp(tau1, tau2) FH = ss2sim(sys, syn, dt=dt) a1 = np.exp(-dt / tau1) a2 = np.exp(-dt / tau2) t1 = 1 / (1 - a1) t2 = 1 / (1 - a2) c = [a1 * a2 * t1 * t2, - (a1 + a2) * t1 * t2, t1 * t2] sys = cont2discrete(sys, dt=dt) A = sys.A FHA = c[2] * np.dot(A, A) + c[1] * A + c[0] * np.eye(len(A)) B = sys.B FHB = (c[2] * A + (c[1] + c[2]) * np.eye(len(A))).dot(B) assert np.allclose(FH.A, FHA) assert np.allclose(FH.B, FHB) assert np.allclose(FH.C, sys.C) assert np.allclose(FH.D, sys.D)
def test_principle3_discrete(): sys = PadeDelay(0.1, order=5) tau = 0.01 dt = 0.002 syn = Lowpass(tau) FH = ss2sim(sys, syn, dt=dt) a = np.exp(-dt / tau) sys = cont2discrete(sys, dt=dt) assert np.allclose(FH.A, (sys.A - a * np.eye(len(sys))) / (1 - a)) assert np.allclose(FH.B, sys.B / (1 - a)) assert np.allclose(FH.C, sys.C) assert np.allclose(FH.D, sys.D) # We can also do the discretization ourselves and then pass in dt=None assert ss_equal( ss2sim(sys, cont2discrete(syn, dt=dt), dt=None), FH)
def test_principle3_continuous(): sys = PadeDelay(0.1, order=5) tau = 0.01 syn = Lowpass(tau) FH = ss2sim(sys, syn, dt=None) assert np.allclose(FH.A, tau * sys.A + np.eye(len(sys))) assert np.allclose(FH.B, tau * sys.B) assert np.allclose(FH.C, sys.C) assert np.allclose(FH.D, sys.D)
def test_unsupported_mapping(): lpf = Lowpass(0.1) with pytest.raises(ValueError): ss2sim(sys=lpf, synapse=Highpass(0.1), dt=None) with pytest.raises(ValueError): ss2sim(sys=~z, synapse=lpf, dt=None) with pytest.raises(ValueError): ss2sim(sys=lpf, synapse=~z, dt=None) with pytest.raises(ValueError): ss2sim(sys=~z, synapse=~z, dt=1.)
def test_mapping(Simulator, plt, seed): sys = Alpha(0.1) syn = Lowpass(0.01) gsyn = 2*syn # scaled lowpass isyn = 2/s # scaled integrator dt = 0.001 ss = ss2sim(sys, syn, None) # normal lowpass, continuous dss = ss2sim(sys, syn, dt) # normal lowpass, discrete gss = ss2sim(sys, gsyn, None) # scaled lowpass, continuous gdss = ss2sim(sys, gsyn, dt) # scaled lowpass, discrete iss = ss2sim(sys, isyn, None) # scaled integrator, continuous idss = ss2sim(sys, isyn, dt) # scaled integrator, discrete assert ss.analog and gss.analog and iss.analog assert not (dss.analog or gdss.analog or idss.analog) with Network(seed=seed) as model: stim = nengo.Node(output=lambda t: np.sin(20*np.pi*t)) probes = [] for mapped, synapse in ((ss, syn), (dss, syn), (gss, gsyn), (gdss, gsyn), (iss, isyn), (idss, isyn)): A, B, C, D = mapped.ss x = nengo.Node(size_in=2) y = nengo.Node(size_in=1) nengo.Connection(stim, x, transform=B, synapse=synapse) nengo.Connection(x, x, transform=A, synapse=synapse) nengo.Connection(x, y, transform=C, synapse=None) nengo.Connection(stim, y, transform=D, synapse=None) probes.append(nengo.Probe(y)) p_stim = nengo.Probe(stim) pss, pdss, pgss, pgdss, piss, pidss = probes with Simulator(model, dt=dt) as sim: sim.run(1.0) expected = shift(sys.filt(sim.data[p_stim], dt)) plt.plot(sim.trange(), sim.data[pss], label="Continuous", alpha=0.5) plt.plot(sim.trange(), sim.data[pdss], label="Discrete", alpha=0.5) plt.plot(sim.trange(), sim.data[pgss], label="Gain Cont.", alpha=0.5) plt.plot(sim.trange(), sim.data[pgdss], label="Gain Disc.", alpha=0.5) plt.plot(sim.trange(), sim.data[piss], label="Integ Cont.", alpha=0.5) plt.plot(sim.trange(), sim.data[pidss], label="Integ Disc.", alpha=0.5) plt.plot(sim.trange(), expected, label="Expected", linestyle='--') plt.legend() assert np.allclose(sim.data[pss], expected, atol=0.01) assert np.allclose(sim.data[pdss], expected) assert np.allclose(sim.data[pgss], expected, atol=0.01) assert np.allclose(sim.data[pgdss], expected) assert np.allclose(sim.data[piss], expected, atol=0.01) assert np.allclose(sim.data[pidss], expected)
def test_doubleexp_continuous(sys): tau1 = 0.05 tau2 = 0.02 syn = DoubleExp(tau1, tau2) FH = ss2sim(sys, syn, dt=None) A = sys.A FHA = tau1 * tau2 * np.dot(A, A) + (tau1 + tau2) * A + np.eye(len(A)) B = sys.B FHB = (tau1 * tau2 * A + (tau1 + tau2) * np.eye(len(A))).dot(B) assert np.allclose(FH.A, FHA) assert np.allclose(FH.B, FHB) assert np.allclose(FH.C, sys.C) assert np.allclose(FH.D, sys.D)
def test_mapping(Simulator, plt, seed): sys = Alpha(0.1) syn = Lowpass(0.01) gsyn = 2 * syn # scaled lowpass isyn = 2 / s # scaled integrator dt = 0.001 ss = ss2sim(sys, syn) # normal lowpass, continuous dss = ss2sim(sys, syn, dt) # normal lowpass, discrete gss = ss2sim(sys, gsyn) # scaled lowpass, continuous gdss = ss2sim(sys, gsyn, dt) # scaled lowpass, discrete iss = ss2sim(sys, isyn) # scaled integrator, continuous idss = ss2sim(sys, isyn, dt) # scaled integrator, discrete assert ss.analog and gss.analog and iss.analog assert not (dss.analog or gdss.analog or idss.analog) with Network(seed=seed) as model: stim = nengo.Node(output=lambda t: np.sin(20 * np.pi * t)) probes = [] for mapped, synapse in ((ss, syn), (dss, syn), (gss, gsyn), (gdss, gsyn), (iss, isyn), (idss, isyn)): A, B, C, D = mapped.ss x = nengo.Node(size_in=2) y = nengo.Node(size_in=1) nengo.Connection(stim, x, transform=B, synapse=synapse) nengo.Connection(x, x, transform=A, synapse=synapse) nengo.Connection(x, y, transform=C, synapse=None) nengo.Connection(stim, y, transform=D, synapse=None) probes.append(nengo.Probe(y)) p_stim = nengo.Probe(stim) pss, pdss, pgss, pgdss, piss, pidss = probes sim = Simulator(model, dt=dt) sim.run(1.0) expected = apply_filter(sys, dt, sim.data[p_stim], axis=0) plt.plot(sim.trange(), sim.data[pss], label="Continuous", alpha=0.5) plt.plot(sim.trange(), sim.data[pdss], label="Discrete", alpha=0.5) plt.plot(sim.trange(), sim.data[pgss], label="Gain Cont.", alpha=0.5) plt.plot(sim.trange(), sim.data[pgdss], label="Gain Disc.", alpha=0.5) plt.plot(sim.trange(), sim.data[piss], label="Integ Cont.", alpha=0.5) plt.plot(sim.trange(), sim.data[pidss], label="Integ Disc.", alpha=0.5) plt.plot(sim.trange(), expected, label="Expected", linestyle="--") plt.legend() assert np.allclose(sim.data[pss], expected, atol=0.01) assert np.allclose(sim.data[pdss], expected) assert np.allclose(sim.data[pgss], expected, atol=0.01) assert np.allclose(sim.data[pgdss], expected) assert np.allclose(sim.data[piss], expected, atol=0.01) assert np.allclose(sim.data[pidss], expected)
def __init__(self, sys, n_neurons, synapse, dt, radii=1.0, input_synapse=None, output_synapse=None, normalizer=default_normalizer(), solver=Default, label=None, seed=None, add_to_container=None, **ens_kwargs): super(LinearNetwork, self).__init__(label, seed, add_to_container) # Parameter checking self.sys = LinearSystem(sys) self.n_neurons = n_neurons self.synapse = synapse self.dt = dt self.radii = radii self.input_synapse = input_synapse self.output_synapse = output_synapse self.normalizer = normalizer if len(self.sys) == 0: raise ValueError("system (%s) is zero order" % self.sys) # TODO: duplicates checks in ss2sim synapse = LinearSystem(self.synapse) if len(synapse) != 1 or not synapse.proper or not synapse.analog: raise ValueError("synapse (%s) must be first-order, proper, and " "analog" % synapse) if not self.sys.analog: # this restriction exists to simplify the life of the normalizer # by assuming we only need to normalize continuous systems. # ss2sim will eventually discretize the system with the given dt # as long as it's not None raise ValueError("system (%s) must be analog" % self.sys) if self.sys.has_passthrough and self.output_synapse is None: # the user shouldn't filter the output node themselves. an # output synapse should be given so we can do it before the # passthrough. warnings.warn("output_synapse should be given if the system has " "a passthrough, otherwise filtering the output will " "also filter the passthrough") if not self.sys.is_stable: # This means certain normalizers won't work, because the worst-case # output is now unbounded. warnings.warn("system (%s) is not exponentially stable" % self.sys) # Obtain a normalized state-space representation self.normalized, self.info = self.normalizer(self.sys, self.radii) self.A, self.B, self.C, self.D = ss2sim( self.normalized, self.synapse, self.dt).ss self.size_in = self.B.shape[1] self.size_state = len(self.A) self.size_out = len(self.C) with self: # Create internal Nengo objects self.input = nengo.Node(size_in=self.size_in, label="input") self.output = nengo.Node(size_in=self.size_out, label="output") self.x = nengo.networks.EnsembleArray( self.n_neurons, self.size_state, ens_dimensions=1, **ens_kwargs) if solver is not Default: # https://github.com/nengo/nengo/issues/1044 solver._hack = random() # https://github.com/nengo/nengo/issues/1040 self.x.add_output('output', function=None, solver=solver) # Connect everything up using (A, B, C, D) self.conn_A = nengo.Connection( self.x.output, self.x.input, transform=self.A, synapse=self.synapse) self.conn_B = nengo.Connection( self.input, self.x.input, transform=self.B, synapse=self.input_synapse) self.conn_C = nengo.Connection( self.x.output, self.output, transform=self.C, synapse=self.output_synapse) self.conn_D = nengo.Connection( self.input, self.output, transform=self.D, synapse=None)
def test_unsupported_system(): with pytest.raises(ValueError): ss2sim(sys=z, synapse=Lowpass(0.1))
def __init__(self, sys, n_neurons_per_ensemble, synapse, dt, radii=1.0, input_synapse=None, output_synapse=None, realizer=Hankel(), solver=Default, label=None, seed=None, add_to_container=None, **ens_kwargs): super(LinearNetwork, self).__init__(label, seed, add_to_container) # Parameter checking self.sys = LinearSystem(sys) self.n_neurons_per_ensemble = n_neurons_per_ensemble self.synapse = synapse self.dt = dt self.radii = radii self.input_synapse = input_synapse self.output_synapse = output_synapse self.realizer = realizer if solver is not Default: # https://github.com/nengo/nengo/issues/1044 solver._hack = random() if len(self.sys) == 0: raise ValueError("system (%s) is zero order" % self.sys) if self.sys.has_passthrough and self.output_synapse is None: # the user shouldn't filter the output node themselves. an # output synapse should be given so we can do it before the # passthrough. warnings.warn("output_synapse should be given if the system has " "a passthrough, otherwise filtering the output will " "also filter the passthrough") if not self.sys.is_stable: # This means certain normalizers won't work, because the worst-case # output is now unbounded. warnings.warn("system (%s) is not exponentially stable" % self.sys) # Obtain state-space transformation and realization self._realizer_result = self.realizer(self.sys, self.radii) # Map the system onto the synapse self._mapped = ss2sim(self.realization, self.synapse, self.dt) with self: # Create internal Nengo objects self._input = nengo.Node(size_in=self.size_in, label="input") self._output = nengo.Node(size_in=self.size_out, label="output") x_input, x_output = self._make_core(solver, **ens_kwargs) # Connect everything up using (A, B, C, D) nengo.Connection( x_output, x_input, transform=self.A, synapse=self.synapse) nengo.Connection( self.input, x_input, transform=self.B, synapse=self.input_synapse) nengo.Connection( x_output, self.output, transform=self.C, synapse=self.output_synapse) if not np.allclose(self.D, 0): logging.info("Passthrough (%s) on LinearNetwork with sys=%s", self.D, self.sys) nengo.Connection( self.input, self.output, transform=self.D, synapse=None)