Esempio n. 1
0
    def test_creation_from_wires_object(self, iterable):
        """Tests that a Wires object can be created from a list of Wires."""

        wires = Wires(iterable)
        assert wires.labels == (0, 1, 2)
Esempio n. 2
0
    def test_creation_from_iterables_of_exotic_elements(self, iterable):
        """Tests that a Wires object can be created from standard iterable inputs."""

        wires = Wires(iterable)
        assert wires.labels == tuple(iterable)
Esempio n. 3
0
    def test_creation_from_wires_lists(self):
        """Tests that a Wires object can be created from a list of Wires."""

        wires = Wires([Wires([0]), Wires([1]), Wires([2])])
        assert wires.labels == (Wires([0]), Wires([1]), Wires([2]))
Esempio n. 4
0
    def test_index_method(self, iterable):
        """Tests the ``index()`` method."""

        wires = Wires(iterable)
        element = iterable[1]
        # check for non-Wires inputs
        assert wires.index(element) == 1
        # check for Wires inputs
        assert wires.index(Wires([element])) == 1
        # check that Wires of length >1 produce an error
        with pytest.raises(WireError, match="Can only retrieve index"):
            wires.index(Wires([1, 2]))
        # check that error raised when wire does not exist
        with pytest.raises(WireError, match="Wire with label d not found"):
            wires.index(Wires(["d"]))
Esempio n. 5
0
 def test_equal_to_tuple(self):
     assert Wires([1, 2, 3]) == (1, 2, 3)
     assert Wires([1, 2, 3]) != (1, 5, 3)
     assert (1, 5, 3) != Wires([1, 2, 3])
Esempio n. 6
0
 def test_add_two_wires_objects(self):
     """Tests that wires objects add correctly."""
     wires1 = Wires([4, 0, 1])
     wires2 = Wires([1, 2])
     assert wires1 + wires2 == Wires([4, 0, 1, 2])
Esempio n. 7
0
 def test_add_wires_with_inbuilt_sum(self):
     """Tests that wires objects add correctly using sum()."""
     wires1 = [4, 0, 1]
     wires2 = Wires([1, 2])
     assert sum([wires1, wires2], Wires([]))
Esempio n. 8
0
 def control_wires(self):
     return Wires(self.wires[:2])
Esempio n. 9
0
 def test_wrong_dim(self):
     """Checks that an error is raised if state has the wrong dimension"""
     dev = qml.device("default.mixed", wires=3)
     state = np.ones(2)
     with pytest.raises(ValueError, match="BasisState parameter and wires"):
         dev._apply_basis_state(state, wires=Wires(range(3)))
Esempio n. 10
0
 def _controlled(self, wire):
     CZ(wires=Wires(wire) + self.wires)
Esempio n. 11
0
 def _controlled(self, wire):
     Toffoli(wires=Wires(wire) + self.wires)
Esempio n. 12
0
    def test_error_for_repeated_wires(self, iterable):
        """Tests that a Wires object cannot be created from iterables with repeated indices."""

        with pytest.raises(WireError, match="Wires must be unique"):
            Wires(iterable)
Esempio n. 13
0
    def test_error_for_incorrect_wire_types(self, iterable):
        """Tests that a Wires object cannot be created from wire types that are not allowed."""

        with pytest.raises(WireError, match="Wires must be represented"):
            Wires(iterable)
Esempio n. 14
0
    def test_creation_from_single_object(self, wire):
        """Tests that a Wires object can be created from a non-iterable object
        representing a single wire index."""

        wires = Wires(wire)
        assert wires.labels == (wire,)
Esempio n. 15
0
    def test_length(self, iterable):
        """Tests that a Wires object returns the correct length."""

        wires = Wires(iterable)
        assert len(wires) == len(iterable)
Esempio n. 16
0
 def test_not_01(self):
     """Checks that an error is raised if state doesn't have entries in {0,1}"""
     dev = qml.device("default.mixed", wires=2)
     state = np.array([INV_SQRT2, INV_SQRT2])
     with pytest.raises(ValueError, match="BasisState parameter must"):
         dev._apply_basis_state(state, wires=Wires(range(2)))
Esempio n. 17
0
    def test_contains_wires(self, ):
        """Tests the dedicated contains_wires() method."""

        wires = Wires([0, 1, 2, 3, Wires([4, 5]), None])

        assert wires.contains_wires(Wires([0, 3]))
        assert wires.contains_wires(Wires([1, 2, None]))
        assert wires.contains_wires(Wires([Wires(
            [4, 5])]))  # Wires([4, 5]) is just a label!

        assert not wires.contains_wires(0)  # wrong type
        assert not wires.contains_wires([0, 1])  # wrong type
        assert not wires.contains_wires(Wires(
            [4, 5]))  # looks up 4 and 5 in wires, which are not present
Esempio n. 18
0
 def test_wrong_dim(self):
     """Checks that an error is raised if state has the wrong dimension"""
     dev = qml.device("default.mixed", wires=3)
     state = np.ones(7) / np.sqrt(7)
     with pytest.raises(ValueError, match="State vector must be"):
         dev._apply_state_vector(state, Wires(range(3)))
Esempio n. 19
0
 def test_add_wires_object_with_iterable(self):
     """Tests that wires objects add correctly."""
     wires1 = [4, 0, 1]
     wires2 = Wires([1, 2])
     assert wires1 + wires2 == Wires([4, 0, 1, 2])
     assert wires2 + wires1 == Wires([1, 2, 4, 0])
Esempio n. 20
0
 def test_not_normalized(self):
     """Checks that an error is raised if state is not normalized"""
     dev = qml.device("default.mixed", wires=3)
     state = np.ones(8) / np.sqrt(7)
     with pytest.raises(ValueError, match="Sum of amplitudes"):
         dev._apply_state_vector(state, Wires(range(3)))
Esempio n. 21
0
class TestWires:
    """Tests for the ``Wires`` class."""
    @pytest.mark.parametrize(
        "iterable",
        [np.array([0, 1, 2]), [0, 1, 2],
         (0, 1, 2), range(3)])
    def test_creation_from_common_iterables(self, iterable):
        """Tests that a Wires object can be created from standard iterable inputs."""

        wires = Wires(iterable)
        assert wires.labels == (0, 1, 2)

    @pytest.mark.parametrize(
        "iterable",
        [
            [qml.RX, qml.RY],
            [qml.PauliX],
            (None, qml.expval),
            (
                qml.device("default.qubit", wires=range(3)),
                qml.device("default.gaussian", wires=[qml.RX, 3]),
            ),
        ],
    )
    def test_creation_from_iterables_of_exotic_elements(self, iterable):
        """Tests that a Wires object can be created from standard iterable inputs."""

        wires = Wires(iterable)
        assert wires.labels == tuple(iterable)

    def test_creation_from_wires_object(self):
        """Tests that a Wires object can be created from another Wires object."""

        wires = Wires(Wires([0, 1, 2]))
        assert wires.labels == (0, 1, 2)

    def test_creation_from_wires_lists(self):
        """Tests that a Wires object can be created from a list of Wires."""

        wires = Wires([Wires([0]), Wires([1]), Wires([2])])
        assert wires.labels == (Wires([0]), Wires([1]), Wires([2]))

    @pytest.mark.parametrize(
        "iterable",
        [[1, 0, 4], ["a", "b", "c"], [0, 1, None], ["a", 1, "ancilla"]])
    def test_creation_from_different_wire_types(self, iterable):
        """Tests that a Wires object can be created from iterables of different
        objects representing a single wire index."""

        wires = Wires(iterable)
        assert wires.labels == tuple(iterable)

    @pytest.mark.parametrize("wire", [1, -2, "a", "q1", -1.4])
    def test_creation_from_single_object(self, wire):
        """Tests that a Wires object can be created from a non-iterable object
        representing a single wire index."""

        wires = Wires(wire)
        assert wires.labels == (wire, )

    @pytest.mark.parametrize(
        "input", [[np.array([0, 1, 2]), np.array([3, 4])], [[0, 1, 2], [3, 4]],
                  np.array(0.0)])
    def test_error_for_incorrect_wire_types(self, input):
        """Tests that a Wires object cannot be created from unhashable objects such as np arrays or lists."""

        with pytest.raises(WireError, match="Wires must be hashable"):
            Wires(input)

    @pytest.mark.parametrize(
        "iterable",
        [np.array([4, 1, 1, 3]), [4, 1, 1, 3], (4, 1, 1, 3), ["a", "a", "b"]])
    def test_error_for_repeated_wires(self, iterable):
        """Tests that a Wires object cannot be created from iterables with repeated indices."""

        with pytest.raises(WireError, match="Wires must be unique"):
            Wires(iterable)

    @pytest.mark.parametrize("iterable", [[4, 1, 0, 3], ["a", "b", "c"]])
    def test_indexing_and_slicing(self, iterable):
        """Tests the indexing and slicing of Wires objects."""

        wires = Wires(iterable)

        # check single index
        for i in range(len(iterable)):
            assert wires[i] == iterable[i]
        # check slicing
        assert wires[:2] == Wires(iterable[:2])

    def test_equality(self):
        """Tests that we can compare Wires objects with the '==' and '!=' operators."""

        wires1 = Wires([1, 2, 3])
        wires2 = Wires([3, 2, 1])
        wires3 = Wires([1, 2, 3])
        assert wires1 != wires2
        assert wires1 == wires3

    @pytest.mark.parametrize("iterable", [[4, 1, 0, 3], ["a", "b", "c"]])
    def test_length(self, iterable):
        """Tests that a Wires object returns the correct length."""

        wires = Wires(iterable)
        assert len(wires) == len(iterable)

    def test_contains(self, ):
        """Tests the __contains__() method."""

        wires = Wires([0, 1, 2, 3, Wires([4, 5]), None])

        assert 0 in wires
        assert Wires([4, 5]) in wires
        assert None in wires
        assert not Wires([1]) in wires
        assert not Wires([0, 3]) in wires
        assert not Wires([0, 4]) in wires

        assert not [0, 4] in wires
        assert not [4] in wires

    def test_contains_wires(self, ):
        """Tests the dedicated contains_wires() method."""

        wires = Wires([0, 1, 2, 3, Wires([4, 5]), None])

        assert wires.contains_wires(Wires([0, 3]))
        assert wires.contains_wires(Wires([1, 2, None]))
        assert wires.contains_wires(Wires([Wires(
            [4, 5])]))  # Wires([4, 5]) is just a label!

        assert not wires.contains_wires(0)  # wrong type
        assert not wires.contains_wires([0, 1])  # wrong type
        assert not wires.contains_wires(Wires(
            [4, 5]))  # looks up 4 and 5 in wires, which are not present

    def test_add_two_wires_objects(self):
        """Tests that wires objects add correctly."""
        wires1 = Wires([4, 0, 1])
        wires2 = Wires([1, 2])
        assert wires1 + wires2 == Wires([4, 0, 1, 2])

    def test_add_wires_object_with_iterable(self):
        """Tests that wires objects add correctly."""
        wires1 = [4, 0, 1]
        wires2 = Wires([1, 2])
        assert wires1 + wires2 == Wires([4, 0, 1, 2])
        assert wires2 + wires1 == Wires([1, 2, 4, 0])

    def test_add_wires_with_inbuilt_sum(self):
        """Tests that wires objects add correctly using sum()."""
        wires1 = [4, 0, 1]
        wires2 = Wires([1, 2])
        assert sum([wires1, wires2], Wires([]))

    def test_representation_and_string(self):
        """Tests the string representation via both __str__ and __repr__."""

        wires_str = str(Wires([1, 2, 3]))
        wires_repr = repr(Wires([1, 2, 3]))
        assert wires_str == "<Wires = [1, 2, 3]>"
        assert wires_repr == "<Wires = [1, 2, 3]>"

    def test_array_representation(self):
        """Tests that Wires object has an array representation."""

        wires = Wires([4, 0, 1])
        array = np.array(wires)
        assert isinstance(array, np.ndarray)
        assert array.shape == (3, )
        for w1, w2 in zip(array, np.array([4, 0, 1])):
            assert w1 == w2

    def test_set_of_wires(self):
        """Tests that a set() of wires is formed correctly."""

        wires = Wires([0, 1, 2])
        list_of_wires = [Wires([1]), Wires([1]), Wires([1, 2, 3]), Wires([4])]

        assert set(wires) == {0, 1, 2}
        assert set(list_of_wires) == {Wires([1]), Wires([1, 2, 3]), Wires([4])}

    def test_label_property(self):
        """Tests the get_label() method."""

        labels = [0, "q1", 16]
        wires = Wires(labels)

        assert wires.labels == tuple(labels)
        assert wires.labels[1] == "q1"
        assert wires.labels[2] == 16

    def test_convert_to_numpy_array(self):
        """Tests that Wires object can be converted to a numpy array."""

        wires = Wires([4, 0, 1])
        array = wires.toarray()
        assert isinstance(array, np.ndarray)
        assert array.shape == (3, )
        for w1, w2 in zip(array, np.array([4, 0, 1])):
            assert w1 == w2

    def test_convert_to_list(self):
        """Tests that Wires object can be converted to a list."""

        wires = Wires([4, 0, 1])
        list_ = wires.tolist()
        assert isinstance(list_, list)
        assert list_ == [4, 0, 1]

    @pytest.mark.parametrize("iterable", [[4, 1, 0, 3], ["a", "b", "c"]])
    def test_index_method(self, iterable):
        """Tests the ``index()`` method."""

        wires = Wires(iterable)
        element = iterable[1]
        # check for non-Wires inputs
        assert wires.index(element) == 1
        # check for Wires inputs
        assert wires.index(Wires([element])) == 1
        # check that Wires of length >1 produce an error
        with pytest.raises(WireError, match="Can only retrieve index"):
            wires.index(Wires([1, 2]))
        # check that error raised when wire does not exist
        with pytest.raises(WireError, match="Wire with label d not found"):
            wires.index(Wires(["d"]))

    def test_indices_method(self):
        """Tests the ``indices()`` method."""

        wires = Wires([4, 0, 1])
        # for Wires inputs
        assert wires.indices(Wires([1, 4])) == [2, 0]
        # for non-Wires inputs
        assert wires.indices([1, 4]) == [2, 0]
        # for integer
        assert wires.indices(1) == [2]

    @pytest.mark.parametrize(
        "wires, wire_map, expected",
        [
            (Wires(["a", "b"]), {
                "a": 0,
                "b": 1
            }, Wires([0, 1])),
            (Wires([-1, 1]), {
                1: "c",
                -1: 1,
                "d": "e"
            }, Wires([1, "c"])),
        ],
    )
    def test_map_method(self, wires, wire_map, expected):
        """Tests the ``map()`` method."""

        assert wires.map(wire_map) == expected

        # error when labels not in wire_map dictionary
        with pytest.raises(WireError, match="No mapping for wire label"):
            wires.map({-1: Wires(4)}) == expected

        # error for non-unique wire labels
        with pytest.raises(WireError, match="Failed to implement wire map"):
            wires = Wires([0, 1])
            wires.map({0: "a", 1: "a"})

    def test_select_random_method(self):
        """Tests the ``select_random()`` method."""

        wires = Wires([4, 0, 1, 5, 6])

        assert len(wires.select_random(2)) == 2
        # check that seed makes call deterministic
        assert wires.select_random(4, seed=1) == wires.select_random(4, seed=1)

        with pytest.raises(WireError, match="Cannot sample"):
            wires.select_random(6)

    def test_subset_method(self):
        """Tests the ``subset()`` method."""

        wires = Wires([4, 0, 1, 5, 6])

        assert wires.subset([2, 3, 0]) == Wires([1, 5, 4])
        assert wires.subset(1) == Wires([0])
        assert wires.subset([4, 5, 7],
                            periodic_boundary=True) == Wires([6, 4, 1])
        # if index does not exist
        with pytest.raises(WireError, match="Cannot subset wire at index"):
            wires.subset([10])

    def test_all_wires_method(self):
        """Tests the ``all_wires()`` method."""

        wires1 = Wires([2, 1, 3])
        wires2 = Wires([1, 4, 5, 2])
        wires3 = Wires([6, 5])

        new_wires = Wires.all_wires([wires1, wires2, wires3])
        assert new_wires.labels == (2, 1, 3, 4, 5, 6)

        new_wires = Wires.all_wires([wires1, wires2, wires3], sort=True)
        assert new_wires.labels == (1, 2, 3, 4, 5, 6)

        with pytest.raises(WireError, match="Expected a Wires object"):
            Wires.all_wires([[3, 4], [8, 5]])

    def test_shared_wires_method(self):
        """Tests the ``shared_wires()`` method."""

        wires1 = Wires([4, 0, 1])
        wires2 = Wires([3, 0, 4])
        wires3 = Wires([4, 0])
        res = Wires.shared_wires([wires1, wires2, wires3])
        assert res == Wires([4, 0])

        res = Wires.shared_wires([wires2, wires1, wires3])
        assert res == Wires([0, 4])

        with pytest.raises(WireError, match="Expected a Wires object"):
            Wires.shared_wires([[3, 4], [8, 5]])

    def test_unique_wires_method(self):
        """Tests the ``unique_wires()`` method."""

        wires1 = Wires([4, 0, 1])
        wires2 = Wires([3, 0, 4])
        wires3 = Wires([4, 0])
        res = Wires.unique_wires([wires1, wires2, wires3])
        assert res == Wires([1, 3])

        res = Wires.unique_wires([wires2, wires1, wires3])
        assert res == Wires([3, 1])

        with pytest.raises(WireError, match="Expected a Wires object"):
            Wires.unique_wires([[2, 1], [8, 5]])

    def test_equal_to_tuple(self):
        assert Wires([1, 2, 3]) == (1, 2, 3)
        assert Wires([1, 2, 3]) != (1, 5, 3)
        assert (1, 5, 3) != Wires([1, 2, 3])

    def test_hash_cached(self):
        """Test that the hash of a Wires object is being cached."""
        wires = Wires([0, 1])
        assert wires._hash is None
        h = hash(wires)
        assert wires._hash == h
