Пример #1
0
    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)
Пример #2
0
    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)
Пример #3
0
    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
Пример #4
0
    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
Пример #5
0
    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
Пример #6
0
    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)
Пример #8
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
    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
Пример #11
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
Пример #12
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
Пример #13
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
Пример #14
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_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
Пример #16
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
Пример #17
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
                )
Пример #18
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_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()