def test_remove_sinkless_signals():
    """Removing sinkless signals will modify the connection map to remove any
    signals which no-longer have any sinks.
    """
    # Construct a connection map containing two signals.
    # Objects to act as sources and sinks
    obj_a = mock.Mock(name="A")
    obj_b = mock.Mock(name="B")

    # Add the connections
    cm = model.ConnectionMap()
    cm.add_connection(obj_a, None, model.SignalParameters(weight=1), None,
                      obj_b, None, None)
    cm.add_connection(obj_b, None, model.SignalParameters(weight=2), None,
                      obj_a, None, None)

    # Remove the sinks from one of the connections
    for _, sinks in cm._connections[obj_b][None]:
        for sink in sinks:
            sinks.remove(sink)

    # Remove all the sinkless signals
    model.remove_sinkless_signals(cm)

    # Assert that only one signal remains
    assert len(cm.get_signals_from_object(obj_a)[None]) == 1
    assert len(cm.get_signals_from_object(obj_b)) == 0
    assert len(list(cm.get_signals())) == 1
Beispiel #2
0
    def test_eq(self):
        # Create several SignalParameters and ensure that they only
        # report equal when they are actually equal.
        ks = BitField()
        ks.add_field("x")

        params = ((False, 5, ks(x=2)),
                  (True, 5, ks(x=2)),
                  (False, 4, ks(x=2)),
                  (False, 5, ks(x=3)),
                  )

        tps = tuple(model.SignalParameters(*args) for args in params)

        # None of these transmission parameters should test as equivalent.
        for a in tps:
            for b in tps:
                if a is not b:
                    assert a != b

        # Create a whole new set of transmission parameters using the same set
        # of parameters and ensure that they all test equivalent to their
        # counterparts in the original list.
        for a, b in zip(tps, tuple(model.SignalParameters(*args)
                                   for args in params)):
            assert a is not b
            assert a == b
Beispiel #3
0
    def test_combine_sig_pars_keyspaces_all_none(self):
        """Check that the signal parameters are combined correctly."""
        # The keyspace should be None if all antecedents are None
        sps1 = model.SignalParameters()
        sps2 = model.SignalParameters()

        combined_sps = sps1.concat(sps2)
        assert combined_sps.keyspace is None
Beispiel #4
0
    def test_combine_sig_pars_latching(self, latching_a, latching_b):
        """Check that the signal parameters are combined correctly."""
        # The signal should be latching if any of its antecedents was latching.
        sps1 = model.SignalParameters(latching=latching_a)
        sps2 = model.SignalParameters(latching=latching_b)

        combined_sps = sps1.concat(sps2)
        assert combined_sps.latching is (latching_a or latching_b)
Beispiel #5
0
    def test_combine_sig_pars_last_weight(self):
        """Check that the signal parameters are combined correctly."""
        # The last weight should be used
        sps1 = model.SignalParameters(weight=1)
        sps2 = model.SignalParameters(weight=5)

        combined_sps = sps1.concat(sps2)
        assert combined_sps.weight == sps2.weight
Beispiel #6
0
    def test_combine_sig_pars_keyspaces_one_specified(self, first):
        """Check that the signal parameters are combined correctly."""
        # If ONE keyspace is specified it should be used for the entire trace
        ks = object()
        sps1 = model.SignalParameters(keyspace=ks if first else None)
        sps2 = model.SignalParameters(keyspace=None if first else ks)

        combined_sps = sps1.concat(sps2)
        assert combined_sps.keyspace is ks
