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)
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_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]))
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_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_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_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 control_wires(self): return Wires(self.wires[:2])
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)))
def _controlled(self, wire): CZ(wires=Wires(wire) + self.wires)
def _controlled(self, wire): Toffoli(wires=Wires(wire) + self.wires)
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)
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)
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,)
def test_length(self, iterable): """Tests that a Wires object returns the correct length.""" wires = Wires(iterable) assert len(wires) == len(iterable)
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)))
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_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)))
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_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)))
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
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])
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)
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
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
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
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 __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
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)
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]])