Esempio n. 22
0
def CVNeuralNetLayers(theta_1, phi_1, varphi_1, r, phi_r, theta_2, phi_2,
                      varphi_2, a, phi_a, k, wires):
    r"""A sequence of layers of a continuous-variable quantum neural network,
    as specified in `arXiv:1806.06871 <https://arxiv.org/abs/1806.06871>`_.

    The layer consists
    of interferometers, displacement and squeezing gates mimicking the linear transformation of
    a neural network in the x-basis of the quantum system, and uses a Kerr gate
    to introduce a 'quantum' nonlinearity.

    The layers act on the :math:`M` modes given in ``wires``,
    and include interferometers of :math:`K=M(M-1)/2` beamsplitters. The different weight parameters
    contain the weights for each layer. The number of layers :math:`L` is therefore derived
    from the first dimension of ``weights``.

    This example shows a 4-mode CVNeuralNet layer with squeezing gates :math:`S`, displacement gates :math:`D` and
    Kerr gates :math:`K`. The two big blocks are interferometers of type
    :mod:`pennylane.templates.layers.Interferometer`:

    .. figure:: ../../_static/layer_cvqnn.png
        :align: center
        :width: 60%
        :target: javascript:void(0);

    .. note::
       The CV neural network architecture includes :class:`~pennylane.ops.Kerr` operations.
       Make sure to use a suitable device, such as the :code:`strawberryfields.fock`
       device of the `PennyLane-SF <https://github.com/XanaduAI/pennylane-sf>`_ plugin.

    Args:
        theta_1 (tensor_like): shape :math:`(L, K)` tensor of transmittivity angles for first interferometer
        phi_1 (tensor_like): shape :math:`(L, K)` tensor of phase angles for first interferometer
        varphi_1 (tensor_like): shape :math:`(L, M)` tensor of rotation angles to apply after first interferometer
        r (tensor_like): shape :math:`(L, M)` tensor of squeezing amounts for :class:`~pennylane.ops.Squeezing` operations
        phi_r (tensor_like): shape :math:`(L, M)` tensor of squeezing angles for :class:`~pennylane.ops.Squeezing` operations
        theta_2 (tensor_like): shape :math:`(L, K)` tensor of transmittivity angles for second interferometer
        phi_2 (tensor_like): shape :math:`(L, K)` tensor of phase angles for second interferometer
        varphi_2 (tensor_like): shape :math:`(L, M)` tensor of rotation angles to apply after second interferometer
        a (tensor_like): shape :math:`(L, M)` tensor of displacement magnitudes for :class:`~pennylane.ops.Displacement` operations
        phi_a (tensor_like): shape :math:`(L, M)` tensor of displacement angles for :class:`~pennylane.ops.Displacement` operations
        k (tensor_like): shape :math:`(L, M)` tensor of kerr parameters for :class:`~pennylane.ops.Kerr` operations
        wires (Iterable or Wires): Wires that the template acts on. Accepts an iterable of numbers or strings, or
            a Wires object.
    Raises:
        ValueError: if inputs do not have the correct format
    """

    wires = Wires(wires)
    repeat = _preprocess(theta_1, phi_1, varphi_1, r, phi_r, theta_2, phi_2,
                         varphi_2, a, phi_a, k, wires)

    for l in range(repeat):

        Interferometer(theta=theta_1[l],
                       phi=phi_1[l],
                       varphi=varphi_1[l],
                       wires=wires)

        r_and_phi_r = qml.math.stack([r[l], phi_r[l]], axis=1)
        broadcast(unitary=Squeezing,
                  pattern="single",
                  wires=wires,
                  parameters=r_and_phi_r)

        Interferometer(theta=theta_2[l],
                       phi=phi_2[l],
                       varphi=varphi_2[l],
                       wires=wires)

        a_and_phi_a = qml.math.stack([a[l], phi_a[l]], axis=1)
        broadcast(unitary=Displacement,
                  pattern="single",
                  wires=wires,
                  parameters=a_and_phi_a)

        broadcast(unitary=Kerr, pattern="single", wires=wires, parameters=k[l])
