-
Notifications
You must be signed in to change notification settings - Fork 97
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This adds a Jupyter notebook sample for invoking Q# callables from Python. I confirmed the new notebook runs in integration tests as expected.
- Loading branch information
Showing
1 changed file
with
356 additions
and
0 deletions.
There are no files selected for viewing
This file contains 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 |
---|---|---|
@@ -0,0 +1,356 @@ | ||
{ | ||
"cells": [ | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"# Invoking Q# callables from Python\n", | ||
"\n", | ||
"The qsharp Python package makes it easy to call Q# operations and functions from within Python. Each Q# callable defined in `%%qsharp` magic cells, through calls to `qsharp.eval()`, or defined in a Q# project loaded via `qsharp.init()` is automatically added to the qsharp.code module in Python:" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": 1, | ||
"metadata": {}, | ||
"outputs": [ | ||
{ | ||
"data": { | ||
"application/javascript": "// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\n// This file provides CodeMirror syntax highlighting for Q# magic cells\n// in classic Jupyter Notebooks. It does nothing in other (Jupyter Notebook 7,\n// VS Code, Azure Notebooks, etc.) environments.\n\n// Detect the prerequisites and do nothing if they don't exist.\nif (window.require && window.CodeMirror && window.Jupyter) {\n // The simple mode plugin for CodeMirror is not loaded by default, so require it.\n window.require([\"codemirror/addon/mode/simple\"], function defineMode() {\n let rules = [\n {\n token: \"comment\",\n regex: /(\\/\\/).*/,\n beginWord: false,\n },\n {\n token: \"string\",\n regex: String.raw`^\\\"(?:[^\\\"\\\\]|\\\\[\\s\\S])*(?:\\\"|$)`,\n beginWord: false,\n },\n {\n token: \"keyword\",\n regex: String.raw`(namespace|open|as|operation|function|body|adjoint|newtype|controlled|internal)\\b`,\n beginWord: true,\n },\n {\n token: \"keyword\",\n regex: String.raw`(if|elif|else|repeat|until|fixup|for|in|return|fail|within|apply)\\b`,\n beginWord: true,\n },\n {\n token: \"keyword\",\n regex: String.raw`(Adjoint|Controlled|Adj|Ctl|is|self|auto|distribute|invert|intrinsic)\\b`,\n beginWord: true,\n },\n {\n token: \"keyword\",\n regex: String.raw`(let|set|use|borrow|mutable)\\b`,\n beginWord: true,\n },\n {\n token: \"operatorKeyword\",\n regex: String.raw`(not|and|or)\\b|(w/)`,\n beginWord: true,\n },\n {\n token: \"operatorKeyword\",\n regex: String.raw`(=)|(!)|(<)|(>)|(\\+)|(-)|(\\*)|(/)|(\\^)|(%)|(\\|)|(&&&)|(~~~)|(\\.\\.\\.)|(\\.\\.)|(\\?)`,\n beginWord: false,\n },\n {\n token: \"meta\",\n regex: String.raw`(Int|BigInt|Double|Bool|Qubit|Pauli|Result|Range|String|Unit)\\b`,\n beginWord: true,\n },\n {\n token: \"atom\",\n regex: String.raw`(true|false|Pauli(I|X|Y|Z)|One|Zero)\\b`,\n beginWord: true,\n },\n ];\n let simpleRules = [];\n for (let rule of rules) {\n simpleRules.push({\n token: rule.token,\n regex: new RegExp(rule.regex, \"g\"),\n sol: rule.beginWord,\n });\n if (rule.beginWord) {\n // Need an additional rule due to the fact that CodeMirror simple mode doesn't work with ^ token\n simpleRules.push({\n token: rule.token,\n regex: new RegExp(String.raw`\\W` + rule.regex, \"g\"),\n sol: false,\n });\n }\n }\n\n // Register the mode defined above with CodeMirror\n window.CodeMirror.defineSimpleMode(\"qsharp\", { start: simpleRules });\n window.CodeMirror.defineMIME(\"text/x-qsharp\", \"qsharp\");\n\n // Tell Jupyter to associate %%qsharp magic cells with the qsharp mode\n window.Jupyter.CodeCell.options_default.highlight_modes[\"qsharp\"] = {\n reg: [/^%%qsharp/],\n };\n\n // Force re-highlighting of all cells the first time this code runs\n for (const cell of window.Jupyter.notebook.get_cells()) {\n cell.auto_highlight();\n }\n });\n}\n", | ||
"text/plain": [] | ||
}, | ||
"metadata": {}, | ||
"output_type": "display_data" | ||
} | ||
], | ||
"source": [ | ||
"import qsharp" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": 2, | ||
"metadata": { | ||
"vscode": { | ||
"languageId": "qsharp" | ||
} | ||
}, | ||
"outputs": [], | ||
"source": [ | ||
"%%qsharp\n", | ||
"\n", | ||
"import Std.Diagnostics.Fact;\n", | ||
"operation TestBellPair() : Result[] {\n", | ||
" use qs = Qubit[2];\n", | ||
" H(qs[0]);\n", | ||
" CNOT(qs[0], qs[1]);\n", | ||
" let rs = MResetEachZ(qs);\n", | ||
" Fact(rs[0] == rs[1], \"The qubits should be in the same state\");\n", | ||
" rs\n", | ||
"}" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": 3, | ||
"metadata": {}, | ||
"outputs": [ | ||
{ | ||
"data": { | ||
"text/plain": [ | ||
"[Zero, Zero]" | ||
] | ||
}, | ||
"execution_count": 3, | ||
"metadata": {}, | ||
"output_type": "execute_result" | ||
} | ||
], | ||
"source": [ | ||
"qsharp.code.TestBellPair()" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"Invoking these functions runs the corresponding Q# code against the sparse state simulator and returns the results as Python types, just as if they were invoked with a call to `qsharp.eval()`. They can also be imported from the module so they can be used like any other Python function:" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": 4, | ||
"metadata": {}, | ||
"outputs": [ | ||
{ | ||
"name": "stdout", | ||
"output_type": "stream", | ||
"text": [ | ||
"[[Zero, Zero], [Zero, Zero], [One, One], [Zero, Zero], [Zero, Zero], [Zero, Zero], [Zero, Zero], [One, One], [Zero, Zero], [One, One], [Zero, Zero], [Zero, Zero], [Zero, Zero], [Zero, Zero], [Zero, Zero], [One, One], [One, One], [One, One], [Zero, Zero], [Zero, Zero], [Zero, Zero], [Zero, Zero], [One, One], [Zero, Zero], [Zero, Zero], [One, One], [Zero, Zero], [Zero, Zero], [Zero, Zero], [One, One], [Zero, Zero], [One, One], [Zero, Zero], [Zero, Zero], [One, One], [Zero, Zero], [One, One], [Zero, Zero], [One, One], [Zero, Zero], [Zero, Zero], [One, One], [One, One], [Zero, Zero], [Zero, Zero], [Zero, Zero], [Zero, Zero], [Zero, Zero], [Zero, Zero], [One, One], [Zero, Zero], [Zero, Zero], [Zero, Zero], [One, One], [Zero, Zero], [Zero, Zero], [One, One], [One, One], [Zero, Zero], [Zero, Zero], [One, One], [Zero, Zero], [Zero, Zero], [One, One], [Zero, Zero], [Zero, Zero], [One, One], [Zero, Zero], [Zero, Zero], [One, One], [One, One], [Zero, Zero], [Zero, Zero], [Zero, Zero], [One, One], [One, One], [Zero, Zero], [One, One], [Zero, Zero], [One, One], [Zero, Zero], [Zero, Zero], [One, One], [One, One], [Zero, Zero], [Zero, Zero], [Zero, Zero], [Zero, Zero], [One, One], [Zero, Zero], [Zero, Zero], [One, One], [One, One], [Zero, Zero], [One, One], [Zero, Zero], [One, One], [One, One], [One, One], [Zero, Zero]]\n" | ||
] | ||
} | ||
], | ||
"source": [ | ||
"from qsharp.code import TestBellPair\n", | ||
"\n", | ||
"results = [TestBellPair() for _ in range(100)]\n", | ||
"print(results)" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"Arguments to Q# callables are converted from Python to the expected Q# type. If an argument cannot be converted to the right type, it will trigger a runtime exception:" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": 5, | ||
"metadata": {}, | ||
"outputs": [ | ||
{ | ||
"name": "stdout", | ||
"output_type": "stream", | ||
"text": [ | ||
"5\n", | ||
"TypeError: 'float' object cannot be interpreted as an integer\n" | ||
] | ||
} | ||
], | ||
"source": [ | ||
"qsharp.eval(\"\"\"\n", | ||
" function AddTwoInts(a : Int, b : Int) : Int {\n", | ||
" return a + b;\n", | ||
" }\n", | ||
" \"\"\")\n", | ||
"\n", | ||
"from qsharp.code import AddTwoInts\n", | ||
"\n", | ||
"print(AddTwoInts(2, 3))\n", | ||
"\n", | ||
"try:\n", | ||
" AddTwoInts(2, 3.0)\n", | ||
"except TypeError as e:\n", | ||
" print(f\"TypeError: {e}\")" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"If you define a Q# callables a namespace (or when loading from a Q# project), they will be exposed with a matching hiearchy of modules in Python:" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": 6, | ||
"metadata": { | ||
"vscode": { | ||
"languageId": "qsharp" | ||
} | ||
}, | ||
"outputs": [], | ||
"source": [ | ||
"%%qsharp\n", | ||
"\n", | ||
"import Std.Diagnostics.DumpMachine;\n", | ||
"namespace Foo {\n", | ||
" operation Bar() : Unit {\n", | ||
" use qs = Qubit[2];\n", | ||
" for q in qs {\n", | ||
" H(q);\n", | ||
" }\n", | ||
" DumpMachine();\n", | ||
" ResetAll(qs);\n", | ||
" }\n", | ||
"}" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": 7, | ||
"metadata": {}, | ||
"outputs": [ | ||
{ | ||
"data": { | ||
"text/markdown": [ | ||
"<table class=\"qs-stateTable\">\n", | ||
" <style>\n", | ||
" .qs-stateTable thead tr {\n", | ||
" background-color: var(\n", | ||
" --vscode-list-hoverBackground,\n", | ||
" var(--jp-layout-color1, inherit)\n", | ||
" );\n", | ||
" }\n", | ||
" .qs-stateTable th {\n", | ||
" text-align: left;\n", | ||
" border: none;\n", | ||
" }\n", | ||
" .qs-stateTable tbody {\n", | ||
" pointer-events: none;\n", | ||
" }\n", | ||
" .qs-stateTable tbody td {\n", | ||
" text-align: left;\n", | ||
" border: none;\n", | ||
" }\n", | ||
" .qs-stateTable tbody td span {\n", | ||
" display: inline-block;\n", | ||
" }\n", | ||
" .qs-stateTable tbody tr:nth-child(even) {\n", | ||
" background-color: var(\n", | ||
" --vscode-list-hoverBackground,\n", | ||
" var(--jp-layout-color1, inherit)\n", | ||
" );\n", | ||
" }\n", | ||
" </style>\n", | ||
" <thead>\n", | ||
" <tr>\n", | ||
" <th>Basis State<br />(|𝜓₁…𝜓ₙ⟩)</th>\n", | ||
" <th>Amplitude</th>\n", | ||
" <th>Measurement Probability</th>\n", | ||
" <th colspan=\"2\">Phase</th>\n", | ||
" </tr>\n", | ||
" </thead>\n", | ||
" <tbody>\n", | ||
" <tr>\n", | ||
" <td>\n", | ||
" <span>|00⟩</span>\n", | ||
" </td>\n", | ||
" <td>\n", | ||
" <span>0.5000+0.0000𝑖</span>\n", | ||
" </td>\n", | ||
" <td>\n", | ||
" <progress max=\"100\" value=\"25.00000000000001\"></progress>\n", | ||
" <span>25.0000%</span>\n", | ||
" </td>\n", | ||
" <td style=\"transform: rotate(0.0000rad)\">↑</td>\n", | ||
" <td>\n", | ||
" <span>0.0000</span>\n", | ||
" </td>\n", | ||
"</tr>\n", | ||
"<tr>\n", | ||
" <td>\n", | ||
" <span>|01⟩</span>\n", | ||
" </td>\n", | ||
" <td>\n", | ||
" <span>0.5000+0.0000𝑖</span>\n", | ||
" </td>\n", | ||
" <td>\n", | ||
" <progress max=\"100\" value=\"25.00000000000001\"></progress>\n", | ||
" <span>25.0000%</span>\n", | ||
" </td>\n", | ||
" <td style=\"transform: rotate(0.0000rad)\">↑</td>\n", | ||
" <td>\n", | ||
" <span>0.0000</span>\n", | ||
" </td>\n", | ||
"</tr>\n", | ||
"<tr>\n", | ||
" <td>\n", | ||
" <span>|10⟩</span>\n", | ||
" </td>\n", | ||
" <td>\n", | ||
" <span>0.5000+0.0000𝑖</span>\n", | ||
" </td>\n", | ||
" <td>\n", | ||
" <progress max=\"100\" value=\"25.00000000000001\"></progress>\n", | ||
" <span>25.0000%</span>\n", | ||
" </td>\n", | ||
" <td style=\"transform: rotate(0.0000rad)\">↑</td>\n", | ||
" <td>\n", | ||
" <span>0.0000</span>\n", | ||
" </td>\n", | ||
"</tr>\n", | ||
"<tr>\n", | ||
" <td>\n", | ||
" <span>|11⟩</span>\n", | ||
" </td>\n", | ||
" <td>\n", | ||
" <span>0.5000+0.0000𝑖</span>\n", | ||
" </td>\n", | ||
" <td>\n", | ||
" <progress max=\"100\" value=\"25.00000000000001\"></progress>\n", | ||
" <span>25.0000%</span>\n", | ||
" </td>\n", | ||
" <td style=\"transform: rotate(0.0000rad)\">↑</td>\n", | ||
" <td>\n", | ||
" <span>0.0000</span>\n", | ||
" </td>\n", | ||
"</tr>\n", | ||
"\n", | ||
" </tbody>\n", | ||
"</table>\n", | ||
"\n", | ||
"\n", | ||
"$|\\psi\\rangle = \\frac{1}{2}|00\\rangle+\\frac{1}{2}|01\\rangle+\\frac{1}{2}|10\\rangle+\\frac{1}{2}|11\\rangle$" | ||
], | ||
"text/plain": [ | ||
"STATE:\n", | ||
"|00⟩: 0.5000+0.0000𝑖\n", | ||
"|01⟩: 0.5000+0.0000𝑖\n", | ||
"|10⟩: 0.5000+0.0000𝑖\n", | ||
"|11⟩: 0.5000+0.0000𝑖" | ||
] | ||
}, | ||
"metadata": {}, | ||
"output_type": "display_data" | ||
} | ||
], | ||
"source": [ | ||
"from qsharp.code.Foo import Bar\n", | ||
"\n", | ||
"Bar()" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"If you run `qsharp.init()`, the compiler and simulator state are reset and all functions exposed into Python are cleared:" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": 8, | ||
"metadata": {}, | ||
"outputs": [ | ||
{ | ||
"name": "stdout", | ||
"output_type": "stream", | ||
"text": [ | ||
"QsharpError: callable not found\n" | ||
] | ||
} | ||
], | ||
"source": [ | ||
"qsharp.init()\n", | ||
"\n", | ||
"try:\n", | ||
" Bar()\n", | ||
"except qsharp.QSharpError as e:\n", | ||
" print(f\"QsharpError: {e}\")" | ||
] | ||
} | ||
], | ||
"metadata": { | ||
"kernelspec": { | ||
"display_name": "Python 3", | ||
"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.11.11" | ||
} | ||
}, | ||
"nbformat": 4, | ||
"nbformat_minor": 2 | ||
} |