{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Guppy quickstart\n",
    "\n",
    "Guppy is the Python-embedded quantum language we write every kernel in. A `@guppy` function compiles to HUGR, which Selene emulates and Nexus executes. This notebook is assembled from the upstream Guppy docs.\n",
    "\n",
    "Generated from the crawled `guppy` corpus (crawl date 2026-08-24) by `python -m quantum.docs_crawler.notebooks`. Do not edit by hand — edit the generator or pin a snippet out via `notebook_manifest.json`.\n",
    "\n",
    "```bash\n",
    "pip install \"guppylang>=1.0\"   # Python >= 3.12; Selene ships inside guppylang\n",
    "```\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 1. Environment check\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import guppylang\n",
    "from guppylang import guppy\n",
    "\n",
    "print('guppylang', guppylang.__version__)\n",
    "\n",
    "# v1 API surface check: pre-v1 code used result(...) and a bare measure(q).\n",
    "from guppylang.std.quantum import measure\n",
    "from guppylang.std.builtins import result  # noqa: F401  (still importable)\n",
    "assert hasattr(guppy, 'comptime'), 'this notebook targets guppylang >= 1.0'\n",
    "print('v1 API present')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 2. Core examples\n",
    "\n",
    "Each cell below is an upstream example, linked to its source page.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**[Getting Started](https://docs.quantinuum.com/guppy/getting_started.html)** — `getting_started` block 0\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from guppylang import guppy\n",
    "from guppylang.std.builtins import output\n",
    "from guppylang.std.quantum import cx, h, measure, qubit, x\n",
    "\n",
    "\n",
    "@guppy\n",
    "def simple_circuit() -> qubit:\n",
    "    q1, q2 = qubit(), qubit()\n",
    "\n",
    "    h(q1)\n",
    "    cx(q1, q2)\n",
    "\n",
    "    outcome = measure(q1).read()\n",
    "    output(\"q1\", outcome)\n",
    "\n",
    "    if outcome:\n",
    "        x(q2)\n",
    "\n",
    "    return q2\n",
    "simple_circuit.check()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**[Getting Started](https://docs.quantinuum.com/guppy/getting_started.html)** — `getting_started` block 1\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "@guppy\n",
    "def evaluate() -> None:\n",
    "    q = simple_circuit()\n",
    "    output(\"q2\", measure(q).read())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**[Functions](https://docs.quantinuum.com/guppy/language_guide/functions.html)** — `language_guide/functions` block 0\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from guppylang import guppy\n",
    "from guppylang.std.quantum import qubit, h, x, measure\n",
    "\n",
    "@guppy\n",
    "def my_subroutine(q: qubit) -> None:\n",
    "    h(q)\n",
    "    x(q)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**[Functions](https://docs.quantinuum.com/guppy/language_guide/functions.html)** — `language_guide/functions` block 1\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "@guppy\n",
    "def entrypoint() -> None:\n",
    "\n",
    "    q1 = qubit()\n",
    "    q2 = qubit()\n",
    "\n",
    "    my_subroutine(q1)\n",
    "    my_subroutine(q2)\n",
    "\n",
    "    measure(q1)\n",
    "    measure(q2)\n",
    "\n",
    "my_hugr_program = entrypoint.compile()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**[Arrays](https://docs.quantinuum.com/guppy/language_guide/data_types/arrays.html)** — `language_guide/data_types/arrays` block 0\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from guppylang import guppy\n",
    "from guppylang.std.builtins import array\n",
    "\n",
    "@guppy\n",
    "def get_array() -> array[int, 3]:\n",
    "    return array(0, 2, 4)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**[Arrays](https://docs.quantinuum.com/guppy/language_guide/data_types/arrays.html)** — `language_guide/data_types/arrays` block 1\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "@guppy\n",
    "def mutate_array() -> array[int, 3]:\n",
    "    numbers = get_array() # Create array containing 0, 2 and 4\n",
    "    numbers[0] = 17 # Change first element to 17\n",
    "    return numbers # Return modified array\n",
    "\n",
    "mutate_array.check()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**[Structs](https://docs.quantinuum.com/guppy/language_guide/data_types/structs.html)** — `language_guide/data_types/structs` block 0\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from guppylang import guppy\n",
    "from guppylang.std.builtins import array\n",
    "\n",
    "@guppy.struct\n",
    "class PauliString:\n",
    "    xs: array[bool, 3]\n",
    "    zs: array[bool, 3]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**[Structs](https://docs.quantinuum.com/guppy/language_guide/data_types/structs.html)** — `language_guide/data_types/structs` block 2\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "@guppy\n",
    "def mutate_pauli() -> None:\n",
    "    # Create an instance of PauliString to represent XZX (XIX * IZI ~ XZX).\n",
    "    my_pauli = PauliString(array(True, False, True), array(False, True, False)) \n",
    "\n",
    "    # After this mutation, my_pauli now represents XIX\n",
    "    my_pauli.zs = array(False, False, False)\n",
    "\n",
    "mutate_pauli.check();"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**[Control flow](https://docs.quantinuum.com/guppy/language_guide/control_flow.html)** — `language_guide/control_flow` block 0\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from guppylang import guppy\n",
    "from guppylang.std.quantum import qubit, measure\n",
    "from guppylang.std.builtins import owned\n",
    "\n",
    "@guppy\n",
    "def outcome_message(q: qubit @owned) -> str:\n",
    "    if measure(q):\n",
    "        return \"Measured 1!\"\n",
    "    else:\n",
    "        return \"Measured 0!\"\n",
    "\n",
    "outcome_message.check()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**[Control flow](https://docs.quantinuum.com/guppy/language_guide/control_flow.html)** — `language_guide/control_flow` block 1\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from guppylang.std.quantum import h, cx\n",
    "from guppylang.std.builtins import array\n",
    "\n",
    "n = guppy.nat_var(\"n\")\n",
    "\n",
    "@guppy\n",
    "def entangle(qs: array[qubit, n]) -> None:\n",
    "    h(qs[0])\n",
    "    for i in range(len(qs)):\n",
    "        if i != 0:\n",
    "            cx(qs[0], qs[i])\n",
    "\n",
    "entangle.check()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**[Measurements](https://docs.quantinuum.com/guppy/language_guide/measurement.html)** — `language_guide/measurement` block 0\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from guppylang import guppy\n",
    "from guppylang.std.builtins import owned\n",
    "from guppylang.std.quantum import measure, qubit\n",
    "\n",
    "@guppy\n",
    "def deferred_measurement(q: qubit @ owned) -> None:\n",
    "    m = measure(q) # measurement requested here\n",
    "    #   â \n",
    "    #   â\n",
    "    #   â  <- other quantum operations can be parallelised\n",
    "    #   â     here while this measurement resolves\n",
    "    #   â¼\n",
    "    output(\"q\", m.read()) # result needed here at the latest - blocks until ready\n",
    "\n",
    "deferred_measurement.check()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**[Measurements](https://docs.quantinuum.com/guppy/language_guide/measurement.html)** — `language_guide/measurement` block 1\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "@guppy\n",
    "def conditional_output(q: qubit @ owned) -> None:\n",
    "    if measure(q):\n",
    "        output(\"true\", 1)\n",
    "    else:\n",
    "        output(\"false\", 0)\n",
    "\n",
    "conditional_output.check()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**[Ownership and Linear Types](https://docs.quantinuum.com/guppy/language_guide/ownership.html)** — `language_guide/ownership` block 0\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from guppylang import guppy\n",
    "from guppylang.std.quantum import qubit, h, cx\n",
    "\n",
    "@guppy\n",
    "def prepare_bell() -> tuple[qubit, qubit]:\n",
    "    q0, q1 = qubit(), qubit() # Allocated qubits owned by this scope\n",
    "    h(q0) \n",
    "    cx(q0, q1)\n",
    "    return q0, q1 # Transfers ownership to the caller"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**[Ownership and Linear Types](https://docs.quantinuum.com/guppy/language_guide/ownership.html)** — `language_guide/ownership` block 1\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from guppylang.std.quantum import measure\n",
    "\n",
    "@guppy\n",
    "def main() -> None:\n",
    "  # Qubit ownership transferred to q0, q1 from inside prepare_bell\n",
    "  q0, q1 = prepare_bell()\n",
    "  # Measurement consumes the qubits\n",
    "  measure(q0)\n",
    "  measure(q1)\n",
    "\n",
    "# If we check, we see that this works :)\n",
    "main.check()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**[@guppy decorator](https://docs.quantinuum.com/guppy/api/decorator.html)** — `api/decorator` block 0\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from guppylang import guppy\n",
    "from guppylang.std.quantum import h, qubit\n",
    "\n",
    "@guppy\n",
    "def plus_q() -> qubit:\n",
    "   \"\"\"Allocate and prepare a qubit in the |+> state\"\"\"\n",
    "   q = qubit()\n",
    "   h(q)\n",
    "   return q"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**[@guppy decorator](https://docs.quantinuum.com/guppy/api/decorator.html)** — `api/decorator` block 1\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from guppylang import guppy\n",
    "from guppylang.std.builtins import array\n",
    "\n",
    "@guppy.comptime\n",
    "def print_arrays(arr1: array[str, 10], arr2: array[str, 10]) -> None:\n",
    "   for s1, s2 in zip(arr1, arr2):\n",
    "      print(f\"({s1}, {s2})\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**[Emulator](https://docs.quantinuum.com/guppy/api/emulator.html)** — `api/emulator` block 0\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from guppylang import guppy\n",
    "from guppylang.std.builtins import output\n",
    "from guppylang.std.quantum import qubit, measure\n",
    "\n",
    "@guppy\n",
    "def foo() -> None:\n",
    "    q = qubit()\n",
    "    output(\"q\", measure(q).read())\n",
    "\n",
    "foo.emulator(n_qubits=1).run()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**[Emulator](https://docs.quantinuum.com/guppy/api/emulator.html)** — `api/emulator` block 4\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from selene_sim.backends.bundled_error_models import DepolarizingErrorModel\n",
    "\n",
    "error_model = DepolarizingErrorModel(\n",
    "    random_seed=123141,\n",
    "    p_1q=1e-5,\n",
    "    p_2q=1.4e-4,\n",
    "    p_meas=1e-3,\n",
    "    p_init=1e-5\n",
    ")\n",
    "\n",
    "foo.emulator(1).with_error_model(error_model).run()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**[N-qubit GHZ and Graph state preparation](https://docs.quantinuum.com/guppy/guppylang/examples/ghz_and_graph.html)** — `guppylang/examples/ghz_and_graph` block 0\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import networkx as nx\n",
    "from collections import Counter\n",
    "from guppylang import guppy\n",
    "from guppylang.defs import GuppyFunctionDefinition\n",
    "from guppylang.std.builtins import array, output\n",
    "from guppylang.std.quantum import cx, cz, h, measure_array, qubit, collect_measurements\n",
    "from guppylang.emulator import EmulatorResult"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**[N-qubit GHZ and Graph state preparation](https://docs.quantinuum.com/guppy/guppylang/examples/ghz_and_graph.html)** — `guppylang/examples/ghz_and_graph` block 1\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Declare generic variable\n",
    "n = guppy.nat_var(\"n\")\n",
    "\n",
    "\n",
    "# define guppy function generic over array size\n",
    "@guppy\n",
    "def build_ghz_state(q: array[qubit, n]) -> None:\n",
    "    h(q[0])\n",
    "    # array size argument used in range to produce statically sized array\n",
    "    for i in range(n - 1):\n",
    "        cx(q[i], q[i + 1])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**[The SWAP test and loading pytket circuits](https://docs.quantinuum.com/guppy/guppylang/examples/swap_test.html)** — `guppylang/examples/swap_test` block 0\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from guppylang import guppy\n",
    "from guppylang.std.quantum import qubit, h, cx, toffoli, measure, discard_array\n",
    "from guppylang.emulator import EmulatorResult\n",
    "from guppylang.std.builtins import output, array\n",
    "\n",
    "\n",
    "from pytket import Circuit\n",
    "from pytket.circuit import StatePreparationBox\n",
    "from pytket.passes import DecomposeBoxes\n",
    "\n",
    "import numpy as np"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**[The SWAP test and loading pytket circuits](https://docs.quantinuum.com/guppy/guppylang/examples/swap_test.html)** — `guppylang/examples/swap_test` block 1\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "@guppy\n",
    "def cswap(control: qubit, q1: qubit, q2: qubit) -> None:\n",
    "    cx(q1, q2)\n",
    "    toffoli(control, q2, q1)\n",
    "    cx(q1, q2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**[Repeat Until Success](https://docs.quantinuum.com/guppy/guppylang/examples/repeat-until-success.html)** — `guppylang/examples/repeat-until-success` block 0\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import math\n",
    "\n",
    "from guppylang import guppy\n",
    "from guppylang.std.builtins import output\n",
    "from guppylang.std.quantum import measure, qubit, discard, h, tdg, cx, t, z\n",
    "\n",
    "\n",
    "@guppy\n",
    "def repeat_until_success(q: qubit) -> None:\n",
    "    attempts = 0\n",
    "    while True:\n",
    "        attempts += 1\n",
    "\n",
    "        # Prepare ancilla qubits\n",
    "        a, b = qubit(), qubit()\n",
    "        h(a)\n",
    "        h(b)\n",
    "\n",
    "        tdg(a)\n",
    "        cx(b, a)\n",
    "        t(a)\n",
    "        h(a)\n",
    "        if measure(a):\n",
    "            # First ancilla failed, consume all ancillas, try again\n",
    "            discard(b)\n",
    "            continue\n",
    "\n",
    "        t(q)\n",
    "        z(q)\n",
    "        cx(q, b)\n",
    "        t(b)\n",
    "        h(b)\n",
    "        if measure(b):\n",
    "            # Second ancilla failed, apply correction and try again\n",
    "            z(q)\n",
    "            continue\n",
    "\n",
    "        output(\"attempts\", attempts)\n",
    "        break\n",
    "\n",
    "\n",
    "repeat_until_success.check()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**[Repeat Until Success](https://docs.quantinuum.com/guppy/guppylang/examples/repeat-until-success.html)** — `guppylang/examples/repeat-until-success` block 1\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "@guppy\n",
    "def main() -> None:\n",
    "    q = qubit()\n",
    "    repeat_until_success(q)\n",
    "    discard(q)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**[Migrating to Guppy Version 1.0](https://docs.quantinuum.com/guppy/v1_migration.html)** — `v1_migration` block 0\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "@guppy\n",
    "def f(a: array[qubit, 6] @owned) -> None:\n",
    "    for q in a:\n",
    "        result(\"t\", measure(q))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**[Migrating to Guppy Version 1.0](https://docs.quantinuum.com/guppy/v1_migration.html)** — `v1_migration` block 1\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "@guppy\n",
    "def f(a: array[qubit, 6] @owned) -> None:\n",
    "    for q in a:\n",
    "        output(\"t\", measure(q).read())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 3. Bridge: running a Guppy kernel through our own stack\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# In this repo every kernel is executed through quantum/emulate.py, which keeps\n",
    "# the legacy build(program).run_shots(...) call shape on top of the v1 emulator\n",
    "# builder. Run from the repo root with PYTHONPATH=.pydeps.\n",
    "#\n",
    "#     from quantum.emulate import build, Quest\n",
    "#     shots = build(my_program).run_shots(Quest(), n_qubits=2, n_shots=512)\n",
    "#\n",
    "# The @guppy function must live in a real .py file on disk — Guppy reads source\n",
    "# with inspect.getsource, so a REPL/notebook-defined kernel fails to compile.\n",
    "# To parameterise a kernel, generate a temp module and import it:\n",
    "#\n",
    "#     see quantum/qpde/kernel.py and references/driver-pattern.md\n",
    "print('see quantum/emulate.py and quantum/qpde/kernel.py')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 4. Pitfalls\n",
    "\n",
    "1. `@guppy` functions must live in a real `.py` file on disk — `inspect.getsource` cannot read a REPL, `exec()` string, or notebook cell. Parameterise by generating a temp module and importing it with `importlib.util`.\n",
    "2. `angle(x)` is in **halfturns**, not radians. `angle(0.5)` is π/2 (an S gate). For a radian θ write `angle(theta / math.pi)`; when a source formula already contains an explicit π, divide it out first.\n",
    "3. Guppy v1 renamed the result API: `output(...)` replaces `result(...)`, and `measure(q)` returns a `Measurement`, so read it with `measure(q).read()`.\n",
    "4. v1 optimises on compile — pin `OptimizationLevel.Classical` for any gate-count or rewriter benchmark, or the compiler's own peepholes get credited to your rewriter.\n"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "name": "python",
   "version": "3.12"
  },
  "title": "Guppy quickstart"
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