Esempio n. 23
0
    def test_creation_from_common_iterables(self, iterable):
        """Tests that a Wires object can be created from standard iterable inputs."""

        wires = Wires(iterable)
        assert wires.labels == (0, 1, 2)
Esempio n. 24
0
def _process_wires(wires, n_wires=None):
    r"""
    Checks and consolidates custom user wire mapping into a consistent, direction-free, `Wires`
    format. Used for converting between OpenFermion qubit numbering and Pennylane wire labels.

    Since OpenFermion's qubit numbering is always consecutive int, simple iterable types such as
    list, tuple, or Wires can be used to specify the 2-way `qubit index` <-> `wire label` mapping
    with indices representing qubits. Dict can also be used as a mapping, but does not provide any
    advantage over lists other than the ability to do partial mapping/permutation in the
    `qubit index` -> `wire label` direction.

    It is recommended to pass Wires/list/tuple `wires` since it's direction-free, i.e. the same
    `wires` argument can be used to convert both ways between OpenFermion and Pennylane. Only use
    dict for partial or unordered mapping.

    Args:
        wires (Wires, list, tuple, dict): User wire labels or mapping for Pennylane ansatz.
            For types Wires, list, or tuple, each item in the iterable represents a wire label
            corresponding to the qubit number equal to its index.
            For type dict, only int-keyed dict (for qubit-to-wire conversion) or
            consecutive-int-valued dict (for wire-to-qubit conversion) is accepted.
            If None, will be set to consecutive int based on ``n_wires``.
        n_wires (int): Number of wires used if known. If None, will be inferred from ``wires``; if
            ``wires`` is not available, will be set to 1.

    Returns:
        Wires: Cleaned wire mapping with indices corresponding to qubits and values
            corresponding to wire labels.

    **Example**

    >>> # consec int wires if no wires mapping provided, ie. identity map: 0<->0, 1<->1, 2<->2
    >>> _process_wires(None, 3)
    <Wires = [0, 1, 2]>

    >>> # List as mapping, qubit indices with wire label values: 0<->w0, 1<->w1, 2<->w2
    >>> _process_wires(['w0','w1','w2'])
    <Wires = ['w0', 'w1', 'w2']>

    >>> # Wires as mapping, qubit indices with wire label values: 0<->w0, 1<->w1, 2<->w2
    >>> _process_wires(Wires(['w0', 'w1', 'w2']))
    <Wires = ['w0', 'w1', 'w2']>

    >>> # Dict as partial mapping, int qubits keys to wire label values: 0->w0, 1 unchanged, 2->w2
    >>> _process_wires({0:'w0',2:'w2'})
    <Wires = ['w0', 1, 'w2']>

    >>> # Dict as mapping, wires label keys to consec int qubit values: w2->2, w0->0, w1->1
    >>> _process_wires({'w2':2, 'w0':0, 'w1':1})
    <Wires = ['w0', 'w1', 'w2']>
    """

    # infer from wires, or assume 1 if wires is not of accepted types.
    if n_wires is None:
        n_wires = len(wires) if isinstance(wires,
                                           (Wires, list, tuple, dict)) else 1

    # defaults to no mapping.
    if wires is None:
        return Wires(range(n_wires))

    if isinstance(wires, (Wires, list, tuple)):
        # does not care about the tail if more wires are provided than n_wires.
        wires = Wires(wires[:n_wires])

    elif isinstance(wires, dict):

        if all(isinstance(w, int) for w in wires.keys()):
            # Assuming keys are taken from consecutive int wires. Allows for partial mapping.
            n_wires = max(wires) + 1
            labels = list(range(
                n_wires))  # used for completing potential partial mapping.
            for k, v in wires.items():
                if k < n_wires:
                    labels[k] = v
            wires = Wires(labels)
        elif set(range(n_wires)).issubset(set(wires.values())):
            # Assuming values are consecutive int wires (up to n_wires, ignores the rest).
            # Does NOT allow for partial mapping.
            wires = {v: k for k, v in wires.items()}  # flip for easy indexing
            wires = Wires([wires[i] for i in range(n_wires)])
        else:
            raise ValueError(
                "Expected only int-keyed or consecutive int-valued dict for `wires`"
            )

    else:
        raise ValueError(
            "Expected type Wires, list, tuple, or dict for `wires`, got {}".
            format(type(wires)))

    if len(wires) != n_wires:
        # check length consistency when all checking and cleaning are done.
        raise ValueError(
            "Length of `wires` ({}) does not match `n_wires` ({})".format(
                len(wires), n_wires))

    return wires
