qml.cond

cond(condition, true_fn=None, false_fn=None, elifs=())[source]

Quantum-compatible if-else conditionals — condition quantum operations on parameters such as the results of mid-circuit qubit measurements.

This method is restricted to simply branching on mid-circuit measurement results when it is not used with the qjit() decorator.

When used with the qjit() decorator, this function allows for general if-elif-else constructs. All true_fn, false_fn and elifs branches will be captured by Catalyst, the just-in-time (JIT) compiler, with the executed branch determined at runtime. For more details, please see catalyst.cond().

Note

With the Python interpreter, support for cond() is device-dependent. If a device doesn’t support mid-circuit measurements natively, then the QNode will apply the defer_measurements() transform.

Note

When used with qjit(), this function only supports the Catalyst compiler. See catalyst.cond() for more details.

Please see the Catalyst quickstart guide, as well as the sharp bits and debugging tips.

Note

When used with pennylane.capture.enabled(), this function allows for general if-elif-else constructs. As with the JIT mode, all branches are captured, with the executed branch determined at runtime.

Each branch can receive arguments, but the arguments must be JAX-compatible. If a branch returns one or more variables, every other branch must return the same abstract values.

Parameters
  • condition (Union[MeasurementValue, bool]) – a conditional expression that may involve a mid-circuit measurement value (see pennylane.measure()).

  • true_fn (callable) – The quantum function or PennyLane operation to apply if condition is True

  • false_fn (callable) – The quantum function or PennyLane operation to apply if condition is False

  • elifs (Sequence(Tuple(bool, callable))) – A sequence of (bool, elif_fn) clauses. Can only be used when decorated by qjit() or if the condition is not a mid-circuit measurement.

Returns

A new function that applies the conditional equivalent of true_fn. The returned function takes the same input arguments as true_fn.

Return type

function

Example

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

@qml.qnode(dev)
def qnode(x, y):
    qml.Hadamard(0)
    m_0 = qml.measure(0)
    qml.cond(m_0, qml.RY)(x, wires=1)

    qml.Hadamard(2)
    qml.RY(-np.pi/2, wires=[2])
    m_1 = qml.measure(2)
    qml.cond(m_1 == 0, qml.RX)(y, wires=1)
    return qml.expval(qml.Z(1))
>>> first_par = np.array(0.3)
>>> sec_par = np.array(1.23)
>>> qnode(first_par, sec_par)
tensor(0.32677361, requires_grad=True)

Note

If the first argument of cond is a measurement value (e.g., m_0 in qml.cond(m_0, qml.RY)), then m_0 == 1 is considered internally.

Warning

Expressions with boolean logic flow using operators like and, or and not are not supported as the condition argument.

While such statements may not result in errors, they may result in incorrect behaviour.

In just-in-time (JIT) mode using the qjit() decorator,

dev = qml.device("lightning.qubit", wires=1)

@qml.qjit
@qml.qnode(dev)
def circuit(x: float):
    def ansatz_true():
        qml.RX(x, wires=0)
        qml.Hadamard(wires=0)

    def ansatz_false():
        qml.RY(x, wires=0)

    qml.cond(x > 1.4, ansatz_true, ansatz_false)()

    return qml.expval(qml.Z(0))
>>> circuit(1.4)
Array(0.16996714, dtype=float64)
>>> circuit(1.6)
Array(0., dtype=float64)

Additional ‘else-if’ clauses can also be included via the elif argument:

@qml.qjit
@qml.qnode(dev)
def circuit(x):

    def true_fn():
        qml.RX(x, wires=0)

    def elif_fn():
        qml.RY(x, wires=0)

    def false_fn():
        qml.RX(x ** 2, wires=0)

    qml.cond(x > 2.7, true_fn, false_fn, ((x > 1.4, elif_fn),))()
    return qml.expval(qml.Z(0))
>>> circuit(1.2)
Array(0.13042371, dtype=float64)

Note

If the above syntax is used with a QNode that is not decorated with qjit() and none of the predicates contain mid-circuit measurements, qml.cond will fall back to using native Python if-elif-else blocks.

Conditional quantum functions

The cond transform allows conditioning quantum functions too:

dev = qml.device("default.qubit")

def qfunc(par, wires):
    qml.Hadamard(wires[0])
    qml.RY(par, wires[0])

@qml.qnode(dev)
def qnode(x):
    qml.Hadamard(0)
    m_0 = qml.measure(0)
    qml.cond(m_0, qfunc)(x, wires=[1])
    return qml.expval(qml.Z(1))
>>> par = np.array(0.3)
>>> qnode(par)
tensor(0.3522399, requires_grad=True)

Postprocessing multiple measurements into a condition

The Boolean condition for cond may consist of arithmetic expressions of one or multiple mid-circuit measurements:

def cond_fn(mcms):
    first_term = np.prod(mcms)
    second_term = (2 ** np.arange(len(mcms))) @ mcms
    return (1 - first_term) * (second_term > 3)

@qml.qnode(dev)
def qnode(x):
    ...
    mcms = [qml.measure(w) for w in range(4)]
    qml.cond(cond_fn(mcms), qml.RX)(x, wires=4)
    ...
    return qml.expval(qml.Z(1))

Passing two quantum functions

In the qubit model, single-qubit measurements may result in one of two outcomes. Such measurement outcomes may then be used to create conditional expressions.

According to the truth value of the conditional expression passed to cond, the transform can apply a quantum function in both the True and False case:

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

def qfunc1(x, wires):
    qml.Hadamard(wires[0])
    qml.RY(x, wires[0])

def qfunc2(x, wires):
    qml.Hadamard(wires[0])
    qml.RZ(x, wires[0])

@qml.qnode(dev)
def qnode1(x):
    qml.Hadamard(0)
    m_0 = qml.measure(0)
    qml.cond(m_0, qfunc1, qfunc2)(x, wires=[1])
    return qml.expval(qml.Z(1))
>>> par = np.array(0.3)
>>> qnode1(par)
tensor(-0.1477601, requires_grad=True)

The previous QNode is equivalent to using cond twice, inverting the conditional expression in the second case using the ~ unary operator:

@qml.qnode(dev)
def qnode2(x):
    qml.Hadamard(0)
    m_0 = qml.measure(0)
    qml.cond(m_0, qfunc1)(x, wires=[1])
    qml.cond(~m_0, qfunc2)(x, wires=[1])
    return qml.expval(qml.Z(1))
>>> qnode2(par)
tensor(-0.1477601, requires_grad=True)

Quantum functions with different signatures

It may be that the two quantum functions passed to qml.cond have different signatures. In such a case, lambda functions taking no arguments can be used with Python closure:

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

def qfunc1(x, wire):
    qml.Hadamard(wire)
    qml.RY(x, wire)

def qfunc2(x, y, z, wire):
    qml.Hadamard(wire)
    qml.Rot(x, y, z, wire)

@qml.qnode(dev)
def qnode(a, x, y, z):
    qml.Hadamard(0)
    m_0 = qml.measure(0)
    qml.cond(m_0, lambda: qfunc1(a, wire=1), lambda: qfunc2(x, y, z, wire=1))()
    return qml.expval(qml.Z(1))
>>> par = np.array(0.3)
>>> x = np.array(1.2)
>>> y = np.array(1.1)
>>> z = np.array(0.3)
>>> qnode(par, x, y, z)
tensor(-0.30922805, requires_grad=True)