qml.adjoint

adjoint(fn, lazy=True)[source]

Create the adjoint of an Operator or a function that applies the adjoint of the provided function.

Parameters

fn (function or Operator) – A single operator or a quantum function that applies quantum operations.

Keyword Arguments

lazy=True (bool) – If the transform is behaving lazily, all operations are wrapped in a Adjoint class and handled later. If lazy=False, operation-specific adjoint decompositions are first attempted.

Returns

If an Operator is provided, returns an Operator that is the adjoint. If a function is provided, returns a function with the same call signature that returns the Adjoint of the provided function.

Return type

(function or Operator)

Note

The adjoint and inverse are identical for unitary gates, but not in general. For example, quantum channels and observables may have different adjoint and inverse operators.

Note

This function supports a batched operator:

>>> op = qml.adjoint(qml.RX([1, 2, 3], wires=0))
>>> qml.matrix(op).shape
(3, 2, 2)

But it doesn’t support batching of operators:

>>> op = qml.adjoint([qml.RX(1, wires=0), qml.RX(2, wires=0)])
ValueError: The object [RX(1, wires=[0]), RX(2, wires=[0])] of type <class 'list'> is not callable.
This error might occur if you apply adjoint to a list of operations instead of a function or template.

Example

The adjoint transform can accept a single operator.

>>> @qml.qnode(qml.device('default.qubit', wires=1))
... def circuit2(y):
...     qml.adjoint(qml.RY(y, wires=0))
...     return qml.expval(qml.PauliZ(0))
>>> print(qml.draw(circuit2)("y"))
0: ──RY(y)†─┤  <Z>
>>> print(qml.draw(circuit2, expansion_strategy="device")(0.1))
0: ──RY(-0.10)─┤  <Z>

The adjoint transforms can also be used to apply the adjoint of any quantum function. In this case, adjoint accepts a single function and returns a function with the same call signature.

We can create a QNode that applies the my_ops function followed by its adjoint:

def my_ops(a, wire):
    qml.RX(a, wires=wire)
    qml.SX(wire)

dev = qml.device('default.qubit', wires=1)

@qml.qnode(dev)
def circuit(a):
    my_ops(a, wire=0)
    qml.adjoint(my_ops)(a, wire=0)
    return qml.expval(qml.PauliZ(0))

Printing this out, we can see that the inverse quantum function has indeed been applied:

>>> print(qml.draw(circuit)(0.2))
0: ──RX(0.20)──SX──SX†──RX(0.20)†─┤  <Z>

When lazy=False, the function first attempts operation-specific decomposition of the adjoint via the Operator.adjoint() method. Only if an Operator doesn’t have an Operator.adjoint() method is the object wrapped with the Adjoint wrapper class.

>>> qml.adjoint(qml.PauliZ(0), lazy=False)
PauliZ(wires=[0])
>>> qml.adjoint(qml.RX, lazy=False)(1.0, wires=0)
RX(-1.0, wires=[0])
>>> qml.adjoint(qml.S, lazy=False)(0)
Adjoint(S)(wires=[0])