Esempio n. 25
0
 def test_hash_cached(self):
     """Test that the hash of a Wires object is being cached."""
     wires = Wires([0, 1])
     assert wires._hash is None
     h = hash(wires)
     assert wires._hash == h
Esempio n. 26
0
def _terms_to_qubit_operator(coeffs, ops, wires=None):
    r"""Converts a 2-tuple of complex coefficients and PennyLane operations to
    OpenFermion ``QubitOperator``.

    This function is the inverse of ``_qubit_operator_to_terms``.

    Args:
        coeffs (array[complex]):
            coefficients for each observable, same length as ops
        ops (Iterable[pennylane.operation.Observable]): List of PennyLane observables as
            Tensor products of Pauli observables
        wires (Wires, list, tuple, dict): Custom wire mapping used to convert to qubit operator
            from an observable terms measurable in a PennyLane ansatz.
            For types Wires/list/tuple, each item in the iterable represents a wire label
            corresponding to the qubit number equal to its index.
            For type dict, only consecutive-int-valued dict (for wire-to-qubit conversion) is
            accepted. If None, will map sorted wires from all `ops` to consecutive int.

    Returns:
        QubitOperator: an instance of OpenFermion's ``QubitOperator``.

    **Example**

    >>> coeffs = np.array([0.1, 0.2])
    >>> ops = [
    ...     qml.operation.Tensor(qml.PauliX(wires=['w0'])),
    ...     qml.operation.Tensor(qml.PauliY(wires=['w0']), qml.PauliZ(wires=['w2']))
    ... ]
    >>> _terms_to_qubit_operator(coeffs, ops, wires=Wires(['w0', 'w1', 'w2']))
    0.1 [X0] +
    0.2 [Y0 Z2]
    """
    all_wires = Wires.all_wires([op.wires for op in ops], sort=True)

    if wires is not None:
        qubit_indexed_wires = _process_wires(wires, )
        if not set(all_wires).issubset(set(qubit_indexed_wires)):
            raise ValueError(
                "Supplied `wires` does not cover all wires defined in `ops`.")
    else:
        qubit_indexed_wires = all_wires

    q_op = openfermion.QubitOperator()
    for coeff, op in zip(coeffs, ops):

        extra_obsvbs = set(
            op.name) - {"PauliX", "PauliY", "PauliZ", "Identity"}
        if extra_obsvbs != set():
            raise ValueError(
                "Expected only PennyLane observables PauliX/Y/Z or Identity, "
                + "but also got {}.".format(extra_obsvbs))

        # Pauli axis names, note s[-1] expects only 'Pauli{X,Y,Z}'
        pauli_names = [s[-1] if s != "Identity" else s for s in op.name]

        all_identity = all(obs.name == "Identity" for obs in op.obs)
        if (op.name == ["Identity"] and len(op.wires) == 1) or all_identity:
            term_str = ""
        else:
            term_str = " ".join([
                "{}{}".format(pauli, qubit_indexed_wires.index(wire))
                for pauli, wire in zip(pauli_names, op.wires)
                if pauli != "Identity"
            ])

        # This is how one makes QubitOperator in OpenFermion
        q_op += coeff * openfermion.QubitOperator(term_str)

    return q_op
