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
[Hadamard(wires=[1]), <QuantumTape: wires=[0], params=1>, X(0), CNOT(wires=[0, 1])]
>>> tape2.operations
[X(0), RZ(0.2, wires=[0])]