def test_node_to_gi(self, passthrough): # Create the ingoing connection parameters if not passthrough: in_transmission_params = NodeTransmissionParameters( slice(10, 20), mock.Mock(), np.ones((100, 1))) else: in_transmission_params = PassthroughNodeTransmissionParameters( np.ones((100, 1))) # Create the outgoing connection parameters out_transmission_params = PassthroughNodeTransmissionParameters( np.eye(100)) # Combine the parameter sets new_tps, new_in_port = model_utils._combine_transmission_params( in_transmission_params, out_transmission_params, EnsembleInputPort.neurons) # Check that all the parameters are correct if not passthrough: assert new_tps.pre_slice == in_transmission_params.pre_slice assert new_tps.function is in_transmission_params.function assert np.all( new_tps.transform == np.dot(out_transmission_params.transform, in_transmission_params.transform)[0]) assert new_tps.transform.shape[0] == 1 assert new_in_port is EnsembleInputPort.global_inhibition
def test_node_to_x(self, passthrough, final_port): # Create the ingoing connection parameters if not passthrough: in_transmission_params = NodeTransmissionParameters( slice(10, 15), mock.Mock(), np.random.uniform(size=(10, 5)), ) else: in_transmission_params = PassthroughNodeTransmissionParameters( np.random.uniform(size=(10, 5)), ) # Create the outgoing connection parameters out_transmission_params = PassthroughNodeTransmissionParameters( np.hstack((np.zeros((5, 5)), np.eye(5)))) # Combine the parameter sets new_tps, new_in_port = model_utils._combine_transmission_params( in_transmission_params, out_transmission_params, final_port) # Check that all the parameters are correct if not passthrough: assert new_tps.pre_slice == in_transmission_params.pre_slice assert new_tps.function is in_transmission_params.function assert np.all( new_tps.transform == np.dot(out_transmission_params.transform, in_transmission_params.transform)) assert new_tps.transform.shape == (5, 5) assert new_in_port is final_port
def test_x_to_x_optimize_out(self, from_type, final_port): # Create the ingoing connection parameters in_transmission_params = { "ensemble": EnsembleTransmissionParameters(np.dot([[1.0], [0.0]], np.random.uniform(size=(100, 1)).T), None), "node": NodeTransmissionParameters( slice(10, 20), mock.Mock(), np.array([[1.0], [0.0]]) ), "ptn": PassthroughNodeTransmissionParameters( np.array([[1.0], [0.0]]) ), }[from_type] # Create the outgoing connection parameters out_transmission_params = PassthroughNodeTransmissionParameters( np.array([[0.0, 0.0], [0.0, 1.0]]) ) # Combine the parameter sets new_tps, new_in_port = model_utils._combine_transmission_params( in_transmission_params, out_transmission_params, final_port ) # Check that the connection is optimised out assert new_tps is None assert new_in_port is None
def test_transmits_signal(self): # Create a series of transmission parameters ptn_2_to_8 = np.zeros((32, 6)) ptn_2_to_8[2:8, :] = np.eye(6) tp0 = PassthroughNodeTransmissionParameters(ptn_2_to_8.T) tp0_slice = set(range(2, 8)) ptn_20_to_26 = np.zeros((32, 6)) ptn_20_to_26[20:26, :] = np.eye(6) tp1 = PassthroughNodeTransmissionParameters(ptn_20_to_26.T) tp1_slice = set(range(20, 26)) signal_parameter_slices = [(tp0, tp0_slice), (tp1, tp1_slice), ] # Create a series of vertex slices pfss = [ ParallelFilterSlice(slice(None), slice(0, 16), {}, signal_parameter_slices), ParallelFilterSlice(slice(None), slice(8, 24), {}, signal_parameter_slices), ParallelFilterSlice(slice(None), slice(16, 32), {}, signal_parameter_slices), ] # Check that `transmits_signal` responds correctly assert pfss[0].transmits_signal(None, tp0) assert not pfss[1].transmits_signal(None, tp0) assert not pfss[2].transmits_signal(None, tp0) assert not pfss[0].transmits_signal(None, tp1) assert pfss[1].transmits_signal(None, tp1) assert pfss[2].transmits_signal(None, tp1)
def test_get_transforms_and_keys(): """Test that the complete transform matrix is constructed correctly and that appropriate keys are assigned. """ # Create 2 mock signals and associated connections sig_a_ks_0 = mock.Mock() sig_a_ks_1 = mock.Mock() sig_a_kss = { 0: sig_a_ks_0, 1: sig_a_ks_1, } sig_a_ks = mock.Mock() sig_a_ks.side_effect = lambda index: sig_a_kss[index] sig_a = SignalParameters(keyspace=sig_a_ks) conn_a = PassthroughNodeTransmissionParameters(np.eye(2)) sig_b_ks_0 = mock.Mock() sig_b_kss = { 0: sig_b_ks_0, } sig_b_ks = mock.Mock() sig_b_ks.side_effect = lambda index: sig_b_kss[index] sig_b = SignalParameters(keyspace=sig_b_ks) conn_b = PassthroughNodeTransmissionParameters(np.array([[0.5, 0.5]])) transform_b = conn_b.transform # Create the dictionary type that will be used pars = [(sig_a, conn_a), (sig_b, conn_b)] # Get the transforms and keys transforms, keys, signal_parameter_slices = get_transforms_and_keys(pars) # Check that the transforms and keys are correct assert set(keys) == set([sig_a_ks_0, sig_a_ks_1, sig_b_ks_0]) assert transforms.shape == (len(keys), 2) assert (np.all(transforms[0] == transform_b) or np.all(transforms[2] == transform_b)) # Check that the signal parameter slices are correct for (par, sl) in signal_parameter_slices: if par == conn_a: assert sl == set(range(0, 2)) or sl == set(range(1, 3)) else: assert par == conn_b assert sl == set(range(0, 1)) or sl == set(range(2, 3))
def test_accepts_signal(self): # Create a series of vertex slices pfss = [ ParallelFilterSlice(slice(0, 16), slice(None)), ParallelFilterSlice(slice(8, 24), slice(None)), ParallelFilterSlice(slice(16, 32), slice(None)), ] # Create a series of transmission parameters, of current known types. ens_0_to_8 = np.random.uniform(size=(100, 10)) ens_0_to_8[:, 8:] = 0.0 tp0 = EnsembleTransmissionParameters(ens_0_to_8, 1.0) ptn_20_to_26 = np.zeros((32, 6)) ptn_20_to_26[20:26, :] = np.eye(6) tp1 = PassthroughNodeTransmissionParameters(ptn_20_to_26) # Check that `accepts_signal` responds correctly assert pfss[0].accepts_signal(None, tp0) assert not pfss[1].accepts_signal(None, tp0) assert not pfss[2].accepts_signal(None, tp0) assert not pfss[0].accepts_signal(None, tp1) assert pfss[1].accepts_signal(None, tp1) assert pfss[2].accepts_signal(None, tp1)
def test_get_transforms_and_keys_removes_zeroed_rows(latching): """Check that zeroed rows (those that would always result in zero valued packets) are removed, and the keys miss this value as well. """ transform = np.ones((10, 5)) transform[1, :] = 0.0 transform[4:7, :] = 0.0 transform[:, 1] = 0.0 # Create a signal and keyspace sig = SignalParameters(latching=latching) # Create a mock connection conn = PassthroughNodeTransmissionParameters( Transform(size_in=5, size_out=10, transform=transform)) signals_connections = [(sig, conn)] # Get the transform and keys t, keys, _ = get_transforms_and_keys(signals_connections, slice(0, 5)) if not latching: # Check the transform is correct assert np.all(t == np.vstack((transform[0], transform[2:4], transform[7:]))) # Check the keys were called for correctly assert keys == [(sig, {"index": i}) for i in [0, 2, 3, 7, 8, 9]] else: # Check the transform is correct assert np.all(t == t) # Check the keys were called for correctly assert keys == [(sig, {"index": i}) for i in range(10)]
def test_get_transforms_and_keys(): """Test that the complete transform matrix is constructed correctly and that appropriate keys are assigned. """ # Create 2 mock signals and associated connections sig_a = SignalParameters() conn_a = PassthroughNodeTransmissionParameters( Transform(size_in=2, size_out=2, transform=np.eye(2))) sig_b = SignalParameters() conn_b = PassthroughNodeTransmissionParameters( Transform(size_in=2, size_out=1, transform=np.array([[0.5, 0.5]]))) transform_b = conn_b.transform # Create the dictionary type that will be used pars = [(sig_a, conn_a), (sig_b, conn_b)] # Get the transforms and keys transforms, keys, signal_parameter_slices = \ get_transforms_and_keys(pars, slice(0, 2)) # Check that the transforms and keys are correct assert (keys == [(sig_b, { "index": 0 }), (sig_a, { "index": 0 }), (sig_a, { "index": 1 })] or keys == [(sig_a, { "index": 0 }), (sig_a, { "index": 1 }), (sig_b, { "index": 0 })]) assert transforms.shape == (len(keys), 2) assert (np.all(transforms[0] == transform_b) or np.all(transforms[2] == transform_b)) # Check that the signal parameter slices are correct for (par, sl) in signal_parameter_slices: if par == conn_a: assert sl == set(range(0, 2)) or sl == set(range(1, 3)) else: assert par == conn_b assert sl == set(range(0, 1)) or sl == set(range(2, 3))
def test_get_transforms_and_keys_removes_zeroed_rows(latching): """Check that zeroed rows (those that would always result in zero valued packets) are removed, and the keys miss this value as well. """ ks = mock.Mock() transform = np.ones((10, 5)) transform[1, :] = 0.0 transform[4:7, :] = 0.0 transform[:, 1] = 0.0 # Create a signal and keyspace sig = mock.Mock() sig.keyspace = ks sig.latching = latching sig = SignalParameters(keyspace=ks, latching=latching) # Create a mock connection conn = PassthroughNodeTransmissionParameters(transform) signals_connections = [(sig, conn)] # Get the transform and keys t, keys, _ = get_transforms_and_keys(signals_connections) if not latching: # Check the transform is correct assert np.all(t == np.vstack((transform[0], transform[2:4], transform[7:]))) # Check the keys were called for correctly ks.assert_has_calls([mock.call(index=0), mock.call(index=2), mock.call(index=3), mock.call(index=7), mock.call(index=8), mock.call(index=9)]) else: # Check the transform is correct assert np.all(t == t) # Check the keys were called for correctly ks.assert_has_calls([mock.call(index=0), mock.call(index=1), mock.call(index=2), mock.call(index=3), mock.call(index=4), mock.call(index=5), mock.call(index=6), mock.call(index=7), mock.call(index=8), mock.call(index=9)])
def _combine_transmission_params(in_transmission_parameters, out_transmission_parameters, final_port): """Combine transmission parameters to join two signals into one, e.g., for optimising out a passthrough Node. Returns ------- transmission_parameters New transmission parameters port New receiving port for the connection """ assert isinstance(out_transmission_parameters, PassthroughNodeTransmissionParameters) # Compute the new transform new_transform = np.dot(out_transmission_parameters.transform, in_transmission_parameters.transform) # If the resultant transform is empty then we return None to indicate that # the connection should be dropped. if np.all(new_transform == 0.0): return None, None # If the connection is a global inhibition connection then truncate the # transform and modify the final port to reroute the connection. if (final_port is EnsembleInputPort.neurons and np.all(new_transform[0] == new_transform[1:])): # Truncate the transform new_transform = new_transform[0] new_transform.shape = (1, -1) # Ensure the result is a matrix # Change the final port final_port = EnsembleInputPort.global_inhibition # Construct the new transmission parameters if isinstance(in_transmission_parameters, EnsembleTransmissionParameters): transmission_params = EnsembleTransmissionParameters( in_transmission_parameters.untransformed_decoders, new_transform) elif isinstance(in_transmission_parameters, NodeTransmissionParameters): transmission_params = NodeTransmissionParameters( in_transmission_parameters.pre_slice, in_transmission_parameters.function, new_transform) elif isinstance(in_transmission_parameters, PassthroughNodeTransmissionParameters): transmission_params = PassthroughNodeTransmissionParameters( new_transform) else: raise NotImplementedError return transmission_params, final_port
def test_unknown_to_x(self): # Create the ingoing connection parameters in_transmission_params = mock.Mock() in_transmission_params.transform = 1.0 # Create the outgoing connection parameters out_transmission_params = PassthroughNodeTransmissionParameters( np.array([[0.0, 0.0], [0.0, 1.0]])) # Combine the parameter sets with pytest.raises(NotImplementedError): model_utils._combine_transmission_params(in_transmission_params, out_transmission_params, None)
def test_ens_to_gi(self): # Create the ingoing connection parameters in_transmission_params = EnsembleTransmissionParameters( np.random.uniform(size=(100, 7)), 1.0) # Create the outgoing connection parameters out_transmission_params = PassthroughNodeTransmissionParameters( np.ones((200, 7))) # Combine the parameter sets new_tps, new_in_port = model_utils._combine_transmission_params( in_transmission_params, out_transmission_params, EnsembleInputPort.neurons) # Check that all the parameters are correct assert np.all(new_tps.transform == 1.0) assert new_tps.transform.shape == (1, 7) assert new_tps.decoders.shape == (1, 100) assert new_in_port is EnsembleInputPort.global_inhibition
def test_ens_to_x(self, final_port): # Create the ingoing connection parameters in_transmission_params = EnsembleTransmissionParameters( np.random.uniform(size=(100, 10)), 1.0) # Create the outgoing connection parameters out_transmission_params = PassthroughNodeTransmissionParameters( np.hstack([np.eye(5), np.zeros((5, 5))])) # Combine the parameter sets new_tps, new_in_port = model_utils._combine_transmission_params( in_transmission_params, out_transmission_params, final_port) # Check that all the parameters are correct assert np.all(new_tps.untransformed_decoders == in_transmission_params.untransformed_decoders) assert np.all(new_tps.transform == out_transmission_params.transform) assert new_tps.decoders.shape == (5, 100) assert new_in_port is final_port
def test_make_vertices_one_group_many_cores_1_chip(self): """Test that many vertices are returned if the matrix has many rows and that there is an appropriate constraint forcing the co-location of the vertices. """ # Create a small filter operator filter_op = Filter(3) # Create a model and add some connections which will cause packets to # be transmitted from the filter operator. m = Model() signal_parameters = SignalParameters(False, 3, m.keyspaces["nengo"]) signal_parameters.keyspace.length = 32 transmission_parameters = PassthroughNodeTransmissionParameters( Transform(size_in=3, size_out=96, transform=np.ones((96, 3)))) m.connection_map.add_connection(filter_op, OutputPort.standard, signal_parameters, transmission_parameters, None, None, None) # Make vertices using the model netlistspec = filter_op.make_vertices(m, 10000) assert len(netlistspec.vertices) == 2 # Two vertices for vx in netlistspec.vertices: assert "filter" in vx.application assert vx.resources[Cores] == 1 assert vx.regions[Regions.system].column_slice == slice(0, 3) keys_region = vx.regions[Regions.keys] assert keys_region.signals_and_arguments == [ (signal_parameters, dict(index=i)) for i in range(32 * 3) ] assert len(keys_region.fields) == 1 assert keys_region.partitioned is True assert vx.regions[Regions.transform].matrix.shape == (32 * 3, 3)
def test_remove_operator_from_connection_map(): """Test that operators are correctly removed from connection maps. We test the following model: O1 ------> O3 -----> O5 /-----/ \ / \----> O6 O2 --> O4 Removing `O3' should result in: O1 --> O6 / /-> 05 O2 --> O4 """ # Construct the operators operators = [mock.Mock(name="O{}".format(i + 1)) for i in range(6)] # Create a connection map cm = model.ConnectionMap() # Add the connection O1 to O3 sps = model.SignalParameters(True, 6) tps = PassthroughNodeTransmissionParameters( np.vstack([np.eye(3), np.zeros((3, 3))])) rps = model.ReceptionParameters(None, 6) cm.add_connection(source_object=operators[0], source_port=None, signal_parameters=sps, transmission_parameters=tps, sink_object=operators[2], sink_port=None, reception_parameters=rps) # Add the connection O2 to O3 sps = model.SignalParameters(False, 6) tps = PassthroughNodeTransmissionParameters( np.vstack([np.zeros((3, 3)), np.eye(3)])) rps = model.ReceptionParameters(None, 6) cm.add_connection(source_object=operators[1], source_port=None, signal_parameters=sps, transmission_parameters=tps, sink_object=operators[2], sink_port=None, reception_parameters=rps) # Add the connection O2 to O4 (with a custom keyspace) sps = model.SignalParameters(False, 6, mock.Mock("Keyspace 1")) tps = PassthroughNodeTransmissionParameters( np.vstack([np.eye(3), np.eye(3)])) rps = model.ReceptionParameters(None, 6) cm.add_connection(source_object=operators[1], source_port=None, signal_parameters=sps, transmission_parameters=tps, sink_object=operators[3], sink_port=None, reception_parameters=rps) # Add the connection O3 to O5 sps = model.SignalParameters(False, 3) tps = PassthroughNodeTransmissionParameters( np.hstack((np.zeros((3, 3)), np.eye(3)))) rps = model.ReceptionParameters(None, 3) cm.add_connection(source_object=operators[2], source_port=None, signal_parameters=sps, transmission_parameters=tps, sink_object=operators[4], sink_port=None, reception_parameters=rps) # Add the connection O3 to O6 sps = model.SignalParameters(False, 3) tps = PassthroughNodeTransmissionParameters( np.hstack([np.eye(3), np.eye(3)]) * 2) rps = model.ReceptionParameters(None, 3) cm.add_connection(source_object=operators[2], source_port=None, signal_parameters=sps, transmission_parameters=tps, sink_object=operators[5], sink_port=None, reception_parameters=rps) # Remove O3 from the connection map model_utils.remove_operator_from_connection_map(cm, operators[2]) # Check that the received and transmitted signals are as expected # FROM O1 from_o1 = cm._connections[operators[0]] assert len(from_o1) == 1 assert len(from_o1[None]) == 1 ((signal_parameters, transmission_parameters), sinks) = from_o1[None][0] assert signal_parameters == model.SignalParameters(True, 3, None) assert transmission_parameters.transform.shape == (3, 3) assert np.all(transmission_parameters.transform == np.eye(3) * 2) assert sinks == [(operators[5], None, model.ReceptionParameters(None, 3))] # FROM O2 from_o2 = cm._connections[operators[1]] assert len(from_o2) == 1 assert len(from_o2[None]) == 3 for ((signal_parameters, transmission_parameters), sinks) in from_o2[None]: if transmission_parameters.transform.shape == (3, 3): assert (signal_parameters == model.SignalParameters( False, 3, None)) if np.any(transmission_parameters.transform == 2.0): # TO O6 assert np.all(transmission_parameters.transform == np.eye(3) * 2) assert sinks == [(operators[5], None, model.ReceptionParameters(None, 3))] else: # TO O5 assert np.all(transmission_parameters.transform == np.eye(3)) assert sinks == [(operators[4], None, model.ReceptionParameters(None, 3))] else: # TO O4 assert transmission_parameters.transform.shape == (6, 3) assert np.all( transmission_parameters.transform == np.vstack([np.eye(3)] * 2)) assert sinks == [(operators[3], None, model.ReceptionParameters(None, 6))] # We now add a connection from O4 to O6 with a custom keyspace. Removing # O4 will fail because keyspaces can't be merged. signal_params = model.SignalParameters(False, 1, mock.Mock("Keyspace 2")) transmission_params = PassthroughNodeTransmissionParameters(1.0) reception_params = model.ReceptionParameters(None, 1) cm.add_connection(source_object=operators[3], source_port=None, signal_parameters=signal_params, transmission_parameters=transmission_params, sink_object=operators[5], sink_port=None, reception_parameters=reception_params) with pytest.raises(NotImplementedError) as err: model_utils.remove_operator_from_connection_map(cm, operators[3]) assert "keyspace" in str(err.value).lower()
def test_remove_operator_from_connection_map_unforced(): """Check that a calculation is made to determine whether it is better to keep or remove an operator depending on the density of the outgoing connections. In the example: /- G[0] A[0] --\ /-- D[0] --\ /-- G[1] A[1] --- B --- C --- D[1] --- E --- F --- G[2] A[n] --/ \-- D[n] --/ \-- G[3] \- G[n] B, C and F should be removed but E should be retained: /- G[0] A[0] --- D[0] --\ /-- G[1] A[1] --- D[1] --- E --- G[2] A[n] --- D[n] --/ \-- G[3] \- G[n] """ # Create the operators D = 512 SD = 16 op_A = [mock.Mock(name="A{}".format(i)) for i in range(D // SD)] op_B = mock.Mock(name="B") op_C = mock.Mock(name="C") op_D = [mock.Mock(name="D{}".format(i)) for i in range(D // SD)] op_E = mock.Mock(name="E") op_F = mock.Mock(name="F") op_G = [mock.Mock(name="G{}".format(i)) for i in range(D)] # Create a connection map cm = model.ConnectionMap() # Create the fan-in connections for sources, sink in ((op_A, op_B), (op_D, op_E)): # Get the signal and reception parameters sps = model.SignalParameters(True, D) rps = model.ReceptionParameters(None, D) for i, source in enumerate(sources): # Get the transform transform = np.zeros((D, SD)) transform[i * SD:(i + 1) * SD, :] = np.eye(SD) # Get the parameters tps = EnsembleTransmissionParameters(np.ones((1, SD)), transform) cm.add_connection(source_object=source, source_port=None, signal_parameters=sps, transmission_parameters=tps, sink_object=sink, sink_port=None, reception_parameters=rps) # Create the fan-out connection C to D[...] # Get the signal and reception parameters sps = model.SignalParameters(True, SD) rps = model.ReceptionParameters(None, SD) for i, sink in enumerate(op_D): # Get the transform transform = np.zeros((SD, D)) transform[:, i * SD:(i + 1) * SD] = np.eye(SD) # Get the parameters tps = PassthroughNodeTransmissionParameters(transform) cm.add_connection(source_object=op_C, source_port=None, signal_parameters=sps, transmission_parameters=tps, sink_object=sink, sink_port=None, reception_parameters=rps) # Create the connection B to C sps = model.SignalParameters(True, D) rps = model.ReceptionParameters(None, D) tps = PassthroughNodeTransmissionParameters(np.eye(D)) cm.add_connection(source_object=op_B, source_port=None, signal_parameters=sps, transmission_parameters=tps, sink_object=op_C, sink_port=None, reception_parameters=rps) # Create the connection E to F transform = np.zeros((D, D)) for i in range(D): for j in range(D): transform[i, j] = i + j sps = model.SignalParameters(True, D) rps = model.ReceptionParameters(None, D) tps = PassthroughNodeTransmissionParameters(transform) cm.add_connection(source_object=op_E, source_port=None, signal_parameters=sps, transmission_parameters=tps, sink_object=op_F, sink_port=None, reception_parameters=rps) # Create the fan-out connections from F sps = model.SignalParameters(True, SD) rps = model.ReceptionParameters(None, SD) for i, sink in enumerate(op_G): # Get the transform transform = np.zeros((1, D)) transform[:, i] = 1.0 # Get the parameters tps = PassthroughNodeTransmissionParameters(transform) cm.add_connection(source_object=op_F, source_port=None, signal_parameters=sps, transmission_parameters=tps, sink_object=sink, sink_port=None, reception_parameters=rps) # Remove all of the passthrough Nodes, only E should be retained assert model_utils.remove_operator_from_connection_map(cm, op_B, force=False) assert model_utils.remove_operator_from_connection_map(cm, op_C, force=False) assert not model_utils.remove_operator_from_connection_map( cm, op_E, force=False) assert model_utils.remove_operator_from_connection_map(cm, op_F, force=False) # Check that each A has only one outgoing signal and that it terminates at # the paired D. Additionally check that each D has only one outgoing # signal and that it terminates at E. for a, d in zip(op_A, op_D): # Connections from A[n] from_a = cm._connections[a] assert len(from_a) == 1 assert len(from_a[None]) == 1 ((signal_parameters, transmission_parameters), sinks) = from_a[None][0] assert signal_parameters == model.SignalParameters(True, SD, None) assert transmission_parameters.transform.shape == (SD, SD) assert np.all(transmission_parameters.transform == np.eye(SD)) assert sinks == [(d, None, model.ReceptionParameters(None, SD))] # Connection(s) from D[n] from_d = cm._connections[d] assert len(from_d) == 1 assert len(from_d[None]) == 1 ((signal_parameters, transmission_parameters), sinks) = from_d[None][0] assert signal_parameters == model.SignalParameters(True, D, None) assert transmission_parameters.transform.shape == (D, SD) # Check that there are many connections from E from_e = cm._connections[op_E] assert len(from_e) == 1 print(from_e[None][0].parameters[1].transform) assert len(from_e[None]) == D