The Qmod Workshop  Part 2: HigherLevel Concepts
This is the second part of the Qmod workshop, covering exercises 6 through 10. Make sure to go through Part 1 before continuing with this notebook.
from dataclasses import dataclass
from classiq import *
Exercise 6  Exponentiation and Pauli Operators
The Qmod language supports different classical types: scalars, arrays, and structs. Structs are objects with member variables, or fields.
See also Classical Types
The builtin struct type PauliTerm
is defined as follows:
@dataclass
class PauliTerm:
pauli: CArray[Pauli]
coefficient: CReal
Note that Pauli
is an enum for all the Pauli matrices (I, X, Y, Z).
Pauli based hamiltonian can be represented as a list of PauliTerm
s. A Pauli operator defined this way is the argument to a hamiltonian evolution functions.
In this exercise we will use the SuzukiTrotter function to find the evolution of H=0.5XZXX + 0.25YIZI + 0.3 XIZY
(captured as a literal value for the paulioperator), with the evolution coefficient being 3, the order being 2, and use 4 repetitions.
The declaration of the suzuki_trotter
function is:
@qfunc(external=True)
def suzuki_trotter(
pauli_operator: CArray[PauliTerm],
evolution_coefficient: CReal,
order: CInt,
repetitions: CInt,
qbv: QArray[QBit],
) > None:
pass
Fill in the missing parts of the following code in order to complete this exercise:
@qfunc
def main(q: Output[QArray[QBit]]) > None:
allocate(4, q)
# suzuki_trotter(
# ...,
# evolution_coefficient=3,
# repetitions=4,
# order=2,
# qbv=q,
# )
qmod = create_model(main)
qprog = synthesize(qmod)
show(qprog)
Opening: https://platform.classiq.io/circuit/338a2b73d26c49cc84ba1e42239bc2ea?version=0.41.2
Exercise 7  Basic Arithmetics
Exercise 7a
In this exercise we will use quantum numeric variables and calculate expressions over them.
See details on the syntax of numeric types under Quantum types. See more on quantum expressions under Numeric assignment
Create the following quantum programs:
1. Initialize variables x=2
, y=7
and computes res = x + y
.
2. Initialize variables x=2
, y=7
and computes res = x * y
.
3. Initialize variables x=2
, y=7
, z=1
and computes res = x * y  z
.
Guidance:
* Use the operator =
to perform outofplace assignment of arithmetic expression.
* To initialize the variables, use the function prepare_int
.
# Your code here:
Exercise 7b
Declare x
to be a 2qubit variable and y
to be 3qubit variable.
We will perform an addition of two superposition states: x
is an equal superposition of 0
and 2
, and y
is an equal superposition of 1
, 2
, 3
, and 6
.
 Use
prepare_state
to initializex
andy
. Note thatprepare_state
works with probabilities, not amplitudes. The declaration of theprepare_state
function is:(Set the bound to 0 in your code)@qfunc(external=True) def prepare_state( probabilities: CArray[CReal], bound: CReal, out: Output[QArray[QBit]], ) > None: pass
 Compute
res = x + y
. Execute the resulting circuit. What did you get?
# Your code here:
Exercise 8  WithinApply
The withinapply statement applies the pattern U_dagger V U
that appears frequently in quantum computing.
It allows you to compute some function V
within the context of another function U
, and afterward uncompute U
in order to release auxiliary qubits storing intermediate results.
See also Within Apply.
Exercise 8a
In this exercise, we will use withinapply to compute an arithmetic expression in steps.
Use the within_apply
operation to calculate res = x + y + z
from a twovariable addition building block with the following steps:
1. Add x
and y
2. Add the result to z
3. Uncompute the result of the first operation
For simplicity, initialize the registers to simple integers: x=3
, y=5
, z=2
.
Hints:
 Use a temporary variable.
 Wrap the arithmetic operation in a function.
Execute the circuit and make sure you obtain the expected result.
# Your code here:
Exercise 8b
Why should we use withinapply
and not just write three concatenated functions?
To understand the motivation, we will create another arithmetic circuit.
This time, however, we will also set Classiq’s synthesis engine to optimize on the circuit’s number of qubits, i.e., its width.
Setting constraints can be done via the set_constraints
operation  see here.
Perform the operation res = w + x + y + z
, where w is initialized to 4 and the rest as before:
 Add
x
andy
(as part of thewithin_apply
operation)  Add the result to
z
(as part of the within_apply operation)  Uncompute the result of the first operation (as part of the
within_apply
operation)  Add the result of the second operation to
w
. There’s no need to perform another uncomputation, as this brings our calculation to an end.
Create the model, optimize on the circuit’s width, and run the circuit. Can you identify where qubits have been released and reused?
# Your code here:
Bonus: Use a Single Arithmetic Expression
What happens when we don't manually decompose this expression?
Use Classiq’s arithmetic engine to calculate res = x + y + z + w
and optimize for width.
Look at the resulting quantum program  can you identify the computation and uncomputation blocks? What else did you notice?
# Your code here:
Exercise 9  Inplace Arithmetics
For the following exercise we will use numeric quantum variables that represent fixedpoint reals.
Arithmetic expressions can be calculated inplace into a target variable, without allocating new qubits to store the result. This is done using the inplacexor operator.
Inplace assignment is often used to nest arithmetic expressions under quantum operators. Note that outofplace assignment requires its leftvalue variable to be uninitialized, and therefore cannot be used under an operator if the variable is declared outside its scope. Applying operators to arithmetic expressions is required in many algorithms. One example is the piecewise evaluation of mathematical functions  calculating different expressions over x
depending on the subdomain where x
falls.
For this exercise, replace the missing parts in the code snippet below to evaluate the result of:
Notes:

We cannot use
x
directly as the control variable in aconstrol
operator, because it also occurs in the nested scope. to determine ifx
is in the lower or higher half of the domain we duplicate the most significant bit onto a separate variable calledlabel
. 
In Python assignment operators cannot be used in lambda expressions, so the computation of the function needs to be factored out to a named Python function (but not necessarily a Qmod function).
Exercise 10  Statepreparation Algorithm using Quantumif
Binding
The bind
operation allows to convert smoothly between different quantum types and split or slice bits when necessary. Here’s an example:
from math import pi
@qfunc
def main(res: Output[QArray[QBit]]) > None:
x: QArray[QBit] = QArray("x")
allocate(3, x)
hadamard_transform(x)
lsb = QBit("lsb")
msb = QNum("msb", 2, False, 0)
bind(x, [lsb, msb])
control(
msb == 1, lambda: RY(pi / 3, lsb)
) # msb==1 <==> bit1 bit2 == 01 (binary of decimal 1)
bind([lsb, msb], res)
model = create_model(main)
qprog = synthesize(model)
show(qprog)
Opening: https://platform.classiq.io/circuit/462d8604ac4845188d419580a986d56a?version=0.41.2
The first bind
operation splits the 3qubit register x
into the 2qubit and singlequbit registers lsb
and msb
, respectively.
After the bind
operation:
1. The registers lsb
and msb
can be operated on as separated registers.
2. The registerx
is consumed and can no longer be used.
The second bind
operation concatenates the registers to the output register res
.
For this exercise, fill in the missing code parts in the above snippet and use the control
statement to manually generate the following lovely 3qubit probability distribution: [1/8, 1/8, 1/8, sqrt(3)/16, 1/8 + sqrt(3)/16, 1/8, 1/8, 1/8, 1/8]
.
The following series of gates generate it:
Perform the Hadamard transform on all three qubits.
Apply a rotation on the LSB (leastsignificant bit) conditioned by the MSB being 0> and the second to last MSB being 1>. How would you write this condition using a QNum?
The following series of gates generate it:
1. Perform the Hadamard transform on all three qubits.
2. Apply a pi/3
rotation on the LSB (leastsignificant bit) conditioned by the MSB being 0> and the second to last MSB being 1>. How would you write this condition using a QNum?
If you want to validate your results without looking at the full solution, compare them to running using Classiq’s builtin prepare_state
function.
@qfunc
def pre_prepared_state(q: Output[QArray[QBit]]) > None:
prepare_state(
[1 / 8, 1 / 8, 1 / 8, 1 / 8 + 0.10825, 1 / 8  0.10825, 1 / 8, 1 / 8, 1 / 8],
0.01,
q,
)
# Your code here: