> ## Documentation Index
> Fetch the complete documentation index at: https://prod-mint.classiq.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Qmod Tutorial - Part 2

<Card title="View on GitHub" icon="github" href="https://github.com/Classiq/classiq-library/blob/main/tutorials/basic_tutorials/the_classiq_tutorial/Qmod_tutorial_part2.ipynb">
  Open this notebook in GitHub to run it yourself
</Card>

In this tutorial, we keep extending our expressive power by introducing more advanced topics:

* Exponentiation and Pauli Operators.
* Arithmetics and numeric assignment.
* The `within_apply` statement.
* The `bind` statement.

Please make sure to go through execises 1-5 of part 1 before continuing with this notebook.

## Exercise 7

* Exponentiation and Pauli Operators

The Qmod language supports different classical types: scalars, arrays, and structs.

Structs are objects with member variables or fields.

See [classical types](https://docs.classiq.io/latest/qmod-reference/language-reference/classical-types/#structs).

The built-in `PauliTerm` struct type is defined as follows:

```python theme={null}
from dataclasses import dataclass

from classiq import *


@dataclass
class PauliTerm:
    pauli: CArray[Pauli]
    coefficient: CReal
```

Note that `Pauli` is an enum for all the Pauli matrices (I, X, Y, Z).

A Pauli-based Hamiltonian can be represented as a list of `PauliTerm`s. A Pauli operator defined this way is the argument to Hamiltonian evolution functions.

This exercise uses the Suzuki-Trotter function to find the evolution of `H=0.5XZXX + 0.25YIZI + 0.3 XIZY` (captured as a literal value for the Pauli operator), with the evolution coefficient being 3, the order being 2, and using 4 repetitions.

See [suzuki\_trotter](https://docs.classiq.io/latest/qmod-reference/library-reference/core-library-functions/hamiltonian_evolution/suzuki_trotter/suzuki_trotter/).

To complete this exercise, allocate q and invoke the `suzuki_trotter` quantum function:

<Accordion title="HINT">
  suzuki\_trotter(<br />
   ...,<br />
   evolution\_coefficient=3,<br />
   repetitions=4,<br />
   order=2,<br />
   qbv=q,<br />
  )
</Accordion>

```python theme={null}
from classiq import *


@qfunc
def main(q: Output[QArray[QBit]]) -> None:
    allocate(4, q)

    # Your code here:


qprog = synthesize(main)
show(qprog)
```

## Exercise 8

* Basic Arithmetics

This exercise uses quantum numeric variables and calculates expressions over them.

See details on the syntax of numeric types in [quantum types](https://docs.classiq.io/latest/qmod-reference/language-reference/quantum-types/#syntax).

See more on quantum expressions in [numeric assignment](https://docs.classiq.io/latest/qmod-reference/language-reference/statements/assignment/).

#

## Exercise 8a

Create this quantum program:

1. Initialize variables `x=2`, `y=7` and compute `res = x + y`.
2. Initialize variables `x=2`, `y=7` and compute `res = x * y`.
3. Initialize variables `x=2`, `y=7`, `z=1` and compute `res = x * y - z`.

Guidance:

* Use the `|=` operators to perform out-of-place assignment of arithmetic expressions.
* To initialize the variables, use the `|=` to assgin it with a numerical value.

```python theme={null}
from classiq import *

# Your code here:


qprog = synthesize(main)
show(qprog)
```

#

## Exercise 8b

1. Declare `x` to be a 2-qubit numeric variable and `y` a 3-qubit numeric variable.
2. Use `prepare_state` to initialize `x` to an equal superposition of `0` and `2`, and `y` to an equal superposition of `1`, `2`, `3`, and `6` (see [prepare\_state](https://docs.classiq.io/latest/qmod-reference/library-reference/core-library-functions/prepare_state_and_amplitudes/prepare_state_and_amplitudes/)).

You can set the error bound to

0.
1. Compute `res = x + y`.

Execute the resulting circuit.

What did you get?

```python theme={null}
from classiq import *

# Your code here:


qprog = synthesize(main)
show(qprog)
```

## Exercise 9

* Within-Apply

The within-apply statement applies the $U^\dagger V U$ pattern that appears frequently in quantum computing.
It allows you to compute a function `V` within the context of another function `U`, and afterward uncompute `U` to release auxiliary qubits storing intermediate results.

See [within apply](https://docs.classiq.io/latest/qmod-reference/language-reference/statements/within-apply/).

#

## Exercise 9a

This exercise uses `within-apply` to compute an arithmetic expression in steps.

Use the `within_apply` operation to calculate `res = x + y + z` from a two-variable addition building block with these 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.
* Use the function syntax of numeric assignment.

Execute the circuit and make sure you obtain the expected result.

```python theme={null}
from classiq import *

# Your code here:


qprog = synthesize(main)
show(qprog)
```

#

## Exercise 9b

Why use `within-apply` and not just write three concatenated functions?
To understand the motivation, create another arithmetic circuit.

This time, however, set the Classiq synthesis engine to optimize on the circuit’s number of qubits; i.e., its width.

Determine constraints with the `set_constraints` operation. (See [here](https://docs.classiq.io/latest/user-guide/synthesis/constraints/)).

Perform the operation `res = w + x + y + z`, where w is initialized to 4 and the rest as before:

1. Add `x` and `y` (as part of the `within_apply` operation)
2. Add the result to `z` (as part of the `within_apply` operation)
3. Uncompute the result of the first operation (as part of the `within_apply` operation)
4. Add the result of the second operation to `w`.

There is no need to perform another uncomputation, as this brings the 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?

```python theme={null}
from classiq import *

# Your code here:


qprog = synthesize(main)
show(qprog)
```

#

## Bonus: Use a Single Arithmetic Expression

What happens when you don't manually decompose this expression?

Use the Classiq 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 do you notice?

```python theme={null}
from classiq import *

# Your code here:


qprog = synthesize(main)
show(qprog)
```

## Exercise 10

* In-place Arithmetics

This exercise uses quantum numeric variables that represent fixed-point reals.

Arithmetic expressions can be calculated in place into a target variable, without allocating new qubits to store the result.

This is done using the in-place-xor operator (`^=`) or the in-place-add operator (`+=`).

See [numeric assignment](https://docs.classiq.io/latest/qmod-reference/language-reference/statements/assignment/).

In-place assignment is often used to nest arithmetic expressions under quantum operators.

Note that out-of-place assignment requires its left-value 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:

$$
f(x) = \begin{cases}
      2x + 1 & \text{ if } 0 \leq x < 0.5 \\
      x + 0.5 & \text{ if } 0.5 \leq x < 1
   \end{cases}
$$

Note: 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.

```python theme={null}
from classiq import *


@qfunc
def main(x: Output[QNum[3]], res: Output[QNum[5]]) -> None:
    allocate(5, res)
    allocate(3, x)
    hadamard_transform(x)

    # Your code here:


qprog = synthesize(main)
show(qprog)
```

## Exercise 11

* A State-preparation Algorithm

#

## Binding

The `bind` operation smoothly converts between different quantum types and splits or slices bits when necessary.

Here is an example:

```python theme={null}
from classiq import *


@qfunc
def main(res: Output[QArray[QBit]]) -> None:
    x: QArray[QBit] = QArray("x")
    allocate(3, x)
    ...
    lsb = QBit("lsb")
    msb = QNum("msb", 2, False, 0)
    bind(x, [lsb, msb])
    ...
    bind([lsb, msb], res)


qprog = synthesize(main)
show(qprog)
```

The first `bind` operation splits the 3-qubit variable `x` into the 2-qubit and single-qubit `lsb` and `msb` variables, respectively.

After the `bind` operation:

1. The `lsb` and `msb` variables can be operated on separately.
2. The `x` variable returns to its uninitialized state and can no longer be used.

The second `bind` operation concatenates the variables back to the `res` output variable.

For this exercise, fill in the missing code parts in the above snippet and use the `control` statement to manually generate the 3-qubit 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 sequence of operations generates it:

1. Perform the Hadamard transform on all three qubits.
2. Apply a `pi/3` rotation on the LSB conditioned by the MSB being $|0\rangle$ and the second-to-last MSB being $|1\rangle$.

How would you write this condition using a QNum?

To validate your results without looking at the full solution, compare them to running using the Classiq built-in `prepare_state` function.

```python theme={null}
import numpy as np

from classiq import *


@qfunc
def pre_prepared_state(q: Output[QArray]) -> None:
    prepare_state(
        [
            1 / 8,
            1 / 8,
            1 / 8 - np.sqrt(3) / 16,
            1 / 8 + np.sqrt(3) / 16,
            1 / 8,
            1 / 8,
            1 / 8,
            1 / 8,
        ],
        0.0,
        q,
    )


# Your code here:
```

## Solutions

#

## Exercise 7

```python theme={null}
# Solution to Exercise 6:


from classiq import *


@qfunc
def main(q: Output[QArray[QBit]]) -> None:
    allocate(4, q)
    suzuki_trotter(
        0.5 * Pauli.X(0) * Pauli.X(1) * Pauli.Z(2) * Pauli.X(3)
        + 0.25 * Pauli.Z(1) * Pauli.Y(3)
        + 0.3 * Pauli.Y(0) * Pauli.Z(1) * Pauli.X(3),
        evolution_coefficient=3,
        repetitions=4,
        order=2,
        qbv=q,
    )


qprog = synthesize(main)
show(qprog)
```

<Info>
  **Output:**

  ```

  Quantum program link: https://platform.classiq.io/circuit/3AnjpNlwKvcYgGDgzGJECBpnnWS
    

  ```
</Info>

#

## Exercise 8

```python theme={null}
# Solution to Exercise 7a:


from classiq import *


@qfunc
def main(res: Output[QNum]) -> None:
    x = QNum("x")
    y = QNum("y")
    z = QNum("z")
    x |= 2
    y |= 7
    z |= 1
    res |= x + y
    # res |= x * y
    # res |= x * y - z


qprog = synthesize(main)
show(qprog)
```

<Info>
  **Output:**

  ```

  Quantum program link: https://platform.classiq.io/circuit/3AnjpmGJAOfvMdqv2SNExm3T7Fy
    

  ```
</Info>

```python theme={null}
# Solution to Exercise 7b:


from classiq import *


@qfunc
def main(res: Output[QNum]) -> None:
    x = QNum("x")
    y = QNum("y")
    prepare_state([0.5, 0, 0.5, 0.0], 0.0, x)
    prepare_state([0, 0.25, 0.25, 0.25, 0.0, 0.0, 0.25, 0.0], 0.0, y)
    res |= x + y
    drop(x)  # X and y cannot be uncomputed automatically so we leave them as trash.
    drop(y)


qprog = synthesize(main)
show(qprog)
```

<Info>
  **Output:**

  ```

  Quantum program link: https://platform.classiq.io/circuit/3AnjqMhCHOxnJQgBwZei65c28B5
    

  ```
</Info>

#

## Exercise 9

```python theme={null}
# Solution to Exercise 8:


from classiq import *


@qfunc
def main(res: Output[QNum]) -> None:
    x = QNum("x")
    y = QNum("y")
    z = QNum("z")
    x |= 3
    y |= 5
    z |= 2

    temp = QNum("temp")
    within_apply(
        within=lambda: assign(x + y, temp), apply=lambda: assign(temp + z, res)
    )


qprog = synthesize(main)
show(qprog)
```

<Info>
  **Output:**

  ```

  Quantum program link: https://platform.classiq.io/circuit/3Anjr05ltMTmt9DSd72KVO35dmy
    

  ```
</Info>

```python theme={null}
# Solution to the advanced part of Exercise 8:


from classiq import *


@qfunc
def main(res: Output[QNum]) -> None:
    x = QNum("x")
    y = QNum("y")
    z = QNum("z")
    w = QNum("w")
    x |= 3
    y |= 5
    z |= 2
    w |= 4

    temp_xy = QNum("temp_xy")
    xyz = QNum("xyz")
    within_apply(
        within=lambda: assign(x + y, temp_xy),
        apply=lambda: assign(temp_xy + z, xyz),
    )
    res |= xyz + w


const = Constraints(optimization_parameter=OptimizationParameter.WIDTH)
qprog = synthesize(main, constraints=const)
show(qprog)
```

<Info>
  **Output:**

  ```

  Quantum program link: https://platform.classiq.io/circuit/3Anjw2jFMn1bsFuzYjBlDyzbd7x
    

  ```
</Info>

#

## Exercise 10

```python theme={null}
# Solution to Exercise 9:
from classiq import *


@qfunc
def main(x: Output[QNum[3, UNSIGNED, 3]], res: Output[QNum[5, UNSIGNED, 3]]) -> None:
    allocate(5, res)
    allocate(3, x)
    hadamard_transform(x)

    aux = QBit("aux")
    aux |= x < 0.5
    control(
        aux,
        stmt_block=lambda: inplace_xor(2.0 * x + 1.0, res),
        else_block=lambda: inplace_xor(1.0 * x + 0.5, res),
    )


qprog = synthesize(main)
show(qprog)
```

<Info>
  **Output:**

  ```

  Quantum program link: https://platform.classiq.io/circuit/3AnjxPhs4YtGuyF1cmJqwk9tiVE
    

  ```
</Info>

#

## Exercise 11

```python theme={null}
# Solution to Exercise 10:


from classiq import *
from classiq.qmod.symbolic 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)
    bind(x, [lsb, msb])

    control(msb == 1, lambda: RY(pi / 3, lsb))

    bind([lsb, msb], res)


qprog = synthesize(main)
show(qprog)
```

<Info>
  **Output:**

  ```

  Quantum program link: https://platform.classiq.io/circuit/3Anjxt8RKH9oVYGtCIohVrtB6nJ
    

  ```
</Info>
