|
5 | 5 | "id": "456a591a-6383-45cf-ac3e-cca3014edf6b", |
6 | 6 | "metadata": {}, |
7 | 7 | "source": [ |
8 | | - "# Introducing quantum functions with Quantum Monte Carlo Integration\n", |
| 8 | + "# Introducing Quantum Functions with Quantum Monte Carlo Integration\n", |
9 | 9 | "\n", |
10 | | - "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." |
| 10 | + "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." |
11 | 11 | ] |
12 | 12 | }, |
13 | 13 | { |
|
25 | 25 | "\\tag{1}\n", |
26 | 26 | "E_{p}(x) = \\int f(x)p(x) dx.\n", |
27 | 27 | "\\end{equation}\n", |
28 | | - "Such evaluations appear in the context of option-pricing or credit risk-analysis.\n", |
| 28 | + "Such evaluations appear in the context of option pricing or credit risk analysis.\n", |
29 | 29 | "\n", |
30 | 30 | "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", |
31 | 31 | "\\begin{align}\n", |
32 | 32 | "\\tag{2}\n", |
33 | 33 | "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", |
34 | 34 | "\\end{align}\n", |
35 | 35 | "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", |
36 | | - "Then, by applying Amplitude Estimation (AE) algorithm for the \"good-state\" $|\\psi_1 \\rangle$, we can estimate its amplitude\n", |
| 36 | + "Then, by applying the amplitude estimation (AE) algorithm for the \"good-state\" $|\\psi_1 \\rangle$, we can estimate its amplitude:\n", |
37 | 37 | "$$\n", |
38 | 38 | "a = \\sum^{2^n-1}_{i=0} f_i p_i.\n", |
39 | 39 | "$$\n", |
40 | 40 | "\n", |
41 | 41 | "The QMCI algorithm can be separated into two parts:\n", |
42 | | - "1) Constructing a Grover operator for the specific problem--- this will be done here almost from scratch.\n", |
43 | | - "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", |
| 42 | + "1) Constructing a Grover operator for the specific problem. This is done here almost from scratch.\n", |
| 43 | + "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", |
44 | 44 | "\n", |
45 | | - "### Specific use-case for the tutorial\n", |
| 45 | + "### Specific Use Case for the Tutorial\n", |
46 | 46 | "\n", |
47 | | - "For simplicity we will consider a simple use-case. We take a probability distribution on the integers\n", |
| 47 | + "For simplicity we consider a simple use case. We take a probability distribution on the integers\n", |
48 | 48 | "$$\n", |
49 | 49 | "\\tag{3}\n", |
50 | 50 | "p_i = \\frac{i}{\\mathcal{N}} \\text{ for } i\\in \\{0,\\dots 2^3-1\\},\n", |
|
54 | 54 | "\\tag{4}\n", |
55 | 55 | "f(x) = \\sin^2(0.25x+0.2).\n", |
56 | 56 | "$$\n", |
57 | | - "Therefore, the value we want to evaluate is:\n", |
| 57 | + "Therefore, the value we want to evaluate is\n", |
58 | 58 | "$$\n", |
59 | 59 | "a= \\frac{1}{\\mathcal{N}} \\sum^7_{k=0} \\sin^2(0.25k+0.2) k \\approx 0.834.\n", |
60 | 60 | "$$" |
|
65 | 65 | "id": "c810e0d5-6fda-4868-aab9-ff036ff8974e", |
66 | 66 | "metadata": {}, |
67 | 67 | "source": [ |
68 | | - "## 1. Building the corresponding Grover Operator \n", |
| 68 | + "## 1. Building the Corresponding Grover Operator \n", |
69 | 69 | "\n", |
70 | 70 | "### Quantum Functions\n", |
71 | 71 | "\n", |
72 | | - "The following example will demonstrate how to define QMOD functions by writing a Python function decorated with the `@qfunc` decorator.\n", |
| 72 | + "This example demonstrates how to define Qmod functions by writing a Python function decorated with the `@qfunc` decorator.\n", |
73 | 73 | "The typical workflow for defining a quantum function:\n", |
74 | | - "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", |
75 | | - "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", |
76 | | - " - Call other `@qfuncs` to insert the corresponding quantum function calls into the body of the resulting QMOD function\n", |
77 | | - " - Introduce local quantum variables, by instantiating a quantum type\n", |
| 74 | + "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", |
| 75 | + "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", |
| 76 | + " - Call other `@qfuncs` to insert the corresponding quantum function calls into the body of the resulting Qmod function\n", |
| 77 | + " - Introduce local quantum variables by instantiating a quantum type\n", |
78 | 78 | " - Use arithmetic and in-place assignment operators to insert special quantum statements into the function\n", |
79 | 79 | " " |
80 | 80 | ] |
|
84 | 84 | "id": "d259adad-9b69-4602-932b-97d98b546503", |
85 | 85 | "metadata": {}, |
86 | 86 | "source": [ |
87 | | - "We can start with relevant imports" |
| 87 | + "We can start with relevant imports:" |
88 | 88 | ] |
89 | 89 | }, |
90 | 90 | { |
|
106 | 106 | "id": "c2be12ee-3d17-49df-a69f-efab41b60b29", |
107 | 107 | "metadata": {}, |
108 | 108 | "source": [ |
109 | | - "### Grover operator for QMCI\n", |
| 109 | + "### Grover Operator for QMCI\n", |
110 | 110 | "\n", |
111 | 111 | "The Grover operator suitable for QMCI is defined as follows:\n", |
112 | 112 | "$$\n", |
113 | 113 | "Q\\equiv - S_{\\psi_1} A^{\\dagger} S_0 A,\n", |
114 | 114 | "$$\n", |
115 | 115 | "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", |
116 | 116 | "\n", |
117 | | - "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. " |
| 117 | + "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. " |
118 | 118 | ] |
119 | 119 | }, |
120 | 120 | { |
121 | 121 | "cell_type": "markdown", |
122 | 122 | "id": "a2c31065-077a-475a-ba06-af9b10a396d5", |
123 | 123 | "metadata": {}, |
124 | 124 | "source": [ |
125 | | - "#### 1.1) The state loading $A$ function\n", |
| 125 | + "#### 1.1) The State Loading $A$ Function\n", |
126 | 126 | "\n", |
127 | | - "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`" |
| 127 | + "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`." |
128 | 128 | ] |
129 | 129 | }, |
130 | 130 | { |
|
133 | 133 | "metadata": {}, |
134 | 134 | "source": [ |
135 | 135 | "The function's signature declares two arguments: \n", |
136 | | - "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", |
| 136 | + "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", |
137 | 137 | "2. A quantum register `ind` of size 1 declared as `QBit` to indicate the good state. " |
138 | 138 | ] |
139 | 139 | }, |
|
143 | 143 | "metadata": {}, |
144 | 144 | "source": [ |
145 | 145 | "Next, we construct the logic flow of the `state_loading` function. \n", |
146 | | - "The function body consists of 2 quantum function calls: `load_probabilities` followed by `amplitude_loading`\n", |
| 146 | + "The function body consists of two quantum function calls:\n", |
147 | 147 | "\n", |
148 | | - "- 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", |
149 | | - "- 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", |
| 148 | + "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", |
| 149 | + "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", |
150 | 150 | "\n", |
151 | | - " *Note: the amplitude should be $sin$ so the probability would be $sin^2$.*\n", |
| 151 | + " *Note: The amplitude should be $sin$ so the probability is $sin^2$.*\n", |
152 | 152 | "\n", |
153 | | - " 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", |
| 153 | + " 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", |
154 | 154 | "\n", |
155 | | - " We will use the function with the Pauli Y matrix and enter the appropriate slope and offset to achieve the right parameters.\n", |
| 155 | + " We use the function with the Pauli Y matrix and enter the appropriate slope and offset to achieve the right parameters.\n", |
156 | 156 | "\n", |
157 | 157 | "\n", |
158 | | - "We will define the probabilities according to our specific problem described by Eqs. ([3](#mjx-eqn-3)-[4](#mjx-eqn-4))" |
| 158 | + "We define the probabilities according to the specific problem described by Eqs. ([3](#mjx-eqn-3)-[4](#mjx-eqn-4))." |
159 | 159 | ] |
160 | 160 | }, |
161 | 161 | { |
|
200 | 200 | "id": "d06ba0e3-bbac-45d4-8ff5-46158b4038c8", |
201 | 201 | "metadata": {}, |
202 | 202 | "source": [ |
203 | | - "To examine our function we define a quantum `main` function from which we can build a model, synthesize and view the quantum program created:" |
| 203 | + "To examine our function we define a quantum `main` function from which we can build a model, synthesize, and view the quantum program created:" |
204 | 204 | ] |
205 | 205 | }, |
206 | 206 | { |
|
214 | 214 | { |
215 | 215 | "name": "stdout", |
216 | 216 | "output_type": "stream", |
217 | | - "text": [ |
218 | | - "" |
219 | | - ] |
| 217 | + "text": [] |
220 | 218 | } |
221 | 219 | ], |
222 | 220 | "source": [ |
|
237 | 235 | "id": "59b38acb-9ca9-4cfd-b87a-4208c75c63ca", |
238 | 236 | "metadata": {}, |
239 | 237 | "source": [ |
240 | | - "#### 1.2) $S_{\\psi_1}$ function - The good state oracle\n", |
| 238 | + "#### 1.2) $S_{\\psi_1}$ Function - The Good State Oracle\n", |
241 | 239 | "\n", |
242 | | - "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" |
| 240 | + "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" |
243 | 241 | ] |
244 | 242 | }, |
245 | 243 | { |
|
261 | 259 | "id": "fcc22b6c-8c2d-4ac9-ba63-c66416d40af9", |
262 | 260 | "metadata": {}, |
263 | 261 | "source": [ |
264 | | - "#### 1.3) $S_{0}$ function - The Grover Diffuser\n", |
| 262 | + "#### 1.3) $S_{0}$ Function - The Grover Diffuser\n", |
265 | 263 | "\n", |
266 | | - "In order to implement the Grover Diffuser we aim to perform a controlled-Z operation on the $|0>^n$ state.\n", |
| 264 | + "To implement the Grover Diffuser we aim to perform a controlled-Z operation on the $|0>^n$ state.\n", |
267 | 265 | "\n", |
268 | 266 | "We can define a `zero_oracle` quantum function with the `io` and `ind` registers as its arguments. \n", |
269 | 267 | "\n", |
270 | | - "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", |
| 268 | + "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", |
271 | 269 | "\n", |
272 | 270 | "The `control` condition is a logical expression over a quantum variable. Currently, expressions are restricted to the form `<var> == <classical-expression>`, where both `<var>` and `<classical-expression>` are integer types." |
273 | 271 | ] |
|
295 | 293 | "id": "a8a9636f-0007-4ca8-98d5-6a1ce7002820", |
296 | 294 | "metadata": {}, |
297 | 295 | "source": [ |
298 | | - "One can verify that:\n", |
| 296 | + "We can verify that\n", |
299 | 297 | "\\begin{eqnarray}\n", |
300 | 298 | "|00\\dots0\\rangle \\xrightarrow[{\\rm ctrl(-Z)(target=q_0, ctrl=q_1\\dots q_n)}]{} -|00\\dots0\\rangle, \\\\\n", |
301 | 299 | "|10\\dots0\\rangle \\xrightarrow[{\\rm ctrl(-Z)(target=q_0, ctrl=q_1\\dots q_n)}]{} |10\\dots0\\rangle, \\\\\n", |
|
311 | 309 | "id": "52d45da1-8090-4e60-beed-9e4b3c57d929", |
312 | 310 | "metadata": {}, |
313 | 311 | "source": [ |
314 | | - "#### 1.4) $Q$ function - The Grover operator\n", |
| 312 | + "#### 1.4) $Q$ Function - The Grover Operator\n", |
315 | 313 | "\n", |
316 | | - "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", |
| 314 | + "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", |
317 | 315 | "1. The good state oracle (`good_state_oracle`)\n", |
318 | 316 | "2. THe inverse of the state preparation (`state_loading`)\n", |
319 | | - "3. The Diffuser (`zero_oracle`)\n", |
| 317 | + "3. The diffuser (`zero_oracle`)\n", |
320 | 318 | "4. The state preparation (`state_loading`)\n", |
321 | 319 | " \n", |
322 | 320 | "*Note:*\n", |
|
352 | 350 | "id": "0f4ffdde-0c92-436a-a28c-65cf843162de", |
353 | 351 | "metadata": {}, |
354 | 352 | "source": [ |
355 | | - "##### Let us look at the `my_grover_operator` function we created" |
| 353 | + "##### Let us look at the `my_grover_operator` function we created:" |
356 | 354 | ] |
357 | 355 | }, |
358 | 356 | { |
|
366 | 364 | { |
367 | 365 | "name": "stdout", |
368 | 366 | "output_type": "stream", |
369 | | - "text": [ |
370 | | - "" |
371 | | - ] |
| 367 | + "text": [] |
372 | 368 | } |
373 | 369 | ], |
374 | 370 | "source": [ |
|
394 | 390 | "source": [ |
395 | 391 | "## 2. Applying Amplitude Estimation (AE) with Quantum Phase Estimation (QPE)\n", |
396 | 392 | "\n", |
397 | | - "Below we apply a basic AE algorithm which is based on QPE. The idea behind this Algorithm is the following:\n", |
| 393 | + "Here we apply a basic AE algorithm that is based on QPE. The idea behind this algorithm is the following:\n", |
398 | 394 | "\n", |
399 | 395 | "The state $A|0\\rangle_n|0\\rangle$ is spanned by two eigenvectors of our Grover operator $Q$, with the two corresponding eigenvalues\n", |
400 | 396 | "\\begin{equation}\n", |
401 | 397 | "\\tag{5}\n", |
402 | 398 | "\\lambda_{\\pm}=\\exp\\left(\\pm i2\\pi \\theta \\right), \\qquad \\sin^2 \\left(\\pi \\theta\\right)\\equiv a.\n", |
403 | 399 | "\\end{equation}\n", |
404 | | - "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." |
| 400 | + "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." |
405 | 401 | ] |
406 | 402 | }, |
407 | 403 | { |
408 | 404 | "cell_type": "markdown", |
409 | 405 | "id": "225566be-8c41-4d7a-abc6-ef3bb83a885b", |
410 | 406 | "metadata": {}, |
411 | 407 | "source": [ |
412 | | - "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." |
| 408 | + "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." |
413 | 409 | ] |
414 | 410 | }, |
415 | 411 | { |
416 | 412 | "cell_type": "markdown", |
417 | 413 | "id": "e0605069-5062-4f01-92f8-a6b599c7e4bd", |
418 | 414 | "metadata": {}, |
419 | 415 | "source": [ |
420 | | - "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. " |
| 416 | + "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. " |
421 | 417 | ] |
422 | 418 | }, |
423 | 419 | { |
|
429 | 425 | { |
430 | 426 | "name": "stdout", |
431 | 427 | "output_type": "stream", |
432 | | - "text": [ |
433 | | - "" |
434 | | - ] |
| 428 | + "text": [] |
435 | 429 | } |
436 | 430 | ], |
437 | 431 | "source": [ |
|
463 | 457 | "id": "14f3bf9f-4740-4849-896d-b9cb0dd064cb", |
464 | 458 | "metadata": {}, |
465 | 459 | "source": [ |
466 | | - "We can simply export our model to a `.qmod` file:" |
| 460 | + "We can export our model to a `.qmod` file:" |
467 | 461 | ] |
468 | 462 | }, |
469 | 463 | { |
|
481 | 475 | "id": "94b452a3-7a47-440d-9c9a-bf88c9f5d3fd", |
482 | 476 | "metadata": {}, |
483 | 477 | "source": [ |
484 | | - "### Finally, we execute the circuit and measure the approximated amplitude\n", |
| 478 | + "### Executing the Circuit and Measuring the Approximated Amplitude\n", |
485 | 479 | "\n", |
486 | | - "We start with a simple execution on a simulator" |
| 480 | + "We execute on a simulator:" |
487 | 481 | ] |
488 | 482 | }, |
489 | 483 | { |
|
525 | 519 | "id": "cee12720-1205-40d6-970f-eb36e76911ad", |
526 | 520 | "metadata": {}, |
527 | 521 | "source": [ |
528 | | - "Plotting the resulting histogram we see two phase values with high probability (however, both corresponds to the same amplitude $a$)" |
| 522 | + "Upon plotting the resulting histogram we see two phase values with high probability (however, both correspond to the same amplitude $a$):" |
529 | 523 | ] |
530 | 524 | }, |
531 | 525 | { |
|
565 | 559 | "id": "e75fe2d0-3e27-48e6-b8ee-0b9a33b7eb12", |
566 | 560 | "metadata": {}, |
567 | 561 | "source": [ |
568 | | - "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:" |
| 562 | + "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:" |
569 | 563 | ] |
570 | 564 | }, |
571 | 565 | { |
|
0 commit comments