Exemplo n.º 1
0
def unitary_to_rot(tape):
    """Quantum function transform to decomposes all instances of single-qubit :class:`~.QubitUnitary`
    operations to parametrized single-qubit operations.

    Diagonal operations will be converted to a single :class:`.RZ` gate, while non-diagonal
    operations will be converted to a :class:`.Rot` gate that implements the original operation
    up to a global phase.

    Args:
        qfunc (function): a quantum function

    **Example**

    Suppose we would like to apply the following unitary operation:

    .. code-block:: python3

        U = np.array([
            [-0.17111489+0.58564875j, -0.69352236-0.38309524j],
            [ 0.25053735+0.75164238j,  0.60700543-0.06171855j]
        ])

    The ``unitary_to_rot`` transform enables us to decompose
    such numerical operations (as well as unitaries that may be defined by parameters
    within the QNode, and instantiated therein), while preserving differentiability.


    .. code-block:: python3

        def qfunc():
            qml.QubitUnitary(U, wires=0)
            return qml.expval(qml.PauliZ(0))

    The original circuit is:

    >>> dev = qml.device('default.qubit', wires=1)
    >>> qnode = qml.QNode(qfunc, dev)
    >>> print(qml.draw(qnode)())
     0: ──U0──┤ ⟨Z⟩
    U0 =
    [[-0.17111489+0.58564875j -0.69352236-0.38309524j]
     [ 0.25053735+0.75164238j  0.60700543-0.06171855j]]

    We can use the transform to decompose the gate:

    >>> transformed_qfunc = unitary_to_rot(qfunc)
    >>> transformed_qnode = qml.QNode(transformed_qfunc, dev)
    >>> print(qml.draw(transformed_qnode)())
     0: ──Rot(-1.35, 1.83, -0.606)──┤ ⟨Z⟩

    """
    for op in tape.operations + tape.measurements:
        if isinstance(op, qml.QubitUnitary):
            # Only act on single-qubit unitary operations
            if qml.math.shape(op.parameters[0]) != (2, 2):
                continue

            zyz_decomposition(op.parameters[0], op.wires[0])
        else:
            qml.apply(op)
    def test_gradients_gaussian_circuit(self, op, obs, tol):
        """Tests that the gradients of circuits of gaussian gates match between the
        finite difference and analytic methods."""
        tol = 1e-2

        with qml.tape.JacobianTape() as tape:
            qml.Displacement(0.5, 0, wires=0)
            qml.apply(op)
            qml.Beamsplitter(1.3, -2.3, wires=[0, 1])
            qml.Displacement(-0.5, 0.1, wires=0)
            qml.Squeezing(0.5, -1.5, wires=0)
            qml.Rotation(-1.1, wires=0)
            qml.expval(obs(wires=0))

        dev = qml.device("default.gaussian", wires=2)
        res = tape.execute(dev)

        tape.trainable_params = set(range(2, 2 + op.num_params))

        tapes, fn = qml.gradients.finite_diff(tape)
        grad_F = fn(dev.batch_execute(tapes))

        tapes, fn = param_shift_cv(tape, dev, force_order2=True)
        grad_A2 = fn(dev.batch_execute(tapes))

        # check that every parameter is analytic
        for i in range(op.num_params):
            assert tape._par_info[2 + i]["grad_method"][0] == "A"

        assert np.allclose(grad_A2, grad_F, atol=tol, rtol=0)

        if obs.ev_order == 1:
            tapes, fn = param_shift_cv(tape, dev)
            grad_A = fn(dev.batch_execute(tapes))
            assert np.allclose(grad_A, grad_F, atol=tol, rtol=0)
Exemplo n.º 3
0
    def test_gradients_gaussian_circuit(self, op, obs, mocker, tol):
        """Tests that the gradients of circuits of gaussian gates match between the
        finite difference and analytic methods."""
        tol = 1e-2

        args = np.linspace(0.2, 0.5, op.num_params)

        with CVParamShiftTape() as tape:
            qml.Displacement(0.5, 0, wires=0)
            qml.apply(op)
            qml.Beamsplitter(1.3, -2.3, wires=[0, 1])
            qml.Displacement(-0.5, 0.1, wires=0)
            qml.Squeezing(0.5, -1.5, wires=0)
            qml.Rotation(-1.1, wires=0)
            qml.var(obs(wires=0))

        dev = qml.device("default.gaussian", wires=2)
        res = tape.execute(dev)

        tape._update_gradient_info()
        tape.trainable_params = set(range(2, 2 + op.num_params))

        # check that every parameter is analytic
        for i in range(op.num_params):
            assert tape._par_info[2 + i]["grad_method"][0] == "A"

        spy = mocker.spy(CVParamShiftTape, "parameter_shift_first_order")
        grad_F = tape.jacobian(dev, method="numeric")
        grad_A = tape.jacobian(dev, method="analytic")
        grad_A2 = tape.jacobian(dev, method="analytic", force_order2=True)

        assert np.allclose(grad_A2, grad_F, atol=tol, rtol=0)
        assert np.allclose(grad_A, grad_F, atol=tol, rtol=0)
