diff --git a/examples/adaptive-qpe.ipynb b/examples/adaptive-qpe.ipynb new file mode 100644 index 000000000..22317a6b9 --- /dev/null +++ b/examples/adaptive-qpe.ipynb @@ -0,0 +1,174 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "c4e97d94-9f03-4956-bb03-2f1753d8afa5", + "metadata": {}, + "source": [ + "# Adaptive QPE\n", + "\n", + "**Download this notebook - {nb-download}`adaptive-qpe.ipynb`**\n", + "\n", + "Implementation of the adaptive random walk phase estimation algorithm from\n", + "https://arxiv.org/abs/2208.04526.\n", + "\n", + "The example Hamiltonian and numbers are taken from https://arxiv.org/abs/2206.12950." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "8462b05b-5e24-46c7-9021-0bdaf9e90029", + "metadata": {}, + "outputs": [], + "source": [ + "import math\n", + "from guppylang import guppy\n", + "from guppylang.std.angles import angle\n", + "from guppylang.std.builtins import comptime, result\n", + "from guppylang.std.quantum import discard, measure, qubit, h, rz, x, crz" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "d77268ee-b737-41cf-86a8-6abfb10b9b89", + "metadata": {}, + "outputs": [], + "source": [ + "@guppy\n", + "def oracle(ctrl: qubit, q: qubit, t: float) -> None:\n", + " \"\"\"Applies a controlled e^-iπHt/2 gate for the example Hamiltonian H = 0.5 * Z.\"\"\"\n", + " crz(ctrl, q, angle(0.5 * t))\n", + "\n", + "\n", + "@guppy\n", + "def eigenstate() -> qubit:\n", + " \"\"\"Prepares eigenstate of the example Hamiltonian H = 0.5 * Z.\"\"\"\n", + " q = qubit()\n", + " x(q)\n", + " return q" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "f4b1a3fe-5586-44a4-b891-7af85c28013e", + "metadata": {}, + "outputs": [], + "source": [ + "sqrt_e = math.sqrt(math.e)\n", + "sqrt_e_div = math.sqrt((math.e - 1) / math.e)\n", + "\n", + "\n", + "@guppy\n", + "def main() -> None:\n", + " # Pick initial estimate of phase mean and stdv\n", + " # and prepare eigenstate\n", + " mu, sigma = comptime(sqrt_e_div), 1 / comptime(sqrt_e)\n", + " tgt = eigenstate()\n", + " for _ in range(24):\n", + " t = 1 / sigma\n", + "\n", + " aux = qubit()\n", + " h(aux)\n", + " rz(aux, angle((sigma - mu) * t))\n", + " oracle(aux, tgt, t)\n", + " h(aux)\n", + "\n", + " if measure(aux):\n", + " mu += sigma / comptime(sqrt_e)\n", + " else:\n", + " mu -= sigma / comptime(sqrt_e)\n", + " sigma *= comptime(sqrt_e_div)\n", + "\n", + " discard(tgt)\n", + " result(\"eigenvalue\", 2 * mu)" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "7719eb57-95a0-4c76-a9ec-6ebbe9316f0f", + "metadata": {}, + "outputs": [], + "source": [ + "from selene_sim import build, Quest\n", + "from hugr.qsystem.result import QsysResult\n", + "\n", + "runner = build(guppy.compile(main))" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "4a51f004-53aa-41d0-884b-cdfd7c6efab5", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[('eigenvalue', 2.3679925580437398)]" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "list(runner.run(Quest(random_seed=2), 2))" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "526d9fad-1a5a-4adb-bf11-41729e03c3cf", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import matplotlib.pyplot as plt\n", + "import matplotlib.ticker as ticker\n", + "\n", + "shots = QsysResult(runner.run_shots(Quest(random_seed=2), n_qubits=2, n_shots=500))\n", + "\n", + "fig, ax = plt.subplots(1, 1)\n", + "ax.hist([shot.as_dict()[\"eigenvalue\"] for shot in shots.results], bins=100)\n", + "ax.xaxis.set_major_locator(ticker.MultipleLocator(0.5))\n", + "plt.show()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "guppylang (3.13.4)", + "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.13.4" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/examples/canonical-qpe.ipynb b/examples/canonical-qpe.ipynb new file mode 100644 index 000000000..bfcac0b51 --- /dev/null +++ b/examples/canonical-qpe.ipynb @@ -0,0 +1,347 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Canonical Quantum Phase estimation\n", + "\n", + "**Download this notebook - {nb-download}`canonical-qpe.ipynb`**\n", + "\n", + "\n", + "\n", + "Based on this [stack exchange post](https://quantumcomputing.stackexchange.com/questions/32594/how-would-you-draw-the-phase-estimation-circuit-for-the-eigenvalues-of-u-mat/32598#32598). See also the old [pytket example notebook on QPE](https://docs.quantinuum.com/tket/user-guide/examples/algorithms_and_protocols/phase_estimation.html).\n" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "from guppylang import guppy\n", + "from guppylang.std.angles import pi\n", + "from guppylang.std.quantum import qubit, h, crz, x, measure_array, discard_array\n", + "from guppylang.std.builtins import result, array, mem_swap\n", + "from hugr.qsystem.result import QsysResult\n", + "\n", + "from selene_sim import build, Quest\n", + "\n", + "from pytket.circuit import DiagonalBox, QControlBox\n", + "from pytket.passes import AutoRebase\n", + "from pytket import OpType\n", + "\n", + "import numpy as np" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Background" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "Phase estimation is an important quantum algorithm for estimating the eigenvalues of a unitary operator $U$ to some precision. Quantum phase estimation appears as an important subroutine in Shor's algorithm and various fault tolerant approaches to quantum chemistry. In this notebook we will consider the \"canonical\" QPE variant which is implemented by a pure unitary circuit.\n", + "\n", + "\n", + "\n", + "If $U$ is a unitary matrix, its eigenvalues must lie on the unit circle.\n", + "\n", + "\\begin{equation*}\n", + "U |\\psi \\rangle = e^{2 \\pi i \\theta}|\\psi\\rangle\\,, \\quad \\theta \\in [0, 1) \n", + "\\end{equation*}\n", + "\n", + "Here $|\\psi\\rangle$ is an eigenstate of $U$.\n", + "\n", + "We estimate the eigenvalues by approximating $\\theta$ in the equation above." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We will consider a very simplified version of phase estimation wherein $U$ is a diagonal matrix. This means the true eigenvalues can be read off the diagonal. This will allow us to clearly see that our implementation is correct." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "$$\n", + "U =\n", + "\\begin{pmatrix}\n", + "1 & 0 & 0 & 0 \\\\\n", + "0 & 1 & 0 & 0 \\\\\n", + "0 & 0 & e^{ i \\frac{\\pi}{4}} & 0 \\\\\n", + "0 & 0 & 0 & e^{ i \\frac{\\pi}{8}}\n", + "\\end{pmatrix}\n", + "$$" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Trivial State Preparation\n", + "\n", + "Clearly the matrix $U$ has the eigenvalue $e^{i \\frac{\\pi}{8}}$ corresponding to the eigenstate $|11\\rangle = (0, 0, 0, 1)^T$.\n", + "\n", + "We can prepare this trivial eigenstate with two Pauli $X$ gates." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "@guppy\n", + "def prepare_trivial_eigenstate() -> array[qubit, 2]:\n", + " q0, q1 = qubit(), qubit()\n", + " x(q0)\n", + " x(q1)\n", + " return array(q0, q1)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Controlled-$U$ Operations\n", + "\n", + "Next we need to create a subroutine applying a Controlled-$U$ operation. This will be repeatedly applied and will kick back phase factors of $e^{i \\frac{\\pi}{8}}$ onto the ancilla qubits. We can generate this using a pytket [DiagonalBox](https://docs.quantinuum.com/tket/api-docs/circuit.html#pytket.circuit.DiagonalBox) where we apply an additional control. " + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "d_box = DiagonalBox(np.array([1, 1, np.exp(1j * np.pi / 4), np.exp(1j * np.pi / 8)]))\n", + "controlled_u_op = QControlBox(d_box, 1)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Circuits created using pytket can be loaded into Guppy as functions using the `load_pytket` function (or alternatively by specifying a function stub with the same signature as the circuit and annotating it with `@pytket(circ)`). However, the circuit being loaded can only contain gates that exist in the Guppy `quantum` library, so we need to rebase the generated circuit to remove a `TK1` gate." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "rebase = AutoRebase({OpType.CX, OpType.Rz, OpType.H, OpType.CCX})\n", + "circ = controlled_u_op.get_circuit()\n", + "rebase.apply(circ)\n", + "\n", + "controlled_u = guppy.load_pytket(\"controlled_u_circuit\", circ, use_arrays=False)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Inverse Quantum Fourier Transform\n", + "\n", + "The final subroutine we need is the inverse quantum fourier transform (IQFT). This has the effect of inducing destructive interference at the end of our circuit. This means that we are more likely to measure a single basis state (or a small set of basis states).\n", + "\n", + "We can define a generalised Guppy function over $n$ qubits. We do this by defining a natural number variable $n$. Our Guppy program for the IQFT is then polymorphic over $n$." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "n = guppy.nat_var(\"n\")\n", + "\n", + "@guppy\n", + "def inverse_qft(qs: array[qubit, n]) -> None:\n", + " # Reverse qubit order with swaps\n", + " for k in range(n // 2):\n", + " mem_swap(qs[k], qs[n - k - 1])\n", + "\n", + " for i in range(n):\n", + " h(qs[n - i - 1])\n", + " for j in range(n - i - 1):\n", + " crz(qs[n - i - 1], qs[n - i - j - 2], -pi / 2 ** (j + 1))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Note that we can have a IQFT subroutine for any number of qubits that we like by adjusting the size of the input array." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## The QPE Program\n", + "\n", + "Now that we have defined subroutines for the state preparation, controlled unitaries and IQFT steps, we can combine these into a single function to perform quantum phase estimation.\n", + "\n", + "First we define an array of measurement qubits of size $m$. The more measurement qubits we have the more precise our estimate of the phase $\\theta$ will be." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now we can define a function to implement phase estimation using our `inverse_qft` and `controlled_u` functions. Here we will have $n$ measurement qubits. We fix the size of the initial state to have only two qubits. \n", + "\n", + "The QPE construction can be generalised in a similar manner to the inverse QFT function. A larger value of $n$ will mean that we can estimate the eigenphase of $U$ to greater precision." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "@guppy\n", + "def phase_estimation(measured: array[qubit, n], state: array[qubit, 2]) -> None:\n", + " for i in range(n):\n", + " h(measured[i])\n", + "\n", + " # Add 2^n - 1 controlled unitaries sequentially\n", + " for n_index in range(n):\n", + " control_index: int = n - n_index - 1\n", + " for _ in range(2**n_index):\n", + " controlled_u(measured[control_index], state[0], state[1])\n", + "\n", + " inverse_qft(measured)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Execution on Selene\n", + "\n", + "Let's execute this QPE program on the Selene emulator for 500 shots." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can define a `main` function which includes our six qubit phase estimation subroutine and measurements. This can then be compiled for execution on the Selene simulator." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "@guppy\n", + "def main() -> None:\n", + " state = prepare_trivial_eigenstate()\n", + " measured = array(qubit() for _ in range(4))\n", + " phase_estimation(measured, state)\n", + "\n", + " # state qubits are not measured so have to be explicitly discarded\n", + " discard_array(state)\n", + "\n", + " # Create a result from the measured array\n", + " result(\"c\", measure_array(measured))\n", + "\n", + "\n", + "compiled_HUGR = guppy.compile(main)" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "n_shots = 500\n", + "\n", + "runner = build(compiled_HUGR)\n", + "sim_result = QsysResult(\n", + " runner.run_shots(Quest(random_seed=5), n_qubits=6, n_shots=n_shots)\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now that we have executed our phase estimation instance on the Selene emulator we can analyse our results.\n", + "\n", + "Let's look at our measurement outcomes. In this highly idealised QPE instance we expect all of our measurement outcomes to be $|0001\\rangle$." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Counter({'0001': 500})\n" + ] + } + ], + "source": [ + "result_counter = sim_result.register_counts()[\"c\"]\n", + "assert result_counter[\"0001\"] == n_shots\n", + "print(result_counter)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We see that all of our measurements yield the basis state $|0001\\rangle$ which encodes the integer $j=1$ in four bits.\n", + "\n", + "$$\n", + "\\theta = \\frac{j}{2^m}\n", + "$$\n", + "\n", + "Here $n$ is the number of evaluation qubits (4 in our case). The value of $j$ is given by the decimal value of the most frequent measurement outcome.\n", + "\n", + "$$\n", + "\\theta = \\frac{1}{2^4} = \\frac{1}{16}\n", + "$$" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "guppylang (3.13.4)", + "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.13.4" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/examples/control-flow.ipynb b/examples/control-flow.ipynb new file mode 100644 index 000000000..545ffbdf2 --- /dev/null +++ b/examples/control-flow.ipynb @@ -0,0 +1,147 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "4b1ce9ef-6970-49bf-bae4-332d3eef0423", + "metadata": {}, + "source": [ + "# Control-Flow\n", + "\n", + "**Download this notebook - {nb-download}`control-flow.ipynb`**" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "0ef5d6cd-cb12-4485-97fb-eae6e4c1cd32", + "metadata": {}, + "outputs": [], + "source": [ + "from guppylang import guppy\n", + "from guppylang.std.builtins import result\n", + "from guppylang.std.quantum import measure, qubit, discard, h, tdg, cx, t, z" + ] + }, + { + "cell_type": "markdown", + "id": "9ab7cb6e-b11b-4b0b-aab9-a113bb2618b0", + "metadata": {}, + "source": [ + "Implement an $V_3 = R_z(-2\\arctan(2)) = (I + 2iZ)/\\sqrt{5}$ gate using the repeat-until-success scheme from https://arxiv.org/abs/1311.1074." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "1be5c488-36df-4a8b-b0af-0a330a8293fd", + "metadata": {}, + "outputs": [], + "source": [ + "@guppy\n", + "def repeat_until_success(q: qubit) -> None:\n", + " attempts = 0\n", + " while True:\n", + " attempts += 1\n", + "\n", + " # Prepare ancillas\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 part failed, 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 part failed, apply correction\n", + " # and try again\n", + " z(q)\n", + " continue\n", + "\n", + " result(\"attempts\", attempts)\n", + " break" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "fbd490b3-8cc4-43c2-87f8-e799d00fa2c2", + "metadata": {}, + "outputs": [], + "source": [ + "@guppy\n", + "def main() -> None:\n", + " q = qubit()\n", + " repeat_until_success(q)\n", + " measure(q)" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "e1078a60-06b8-4d9c-a7be-8db7736f0eeb", + "metadata": {}, + "outputs": [], + "source": [ + "from selene_sim import build, Quest\n", + "from hugr.qsystem.result import QsysResult\n", + "\n", + "runner = build(guppy.compile(main))" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "831c0673-a3f1-4159-bf39-aae7ec9e3323", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "1.49" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "shots = QsysResult(runner.run_shots(Quest(random_seed=0), n_qubits=3, n_shots=100))\n", + "\n", + "sum(int(shot.as_dict()[\"attempts\"]) for shot in shots.results) / len(shots.results)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".venv", + "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.12.6" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/examples/ghz_and_graph.ipynb b/examples/ghz_and_graph.ipynb new file mode 100644 index 000000000..fab6e5e91 --- /dev/null +++ b/examples/ghz_and_graph.ipynb @@ -0,0 +1,247 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# N-qubit GHZ and Graph state preparation\n", + "\n", + "**Download this notebook - {nb-download}`ghz_and_graph.ipynb`**\n", + "\n", + "In this example we prepare n-qubit GHZ and graph states and perform stabilizer\n", + "simulation using Stim. To do this we make use of the fact that Guppy can load in values\n", + "from the host python program and inject them in to the resulting Guppy program as constants." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import networkx as nx\n", + "from collections import Counter\n", + "from guppylang import guppy\n", + "from hugr.qsystem.result import QsysResult\n", + "from guppylang.std.builtins import array, comptime, result\n", + "from guppylang.std.quantum import cx, cz, h, measure_array, qubit\n", + "from selene_sim import build, Stim" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We would like to write one Guppy function for any number of qubits, but we want the\n", + "number of qubits to be known at Guppy compile time so we can check the number of qubits\n", + "and avoid using dynamic classical or quantum allocation (which can be slow and induce\n", + "memory error).\n", + "\n", + "To achieve this we can define a Guppy function that is _generic_ over the size of an array,\n", + "because arrays have sizes known at compile time." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "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": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "def build_ghz_prog(n_qb: int):\n", + " \"\"\"Build a Guppy program that prepares a GHZ state on `n_qb` qubits.\"\"\"\n", + "\n", + " # we can define the entry point to the guppy program dependent on\n", + " # the number of qubits we want to use.\n", + "\n", + " @guppy\n", + " def main() -> None:\n", + " # allocate number of qubits specified from outer\n", + " # python function using a `comptime` expression.\n", + " q = array(qubit() for _ in range(comptime(n_qb)))\n", + "\n", + " build_ghz_state(q)\n", + "\n", + " result(\"c\", measure_array(q))\n", + "\n", + " # return the compiled program (HUGR)\n", + " return main.compile()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's define a quick utility to help us read out our results as bitstring counts." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "def get_counts(shots: QsysResult) -> Counter[str]:\n", + " \"\"\"Counter treating all results from a shot as entries in a single bitstring\"\"\"\n", + " counter_list = []\n", + " for shot in shots.results:\n", + " for e in shot.entries:\n", + " bitstring = \"\".join(str(k) for k in e[1])\n", + " counter_list.append(bitstring)\n", + "\n", + " return Counter(counter_list)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can now run our GHZ prep, we expect to see an even mix of |0..0> and |1..1>." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Counter({'000000': 51, '111111': 49})" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "ghz_prog = build_ghz_prog(6)\n", + "runner = build(ghz_prog)\n", + "\n", + "shots = QsysResult(runner.run_shots(Stim(random_seed=2), n_qubits=6, n_shots=100))\n", + "\n", + "get_counts(shots)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Similarly, we can define a [graph state](https://en.wikipedia.org/wiki/Graph_state) over\n", + "an arbitrary graph by first using `networkx` to define our graph as a list of edges, and\n", + "loading those edge pairs in to Guppy.\n", + "\n", + "Because the Guppy compiler knows the length of the list being pulled in, it can\n", + "load it in as a statically sized array (just like the qubit array)." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "def build_graph_state(graph: nx.Graph):\n", + " edges = list(graph.edges)\n", + " n_qb = graph.number_of_nodes()\n", + "\n", + " @guppy\n", + " def main() -> None:\n", + " qs = array(qubit() for _ in range(comptime(n_qb)))\n", + "\n", + " for i in range(len(qs)):\n", + " h(qs[i])\n", + "\n", + " for i, j in comptime(edges):\n", + " # apply CZ along every graph edge\n", + " cz(qs[i], qs[j])\n", + "\n", + " result(\"c\", measure_array(qs))\n", + "\n", + " return main.compile()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's test out our graph state builder with a $K_3$ complete graph over 3 nodes. We\n", + "expect to see an even mix of all 3 qubit basis states when measured." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[('000', 12),\n", + " ('001', 14),\n", + " ('010', 15),\n", + " ('011', 10),\n", + " ('100', 16),\n", + " ('101', 9),\n", + " ('110', 13),\n", + " ('111', 11)]" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "k3_graph = nx.complete_graph(3)\n", + "\n", + "graph_prog = build_graph_state(k3_graph)\n", + "runner = build(graph_prog)\n", + "\n", + "shots = QsysResult(runner.run_shots(Stim(random_seed=2), n_qubits=3, n_shots=100))\n", + "\n", + "sorted(get_counts(shots).items())" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "guppylang (3.13.4)", + "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.13.4" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/examples/img/swap_test_circuit.png b/examples/img/swap_test_circuit.png new file mode 100644 index 000000000..d0d6f02ae Binary files /dev/null and b/examples/img/swap_test_circuit.png differ diff --git a/examples/postselect.ipynb b/examples/postselect.ipynb new file mode 100644 index 000000000..9e9e6f99f --- /dev/null +++ b/examples/postselect.ipynb @@ -0,0 +1,292 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Postselection: exit and panic\n", + "\n", + "**Download this notebook - {nb-download}`postselect.ipynb`**\n", + "\n", + "\n", + "In this example we will look at two ways of ending a Guppy program early:\n", + "\n", + "1. `exit` will end the current shot and carry on with subsequent ones. We will use this to\n", + " implement postselection.\n", + "2. `panic` is used to signal some unexpected error and as such it will end the shot and\n", + " not run any subsequent ones." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "from typing import Counter\n", + "\n", + "from guppylang import guppy\n", + "from guppylang.std.builtins import result, array, exit, panic\n", + "from guppylang.std.quantum import measure_array, measure, qubit, h, cx\n", + "from hugr.qsystem.result import QsysResult\n", + "from guppylang.defs import GuppyDefinition\n", + "\n", + "from selene_sim import build, Stim, DepolarizingErrorModel\n", + "from selene_sim.exceptions import SelenePanicError" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Postselection\n", + "\n", + "We can use postselection to implement fault tolerant state preparation for the [[7, 1,\n", + "3]] [Steane code](https://en.wikipedia.org/wiki/Steane_code).\n", + "\n", + "Let's first define our \"Steane qubit\" as a struct containing our 7 data qubits, then\n", + "write a function to prepare an encoded $|0\\rangle$ non-fault tolerantly. We use the\n", + "preparation circuit from [_Realization of real-time fault-tolerant quantum error correction_](https://arxiv.org/abs/2107.07505).\n" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "@guppy.struct\n", + "class SteaneQubit:\n", + " data_qs: array[qubit, 7]\n", + "\n", + "\n", + "@guppy\n", + "def non_ft_zero() -> SteaneQubit:\n", + " data_qubits = array(qubit() for _ in range(7))\n", + " plus_ids = array(0, 4, 6)\n", + " for i in plus_ids:\n", + " h(data_qubits[i])\n", + "\n", + " cx_pairs = array((0, 1), (4, 5), (6, 3), (6, 5), (4, 2), (0, 3), (4, 1), (3, 2))\n", + " for c, t in cx_pairs:\n", + " cx(data_qubits[c], data_qubits[t])\n", + " return SteaneQubit(data_qubits)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can now implement fault-tolerant preparation using postselection. We can use an\n", + "ancilla to check the prepared state, and if we detect an error use `exit` to end the\n", + "shot with a message about why we exited." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "@guppy\n", + "def ft_zero() -> SteaneQubit:\n", + " q = non_ft_zero()\n", + " ancilla = qubit()\n", + " flags = array(1, 3, 5)\n", + " for f in flags:\n", + " cx(q.data_qs[f], ancilla)\n", + " if measure(ancilla):\n", + " exit(\"Postselected: FT prep failed\", 1)\n", + " return q" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's define a couple of utility functions - a Guppy function to check the parity of a\n", + "bit array, and a python function to run our program and report the results.\n", + "\n", + "We use a simple depolarizing error model to induce errors in the preparation." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "n = guppy.nat_var(\"n\")\n", + "\n", + "\n", + "@guppy\n", + "def parity_check(data_bits: array[bool, n]) -> bool:\n", + " out = False\n", + " for i in range(n):\n", + " out ^= data_bits[i]\n", + " return out\n", + "\n", + "\n", + "error_model = DepolarizingErrorModel(\n", + " random_seed=1234,\n", + " # single qubit gate error rate\n", + " p_1q=1e-3,\n", + " # two qubit gate error rate\n", + " p_2q=1e-3,\n", + " # set state preparation and measurement error rates to 0\n", + " p_meas=0,\n", + " p_init=0,\n", + ")\n", + "\n", + "\n", + "def run(main_def: GuppyDefinition) -> Counter:\n", + " runner = build(main_def.compile())\n", + " res = QsysResult(\n", + " runner.run_shots(\n", + " Stim(random_seed=42),\n", + " n_qubits=8,\n", + " n_shots=1000,\n", + " error_model=error_model,\n", + " )\n", + " )\n", + "\n", + " return res.collated_counts()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can now define our `main` program and run it. We know that all basis states of the\n", + "encoded Steane $|0\\rangle$ state have a $0$ parity, so we can use that to verify our preparation." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Counter({(('parity', '0'),): 968,\n", + " (('exit: Postselected: FT prep failed', '1'),): 23,\n", + " (('parity', '1'),): 9})" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "@guppy\n", + "def main() -> None:\n", + " steane_q = ft_zero()\n", + "\n", + " # Measure the data qubits\n", + " data = measure_array(steane_q.data_qs)\n", + " result(\"parity\", parity_check(data))\n", + "\n", + "\n", + "run(main)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As we can see, the state preparation succeeded in 968 cases out of 1000.\n", + "In 23 of the unsuccessful cases, an error was detected on the ancillas and was\n", + "discarded through postselection. In the remaining 9 cases, the state failed\n", + "in a way that was not detected by postselection but was instead detected by a\n", + "parity check performed after measuring all of the qubits.\n", + "\n", + "Note the result tag in the discarded shot is prefixed with `exit: ` followed\n", + "by the specified message, and the value of the result entry is the error code\n", + "(1 in this case)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Panic\n", + "\n", + "The `panic` function is similar to `exit` but is used for exceptional circumstances -\n", + "when something unexpected has gone wrong. For example we could define a physical\n", + "hadamard function that takes an index to act on the data qubit array. If the index is\n", + "out of bounds, we can panic with a helpful message. This will raise an error during the\n", + "simulation, and no subsequent shots will be run." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "tags": [ + "raises-exception" + ] + }, + "outputs": [ + { + "ename": "SelenePanicError", + "evalue": "Panic (#1001): Invalid data index in physical_h", + "output_type": "error", + "traceback": [ + "\u001b[31m---------------------------------------------------------------------------\u001b[39m", + "\u001b[31mSelenePanicError\u001b[39m Traceback (most recent call last)", + "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[6]\u001b[39m\u001b[32m, line 18\u001b[39m\n\u001b[32m 15\u001b[39m data = measure_array(steane_q.data_qs)\n\u001b[32m 16\u001b[39m result(\u001b[33m\"\u001b[39m\u001b[33mparity\u001b[39m\u001b[33m\"\u001b[39m, parity_check(data))\n\u001b[32m---> \u001b[39m\u001b[32m18\u001b[39m \u001b[43mrun\u001b[49m\u001b[43m(\u001b[49m\u001b[43mmain\u001b[49m\u001b[43m)\u001b[49m\n", + "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[4]\u001b[39m\u001b[32m, line 26\u001b[39m, in \u001b[36mrun\u001b[39m\u001b[34m(main_def)\u001b[39m\n\u001b[32m 24\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34mrun\u001b[39m(main_def: GuppyDefinition) -> Counter:\n\u001b[32m 25\u001b[39m runner = build(main_def.compile())\n\u001b[32m---> \u001b[39m\u001b[32m26\u001b[39m res = \u001b[43mQsysResult\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m 27\u001b[39m \u001b[43m \u001b[49m\u001b[43mrunner\u001b[49m\u001b[43m.\u001b[49m\u001b[43mrun_shots\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m 28\u001b[39m \u001b[43m \u001b[49m\u001b[43mStim\u001b[49m\u001b[43m(\u001b[49m\u001b[43mrandom_seed\u001b[49m\u001b[43m=\u001b[49m\u001b[32;43m42\u001b[39;49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 29\u001b[39m \u001b[43m \u001b[49m\u001b[43mn_qubits\u001b[49m\u001b[43m=\u001b[49m\u001b[32;43m8\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[32m 30\u001b[39m \u001b[43m \u001b[49m\u001b[43mn_shots\u001b[49m\u001b[43m=\u001b[49m\u001b[32;43m1000\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[32m 31\u001b[39m \u001b[43m \u001b[49m\u001b[43merror_model\u001b[49m\u001b[43m=\u001b[49m\u001b[43merror_model\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 32\u001b[39m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 33\u001b[39m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 35\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m res.collated_counts()\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/Code/CQCL/guppylang/hs/update_examples/.venv/lib/python3.13/site-packages/hugr/qsystem/result.py:145\u001b[39m, in \u001b[36mQsysResult.__init__\u001b[39m\u001b[34m(self, results)\u001b[39m\n\u001b[32m 141\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34m__init__\u001b[39m(\n\u001b[32m 142\u001b[39m \u001b[38;5;28mself\u001b[39m, results: Iterable[QsysShot | Iterable[TaggedResult]] | \u001b[38;5;28;01mNone\u001b[39;00m = \u001b[38;5;28;01mNone\u001b[39;00m\n\u001b[32m 143\u001b[39m ):\n\u001b[32m 144\u001b[39m \u001b[38;5;28mself\u001b[39m.results = [\n\u001b[32m--> \u001b[39m\u001b[32m145\u001b[39m res \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(res, QsysShot) \u001b[38;5;28;01melse\u001b[39;00m \u001b[43mQsysShot\u001b[49m\u001b[43m(\u001b[49m\u001b[43mres\u001b[49m\u001b[43m)\u001b[49m \u001b[38;5;28;01mfor\u001b[39;00m res \u001b[38;5;129;01min\u001b[39;00m results \u001b[38;5;129;01mor\u001b[39;00m []\n\u001b[32m 146\u001b[39m ]\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/Code/CQCL/guppylang/hs/update_examples/.venv/lib/python3.13/site-packages/hugr/qsystem/result.py:59\u001b[39m, in \u001b[36mQsysShot.__init__\u001b[39m\u001b[34m(self, entries)\u001b[39m\n\u001b[32m 58\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34m__init__\u001b[39m(\u001b[38;5;28mself\u001b[39m, entries: Iterable[TaggedResult] | \u001b[38;5;28;01mNone\u001b[39;00m = \u001b[38;5;28;01mNone\u001b[39;00m):\n\u001b[32m---> \u001b[39m\u001b[32m59\u001b[39m \u001b[38;5;28mself\u001b[39m.entries = \u001b[38;5;28;43mlist\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mentries\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;129;43;01mor\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43m[\u001b[49m\u001b[43m]\u001b[49m\u001b[43m)\u001b[49m\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/Code/CQCL/guppylang/hs/update_examples/.venv/lib/python3.13/site-packages/selene_sim/result_handling/parse_shot.py:154\u001b[39m, in \u001b[36mparse_shot\u001b[39m\u001b[34m(parser, event_hook, parse_results, stdout_file, stderr_file)\u001b[39m\n\u001b[32m 152\u001b[39m \u001b[38;5;66;03m# pass panic errors to the caller\u001b[39;00m\n\u001b[32m 153\u001b[39m \u001b[38;5;28;01mexcept\u001b[39;00m SelenePanicError \u001b[38;5;28;01mas\u001b[39;00m panic:\n\u001b[32m--> \u001b[39m\u001b[32m154\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m panic\n\u001b[32m 155\u001b[39m \u001b[38;5;28;01mexcept\u001b[39;00m SeleneRuntimeError \u001b[38;5;28;01mas\u001b[39;00m error:\n\u001b[32m 156\u001b[39m error.stdout = stdout_file.read_text()\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/Code/CQCL/guppylang/hs/update_examples/.venv/lib/python3.13/site-packages/selene_sim/result_handling/parse_shot.py:139\u001b[39m, in \u001b[36mparse_shot\u001b[39m\u001b[34m(parser, event_hook, parse_results, stdout_file, stderr_file)\u001b[39m\n\u001b[32m 137\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m parse_results:\n\u001b[32m 138\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m parsed.code >= \u001b[32m1000\u001b[39m:\n\u001b[32m--> \u001b[39m\u001b[32m139\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m SelenePanicError(\n\u001b[32m 140\u001b[39m message=parsed.message,\n\u001b[32m 141\u001b[39m code=parsed.code,\n\u001b[32m 142\u001b[39m stdout=stdout_file.read_text(),\n\u001b[32m 143\u001b[39m stderr=stderr_file.read_text(),\n\u001b[32m 144\u001b[39m )\n\u001b[32m 145\u001b[39m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[32m 146\u001b[39m \u001b[38;5;28;01myield\u001b[39;00m (\u001b[33mf\u001b[39m\u001b[33m\"\u001b[39m\u001b[33mexit: \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mparsed.message\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m\"\u001b[39m, parsed.code)\n", + "\u001b[31mSelenePanicError\u001b[39m: Panic (#1001): Invalid data index in physical_h" + ] + } + ], + "source": [ + "@guppy\n", + "def physical_h(q: SteaneQubit, data_idx: int) -> None:\n", + " if data_idx >= 7:\n", + " panic(\"Invalid data index in physical_h\")\n", + " h(q.data_qs[data_idx])\n", + "\n", + "\n", + "@guppy\n", + "def main() -> None:\n", + " steane_q = ft_zero()\n", + "\n", + " # add a physical H gate\n", + " physical_h(steane_q, 8)\n", + "\n", + " data = measure_array(steane_q.data_qs)\n", + " result(\"parity\", parity_check(data))\n", + "\n", + "run(main)\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "guppylang (3.13.4)", + "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.13.4" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/examples/random_numbers.ipynb b/examples/random_numbers.ipynb new file mode 100644 index 000000000..e39f94e56 --- /dev/null +++ b/examples/random_numbers.ipynb @@ -0,0 +1,265 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Random Number Generation\n", + "\n", + "**Download this notebook - {nb-download}`random_numbers.ipynb`**\n", + "\n", + "In this simple example we look at using random numbers with Guppy and Selene.\n", + "The example randomly applies simple identities to a qubit. This style of program is\n", + "often used in [Randomized\n", + "Benchmarking](https://en.wikipedia.org/wiki/Randomized_benchmarking)\n", + "as the rate of unexpected outcomes can indicate error rates in the system.\n", + "\n", + "There are two ways of using random numbers:\n", + "1. Generate them ahead of time in Python and store in the Guppy program.\n", + "2. Generate them on the fly.\n", + "\n", + "We will look at both options." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import random\n", + "\n", + "from guppylang import guppy\n", + "from hugr.qsystem.result import QsysResult\n", + "from guppylang.std.builtins import barrier, comptime, result\n", + "from guppylang.std.qsystem.random import RNG\n", + "from guppylang.std.qsystem.utils import get_current_shot\n", + "from guppylang.std.quantum import measure, qubit, x, y, z\n", + "from guppylang.defs import GuppyDefinition\n", + "from selene_sim import build, Stim" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's decide on a seed and make sure we use it for both Python and Guppy randomness, to\n", + "get deterministic results." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "INIT_SEED = 91919\n", + "random.seed(INIT_SEED)\n", + "\n", + "# number of shots we will run\n", + "N_SHOTS = 100" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's first write our simple experiment functions in Guppy." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "@guppy\n", + "def rand_pauli(q: qubit, randval: int) -> None:\n", + " \"\"\"Applies a random pauli to a qubit given a number in {0, 1, 2}\"\"\"\n", + " if randval == 0:\n", + " x(q)\n", + " elif randval == 1:\n", + " y(q)\n", + " else:\n", + " z(q)\n", + "\n", + "\n", + "@guppy\n", + "def pauli_identity(q: qubit, randval: int) -> None:\n", + " \"\"\"Apply the same random pauli twice separated by a barrier.\n", + " Acts as identity in noiseless case since Paulis are self-adjoint.\"\"\"\n", + " rand_pauli(q, randval)\n", + " barrier(q)\n", + " rand_pauli(q, randval)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Pre-compiled randomness\n", + "\n", + "First we generate all the random numbers we need ahead of time in Python.\n", + "This can be stored as an array of integers in a Guppy program using the `comptime`\n", + "expression.\n", + "At runtime we use the current shot value to index in to the array and get a fresh random\n", + "integer for the shot.\n", + "\n", + "We report out both the result of measuring the qubit, and the random number used." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "# randint upper bound is inclusive\n", + "randints = [random.randint(0, 2) for _ in range(N_SHOTS)]\n", + "\n", + "\n", + "@guppy\n", + "def main() -> None:\n", + " q = qubit()\n", + " randval = comptime(randints)[get_current_shot()]\n", + " pauli_identity(q, randval)\n", + " result(\"rng\", randval)\n", + " result(\"c\", measure(q))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We also define a helper function to run our Guppy program.\n", + "It runs `N_SHOTS` repeats of the program using Stim simulation (since our program is Clifford).\n", + "\n", + "No noise model is used so the measurement result \"c\" should always be 0.\n", + "The function reports the mean of the random numbers over the shots - we expect this to be around 1." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "def run(main_def: GuppyDefinition) -> float:\n", + " \"\"\"Run the rng example and return the mean of the random numbers generated.\n", + "\n", + " Checks measurement always returns 0 (identity operation on 0 state).\n", + " \"\"\"\n", + " runner = build(main_def.compile())\n", + " shots = QsysResult(\n", + " runner.run_shots(Stim(random_seed=INIT_SEED), n_qubits=1, n_shots=N_SHOTS)\n", + " )\n", + " rands = []\n", + " for out in shots.collated_shots():\n", + " assert out[\"c\"] == [0]\n", + " rands.extend(out[\"rng\"])\n", + " return sum(rands) / len(rands)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "1.04" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "run(main)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Runtime randomness\n", + "\n", + "The Selene emulator supports random number generation at runtime.\n", + "This has slightly different behaviour to Python:\n", + "- To ensure reproducibility you always have to provide a seed to initialise an `RNG`\n", + " object. Methods on this object can return random values.\n", + "- The `RNG` type is linear (like a qubit) and has to be explicitly discarded. This is also\n", + " to ensure determinism as it prevents random number generation calls being re-ordered\n", + " by the compiler.\n", + "\n", + "The benefit over generating ahead of time is that for large experiments you can avoid storing a huge amount of random\n", + "numbers in the program.\n", + "\n", + "We can use the `RNG` type to re-write our main function, using our `INIT_SEED` along\n", + "with a shift by current shot number to get a different number in each shot:" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "@guppy\n", + "def main() -> None:\n", + " q = qubit()\n", + " rng = RNG(comptime(INIT_SEED) + get_current_shot())\n", + " # random_int_bounded upper bound is exclusive,\n", + " # chooses a number in {0, 1, 2}\n", + " randval = rng.random_int_bounded(3)\n", + " rng.discard()\n", + " pauli_identity(q, randval)\n", + " result(\"rng\", randval)\n", + " result(\"c\", measure(q))" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "0.95" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "run(main)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "guppylang (3.13.4)", + "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.13.4" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/examples/random_walk_qpe.py b/examples/random_walk_qpe.py deleted file mode 100644 index 092889315..000000000 --- a/examples/random_walk_qpe.py +++ /dev/null @@ -1,104 +0,0 @@ -""" -Implementation of the adaptive random walk phase estimation algorithm from -https://arxiv.org/abs/2208.04526. - -The example Hamiltonian and numbers are taken from https://arxiv.org/abs/2206.12950. -""" - -import math -from collections.abc import Callable - -from guppylang.decorator import guppy -from guppylang.std.angles import angle -from guppylang.std.builtins import owned, py, result -from guppylang.std.quantum import discard, measure, qubit -from guppylang.std.quantum_functional import cx, h, rz, x - -sqrt_e = math.sqrt(math.e) -sqrt_e_div = math.sqrt((math.e - 1) / math.e) - - -@guppy -def random_walk_phase_estimation( - eigenstate: Callable[[], qubit], - controlled_oracle: Callable[ - [qubit @ owned, qubit @ owned, float], tuple[qubit, qubit] - ], - num_iters: int, - reset_rate: int, - mu: float, - sigma: float, -) -> float: - """Performs the random walk phase estimation algorithm on a single qubit for - some Hamiltonian H. - - Arguments: - eigenstate: Function preparing the eigenstate of e^itH - controlled_oracle: The oracle circuit for a controlled e^itH - num_iters: Number of iterations to run the algorithm for - reset_rate: Reset the eigenstate every x iterations - mu: Initial mean for the eigenvalue estimate - sigma: Initial standard deviation for the eigenvalue estimate - """ - tgt = eigenstate() - i = 0 - while i < num_iters: - aux = h(qubit()) - t = 1 / sigma - aux = rz(h(aux), angle((sigma - mu) * t)) - aux, tgt = controlled_oracle(aux, tgt, t) - if measure(h(aux)): - mu += sigma / py(sqrt_e) - else: - mu -= sigma / py(sqrt_e) - sigma *= py(sqrt_e_div) - - # Reset the eigenstate every few iterations to increase the fidelity of - # the algorithm - if i % reset_rate == 0: - discard(tgt) - tgt = eigenstate() - i += 1 - discard(tgt) - return mu - - -@guppy -def example_controlled_oracle( - q1: qubit @ owned, q2: qubit @ owned, t: float -) -> tuple[qubit, qubit]: - """A controlled e^itH gate for the example Hamiltonian H = -0.5 * Z""" - # This is just a controlled rz gate - a = angle(-0.5 * t) - q2 = rz(q2, a / 2) - q1, q2 = cx(q1, q2) - q2 = rz(q2, -a / 2) - return cx(q1, q2) - - -@guppy -def example_eigenstate() -> qubit: - """The eigenstate of e^itH for the example Hamiltonian H = -0.5 * Z""" - # This is just |1> - return x(qubit()) - - -@guppy -def main() -> int: - num_iters = 24 # To avoid underflows - reset_rate = 8 - mu = py(sqrt_e) - sigma = py(sqrt_e_div) - eigenvalue = random_walk_phase_estimation( - example_eigenstate, - example_controlled_oracle, - num_iters, - reset_rate, - mu, - sigma, - ) - result("eigenvalue", eigenvalue) # Expected outcome is 0.5 - return 0 - - -hugr = guppy.compile(main) diff --git a/examples/state_results.ipynb b/examples/state_results.ipynb new file mode 100644 index 000000000..d9d267282 --- /dev/null +++ b/examples/state_results.ipynb @@ -0,0 +1,295 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "8b376019", + "metadata": {}, + "source": [ + "# Debugging with `state_result` statements\n", + "\n", + "**Download this notebook - {nb-download}`state_results.ipynb`**\n", + "\n", + "In this example we will demonstrate the use of `state_result` statements for surfacing the simulator state while running a Guppy program with Selene. " + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "aa81f6b0", + "metadata": {}, + "outputs": [], + "source": [ + "from guppylang import guppy\n", + "from guppylang.std.builtins import array\n", + "from guppylang.std.debug import state_result\n", + "from guppylang.std.quantum import qubit, discard, discard_array, cx, h, measure, x\n", + "\n", + "from hugr.qsystem.result import QsysResult\n", + "\n", + "from selene_sim import build, Quest\n", + "\n", + "import numpy as np\n", + "\n", + "np.set_printoptions(precision=4, suppress=True, linewidth=120)" + ] + }, + { + "cell_type": "markdown", + "id": "aed12879", + "metadata": {}, + "source": [ + "Consider the following Guppy program where we create a 3-qubit GHZ state in two different ways that should be unitarily equivalent. " + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "dba2658d", + "metadata": {}, + "outputs": [], + "source": [ + "@guppy\n", + "def ghz_state(q: array[qubit, 3]) -> None:\n", + " h(q[0])\n", + " cx(q[0], q[1])\n", + " # Apply CX to second and third qubit.\n", + " cx(q[1], q[2])\n", + "\n", + "\n", + "@guppy\n", + "def ghz_state_alternative(q: array[qubit, 3]) -> None:\n", + " h(q[0])\n", + " cx(q[0], q[1])\n", + " # Apply CX to first and third qubit.\n", + " cx(q[0], q[2])" + ] + }, + { + "cell_type": "markdown", + "id": "2bc04c3f", + "metadata": {}, + "source": [ + "In order to check whether the resulting state vectors are equal, we can use the `state_result` function. As inputs it takes a tag (just as `result` statements do) and then either one array of qubits or a variable number of single qubit arguments. The order of arguments later determines the order of state vector outputs." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "23d0ac6e", + "metadata": {}, + "outputs": [], + "source": [ + "@guppy\n", + "def main() -> None:\n", + " qs = array(qubit() for _ in range(3))\n", + " ghz_state(qs)\n", + " state_result(\"ghz\", qs)\n", + " discard_array(qs)\n", + "\n", + " qs = array(qubit() for _ in range(3))\n", + " ghz_state_alternative(qs)\n", + " state_result(\"ghz_alternative\", qs)\n", + " discard_array(qs)" + ] + }, + { + "cell_type": "markdown", + "id": "79ecfb4d", + "metadata": {}, + "source": [ + "Let's now extract the requested state from the simulator. This can be done by calling `extract_states_dict` on the simulator plugin for each shot to get a dictionary mapping tags to states, and then retrieving the results from the state using `get_single_state`. \n", + "\n", + "Here our `ghz_state` and `ghz_state_alternative` functions deterministically prepare a single quantum state.\n", + "\n", + "Note that this is currently only possible using the `Quest` simulator plugin with the default `SimpleRuntime` and `IdealErrorModel`. " + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "1d27f005", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "State `ghz1`: [0.7071+0.j 0. +0.j 0. +0.j 0. +0.j 0. +0.j 0. +0.j 0. +0.j 0.7071+0.j]\n", + "State `ghz2`: [0.7071+0.j 0. +0.j 0. +0.j 0. +0.j 0. +0.j 0. +0.j 0. +0.j 0.7071+0.j]\n" + ] + } + ], + "source": [ + "runner = build(guppy.compile(main))\n", + "\n", + "shots = QsysResult(\n", + " runner.run_shots(\n", + " simulator=Quest(),\n", + " n_qubits=4,\n", + " n_shots=1,\n", + " )\n", + ")\n", + "\n", + "for shot in shots.results:\n", + " states = Quest.extract_states_dict(shot.entries)\n", + "\n", + " state_vector1 = states[\"ghz\"].get_single_state()\n", + " print(f\"State `ghz1`: {state_vector1}\")\n", + "\n", + " state_vector2 = states[\"ghz_alternative\"].get_single_state()\n", + " print(f\"State `ghz2`: {state_vector2}\")\n", + "\n", + " assert np.allclose(state_vector1, state_vector2)" + ] + }, + { + "cell_type": "markdown", + "id": "893c11a0", + "metadata": {}, + "source": [ + "We can see that the state vector is the same for both GHZ functions!\n", + "\n", + "It is important to bear in mind that the state result is simply the internal state of the simulator plugin, so the output at any given point in the middle of the program could be very different on hardware, where various optimisations and different scheduling likely apply.\n", + "\n", + "If you chose to request a subset of the qubits making up the overall state, you should also be aware that qubits in this subset could be entangled with other qubits in the system, so the qubits not requested in the state result are traced out to provide a probabilistic set of state vectors. For example, if we only request the state of any two out of the three qubits in the GHZ state, we expect to see either $\\ket{00}$ or $\\ket{11}$ with a probability of $\\frac{1}{2}$." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "063ea31e", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[1.+0.j 0.+0.j 0.+0.j 0.+0.j] -> 0.5, [0.+0.j 0.+0.j 0.+0.j 1.+0.j] -> 0.5\n", + "[1.+0.j 0.+0.j 0.+0.j 0.+0.j] -> 0.5, [0.+0.j 0.+0.j 0.+0.j 1.+0.j] -> 0.5\n", + "[1.+0.j 0.+0.j 0.+0.j 0.+0.j] -> 0.5, [0.+0.j 0.+0.j 0.+0.j 1.+0.j] -> 0.5\n" + ] + } + ], + "source": [ + "@guppy\n", + "def main() -> None:\n", + " qs = array(qubit() for _ in range(3))\n", + " ghz_state(qs)\n", + " state_result(\"subset01\", qs[0], qs[1])\n", + " state_result(\"subset02\", qs[0], qs[2])\n", + " state_result(\"subset12\", qs[1], qs[2])\n", + " discard_array(qs)\n", + "\n", + "\n", + "runner = build(guppy.compile(main))\n", + "\n", + "shots = QsysResult(\n", + " runner.run_shots(\n", + " simulator=Quest(),\n", + " n_qubits=4,\n", + " n_shots=1,\n", + " )\n", + ")\n", + "\n", + "for shot in shots.results:\n", + " states = Quest.extract_states_dict(shot.entries)\n", + " state01 = states[\"subset01\"].get_state_vector_distribution()\n", + " state02 = states[\"subset02\"].get_state_vector_distribution()\n", + " state12 = states[\"subset12\"].get_state_vector_distribution()\n", + "\n", + " print(\n", + " f\"{state01[0].state} -> {round(state01[0].probability, 2)}, {state01[1].state} -> {round(state01[1].probability, 2)}\"\n", + " )\n", + " print(\n", + " f\"{state02[0].state} -> {round(state02[0].probability, 2)}, {state02[1].state} -> {round(state02[1].probability, 2)}\"\n", + " )\n", + " print(\n", + " f\"{state12[0].state} -> {round(state12[0].probability, 2)}, {state12[1].state} -> {round(state12[1].probability, 2)}\"\n", + " )" + ] + }, + { + "cell_type": "markdown", + "id": "82e40c32", + "metadata": {}, + "source": [ + "Also keep in mind that state results can vary between shots depending on control flow." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "2ce50362", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{0: 58, 1: 42}\n" + ] + } + ], + "source": [ + "@guppy\n", + "def main() -> None:\n", + " q = qubit()\n", + " h(q)\n", + " q1 = qubit()\n", + " q2 = qubit()\n", + " x(q2)\n", + " if measure(q):\n", + " state_result(\"state\", q2)\n", + " else:\n", + " state_result(\"state\", q1)\n", + " discard(q1)\n", + " discard(q2)\n", + "\n", + "\n", + "runner = build(guppy.compile(main))\n", + "\n", + "shots = QsysResult(\n", + " runner.run_shots(\n", + " simulator=Quest(random_seed=2),\n", + " n_qubits=4,\n", + " n_shots=100,\n", + " )\n", + ")\n", + "\n", + "state_counts = {0: 0, 1: 0}\n", + "for shot in shots.results:\n", + " state = Quest.extract_states_dict(shot.entries)[\n", + " \"state\"\n", + " ].get_state_vector_distribution()\n", + " s = state[0].state\n", + " if np.allclose(s, [0, 1]):\n", + " state_counts[1] += 1\n", + " elif np.allclose(s, [1, 0]):\n", + " state_counts[0] += 1\n", + "\n", + "print(state_counts)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "guppylang (3.13.4)", + "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.13.4" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/examples/swap_test.ipynb b/examples/swap_test.ipynb new file mode 100644 index 000000000..d4752d668 --- /dev/null +++ b/examples/swap_test.ipynb @@ -0,0 +1,414 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# The SWAP test and loading `pytket` circuits\n", + "\n", + "**Download this notebook - {nb-download}`swap_test.ipynb`**\n", + "\n", + "In this example we will use the SWAP test to demonstrate the construction of a simple Guppy program and show how pytket circuits with multiple registers can be loaded as Guppy functions." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "from guppylang import guppy\n", + "from guppylang.std.quantum import qubit, h, cx, toffoli, measure, discard_array\n", + "from hugr.qsystem.result import QsysResult\n", + "from guppylang.std.builtins import result, array\n", + "\n", + "from selene_sim import build, Quest\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": [ + "## Background" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The swap test is a simple quantum circuit that allows us to approximate the squared overlap between two quantum states. \n", + "\n", + "\n", + "![](./img/swap_test_circuit.png){w=240px align=center}\n", + "\n", + "\n", + "We apply controlled swap operation to swap the qubits in the $|\\psi\\rangle$ and $|\\phi\\rangle$ registers. This swaps the two target qubits if the control qubit is in the $|1\\rangle$ state and acts as the identity otherwise. \n", + "\n", + "\n", + "Notice how only the first qubit is measured, giving $|0\\rangle$ or $|1\\rangle$. The value of our squared inner product is related to the probability of measuring $|0\\rangle$ and $|1\\rangle$ as follows.\n", + "\n", + "$$\n", + "P_0 = \\frac{1}{2} + \\frac{1}{2}|\\langle\\psi|\\phi\\rangle|^2\\,, \\quad \n", + "P_1 = \\frac{1}{2} - \\frac{1}{2}|\\langle\\psi|\\phi\\rangle|^2\n", + "$$\n", + "\n", + "To approximate $|\\langle\\psi|\\phi\\rangle|^2$ within an error $\\epsilon$ we will need to run the program for $\\mathcal{O}(\\frac{1}{\\epsilon^2})$ shots. For more background on the swap test, see this [Wikipedia article](https://en.wikipedia.org/wiki/Swap_test).\n", + "\n", + "\n", + "In this example we will consider the following three qubit states\n", + "\n", + "$$\n", + "|W\\rangle = \\frac{1}{\\sqrt{3}} \\big(|001\\rangle + |010\\rangle + |100\\rangle \\big)\n", + "$$\n", + "\n", + "$$\n", + "|S\\rangle = \\frac{1}{\\sqrt{7}}\\big(|001\\rangle+|010\\rangle+|011\\rangle+|100\\rangle+|101\\rangle+|110\\rangle+|111\\rangle \\big)\\,.\n", + "$$\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Controlled SWAP" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Firstly lets define a Guppy function to implement our CSWAP gate in terms of Toffoli and CX gates." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "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": [ + "As we are doing the swap test for two states with three qubits each, we will need to use three CSWAP gates to swap all of the qubits in both state preparation registers." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "@guppy\n", + "def cswap_layer(ancilla: qubit, arr0: array[qubit, 3], arr1: array[qubit, 3]) -> None:\n", + " for i in range(3):\n", + " cswap(ancilla, arr0[i], arr1[i])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## State Preparation using pytket\n", + "\n", + "Now for the state preparation phase. Here we will leverage the [StatePreparationBox](https://docs.quantinuum.com/tket/api-docs/circuit.html#pytket.circuit.StatePreparationBox) construct from pytket.\n", + "\n", + "We will create a pytket `Circuit` with three registers. One qubit is used for the ancilla, and three qubits each for the two state preparation registers. " + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[StatePreparationBox s[0], s[1], s[2]; StatePreparationBox w[0], w[1], w[2]; ]" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Define two StatePreparationBox(es) by passing the amplitudes for |S> and |W> as numpy arrays\n", + "w_state = 1 / np.sqrt(3) * np.array([0, 1, 1, 0, 1, 0, 0, 0])\n", + "s_state = 1 / np.sqrt(7) * np.array([0] + [1] * 7)\n", + "\n", + "w_state_box = StatePreparationBox(w_state)\n", + "s_state_box = StatePreparationBox(s_state)\n", + "\n", + "\n", + "# Build a pytket Circuit with 7 qubits\n", + "pytket_circ = Circuit()\n", + "ancilla = pytket_circ.add_q_register(\"a\", 1)\n", + "w_qubits = pytket_circ.add_q_register(\"w\", 3)\n", + "s_qubits = pytket_circ.add_q_register(\"s\", 3)\n", + "\n", + "\n", + "# Append the state preparation subroutines to the empty circuit\n", + "pytket_circ.add_gate(w_state_box, list(w_qubits))\n", + "pytket_circ.add_gate(s_state_box, list(s_qubits))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Before loading the state preparation circuit into Guppy, we can use the [DecomposeBoxes](https://docs.quantinuum.com/tket/api-docs/passes.html#pytket.passes.DecomposeBoxes) pass to decompose the pytket circuit into CX and Ry gates." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "DecomposeBoxes().apply(pytket_circ)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can now load in the pytket circuit and create a corresponding Guppy function which takes the ancilla, $|S\\rangle$ and $|W\\rangle$ registers as inputs." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "pytket_state_prep = guppy.load_pytket(\"pytket_state_prep\", pytket_circ)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Here, the separate quantum registers in the circuit are treated as distinct Guppy arrays. The loaded circuit can now be invoked as a `pytket_state_prep` Guppy function which takes three arrays (corresponding to `\"a\"` `\"s\"` and `\"w\"`) as input. Note that as with pytket the qubit ordering here is lexicographic. So the arrays are arranged in alphabetical order with `\"a\"` first followed by `\"s\"` and finally `\"w\"`.\n", + "\n", + "The `load_pytket` function has a `use_arrays` flag which is set to `True` by default. Setting this argument to `False` means that Guppy will not create arrays for the distinct registers but instead will treat each named qubit as a separate function argument to `pytket_state_prep`. In this case we would have seven distinct qubit arguments to `pytket_state_prep` instead of three arrays." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Execution on Selene" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now we can compose the different parts of our program together into a `main` function. Here we only need to measure the first qubit so we discard the two state preparation registers." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "@guppy\n", + "def main() -> None:\n", + " w_qubits = array(qubit() for _ in range(3))\n", + " s_qubits = array(qubit() for _ in range(3))\n", + " ancilla_reg = array(qubit())\n", + " # The pytket function only acts on arrays\n", + " pytket_state_prep(ancilla_reg, s_qubits, w_qubits)\n", + "\n", + " (ancilla,) = ancilla_reg\n", + "\n", + " h(ancilla)\n", + " cswap_layer(ancilla, w_qubits, s_qubits)\n", + " h(ancilla)\n", + "\n", + " result(\"c\", measure(ancilla))\n", + "\n", + " # We are only interested in measuring the first qubit\n", + " # Discard all the of |W> and |S> qubits to avoid linearity violation.\n", + " discard_array(w_qubits)\n", + " discard_array(s_qubits)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can now compile our swap test program for execution on Selene." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "compiled_hugr = guppy.compile(main)" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [], + "source": [ + "n_shots = 2000\n", + "\n", + "runner = build(compiled_hugr)\n", + "my_shots = QsysResult(\n", + " runner.run_shots(Quest(random_seed=91919), n_qubits=7, n_shots=n_shots)\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now let's look at the frequencies of the $|0\\rangle$ and $|1\\rangle$ measurement outcomes." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Counter({'0': 1414, '1': 586})\n" + ] + } + ], + "source": [ + "print(my_shots.register_counts()[\"c\"])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Conclusion" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Finally, lets get an estimate for the value of $|\\langle W| S \\rangle|^2$ by rearranging our expression for $P_0$\n", + "\n", + "$$\n", + "|\\langle W | S\\rangle|^2 = 2P_0 -1\n", + "$$" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [], + "source": [ + "def eval_squared_overlap(shots: QsysResult) -> float:\n", + " counter = shots.register_counts()[\"c\"]\n", + " p0 = counter[\"0\"] / (counter[\"0\"] + counter[\"1\"])\n", + " return 2 * p0 - 1" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "0.4139999999999999" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "eval_squared_overlap(my_shots)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now lets compare with the classical calculation. We can just compute the inner product between the $|S\\rangle$ and $|W\\rangle$ statevectors with numpy." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0.42857142857142866\n" + ] + } + ], + "source": [ + "print(abs(np.vdot(w_state, s_state)) ** 2)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "guppylang (3.13.4)", + "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.13.4" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/examples/t_factory.ipynb b/examples/t_factory.ipynb new file mode 100644 index 000000000..166f8be2c --- /dev/null +++ b/examples/t_factory.ipynb @@ -0,0 +1,361 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "ff7370fb", + "metadata": {}, + "source": [ + "# T state distillation and the `Option` type\n", + "\n", + "**Download this notebook - {nb-download}`t_factory.ipynb`**\n", + "\n", + "In this example we will demonstrate how to create a T state using magic state distillation, including the use of the `Option` type to indicate success or failure in a repeat-until-success algorithm." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "73e8e28c", + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "\n", + "from guppylang.decorator import guppy\n", + "from guppylang.std.angles import angle, pi\n", + "from guppylang.std.builtins import array, owned, comptime\n", + "from guppylang.std.option import Option, nothing, some\n", + "from guppylang.std.debug import state_result\n", + "from guppylang.std.quantum import (\n", + " cz,\n", + " discard,\n", + " h,\n", + " measure,\n", + " qubit,\n", + " ry,\n", + " rz,\n", + ")\n", + "\n", + "from selene_sim import build, Quest\n", + "\n", + "from hugr.qsystem.result import QsysResult\n", + "\n", + "np.set_printoptions(precision=4, suppress=True, linewidth=120)" + ] + }, + { + "cell_type": "markdown", + "id": "5472c92a", + "metadata": {}, + "source": [ + "## Preparation\n", + "\n", + "We begin by implementing a function to prepare an approximate T state, taken from https://arxiv.org/abs/2310.12106." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "d9646c0f", + "metadata": {}, + "outputs": [], + "source": [ + "phi = np.arccos(1 / 3)\n", + "\n", + "\n", + "@guppy\n", + "def prepare_approx() -> qubit:\n", + " q = qubit()\n", + " ry(q, angle(comptime(phi)))\n", + " rz(q, pi / 4)\n", + " return q" + ] + }, + { + "cell_type": "markdown", + "id": "b3acda45", + "metadata": {}, + "source": [ + "## Distillation\n", + "\n", + "This is the inverse of the $[[5,3,1]]$ encoder in figure 3 of https://arxiv.org/abs/2208.01863. \n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "704030e7", + "metadata": {}, + "outputs": [], + "source": [ + "@guppy\n", + "def distill(\n", + " target: qubit @ owned,\n", + " qs: array[qubit, 4] @ owned,\n", + ") -> tuple[qubit, bool]:\n", + " \"\"\"First argument is the target qubit which will be returned from the circuit.\n", + " Other arguments are ancillae, which should also be in an approximate T state.\n", + " Returns target qubit and a bool, which is true if the distillation succeeded.\n", + " \"\"\"\n", + " cz(qs[0], qs[1])\n", + " cz(qs[2], qs[3])\n", + " cz(target, qs[0])\n", + " cz(qs[1], qs[2])\n", + " cz(target, qs[3])\n", + " # Measuring gives false for success, true for failure.\n", + " # We check for all falses to say whether distillation succeeded.\n", + " for i in range(4):\n", + " h(qs[i])\n", + " bits = array(not measure(q) for q in qs)\n", + " # Guppy doesn't yet support the `any` or `all` operators yet.\n", + " success = True\n", + " for b in bits:\n", + " success &= b\n", + " return target, success" + ] + }, + { + "cell_type": "markdown", + "id": "2ff9a83c", + "metadata": {}, + "source": [ + "## Repeat-Until-Success and `Option`\n", + "\n", + "We can now put both of the above together by preparing 5 qubits using `prepare_approx` and then attempting to distill a T state from them using `distill`, for some maximum number of attempts." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "bcc5acd4", + "metadata": {}, + "outputs": [], + "source": [ + "@guppy\n", + "def t_state(attempts: int) -> Option[qubit]:\n", + " \"\"\"Create a T state using magic state distillation with a fixed number of attempts.\n", + "\n", + " On success returns a qubit in a magic T state.\n", + "\n", + " On failure (i.e. number of attempts are exceeded) returns nothing.\n", + " \"\"\"\n", + " if attempts > 0:\n", + " tgt = prepare_approx()\n", + " qs = array(prepare_approx() for _ in range(4))\n", + "\n", + " q, success = distill(tgt, qs)\n", + " if success:\n", + " return some(q)\n", + " else:\n", + " # Discard the qubit and start over.\n", + " # Note, this could just as easily be a while loop!\n", + " discard(q)\n", + " return t_state(attempts - 1)\n", + "\n", + " # We ran out of attempts.\n", + " return nothing()" + ] + }, + { + "cell_type": "markdown", + "id": "d7945b35", + "metadata": {}, + "source": [ + "Note the use of the `Option[qubit]` type for the result. Option types enable us to represent either a value, using `some(value)` or the absence of it, using `nothing()`. In this case it is a good way to represent failure or success without having to rely on errors. Before using the value if it exists, it needs to be unwrapped. " + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "9ff753c6", + "metadata": {}, + "outputs": [], + "source": [ + "attempts = 3\n", + "\n", + "\n", + "@guppy\n", + "def main() -> None:\n", + " option_t = t_state(comptime(attempts))\n", + " # Check whether the option contains a value.\n", + " if option_t.is_some():\n", + " # Unwrap the qubit.\n", + " t = option_t.unwrap()\n", + " state_result(\"t_state\", t)\n", + " discard(t)\n", + " else:\n", + " # Since qubits are linear, Option[qubit] is also linear, so we need to consume\n", + " # it either way.\n", + " option_t.unwrap_nothing()\n", + "\n", + "\n", + "compiled = guppy.compile(main)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "40a25f2d", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[0.2967-0.j 0.8094-0.5068j]\n" + ] + } + ], + "source": [ + "runner = build(compiled)\n", + "\n", + "shots = QsysResult(\n", + " runner.run_shots(\n", + " simulator=Quest(random_seed=1),\n", + " n_qubits=8,\n", + " n_shots=1,\n", + " )\n", + ")\n", + "\n", + "# See the `Debugging with `state_result` statements` example notebook for more details\n", + "# about state results.\n", + "for shot in shots.results:\n", + " states = Quest.extract_states_dict(shot.entries)\n", + " if \"t_state\" in states:\n", + " dist = states[\"t_state\"].get_state_vector_distribution()\n", + " print(f\"{dist[0].state}\")" + ] + }, + { + "cell_type": "markdown", + "id": "0a8ee66c", + "metadata": {}, + "source": [ + "A similar concept that is also available in the Guppy standard library is the `Either` type, which generalises the concept of having an optional value to having a value that could be of one of two types, either `left(value)` or `right(value)`." + ] + }, + { + "cell_type": "markdown", + "id": "ce9f778c", + "metadata": {}, + "source": [ + "## Selectively measuring arrays with `Option`\n", + "\n", + "Consider this attempt at measuring a subscript of an array of qubits:" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "ee6a43c5", + "metadata": { + "tags": [ + "raises-exception" + ] + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Error: Subscript consumed (at :3:12)\n", + " | \n", + "1 | @guppy\n", + "2 | def attempt_measure(qs: array[qubit, 10] @ owned) -> None:\n", + "3 | measure(qs[5])\n", + " | ^^^^^ Cannot consume a subscript of `qs` with non-copyable type\n", + " | `array[qubit, 10]`\n", + "\n", + "Note: Subscripts on non-copyable types are only allowed to be borrowed, not\n", + "consumed\n", + "\n", + "Guppy compilation failed due to 1 previous error\n" + ] + } + ], + "source": [ + "@guppy\n", + "def attempt_measure(qs: array[qubit, 10] @ owned) -> None:\n", + " measure(qs[5])\n", + "\n", + "\n", + "compiled = guppy.compile(attempt_measure)" + ] + }, + { + "cell_type": "markdown", + "id": "1d8861e5", + "metadata": {}, + "source": [ + "As expected, this leads to an error because you can't consume subscripts of a linear array. \n", + "\n", + "However we can use arrays of type `array[Option[qubit], N]` to measure some qubits in an array without consuming the whole array at once by swapping qubits you want to measure with`nothing()`. " + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "351ec80b", + "metadata": {}, + "outputs": [], + "source": [ + "n = guppy.nat_var(\"n\")\n", + "\n", + "\n", + "@guppy\n", + "def measure_mask(\n", + " qs: array[Option[qubit], n], mask: array[bool, n] @ owned\n", + ") -> array[int, n]:\n", + " \"\"\"Measure all gubits in `qs` with a corresponding `True` index in `mask`.\"\"\"\n", + " res = array(-1 for _ in range(n))\n", + " idx = 0\n", + " for m in mask:\n", + " if m and qs[idx].is_some():\n", + " # `take` swaps an optional value with nothing().\n", + " q = qs[idx].take()\n", + " res[idx] = int(measure(q.unwrap()))\n", + " idx += 1\n", + " return res\n", + "\n", + "\n", + "@guppy\n", + "def main() -> None:\n", + " qs = array(some(qubit()) for _ in range(3))\n", + " mask = array(True, False, True)\n", + " measure_mask(qs, mask)\n", + " # We need to consume the array of options at some point, as it can't be leaked.\n", + " for q_opt in qs:\n", + " if q_opt.is_some():\n", + " q = q_opt.unwrap()\n", + " discard(q)\n", + " else:\n", + " q_opt.unwrap_nothing()\n", + "\n", + "\n", + "guppy.compile(main);" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "guppylang (3.13.4)", + "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.13.4" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/examples/t_factory.py b/examples/t_factory.py deleted file mode 100644 index 5106eb0c5..000000000 --- a/examples/t_factory.py +++ /dev/null @@ -1,81 +0,0 @@ -import numpy as np - -from guppylang.decorator import guppy -from guppylang.std.angles import angle, pi -from guppylang.std.builtins import array, owned, py -from guppylang.std.option import Option, nothing, some -from guppylang.std.quantum import ( - cz, - discard, - h, - measure, - qubit, - ry, - rz, -) - -phi = np.arccos(1 / 3) - - -# Preparation of approximate T state, from https://arxiv.org/abs/2310.12106 -@guppy -def prepare_approx() -> qubit: - q = qubit() - ry(q, angle(py(phi))) - rz(q, pi / 4) - return q - - -# The inverse of the [[5,3,1]] encoder in figure 3 of https://arxiv.org/abs/2208.01863 -@guppy -def distill( - target: qubit @ owned, - qs: array[qubit, 4] @ owned, -) -> tuple[qubit, bool]: - """First argument is the target qubit which will be returned from the circuit. - Other arguments are ancillae, which should also be in an approximate T state. - Returns target qubit and a bool, which is true if the distillation succeeded. - """ - cz(qs[0], qs[1]) - cz(qs[2], qs[3]) - cz(target, qs[0]) - cz(qs[1], qs[2]) - cz(target, qs[3]) - # Measuring gives false for success, true for failure. - # We check for all falses to say whether distillation succeeded. - for i in range(4): - h(qs[i]) - bits = array(not measure(q) for q in qs) - # guppy doesn't yet support the `any` or `all` operators... - success = True - for b in bits: - success &= b - return target, success - - -@guppy -def t_state(timeout: int) -> Option[qubit]: - """Create a T state using magic state distillation with `timeout` attempts. - - On success returns a qubit in a magic T state. - - On failure (i.e. number of attempts are exceeded) returns nothing. - """ - if timeout > 0: - tgt = prepare_approx() - qs = array(prepare_approx() for _ in range(4)) - - q, success = distill(tgt, qs) - if success: - return some(q) - else: - # Discard the qubit and start over - # Note, this could just as easily be a while loop! - discard(q) - return t_state(timeout - 1) - - # We ran out of attempts - return nothing() - - -hugr = guppy.compile(t_state) diff --git a/pyproject.toml b/pyproject.toml index 90c0ccfec..f861b4de6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -70,6 +70,7 @@ test = [ "miette-py", "guppylang[emulator]", "selene-hugr-qis-compiler>=0.2.0rc1", + "matplotlib>=3.9.2", ] pytket_integration = [{ include-group = "test" }, "pytket >=1.34.0"] diff --git a/tests/integration/test_examples.py b/tests/integration/test_examples.py index a5cdbb668..f38b02908 100644 --- a/tests/integration/test_examples.py +++ b/tests/integration/test_examples.py @@ -1,5 +1,21 @@ """Tests validating the files in the `examples` directory.""" +import pytest +from pathlib import Path + +notebook_files = list( + (Path(__file__).parent.parent.parent / "examples").glob("*.ipynb") +) + + +@pytest.mark.parametrize("notebook", notebook_files) +def test_example_notebooks(nb_regression, notebook: Path): + nb_regression.diff_ignore += ( + "/metadata/language_info/version", + "/cells/*/outputs/*/data/image/png", + ) + nb_regression.check(notebook) + def test_demo_notebook(nb_regression): nb_regression.diff_ignore += ("/metadata/language_info/version",) @@ -14,15 +30,3 @@ def test_comptime_notebook(nb_regression): def test_misc_notebook_tests(nb_regression): nb_regression.diff_ignore += ("/metadata/language_info/version",) nb_regression.check("tests/integration/notebooks/misc_notebook_tests.ipynb") - - -def test_random_walk_qpe(validate): - from examples.random_walk_qpe import hugr - - validate(hugr) - - -def test_t_factory(validate): - from examples.t_factory import hugr - - validate(hugr) diff --git a/uv.lock b/uv.lock index 96020aaec..a13adade5 100644 --- a/uv.lock +++ b/uv.lock @@ -314,6 +314,161 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e6/75/49e5bfe642f71f272236b5b2d2691cf915a7283cc0ceda56357b61daa538/comm-0.2.2-py3-none-any.whl", hash = "sha256:e6fb86cb70ff661ee8c9c14e7d36d6de3b4066f1441be4063df9c5009f0a64d3", size = 7180, upload-time = "2024-03-12T16:53:39.226Z" }, ] +[[package]] +name = "contourpy" +version = "1.3.2" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.11'", +] +dependencies = [ + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/66/54/eb9bfc647b19f2009dd5c7f5ec51c4e6ca831725f1aea7a993034f483147/contourpy-1.3.2.tar.gz", hash = "sha256:b6945942715a034c671b7fc54f9588126b0b8bf23db2696e3ca8328f3ff0ab54", size = 13466130, upload-time = "2025-04-15T17:47:53.79Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/12/a3/da4153ec8fe25d263aa48c1a4cbde7f49b59af86f0b6f7862788c60da737/contourpy-1.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ba38e3f9f330af820c4b27ceb4b9c7feee5fe0493ea53a8720f4792667465934", size = 268551, upload-time = "2025-04-15T17:34:46.581Z" }, + { url = "https://files.pythonhosted.org/packages/2f/6c/330de89ae1087eb622bfca0177d32a7ece50c3ef07b28002de4757d9d875/contourpy-1.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:dc41ba0714aa2968d1f8674ec97504a8f7e334f48eeacebcaa6256213acb0989", size = 253399, upload-time = "2025-04-15T17:34:51.427Z" }, + { url = "https://files.pythonhosted.org/packages/c1/bd/20c6726b1b7f81a8bee5271bed5c165f0a8e1f572578a9d27e2ccb763cb2/contourpy-1.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9be002b31c558d1ddf1b9b415b162c603405414bacd6932d031c5b5a8b757f0d", size = 312061, upload-time = "2025-04-15T17:34:55.961Z" }, + { url = "https://files.pythonhosted.org/packages/22/fc/a9665c88f8a2473f823cf1ec601de9e5375050f1958cbb356cdf06ef1ab6/contourpy-1.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8d2e74acbcba3bfdb6d9d8384cdc4f9260cae86ed9beee8bd5f54fee49a430b9", size = 351956, upload-time = "2025-04-15T17:35:00.992Z" }, + { url = "https://files.pythonhosted.org/packages/25/eb/9f0a0238f305ad8fb7ef42481020d6e20cf15e46be99a1fcf939546a177e/contourpy-1.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e259bced5549ac64410162adc973c5e2fb77f04df4a439d00b478e57a0e65512", size = 320872, upload-time = "2025-04-15T17:35:06.177Z" }, + { url = "https://files.pythonhosted.org/packages/32/5c/1ee32d1c7956923202f00cf8d2a14a62ed7517bdc0ee1e55301227fc273c/contourpy-1.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad687a04bc802cbe8b9c399c07162a3c35e227e2daccf1668eb1f278cb698631", size = 325027, upload-time = "2025-04-15T17:35:11.244Z" }, + { url = "https://files.pythonhosted.org/packages/83/bf/9baed89785ba743ef329c2b07fd0611d12bfecbedbdd3eeecf929d8d3b52/contourpy-1.3.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cdd22595308f53ef2f891040ab2b93d79192513ffccbd7fe19be7aa773a5e09f", size = 1306641, upload-time = "2025-04-15T17:35:26.701Z" }, + { url = "https://files.pythonhosted.org/packages/d4/cc/74e5e83d1e35de2d28bd97033426b450bc4fd96e092a1f7a63dc7369b55d/contourpy-1.3.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b4f54d6a2defe9f257327b0f243612dd051cc43825587520b1bf74a31e2f6ef2", size = 1374075, upload-time = "2025-04-15T17:35:43.204Z" }, + { url = "https://files.pythonhosted.org/packages/0c/42/17f3b798fd5e033b46a16f8d9fcb39f1aba051307f5ebf441bad1ecf78f8/contourpy-1.3.2-cp310-cp310-win32.whl", hash = "sha256:f939a054192ddc596e031e50bb13b657ce318cf13d264f095ce9db7dc6ae81c0", size = 177534, upload-time = "2025-04-15T17:35:46.554Z" }, + { url = "https://files.pythonhosted.org/packages/54/ec/5162b8582f2c994721018d0c9ece9dc6ff769d298a8ac6b6a652c307e7df/contourpy-1.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:c440093bbc8fc21c637c03bafcbef95ccd963bc6e0514ad887932c18ca2a759a", size = 221188, upload-time = "2025-04-15T17:35:50.064Z" }, + { url = "https://files.pythonhosted.org/packages/b3/b9/ede788a0b56fc5b071639d06c33cb893f68b1178938f3425debebe2dab78/contourpy-1.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6a37a2fb93d4df3fc4c0e363ea4d16f83195fc09c891bc8ce072b9d084853445", size = 269636, upload-time = "2025-04-15T17:35:54.473Z" }, + { url = "https://files.pythonhosted.org/packages/e6/75/3469f011d64b8bbfa04f709bfc23e1dd71be54d05b1b083be9f5b22750d1/contourpy-1.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b7cd50c38f500bbcc9b6a46643a40e0913673f869315d8e70de0438817cb7773", size = 254636, upload-time = "2025-04-15T17:35:58.283Z" }, + { url = "https://files.pythonhosted.org/packages/8d/2f/95adb8dae08ce0ebca4fd8e7ad653159565d9739128b2d5977806656fcd2/contourpy-1.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d6658ccc7251a4433eebd89ed2672c2ed96fba367fd25ca9512aa92a4b46c4f1", size = 313053, upload-time = "2025-04-15T17:36:03.235Z" }, + { url = "https://files.pythonhosted.org/packages/c3/a6/8ccf97a50f31adfa36917707fe39c9a0cbc24b3bbb58185577f119736cc9/contourpy-1.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:70771a461aaeb335df14deb6c97439973d253ae70660ca085eec25241137ef43", size = 352985, upload-time = "2025-04-15T17:36:08.275Z" }, + { url = "https://files.pythonhosted.org/packages/1d/b6/7925ab9b77386143f39d9c3243fdd101621b4532eb126743201160ffa7e6/contourpy-1.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65a887a6e8c4cd0897507d814b14c54a8c2e2aa4ac9f7686292f9769fcf9a6ab", size = 323750, upload-time = "2025-04-15T17:36:13.29Z" }, + { url = "https://files.pythonhosted.org/packages/c2/f3/20c5d1ef4f4748e52d60771b8560cf00b69d5c6368b5c2e9311bcfa2a08b/contourpy-1.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3859783aefa2b8355697f16642695a5b9792e7a46ab86da1118a4a23a51a33d7", size = 326246, upload-time = "2025-04-15T17:36:18.329Z" }, + { url = "https://files.pythonhosted.org/packages/8c/e5/9dae809e7e0b2d9d70c52b3d24cba134dd3dad979eb3e5e71f5df22ed1f5/contourpy-1.3.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:eab0f6db315fa4d70f1d8ab514e527f0366ec021ff853d7ed6a2d33605cf4b83", size = 1308728, upload-time = "2025-04-15T17:36:33.878Z" }, + { url = "https://files.pythonhosted.org/packages/e2/4a/0058ba34aeea35c0b442ae61a4f4d4ca84d6df8f91309bc2d43bb8dd248f/contourpy-1.3.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d91a3ccc7fea94ca0acab82ceb77f396d50a1f67412efe4c526f5d20264e6ecd", size = 1375762, upload-time = "2025-04-15T17:36:51.295Z" }, + { url = "https://files.pythonhosted.org/packages/09/33/7174bdfc8b7767ef2c08ed81244762d93d5c579336fc0b51ca57b33d1b80/contourpy-1.3.2-cp311-cp311-win32.whl", hash = "sha256:1c48188778d4d2f3d48e4643fb15d8608b1d01e4b4d6b0548d9b336c28fc9b6f", size = 178196, upload-time = "2025-04-15T17:36:55.002Z" }, + { url = "https://files.pythonhosted.org/packages/5e/fe/4029038b4e1c4485cef18e480b0e2cd2d755448bb071eb9977caac80b77b/contourpy-1.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:5ebac872ba09cb8f2131c46b8739a7ff71de28a24c869bcad554477eb089a878", size = 222017, upload-time = "2025-04-15T17:36:58.576Z" }, + { url = "https://files.pythonhosted.org/packages/34/f7/44785876384eff370c251d58fd65f6ad7f39adce4a093c934d4a67a7c6b6/contourpy-1.3.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4caf2bcd2969402bf77edc4cb6034c7dd7c0803213b3523f111eb7460a51b8d2", size = 271580, upload-time = "2025-04-15T17:37:03.105Z" }, + { url = "https://files.pythonhosted.org/packages/93/3b/0004767622a9826ea3d95f0e9d98cd8729015768075d61f9fea8eeca42a8/contourpy-1.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:82199cb78276249796419fe36b7386bd8d2cc3f28b3bc19fe2454fe2e26c4c15", size = 255530, upload-time = "2025-04-15T17:37:07.026Z" }, + { url = "https://files.pythonhosted.org/packages/e7/bb/7bd49e1f4fa805772d9fd130e0d375554ebc771ed7172f48dfcd4ca61549/contourpy-1.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:106fab697af11456fcba3e352ad50effe493a90f893fca6c2ca5c033820cea92", size = 307688, upload-time = "2025-04-15T17:37:11.481Z" }, + { url = "https://files.pythonhosted.org/packages/fc/97/e1d5dbbfa170725ef78357a9a0edc996b09ae4af170927ba8ce977e60a5f/contourpy-1.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d14f12932a8d620e307f715857107b1d1845cc44fdb5da2bc8e850f5ceba9f87", size = 347331, upload-time = "2025-04-15T17:37:18.212Z" }, + { url = "https://files.pythonhosted.org/packages/6f/66/e69e6e904f5ecf6901be3dd16e7e54d41b6ec6ae3405a535286d4418ffb4/contourpy-1.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:532fd26e715560721bb0d5fc7610fce279b3699b018600ab999d1be895b09415", size = 318963, upload-time = "2025-04-15T17:37:22.76Z" }, + { url = "https://files.pythonhosted.org/packages/a8/32/b8a1c8965e4f72482ff2d1ac2cd670ce0b542f203c8e1d34e7c3e6925da7/contourpy-1.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f26b383144cf2d2c29f01a1e8170f50dacf0eac02d64139dcd709a8ac4eb3cfe", size = 323681, upload-time = "2025-04-15T17:37:33.001Z" }, + { url = "https://files.pythonhosted.org/packages/30/c6/12a7e6811d08757c7162a541ca4c5c6a34c0f4e98ef2b338791093518e40/contourpy-1.3.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:c49f73e61f1f774650a55d221803b101d966ca0c5a2d6d5e4320ec3997489441", size = 1308674, upload-time = "2025-04-15T17:37:48.64Z" }, + { url = "https://files.pythonhosted.org/packages/2a/8a/bebe5a3f68b484d3a2b8ffaf84704b3e343ef1addea528132ef148e22b3b/contourpy-1.3.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3d80b2c0300583228ac98d0a927a1ba6a2ba6b8a742463c564f1d419ee5b211e", size = 1380480, upload-time = "2025-04-15T17:38:06.7Z" }, + { url = "https://files.pythonhosted.org/packages/34/db/fcd325f19b5978fb509a7d55e06d99f5f856294c1991097534360b307cf1/contourpy-1.3.2-cp312-cp312-win32.whl", hash = "sha256:90df94c89a91b7362e1142cbee7568f86514412ab8a2c0d0fca72d7e91b62912", size = 178489, upload-time = "2025-04-15T17:38:10.338Z" }, + { url = "https://files.pythonhosted.org/packages/01/c8/fadd0b92ffa7b5eb5949bf340a63a4a496a6930a6c37a7ba0f12acb076d6/contourpy-1.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:8c942a01d9163e2e5cfb05cb66110121b8d07ad438a17f9e766317bcb62abf73", size = 223042, upload-time = "2025-04-15T17:38:14.239Z" }, + { url = "https://files.pythonhosted.org/packages/2e/61/5673f7e364b31e4e7ef6f61a4b5121c5f170f941895912f773d95270f3a2/contourpy-1.3.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:de39db2604ae755316cb5967728f4bea92685884b1e767b7c24e983ef5f771cb", size = 271630, upload-time = "2025-04-15T17:38:19.142Z" }, + { url = "https://files.pythonhosted.org/packages/ff/66/a40badddd1223822c95798c55292844b7e871e50f6bfd9f158cb25e0bd39/contourpy-1.3.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3f9e896f447c5c8618f1edb2bafa9a4030f22a575ec418ad70611450720b5b08", size = 255670, upload-time = "2025-04-15T17:38:23.688Z" }, + { url = "https://files.pythonhosted.org/packages/1e/c7/cf9fdee8200805c9bc3b148f49cb9482a4e3ea2719e772602a425c9b09f8/contourpy-1.3.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:71e2bd4a1c4188f5c2b8d274da78faab884b59df20df63c34f74aa1813c4427c", size = 306694, upload-time = "2025-04-15T17:38:28.238Z" }, + { url = "https://files.pythonhosted.org/packages/dd/e7/ccb9bec80e1ba121efbffad7f38021021cda5be87532ec16fd96533bb2e0/contourpy-1.3.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de425af81b6cea33101ae95ece1f696af39446db9682a0b56daaa48cfc29f38f", size = 345986, upload-time = "2025-04-15T17:38:33.502Z" }, + { url = "https://files.pythonhosted.org/packages/dc/49/ca13bb2da90391fa4219fdb23b078d6065ada886658ac7818e5441448b78/contourpy-1.3.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:977e98a0e0480d3fe292246417239d2d45435904afd6d7332d8455981c408b85", size = 318060, upload-time = "2025-04-15T17:38:38.672Z" }, + { url = "https://files.pythonhosted.org/packages/c8/65/5245ce8c548a8422236c13ffcdcdada6a2a812c361e9e0c70548bb40b661/contourpy-1.3.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:434f0adf84911c924519d2b08fc10491dd282b20bdd3fa8f60fd816ea0b48841", size = 322747, upload-time = "2025-04-15T17:38:43.712Z" }, + { url = "https://files.pythonhosted.org/packages/72/30/669b8eb48e0a01c660ead3752a25b44fdb2e5ebc13a55782f639170772f9/contourpy-1.3.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c66c4906cdbc50e9cba65978823e6e00b45682eb09adbb78c9775b74eb222422", size = 1308895, upload-time = "2025-04-15T17:39:00.224Z" }, + { url = "https://files.pythonhosted.org/packages/05/5a/b569f4250decee6e8d54498be7bdf29021a4c256e77fe8138c8319ef8eb3/contourpy-1.3.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8b7fc0cd78ba2f4695fd0a6ad81a19e7e3ab825c31b577f384aa9d7817dc3bef", size = 1379098, upload-time = "2025-04-15T17:43:29.649Z" }, + { url = "https://files.pythonhosted.org/packages/19/ba/b227c3886d120e60e41b28740ac3617b2f2b971b9f601c835661194579f1/contourpy-1.3.2-cp313-cp313-win32.whl", hash = "sha256:15ce6ab60957ca74cff444fe66d9045c1fd3e92c8936894ebd1f3eef2fff075f", size = 178535, upload-time = "2025-04-15T17:44:44.532Z" }, + { url = "https://files.pythonhosted.org/packages/12/6e/2fed56cd47ca739b43e892707ae9a13790a486a3173be063681ca67d2262/contourpy-1.3.2-cp313-cp313-win_amd64.whl", hash = "sha256:e1578f7eafce927b168752ed7e22646dad6cd9bca673c60bff55889fa236ebf9", size = 223096, upload-time = "2025-04-15T17:44:48.194Z" }, + { url = "https://files.pythonhosted.org/packages/54/4c/e76fe2a03014a7c767d79ea35c86a747e9325537a8b7627e0e5b3ba266b4/contourpy-1.3.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0475b1f6604896bc7c53bb070e355e9321e1bc0d381735421a2d2068ec56531f", size = 285090, upload-time = "2025-04-15T17:43:34.084Z" }, + { url = "https://files.pythonhosted.org/packages/7b/e2/5aba47debd55d668e00baf9651b721e7733975dc9fc27264a62b0dd26eb8/contourpy-1.3.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:c85bb486e9be652314bb5b9e2e3b0d1b2e643d5eec4992c0fbe8ac71775da739", size = 268643, upload-time = "2025-04-15T17:43:38.626Z" }, + { url = "https://files.pythonhosted.org/packages/a1/37/cd45f1f051fe6230f751cc5cdd2728bb3a203f5619510ef11e732109593c/contourpy-1.3.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:745b57db7758f3ffc05a10254edd3182a2a83402a89c00957a8e8a22f5582823", size = 310443, upload-time = "2025-04-15T17:43:44.522Z" }, + { url = "https://files.pythonhosted.org/packages/8b/a2/36ea6140c306c9ff6dd38e3bcec80b3b018474ef4d17eb68ceecd26675f4/contourpy-1.3.2-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:970e9173dbd7eba9b4e01aab19215a48ee5dd3f43cef736eebde064a171f89a5", size = 349865, upload-time = "2025-04-15T17:43:49.545Z" }, + { url = "https://files.pythonhosted.org/packages/95/b7/2fc76bc539693180488f7b6cc518da7acbbb9e3b931fd9280504128bf956/contourpy-1.3.2-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c6c4639a9c22230276b7bffb6a850dfc8258a2521305e1faefe804d006b2e532", size = 321162, upload-time = "2025-04-15T17:43:54.203Z" }, + { url = "https://files.pythonhosted.org/packages/f4/10/76d4f778458b0aa83f96e59d65ece72a060bacb20cfbee46cf6cd5ceba41/contourpy-1.3.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc829960f34ba36aad4302e78eabf3ef16a3a100863f0d4eeddf30e8a485a03b", size = 327355, upload-time = "2025-04-15T17:44:01.025Z" }, + { url = "https://files.pythonhosted.org/packages/43/a3/10cf483ea683f9f8ab096c24bad3cce20e0d1dd9a4baa0e2093c1c962d9d/contourpy-1.3.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:d32530b534e986374fc19eaa77fcb87e8a99e5431499949b828312bdcd20ac52", size = 1307935, upload-time = "2025-04-15T17:44:17.322Z" }, + { url = "https://files.pythonhosted.org/packages/78/73/69dd9a024444489e22d86108e7b913f3528f56cfc312b5c5727a44188471/contourpy-1.3.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:e298e7e70cf4eb179cc1077be1c725b5fd131ebc81181bf0c03525c8abc297fd", size = 1372168, upload-time = "2025-04-15T17:44:33.43Z" }, + { url = "https://files.pythonhosted.org/packages/0f/1b/96d586ccf1b1a9d2004dd519b25fbf104a11589abfd05484ff12199cca21/contourpy-1.3.2-cp313-cp313t-win32.whl", hash = "sha256:d0e589ae0d55204991450bb5c23f571c64fe43adaa53f93fc902a84c96f52fe1", size = 189550, upload-time = "2025-04-15T17:44:37.092Z" }, + { url = "https://files.pythonhosted.org/packages/b0/e6/6000d0094e8a5e32ad62591c8609e269febb6e4db83a1c75ff8868b42731/contourpy-1.3.2-cp313-cp313t-win_amd64.whl", hash = "sha256:78e9253c3de756b3f6a5174d024c4835acd59eb3f8e2ca13e775dbffe1558f69", size = 238214, upload-time = "2025-04-15T17:44:40.827Z" }, + { url = "https://files.pythonhosted.org/packages/33/05/b26e3c6ecc05f349ee0013f0bb850a761016d89cec528a98193a48c34033/contourpy-1.3.2-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:fd93cc7f3139b6dd7aab2f26a90dde0aa9fc264dbf70f6740d498a70b860b82c", size = 265681, upload-time = "2025-04-15T17:44:59.314Z" }, + { url = "https://files.pythonhosted.org/packages/2b/25/ac07d6ad12affa7d1ffed11b77417d0a6308170f44ff20fa1d5aa6333f03/contourpy-1.3.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:107ba8a6a7eec58bb475329e6d3b95deba9440667c4d62b9b6063942b61d7f16", size = 315101, upload-time = "2025-04-15T17:45:04.165Z" }, + { url = "https://files.pythonhosted.org/packages/8f/4d/5bb3192bbe9d3f27e3061a6a8e7733c9120e203cb8515767d30973f71030/contourpy-1.3.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:ded1706ed0c1049224531b81128efbd5084598f18d8a2d9efae833edbd2b40ad", size = 220599, upload-time = "2025-04-15T17:45:08.456Z" }, + { url = "https://files.pythonhosted.org/packages/ff/c0/91f1215d0d9f9f343e4773ba6c9b89e8c0cc7a64a6263f21139da639d848/contourpy-1.3.2-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:5f5964cdad279256c084b69c3f412b7801e15356b16efa9d78aa974041903da0", size = 266807, upload-time = "2025-04-15T17:45:15.535Z" }, + { url = "https://files.pythonhosted.org/packages/d4/79/6be7e90c955c0487e7712660d6cead01fa17bff98e0ea275737cc2bc8e71/contourpy-1.3.2-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:49b65a95d642d4efa8f64ba12558fcb83407e58a2dfba9d796d77b63ccfcaff5", size = 318729, upload-time = "2025-04-15T17:45:20.166Z" }, + { url = "https://files.pythonhosted.org/packages/87/68/7f46fb537958e87427d98a4074bcde4b67a70b04900cfc5ce29bc2f556c1/contourpy-1.3.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:8c5acb8dddb0752bf252e01a3035b21443158910ac16a3b0d20e7fed7d534ce5", size = 221791, upload-time = "2025-04-15T17:45:24.794Z" }, +] + +[[package]] +name = "contourpy" +version = "1.3.3" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.11'", +] +dependencies = [ + { name = "numpy", version = "2.3.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/58/01/1253e6698a07380cd31a736d248a3f2a50a7c88779a1813da27503cadc2a/contourpy-1.3.3.tar.gz", hash = "sha256:083e12155b210502d0bca491432bb04d56dc3432f95a979b429f2848c3dbe880", size = 13466174, upload-time = "2025-07-26T12:03:12.549Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/91/2e/c4390a31919d8a78b90e8ecf87cd4b4c4f05a5b48d05ec17db8e5404c6f4/contourpy-1.3.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:709a48ef9a690e1343202916450bc48b9e51c049b089c7f79a267b46cffcdaa1", size = 288773, upload-time = "2025-07-26T12:01:02.277Z" }, + { url = "https://files.pythonhosted.org/packages/0d/44/c4b0b6095fef4dc9c420e041799591e3b63e9619e3044f7f4f6c21c0ab24/contourpy-1.3.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:23416f38bfd74d5d28ab8429cc4d63fa67d5068bd711a85edb1c3fb0c3e2f381", size = 270149, upload-time = "2025-07-26T12:01:04.072Z" }, + { url = "https://files.pythonhosted.org/packages/30/2e/dd4ced42fefac8470661d7cb7e264808425e6c5d56d175291e93890cce09/contourpy-1.3.3-cp311-cp311-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:929ddf8c4c7f348e4c0a5a3a714b5c8542ffaa8c22954862a46ca1813b667ee7", size = 329222, upload-time = "2025-07-26T12:01:05.688Z" }, + { url = "https://files.pythonhosted.org/packages/f2/74/cc6ec2548e3d276c71389ea4802a774b7aa3558223b7bade3f25787fafc2/contourpy-1.3.3-cp311-cp311-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9e999574eddae35f1312c2b4b717b7885d4edd6cb46700e04f7f02db454e67c1", size = 377234, upload-time = "2025-07-26T12:01:07.054Z" }, + { url = "https://files.pythonhosted.org/packages/03/b3/64ef723029f917410f75c09da54254c5f9ea90ef89b143ccadb09df14c15/contourpy-1.3.3-cp311-cp311-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0bf67e0e3f482cb69779dd3061b534eb35ac9b17f163d851e2a547d56dba0a3a", size = 380555, upload-time = "2025-07-26T12:01:08.801Z" }, + { url = "https://files.pythonhosted.org/packages/5f/4b/6157f24ca425b89fe2eb7e7be642375711ab671135be21e6faa100f7448c/contourpy-1.3.3-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:51e79c1f7470158e838808d4a996fa9bac72c498e93d8ebe5119bc1e6becb0db", size = 355238, upload-time = "2025-07-26T12:01:10.319Z" }, + { url = "https://files.pythonhosted.org/packages/98/56/f914f0dd678480708a04cfd2206e7c382533249bc5001eb9f58aa693e200/contourpy-1.3.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:598c3aaece21c503615fd59c92a3598b428b2f01bfb4b8ca9c4edeecc2438620", size = 1326218, upload-time = "2025-07-26T12:01:12.659Z" }, + { url = "https://files.pythonhosted.org/packages/fb/d7/4a972334a0c971acd5172389671113ae82aa7527073980c38d5868ff1161/contourpy-1.3.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:322ab1c99b008dad206d406bb61d014cf0174df491ae9d9d0fac6a6fda4f977f", size = 1392867, upload-time = "2025-07-26T12:01:15.533Z" }, + { url = "https://files.pythonhosted.org/packages/75/3e/f2cc6cd56dc8cff46b1a56232eabc6feea52720083ea71ab15523daab796/contourpy-1.3.3-cp311-cp311-win32.whl", hash = "sha256:fd907ae12cd483cd83e414b12941c632a969171bf90fc937d0c9f268a31cafff", size = 183677, upload-time = "2025-07-26T12:01:17.088Z" }, + { url = "https://files.pythonhosted.org/packages/98/4b/9bd370b004b5c9d8045c6c33cf65bae018b27aca550a3f657cdc99acdbd8/contourpy-1.3.3-cp311-cp311-win_amd64.whl", hash = "sha256:3519428f6be58431c56581f1694ba8e50626f2dd550af225f82fb5f5814d2a42", size = 225234, upload-time = "2025-07-26T12:01:18.256Z" }, + { url = "https://files.pythonhosted.org/packages/d9/b6/71771e02c2e004450c12b1120a5f488cad2e4d5b590b1af8bad060360fe4/contourpy-1.3.3-cp311-cp311-win_arm64.whl", hash = "sha256:15ff10bfada4bf92ec8b31c62bf7c1834c244019b4a33095a68000d7075df470", size = 193123, upload-time = "2025-07-26T12:01:19.848Z" }, + { url = "https://files.pythonhosted.org/packages/be/45/adfee365d9ea3d853550b2e735f9d66366701c65db7855cd07621732ccfc/contourpy-1.3.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b08a32ea2f8e42cf1d4be3169a98dd4be32bafe4f22b6c4cb4ba810fa9e5d2cb", size = 293419, upload-time = "2025-07-26T12:01:21.16Z" }, + { url = "https://files.pythonhosted.org/packages/53/3e/405b59cfa13021a56bba395a6b3aca8cec012b45bf177b0eaf7a202cde2c/contourpy-1.3.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:556dba8fb6f5d8742f2923fe9457dbdd51e1049c4a43fd3986a0b14a1d815fc6", size = 273979, upload-time = "2025-07-26T12:01:22.448Z" }, + { url = "https://files.pythonhosted.org/packages/d4/1c/a12359b9b2ca3a845e8f7f9ac08bdf776114eb931392fcad91743e2ea17b/contourpy-1.3.3-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:92d9abc807cf7d0e047b95ca5d957cf4792fcd04e920ca70d48add15c1a90ea7", size = 332653, upload-time = "2025-07-26T12:01:24.155Z" }, + { url = "https://files.pythonhosted.org/packages/63/12/897aeebfb475b7748ea67b61e045accdfcf0d971f8a588b67108ed7f5512/contourpy-1.3.3-cp312-cp312-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b2e8faa0ed68cb29af51edd8e24798bb661eac3bd9f65420c1887b6ca89987c8", size = 379536, upload-time = "2025-07-26T12:01:25.91Z" }, + { url = "https://files.pythonhosted.org/packages/43/8a/a8c584b82deb248930ce069e71576fc09bd7174bbd35183b7943fb1064fd/contourpy-1.3.3-cp312-cp312-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:626d60935cf668e70a5ce6ff184fd713e9683fb458898e4249b63be9e28286ea", size = 384397, upload-time = "2025-07-26T12:01:27.152Z" }, + { url = "https://files.pythonhosted.org/packages/cc/8f/ec6289987824b29529d0dfda0d74a07cec60e54b9c92f3c9da4c0ac732de/contourpy-1.3.3-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4d00e655fcef08aba35ec9610536bfe90267d7ab5ba944f7032549c55a146da1", size = 362601, upload-time = "2025-07-26T12:01:28.808Z" }, + { url = "https://files.pythonhosted.org/packages/05/0a/a3fe3be3ee2dceb3e615ebb4df97ae6f3828aa915d3e10549ce016302bd1/contourpy-1.3.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:451e71b5a7d597379ef572de31eeb909a87246974d960049a9848c3bc6c41bf7", size = 1331288, upload-time = "2025-07-26T12:01:31.198Z" }, + { url = "https://files.pythonhosted.org/packages/33/1d/acad9bd4e97f13f3e2b18a3977fe1b4a37ecf3d38d815333980c6c72e963/contourpy-1.3.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:459c1f020cd59fcfe6650180678a9993932d80d44ccde1fa1868977438f0b411", size = 1403386, upload-time = "2025-07-26T12:01:33.947Z" }, + { url = "https://files.pythonhosted.org/packages/cf/8f/5847f44a7fddf859704217a99a23a4f6417b10e5ab1256a179264561540e/contourpy-1.3.3-cp312-cp312-win32.whl", hash = "sha256:023b44101dfe49d7d53932be418477dba359649246075c996866106da069af69", size = 185018, upload-time = "2025-07-26T12:01:35.64Z" }, + { url = "https://files.pythonhosted.org/packages/19/e8/6026ed58a64563186a9ee3f29f41261fd1828f527dd93d33b60feca63352/contourpy-1.3.3-cp312-cp312-win_amd64.whl", hash = "sha256:8153b8bfc11e1e4d75bcb0bff1db232f9e10b274e0929de9d608027e0d34ff8b", size = 226567, upload-time = "2025-07-26T12:01:36.804Z" }, + { url = "https://files.pythonhosted.org/packages/d1/e2/f05240d2c39a1ed228d8328a78b6f44cd695f7ef47beb3e684cf93604f86/contourpy-1.3.3-cp312-cp312-win_arm64.whl", hash = "sha256:07ce5ed73ecdc4a03ffe3e1b3e3c1166db35ae7584be76f65dbbe28a7791b0cc", size = 193655, upload-time = "2025-07-26T12:01:37.999Z" }, + { url = "https://files.pythonhosted.org/packages/68/35/0167aad910bbdb9599272bd96d01a9ec6852f36b9455cf2ca67bd4cc2d23/contourpy-1.3.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:177fb367556747a686509d6fef71d221a4b198a3905fe824430e5ea0fda54eb5", size = 293257, upload-time = "2025-07-26T12:01:39.367Z" }, + { url = "https://files.pythonhosted.org/packages/96/e4/7adcd9c8362745b2210728f209bfbcf7d91ba868a2c5f40d8b58f54c509b/contourpy-1.3.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d002b6f00d73d69333dac9d0b8d5e84d9724ff9ef044fd63c5986e62b7c9e1b1", size = 274034, upload-time = "2025-07-26T12:01:40.645Z" }, + { url = "https://files.pythonhosted.org/packages/73/23/90e31ceeed1de63058a02cb04b12f2de4b40e3bef5e082a7c18d9c8ae281/contourpy-1.3.3-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:348ac1f5d4f1d66d3322420f01d42e43122f43616e0f194fc1c9f5d830c5b286", size = 334672, upload-time = "2025-07-26T12:01:41.942Z" }, + { url = "https://files.pythonhosted.org/packages/ed/93/b43d8acbe67392e659e1d984700e79eb67e2acb2bd7f62012b583a7f1b55/contourpy-1.3.3-cp313-cp313-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:655456777ff65c2c548b7c454af9c6f33f16c8884f11083244b5819cc214f1b5", size = 381234, upload-time = "2025-07-26T12:01:43.499Z" }, + { url = "https://files.pythonhosted.org/packages/46/3b/bec82a3ea06f66711520f75a40c8fc0b113b2a75edb36aa633eb11c4f50f/contourpy-1.3.3-cp313-cp313-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:644a6853d15b2512d67881586bd03f462c7ab755db95f16f14d7e238f2852c67", size = 385169, upload-time = "2025-07-26T12:01:45.219Z" }, + { url = "https://files.pythonhosted.org/packages/4b/32/e0f13a1c5b0f8572d0ec6ae2f6c677b7991fafd95da523159c19eff0696a/contourpy-1.3.3-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4debd64f124ca62069f313a9cb86656ff087786016d76927ae2cf37846b006c9", size = 362859, upload-time = "2025-07-26T12:01:46.519Z" }, + { url = "https://files.pythonhosted.org/packages/33/71/e2a7945b7de4e58af42d708a219f3b2f4cff7386e6b6ab0a0fa0033c49a9/contourpy-1.3.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a15459b0f4615b00bbd1e91f1b9e19b7e63aea7483d03d804186f278c0af2659", size = 1332062, upload-time = "2025-07-26T12:01:48.964Z" }, + { url = "https://files.pythonhosted.org/packages/12/fc/4e87ac754220ccc0e807284f88e943d6d43b43843614f0a8afa469801db0/contourpy-1.3.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ca0fdcd73925568ca027e0b17ab07aad764be4706d0a925b89227e447d9737b7", size = 1403932, upload-time = "2025-07-26T12:01:51.979Z" }, + { url = "https://files.pythonhosted.org/packages/a6/2e/adc197a37443f934594112222ac1aa7dc9a98faf9c3842884df9a9d8751d/contourpy-1.3.3-cp313-cp313-win32.whl", hash = "sha256:b20c7c9a3bf701366556e1b1984ed2d0cedf999903c51311417cf5f591d8c78d", size = 185024, upload-time = "2025-07-26T12:01:53.245Z" }, + { url = "https://files.pythonhosted.org/packages/18/0b/0098c214843213759692cc638fce7de5c289200a830e5035d1791d7a2338/contourpy-1.3.3-cp313-cp313-win_amd64.whl", hash = "sha256:1cadd8b8969f060ba45ed7c1b714fe69185812ab43bd6b86a9123fe8f99c3263", size = 226578, upload-time = "2025-07-26T12:01:54.422Z" }, + { url = "https://files.pythonhosted.org/packages/8a/9a/2f6024a0c5995243cd63afdeb3651c984f0d2bc727fd98066d40e141ad73/contourpy-1.3.3-cp313-cp313-win_arm64.whl", hash = "sha256:fd914713266421b7536de2bfa8181aa8c699432b6763a0ea64195ebe28bff6a9", size = 193524, upload-time = "2025-07-26T12:01:55.73Z" }, + { url = "https://files.pythonhosted.org/packages/c0/b3/f8a1a86bd3298513f500e5b1f5fd92b69896449f6cab6a146a5d52715479/contourpy-1.3.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:88df9880d507169449d434c293467418b9f6cbe82edd19284aa0409e7fdb933d", size = 306730, upload-time = "2025-07-26T12:01:57.051Z" }, + { url = "https://files.pythonhosted.org/packages/3f/11/4780db94ae62fc0c2053909b65dc3246bd7cecfc4f8a20d957ad43aa4ad8/contourpy-1.3.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:d06bb1f751ba5d417047db62bca3c8fde202b8c11fb50742ab3ab962c81e8216", size = 287897, upload-time = "2025-07-26T12:01:58.663Z" }, + { url = "https://files.pythonhosted.org/packages/ae/15/e59f5f3ffdd6f3d4daa3e47114c53daabcb18574a26c21f03dc9e4e42ff0/contourpy-1.3.3-cp313-cp313t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e4e6b05a45525357e382909a4c1600444e2a45b4795163d3b22669285591c1ae", size = 326751, upload-time = "2025-07-26T12:02:00.343Z" }, + { url = "https://files.pythonhosted.org/packages/0f/81/03b45cfad088e4770b1dcf72ea78d3802d04200009fb364d18a493857210/contourpy-1.3.3-cp313-cp313t-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ab3074b48c4e2cf1a960e6bbeb7f04566bf36b1861d5c9d4d8ac04b82e38ba20", size = 375486, upload-time = "2025-07-26T12:02:02.128Z" }, + { url = "https://files.pythonhosted.org/packages/0c/ba/49923366492ffbdd4486e970d421b289a670ae8cf539c1ea9a09822b371a/contourpy-1.3.3-cp313-cp313t-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6c3d53c796f8647d6deb1abe867daeb66dcc8a97e8455efa729516b997b8ed99", size = 388106, upload-time = "2025-07-26T12:02:03.615Z" }, + { url = "https://files.pythonhosted.org/packages/9f/52/5b00ea89525f8f143651f9f03a0df371d3cbd2fccd21ca9b768c7a6500c2/contourpy-1.3.3-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:50ed930df7289ff2a8d7afeb9603f8289e5704755c7e5c3bbd929c90c817164b", size = 352548, upload-time = "2025-07-26T12:02:05.165Z" }, + { url = "https://files.pythonhosted.org/packages/32/1d/a209ec1a3a3452d490f6b14dd92e72280c99ae3d1e73da74f8277d4ee08f/contourpy-1.3.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4feffb6537d64b84877da813a5c30f1422ea5739566abf0bd18065ac040e120a", size = 1322297, upload-time = "2025-07-26T12:02:07.379Z" }, + { url = "https://files.pythonhosted.org/packages/bc/9e/46f0e8ebdd884ca0e8877e46a3f4e633f6c9c8c4f3f6e72be3fe075994aa/contourpy-1.3.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:2b7e9480ffe2b0cd2e787e4df64270e3a0440d9db8dc823312e2c940c167df7e", size = 1391023, upload-time = "2025-07-26T12:02:10.171Z" }, + { url = "https://files.pythonhosted.org/packages/b9/70/f308384a3ae9cd2209e0849f33c913f658d3326900d0ff5d378d6a1422d2/contourpy-1.3.3-cp313-cp313t-win32.whl", hash = "sha256:283edd842a01e3dcd435b1c5116798d661378d83d36d337b8dde1d16a5fc9ba3", size = 196157, upload-time = "2025-07-26T12:02:11.488Z" }, + { url = "https://files.pythonhosted.org/packages/b2/dd/880f890a6663b84d9e34a6f88cded89d78f0091e0045a284427cb6b18521/contourpy-1.3.3-cp313-cp313t-win_amd64.whl", hash = "sha256:87acf5963fc2b34825e5b6b048f40e3635dd547f590b04d2ab317c2619ef7ae8", size = 240570, upload-time = "2025-07-26T12:02:12.754Z" }, + { url = "https://files.pythonhosted.org/packages/80/99/2adc7d8ffead633234817ef8e9a87115c8a11927a94478f6bb3d3f4d4f7d/contourpy-1.3.3-cp313-cp313t-win_arm64.whl", hash = "sha256:3c30273eb2a55024ff31ba7d052dde990d7d8e5450f4bbb6e913558b3d6c2301", size = 199713, upload-time = "2025-07-26T12:02:14.4Z" }, + { url = "https://files.pythonhosted.org/packages/72/8b/4546f3ab60f78c514ffb7d01a0bd743f90de36f0019d1be84d0a708a580a/contourpy-1.3.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fde6c716d51c04b1c25d0b90364d0be954624a0ee9d60e23e850e8d48353d07a", size = 292189, upload-time = "2025-07-26T12:02:16.095Z" }, + { url = "https://files.pythonhosted.org/packages/fd/e1/3542a9cb596cadd76fcef413f19c79216e002623158befe6daa03dbfa88c/contourpy-1.3.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:cbedb772ed74ff5be440fa8eee9bd49f64f6e3fc09436d9c7d8f1c287b121d77", size = 273251, upload-time = "2025-07-26T12:02:17.524Z" }, + { url = "https://files.pythonhosted.org/packages/b1/71/f93e1e9471d189f79d0ce2497007731c1e6bf9ef6d1d61b911430c3db4e5/contourpy-1.3.3-cp314-cp314-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:22e9b1bd7a9b1d652cd77388465dc358dafcd2e217d35552424aa4f996f524f5", size = 335810, upload-time = "2025-07-26T12:02:18.9Z" }, + { url = "https://files.pythonhosted.org/packages/91/f9/e35f4c1c93f9275d4e38681a80506b5510e9327350c51f8d4a5a724d178c/contourpy-1.3.3-cp314-cp314-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a22738912262aa3e254e4f3cb079a95a67132fc5a063890e224393596902f5a4", size = 382871, upload-time = "2025-07-26T12:02:20.418Z" }, + { url = "https://files.pythonhosted.org/packages/b5/71/47b512f936f66a0a900d81c396a7e60d73419868fba959c61efed7a8ab46/contourpy-1.3.3-cp314-cp314-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:afe5a512f31ee6bd7d0dda52ec9864c984ca3d66664444f2d72e0dc4eb832e36", size = 386264, upload-time = "2025-07-26T12:02:21.916Z" }, + { url = "https://files.pythonhosted.org/packages/04/5f/9ff93450ba96b09c7c2b3f81c94de31c89f92292f1380261bd7195bea4ea/contourpy-1.3.3-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f64836de09927cba6f79dcd00fdd7d5329f3fccc633468507079c829ca4db4e3", size = 363819, upload-time = "2025-07-26T12:02:23.759Z" }, + { url = "https://files.pythonhosted.org/packages/3e/a6/0b185d4cc480ee494945cde102cb0149ae830b5fa17bf855b95f2e70ad13/contourpy-1.3.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:1fd43c3be4c8e5fd6e4f2baeae35ae18176cf2e5cced681cca908addf1cdd53b", size = 1333650, upload-time = "2025-07-26T12:02:26.181Z" }, + { url = "https://files.pythonhosted.org/packages/43/d7/afdc95580ca56f30fbcd3060250f66cedbde69b4547028863abd8aa3b47e/contourpy-1.3.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:6afc576f7b33cf00996e5c1102dc2a8f7cc89e39c0b55df93a0b78c1bd992b36", size = 1404833, upload-time = "2025-07-26T12:02:28.782Z" }, + { url = "https://files.pythonhosted.org/packages/e2/e2/366af18a6d386f41132a48f033cbd2102e9b0cf6345d35ff0826cd984566/contourpy-1.3.3-cp314-cp314-win32.whl", hash = "sha256:66c8a43a4f7b8df8b71ee1840e4211a3c8d93b214b213f590e18a1beca458f7d", size = 189692, upload-time = "2025-07-26T12:02:30.128Z" }, + { url = "https://files.pythonhosted.org/packages/7d/c2/57f54b03d0f22d4044b8afb9ca0e184f8b1afd57b4f735c2fa70883dc601/contourpy-1.3.3-cp314-cp314-win_amd64.whl", hash = "sha256:cf9022ef053f2694e31d630feaacb21ea24224be1c3ad0520b13d844274614fd", size = 232424, upload-time = "2025-07-26T12:02:31.395Z" }, + { url = "https://files.pythonhosted.org/packages/18/79/a9416650df9b525737ab521aa181ccc42d56016d2123ddcb7b58e926a42c/contourpy-1.3.3-cp314-cp314-win_arm64.whl", hash = "sha256:95b181891b4c71de4bb404c6621e7e2390745f887f2a026b2d99e92c17892339", size = 198300, upload-time = "2025-07-26T12:02:32.956Z" }, + { url = "https://files.pythonhosted.org/packages/1f/42/38c159a7d0f2b7b9c04c64ab317042bb6952b713ba875c1681529a2932fe/contourpy-1.3.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:33c82d0138c0a062380332c861387650c82e4cf1747aaa6938b9b6516762e772", size = 306769, upload-time = "2025-07-26T12:02:34.2Z" }, + { url = "https://files.pythonhosted.org/packages/c3/6c/26a8205f24bca10974e77460de68d3d7c63e282e23782f1239f226fcae6f/contourpy-1.3.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ea37e7b45949df430fe649e5de8351c423430046a2af20b1c1961cae3afcda77", size = 287892, upload-time = "2025-07-26T12:02:35.807Z" }, + { url = "https://files.pythonhosted.org/packages/66/06/8a475c8ab718ebfd7925661747dbb3c3ee9c82ac834ccb3570be49d129f4/contourpy-1.3.3-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d304906ecc71672e9c89e87c4675dc5c2645e1f4269a5063b99b0bb29f232d13", size = 326748, upload-time = "2025-07-26T12:02:37.193Z" }, + { url = "https://files.pythonhosted.org/packages/b4/a3/c5ca9f010a44c223f098fccd8b158bb1cb287378a31ac141f04730dc49be/contourpy-1.3.3-cp314-cp314t-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ca658cd1a680a5c9ea96dc61cdbae1e85c8f25849843aa799dfd3cb370ad4fbe", size = 375554, upload-time = "2025-07-26T12:02:38.894Z" }, + { url = "https://files.pythonhosted.org/packages/80/5b/68bd33ae63fac658a4145088c1e894405e07584a316738710b636c6d0333/contourpy-1.3.3-cp314-cp314t-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ab2fd90904c503739a75b7c8c5c01160130ba67944a7b77bbf36ef8054576e7f", size = 388118, upload-time = "2025-07-26T12:02:40.642Z" }, + { url = "https://files.pythonhosted.org/packages/40/52/4c285a6435940ae25d7410a6c36bda5145839bc3f0beb20c707cda18b9d2/contourpy-1.3.3-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b7301b89040075c30e5768810bc96a8e8d78085b47d8be6e4c3f5a0b4ed478a0", size = 352555, upload-time = "2025-07-26T12:02:42.25Z" }, + { url = "https://files.pythonhosted.org/packages/24/ee/3e81e1dd174f5c7fefe50e85d0892de05ca4e26ef1c9a59c2a57e43b865a/contourpy-1.3.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:2a2a8b627d5cc6b7c41a4beff6c5ad5eb848c88255fda4a8745f7e901b32d8e4", size = 1322295, upload-time = "2025-07-26T12:02:44.668Z" }, + { url = "https://files.pythonhosted.org/packages/3c/b2/6d913d4d04e14379de429057cd169e5e00f6c2af3bb13e1710bcbdb5da12/contourpy-1.3.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:fd6ec6be509c787f1caf6b247f0b1ca598bef13f4ddeaa126b7658215529ba0f", size = 1391027, upload-time = "2025-07-26T12:02:47.09Z" }, + { url = "https://files.pythonhosted.org/packages/93/8a/68a4ec5c55a2971213d29a9374913f7e9f18581945a7a31d1a39b5d2dfe5/contourpy-1.3.3-cp314-cp314t-win32.whl", hash = "sha256:e74a9a0f5e3fff48fb5a7f2fd2b9b70a3fe014a67522f79b7cca4c0c7e43c9ae", size = 202428, upload-time = "2025-07-26T12:02:48.691Z" }, + { url = "https://files.pythonhosted.org/packages/fa/96/fd9f641ffedc4fa3ace923af73b9d07e869496c9cc7a459103e6e978992f/contourpy-1.3.3-cp314-cp314t-win_amd64.whl", hash = "sha256:13b68d6a62db8eafaebb8039218921399baf6e47bf85006fd8529f2a08ef33fc", size = 250331, upload-time = "2025-07-26T12:02:50.137Z" }, + { url = "https://files.pythonhosted.org/packages/ae/8c/469afb6465b853afff216f9528ffda78a915ff880ed58813ba4faf4ba0b6/contourpy-1.3.3-cp314-cp314t-win_arm64.whl", hash = "sha256:b7448cb5a725bb1e35ce88771b86fba35ef418952474492cf7c764059933ff8b", size = 203831, upload-time = "2025-07-26T12:02:51.449Z" }, + { url = "https://files.pythonhosted.org/packages/a5/29/8dcfe16f0107943fa92388c23f6e05cff0ba58058c4c95b00280d4c75a14/contourpy-1.3.3-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:cd5dfcaeb10f7b7f9dc8941717c6c2ade08f587be2226222c12b25f0483ed497", size = 278809, upload-time = "2025-07-26T12:02:52.74Z" }, + { url = "https://files.pythonhosted.org/packages/85/a9/8b37ef4f7dafeb335daee3c8254645ef5725be4d9c6aa70b50ec46ef2f7e/contourpy-1.3.3-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:0c1fc238306b35f246d61a1d416a627348b5cf0648648a031e14bb8705fcdfe8", size = 261593, upload-time = "2025-07-26T12:02:54.037Z" }, + { url = "https://files.pythonhosted.org/packages/0a/59/ebfb8c677c75605cc27f7122c90313fd2f375ff3c8d19a1694bda74aaa63/contourpy-1.3.3-pp311-pypy311_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:70f9aad7de812d6541d29d2bbf8feb22ff7e1c299523db288004e3157ff4674e", size = 302202, upload-time = "2025-07-26T12:02:55.947Z" }, + { url = "https://files.pythonhosted.org/packages/3c/37/21972a15834d90bfbfb009b9d004779bd5a07a0ec0234e5ba8f64d5736f4/contourpy-1.3.3-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5ed3657edf08512fc3fe81b510e35c2012fbd3081d2e26160f27ca28affec989", size = 329207, upload-time = "2025-07-26T12:02:57.468Z" }, + { url = "https://files.pythonhosted.org/packages/0c/58/bd257695f39d05594ca4ad60df5bcb7e32247f9951fd09a9b8edb82d1daa/contourpy-1.3.3-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:3d1a3799d62d45c18bafd41c5fa05120b96a28079f2393af559b843d1a966a77", size = 225315, upload-time = "2025-07-26T12:02:58.801Z" }, +] + [[package]] name = "coverage" version = "7.9.2" @@ -383,6 +538,15 @@ toml = [ { name = "tomli", marker = "python_full_version <= '3.11'" }, ] +[[package]] +name = "cycler" +version = "0.12.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a9/95/a3dbbb5028f35eafb79008e7522a75244477d2838f38cbb722248dabc2a8/cycler-0.12.1.tar.gz", hash = "sha256:88bb128f02ba341da8ef447245a9e138fae777f6a23943da4540077d3601eb1c", size = 7615, upload-time = "2023-10-07T05:32:18.335Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30", size = 8321, upload-time = "2023-10-07T05:32:16.783Z" }, +] + [[package]] name = "debugpy" version = "1.8.14" @@ -483,6 +647,47 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/4d/36/2a115987e2d8c300a974597416d9de88f2444426de9571f4b59b2cca3acc/filelock-3.18.0-py3-none-any.whl", hash = "sha256:c401f4f8377c4464e6db25fff06205fd89bdd83b65eb0488ed1b160f780e21de", size = 16215, upload-time = "2025-03-14T07:11:39.145Z" }, ] +[[package]] +name = "fonttools" +version = "4.59.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8a/27/ec3c723bfdf86f34c5c82bf6305df3e0f0d8ea798d2d3a7cb0c0a866d286/fonttools-4.59.0.tar.gz", hash = "sha256:be392ec3529e2f57faa28709d60723a763904f71a2b63aabe14fee6648fe3b14", size = 3532521, upload-time = "2025-07-16T12:04:54.613Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1c/1f/3dcae710b7c4b56e79442b03db64f6c9f10c3348f7af40339dffcefb581e/fonttools-4.59.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:524133c1be38445c5c0575eacea42dbd44374b310b1ffc4b60ff01d881fabb96", size = 2761846, upload-time = "2025-07-16T12:03:33.267Z" }, + { url = "https://files.pythonhosted.org/packages/eb/0e/ae3a1884fa1549acac1191cc9ec039142f6ac0e9cbc139c2e6a3dab967da/fonttools-4.59.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:21e606b2d38fed938dde871c5736822dd6bda7a4631b92e509a1f5cd1b90c5df", size = 2332060, upload-time = "2025-07-16T12:03:36.472Z" }, + { url = "https://files.pythonhosted.org/packages/75/46/58bff92a7216829159ac7bdb1d05a48ad1b8ab8c539555f12d29fdecfdd4/fonttools-4.59.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e93df708c69a193fc7987192f94df250f83f3851fda49413f02ba5dded639482", size = 4852354, upload-time = "2025-07-16T12:03:39.102Z" }, + { url = "https://files.pythonhosted.org/packages/05/57/767e31e48861045d89691128bd81fd4c62b62150f9a17a666f731ce4f197/fonttools-4.59.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:62224a9bb85b4b66d1b46d45cbe43d71cbf8f527d332b177e3b96191ffbc1e64", size = 4781132, upload-time = "2025-07-16T12:03:41.415Z" }, + { url = "https://files.pythonhosted.org/packages/d7/78/adb5e9b0af5c6ce469e8b0e112f144eaa84b30dd72a486e9c778a9b03b31/fonttools-4.59.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b8974b2a266b54c96709bd5e239979cddfd2dbceed331aa567ea1d7c4a2202db", size = 4832901, upload-time = "2025-07-16T12:03:43.115Z" }, + { url = "https://files.pythonhosted.org/packages/ac/92/bc3881097fbf3d56d112bec308c863c058e5d4c9c65f534e8ae58450ab8a/fonttools-4.59.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:209b75943d158f610b78320eacb5539aa9e920bee2c775445b2846c65d20e19d", size = 4940140, upload-time = "2025-07-16T12:03:44.781Z" }, + { url = "https://files.pythonhosted.org/packages/4a/54/39cdb23f0eeda2e07ae9cb189f2b6f41da89aabc682d3a387b3ff4a4ed29/fonttools-4.59.0-cp310-cp310-win32.whl", hash = "sha256:4c908a7036f0f3677f8afa577bcd973e3e20ddd2f7c42a33208d18bee95cdb6f", size = 2215890, upload-time = "2025-07-16T12:03:46.961Z" }, + { url = "https://files.pythonhosted.org/packages/d8/eb/f8388d9e19f95d8df2449febe9b1a38ddd758cfdb7d6de3a05198d785d61/fonttools-4.59.0-cp310-cp310-win_amd64.whl", hash = "sha256:8b4309a2775e4feee7356e63b163969a215d663399cce1b3d3b65e7ec2d9680e", size = 2260191, upload-time = "2025-07-16T12:03:48.908Z" }, + { url = "https://files.pythonhosted.org/packages/06/96/520733d9602fa1bf6592e5354c6721ac6fc9ea72bc98d112d0c38b967199/fonttools-4.59.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:841b2186adce48903c0fef235421ae21549020eca942c1da773ac380b056ab3c", size = 2782387, upload-time = "2025-07-16T12:03:51.424Z" }, + { url = "https://files.pythonhosted.org/packages/87/6a/170fce30b9bce69077d8eec9bea2cfd9f7995e8911c71be905e2eba6368b/fonttools-4.59.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9bcc1e77fbd1609198966ded6b2a9897bd6c6bcbd2287a2fc7d75f1a254179c5", size = 2342194, upload-time = "2025-07-16T12:03:53.295Z" }, + { url = "https://files.pythonhosted.org/packages/b0/b6/7c8166c0066856f1408092f7968ac744060cf72ca53aec9036106f57eeca/fonttools-4.59.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:37c377f7cb2ab2eca8a0b319c68146d34a339792f9420fca6cd49cf28d370705", size = 5032333, upload-time = "2025-07-16T12:03:55.177Z" }, + { url = "https://files.pythonhosted.org/packages/eb/0c/707c5a19598eafcafd489b73c4cb1c142102d6197e872f531512d084aa76/fonttools-4.59.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fa39475eaccb98f9199eccfda4298abaf35ae0caec676ffc25b3a5e224044464", size = 4974422, upload-time = "2025-07-16T12:03:57.406Z" }, + { url = "https://files.pythonhosted.org/packages/f6/e7/6d33737d9fe632a0f59289b6f9743a86d2a9d0673de2a0c38c0f54729822/fonttools-4.59.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d3972b13148c1d1fbc092b27678a33b3080d1ac0ca305742b0119b75f9e87e38", size = 5010631, upload-time = "2025-07-16T12:03:59.449Z" }, + { url = "https://files.pythonhosted.org/packages/63/e1/a4c3d089ab034a578820c8f2dff21ef60daf9668034a1e4fb38bb1cc3398/fonttools-4.59.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a408c3c51358c89b29cfa5317cf11518b7ce5de1717abb55c5ae2d2921027de6", size = 5122198, upload-time = "2025-07-16T12:04:01.542Z" }, + { url = "https://files.pythonhosted.org/packages/09/77/ca82b9c12fa4de3c520b7760ee61787640cf3fde55ef1b0bfe1de38c8153/fonttools-4.59.0-cp311-cp311-win32.whl", hash = "sha256:6770d7da00f358183d8fd5c4615436189e4f683bdb6affb02cad3d221d7bb757", size = 2214216, upload-time = "2025-07-16T12:04:03.515Z" }, + { url = "https://files.pythonhosted.org/packages/ab/25/5aa7ca24b560b2f00f260acf32c4cf29d7aaf8656e159a336111c18bc345/fonttools-4.59.0-cp311-cp311-win_amd64.whl", hash = "sha256:84fc186980231a287b28560d3123bd255d3c6b6659828c642b4cf961e2b923d0", size = 2261879, upload-time = "2025-07-16T12:04:05.015Z" }, + { url = "https://files.pythonhosted.org/packages/e2/77/b1c8af22f4265e951cd2e5535dbef8859efcef4fb8dee742d368c967cddb/fonttools-4.59.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:f9b3a78f69dcbd803cf2fb3f972779875b244c1115481dfbdd567b2c22b31f6b", size = 2767562, upload-time = "2025-07-16T12:04:06.895Z" }, + { url = "https://files.pythonhosted.org/packages/ff/5a/aeb975699588176bb357e8b398dfd27e5d3a2230d92b81ab8cbb6187358d/fonttools-4.59.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:57bb7e26928573ee7c6504f54c05860d867fd35e675769f3ce01b52af38d48e2", size = 2335168, upload-time = "2025-07-16T12:04:08.695Z" }, + { url = "https://files.pythonhosted.org/packages/54/97/c6101a7e60ae138c4ef75b22434373a0da50a707dad523dd19a4889315bf/fonttools-4.59.0-cp312-cp312-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:4536f2695fe5c1ffb528d84a35a7d3967e5558d2af58b4775e7ab1449d65767b", size = 4909850, upload-time = "2025-07-16T12:04:10.761Z" }, + { url = "https://files.pythonhosted.org/packages/bd/6c/fa4d18d641054f7bff878cbea14aa9433f292b9057cb1700d8e91a4d5f4f/fonttools-4.59.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:885bde7d26e5b40e15c47bd5def48b38cbd50830a65f98122a8fb90962af7cd1", size = 4955131, upload-time = "2025-07-16T12:04:12.846Z" }, + { url = "https://files.pythonhosted.org/packages/20/5c/331947fc1377deb928a69bde49f9003364f5115e5cbe351eea99e39412a2/fonttools-4.59.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6801aeddb6acb2c42eafa45bc1cb98ba236871ae6f33f31e984670b749a8e58e", size = 4899667, upload-time = "2025-07-16T12:04:14.558Z" }, + { url = "https://files.pythonhosted.org/packages/8a/46/b66469dfa26b8ff0baa7654b2cc7851206c6d57fe3abdabbaab22079a119/fonttools-4.59.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:31003b6a10f70742a63126b80863ab48175fb8272a18ca0846c0482968f0588e", size = 5051349, upload-time = "2025-07-16T12:04:16.388Z" }, + { url = "https://files.pythonhosted.org/packages/2e/05/ebfb6b1f3a4328ab69787d106a7d92ccde77ce66e98659df0f9e3f28d93d/fonttools-4.59.0-cp312-cp312-win32.whl", hash = "sha256:fbce6dae41b692a5973d0f2158f782b9ad05babc2c2019a970a1094a23909b1b", size = 2201315, upload-time = "2025-07-16T12:04:18.557Z" }, + { url = "https://files.pythonhosted.org/packages/09/45/d2bdc9ea20bbadec1016fd0db45696d573d7a26d95ab5174ffcb6d74340b/fonttools-4.59.0-cp312-cp312-win_amd64.whl", hash = "sha256:332bfe685d1ac58ca8d62b8d6c71c2e52a6c64bc218dc8f7825c9ea51385aa01", size = 2249408, upload-time = "2025-07-16T12:04:20.489Z" }, + { url = "https://files.pythonhosted.org/packages/f3/bb/390990e7c457d377b00890d9f96a3ca13ae2517efafb6609c1756e213ba4/fonttools-4.59.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:78813b49d749e1bb4db1c57f2d4d7e6db22c253cb0a86ad819f5dc197710d4b2", size = 2758704, upload-time = "2025-07-16T12:04:22.217Z" }, + { url = "https://files.pythonhosted.org/packages/df/6f/d730d9fcc9b410a11597092bd2eb9ca53e5438c6cb90e4b3047ce1b723e9/fonttools-4.59.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:401b1941ce37e78b8fd119b419b617277c65ae9417742a63282257434fd68ea2", size = 2330764, upload-time = "2025-07-16T12:04:23.985Z" }, + { url = "https://files.pythonhosted.org/packages/75/b4/b96bb66f6f8cc4669de44a158099b249c8159231d254ab6b092909388be5/fonttools-4.59.0-cp313-cp313-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:efd7e6660674e234e29937bc1481dceb7e0336bfae75b856b4fb272b5093c5d4", size = 4890699, upload-time = "2025-07-16T12:04:25.664Z" }, + { url = "https://files.pythonhosted.org/packages/b5/57/7969af50b26408be12baa317c6147588db5b38af2759e6df94554dbc5fdb/fonttools-4.59.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:51ab1ff33c19e336c02dee1e9fd1abd974a4ca3d8f7eef2a104d0816a241ce97", size = 4952934, upload-time = "2025-07-16T12:04:27.733Z" }, + { url = "https://files.pythonhosted.org/packages/d6/e2/dd968053b6cf1f46c904f5bd409b22341477c017d8201619a265e50762d3/fonttools-4.59.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a9bf8adc9e1f3012edc8f09b08336272aec0c55bc677422273e21280db748f7c", size = 4892319, upload-time = "2025-07-16T12:04:30.074Z" }, + { url = "https://files.pythonhosted.org/packages/6b/95/a59810d8eda09129f83467a4e58f84205dc6994ebaeb9815406363e07250/fonttools-4.59.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:37e01c6ec0c98599778c2e688350d624fa4770fbd6144551bd5e032f1199171c", size = 5034753, upload-time = "2025-07-16T12:04:32.292Z" }, + { url = "https://files.pythonhosted.org/packages/a5/84/51a69ee89ff8d1fea0c6997e946657e25a3f08513de8435fe124929f3eef/fonttools-4.59.0-cp313-cp313-win32.whl", hash = "sha256:70d6b3ceaa9cc5a6ac52884f3b3d9544e8e231e95b23f138bdb78e6d4dc0eae3", size = 2199688, upload-time = "2025-07-16T12:04:34.444Z" }, + { url = "https://files.pythonhosted.org/packages/a0/ee/f626cd372932d828508137a79b85167fdcf3adab2e3bed433f295c596c6a/fonttools-4.59.0-cp313-cp313-win_amd64.whl", hash = "sha256:26731739daa23b872643f0e4072d5939960237d540c35c14e6a06d47d71ca8fe", size = 2248560, upload-time = "2025-07-16T12:04:36.034Z" }, + { url = "https://files.pythonhosted.org/packages/d0/9c/df0ef2c51845a13043e5088f7bb988ca6cd5bb82d5d4203d6a158aa58cf2/fonttools-4.59.0-py3-none-any.whl", hash = "sha256:241313683afd3baacb32a6bd124d0bce7404bc5280e12e291bae1b9bba28711d", size = 1128050, upload-time = "2025-07-16T12:04:52.687Z" }, +] + [[package]] name = "fqdn" version = "1.5.1" @@ -567,6 +772,7 @@ pytket = [ dev = [ { name = "guppylang", extra = ["emulator"] }, { name = "ipykernel" }, + { name = "matplotlib" }, { name = "miette-py" }, { name = "mypy" }, { name = "pre-commit" }, @@ -593,6 +799,7 @@ lint = [ pytket-integration = [ { name = "guppylang", extra = ["emulator"] }, { name = "ipykernel" }, + { name = "matplotlib" }, { name = "miette-py" }, { name = "pytest" }, { name = "pytest-benchmark" }, @@ -606,6 +813,7 @@ pytket-integration = [ test = [ { name = "guppylang", extra = ["emulator"] }, { name = "ipykernel" }, + { name = "matplotlib" }, { name = "miette-py" }, { name = "pytest" }, { name = "pytest-benchmark" }, @@ -633,6 +841,7 @@ provides-extras = ["pytket", "emulator"] dev = [ { name = "guppylang", extras = ["emulator"] }, { name = "ipykernel", specifier = ">=6.29.5,<7" }, + { name = "matplotlib", specifier = ">=3.9.2" }, { name = "miette-py", editable = "miette-py" }, { name = "mypy", specifier = "==1.10.0" }, { name = "pre-commit", specifier = ">=3.6.0,<4" }, @@ -658,6 +867,7 @@ lint = [ pytket-integration = [ { name = "guppylang", extras = ["emulator"] }, { name = "ipykernel", specifier = ">=6.29.5,<7" }, + { name = "matplotlib", specifier = ">=3.9.2" }, { name = "miette-py", editable = "miette-py" }, { name = "pytest", specifier = ">=8.3.2,<9" }, { name = "pytest-benchmark", specifier = ">=5.1.0" }, @@ -671,6 +881,7 @@ pytket-integration = [ test = [ { name = "guppylang", extras = ["emulator"] }, { name = "ipykernel", specifier = ">=6.29.5,<7" }, + { name = "matplotlib", specifier = ">=3.9.2" }, { name = "miette-py", editable = "miette-py" }, { name = "pytest", specifier = ">=8.3.2,<9" }, { name = "pytest-benchmark", specifier = ">=5.1.0" }, @@ -1043,6 +1254,93 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b1/dd/ead9d8ea85bf202d90cc513b533f9c363121c7792674f78e0d8a854b63b4/jupyterlab_pygments-0.3.0-py3-none-any.whl", hash = "sha256:841a89020971da1d8693f1a99997aefc5dc424bb1b251fd6322462a1b8842780", size = 15884, upload-time = "2023-11-23T09:26:34.325Z" }, ] +[[package]] +name = "kiwisolver" +version = "1.4.8" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/82/59/7c91426a8ac292e1cdd53a63b6d9439abd573c875c3f92c146767dd33faf/kiwisolver-1.4.8.tar.gz", hash = "sha256:23d5f023bdc8c7e54eb65f03ca5d5bb25b601eac4d7f1a042888a1f45237987e", size = 97538, upload-time = "2024-12-24T18:30:51.519Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/47/5f/4d8e9e852d98ecd26cdf8eaf7ed8bc33174033bba5e07001b289f07308fd/kiwisolver-1.4.8-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:88c6f252f6816a73b1f8c904f7bbe02fd67c09a69f7cb8a0eecdbf5ce78e63db", size = 124623, upload-time = "2024-12-24T18:28:17.687Z" }, + { url = "https://files.pythonhosted.org/packages/1d/70/7f5af2a18a76fe92ea14675f8bd88ce53ee79e37900fa5f1a1d8e0b42998/kiwisolver-1.4.8-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c72941acb7b67138f35b879bbe85be0f6c6a70cab78fe3ef6db9c024d9223e5b", size = 66720, upload-time = "2024-12-24T18:28:19.158Z" }, + { url = "https://files.pythonhosted.org/packages/c6/13/e15f804a142353aefd089fadc8f1d985561a15358c97aca27b0979cb0785/kiwisolver-1.4.8-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ce2cf1e5688edcb727fdf7cd1bbd0b6416758996826a8be1d958f91880d0809d", size = 65413, upload-time = "2024-12-24T18:28:20.064Z" }, + { url = "https://files.pythonhosted.org/packages/ce/6d/67d36c4d2054e83fb875c6b59d0809d5c530de8148846b1370475eeeece9/kiwisolver-1.4.8-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:c8bf637892dc6e6aad2bc6d4d69d08764166e5e3f69d469e55427b6ac001b19d", size = 1650826, upload-time = "2024-12-24T18:28:21.203Z" }, + { url = "https://files.pythonhosted.org/packages/de/c6/7b9bb8044e150d4d1558423a1568e4f227193662a02231064e3824f37e0a/kiwisolver-1.4.8-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:034d2c891f76bd3edbdb3ea11140d8510dca675443da7304205a2eaa45d8334c", size = 1628231, upload-time = "2024-12-24T18:28:23.851Z" }, + { url = "https://files.pythonhosted.org/packages/b6/38/ad10d437563063eaaedbe2c3540a71101fc7fb07a7e71f855e93ea4de605/kiwisolver-1.4.8-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d47b28d1dfe0793d5e96bce90835e17edf9a499b53969b03c6c47ea5985844c3", size = 1408938, upload-time = "2024-12-24T18:28:26.687Z" }, + { url = "https://files.pythonhosted.org/packages/52/ce/c0106b3bd7f9e665c5f5bc1e07cc95b5dabd4e08e3dad42dbe2faad467e7/kiwisolver-1.4.8-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:eb158fe28ca0c29f2260cca8c43005329ad58452c36f0edf298204de32a9a3ed", size = 1422799, upload-time = "2024-12-24T18:28:30.538Z" }, + { url = "https://files.pythonhosted.org/packages/d0/87/efb704b1d75dc9758087ba374c0f23d3254505edaedd09cf9d247f7878b9/kiwisolver-1.4.8-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d5536185fce131780ebd809f8e623bf4030ce1b161353166c49a3c74c287897f", size = 1354362, upload-time = "2024-12-24T18:28:32.943Z" }, + { url = "https://files.pythonhosted.org/packages/eb/b3/fd760dc214ec9a8f208b99e42e8f0130ff4b384eca8b29dd0efc62052176/kiwisolver-1.4.8-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:369b75d40abedc1da2c1f4de13f3482cb99e3237b38726710f4a793432b1c5ff", size = 2222695, upload-time = "2024-12-24T18:28:35.641Z" }, + { url = "https://files.pythonhosted.org/packages/a2/09/a27fb36cca3fc01700687cc45dae7a6a5f8eeb5f657b9f710f788748e10d/kiwisolver-1.4.8-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:641f2ddf9358c80faa22e22eb4c9f54bd3f0e442e038728f500e3b978d00aa7d", size = 2370802, upload-time = "2024-12-24T18:28:38.357Z" }, + { url = "https://files.pythonhosted.org/packages/3d/c3/ba0a0346db35fe4dc1f2f2cf8b99362fbb922d7562e5f911f7ce7a7b60fa/kiwisolver-1.4.8-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:d561d2d8883e0819445cfe58d7ddd673e4015c3c57261d7bdcd3710d0d14005c", size = 2334646, upload-time = "2024-12-24T18:28:40.941Z" }, + { url = "https://files.pythonhosted.org/packages/41/52/942cf69e562f5ed253ac67d5c92a693745f0bed3c81f49fc0cbebe4d6b00/kiwisolver-1.4.8-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:1732e065704b47c9afca7ffa272f845300a4eb959276bf6970dc07265e73b605", size = 2467260, upload-time = "2024-12-24T18:28:42.273Z" }, + { url = "https://files.pythonhosted.org/packages/32/26/2d9668f30d8a494b0411d4d7d4ea1345ba12deb6a75274d58dd6ea01e951/kiwisolver-1.4.8-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:bcb1ebc3547619c3b58a39e2448af089ea2ef44b37988caf432447374941574e", size = 2288633, upload-time = "2024-12-24T18:28:44.87Z" }, + { url = "https://files.pythonhosted.org/packages/98/99/0dd05071654aa44fe5d5e350729961e7bb535372935a45ac89a8924316e6/kiwisolver-1.4.8-cp310-cp310-win_amd64.whl", hash = "sha256:89c107041f7b27844179ea9c85d6da275aa55ecf28413e87624d033cf1f6b751", size = 71885, upload-time = "2024-12-24T18:28:47.346Z" }, + { url = "https://files.pythonhosted.org/packages/6c/fc/822e532262a97442989335394d441cd1d0448c2e46d26d3e04efca84df22/kiwisolver-1.4.8-cp310-cp310-win_arm64.whl", hash = "sha256:b5773efa2be9eb9fcf5415ea3ab70fc785d598729fd6057bea38d539ead28271", size = 65175, upload-time = "2024-12-24T18:28:49.651Z" }, + { url = "https://files.pythonhosted.org/packages/da/ed/c913ee28936c371418cb167b128066ffb20bbf37771eecc2c97edf8a6e4c/kiwisolver-1.4.8-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a4d3601908c560bdf880f07d94f31d734afd1bb71e96585cace0e38ef44c6d84", size = 124635, upload-time = "2024-12-24T18:28:51.826Z" }, + { url = "https://files.pythonhosted.org/packages/4c/45/4a7f896f7467aaf5f56ef093d1f329346f3b594e77c6a3c327b2d415f521/kiwisolver-1.4.8-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:856b269c4d28a5c0d5e6c1955ec36ebfd1651ac00e1ce0afa3e28da95293b561", size = 66717, upload-time = "2024-12-24T18:28:54.256Z" }, + { url = "https://files.pythonhosted.org/packages/5f/b4/c12b3ac0852a3a68f94598d4c8d569f55361beef6159dce4e7b624160da2/kiwisolver-1.4.8-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c2b9a96e0f326205af81a15718a9073328df1173a2619a68553decb7097fd5d7", size = 65413, upload-time = "2024-12-24T18:28:55.184Z" }, + { url = "https://files.pythonhosted.org/packages/a9/98/1df4089b1ed23d83d410adfdc5947245c753bddfbe06541c4aae330e9e70/kiwisolver-1.4.8-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c5020c83e8553f770cb3b5fc13faac40f17e0b205bd237aebd21d53d733adb03", size = 1343994, upload-time = "2024-12-24T18:28:57.493Z" }, + { url = "https://files.pythonhosted.org/packages/8d/bf/b4b169b050c8421a7c53ea1ea74e4ef9c335ee9013216c558a047f162d20/kiwisolver-1.4.8-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dace81d28c787956bfbfbbfd72fdcef014f37d9b48830829e488fdb32b49d954", size = 1434804, upload-time = "2024-12-24T18:29:00.077Z" }, + { url = "https://files.pythonhosted.org/packages/66/5a/e13bd341fbcf73325ea60fdc8af752addf75c5079867af2e04cc41f34434/kiwisolver-1.4.8-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:11e1022b524bd48ae56c9b4f9296bce77e15a2e42a502cceba602f804b32bb79", size = 1450690, upload-time = "2024-12-24T18:29:01.401Z" }, + { url = "https://files.pythonhosted.org/packages/9b/4f/5955dcb376ba4a830384cc6fab7d7547bd6759fe75a09564910e9e3bb8ea/kiwisolver-1.4.8-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3b9b4d2892fefc886f30301cdd80debd8bb01ecdf165a449eb6e78f79f0fabd6", size = 1376839, upload-time = "2024-12-24T18:29:02.685Z" }, + { url = "https://files.pythonhosted.org/packages/3a/97/5edbed69a9d0caa2e4aa616ae7df8127e10f6586940aa683a496c2c280b9/kiwisolver-1.4.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a96c0e790ee875d65e340ab383700e2b4891677b7fcd30a699146f9384a2bb0", size = 1435109, upload-time = "2024-12-24T18:29:04.113Z" }, + { url = "https://files.pythonhosted.org/packages/13/fc/e756382cb64e556af6c1809a1bbb22c141bbc2445049f2da06b420fe52bf/kiwisolver-1.4.8-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:23454ff084b07ac54ca8be535f4174170c1094a4cff78fbae4f73a4bcc0d4dab", size = 2245269, upload-time = "2024-12-24T18:29:05.488Z" }, + { url = "https://files.pythonhosted.org/packages/76/15/e59e45829d7f41c776d138245cabae6515cb4eb44b418f6d4109c478b481/kiwisolver-1.4.8-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:87b287251ad6488e95b4f0b4a79a6d04d3ea35fde6340eb38fbd1ca9cd35bbbc", size = 2393468, upload-time = "2024-12-24T18:29:06.79Z" }, + { url = "https://files.pythonhosted.org/packages/e9/39/483558c2a913ab8384d6e4b66a932406f87c95a6080112433da5ed668559/kiwisolver-1.4.8-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:b21dbe165081142b1232a240fc6383fd32cdd877ca6cc89eab93e5f5883e1c25", size = 2355394, upload-time = "2024-12-24T18:29:08.24Z" }, + { url = "https://files.pythonhosted.org/packages/01/aa/efad1fbca6570a161d29224f14b082960c7e08268a133fe5dc0f6906820e/kiwisolver-1.4.8-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:768cade2c2df13db52475bd28d3a3fac8c9eff04b0e9e2fda0f3760f20b3f7fc", size = 2490901, upload-time = "2024-12-24T18:29:09.653Z" }, + { url = "https://files.pythonhosted.org/packages/c9/4f/15988966ba46bcd5ab9d0c8296914436720dd67fca689ae1a75b4ec1c72f/kiwisolver-1.4.8-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d47cfb2650f0e103d4bf68b0b5804c68da97272c84bb12850d877a95c056bd67", size = 2312306, upload-time = "2024-12-24T18:29:12.644Z" }, + { url = "https://files.pythonhosted.org/packages/2d/27/bdf1c769c83f74d98cbc34483a972f221440703054894a37d174fba8aa68/kiwisolver-1.4.8-cp311-cp311-win_amd64.whl", hash = "sha256:ed33ca2002a779a2e20eeb06aea7721b6e47f2d4b8a8ece979d8ba9e2a167e34", size = 71966, upload-time = "2024-12-24T18:29:14.089Z" }, + { url = "https://files.pythonhosted.org/packages/4a/c9/9642ea855604aeb2968a8e145fc662edf61db7632ad2e4fb92424be6b6c0/kiwisolver-1.4.8-cp311-cp311-win_arm64.whl", hash = "sha256:16523b40aab60426ffdebe33ac374457cf62863e330a90a0383639ce14bf44b2", size = 65311, upload-time = "2024-12-24T18:29:15.892Z" }, + { url = "https://files.pythonhosted.org/packages/fc/aa/cea685c4ab647f349c3bc92d2daf7ae34c8e8cf405a6dcd3a497f58a2ac3/kiwisolver-1.4.8-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:d6af5e8815fd02997cb6ad9bbed0ee1e60014438ee1a5c2444c96f87b8843502", size = 124152, upload-time = "2024-12-24T18:29:16.85Z" }, + { url = "https://files.pythonhosted.org/packages/c5/0b/8db6d2e2452d60d5ebc4ce4b204feeb16176a851fd42462f66ade6808084/kiwisolver-1.4.8-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:bade438f86e21d91e0cf5dd7c0ed00cda0f77c8c1616bd83f9fc157fa6760d31", size = 66555, upload-time = "2024-12-24T18:29:19.146Z" }, + { url = "https://files.pythonhosted.org/packages/60/26/d6a0db6785dd35d3ba5bf2b2df0aedc5af089962c6eb2cbf67a15b81369e/kiwisolver-1.4.8-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b83dc6769ddbc57613280118fb4ce3cd08899cc3369f7d0e0fab518a7cf37fdb", size = 65067, upload-time = "2024-12-24T18:29:20.096Z" }, + { url = "https://files.pythonhosted.org/packages/c9/ed/1d97f7e3561e09757a196231edccc1bcf59d55ddccefa2afc9c615abd8e0/kiwisolver-1.4.8-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:111793b232842991be367ed828076b03d96202c19221b5ebab421ce8bcad016f", size = 1378443, upload-time = "2024-12-24T18:29:22.843Z" }, + { url = "https://files.pythonhosted.org/packages/29/61/39d30b99954e6b46f760e6289c12fede2ab96a254c443639052d1b573fbc/kiwisolver-1.4.8-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:257af1622860e51b1a9d0ce387bf5c2c4f36a90594cb9514f55b074bcc787cfc", size = 1472728, upload-time = "2024-12-24T18:29:24.463Z" }, + { url = "https://files.pythonhosted.org/packages/0c/3e/804163b932f7603ef256e4a715e5843a9600802bb23a68b4e08c8c0ff61d/kiwisolver-1.4.8-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:69b5637c3f316cab1ec1c9a12b8c5f4750a4c4b71af9157645bf32830e39c03a", size = 1478388, upload-time = "2024-12-24T18:29:25.776Z" }, + { url = "https://files.pythonhosted.org/packages/8a/9e/60eaa75169a154700be74f875a4d9961b11ba048bef315fbe89cb6999056/kiwisolver-1.4.8-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:782bb86f245ec18009890e7cb8d13a5ef54dcf2ebe18ed65f795e635a96a1c6a", size = 1413849, upload-time = "2024-12-24T18:29:27.202Z" }, + { url = "https://files.pythonhosted.org/packages/bc/b3/9458adb9472e61a998c8c4d95cfdfec91c73c53a375b30b1428310f923e4/kiwisolver-1.4.8-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc978a80a0db3a66d25767b03688f1147a69e6237175c0f4ffffaaedf744055a", size = 1475533, upload-time = "2024-12-24T18:29:28.638Z" }, + { url = "https://files.pythonhosted.org/packages/e4/7a/0a42d9571e35798de80aef4bb43a9b672aa7f8e58643d7bd1950398ffb0a/kiwisolver-1.4.8-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:36dbbfd34838500a31f52c9786990d00150860e46cd5041386f217101350f0d3", size = 2268898, upload-time = "2024-12-24T18:29:30.368Z" }, + { url = "https://files.pythonhosted.org/packages/d9/07/1255dc8d80271400126ed8db35a1795b1a2c098ac3a72645075d06fe5c5d/kiwisolver-1.4.8-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:eaa973f1e05131de5ff3569bbba7f5fd07ea0595d3870ed4a526d486fe57fa1b", size = 2425605, upload-time = "2024-12-24T18:29:33.151Z" }, + { url = "https://files.pythonhosted.org/packages/84/df/5a3b4cf13780ef6f6942df67b138b03b7e79e9f1f08f57c49957d5867f6e/kiwisolver-1.4.8-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:a66f60f8d0c87ab7f59b6fb80e642ebb29fec354a4dfad687ca4092ae69d04f4", size = 2375801, upload-time = "2024-12-24T18:29:34.584Z" }, + { url = "https://files.pythonhosted.org/packages/8f/10/2348d068e8b0f635c8c86892788dac7a6b5c0cb12356620ab575775aad89/kiwisolver-1.4.8-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:858416b7fb777a53f0c59ca08190ce24e9abbd3cffa18886a5781b8e3e26f65d", size = 2520077, upload-time = "2024-12-24T18:29:36.138Z" }, + { url = "https://files.pythonhosted.org/packages/32/d8/014b89fee5d4dce157d814303b0fce4d31385a2af4c41fed194b173b81ac/kiwisolver-1.4.8-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:085940635c62697391baafaaeabdf3dd7a6c3643577dde337f4d66eba021b2b8", size = 2338410, upload-time = "2024-12-24T18:29:39.991Z" }, + { url = "https://files.pythonhosted.org/packages/bd/72/dfff0cc97f2a0776e1c9eb5bef1ddfd45f46246c6533b0191887a427bca5/kiwisolver-1.4.8-cp312-cp312-win_amd64.whl", hash = "sha256:01c3d31902c7db5fb6182832713d3b4122ad9317c2c5877d0539227d96bb2e50", size = 71853, upload-time = "2024-12-24T18:29:42.006Z" }, + { url = "https://files.pythonhosted.org/packages/dc/85/220d13d914485c0948a00f0b9eb419efaf6da81b7d72e88ce2391f7aed8d/kiwisolver-1.4.8-cp312-cp312-win_arm64.whl", hash = "sha256:a3c44cb68861de93f0c4a8175fbaa691f0aa22550c331fefef02b618a9dcb476", size = 65424, upload-time = "2024-12-24T18:29:44.38Z" }, + { url = "https://files.pythonhosted.org/packages/79/b3/e62464a652f4f8cd9006e13d07abad844a47df1e6537f73ddfbf1bc997ec/kiwisolver-1.4.8-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:1c8ceb754339793c24aee1c9fb2485b5b1f5bb1c2c214ff13368431e51fc9a09", size = 124156, upload-time = "2024-12-24T18:29:45.368Z" }, + { url = "https://files.pythonhosted.org/packages/8d/2d/f13d06998b546a2ad4f48607a146e045bbe48030774de29f90bdc573df15/kiwisolver-1.4.8-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:54a62808ac74b5e55a04a408cda6156f986cefbcf0ada13572696b507cc92fa1", size = 66555, upload-time = "2024-12-24T18:29:46.37Z" }, + { url = "https://files.pythonhosted.org/packages/59/e3/b8bd14b0a54998a9fd1e8da591c60998dc003618cb19a3f94cb233ec1511/kiwisolver-1.4.8-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:68269e60ee4929893aad82666821aaacbd455284124817af45c11e50a4b42e3c", size = 65071, upload-time = "2024-12-24T18:29:47.333Z" }, + { url = "https://files.pythonhosted.org/packages/f0/1c/6c86f6d85ffe4d0ce04228d976f00674f1df5dc893bf2dd4f1928748f187/kiwisolver-1.4.8-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:34d142fba9c464bc3bbfeff15c96eab0e7310343d6aefb62a79d51421fcc5f1b", size = 1378053, upload-time = "2024-12-24T18:29:49.636Z" }, + { url = "https://files.pythonhosted.org/packages/4e/b9/1c6e9f6dcb103ac5cf87cb695845f5fa71379021500153566d8a8a9fc291/kiwisolver-1.4.8-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ddc373e0eef45b59197de815b1b28ef89ae3955e7722cc9710fb91cd77b7f47", size = 1472278, upload-time = "2024-12-24T18:29:51.164Z" }, + { url = "https://files.pythonhosted.org/packages/ee/81/aca1eb176de671f8bda479b11acdc42c132b61a2ac861c883907dde6debb/kiwisolver-1.4.8-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:77e6f57a20b9bd4e1e2cedda4d0b986ebd0216236f0106e55c28aea3d3d69b16", size = 1478139, upload-time = "2024-12-24T18:29:52.594Z" }, + { url = "https://files.pythonhosted.org/packages/49/f4/e081522473671c97b2687d380e9e4c26f748a86363ce5af48b4a28e48d06/kiwisolver-1.4.8-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:08e77738ed7538f036cd1170cbed942ef749137b1311fa2bbe2a7fda2f6bf3cc", size = 1413517, upload-time = "2024-12-24T18:29:53.941Z" }, + { url = "https://files.pythonhosted.org/packages/8f/e9/6a7d025d8da8c4931522922cd706105aa32b3291d1add8c5427cdcd66e63/kiwisolver-1.4.8-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a5ce1e481a74b44dd5e92ff03ea0cb371ae7a0268318e202be06c8f04f4f1246", size = 1474952, upload-time = "2024-12-24T18:29:56.523Z" }, + { url = "https://files.pythonhosted.org/packages/82/13/13fa685ae167bee5d94b415991c4fc7bb0a1b6ebea6e753a87044b209678/kiwisolver-1.4.8-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:fc2ace710ba7c1dfd1a3b42530b62b9ceed115f19a1656adefce7b1782a37794", size = 2269132, upload-time = "2024-12-24T18:29:57.989Z" }, + { url = "https://files.pythonhosted.org/packages/ef/92/bb7c9395489b99a6cb41d502d3686bac692586db2045adc19e45ee64ed23/kiwisolver-1.4.8-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:3452046c37c7692bd52b0e752b87954ef86ee2224e624ef7ce6cb21e8c41cc1b", size = 2425997, upload-time = "2024-12-24T18:29:59.393Z" }, + { url = "https://files.pythonhosted.org/packages/ed/12/87f0e9271e2b63d35d0d8524954145837dd1a6c15b62a2d8c1ebe0f182b4/kiwisolver-1.4.8-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:7e9a60b50fe8b2ec6f448fe8d81b07e40141bfced7f896309df271a0b92f80f3", size = 2376060, upload-time = "2024-12-24T18:30:01.338Z" }, + { url = "https://files.pythonhosted.org/packages/02/6e/c8af39288edbce8bf0fa35dee427b082758a4b71e9c91ef18fa667782138/kiwisolver-1.4.8-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:918139571133f366e8362fa4a297aeba86c7816b7ecf0bc79168080e2bd79957", size = 2520471, upload-time = "2024-12-24T18:30:04.574Z" }, + { url = "https://files.pythonhosted.org/packages/13/78/df381bc7b26e535c91469f77f16adcd073beb3e2dd25042efd064af82323/kiwisolver-1.4.8-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e063ef9f89885a1d68dd8b2e18f5ead48653176d10a0e324e3b0030e3a69adeb", size = 2338793, upload-time = "2024-12-24T18:30:06.25Z" }, + { url = "https://files.pythonhosted.org/packages/d0/dc/c1abe38c37c071d0fc71c9a474fd0b9ede05d42f5a458d584619cfd2371a/kiwisolver-1.4.8-cp313-cp313-win_amd64.whl", hash = "sha256:a17b7c4f5b2c51bb68ed379defd608a03954a1845dfed7cc0117f1cc8a9b7fd2", size = 71855, upload-time = "2024-12-24T18:30:07.535Z" }, + { url = "https://files.pythonhosted.org/packages/a0/b6/21529d595b126ac298fdd90b705d87d4c5693de60023e0efcb4f387ed99e/kiwisolver-1.4.8-cp313-cp313-win_arm64.whl", hash = "sha256:3cd3bc628b25f74aedc6d374d5babf0166a92ff1317f46267f12d2ed54bc1d30", size = 65430, upload-time = "2024-12-24T18:30:08.504Z" }, + { url = "https://files.pythonhosted.org/packages/34/bd/b89380b7298e3af9b39f49334e3e2a4af0e04819789f04b43d560516c0c8/kiwisolver-1.4.8-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:370fd2df41660ed4e26b8c9d6bbcad668fbe2560462cba151a721d49e5b6628c", size = 126294, upload-time = "2024-12-24T18:30:09.508Z" }, + { url = "https://files.pythonhosted.org/packages/83/41/5857dc72e5e4148eaac5aa76e0703e594e4465f8ab7ec0fc60e3a9bb8fea/kiwisolver-1.4.8-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:84a2f830d42707de1d191b9490ac186bf7997a9495d4e9072210a1296345f7dc", size = 67736, upload-time = "2024-12-24T18:30:11.039Z" }, + { url = "https://files.pythonhosted.org/packages/e1/d1/be059b8db56ac270489fb0b3297fd1e53d195ba76e9bbb30e5401fa6b759/kiwisolver-1.4.8-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:7a3ad337add5148cf51ce0b55642dc551c0b9d6248458a757f98796ca7348712", size = 66194, upload-time = "2024-12-24T18:30:14.886Z" }, + { url = "https://files.pythonhosted.org/packages/e1/83/4b73975f149819eb7dcf9299ed467eba068ecb16439a98990dcb12e63fdd/kiwisolver-1.4.8-cp313-cp313t-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7506488470f41169b86d8c9aeff587293f530a23a23a49d6bc64dab66bedc71e", size = 1465942, upload-time = "2024-12-24T18:30:18.927Z" }, + { url = "https://files.pythonhosted.org/packages/c7/2c/30a5cdde5102958e602c07466bce058b9d7cb48734aa7a4327261ac8e002/kiwisolver-1.4.8-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f0121b07b356a22fb0414cec4666bbe36fd6d0d759db3d37228f496ed67c880", size = 1595341, upload-time = "2024-12-24T18:30:22.102Z" }, + { url = "https://files.pythonhosted.org/packages/ff/9b/1e71db1c000385aa069704f5990574b8244cce854ecd83119c19e83c9586/kiwisolver-1.4.8-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d6d6bd87df62c27d4185de7c511c6248040afae67028a8a22012b010bc7ad062", size = 1598455, upload-time = "2024-12-24T18:30:24.947Z" }, + { url = "https://files.pythonhosted.org/packages/85/92/c8fec52ddf06231b31cbb779af77e99b8253cd96bd135250b9498144c78b/kiwisolver-1.4.8-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:291331973c64bb9cce50bbe871fb2e675c4331dab4f31abe89f175ad7679a4d7", size = 1522138, upload-time = "2024-12-24T18:30:26.286Z" }, + { url = "https://files.pythonhosted.org/packages/0b/51/9eb7e2cd07a15d8bdd976f6190c0164f92ce1904e5c0c79198c4972926b7/kiwisolver-1.4.8-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:893f5525bb92d3d735878ec00f781b2de998333659507d29ea4466208df37bed", size = 1582857, upload-time = "2024-12-24T18:30:28.86Z" }, + { url = "https://files.pythonhosted.org/packages/0f/95/c5a00387a5405e68ba32cc64af65ce881a39b98d73cc394b24143bebc5b8/kiwisolver-1.4.8-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:b47a465040146981dc9db8647981b8cb96366fbc8d452b031e4f8fdffec3f26d", size = 2293129, upload-time = "2024-12-24T18:30:30.34Z" }, + { url = "https://files.pythonhosted.org/packages/44/83/eeb7af7d706b8347548313fa3a3a15931f404533cc54fe01f39e830dd231/kiwisolver-1.4.8-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:99cea8b9dd34ff80c521aef46a1dddb0dcc0283cf18bde6d756f1e6f31772165", size = 2421538, upload-time = "2024-12-24T18:30:33.334Z" }, + { url = "https://files.pythonhosted.org/packages/05/f9/27e94c1b3eb29e6933b6986ffc5fa1177d2cd1f0c8efc5f02c91c9ac61de/kiwisolver-1.4.8-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:151dffc4865e5fe6dafce5480fab84f950d14566c480c08a53c663a0020504b6", size = 2390661, upload-time = "2024-12-24T18:30:34.939Z" }, + { url = "https://files.pythonhosted.org/packages/d9/d4/3c9735faa36ac591a4afcc2980d2691000506050b7a7e80bcfe44048daa7/kiwisolver-1.4.8-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:577facaa411c10421314598b50413aa1ebcf5126f704f1e5d72d7e4e9f020d90", size = 2546710, upload-time = "2024-12-24T18:30:37.281Z" }, + { url = "https://files.pythonhosted.org/packages/4c/fa/be89a49c640930180657482a74970cdcf6f7072c8d2471e1babe17a222dc/kiwisolver-1.4.8-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:be4816dc51c8a471749d664161b434912eee82f2ea66bd7628bd14583a833e85", size = 2349213, upload-time = "2024-12-24T18:30:40.019Z" }, + { url = "https://files.pythonhosted.org/packages/1f/f9/ae81c47a43e33b93b0a9819cac6723257f5da2a5a60daf46aa5c7226ea85/kiwisolver-1.4.8-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:e7a019419b7b510f0f7c9dceff8c5eae2392037eae483a7f9162625233802b0a", size = 60403, upload-time = "2024-12-24T18:30:41.372Z" }, + { url = "https://files.pythonhosted.org/packages/58/ca/f92b5cb6f4ce0c1ebfcfe3e2e42b96917e16f7090e45b21102941924f18f/kiwisolver-1.4.8-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:286b18e86682fd2217a48fc6be6b0f20c1d0ed10958d8dc53453ad58d7be0bf8", size = 58657, upload-time = "2024-12-24T18:30:42.392Z" }, + { url = "https://files.pythonhosted.org/packages/80/28/ae0240f732f0484d3a4dc885d055653c47144bdf59b670aae0ec3c65a7c8/kiwisolver-1.4.8-pp310-pypy310_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4191ee8dfd0be1c3666ccbac178c5a05d5f8d689bbe3fc92f3c4abec817f8fe0", size = 84948, upload-time = "2024-12-24T18:30:44.703Z" }, + { url = "https://files.pythonhosted.org/packages/5d/eb/78d50346c51db22c7203c1611f9b513075f35c4e0e4877c5dde378d66043/kiwisolver-1.4.8-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7cd2785b9391f2873ad46088ed7599a6a71e762e1ea33e87514b1a441ed1da1c", size = 81186, upload-time = "2024-12-24T18:30:45.654Z" }, + { url = "https://files.pythonhosted.org/packages/43/f8/7259f18c77adca88d5f64f9a522792e178b2691f3748817a8750c2d216ef/kiwisolver-1.4.8-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c07b29089b7ba090b6f1a669f1411f27221c3662b3a1b7010e67b59bb5a6f10b", size = 80279, upload-time = "2024-12-24T18:30:47.951Z" }, + { url = "https://files.pythonhosted.org/packages/3a/1d/50ad811d1c5dae091e4cf046beba925bcae0a610e79ae4c538f996f63ed5/kiwisolver-1.4.8-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:65ea09a5a3faadd59c2ce96dc7bf0f364986a315949dc6374f04396b0d60e09b", size = 71762, upload-time = "2024-12-24T18:30:48.903Z" }, +] + [[package]] name = "lark" version = "1.2.2" @@ -1156,6 +1454,60 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/4f/65/6079a46068dfceaeabb5dcad6d674f5f5c61a6fa5673746f42a9f4c233b3/MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f", size = 15739, upload-time = "2024-10-18T15:21:42.784Z" }, ] +[[package]] +name = "matplotlib" +version = "3.10.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "contourpy", version = "1.3.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "contourpy", version = "1.3.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "cycler" }, + { name = "fonttools" }, + { name = "kiwisolver" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "numpy", version = "2.3.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "packaging" }, + { name = "pillow" }, + { name = "pyparsing" }, + { name = "python-dateutil" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/26/91/d49359a21893183ed2a5b6c76bec40e0b1dcbf8ca148f864d134897cfc75/matplotlib-3.10.3.tar.gz", hash = "sha256:2f82d2c5bb7ae93aaaa4cd42aca65d76ce6376f83304fa3a630b569aca274df0", size = 34799811, upload-time = "2025-05-08T19:10:54.39Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d0/ea/2bba25d289d389c7451f331ecd593944b3705f06ddf593fa7be75037d308/matplotlib-3.10.3-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:213fadd6348d106ca7db99e113f1bea1e65e383c3ba76e8556ba4a3054b65ae7", size = 8167862, upload-time = "2025-05-08T19:09:39.563Z" }, + { url = "https://files.pythonhosted.org/packages/41/81/cc70b5138c926604e8c9ed810ed4c79e8116ba72e02230852f5c12c87ba2/matplotlib-3.10.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d3bec61cb8221f0ca6313889308326e7bb303d0d302c5cc9e523b2f2e6c73deb", size = 8042149, upload-time = "2025-05-08T19:09:42.413Z" }, + { url = "https://files.pythonhosted.org/packages/4a/9a/0ff45b6bfa42bb16de597e6058edf2361c298ad5ef93b327728145161bbf/matplotlib-3.10.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c21ae75651c0231b3ba014b6d5e08fb969c40cdb5a011e33e99ed0c9ea86ecb", size = 8453719, upload-time = "2025-05-08T19:09:44.901Z" }, + { url = "https://files.pythonhosted.org/packages/85/c7/1866e972fed6d71ef136efbc980d4d1854ab7ef1ea8152bbd995ca231c81/matplotlib-3.10.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a49e39755580b08e30e3620efc659330eac5d6534ab7eae50fa5e31f53ee4e30", size = 8590801, upload-time = "2025-05-08T19:09:47.404Z" }, + { url = "https://files.pythonhosted.org/packages/5d/b9/748f6626d534ab7e255bdc39dc22634d337cf3ce200f261b5d65742044a1/matplotlib-3.10.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:cf4636203e1190871d3a73664dea03d26fb019b66692cbfd642faafdad6208e8", size = 9402111, upload-time = "2025-05-08T19:09:49.474Z" }, + { url = "https://files.pythonhosted.org/packages/1f/78/8bf07bd8fb67ea5665a6af188e70b57fcb2ab67057daa06b85a08e59160a/matplotlib-3.10.3-cp310-cp310-win_amd64.whl", hash = "sha256:fd5641a9bb9d55f4dd2afe897a53b537c834b9012684c8444cc105895c8c16fd", size = 8057213, upload-time = "2025-05-08T19:09:51.489Z" }, + { url = "https://files.pythonhosted.org/packages/f5/bd/af9f655456f60fe1d575f54fb14704ee299b16e999704817a7645dfce6b0/matplotlib-3.10.3-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:0ef061f74cd488586f552d0c336b2f078d43bc00dc473d2c3e7bfee2272f3fa8", size = 8178873, upload-time = "2025-05-08T19:09:53.857Z" }, + { url = "https://files.pythonhosted.org/packages/c2/86/e1c86690610661cd716eda5f9d0b35eaf606ae6c9b6736687cfc8f2d0cd8/matplotlib-3.10.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d96985d14dc5f4a736bbea4b9de9afaa735f8a0fc2ca75be2fa9e96b2097369d", size = 8052205, upload-time = "2025-05-08T19:09:55.684Z" }, + { url = "https://files.pythonhosted.org/packages/54/51/a9f8e49af3883dacddb2da1af5fca1f7468677f1188936452dd9aaaeb9ed/matplotlib-3.10.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7c5f0283da91e9522bdba4d6583ed9d5521566f63729ffb68334f86d0bb98049", size = 8465823, upload-time = "2025-05-08T19:09:57.442Z" }, + { url = "https://files.pythonhosted.org/packages/e7/e3/c82963a3b86d6e6d5874cbeaa390166458a7f1961bab9feb14d3d1a10f02/matplotlib-3.10.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fdfa07c0ec58035242bc8b2c8aae37037c9a886370eef6850703d7583e19964b", size = 8606464, upload-time = "2025-05-08T19:09:59.471Z" }, + { url = "https://files.pythonhosted.org/packages/0e/34/24da1027e7fcdd9e82da3194c470143c551852757a4b473a09a012f5b945/matplotlib-3.10.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c0b9849a17bce080a16ebcb80a7b714b5677d0ec32161a2cc0a8e5a6030ae220", size = 9413103, upload-time = "2025-05-08T19:10:03.208Z" }, + { url = "https://files.pythonhosted.org/packages/a6/da/948a017c3ea13fd4a97afad5fdebe2f5bbc4d28c0654510ce6fd6b06b7bd/matplotlib-3.10.3-cp311-cp311-win_amd64.whl", hash = "sha256:eef6ed6c03717083bc6d69c2d7ee8624205c29a8e6ea5a31cd3492ecdbaee1e1", size = 8065492, upload-time = "2025-05-08T19:10:05.271Z" }, + { url = "https://files.pythonhosted.org/packages/eb/43/6b80eb47d1071f234ef0c96ca370c2ca621f91c12045f1401b5c9b28a639/matplotlib-3.10.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:0ab1affc11d1f495ab9e6362b8174a25afc19c081ba5b0775ef00533a4236eea", size = 8179689, upload-time = "2025-05-08T19:10:07.602Z" }, + { url = "https://files.pythonhosted.org/packages/0f/70/d61a591958325c357204870b5e7b164f93f2a8cca1dc6ce940f563909a13/matplotlib-3.10.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2a818d8bdcafa7ed2eed74487fdb071c09c1ae24152d403952adad11fa3c65b4", size = 8050466, upload-time = "2025-05-08T19:10:09.383Z" }, + { url = "https://files.pythonhosted.org/packages/e7/75/70c9d2306203148cc7902a961240c5927dd8728afedf35e6a77e105a2985/matplotlib-3.10.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:748ebc3470c253e770b17d8b0557f0aa85cf8c63fd52f1a61af5b27ec0b7ffee", size = 8456252, upload-time = "2025-05-08T19:10:11.958Z" }, + { url = "https://files.pythonhosted.org/packages/c4/91/ba0ae1ff4b3f30972ad01cd4a8029e70a0ec3b8ea5be04764b128b66f763/matplotlib-3.10.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ed70453fd99733293ace1aec568255bc51c6361cb0da94fa5ebf0649fdb2150a", size = 8601321, upload-time = "2025-05-08T19:10:14.47Z" }, + { url = "https://files.pythonhosted.org/packages/d2/88/d636041eb54a84b889e11872d91f7cbf036b3b0e194a70fa064eb8b04f7a/matplotlib-3.10.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dbed9917b44070e55640bd13419de83b4c918e52d97561544814ba463811cbc7", size = 9406972, upload-time = "2025-05-08T19:10:16.569Z" }, + { url = "https://files.pythonhosted.org/packages/b1/79/0d1c165eac44405a86478082e225fce87874f7198300bbebc55faaf6d28d/matplotlib-3.10.3-cp312-cp312-win_amd64.whl", hash = "sha256:cf37d8c6ef1a48829443e8ba5227b44236d7fcaf7647caa3178a4ff9f7a5be05", size = 8067954, upload-time = "2025-05-08T19:10:18.663Z" }, + { url = "https://files.pythonhosted.org/packages/3b/c1/23cfb566a74c696a3b338d8955c549900d18fe2b898b6e94d682ca21e7c2/matplotlib-3.10.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:9f2efccc8dcf2b86fc4ee849eea5dcaecedd0773b30f47980dc0cbeabf26ec84", size = 8180318, upload-time = "2025-05-08T19:10:20.426Z" }, + { url = "https://files.pythonhosted.org/packages/6c/0c/02f1c3b66b30da9ee343c343acbb6251bef5b01d34fad732446eaadcd108/matplotlib-3.10.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3ddbba06a6c126e3301c3d272a99dcbe7f6c24c14024e80307ff03791a5f294e", size = 8051132, upload-time = "2025-05-08T19:10:22.569Z" }, + { url = "https://files.pythonhosted.org/packages/b4/ab/8db1a5ac9b3a7352fb914133001dae889f9fcecb3146541be46bed41339c/matplotlib-3.10.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:748302b33ae9326995b238f606e9ed840bf5886ebafcb233775d946aa8107a15", size = 8457633, upload-time = "2025-05-08T19:10:24.749Z" }, + { url = "https://files.pythonhosted.org/packages/f5/64/41c4367bcaecbc03ef0d2a3ecee58a7065d0a36ae1aa817fe573a2da66d4/matplotlib-3.10.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a80fcccbef63302c0efd78042ea3c2436104c5b1a4d3ae20f864593696364ac7", size = 8601031, upload-time = "2025-05-08T19:10:27.03Z" }, + { url = "https://files.pythonhosted.org/packages/12/6f/6cc79e9e5ab89d13ed64da28898e40fe5b105a9ab9c98f83abd24e46d7d7/matplotlib-3.10.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:55e46cbfe1f8586adb34f7587c3e4f7dedc59d5226719faf6cb54fc24f2fd52d", size = 9406988, upload-time = "2025-05-08T19:10:29.056Z" }, + { url = "https://files.pythonhosted.org/packages/b1/0f/eed564407bd4d935ffabf561ed31099ed609e19287409a27b6d336848653/matplotlib-3.10.3-cp313-cp313-win_amd64.whl", hash = "sha256:151d89cb8d33cb23345cd12490c76fd5d18a56581a16d950b48c6ff19bb2ab93", size = 8068034, upload-time = "2025-05-08T19:10:31.221Z" }, + { url = "https://files.pythonhosted.org/packages/3e/e5/2f14791ff69b12b09e9975e1d116d9578ac684460860ce542c2588cb7a1c/matplotlib-3.10.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:c26dd9834e74d164d06433dc7be5d75a1e9890b926b3e57e74fa446e1a62c3e2", size = 8218223, upload-time = "2025-05-08T19:10:33.114Z" }, + { url = "https://files.pythonhosted.org/packages/5c/08/30a94afd828b6e02d0a52cae4a29d6e9ccfcf4c8b56cc28b021d3588873e/matplotlib-3.10.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:24853dad5b8c84c8c2390fc31ce4858b6df504156893292ce8092d190ef8151d", size = 8094985, upload-time = "2025-05-08T19:10:35.337Z" }, + { url = "https://files.pythonhosted.org/packages/89/44/f3bc6b53066c889d7a1a3ea8094c13af6a667c5ca6220ec60ecceec2dabe/matplotlib-3.10.3-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68f7878214d369d7d4215e2a9075fef743be38fa401d32e6020bab2dfabaa566", size = 8483109, upload-time = "2025-05-08T19:10:37.611Z" }, + { url = "https://files.pythonhosted.org/packages/ba/c7/473bc559beec08ebee9f86ca77a844b65747e1a6c2691e8c92e40b9f42a8/matplotlib-3.10.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6929fc618cb6db9cb75086f73b3219bbb25920cb24cee2ea7a12b04971a4158", size = 8618082, upload-time = "2025-05-08T19:10:39.892Z" }, + { url = "https://files.pythonhosted.org/packages/d8/e9/6ce8edd264c8819e37bbed8172e0ccdc7107fe86999b76ab5752276357a4/matplotlib-3.10.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6c7818292a5cc372a2dc4c795e5c356942eb8350b98ef913f7fda51fe175ac5d", size = 9413699, upload-time = "2025-05-08T19:10:42.376Z" }, + { url = "https://files.pythonhosted.org/packages/1b/92/9a45c91089c3cf690b5badd4be81e392ff086ccca8a1d4e3a08463d8a966/matplotlib-3.10.3-cp313-cp313t-win_amd64.whl", hash = "sha256:4f23ffe95c5667ef8a2b56eea9b53db7f43910fa4a2d5472ae0f72b64deab4d5", size = 8139044, upload-time = "2025-05-08T19:10:44.551Z" }, + { url = "https://files.pythonhosted.org/packages/3d/d1/f54d43e95384b312ffa4a74a4326c722f3b8187aaaa12e9a84cdf3037131/matplotlib-3.10.3-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:86ab63d66bbc83fdb6733471d3bff40897c1e9921cba112accd748eee4bce5e4", size = 8162896, upload-time = "2025-05-08T19:10:46.432Z" }, + { url = "https://files.pythonhosted.org/packages/24/a4/fbfc00c2346177c95b353dcf9b5a004106abe8730a62cb6f27e79df0a698/matplotlib-3.10.3-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:a48f9c08bf7444b5d2391a83e75edb464ccda3c380384b36532a0962593a1751", size = 8039702, upload-time = "2025-05-08T19:10:49.634Z" }, + { url = "https://files.pythonhosted.org/packages/6a/b9/59e120d24a2ec5fc2d30646adb2efb4621aab3c6d83d66fb2a7a182db032/matplotlib-3.10.3-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cb73d8aa75a237457988f9765e4dfe1c0d2453c5ca4eabc897d4309672c8e014", size = 8594298, upload-time = "2025-05-08T19:10:51.738Z" }, +] + [[package]] name = "matplotlib-inline" version = "0.1.7" @@ -1522,6 +1874,108 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/9e/c3/059298687310d527a58bb01f3b1965787ee3b40dce76752eda8b44e9a2c5/pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523", size = 63772, upload-time = "2023-11-25T06:56:14.81Z" }, ] +[[package]] +name = "pillow" +version = "11.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f3/0d/d0d6dea55cd152ce3d6767bb38a8fc10e33796ba4ba210cbab9354b6d238/pillow-11.3.0.tar.gz", hash = "sha256:3828ee7586cd0b2091b6209e5ad53e20d0649bbe87164a459d0676e035e8f523", size = 47113069, upload-time = "2025-07-01T09:16:30.666Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4c/5d/45a3553a253ac8763f3561371432a90bdbe6000fbdcf1397ffe502aa206c/pillow-11.3.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:1b9c17fd4ace828b3003dfd1e30bff24863e0eb59b535e8f80194d9cc7ecf860", size = 5316554, upload-time = "2025-07-01T09:13:39.342Z" }, + { url = "https://files.pythonhosted.org/packages/7c/c8/67c12ab069ef586a25a4a79ced553586748fad100c77c0ce59bb4983ac98/pillow-11.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:65dc69160114cdd0ca0f35cb434633c75e8e7fad4cf855177a05bf38678f73ad", size = 4686548, upload-time = "2025-07-01T09:13:41.835Z" }, + { url = "https://files.pythonhosted.org/packages/2f/bd/6741ebd56263390b382ae4c5de02979af7f8bd9807346d068700dd6d5cf9/pillow-11.3.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7107195ddc914f656c7fc8e4a5e1c25f32e9236ea3ea860f257b0436011fddd0", size = 5859742, upload-time = "2025-07-03T13:09:47.439Z" }, + { url = "https://files.pythonhosted.org/packages/ca/0b/c412a9e27e1e6a829e6ab6c2dca52dd563efbedf4c9c6aa453d9a9b77359/pillow-11.3.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:cc3e831b563b3114baac7ec2ee86819eb03caa1a2cef0b481a5675b59c4fe23b", size = 7633087, upload-time = "2025-07-03T13:09:51.796Z" }, + { url = "https://files.pythonhosted.org/packages/59/9d/9b7076aaf30f5dd17e5e5589b2d2f5a5d7e30ff67a171eb686e4eecc2adf/pillow-11.3.0-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f1f182ebd2303acf8c380a54f615ec883322593320a9b00438eb842c1f37ae50", size = 5963350, upload-time = "2025-07-01T09:13:43.865Z" }, + { url = "https://files.pythonhosted.org/packages/f0/16/1a6bf01fb622fb9cf5c91683823f073f053005c849b1f52ed613afcf8dae/pillow-11.3.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4445fa62e15936a028672fd48c4c11a66d641d2c05726c7ec1f8ba6a572036ae", size = 6631840, upload-time = "2025-07-01T09:13:46.161Z" }, + { url = "https://files.pythonhosted.org/packages/7b/e6/6ff7077077eb47fde78739e7d570bdcd7c10495666b6afcd23ab56b19a43/pillow-11.3.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:71f511f6b3b91dd543282477be45a033e4845a40278fa8dcdbfdb07109bf18f9", size = 6074005, upload-time = "2025-07-01T09:13:47.829Z" }, + { url = "https://files.pythonhosted.org/packages/c3/3a/b13f36832ea6d279a697231658199e0a03cd87ef12048016bdcc84131601/pillow-11.3.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:040a5b691b0713e1f6cbe222e0f4f74cd233421e105850ae3b3c0ceda520f42e", size = 6708372, upload-time = "2025-07-01T09:13:52.145Z" }, + { url = "https://files.pythonhosted.org/packages/6c/e4/61b2e1a7528740efbc70b3d581f33937e38e98ef3d50b05007267a55bcb2/pillow-11.3.0-cp310-cp310-win32.whl", hash = "sha256:89bd777bc6624fe4115e9fac3352c79ed60f3bb18651420635f26e643e3dd1f6", size = 6277090, upload-time = "2025-07-01T09:13:53.915Z" }, + { url = "https://files.pythonhosted.org/packages/a9/d3/60c781c83a785d6afbd6a326ed4d759d141de43aa7365725cbcd65ce5e54/pillow-11.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:19d2ff547c75b8e3ff46f4d9ef969a06c30ab2d4263a9e287733aa8b2429ce8f", size = 6985988, upload-time = "2025-07-01T09:13:55.699Z" }, + { url = "https://files.pythonhosted.org/packages/9f/28/4f4a0203165eefb3763939c6789ba31013a2e90adffb456610f30f613850/pillow-11.3.0-cp310-cp310-win_arm64.whl", hash = "sha256:819931d25e57b513242859ce1876c58c59dc31587847bf74cfe06b2e0cb22d2f", size = 2422899, upload-time = "2025-07-01T09:13:57.497Z" }, + { url = "https://files.pythonhosted.org/packages/db/26/77f8ed17ca4ffd60e1dcd220a6ec6d71210ba398cfa33a13a1cd614c5613/pillow-11.3.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:1cd110edf822773368b396281a2293aeb91c90a2db00d78ea43e7e861631b722", size = 5316531, upload-time = "2025-07-01T09:13:59.203Z" }, + { url = "https://files.pythonhosted.org/packages/cb/39/ee475903197ce709322a17a866892efb560f57900d9af2e55f86db51b0a5/pillow-11.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9c412fddd1b77a75aa904615ebaa6001f169b26fd467b4be93aded278266b288", size = 4686560, upload-time = "2025-07-01T09:14:01.101Z" }, + { url = "https://files.pythonhosted.org/packages/d5/90/442068a160fd179938ba55ec8c97050a612426fae5ec0a764e345839f76d/pillow-11.3.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7d1aa4de119a0ecac0a34a9c8bde33f34022e2e8f99104e47a3ca392fd60e37d", size = 5870978, upload-time = "2025-07-03T13:09:55.638Z" }, + { url = "https://files.pythonhosted.org/packages/13/92/dcdd147ab02daf405387f0218dcf792dc6dd5b14d2573d40b4caeef01059/pillow-11.3.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:91da1d88226663594e3f6b4b8c3c8d85bd504117d043740a8e0ec449087cc494", size = 7641168, upload-time = "2025-07-03T13:10:00.37Z" }, + { url = "https://files.pythonhosted.org/packages/6e/db/839d6ba7fd38b51af641aa904e2960e7a5644d60ec754c046b7d2aee00e5/pillow-11.3.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:643f189248837533073c405ec2f0bb250ba54598cf80e8c1e043381a60632f58", size = 5973053, upload-time = "2025-07-01T09:14:04.491Z" }, + { url = "https://files.pythonhosted.org/packages/f2/2f/d7675ecae6c43e9f12aa8d58b6012683b20b6edfbdac7abcb4e6af7a3784/pillow-11.3.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:106064daa23a745510dabce1d84f29137a37224831d88eb4ce94bb187b1d7e5f", size = 6640273, upload-time = "2025-07-01T09:14:06.235Z" }, + { url = "https://files.pythonhosted.org/packages/45/ad/931694675ede172e15b2ff03c8144a0ddaea1d87adb72bb07655eaffb654/pillow-11.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:cd8ff254faf15591e724dc7c4ddb6bf4793efcbe13802a4ae3e863cd300b493e", size = 6082043, upload-time = "2025-07-01T09:14:07.978Z" }, + { url = "https://files.pythonhosted.org/packages/3a/04/ba8f2b11fc80d2dd462d7abec16351b45ec99cbbaea4387648a44190351a/pillow-11.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:932c754c2d51ad2b2271fd01c3d121daaa35e27efae2a616f77bf164bc0b3e94", size = 6715516, upload-time = "2025-07-01T09:14:10.233Z" }, + { url = "https://files.pythonhosted.org/packages/48/59/8cd06d7f3944cc7d892e8533c56b0acb68399f640786313275faec1e3b6f/pillow-11.3.0-cp311-cp311-win32.whl", hash = "sha256:b4b8f3efc8d530a1544e5962bd6b403d5f7fe8b9e08227c6b255f98ad82b4ba0", size = 6274768, upload-time = "2025-07-01T09:14:11.921Z" }, + { url = "https://files.pythonhosted.org/packages/f1/cc/29c0f5d64ab8eae20f3232da8f8571660aa0ab4b8f1331da5c2f5f9a938e/pillow-11.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:1a992e86b0dd7aeb1f053cd506508c0999d710a8f07b4c791c63843fc6a807ac", size = 6986055, upload-time = "2025-07-01T09:14:13.623Z" }, + { url = "https://files.pythonhosted.org/packages/c6/df/90bd886fabd544c25addd63e5ca6932c86f2b701d5da6c7839387a076b4a/pillow-11.3.0-cp311-cp311-win_arm64.whl", hash = "sha256:30807c931ff7c095620fe04448e2c2fc673fcbb1ffe2a7da3fb39613489b1ddd", size = 2423079, upload-time = "2025-07-01T09:14:15.268Z" }, + { url = "https://files.pythonhosted.org/packages/40/fe/1bc9b3ee13f68487a99ac9529968035cca2f0a51ec36892060edcc51d06a/pillow-11.3.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fdae223722da47b024b867c1ea0be64e0df702c5e0a60e27daad39bf960dd1e4", size = 5278800, upload-time = "2025-07-01T09:14:17.648Z" }, + { url = "https://files.pythonhosted.org/packages/2c/32/7e2ac19b5713657384cec55f89065fb306b06af008cfd87e572035b27119/pillow-11.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:921bd305b10e82b4d1f5e802b6850677f965d8394203d182f078873851dada69", size = 4686296, upload-time = "2025-07-01T09:14:19.828Z" }, + { url = "https://files.pythonhosted.org/packages/8e/1e/b9e12bbe6e4c2220effebc09ea0923a07a6da1e1f1bfbc8d7d29a01ce32b/pillow-11.3.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:eb76541cba2f958032d79d143b98a3a6b3ea87f0959bbe256c0b5e416599fd5d", size = 5871726, upload-time = "2025-07-03T13:10:04.448Z" }, + { url = "https://files.pythonhosted.org/packages/8d/33/e9200d2bd7ba00dc3ddb78df1198a6e80d7669cce6c2bdbeb2530a74ec58/pillow-11.3.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:67172f2944ebba3d4a7b54f2e95c786a3a50c21b88456329314caaa28cda70f6", size = 7644652, upload-time = "2025-07-03T13:10:10.391Z" }, + { url = "https://files.pythonhosted.org/packages/41/f1/6f2427a26fc683e00d985bc391bdd76d8dd4e92fac33d841127eb8fb2313/pillow-11.3.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:97f07ed9f56a3b9b5f49d3661dc9607484e85c67e27f3e8be2c7d28ca032fec7", size = 5977787, upload-time = "2025-07-01T09:14:21.63Z" }, + { url = "https://files.pythonhosted.org/packages/e4/c9/06dd4a38974e24f932ff5f98ea3c546ce3f8c995d3f0985f8e5ba48bba19/pillow-11.3.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:676b2815362456b5b3216b4fd5bd89d362100dc6f4945154ff172e206a22c024", size = 6645236, upload-time = "2025-07-01T09:14:23.321Z" }, + { url = "https://files.pythonhosted.org/packages/40/e7/848f69fb79843b3d91241bad658e9c14f39a32f71a301bcd1d139416d1be/pillow-11.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3e184b2f26ff146363dd07bde8b711833d7b0202e27d13540bfe2e35a323a809", size = 6086950, upload-time = "2025-07-01T09:14:25.237Z" }, + { url = "https://files.pythonhosted.org/packages/0b/1a/7cff92e695a2a29ac1958c2a0fe4c0b2393b60aac13b04a4fe2735cad52d/pillow-11.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6be31e3fc9a621e071bc17bb7de63b85cbe0bfae91bb0363c893cbe67247780d", size = 6723358, upload-time = "2025-07-01T09:14:27.053Z" }, + { url = "https://files.pythonhosted.org/packages/26/7d/73699ad77895f69edff76b0f332acc3d497f22f5d75e5360f78cbcaff248/pillow-11.3.0-cp312-cp312-win32.whl", hash = "sha256:7b161756381f0918e05e7cb8a371fff367e807770f8fe92ecb20d905d0e1c149", size = 6275079, upload-time = "2025-07-01T09:14:30.104Z" }, + { url = "https://files.pythonhosted.org/packages/8c/ce/e7dfc873bdd9828f3b6e5c2bbb74e47a98ec23cc5c74fc4e54462f0d9204/pillow-11.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:a6444696fce635783440b7f7a9fc24b3ad10a9ea3f0ab66c5905be1c19ccf17d", size = 6986324, upload-time = "2025-07-01T09:14:31.899Z" }, + { url = "https://files.pythonhosted.org/packages/16/8f/b13447d1bf0b1f7467ce7d86f6e6edf66c0ad7cf44cf5c87a37f9bed9936/pillow-11.3.0-cp312-cp312-win_arm64.whl", hash = "sha256:2aceea54f957dd4448264f9bf40875da0415c83eb85f55069d89c0ed436e3542", size = 2423067, upload-time = "2025-07-01T09:14:33.709Z" }, + { url = "https://files.pythonhosted.org/packages/1e/93/0952f2ed8db3a5a4c7a11f91965d6184ebc8cd7cbb7941a260d5f018cd2d/pillow-11.3.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:1c627742b539bba4309df89171356fcb3cc5a9178355b2727d1b74a6cf155fbd", size = 2128328, upload-time = "2025-07-01T09:14:35.276Z" }, + { url = "https://files.pythonhosted.org/packages/4b/e8/100c3d114b1a0bf4042f27e0f87d2f25e857e838034e98ca98fe7b8c0a9c/pillow-11.3.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:30b7c02f3899d10f13d7a48163c8969e4e653f8b43416d23d13d1bbfdc93b9f8", size = 2170652, upload-time = "2025-07-01T09:14:37.203Z" }, + { url = "https://files.pythonhosted.org/packages/aa/86/3f758a28a6e381758545f7cdb4942e1cb79abd271bea932998fc0db93cb6/pillow-11.3.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:7859a4cc7c9295f5838015d8cc0a9c215b77e43d07a25e460f35cf516df8626f", size = 2227443, upload-time = "2025-07-01T09:14:39.344Z" }, + { url = "https://files.pythonhosted.org/packages/01/f4/91d5b3ffa718df2f53b0dc109877993e511f4fd055d7e9508682e8aba092/pillow-11.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ec1ee50470b0d050984394423d96325b744d55c701a439d2bd66089bff963d3c", size = 5278474, upload-time = "2025-07-01T09:14:41.843Z" }, + { url = "https://files.pythonhosted.org/packages/f9/0e/37d7d3eca6c879fbd9dba21268427dffda1ab00d4eb05b32923d4fbe3b12/pillow-11.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7db51d222548ccfd274e4572fdbf3e810a5e66b00608862f947b163e613b67dd", size = 4686038, upload-time = "2025-07-01T09:14:44.008Z" }, + { url = "https://files.pythonhosted.org/packages/ff/b0/3426e5c7f6565e752d81221af9d3676fdbb4f352317ceafd42899aaf5d8a/pillow-11.3.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2d6fcc902a24ac74495df63faad1884282239265c6839a0a6416d33faedfae7e", size = 5864407, upload-time = "2025-07-03T13:10:15.628Z" }, + { url = "https://files.pythonhosted.org/packages/fc/c1/c6c423134229f2a221ee53f838d4be9d82bab86f7e2f8e75e47b6bf6cd77/pillow-11.3.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f0f5d8f4a08090c6d6d578351a2b91acf519a54986c055af27e7a93feae6d3f1", size = 7639094, upload-time = "2025-07-03T13:10:21.857Z" }, + { url = "https://files.pythonhosted.org/packages/ba/c9/09e6746630fe6372c67c648ff9deae52a2bc20897d51fa293571977ceb5d/pillow-11.3.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c37d8ba9411d6003bba9e518db0db0c58a680ab9fe5179f040b0463644bc9805", size = 5973503, upload-time = "2025-07-01T09:14:45.698Z" }, + { url = "https://files.pythonhosted.org/packages/d5/1c/a2a29649c0b1983d3ef57ee87a66487fdeb45132df66ab30dd37f7dbe162/pillow-11.3.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:13f87d581e71d9189ab21fe0efb5a23e9f28552d5be6979e84001d3b8505abe8", size = 6642574, upload-time = "2025-07-01T09:14:47.415Z" }, + { url = "https://files.pythonhosted.org/packages/36/de/d5cc31cc4b055b6c6fd990e3e7f0f8aaf36229a2698501bcb0cdf67c7146/pillow-11.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:023f6d2d11784a465f09fd09a34b150ea4672e85fb3d05931d89f373ab14abb2", size = 6084060, upload-time = "2025-07-01T09:14:49.636Z" }, + { url = "https://files.pythonhosted.org/packages/d5/ea/502d938cbaeec836ac28a9b730193716f0114c41325db428e6b280513f09/pillow-11.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:45dfc51ac5975b938e9809451c51734124e73b04d0f0ac621649821a63852e7b", size = 6721407, upload-time = "2025-07-01T09:14:51.962Z" }, + { url = "https://files.pythonhosted.org/packages/45/9c/9c5e2a73f125f6cbc59cc7087c8f2d649a7ae453f83bd0362ff7c9e2aee2/pillow-11.3.0-cp313-cp313-win32.whl", hash = "sha256:a4d336baed65d50d37b88ca5b60c0fa9d81e3a87d4a7930d3880d1624d5b31f3", size = 6273841, upload-time = "2025-07-01T09:14:54.142Z" }, + { url = "https://files.pythonhosted.org/packages/23/85/397c73524e0cd212067e0c969aa245b01d50183439550d24d9f55781b776/pillow-11.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:0bce5c4fd0921f99d2e858dc4d4d64193407e1b99478bc5cacecba2311abde51", size = 6978450, upload-time = "2025-07-01T09:14:56.436Z" }, + { url = "https://files.pythonhosted.org/packages/17/d2/622f4547f69cd173955194b78e4d19ca4935a1b0f03a302d655c9f6aae65/pillow-11.3.0-cp313-cp313-win_arm64.whl", hash = "sha256:1904e1264881f682f02b7f8167935cce37bc97db457f8e7849dc3a6a52b99580", size = 2423055, upload-time = "2025-07-01T09:14:58.072Z" }, + { url = "https://files.pythonhosted.org/packages/dd/80/a8a2ac21dda2e82480852978416cfacd439a4b490a501a288ecf4fe2532d/pillow-11.3.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:4c834a3921375c48ee6b9624061076bc0a32a60b5532b322cc0ea64e639dd50e", size = 5281110, upload-time = "2025-07-01T09:14:59.79Z" }, + { url = "https://files.pythonhosted.org/packages/44/d6/b79754ca790f315918732e18f82a8146d33bcd7f4494380457ea89eb883d/pillow-11.3.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:5e05688ccef30ea69b9317a9ead994b93975104a677a36a8ed8106be9260aa6d", size = 4689547, upload-time = "2025-07-01T09:15:01.648Z" }, + { url = "https://files.pythonhosted.org/packages/49/20/716b8717d331150cb00f7fdd78169c01e8e0c219732a78b0e59b6bdb2fd6/pillow-11.3.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:1019b04af07fc0163e2810167918cb5add8d74674b6267616021ab558dc98ced", size = 5901554, upload-time = "2025-07-03T13:10:27.018Z" }, + { url = "https://files.pythonhosted.org/packages/74/cf/a9f3a2514a65bb071075063a96f0a5cf949c2f2fce683c15ccc83b1c1cab/pillow-11.3.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f944255db153ebb2b19c51fe85dd99ef0ce494123f21b9db4877ffdfc5590c7c", size = 7669132, upload-time = "2025-07-03T13:10:33.01Z" }, + { url = "https://files.pythonhosted.org/packages/98/3c/da78805cbdbee9cb43efe8261dd7cc0b4b93f2ac79b676c03159e9db2187/pillow-11.3.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1f85acb69adf2aaee8b7da124efebbdb959a104db34d3a2cb0f3793dbae422a8", size = 6005001, upload-time = "2025-07-01T09:15:03.365Z" }, + { url = "https://files.pythonhosted.org/packages/6c/fa/ce044b91faecf30e635321351bba32bab5a7e034c60187fe9698191aef4f/pillow-11.3.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:05f6ecbeff5005399bb48d198f098a9b4b6bdf27b8487c7f38ca16eeb070cd59", size = 6668814, upload-time = "2025-07-01T09:15:05.655Z" }, + { url = "https://files.pythonhosted.org/packages/7b/51/90f9291406d09bf93686434f9183aba27b831c10c87746ff49f127ee80cb/pillow-11.3.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a7bc6e6fd0395bc052f16b1a8670859964dbd7003bd0af2ff08342eb6e442cfe", size = 6113124, upload-time = "2025-07-01T09:15:07.358Z" }, + { url = "https://files.pythonhosted.org/packages/cd/5a/6fec59b1dfb619234f7636d4157d11fb4e196caeee220232a8d2ec48488d/pillow-11.3.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:83e1b0161c9d148125083a35c1c5a89db5b7054834fd4387499e06552035236c", size = 6747186, upload-time = "2025-07-01T09:15:09.317Z" }, + { url = "https://files.pythonhosted.org/packages/49/6b/00187a044f98255225f172de653941e61da37104a9ea60e4f6887717e2b5/pillow-11.3.0-cp313-cp313t-win32.whl", hash = "sha256:2a3117c06b8fb646639dce83694f2f9eac405472713fcb1ae887469c0d4f6788", size = 6277546, upload-time = "2025-07-01T09:15:11.311Z" }, + { url = "https://files.pythonhosted.org/packages/e8/5c/6caaba7e261c0d75bab23be79f1d06b5ad2a2ae49f028ccec801b0e853d6/pillow-11.3.0-cp313-cp313t-win_amd64.whl", hash = "sha256:857844335c95bea93fb39e0fa2726b4d9d758850b34075a7e3ff4f4fa3aa3b31", size = 6985102, upload-time = "2025-07-01T09:15:13.164Z" }, + { url = "https://files.pythonhosted.org/packages/f3/7e/b623008460c09a0cb38263c93b828c666493caee2eb34ff67f778b87e58c/pillow-11.3.0-cp313-cp313t-win_arm64.whl", hash = "sha256:8797edc41f3e8536ae4b10897ee2f637235c94f27404cac7297f7b607dd0716e", size = 2424803, upload-time = "2025-07-01T09:15:15.695Z" }, + { url = "https://files.pythonhosted.org/packages/73/f4/04905af42837292ed86cb1b1dabe03dce1edc008ef14c473c5c7e1443c5d/pillow-11.3.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:d9da3df5f9ea2a89b81bb6087177fb1f4d1c7146d583a3fe5c672c0d94e55e12", size = 5278520, upload-time = "2025-07-01T09:15:17.429Z" }, + { url = "https://files.pythonhosted.org/packages/41/b0/33d79e377a336247df6348a54e6d2a2b85d644ca202555e3faa0cf811ecc/pillow-11.3.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0b275ff9b04df7b640c59ec5a3cb113eefd3795a8df80bac69646ef699c6981a", size = 4686116, upload-time = "2025-07-01T09:15:19.423Z" }, + { url = "https://files.pythonhosted.org/packages/49/2d/ed8bc0ab219ae8768f529597d9509d184fe8a6c4741a6864fea334d25f3f/pillow-11.3.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0743841cabd3dba6a83f38a92672cccbd69af56e3e91777b0ee7f4dba4385632", size = 5864597, upload-time = "2025-07-03T13:10:38.404Z" }, + { url = "https://files.pythonhosted.org/packages/b5/3d/b932bb4225c80b58dfadaca9d42d08d0b7064d2d1791b6a237f87f661834/pillow-11.3.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2465a69cf967b8b49ee1b96d76718cd98c4e925414ead59fdf75cf0fd07df673", size = 7638246, upload-time = "2025-07-03T13:10:44.987Z" }, + { url = "https://files.pythonhosted.org/packages/09/b5/0487044b7c096f1b48f0d7ad416472c02e0e4bf6919541b111efd3cae690/pillow-11.3.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:41742638139424703b4d01665b807c6468e23e699e8e90cffefe291c5832b027", size = 5973336, upload-time = "2025-07-01T09:15:21.237Z" }, + { url = "https://files.pythonhosted.org/packages/a8/2d/524f9318f6cbfcc79fbc004801ea6b607ec3f843977652fdee4857a7568b/pillow-11.3.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:93efb0b4de7e340d99057415c749175e24c8864302369e05914682ba642e5d77", size = 6642699, upload-time = "2025-07-01T09:15:23.186Z" }, + { url = "https://files.pythonhosted.org/packages/6f/d2/a9a4f280c6aefedce1e8f615baaa5474e0701d86dd6f1dede66726462bbd/pillow-11.3.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7966e38dcd0fa11ca390aed7c6f20454443581d758242023cf36fcb319b1a874", size = 6083789, upload-time = "2025-07-01T09:15:25.1Z" }, + { url = "https://files.pythonhosted.org/packages/fe/54/86b0cd9dbb683a9d5e960b66c7379e821a19be4ac5810e2e5a715c09a0c0/pillow-11.3.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:98a9afa7b9007c67ed84c57c9e0ad86a6000da96eaa638e4f8abe5b65ff83f0a", size = 6720386, upload-time = "2025-07-01T09:15:27.378Z" }, + { url = "https://files.pythonhosted.org/packages/e7/95/88efcaf384c3588e24259c4203b909cbe3e3c2d887af9e938c2022c9dd48/pillow-11.3.0-cp314-cp314-win32.whl", hash = "sha256:02a723e6bf909e7cea0dac1b0e0310be9d7650cd66222a5f1c571455c0a45214", size = 6370911, upload-time = "2025-07-01T09:15:29.294Z" }, + { url = "https://files.pythonhosted.org/packages/2e/cc/934e5820850ec5eb107e7b1a72dd278140731c669f396110ebc326f2a503/pillow-11.3.0-cp314-cp314-win_amd64.whl", hash = "sha256:a418486160228f64dd9e9efcd132679b7a02a5f22c982c78b6fc7dab3fefb635", size = 7117383, upload-time = "2025-07-01T09:15:31.128Z" }, + { url = "https://files.pythonhosted.org/packages/d6/e9/9c0a616a71da2a5d163aa37405e8aced9a906d574b4a214bede134e731bc/pillow-11.3.0-cp314-cp314-win_arm64.whl", hash = "sha256:155658efb5e044669c08896c0c44231c5e9abcaadbc5cd3648df2f7c0b96b9a6", size = 2511385, upload-time = "2025-07-01T09:15:33.328Z" }, + { url = "https://files.pythonhosted.org/packages/1a/33/c88376898aff369658b225262cd4f2659b13e8178e7534df9e6e1fa289f6/pillow-11.3.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:59a03cdf019efbfeeed910bf79c7c93255c3d54bc45898ac2a4140071b02b4ae", size = 5281129, upload-time = "2025-07-01T09:15:35.194Z" }, + { url = "https://files.pythonhosted.org/packages/1f/70/d376247fb36f1844b42910911c83a02d5544ebd2a8bad9efcc0f707ea774/pillow-11.3.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f8a5827f84d973d8636e9dc5764af4f0cf2318d26744b3d902931701b0d46653", size = 4689580, upload-time = "2025-07-01T09:15:37.114Z" }, + { url = "https://files.pythonhosted.org/packages/eb/1c/537e930496149fbac69efd2fc4329035bbe2e5475b4165439e3be9cb183b/pillow-11.3.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ee92f2fd10f4adc4b43d07ec5e779932b4eb3dbfbc34790ada5a6669bc095aa6", size = 5902860, upload-time = "2025-07-03T13:10:50.248Z" }, + { url = "https://files.pythonhosted.org/packages/bd/57/80f53264954dcefeebcf9dae6e3eb1daea1b488f0be8b8fef12f79a3eb10/pillow-11.3.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c96d333dcf42d01f47b37e0979b6bd73ec91eae18614864622d9b87bbd5bbf36", size = 7670694, upload-time = "2025-07-03T13:10:56.432Z" }, + { url = "https://files.pythonhosted.org/packages/70/ff/4727d3b71a8578b4587d9c276e90efad2d6fe0335fd76742a6da08132e8c/pillow-11.3.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4c96f993ab8c98460cd0c001447bff6194403e8b1d7e149ade5f00594918128b", size = 6005888, upload-time = "2025-07-01T09:15:39.436Z" }, + { url = "https://files.pythonhosted.org/packages/05/ae/716592277934f85d3be51d7256f3636672d7b1abfafdc42cf3f8cbd4b4c8/pillow-11.3.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:41342b64afeba938edb034d122b2dda5db2139b9a4af999729ba8818e0056477", size = 6670330, upload-time = "2025-07-01T09:15:41.269Z" }, + { url = "https://files.pythonhosted.org/packages/e7/bb/7fe6cddcc8827b01b1a9766f5fdeb7418680744f9082035bdbabecf1d57f/pillow-11.3.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:068d9c39a2d1b358eb9f245ce7ab1b5c3246c7c8c7d9ba58cfa5b43146c06e50", size = 6114089, upload-time = "2025-07-01T09:15:43.13Z" }, + { url = "https://files.pythonhosted.org/packages/8b/f5/06bfaa444c8e80f1a8e4bff98da9c83b37b5be3b1deaa43d27a0db37ef84/pillow-11.3.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:a1bc6ba083b145187f648b667e05a2534ecc4b9f2784c2cbe3089e44868f2b9b", size = 6748206, upload-time = "2025-07-01T09:15:44.937Z" }, + { url = "https://files.pythonhosted.org/packages/f0/77/bc6f92a3e8e6e46c0ca78abfffec0037845800ea38c73483760362804c41/pillow-11.3.0-cp314-cp314t-win32.whl", hash = "sha256:118ca10c0d60b06d006be10a501fd6bbdfef559251ed31b794668ed569c87e12", size = 6377370, upload-time = "2025-07-01T09:15:46.673Z" }, + { url = "https://files.pythonhosted.org/packages/4a/82/3a721f7d69dca802befb8af08b7c79ebcab461007ce1c18bd91a5d5896f9/pillow-11.3.0-cp314-cp314t-win_amd64.whl", hash = "sha256:8924748b688aa210d79883357d102cd64690e56b923a186f35a82cbc10f997db", size = 7121500, upload-time = "2025-07-01T09:15:48.512Z" }, + { url = "https://files.pythonhosted.org/packages/89/c7/5572fa4a3f45740eaab6ae86fcdf7195b55beac1371ac8c619d880cfe948/pillow-11.3.0-cp314-cp314t-win_arm64.whl", hash = "sha256:79ea0d14d3ebad43ec77ad5272e6ff9bba5b679ef73375ea760261207fa8e0aa", size = 2512835, upload-time = "2025-07-01T09:15:50.399Z" }, + { url = "https://files.pythonhosted.org/packages/6f/8b/209bd6b62ce8367f47e68a218bffac88888fdf2c9fcf1ecadc6c3ec1ebc7/pillow-11.3.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:3cee80663f29e3843b68199b9d6f4f54bd1d4a6b59bdd91bceefc51238bcb967", size = 5270556, upload-time = "2025-07-01T09:16:09.961Z" }, + { url = "https://files.pythonhosted.org/packages/2e/e6/231a0b76070c2cfd9e260a7a5b504fb72da0a95279410fa7afd99d9751d6/pillow-11.3.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:b5f56c3f344f2ccaf0dd875d3e180f631dc60a51b314295a3e681fe8cf851fbe", size = 4654625, upload-time = "2025-07-01T09:16:11.913Z" }, + { url = "https://files.pythonhosted.org/packages/13/f4/10cf94fda33cb12765f2397fc285fa6d8eb9c29de7f3185165b702fc7386/pillow-11.3.0-pp310-pypy310_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:e67d793d180c9df62f1f40aee3accca4829d3794c95098887edc18af4b8b780c", size = 4874207, upload-time = "2025-07-03T13:11:10.201Z" }, + { url = "https://files.pythonhosted.org/packages/72/c9/583821097dc691880c92892e8e2d41fe0a5a3d6021f4963371d2f6d57250/pillow-11.3.0-pp310-pypy310_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d000f46e2917c705e9fb93a3606ee4a819d1e3aa7a9b442f6444f07e77cf5e25", size = 6583939, upload-time = "2025-07-03T13:11:15.68Z" }, + { url = "https://files.pythonhosted.org/packages/3b/8e/5c9d410f9217b12320efc7c413e72693f48468979a013ad17fd690397b9a/pillow-11.3.0-pp310-pypy310_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:527b37216b6ac3a12d7838dc3bd75208ec57c1c6d11ef01902266a5a0c14fc27", size = 4957166, upload-time = "2025-07-01T09:16:13.74Z" }, + { url = "https://files.pythonhosted.org/packages/62/bb/78347dbe13219991877ffb3a91bf09da8317fbfcd4b5f9140aeae020ad71/pillow-11.3.0-pp310-pypy310_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:be5463ac478b623b9dd3937afd7fb7ab3d79dd290a28e2b6df292dc75063eb8a", size = 5581482, upload-time = "2025-07-01T09:16:16.107Z" }, + { url = "https://files.pythonhosted.org/packages/d9/28/1000353d5e61498aaeaaf7f1e4b49ddb05f2c6575f9d4f9f914a3538b6e1/pillow-11.3.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:8dc70ca24c110503e16918a658b869019126ecfe03109b754c402daff12b3d9f", size = 6984596, upload-time = "2025-07-01T09:16:18.07Z" }, + { url = "https://files.pythonhosted.org/packages/9e/e3/6fa84033758276fb31da12e5fb66ad747ae83b93c67af17f8c6ff4cc8f34/pillow-11.3.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:7c8ec7a017ad1bd562f93dbd8505763e688d388cde6e4a010ae1486916e713e6", size = 5270566, upload-time = "2025-07-01T09:16:19.801Z" }, + { url = "https://files.pythonhosted.org/packages/5b/ee/e8d2e1ab4892970b561e1ba96cbd59c0d28cf66737fc44abb2aec3795a4e/pillow-11.3.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:9ab6ae226de48019caa8074894544af5b53a117ccb9d3b3dcb2871464c829438", size = 4654618, upload-time = "2025-07-01T09:16:21.818Z" }, + { url = "https://files.pythonhosted.org/packages/f2/6d/17f80f4e1f0761f02160fc433abd4109fa1548dcfdca46cfdadaf9efa565/pillow-11.3.0-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:fe27fb049cdcca11f11a7bfda64043c37b30e6b91f10cb5bab275806c32f6ab3", size = 4874248, upload-time = "2025-07-03T13:11:20.738Z" }, + { url = "https://files.pythonhosted.org/packages/de/5f/c22340acd61cef960130585bbe2120e2fd8434c214802f07e8c03596b17e/pillow-11.3.0-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:465b9e8844e3c3519a983d58b80be3f668e2a7a5db97f2784e7079fbc9f9822c", size = 6583963, upload-time = "2025-07-03T13:11:26.283Z" }, + { url = "https://files.pythonhosted.org/packages/31/5e/03966aedfbfcbb4d5f8aa042452d3361f325b963ebbadddac05b122e47dd/pillow-11.3.0-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5418b53c0d59b3824d05e029669efa023bbef0f3e92e75ec8428f3799487f361", size = 4957170, upload-time = "2025-07-01T09:16:23.762Z" }, + { url = "https://files.pythonhosted.org/packages/cc/2d/e082982aacc927fc2cab48e1e731bdb1643a1406acace8bed0900a61464e/pillow-11.3.0-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:504b6f59505f08ae014f724b6207ff6222662aab5cc9542577fb084ed0676ac7", size = 5581505, upload-time = "2025-07-01T09:16:25.593Z" }, + { url = "https://files.pythonhosted.org/packages/34/e7/ae39f538fd6844e982063c3a5e4598b8ced43b9633baa3a85ef33af8c05c/pillow-11.3.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:c84d689db21a1c397d001aa08241044aa2069e7587b398c8cc63020390b1c1b8", size = 6984598, upload-time = "2025-07-01T09:16:27.732Z" }, +] + [[package]] name = "platformdirs" version = "4.3.8"