Skip to content

Commit

Permalink
Add Python interop sample notebook
Browse files Browse the repository at this point in the history
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
swernli committed Dec 13, 2024
1 parent f6e8acf commit 030bcc4
Showing 1 changed file with 356 additions and 0 deletions.
356 changes: 356 additions & 0 deletions samples/python_interop/calling_qsharp.ipynb
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
}

0 comments on commit 030bcc4

Please sign in to comment.