def test_concat(self): """Test that passthrough node connection parameters can be combined with later passthrough node connection parameters to build a new set of parameters. """ # Check that these parameters are combined correctly a = PassthroughNodeTransmissionParameters( Transform(size_in=5, size_out=3, transform=2.0, slice_in=slice(2), slice_out=slice(2)) ) b = PassthroughNodeTransmissionParameters( Transform(size_in=3, size_out=3, slice_in=slice(2), slice_out=slice(1, 3), transform=[-1.0, 1.5]) ) # Combine the connections c = a.concat(b) # Check the new parameters assert c.size_in == a.size_in assert c.size_out == b.size_out assert np.array_equal(c.slice_in, a.slice_in) assert np.array_equal(c.slice_out, b.slice_out) assert np.array_equal( c.full_transform(False, False), np.dot(b.full_transform(False, False), a.full_transform(False, False)) )
def test_concat(self): """Test concatenating Node transmission parameters with passthrough node transmission parameters. """ a = NodeTransmissionParameters(Transform(size_in=2, size_out=5, transform=[[2.0, 0.0], [0.0, 2.0]], slice_out=(1, 3)), pre_slice=slice(0, 3), function=object()) b = PassthroughNodeTransmissionParameters( Transform(size_in=5, size_out=2, transform=[0.5, 0.25], slice_in=(0, 3))) # Combine c = a.concat(b) assert c.size_in == a.size_in assert c.pre_slice == a.pre_slice assert c.function is a.function assert np.array_equal( c.full_transform(False, False), np.dot(b.full_transform(False, False), a.full_transform(False, False)))
def test_concat(self): """Test concatenating Node transmission parameters with passthrough node transmission parameters. """ a = NodeTransmissionParameters( Transform(size_in=2, size_out=5, transform=[[2.0, 0.0], [0.0, 2.0]], slice_out=(1, 3)), pre_slice=slice(0, 3), function=object() ) b = PassthroughNodeTransmissionParameters( Transform(size_in=5, size_out=2, transform=[0.5, 0.25], slice_in=(0, 3)) ) # Combine c = a.concat(b) assert c.size_in == a.size_in assert c.pre_slice == a.pre_slice assert c.function is a.function assert np.array_equal( c.full_transform(False, False), np.dot(b.full_transform(False, False), a.full_transform(False, False)) )
def test_concat_no_connection(self): """Test that None is returned if concatenating connections results in an empty transform. """ a = PassthroughNodeTransmissionParameters( Transform(size_in=4, size_out=16, slice_out=slice(4), transform=1.0) ) b = PassthroughNodeTransmissionParameters( Transform(size_in=16, size_out=4, slice_in=slice(4, 8), transform=1.0) ) # Combine the connections assert a.concat(b) is None
def _copy_connections_from_interposer(self, node, port, conn, interposer, target_map): """Copy the pattern of connectivity from a node, replacing a specific connection with an interposer. """ # Get the sinks of this connection sinks = self._connections[node][port][conn] # Extract parameters signal_pars, transmission_pars = conn # If the original object was an ensemble then modify the transmission # parameters to remove the decoders. if isinstance(node, EnsembleLIF): transmission_pars = PassthroughNodeTransmissionParameters( transmission_pars._transform) # Copy the connections to the sinks, note that we specify an empty # interposers dictionary so that no connections to further interposers # are inserted, likewise we specify no additional reception parameters. # The connectivity from the sink will be recursed if it is a # passthrough node. for sink in sinks: self._copy_connection(target_map, dict(), interposer, OutputPort.standard, signal_pars, transmission_pars, sink.sink_object, sink.port, sink.reception_parameters)
def test_concat_no_connection(self): """Test that None is returned if concatenating connections results in an empty transform. """ a = PassthroughNodeTransmissionParameters( Transform(size_in=4, size_out=16, slice_out=slice(4), transform=1.0)) b = PassthroughNodeTransmissionParameters( Transform(size_in=16, size_out=4, slice_in=slice(4, 8), transform=1.0)) # Combine the connections assert a.concat(b) is None
def test_concat(self): """Test that passthrough node connection parameters can be combined with later passthrough node connection parameters to build a new set of parameters. """ # Check that these parameters are combined correctly a = PassthroughNodeTransmissionParameters( Transform(size_in=5, size_out=3, transform=2.0, slice_in=slice(2), slice_out=slice(2))) b = PassthroughNodeTransmissionParameters( Transform(size_in=3, size_out=3, slice_in=slice(2), slice_out=slice(1, 3), transform=[-1.0, 1.5])) # Combine the connections c = a.concat(b) # Check the new parameters assert c.size_in == a.size_in assert c.size_out == b.size_out assert np.array_equal(c.slice_in, a.slice_in) assert np.array_equal(c.slice_out, b.slice_out) assert np.array_equal( c.full_transform(False, False), np.dot(b.full_transform(False, False), a.full_transform(False, False)))
def test_supports_global_inhibition(self): tp1 = PassthroughNodeTransmissionParameters( Transform(size_in=10, size_out=100, transform=np.ones((100, 10)))) assert tp1.supports_global_inhibition tp2 = tp1.as_global_inhibition_connection assert tp2.size_in == tp1.size_in assert tp2.size_out == 1 assert tp2.slice_out.size == 1 assert np.array_equal(tp2.full_transform(), np.ones((1, 10)))
def test_projects_to(self, method): """Test that the parameters correctly report if they transmit any values to the dimensions listed. """ if method == "scalar": transform = 1.0 elif method == "vector": transform = [1.0, 1.0, 1.0, 1.0] else: transform = np.eye(4) tp = PassthroughNodeTransmissionParameters( Transform(size_in=4, size_out=16, slice_out=slice(0, 4), transform=transform) ) assert tp.projects_to(slice(1)) assert tp.projects_to(slice(5)) assert not tp.projects_to(slice(4, 8)) assert tp.projects_to((0, 1, 2, 4))
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_projects_to(self, method): """Test that the parameters correctly report if they transmit any values to the dimensions listed. """ if method == "scalar": transform = 1.0 elif method == "vector": transform = [1.0, 1.0, 1.0, 1.0] else: transform = np.eye(4) tp = PassthroughNodeTransmissionParameters( Transform(size_in=4, size_out=16, slice_out=slice(0, 4), transform=transform)) assert tp.projects_to(slice(1)) assert tp.projects_to(slice(5)) assert not tp.projects_to(slice(4, 8)) assert tp.projects_to((0, 1, 2, 4))
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_concat_no_connection(self): a = EnsembleTransmissionParameters(decoders=[[1.0, 2.0, 3.0, 4.0], [4.0, 3.0, 2.0, 1.0]], transform=Transform(2, 4, 1, slice_out=(1, 2))) b = PassthroughNodeTransmissionParameters( Transform(size_in=4, size_out=2, transform=1.0, slice_in=(0, 3))) # Combine the parameters assert a.concat(b) is None
def test_concat_no_connection(self): a = NodeTransmissionParameters(Transform(size_in=2, size_out=5, transform=[[2.0, 0.0], [0.0, 2.0]], slice_out=(1, 3)), pre_slice=slice(0, 3), function=object()) b = PassthroughNodeTransmissionParameters( Transform(size_in=5, size_out=2, transform=[0.5, 0.25], slice_in=(0, 2))) # Combine assert a.concat(b) is None
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_concat_no_learning_rule(self): a = EnsembleTransmissionParameters(decoders=[[1.0, 2.0, 3.0, 4.0], [4.0, 3.0, 2.0, 1.0]], transform=Transform(2, 4, 1, slice_out=(1, 2))) b = PassthroughNodeTransmissionParameters( Transform(size_in=4, size_out=2, transform=1.0, slice_in=(1, 2))) # Combine the parameters c = a.concat(b) # Check the results assert isinstance(c, EnsembleTransmissionParameters) assert c.learning_rule is None assert c.size_out == b.size_out assert np.array_equal(c.slice_out, b.slice_out) assert np.array_equal(c.decoders, a.decoders)
def stack_interposers(self, interposers): """Return a new list of interposers and a new communication map resulting from combining compatible interposers. Returns ------- ([Interposer, ...], ConnectionMap) A collection of new interposer operators and a new connection map with compatible interposers stacked. """ # Determine which interposers can be stacked and build a map of # {interposer: (StackedInterposer, offset)}. Using this information we # copy signals from the old connection map into the new connection map, # replacing connections to stacked interposers by modifying their # output slice and stacking the connections from interposers in a # similar manner. # Create a mapping {{(sig params, sink), ...}: [Interposer, ...], ...} # to determine which interposers can be stacked (as they have a common # output set). compatible_interposers = defaultdict(list) for interposer in interposers: # Build up a set of the outputs for each interposer. outputs = list() for (sig_pars, _), sinks in iteritems( self._connections[interposer][OutputPort.standard]): outputs.extend((sig_pars, sink) for sink in sinks) # Freeze the output set for hashing output_set = frozenset(outputs) compatible_interposers[output_set].append(interposer) # For each group of compatible interposers create a new, stacked, # interposer. stacked_interposers = dict() # {StackedInterposer: [Interposer,...]} stacking = dict() # {Interposers: (StackedInterposer, offset)} for interposer_group in itervalues(compatible_interposers): # Create the new interposer new_interposer = Filter(sum(i.size_in for i in interposer_group)) stacked_interposers[new_interposer] = interposer_group # Store a mapping from the original interposers to the new # interposer and their offset into its input space. offset = 0 for interposer in interposer_group: stacking[interposer] = (new_interposer, offset) offset += interposer.size_in # Increase the offset # Create a new connection map and copy connections into it. cm = ConnectionMap() for source, ports_conns_sinks in iteritems(self._connections): # Ignore sources which are interposers for the moment if source in stacking: continue # Add connections to the new connection map, if they target an # interposer then modify the connection by projecting it into the # space of the new interposer. for port, conns_sinks in iteritems(ports_conns_sinks): for conn, sinks in iteritems(conns_sinks): sps, tps = conn # Extract parameters for sink in sinks: if sink.sink_object in stacking: # If the sink is an interposer then add a modified # connection to the new interposer. sink_object, offset = stacking[sink.sink_object] # Create a projection to apply. slice_out = slice(offset, tps.size_out + offset) projection = PassthroughNodeTransmissionParameters( Transform(size_in=tps.size_out, size_out=sink_object.size_in, transform=1.0, slice_out=slice_out)) # Get the new transmission parameters new_tps = tps.concat(projection) assert new_tps is not None cm.add_connection(source, port, copy(sps), new_tps, sink_object, sink.port, sink.reception_parameters) else: # Otherwise just add the signal unchanged cm.add_connection(source, port, copy(sps), tps, sink.sink_object, sink.port, sink.reception_parameters) # For each stacked interposer build up a mapping from sinks, reception # parameters and signal parameters to the transmission parameters that # target it for each unstacked interposer. Subsequently stack these # transmission parameters and add the resulting signal to the network. for new_interposer, components in iteritems(stacked_interposers): # Map from (sink, signal_parameters) to an ordered list of # transmission parameters. connections = defaultdict(list) for node in components: for conns_and_sinks in itervalues(self._connections[node]): for (sps, tps), sinks in iteritems(conns_and_sinks): for sink in sinks: connections[(sink, sps)].append(tps) # For each unique pair of sink and signal parameters add a new # connection to the network which is the result of stacking # together the transmission parameters. for (sink, sig_params), trans_pars in iteritems(connections): # Construct the combined signal parameters transmission_params = trans_pars[0].hstack(*trans_pars[1:]) # Add the new connection to the network cm.add_connection(new_interposer, OutputPort.standard, copy(sig_params), transmission_params, sink.sink_object, sink.port, sink.reception_parameters) return list(stacked_interposers.keys()), cm
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