qml.cond

cond(condition, true_fn, 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.

Parameters
  • condition (Union[MeasurementValue, bool]) – a conditional expression involving a mid-circuit measurement value (see pennylane.measure()). This can only be of type bool when decorated by qjit().

  • 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 (List(Tuple(bool, callable))) – A list of (bool, elif_fn) clauses. Can only be used when is decorated by qjit().

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, requires_grad=True)
>>> sec_par = np.array(1.23, requires_grad=True)
>>> 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)
>>> circuit(1.6)
array(0.)

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)

Conditional quantum functions

The cond transform allows conditioning quantum functions too:

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

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, requires_grad=True)
>>> qnode(par)
tensor(0.3522399, requires_grad=True)

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, requires_grad=True)
>>> 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, requires_grad=True)
>>> x = np.array(1.2, requires_grad=True)
>>> y = np.array(1.1, requires_grad=True)
>>> z = np.array(0.3, requires_grad=True)
>>> qnode(par, x, y, z)
tensor(-0.30922805, requires_grad=True)