Beispiel #7
0
    def test_get_signals(self):
        # Construct some connections and ensure that these result in
        # appropriate signals being returned.
        # Objects to act as sources and sinks
        obj_a = mock.Mock(name="A")
        obj_b = mock.Mock(name="B")
        obj_c = mock.Mock(name="C")

        # Keyspaces
        ks_abc = mock.Mock(name="Keyspace A -> B,C")
        ks_cb = mock.Mock(name="Keyspace C -> B")

        # Add the connections
        cm = model.ConnectionMap()
        tp_a = mock.Mock(name="Transmission Parameters A")
        tp_b = mock.Mock(name="Transmission Parameters B")

        cm.add_connection(
            obj_a, None, model.SignalParameters(weight=3, keyspace=ks_abc),
            tp_a, obj_b, None, None
        )
        cm.add_connection(
            obj_a, None, model.SignalParameters(weight=3, keyspace=ks_abc),
            tp_a, obj_c, None, None
        )
        cm.add_connection(
            obj_c, None, model.SignalParameters(weight=5, keyspace=ks_cb),
            tp_b, obj_b, None, None
        )

        # Get the signals, this should be a list of two signals
        signals = list(cm.get_signals())
        assert len(signals) == 2
        for signal, transmission_params in signals:
            if signal.source is obj_a:
                # Assert the sinks are correct
                assert len(signal.sinks) == 2
                assert obj_b in signal.sinks
                assert obj_c in signal.sinks

                # Assert the keyspace is correct
                assert signal.keyspace is ks_abc

                # Assert the weight is correct
                assert signal.weight == 3

                # Assert the correct paired transmission parameters are used.
                assert transmission_params is tp_a
            else:
                # Source should be C, sink B
                assert signal.source is obj_c
                assert signal.sinks == [obj_b]
                assert signal.keyspace is ks_cb
                assert signal.weight == 5

                # Assert the correct paired transmission parameters are used.
                assert transmission_params is tp_b
Beispiel #8
0
    def test_combine_sig_pars_keyspaces_two_specified(self):
        """Check that the signal parameters are combined correctly."""
        # An error should be raised if multiple keyspaces are found
        ks = object()
        sps1 = model.SignalParameters(keyspace=ks)
        sps2 = model.SignalParameters(keyspace=ks)

        with pytest.raises(Exception) as exc:
            sps1.concat(sps2)

        assert "keyspace" in str(exc.value)
Beispiel #9
0
    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
Beispiel #10
0
    def test_get_signals_to_all_objects(self):
        # Create two ports
        sp_1 = mock.Mock(name="Sink Port 1")
        sp_2 = mock.Mock(name="Sink Port 2")

        # Create some connections to an object
        sink_a = mock.Mock(name="Sink A")

        tp1 = model.SignalParameters()
        tp2 = model.SignalParameters(True)
        tp3 = model.SignalParameters(weight=1)

        rp1 = mock.Mock(name="Reception Parameters 1")
        rp2 = mock.Mock(name="Reception Parameters 2")

        # Add the connections
        cm = model.ConnectionMap()

        conns_a = (
            (tp1, sink_a, sp_1, rp1),
            (tp1, sink_a, sp_1, rp2),
            (tp2, sink_a, sp_2, rp1),
            (tp3, sink_a, sp_2, rp1),
        )
        for tp, sink, sink_port, rp in conns_a:
            cm.add_connection(None, None, tp, None, sink, sink_port, rp)

        # Add another connection to another object
        sink_b = mock.Mock(name="Sink B")
        cm.add_connection(None, None, tp1, None, sink_b, sp_1, rp)

        # Get the signals to sink_a, check that they are as expected
        sigs_a = cm.get_signals_to_all_objects()[sink_a]
        assert len(sigs_a[sp_1]) == 2
        seen_rps = []

        for spec in sigs_a[sp_1]:
            assert spec.signal_parameters is tp1
            seen_rps.append(spec.reception_parameters)

        assert rp1 in seen_rps and rp2 in seen_rps

        assert len(sigs_a[sp_2]) == 2
        seen_tps = []

        for spec in sigs_a[sp_2]:
            assert spec.reception_parameters is rp1
            seen_tps.append(spec.signal_parameters)

        assert tp2 in seen_tps and tp3 in seen_tps
Beispiel #11
0
    def test_add_connection_different_port(self):
        # Create parameters for the first connection
        source1 = mock.Mock(name="Source 1")
        soport1 = mock.Mock(name="Source Port 1")

        sp1 = model.SignalParameters(False)
        tp1 = mock.Mock(name="Transmission Parameters")

        sink1 = mock.Mock(name="Sink 1")
        siport1 = mock.Mock(name="Sink Port 1")

        rp1 = mock.Mock(name="Reception Parameters")

        # Create the connection map and add the connection
        cm = model.ConnectionMap()
        cm.add_connection(source1, soport1, sp1, tp1, sink1, siport1, rp1)

        # Create and add the second connection with a different port
        soport2 = mock.Mock(name="Source Port 2")
        cm.add_connection(source1, soport2, sp1, tp1, sink1, siport1, rp1)

        # Assert the map is still correct
        assert (cm._connections[source1][soport1][(sp1, tp1)] ==
                [(sink1, siport1, rp1)])
        assert (cm._connections[source1][soport2][(sp1, tp1)] ==
                [(sink1, siport1, rp1)])