Exemplo n.º 4
0
    def test_nested_apply(self, qnodes, interface, tf_support, torch_support, tol):
        """Test that nested apply can be done using all interfaces"""
        if interface == "torch" and not torch_support:
            pytest.skip("Skipped, no torch support")

        if interface == "tf" and not tf_support:
            pytest.skip("Skipped, no tf support")

        qnode1, qnode2 = qnodes
        qc = qml.QNodeCollection([qnode1, qnode2])

        if interface == "tf":
            sinfn = tf.sin
            sfn = tf.reduce_sum
        elif interface == "torch":
            sinfn = torch.sin
            sfn = torch.sum
        else:
            sinfn = np.sin
            sfn = np.sum

        cost = qml.apply(sfn, qml.apply(sinfn, qc))

        params = [0.5643, -0.45]
        res = cost(params)
        expected = sfn(sinfn(qc(params)))

        assert np.allclose(res, expected, atol=tol, rtol=0)
Exemplo n.º 5
0
    def test_control_gates(self, op, label):
        """Test a variety of non-special gates. Checks control wires are drawn, and
        that a box is drawn over the target wires."""

        with QuantumTape() as tape:
            qml.apply(op)

        _, ax = tape_mpl(tape)
        layer = 0

        assert isinstance(ax.patches[0], mpl.patches.Circle)
        assert ax.patches[0].center == (layer, 0)

        control_line = ax.lines[2]
        assert control_line.get_data() == ((layer, layer), (0, 1))

        assert isinstance(ax.patches[1], mpl.patches.FancyBboxPatch)
        assert ax.patches[1].get_x() == layer - self.width / 2.0
        assert ax.patches[1].get_y() == 1 - self.width / 2.0

        # two wire labels, so [2] is box gate label
        assert ax.texts[2].get_text() == label

        # box and text must be raised above control wire
        # text raised over box
        assert ax.patches[1].get_zorder() > control_line.get_zorder()
        assert ax.texts[2].get_zorder() > ax.patches[1].get_zorder()

        plt.close()
Exemplo n.º 6
0
    def my_transform(tape, weights):
        """Generates two tapes, one with all RX replaced with RY,
        and the other with all RX replaced with RZ."""

        tape1 = qml.tape.JacobianTape()
        tape2 = qml.tape.JacobianTape()

        # loop through all operations on the input tape
        for op in tape.operations + tape.measurements:
            if op.name == "RX":
                wires = op.wires
                param = op.parameters[0]

                with tape1:
                    qml.RY(weights[0] * qml.math.sin(param), wires=wires)

                with tape2:
                    qml.RZ(weights[1] * qml.math.cos(param), wires=wires)
            else:
                for t in [tape1, tape2]:
                    with t:
                        qml.apply(op)

        def processing_fn(results):
            return qml.math.sum(qml.math.stack(results))

        return [tape1, tape2], processing_fn
    def test_apply(self, op, apply_unitary, tol):
        """Test the application of gates to a state"""
        dev = plf.NumpyWavefunctionDevice(wires=3)

        obs = qml.expval(qml.PauliZ(0))

        if op.name == "QubitUnitary":
            state = apply_unitary(U, 3)
        elif op.name == "BasisState":
            state = np.array([0, 0, 0, 0, 0, 0, 0, 1])
        elif op.name == "CPHASE":
            state = apply_unitary(test_operation_map["CPHASE"](0.432, 2), 3)
        elif op.name == "ISWAP":
            state = apply_unitary(test_operation_map["ISWAP"], 3)
        elif op.name == "PSWAP":
            state = apply_unitary(test_operation_map["PSWAP"](0.432), 3)
        else:
            state = apply_unitary(op.matrix, 3)

        with qml.tape.QuantumTape() as tape:
            qml.apply(op)
            obs

        dev.apply(tape.operations, rotations=tape.diagonalizing_gates)

        # verify the device is now in the expected state
        self.assertAllAlmostEqual(dev._state, state, delta=tol)
Exemplo n.º 8
0
    def test_gradients(self, op, obs, tol, dev):
        """Tests that the gradients of circuits match between the finite difference and device
        methods."""

        with qml.tape.JacobianTape() as tape:
            qml.Hadamard(wires=0)
            qml.RX(0.543, wires=0)
            qml.CNOT(wires=[0, 1])

            qml.apply(op)

            qml.Rot(1.3, -2.3, 0.5, wires=[0])
            qml.RZ(-0.5, wires=0)
            qml.RY(0.5, wires=1).inv()
            qml.CNOT(wires=[0, 1])

            qml.expval(obs(wires=0))
            qml.expval(qml.PauliZ(wires=1))

        tape.trainable_params = set(range(1, 1 + op.num_params))

        grad_F = (lambda t, fn: fn(qml.execute(t, dev, None)))(
            *qml.gradients.finite_diff(tape))
        grad_D = dev.adjoint_jacobian(tape)

        assert np.allclose(grad_D, grad_F, atol=tol, rtol=0)
 def circuit():
     """4-qubit circuit with layers of randomly selected gates and random connections for
     multi-qubit gates."""
     np.random.seed(1967)
     for gates in gates_per_layers:
         for gate in gates:
             qml.apply(gate)
     return qml.expval(qml.PauliZ(0))
Exemplo n.º 10
0
    def test_default_queue_measurements_outside(self, obs):
        """Test applying a measurement instantiated outside a queuing context
        to an existing active queue"""
        op = qml.expval(obs)

        with qml.tape.QuantumTape() as tape:
            qml.apply(op)

        assert tape.measurements == [op]
Exemplo n.º 11
0
    def test_default_queue_operation_outside(self):
        """Test applying an operation instantiated outside a queuing context
        to an existing active queue"""
        op = qml.PauliZ(0)

        with qml.tape.QuantumTape() as tape:
            qml.apply(op)

        assert tape.operations == [op]
Exemplo n.º 12
0
def remove_barrier(tape):
    """Quantum function transform to remove Barrier gates.

    Args:
        qfunc (function): A quantum function.

    Returns:
        function: the transformed quantum function

    **Example**

    Consider the following quantum function:

    .. code-block:: python

        def qfunc(x, y):
            qml.Hadamard(wires=0)
            qml.Hadamard(wires=1)
            qml.Barrier(wires=[0,1])
            qml.PauliX(wires=0)
            return qml.expval(qml.PauliZ(0))

    The circuit before optimization:

    >>> dev = qml.device('default.qubit', wires=2)
    >>> qnode = qml.QNode(qfunc, dev)
    >>> print(qml.draw(qnode)(1, 2))
        0: ──H──╭||──X──┤ ⟨Z⟩
        1: ──H──╰||─────┤


    We can remove the Barrier by running the ``remove_barrier`` transform:

    >>> optimized_qfunc = remove_barrier(qfunc)
    >>> optimized_qnode = qml.QNode(optimized_qfunc, dev)
    >>> print(qml.draw(optimized_qnode)(1, 2))
       0: ──H──X──┤ ⟨Z⟩
       1: ──H─────┤

    """
    # Make a working copy of the list to traverse
    list_copy = tape.operations.copy()

    while len(list_copy) > 0:
        current_gate = list_copy[0]

        # Remove Barrier gate
        if current_gate.name != "Barrier":
            apply(current_gate)

        list_copy.pop(0)
        continue

    # Queue the measurements normally
    for m in tape.measurements:
        apply(m)
Exemplo n.º 13
0
    def test_different_queue_operation_outside(self):
        """Test applying an operation instantiated outside a queuing context
        to a specfied queuing context"""
        op = qml.PauliZ(0)

        with qml.tape.QuantumTape() as tape1:
            with qml.tape.QuantumTape() as tape2:
                qml.apply(op, tape1)

        assert tape1.operations == [tape2, op]
        assert tape2.operations == []
Exemplo n.º 14
0
    def test_different_queue_measurements_outside(self, obs):
        """Test applying a measurement instantiated outside a queuing context
        to a specfied queuing context"""
        op = qml.expval(obs)

        with qml.tape.QuantumTape() as tape1:
            with qml.tape.QuantumTape() as tape2:
                qml.apply(op, tape1)

        assert tape1.measurements == [op]
        assert tape2.measurements == []
