Skip to content

Commit 939e2aa

Browse files
authored
Add Python interop sample notebook (#2066)
This adds a Jupyter notebook sample for invoking Q# callables from Python. I confirmed the new notebook runs in integration tests as expected.
1 parent 0e3c2d7 commit 939e2aa

File tree

3 files changed

+148
-15
lines changed

3 files changed

+148
-15
lines changed

samples/notebooks/project.ipynb

+23-2
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,32 @@
4242
"\n",
4343
"Sample.Main()\n"
4444
]
45+
},
46+
{
47+
"cell_type": "markdown",
48+
"id": "5fad86c4",
49+
"metadata": {},
50+
"source": [
51+
"The callables from the project are also available under `qsharp.code` and can be called or imported from Python."
52+
]
53+
},
54+
{
55+
"cell_type": "code",
56+
"execution_count": null,
57+
"id": "423b6fc4",
58+
"metadata": {},
59+
"outputs": [],
60+
"source": [
61+
"from qsharp.code.Sample import Main\n",
62+
"\n",
63+
"res = Main()\n",
64+
"print(f\"Got return value from Q# code: {res}\")"
65+
]
4566
}
4667
],
4768
"metadata": {
4869
"kernelspec": {
49-
"display_name": "Python 3 (ipykernel)",
70+
"display_name": "Python 3",
5071
"language": "python",
5172
"name": "python3"
5273
},
@@ -60,7 +81,7 @@
6081
"name": "python",
6182
"nbconvert_exporter": "python",
6283
"pygments_lexer": "ipython3",
63-
"version": "3.11.6"
84+
"version": "3.11.11"
6485
}
6586
},
6687
"nbformat": 4,

samples/notebooks/sample.ipynb