Beispiel #12
0
    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
Beispiel #13
0
    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
Beispiel #14
0
    def test_get_signals_from_object(self):
        # Create two ports
        sp_1 = mock.Mock(name="Source Port 1")
        sp_2 = mock.Mock(name="Source Port 2")

        # Create some connections from two objects
        source_a = mock.Mock(name="Source A")
        source_b = mock.Mock(name="Source B")

        sp1 = model.SignalParameters()
        sp2 = model.SignalParameters(True)
        sp3 = model.SignalParameters(weight=1)

        tp1 = mock.Mock(name="Transmission Parameters 1")
        tp2 = mock.Mock(name="Transmission Parameters 2")
        tp3 = mock.Mock(name="Transmission Parameters 3")

        # Add the connections
        cm = model.ConnectionMap()

        conns_a = ((sp_1, sp1, tp1), (sp_1, sp2, tp2), (sp_2, sp3, tp3))
        for port, sp, tp in conns_a:
            cm.add_connection(source_a, port, sp, tp, None, None, None)

        conns_b = ((sp_2, sp1, tp2), (sp_2, sp2, tp2), (sp_1, sp3, tp3))
        for port, sp, tp in conns_b:
            cm.add_connection(source_b, port, sp, tp, None, None, None)

        # Get the signals from source_a, check that they are as expected
        sigs_a = cm.get_signals_from_object(source_a)
        assert (sp1, tp1) in sigs_a[sp_1]
        assert (sp2, tp2) in sigs_a[sp_1]
        assert sigs_a[sp_2] == [(sp3, tp3)]

        # Get the signals from source_b, check that they are as expected
        sigs_b = cm.get_signals_from_object(source_b)
        assert sigs_b[sp_1] == [(sp3, tp3)]
        assert (sp1, tp2) in sigs_b[sp_2]
        assert (sp2, tp2) in sigs_b[sp_2]
def test_remove_sinkless_objects():
    """Filter objects with no outgoing connections (hence sinks) can safely be
    removed from the network.  This process should be called repeatedly as long
    as there are filters which can be removed.

    We construct the following network and then expect that all items apart
    from A and G are removed from the connection map.

                /--> C --\
               /          \
        A --> B            E --> F
        |      \          /
        |       \--> D --/
        \-> G

    The result should be a reduced connection map and a list of all objects
    apart from A and G.
    """
    class G(object):
        """Separate class to indicate the type filtering works as expected."""

    # Construct the network objects
    a = mock.Mock(name="A")
    b = mock.Mock(name="B")
    c = mock.Mock(name="C")
    d = mock.Mock(name="D")
    e = mock.Mock(name="E")
    f = mock.Mock(name="F")
    g = G()

    # Make the connection map
    cm = model.ConnectionMap()
    cm.add_connection(a, None, model.SignalParameters(), None, b, None, None)
    cm.add_connection(a, None, model.SignalParameters(), None, g, None, None)
    cm.add_connection(b, None, model.SignalParameters(), None, c, None, None)
    cm.add_connection(b, None, model.SignalParameters(), None, d, None, None)
    cm.add_connection(c, None, model.SignalParameters(), None, e, None, None)
    cm.add_connection(d, None, model.SignalParameters(), None, e, None, None)
    cm.add_connection(e, None, model.SignalParameters(), None, f, None, None)
    cm._connections[f][None] = list()

    # Remove the sinkless filters
    removed = model.remove_sinkless_objects(cm, mock.Mock)

    # Check that the expected connection A->G still exists.
    assert len(cm.get_signals_from_object(a)[None]) == 1
    assert len(list(cm.get_signals())) == 1

    # Assert that the removed objects list is correct
    expected_removed = set((b, c, d, e, f))
    for x in expected_removed:
        assert len(cm.get_signals_from_object(x)) == 0
    assert removed == expected_removed