Exemplo n.º 15
0
    def test_general_operations_decimals(self, op):
        """Check that the decimals argument affects text strings when applicable."""

        with QuantumTape() as tape:
            qml.apply(op)

        _, ax = tape_mpl(tape, decimals=2)

        num_wires = len(op.wires)
        assert ax.texts[num_wires].get_text() == op.label(decimals=2)

        plt.close()
Exemplo n.º 16
0
def _get_first_term_tapes(tape, layer_i, layer_j, allow_nonunitary, aux_wire):
    r"""Obtain the tapes for the first term of all tensor entries
    belonging to an off-diagonal block.

    Args:
        tape (pennylane.tape.QuantumTape): Tape that is being transformed
        layer_i (list): The first layer of parametrized ops, of the format of
            the layers generated by ``iterate_parametrized_layers``
        layer_j (list): The second layer of parametrized ops
        allow_nonunitary (bool): Whether non-unitary operations are allowed
            in the circuit; passed to ``_get_gen_op``
        aux_wire (object or pennylane.wires.Wires): Auxiliary wire on which to
            control the controlled-generator operations

    Returns:
        list[pennylane.tape.QuantumTape]: Transformed tapes that compute the
            first term of the metric tensor for the off-diagonal block belonging
            to the input layers
        list[tuple[int]]: 2-tuple indices assigning the tapes to metric tensor
            entries
    """

    tapes = []
    ids = []
    # Exclude the backwards cone of layer_i from the backwards cone of layer_j
    ops_between_cgens = [
        op for op in layer_j.pre_ops if op not in layer_i.pre_ops
    ]

    # Iterate over differentiated operation in first layer
    for diffed_op_i, par_idx_i in zip(layer_i.ops, layer_i.param_inds):
        gen_op_i = _get_gen_op(diffed_op_i, allow_nonunitary, aux_wire)

        # Iterate over differentiated operation in second layer
        # There will be one tape per pair of differentiated operations
        for diffed_op_j, par_idx_j in zip(layer_j.ops, layer_j.param_inds):
            gen_op_j = _get_gen_op(diffed_op_j, allow_nonunitary, aux_wire)

            with tape.__class__() as new_tape:
                # Initialize auxiliary wire
                qml.Hadamard(wires=aux_wire)
                # Apply backward cone of first layer
                for op in layer_i.pre_ops:
                    qml.apply(op)
                # Controlled-generator operation of first diff'ed op
                qml.apply(gen_op_i)
                # Apply first layer and operations between layers
                for op in ops_between_cgens:
                    qml.apply(op)
                # Controlled-generator operation of second diff'ed op
                qml.apply(gen_op_j)
                # Measure X on auxiliary wire
                qml.expval(qml.PauliX(aux_wire))

            tapes.append(new_tape)
            # Memorize to which metric entry this tape belongs
            ids.append((par_idx_i, par_idx_j))

    return tapes, ids
Exemplo n.º 17
0
    def test_apply_no_queue_method(self):
        """Test that an object with no queue method is still
        added to the queuing context"""
        with qml.tape.QuantumTape() as tape1:
            with qml.tape.QuantumTape() as tape2:
                op1 = qml.apply(5)
                op2 = qml.apply(6, tape1)

        assert tape1.queue == [tape2, op2]
        assert tape2.queue == [op1]

        # note that tapes don't know how to process integers,
        # so they are not included after queue processing
        assert tape1.operations == [tape2]
        assert tape2.operations == []
Exemplo n.º 18
0
    def test_two_qubit_decomposition_tf(self, U, wires):
        """Test that a two-qubit operation in Tensorflow is correctly decomposed."""
        tf = pytest.importorskip("tensorflow")

        U = tf.Variable(U, dtype=tf.complex128)

        obtained_decomposition = two_qubit_decomposition(U, wires=wires)

        with qml.tape.QuantumTape() as tape:
            for op in obtained_decomposition:
                qml.apply(op)

        obtained_matrix = get_unitary_matrix(tape, wire_order=wires)()

        assert check_matrix_equivalence(U, obtained_matrix, atol=1e-7)
