qml.queuing.apply

apply(op, context=<class 'pennylane.queuing.QueuingManager'>)[source]

Apply an instantiated operator or measurement to a queuing context.

Parameters:
  • op (.Operator or .MeasurementProcess) – the operator or measurement to apply/queue

  • context (.QueuingManager) – The queuing context to queue the operator to. Note that if no context is specified, the operator is applied to the currently active queuing context.

Returns:

the input operator is returned for convenience

Return type:

.Operator or .MeasurementProcess

Example

In PennyLane, operations and measurements are ‘queued’ or added to a circuit when they are instantiated.

The apply function can be used to add operations that might have already been instantiated elsewhere to the QNode:

op = qml.RX(0.4, wires=0)
dev = qml.device("default.qubit", wires=2)

@qml.qnode(dev)
def circuit(x):
    qml.RY(x, wires=0)  # applied during instantiation
    qml.apply(op)  # manually applied
    return qml.expval(qml.Z(0))
>>> print(qml.draw(circuit)(0.6))
0: ──RY(0.6)──RX(0.4)──┤ ⟨Z⟩

It can also be used to apply functions repeatedly:

@qml.qnode(dev)
def circuit(x):
    qml.apply(op)
    qml.RY(x, wires=0)
    qml.apply(op)
    return qml.expval(qml.Z(0))
>>> print(qml.draw(circuit)(0.6))
0: ──RX(0.4)──RY(0.6)──RX(0.4)──┤ ⟨Z⟩

Warning

If you use apply on an operator that has already been queued, it will be queued for a second time. For example:

@qml.qnode(dev)
def circuit():
    op = qml.Hadamard(0)
    qml.apply(op)
    return qml.expval(qml.Z(0))
>>> print(qml.draw(circuit)())
0: ──H──H─┤  <Z>

Instantiated measurements can also be applied to queuing contexts using apply:

meas = qml.expval(qml.Z(0) @ qml.Y(1))
dev = qml.device("default.qubit", wires=2)

@qml.qnode(dev)
def circuit(x):
    qml.RY(x, wires=0)
    qml.CNOT(wires=[0, 1])
    return qml.apply(meas)
>>> print(qml.draw(circuit)(0.6))
 0: ──RY(0.6)──╭●──╭┤ ⟨Z ⊗ Y⟩
 1: ───────────╰X──╰┤ ⟨Z ⊗ Y⟩

By default, apply will queue operators to the currently active queuing context.

When working with low-level queuing contexts such as quantum tapes, the desired context to queue the operation to can be explicitly passed:

with qml.tape.QuantumTape() as tape1:
    qml.Hadamard(wires=1)

    with qml.tape.QuantumTape() as tape2:
        # Due to the nesting behaviour of queuing contexts,
        # tape2 will be queued to tape1.

        # The following PauliX operation will be queued
        # to the active queuing context, tape2, during instantiation.
        op1 = qml.X(0)

        # We can use qml.apply to apply the same operation to tape1
        # without leaving the tape2 context.
        qml.apply(op1, context=tape1)

        qml.RZ(0.2, wires=0)

    qml.CNOT(wires=[0, 1])
>>> tape1.operations
[H(1), <QuantumTape: wires=[0], params=1>, X(0), CNOT(wires=[0, 1])]
>>> tape2.operations
[X(0), RZ(0.2, wires=[0])]