def test_combine_learning_rules_fails(self): # Cannot combine two learning rules rp1 = model.ReceptionParameters(None, 1, object()) rp2 = model.ReceptionParameters(None, 1, object()) with pytest.raises(NotImplementedError): rp1.concat(rp2)
def test_combine_unknown_filters(self): f1 = nengo.synapses.LinearFilter([1, 2], [1, 2, 3]) rp1 = model.ReceptionParameters(f1, 1, None) rp2 = model.ReceptionParameters(object(), 1, None) # Combining filters should fail with pytest.raises(NotImplementedError): rp1.concat(rp2)
def test_combine_filters_none_none(self): # Create two reception parameters with None filters rp1 = model.ReceptionParameters(None, 1, None) rp2 = model.ReceptionParameters(None, 5, None) # Check that combined they have a None filter and that the last width # is used. rp_expected = model.ReceptionParameters(None, rp2.width, None) assert rp1.concat(rp2) == rp_expected
def test_combine_filters_one_is_none(self, first): # Create two reception parameters, of which one has a None filter f = object() rp1 = model.ReceptionParameters(f if first else None, 1, None) rp2 = model.ReceptionParameters(None if first else f, 5, None) # Check that combined they have a None filter and that the last width # is used. rp_expected = model.ReceptionParameters(f, rp2.width, None) assert rp1.concat(rp2) == rp_expected
def test_combine_learning_rules(self, first): # Create two reception parameters, of which one has a None filter lr = object() rp1 = model.ReceptionParameters(None, 1, lr if first else None) rp2 = model.ReceptionParameters(None, 1, None if first else lr) # Check that combined they have a None filter and that the last width # is used. rp_expected = model.ReceptionParameters(None, rp2.width, lr) assert rp1.concat(rp2) == rp_expected
def test_combine_lti_filters(self): f1 = nengo.synapses.LinearFilter([1, 2], [1, 2, 3]) f2 = nengo.synapses.LinearFilter([2], [1, 2]) rp1 = model.ReceptionParameters(f1, 1, None) rp2 = model.ReceptionParameters(f2, 1, None) # Combine and extract the filter f3 = rp1.concat(rp2).filter assert isinstance(f3, nengo.synapses.LinearFilter) assert np.array_equal(f3.num, [2, 4]) assert np.array_equal(f3.den, [1, 4, 7, 6])
def test_combine_unknown_filter(self): # Create the ingoing reception parameters reception_params_a = model.ReceptionParameters(nengo.Lowpass(0.05), 1) # Create the outgoing reception parameters reception_params_b = model.ReceptionParameters(mock.Mock(), 1) # Combine the parameter each way round for a, b in ((reception_params_a, reception_params_b), (reception_params_a, reception_params_b)): with pytest.raises(NotImplementedError): new_rps = model_utils._combine_reception_params(a, b)
def test_insert_interposers_simple(self): """Test that interposers are inserted correctly.""" cm = model.ConnectionMap() # Add a connection from a node to a passthrough node to the model nodes = [object(), object()] ptn = model.PassthroughNode() for node in nodes: cm.add_connection( node, OutputPort.standard, model.SignalParameters(weight=1), NodeTransmissionParameters(Transform(1, 1, 1)), ptn, InputPort.standard, model.ReceptionParameters(None, 1, None) ) # Add a connection from the passthrough node to another node sink = object() sink_port = object() cm.add_connection( ptn, OutputPort.standard, model.SignalParameters(weight=70), PassthroughNodeTransmissionParameters( Transform(1, 70, np.ones((70, 1))) ), sink, sink_port, model.ReceptionParameters(None, 70, None) ) # Insert interposers, getting a list of interposers and a new # connection map. interposers, new_cm = cm.insert_interposers() assert len(interposers) == 1 # Should insert 1 interposer interposer = interposers[0] # Check that each of the nodes connects to the interposer for node in nodes: from_node = new_cm._connections[node][OutputPort.standard] assert len(from_node) == 1 for sinks in itervalues(from_node): assert len(sinks) == 1 for s in sinks: assert s.sink_object is interposer assert s.port is InputPort.standard # Check that the interposer connects to the sink from_interposer = new_cm._connections[interposer][OutputPort.standard] assert len(from_interposer) == 1 for sinks in itervalues(from_interposer): assert len(sinks) == 1 for s in sinks: assert s.sink_object is sink assert s.port is sink_port
def test_combine_none_and_lowpass_filter(self): # Create the ingoing reception parameters reception_params_a = model.ReceptionParameters(nengo.Lowpass(0.05), 1) # Create the outgoing reception parameters reception_params_b = model.ReceptionParameters(None, 3) # Combine the parameter each way round for a, b in ((reception_params_a, reception_params_b), (reception_params_a, reception_params_b)): new_rps = model_utils._combine_reception_params(a, b) # Check filter type assert new_rps.filter == reception_params_a.filter # Check width is the width of the receiving item assert new_rps.width == b.width
def test_combine_linear_and_linear_filter(self): # Create the ingoing reception parameters reception_params_a = model.ReceptionParameters(nengo.Lowpass(0.05), 1) # Create the outgoing reception parameters reception_params_b = model.ReceptionParameters(nengo.Lowpass(0.01), 5) # Combine the parameter each way round for a, b in ((reception_params_a, reception_params_b), (reception_params_a, reception_params_b)): new_rps = model_utils._combine_reception_params(a, b) # Check filter type synapse = new_rps.filter assert synapse.num == [1] assert np.all(synapse.den == [0.05 * 0.01, 0.05 + 0.01, 1]) # Check width is the width of the receiving item assert new_rps.width == b.width
def test_insert_interposers_removes_passthrough_node(self): """Test that passthrough nodes are removed while inserting interposers. """ cm = model.ConnectionMap() # Add a connection from a node to a passthrough node to the model node = object() ptn = model.PassthroughNode() cm.add_connection( node, OutputPort.standard, model.SignalParameters(weight=1), NodeTransmissionParameters(Transform(1, 1, 1)), ptn, InputPort.standard, model.ReceptionParameters(None, 1, None) ) # Add a connection from the passthrough node to another node sink = object() cm.add_connection( ptn, OutputPort.standard, model.SignalParameters(weight=1), PassthroughNodeTransmissionParameters(Transform(1, 1, 1)), sink, InputPort.standard, model.ReceptionParameters(None, 1, None) ) # Insert interposers, getting a list of interposers (empty) and a new # connection map. interposers, new_cm = cm.insert_interposers() assert len(interposers) == 0 # No interposers expected # Check that there is now just one connection from the node to the sink from_node = new_cm._connections[node] assert list(from_node) == [OutputPort.standard] for (signal_pars, transmission_pars), sinks in \ iteritems(from_node[OutputPort.standard]): # Check the transmission parameters assert transmission_pars == NodeTransmissionParameters( Transform(1, 1, 1) ) # Check that the sink is correct assert len(sinks) == 1 for s in sinks: assert s.sink_object is sink
def test_insert_interposers_ignore_sinkless(self): """Test that interposers are inserted correctly, ignoring those that don't connect to anything. """ cm = model.ConnectionMap() # Add a connection from a node to a passthrough node to the model nodes = [object(), object()] ptn = model.PassthroughNode() for node in nodes: cm.add_connection( node, OutputPort.standard, model.SignalParameters(weight=1), NodeTransmissionParameters(Transform(1, 1, 1)), ptn, InputPort.standard, model.ReceptionParameters(None, 1, None) ) # Connect the passthrough node to another passthrough node ptn2 = model.PassthroughNode() cm.add_connection(ptn, OutputPort.standard, model.SignalParameters(), PassthroughNodeTransmissionParameters( Transform(1, 70, transform=np.ones((70, 1)))), ptn2, InputPort.standard, model.ReceptionParameters(None, 70, None)) # Connect the passthrough node to another passthrough node ptn3 = model.PassthroughNode() cm.add_connection(ptn2, OutputPort.standard, model.SignalParameters(), PassthroughNodeTransmissionParameters( Transform(70, 70, 1)), ptn3, InputPort.standard, model.ReceptionParameters(None, 70, None)) # Insert interposers, getting a list of interposers and a new # connection map. interposers, new_cm = cm.insert_interposers() assert len(interposers) == 0 # No interposers # No signals at all assert len(list(new_cm.get_signals())) == 0
def test_stack_interposers_no_interposer(self): # Create a network consisting of two ensembles targeting a sink MockEnsemble = collections.namedtuple("MockEnsemble", "size_in") source_1 = EnsembleLIF(MockEnsemble(1)) source_2 = EnsembleLIF(MockEnsemble(1)) sink = object() # Create a connection map and add the connections cm = model.ConnectionMap() cm.add_connection( source_1, OutputPort.standard, model.SignalParameters(weight=512), EnsembleTransmissionParameters( decoders=np.ones((1, 100)), transform=Transform( size_in=1, size_out=1, transform=1.0 ) ), sink, InputPort.standard, model.ReceptionParameters(None, 1, None) ) cm.add_connection( # Make sure the transform is distinct! source_2, OutputPort.standard, model.SignalParameters(weight=512), EnsembleTransmissionParameters( decoders=np.ones((1, 100)), transform=Transform( size_in=1, size_out=1, transform=0.5 ) ), sink, InputPort.standard, model.ReceptionParameters(None, 512, None) ) # Insert and stack the interposers interposers, new_cm = cm.insert_and_stack_interposers() assert len(interposers) == 0 # The connection map should be unchanged assert new_cm._connections == cm._connections
def test_stack_interposers_no_stack(self): # Create a network consisting of two ensembles targeting a sink, insert # interposers into this network and then stack the interposers to # reduce later connectivity. MockEnsemble = collections.namedtuple("MockEnsemble", "size_in") source_1 = EnsembleLIF(MockEnsemble(1)) source_2 = EnsembleLIF(MockEnsemble(1)) sink = object() # Create a connection map and add the connections cm = model.ConnectionMap() cm.add_connection( source_1, OutputPort.standard, model.SignalParameters(weight=512), EnsembleTransmissionParameters( decoders=np.ones((1, 100)), transform=Transform( size_in=1, size_out=512, transform=np.ones((512, 1)) ) ), sink, InputPort.standard, model.ReceptionParameters(None, 512, None) ) cm.add_connection( # Transform and the filter are distinct! source_2, OutputPort.standard, model.SignalParameters(weight=512), EnsembleTransmissionParameters( decoders=np.ones((1, 100)), transform=Transform( size_in=1, size_out=512, transform=0.5*np.ones((512, 1)) ) ), sink, InputPort.standard, model.ReceptionParameters(object(), 512, None) ) # Insert and stack the interposers interposers, new_cm = cm.insert_and_stack_interposers() new_cm = new_cm._connections assert len(interposers) == 2
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
def test_stack_interposers(self): """Test that interposers with equivalent output sets are stacked correctly and that this modifies both their incoming and output connections. """ # Create a network consisting of two ensembles targeting a sink, insert # interposers into this network and then stack the interposers to # reduce later connectivity. MockEnsemble = collections.namedtuple("MockEnsemble", "size_in") source_1 = EnsembleLIF(MockEnsemble(1)) source_2 = EnsembleLIF(MockEnsemble(1)) sink = object() # Create a connection map and add the connections cm = model.ConnectionMap() cm.add_connection( source_1, OutputPort.standard, model.SignalParameters(weight=512), EnsembleTransmissionParameters( decoders=np.ones((1, 100)), transform=Transform( size_in=1, size_out=512, transform=np.ones((512, 1)) ) ), sink, InputPort.standard, model.ReceptionParameters(None, 512, None) ) cm.add_connection( # Make sure the transform is distinct! source_2, OutputPort.standard, model.SignalParameters(weight=512), EnsembleTransmissionParameters( decoders=np.ones((1, 100)), transform=Transform( size_in=1, size_out=512, transform=0.5*np.ones((512, 1)) ) ), sink, InputPort.standard, model.ReceptionParameters(None, 512, None) ) # Insert and stack the interposers interposers, new_cm = cm.insert_and_stack_interposers() new_cm = new_cm._connections assert len(interposers) == 1 # Should only be one interposer interposer = interposers[0] assert interposer.size_in == 2 # The connections from the sources and from the interposer can be one # of two forms, attempt to determine which and then check that the rest # of the connections are correct. assert set(new_cm.keys()) == {source_1, source_2, interposer} assert len(new_cm[source_1][OutputPort.standard]) == 1 assert len(new_cm[source_2][OutputPort.standard]) == 1 assert len(new_cm[interposer][OutputPort.standard]) == 1 for params, sinks in iteritems(new_cm[source_1][OutputPort.standard]): sps, tps = params # Extract signal and transmission parameters assert isinstance(sps, model.SignalParameters) assert sps.weight == 1 # Weight should not be modified assert isinstance(tps, EnsembleTransmissionParameters) assert tps.size_in == 1 assert tps.size_out == 2 if np.array_equal(tps.slice_out, np.array([0])): # Source 1 points to the first dimension of the interposer. # Assert that Source 2 points to the other dimension of the # interposer. expected_source_2_slice = np.array([1]) expected_transform = np.hstack( (np.ones((512, 1)), 0.5*np.ones((512, 1))) ) elif np.array_equal(tps.slice_out, np.array([1])): # Source 2 points to the second dimension of the interposer. # Assert that Source 2 points to the other dimension of the # interposer. expected_source_2_slice = np.array([0]) expected_transform = np.hstack( (0.5*np.ones((512, 1)), np.ones((512, 1))) ) else: assert False, "Unexpected dimension in slice" # Check that the sinks are as expected assert len(sinks) == 1 for new_sink in sinks: assert new_sink.sink_object is interposer for params, sinks in iteritems(new_cm[source_2][OutputPort.standard]): sps, tps = params # Extract signal and transmission parameters assert isinstance(sps, model.SignalParameters) assert sps.weight == 1 assert isinstance(tps, EnsembleTransmissionParameters) assert tps.size_in == 1 assert tps.size_out == 2 assert np.array_equal(tps.slice_out, expected_source_2_slice) assert len(sinks) == 1 for new_sink in sinks: assert new_sink.sink_object is interposer # Check the connection from the interposer for params, sinks in iteritems( new_cm[interposer][OutputPort.standard]): sps, tps = params # Extract signal and transmission parameters assert isinstance(sps, model.SignalParameters) assert sps.weight == 512 # Not changed assert isinstance(tps, PassthroughNodeTransmissionParameters) assert tps.size_in == 2 assert tps.size_out == 512 assert np.array_equal( tps.full_transform(False, False), expected_transform ) assert len(sinks) == 1 for new_sink in sinks: assert new_sink.sink_object is sink
def test_insert_interposer_after_ensemble(self): """Test that an interposer can be inserted after a connection from an ensemble. """ # Create an ensemble and connect it to another ensemble with a # significantly larger size in. mock_ensemble_1 = mock.Mock(spec_set=["size_in"]) mock_ensemble_1.size_in = 1 e1 = EnsembleLIF(mock_ensemble_1) mock_ensemble_2 = mock.Mock(spec_set=["size_in"]) mock_ensemble_2.size_in = 512 e2 = EnsembleLIF(mock_ensemble_2) # Create a new connection map and add a connection between the # ensembles. transform = Transform(2, 512, np.ones((512, 2))) tps = EnsembleTransmissionParameters(np.ones((2, 100)), transform) sps = model.SignalParameters(weight=512) filt = object() cm = model.ConnectionMap() cm.add_connection( e1, OutputPort.standard, sps, tps, e2, InputPort.standard, model.ReceptionParameters(filt, 512, None) ) # Insert interposers interposers, new_cm = cm.insert_interposers() # Check that an interposer was inserted assert len(interposers) == 1 interposer = interposers[0] # Grab the interposer assert interposer.size_in == 2 # Check that the connection from e1 to the interposer is as expected assert len(new_cm._connections[e1][OutputPort.standard]) == 1 for conn, sinks in iteritems( new_cm._connections[e1][OutputPort.standard]): new_sps, new_tps = conn # Get signal and transmission pars assert isinstance(tps, EnsembleTransmissionParameters) assert np.array_equal(tps.decoders, new_tps.decoders) assert new_tps.size_out == 2 assert new_sps.weight == 2 assert np.array_equal( new_tps.full_transform(False, False), np.eye(2) ) assert len(sinks) == 1 for sink in sinks: assert sink.sink_object is interposer assert sink.reception_parameters == model.ReceptionParameters( None, 2, None ) # Check that the connection from the interposer to e2 is as expected assert len(new_cm._connections[interposer][OutputPort.standard]) == 1 for conn, sinks in iteritems( new_cm._connections[interposer][OutputPort.standard]): new_sps, new_tps = conn # Check that the transform is as expected assert isinstance(new_tps, PassthroughNodeTransmissionParameters) assert np.array_equal(tps.full_transform(False, False), new_tps.full_transform(False, False)) assert np.array_equal(tps.slice_out, new_tps.slice_out) # Check the sinks are correct assert len(sinks) == 1 for sink in sinks: assert sink.sink_object is e2 assert sink.reception_parameters == model.ReceptionParameters( filt, 512, None )
def test_insert_interposers_earliest_interposer_only(self): """Test that only the first interposer in a network of possible interposers is inserted. """ cm = model.ConnectionMap() node = object() ptn1 = model.PassthroughNode() ptn2 = model.PassthroughNode() sink = object() # Add connections cm.add_connection( node, OutputPort.standard, model.SignalParameters(), NodeTransmissionParameters(Transform(16, 512, 1, slice_out=slice(16, 32))), ptn1, InputPort.standard, model.ReceptionParameters(None, 512, None) ) cm.add_connection( ptn1, OutputPort.standard, model.SignalParameters(), PassthroughNodeTransmissionParameters( Transform(512, 512, np.ones((512, 512))) ), ptn2, InputPort.standard, model.ReceptionParameters(None, 512, None) ) cm.add_connection( ptn2, OutputPort.standard, model.SignalParameters(), PassthroughNodeTransmissionParameters( Transform(512, 1024, np.ones((1024, 512))) ), sink, InputPort.standard, model.ReceptionParameters(None, 1024, None) ) # Insert interposers, only one should be included interposers, new_cm = cm.insert_interposers() assert len(interposers) == 1 interposer = interposers[0] # Check that the node connects to the interposer and that the # interposer was the first passthrough node. from_node = new_cm._connections[node][OutputPort.standard] for (_, transmission_pars), sinks in iteritems(from_node): assert transmission_pars == NodeTransmissionParameters( Transform(16, 512, 1, slice_out=slice(16, 32)) ) assert len(sinks) == 1 for s in sinks: assert s.sink_object is interposer # Check that the interposer connects to the sink from_interposer = new_cm._connections[interposer][OutputPort.standard] for (_, transmission_pars), sinks in iteritems(from_interposer): assert transmission_pars.size_in == 512 assert transmission_pars.size_out == 1024 assert len(sinks) == 1 for s in sinks: assert s.sink_object is sink
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()