Design - Classical Variables and Operations
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 Struct
s (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 thex
qubit array is also a classical integer variable that is used implicitly in therepeat
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 operationX(qubit)
if the classical variableindex
is even, and the classical operationrepeat
applies the quantum operationapply_condition
10 times as the result of evaluatingx.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]);
}
}