Exemplo n.º 19
0
    def test_two_qubit_decomposition_1_cnot(self, U, wires):
        """Test that a two-qubit matrix using one CNOT is correctly decomposed."""
        U = _convert_to_su4(np.array(U))

        assert _compute_num_cnots(U) == 1

        obtained_decomposition = two_qubit_decomposition(U, wires=wires)
        assert len(obtained_decomposition) == 5

        with qml.tape.QuantumTape() as tape:
            for op in obtained_decomposition:
                qml.apply(op)

        obtained_matrix = get_unitary_matrix(tape, wire_order=wires)()

        assert check_matrix_equivalence(U, obtained_matrix, atol=1e-7)
Exemplo n.º 20
0
def append_time_evolution(tape, riemannian_gradient, t, n, exact=False):
    r"""Append an approximate time evolution, corresponding to a Riemannian
    gradient on the Lie group, to an existing circuit.

    We want to implement the time evolution generated by an operator of the form

    .. math::

        \text{grad} f(U) = sum_i c_i O_i,

    where :math:`O_i` are Pauli words and :math:`c_t \in \mathbb{R}`.
    If ``exact`` is ``False``, we Trotterize this operator and apply the unitary

    .. math::

        U' = \prod_{n=1}^{N_{Trot.}} \left(\prod_i \exp{-it / N_{Trot.} O_i}\right),

    which is then appended to the current circuit.

    If ``exact`` is ``True``, we calculate the exact time evolution for the Riemannian gradient
     by way of the matrix exponential.

    .. math:

        U' = \exp{-it \text{grad} f(U)}

    and append this unitary.

    Args:
        tape (QuantumTape or .QNode): circuit to transform.
        riemannian_gradient (.Hamiltonian): Hamiltonian object representing the Riemannian gradient.
        t (float): time evolution parameter.
        n (int): number of Trotter steps.

    """
    for obj in tape.operations:
        qml.apply(obj)
    if exact:
        qml.QubitUnitary(
            expm(-1j * t * qml.utils.sparse_hamiltonian(riemannian_gradient).toarray()),
            wires=range(len(riemannian_gradient.wires)),
        )
    else:
        qml.templates.ApproxTimeEvolution(riemannian_gradient, t, n)

    for obj in tape.measurements:
        qml.apply(obj)
Exemplo n.º 21
0
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
Exemplo n.º 22
0
    def test_two_qubit_decomposition_tensor_products(self, U_pair, wires):
        """Test that a two-qubit tensor product matrix is correctly decomposed."""
        U = _convert_to_su4(
            qml.math.kron(np.array(U_pair[0]), np.array(U_pair[1])))

        assert _compute_num_cnots(U) == 0

        obtained_decomposition = two_qubit_decomposition(U, wires=wires)
        assert len(obtained_decomposition) == 2

        with qml.tape.QuantumTape() as tape:
            for op in obtained_decomposition:
                qml.apply(op)

        obtained_matrix = get_unitary_matrix(tape, wire_order=wires)()

        assert check_matrix_equivalence(U, obtained_matrix, atol=1e-7)
Exemplo n.º 23
0
    def test_default_queue_operation_inside(self):
        """Test applying an operation instantiated within the queuing
        context to the existing active queue"""
        with qml.tape.QuantumTape() as tape:
            op1 = qml.PauliZ(0)
            op2 = qml.apply(op1)

        assert tape.operations == [op1, op2]
Exemplo n.º 24
0
    def test_general_operations(self, op):
        """Test that a variety of operations produce a rectangle across relevant wires
        and a correct label text."""

        with QuantumTape() as tape:
            qml.apply(op)

        _, ax = tape_mpl(tape)

        num_wires = len(op.wires)
        assert ax.texts[num_wires].get_text() == op.label()

        assert isinstance(ax.patches[0], mpl.patches.Rectangle)
        assert ax.patches[0].get_xy() == (-0.4, -0.4)
        assert ax.patches[0].get_width() == 0.8
        assert ax.patches[0].get_height() == num_wires - 0.2

        plt.close()
Exemplo n.º 25
0
    def test_measurements(self, measurements, wires):
        """Tests a variety of measurements draw measurement boxes on the correct wires."""

        with QuantumTape() as tape:
            for m in measurements:
                qml.apply(m)

        _, ax = tape_mpl(tape)

        assert len(ax.patches) == 3 * len(wires)

        for ii, w in enumerate(wires):
            assert ax.patches[3 * ii].get_xy() == (0.6, w - 0.4)  # rectangle
            assert ax.patches[3 * ii + 1].center == (1, w + 0.05)  # arc
            assert isinstance(ax.patches[3 * ii + 2],
                              mpl.patches.FancyArrow)  # fancy arrow

        plt.close()
