diff --git a/community/QClass_2024/Submissions/HW1/Noah_Nzeki_William_HW1_QClass2024.ipynb b/community/QClass_2024/Submissions/HW1/Noah_Nzeki_William_HW1_QClass2024.ipynb new file mode 100755 index 00000000..77d83a26 --- /dev/null +++ b/community/QClass_2024/Submissions/HW1/Noah_Nzeki_William_HW1_QClass2024.ipynb @@ -0,0 +1,544 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# The Qmod Workshop - Introduction\n", + "\n", + "The Classiq platform features a high-level quantum modeling language called Qmod. Qmod is compiled into concrete gate-level implementation using a powerful synthesis engine that optimizes and adapts the implementation to different target hardware/simulation environments.\n", + "\n", + "In this workshop, we will learn how to write quantum models using Qmod. We will be using the Python embedding of Qmod, available as part of the Classiq Python SDK. We will learn basic concepts in the Qmod language, such as functions, operators, quantum variables, and quantum types. We will develop useful building blocks and small algorithms.\n", + "\n", + "The [QMOD language reference](https://docs.classiq.io/latest/user-guide/platform/qmod/language-reference/) covers these concepts more systematically and includes more examples.\n", + "\n", + "This workshop consists of step-by-step exercises. It is structured as follows:\n", + "\n", + "- Part 1: Language Fundamentals - Exercises 1-5\n", + "- Part 2: Higher-Level Concepts - Exercises 6-10\n", + "- Part 3: Execution Flows - Exercises 11, 12\n", + "\n", + "The introduction and Part 1 are included in this notebook. Part 2 and 3 are each in its own separate notebook. For each exercise you will find the solution to the exercises at the bottom of the same notebook.\n", + "\n", + "### Preparations\n", + "\n", + "Make sure you have a Python version of 3.8 through 3.11 installed. Unfortunately, Classiq is not yet supported with Python 3.12.\n", + "\n", + "Install Classiq’s Python SDK by following the instructions on this page: [Getting Started - Classiq](https://docs.classiq.io/latest/getting-started/).\n", + "\n", + "### Python Qmod Exercises - General Instructions\n", + "\n", + "In order to synthesize and execute your Qmod code, you should:\n", + "1. Make sure you define a `main` function that calls functions you create.\n", + "2. Use `create_model` by running `qmod = create_model(main)` to construct a representation of your model.\n", + "3. You can synthesize the model (using `qprog = synthesize(qmod)`) to obtain an implementation - a quantum program.\n", + "4. You can then visualize the quantum program (`show(qprog)`) or execute it (using `execute(qprog)`. See: [Execution - Classiq](https://docs.classiq.io/latest/user-guide/platform/executor/#full-example)). You can also execute it with the IDE after visualizing the circuit.\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Exercise 0: From Model to Execution\n", + "\n", + "The following model defines a function that applies X and H gates on a single qubit, and subsequently calls it:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "from classiq import *\n", + "\n", + "\n", + "# Define a quantum function using the @qfunc decorator\n", + "@qfunc\n", + "def foo(q: QBit) -> None:\n", + " X(target=q)\n", + " H(target=q)\n", + "\n", + "\n", + "# Define a main function\n", + "@qfunc\n", + "def main(res: Output[QBit]) -> None:\n", + " allocate(1, res)\n", + " foo(q=res)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Create a model from it, and synthesize, visualize, and execute it.\n", + "\n", + "Use the General Instructions above to do so.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Opening: https://platform.classiq.io/circuit/f3d7a8a9-b4e5-415b-907b-7545339467c6?version=0.41.1\n", + "Opening in existing browser session.\n" + ] + } + ], + "source": [ + "from classiq import *\n", + "\n", + "# Your code here: \n", + "qmod = create_model(main)\n", + "qprog = synthesize(qmod)\n", + "show(qprog)\n", + "#result = execute(qprog).result()\n", + "#print(result)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In Qmod `QBit` is the simplest quantum type, and in this example, `q` is a quantum variable of type `QBit`. Quantum variables abstract away the mapping of quantum objects to qubits in the actual circuit.\n", + "\n", + "See also [Quantum Variables](https://docs.classiq.io/latest/user-guide/platform/qmod/language-reference/quantum-variables/).\n", + "\n", + "We will discuss other quantum types during the workshop.\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# The Qmod Workshop - Part 1: Language Fundamentals\n", + "\n", + "Follow exercises 1 through 5 for the first session of the workshop." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Exercise 1 - Bell Pair\n", + "\n", + "Create a function that takes two single-qubit (`QBit`) quantum arguments and prepares the bell state on them ([Bell state](https://en.wikipedia.org/wiki/Bell_state)) by applying `H` on one variable and then using it as the control of a `CX` function with the second variable as the target.\n", + "Create a main function that uses this function and has two single-qubit outputs, initialize them to the |0> state (using the `allocate` function), and apply your function to them.\n", + "\n", + "See also [Functions](https://docs.classiq.io/latest/user-guide/platform/qmod/language-reference/functions#syntax)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Opening: https://platform.classiq.io/circuit/4ebd2a6c-14e3-4b14-8628-13534e35c36b?version=0.41.1\n", + "Opening in existing browser session.\n" + ] + } + ], + "source": [ + "from classiq import *\n", + "\n", + "# Your code here:\n", + "@qfunc\n", + "def prep_bell_state(q1: QBit, q2: QBit) -> None:\n", + " H(q1)\n", + " CX(q1, q2)\n", + " \n", + "@qfunc\n", + "def main(res1: Output[QBit], res2: Output[QBit]) -> None:\n", + " allocate(1, res1)\n", + " allocate(1, res2)\n", + " prep_bell_state(res1, res2)\n", + "#\n", + "qmod = create_model(main)\n", + "qprog = synthesize(qmod)\n", + "show(qprog)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Use qubit array subscript (the syntax - _variable_ **[** _index-expression_ **]**) to change the function from subsection 1 to receive a single quantum variable, a qubit array (`QArray`) of size 2.\n", + "Change your main function to declare a single output (also an array of size 2).\n" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Opening: https://platform.classiq.io/circuit/7056fa8c-c9bd-450c-84ea-0e7b866bd8fe?version=0.41.1\n", + "Opening in existing browser session.\n" + ] + } + ], + "source": [ + "from classiq import *\n", + "\n", + "# Your code here:\n", + "@qfunc\n", + "def prep_bell_state(qubits: QArray[QBit, 2]) -> None:\n", + " H(qubits[1])\n", + " CX(qubits[1], qubits[0])\n", + "\n", + "@qfunc\n", + "def main(res: Output[QArray[QBit, 2]]) -> None:\n", + " allocate(2, res) \n", + " prep_bell_state(res)\n", + "\n", + "\n", + "model = create_model(main)\n", + "\n", + "\n", + "qmod = create_model(main)\n", + "qprog = synthesize(qmod)\n", + "show(qprog)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Exercise 2 - Repeat\n", + "\n", + "Use the built-in `repeat` operator to create your own Hadamard transform function (call it `my_hadamard_transform`). The Hadamard transform function is a function that takes as argument a qubit array of an unspecified size and applies `H` to each of its qubit.\n", + "\n", + "See also [Classical repeat](https://docs.classiq.io/latest/user-guide/platform/qmod/language-reference/statements/classical-control-flow/#classical-repeat).\n", + "\n", + "Set your main function to have a quantum array output of unspecified size, allocate 10 qubits, and then apply your Hadamard transform function.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Opening: https://platform.classiq.io/circuit/e0286555-126e-4c74-a26b-130744f21789?version=0.41.1\n" + ] + } + ], + "source": [ + "from classiq import *\n", + "\n", + "# Your code here:\n", + "@qfunc\n", + "def my_hadamard_transform(q: QArray[QBit]) -> None:\n", + " repeat(q.len, lambda i: H(q[i]))\n", + "\n", + "@qfunc\n", + "def main(reg: Output[QArray]): \n", + " allocate(10,reg)\n", + " my_hadamard_transform(reg)\n", + "\n", + "qmod = create_model(main)\n", + "qprog = synthesize(qmod)\n", + "show(qprog)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Note: Quantum Variable Capture\n", + "The `repeat` operator invokes a statement block multiple times. The statement block is specified using a Python callable, typically a lambda expression. Inside the block you can refer to variables declared in the outer function scope.\n", + "This concept is called `quantum variable capture`, equivalent to [capture](https://en.wikipedia.org/wiki/Closure_(computer_programming)) in classical languages.\n", + "\n", + "See also [Capturing context variables and parameters](https://docs.classiq.io/latest/user-guide/platform/qmod/language-reference/operators/#capturing-context-variables-and-parameters)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Exercise 3 - Power\n", + "Raising a quantum operation to a power appears in many known algorithms, for examples, in Grover search and Quantum Phase Estimation.\n", + "For most operations, it simply means repeating the same circuit multiple times.\n", + "\n", + "Sometimes, however, power can be simplified, thus saving computational resources.\n", + "The most trivial example is a quantum operation expressed as a single explicit unitary matrix (i.e., all n*n matrix terms are given) - raising the operation can be done by raising the matrix to that power via classical programming.\n", + "\n", + "See also [Power operator](https://docs.classiq.io/latest/user-guide/platform/qmod/language-reference/statements/quantum-operators/#syntax).\n", + "\n", + "Use the following code to generate a 2-qubit (real) unitary matrix:" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [], + "source": [ + "from typing import List\n", + "\n", + "import numpy as np\n", + "\n", + "from classiq import *\n", + "\n", + "rng = np.random.default_rng(seed=0)\n", + "random_matrix = rng.random((4, 4))\n", + "qr_unitary, _ = np.linalg.qr(random_matrix)\n", + "\n", + "unitary_matrix = QConstant(\"unitary_matrix\", List[List[float]], qr_unitary.tolist())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In order to reuse some classical value we can define a `QConstant` to store that value.\n", + "\n", + "1. Create a model that applies `unitary_matrix` on a 2 qubit variable.\n", + "2. Create another model that applies `unitary_matrix` raised to power 3 on a 2 qubit variable.\n", + "3. Compare the gate count via the Classiq’s IDE in both cases.\n", + "\n", + "Note - the signature of function `unitary` is:" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [], + "source": [ + "def unitary(\n", + " elements: CArray[CArray[CReal]],\n", + " target: QArray[QBit],\n", + ") -> None:\n", + " pass" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Opening: https://platform.classiq.io/circuit/772b8449-7fe3-4534-be64-a90c15ab314b?version=0.41.1\n", + "Opening in existing browser session.\n" + ] + } + ], + "source": [ + "from classiq import *\n", + "\n", + "# Your code here:\n", + "\n", + "@qfunc\n", + "def main(qubit: Output[QArray[QBit]]) -> None:\n", + " allocate(2, qubit)\n", + " power(3, lambda: unitary(unitary_matrix, qubit))\n", + "\n", + "qmod = create_model(main)\n", + "qprog = synthesize(qmod)\n", + "show(qprog)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Exercise 4 - User-defined Operators\n", + "Create a function that applies a given single-qubit operation to all qubits in its quantum argument (Call your function `my_apply_to_all`). Such a function is also called an operator, i.e. a function that one of its arguments is another function (its operand).\n", + "\n", + "See also [Operators](https://docs.classiq.io/latest/user-guide/platform/qmod/language-reference/operators/).\n", + "\n", + "Follow these guidelines:\n", + "1. Your function should declare a quantum argument of type qubit array. It should also declare an argument of a function type with a single qubit argument.\n", + "2. The body should apply the operand to all qubits in the argument.\n", + "\n", + "When you're done, re-implement `my_hadamard_transform` from exercise 2 using this function instead of `repeat`.\n", + "Use the same main function from exercise 2." + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Opening: https://platform.classiq.io/circuit/f6020437-edac-4498-b445-426471d80b88?version=0.41.1\n", + "Opening in existing browser session.\n" + ] + } + ], + "source": [ + "from classiq import *\n", + "\n", + "# Your code here:\n", + "@qfunc\n", + "def my_apply_to_all(my_operand: QCallable[QBit], qubit: QArray[QBit]) -> None:\n", + " repeat(qubit.len, lambda i: my_operand(qubit[i]))\n", + " \n", + "@qfunc\n", + "def my_hadamard_transform(qubit: QArray[QBit]) -> None:\n", + " my_apply_to_all(lambda t: H(t), qubit)\n", + "\n", + "@qfunc\n", + "def main(qubit: Output[QArray[QBit]]) -> None:\n", + " allocate(10, qubit)\n", + " my_hadamard_transform(qubit)\n", + "\n", + "qmod = create_model(main)\n", + "qprog = synthesize(qmod)\n", + "show(qprog)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Exercise 5 - Quantum Conditionals\n", + "\n", + "### Exercise 5a - Control Operator\n", + "Use the built-in `control` operator to create a function that receives two single qubit variables and uses one of the variables to control an RY gate with a `pi/2` angle acting on the other variable (without using the `CRY` function).\n", + "\n", + "See also [Quantum operators](https://docs.classiq.io/latest/user-guide/platform/qmod/language-reference/statements/quantum-operators/#syntax).\n" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Opening: https://platform.classiq.io/circuit/8b0e59eb-c207-4a24-bd01-0808bdcf0d29?version=0.41.1\n", + "Opening in existing browser session.\n" + ] + } + ], + "source": [ + "from classiq import *\n", + "\n", + "# Your code here:\n", + "from classiq.qmod.symbolic import pi\n", + "\n", + "@qfunc\n", + "def controlled_ry_gate(control_bit: QBit, target: QBit) -> None:\n", + " control(control_bit, lambda: RY(pi / 2, target))\n", + "\n", + "@qfunc\n", + "def main(control_bit: Output[QBit], target: Output[QBit]) -> None:\n", + " allocate(1, control_bit)\n", + " allocate(1, target)\n", + " controlled_ry_gate(control_bit, target)\n", + " \n", + "qmod = create_model(main)\n", + "qprog = synthesize(qmod)\n", + "show(qprog)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Exercise 5b - Control (\"Quantum If\")\n", + "The `control` operator is the conditional application of some operation, with the condition being that all control qubits are in the state |1>. This notion is generalized in QMOD to other control states, where the condition is specified as a comparison between a quantum numeric variable and a numeric value, similar to a classical `if` statement. Quantum numeric variables are declared with class `QNum`.\n", + "\n", + "See also [Numeric types](https://docs.classiq.io/latest/user-guide/platform/qmod/language-reference/quantum-types/#syntax).\n", + "\n", + "In QMOD this generalization is available as a native statement - control.\n", + "\n", + "See also [control](https://docs.classiq.io/latest/user-guide/platform/qmod/language-reference/statements/quantum-operators/).\n", + "\n", + "1. Declare a `QNum` output argument using `Output[QNum]` and name it `x`.\n", + "2. Use the `prepare_int` function to initialize it to `9`. Note that you don't need to specify the `QNum` attributes - size, sign, and fraction digits, as they are inferred at the point of initialization.\n", + "3. Execute the circuit and observe the results.\n", + "4. Declare another output argument of type `QBit` and perform a `control` such that under the condition that `x` is 9, the qubit is flipped. Execute the circuit and observe the results. Repeat for a different condition." + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Opening: https://platform.classiq.io/circuit/756325a0-8671-49de-a68a-a63b97d159e2?version=0.41.1\n", + "Opening in existing browser session.\n" + ] + } + ], + "source": [ + "from classiq import *\n", + "\n", + "# Your code here:\n", + "@qfunc\n", + "def main(x: Output[QNum], target: Output[QBit]) -> None:\n", + " prepare_int(9, x)\n", + " allocate(1, target)\n", + " control(x == 9, lambda: X(target))\n", + "\n", + "qmod = create_model(main)\n", + "qprog = synthesize(qmod)\n", + "show(qprog)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.12" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/community/QClass_2024/Submissions/HW2/Noah_Nzeki_William_HW2_QClass2024.ipynb b/community/QClass_2024/Submissions/HW2/Noah_Nzeki_William_HW2_QClass2024.ipynb new file mode 100755 index 00000000..c016b0bc --- /dev/null +++ b/community/QClass_2024/Submissions/HW2/Noah_Nzeki_William_HW2_QClass2024.ipynb @@ -0,0 +1,781 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# The Qmod Workshop - Part 2: Higher-Level Concepts\n", + "\n", + "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." + ] + }, + { + "cell_type": "code", + "execution_count": 67, + "metadata": {}, + "outputs": [], + "source": [ + "from classiq import *" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Exercise 6 - Exponentiation and Pauli Operators\n", + "\n", + "The Qmod language supports different classical types: scalars, arrays, and structs. Structs are objects with member variables, or fields.\n", + "\n", + "See also [Classical Types](https://docs.classiq.io/latest/user-guide/platform/qmod/language-reference/classical-types/#structs).\n", + "\n", + "The builtin struct type `PauliTerm` is defined as follows:" + ] + }, + { + "cell_type": "code", + "execution_count": 68, + "metadata": {}, + "outputs": [], + "source": [ + "@struct\n", + "class PauliTerm:\n", + " pauli: CArray[Pauli]\n", + " coefficient: CReal" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Note that `Pauli` is an enum for all the Pauli matrices (I, X, Y, Z).\n", + "\n", + "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.\n", + "\n", + "In this exercise we will use 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 use 4 repetitions.\n", + "\n", + "The declaration of the `suzuki_trotter` function is:" + ] + }, + { + "cell_type": "code", + "execution_count": 69, + "metadata": {}, + "outputs": [], + "source": [ + "@qfunc(external=True)\n", + "def suzuki_trotter(\n", + " pauli_operator: CArray[PauliTerm],\n", + " evolution_coefficient: CReal,\n", + " order: CInt,\n", + " repetitions: CInt,\n", + " qbv: QArray[QBit],\n", + ") -> None:\n", + " pass" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Fill in the missing parts of the following code in order to complete this exercise:" + ] + }, + { + "cell_type": "code", + "execution_count": 39, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Opening: https://platform.classiq.io/circuit/946ff782-cf9a-4021-b5b8-50f8aa846fbf?version=0.41.2\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "[0526/215345.162702:ERROR:file_io_posix.cc(153)] open /home/noah/.config/BraveSoftware/Brave-Browser/Crash Reports/pending/1de95ac3-f687-4fe5-bee0-5fd5e91b4c38.lock: File exists (17)\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Opening in existing browser session.\n" + ] + } + ], + "source": [ + "from classiq import *\n", + "@qfunc\n", + "def main(q: Output[QArray[QBit]]) -> None: \n", + " allocate(4, q) \n", + " suzuki_trotter( \n", + " \n", + " [ \n", + " PauliTerm(pauli=[Pauli.X, Pauli.Z, Pauli.X, Pauli.X], coefficient=0.5), \n", + " PauliTerm(pauli=[Pauli.Y, Pauli.I, Pauli.Z, Pauli.I], coefficient=0.25), \n", + " PauliTerm(pauli=[Pauli.X, Pauli.I, Pauli.Z, Pauli.Y], coefficient=0.3), \n", + " ], \n", + " \n", + " evolution_coefficient=3, \n", + " repetitions=4, \n", + " order=2, \n", + " qbv=q, \n", + " )\n", + "qmod = create_model(main)\n", + "qprog = synthesize(qmod)\n", + "show(qprog)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Exercise 7 - Basic Arithmetics\n", + "\n", + "#### Exercise 7a\n", + "In this exercise we will use quantum numeric variables and calculate expressions over them.\n", + "\n", + "See details on the syntax of numeric types under [Quantum types](https://docs.classiq.io/latest/user-guide/platform/qmod/language-reference/quantum-types/#syntax).\n", + "See more on quantum expressions under [Numeric assignment](https://docs.classiq.io/latest/user-guide/platform/qmod/language-reference/statements/numeric-assignment/)\n", + "\n", + "Create the following quantum programs:\n", + "1. Initialize variables `x=2`, `y=7` and computes `res = x + y`.\n", + "2. Initialize variables `x=2`, `y=7` and computes `res = x * y`.\n", + "3. Initialize variables `x=2`, `y=7`, `z=1` and computes `res = x * y - z`.\n", + "\n", + "Guidance:\n", + "* Use the operator `|=` to perform out-of-place assignment of arithmetic expression.\n", + "* To initialize the variables, use the function `prepare_int`.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 71, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Opening: https://platform.classiq.io/circuit/2e9c6e49-3ad0-4635-81b6-423947b61d44?version=0.41.2\n" + ] + } + ], + "source": [ + "from classiq import *\n", + "\n", + "# Your code here:\n", + "\n", + "@qfunc\n", + "def main(x: Output[QNum], y: Output[QNum], res: Output[QNum]) -> None:\n", + " prepare_int(2, x)\n", + " prepare_int(7, y)\n", + " res |= x + y\n", + "\n", + "qmod = create_model(main)\n", + "qprog = synthesize(qmod)\n", + "show(qprog)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from classiq import *\n", + "# Your code here:\n", + "@qfunc\n", + "def main(x: Output[QNum], y: Output[QNum], res: Output[QNum]) -> None: \n", + " prepare_int(2, x) \n", + " prepare_int(7, y) \n", + " res |= x * y\n", + " \n", + "qmod = create_model(main)\n", + "qprog = synthesize(qmod)\n", + "show(qprog) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from classiq import *\n", + "\n", + "# Your code here:\n", + "\n", + "@qfunc\n", + "def main(x: Output[QNum], y: Output[QNum], z: Output[QNum], res: Output[QNum]) -> None:\n", + " prepare_int(2, x)\n", + " prepare_int(7, y)\n", + " prepare_int(1, z)\n", + " res |= (x * y) - z\n", + "\n", + "qmod = create_model(main)\n", + "qprog = synthesize(qmod)\n", + "show(qprog)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Exercise 7b\n", + "Declare `x` to be a 2-qubit variable and `y` to be 3-qubit variable.\n", + "\n", + "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`.\n", + "\n", + "1. Use `prepare_state` to initialize `x` and `y`. Note that `prepare_state` works with probabilities, not amplitudes.\n", + " The declaration of the `prepare_state` function is:\n", + " ```\n", + " @qfunc(external=True)\n", + " def prepare_state(\n", + " probabilities: CArray[CReal],\n", + " bound: CReal,\n", + " out: Output[QArray[QBit]],\n", + " ) -> None:\n", + " pass\n", + " ```\n", + " (Set the bound to 0 in your code)\n", + "2. Compute `res = x + y`. Execute the resulting circuit. What did you get?" + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "metadata": { + "scrolled": true + }, + "outputs": [ + { + "ename": "TypeError", + "evalue": "unsupported operand type(s) for +: 'QArray' and 'QArray'", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[0;32mIn[36], line 46\u001b[0m\n\u001b[1;32m 41\u001b[0m res\u001b[38;5;241m|\u001b[39m\u001b[38;5;241m=\u001b[39m x \u001b[38;5;241m+\u001b[39m y\n\u001b[1;32m 42\u001b[0m \u001b[38;5;66;03m#add(x, y, res)\u001b[39;00m\n\u001b[1;32m 43\u001b[0m \n\u001b[1;32m 44\u001b[0m \u001b[38;5;66;03m#return res\u001b[39;00m\n\u001b[0;32m---> 46\u001b[0m model \u001b[38;5;241m=\u001b[39m \u001b[43mcreate_model\u001b[49m\u001b[43m(\u001b[49m\u001b[43mmain\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 47\u001b[0m qprog \u001b[38;5;241m=\u001b[39m synthesize(model)\n\u001b[1;32m 48\u001b[0m show(qprog)\n", + "File \u001b[0;32m~/.local/lib/python3.10/site-packages/classiq/qmod/quantum_function.py:43\u001b[0m, in \u001b[0;36mcreate_model\u001b[0;34m(entry_point, constraints, execution_preferences, preferences, classical_execution_function)\u001b[0m\n\u001b[1;32m 39\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m entry_point\u001b[38;5;241m.\u001b[39mfunc_decl\u001b[38;5;241m.\u001b[39mname \u001b[38;5;241m!=\u001b[39m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mmain\u001b[39m\u001b[38;5;124m\"\u001b[39m:\n\u001b[1;32m 40\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m ClassiqError(\n\u001b[1;32m 41\u001b[0m \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mThe entry point function must be named \u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mmain\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124m, got \u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mentry_point\u001b[38;5;241m.\u001b[39mfunc_decl\u001b[38;5;241m.\u001b[39mname\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 42\u001b[0m )\n\u001b[0;32m---> 43\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mentry_point\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mcreate_model\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 44\u001b[0m \u001b[43m \u001b[49m\u001b[43mconstraints\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mexecution_preferences\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mpreferences\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mclassical_execution_function\u001b[49m\n\u001b[1;32m 45\u001b[0m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\u001b[38;5;241m.\u001b[39mget_model()\n", + "File \u001b[0;32m~/.local/lib/python3.10/site-packages/classiq/qmod/quantum_function.py:78\u001b[0m, in \u001b[0;36mQFunc.create_model\u001b[0;34m(self, constraints, execution_preferences, preferences, classical_execution_function)\u001b[0m\n\u001b[1;32m 76\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_qmodule\u001b[38;5;241m.\u001b[39mconstants \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mdict\u001b[39m()\n\u001b[1;32m 77\u001b[0m QConstant\u001b[38;5;241m.\u001b[39mset_current_model(\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_qmodule)\n\u001b[0;32m---> 78\u001b[0m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_add_native_func_def\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 79\u001b[0m model_extra_settings: List[Tuple[\u001b[38;5;28mstr\u001b[39m, Any]] \u001b[38;5;241m=\u001b[39m [\n\u001b[1;32m 80\u001b[0m (\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mconstraints\u001b[39m\u001b[38;5;124m\"\u001b[39m, constraints),\n\u001b[1;32m 81\u001b[0m (\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mexecution_preferences\u001b[39m\u001b[38;5;124m\"\u001b[39m, execution_preferences),\n\u001b[1;32m 82\u001b[0m (\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mpreferences\u001b[39m\u001b[38;5;124m\"\u001b[39m, preferences),\n\u001b[1;32m 83\u001b[0m ]\n\u001b[1;32m 84\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m classical_execution_function \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n", + "File \u001b[0;32m~/.local/lib/python3.10/site-packages/classiq/qmod/quantum_function.py:99\u001b[0m, in \u001b[0;36mQFunc._add_native_func_def\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 97\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mfunc_decl\u001b[38;5;241m.\u001b[39mname \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_qmodule\u001b[38;5;241m.\u001b[39mnative_defs:\n\u001b[1;32m 98\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m\n\u001b[0;32m---> 99\u001b[0m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mexpand\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 100\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_qmodule\u001b[38;5;241m.\u001b[39mnative_defs[\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mfunc_decl\u001b[38;5;241m.\u001b[39mname] \u001b[38;5;241m=\u001b[39m NativeFunctionDefinition(\n\u001b[1;32m 101\u001b[0m \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39m{\u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39m\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mfunc_decl\u001b[38;5;241m.\u001b[39mdict(), \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39m{\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mbody\u001b[39m\u001b[38;5;124m\"\u001b[39m: \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mbody}}\n\u001b[1;32m 102\u001b[0m )\n", + "File \u001b[0;32m~/.local/lib/python3.10/site-packages/classiq/qmod/quantum_expandable.py:86\u001b[0m, in \u001b[0;36mQExpandable.expand\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 84\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;129;01min\u001b[39;00m QExpandable\u001b[38;5;241m.\u001b[39mSTACK:\n\u001b[1;32m 85\u001b[0m \u001b[38;5;28;01mwith\u001b[39;00m \u001b[38;5;28mself\u001b[39m:\n\u001b[0;32m---> 86\u001b[0m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_py_callable\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_get_positional_args\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[43m)\u001b[49m\n", + "Cell \u001b[0;32mIn[36], line 41\u001b[0m, in \u001b[0;36mmain\u001b[0;34m(res)\u001b[0m\n\u001b[1;32m 37\u001b[0m prepare_state(prob_x, \u001b[38;5;241m0.0\u001b[39m, x) \n\u001b[1;32m 39\u001b[0m prepare_state(prob_y, \u001b[38;5;241m0.0\u001b[39m, y) \n\u001b[0;32m---> 41\u001b[0m res\u001b[38;5;241m|\u001b[39m\u001b[38;5;241m=\u001b[39m \u001b[43mx\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m+\u001b[39;49m\u001b[43m \u001b[49m\u001b[43my\u001b[49m\n", + "\u001b[0;31mTypeError\u001b[0m: unsupported operand type(s) for +: 'QArray' and 'QArray'" + ] + } + ], + "source": [ + "from classiq import *\n", + "from classiq.qmod.symbolic import sqrt\n", + "\n", + "\n", + "@qfunc(external=True)\n", + "def prepare_state(\n", + " probabilities: CArray[CReal],\n", + " bound: CReal,\n", + " out: Output[QArray[QBit]],\n", + ") -> None:\n", + " pass\n", + "\n", + "#@qfunc\n", + "#def add(x1: Output[QArray], y1: Output[QArray], res1: Output[QArray]):\n", + "# res1 = []\n", + "# for i in x1:\n", + "# for j in y1:\n", + "# res1.append(i + j)\n", + "# return res1\n", + "\n", + "prob_x = [0.5, 0.0, 0.5, 0.0] \n", + "prob_y = [0.0, 0.25, 0.25, 0.25, 0.0, 0.0, 0.25, 0.0] \n", + "\n", + "@qfunc\n", + "def main(res: Output[QArray]) -> None:\n", + " \n", + " #x = (QNum, False,0,\"x\") \n", + " x = QArray(\"x\")\n", + " #x: QArray[QBit] = QArray(\"x\")\n", + " # allocate(2, x)\n", + " #x: QArray[QBit] = QArray(\"x\")\n", + "\n", + " \n", + " #y = (QNum, False,0,\"y\")\n", + " y = QArray(\"y\")\n", + " #y: QArray[QBit] = QArray(\"y\")\n", + "\n", + " allocate(2, x)\n", + " allocate(3, y)\n", + " \n", + " prepare_state(prob_x, 0.0, x) \n", + " \n", + " prepare_state(prob_y, 0.0, y) \n", + "\n", + " #res|= x + y\n", + " #add(x, y, res)\n", + " \n", + " #return res\n", + "\n", + "model = create_model(main)\n", + "qprog = synthesize(model)\n", + "show(qprog)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Exercise 8 - Within-Apply\n", + "\n", + "The within-apply statement applies the pattern `U_dagger V U` that appears frequently in quantum computing.\n", + "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.\n", + "\n", + "See also [Within Apply](https://docs.classiq.io/latest/user-guide/platform/qmod/language-reference/statements/within-apply/).\n", + "\n", + "#### Exercise 8a\n", + "\n", + "In this exercise, we will use within-apply to compute an arithmetic expression in steps.\n", + "\n", + "Use the `within_apply` operation to calculate `res = x + y + z` from a two-variable addition building block with the following steps:\n", + "1. Add `x` and `y`\n", + "2. Add the result to `z`\n", + "3. Uncompute the result of the first operation\n", + "\n", + "For simplicity, initialize the registers to simple integers: `x=3`, `y=5`, `z=2`.\n", + "\n", + "Hints:\n", + "\n", + "* Use a temporary variable.\n", + "* Wrap the arithmetic operation in a function.\n", + "\n", + "Execute the circuit and make sure you obtain the expected result." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Opening: https://platform.classiq.io/circuit/97d691e6-72da-486a-9996-f222c3e2b2b0?version=0.41.2\n", + "Synthesized circuit width: 16, depth: 100\n", + "Opening in existing browser session.\n" + ] + } + ], + "source": [ + "from classiq import * \n", + "# Your code here:\n", + "@qfunc\n", + "def linear_func(x:QNum, y:QNum, res: Output[QNum]):\n", + " res |= x + y\n", + "\n", + "@qfunc\n", + "def main(res: Output[QNum]):\n", + " x = QNum('x')\n", + " y = QNum('y')\n", + " z = QNum('z') \n", + " prepare_int(3, x) \n", + " prepare_int(5, y) \n", + " prepare_int(2, z) \n", + " \n", + " res1 = QNum('res1')\n", + "\n", + " within_apply(compute= lambda: linear_func(x, y, res1), \n", + " #action= lambda: inplace_xor(res1,z)) \n", + " action= lambda: linear_func(res1,z,res)) \n", + "\n", + "qmod = create_model(main)\n", + "qprog = synthesize(qmod)\n", + "show(qprog)\n", + "\n", + "def print_depth_width(quantum_program): \n", + " generated_circuit = QuantumProgram.parse_raw(quantum_program) \n", + " print(f\"Synthesized circuit width: {generated_circuit.data.width}, depth: {generated_circuit.transpiled_circuit.depth}\")\n", + "print_depth_width(qprog)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Exercise 8b\n", + "\n", + "Why should we use `within-apply` and not just write three concatenated functions?\n", + "To understand the motivation, we will create another arithmetic circuit.\n", + "This time, however, we will also set Classiq’s synthesis engine to optimize on the circuit’s number of qubits, i.e., its width.\n", + "\n", + "Setting constraints can be done via the `set_constraints` operation - see [here](https://docs.classiq.io/latest/user-guide/platform/synthesis/constraints/).\n", + "\n", + "Perform the operation `res = w + x + y + z`, where w is initialized to 4 and the rest as before:\n", + "\n", + "1. Add `x` and `y` (as part of the `within_apply` operation)\n", + "2. Add the result to `z` (as part of the within_apply operation)\n", + "3. Uncompute the result of the first operation (as part of the `within_apply` operation)\n", + "4. 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.\n", + "\n", + "Create the model, optimize on the circuit’s width, and run the circuit. Can you identify where qubits have been released and reused?" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Opening: https://platform.classiq.io/circuit/a983d0b8-ebd3-4496-9a4c-90af7ad5f361?version=0.41.2\n", + "Opening in existing browser session.\n", + "{'res': 14.0}\n", + "Synthesized circuit width: 16, depth: 165\n" + ] + } + ], + "source": [ + "from classiq import *\n", + "\n", + "\n", + "# Your code here:\n", + "@qfunc\n", + "def linear_func(x:QNum, y:QNum, res: Output[QNum]):\n", + " res |= x + y\n", + "\n", + "\n", + "@qfunc\n", + "def main(res: Output[QNum]):\n", + " w = QNum('w')\n", + " x = QNum('x')\n", + " y = QNum('y')\n", + " z = QNum('z')\n", + " prepare_int(4, w)\n", + " prepare_int(3, x)\n", + " prepare_int(5, y)\n", + " prepare_int(2, z)\n", + "\n", + "\n", + " \n", + " res1 = QNum('res1')\n", + " res2 = QNum('res2')\n", + " within_apply(compute= lambda: linear_func(x, y, res1),\n", + " action= lambda: linear_func(res1, z, res2))\n", + "\n", + "\n", + " res |= w + res2\n", + "\n", + "\n", + "qmod = create_model(main)\n", + "qmod = set_constraints(qmod,Constraints(optimization_parameter='width'))\n", + "qprog = synthesize(qmod)\n", + "show(qprog)\n", + "\n", + "\n", + "def print_parsed_counts(job):\n", + " results = job.result()\n", + " parsed_counts = results[0].value.parsed_counts\n", + " for parsed_state in parsed_counts: print(parsed_state.state)\n", + "\n", + "\n", + "job = execute(qprog)\n", + "print_parsed_counts(job)\n", + "\n", + "\n", + "def print_depth_width(quantum_program):\n", + " generated_circuit = QuantumProgram.parse_raw(quantum_program)\n", + " print(f\"Synthesized circuit width: {generated_circuit.data.width}, depth: {generated_circuit.transpiled_circuit.depth}\")\n", + "print_depth_width(qprog)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Bonus: Use a Single Arithmetic Expression\n", + "\n", + "What happens when we don't manually decompose this expression?\n", + "\n", + "Use Classiq’s arithmetic engine to calculate `res |= x + y + z + w` and optimize for width.\n", + "Look at the resulting quantum program - can you identify the computation and uncomputation blocks? What else did you notice?" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from classiq import *\n", + "\n", + "# Your code here:\n", + "@qfunc\n", + "def main(res: Output[QNum]):\n", + " w = QNum('w')\n", + " x = QNum('x')\n", + " y = QNum('y')\n", + " z = QNum('z')\n", + " prepare_int(4, w)\n", + " prepare_int(3, x)\n", + " prepare_int(5, y)\n", + " prepare_int(2, z)\n", + "\n", + " res |= x + y + z + w\n", + "\n", + "qmod = create_model(main)\n", + "qmod = set_constraints(qmod,Constraints(optimization_parameter='width'))\n", + "qprog = synthesize(qmod)\n", + "show(qprog)\n", + "\n", + "def print_parsed_counts(job):\n", + " results = job.result()\n", + " parsed_counts = results[0].value.parsed_counts\n", + " for parsed_state in parsed_counts: print(parsed_state.state)\n", + "\n", + "job = execute(qprog)\n", + "print_parsed_counts(job)\n", + "\n", + "def print_depth_width(quantum_program):\n", + " generated_circuit = QuantumProgram.parse_raw(quantum_program)\n", + " print(f\"Synthesized circuit width: {generated_circuit.data.width}, depth: {generated_circuit.transpiled_circuit.depth}\")\n", + "print_depth_width(qprog)\n", + "\n", + "qmod = create_model(main)\n", + "qprog = synthesize(qmod)\n", + "show(qprog)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Exercise 9 - In-place Arithmetics\n", + "\n", + "For the following exercise we will use numeric quantum variables that represent fixed-point reals.\n", + "\n", + "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.\n", + "\n", + "See also [Numeric assignment](https://docs.classiq.io/latest/user-guide/platform/qmod/language-reference/statements/numeric-assignment/#semantics).\n", + "\n", + "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 un-initialized, 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.\n", + "\n", + "For this exercise, replace the missing parts in the code snippet below to evaluate the result of:\n", + "\n", + "$$\n", + "f(x) = \\begin{cases}\n", + " 2x + 1 & \\text{ if } 0 \\leq x < 0.5 \\\\\n", + " x + 0.5 & \\text{ if } 0.5 \\leq x < 1\n", + " \\end{cases}\n", + "$$\n", + "\n", + "Notes:\n", + "- We cannot use `x` directly as the control variable in a `constrol` operator, because it also occurs in the nested scope. to determine if `x` is in the lower or higher half of the domain we duplicate the most significant bit onto a separate variable called `label`.\n", + "- 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).\n" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Opening: https://platform.classiq.io/circuit/820a33c8-8d74-40f1-910d-bc58a9036ae0?version=0.40.0\n" + ] + } + ], + "source": [ + "\n", + "from classiq import *\n", + "\n", + "\n", + "def linear_func(a: float, b: float, x: QNum, res: QNum) -> None:\n", + " res ^= a * x + b\n", + "\n", + "\n", + "@qfunc\n", + "def dup_msb(qba: QArray[QBit], msb: QBit) -> None:\n", + " CX(qba[qba.len - 1], msb)\n", + "\n", + "\n", + "@qfunc\n", + "def main(x: Output[QNum[3, False, 3]], res: Output[QNum[5, False, 3]]) -> None:\n", + " allocate(5, res)\n", + " allocate(3, x)\n", + " hadamard_transform(x)\n", + "\n", + " label = QArray(\"label\")\n", + " allocate(1, label)\n", + "\n", + " dup_msb(x, label)\n", + " control(label, lambda: linear_func(1.0, 0.5, x, res)) # 0.5 <= x < 1.0\n", + " X(label)\n", + " control(label, lambda: linear_func(2.0, 1.0, x, res)) # 0.0 <= x < 0.5\n", + "\n", + "\n", + "qmod = create_model(main)\n", + "qprog = synthesize(qmod)\n", + "show(qprog)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Exercise 10 - State-preparation Algorithm using Quantum-if\n", + "\n", + "#### Binding\n", + "The `bind` operation allows to convert smoothly between different quantum types and split or slice bits when necessary. Here’s an example:" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Opening: https://platform.classiq.io/circuit/d73b0ff9-bbc6-45d8-8dd8-6b8704e3b4b4?version=0.41.2\n", + "Opening in existing browser session.\n" + ] + } + ], + "source": [ + "from classiq import *\n", + "\n", + "\n", + "\n", + "from classiq import *\n", + "from math import pi\n", + "\n", + "\n", + "@qfunc\n", + "def main(res: Output[QArray[QBit]]) -> None:\n", + " x: QArray[QBit] = QArray(\"x\")\n", + " allocate(3, x)\n", + " hadamard_transform(x)\n", + "\n", + " lsb = QBit(\"lsb\")\n", + " msb = QNum(\"msb\", 2, False, 0)\n", + " bind(x, [lsb, msb])\n", + "\n", + " control(msb == 1, lambda: RY(pi / 3, lsb)) # msb==1 <==> bit1 bit2 == 01 (binary of decimal 1)\n", + "\n", + " bind([lsb, msb], res)\n", + "\n", + "model = create_model(main)\n", + "qprog = synthesize(model)\n", + "show(qprog)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The first `bind` operation splits the 3-qubit register `x` into the 2-qubit and single-qubit registers `lsb` and `msb`, respectively.\n", + "\n", + "After the `bind` operation:\n", + "1. The registers `lsb` and `msb` can be operated on as separated registers.\n", + "2. The register`x` is consumed and can no longer be used.\n", + "\n", + "The second `bind` operation concatenates the registers to the output register `res`.\n", + "\n", + "For this exercise, fill in the missing code parts in the above snippet and use the `control` statement to manually generate the following lovely 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]`.\n", + "\n", + "The following series of gates generate it:\n", + "\n", + "Perform the Hadamard transform on all three qubits.\n", + "\n", + "Apply a rotation on the LSB (least-significant bit) conditioned by the MSB being |0> and the second to last MSB being |1>. How would you write this condition using a QNum?\n", + "\n", + "The following series of gates generate it:\n", + "1. Perform the Hadamard transform on all three qubits.\n", + "2. Apply a `pi/3` rotation on the LSB (least-significant bit) conditioned by the MSB being |0> and the second to last MSB being |1>. How would you write this condition using a QNum?\n", + "\n", + "If you want to validate your results without looking at the full solution, compare them to running using Classiq’s built-in `prepare_state` function.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Opening: https://platform.classiq.io/circuit/93ba2b8f-8a93-4ee6-9602-b7261094baab?version=0.41.2\n", + "Opening in existing browser session.\n" + ] + } + ], + "source": [ + "from classiq import *from classiq.qmod.symbolic import sqrtfrom math import piprob_list= [ 1 / 8, 1 / 8, 1 / 8-sqrt(3) / 16, 1 / 8 + sqrt(3) / 16, 1 / 8, 1 / 8, 1 / 8, 1 / 8, ]@qfuncdef pre_prepared_state(q: QArray) -> None: prepare_state( [ 1 / 8, 1 / 8, 1 / 8-sqrt(3) / 16, 1 / 8 + sqrt(3) / 16, 1 / 8, 1 / 8, 1 / 8, 1 / 8, ], 0.0, q, )@qfuncdef main(validate_res: Output[QArray]) -> None: prepare_state(prob_list,0,validate_res) model = create_model(main)qprog = synthesize(model)show(qprog)#========================" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Opening in existing browser session.\n", + "Opening: https://platform.classiq.io/circuit/23bac3ff-480f-4622-bfe6-00940d44c4f5?version=0.41.2\n", + "Opening in existing browser session.\n" + ] + } + ], + "source": [ + "from classiq import *from classiq.qmod.symbolic import sqrtfrom math import pi@qfuncdef 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]) condition = (msb == 1) # (msb == 2) control(condition, lambda: RY(pi / 3, lsb)) bind([lsb, msb], res)model = create_model(main)qprog = synthesize(model)show(qprog)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.12" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +}