Skip to content

Classical Control Flow

Loops and conditionals on classical expressions are useful means to describe reusable building blocks. Qmod has two basic forms - the repeat statement and the if statement.

Classical Repeat

Syntax

def repeat(count: CInt, iteration: QCallable[CInt]) -> None:
    pass

repeat ( iteration_variable : count ) { iteration-statements }

Semantics

  • Invoke the iteration block count times, binding the index variable to the respective iteration number - 0, 1,... count-1.
  • Inside the statement block, use of quantum variables declared outside it is restricted to contexts where the variable is initialized and remains initialized (see Quantum Variables)

Example

The following example defines a useful function - applying the Hadamard function across all qubits in a qubit array - using repeat. Note that a similar function is available in the Classiq open-library.

from classiq import H, QArray, QBit, qfunc, repeat


@qfunc
def my_hadamard_transform(qba: QArray[QBit]):
    repeat(
        count=qba.len,
        iteration=lambda index: H(qba[index]),
    )
qfunc my_hadamard_transform(qba: qbit[]) {
  repeat (index: qba.len) {
    H(qba[index]);
  }
}

Classical If

Syntax

def if_(condition: CBool, then: QCallable, else_: Optional[QCallable] = None) -> None:
    pass

Note that identifiers in Qmod that happen to conflict with Python keywords have _ suffix. This is the case with if_ and else_ in the second function.

if ( condition ) { then-statements } else { else-statements }

Semantics

  • Invoke the then block if condition evaluates to true and otherwise invoke the else block
  • Inside the statement block, use of quantum variables declared outside it is restricted to contexts where the variable is initialized and remains initialized (see Quantum Variables)

Example

from classiq import CBool, X, Y, QBit, qfunc, if_


@qfunc
def my_conditional_gate(cond: CBool, qb: QBit):
    if_(
        condition=cond,
        then=lambda _: X(qb),
        else_=lambda _: Y(qb),
    )
qfunc my_conditional_gate(cond: bool, qb: qbit) {
  if (cond) {
    X(qb);
  } else {
    Y(qb);
  }
}

Classical Foreach

The foreach statement iterates through the elements of a classical array. Its compilation and simulation are more efficient.

Warning

The foreach statement is an experimental feature. It is only supported in Qmod Python.

Syntax

def foreach(values: CArray | list, iteration: Callable) -> None:
    pass

The iteration callable accepts one or more iteration variables.

Semantics

  • Invoke the iteration block once for every element of values.
  • If the iteration block accepts a single iteration variable, the elements of values will be bound to it sequentially.
  • If values is a nested array (CArray[CArray]), the length of values' elements is \(n\), \(n>=2\), and the iteration block has \(n\) iteration variables, then values' elements will be unpacked into the iteration variables in each iteration.
  • Inside the statement block, use of quantum variables declared outside it is restricted to contexts where the variable is initialized and remains initialized (see Quantum Variables)
  • values must be a value of type CArray[CReal] or CArray[CArray[CReal]].

Examples

In the following example, the foreach statement iterates through the elements of the classical list [0.1, 0.2] and assigns its elements into the iteration variable i. This is equivalent to calling RX and Y twice, once with angle 0.1 and once with 0.2.

from classiq import *


@qfunc
def main(q: Output[QBit]) -> None:
    allocate(q)
    foreach(
        [0.1, 0.2],
        lambda i: [
            RX(i, q),
            Y(q),
        ],
    )

In the following example, the foreach statement iterates through the elements of the classical list [[0.1, 0.2], [0.3, 0.4]] and assigns its elements into the iteration variables i and j. In each iteration, the elements of the nested arrays are "unpacked" into the iteration variables. The first iteration assigns 0.1 into i and 0.2 into j, and the second iteration assigns 0.3 into i and 0.4 into j.

from classiq import *


@qfunc
def main(q: Output[QBit]) -> None:
    allocate(q)
    foreach(
        [[0.1, 0.2], [0.3, 0.4]],
        lambda i, j: [
            RX(i, q),
            RY(j, q),
        ],
    )