Exemplo n.º 26
0
    def test_two_qubit_decomposition_tensor_products_torch(
            self, U_pair, wires):
        """Test that a two-qubit tensor product in Torch is correctly decomposed."""
        torch = pytest.importorskip("torch")

        U1 = torch.tensor(U_pair[0], dtype=torch.complex128)
        U2 = torch.tensor(U_pair[1], dtype=torch.complex128)
        U = qml.math.kron(U1, U2)

        obtained_decomposition = two_qubit_decomposition(U, wires=wires)

        with qml.tape.QuantumTape() as tape:
            for op in obtained_decomposition:
                qml.apply(op)

        obtained_matrix = get_unitary_matrix(tape, wire_order=wires)()

        assert check_matrix_equivalence(U, obtained_matrix, atol=1e-7)
Exemplo n.º 27
0
    def test_two_qubit_decomposition_3_cnots(self, U, wires):
        """Test that a two-qubit matrix using 3 CNOTs is correctly decomposed."""
        U = _convert_to_su4(np.array(U))

        assert _compute_num_cnots(U) == 3

        obtained_decomposition = two_qubit_decomposition(U, wires=wires)
        assert len(obtained_decomposition) == 10

        with qml.tape.QuantumTape() as tape:
            for op in obtained_decomposition:
                qml.apply(op)

        obtained_matrix = get_unitary_matrix(tape, wire_order=wires)()

        # We check with a slightly great tolerance threshold here simply because the
        # test matrices were copied in here with reduced precision.
        assert check_matrix_equivalence(U, obtained_matrix, atol=1e-7)
Exemplo n.º 28
0
    def test_different_queue_operation_inside(self):
        """Test applying an operation instantiated within the queuing
        context to a specfied queuing context"""
        with qml.tape.QuantumTape() as tape1:
            with qml.tape.QuantumTape() as tape2:
                op1 = qml.PauliZ(0)
                op2 = qml.apply(op1, tape1)

        assert tape1.operations == [tape2, op2]
        assert tape2.operations == [op1]
Exemplo n.º 29
0
    def test_two_qubit_decomposition_jax(self, U, wires):
        """Test that a two-qubit operation in JAX is correctly decomposed."""
        jax = pytest.importorskip("jax")

        from jax.config import config

        remember = config.read("jax_enable_x64")
        config.update("jax_enable_x64", True)

        U = jax.numpy.array(U, dtype=jax.numpy.complex128)

        obtained_decomposition = two_qubit_decomposition(U, wires=wires)

        with qml.tape.QuantumTape() as tape:
            for op in obtained_decomposition:
                qml.apply(op)

        obtained_matrix = get_unitary_matrix(tape, wire_order=wires)()

        assert check_matrix_equivalence(U, obtained_matrix, atol=1e-7)
Exemplo n.º 30
0
    def test_compare_analytic_and_numeric_gradients(self, op, mocker, tol):
        """Test for selected gates that the gradients of circuits match between the
        finite difference and analytic methods."""

        with ReversibleTape() as tape:
            qml.Hadamard(wires=0)
            qml.RX(0.543, wires=0)
            qml.CNOT(wires=[0, 1])

            qml.apply(op)

            qml.Rot(1.3, -2.3, 0.5, wires=[0])
            qml.RZ(-0.5, wires=0)
            qml.RY(0.5, wires=1)
            qml.CNOT(wires=[0, 1])

            qml.expval(qml.PauliX(wires=0))
            qml.expval(qml.PauliZ(wires=1))

        dev = qml.device("default.qubit", wires=2)
        res = tape.execute(dev)

        tape._update_gradient_info()
        tape.trainable_params = set(range(1, 1 + op.num_params))

        # check that every parameter is analytic
        for i in range(op.num_params):
            assert tape._par_info[1 + i]["grad_method"][0] == "A"

        grad_F = tape.jacobian(dev, method="numeric")

        spy = mocker.spy(ReversibleTape, "analytic_pd")
        spy_execute = mocker.spy(tape, "execute_device")
        grad_A = tape.jacobian(dev, method="analytic")
        spy.assert_called()

        # check that the execute device method has only been called
        # once, for all parameters.
        spy_execute.assert_called_once()

        assert np.allclose(grad_A, grad_F, atol=tol, rtol=0)