Esempio n. 27
0
    def test_creation_from_wires_object(self):
        """Tests that a Wires object can be created from another Wires object."""

        wires = Wires(Wires([0, 1, 2]))
        assert wires.labels == (0, 1, 2)
Esempio n. 28
0
    def __init__(self, ops, obs, wires, par_info=None, trainable_params=None):
        self._operations = ops
        self._observables = obs
        self.par_info = par_info
        self.trainable_params = trainable_params

        queue = ops + obs

        self._depth = None

        self._grid = {}
        """dict[int, list[Operator]]: dictionary representing the quantum circuit as a grid.
        Here, the key is the wire number, and the value is a list containing the operators on that wire.
        """
        self.wires = wires
        """Wires: wires that are addressed in the operations.
        Required to translate between wires and indices of the wires on the device."""
        self.num_wires = len(wires)
        """int: number of wires the circuit contains"""
        for k, op in enumerate(queue):
            op.queue_idx = k  # store the queue index in the Operator

            if hasattr(op, "return_type"):
                if op.return_type is qml.operation.State:
                    # State measurements contain no wires by default, but wires are
                    # required for the circuit drawer, so we recreate the state
                    # measurement with all wires
                    op = qml.measure.MeasurementProcess(qml.operation.State,
                                                        wires=wires)

                elif op.return_type is qml.operation.Sample and op.wires == Wires(
                    []):
                    # Sampling without specifying wires is treated as sampling all wires
                    op = qml.measure.MeasurementProcess(qml.operation.Sample,
                                                        wires=wires)

                op.queue_idx = k

            for w in op.wires:
                # get the index of the wire on the device
                wire = wires.index(w)
                # add op to the grid, to the end of wire w
                self._grid.setdefault(wire, []).append(op)

        # TODO: State preparations demolish the incoming state entirely, and therefore should have no incoming edges.

        self._graph = nx.DiGraph(
        )  #: nx.DiGraph: DAG representation of the quantum circuit
        # Iterate over each (populated) wire in the grid
        for wire in self._grid.values():
            # Add the first operator on the wire to the graph
            # This operator does not depend on any others
            self._graph.add_node(wire[0])

            for i in range(1, len(wire)):
                # For subsequent operators on the wire:
                if wire[i] not in self._graph:
                    # Add them to the graph if they are not already
                    # in the graph (multi-qubit operators might already have been placed)
                    self._graph.add_node(wire[i])

                # Create an edge between this and the previous operator
                self._graph.add_edge(wire[i - 1], wire[i])

        # For computing depth; want only a graph with the operations, not
        # including the observables
        self._operation_graph = None

        # Required to keep track if we need to handle multiple returned
        # observables per wire
        self._max_simultaneous_measurements = None
Esempio n. 29
0
    def test_creation_from_different_wire_types(self, iterable):
        """Tests that a Wires object can be created from iterables of different
        objects representing a single wire index."""

        wires = Wires(iterable)
        assert wires.labels == tuple(iterable)
