def test_PauliRot_decomposition_XY(self): """Test that the decomposition for a XY rotation is correct.""" theta = 0.4 op = qml.PauliRot(theta, "XY", wires=[0, 1]) decomp_ops = op.decomposition(theta, "XY", wires=[0, 1]) assert len(decomp_ops) == 5 assert decomp_ops[0].name == "Hadamard" assert decomp_ops[0].wires == [0] assert decomp_ops[1].name == "RX" assert decomp_ops[1].wires == [1] assert decomp_ops[1].params[0] == np.pi / 2 assert decomp_ops[2].name == "MultiRZ" assert decomp_ops[2].wires == [0, 1] assert decomp_ops[2].params[0] == theta assert decomp_ops[3].name == "Hadamard" assert decomp_ops[3].wires == [0] assert decomp_ops[4].name == "RX" assert decomp_ops[4].wires == [1] assert decomp_ops[4].params[0] == -np.pi / 2
def expand(self): with qml.tape.QuantumTape() as tape: for i, pauli_word in enumerate(_state_preparation_pauli_words(len(self.wires))): qml.PauliRot(self.parameters[0][i], pauli_word, wires=self.wires) return tape
def ArbitraryUnitary(weights, wires): """Implements an arbitrary unitary on the specified wires. An arbitrary unitary on :math:`n` wires is parametrized by :math:`4^n - 1` independent real parameters. This templates uses Pauli word rotations to parametrize the unitary. **Example** ArbitraryUnitary can be used as a building block, e.g. to parametrize arbitrary two-qubit operations in a circuit: .. code-block:: python @qml.template def arbitrary_nearest_neighbour_interaction(weights, wires): qml.broadcast(unitary=ArbitraryUnitary, pattern="double", wires=wires, params=weights) Args: weights (tensor_like): The angles of the Pauli word rotations, needs to have length :math:`4^n - 1` where :math:`n` is the number of wires the template acts upon. wires (Iterable or Wires): Wires that the template acts on. Accepts an iterable of numbers or strings, or a Wires object. """ wires = Wires(wires) _preprocess(weights, wires) for i, pauli_word in enumerate(_all_pauli_words_but_identity(len(wires))): qml.PauliRot(weights[i], pauli_word, wires=wires)
def ArbitraryUnitary(weights, wires): """Implements an arbitrary unitary on the specified wires. An arbitrary unitary on :math:`n` wires is parametrized by :math:`4^n - 1` independent real parameters. This templates uses Pauli word rotations to parametrize the unitary. **Example** ArbitraryUnitary can be used as a building block, e.g. to parametrize arbitrary two-qubit operations in a circuit: .. code-block:: python @qml.template def arbitrary_nearest_neighbour_interaction(weights, wires): qml.broadcast(unitary=ArbitraryUnitary, pattern="double", wires=wires, params=weights) Args: weights (array[float]): The angles of the Pauli word rotations, needs to have length :math:`4^n - 1` where :math:`n` is the number of wires the template acts upon. wires (List[int]): The wires on which the arbitrary unitary acts. """ wires = check_wires(wires) n_wires = len(wires) expected_shape = (4 ** n_wires - 1,) check_shape( weights, expected_shape, msg="'weights' must be of shape {}; got {}." "".format(expected_shape, get_shape(weights)), ) for i, pauli_word in enumerate(_all_pauli_words_but_identity(len(wires))): qml.PauliRot(weights[i], pauli_word, wires=wires)
def test_PauliRot_decomposition_XIYZ(self): """Test that the decomposition for a XIYZ rotation is correct.""" theta = 0.4 op = qml.PauliRot(theta, "XIYZ", wires=[0, 1, 2, 3]) decomp_ops = op.decomposition(theta, "XIYZ", wires=[0, 1, 2, 3]) assert len(decomp_ops) == 5 assert decomp_ops[0].name == "Hadamard" assert decomp_ops[0].wires == Wires([0]) assert decomp_ops[1].name == "RX" assert decomp_ops[1].wires == Wires([2]) assert decomp_ops[1].data[0] == np.pi / 2 assert decomp_ops[2].name == "MultiRZ" assert decomp_ops[2].wires == Wires([0, 2, 3]) assert decomp_ops[2].data[0] == theta assert decomp_ops[3].name == "Hadamard" assert decomp_ops[3].wires == Wires([0]) assert decomp_ops[4].name == "RX" assert decomp_ops[4].wires == Wires([2]) assert decomp_ops[4].data[0] == -np.pi / 2
def test_multirz_generator(self, pauli_word): """Test that the generator of the MultiRZ gate is correct.""" op = qml.PauliRot(0.3, pauli_word, wires=range(len(pauli_word))) gen = op.generator if pauli_word[0] == 'I': # this is the identity expected_gen = qml.Identity(wires=0) else: expected_gen = getattr( qml, 'Pauli{}'.format(pauli_word[0]))(wires=0) for i, pauli in enumerate(pauli_word[1:]): i += 1 if pauli == 'I': expected_gen = expected_gen @ qml.Identity( wires=i) else: expected_gen = expected_gen @ getattr( qml, 'Pauli{}'.format(pauli))(wires=i) expected_gen_mat = expected_gen.matrix assert np.allclose(gen[0], expected_gen_mat) assert gen[1] == -0.5
def ArbitraryStatePreparation(weights, wires): """Implements an arbitrary state preparation on the specified wires. An arbitrary state on :math:`n` wires is parametrized by :math:`2^{n+1} - 2` independent real parameters. This templates uses Pauli word rotations to parametrize the unitary. **Example** ArbitraryStatePreparation can be used to train state preparations, for example using a circuit with some measurement observable ``H``: .. code-block:: python dev = qml.device("default.qubit", wires=4) @qml.qnode(dev) def vqe(weights): qml.ArbitraryStatePreparations(weights, wires=[0, 1, 2, 3]) return qml.expval(qml.Hermitian(H, wires=[0, 1, 2, 3])) Args: weights (tensor_like): The angles of the Pauli word rotations, needs to have length :math:`2^(n+1) - 2` where :math:`n` is the number of wires the template acts upon. wires (Iterable or Wires): Wires that the template acts on. Accepts an iterable of numbers or strings, or a Wires object. """ wires = Wires(wires) _preprocess(weights, wires) for i, pauli_word in enumerate(_state_preparation_pauli_words(len(wires))): qml.PauliRot(weights[i], pauli_word, wires=wires)
def algebra_commutator(tape, observables, lie_algebra_basis_names, nqubits): """Calculate the Riemannian gradient in the Lie algebra with the parameter shift rule (see :meth:`LieAlgebraOptimizer.get_omegas`). Args: tape (.QuantumTape or .QNode): input circuit observables (list[.Observable]): list of observables to be measured. Can be grouped. lie_algebra_basis_names (list[str]): List of strings corresponding to valid Pauli words. nqubits (int): the number of qubits. Returns: func: Function which accepts the same arguments as the QNode. When called, this function will return the Lie algebra commutator. """ tapes_plus_total = [] tapes_min_total = [] for obs in observables: for o in obs: # create a list of tapes for the plus and minus shifted circuits tapes_plus = [qml.tape.JacobianTape(p + "_p") for p in lie_algebra_basis_names] tapes_min = [qml.tape.JacobianTape(p + "_m") for p in lie_algebra_basis_names] # loop through all operations on the input tape for op in tape.operations: for t in tapes_plus + tapes_min: with t: qml.apply(op) for i, t in enumerate(tapes_plus): with t: qml.PauliRot( np.pi / 2, lie_algebra_basis_names[i], wires=list(range(nqubits)), ) qml.expval(o) for i, t in enumerate(tapes_min): with t: qml.PauliRot( -np.pi / 2, lie_algebra_basis_names[i], wires=list(range(nqubits)), ) qml.expval(o) tapes_plus_total.extend(tapes_plus) tapes_min_total.extend(tapes_min) return tapes_plus_total + tapes_min_total, None
def test_PauliRot_decomposition_Identity(self): """Test that decomposing the all-identity Pauli has no effect.""" theta = 0.4 op = qml.PauliRot(theta, "II", wires=[0, 1]) decomp_ops = op.decomposition(theta, "II", wires=[0, 1]) assert len(decomp_ops) == 0
def test_init_incorrect_pauli_word_length_error(self, pauli_word, wires): """Test that __init__ throws an error if a Pauli word of wrong length is supplied.""" with pytest.raises( ValueError, match="The given Pauli word has length .*, length .* was expected for wires .*", ): qml.PauliRot(0.3, pauli_word, wires=wires)
def qfunc(): qml.RZ(0.1, wires=0) qml.Hadamard(wires=0) # No rotation angles specified for PauliRot since it is a gate that # in principle acts on an arbitrary number of wires. qml.PauliRot(0.2, "X", wires=0) qml.RZ(0.1, wires=0) qml.Hadamard(wires=0)
def test_init_incorrect_pauli_word_error(self): """Test that __init__ throws an error if a wrong Pauli word is supplied.""" with pytest.raises( ValueError, match='The given Pauli word ".*" contains characters that are not allowed.' " Allowed characters are I, X, Y and Z", ): qml.PauliRot(0.3, "IXYZV", wires=[0, 1, 2, 3, 4])
def test_PauliRot_all_Identity(self): """Test handling of the all-identity Pauli.""" theta = 0.4 op = qml.PauliRot(theta, "II", wires=[0, 1]) decomp_ops = op.decomposition(theta, "II", wires=[0, 1]) assert np.allclose(op.eigvals, np.exp(-1j * theta / 2) * np.ones(4)) assert np.allclose(op.matrix / op.matrix[0, 0], np.eye(4)) assert len(decomp_ops) == 0
def test_PauliRot_decomposition_ZZ(self): """Test that the decomposition for a ZZ rotation is correct.""" theta = 0.4 op = qml.PauliRot(theta, "ZZ", wires=[0, 1]) decomp_ops = op.decomposition(theta, "ZZ", wires=[0, 1]) assert len(decomp_ops) == 1 assert decomp_ops[0].name == "MultiRZ" assert decomp_ops[0].wires == [0, 1] assert decomp_ops[0].params[0] == theta
def test_do_not_expand(self): """Test that a tape with single-parameter operations with unitary generators and non-parametric operations is not touched.""" with qml.tape.JacobianTape() as tape: qml.RX(0.2, wires=0) qml.Hadamard(0) qml.PauliRot(0.9, "XY", wires=[0, 1]) qml.SingleExcitationPlus(-1.2, wires=[1, 0]) new_tape = qml.transforms.expand_nonunitary_gen(tape) assert tape.operations == new_tape.operations
def op(op_name): ops_list = { "RX": qml.RX(0.123, wires=0), "RY": qml.RY(1.434, wires=0), "RZ": qml.RZ(2.774, wires=0), "S": qml.S(wires=0), "SX": qml.SX(wires=0), "T": qml.T(wires=0), "CNOT": qml.CNOT(wires=[0, 1]), "CZ": qml.CZ(wires=[0, 1]), "CY": qml.CY(wires=[0, 1]), "SWAP": qml.SWAP(wires=[0, 1]), "ISWAP": qml.ISWAP(wires=[0, 1]), "SISWAP": qml.SISWAP(wires=[0, 1]), "SQISW": qml.SQISW(wires=[0, 1]), "CSWAP": qml.CSWAP(wires=[0, 1, 2]), "PauliRot": qml.PauliRot(0.123, "Y", wires=0), "IsingXX": qml.IsingXX(0.123, wires=[0, 1]), "IsingXY": qml.IsingXY(0.123, wires=[0, 1]), "IsingYY": qml.IsingYY(0.123, wires=[0, 1]), "IsingZZ": qml.IsingZZ(0.123, wires=[0, 1]), "Identity": qml.Identity(wires=0), "Rot": qml.Rot(0.123, 0.456, 0.789, wires=0), "Toffoli": qml.Toffoli(wires=[0, 1, 2]), "PhaseShift": qml.PhaseShift(2.133, wires=0), "ControlledPhaseShift": qml.ControlledPhaseShift(1.777, wires=[0, 2]), "CPhase": qml.CPhase(1.777, wires=[0, 2]), "MultiRZ": qml.MultiRZ(0.112, wires=[1, 2, 3]), "CRX": qml.CRX(0.836, wires=[2, 3]), "CRY": qml.CRY(0.721, wires=[2, 3]), "CRZ": qml.CRZ(0.554, wires=[2, 3]), "Hadamard": qml.Hadamard(wires=0), "PauliX": qml.PauliX(wires=0), "PauliY": qml.PauliY(wires=0), "PauliZ": qml.PauliZ(wires=0), "CRot": qml.CRot(0.123, 0.456, 0.789, wires=[0, 1]), "DiagonalQubitUnitary": qml.DiagonalQubitUnitary(np.array([1.0, 1.0j]), wires=1), "ControlledQubitUnitary": qml.ControlledQubitUnitary( np.eye(2) * 1j, wires=[0], control_wires=[2] ), "MultiControlledX": qml.MultiControlledX(wires=(0, 1, 2), control_values="01"), "SingleExcitation": qml.SingleExcitation(0.123, wires=[0, 3]), "SingleExcitationPlus": qml.SingleExcitationPlus(0.123, wires=[0, 3]), "SingleExcitationMinus": qml.SingleExcitationMinus(0.123, wires=[0, 3]), "DoubleExcitation": qml.DoubleExcitation(0.123, wires=[0, 1, 2, 3]), "DoubleExcitationPlus": qml.DoubleExcitationPlus(0.123, wires=[0, 1, 2, 3]), "DoubleExcitationMinus": qml.DoubleExcitationMinus(0.123, wires=[0, 1, 2, 3]), "QFT": qml.QFT(wires=0), "QubitSum": qml.QubitSum(wires=[0, 1, 2]), "QubitCarry": qml.QubitCarry(wires=[0, 1, 2, 3]), "QubitUnitary": qml.QubitUnitary(np.eye(2) * 1j, wires=0), } return ops_list.get(op_name)
def circuit_decomposed(weights): qml.PauliRot(weights[0], "XI", wires=[0, 1]) qml.PauliRot(weights[1], "YI", wires=[0, 1]) qml.PauliRot(weights[2], "IX", wires=[0, 1]) qml.PauliRot(weights[3], "IY", wires=[0, 1]) qml.PauliRot(weights[4], "XX", wires=[0, 1]) qml.PauliRot(weights[5], "XY", wires=[0, 1]) return qml.expval(qml.PauliZ(0))
def test_PauliRot_wire_as_int(self): """Test that passing a single wire as an integer works.""" theta = 0.4 op = qml.PauliRot(theta, "Z", wires=0) decomp_ops = op.decomposition(theta, "Z", wires=0) assert np.allclose(op.eigvals, np.array([np.exp(-1j * theta / 2), np.exp(1j * theta / 2)])) assert np.allclose(op.matrix, np.diag([np.exp(-1j * theta / 2), np.exp(1j * theta / 2)])) assert len(decomp_ops) == 1 assert decomp_ops[0].name == "MultiRZ" assert decomp_ops[0].wires == Wires([0]) assert decomp_ops[0].data[0] == theta
def ArbitraryStatePreparation(weights, wires): """Implements an arbitrary state preparation on the specified wires. An arbitrary state on :math:`n` wires is parametrized by :math:`2^{n+1} - 2` independent real parameters. This templates uses Pauli word rotations to parametrize the unitary. **Example** ArbitraryStatePreparation can be used to train state preparations, for example using a circuit with some measurement observable ``H``: .. code-block:: python dev = qml.device("default.qubit", wires=4) @qml.qnode(dev) def vqe(weights): qml.ArbitraryStatePreparations(weights, wires=[0, 1, 2, 3]) return qml.expval(qml.Hermitian(H, wires=[0, 1, 2, 3])) Args: weights (array[float]): The angles of the Pauli word rotations, needs to have length :math:`2^(n+1) - 2` where :math:`n` is the number of wires the template acts upon. wires (Iterable or Wires): Wires that the template acts on. Accepts an iterable of numbers or strings, or a Wires object. """ wires = Wires(wires) n_wires = len(wires) expected_shape = (2**(n_wires + 1) - 2, ) check_shape( weights, expected_shape, msg="'weights' must be of shape {}; got {}." "".format(expected_shape, get_shape(weights)), ) wires = wires.tolist() # Todo: remove when ops take Wires object for i, pauli_word in enumerate(_state_preparation_pauli_words(len(wires))): qml.PauliRot(weights[i], pauli_word, wires=wires)
class TestApproxTimeEvolution: """Tests for the ApproxTimeEvolution template from the pennylane.templates.subroutine module.""" def test_hamiltonian_error(self): """Tests if the correct error is thrown when hamiltonian is not a pennylane.Hamiltonian object""" n_wires = 2 dev = qml.device("default.qubit", wires=n_wires) hamiltonian = np.array([[1, 1], [1, 1]]) @qml.qnode(dev) def circuit(): ApproxTimeEvolution(hamiltonian, 2, 3) return [qml.expval(qml.PauliZ(wires=i)) for i in range(n_wires)] with pytest.raises(ValueError, match="hamiltonian must be of type pennylane.Hamiltonian"): circuit() def test_n_error(self): """Tests if the correct error is thrown when n is not an integer""" n_wires = 2 dev = qml.device("default.qubit", wires=n_wires) hamiltonian = qml.Hamiltonian([1, 1], [qml.PauliX(0), qml.PauliX(1)]) n = 1.37 @qml.qnode(dev) def circuit(): ApproxTimeEvolution(hamiltonian, 2, n) return [qml.expval(qml.PauliZ(wires=i)) for i in range(n_wires)] with pytest.raises(ValueError, match="n must be of type int"): circuit() @pytest.mark.parametrize( ("hamiltonian", "output"), [ (qml.Hamiltonian([1, 1], [qml.PauliX(0), qml.Hadamard(0)]), "Hadamard"), ( qml.Hamiltonian( [1, 1], [qml.PauliX(0) @ qml.Hermitian(np.array([[1, 1], [1, 1]]), 1), qml.PauliX(0)], ), "Hermitian", ), ], ) def test_non_pauli_error(self, hamiltonian, output): """Tests if the correct errors are thrown when the user attempts to input a matrix with non-Pauli terms""" n_wires = 2 dev = qml.device("default.qubit", wires=n_wires) @qml.qnode(dev) def circuit(): ApproxTimeEvolution(hamiltonian, 2, 3) return [qml.expval(qml.PauliZ(wires=i)) for i in range(n_wires)] with pytest.raises( ValueError, match="hamiltonian must be written in terms of Pauli matrices" ): circuit() @pytest.mark.parametrize( ("time", "hamiltonian", "steps", "gates"), [ ( 2, qml.Hamiltonian([1, 1], [qml.PauliX(0), qml.PauliX(1)]), 2, [ qml.PauliRot(2.0, "X", wires=[0]), qml.PauliRot(2.0, "X", wires=[1]), qml.PauliRot(2.0, "X", wires=[0]), qml.PauliRot(2.0, "X", wires=[1]), ], ), ( 2, qml.Hamiltonian([2, 0.5], [qml.PauliX("a"), qml.PauliZ("b") @ qml.PauliX("a")]), 2, [ qml.PauliRot(4.0, "X", wires=["a"]), qml.PauliRot(1.0, "ZX", wires=["b", "a"]), qml.PauliRot(4.0, "X", wires=["a"]), qml.PauliRot(1.0, "ZX", wires=["b", "a"]), ], ), ( 2, qml.Hamiltonian([1, 1], [qml.PauliX(0), qml.Identity(0) @ qml.Identity(1)]), 2, [qml.PauliRot(2.0, "X", wires=[0]), qml.PauliRot(2.0, "X", wires=[0])], ), ( 2, qml.Hamiltonian( [2, 0.5, 0.5], [ qml.PauliX("a"), qml.PauliZ(-15) @ qml.PauliX("a"), qml.Identity(0) @ qml.PauliY(-15), ], ), 1, [ qml.PauliRot(8.0, "X", wires=["a"]), qml.PauliRot(2.0, "ZX", wires=[-15, "a"]), qml.PauliRot(2.0, "IY", wires=[0, -15]), ], ), ], ) def test_evolution_operations(self, time, hamiltonian, steps, gates): """Tests that the sequence of gates implemented in the ApproxTimeEvolution template is correct""" n_wires = 2 with qml._queuing.OperationRecorder() as rec: ApproxTimeEvolution(hamiltonian, time, steps) for i, gate in enumerate(rec.operations): prep = [gate.parameters, gate.wires] target = [gates[i].parameters, gates[i].wires] assert prep == target @pytest.mark.parametrize( ("time", "hamiltonian", "steps", "expectation"), [ (np.pi, qml.Hamiltonian([1, 1], [qml.PauliX(0), qml.PauliX(1)]), 2, [1.0, 1.0]), ( np.pi / 2, qml.Hamiltonian([0.5, 1], [qml.PauliY(0), qml.Identity(0) @ qml.PauliX(1)]), 1, [0.0, -1.0], ), ( np.pi / 4, qml.Hamiltonian( [1, 1, 1], [qml.PauliX(0), qml.PauliZ(0) @ qml.PauliZ(1), qml.PauliX(1)] ), 1, [0.0, 0.0], ), ( 1, qml.Hamiltonian([1, 1], [qml.PauliX(0), qml.PauliX(1)]), 2, [-0.41614684, -0.41614684], ), ( 2, qml.Hamiltonian( [1, 1, 1, 1], [qml.PauliX(0), qml.PauliY(0), qml.PauliZ(0) @ qml.PauliZ(1), qml.PauliY(1)], ), 2, [-0.87801124, 0.51725747], ), ], ) def test_evolution_output(self, time, hamiltonian, steps, expectation): """Tests that the output from the ApproxTimeEvolution template is correct""" n_wires = 2 dev = qml.device("default.qubit", wires=n_wires) @qml.qnode(dev) def circuit(): ApproxTimeEvolution(hamiltonian, time, steps) return [qml.expval(qml.PauliZ(wires=i)) for i in range(n_wires)] assert np.allclose(circuit(), expectation)
class TestLayers: """Tests that the cost and mixer layers are being constructed properly""" def test_mixer_layer_errors(self): """Tests that the mixer layer is throwing the correct errors""" hamiltonian = [[1, 1], [1, 1]] with pytest.raises( ValueError, match=r"hamiltonian must be of type pennylane.Hamiltonian"): qaoa.mixer_layer(0.1, hamiltonian) def test_cost_layer_errors(self): """Tests that the cost layer is throwing the correct errors""" hamiltonian = [[1, 1], [1, 1]] with pytest.raises( ValueError, match=r"hamiltonian must be of type pennylane.Hamiltonian"): qaoa.cost_layer(0.1, hamiltonian) hamiltonian = qml.Hamiltonian([1, 1], [qml.PauliZ(0), qml.PauliX(1)]) with pytest.raises( ValueError, match= r"hamiltonian must be written only in terms of PauliZ and Identity gates" ): qaoa.cost_layer(0.1, hamiltonian) @pytest.mark.parametrize( ("mixer", "gates"), [[ qml.Hamiltonian([1, 1], [qml.PauliX(0), qml.PauliX(1)]), [qml.PauliRot(2, "X", wires=[0]), qml.PauliRot(2, "X", wires=[1])] ], [ qaoa.xy_mixer(Graph([(0, 1), (1, 2), (2, 0)])), [ qml.PauliRot(1, "XX", wires=[0, 1]), qml.PauliRot(1, "YY", wires=[0, 1]), qml.PauliRot(1, "XX", wires=[0, 2]), qml.PauliRot(1, "YY", wires=[0, 2]), qml.PauliRot(1, "XX", wires=[1, 2]), qml.PauliRot(1, "YY", wires=[1, 2]) ] ]]) def test_mixer_layer_output(self, mixer, gates): """Tests that the gates of the mixer layer are correct""" alpha = 1 with qml._queuing.OperationRecorder() as rec: qaoa.mixer_layer(alpha, mixer) for i, j in zip(rec.operations, gates): prep = [i.name, i.parameters, i.wires] target = [j.name, j.parameters, j.wires] assert prep == target @pytest.mark.parametrize( ("cost", "gates"), [[ qml.Hamiltonian([1, 1], [qml.PauliZ(0), qml.PauliZ(1)]), [qml.PauliRot(2, "Z", wires=[0]), qml.PauliRot(2, "Z", wires=[1])] ], [ qaoa.maxcut(Graph([(0, 1), (1, 2), (2, 0)]))[0], [ qml.PauliRot(1, "ZZ", wires=[0, 1]), qml.PauliRot(1, "ZZ", wires=[0, 2]), qml.PauliRot(1, "ZZ", wires=[1, 2]) ] ]]) def test_cost_layer_output(self, cost, gates): """Tests that the gates of the cost layer is correct""" gamma = 1 with qml._queuing.OperationRecorder() as rec: qaoa.cost_layer(gamma, cost) for i, j in zip(rec.operations, gates): prep = [i.name, i.parameters, i.wires] target = [j.name, j.parameters, j.wires] assert prep == target
def circuit(theta): qml.PauliRot(theta, "XX", wires=[0, 1]) return qml.expval(qml.PauliZ(0))
"T": qml.T(wires=[0]), "SX": qml.SX(wires=[0]), "Toffoli": qml.Toffoli(wires=[0, 1, 2]), "QFT": qml.templates.QFT(wires=[0, 1, 2]), "IsingXX": qml.IsingXX(0, wires=[0, 1]), "IsingYY": qml.IsingYY(0, wires=[0, 1]), "IsingZZ": qml.IsingZZ(0, wires=[0, 1]), "SingleExcitation": qml.SingleExcitation(0, wires=[0, 1]), "SingleExcitationPlus": qml.SingleExcitationPlus(0, wires=[0, 1]), "SingleExcitationMinus": qml.SingleExcitationMinus(0, wires=[0, 1]), "DoubleExcitation": qml.DoubleExcitation(0, wires=[0, 1, 2, 3]), "DoubleExcitationPlus": qml.DoubleExcitationPlus(0, wires=[0, 1, 2, 3]), "DoubleExcitationMinus": qml.DoubleExcitationMinus(0, wires=[0, 1, 2, 3]), "QubitCarry": qml.QubitCarry(wires=[0, 1, 2, 3]), "QubitSum": qml.QubitSum(wires=[0, 1, 2]), "PauliRot": qml.PauliRot(0, "XXYY", wires=[0, 1, 2, 3]), "U1": qml.U1(0, wires=0), "U2": qml.U2(0, 0, wires=0), "U3": qml.U3(0, 0, 0, wires=0), "SISWAP": qml.SISWAP(wires=[0, 1]), } all_ops = ops.keys() # All qubit operations should be available to test in the device test suite all_available_ops = qml.ops._qubit__ops__.copy() # pylint: disable=protected-access all_available_ops.remove("CPhase") # CPhase is an alias of ControlledPhaseShift all_available_ops.remove("SQISW") # SQISW is an alias of SISWAP all_available_ops.add("QFT") # QFT was recently moved to being a template, but let's keep it here if not set(all_ops) == all_available_ops:
class TestRepresentationResolver: """Test the RepresentationResolver class.""" @pytest.mark.parametrize( "list,element,index,list_after", [ ([1, 2, 3], 2, 1, [1, 2, 3]), ([1, 2, 2, 3], 2, 1, [1, 2, 2, 3]), ([1, 2, 3], 4, 3, [1, 2, 3, 4]), ], ) def test_index_of_array_or_append(self, list, element, index, list_after): """Test the method index_of_array_or_append.""" assert RepresentationResolver.index_of_array_or_append(element, list) == index assert list == list_after @pytest.mark.parametrize( "par,expected", [ (3, "3"), (5.236422, "5.24"), ], ) def test_single_parameter_representation(self, unicode_representation_resolver, par, expected): """Test that single parameters are properly resolved.""" assert unicode_representation_resolver.single_parameter_representation( par) == expected @pytest.mark.parametrize( "op,wire,target", [ (qml.PauliX(wires=[1]), 1, "X"), (qml.CNOT(wires=[0, 1]), 1, "X"), (qml.CNOT(wires=[0, 1]), 0, "C"), (qml.Toffoli(wires=[0, 2, 1]), 1, "X"), (qml.Toffoli(wires=[0, 2, 1]), 0, "C"), (qml.Toffoli(wires=[0, 2, 1]), 2, "C"), (qml.CSWAP(wires=[0, 2, 1]), 1, "SWAP"), (qml.CSWAP(wires=[0, 2, 1]), 2, "SWAP"), (qml.CSWAP(wires=[0, 2, 1]), 0, "C"), (qml.PauliY(wires=[1]), 1, "Y"), (qml.PauliZ(wires=[1]), 1, "Z"), (qml.CZ(wires=[0, 1]), 1, "Z"), (qml.CZ(wires=[0, 1]), 0, "C"), (qml.Identity(wires=[1]), 1, "I"), (qml.Hadamard(wires=[1]), 1, "H"), (qml.PauliRot(3.14, "XX", wires=[0, 1]), 1, "RX(3.14)"), (qml.PauliRot(3.14, "YZ", wires=[0, 1]), 1, "RZ(3.14)"), (qml.PauliRot(3.14, "IXYZI", wires=[0, 1, 2, 3, 4 ]), 0, "RI(3.14)"), (qml.PauliRot(3.14, "IXYZI", wires=[0, 1, 2, 3, 4 ]), 1, "RX(3.14)"), (qml.PauliRot(3.14, "IXYZI", wires=[0, 1, 2, 3, 4 ]), 2, "RY(3.14)"), (qml.PauliRot(3.14, "IXYZI", wires=[0, 1, 2, 3, 4 ]), 3, "RZ(3.14)"), (qml.PauliRot(3.14, "IXYZI", wires=[0, 1, 2, 3, 4 ]), 4, "RI(3.14)"), (qml.MultiRZ(3.14, wires=[0, 1]), 0, "RZ(3.14)"), (qml.MultiRZ(3.14, wires=[0, 1]), 1, "RZ(3.14)"), (qml.CRX(3.14, wires=[0, 1]), 1, "RX(3.14)"), (qml.CRX(3.14, wires=[0, 1]), 0, "C"), (qml.CRY(3.14, wires=[0, 1]), 1, "RY(3.14)"), (qml.CRY(3.14, wires=[0, 1]), 0, "C"), (qml.CRZ(3.14, wires=[0, 1]), 1, "RZ(3.14)"), (qml.CRZ(3.14, wires=[0, 1]), 0, "C"), (qml.CRot(3.14, 2.14, 1.14, wires=[0, 1 ]), 1, "Rot(3.14, 2.14, 1.14)"), (qml.CRot(3.14, 2.14, 1.14, wires=[0, 1]), 0, "C"), (qml.PhaseShift(3.14, wires=[0]), 0, "Rϕ(3.14)"), (qml.Beamsplitter(1, 2, wires=[0, 1]), 1, "BS(1, 2)"), (qml.Beamsplitter(1, 2, wires=[0, 1]), 0, "BS(1, 2)"), (qml.Squeezing(1, 2, wires=[1]), 1, "S(1, 2)"), (qml.TwoModeSqueezing(1, 2, wires=[0, 1]), 1, "S(1, 2)"), (qml.TwoModeSqueezing(1, 2, wires=[0, 1]), 0, "S(1, 2)"), (qml.Displacement(1, 2, wires=[1]), 1, "D(1, 2)"), (qml.NumberOperator(wires=[1]), 1, "n"), (qml.Rotation(3.14, wires=[1]), 1, "R(3.14)"), (qml.ControlledAddition(3.14, wires=[0, 1]), 1, "X(3.14)"), (qml.ControlledAddition(3.14, wires=[0, 1]), 0, "C"), (qml.ControlledPhase(3.14, wires=[0, 1]), 1, "Z(3.14)"), (qml.ControlledPhase(3.14, wires=[0, 1]), 0, "C"), (qml.ThermalState(3, wires=[1]), 1, "Thermal(3)"), ( qml.GaussianState(np.array([[2, 0], [0, 2]]), np.array([1, 2]), wires=[1]), 1, "Gaussian(M0,M1)", ), (qml.QuadraticPhase(3.14, wires=[1]), 1, "P(3.14)"), (qml.RX(3.14, wires=[1]), 1, "RX(3.14)"), (qml.S(wires=[2]), 2, "S"), (qml.T(wires=[2]), 2, "T"), (qml.RX(3.14, wires=[1]), 1, "RX(3.14)"), (qml.RY(3.14, wires=[1]), 1, "RY(3.14)"), (qml.RZ(3.14, wires=[1]), 1, "RZ(3.14)"), (qml.Rot(3.14, 2.14, 1.14, wires=[1]), 1, "Rot(3.14, 2.14, 1.14)"), (qml.U1(3.14, wires=[1]), 1, "U1(3.14)"), (qml.U2(3.14, 2.14, wires=[1]), 1, "U2(3.14, 2.14)"), (qml.U3(3.14, 2.14, 1.14, wires=[1]), 1, "U3(3.14, 2.14, 1.14)"), (qml.BasisState(np.array([0, 1, 0]), wires=[1, 2, 3]), 1, "|0⟩"), (qml.BasisState(np.array([0, 1, 0]), wires=[1, 2, 3]), 2, "|1⟩"), (qml.BasisState(np.array([0, 1, 0]), wires=[1, 2, 3]), 3, "|0⟩"), (qml.QubitStateVector(np.array([0, 1, 0, 0]), wires=[1, 2]), 1, "QubitStateVector(M0)"), (qml.QubitStateVector(np.array([0, 1, 0, 0]), wires=[1, 2]), 2, "QubitStateVector(M0)"), (qml.QubitUnitary(np.eye(2), wires=[1]), 1, "U0"), (qml.QubitUnitary(np.eye(4), wires=[1, 2]), 2, "U0"), (qml.Kerr(3.14, wires=[1]), 1, "Kerr(3.14)"), (qml.CrossKerr(3.14, wires=[1, 2]), 1, "CrossKerr(3.14)"), (qml.CrossKerr(3.14, wires=[1, 2]), 2, "CrossKerr(3.14)"), (qml.CubicPhase(3.14, wires=[1]), 1, "V(3.14)"), (qml.InterferometerUnitary( np.eye(4), wires=[1, 3]), 1, "InterferometerUnitary(M0)"), (qml.InterferometerUnitary( np.eye(4), wires=[1, 3]), 3, "InterferometerUnitary(M0)"), (qml.CatState(3.14, 2.14, 1, wires=[1]), 1, "CatState(3.14, 2.14, 1)"), (qml.CoherentState(3.14, 2.14, wires=[1]), 1, "CoherentState(3.14, 2.14)"), ( qml.FockDensityMatrix(np.kron(np.eye(4), np.eye(4)), wires=[1, 2]), 1, "FockDensityMatrix(M0)", ), ( qml.FockDensityMatrix(np.kron(np.eye(4), np.eye(4)), wires=[1, 2]), 2, "FockDensityMatrix(M0)", ), ( qml.DisplacedSqueezedState(3.14, 2.14, 1.14, 0.14, wires=[1]), 1, "DisplacedSqueezedState(3.14, 2.14, 1.14, 0.14)", ), (qml.FockState(7, wires=[1]), 1, "|7⟩"), (qml.FockStateVector(np.array([4, 5, 7]), wires=[1, 2, 3 ]), 1, "|4⟩"), (qml.FockStateVector(np.array([4, 5, 7]), wires=[1, 2, 3 ]), 2, "|5⟩"), (qml.FockStateVector(np.array([4, 5, 7]), wires=[1, 2, 3 ]), 3, "|7⟩"), (qml.SqueezedState(3.14, 2.14, wires=[1]), 1, "SqueezedState(3.14, 2.14)"), (qml.Hermitian(np.eye(4), wires=[1, 2]), 1, "H0"), (qml.Hermitian(np.eye(4), wires=[1, 2]), 2, "H0"), (qml.X(wires=[1]), 1, "x"), (qml.P(wires=[1]), 1, "p"), (qml.FockStateProjector(np.array([4, 5, 7]), wires=[1, 2, 3]), 1, "|4,5,7╳4,5,7|"), ( qml.PolyXP(np.array([1, 2, 0, -1.3, 6]), wires=[1]), 2, "1+2x₀-1.3x₁+6p₁", ), ( qml.PolyXP(np.array([[1.2, 2.3, 4.5], [-1.2, 1.2, -1.5], [-1.3, 4.5, 2.3]]), wires=[1]), 1, "1.2+1.1x₀+3.2p₀+1.2x₀²+2.3p₀²+3x₀p₀", ), ( qml.PolyXP( np.array([ [1.2, 2.3, 4.5, 0, 0], [-1.2, 1.2, -1.5, 0, 0], [-1.3, 4.5, 2.3, 0, 0], [0, 2.6, 0, 0, 0], [0, 0, 0, -4.7, -1.0], ]), wires=[1], ), 1, "1.2+1.1x₀+3.2p₀+1.2x₀²+2.3p₀²+3x₀p₀+2.6x₀x₁-p₁²-4.7x₁p₁", ), (qml.QuadOperator(3.14, wires=[1]), 1, "cos(3.14)x+sin(3.14)p"), (qml.PauliX(wires=[1]).inv(), 1, "X⁻¹"), (qml.CNOT(wires=[0, 1]).inv(), 1, "X⁻¹"), (qml.CNOT(wires=[0, 1]).inv(), 0, "C"), (qml.Toffoli(wires=[0, 2, 1]).inv(), 1, "X⁻¹"), (qml.Toffoli(wires=[0, 2, 1]).inv(), 0, "C"), (qml.Toffoli(wires=[0, 2, 1]).inv(), 2, "C"), (qml.measure.sample(wires=[0, 1]), 0, "basis"), # not providing an observable in (qml.measure.sample(wires=[0, 1]), 1, "basis"), # sample gets displayed as raw (two_wire_quantum_tape(), 0, "QuantumTape:T0"), (two_wire_quantum_tape(), 1, "QuantumTape:T0"), ], ) def test_operator_representation_unicode(self, unicode_representation_resolver, op, wire, target): """Test that an Operator instance is properly resolved.""" assert unicode_representation_resolver.operator_representation( op, wire) == target @pytest.mark.parametrize( "op,wire,target", [ (qml.PauliX(wires=[1]), 1, "X"), (qml.CNOT(wires=[0, 1]), 1, "X"), (qml.CNOT(wires=[0, 1]), 0, "C"), (qml.Toffoli(wires=[0, 2, 1]), 1, "X"), (qml.Toffoli(wires=[0, 2, 1]), 0, "C"), (qml.Toffoli(wires=[0, 2, 1]), 2, "C"), (qml.CSWAP(wires=[0, 2, 1]), 1, "SWAP"), (qml.CSWAP(wires=[0, 2, 1]), 2, "SWAP"), (qml.CSWAP(wires=[0, 2, 1]), 0, "C"), (qml.PauliY(wires=[1]), 1, "Y"), (qml.PauliZ(wires=[1]), 1, "Z"), (qml.CZ(wires=[0, 1]), 1, "Z"), (qml.CZ(wires=[0, 1]), 0, "C"), (qml.Identity(wires=[1]), 1, "I"), (qml.Hadamard(wires=[1]), 1, "H"), (qml.CRX(3.14, wires=[0, 1]), 1, "RX(3.14)"), (qml.CRX(3.14, wires=[0, 1]), 0, "C"), (qml.CRY(3.14, wires=[0, 1]), 1, "RY(3.14)"), (qml.CRY(3.14, wires=[0, 1]), 0, "C"), (qml.CRZ(3.14, wires=[0, 1]), 1, "RZ(3.14)"), (qml.CRZ(3.14, wires=[0, 1]), 0, "C"), (qml.CRot(3.14, 2.14, 1.14, wires=[0, 1 ]), 1, "Rot(3.14, 2.14, 1.14)"), (qml.CRot(3.14, 2.14, 1.14, wires=[0, 1]), 0, "C"), (qml.PhaseShift(3.14, wires=[0]), 0, "Rϕ(3.14)"), (qml.Beamsplitter(1, 2, wires=[0, 1]), 1, "BS(1, 2)"), (qml.Beamsplitter(1, 2, wires=[0, 1]), 0, "BS(1, 2)"), (qml.Squeezing(1, 2, wires=[1]), 1, "S(1, 2)"), (qml.TwoModeSqueezing(1, 2, wires=[0, 1]), 1, "S(1, 2)"), (qml.TwoModeSqueezing(1, 2, wires=[0, 1]), 0, "S(1, 2)"), (qml.Displacement(1, 2, wires=[1]), 1, "D(1, 2)"), (qml.NumberOperator(wires=[1]), 1, "n"), (qml.Rotation(3.14, wires=[1]), 1, "R(3.14)"), (qml.ControlledAddition(3.14, wires=[0, 1]), 1, "X(3.14)"), (qml.ControlledAddition(3.14, wires=[0, 1]), 0, "C"), (qml.ControlledPhase(3.14, wires=[0, 1]), 1, "Z(3.14)"), (qml.ControlledPhase(3.14, wires=[0, 1]), 0, "C"), (qml.ThermalState(3, wires=[1]), 1, "Thermal(3)"), ( qml.GaussianState(np.array([[2, 0], [0, 2]]), np.array([1, 2]), wires=[1]), 1, "Gaussian(M0,M1)", ), (qml.QuadraticPhase(3.14, wires=[1]), 1, "P(3.14)"), (qml.RX(3.14, wires=[1]), 1, "RX(3.14)"), (qml.S(wires=[2]), 2, "S"), (qml.T(wires=[2]), 2, "T"), (qml.RX(3.14, wires=[1]), 1, "RX(3.14)"), (qml.RY(3.14, wires=[1]), 1, "RY(3.14)"), (qml.RZ(3.14, wires=[1]), 1, "RZ(3.14)"), (qml.Rot(3.14, 2.14, 1.14, wires=[1]), 1, "Rot(3.14, 2.14, 1.14)"), (qml.U1(3.14, wires=[1]), 1, "U1(3.14)"), (qml.U2(3.14, 2.14, wires=[1]), 1, "U2(3.14, 2.14)"), (qml.U3(3.14, 2.14, 1.14, wires=[1]), 1, "U3(3.14, 2.14, 1.14)"), (qml.BasisState(np.array([0, 1, 0]), wires=[1, 2, 3]), 1, "|0>"), (qml.BasisState(np.array([0, 1, 0]), wires=[1, 2, 3]), 2, "|1>"), (qml.BasisState(np.array([0, 1, 0]), wires=[1, 2, 3]), 3, "|0>"), (qml.QubitStateVector(np.array([0, 1, 0, 0]), wires=[1, 2]), 1, "QubitStateVector(M0)"), (qml.QubitStateVector(np.array([0, 1, 0, 0]), wires=[1, 2]), 2, "QubitStateVector(M0)"), (qml.QubitUnitary(np.eye(2), wires=[1]), 1, "U0"), (qml.QubitUnitary(np.eye(4), wires=[1, 2]), 2, "U0"), (qml.Kerr(3.14, wires=[1]), 1, "Kerr(3.14)"), (qml.CrossKerr(3.14, wires=[1, 2]), 1, "CrossKerr(3.14)"), (qml.CrossKerr(3.14, wires=[1, 2]), 2, "CrossKerr(3.14)"), (qml.CubicPhase(3.14, wires=[1]), 1, "V(3.14)"), (qml.InterferometerUnitary( np.eye(4), wires=[1, 3]), 1, "InterferometerUnitary(M0)"), (qml.InterferometerUnitary( np.eye(4), wires=[1, 3]), 3, "InterferometerUnitary(M0)"), (qml.CatState(3.14, 2.14, 1, wires=[1]), 1, "CatState(3.14, 2.14, 1)"), (qml.CoherentState(3.14, 2.14, wires=[1]), 1, "CoherentState(3.14, 2.14)"), ( qml.FockDensityMatrix(np.kron(np.eye(4), np.eye(4)), wires=[1, 2]), 1, "FockDensityMatrix(M0)", ), ( qml.FockDensityMatrix(np.kron(np.eye(4), np.eye(4)), wires=[1, 2]), 2, "FockDensityMatrix(M0)", ), ( qml.DisplacedSqueezedState(3.14, 2.14, 1.14, 0.14, wires=[1]), 1, "DisplacedSqueezedState(3.14, 2.14, 1.14, 0.14)", ), (qml.FockState(7, wires=[1]), 1, "|7>"), (qml.FockStateVector(np.array([4, 5, 7]), wires=[1, 2, 3 ]), 1, "|4>"), (qml.FockStateVector(np.array([4, 5, 7]), wires=[1, 2, 3 ]), 2, "|5>"), (qml.FockStateVector(np.array([4, 5, 7]), wires=[1, 2, 3 ]), 3, "|7>"), (qml.SqueezedState(3.14, 2.14, wires=[1]), 1, "SqueezedState(3.14, 2.14)"), (qml.Hermitian(np.eye(4), wires=[1, 2]), 1, "H0"), (qml.Hermitian(np.eye(4), wires=[1, 2]), 2, "H0"), (qml.X(wires=[1]), 1, "x"), (qml.P(wires=[1]), 1, "p"), (qml.FockStateProjector(np.array([4, 5, 7]), wires=[1, 2, 3]), 1, "|4,5,7X4,5,7|"), ( qml.PolyXP(np.array([1, 2, 0, -1.3, 6]), wires=[1]), 2, "1+2x_0-1.3x_1+6p_1", ), ( qml.PolyXP(np.array([[1.2, 2.3, 4.5], [-1.2, 1.2, -1.5], [-1.3, 4.5, 2.3]]), wires=[1]), 1, "1.2+1.1x_0+3.2p_0+1.2x_0^2+2.3p_0^2+3x_0p_0", ), ( qml.PolyXP( np.array([ [1.2, 2.3, 4.5, 0, 0], [-1.2, 1.2, -1.5, 0, 0], [-1.3, 4.5, 2.3, 0, 0], [0, 2.6, 0, 0, 0], [0, 0, 0, -4.7, 0], ]), wires=[1], ), 1, "1.2+1.1x_0+3.2p_0+1.2x_0^2+2.3p_0^2+3x_0p_0+2.6x_0x_1-4.7x_1p_1", ), (qml.QuadOperator(3.14, wires=[1]), 1, "cos(3.14)x+sin(3.14)p"), (qml.QuadOperator(3.14, wires=[1]), 1, "cos(3.14)x+sin(3.14)p"), (qml.PauliX(wires=[1]).inv(), 1, "X^-1"), (qml.CNOT(wires=[0, 1]).inv(), 1, "X^-1"), (qml.CNOT(wires=[0, 1]).inv(), 0, "C"), (qml.Toffoli(wires=[0, 2, 1]).inv(), 1, "X^-1"), (qml.Toffoli(wires=[0, 2, 1]).inv(), 0, "C"), (qml.Toffoli(wires=[0, 2, 1]).inv(), 2, "C"), (qml.measure.sample(wires=[0, 1]), 0, "basis"), # not providing an observable in (qml.measure.sample(wires=[0, 1]), 1, "basis"), # sample gets displayed as raw (two_wire_quantum_tape(), 0, "QuantumTape:T0"), (two_wire_quantum_tape(), 1, "QuantumTape:T0"), ], ) def test_operator_representation_ascii(self, ascii_representation_resolver, op, wire, target): """Test that an Operator instance is properly resolved.""" assert ascii_representation_resolver.operator_representation( op, wire) == target @pytest.mark.parametrize( "obs,wire,target", [ (qml.expval(qml.PauliX(wires=[1])), 1, "⟨X⟩"), (qml.expval(qml.PauliY(wires=[1])), 1, "⟨Y⟩"), (qml.expval(qml.PauliZ(wires=[1])), 1, "⟨Z⟩"), (qml.expval(qml.Hadamard(wires=[1])), 1, "⟨H⟩"), (qml.expval(qml.Hermitian(np.eye(4), wires=[1, 2])), 1, "⟨H0⟩"), (qml.expval(qml.Hermitian(np.eye(4), wires=[1, 2])), 2, "⟨H0⟩"), (qml.expval(qml.NumberOperator(wires=[1])), 1, "⟨n⟩"), (qml.expval(qml.X(wires=[1])), 1, "⟨x⟩"), (qml.expval(qml.P(wires=[1])), 1, "⟨p⟩"), ( qml.expval( qml.FockStateProjector(np.array([4, 5, 7]), wires=[1, 2, 3])), 1, "⟨|4,5,7╳4,5,7|⟩", ), ( qml.expval(qml.PolyXP(np.array([1, 2, 0, -1.3, 6]), wires=[1 ])), 2, "⟨1+2x₀-1.3x₁+6p₁⟩", ), ( qml.expval( qml.PolyXP(np.array([[1.2, 2.3, 4.5], [-1.2, 1.2, -1.5], [-1.3, 4.5, 2.3]]), wires=[1])), 1, "⟨1.2+1.1x₀+3.2p₀+1.2x₀²+2.3p₀²+3x₀p₀⟩", ), (qml.expval(qml.QuadOperator( 3.14, wires=[1])), 1, "⟨cos(3.14)x+sin(3.14)p⟩"), (qml.var(qml.PauliX(wires=[1])), 1, "Var[X]"), (qml.var(qml.PauliY(wires=[1])), 1, "Var[Y]"), (qml.var(qml.PauliZ(wires=[1])), 1, "Var[Z]"), (qml.var(qml.Hadamard(wires=[1])), 1, "Var[H]"), (qml.var(qml.Hermitian(np.eye(4), wires=[1, 2])), 1, "Var[H0]"), (qml.var(qml.Hermitian(np.eye(4), wires=[1, 2])), 2, "Var[H0]"), (qml.var(qml.NumberOperator(wires=[1])), 1, "Var[n]"), (qml.var(qml.X(wires=[1])), 1, "Var[x]"), (qml.var(qml.P(wires=[1])), 1, "Var[p]"), ( qml.var( qml.FockStateProjector(np.array([4, 5, 7]), wires=[1, 2, 3])), 1, "Var[|4,5,7╳4,5,7|]", ), ( qml.var(qml.PolyXP(np.array([1, 2, 0, -1.3, 6]), wires=[1])), 2, "Var[1+2x₀-1.3x₁+6p₁]", ), ( qml.var( qml.PolyXP(np.array([[1.2, 2.3, 4.5], [-1.2, 1.2, -1.5], [-1.3, 4.5, 2.3]]), wires=[1])), 1, "Var[1.2+1.1x₀+3.2p₀+1.2x₀²+2.3p₀²+3x₀p₀]", ), (qml.var(qml.QuadOperator( 3.14, wires=[1])), 1, "Var[cos(3.14)x+sin(3.14)p]"), (qml.sample(qml.PauliX(wires=[1])), 1, "Sample[X]"), (qml.sample(qml.PauliY(wires=[1])), 1, "Sample[Y]"), (qml.sample(qml.PauliZ(wires=[1])), 1, "Sample[Z]"), (qml.sample(qml.Hadamard(wires=[1])), 1, "Sample[H]"), (qml.sample(qml.Hermitian(np.eye(4), wires=[1, 2 ])), 1, "Sample[H0]"), (qml.sample(qml.Hermitian(np.eye(4), wires=[1, 2 ])), 2, "Sample[H0]"), (qml.sample(qml.NumberOperator(wires=[1])), 1, "Sample[n]"), (qml.sample(qml.X(wires=[1])), 1, "Sample[x]"), (qml.sample(qml.P(wires=[1])), 1, "Sample[p]"), ( qml.sample( qml.FockStateProjector(np.array([4, 5, 7]), wires=[1, 2, 3])), 1, "Sample[|4,5,7╳4,5,7|]", ), ( qml.sample(qml.PolyXP(np.array([1, 2, 0, -1.3, 6]), wires=[1 ])), 2, "Sample[1+2x₀-1.3x₁+6p₁]", ), ( qml.sample( qml.PolyXP(np.array([[1.2, 2.3, 4.5], [-1.2, 1.2, -1.5], [-1.3, 4.5, 2.3]]), wires=[1])), 1, "Sample[1.2+1.1x₀+3.2p₀+1.2x₀²+2.3p₀²+3x₀p₀]", ), (qml.sample(qml.QuadOperator( 3.14, wires=[1])), 1, "Sample[cos(3.14)x+sin(3.14)p]"), ( qml.expval( qml.PauliX(wires=[1]) @ qml.PauliY(wires=[2]) @ qml.PauliZ(wires=[3])), 1, "⟨X ⊗ Y ⊗ Z⟩", ), ( qml.expval( qml.FockStateProjector(np.array([4, 5, 7]), wires=[1, 2, 3]) @ qml.X(wires=[4])), 1, "⟨|4,5,7╳4,5,7| ⊗ x⟩", ), ( qml.expval( qml.FockStateProjector(np.array([4, 5, 7]), wires=[1, 2, 3]) @ qml.X(wires=[4])), 2, "⟨|4,5,7╳4,5,7| ⊗ x⟩", ), ( qml.expval( qml.FockStateProjector(np.array([4, 5, 7]), wires=[1, 2, 3]) @ qml.X(wires=[4])), 3, "⟨|4,5,7╳4,5,7| ⊗ x⟩", ), ( qml.expval( qml.FockStateProjector(np.array([4, 5, 7]), wires=[1, 2, 3]) @ qml.X(wires=[4])), 4, "⟨|4,5,7╳4,5,7| ⊗ x⟩", ), ( qml.sample( qml.Hermitian(np.eye(4), wires=[1, 2]) @ qml.Hermitian( np.eye(4), wires=[0, 3])), 0, "Sample[H0 ⊗ H0]", ), ( qml.sample( qml.Hermitian(np.eye(4), wires=[1, 2]) @ qml.Hermitian( 2 * np.eye(4), wires=[0, 3])), 0, "Sample[H0 ⊗ H1]", ), (qml.probs([0]), 0, "Probs"), (state(), 0, "State"), ], ) def test_output_representation_unicode(self, unicode_representation_resolver, obs, wire, target): """Test that an Observable instance with return type is properly resolved.""" assert unicode_representation_resolver.output_representation( obs, wire) == target def test_fallback_output_representation_unicode( self, unicode_representation_resolver): """Test that an Observable instance with return type is properly resolved.""" obs = qml.PauliZ(0) obs.return_type = "TestReturnType" assert unicode_representation_resolver.output_representation( obs, 0) == "TestReturnType[Z]" @pytest.mark.parametrize( "obs,wire,target", [ (qml.expval(qml.PauliX(wires=[1])), 1, "<X>"), (qml.expval(qml.PauliY(wires=[1])), 1, "<Y>"), (qml.expval(qml.PauliZ(wires=[1])), 1, "<Z>"), (qml.expval(qml.Hadamard(wires=[1])), 1, "<H>"), (qml.expval(qml.Hermitian(np.eye(4), wires=[1, 2])), 1, "<H0>"), (qml.expval(qml.Hermitian(np.eye(4), wires=[1, 2])), 2, "<H0>"), (qml.expval(qml.NumberOperator(wires=[1])), 1, "<n>"), (qml.expval(qml.X(wires=[1])), 1, "<x>"), (qml.expval(qml.P(wires=[1])), 1, "<p>"), ( qml.expval( qml.FockStateProjector(np.array([4, 5, 7]), wires=[1, 2, 3])), 1, "<|4,5,7X4,5,7|>", ), ( qml.expval(qml.PolyXP(np.array([1, 2, 0, -1.3, 6]), wires=[1 ])), 2, "<1+2x_0-1.3x_1+6p_1>", ), ( qml.expval( qml.PolyXP(np.array([[1.2, 2.3, 4.5], [-1.2, 1.2, -1.5], [-1.3, 4.5, 2.3]]), wires=[1])), 1, "<1.2+1.1x_0+3.2p_0+1.2x_0^2+2.3p_0^2+3x_0p_0>", ), (qml.expval(qml.QuadOperator( 3.14, wires=[1])), 1, "<cos(3.14)x+sin(3.14)p>"), (qml.var(qml.PauliX(wires=[1])), 1, "Var[X]"), (qml.var(qml.PauliY(wires=[1])), 1, "Var[Y]"), (qml.var(qml.PauliZ(wires=[1])), 1, "Var[Z]"), (qml.var(qml.Hadamard(wires=[1])), 1, "Var[H]"), (qml.var(qml.Hermitian(np.eye(4), wires=[1, 2])), 1, "Var[H0]"), (qml.var(qml.Hermitian(np.eye(4), wires=[1, 2])), 2, "Var[H0]"), (qml.var(qml.NumberOperator(wires=[1])), 1, "Var[n]"), (qml.var(qml.X(wires=[1])), 1, "Var[x]"), (qml.var(qml.P(wires=[1])), 1, "Var[p]"), ( qml.var( qml.FockStateProjector(np.array([4, 5, 7]), wires=[1, 2, 3])), 1, "Var[|4,5,7X4,5,7|]", ), ( qml.var(qml.PolyXP(np.array([1, 2, 0, -1.3, 6]), wires=[1])), 2, "Var[1+2x_0-1.3x_1+6p_1]", ), ( qml.var( qml.PolyXP(np.array([[1.2, 2.3, 4.5], [-1.2, 1.2, -1.5], [-1.3, 4.5, 2.3]]), wires=[1])), 1, "Var[1.2+1.1x_0+3.2p_0+1.2x_0^2+2.3p_0^2+3x_0p_0]", ), (qml.var(qml.QuadOperator( 3.14, wires=[1])), 1, "Var[cos(3.14)x+sin(3.14)p]"), (qml.sample(qml.PauliX(wires=[1])), 1, "Sample[X]"), (qml.sample(qml.PauliY(wires=[1])), 1, "Sample[Y]"), (qml.sample(qml.PauliZ(wires=[1])), 1, "Sample[Z]"), (qml.sample(qml.Hadamard(wires=[1])), 1, "Sample[H]"), (qml.sample(qml.Hermitian(np.eye(4), wires=[1, 2 ])), 1, "Sample[H0]"), (qml.sample(qml.Hermitian(np.eye(4), wires=[1, 2 ])), 2, "Sample[H0]"), (qml.sample(qml.NumberOperator(wires=[1])), 1, "Sample[n]"), (qml.sample(qml.X(wires=[1])), 1, "Sample[x]"), (qml.sample(qml.P(wires=[1])), 1, "Sample[p]"), ( qml.sample( qml.FockStateProjector(np.array([4, 5, 7]), wires=[1, 2, 3])), 1, "Sample[|4,5,7X4,5,7|]", ), ( qml.sample(qml.PolyXP(np.array([1, 2, 0, -1.3, 6]), wires=[1 ])), 2, "Sample[1+2x_0-1.3x_1+6p_1]", ), ( qml.sample( qml.PolyXP(np.array([[1.2, 2.3, 4.5], [-1.2, 1.2, -1.5], [-1.3, 4.5, 2.3]]), wires=[1])), 1, "Sample[1.2+1.1x_0+3.2p_0+1.2x_0^2+2.3p_0^2+3x_0p_0]", ), (qml.sample(qml.QuadOperator( 3.14, wires=[1])), 1, "Sample[cos(3.14)x+sin(3.14)p]"), ( qml.expval( qml.PauliX(wires=[1]) @ qml.PauliY(wires=[2]) @ qml.PauliZ(wires=[3])), 1, "<X @ Y @ Z>", ), ( qml.expval( qml.FockStateProjector(np.array([4, 5, 7]), wires=[1, 2, 3]) @ qml.X(wires=[4])), 1, "<|4,5,7X4,5,7| @ x>", ), ( qml.expval( qml.FockStateProjector(np.array([4, 5, 7]), wires=[1, 2, 3]) @ qml.X(wires=[4])), 2, "<|4,5,7X4,5,7| @ x>", ), ( qml.expval( qml.FockStateProjector(np.array([4, 5, 7]), wires=[1, 2, 3]) @ qml.X(wires=[4])), 3, "<|4,5,7X4,5,7| @ x>", ), ( qml.expval( qml.FockStateProjector(np.array([4, 5, 7]), wires=[1, 2, 3]) @ qml.X(wires=[4])), 4, "<|4,5,7X4,5,7| @ x>", ), ( qml.sample( qml.Hermitian(np.eye(4), wires=[1, 2]) @ qml.Hermitian( np.eye(4), wires=[0, 3])), 0, "Sample[H0 @ H0]", ), ( qml.sample( qml.Hermitian(np.eye(4), wires=[1, 2]) @ qml.Hermitian( 2 * np.eye(4), wires=[0, 3])), 0, "Sample[H0 @ H1]", ), (qml.probs([0]), 0, "Probs"), (state(), 0, "State"), ], ) def test_output_representation_ascii(self, ascii_representation_resolver, obs, wire, target): """Test that an Observable instance with return type is properly resolved.""" assert ascii_representation_resolver.output_representation( obs, wire) == target def test_element_representation_none(self, unicode_representation_resolver): """Test that element_representation properly handles None.""" assert unicode_representation_resolver.element_representation(None, 0) == "" def test_element_representation_str(self, unicode_representation_resolver): """Test that element_representation properly handles strings.""" assert unicode_representation_resolver.element_representation( "Test", 0) == "Test" def test_element_representation_calls_output( self, unicode_representation_resolver): """Test that element_representation calls output_representation for returned observables.""" unicode_representation_resolver.output_representation = Mock() obs = qml.sample(qml.PauliX(3)) wire = 3 unicode_representation_resolver.element_representation(obs, wire) assert unicode_representation_resolver.output_representation.call_args[ 0] == (obs, wire) def test_element_representation_calls_operator( self, unicode_representation_resolver): """Test that element_representation calls operator_representation for all operators that are not returned.""" unicode_representation_resolver.operator_representation = Mock() op = qml.PauliX(3) wire = 3 unicode_representation_resolver.element_representation(op, wire) assert unicode_representation_resolver.operator_representation.call_args[ 0] == (op, wire)
def circuit_decomposed(time): qml.PauliRot(time, "X", wires=[0]) qml.PauliRot(time, "X", wires=[1]) qml.PauliRot(time, "X", wires=[0]) qml.PauliRot(time, "X", wires=[1]) return qml.expval(qml.PauliZ(0))
def circuit_decomposed(weights): for i in range(qml.math.shape(weights)[0]): qml.PauliRot(weights[i], paulis_two_qubits[i], wires=[0, 1]) return qml.expval(qml.PauliZ(0))
def ApproxTimeEvolution(hamiltonian, time, n): r"""Applies the Trotterized time-evolution operator for an arbitrary Hamiltonian, expressed in terms of Pauli gates. The general time-evolution operator for a time-independent Hamiltonian is given by .. math:: U(t) \ = \ e^{-i H t}, for some Hamiltonian of the form: .. math:: H \ = \ \displaystyle\sum_{j} H_j. Implementing this unitary with a set of quantum gates is difficult, as the terms :math:`H_j` don't necessarily commute with one another. However, we are able to exploit the Trotter-Suzuki decomposition formula, .. math:: e^{A \ + \ B} \ = \ \lim_{n \to \infty} \Big[ e^{A/n} e^{B/n} \Big]^n, to implement an approximation of the time-evolution operator as .. math:: U \ \approx \ \displaystyle\prod_{k \ = \ 1}^{n} \displaystyle\prod_{j} e^{-i H_j t / n}, with the approximation becoming better for larger :math:`n`. The circuit implementing this unitary is of the form: .. figure:: ../../_static/templates/subroutines/approx_time_evolution.png :align: center :width: 60% :target: javascript:void(0); It is also important to note that this decomposition is exact for any value of :math:`n` when each term of the Hamiltonian commutes with every other term. .. note:: This template uses the :class:`~.PauliRot` operation in order to implement exponentiated terms of the input Hamiltonian. This operation only takes terms that are explicitly written in terms of products of Pauli matrices (:class:`~.PauliX`, :class:`~.PauliY`, :class:`~.PauliZ`, and :class:`~.Identity`). Thus, each term in the Hamiltonian must be expressed this way upon input, or else an error will be raised. Args: hamiltonian (.Hamiltonian): The Hamiltonian defining the time-evolution operator. The Hamiltonian must be explicitly written in terms of products of Pauli gates (:class:`~.PauliX`, :class:`~.PauliY`, :class:`~.PauliZ`, and :class:`~.Identity`). time (int or float): The time of evolution, namely the parameter :math:`t` in :math:`e^{- i H t}`. n (int): The number of Trotter steps used when approximating the time-evolution operator. Raises: ValueError: if inputs do not have the correct format .. UsageDetails:: The template is used inside a qnode: .. code-block:: python import pennylane as qml from pennylane.templates import ApproxTimeEvolution n_wires = 2 wires = range(n_wires) dev = qml.device('default.qubit', wires=n_wires) coeffs = [1, 1] obs = [qml.PauliX(0), qml.PauliX(1)] hamiltonian = qml.Hamiltonian(coeffs, obs) @qml.qnode(dev) def circuit(time): ApproxTimeEvolution(hamiltonian, time, 1) return [qml.expval(qml.PauliZ(wires=i)) for i in wires] >>> circuit(1) [-0.41614684 -0.41614684] """ _preprocess(hamiltonian) pauli = {"Identity": "I", "PauliX": "X", "PauliY": "Y", "PauliZ": "Z"} theta = [] pauli_words = [] wires = [] for i, term in enumerate(hamiltonian.ops): word = "" try: if isinstance(term.name, str): word = pauli[term.name] if isinstance(term.name, list): word = "".join(pauli[j] for j in term.name) except KeyError as error: raise ValueError( "hamiltonian must be written in terms of Pauli matrices, got {}" .format(error)) from error # Skips terms composed solely of identities if word.count("I") != len(word): theta.append((2 * time * hamiltonian.coeffs[i]) / n) pauli_words.append(word) wires.append(term.wires) for i in range(n): for j, term in enumerate(pauli_words): qml.PauliRot(theta[j], term, wires=wires[j])
class TestDecomposition: """Tests that the template defines the correct decomposition.""" @pytest.mark.parametrize( ("time", "hamiltonian", "steps", "expected_queue"), [ ( 2, qml.Hamiltonian([1, 1], [qml.PauliX(0), qml.PauliX(1)]), 2, [ qml.PauliRot(2.0, "X", wires=[0]), qml.PauliRot(2.0, "X", wires=[1]), qml.PauliRot(2.0, "X", wires=[0]), qml.PauliRot(2.0, "X", wires=[1]), ], ), ( 2, qml.Hamiltonian([2, 0.5], [qml.PauliX("a"), qml.PauliZ("b") @ qml.PauliX("a")]), 2, [ qml.PauliRot(4.0, "X", wires=["a"]), qml.PauliRot(1.0, "ZX", wires=["b", "a"]), qml.PauliRot(4.0, "X", wires=["a"]), qml.PauliRot(1.0, "ZX", wires=["b", "a"]), ], ), ( 2, qml.Hamiltonian([1, 1], [qml.PauliX(0), qml.Identity(0) @ qml.Identity(1)]), 2, [qml.PauliRot(2.0, "X", wires=[0]), qml.PauliRot(2.0, "X", wires=[0])], ), ( 2, qml.Hamiltonian( [2, 0.5, 0.5], [ qml.PauliX("a"), qml.PauliZ(-15) @ qml.PauliX("a"), qml.Identity(0) @ qml.PauliY(-15), ], ), 1, [ qml.PauliRot(8.0, "X", wires=["a"]), qml.PauliRot(2.0, "ZX", wires=[-15, "a"]), qml.PauliRot(2.0, "IY", wires=[0, -15]), ], ), ], ) def test_evolution_operations(self, time, hamiltonian, steps, expected_queue): """Tests that the sequence of gates implemented in the ApproxTimeEvolution template is correct""" op = qml.ApproxTimeEvolution(hamiltonian, time, steps) queue = op.expand().operations for expected_gate, gate in zip(expected_queue, queue): prep = [gate.parameters, gate.wires] target = [expected_gate.parameters, expected_gate.wires] assert prep == target @pytest.mark.parametrize( ("time", "hamiltonian", "steps", "expectation"), [ (np.pi, qml.Hamiltonian([1, 1], [qml.PauliX(0), qml.PauliX(1)]), 2, [1.0, 1.0]), ( np.pi / 2, qml.Hamiltonian([0.5, 1], [qml.PauliY(0), qml.Identity(0) @ qml.PauliX(1)]), 1, [0.0, -1.0], ), ( np.pi / 4, qml.Hamiltonian( [1, 1, 1], [qml.PauliX(0), qml.PauliZ(0) @ qml.PauliZ(1), qml.PauliX(1)] ), 1, [0.0, 0.0], ), ( 1, qml.Hamiltonian([1, 1], [qml.PauliX(0), qml.PauliX(1)]), 2, [-0.41614684, -0.41614684], ), ( 2, qml.Hamiltonian( [1, 1, 1, 1], [qml.PauliX(0), qml.PauliY(0), qml.PauliZ(0) @ qml.PauliZ(1), qml.PauliY(1)], ), 2, [-0.87801124, 0.51725747], ), ], ) def test_evolution_output(self, time, hamiltonian, steps, expectation): """Tests that the output from the ApproxTimeEvolution template is correct""" n_wires = 2 dev = qml.device("default.qubit", wires=n_wires) @qml.qnode(dev) def circuit(): qml.ApproxTimeEvolution(hamiltonian, time, steps) return [qml.expval(qml.PauliZ(wires=i)) for i in range(n_wires)] assert np.allclose(circuit(), expectation) def test_custom_wire_labels(self, tol): """Test that template can deal with non-numeric, nonconsecutive wire labels.""" hamiltonian = qml.Hamiltonian([1, 1, 1], [qml.PauliX(0), qml.PauliX(1), qml.PauliX(2)]) hamiltonian2 = qml.Hamiltonian( [1, 1, 1], [qml.PauliX("z"), qml.PauliX("a"), qml.PauliX("k")] ) dev = qml.device("default.qubit", wires=3) dev2 = qml.device("default.qubit", wires=["z", "a", "k"]) @qml.qnode(dev) def circuit(): qml.ApproxTimeEvolution(hamiltonian, 0.5, 2) return qml.expval(qml.Identity(0)) @qml.qnode(dev2) def circuit2(): qml.ApproxTimeEvolution(hamiltonian2, 0.5, 2) return qml.expval(qml.Identity("z")) circuit() circuit2() assert np.allclose(dev.state, dev2.state, atol=tol, rtol=0)