Using QMOD Classical Types¶
QMOD functions may take classical parameters of different types - scalars, lists, and
structs. When declaring and calling QMOD functions in Python, the builtin Python scalar
types are used - int
, float
, and bool
. int
is also used to represent QMOD enumerated
types. The builtin Python list
(or typing.List
) is used for QMOD lists. Each QMOD struct
type has a corresponding Python class definition.
Defining and using structs¶
QMOD struct types can be declared by writing a Python class and decorating it with
the QStruct
decorator. With this decorator, a class behaves like a Python data-class
(i.e. a class decorated with dataclasses.dataclass
). Note that subclassing is not
supported for QMOD structs.
In the following example a struct with two scalar fields is declared and used by a function:
from classiq import QStruct, QFunc, QParam, QBit, repeat, PHASE
@QStruct
class MyStruct:
count: int
angle: float
@QFunc
def foo(ms: QParam[MyStruct], qv: QBit) -> None:
repeat(
count=ms.count,
iteration=lambda index, qbv: PHASE(ms.angle + index, qbv),
qbv=qv,
)
Using aggregate types¶
QMOD lists can store elements of any QMOD type, as long as they are of the same
type. List elements are accessed with the native Python subscript ([...]
) operator,
and list literals are specified using native Python constructs.
QMOD structs can have fields of any QMOD types. Struct fields are accessed with the
native Python dot (.
) operator. Native class constructor is used to specify the
literal value of a struct. Note that recursive struct definition is not allowed.
Here is a more elaborate example, in which structs and lists are aggregated to form a
data-structure. An instances of the struct is created in function main
and queried in
different contexts within function foo
.
from typing import List
from classiq import QStruct, QFunc, QParam, QBit, QArray, Output, PHASE, allocate
@QStruct
class MyStruct:
position: List[int]
angle: float
@QStruct
class YourStruct:
msl: List[MyStruct]
@QFunc
def foo(ys: QParam[YourStruct], index: QParam[int], qbv: QArray[QBit, 5]) -> None:
PHASE(ys.msl[index].angle + 0.5, qbv[ys.msl[index].position[0]]),
@QFunc
def main(res: Output[QArray[QBit]]) -> None:
ys = YourStruct(
msl=[
MyStruct(position=[1, 2], angle=0.1),
MyStruct(position=[0, 3, 4], angle=0.2),
],
)
allocate(5, res)
H(res[0])
foo(ys, 0, res)
Fixed-sized Arrays¶
In addition to lists, QMOD supports array types, which declare both the element type and number of elements. The main application of array types is to define execution parameters of a model, typically in hybrid algorithms.
Array parameters are declared with type hint of the form Array[<element-type>, <size>]
.
The operators [<index>]
and <array>.len()
apply to arrays in the same way as lists.
In the example below function main
expects an array of 6 angle values, and applies
an X rotation to the respective qubit in its output.
from classiq import (
QFunc,
QParam,
QBit,
Array,
Output,
QArray,
RX,
allocate,
repeat,
)
@QFunc
def main(angle: QParam[Array[float, 6]], res: Output[QArray[QBit, 6]]) -> None:
allocate(6, res)
repeat(
count=angle.len(),
iteration=lambda index, qbv: RX(angle[index], qbv[index]),
qbv=res,
)
Builtin types¶
Struct types required for builtin functions are available for use (see the full list
of builtin structs under classiq/qmod/builtins.py
). In the following example the
builtin function suzuki_trotter
is used, which in turn takes a list of struct type
PauliTerm
as parameter.
from classiq import (
Output,
QArray,
QBit,
allocate,
suzuki_trotter,
PauliTerm,
QFunc,
Pauli,
)
@QFunc
def main(res: Output[QArray[QBit]]) -> None:
allocate(3, res)
suzuki_trotter(
pauli_operator=[
PauliTerm(pauli=[Pauli.X, Pauli.X, Pauli.Z], coefficient=1),
PauliTerm(pauli=[Pauli.Y, Pauli.X, Pauli.Y], coefficient=0.5),
],
evolution_coefficient=0.7,
order=4,
repetitions=2,
qbv=res,
)
Note that a native Python enum class Pauli
can be used for
the corresponding QMOD enumerated type.