diff --git a/tests/notebooks/workshops/test_grover_workshop.py b/tests/notebooks/workshops/test_grover_workshop.py index 12aa11256..5955e27bf 100644 --- a/tests/notebooks/workshops/test_grover_workshop.py +++ b/tests/notebooks/workshops/test_grover_workshop.py @@ -10,14 +10,11 @@ def test_notebook(tb: TestbookNotebookClient) -> None: # warning: the `qmod` and `qprog` are being overriden too many times - # test models - validate_quantum_model(tb.ref("qmod")) - # test quantum programs validate_quantum_program_size( tb.ref_pydantic("qprog"), expected_width=30, # actual width: 19 - expected_depth=250, # actual depth: 143 + expected_depth=650, # actual depth: 593 ) # test notebook content diff --git a/tutorials/workshops/grover_workshop/assets/grover.png b/tutorials/workshops/grover_workshop/assets/grover.png new file mode 100644 index 000000000..b91f986af Binary files /dev/null and b/tutorials/workshops/grover_workshop/assets/grover.png differ diff --git a/tutorials/workshops/grover_workshop/assets/oracle.png b/tutorials/workshops/grover_workshop/assets/oracle.png new file mode 100644 index 000000000..921b63db4 Binary files /dev/null and b/tutorials/workshops/grover_workshop/assets/oracle.png differ diff --git a/tutorials/workshops/grover_workshop/assets/reflect-about-mean.png b/tutorials/workshops/grover_workshop/assets/reflect-about-mean.png new file mode 100644 index 000000000..a96125f58 Binary files /dev/null and b/tutorials/workshops/grover_workshop/assets/reflect-about-mean.png differ diff --git a/tutorials/workshops/grover_workshop/assets/reflect-about-zero.png b/tutorials/workshops/grover_workshop/assets/reflect-about-zero.png new file mode 100644 index 000000000..7036ff4af Binary files /dev/null and b/tutorials/workshops/grover_workshop/assets/reflect-about-zero.png differ diff --git a/tutorials/workshops/grover_workshop/grover_workshop.ipynb b/tutorials/workshops/grover_workshop/grover_workshop.ipynb index c2bd5d5b1..8074bae10 100644 --- a/tutorials/workshops/grover_workshop/grover_workshop.ipynb +++ b/tutorials/workshops/grover_workshop/grover_workshop.ipynb @@ -18,7 +18,7 @@ }, { "cell_type": "code", - "execution_count": 25, + "execution_count": 1, "id": "2", "metadata": {}, "outputs": [], @@ -28,7 +28,7 @@ }, { "cell_type": "code", - "execution_count": 26, + "execution_count": 2, "id": "3", "metadata": {}, "outputs": [], @@ -61,31 +61,26 @@ "id": "6", "metadata": {}, "source": [ - "Write a function that prepares the minus state $|{-}\\rangle=\\frac{1}{\\sqrt2}(|{0}\\rangle-|{1}\\rangle)$, assuming it recives the qubit $|{x}\\rangle=|{0}\\rangle$ \n", + "Write a function that prepares the plus state $|{+}\\rangle^{\\otimes n}=\\left[\\frac{1}{\\sqrt2}(|{0}\\rangle+|{1}\\rangle)\\right]^{\\otimes n}$, assuming the state of the input quantum variable is $|x\\rangle^{\\otimes n}$ \n", "\n", "
\n", "\n", "HINT\n", "\n", "\n", - "Use `H(x)`,`X(x)`\n", + "Use either `apply_to_all()` with `H(x)`, or `hadamard_transform()`.\n", "
" ] }, { "cell_type": "code", - "execution_count": 27, + "execution_count": 3, "id": "7", "metadata": {}, "outputs": [], "source": [ "from classiq import *\n", - "\n", - "\n", - "@qfunc\n", - "def prepare_minus_state(x: QBit):\n", - " pass\n", - " # TODO complete here" + "from classiq.qmod.symbolic import pi" ] }, { @@ -98,20 +93,23 @@ }, { "cell_type": "code", - "execution_count": 28, + "execution_count": null, "id": "9", "metadata": {}, "outputs": [], "source": [ + "n = 5\n", + "\n", + "\n", "@qfunc\n", - "def main(x: Output[QBit]):\n", - " allocate(1, x)\n", - " prepare_minus_state(x) # Prepare the minus state" + "def main(x: Output[QArray[QBit]]):\n", + " allocate(5, x)\n", + " hadamard_transform(x) # Prepare the plus state" ] }, { "cell_type": "code", - "execution_count": 29, + "execution_count": 5, "id": "10", "metadata": {}, "outputs": [], @@ -121,7 +119,7 @@ }, { "cell_type": "code", - "execution_count": 30, + "execution_count": 6, "id": "11", "metadata": {}, "outputs": [ @@ -129,7 +127,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "Quantum program link: https://platform.classiq.io/circuit/2yjA2tiLZD0kptA5ufekX5I4Gil\n" + "Quantum program link: https://platform.classiq.io/circuit/3BDZ0CrFpw76x6WbQ3GSz1oh9aD\n" ] } ], @@ -156,15 +154,15 @@ "id": "13", "metadata": {}, "source": [ - "Some basic guidelines about the modeling language (QMOD):\n", + "Some basic guidelines about the modeling language (Qmod):\n", "\n", - "1. Every quantum variable should be declared, either as a parameter of a funciton e.g. `def prepare_minus(x: QBit)` or within the function itself with `x = QBit('x')`\n", + "1. Every quantum variable should be declared, either as a parameter of a funciton e.g. `def main(x: Output[QBit])` or within the function itself with `x = QBit()`\n", "\n", "2. Some quantum variables need to be initalized with the `allocate` function. This is required in 2 cases:\n", "* A variable is a parameter of a function with the declaration `Output` like `def main(x: Output[QNum])`\n", - "* A variable that was declared within a function like `a = QNum('a')`\n", + "* A variable that is declared within a function like `a = QNum()`\n", "\n", - "3. For the `main` function, you will always use `Output` for all variables, as the function does not receive any input" + "3. For the `main` function, you should always use `Output` for all variables, as the function does not receive any input" ] }, { @@ -174,7 +172,7 @@ "source": [ "Important tip!\n", "\n", - "You can see all the declarations of the functions with what are their input arguments in the `functions.py` file within the classiq package (or by just right clicking a function and presing `Go To Defintion`)" + "You can see all the declarations of the functions with their parameters in the `functions.py` file within the classiq package (or by just right clicking a function and presing `Go To Defintion`)" ] }, { @@ -182,7 +180,7 @@ "id": "15", "metadata": {}, "source": [ - "### Uniform Superposition" + "## Grover's algorithm - Summary" ] }, { @@ -190,20 +188,21 @@ "id": "16", "metadata": {}, "source": [ - "Let's continue warming up with creating a function that receives a quantum register and creates a uniform superposition for all qubits within this array. You should use the function `apply_to_all(gate_operand=, target=)`:" + "Before diving into Grover's algorithm implementation, it is important to analyze its different building blocks. We start by looking at the overall quantum algorithm and then start build it step-by-step.\n", + "\n", + "![Visualization](assets/grover.png)" ] }, { - "cell_type": "code", - "execution_count": 31, + "cell_type": "markdown", "id": "17", "metadata": {}, - "outputs": [], "source": [ - "@qfunc\n", - "def create_initial_state(reg: QArray):\n", - " pass\n", - " # TODO complete here apply_to_all(gate_operand=, target=)" + "* **Initial state preparation**: The algorithm starts by preparing a uniform superposition $\\lvert + \\rangle^{\\otimes n}$ using Hadamard gates. This ensures that all possible states are explored simultaneously. At this stage, each state has equal probability amplitude.\n", + "\n", + "* **Grover Oracle**: The oracle encodes the problem by marking the solution state(s). It does this by applying a phase flip, effectively distinguishing \"good\" states from \"bad\" ones. Importantly, it does not reveal the solution directly, only modifies its phase.\n", + "\n", + "* **Grover Diffuser**: The diffuser amplifies the probability of the marked states through a reflection about the average amplitude. This step increases the likelihood of measuring the correct solution. Repeating the oracle and diffuser gradually concentrates probability on the target state." ] }, { @@ -211,89 +210,12 @@ "id": "18", "metadata": {}, "source": [ - "Test yout function by creating a new main function, synthesizing and viewing the circuit:" - ] - }, - { - "cell_type": "code", - "execution_count": 32, - "id": "19", - "metadata": {}, - "outputs": [], - "source": [ - "@qfunc\n", - "def main(x: Output[QArray]):\n", - " allocate(7, x)\n", - " create_initial_state(x)" - ] - }, - { - "cell_type": "code", - "execution_count": 33, - "id": "20", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Quantum program link: https://platform.classiq.io/circuit/2yjA3FxJAIoqaq58u2aQbECoUar\n" - ] - } - ], - "source": [ - "qprog = synthesize(main)\n", - "show(qprog)" - ] - }, - { - "cell_type": "markdown", - "id": "21", - "metadata": {}, - "source": [ - "### Function of a function" + "## Oracle - Reflection about bad states" ] }, { "cell_type": "markdown", - "id": "22", - "metadata": {}, - "source": [ - "In our Grover example we will have 3 variables `a,b,c`. We want to prepare all of them in an initial state of equal superposition. Create a fucntion that receives these 3 quantum variables as quantum integers (`QNum`) and applies the `create_inital_state` function to each:" - ] - }, - { - "cell_type": "code", - "execution_count": 34, - "id": "23", - "metadata": {}, - "outputs": [], - "source": [ - "@qfunc\n", - "def create_initial_states(a: QNum, b: QNum, c: QNum):\n", - " pass\n", - " # TODO Complete here" - ] - }, - { - "cell_type": "markdown", - "id": "24", - "metadata": {}, - "source": [ - "You can create a main function, synthesize and visualize the generated circuit if you want to test yourself." - ] - }, - { - "cell_type": "markdown", - "id": "25", - "metadata": {}, - "source": [ - "## Oracle - Reflection around bad states" - ] - }, - { - "cell_type": "markdown", - "id": "26", + "id": "19", "metadata": {}, "source": [ "### Theoretical Background" @@ -301,30 +223,30 @@ }, { "cell_type": "markdown", - "id": "27", + "id": "20", "metadata": {}, "source": [ "Overall we can understand the Grover operator as composed of two reflection operators:\n", - "1. Around the superposition of 'bad states' (i.e. not the solutions)\n", - "2. Around the initial guess state\n", + "1. about the superposition of 'bad states' (i.e. not the solutions)\n", + "2. about the initial guess state\n", "\n", "In this section we will build the first reflection operator which is also the implementation of the oracle function. Geometrically it can be understood in the 2D vector space of $\\text{Span}\\{|{\\psi_{\\text{good}}}\\rangle,|{\\psi_{\\text{bad}}}\\rangle\\}$." ] }, { "cell_type": "markdown", - "id": "28", + "id": "21", "metadata": {}, "source": [ - "![Oracle](assets/graph1.jpg)" + "![reflect](assets/reflect-about-zero.png)" ] }, { "cell_type": "markdown", - "id": "29", + "id": "22", "metadata": {}, "source": [ - "The above figures describes geometrically the reflection of some state $|{\\psi}\\rangle=\\alpha|{\\psi_\\text{good}}\\rangle+\\beta|{\\psi_\\text{bad}}\\rangle$ around the state $|{\\psi_\\text{bad}}\\rangle$ such that\n", + "The above figure describe geometrically the reflection of some state $|{\\psi}\\rangle=\\alpha|{\\psi_\\text{good}}\\rangle+\\beta|{\\psi_\\text{bad}}\\rangle$ about the state $|{\\psi_\\text{bad}}\\rangle$ such that\n", "$\\begin{equation}\n", "R(\\alpha|{\\psi_\\text{good}}\\rangle+\\beta|{\\psi_\\text{bad}}\\rangle) = -\\alpha|{\\psi_\\text{good}}\\rangle+\\beta|{\\psi_\\text{bad}}\\rangle\n", "\\end{equation}$\n", @@ -339,7 +261,7 @@ }, { "cell_type": "markdown", - "id": "30", + "id": "23", "metadata": {}, "source": [ "### Implementation" @@ -347,112 +269,62 @@ }, { "cell_type": "markdown", - "id": "31", - "metadata": {}, - "source": [ - "Now we will actually implement the black box, the oracle, that people are speaking about in quantum algorithms. The beauty of Classiq is that we just need to specify its functionality, and Classiq automatically implements it for us.\n", - "For our purposes, we want to find all the states that obey $2a+b=c$ so there are 3 quantum variables. In addition, we want to store our results somewhere, i.e. to indicate for each tupple of 3 numbers (a,b,c) (e.g. (1,2,2)) if the state is what we are looking for ($2*1+2=4!=2$ so the result is FALSE in this case). \n", - "\n", - "We will store the result in the variable `res` such that: $\\begin{equation} \\text{res} = \\text{res} \\oplus (2a+b==c) \\end{equation}$" - ] - }, - { - "cell_type": "markdown", - "id": "32", - "metadata": {}, - "source": [ - "What we really want to implement here is graphically described as:" - ] - }, - { - "cell_type": "markdown", - "id": "33", - "metadata": {}, - "source": [ - "![res](assets/oracle1.jpg)" - ] - }, - { - "cell_type": "markdown", - "id": "34", - "metadata": {}, - "source": [ - "Adapt the following function so it will apply the desired equation: " - ] - }, - { - "cell_type": "code", - "execution_count": 35, - "id": "35", + "id": "24", "metadata": {}, - "outputs": [], "source": [ - "@qperm\n", - "def oracle_black_box(res: QNum, a: Const[QNum], b: Const[QNum], c: Const[QNum]):\n", - " # TODO Adapt with the correct statement\n", - " res ^= a + b + c == 8" + "Now we turn to actually implementing the oracle. With Qmod quantum expressions, capturing the intent becomes straightforward. The compiler does the heavy-lifting of synthesizing the reversible circuits for us.\n", + "For our purposes, we want to find all the states that obey $2a+b=c$ so there are 3 quantum variables. In addition, we want to store our results in the relative phase of such states. In other words, we want:" ] }, { "cell_type": "markdown", - "id": "36", + "id": "25", "metadata": {}, "source": [ - "Now let's go quantum! We want to store the result of the above operation in the phase of the state $|{a,b,c}\\rangle$. That is, we want $\\begin{equation} |{a,b,c}\\rangle\\rightarrow(-1)^{(2a+b==c)}|{a,b,c}\\rangle\\end{equation}$\n", - "\n", - "There is a common procedure in quantum algorithms that applies this and it is called phase kickback. It's working by applying the above `oracle_black_box` function to an initial `res` qubit to the state $|{-}\\rangle$ (has anyone prepared a function `prepare_minus(x)` by any chance?)\n", + "$\\begin{equation} |{a,b,c}\\rangle\\rightarrow(-1)^{(2a+b==c)}|{a,b,c}\\rangle\\end{equation}$\n", "\n", - "You can work out the math to see that the following scheme implements what we want:" + "In a visual representation, this is what we want:" ] }, { "cell_type": "markdown", - "id": "37", + "id": "26", "metadata": {}, "source": [ - "![phase_kickback_scheme](assets/oracle2.jpg)" + "![phase_oracle](assets/oracle.png)" ] }, { "cell_type": "markdown", - "id": "38", + "id": "27", "metadata": {}, "source": [ - "Now we can implement it using our oracle:" + "Now we can implement it by defining the `oracle_function`:" ] }, { "cell_type": "code", - "execution_count": 36, - "id": "39", + "execution_count": 7, + "id": "28", "metadata": {}, "outputs": [], "source": [ - "@qperm(disable_perm_check=True)\n", + "@qperm\n", "def oracle_function(a: Const[QNum], b: Const[QNum], c: Const[QNum]):\n", - " aux = QBit()\n", - "\n", - " allocate(aux)\n", - " prepare_minus_state(aux)\n", - "\n", - " oracle_black_box(aux, a, b, c)\n", - "\n", - " # We want to bring the aux state back to it's initial value and to free it up for further use\n", - " invert(lambda: prepare_minus_state(aux))\n", - " free(aux) # and that it can be re-used" + " control((2 * a + b == c), phase(pi))" ] }, { "cell_type": "markdown", - "id": "40", + "id": "29", "metadata": {}, "source": [ - "## Diffuser - Reflection around initial guess" + "## Diffuser - Reflection about initial guess" ] }, { "cell_type": "markdown", - "id": "41", + "id": "30", "metadata": {}, "source": [ "### Theoretical Background" @@ -460,23 +332,23 @@ }, { "cell_type": "markdown", - "id": "42", + "id": "31", "metadata": {}, "source": [ - "The second part of the Grover operator is the diffuser, which can be viewed as the reflection operator around our initial guess. " + "The second part of the Grover operator is the diffuser, which can be viewed as the reflection operator about our initial guess. " ] }, { "cell_type": "markdown", - "id": "43", + "id": "32", "metadata": {}, "source": [ - "![diffuser](assets/graph2.jpg)" + "![diffuser](assets/reflect-about-mean.png)" ] }, { "cell_type": "markdown", - "id": "44", + "id": "33", "metadata": {}, "source": [ "As with the oracle reflection operator, we can describe any state $|{\\psi}\\rangle$ as a superposition of the initial state $|{\\psi_0}\\rangle$ such that and the orthogoanl state to it $|{\\psi_0^{\\bot}}\\rangle$ \n", @@ -485,7 +357,7 @@ "|{\\psi}\\rangle = \\alpha |{\\psi_0}\\rangle +\\beta |{\\psi_0^{\\bot}}\\rangle\n", "\\end{equation}$\n", "\n", - "Here what we want to implement is to add a $(-)$ phase for all states that are not equal our initial guess, that is the reflection operator (our diffuser) is defined as:\n", + "Here we want to apply a $\\pi$ phase to all states that are not equal our initial guess. The reflection operator (our diffuser) is defined as:\n", "\n", "$\\begin{equation}\n", "R(\\alpha |{\\psi_0}\\rangle +\\beta |{\\psi_0^{\\bot}}\\rangle) = \\alpha |{\\psi_0}\\rangle -\\beta |{\\psi_0^{\\bot}}\\rangle\n", @@ -494,15 +366,15 @@ }, { "cell_type": "markdown", - "id": "45", + "id": "34", "metadata": {}, "source": [ - "In order to implement the reflection around our initial state, we will implement a reflection around the zero state $|{0}\\rangle$, and then squeeze it between to state preperations operators for our initial state $|{\\psi_0}\\rangle$. That is, if $U_{\\psi_0}|{0}\\rangle=|{\\psi_0}\\rangle$ then we will implement the desired $R$ operator with:\n", + "To implement a reflection about the initial state $\\vert \\psi_0 \\rangle$, we instead perform a reflection about the computational zero state $\\vert 0 \\rangle$, conjugated by our state-preparation unitary for the initial state $\\vert \\psi_0 \\rangle$. That is, if $U_{\\psi_0}|{0}\\rangle=|{\\psi_0}\\rangle$ then we will implement the desired $R$ operator with:\n", "$\\begin{equation}\n", "R = U_{\\psi_0}R_0 U_{\\psi_0}^{\\dagger}\n", "\\end{equation}$\n", "\n", - "where $R_0$ is the reflection operator around the zero state:\n", + "where $R_0$ is the reflection operator about the zero state:\n", "$\\begin{equation}\n", "R_0|{x}\\rangle = (-1)^{(x\\ne0)}|{x}\\rangle= (2|{0}\\rangle\\langle{0}|-I)|{x}\\rangle\n", "\\end{equation}$" @@ -510,7 +382,7 @@ }, { "cell_type": "markdown", - "id": "46", + "id": "35", "metadata": {}, "source": [ "### Implementation" @@ -518,97 +390,30 @@ }, { "cell_type": "markdown", - "id": "47", - "metadata": {}, - "source": [ - "First we will implement the not equal zero function which takes `aux` and `x` as inputs and applies \n", - "$\\begin{equation}\n", - "\\text{res} = \\text{res} \\oplus (x\\ne0)\n", - "\\end{equation}$" - ] - }, - { - "cell_type": "code", - "execution_count": 37, - "id": "48", - "metadata": {}, - "outputs": [], - "source": [ - "@qperm\n", - "def not_equal_zero(aux: QBit, x: Const[QNum]):\n", - " aux ^= x == 0\n", - " X(aux)" - ] - }, - { - "cell_type": "markdown", - "id": "49", - "metadata": {}, - "source": [ - "Now we will use the common trick of phase kick back again. As the `aux` qubit for the `not_equal_zero` funciton we need to insert the $|{-}\\rangle$ state after it has been initialized and apploied, and after the application of the function we need to return `aux` to its initial state and to free it up (We did this precedure before).\n", - "\n", - "
\n", - "\n", - "HINT\n", - "\n", - "\n", - "1. Declare the auxilary qubit\n", - "2. Initalize it using `allocate`\n", - "3. Prepare the $|{-}\\rangle$ state\n", - "4. Apply the `not_equal_zero` funciton\n", - "5. Reverse the $|{-}\\rangle$ with the `invert` operation\n", - "6. Free the auxilary qubit\n", - "\n", - "
\n" - ] - }, - { - "cell_type": "code", - "execution_count": 38, - "id": "50", - "metadata": {}, - "outputs": [], - "source": [ - "@qfunc\n", - "def zero_diffuser(x: QNum):\n", - " pass\n", - " # TODO complete here" - ] - }, - { - "cell_type": "markdown", - "id": "51", + "id": "36", "metadata": {}, "source": [ - "Now after we've implemented the zero diffuser, we need to sandwich it with the state preparations of our initial state `a,b,c`. The tricky part here is that the zero diffuser expects to receive only 1 quantunm variable `x` but we have three. So what should we do? We combine them to one quantum variable with the `bind` operation, treating them as one variable, and then splitting them back into 3 variables with the `bind` operation again." + "We will use the controlled `phase` operation once more. To conjugate it within Hadamard transforms, we use `within_apply`:" ] }, { "cell_type": "code", - "execution_count": 39, - "id": "52", + "execution_count": 8, + "id": "37", "metadata": {}, "outputs": [], "source": [ - "size_a = 2\n", - "size_b = 2\n", - "size_c = 3\n", - "\n", - "\n", "@qfunc\n", - "def initial_state_diffuser(a: QNum, b: QNum, c: QNum):\n", - " create_initial_states(a, b, c)\n", - "\n", - " abc = QNum()\n", - " bind([a, b, c], abc)\n", - " zero_diffuser(abc)\n", - " bind(abc, [a, b, c])\n", - " invert(lambda: create_initial_states(a, b, c))" + "def grover_diffuser(state: QNum) -> None:\n", + " within_apply(\n", + " lambda: hadamard_transform(state),\n", + " lambda: control(state != 0, lambda: phase(pi)),\n", + " )" ] }, { "cell_type": "markdown", - "id": "53", + "id": "38", "metadata": {}, "source": [ "## Putting all together" @@ -616,17 +421,16 @@ }, { "cell_type": "markdown", - "id": "54", + "id": "39", "metadata": {}, "source": [ - "That's it! You've made it! Now is the time to harvest the fruits of the hard work and put everything together!\n", - "Complete your grover operator by implementing the two functions that you've built, first the `oracle_fucntion` and then the `inital_state_diffuser`:" + "That's it! Complete your grover operator by implementing the two functions that you've built, first the `oracle_function` and then the `grover_diffuser`:" ] }, { "cell_type": "code", - "execution_count": 40, - "id": "55", + "execution_count": 9, + "id": "40", "metadata": {}, "outputs": [], "source": [ @@ -638,7 +442,7 @@ }, { "cell_type": "markdown", - "id": "56", + "id": "41", "metadata": {}, "source": [ "Now that we have our Grover operator, we can run it within our code. We have 3 steps here:\n", @@ -649,11 +453,16 @@ }, { "cell_type": "code", - "execution_count": 41, - "id": "57", + "execution_count": 10, + "id": "42", "metadata": {}, "outputs": [], "source": [ + "size_a = 2\n", + "size_b = 2\n", + "size_c = 3\n", + "\n", + "\n", "@qfunc\n", "def main(a: Output[QNum], b: Output[QNum], c: Output[QNum]):\n", " allocate(size_a, a)\n", @@ -666,7 +475,7 @@ }, { "cell_type": "markdown", - "id": "58", + "id": "43", "metadata": {}, "source": [ "Synthesize your model:" @@ -674,8 +483,8 @@ }, { "cell_type": "code", - "execution_count": 42, - "id": "59", + "execution_count": 11, + "id": "44", "metadata": {}, "outputs": [], "source": [ @@ -684,7 +493,7 @@ }, { "cell_type": "markdown", - "id": "60", + "id": "45", "metadata": {}, "source": [ "And view it within the IDE:" @@ -692,17 +501,25 @@ }, { "cell_type": "code", - "execution_count": null, - "id": "61", + "execution_count": 12, + "id": "46", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Quantum program link: https://platform.classiq.io/circuit/3BDZ0Oj00fjmqsR9QKcsGqmJ0HB\n" + ] + } + ], "source": [ "show(qprog)" ] }, { "cell_type": "markdown", - "id": "62", + "id": "47", "metadata": {}, "source": [ "Is it what you were expecting?\n", @@ -711,20 +528,28 @@ }, { "cell_type": "code", - "execution_count": null, - "id": "63", + "execution_count": 13, + "id": "48", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Quantum program link: https://platform.classiq.io/circuit/3BDZ0fTM0RneZZeGNTJ9XxpiAx6\n" + ] + } + ], "source": [ - "qprog = synthesize(\n", + "qprog_depth_optimized = synthesize(\n", " main, constraints=Constraints(optimization_parameter=\"depth\")\n", ") # or 'width'\n", - "show(qprog)" + "show(qprog_depth_optimized)" ] }, { "cell_type": "markdown", - "id": "64", + "id": "49", "metadata": {}, "source": [ "### CONGRATULATIONS!\n", @@ -734,7 +559,7 @@ }, { "cell_type": "markdown", - "id": "65", + "id": "50", "metadata": {}, "source": [ "#### The full solution for your reference" @@ -742,93 +567,41 @@ }, { "cell_type": "code", - "execution_count": null, - "id": "66", + "execution_count": 14, + "id": "51", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Quantum program link: https://platform.classiq.io/circuit/3BDZ1HvwH1GQxmlAVeElrmEtqZm\n", + "Quantum program link: https://platform.classiq.io/circuit/3BDZ2l0vxNe8XvU6ZbqoUsEvTnN\n" + ] + } + ], "source": [ "from classiq import *\n", - "\n", - "\n", - "@qfunc\n", - "def prepare_minus_state(x: QBit):\n", - " X(x)\n", - " H(x)\n", - "\n", - "\n", - "@qfunc\n", - "def create_initial_state(reg: QArray):\n", - " apply_to_all(gate_operand=H, target=reg)\n", - "\n", - "\n", - "@qfunc\n", - "def create_initial_state(reg: QArray):\n", - " apply_to_all(lambda qb: H(qb), reg)\n", - "\n", - "\n", - "@qfunc\n", - "def create_initial_states(a: QNum, b: QNum, c: QNum):\n", - " create_initial_state(a)\n", - " create_initial_state(b)\n", - " create_initial_state(c)\n", + "from classiq.qmod.symbolic import pi\n", "\n", "\n", "@qperm\n", - "def oracle_black_box(res: QNum, a: Const[QNum], b: Const[QNum], c: Const[QNum]):\n", - " res ^= 2 * a + b == c\n", - "\n", - "\n", - "@qperm(disable_perm_check=True)\n", "def oracle_function(a: Const[QNum], b: Const[QNum], c: Const[QNum]):\n", - " aux = QBit()\n", - "\n", - " allocate(aux)\n", - " prepare_minus_state(aux)\n", - "\n", - " oracle_black_box(aux, a, b, c)\n", - "\n", - " # We want to bring the aux state back to it's initial value and to free it up for further use\n", - " invert(lambda: prepare_minus_state(aux))\n", - " free(aux) # and that it can be re-used\n", - "\n", - "\n", - "@qperm\n", - "def not_equal_zero(aux: QBit, x: Const[QNum]):\n", - " aux ^= x == 0\n", - " X(aux)\n", + " control((2 * a + b == c), lambda: phase(pi))\n", "\n", "\n", "@qfunc\n", - "def zero_diffuser(x: QNum):\n", - " aux = QBit()\n", - " allocate(aux)\n", - "\n", - " prepare_minus_state(aux)\n", - " not_equal_zero(aux, x)\n", - " invert(lambda: prepare_minus_state(aux))\n", - " free(aux)\n", - "\n", - "\n", - "size_a = 2\n", - "size_b = 2\n", - "size_c = 3\n", - "\n", - "\n", - "@qfunc\n", - "def initial_state_diffuser(a: QNum, b: QNum, c: QNum):\n", - " create_initial_states(a, b, c)\n", - "\n", - " abc = QNum()\n", - " bind([a, b, c], abc)\n", - " zero_diffuser(abc)\n", - " bind(abc, [a, b, c])\n", - " invert(lambda: create_initial_states(a, b, c))\n", + "def grover_diffuser(state: QNum) -> None:\n", + " within_apply(\n", + " lambda: hadamard_transform(state),\n", + " lambda: control(state != 0, lambda: phase(pi)),\n", + " )\n", "\n", "\n", "@qfunc\n", "def my_grover_operator(a: QNum, b: QNum, c: QNum):\n", " oracle_function(a, b, c)\n", - " initial_state_diffuser(a, b, c)\n", + " grover_diffuser([a, b, c])\n", "\n", "\n", "@qfunc\n", @@ -836,23 +609,22 @@ " allocate(size_a, a)\n", " allocate(size_b, b)\n", " allocate(size_c, c)\n", - " create_initial_states(a, b, c)\n", + " hadamard_transform([a, b, c])\n", " my_grover_operator(a, b, c)\n", "\n", "\n", - "qmod = create_model(main)\n", "qprog = synthesize(main)\n", "show(qprog)\n", "\n", - "qmod = set_constraints(qmod, optimization_parameter=\"depth\") # or 'width'\n", - "qprog = synthesize(qmod)\n", - "show(qprog)" + "constraints = Constraints(optimization_parameter=\"depth\") # or 'width'\n", + "qprog_depth_optimized = synthesize(main, constraints=constraints)\n", + "show(qprog_depth_optimized)" ] } ], "metadata": { "kernelspec": { - "display_name": "Python 3 (ipykernel)", + "display_name": "venv", "language": "python", "name": "python3" }, @@ -866,12 +638,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.7" - }, - "vscode": { - "interpreter": { - "hash": "529b62266d4f537a408698cf820854c65fe877011c7661f0f70aa11c4383fddc" - } + "version": "3.11.14" } }, "nbformat": 4,