Skip to content

Defining and Calling Simple Functions

Use classiq to define a QMOD function by writing a Python function decorated with the @QFunc decorator:

from classiq import QFunc, QParam, QBit, H, PHASE


@QFunc
def foo(n: QParam[int], qv: QBit) -> None:
    H(qv)
    PHASE(n * pi, qv)

The @QFunc decorator relies on Python's type-hint mechanism to create a corresponding QMOD function definition from the specified Python function.

Use the QParam type-hint to specify a classical parameter. Note it symbolically represents a classical parameter of a quantum function, and cannot be used in an arbitrary Python expression (but only as a parameter to other QParam arguments of QFunc objects). QParam inherits from the sympy.Symbol class, and thus can be used in a Sympy expression context.

Use the QBit quantum type-hint to specify an inout port of the function of a single qubit. To specify an input-only or an output-only port, use Input[QBit] or Output[QBit] respectively. Alternatively, you can use QArray[QBit] to declare an array of qubits with an unspecified size (for more details on advanced quantum types, see Quantum Types and Expressions). The size of the array can also depend on previously defined classical parameters, for example:

from classiq import QFunc, QParam, QArray, QBit


@QFunc
def foo(n: QParam[int], qbv: QArray[QBit, "n"]) -> None:
    pass

Inside the body of the @QFunc, you can call other @QFuncs, which results in quantum function calls in the body of the resulting QMOD definition. You can also introduce local quantum-variables, by using the declare_qvar function, specifying the variable's QMOD name and type (not specifying a type will default to QArray[QBit]). You need to allocate the quantum variable using builtin functions allocate or prepare_state before you pass it to a function with an input or an inout port. Many more builtin QMOD functions are available (see the full list of functions and their arguments under classiq/qmod/builtins.py).

In the next example, the local variable a_var must be allocated prior to applying X on it, since it is passed to an inout port. In contrast, the local variable b_var is passed without allocation, because prepare_state has an output-only port and allocates the variable inside.

from classiq import (
    allocate,
    QFunc,
    QBit,
    QArray,
    create_model,
    prepare_state,
    CX,
)


@QFunc
def main() -> None:
    a_var = QBit("a_var")
    allocate(1, a_var)
    X(a_var)
    b_var = QArray("b_var")
    prepare_state(probabilities=[0.25, 0.25, 0.25, 0.25], bound=0.01, out=b_var)

Within a function you can also use Python statements; e.g., a for loop to create multiple calls. (Do not use the n parameter as it is a declarative QParam, not a true Python integer.):

from sympy import pi
from classiq import QFunc, QParam, QBit, H, PHASE, allocate
from classiq import create_model, synthesize, show


@QFunc
def foo(n: QParam[int], qv: QBit) -> None:
    H(target=qv)
    for i in range(4):
        PHASE(theta=(i / n) * pi, target=qv)


@QFunc
def bar(m: QParam[int], qv: QBit) -> None:
    foo(n=m * 2, qv=qv)
    foo(n=m // 2, qv=qv)


@QFunc
def main() -> None:
    qv = QBit("qv")
    allocate(1, qv)
    bar(m=2, qv=qv)


model = create_model(main)
qprog = synthesize(model)
show(qprog)

This results in the following quantum program: circuit_with_for.png

QVar Slicing

When creating a model, you can use Python slicing on QArray[QBit] variables to connect to a port that accepts a smaller number of qubits:

from sympy import pi
from classiq import QFunc, QParam, QArray, QBit, H, PHASE, allocate
from classiq import create_model, synthesize, show


@QFunc
def foo(n: QParam[int], qv: QBit) -> None:
    H(target=qv)
    for i in range(4):
        PHASE(theta=(i / n) * pi, target=qv)


@QFunc
def bar(m: QParam[int], qbv: QArray[QBit, 2]) -> None:
    foo(n=m * 2, qv=qbv[0])
    foo(n=m // 2, qv=qbv[1])


@QFunc
def main() -> None:
    qbv = QArray("qbv")
    allocate(2, qbv)
    bar(m=2, qbv=qbv)


model = create_model(main)
qprog = synthesize(model)
show(qprog)

This results in the following quantum program: slicing.png

Note that the slice also accepts QParam objects and expressions involving them.