Skip to content

Design - Classical Variables and Operations

View on GitHub Experiment in the IDE

Quantum variables and operations lie at the heart of the Qmod language but they are not enough for designing all quantum algorithms. To complete the picture, you may need to perform classical control flow with classical operations over classical variables. Typical Python operations include for loops and if-else statements over int or float variables.

In Qmod, equivalent types for the common Python int, float, and bool types are denoted by int, real, and bool in the native syntax and by CInt, CReal, and CBool in the Python SDK. In addition, arrays of classical values are also supported as well as more configurable data structures called Structs (which are the equivalent of Python dataclasses without methods). See the full list of classical variables and types supported in Qmod.

For now, the only two classical operations supported in Qmod are repeat (which is the equivalent of a Python for loop) and if-else statements. See a detailed description.

Examine how to manage classical control flow in Qmod via this example.

Concrete Example

The task is to create a quantum array with \(10\) qubits in the state of \(|1010101010\rangle\); i.e., a quantum state with alternating qubit states of zeros and ones.

How to do this? Knowing that a general qubit array can be easily initialized to the state of all zeros; i.e., \(|0000000000\rangle\), you need to flip the state of every second qubit or all the qubits in an even position (the qubits in the \(0th\) position, \(2nd\) position, \(4th\) position, etc.).

Approach this task in two steps. First, create a apply_condition function that receives a qubit and its position within the qubit array. If the position is even then it flips its state. Secondly, go over all the qubits in the qubit array and apply the function on each qubit.

The apply_condition function receives a qubit variable named qubit and a classical integer variable index. If the index variable is an even number, apply an X gate on the qubit, which flips its state.

from classiq import *


@qfunc
def apply_condition(index: CInt, qubit: QBit):
    if_(condition=index % 2 == 0, then=lambda: X(qubit))
qfunc apply_condition(index: int, qubit: qbit) {
  if ((index % 2) == 0) {
    X(qubit);
  }
}

The condition of whether the index variable is even evaluated is using the % modulo operation, which calculates the reminder of the index integer divided by \(2\). Note that the Python SDK uses the lambda: construct before the X(qubit) operation. As described in the Quantum Variables and Functions Tutorial, in the Python SDK every function that is given as an argument for another function needs to have the lambda: prefix.

The argument of the main function is a qubit array named x. Initalize it to the state \(|0000000000\rangle\) with \(10\) qubits using the allocate function. Then apply the repeat classical operator, applying the apply_condition function \(10\) times, once per qubit in the x qubit array.

@qfunc
def main(x: Output[QArray]):
    allocate(10, x)
    repeat(count=x.len, iteration=lambda index: apply_condition(index, x[index]))
qfunc main(output x: qbit[]) {
  allocate(10, x);
  repeat (index: x.len) {
    apply_condition(index, x[index]);
  }
}

The repeat operator has two arguments. First, specify the number of repetitions. In this case, set it to the length of the x qubit array. Second, specify the repeated statement (or statements), using the apply_condition function with the index and corresponding qubit x[index] arguments. Take note of the differences in syntax between the native and Python SDK versions when declaring the counting index with the index variable.

That's it, the algorithm is ready for you to synthesize and view:

quantum_program = synthesize(create_model(main))
show(quantum_program)

Evaluate the results by executing the quantum program on the default simulator:

results = execute(quantum_program).result()[0].value
print(results.counts)

The only measured bit string is \(0101010101\), which in Classiq notation is read from right to left when interpreted. Hence, it corresponds to the \(|1010101010\rangle\) state, which is exactly what you tried to achieve!

Summary - Classical Variables and Operations

A summary of the main points:

  • Qmod supports the use of classical variables. The example above explicitly declares and uses the index integer variable. The length of the x qubit array is also a classical integer variable that is used implicitly in the repeat operation.

  • Qmod supports the use of classical operations that receive classical variables and quantum operations that are applied according to the classical operation logic. In the example, the if classical operation applies the quantum operation X(qubit) if the classical variable index is even, and the classical operation repeat applies the quantum operation apply_condition 10 times as the result of evaluating x.len.

All the Code Together

from classiq import *


@qfunc
def apply_condition(index: CInt, qubit: QBit):
    if_(condition=index % 2 == 0, then=lambda: X(qubit))


@qfunc
def main(x: Output[QArray]):
    allocate(10, x)
    repeat(count=x.len, iteration=lambda index: apply_condition(index, x[index]))


quantum_program = synthesize(create_model(main))
show(quantum_program)

results = execute(quantum_program).result()[0].value
print(results.counts)

write_qmod(create_model(main), "classical_variables_and_operations")
qfunc apply_condition(index: int, qubit: qbit) {
  if ((index % 2) == 0) {
    X(qubit);
  }
}

qfunc main(output x: qbit[]) {
  allocate(10, x);
  repeat (index: x.len) {
    apply_condition(index, x[index]);
  }
}