def test_invalid_vectorize_folds_jagged(): @vectorize def f(a): return a with pytest.raises(TypeError, match="instantiated with a Fold"): f(fold([stimuli([0, 1]), stimuli(2)]))
def test_join_invalid(): with pytest.raises(ValueError, match="expected all input ops to be an Operator"): bundle([stimuli(np.ones(3)), stimuli(np.ones(2))]) with pytest.raises(ValueError, match="cannot bundle zero nodes"): bundle([])
def test_lower_folds_recursive(): @lower_folds def f(a): assert isinstance(a, list) assert not any(isinstance(a_i, Fold) for a_i in a) return a f([stimuli(np.ones((3, 1))), stimuli(np.ones((3, 1)))])
def test_invalid_vectorize_op_input(): class InvalidInput(Operator): @property def size_out(self): return [1, 1] with pytest.raises(ValueError, match="all input_ops produce an integer size_out"): stimuli(InvalidInput([]))
def test_custom_subnetworks(): synapse = 0.01 def gyrus_custom_subnetwork(node_a, node_b, synapse): out_a = node_a**2 out_b = (node_a * node_b - out_a) / 2 return fold([out_a, out_b]).filter(synapse) def nengo_custom_subnetwork(node_a, node_b, synapse): assert node_a.size_out == node_b.size_out d = node_a.size_out product = nengo.networks.Product(200, dimensions=d, seed=0) ens = nengo.Ensemble(100, d, seed=0) nengo.Connection(node_a, ens, synapse=None) nengo.Connection(node_a, product.input_a, synapse=None) nengo.Connection(node_b, product.input_b, synapse=None) decode_a = nengo.Node(size_in=d) decode_b = nengo.Node(size_in=d) nengo.Connection(ens, decode_a, function=np.square, synapse=synapse) nengo.Connection(product.output, decode_b, transform=0.5, synapse=synapse) nengo.Connection(decode_a, decode_b, transform=-0.5, synapse=None) return [decode_a, decode_b] # Running it in Gyrus. input_function1 = lambda t: np.sin(2 * np.pi * t) input_function2 = lambda t: t x1 = stimuli(input_function1) x2 = stimuli(input_function2) y = gyrus_custom_subnetwork(x1, x2, synapse) out1, out2 = y.run(1) # Running it in Nengo. with nengo.Network() as model: stim1 = nengo.Node(input_function1) stim2 = nengo.Node(input_function2) y1, y2 = nengo_custom_subnetwork(stim1, stim2, synapse) p1 = nengo.Probe(y1) p2 = nengo.Probe(y2) with nengo.Simulator(model) as sim: sim.run(1) assert np.allclose(sim.data[p1], out1) assert np.allclose(sim.data[p2], out2)
def test_config_left_to_right_precedence(): x1 = stimuli(0).configure(n_neurons=300) x2 = stimuli(1).configure(n_neurons=500) assert (x1 * x2)._impl_kwargs == {"n_neurons": 300} assert (x2 * x1)._impl_kwargs == {"n_neurons": 500} assert Configure.resolve_config([x1, x2]) == {"n_neurons": 300} assert Configure.resolve_config([x2, x1]) == {"n_neurons": 500}
def test_lower_folds(): @lower_folds def f(a, b=0): assert not isinstance(a, Fold) assert not isinstance(b, Fold) return a f(stimuli(np.ones((3, 1)))) f(stimulus(0), b=stimuli(np.ones((3, 1))))
def test_multiple_outputs(): input_functions = [ lambda t: np.sqrt(t) * np.cos(2 * np.pi * t), lambda t: np.sqrt(t) * np.sin(2 * np.pi * t), ] n_neurons = 100 with nengo.Network(seed=0) as model: model.config[nengo.Connection].synapse = None stim1 = nengo.Node(output=input_functions[0]) stim2 = nengo.Node(output=input_functions[1]) x1 = nengo.Ensemble(n_neurons, stim1.size_out, seed=0) x2 = nengo.Ensemble(n_neurons, stim2.size_out, seed=0) y = nengo.Node(size_in=1) y_hat = nengo.Node(size_in=1) error = nengo.Node(size_in=1) nengo.Connection(stim1, y, function=np.square) nengo.Connection(stim2, y, function=np.square) nengo.Connection(y, error) nengo.Connection(stim1, x1) nengo.Connection(stim2, x2) nengo.Connection(x1, y_hat, function=np.square) nengo.Connection(x2, y_hat, function=np.square) nengo.Connection(y_hat, error, transform=-1) p_y = nengo.Probe(y) p_y_hat = nengo.Probe(y_hat) p_error = nengo.Probe(error) with nengo.Simulator(model, dt=0.005) as sim: sim.run(1) out1 = sim.data[p_y], sim.data[p_y_hat], sim.data[p_error] # An explicit approach in Gyrus. x = stimuli(input_functions) y = x[0].apply(np.square) + x[1].apply(np.square) y_hat = x[0].decode(np.square, n_neurons=n_neurons, seed=0) + x[1].decode( np.square, n_neurons=n_neurons, seed=0) out2 = fold([y, y_hat, y - y_hat]).run(1, dt=0.005, seed=0) # Alternative operator overload approach in Gyrus. x = stimuli(input_functions) y = np.sum(x.apply(np.square)) y_hat = np.sum(x**2) out3 = fold([y, y_hat, y - y_hat]).run(1, dt=0.005, seed=0) assert np.allclose(out1, out2) assert np.allclose(out2, out3)
def test_str(): assert str(stimuli(np.ones((1, 2)))) == "Fold(Fold(Stimuli(), Stimuli()))" assert str(stimuli(np.ones((1, 1, 1)))) == "Fold(Fold(Fold(Stimuli())))" assert str(stimuli(np.ones( (1, 1, 2)))) == "Fold(Fold(Fold(Stimuli(), Stimuli())))" assert str(stimuli(np.ones((1, 1, 1, 1)))) == "Fold(Fold(Fold(Fold(...))))" assert (stimuli(np.ones(3)).bundle().__str__( max_width=2) == "Bundle1D(Stimuli(), Stimuli(), ...)") assert (stimulus(np.ones(2)).unbundle().__str__( max_depth=2, max_width=2) == "Fold(Slice(...), Slice(...))")
def test_asoperator(): stim = stimuli(0) assert asoperator(stim) is stim input_ops = (stimuli(0), stimuli(1)) op = asoperator(input_ops) assert isinstance(op, Fold) assert op.input_ops == input_ops assert asoperator(np.asarray(stim)) is stim with pytest.raises(TypeError, match="expected array scalar .* to be an Operator"): asoperator(np.asarray(0))
def test_high_dimensional_integrator(plt): d = 32 dt = 1e-3 def f(t, hz): return np.cos(2 * np.pi * hz * (t - dt)) * (2 * np.pi * hz) input_functions = [ lambda t, hz=hz: f(t, hz) for hz in np.linspace(1, 2, d) ] u = stimuli(input_functions) x = u.integrate() y = np.asarray(x.run(1, dt=dt)) y_hat = np.asarray(x.decode().filter(5e-3).run(1, dt=dt)) assert y.shape == y_hat.shape t = (1 + np.arange(y.shape[1])) * dt y_check = np.cumsum(np.asarray([f(t) for f in input_functions]), axis=1) * dt assert np.allclose(y.squeeze(axis=2)[:, 1:], y_check[:, :-1]) assert rms(y - y_hat) < 0.1 for y_i, y_hat_i in zip(y, y_hat): plt.plot(y_i.squeeze(axis=1), linestyle="--") plt.plot(y_hat_i.squeeze(axis=1), alpha=0.7) plt.xlabel("Time-step")
def test_multidimensional_function(): input_function = lambda t: [t, -np.sqrt(t), t**3] n_neurons = 2000 tau = 0.005 with nengo.Network() as model: stim = nengo.Node(output=input_function) x = nengo.Ensemble(n_neurons, 3, radius=np.sqrt(3), seed=0) y = nengo.Node(size_in=1) nengo.Connection(stim, x, synapse=None) nengo.Connection(x, y, function=np.prod, synapse=tau) p = nengo.Probe(y) with nengo.Simulator(model) as sim: sim.run(1) out1 = sim.data[p] x = (stimuli(input_function).decode(np.prod, n_neurons=n_neurons, radius=np.sqrt(3)).filter(tau)) out2 = x.run(1) assert np.allclose(out1, out2)
def test_broadcast_to(): a = stimuli([1, 2]) b = np.broadcast_to(a, (3, 2)) assert isinstance(b, Fold) assert b.shape == (3, 2) assert b[0, 0] is b[1, 0] is b[2, 0] assert b[0, 1] is b[1, 1] is b[2, 1]
def test_invalid_vectorize_folds(): @vectorize("F", excluded=[0]) def f(a): return a with pytest.raises(TypeError, match="instantiated with a Fold"): f(stimuli([0, 1]))
def test_transform(): twod = stimuli([1, 2]).bundle() assert twod.size_out == 2 assert np.all(twod.run(1, 1) == [[1, 2]]) assert np.allclose(twod.transform([2, -3]).run(1, 1), [[2, -6]]) assert np.allclose(twod.transform([[2, -3]]).run(1, 1), [[-4]]) assert np.allclose(twod.transform([[2, -3], [4, -1]]).run(1, 1), [[-4, 2]])
def test_flatten(): data = np.arange(2 * 3 * 4).reshape((2, 3, 4)) stim = stimuli(data) assert stim.shape == data.shape flat = stim.flatten() assert flat.shape == (data.size, ) assert np.allclose(flat.run(1, 1), data.flatten()[:, None, None])
def test_np_prod(): input_functions = [ lambda t: [1, -1, 0, 0.5], lambda t: [1, 0.5, -1, 1], ] d = 4 tau = 0.1 with nengo.Network() as model: stim1 = nengo.Node(output=input_functions[0]) stim2 = nengo.Node(output=input_functions[1]) x = nengo.networks.Product(200, dimensions=d, seed=0) nengo.Connection(stim1, x.input_a, synapse=None) nengo.Connection(stim2, x.input_b, synapse=None) p = nengo.Probe(x.output, synapse=tau) with nengo.Simulator(model) as sim: sim.run(1) out1 = sim.data[p] out2 = np.prod(stimuli(input_functions)).filter(tau).run(1) assert np.allclose(out1, out2)
def test_oscillator(): frequency = 4 t = 1 A, _ = oscillator(frequency) input_function = nengo.processes.Piecewise({0: [10, 0], 0.1: [0, 0]}) kick = stimuli(input_function) x = kick.integrate(lambda x: x.decode().transform(A)) y = x.run(t) with nengo.Network() as model: ens = nengo.Ensemble(100, dimensions=2, seed=0) kick = nengo.Node(input_function) psc = nengo.Node(size_in=2) integrator = nengo.synapses.LinearFilter([1], [1, 0]) nengo.Connection(kick, psc, synapse=integrator) nengo.Connection(ens, psc, transform=A, synapse=integrator) nengo.Connection(psc, ens, synapse=None) ens_probe = nengo.Probe(psc) with nengo.Simulator(model) as sim: sim.run(t) assert np.allclose(sim.data[ens_probe], y)
def test_make_context(): x = stimuli(0).decode() with nengo.Network() as model: assert model not in Operator._network_to_cache a = x.make() assert model in Operator._network_to_cache # Test that next two makes are redundant. assert len(model.all_ensembles) == 1 b = x.make() c = x.make() assert len(model.all_ensembles) == 1 with nengo.Network() as subnet: assert subnet not in Operator._network_to_cache d = x.make() assert subnet in Operator._network_to_cache assert a is b is c assert c is not d # There should be two ensembles, one in model and one in subnet. assert len(model.all_ensembles) == 2 assert len(model.ensembles) == 1 assert len(subnet.ensembles) == 1
def test_subtract_scalar(): input_function = lambda t: (2 * t - 1) * np.pi n_neurons = 100 tau = 0.005 with nengo.Network(seed=0) as model: stim = nengo.Node(input_function) one = nengo.Node(1) x = nengo.Ensemble(n_neurons, stim.size_out, radius=np.pi, seed=0) y = nengo.Node(size_in=1) one_minus_y = nengo.Node(size_in=1) nengo.Connection(stim, x, synapse=None) nengo.Connection(x, y, function=np.sin, synapse=tau) nengo.Connection(one, one_minus_y, synapse=None) nengo.Connection(y, one_minus_y, synapse=None, transform=-1) p_one_minus_y = nengo.Probe(one_minus_y) with nengo.Simulator(model) as sim: sim.run(1.0) out1 = sim.data[p_one_minus_y] x = stimuli(input_function) y = x.decode(np.sin, n_neurons=n_neurons, radius=np.pi, seed=0).filter(tau) out2 = (1 - y).run(1) out3 = ((-y) + 1).run(1) assert np.allclose(out1, out2) assert np.allclose(out2, out3)
def _lift_arg(cls, arg): # A bit like the inverse of base.py::lower_folds::_lower_arg except direct mode # stimuli are automatically created from arrays for testing convenience. if is_array(arg): return stimuli(arg).configure(neuron_type=nengo.Direct()) if isinstance(arg, (tuple, list)): return type(arg)(map(cls._lift_arg, arg)) return arg
def test_config_downstream_precedence(): stim = stimuli(0).configure(n_neurons=200) assert stim.config == {"n_neurons": 200} x = stim.configure(n_neurons=300) y = x.transform(2) z = y ** 2 assert z._impl_kwargs == {"n_neurons": 300} assert Configure.resolve_config([z]) == {"n_neurons": 300} assert z in Configure._cache a = stimuli(0).configure(n_neurons=400, radius=2) assert Configure.resolve_config([z + a]) == {"n_neurons": 300, "radius": 2} assert Configure.resolve_config([a + z]) == {"n_neurons": 400, "radius": 2} # Test hard reset of config downstream. b = (z + a).configure(reset=True, radius=3) assert b.config == {"radius": 3} assert Configure.resolve_config([b]) == {"radius": 3}
def test_transform_invalid(): with pytest.raises(TypeError, match="must be a Fold"): Transforms(stimulus(0), [1]) with pytest.raises( ValueError, match= r"input operators \(2\) must equal the number of transforms \(3\)", ): Transforms(stimuli([2, 2]), np.ones(3)) with pytest.raises(ValueError, match="expected a Fold with only a single axis"): Transforms(stimuli(np.eye(2)), np.ones(2)) with pytest.raises(ValueError, match="input_ops must have all the same size"): Transforms(fold([stimulus(1), stimulus([1, 1])]), np.ones(2))
def test_multiply_direct(rng): shape = (2, 1, 3) a = rng.randn(*shape) b = rng.randn(*shape) input_a = stimuli(a).configure(neuron_type=nengo.Direct()) input_b = stimuli(b) y = input_a * input_b with nengo.Network() as model: y.make() for ens in model.all_ensembles: assert ens.neuron_type == nengo.Direct() assert len(model.all_ensembles) == 2 * np.prod(shape) out = np.asarray(y.run(1, 1)) assert np.allclose(out.squeeze(axis=(-2, -1)), a * b)
def test_vectorize_example(): def multiply_by_two(x): y = nengo.Node(size_in=x.size_out) nengo.Connection(x, y, transform=2, synapse=None) return y x = stimuli([1, 2, 3]) y = vectorize(multiply_by_two)(x) assert np.all(np.asarray(y.run(1, dt=1)).squeeze(axis=(1, 2)) == [2, 4, 6])
def test_sum_dot(): A = [[1, 2, 3], [4, 5, 6]] x = [7, 8, 9] y_check = np.dot(A, x) y1 = np.dot(stimuli(A), x) assert y1.shape == (2, ) assert np.all(y1.size_out == [1, 1]) out1 = np.asarray(y1.run(1, 1)).squeeze(axis=(-2, -1)) assert np.allclose(out1, y_check) y2 = np.sum((stimuli(A).bundle().transform(x)).unbundle(), axis=1) assert y2.shape == (2, ) out2 = np.asarray(y2.run(1, 1)).squeeze(axis=(-2, -1)) assert np.allclose(out1, out2) y3 = stimuli(A).bundle().transform(x).transform(np.ones((1, len(x)))) assert y3.shape == (2, ) out3 = np.asarray(y3.run(1, 1)).squeeze(axis=(-2, -1)) assert np.allclose(out2, out3)
def test_multiply_bundle(rng): a = rng.randn(4, 3) b = rng.randn(4, 3) op_a = stimuli(a).configure(neuron_type=nengo.Direct()) op_b = stimuli(b) out_ideal = a * b y1 = op_a.bundle() * op_b.bundle() assert y1.shape == (4, ) assert np.all(y1.size_out == [3, 3, 3, 3]) out1 = np.asarray(y1.run(1, 1)).squeeze(axis=-2) y2 = op_a * op_b assert y2.shape == (4, 3) out2 = np.asarray(y2.run(1, 1)).squeeze(axis=(-2, -1)) assert np.allclose(out_ideal, out1) assert np.allclose(out_ideal, out2)
def test_add_folds(rng): shape = (3, 1, 2) a = rng.randn(*shape) b = rng.randn(*shape) op_a = stimuli(a) op_b = stimuli(b) y1 = op_a + b y2 = b + op_a y3 = op_a + op_b out1 = np.asarray(y1.run(1, 1)).squeeze(axis=(-2, -1)) out2 = np.asarray(y2.run(1, 1)).squeeze(axis=(-2, -1)) out3 = np.asarray(y3.run(1, 1)).squeeze(axis=(-2, -1)) assert np.allclose(a + b, out1) assert np.allclose(a + b, out2) assert np.allclose(a + b, out3)
def test_recursive_fold(): shape = (2, 3, 4, 5) constants = np.arange(np.prod(shape)).reshape(shape) @np.vectorize def make_lambdas(x): return lambda _: x * np.ones(7) input_functions = make_lambdas(constants) assert input_functions.shape == shape stim = stimuli(input_functions) assert stim.shape == shape r = np.asarray(stim.run(6, dt=1)) assert r.shape == shape + (6, 7) assert np.allclose(r, constants[..., None, None]) # Test jagged dimensions across fold out1, out2 = stimuli([lambda t: [1, 3, 4], lambda t: [1, 2]]).run(1, dt=1) assert np.allclose(out1, [1, 3, 4]) assert np.allclose(out2, [1, 2])
def test_bundle_unbundle(): data = np.arange(3 * 4 * 5).reshape(3, 4, 5) stim = stimuli(data) t = 6 y = stim.bundle() assert y.shape == (3, 4) out = np.asarray(y.run(t, 1)) y_split = y.unbundle() assert y_split.shape == (3, 4, 5) y_check = y_split.bundle() assert y_check.shape == (3, 4) out_check = np.asarray(y_check.run(t, 1)) assert np.allclose(out, out_check) out_check = np.asarray(y_split.run(t, 1)).squeeze(axis=-1).transpose( (0, 1, 3, 2)) assert out.shape == (3, 4, t, 5) assert np.allclose(out_check, out) for i in range(t): assert np.allclose(out[:, :, i, :], data) y = bundle(stim, axis=-2) assert y.shape == (3, 5) out = np.asarray(y.run(t, 1)) y_split = y.unbundle() assert y_split.shape == (3, 5, 4) out_check = np.asarray(y_split.run(t, 1)).squeeze(axis=-1).transpose( (0, 1, 3, 2)) assert out.shape == (3, 5, t, 4) assert np.allclose(out_check, out) for i in range(t): assert np.allclose(out[:, :, i, :], data.transpose((0, 2, 1))) y_split = y.unbundle(axis=-2) assert y_split.shape == (3, 4, 5) out_check = np.asarray(y_split.run(t, 1)).squeeze(axis=-1) for i in range(t): assert np.allclose(out_check[..., i], data) y = bundle(stim, axis=0) assert y.shape == (4, 5) out = np.asarray(y.run(t, 1)) y_split = y.unbundle() assert y_split.shape == (4, 5, 3) out_check = np.asarray(y_split.run(t, 1)).squeeze(axis=-1).transpose( (0, 1, 3, 2)) assert out.shape == (4, 5, t, 3) assert np.allclose(out_check, out) for i in range(t): assert np.allclose(out[:, :, i, :], data.transpose((1, 2, 0)))