diff --git a/samples/python_interop/calling_qsharp.ipynb b/samples/python_interop/calling_qsharp.ipynb new file mode 100644 index 0000000000..9090805239 --- /dev/null +++ b/samples/python_interop/calling_qsharp.ipynb @@ -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": [ + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n", + "\n", + " \n", + "
Basis State
(|𝜓₁…𝜓ₙ⟩)
AmplitudeMeasurement ProbabilityPhase
\n", + " |00⟩\n", + " \n", + " 0.5000+0.0000𝑖\n", + " \n", + " \n", + " 25.0000%\n", + " \n", + " 0.0000\n", + "
\n", + " |01⟩\n", + " \n", + " 0.5000+0.0000𝑖\n", + " \n", + " \n", + " 25.0000%\n", + " \n", + " 0.0000\n", + "
\n", + " |10⟩\n", + " \n", + " 0.5000+0.0000𝑖\n", + " \n", + " \n", + " 25.0000%\n", + " \n", + " 0.0000\n", + "
\n", + " |11⟩\n", + " \n", + " 0.5000+0.0000𝑖\n", + " \n", + " \n", + " 25.0000%\n", + " \n", + " 0.0000\n", + "
\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 +}