Beispiel #16
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
Beispiel #17
0
    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_get_passthrough_node_dependencies():
    """Check that the creation of a passthrough Node dependency graph works as
    expected.
    """
    # Construct a mock network with three sets of passthrough Nodes in the
    # configuration:
    #
    # 0 --> 2 --> 3
    # 1 --/  \--> 4
    #
    # 5 --------> 6
    #
    # 7

    # Create the Nodes and operators
    ns = [nengo.Node(size_in=1, label="N{}".format(i), add_to_container=False)
          for i in range(8)]
    ops = [mock.Mock(name="O{}".format(i)) for i, _ in enumerate(ns)]

    # Create the model
    m = Model()
    m.object_operators = ptns = dict(zip(ns, ops))

    # Add the connections
    for i, j in ((0, 2), (1, 2), (2, 3), (2, 4), (5, 6)):
        m.connection_map.add_connection(
            ops[i], None, model.SignalParameters(), None, ops[j], None, None)

    # Check the dependencies against the expected dependencies.
    deps = model_utils.get_passthrough_node_dependencies(m, ptns)
    assert deps == {
        (ns[0], ops[0]): set(),
        (ns[1], ops[1]): set(),
        (ns[2], ops[2]): {(ns[0], ops[0]), (ns[1], ops[1])},
        (ns[3], ops[3]): {(ns[2], ops[2])},
        (ns[4], ops[4]): {(ns[2], ops[2])},
        (ns[5], ops[5]): set(),
        (ns[6], ops[6]): {(ns[5], ops[5])},
        (ns[7], ops[7]): set(),
    }
Beispiel #19
0
    def test_add_connection_repeated(self):
        # Create parameters for the first connection
        source1 = mock.Mock(name="Source 1")
        soport1 = mock.Mock(name="Source Port 1")

        sp1 = model.SignalParameters(False)
        tp1 = mock.Mock(name="Transmission Parameters")

        sink1 = mock.Mock(name="Sink 1")
        siport1 = mock.Mock(name="Sink Port 1")

        rp1 = mock.Mock(name="Reception Parameters")

        # Create the connection map and add the connection
        cm = model.ConnectionMap()
        cm.add_connection(source1, soport1, sp1, tp1, sink1, siport1, rp1)

        # Add the "same" connection again
        cm.add_connection(source1, soport1, sp1, tp1, sink1, siport1, rp1)

        # Check that the connection was added twice
        assert (cm._connections[source1][soport1][(sp1, tp1)] ==
                [(sink1, siport1, rp1)]*2)
Beispiel #20
0
    def test_add_connection_basic(self):
        """Test adding connections to a connection map."""
        # Create parameters for the first connection
        source1 = mock.Mock(name="Source 1")
        soport1 = mock.Mock(name="Source Port 1")

        sp1 = model.SignalParameters(False)

        sink1 = mock.Mock(name="Sink 1")
        siport1 = mock.Mock(name="Sink Port 1")

        rp1 = mock.Mock(name="Reception Parameters")

        # Create the connection map and add the connection
        cm = model.ConnectionMap()
        assert len(list(cm.get_signals())) == 0
        cm.add_connection(source1, soport1, sp1, None, sink1, siport1, rp1)

        # Assert that this connection was added correctly
        assert len(list(cm.get_signals())) == 1
        assert len(cm._connections) == 1
        assert len(cm._connections[source1]) == 1
        assert (cm._connections[source1][soport1][(sp1, None)] ==
                [(sink1, siport1, rp1)])
