def test_bad_filt(): sys = PadeDelay(0.1, order=4).X with pytest.raises(ValueError): sys.filt(np.ones((4, 4))) with pytest.raises(ValueError): sys.filt(np.ones((4, 1)), filtfilt=True) with pytest.raises(ValueError): sys.filt(np.ones((4,)), copy=False)
def test_readout(legendre, d, tol): theta = 0.1 if legendre: sys = LegendreDelay(theta, d) readout = _legendre_readout else: sys = PadeDelay(theta, d) readout = _pade_readout # decoding at r=1 (t=-theta) is equivalent to decoding a delay of theta C = readout(d, 1) assert np.allclose(sys.C, C) assert np.allclose(sys.D, 0) freqs = np.linspace(0, 5, 100) s = 2j * np.pi * freqs # check that frequency response has small error at low-frequencies # for a variety of different readouts for r in np.linspace(0, 1, 100): C = readout(d, r) sys = LinearSystem((sys.A, sys.B, C, sys.D), analog=True) error = np.abs(sys(s) - np.exp(-r*theta*s)) assert 0 < rms(error) < tol, r
def test_output_filter(Simulator, seed, rng): dt = 0.001 T = 1.0 sys = PadeDelay(0.1, order=3, p=3) assert sys.has_passthrough synapse = 0.01 with Network(seed=seed) as model: stim = nengo.Node( output=nengo.processes.WhiteSignal(T, high=10, seed=seed)) subnet = LinearNetwork(sys, 1, synapse=synapse, output_synapse=synapse, dt=dt, neuron_type=nengo.neurons.Direct()) nengo.Connection(stim, subnet.input, synapse=None) assert subnet.input_synapse is None p_ideal = nengo.Probe(subnet.input, synapse=sys) p_output = nengo.Probe(subnet.output, synapse=None) with Simulator(model, dt=dt) as sim: sim.run(T) assert np.allclose(sim.data[p_output][:-1], sim.data[p_ideal][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 delayed_synapse(): a = 0.1 # desired delay b = 0.01 # synapse delay tau = 0.01 # recurrent tau hz = 15 # input frequency t = 1.0 # simulation time dt = 0.00001 # simulation timestep order = 6 # order of pade approximation tau_probe = 0.02 dexp_synapse = DoubleExp(tau, tau / 5) sys_lambert = lambert_delay(a, b, tau, order - 1, order) synapse = (cont2discrete(Lowpass(tau), dt=dt) * DiscreteDelay(int(b / dt))) n_neurons = 2000 neuron_type = PerfectLIF() A, B, C, D = sys_lambert.observable.transform(5*np.eye(order)).ss sys_normal = PadeDelay(a, order) assert len(sys_normal) == order with Network(seed=0) as model: stim = Node(output=WhiteSignal(t, high=hz, y0=0)) x = EnsembleArray(n_neurons / order, len(A), neuron_type=neuron_type) output = Node(size_in=1) Connection(x.output, x.input, transform=A, synapse=synapse) Connection(stim, x.input, transform=B, synapse=synapse) Connection(x.output, output, transform=C, synapse=None) Connection(stim, output, transform=D, synapse=None) lowpass_delay = LinearNetwork( sys_normal, n_neurons_per_ensemble=n_neurons / order, synapse=tau, input_synapse=tau, dt=None, neuron_type=neuron_type, radii=1.0) Connection(stim, lowpass_delay.input, synapse=None) dexp_delay = LinearNetwork( sys_normal, n_neurons_per_ensemble=n_neurons / order, synapse=dexp_synapse, input_synapse=dexp_synapse, dt=None, neuron_type=neuron_type, radii=1.0) Connection(stim, dexp_delay.input, synapse=None) p_stim = Probe(stim, synapse=tau_probe) p_output_delayed = Probe(output, synapse=tau_probe) p_output_lowpass = Probe(lowpass_delay.output, synapse=tau_probe) p_output_dexp = Probe(dexp_delay.output, synapse=tau_probe) with Simulator(model, dt=dt, seed=0) as sim: sim.run(t) return (a, dt, sim.trange(), sim.data[p_stim], sim.data[p_output_delayed], sim.data[p_output_lowpass], sim.data[p_output_dexp])
def time_cells(order): seed = 0 n_neurons = 300 theta = 4.784 tau = 0.1 radius = 0.3 realizer = Balanced # The following was patched from nengolib commit # 7e204e0c305e34a4f63d0a6fbba7197862bbcf22, prior to # aee92b8fc45749f07f663fe696745cf0a33bfa17, so that # the generated PDF is consistent with the version that the # overlay was added to. def PadeDelay(c, q): j = np.arange(1, q+1, dtype=np.float64) u = (q + j - 1) * (q - j + 1) / (c * j) A = np.zeros((q, q)) B = np.zeros((q, 1)) C = np.zeros((1, q)) D = np.zeros((1,)) A[0, :] = B[0, 0] = -u[0] A[1:, :-1][np.diag_indices(q-1)] = u[1:] C[0, :] = - j / float(q) * (-1) ** (q - j) return LinearSystem((A, B, C, D), analog=True) F = PadeDelay(theta, order) synapse = Alpha(tau) pulse_s = 0 pulse_w = 1.0 pulse_h = 1.5 T = 6.0 dt = 0.001 pulse = np.zeros(int(T/dt)) pulse[int(pulse_s/dt):int((pulse_s + pulse_w)/dt)] = pulse_h with Network(seed=seed) as model: u = Node(output=PresentInput(pulse, dt)) delay = LinearNetwork( F, n_neurons_per_ensemble=n_neurons / len(F), synapse=synapse, input_synapse=None, radii=radius, dt=dt, realizer=realizer()) Connection(u, delay.input, synapse=None) p_x = Probe(delay.state.input, synapse=None) p_a = Probe(delay.state.add_neuron_output(), synapse=None) with Simulator(model, dt=dt) as sim: sim.run(T) return sim.trange(), sim.data[p_x], sim.data[p_a]
def test_invalid_sample(): process = WhiteSignal(1.0, high=10) sys = PadeDelay(0.1, order=4) dist = EvalPoints(sys, process) with pytest.raises(ValidationError): dist.sample(100, len(sys)) # needs to equal sys.size_out dist = Encoders(sys, process) with pytest.raises(ValidationError): dist.sample(100, len(sys)) # needs to equal sys.size_out
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_non_siso_filtering(rng): sys = PadeDelay(0.1, order=4) length = 1000 SIMO = sys.X assert not SIMO.is_SISO assert SIMO.size_in == 1 assert SIMO.size_out == len(sys) x = SIMO.impulse(length) for i, (sub1, sub2) in enumerate(zip(sys, SIMO)): assert sub1 == sub2 y1 = sub1.impulse(length) y2 = sub2.impulse(length) _transclose(shift(y1), shift(y2), x[:, i]) B = np.asarray([[1, 2, 3], [0, 0, 0], [0, 0, 0], [0, 0, 0]]) * sys.B u = rng.randn(length, 3) Bu = u.dot([1, 2, 3]) assert Bu.shape == (length,) MISO = LinearSystem((sys.A, B, sys.C, np.zeros((1, 3))), analog=True) assert not MISO.is_SISO assert MISO.size_in == 3 assert MISO.size_out == 1 y = cont2discrete(MISO, dt=0.001).filt(u) assert y.shape == (length,) assert np.allclose(shift(sys.filt(Bu)), y) MIMO = MISO.X assert not MIMO.is_SISO assert MIMO.size_in == 3 assert MIMO.size_out == 4 y = MIMO.filt(u) I = np.eye(len(sys)) for i, sub1 in enumerate(MIMO): sub2 = LinearSystem((sys.A, B, I[i:i+1], np.zeros((1, 3)))) _transclose(sub1.filt(u), sub2.filt(u), y[:, i])
def discrete_example(seed, dt): n_neurons = 1000 theta = 0.1 freq = 50 q = 27 radii = 1.0 sys = PadeDelay(theta, q) T = 5000*(dt+0.001) rms = 1.0 signal = WhiteSignal(T, high=freq, rms=rms, y0=0) tau = 0.1 tau_probe = 0.02 reg = 0.1 # Determine radii using direct mode with LinearNetwork( sys, n_neurons_per_ensemble=1, input_synapse=tau, synapse=tau, dt=dt, neuron_type=Direct(), realizer=Balanced()) as model: Connection(Node(output=signal), model.input, synapse=None) p_x = Probe(model.state.input, synapse=None) with Simulator(model, dt=dt, seed=seed+1) as sim: sim.run(T) radii *= np.max(abs(sim.data[p_x]), axis=0) logging.info("Radii: %s", radii) with Network(seed=seed) as model: u = Node(output=signal) kwargs = dict( n_neurons_per_ensemble=n_neurons / len(sys), input_synapse=tau, synapse=tau, radii=radii, solver=LstsqL2(reg=reg), realizer=Balanced()) delay_disc = LinearNetwork(sys, dt=dt, **kwargs) delay_cont = LinearNetwork(sys, dt=None, **kwargs) Connection(u, delay_disc.input, synapse=None) Connection(u, delay_cont.input, synapse=None) p_u = Probe(u, synapse=tau_probe) p_y_disc = Probe(delay_disc.output, synapse=tau_probe) p_y_cont = Probe(delay_cont.output, synapse=tau_probe) with Simulator(model, dt=dt, seed=seed) as sim: sim.run(T) return (theta, dt, sim.trange(), sim.data[p_u], sim.data[p_y_disc], sim.data[p_y_cont])
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_radii(Simulator, seed, plt): sys = canonical(PadeDelay(0.2, order=3)) dt = 0.001 T = 0.5 plt.figure() # Precompute the exact bounds for an impulse stimulus radii = [] for sub in sys: response = sub.impulse(int(T / dt), dt=dt) amplitude = np.max(abs(response)) assert amplitude >= 1e-4 # otherwise numerical issues radii.append(amplitude) plt.plot(response / amplitude, linestyle='--') with Network(seed=seed) as model: # Impulse stimulus stim = nengo.Node(output=lambda t: 1 / dt if t <= dt else 0) # Set explicit radii for controllable realization subnet = LinearNetwork(sys, n_neurons_per_ensemble=1, synapse=0.2, input_synapse=0.2, dt=dt, radii=radii, realizer=Identity(), neuron_type=nengo.neurons.Direct()) nengo.Connection(stim, subnet.input, synapse=None) p = nengo.Probe(subnet.state.output, synapse=None) with Simulator(model, dt=dt) as sim: sim.run(T) plt.plot(sim.data[p], lw=5, alpha=0.5) assert np.allclose(np.max(abs(sim.data[p]), axis=0), 1, atol=1e-4)
Q = observe_gram(balsys) diag = np.diag_indices(len(P)) offdiag = np.ones_like(P, dtype=bool) offdiag[diag] = False offdiag = np.where(offdiag) assert np.allclose(P[diag], S) assert np.allclose(P[offdiag], 0) assert np.allclose(Q[diag], S) assert np.allclose(Q[offdiag], 0) @pytest.mark.parametrize( "sys", [PadeDelay(0.1, 4), PadeDelay(0.2, 5, 5), Alpha(0.2)]) def test_hankel(sys): assert np.allclose(hsvd(sys), balanced_transformation(sys)[2]) def test_l1_norm_known(): # Check that Lowpass has a norm of exactly 1 l1, rtol = l1_norm(Lowpass(0.1)) assert np.allclose(l1, 1) assert np.allclose(rtol, 0) # Check that passthrough is handled properly assert np.allclose(l1_norm(Lowpass(0.1) + 5)[0], 6) assert np.allclose(l1_norm(Lowpass(0.1) - 5)[0], 6)
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) @pytest.mark.parametrize("sys", [PadeDelay(0.1, order=5), Lowpass(0.1)]) 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 go(name, tau, factory, recurrent_solver=Default, pre_fixture=None, post_fixture=None): set_style() theta = 0.1 order = 3 freq = 3 power = 1.0 # chosen to keep radii within [-1, 1] # print("PadeDelay(%s, %s) => %f%% error @ %sHz" % ( # theta, order, 100*abs(pade_delay_error(theta*freq, order=order)), freq)) pd = PadeDelay(theta=theta, order=order) # Heuristic for normalizing state so that each dimension is ~[-1, +1] rz = Balanced()(pd, radii=1./(np.arange(len(pd))+1)) sys = rz.realization # Compute matrix to transform from state (x) -> sampled window (u) t_samples = 100 C = np.asarray([readout(len(pd), r) for r in np.linspace(0, 1, t_samples)]).dot(rz.T) assert C.shape == (t_samples, len(sys)) n_neurons = 128 # per dimension map_hw = ss2sim(sys, synapse=Lowpass(tau), dt=None) # analog mapping assert np.allclose(map_hw.A, tau*sys.A + np.eye(len(sys))) assert np.allclose(map_hw.B, tau*sys.B) with nengo.Network() as model: if pre_fixture is not None: pre_fixture(model) u = nengo.Node(output=0, label='u') p_u = nengo.Probe(u, synapse=None) # This is needed because a single node can't connect to multiple # different ensembles. We need a separate node for each ensemble. Bu = [nengo.Node(output=lambda _, u, b_i=map_hw.B[i].squeeze(): b_i*u, size_in=1, label='Bu[%d]' % i) for i in range(len(sys))] X = [] for i in range(len(sys)): ens = nengo.Ensemble( n_neurons=n_neurons, dimensions=1, label='X[%d]' % i) X.append(ens) P = [] for i in range(len(sys)): nengo.Connection(u, Bu[i], synapse=None) nengo.Connection(Bu[i], X[i], synapse=tau) for j in range(len(sys)): nengo.Connection(X[j], X[i], synapse=tau, function=lambda x_j, a_ij=map_hw.A[i, j]: a_ij*x_j, solver=recurrent_solver) P.append(nengo.Probe(X[i], synapse=None)) def do_trial(name, seed, length=2000, dt=0.001, tau_probe=0.02, sanity=False, **kwargs): # Note: depends on the globals, (factory, C, model, u, p_u, P, sys) process = nengo.processes.WhiteSignal( period=length*dt, rms=power, high=freq, y0=0, seed=seed) test_u = process.run_steps(length, dt=dt) x_ideal = sys.X.filt(test_u, dt=dt) if sanity: analyze("ideal-%s" % name, t=process.ntrange(length, dt=dt), u=test_u, x_hat=x_ideal, x_ideal=x_ideal, C=C, theta=theta, dump_file=False, do_plot=False) u.output = process with factory(network=model, dt=dt) as sim: sim.run(length*dt) if post_fixture: post_fixture(sim) assert np.allclose(test_u, np.asarray(sim.data[p_u])) # Use discrete principle 3, offline, to get x_hat # from the unfiltered spikes representing x. # This is analagous to probing the PSC, pre-encoding. syn_probe = Lowpass(tau_probe) map_out = ss2sim(sys, synapse=syn_probe, dt=dt) x_raw = np.asarray([sim.data[p] for p in P]).squeeze() f = map_out.A.dot(x_raw) + map_out.B.dot(test_u.T) x_hat = syn_probe.filt(f, axis=1, dt=dt).T return analyze( name=name, t=sim.trange(), u=test_u, x_hat=x_hat, x_ideal=x_ideal, C=C, theta=theta, **kwargs) data = defaultdict(list) for trial in range(25): for seed in range(1, 11): data['Trial'].append(trial) data['Test Case (#)'].append(seed) data['NRMSE'].append( do_trial(name="scratch-%s-DN-%d-%d" % (name, trial, seed), seed=seed, dump_file=False)) df = DataFrame(data) df.to_pickle(datapath("%s-delay-network.pkl" % name)) return bs.bootstrap(np.asarray(df['NRMSE']), stat_func=bs_stats.mean, alpha=1-0.95) # 95% CI
def figure_delay_full(targets): q = 12 freq_times_theta = 1.0 theta = 0.2 # affects resolution of plot alongside dt T = 10 * theta dt = 0.005 seed = 5 # chosen to look "interesting" u = WhiteSignal(T, high=freq_times_theta / theta, seed=seed, y0=0, rms=0.4).run(T, dt=dt) t = np.arange(0, T, dt) i = q - 1 - np.arange(q, dtype=np.float64) assert np.allclose(delay_readout(q, theta, theta), (-1)**(q - 1 - i) * (i + 1) / q) num_thetas = 200 num_freqs = 200 props = np.linspace(0, 1.0, num_thetas) freqs = np.linspace(0, 10. / theta, num_freqs) s = 2.j * np.pi * freqs cmap = sns.diverging_palette(h_neg=34, h_pos=215, s=99, l=66, sep=1, center="dark", n=num_thetas) with sns.axes_style('ticks'): with sns.plotting_context('paper', font_scale=2.8): pylab.figure(figsize=(18, 5)) gs = gridspec.GridSpec(1, 2, width_ratios=[1, 1.618]) gs.update(wspace=0.3) ax1 = plt.subplot(gs[0]) ax2 = plt.subplot(gs[1]) ax1.set_title(r"Temporal Coding Accuracy").set_y(1.05) ax2.set_title(r"Decoding at $\theta$ = Frequency$^{-1}$").set_y( 1.05) for i, thetap in enumerate(props * theta): A, B, _, D = PadeDelay(theta, order=q).ss C = delay_readout(q, thetap, theta)[::-1] tf = LinearSystem((A, B, C, D)) ax1.plot(freqs * theta, abs(np.exp(-s * thetap) - tf(s)), c=cmap[i], alpha=0.7) ax2.plot(t / theta, tf.filt(u, dt=dt), c=cmap[i], alpha=0.5) s = 0.4 pts = np.asarray([[freq_times_theta, 0], [(1 - s) * freq_times_theta, -s / 2], [(1 + s) * freq_times_theta, -s / 2]]) ax1.add_patch(Polygon(pts, closed=True, color='black')) ax1.set_xlabel(r"Frequency $\times \, \theta$ [Hz $\times$ s]", labelpad=20) ax1.set_ylabel(r"Absolute Error", labelpad=20) ax2.set_xlabel(r"Time $\times \, \theta^{-1}$ [s / s]", labelpad=20) ax2.set_ylabel(r"Output") lc = LineCollection(len(cmap) * [[(0, 0)]], lw=10, colors=cmap) ax2.legend([lc], [r"$\theta'$"], handlelength=2, handler_map={type(lc): HandlerDashedLines()}, bbox_to_anchor=(1.02, 1), loc=2, borderaxespad=0.) sns.despine(offset=15) savefig(targets[0])
def delay_example(): seed = 2 n_neurons = 1000 theta = 1.0 sys = PadeDelay(theta, 6) T = 20.0 dt = 0.001 freq = 1 rms = 0.4 tau = 0.1 #tau_probe = 0.02 radii = np.ones(len(sys)) # initial guess desired_radius = 0.8 # aiming to get this as largest x num_iter = 5 # number of times to simulate and retry new radius # could also do this simply by the direct method in discrete_example # but this is just to demonstrate that you can do something iterative # within the same network for _ in range(num_iter): with Network(seed=seed) as model: signal = WhiteSignal(T, high=freq, rms=rms, y0=0) u = Node(output=signal) delay = LinearNetwork( sys, n_neurons_per_ensemble=n_neurons / len(sys), synapse=tau, input_synapse=tau, radii=radii, realizer=Balanced(), dt=None) Connection(u, delay.input, synapse=None) # Since delay.state.input is the PSC x, when we can transform # that with C to get y (note D=0) without applying any filters assert np.allclose(delay.D, 0) output = Node(size_in=1) Connection(delay.state.input, output, transform=delay.C, synapse=None) # Alternative: create an output tau*dy + y such that when # filtered we get back y! Note: dy = C(Ax + Bu), since D=0. #Connection(delay.state.output, output, # transform=tau*delay.C.dot(delay.A), synapse=tau) #Connection(u, output, # transform=tau*delay.C.dot(delay.B), synapse=tau) #Connection(delay.output, output, synapse=tau) p_u = Probe(u, synapse=None) p_x = Probe(delay.state.input, synapse=None) p_a = Probe(delay.state.add_neuron_output(), synapse=None) p_y = Probe(output, synapse=None) with Simulator(model, dt=dt, seed=seed) as sim: sim.run(T) # place the worst case at x=desired_radius and re-run worst_x = np.max(np.abs(sim.data[p_x]), axis=0) radii *= (worst_x / desired_radius) logging.info("Radii: %s\nWorst x: %s", radii, worst_x) return (theta, dt, sim.trange(), sim.data[p_u], sim.data[p_x], sim.data[p_a], sim.data[p_y])
def figure_pca(targets): orders = [3, 6, 9, 12, 15, 27] theta = 10. dt = 0.01 T = theta length = int(T / dt) t = np.linspace(0, T - dt, length) t_norm = np.linspace(0, 1, len(t)) cmap = sns.diverging_palette(h_neg=34, h_pos=215, s=99, l=66, sep=1, center="dark", as_cmap=True) class MidpointNormalize(colors.Normalize): """Stolen from http://matplotlib.org/users/colormapnorms.html""" def __init__(self, vmin=None, vmax=None, midpoint=None, clip=False): self.midpoint = midpoint colors.Normalize.__init__(self, vmin, vmax, clip) def __call__(self, value, clip=None): # I'm ignoring masked values and all kinds of edge cases to make a # simple example... x, y = [self.vmin, self.midpoint, self.vmax], [0, 0.5, 1] return np.ma.masked_array(np.interp(value, x, y)) with sns.axes_style('white'): with sns.plotting_context('paper', font_scale=2.8): pylab.figure(figsize=(22, 7)) gs = gridspec.GridSpec(2, len(orders), height_ratios=[1.3, 1]) for k, order in enumerate(orders): F = PadeDelay(theta, order) A = F.A dA, dB, _, _ = cont2discrete(F, dt=dt).ss dx = np.empty((length, len(F))) x = np.empty((length, len(F))) x[0, :] = dB.squeeze() # the x0 from delta input for j in range(length - 1): dx[j, :] = A.dot(x[j, :]) x[j + 1, :] = dA.dot(x[j, :]) dx[-1, :] = A.dot(x[-1, :]) # Compute PCA of trajectory for top half pca = PCA(x, standardize=False) p = pca.Y[:, :3] logging.info("%d Accounted Variance: %s", order, np.sum(pca.fracs[:3]) / np.sum(pca.fracs)) # Compute curve for bottom half (and color map center) dist = np.cumsum(np.linalg.norm(dx, axis=1)) dist = dist / np.max(dist) infl = np.where((np.diff(np.diff(dist)) >= 0) & (t_norm[:-2] >= 0))[0][-1] cnorm = MidpointNormalize(midpoint=t_norm[infl]) ax = plt.subplot(gs[k], projection='3d') ax.set_title(r"$q = %d$" % order).set_y(1.1) # Draw in reverse order so the start is on top ax.scatter(p[::-1, 0], p[::-1, 1], p[::-1, 2], lw=5, c=t_norm[::-1], cmap=cmap, norm=cnorm, alpha=0.5) ax.set_xticks([]) ax.set_yticks([]) ax.set_zticks([]) if k == 0: ax.annotate('PCA', xy=(-50, 150), ha='left', va='top', size=22, rotation=90, bbox=None, xycoords='axes points') ax.view_init(elev=25, azim=150) ax = plt.subplot(gs[len(orders) + k]) ax.scatter(t, dist, lw=5, c=t_norm, cmap=cmap, norm=cnorm) ax.vlines(t[infl], 0, 1, linestyle='--', lw=3, alpha=0.7) if k == 0: ax.set_yticks([0, 1]) ax.set_ylabel("Length of Curve") else: ax.set_yticks([]) ax.set_xticks([0, theta / 2, theta]) ax.set_xticklabels( [r"$0$", r"$\frac{\theta}{2}$", r"$\theta$"]) ax.xaxis.set_tick_params(pad=10) ax.set_xlabel("Time [s]", labelpad=20) sns.despine(offset=10, ax=ax) savefig(targets[0])
([[-20, 1], [-100, 0]], [[0], [100]], [[1, 0]], [[0]])) def test_is_stable(): sys = Lowpass(0.1) assert sys.is_stable assert not (~s).is_stable # integrator assert LinearSystem(1).is_stable assert (~(z * (z - 0.5))).is_stable assert not (z / (z - 1)).is_stable # discrete integrator @pytest.mark.parametrize("sys", [PadeDelay(0.1, 4), PadeDelay(0.2, 5, 5)]) def test_decompose_states(sys): assert np.dot(sys.C, list(sys)) + sys.D == sys def test_non_siso_manipulation(): sys = Alpha(0.1) A, B, C, D = sys.ss SIMO = LinearSystem((A, B, np.eye(len(A)), [[0], [0]])) assert not SIMO.is_SISO assert SIMO.size_in == 1 assert SIMO.size_out == 2 assert SIMO.shape == (2, 1) assert not SIMO.has_passthrough assert ss_equal(_eval(SIMO), SIMO)
rsys = realize_result.realization assert ss_equal(rsys, sys.transform(realize_result.T)) # Check that it's still the same system, even though different matrices assert sys_equal(sys, rsys) if radii == 1: assert ss_equal(sys, rsys) else: assert not np.allclose(sys.B, rsys.B) assert not np.allclose(sys.C, rsys.C) # Check that the state vectors have scaled power assert np.allclose(state_norm(sys) / radii, state_norm(rsys)) @pytest.mark.parametrize("sys", [PadeDelay(0.1, 4), PadeDelay(0.05, 5, 5)]) def test_balreal_normalization(sys): radii = np.arange(len(sys)) + 1 balanced = Balanced() assert repr(balanced) == "Balanced()" realizer_result = balanced(sys, radii) T, Tinv, _ = balanced_transformation(sys) assert np.allclose(realizer_result.T / radii[None, :], T) assert np.allclose(realizer_result.Tinv * radii[:, None], Tinv) assert np.allclose(inv(T), Tinv) assert sys_equal(sys, realizer_result.realization)
def figure_principle3(targets): theta = 0.1 tau = 0.1 * theta lmbda = tau orders = range(6, 28) freqs = np.linspace(0.1 / theta, 16 / theta, 1000) s = 2.j * np.pi * freqs y = np.exp(-theta * s) Hinvs = (tau * s + 1) * np.exp(lmbda * s) cmap_lamb = sns.color_palette("GnBu_d", len(orders))[::-1] cmap_ignore = sns.color_palette("OrRd_d", len(orders))[::-1] data = np.empty((2, len(DISCRETE_DTS), DISCRETE_SEEDS)) for seed in range(DISCRETE_SEEDS): for i, dt in enumerate(DISCRETE_DTS): npfile = np.load(DISCRETE_SIM % (seed, i)) assert np.allclose(npfile['dt'], dt) delay = npfile['delay'] # t = npfile['t'] stim = npfile['stim'] disc = npfile['disc'] cont = npfile['cont'] target = ideal_delay(stim, delay, dt) e_disc = nrmse(disc, target=target) e_cont = nrmse(cont, target=target) data[0, i, seed] = e_disc data[1, i, seed] = e_cont i = np.where(DISCRETE_DTS == 0.001)[0][0] assert np.allclose(DISCRETE_DTS[i], 0.001) e_disc = np.mean(data, axis=2)[0, i] e_cont = np.mean(data, axis=2)[1, i] improvement = (e_cont - e_disc) / e_cont * 100 logging.info("Paper constant: Improvement at 1 ms: %s (%s -> %s)", improvement, e_cont, e_disc) with sns.axes_style('ticks'): with sns.plotting_context('paper', font_scale=2.8): f, (ax1, ax2) = pylab.subplots(1, 2, figsize=(18, 5)) ax1.set_title("Discrete Lowpass Improvement").set_y(1.05) for i, condition, cpal, marker in ((1, 'Principle 3', sns.color_palette("OrRd_d"), 'X'), (0, 'Extension', sns.color_palette("GnBu_d"), 'o')): sns.tsplot(data[i].T, 1000 * DISCRETE_DTS, condition=condition, color=cpal, marker=marker, markersize=15, lw=3, ci=95, alpha=0.7, ax=ax1) ax1.vlines([1.0], np.min(data[0]), 2.0, linestyle='--', color='black', lw=4, alpha=0.7, zorder=0) ax1.set_xlabel("Discrete Time-step [ms]", labelpad=20) ax1.set_ylabel("Absolute Error", labelpad=20) ax1.set_xlim(0, 1000 * DISCRETE_DTS[-1] + 0.1) ax2.set_title("Delayed Lowpass Improvement").set_y(1.05) for i, q in enumerate(orders): sys = PadeDelay(theta, order=q) mapped = ss2sim(sys, Lowpass(tau), dt=None) lambert = lambert_delay(theta, lmbda, tau, q - 1, q) y_lamb = lambert(Hinvs) y_ignore = mapped(Hinvs) ax2.semilogy(freqs * theta, abs(y - y_ignore), lw=2, alpha=0.8, zorder=len(orders) - i, c=cmap_ignore[i]) ax2.semilogy(freqs * theta, abs(y - y_lamb), lw=2, alpha=0.8, zorder=len(orders) - i, c=cmap_lamb[i]) lc_ignore = LineCollection(len(orders) * [[(0, 0)]], lw=10, colors=cmap_ignore) lc_lamb = LineCollection(len(orders) * [[(0, 0)]], lw=10, colors=cmap_lamb) ax2.legend([lc_ignore, lc_lamb], ['Principle 3', 'Extension'], handlelength=2, handler_map={LineCollection: HandlerDashedLines()}) ax2.set_xlabel(r"Frequency $\times \, \theta$ [Hz $\times$ s]", labelpad=20) sns.despine(offset=15) savefig(targets[0])
def compute_error(phi_times_freq, depth, order, p=None): pf = np.asarray(phi_times_freq) # switch to a delay of 1 for simplicity # this works due to the substitution of variables: theta*s <-> 1*s' sys = PadeDelay(1., order, p=p) return sys.evaluate(pf / depth)**depth - np.exp(-2j * np.pi * pf)
def figure_lambert(targets): npfile = np.load(LAMBERT_SIM) delay = npfile['delay'] dt = npfile['dt'] t = npfile['t'] stim = npfile['stim'] delayed = npfile['delayed'] lowpass = npfile['lowpass'] dexp = npfile['dexp'] target = ideal_delay(stim, delay, dt) e_delayed = nrmse(delayed, target=target) e_lowpass = nrmse(lowpass, target=target) e_dexp = nrmse(dexp, target=target) improvement = (e_lowpass - e_delayed) / e_lowpass * 100 logging.info("Paper constant: Lambert improvement: %s", improvement) logging.info("Paper constant: Delayed NRMSE: %s", e_delayed) logging.info("Paper constant: Lowpass NRMSE: %s", e_lowpass) logging.info("Paper constant: Double Exp NRMSE: %s", e_dexp) sample_rate = 100 t = t[::sample_rate] stim = stim[::sample_rate] delayed = delayed[::sample_rate] lowpass = lowpass[::sample_rate] dexp = dexp[::sample_rate] target = target[::sample_rate] tau_over_theta = 0.1 lambda_over_tau = 1.0 max_freq_times_theta = 4.0 theta = 0.1 # <-- the graph is still the same, no matter theta! tau = tau_over_theta * theta lmbda = lambda_over_tau * tau max_freq = max_freq_times_theta / theta tau2 = tau / 5 # TODO: parameters copied from delayed_synapse() q = 6 assert np.allclose(tau, 0.01) assert np.allclose(lmbda, 0.01) freqs = np.linspace(0, max_freq, 200) s = 2.j * np.pi * freqs axis = freqs * theta # scale-invariant axis lw = 3 alpha = 0.8 cmap = sns.color_palette(None, 4) F_lamb = lambert_delay(theta, lmbda, tau, q - 1, q) F_low = ss2sim(PadeDelay(theta, order=q), Lowpass(tau), dt=None) F_alpha = ss2sim(PadeDelay(theta, order=q), DoubleExp(tau, tau2), dt=None) y_low = F_low(tau * s + 1) y_lamb = F_lamb((tau * s + 1) * np.exp(lmbda * s)) y_alpha = F_alpha((tau * s + 1) * (tau2 * s + 1)) y = np.exp(-theta * s) # Compute where lmbda*s + lmbda/tau is within the principal branch tx = lmbda * 2 * np.pi * freqs st = (lmbda / tau > -(tx / np.tan(tx))) & (tx < np.pi) | (tx == 0) p = lmbda * s[st] + lmbda / tau assert np.allclose(lambertw(p * np.exp(p)), p) with sns.axes_style('ticks'): with sns.plotting_context('paper', font_scale=2.8): pylab.figure(figsize=(18, 5)) gs = gridspec.GridSpec(1, 2, width_ratios=[1, 1.618]) gs.update(wspace=0.3) ax1 = plt.subplot(gs[1]) ax2 = plt.subplot(gs[0]) ax1.set_title(r"$0.1\,$s Delay of $15\,$Hz White Noise").set_y( 1.05) ax1.plot(t, target, lw=4, c=cmap[0], zorder=4, linestyle='--') ax1.plot(t, lowpass, alpha=alpha, c=cmap[1], zorder=2) ax1.plot(t, dexp, alpha=alpha, c=cmap[3], zorder=2) ax1.plot(t, delayed, alpha=alpha, c=cmap[2], zorder=3) ax1.set_ylim(-0.5, 0.5) ax1.set_xlabel("Time [s]", labelpad=20) ax1.set_ylabel("Output") ax2.set_title("Delay Accuracy").set_y(1.05) ax2.plot(axis, np.zeros_like(freqs), lw=lw, c=cmap[0], zorder=4, linestyle='--', label=r"Ideal") ax2.plot(axis, abs(y - y_low), lw=lw, alpha=alpha, c=cmap[1], zorder=3, label=r"Lowpass") ax2.plot(axis, abs(y - y_alpha), lw=lw, alpha=alpha, c=cmap[3], zorder=3, label=r"Double-exponential") ax2.plot(axis, abs(y - y_lamb), lw=lw, alpha=alpha, c=cmap[2], zorder=3, label=r"Delayed Lowpass") s = 0.8 pts = np.asarray([[1.5, 0], [1.5 - s, -s], [1.5 + s, -s]]) ax2.add_patch(Polygon(pts, closed=True, color='black')) ax2.set_xlabel(r"Frequency $\times \, \theta$ [Hz $\times$ s]", labelpad=20) ax2.set_ylabel(r"Absolute Error", labelpad=20) ax2.legend(loc='upper left', frameon=True).get_frame().set_alpha(0.8) sns.despine(offset=15) savefig(targets[0])