-
Notifications
You must be signed in to change notification settings - Fork 874
New classical_vars_and_ops tutorial #721
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
EdenSchirmanClassiq
merged 8 commits into
Classiq:main
from
alexandre-ricardo:generative
Jan 14, 2025
Merged
Changes from all commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
04de26a
New classical_vars_and_ops tutorial
alexandre-ricardo bd03ba3
Merge branch 'Classiq:main' into generative
alexandre-ricardo 48a2fc0
Merge branch 'Classiq:main' into generative
alexandre-ricardo e49134d
Update classical_variables_and_operations.ipynb
alexandre-ricardo f8276e5
Update classical_variables_and_operations.ipynb
alexandre-ricardo 2514bcb
Merge branch 'Classiq:main' into generative
alexandre-ricardo cbaf5a6
Merge branch 'Classiq:main' into generative
alexandre-ricardo 96b4965
modifications after revision
alexandre-ricardo File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -11,89 +11,96 @@ | |
| "cell_type": "markdown", | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| "metadata": {}, | ||
| "source": [ | ||
| "Quantum variables and operations lie at the heart of the Qmod language but they are not enough for designing all quantum algorithms. To complete the picture, you may need to perform classical control flow with classical operations over classical variables. Typical Python operations include `for` loops and `if-else` statements over `int` or `float` variables.\n", | ||
| "Classical variables and operations lie at the heart of the Qmod language, but they are complemented by classical control flow and operations, making it easier to design quantum algorithms with hybrid quantum-classical logic. With the latest updates, Qmod now fully supports Python's classical operations, such as `for` loops, `if-else` statements, and more, smoothly integrating with quantum workflows.\n", | ||
| "\n", | ||
| "In Qmod, equivalent types for the common Python `int`, `float`, and `bool` types are denoted by `int`, `real`, and `bool` in the native syntax and by `CInt`, `CReal`, and `CBool` in the Python SDK. In addition, arrays of classical values are also supported as well as more configurable data structures called `Struct`s (which are the equivalent of Python dataclasses without methods). See the full list of [classical variables and types supported in Qmod](https://docs.classiq.io/latest/qmod-reference/language-reference/classical-types/#structs). \n", | ||
| "In Qmod, equivalent types for common Python `int`, `float`, `list`, and `bool` types are denoted by `int`, `real`, `array`, and `bool` in the native syntax and by `CInt`, `CReal`, `CArray`, and `CBool` in the Python SDK. The Qmod types can be used in quantum workflows, though Python types can also be passed as classical parameters. For example, when writing quantum workflows, it is possible to use both the length of a quantum array, which is a Qmod quantity, or a Python `int` quantity to control the action of a loop. This ensures the type consistency required for quantum control flow operations like `repeat` (the Qmod equivalent of a Python `for` loop) and `if-else` statements, which enable sophisticated hybrid control logic, see a [detailed description](https://docs.classiq.io/latest/qmod-reference/language-reference/statements/classical-control-flow/). Additionally, Qmod supports arrays of classical values and more configurable data structures called `Struct`s (equivalent to Python dataclasses without methods). See the full list of [classical variables and types supported in Qmod](https://docs.classiq.io/latest/qmod-reference/language-reference/classical-types/#structs). \n", | ||
| "\n", | ||
| "For now, the only two classical operations supported in Qmod are `repeat` (which is the equivalent of a Python `for` loop) and `if-else` statements. See a [detailed description](https://docs.classiq.io/latest/qmod-reference/language-reference/statements/classical-control-flow/).\n", | ||
| "\n", | ||
| "Examine how to manage classical control flow in Qmod via this example." | ||
| "While Qmod enables the integration of Python types within its quantum workflows, the converse is not true with the capabilities covered so far. Python control flow constructs, such as `range`, `for` loops and `if-else` statements, cannot yet incorporate Qmod-specific quantities such as the `len` of a quantum array, within Python’s native control flow. Qmod quantities remain tightly integrated within the Qmod execution environment and cannot directly influence Python logic, ensuring a clear separation between classical Python constructs and Qmod quantum operations." | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "markdown", | ||
| "metadata": {}, | ||
| "source": [ | ||
| "## Concrete Example" | ||
| "### Generative Functions in Qmod" | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "markdown", | ||
| "metadata": {}, | ||
| "source": [ | ||
| "The task is to create a quantum array with $10$ qubits in the state of $|1010101010\\rangle$; i.e., a quantum state with alternating qubit states of zeros and ones. \n", | ||
| "Generative functions in Qmod offer a powerful way to create and manipulate classical objects of Qmod with Python constructs. These functions allow the interaction between quantum and classical variables, enabling advanced logic and operations. Currently, it is possible to benefit from the following Python features within quantum programs using generative functions:\n", | ||
| "\n", | ||
| "- **`range`**: Generate sequences of classical values to iterate quantum operations dynamically over classical types within Qmod types. \n", | ||
| "- **`if` statements**: Define conditional logic for generating and applying quantum operations using Qmod types as classical control parameters.\n", | ||
| "- **Treating quantum constants as classical**: Converts parameters intricate to the quantum systems to Python types. This allows one to use, for example, integration of functions from external packages such as `math.sqrt` over classical quantities of a quantum object, such as the `len` of a quantum array.\n", | ||
| "\n", | ||
| "\n", | ||
| "How to do this? Knowing that a general qubit array can be easily initialized to the state of all zeros; i.e., $|0000000000\\rangle$, you need to flip the state of every second qubit or all the qubits in an even position (the qubits in the $0th$ position, $2nd$ position, $4th$ position, etc.).\n", | ||
| "Generative functions provide an elegant way to manage hybrid classical-quantum workflows, enhancing the expressiveness of your quantum algorithms. For more details and examples, refer to the [Generative Functions documentation](https://docs.classiq.io/latest/qmod-reference/language-reference/generative-functions/?h=ge).\n", | ||
| "\n", | ||
| "Approach this task in two steps. First, create a `apply_condition` function that receives a qubit and its position within the qubit array. If the position is even then it flips its state. Secondly, go over all the qubits in the qubit array and apply the function on each qubit.\n", | ||
| "To enable generative functions in quantum function, it is necessary to modify the `@qfunc` decorator to `@qfunc(generative=True)`.\n", | ||
| "\n", | ||
| "The `apply_condition` function receives a qubit variable named `qubit` and a classical integer variable `index`. If the `index` variable is an even number, apply an `X` gate on the `qubit`, which flips its state." | ||
| "---" | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "code", | ||
| "execution_count": 1, | ||
| "cell_type": "markdown", | ||
| "metadata": {}, | ||
| "outputs": [], | ||
| "source": [ | ||
| "from classiq import *\n", | ||
| "\n", | ||
| "\n", | ||
| "@qfunc\n", | ||
| "def apply_condition(index: CInt, qubit: QBit):\n", | ||
| " if_(condition=index % 2 == 0, then=lambda: X(qubit))" | ||
| "## First Example: State preparation" | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "markdown", | ||
| "metadata": {}, | ||
| "source": [ | ||
| "The condition of whether the `index` variable is even evaluated is using the `%` modulo operation, which calculates the reminder of the `index` integer divided by $2$. Note that the Python SDK uses the `lambda:` construct before the `X(qubit)` operation. As described in the [Quantum Variables and Functions Tutorial](../quantum_variables_and_functions/quantum_variables_and_functions.ipynb), in the Python SDK every function that is given as an argument for another function needs to have the `lambda:` prefix.\n", | ||
| "The task is to create a quantum array with $10$ qubits in the state of $|1010101010\\rangle$; i.e., a quantum state with alternating qubit states of zeros and ones. \n", | ||
| "\n", | ||
| "The way of doing this is based on knowing that a general qubit array can be easily initialized to the state of all zeros; i.e., $|0000000000\\rangle$, it should be sufficient to flip the state of every all the qubits in an even position (the qubits in the $0th$ position, $2nd$ position, $4th$ position, etc.).\n", | ||
| "\n", | ||
| "The argument of the `main` function is a qubit array named `x`. Initalize it to the state $|0000000000\\rangle$ with $10$ qubits using the `allocate` function. Then apply the `repeat` classical operator, applying the `apply_condition` function $10$ times, once per qubit in the `x` qubit array." | ||
| "The argument of the `main` function is a qubit array named `x`. Initalize it to the state $|0000000000\\rangle$ with $10$ qubits using the `allocate` function.\n", | ||
| "\n", | ||
| "After that, by allowing generative functions by setting `@qfunc(generative=True)`, it is possible to use the length of the qubit array `x` as a classical variable, and apply quantum operations over qubits dynamically, using Python's `if` and `for` statements over the qmod `x.len` quantity, according to the pre-defined rule of flipping the state of the even qubits. \n", | ||
| "\n", | ||
| "The condition of whether `i` is even evaluated is using the `%` modulo operation, which calculates the reminder of the integer `i` divided by $2$." | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "code", | ||
| "execution_count": 2, | ||
| "execution_count": 3, | ||
| "metadata": {}, | ||
| "outputs": [], | ||
| "source": [ | ||
| "@qfunc\n", | ||
| "from classiq import *\n", | ||
| "\n", | ||
| "\n", | ||
| "@qfunc(generative=True)\n", | ||
| "def main(x: Output[QArray]):\n", | ||
| " allocate(10, x)\n", | ||
| " repeat(count=x.len, iteration=lambda index: apply_condition(index, x[index]))" | ||
| " print(x.len)\n", | ||
| " for i in range(x.len):\n", | ||
| " if i%2==0:\n", | ||
| " X(x[i])" | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "markdown", | ||
| "metadata": {}, | ||
| "source": [ | ||
| "The `repeat` operator has two arguments. First, specify the number of repetitions. In this case, set it to the length of the `x` qubit array. Second, specify the repeated statement (or statements), using the `apply_condition` function with the `index` and corresponding qubit `x[index]` arguments. Take note of the differences in syntax between the native and Python SDK versions when declaring the counting index with the `index` variable.\n", | ||
| "\n", | ||
| "That's it, the algorithm is ready for you to synthesize and view:" | ||
| "That's it, the algorithm can be synthesized and viewed in the IDE:" | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "code", | ||
| "execution_count": 9, | ||
| "execution_count": 4, | ||
| "metadata": {}, | ||
| "outputs": [ | ||
| { | ||
| "name": "stdout", | ||
| "output_type": "stream", | ||
| "text": [ | ||
| "Opening: https://platform.classiq.io/circuit/578f79f5-1801-4cde-874b-79914c46b89a?version=0.40.0\n" | ||
| "10\n", | ||
| "Opening: https://platform.classiq.io/circuit/c66bf31f-0016-4690-bca5-6a0959df4599?version=0.60.1\n" | ||
| ] | ||
| } | ||
| ], | ||
|
|
@@ -107,7 +114,7 @@ | |
| "metadata": {}, | ||
| "source": [ | ||
| "<div style=\"text-align:center;\">\n", | ||
| " <img src=\"https://docs.classiq.io/resources/classical_operations.gif\n", | ||
| " <img src=\"https://docs.classiq.io/resources/class_var_and_ops_1.png\n", | ||
| "\">\n", | ||
| "</div>" | ||
| ] | ||
|
|
@@ -121,7 +128,7 @@ | |
| }, | ||
| { | ||
| "cell_type": "code", | ||
| "execution_count": 8, | ||
| "execution_count": 4, | ||
| "metadata": {}, | ||
| "outputs": [ | ||
| { | ||
|
|
@@ -142,7 +149,7 @@ | |
| "metadata": {}, | ||
| "source": [ | ||
| "<div style=\"text-align:center;\">\n", | ||
| " <img src=\"https://docs.classiq.io/resources/classical_operations_execute.gif\n", | ||
| " <img src=\"https://docs.classiq.io/resources/class_var_and_ops.gif\n", | ||
| "\">\n", | ||
| "</div>" | ||
| ] | ||
|
|
@@ -151,67 +158,87 @@ | |
| "cell_type": "markdown", | ||
| "metadata": {}, | ||
| "source": [ | ||
| "The only measured bit string is $0101010101$, which in Classiq notation is read from right to left when interpreted. Hence, it corresponds to the $|1010101010\\rangle$ state, which is exactly what you tried to achieve!" | ||
| "The only measured bit string is $0101010101$, which in Classiq notation is read from right to left when interpreted. Hence, it corresponds to the $|1010101010\\rangle$ state, which is exactly the target state.\n", | ||
| "\n", | ||
| "---" | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "markdown", | ||
| "metadata": {}, | ||
| "source": [ | ||
| "## Summary - Classical Variables and Operations" | ||
| "## State preparation: Slicing qubits" | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "markdown", | ||
| "metadata": {}, | ||
| "source": [ | ||
| "A summary of the main points:\n", | ||
| "\n", | ||
| "* Qmod supports the use of classical variables. The example above explicitly declares and uses the `index` integer variable. The length of the `x` qubit array is also a classical integer variable that is used implicitly in the `repeat` operation.\n", | ||
| "\n", | ||
| "* Qmod supports the use of classical operations that receive classical variables and quantum operations that are applied according to the classical operation logic. In the example, the `if` classical operation applies the quantum operation `X(qubit)` if the classical variable `index` is even, and the classical operation `repeat` applies the quantum operation `apply_condition` 10 times as the result of evaluating `x.len`." | ||
| "In this example, the task is to prepare the $N$-qubit state $|00011111000\\dots\\rangle$, where the state $|1\\rangle$ is prepared in the qubits over the integer interval $(N/3, N/2)$. For this, it is possible to create a program that apply alternately the $X$ operation to the correct qubits. In this example, consider a total of $N=24$ qubits." | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "markdown", | ||
| "cell_type": "code", | ||
| "execution_count": 7, | ||
| "metadata": {}, | ||
| "outputs": [], | ||
| "source": [ | ||
| "## All the Code Together" | ||
| "from classiq import *\n", | ||
| "@qfunc(generative=True)\n", | ||
| "def main(x: Output[QArray]):\n", | ||
| " allocate(24, x)\n", | ||
| " k=0\n", | ||
| " for i in range(x.len//3,x.len//2):\n", | ||
| " X(x[i])" | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "code", | ||
| "execution_count": null, | ||
| "execution_count": 8, | ||
| "metadata": {}, | ||
| "outputs": [], | ||
| "outputs": [ | ||
| { | ||
| "name": "stdout", | ||
| "output_type": "stream", | ||
| "text": [ | ||
| "Opening: https://platform.classiq.io/circuit/8a864834-32a8-445e-a0e0-e38515edf8c1?version=0.60.1\n" | ||
| ] | ||
| } | ||
| ], | ||
| "source": [ | ||
| "from classiq import *\n", | ||
| "\n", | ||
| "\n", | ||
| "@qfunc\n", | ||
| "def apply_condition(index: CInt, qubit: QBit):\n", | ||
| " if_(condition=index % 2 == 0, then=lambda: X(qubit))\n", | ||
| "\n", | ||
| "quantum_program = synthesize(create_model(main))\n", | ||
| "show(quantum_program)" | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "markdown", | ||
| "metadata": {}, | ||
| "source": [ | ||
| "## Summary - Classical Variables and Operations" | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "markdown", | ||
| "metadata": {}, | ||
| "source": [ | ||
| "Key features of this tutorial: \n", | ||
| "\n", | ||
| "@qfunc\n", | ||
| "def main(x: Output[QArray]):\n", | ||
| " allocate(10, x)\n", | ||
| " repeat(count=x.len, iteration=lambda index: apply_condition(index, x[index]))\n", | ||
| "- **Classical data types**: Data types `int`, `real`, and `bool` are denoted as `CInt`, `CReal`, and `CBool` in the Python SDK. Complex structures like arrays and `Struct`s (similar to Python dataclasses) are also supported. \n", | ||
| "- **Quantum control flow**: Qmod fully supports Python types such as `float`, `int` and `bool` variables within Qmod control flow, such as `repeat` and `if-for` statements.\n", | ||
| "\n", | ||
| "\n", | ||
| "quantum_program = synthesize(create_model(main))\n", | ||
| "show(quantum_program)\n", | ||
| "### Generative Functions in Qmod \n", | ||
| "\n", | ||
| "results = execute(quantum_program).result()[0].value\n", | ||
| "print(results.counts)\n", | ||
| "Generative functions are employed to enhance hybrid quantum-classical workflows by treating quantum objects into classical variables: \n", | ||
| "\n", | ||
| "write_qmod(create_model(main), \"classical_variables_and_operations\")" | ||
| "- **Dynamic iteration**: Using `range` and `for` loops integration with Qmod types, such as `len`. \n", | ||
| "- **Conditional operations**: Using `if` statements controlled by Qmod types." | ||
| ] | ||
| } | ||
| ], | ||
| "metadata": { | ||
| "kernelspec": { | ||
| "display_name": "Python 3.11.7 ('classiq_devolpment')", | ||
| "display_name": ".venv", | ||
| "language": "python", | ||
| "name": "python3" | ||
| }, | ||
|
|
@@ -225,14 +252,9 @@ | |
| "name": "python", | ||
| "nbconvert_exporter": "python", | ||
| "pygments_lexer": "ipython3", | ||
| "version": "3.11.7" | ||
| "version": "3.12.7" | ||
| }, | ||
| "orig_nbformat": 4, | ||
| "vscode": { | ||
| "interpreter": { | ||
| "hash": "e992e515f6583afc67b46eeabcda0f30363069fab8b382c7517b274ba7a59477" | ||
| } | ||
| } | ||
| "orig_nbformat": 4 | ||
| }, | ||
| "nbformat": 4, | ||
| "nbformat_minor": 2 | ||
|
|
||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lenght -> length
I would shorten the last pargraph and write that:" ... with the capabilities covered so far.... "; This way you hint that it is possible and this is what we will cover.
Reply via ReviewNB