qml.transforms.insert

insert(tape, op, op_args, position='all', before=False)[source]

Insert an operation into specified points in an input circuit.

Circuits passed through this transform will be updated to have the operation, specified by the op argument, added according to the positioning specified in the position argument. Only single qubit operations are permitted to be inserted.

The type of op can be either a single operation or a quantum function acting on a single wire. A quantum function can be used to specify a sequence of operations acting on a single qubit (see the usage details for more information).

Parameters
  • tape (QNode or QuantumTape or Callable or pennylane.devices.Device) – the input circuit to be transformed.

  • op (callable or Type[Operation]) – the single-qubit operation, or sequence of operations acting on a single qubit, to be inserted into the circuit

  • op_args (tuple or float) – the arguments fed to the operation, either as a tuple or a single float

  • position (str or PennyLane operation or list of operations) – Specification of where to add the operation. Should be one of: "all" to add the operation after all gates (except state preparations); "start" to add the operation to all wires at the start of the circuit (but after state preparations); "end" to add the operation to all wires at the end of the circuit; list of operations to add the operation before or after depending on before.

  • before (bool) – Whether to add the operation before the given operation(s) in position. Default is False and the operation is inserted after.

Returns

The transformed circuit as described in qml.transform.

Return type

qnode (QNode) or quantum function (Callable) or tuple[List[QuantumTape], function] or device (pennylane.devices.Device)

Raises
  • ValueError – if a single operation acting on multiple wires is passed to op

  • ValueError – if the requested position argument is not 'start', 'end' or 'all' OR PennyLane Operation

Example:

The following QNode can be transformed to add noise to the circuit:

from functools import partial

dev = qml.device("default.mixed", wires=2)

@partial(qml.transforms.insert, op=qml.AmplitudeDamping, op_args=0.2, position="end")
@qml.qnode(dev)
def f(w, x, y, z):
    qml.RX(w, wires=0)
    qml.RY(x, wires=1)
    qml.CNOT(wires=[0, 1])
    qml.RY(y, wires=0)
    qml.RX(z, wires=1)
    return qml.expval(qml.Z(0) @ qml.Z(1))

Executions of this circuit will differ from the noise-free value:

>>> f(0.9, 0.4, 0.5, 0.6)
tensor(0.754847, requires_grad=True)
>>> print(qml.draw(f)(0.9, 0.4, 0.5, 0.6))
0: ──RX(0.90)─╭●──RY(0.50)──AmplitudeDamping(0.20)─┤ ╭<Z@Z>
1: ──RY(0.40)─╰X──RX(0.60)──AmplitudeDamping(0.20)─┤ ╰<Z@Z>

Specifying the operation as a quantum function:

Instead of specifying op as a single Operation, we can instead define a quantum function. For example:

def op(x, y, wires):
    qml.RX(x, wires=wires)
    qml.PhaseShift(y, wires=wires)

This operation can be inserted into the following circuit:

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

@qml.qnode(dev)
@partial(qml.transforms.insert, op=op, op_args=[0.2, 0.3], position="end")
def f(w, x, y, z):
    qml.RX(w, wires=0)
    qml.RY(x, wires=1)
    qml.CNOT(wires=[0, 1])
    qml.RY(y, wires=0)
    qml.RX(z, wires=1)
    return qml.expval(qml.Z(0) @ qml.Z(1))

To check this, let’s print out the circuit:

>>> print(qml.draw(f)(0.9, 0.4, 0.5, 0.6))
0: ──RX(0.90)─╭●──RY(0.50)──RX(0.20)──Rϕ(0.30)─┤ ╭<Z@Z>
1: ──RY(0.40)─╰X──RX(0.60)──RX(0.20)──Rϕ(0.30)─┤ ╰<Z@Z>

Transforming tapes:

Consider the following tape:

ops = [
    qml.RX(0.9, wires=0),
    qml.RY(0.4, wires=1),
    qml.CNOT((0,1)),
    qml.RY(0.5, wires=0),
    qml.RX(0.6, wires=1)
]
measurements = [qml.expval(qml.Z(0) @ qml.Z(1))]
tape = qml.tape.QuantumTape(ops, measurements)

We can add the AmplitudeDamping channel to the end of the circuit using:

>>> from pennylane.transforms import insert
>>> [noisy_tape], _ = insert(tape, qml.AmplitudeDamping, 0.05, position="end")
>>> print(qml.drawer.tape_text(noisy_tape, decimals=2))
0: ──RX(0.90)─╭●──RY(0.50)──AmplitudeDamping(0.05)─┤ ╭<Z@Z>
1: ──RY(0.40)─╰X──RX(0.60)──AmplitudeDamping(0.05)─┤ ╰<Z@Z>

Transforming devices:

Consider the following QNode:

dev = qml.device("default.mixed", wires=2)

def f(w, x, y, z):
    qml.RX(w, wires=0)
    qml.RY(x, wires=1)
    qml.CNOT(wires=[0, 1])
    qml.RY(y, wires=0)
    qml.RX(z, wires=1)
    return qml.expval(qml.Z(0) @ qml.Z(1))

qnode = qml.QNode(f, dev)

Execution of the circuit on dev will be noise-free:

>>> qnode(0.9, 0.4, 0.5, 0.6)
tensor(0.86243536, requires_grad=True)

However, noise can be easily added to the device:

>>> dev_noisy = qml.transforms.insert(dev, qml.AmplitudeDamping, 0.2)
>>> qnode_noisy = qml.QNode(f, dev_noisy)
>>> qnode_noisy(0.9, 0.4, 0.5, 0.6)
tensor(0.72945434, requires_grad=True)