+122-10
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,6 @@
1010
"This enables the `%%qsharp` magic and initializes a Q# interpreter singleton."
1111
]
1212
},
13-
{
14-
"cell_type": "markdown",
15-
"id": "ed1b75bf",
16-
"metadata": {},
17-
"source": []
18-
},
1913
{
2014
"cell_type": "code",
2115
"execution_count": null,
@@ -69,7 +63,7 @@
6963
"source": [
7064
"`qsharp.eval()` does the same thing as the `%%qsharp` magic.\n",
7165
"\n",
72-
"`DumpMachine()` and `Message()` print to stdout and get displayed in the notebook as plain text"
66+
"`DumpMachine()` and `Message()` print to stdout and get displayed in the notebook as plain text."
7367
]
7468
},
7569
{
@@ -84,6 +78,24 @@
8478
"qsharp.eval(\"Main()\")\n"
8579
]
8680
},
81+
{
82+
"cell_type": "markdown",
83+
"id": "19f4ef6d",
84+
"metadata": {},
85+
"source": [
86+
"`qsharp.code` provides direct access to simulating callables defined in Q#."
87+
]
88+
},
89+
{
90+
"cell_type": "code",
91+
"execution_count": null,
92+
"id": "30b92222",
93+
"metadata": {},
94+
"outputs": [],
95+
"source": [
96+
"qsharp.code.Main()"
97+
]
98+
},
8799
{
88100
"cell_type": "markdown",
89101
"id": "a3bde193",
@@ -256,7 +268,7 @@
256268
},
257269
{
258270
"cell_type": "code",
259-
"execution_count": null,
271+
"execution_count": 11,
260272
"id": "eb3cd29f",
261273
"metadata": {
262274
"vscode": {
@@ -382,7 +394,7 @@
382394
},
383395
{
384396
"cell_type": "code",
385-
"execution_count": null,
397+
"execution_count": 14,
386398
"id": "9b85eb2d",
387399
"metadata": {
388400
"vscode": {
@@ -414,6 +426,106 @@
414426
"source": [
415427
"qsharp.run(\"Bad()\", 10)\n"
416428
]
429+
},
430+
{
431+
"cell_type": "markdown",
432+
"id": "9f738245",
433+
"metadata": {},
434+
"source": [
435+
"When invoked from Python, arguments to Q# callables are converted from their Python type to the expected Q# type. If an argument cannot be converted to the right type, it will trigger a runtime exception."
436+
]
437+
},
438+
{
439+
"cell_type": "code",
440+
"execution_count": null,
441+
"id": "9ae2729d",
442+
"metadata": {},
443+
"outputs": [],
444+
"source": [
445+
"qsharp.eval(\"\"\"\n",
446+
" function AddTwoInts(a : Int, b : Int) : Int {\n",
447+
" return a + b;\n",
448+
" }\n",
449+
" \"\"\")\n",
450+
"\n",
451+
"from qsharp.code import AddTwoInts\n",
452+
"\n",
453+
"print(AddTwoInts(2, 3))\n",
454+
"\n",
455+
"try:\n",
456+
" AddTwoInts(2, 3.0)\n",
457+
"except TypeError as e:\n",
458+
" print(f\"TypeError: {e}\")"
459+
]
460+
},
461+
{
462+
"cell_type": "markdown",
463+
"id": "0f0795e9",
464+
"metadata": {},
465+
"source": [
466+
"If you define any Q# callables in a namespace (or when initializing with a Q# project), those callables will be exposed with a matching hierarchy of modules in Python:"
467+
]
468+
},
469+
{
470+
"cell_type": "code",
471+
"execution_count": null,
472+
"id": "e7b84a41",
473+
"metadata": {
474+
"vscode": {
475+
"languageId": "qsharp"
476+
}
477+
},
478+
"outputs": [],
479+
"source": [
480+
"%%qsharp\n",
481+
"\n",
482+
"import Std.Diagnostics.DumpMachine;\n",
483+
"namespace Foo {\n",
484+
" operation Bar() : Unit {\n",
485+
" use qs = Qubit[2];\n",
486+
" for q in qs {\n",
487+
" H(q);\n",
488+
" }\n",
489+
" DumpMachine();\n",
490+
" ResetAll(qs);\n",
491+
" }\n",
492+
"}"
493+
]
494+
},
495+
{
496+
"cell_type": "code",
497+
"execution_count": null,
498+
"id": "5591587c",
499+
"metadata": {},
500+
"outputs": [],
501+
"source": [
502+
"from qsharp.code.Foo import Bar\n",
503+
"\n",
504+
"Bar()"
505+
]
506+
},
507+
{
508+
"cell_type": "markdown",
509+
"id": "020b244b",
510+
"metadata": {},
511+
"source": [
512+
"If you run `qsharp.init()`, the compiler and simulator state are reset and all functions exposed into Python are cleared:"
513+
]
514+
},
515+
{
516+
"cell_type": "code",
517+
"execution_count": null,
518+
"id": "3d73c247",
519+
"metadata": {},
520+
"outputs": [],
521+
"source": [
522+
"qsharp.init()\n",
523+
"\n",
524+
"try:\n",
525+
" Bar()\n",
526+
"except qsharp.QSharpError as e:\n",
527+
" print(f\"QsharpError: {e}\")"
528+
]
417529
}
418530
],
419531
"metadata": {
@@ -432,7 +544,7 @@
432544
"name": "python",
433545
"nbconvert_exporter": "python",
434546
"pygments_lexer": "ipython3",
435-
"version": "3.12.1"
547+
"version": "3.11.11"
436548
}
437549
},
438550
"nbformat": 4,

samples/python_interop/generating_n_random_bits/RunGenerateRandom.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@
22

33
qsharp.init(project_root=".")
44

5+
from qsharp.code.GenerateRandomNumbers import GenerateRandomNumbers
6+
57
nQubits = input("Enter the number of random bits to be generated: ")
6-
(results, number) = qsharp.eval(
7-
f"GenerateRandomNumbers.GenerateRandomNumbers({nQubits})"
8-
)
8+
(results, number) = GenerateRandomNumbers(int(nQubits))
99

1010
count = 0
1111
for result in results:

0 commit comments

Comments
 (0)