Beispiel #21
0
    def test_get_coarsened_graph_and_extract_cliques(self):
        """Check that a coarsened representation of the graph can be extracted.
        """
        # Construct a graph of the form:
        #
        #         /--<-------------<--\
        #         |                   |
        # E ->-\  v /->- E ->-\       |
        # E ->--> o -->- E ->--> o ->-/
        # E ->-/    \->- E ->-/
        #
        # Where E are ensembles and `o' is a passthrough node.
        cm = model.ConnectionMap()

        # Network objects
        ens_a = [object() for _ in range(3)]
        ens_b = [object() for _ in range(3)]
        ptns = [model.PassthroughNode(), model.PassthroughNode()]

        # Add connections to the network, excluding the feedback loop.
        for ens in ens_a:
            cm.add_connection(
                ens, OutputPort.standard, model.SignalParameters(),
                object(), ptns[0], InputPort.standard, None
            )

        for ens in ens_b:
            cm.add_connection(
                ptns[0], OutputPort.standard, model.SignalParameters(),
                object(), ens, InputPort.standard, None
            )
            cm.add_connection(
                ens, OutputPort.standard, model.SignalParameters(),
                object(), ptns[1], InputPort.standard, None
            )

        # Get a coarsened representation of the connectivity
        graph = cm.get_coarsened_graph()

        for ens in ens_a:
            assert graph[ens].inputs == set()
            assert graph[ens].outputs == {ptns[0]}

        for ens in ens_b:
            assert graph[ens].inputs == {ptns[0]}
            assert graph[ens].outputs == {ptns[1]}

        assert graph[ptns[0]].inputs == set(ens_a)
        assert graph[ptns[0]].outputs == set(ens_b)

        assert graph[ptns[1]].inputs == set(ens_b)
        assert graph[ptns[1]].outputs == set()

        # Extract cliques from this graph, without the feedback in place there
        # should be two cliques.
        for sources, nodes in cm.get_cliques():
            if nodes == {ptns[0]}:
                assert sources == set(ens_a)
            elif nodes == {ptns[1]}:
                assert sources == set(ens_b)
            else:
                assert False, "Unexpected clique."

        # Add a feedback connection to the graph, this should mean that only a
        # single clique exists.
        cm.add_connection(
            ptns[1], OutputPort.standard, model.SignalParameters(),
            object(), ptns[0], InputPort.standard, None
        )

        # Get a coarsened representation of the connectivity
        graph = cm.get_coarsened_graph()

        for ens in ens_a:
            assert graph[ens].inputs == set()
            assert graph[ens].outputs == {ptns[0]}

        for ens in ens_b:
            assert graph[ens].inputs == {ptns[0]}
            assert graph[ens].outputs == {ptns[1]}

        assert graph[ptns[0]].inputs == set(ens_a) | {ptns[1]}
        assert graph[ptns[0]].outputs == set(ens_b)

        assert graph[ptns[1]].inputs == set(ens_b)
        assert graph[ptns[1]].outputs == {ptns[0]}

        # Extract cliques from this graph, without the feedback in place there
        # should be two cliques.
        for sources, nodes in cm.get_cliques():
            assert nodes == set(ptns)
            assert sources == set(ens_a) | set(ens_b)
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_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()
Beispiel #24
0
    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
Beispiel #25
0
    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
                )
Beispiel #26
0
    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_add_default_keyspace(self):
        # Adding a default keyspace will modify all transmission parameters
        # which have their keyspace as None by adding a new keyspace instance
        # with the "object" and "connection" ID automatically set.
        #
        # Test this by creating 3 objects.  The first object will have 2
        # outgoing connection, the second will have an outgoing connection with
        # a pre-existing keyspace and the third will have 1 outgoing connection
        # only. Check that the object and connection IDs are set sequentially
        # only for connections without existing keyspaces.

        # Create 3 source objects
        sources = [mock.Mock(name="Source {}".format(i)) for i in range(3)]

        # Create transmission parameters
        sp00 = model.SignalParameters()
        sp01 = model.SignalParameters(latching=True)

        ks10 = mock.Mock(name="Keyspace")
        sp10 = model.SignalParameters(keyspace=ks10)

        sp20 = model.SignalParameters()
        port21 = mock.Mock(name="Port 2.1")
        sp21 = model.SignalParameters()

        # Manually create the connection map so that the order can be
        # guaranteed.
        cm = model.ConnectionMap()
        cm._connections = collections.OrderedDict()

        cm._connections[sources[0]] = collections.OrderedDict()
        cm._connections[sources[0]][None] = [
            model._ParsSinksPair((sp00, None)),
            model._ParsSinksPair((sp01, None))
        ]

        cm._connections[sources[1]] = collections.OrderedDict()
        cm._connections[sources[1]][None] = [
            model._ParsSinksPair((sp10, None))
        ]

        cm._connections[sources[2]] = collections.OrderedDict()
        cm._connections[sources[2]][None] = [
            model._ParsSinksPair((sp20, None))
        ]
        cm._connections[sources[2]][port21] = [
            model._ParsSinksPair((sp21, None))
        ]

        # Add the default keyspace
        ks = mock.Mock()
        kss = {
            0: mock.Mock(name="Keyspace00"),
            1: mock.Mock(name="Keyspace01"),
            2: mock.Mock(name="Keyspace20"),
            3: mock.Mock(name="Keyspace21"),
        }
        ks.side_effect = lambda connection_id: kss[connection_id]

        cm.add_default_keyspace(ks)

        # Ensure that the correct calls were made to "ks" in the correct order.
        ks.assert_has_calls([mock.call(connection_id=j) for j in range(4)])

        # Assert that the correct keyspaces were assigned to the correct
        # objects.
        assert sp00.keyspace is kss[0]
        assert sp01.keyspace is kss[1]
        assert sp10.keyspace is ks10
        assert sp20.keyspace is kss[2]
        assert sp21.keyspace is kss[3]