Esempio n. 30
0
class TestWires:
    """Tests for the ``Wires`` class."""

    @pytest.mark.parametrize("iterable", [np.array([0, 1, 2]),
                                          [0, 1, 2],
                                          (0, 1, 2),
                                          range(3)
                                          ])
    def test_creation_from_common_iterables(self, iterable):
        """Tests that a Wires object can be created from standard iterable inputs."""

        wires = Wires(iterable)
        assert wires.labels == (0, 1, 2)

    @pytest.mark.parametrize("iterable", [Wires([0, 1, 2])])
    def test_creation_from_wires_object(self, iterable):
        """Tests that a Wires object can be created from another Wires object."""

        wires = Wires(iterable)
        assert wires.labels == (0, 1, 2)

    @pytest.mark.parametrize("iterable", [[Wires([0, 1]), Wires([2])],
                                          [Wires([0]), Wires([1]), Wires([2])]])
    def test_creation_from_wires_object(self, iterable):
        """Tests that a Wires object can be created from a list of Wires."""

        wires = Wires(iterable)
        assert wires.labels == (0, 1, 2)

    @pytest.mark.parametrize("iterable", [[1, 0, 4],
                                          ['a', 'b', 'c'],
                                          ['a', 1, "ancilla"]])
    def test_creation_from_different_wire_types(self, iterable):
        """Tests that a Wires object can be created from iterables of different
        objects representing a single wire index."""

        wires = Wires(iterable)
        assert wires.labels == tuple(iterable)

    @pytest.mark.parametrize("wire", [1, -2, 'a', 'q1', -1.4])
    def test_creation_from_single_object(self, wire):
        """Tests that a Wires object can be created from a non-iterable object
        representing a single wire index."""

        wires = Wires(wire)
        assert wires.labels == (wire,)

    @pytest.mark.parametrize("iterable", [[0, 1, None],
                                          [qml.RX],
                                          None,
                                          qml.RX])
    def test_error_for_incorrect_wire_types(self, iterable):
        """Tests that a Wires object cannot be created from wire types that are not allowed."""

        with pytest.raises(WireError, match="Wires must be represented"):
            Wires(iterable)
            
    @pytest.mark.parametrize("iterable", [np.array([4, 1, 1, 3]),
                                          [4, 1, 1, 3],
                                          (4, 1, 1, 3),
                                          ['a', 'a', 'b'],
                                          [Wires([1, 0]), Wires([1, 2]), Wires([3])]])
    def test_error_for_repeated_wires(self, iterable):
        """Tests that a Wires object cannot be created from iterables with repeated indices."""

        with pytest.raises(WireError, match="Wires must be unique"):
            Wires(iterable)

    @pytest.mark.parametrize("iterable", [[4, 1, 0, 3],
                                          ['a', 'b', 'c']])
    def test_indexing_and_slicing(self, iterable):
        """Tests the indexing and slicing of Wires objects."""

        wires = Wires(iterable)

        # check single index
        for i in range(len(iterable)):
            assert wires[i] == Wires(iterable[i])
        # check slicing
        assert wires[:2] == Wires(iterable[:2])

    def test_equality(self):
        """Tests that we can compare Wires objects with the '==' and '!=' operators."""

        wires1 = Wires([1, 2, 3])
        wires2 = Wires([3, 2, 1])
        wires3 = Wires([1, 2, 3])
        assert wires1 != wires2
        assert wires1 == wires3

    @pytest.mark.parametrize("iterable", [[4, 1, 0, 3],
                                          ['a', 'b', 'c']])
    def test_length(self, iterable):
        """Tests that a Wires object returns the correct length."""

        wires = Wires(iterable)
        assert len(wires) == len(iterable)

    def test_contains(self, ):
        """Tests the __contains__() method."""

        wires = Wires([0, 1, 2, 3])

        assert Wires([0, 3]) in wires
        assert Wires([1]) in wires
        assert not Wires([0, 4]) in wires
        assert not Wires([4]) in wires

        assert [0, 3] in wires
        assert [1] in wires
        assert not [0, 4] in wires
        assert not [4] in wires

        assert (0, 3) in wires

    def test_add_two_wires_objects(self):
        """Tests that wires objects add correctly."""
        wires1 = Wires([4, 0, 1])
        wires2 = Wires([1, 2])
        assert wires1 + wires2 == Wires([4, 0, 1, 2])

    def test_add_wires_object_with_iterable(self):
        """Tests that wires objects add correctly."""
        wires1 = [4, 0, 1]
        wires2 = Wires([1, 2])
        assert wires1 + wires2 == Wires([4, 0, 1, 2])
        assert wires2 + wires1 == Wires([1, 2, 4, 0])

    def test_add_wires_with_inbuilt_sum(self):
        """Tests that wires objects add correctly using sum()."""
        wires1 = [4, 0, 1]
        wires2 = Wires([1, 2])
        assert sum([wires1, wires2], Wires([]))

    def test_representation(self):
        """Tests the string representation."""

        wires_str = str(Wires([1, 2, 3]))
        assert wires_str == "<Wires = [1, 2, 3]>"

    def test_array_representation(self):
        """Tests that Wires object has an array representation."""

        wires = Wires([4, 0, 1])
        array = np.array(wires)
        assert isinstance(array, np.ndarray)
        assert array.shape == (3,)
        for w1, w2 in zip(array, np.array([4, 0, 1])):
            assert w1 == w2

    def test_set_of_wires(self):
        """Tests that a set() of wires is formed correctly."""

        wires = Wires([0, 1, 2])
        list_of_wires = [Wires([1]), Wires([1]), Wires([1, 2, 3]), Wires([4])]

        assert set(wires) == {Wires([0]), Wires([1]), Wires([2])}
        assert set(list_of_wires) == {Wires([1]), Wires([1, 2, 3]), Wires([4])}

    def test_label_property(self):
        """Tests the get_label() method."""

        labels = [0, 'q1', 16]
        wires = Wires(labels)

        assert wires.labels == tuple(labels)
        assert wires.labels[1] == 'q1'
        assert wires.labels[2] == 16

    def test_convert_to_numpy_array(self):
        """Tests that Wires object can be converted to a numpy array."""

        wires = Wires([4, 0, 1])
        array = wires.toarray()
        assert isinstance(array, np.ndarray)
        assert array.shape == (3,)
        for w1, w2 in zip(array, np.array([4, 0, 1])):
            assert w1 == w2

    def test_convert_to_list(self):
        """Tests that Wires object can be converted to a list."""

        wires = Wires([4, 0, 1])
        list_ = wires.tolist()
        assert isinstance(list_, list)
        assert list_ == [4, 0, 1]

    @pytest.mark.parametrize("iterable", [[4, 1, 0, 3],
                                          ['a', 'b', 'c']])
    def test_index_method(self, iterable):
        """Tests the ``index()`` method."""

        wires = Wires(iterable)
        element = iterable[1]
        # check for non-Wires inputs
        assert wires.index(element) == 1
        # check for Wires inputs
        assert wires.index(Wires([element])) == 1
        # check that Wires of length >1 produce an error
        with pytest.raises(WireError, match="Can only retrieve index"):
            wires.index(Wires([1, 2]))
        # check that error raised when wire does not exist
        with pytest.raises(WireError, match="Wire with label d not found"):
            wires.index(Wires(['d']))

    def test_indices_method(self):
        """Tests the ``indices()`` method."""

        wires = Wires([4, 0, 1])
        # for Wires inputs
        assert wires.indices(Wires([1, 4])) == [2, 0]
        # for non-Wires inputs
        assert wires.indices([1, 4]) == [2, 0]
        # for integer
        assert wires.indices(1) == [2]

    @pytest.mark.parametrize("wires, wire_map, expected", [(Wires(['a', 'b']),
                                                            {Wires('a'): Wires(0), Wires('b'): Wires(1)},
                                                            Wires([0, 1])),
                                                           (Wires([-1, 1]),
                                                            {Wires(1): Wires('c'), Wires(-1): Wires(1), Wires('d'): Wires('e')},
                                                            Wires([1, 'c'])),
                                                           ])
    def test_map_method(self, wires, wire_map, expected):
        """Tests the ``map()`` method."""

        assert wires.map(wire_map) == expected

        # error when labels not in wire_map dictionary
        with pytest.raises(WireError, match="No mapping for wire label"):
            wires.map({Wires(-1): Wires(4)}) == expected

        # error for non-unique wire labels
        with pytest.raises(WireError, match="Failed to implement wire map"):
            wires = Wires([0, 1])
            wires.map({Wires(0): Wires('a'), Wires(1): Wires('a')})

    def test_select_random_method(self):
        """Tests the ``select_random()`` method."""

        wires = Wires([4, 0, 1, 5, 6])

        assert len(wires.select_random(2)) == 2
        # check that seed makes call deterministic
        assert wires.select_random(4, seed=1) == wires.select_random(4, seed=1)

        with pytest.raises(WireError, match="Cannot sample"):
            wires.select_random(6)

    def test_subset_method(self):
        """Tests the ``subset()`` method."""

        wires = Wires([4, 0, 1, 5, 6])

        assert wires.subset([2, 3, 0]) == Wires([1, 5, 4])
        assert wires.subset(1) == Wires([0])
        assert wires.subset([4, 5, 7], periodic_boundary=True) == Wires([6, 4, 1])
        # if index does not exist
        with pytest.raises(WireError, match="Cannot subset wire at index"):
            wires.subset([10])

    def test_all_wires_method(self):
        """Tests the ``all_wires()`` method."""

        wires1 = Wires([2, 1, 3])
        wires2 = Wires([1, 4, 5, 2])
        wires3 = Wires([6, 5])

        new_wires = Wires.all_wires([wires1, wires2, wires3])
        assert new_wires.labels == (2, 1, 3, 4, 5, 6)

        new_wires = Wires.all_wires([wires1, wires2, wires3], sort=True)
        assert new_wires.labels == (1, 2, 3, 4, 5, 6)

        with pytest.raises(WireError, match="Expected a Wires object"):
            Wires.all_wires([[3, 4], [8, 5]])

    def test_shared_wires_method(self):
        """Tests the ``shared_wires()`` method."""

        wires1 = Wires([4, 0, 1])
        wires2 = Wires([3, 0, 4])
        wires3 = Wires([4, 0])
        res = Wires.shared_wires([wires1, wires2, wires3])
        assert res == Wires([4, 0])

        res = Wires.shared_wires([wires2, wires1, wires3])
        assert res == Wires([0, 4])

        with pytest.raises(WireError, match="Expected a Wires object"):
            Wires.shared_wires([[3, 4], [8, 5]])

    def test_unique_wires_method(self):
        """Tests the ``unique_wires()`` method."""

        wires1 = Wires([4, 0, 1])
        wires2 = Wires([3, 0, 4])
        wires3 = Wires([4, 0])
        res = Wires.unique_wires([wires1, wires2, wires3])
        assert res == Wires([1, 3])

        res = Wires.unique_wires([wires2, wires1, wires3])
        assert res == Wires([3, 1])

        with pytest.raises(WireError, match="Expected a Wires object"):
            Wires.unique_wires([[2, 1], [8, 5]])