diff --git a/algorithms/amplitude_estimation/qmc_user_defined/qmc_user_defined.ipynb b/algorithms/amplitude_estimation/qmc_user_defined/qmc_user_defined.ipynb index f54391b9..5b88ac02 100644 --- a/algorithms/amplitude_estimation/qmc_user_defined/qmc_user_defined.ipynb +++ b/algorithms/amplitude_estimation/qmc_user_defined/qmc_user_defined.ipynb @@ -5,9 +5,9 @@ "id": "456a591a-6383-45cf-ac3e-cca3014edf6b", "metadata": {}, "source": [ - "# Introducing quantum functions with Quantum Monte Carlo Integration\n", + "# Introducing Quantum Functions with Quantum Monte Carlo Integration\n", "\n", - "In this tutorial we introduce how to write custom quantum functions with Classiq, and subsequently use them for more complex functions/algorithms. This will be illustrated on a specific use-case of Quantum Monte Carlo Integration (QMCI). The example below demonstrates how we can exploit various concepts of modeling quantum algorithms with Classiq when building our own functions." + "This tutorial explains how to write custom quantum functions with Classiq and subsequently uses them for more complex functions or algorithms. This is illustrated on a specific use case of Quantum Monte Carlo Integration (QMCI). The example below demonstrates how we can exploit various concepts of modeling quantum algorithms with Classiq when building our own functions." ] }, { @@ -25,7 +25,7 @@ "\\tag{1}\n", "E_{p}(x) = \\int f(x)p(x) dx.\n", "\\end{equation}\n", - "Such evaluations appear in the context of option-pricing or credit risk-analysis.\n", + "Such evaluations appear in the context of option pricing or credit risk analysis.\n", "\n", "The basic idea of QMCI assumes that we have a quantum function $A$, which, for a given $f$ and $p$, loads the following state of $n+1$ qubits:\n", "\\begin{align}\n", @@ -33,18 +33,18 @@ "A|0\\rangle_n|0\\rangle = \\sum^{2^n-1}_{i=0} \\sqrt{f_i} \\sqrt{p_i}|i\\rangle_n|1\\rangle + \\sum^{2^n-1}_{i=0} \\sqrt{1-f_i} \\sqrt{p_i}|i\\rangle_n|0\\rangle = \\sqrt{a}|\\psi_1\\rangle+\\sqrt{1-a^2}|\\psi_0\\rangle,\n", "\\end{align}\n", "where it is understood that the first $2^n$ states represent a discretized space of $x$, and that $0\\leq f(x)\\leq 1$.\n", - "Then, by applying Amplitude Estimation (AE) algorithm for the \"good-state\" $|\\psi_1 \\rangle$, we can estimate its amplitude\n", + "Then, by applying the amplitude estimation (AE) algorithm for the \"good-state\" $|\\psi_1 \\rangle$, we can estimate its amplitude:\n", "$$\n", "a = \\sum^{2^n-1}_{i=0} f_i p_i.\n", "$$\n", "\n", "The QMCI algorithm can be separated into two parts:\n", - "1) Constructing a Grover operator for the specific problem--- this will be done here almost from scratch.\n", - "2) Applying AE algorithm based on the Grover operator [[1](#AE)]--- this will be done by calling Classiq's Quantum Phase Estimation (QPE) function.\n", + "1) Constructing a Grover operator for the specific problem. This is done here almost from scratch.\n", + "2) Applying the AE algorithm based on the Grover operator [[1](#AE)]. This is done by calling the Classiq Quantum Phase Estimation (QPE) function.\n", "\n", - "### Specific use-case for the tutorial\n", + "### Specific Use Case for the Tutorial\n", "\n", - "For simplicity we will consider a simple use-case. We take a probability distribution on the integers\n", + "For simplicity we consider a simple use case. We take a probability distribution on the integers\n", "$$\n", "\\tag{3}\n", "p_i = \\frac{i}{\\mathcal{N}} \\text{ for } i\\in \\{0,\\dots 2^3-1\\},\n", @@ -54,7 +54,7 @@ "\\tag{4}\n", "f(x) = \\sin^2(0.25x+0.2).\n", "$$\n", - "Therefore, the value we want to evaluate is:\n", + "Therefore, the value we want to evaluate is\n", "$$\n", "a= \\frac{1}{\\mathcal{N}} \\sum^7_{k=0} \\sin^2(0.25k+0.2) k \\approx 0.834.\n", "$$" @@ -65,16 +65,16 @@ "id": "c810e0d5-6fda-4868-aab9-ff036ff8974e", "metadata": {}, "source": [ - "## 1. Building the corresponding Grover Operator \n", + "## 1. Building the Corresponding Grover Operator \n", "\n", "### Quantum Functions\n", "\n", - "The following example will demonstrate how to define QMOD functions by writing a Python function decorated with the `@qfunc` decorator.\n", + "This example demonstrates how to define Qmod functions by writing a Python function decorated with the `@qfunc` decorator.\n", "The typical workflow for defining a quantum function:\n", - "1. Specifying the function signature: The `@qfunc` decorator relies on Python's type-hint mechanism to extract the signature of the QMOD function from the argument list of the Python function.\n", - "2. Specifying the function body: A function decorated with `@qfunc` is executed by the Python interpreter to construct the body of the QMOD function. Inside it, you can do one of the following:\n", - " - Call other `@qfuncs` to insert the corresponding quantum function calls into the body of the resulting QMOD function\n", - " - Introduce local quantum variables, by instantiating a quantum type\n", + "1. Specifying the function signature: The `@qfunc` decorator relies on Python's type-hint mechanism to extract the signature of the Qmod function from the argument list of the Python function.\n", + "2. Specifying the function body: To construct the body of the Qmod function, the Python interpreter executes a function decorated with `@qfunc`. Inside, you can do one of these:\n", + " - Call other `@qfuncs` to insert the corresponding quantum function calls into the body of the resulting Qmod function\n", + " - Introduce local quantum variables by instantiating a quantum type\n", " - Use arithmetic and in-place assignment operators to insert special quantum statements into the function\n", " " ] @@ -84,7 +84,7 @@ "id": "d259adad-9b69-4602-932b-97d98b546503", "metadata": {}, "source": [ - "We can start with relevant imports" + "We can start with relevant imports:" ] }, { @@ -106,7 +106,7 @@ "id": "c2be12ee-3d17-49df-a69f-efab41b60b29", "metadata": {}, "source": [ - "### Grover operator for QMCI\n", + "### Grover Operator for QMCI\n", "\n", "The Grover operator suitable for QMCI is defined as follows:\n", "$$\n", @@ -114,7 +114,7 @@ "$$\n", "with $S_0$ and $S_{\\psi_1}$ being reflection operators around the zero state $|0\\rangle_n|0\\rangle$ and the good-state $|\\psi_1\\rangle$, respectively, and the function $A$ is defined in Eq. ([2](#mjx-eqn-2)).\n", "\n", - "In subsections (1.1)-(1.3) below we build each of the quantum sub-functions, and then in subsection (1.4) we combine them to define a complete Grover operator. On the way we introduce several concepts of functional modeling which allow Classiq's Synthesis Engine to reach better optimized circuits. " + "In subsections (1.1)-(1.3) below we build each of the quantum sub-functions, and then in subsection (1.4) we combine them to define a complete Grover operator. On the way we introduce several concepts of functional modeling, which allow the Classiq synthesis engine to reach better optimized circuits. " ] }, { @@ -122,9 +122,9 @@ "id": "a2c31065-077a-475a-ba06-af9b10a396d5", "metadata": {}, "source": [ - "#### 1.1) The state loading $A$ function\n", + "#### 1.1) The State Loading $A$ Function\n", "\n", - "We start with constructing the $A$ operator in Eq. ([2](#mjx-eqn-2)). We define a quantum function and give it the name `state_loading`" + "We start with constructing the $A$ operator in Eq. ([2](#mjx-eqn-2)). We define a quantum function and give it the name `state_loading`." ] }, { @@ -133,7 +133,7 @@ "metadata": {}, "source": [ "The function's signature declares two arguments: \n", - "1. A quantum register `io` declared as `QArray[QBit]` (an array of qubits with an unspecified size): will be used to represent the discretization of space\n", + "1. A quantum register `io` declared as `QArray[QBit]` (an array of qubits with an unspecified size) that is used to represent the discretization of space.\n", "2. A quantum register `ind` of size 1 declared as `QBit` to indicate the good state. " ] }, @@ -143,19 +143,19 @@ "metadata": {}, "source": [ "Next, we construct the logic flow of the `state_loading` function. \n", - "The function body consists of 2 quantum function calls: `load_probabilities` followed by `amplitude_loading`\n", + "The function body consists of two quantum function calls:\n", "\n", - "- As can be seen from Eq. ([2](#mjx-eqn-2)), the `load_probabilities` function is constructed using Classiq's `inplace_prepare_state` function call on $n=3$ qubits with probabilities $p_i$ \n", - "- The `amplitude_loading` body consists of a function call to Classiq's `linear_pauli_rotations`. The `linear_pauli_rotations` is used to load the amplitude of the function $ f(x) = sin^2(0.25 x + 0.2) $.\n", + "1. As can be seen from Eq. ([2](#mjx-eqn-2)), the `load_probabilities` function is constructed using the Classiq `inplace_prepare_state` function call on $n=3$ qubits with probabilities $p_i$. \n", + "2. The `amplitude_loading` body calls the Classiq `linear_pauli_rotations` function. The `linear_pauli_rotations` loads the amplitude of the function $ f(x) = sin^2(0.25 x + 0.2) $.\n", "\n", - " *Note: the amplitude should be $sin$ so the probability would be $sin^2$.*\n", + " *Note: The amplitude should be $sin$ so the probability is $sin^2$.*\n", "\n", - " The function uses an auxiliary qubit that is utilized so that the desired probability will reflect on the auxiliary qubit if it is in the `|1>` state.\n", + " The function uses an auxiliary qubit that is utilized so that the desired probability reflects on the auxiliary qubit if it is in the `|1>` state.\n", "\n", - " We will use the function with the Pauli Y matrix and enter the appropriate slope and offset to achieve the right parameters.\n", + " We use the function with the Pauli Y matrix and enter the appropriate slope and offset to achieve the right parameters.\n", "\n", "\n", - "We will define the probabilities according to our specific problem described by Eqs. ([3](#mjx-eqn-3)-[4](#mjx-eqn-4))" + "We define the probabilities according to the specific problem described by Eqs. ([3](#mjx-eqn-3)-[4](#mjx-eqn-4))." ] }, { @@ -200,7 +200,7 @@ "id": "d06ba0e3-bbac-45d4-8ff5-46158b4038c8", "metadata": {}, "source": [ - "To examine our function we define a quantum `main` function from which we can build a model, synthesize and view the quantum program created:" + "To examine our function we define a quantum `main` function from which we can build a model, synthesize, and view the quantum program created:" ] }, { @@ -214,9 +214,7 @@ { "name": "stdout", "output_type": "stream", - "text": [ - "" - ] + "text": [] } ], "source": [ @@ -237,9 +235,9 @@ "id": "59b38acb-9ca9-4cfd-b87a-4208c75c63ca", "metadata": {}, "source": [ - "#### 1.2) $S_{\\psi_1}$ function - The good state oracle\n", + "#### 1.2) $S_{\\psi_1}$ Function - The Good State Oracle\n", "\n", - "The next quantum function we define is the one which reflects around the good state: any $n+1$ state in which the `ind` register is at state $|1\\rangle$. This function can be simply constructed with a ZGate on the `ind` register. \n" + "The next quantum function we define is the one that reflects around the good state: any $n+1$ state in which the `ind` register is at state $|1\\rangle$. This function can be constructed with a ZGate on the `ind` register. \n" ] }, { @@ -261,13 +259,13 @@ "id": "fcc22b6c-8c2d-4ac9-ba63-c66416d40af9", "metadata": {}, "source": [ - "#### 1.3) $S_{0}$ function - The Grover Diffuser\n", + "#### 1.3) $S_{0}$ Function - The Grover Diffuser\n", "\n", - "In order to implement the Grover Diffuser we aim to perform a controlled-Z operation on the $|0>^n$ state.\n", + "To implement the Grover Diffuser we aim to perform a controlled-Z operation on the $|0>^n$ state.\n", "\n", "We can define a `zero_oracle` quantum function with the `io` and `ind` registers as its arguments. \n", "\n", - "The `within_apply` operator takes two function arguments - compute and action, and invokes the sequence compute(), action(), and invert(compute()). Quantum objects that are allocated and prepared by compute are subsequently uncomputed and released.\n", + "The `within_apply` operator takes two function arguments—compute and action—and invokes the sequence `compute()`, `action()`, and `invert(compute())`. Quantum objects that are allocated and prepared by compute are subsequently uncomputed and released.\n", "\n", "The `control` condition is a logical expression over a quantum variable. Currently, expressions are restricted to the form ` == `, where both `` and `` are integer types." ] @@ -295,7 +293,7 @@ "id": "a8a9636f-0007-4ca8-98d5-6a1ce7002820", "metadata": {}, "source": [ - "One can verify that:\n", + "We can verify that\n", "\\begin{eqnarray}\n", "|00\\dots0\\rangle \\xrightarrow[{\\rm ctrl(-Z)(target=q_0, ctrl=q_1\\dots q_n)}]{} -|00\\dots0\\rangle, \\\\\n", "|10\\dots0\\rangle \\xrightarrow[{\\rm ctrl(-Z)(target=q_0, ctrl=q_1\\dots q_n)}]{} |10\\dots0\\rangle, \\\\\n", @@ -311,12 +309,12 @@ "id": "52d45da1-8090-4e60-beed-9e4b3c57d929", "metadata": {}, "source": [ - "#### 1.4) $Q$ function - The Grover operator\n", + "#### 1.4) $Q$ Function - The Grover Operator\n", "\n", - "We can now define a complete Grover operator $Q\\equiv -S_{\\psi_1} A^{\\dagger} S_0 A$. We will do this in a single code block that will call the following:\n", + "We can now define a complete Grover operator $Q\\equiv -S_{\\psi_1} A^{\\dagger} S_0 A$. We do this in a single code block that calls the following:\n", "1. The good state oracle (`good_state_oracle`)\n", "2. THe inverse of the state preparation (`state_loading`)\n", - "3. The Diffuser (`zero_oracle`)\n", + "3. The diffuser (`zero_oracle`)\n", "4. The state preparation (`state_loading`)\n", " \n", "*Note:*\n", @@ -352,7 +350,7 @@ "id": "0f4ffdde-0c92-436a-a28c-65cf843162de", "metadata": {}, "source": [ - "##### Let us look at the `my_grover_operator` function we created" + "##### Let us look at the `my_grover_operator` function we created:" ] }, { @@ -366,9 +364,7 @@ { "name": "stdout", "output_type": "stream", - "text": [ - "" - ] + "text": [] } ], "source": [ @@ -394,14 +390,14 @@ "source": [ "## 2. Applying Amplitude Estimation (AE) with Quantum Phase Estimation (QPE)\n", "\n", - "Below we apply a basic AE algorithm which is based on QPE. The idea behind this Algorithm is the following:\n", + "Here we apply a basic AE algorithm that is based on QPE. The idea behind this algorithm is the following:\n", "\n", "The state $A|0\\rangle_n|0\\rangle$ is spanned by two eigenvectors of our Grover operator $Q$, with the two corresponding eigenvalues\n", "\\begin{equation}\n", "\\tag{5}\n", "\\lambda_{\\pm}=\\exp\\left(\\pm i2\\pi \\theta \\right), \\qquad \\sin^2 \\left(\\pi \\theta\\right)\\equiv a.\n", "\\end{equation}\n", - "Therefore, if we apply a QPE on $A|0\\rangle_n|0\\rangle$ we will have these two eigenvalues encoded in the QPE register, however, both give the value of $a$, so there is no ambiguity here." + "Therefore, if we apply a QPE on $A|0\\rangle_n|0\\rangle$, we have these two eigenvalues encoded in the QPE register. However, both give the value of $a$, so there is no ambiguity." ] }, { @@ -409,7 +405,7 @@ "id": "225566be-8c41-4d7a-abc6-ef3bb83a885b", "metadata": {}, "source": [ - "To find $a$ we are going to build a simple quantum model: we apply $A$ on a quantum register of size $n+1$ initialized to zero, and then apply Classiq's QPE with the `my_grover_operator` we defined." + "To find $a$ we build a simple quantum model, applying $A$ on a quantum register of size $n+1$ initialized to zero, and then applying the Classiq QPE with the `my_grover_operator` we defined." ] }, { @@ -417,7 +413,7 @@ "id": "e0605069-5062-4f01-92f8-a6b599c7e4bd", "metadata": {}, "source": [ - "Below is the `main` function from which we can build our model and synthesize it. In particular, we define the output register `phase` as `QNum` to hold the phase register output of the QPE. We choose a QPE with phase register of size 3, governing the accuracy of our Phase-, and thus Amplitude-, Estimation. " + "Below is the `main` function from which we can build our model and synthesize it. In particular, we define the output register `phase` as `QNum` to hold the phase register output of the QPE. We choose a QPE with phase register of size 3, governing the accuracy of our phase-, and thus amplitude-, estimation. " ] }, { @@ -429,9 +425,7 @@ { "name": "stdout", "output_type": "stream", - "text": [ - "" - ] + "text": [] } ], "source": [ @@ -463,7 +457,7 @@ "id": "14f3bf9f-4740-4849-896d-b9cb0dd064cb", "metadata": {}, "source": [ - "We can simply export our model to a `.qmod` file:" + "We can export our model to a `.qmod` file:" ] }, { @@ -481,9 +475,9 @@ "id": "94b452a3-7a47-440d-9c9a-bf88c9f5d3fd", "metadata": {}, "source": [ - "### Finally, we execute the circuit and measure the approximated amplitude\n", + "### Executing the Circuit and Measuring the Approximated Amplitude\n", "\n", - "We start with a simple execution on a simulator" + "We execute on a simulator:" ] }, { @@ -525,7 +519,7 @@ "id": "cee12720-1205-40d6-970f-eb36e76911ad", "metadata": {}, "source": [ - "Plotting the resulting histogram we see two phase values with high probability (however, both corresponds to the same amplitude $a$)" + "Upon plotting the resulting histogram we see two phase values with high probability (however, both correspond to the same amplitude $a$):" ] }, { @@ -565,7 +559,7 @@ "id": "e75fe2d0-3e27-48e6-b8ee-0b9a33b7eb12", "metadata": {}, "source": [ - "Recall the relation in Eq. ([5](#mjx-eqn-5)), we can read the amplitude $a$ from the phase with max probability, and compare to the expected amplitude:" + "Recalling the relation in Eq. ([5](#mjx-eqn-5)), we can read the amplitude $a$ from the phase with maximum probability and compare to the expected amplitude:" ] }, {