diff --git a/.github/workflows/benchmark_base.yml b/.github/workflows/benchmark_base.yml index f56c957a5..8953f9190 100644 --- a/.github/workflows/benchmark_base.yml +++ b/.github/workflows/benchmark_base.yml @@ -9,13 +9,13 @@ on: jobs: benchmark_base_branch: name: Continuous Benchmarking with Bencher - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 - - name: Set up Python 3.9 - uses: actions/setup-python@v2 + - name: Set up Python 3.12 + uses: actions/setup-python@v5 with: - python-version: '3.9' + python-version: '3.12' - uses: actions/cache@v4 with: path: .venv diff --git a/.github/workflows/benchmark_pr.yml b/.github/workflows/benchmark_pr.yml index 77df0e807..02dc229a2 100644 --- a/.github/workflows/benchmark_pr.yml +++ b/.github/workflows/benchmark_pr.yml @@ -8,13 +8,13 @@ jobs: if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository permissions: pull-requests: write - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 - - name: Set up Python 3.9 - uses: actions/setup-python@v2 + - name: Set up Python 3.12 + uses: actions/setup-python@v5 with: - python-version: '3.9' + python-version: '3.12' - uses: actions/cache@v4 with: path: .venv diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 6049701ce..ed5f11b07 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -11,8 +11,10 @@ jobs: name: Build and Publish runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - uses: actions/setup-python@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: '3.12' - uses: snok/install-poetry@v1 with: virtualenvs-in-project: true @@ -42,8 +44,10 @@ jobs: id-token: write contents: read steps: - - uses: actions/checkout@v3 - - uses: actions/setup-python@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: '3.12' - uses: snok/install-poetry@v1 with: virtualenvs-in-project: true @@ -55,7 +59,7 @@ jobs: run: | poetry build --no-interaction - name: Upload wheels as artifacts - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: wheels path: dist @@ -78,7 +82,7 @@ jobs: # Determine the tags to publish based on the release tag - name: Docker Metadata id: meta - uses: docker/metadata-action@v4 + uses: docker/metadata-action@v5 with: images: | ${{ vars.DOCKER_IMAGE_NAME }} @@ -88,9 +92,9 @@ jobs: type=raw,value=${{ env.PYQUIL_TAG_RC }},enable=${{ env.PYQUIL_TAG_RC != '' }} # Checkout is needed to use the path context: . - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Build and Test - uses: docker/build-push-action@v4 + uses: docker/build-push-action@v6 with: context: . load: true @@ -102,12 +106,12 @@ jobs: docker run --rm "${{ vars.DOCKER_IMAGE_NAME }}:test" python -c "from pyquil import get_qc" # Build and publish the image - name: Login to Docker Hub - uses: docker/login-action@v2 + uses: docker/login-action@v3 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_PASSWORD }} - name: Build and Push - uses: docker/build-push-action@v4 + uses: docker/build-push-action@v6 with: context: . push: true diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f8b11958d..0b0ba567d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -20,7 +20,7 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.PAT }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: fetch-depth: 0 token: ${{ secrets.PAT }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index c94022271..23717b885 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -12,11 +12,11 @@ jobs: name: Build and test documentation runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - name: Set up Python 3.9 - uses: actions/setup-python@v4 + - uses: actions/checkout@v4 + - name: Set up Python 3.11 + uses: actions/setup-python@v5 with: - python-version: '3.9' + python-version: '3.11' - uses: actions/cache@v4 with: path: .venv @@ -32,11 +32,11 @@ jobs: name: Check formatting runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - name: Set up Python 3.9 - uses: actions/setup-python@v4 + - uses: actions/checkout@v4 + - name: Set up Python 3.11 + uses: actions/setup-python@v5 with: - python-version: '3.9' + python-version: '3.11' - uses: actions/cache@v4 with: path: .venv @@ -51,11 +51,11 @@ jobs: name: Check style runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - name: Set up Python 3.9 - uses: actions/setup-python@v4 + - uses: actions/checkout@v4 + - name: Set up Python 3.11 + uses: actions/setup-python@v5 with: - python-version: '3.9' + python-version: '3.11' - uses: actions/cache@v4 with: path: .venv @@ -70,11 +70,11 @@ jobs: name: Check types runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - name: Set up Python 3.9 - uses: actions/setup-python@v4 + - uses: actions/checkout@v4 + - name: Set up Python 3.11 + uses: actions/setup-python@v5 with: - python-version: '3.9' + python-version: '3.11' - uses: actions/cache@v4 with: path: .venv @@ -90,7 +90,7 @@ jobs: name: Check dependencies for vulnerabilities runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Set up Go uses: actions/setup-go@v5 - name: Install OSV scanner @@ -105,11 +105,11 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ["3.9", "3.10", "3.11", "3.12"] + python-version: ["3.11", "3.12"] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - uses: actions/cache@v4 @@ -133,11 +133,11 @@ jobs: pull-requests: write # allows coverage bot to comment strategy: matrix: - python-version: ["3.9", "3.10", "3.11", "3.12"] + python-version: ["3.11", "3.12"] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - uses: actions/cache@v4 @@ -165,11 +165,11 @@ jobs: name: Check docker image runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - name: Set up Python 3.10 - uses: actions/setup-python@v2 + - uses: actions/checkout@v4 + - name: Set up Python 3.11 + uses: actions/setup-python@v5 with: - python-version: '3.10' + python-version: '3.11' - uses: actions/cache@v4 with: path: .venv @@ -180,7 +180,7 @@ jobs: . scripts/ci_install_deps poetry build -o wheels - name: Build and Test - uses: docker/build-push-action@v4 + uses: docker/build-push-action@v6 with: file: Dockerfile.test context: . @@ -195,11 +195,11 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ["3.9", "3.10", "3.11", "3.12"] + python-version: ["3.11", "3.12"] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - uses: actions/cache@v4 diff --git a/Dockerfile b/Dockerfile index 6bc5187eb..d2028527f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,7 @@ # use multi-stage builds to independently pull dependency versions ARG quilc_version=1.20.0 ARG qvm_version=1.17.1 -ARG python_version=3.10 +ARG python_version=3.11 # use multi-stage builds to independently pull dependency versions FROM rigetti/quilc:$quilc_version as quilc diff --git a/Dockerfile.test b/Dockerfile.test index cac3b69e1..cd87f5e6e 100644 --- a/Dockerfile.test +++ b/Dockerfile.test @@ -1,7 +1,7 @@ # use multi-stage builds to independently pull dependency versions ARG quilc_version=1.20.0 ARG qvm_version=1.17.1 -ARG python_version=3.10 +ARG python_version=3.11 # use multi-stage builds to independently pull dependency versions FROM rigetti/quilc:$quilc_version as quilc diff --git a/Makefile b/Makefile index e1db7e614..ff065ee1c 100644 --- a/Makefile +++ b/Makefile @@ -3,6 +3,14 @@ DEFAULT_QUILC_URL=tcp://localhost:5555 DEFAULT_QVM_URL=http://localhost:5000 DOCKER_TAG=rigetti/forest:$(COMMIT_HASH) +.DEFAULT := help + +.PHONY: help +help: + @awk 'BEGIN { FS=":.*##"; print "Supported Makefile commands:\n" } \ + /^[a-zA-Z0-9_-]+:.*##/ { cmd=$$1; desc=$$2; printf " \033[36m%-20s\033[0m %s\n", cmd, desc } \ + END { print "" }' $(MAKEFILE_LIST) + .PHONY: all all: dist diff --git a/docs/source/compiler.rst b/docs/source/compiler.rst index 9571f958d..0fad65457 100644 --- a/docs/source/compiler.rst +++ b/docs/source/compiler.rst @@ -364,8 +364,7 @@ For example, consider running a ``CZ`` on non-neighboring qubits on a linear dev .. testoutput:: swaps - CZ 2 1 - HALT + CZ 2 1... We see that the resulting program has only a single ``CZ`` even though the original program would usually require the insertion of a ``SWAP`` gate. The compiler instead opted to just relabel (or diff --git a/docs/source/getting_started.rst b/docs/source/getting_started.rst index ceb22506e..c0d33e820 100644 --- a/docs/source/getting_started.rst +++ b/docs/source/getting_started.rst @@ -40,7 +40,7 @@ If you would like to stay up to date with the latest changes and bug fixes, you .. note:: - pyQuil requires Python 3.9 or later. + pyQuil requires Python 3.11 or later and supports Python versions earlier than 3.13. .. testcode:: verify-min-version :hide: @@ -58,7 +58,7 @@ If you would like to stay up to date with the latest changes and bug fixes, you .. testoutput:: verify-min-version :hide: - ^3.9... + >=3.11, <3.13 .. note:: @@ -165,7 +165,7 @@ the terminal windows where your servers are running, you should see output print pyQuil also provides the :py:func:`~pyquil.api.local_forest_runtime()` context manager to ensure both ``quilc`` and ``qvm`` servers are running by starting them as subprocesses if they aren't already. - .. testcode:: first-program + .. code:: python from pyquil import get_qc, Program from pyquil.gates import CNOT, Z, MEASURE diff --git a/docs/source/index.rst b/docs/source/index.rst index 5f0282e5b..ee9758bb6 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -32,6 +32,8 @@ If you’re new to pyQuil, head to the `getting started `_ guid wavefunction_simulator compiler noise + simulation_architecture + dynamic_simulator_benchmark advanced_usage troubleshooting introducing_v4 diff --git a/docs/source/simulation_architecture.rst b/docs/source/simulation_architecture.rst new file mode 100644 index 000000000..056e96a22 --- /dev/null +++ b/docs/source/simulation_architecture.rst @@ -0,0 +1,442 @@ +.. _simulation_architecture: + +========================================= +Noisy simulation architecture +========================================= + +.. note:: + + The simulators described here live in the experimental, private modules + ``pyquil.simulation._simulator`` and ``pyquil.simulation._resolver`` (and the + noise model in ``pyquil.noise._noise_model`` / ``pyquil.noise._channels``). + The API is not yet stable and the import paths are private. It is documented + here because the design is intended to become the default simulation backend + in a future major release, replacing the NumPy reference simulators. + + These modules depend on `JAX `_ (via the + ``rigetti-quax`` package), which provides the operator algebra and the + ``jit``/``grad``/``vmap`` machinery the simulators are built on. + + +Goal of the module +================== + +The module simulates the action of a (possibly noisy) Quil program on a quantum +register and returns the resulting quantum state or measurement statistics. It +is designed to solve two problems with the existing simulators simultaneously: + +* **Expressiveness.** Device-realistic noise is not limited to a fixed menu of + Kraus channels. The module represents noise as arbitrary completely-positive, + trace-preserving (CPTP) maps attached to individual instructions — coherent + errors, stochastic Pauli channels, thermal relaxation, leakage to higher + levels (qutrits and beyond), readout confusion, and reset infidelity — and + composes them exactly. + +* **Performance and differentiability.** Every stage is expressed in JAX so that + the entire forward simulation is a single traceable function. It can be + ``jax.jit``-compiled (amortizing compilation across a parameter sweep) and + ``jax.grad``-differentiated (exact gradients of an output observable with + respect to gate parameters), and trajectories can be vectorized with + ``jax.vmap`` and sharded across devices. + +The unit of noise is a **channel** keyed to a program instruction. A +:class:`~pyquil.noise._noise_model.NoiseModel` is, conceptually, a partial map +from instructions to channels, + +.. math:: + + \mathcal{N} : \text{instruction} \longmapsto \mathcal{E}, + +queried during simulation via ``NoiseModel.get_channel(inst)``. A channel's +``process`` is a superoperator that *includes* the ideal gate, so the channel +**replaces** the instruction rather than being appended after it: + +.. math:: + + \mathcal{E} \;=\; \Lambda \circ \mathcal{U}, + +where :math:`\mathcal{U}(\rho) = U \rho U^\dagger` is the ideal gate and +:math:`\Lambda` is the noise. An instruction with no channel is simulated +ideally. + + +Operator vocabulary +=================== + +The pipeline manipulates a small set of ``quax`` operator types. Each carries +explicit per-qudit dimensions (e.g. ``((2, 2), (2, 2))`` for a two-qubit +operator, ``((3,), (3,))`` for a qutrit), so qubit and qudit systems are treated +uniformly. + +.. list-table:: + :header-rows: 1 + :widths: 22 78 + + * - Type + - Meaning + * - ``Unitary`` + - An ideal gate :math:`U`, acting as :math:`\rho \mapsto U \rho U^\dagger`. + * - ``SuperOp`` + - A general linear map on density operators in column-stacking + (Liouville) form, the canonical representation of a noisy ``Channel``. + * - ``KrausMap`` + - A channel as a set of Kraus operators :math:`\{K_i\}` with + :math:`\sum_i K_i^\dagger K_i = I`; the form a state-vector trajectory + samples from. + * - ``QuantumInstrument`` + - A measurement: a collection of outcome-labelled CP maps whose sum is + trace-preserving. Models classification (confusion) error and + post-measurement back-action. + +Conversions (``to_superop``, ``to_kraus``, ``to_choi``, ``to_pauli_liouville``) +are provided by ``quax`` and are used by the adapters described below. + + +The simulation pipeline +======================= + +A simulator is an **object constructed from a program**, not a function called +on one. The reason is that efficient simulation requires several closures whose +structure is fixed by the program (and noise model) but whose inputs are the +runtime parameters. Building them once, at construction time, lets the +expensive analysis (circuit expansion, dependency analysis, operator merging, +trace/compile) be shared across every subsequent evaluation — a parameter sweep, +a gradient computation, or a batch of Monte-Carlo trajectories. + +Construction runs four conceptual stages, each materialized as a closure: + +.. code-block:: text + + Program (+ NoiseModel) + │ + ▼ + ┌──────────────┐ MemoryMap ──► flat parameter vector θ + │ Linearizer │ + │ + ▼ + ┌──────────────┐ θ ──► [(operator, subsystem), ...] + │ Resolver │ (consults the noise model; ideal gates stay parametric) + │ + ▼ + ┌──────────────┐ merge adjacent operators on the program DAG + │ Compressor │ up to `max_subsystem_size` qubits + │ + ▼ + ┌──────────────┐ apply the operator stack to the initial state + │ Calculator │ (jit/grad/vmap-friendly) + │ + ▼ + StateVector / DensityMatrix / (StateVector, outcomes) + +The first three stages are shared infrastructure in ``_resolver.py``; the last +is specialized per simulator in ``_simulator.py``. + +Linearizer +---------- + +A Quil program references classical memory by name and offset (e.g. +``theta[0]``). The linearizer flattens a :class:`~pyquil.api.MemoryMap` into the +dense parameter vector :math:`\theta \in \mathbb{R}^{n}` that the rest of the +pipeline (and ``jax.grad``) operates on. The layout — which ``(register, +offset)`` pair occupies each slot — is discovered during expansion and fixed for +the life of the object, so ``linearize`` is a cheap gather. + +Resolver +-------- + +The resolver turns :math:`\theta` into an ordered list of +``(operator, subsystem)`` pairs, one per operation, where ``subsystem`` is the +tuple of (zero-based) qudit indices the operator acts on. It is produced by +:func:`~pyquil.simulation._resolver.resolve_program`, which returns a +``Resolution`` bundling the inferred dimensions, the expanded operators, their +subsystems, the parameter layout, and the resolve closure. + +Expansion does several things at once: + +* **Noise resolution.** Each instruction is looked up in the noise model. A + noisy gate becomes its ``SuperOp``; a noisy measurement becomes a + ``QuantumInstrument``; a noisy reset becomes a ``SuperOp``. Instructions with + no channel resolve to their ideal operator. + +* **Most-specific typing.** Operators are kept in their tightest native type — + ideal gates as ``Unitary``, channels as ``SuperOp``, measurements as + ``QuantumInstrument``. This lets the cheapest backend (pure state vector) + avoid density-matrix arithmetic whenever a program happens to be noiseless, + and lets each backend choose how to adapt the rest (see *Adapters*). + +* **Parametric closures.** A gate whose angle is a runtime memory reference is + *not* resolved to a number. It is wrapped in a ``ParametricGate`` that, given + :math:`\theta`, constructs the gate matrix. This keeps gate construction + inside the traced/differentiated graph, which is what makes ``jax.grad`` with + respect to gate angles work. + +* **DEFCIRCUIT and cycle expansion.** ``DEFCIRCUIT`` bodies are expanded with + formal-argument substitution. When a circuit invocation matches a + :class:`~pyquil.noise._channels.CycleChannel` in the noise model — a single + channel describing the joint noise of a whole parallel cycle — the cycle is + replaced by the channel's constituent operators directly. + +* **Dimension inference.** The register dimension of each qudit is inferred from + the operators that act on it (e.g. a ``TX`` gate or a qutrit channel promotes a + line to dimension 3). The program is expanded twice: once with default qubit + dimensions to infer the true dimensions, then again with those dimensions so + that *ideal* measurement and reset operators are built at the correct size. + Passing ``dims`` explicitly skips the first pass. + +Dependency DAG +-------------- + +The subsystem list induces a dependency DAG +(:func:`~pyquil.simulation._resolver.build_dag`): one node per operation, with an +edge :math:`u \to v` whenever :math:`u` and :math:`v` share a qubit and :math:`u` +precedes :math:`v` in program order. The DAG encodes exactly the orderings that +must be preserved; everything else is free to be reordered or merged. + +Compressor +---------- + +Applying operators one at a time is wasteful: a depth-:math:`D`, +:math:`N`-qubit program issues many small one- and two-qubit operators, and +under ``jit`` each distinct operator shape becomes a distinct branch in the +compiled graph. The compressor +(:func:`~pyquil.simulation._resolver.compressor_from_dag`) performs **greedy edge +contraction** on the DAG, fusing adjacent operators into a single operator on the +union of their qubits, up to a cap of ``max_subsystem_size`` qubits. + +Key properties: + +* **Small-first priority.** Candidate merges are taken from a priority queue + ordered by the size of the resulting subsystem, so one-qubit gates are + absorbed into neighbouring multi-qubit groups first. This reduces the number + of *distinct* subsystem shapes, which is what governs compile time. + +* **Convexity / barrier safety.** A merge is rejected if it would create a cycle + in the contracted (quotient) graph — i.e. if some operation lies on a + dependency path *between* the two groups. This is what prevents two gates that + straddle a mid-circuit measurement from being fused, which would silently + reorder the measurement. Measurements (and any explicit barrier) are marked as + non-mergeable. + +* **Order-preserving emission.** The merged groups are emitted in a + lexicographic topological order keyed by program index, guaranteeing that + measurement operators appear in the compressed list in the same order as the + ``MEASURE`` instructions in the program, regardless of how gates were fused. + +Setting ``max_subsystem_size=0`` disables merging entirely (useful for +debugging or for exact per-instruction inspection). + +Adapters +-------- + +The resolver is backend-agnostic: it yields each operator in its most specific +type. Each simulator then adapts the compressed list to the representation it +evolves: + +* **Density matrix** (:func:`~pyquil.simulation._resolver.adapt_for_density_matrix`): + everything becomes a ``SuperOp`` (a ``QuantumInstrument`` is collapsed to its + total channel, since the density-matrix backend does not branch on outcomes). + +* **Trajectory** (:func:`~pyquil.simulation._resolver.adapt_for_trajectory`): a + ``SuperOp`` is converted to a (truncated) ``KrausMap``; ``Unitary``, + ``KrausMap``, and ``QuantumInstrument`` pass through unchanged. + +Calculator +---------- + +The calculator applies the operator stack to the initial state. Two strategies +appear, both designed so that the compiled graph scales with the number of +*distinct subsystem shapes* rather than the number of operations: + +* **Scan + switch.** Operators are stacked into one array and applied with a + :func:`jax.lax.scan`; the loop body dispatches each operator to a + :func:`jax.lax.switch` branch selected by its base subsystem. Only one branch + per distinct subsystem is traced. + +* **Vectorized construction.** For state-vector evolution, gate matrices of the + same *kind* (same constructor, constant arguments, and embedding) are built in + a single ``jax.vmap`` and then folded within each merge group by a segmented + matrix-product scan. The traced graph is then proportional to the number of + gate kinds, not the number of gates. For parameter-free programs the operator + stack is a compile-time constant and is materialized once and reused. + +Because the whole calculator is a pure JAX function of :math:`\theta` (and, for +trajectories, a PRNG key), ``jax.jit`` and ``jax.grad`` compose with it directly. + + +The three simulators +==================== + +All three share the pipeline above and differ only in the state they evolve and +the operations they admit. + +.. list-table:: + :header-rows: 1 + :widths: 26 30 14 14 16 + + * - Simulator + - Use case + - Noise + - Measurements / resets + - Differentiable + * - ``PureStateVectorSimulator`` + - Gate-only programs + - No + - No + - ``jit`` + ``grad`` + * - ``DensityMatrixSimulator`` + - Any program, optional noise + - Yes + - Resets (measurements as total channel) + - ``jit`` + ``grad`` + * - ``TrajectorySimulator`` + - Monte-Carlo sampling + - Yes + - Yes + - ``jit`` (per batch) + +The qubit ceilings are set by memory: a state vector holds :math:`2^{N}` +amplitudes, so pure-state and trajectory simulation are practical to roughly +:math:`N \lesssim 26`; a density matrix holds :math:`4^{N}` entries, limiting the +density-matrix backend to roughly :math:`N \lesssim 13`. + +All simulators take the program (and, where relevant, a ``noise_model`` and +``max_subsystem_size``) at construction, and expose ``linearize``, ``resolve``, +``compress``, and ``compute``. ``compute`` is the entry point and takes the flat +parameter vector from ``linearize``. + +Pure state vector +----------------- + +For unitary, noiseless, measurement-free programs, evolve a pure state +:math:`|\psi\rangle = U_D \cdots U_1 |0\rangle`. This is the cheapest backend and +the natural target for gradient-based circuit optimization, and it can return the +full program unitary in addition to the state. + +.. code-block:: python + + import jax + import jax.numpy as jnp + from pyquil import Program + from pyquil.gates import H, CNOT, RX + from pyquil.simulation._simulator import PureStateVectorSimulator + + # A Bell state (no runtime parameters). + sim = PureStateVectorSimulator(Program(H(0), CNOT(0, 1))) + psi = sim.compute(jnp.array([])) # final state vector + + # The full 4x4 program unitary. + U = sim.unitary(jnp.array([])) + + # A parametric program, jit-compiled and differentiated. + from pyquil.quilatom import MemoryReference + from pyquil.quilbase import Declare + + p = Program(Declare("theta", "REAL", 1), RX(MemoryReference("theta", 0), 0)) + sim = PureStateVectorSimulator(p) + + def excited_pop(theta): + psi = sim.compute(jnp.array([theta])) + amps = psi.matrix.reshape(-1) + return jnp.abs(amps[1]) ** 2 # P(|1>) + + grad_pop = jax.jit(jax.grad(excited_pop)) + print(grad_pop(0.3)) # exact d P(|1>) / d theta + +Density matrix +-------------- + +For noisy, deterministic evolution, propagate the density matrix +:math:`\rho \mapsto \mathcal{E}_D \circ \cdots \circ \mathcal{E}_1 (\rho)` exactly. +This is the backend to use for expectation values and process metrics under +noise, since it tracks the full mixed state without sampling. Measurements are +applied as their total (outcome-averaged) channel. + +.. code-block:: python + + import jax.numpy as jnp + from pyquil import Program + from pyquil.gates import RX + from pyquil.noise._channels import Channel + from pyquil.noise._noise_model import NoiseModel + from pyquil.simulation._simulator import DensityMatrixSimulator + + gate = RX(jnp.pi, 0) + noise = NoiseModel.from_channels([ + Channel.from_gate_fidelity(inst=gate, fidelity=0.99), + ]) + + sim = DensityMatrixSimulator(Program(gate), noise_model=noise) + rho = sim.compute(jnp.array([])) # final density matrix (a quax DensityMatrix) + +A device-realistic model can be built directly from an instruction set +architecture with :meth:`NoiseModel.from_isa `, +which converts per-gate fidelities to depolarizing channels and per-qubit +readout fidelities to symmetric confusion. + +Trajectory +---------- + +For programs with mid-circuit measurements, resets, and feed-forward-style +sampling, unravel the dynamics into pure-state **quantum trajectories**: each +trajectory samples a Kraus operator (or measurement outcome) at every noisy step +and evolves a single state vector, so the cost is that of a state vector rather +than a density matrix. Averaging over trajectories recovers the density-matrix +result; the individual trajectories *are* the sampled measurement records. + +The number of trajectories is set by the shape of the PRNG key: a scalar key +runs one trajectory, while a batch of keys (from ``jax.random.split``) runs that +many in parallel via ``vmap``. A measurement is handled by flattening its +``QuantumInstrument`` into a single Kraus axis, so sampling a Kraus index also +selects the outcome. + +.. code-block:: python + + import jax + import jax.numpy as jnp + from pyquil import Program + from pyquil.gates import H, MEASURE + from pyquil.quilatom import MemoryReference + from pyquil.quilbase import Declare + from pyquil.simulation._simulator import TrajectorySimulator + + p = Program(Declare("ro", "BIT", 1), H(0), MEASURE(0, MemoryReference("ro", 0))) + sim = TrajectorySimulator(p) + params = jnp.array([]) + + # A batch of 1000 trajectories in parallel. + keys = jax.random.split(jax.random.key(0), 1000) + psi_batch, outcomes = sim.compute(params, keys) + # outcomes has shape (1000, n_measurements); ~50/50 for an H gate. + + # Or, scalable sampling that streams batches and keeps only the outcomes: + shots = sim.sample(params, num_trajectories=100_000, batch_size=2_000) + +``sample`` runs trajectories in fixed-size batches, discarding state vectors +between batches so the total number of shots is unbounded by memory. When +multiple JAX devices are available, each batch is run data-parallel via +:func:`jax.pmap` — one independent kernel replica per device, with no +cross-device communication. In that case ``batch_size`` is interpreted **per +device**, so ``n`` devices run ``n * batch_size`` trajectories per batch and +each device's memory footprint matches a single-device run. + + +Choosing a simulator +==================== + +* Use **``PureStateVectorSimulator``** for ideal, measurement-free circuits — + variational ansätze, unitary verification, gradient-based optimization. It is + the fastest and supports ``jax.grad`` and the full-unitary readout. + +* Use **``DensityMatrixSimulator``** when you need the *exact* noisy state or a + noise-averaged expectation value at modest qubit count (:math:`\lesssim 13`), + with no sampling noise. It is also differentiable. + +* Use **``TrajectorySimulator``** when the program contains mid-circuit + measurements or resets, when you want sampled bitstrings rather than a state, + or when the qubit count is too large for a density matrix but a state vector + still fits. Increase the trajectory count to reduce sampling error. + +In all cases, ``max_subsystem_size`` trades compile time against runtime: larger +groups mean fewer, denser operator applications (faster steady-state runtime) at +the cost of larger merged matrices and longer compilation. The default (2) is a +reasonable balance for circuits dominated by one- and two-qubit gates. diff --git a/poetry.lock b/poetry.lock index fb05d37c1..4c6135858 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 2.1.4 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.4.1 and should not be changed by hand. [[package]] name = "alabaster" @@ -15,66 +15,64 @@ files = [ [[package]] name = "anyio" -version = "4.11.0" +version = "4.13.0" description = "High-level concurrency and networking framework on top of asyncio or Trio" optional = false -python-versions = ">=3.9" +python-versions = ">=3.10" groups = ["main", "dev"] files = [ - {file = "anyio-4.11.0-py3-none-any.whl", hash = "sha256:0287e96f4d26d4149305414d4e3bc32f0dcd0862365a4bddea19d7a1ec38c4fc"}, - {file = "anyio-4.11.0.tar.gz", hash = "sha256:82a8d0b81e318cc5ce71a5f1f8b5c4e63619620b63141ef8c995fa0db95a57c4"}, + {file = "anyio-4.13.0-py3-none-any.whl", hash = "sha256:08b310f9e24a9594186fd75b4f73f4a4152069e3853f1ed8bfbf58369f4ad708"}, + {file = "anyio-4.13.0.tar.gz", hash = "sha256:334b70e641fd2221c1505b3890c69882fe4a2df910cba14d97019b90b24439dc"}, ] [package.dependencies] -exceptiongroup = {version = ">=1.0.2", markers = "python_version < \"3.11\""} idna = ">=2.8" -sniffio = ">=1.1" typing_extensions = {version = ">=4.5", markers = "python_version < \"3.13\""} [package.extras] -trio = ["trio (>=0.31.0)"] +trio = ["trio (>=0.32.0)"] [[package]] name = "asttokens" -version = "3.0.0" +version = "3.0.1" description = "Annotate AST trees with source code positions" optional = true python-versions = ">=3.8" groups = ["main"] markers = "extra == \"latex\"" files = [ - {file = "asttokens-3.0.0-py3-none-any.whl", hash = "sha256:e3078351a059199dd5138cb1c706e6430c05eff2ff136af5eb4790f9d28932e2"}, - {file = "asttokens-3.0.0.tar.gz", hash = "sha256:0dcd8baa8d62b0c1d118b399b2ddba3c4aff271d0d7a9e0d4c1681c79035bbc7"}, + {file = "asttokens-3.0.1-py3-none-any.whl", hash = "sha256:15a3ebc0f43c2d0a50eeafea25e19046c68398e487b9f1f5b517f7c0f40f976a"}, + {file = "asttokens-3.0.1.tar.gz", hash = "sha256:71a4ee5de0bde6a31d64f6b13f2293ac190344478f081c3d1bccfcf5eacb0cb7"}, ] [package.extras] -astroid = ["astroid (>=2,<4)"] -test = ["astroid (>=2,<4)", "pytest", "pytest-cov", "pytest-xdist"] +astroid = ["astroid (>=2,<5)"] +test = ["astroid (>=2,<5)", "pytest (<9.0)", "pytest-cov", "pytest-xdist"] [[package]] name = "attrs" -version = "25.4.0" +version = "26.1.0" description = "Classes Without Boilerplate" optional = true python-versions = ">=3.9" groups = ["main"] markers = "extra == \"docs\"" files = [ - {file = "attrs-25.4.0-py3-none-any.whl", hash = "sha256:adcf7e2a1fb3b36ac48d97835bb6d8ade15b8dcce26aba8bf1d14847b57a3373"}, - {file = "attrs-25.4.0.tar.gz", hash = "sha256:16d5969b87f0859ef33a48b35d55ac1be6e42ae49d5e853b597db70c35c57e11"}, + {file = "attrs-26.1.0-py3-none-any.whl", hash = "sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309"}, + {file = "attrs-26.1.0.tar.gz", hash = "sha256:d03ceb89cb322a8fd706d4fb91940737b6642aa36998fe130a9bc96c985eff32"}, ] [[package]] name = "babel" -version = "2.17.0" +version = "2.18.0" description = "Internationalization utilities" optional = true python-versions = ">=3.8" groups = ["main"] markers = "extra == \"docs\"" files = [ - {file = "babel-2.17.0-py3-none-any.whl", hash = "sha256:4d0b53093fdfb4b21c92b5213dba5a1b23885afa8383709427046b21c366e5f2"}, - {file = "babel-2.17.0.tar.gz", hash = "sha256:0c54cffb19f690cdcc52a3b50bcbf71e07a808d1c80d549f2459b9d2cf0afb9d"}, + {file = "babel-2.18.0-py3-none-any.whl", hash = "sha256:e2b422b277c2b9a9630c1d7903c2a00d0830c409c59ac8cae9081c92f1aeba35"}, + {file = "babel-2.18.0.tar.gz", hash = "sha256:b80b99a14bd085fcacfa15c9165f651fbb3406e66cc603abf11c5750937c992d"}, ] [package.extras] @@ -82,19 +80,19 @@ dev = ["backports.zoneinfo ; python_version < \"3.9\"", "freezegun (>=1.0,<2.0)" [[package]] name = "beautifulsoup4" -version = "4.14.2" +version = "4.14.3" description = "Screen-scraping library" optional = true python-versions = ">=3.7.0" groups = ["main"] markers = "extra == \"docs\"" files = [ - {file = "beautifulsoup4-4.14.2-py3-none-any.whl", hash = "sha256:5ef6fa3a8cbece8488d66985560f97ed091e22bbc4e9c2338508a9d5de6d4515"}, - {file = "beautifulsoup4-4.14.2.tar.gz", hash = "sha256:2a98ab9f944a11acee9cc848508ec28d9228abfd522ef0fad6a02a72e0ded69e"}, + {file = "beautifulsoup4-4.14.3-py3-none-any.whl", hash = "sha256:0918bfe44902e6ad8d57732ba310582e98da931428d231a5ecb9e7c703a735bb"}, + {file = "beautifulsoup4-4.14.3.tar.gz", hash = "sha256:6292b1c5186d356bba669ef9f7f051757099565ad9ada5dd630bd9de5fa7fb86"}, ] [package.dependencies] -soupsieve = ">1.2" +soupsieve = ">=1.6.1" typing-extensions = ">=4.0.0" [package.extras] @@ -106,34 +104,34 @@ lxml = ["lxml"] [[package]] name = "bleach" -version = "6.2.0" +version = "6.4.0" description = "An easy safelist-based HTML-sanitizing tool." optional = true -python-versions = ">=3.9" +python-versions = ">=3.10" groups = ["main"] markers = "extra == \"docs\"" files = [ - {file = "bleach-6.2.0-py3-none-any.whl", hash = "sha256:117d9c6097a7c3d22fd578fcd8d35ff1e125df6736f554da4e432fdd63f31e5e"}, - {file = "bleach-6.2.0.tar.gz", hash = "sha256:123e894118b8a599fd80d3ec1a6d4cc7ce4e5882b1317a7e1ba69b56e95f991f"}, + {file = "bleach-6.4.0-py3-none-any.whl", hash = "sha256:4b6b6a54fff2e69a3dde9d21cc6301220bee3c3cb792187d11403fd795031081"}, + {file = "bleach-6.4.0.tar.gz", hash = "sha256:4202482733d85cedd04e59fcb2f89f4e4c7c385a78d3c3c23c30446843a37452"}, ] [package.dependencies] -tinycss2 = {version = ">=1.1.0,<1.5", optional = true, markers = "extra == \"css\""} +tinycss2 = {version = ">=1.1.0", optional = true, markers = "extra == \"css\""} webencodings = "*" [package.extras] -css = ["tinycss2 (>=1.1.0,<1.5)"] +css = ["tinycss2 (>=1.1.0)"] [[package]] name = "certifi" -version = "2025.10.5" +version = "2026.4.22" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.7" groups = ["main", "dev"] files = [ - {file = "certifi-2025.10.5-py3-none-any.whl", hash = "sha256:0f212c2744a9bb6de0c56639a6f68afe01ecd92d91f14ae897c4fe7bbeeef0de"}, - {file = "certifi-2025.10.5.tar.gz", hash = "sha256:47c09d31ccf2acf0be3f701ea53595ee7e0b8fa08801c6624be771df09ae7b43"}, + {file = "certifi-2026.4.22-py3-none-any.whl", hash = "sha256:3cb2210c8f88ba2318d29b0388d1023c8492ff72ecdde4ebdaddbb13a31b1c4a"}, + {file = "certifi-2026.4.22.tar.gz", hash = "sha256:8d455352a37b71bf76a79caa83a3d6c25afee4a385d632127b6afb3963f1c580"}, ] [[package]] @@ -236,92 +234,142 @@ pycparser = {version = "*", markers = "implementation_name != \"PyPy\""} [[package]] name = "charset-normalizer" -version = "3.4.3" +version = "3.4.7" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." optional = true python-versions = ">=3.7" groups = ["main"] markers = "extra == \"docs\"" files = [ - {file = "charset_normalizer-3.4.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:fb7f67a1bfa6e40b438170ebdc8158b78dc465a5a67b6dde178a46987b244a72"}, - {file = "charset_normalizer-3.4.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cc9370a2da1ac13f0153780040f465839e6cccb4a1e44810124b4e22483c93fe"}, - {file = "charset_normalizer-3.4.3-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:07a0eae9e2787b586e129fdcbe1af6997f8d0e5abaa0bc98c0e20e124d67e601"}, - {file = "charset_normalizer-3.4.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:74d77e25adda8581ffc1c720f1c81ca082921329452eba58b16233ab1842141c"}, - {file = "charset_normalizer-3.4.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d0e909868420b7049dafd3a31d45125b31143eec59235311fc4c57ea26a4acd2"}, - {file = "charset_normalizer-3.4.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c6f162aabe9a91a309510d74eeb6507fab5fff92337a15acbe77753d88d9dcf0"}, - {file = "charset_normalizer-3.4.3-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:4ca4c094de7771a98d7fbd67d9e5dbf1eb73efa4f744a730437d8a3a5cf994f0"}, - {file = "charset_normalizer-3.4.3-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:02425242e96bcf29a49711b0ca9f37e451da7c70562bc10e8ed992a5a7a25cc0"}, - {file = "charset_normalizer-3.4.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:78deba4d8f9590fe4dae384aeff04082510a709957e968753ff3c48399f6f92a"}, - {file = "charset_normalizer-3.4.3-cp310-cp310-win32.whl", hash = "sha256:d79c198e27580c8e958906f803e63cddb77653731be08851c7df0b1a14a8fc0f"}, - {file = "charset_normalizer-3.4.3-cp310-cp310-win_amd64.whl", hash = "sha256:c6e490913a46fa054e03699c70019ab869e990270597018cef1d8562132c2669"}, - {file = "charset_normalizer-3.4.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:b256ee2e749283ef3ddcff51a675ff43798d92d746d1a6e4631bf8c707d22d0b"}, - {file = "charset_normalizer-3.4.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:13faeacfe61784e2559e690fc53fa4c5ae97c6fcedb8eb6fb8d0a15b475d2c64"}, - {file = "charset_normalizer-3.4.3-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:00237675befef519d9af72169d8604a067d92755e84fe76492fef5441db05b91"}, - {file = "charset_normalizer-3.4.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:585f3b2a80fbd26b048a0be90c5aae8f06605d3c92615911c3a2b03a8a3b796f"}, - {file = "charset_normalizer-3.4.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0e78314bdc32fa80696f72fa16dc61168fda4d6a0c014e0380f9d02f0e5d8a07"}, - {file = "charset_normalizer-3.4.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:96b2b3d1a83ad55310de8c7b4a2d04d9277d5591f40761274856635acc5fcb30"}, - {file = "charset_normalizer-3.4.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:939578d9d8fd4299220161fdd76e86c6a251987476f5243e8864a7844476ba14"}, - {file = "charset_normalizer-3.4.3-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:fd10de089bcdcd1be95a2f73dbe6254798ec1bda9f450d5828c96f93e2536b9c"}, - {file = "charset_normalizer-3.4.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1e8ac75d72fa3775e0b7cb7e4629cec13b7514d928d15ef8ea06bca03ef01cae"}, - {file = "charset_normalizer-3.4.3-cp311-cp311-win32.whl", hash = "sha256:6cf8fd4c04756b6b60146d98cd8a77d0cdae0e1ca20329da2ac85eed779b6849"}, - {file = "charset_normalizer-3.4.3-cp311-cp311-win_amd64.whl", hash = "sha256:31a9a6f775f9bcd865d88ee350f0ffb0e25936a7f930ca98995c05abf1faf21c"}, - {file = "charset_normalizer-3.4.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e28e334d3ff134e88989d90ba04b47d84382a828c061d0d1027b1b12a62b39b1"}, - {file = "charset_normalizer-3.4.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0cacf8f7297b0c4fcb74227692ca46b4a5852f8f4f24b3c766dd94a1075c4884"}, - {file = "charset_normalizer-3.4.3-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c6fd51128a41297f5409deab284fecbe5305ebd7e5a1f959bee1c054622b7018"}, - {file = "charset_normalizer-3.4.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3cfb2aad70f2c6debfbcb717f23b7eb55febc0bb23dcffc0f076009da10c6392"}, - {file = "charset_normalizer-3.4.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1606f4a55c0fd363d754049cdf400175ee96c992b1f8018b993941f221221c5f"}, - {file = "charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:027b776c26d38b7f15b26a5da1044f376455fb3766df8fc38563b4efbc515154"}, - {file = "charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:42e5088973e56e31e4fa58eb6bd709e42fc03799c11c42929592889a2e54c491"}, - {file = "charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:cc34f233c9e71701040d772aa7490318673aa7164a0efe3172b2981218c26d93"}, - {file = "charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:320e8e66157cc4e247d9ddca8e21f427efc7a04bbd0ac8a9faf56583fa543f9f"}, - {file = "charset_normalizer-3.4.3-cp312-cp312-win32.whl", hash = "sha256:fb6fecfd65564f208cbf0fba07f107fb661bcd1a7c389edbced3f7a493f70e37"}, - {file = "charset_normalizer-3.4.3-cp312-cp312-win_amd64.whl", hash = "sha256:86df271bf921c2ee3818f0522e9a5b8092ca2ad8b065ece5d7d9d0e9f4849bcc"}, - {file = "charset_normalizer-3.4.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:14c2a87c65b351109f6abfc424cab3927b3bdece6f706e4d12faaf3d52ee5efe"}, - {file = "charset_normalizer-3.4.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:41d1fc408ff5fdfb910200ec0e74abc40387bccb3252f3f27c0676731df2b2c8"}, - {file = "charset_normalizer-3.4.3-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1bb60174149316da1c35fa5233681f7c0f9f514509b8e399ab70fea5f17e45c9"}, - {file = "charset_normalizer-3.4.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:30d006f98569de3459c2fc1f2acde170b7b2bd265dc1943e87e1a4efe1b67c31"}, - {file = "charset_normalizer-3.4.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:416175faf02e4b0810f1f38bcb54682878a4af94059a1cd63b8747244420801f"}, - {file = "charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6aab0f181c486f973bc7262a97f5aca3ee7e1437011ef0c2ec04b5a11d16c927"}, - {file = "charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:fdabf8315679312cfa71302f9bd509ded4f2f263fb5b765cf1433b39106c3cc9"}, - {file = "charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:bd28b817ea8c70215401f657edef3a8aa83c29d447fb0b622c35403780ba11d5"}, - {file = "charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:18343b2d246dc6761a249ba1fb13f9ee9a2bcd95decc767319506056ea4ad4dc"}, - {file = "charset_normalizer-3.4.3-cp313-cp313-win32.whl", hash = "sha256:6fb70de56f1859a3f71261cbe41005f56a7842cc348d3aeb26237560bfa5e0ce"}, - {file = "charset_normalizer-3.4.3-cp313-cp313-win_amd64.whl", hash = "sha256:cf1ebb7d78e1ad8ec2a8c4732c7be2e736f6e5123a4146c5b89c9d1f585f8cef"}, - {file = "charset_normalizer-3.4.3-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3cd35b7e8aedeb9e34c41385fda4f73ba609e561faedfae0a9e75e44ac558a15"}, - {file = "charset_normalizer-3.4.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b89bc04de1d83006373429975f8ef9e7932534b8cc9ca582e4db7d20d91816db"}, - {file = "charset_normalizer-3.4.3-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2001a39612b241dae17b4687898843f254f8748b796a2e16f1051a17078d991d"}, - {file = "charset_normalizer-3.4.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8dcfc373f888e4fb39a7bc57e93e3b845e7f462dacc008d9749568b1c4ece096"}, - {file = "charset_normalizer-3.4.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:18b97b8404387b96cdbd30ad660f6407799126d26a39ca65729162fd810a99aa"}, - {file = "charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ccf600859c183d70eb47e05a44cd80a4ce77394d1ac0f79dbd2dd90a69a3a049"}, - {file = "charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:53cd68b185d98dde4ad8990e56a58dea83a4162161b1ea9272e5c9182ce415e0"}, - {file = "charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:30a96e1e1f865f78b030d65241c1ee850cdf422d869e9028e2fc1d5e4db73b92"}, - {file = "charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d716a916938e03231e86e43782ca7878fb602a125a91e7acb8b5112e2e96ac16"}, - {file = "charset_normalizer-3.4.3-cp314-cp314-win32.whl", hash = "sha256:c6dbd0ccdda3a2ba7c2ecd9d77b37f3b5831687d8dc1b6ca5f56a4880cc7b7ce"}, - {file = "charset_normalizer-3.4.3-cp314-cp314-win_amd64.whl", hash = "sha256:73dc19b562516fc9bcf6e5d6e596df0b4eb98d87e4f79f3ae71840e6ed21361c"}, - {file = "charset_normalizer-3.4.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:0f2be7e0cf7754b9a30eb01f4295cc3d4358a479843b31f328afd210e2c7598c"}, - {file = "charset_normalizer-3.4.3-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c60e092517a73c632ec38e290eba714e9627abe9d301c8c8a12ec32c314a2a4b"}, - {file = "charset_normalizer-3.4.3-cp38-cp38-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:252098c8c7a873e17dd696ed98bbe91dbacd571da4b87df3736768efa7a792e4"}, - {file = "charset_normalizer-3.4.3-cp38-cp38-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3653fad4fe3ed447a596ae8638b437f827234f01a8cd801842e43f3d0a6b281b"}, - {file = "charset_normalizer-3.4.3-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8999f965f922ae054125286faf9f11bc6932184b93011d138925a1773830bbe9"}, - {file = "charset_normalizer-3.4.3-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:d95bfb53c211b57198bb91c46dd5a2d8018b3af446583aab40074bf7988401cb"}, - {file = "charset_normalizer-3.4.3-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:5b413b0b1bfd94dbf4023ad6945889f374cd24e3f62de58d6bb102c4d9ae534a"}, - {file = "charset_normalizer-3.4.3-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:b5e3b2d152e74e100a9e9573837aba24aab611d39428ded46f4e4022ea7d1942"}, - {file = "charset_normalizer-3.4.3-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:a2d08ac246bb48479170408d6c19f6385fa743e7157d716e144cad849b2dd94b"}, - {file = "charset_normalizer-3.4.3-cp38-cp38-win32.whl", hash = "sha256:ec557499516fc90fd374bf2e32349a2887a876fbf162c160e3c01b6849eaf557"}, - {file = "charset_normalizer-3.4.3-cp38-cp38-win_amd64.whl", hash = "sha256:5d8d01eac18c423815ed4f4a2ec3b439d654e55ee4ad610e153cf02faf67ea40"}, - {file = "charset_normalizer-3.4.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:70bfc5f2c318afece2f5838ea5e4c3febada0be750fcf4775641052bbba14d05"}, - {file = "charset_normalizer-3.4.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:23b6b24d74478dc833444cbd927c338349d6ae852ba53a0d02a2de1fce45b96e"}, - {file = "charset_normalizer-3.4.3-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:34a7f768e3f985abdb42841e20e17b330ad3aaf4bb7e7aeeb73db2e70f077b99"}, - {file = "charset_normalizer-3.4.3-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:fb731e5deb0c7ef82d698b0f4c5bb724633ee2a489401594c5c88b02e6cb15f7"}, - {file = "charset_normalizer-3.4.3-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:257f26fed7d7ff59921b78244f3cd93ed2af1800ff048c33f624c87475819dd7"}, - {file = "charset_normalizer-3.4.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:1ef99f0456d3d46a50945c98de1774da86f8e992ab5c77865ea8b8195341fc19"}, - {file = "charset_normalizer-3.4.3-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:2c322db9c8c89009a990ef07c3bcc9f011a3269bc06782f916cd3d9eed7c9312"}, - {file = "charset_normalizer-3.4.3-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:511729f456829ef86ac41ca78c63a5cb55240ed23b4b737faca0eb1abb1c41bc"}, - {file = "charset_normalizer-3.4.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:88ab34806dea0671532d3f82d82b85e8fc23d7b2dd12fa837978dad9bb392a34"}, - {file = "charset_normalizer-3.4.3-cp39-cp39-win32.whl", hash = "sha256:16a8770207946ac75703458e2c743631c79c59c5890c80011d536248f8eaa432"}, - {file = "charset_normalizer-3.4.3-cp39-cp39-win_amd64.whl", hash = "sha256:d22dbedd33326a4a5190dd4fe9e9e693ef12160c77382d9e87919bce54f3d4ca"}, - {file = "charset_normalizer-3.4.3-py3-none-any.whl", hash = "sha256:ce571ab16d890d23b5c278547ba694193a45011ff86a9162a71307ed9f86759a"}, - {file = "charset_normalizer-3.4.3.tar.gz", hash = "sha256:6fce4b8500244f6fcb71465d4a4930d132ba9ab8e71a7859e6a5d59851068d14"}, + {file = "charset_normalizer-3.4.7-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cdd68a1fb318e290a2077696b7eb7a21a49163c455979c639bf5a5dcdc46617d"}, + {file = "charset_normalizer-3.4.7-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e17b8d5d6a8c47c85e68ca8379def1303fd360c3e22093a807cd34a71cd082b8"}, + {file = "charset_normalizer-3.4.7-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:511ef87c8aec0783e08ac18565a16d435372bc1ac25a91e6ac7f5ef2b0bff790"}, + {file = "charset_normalizer-3.4.7-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:007d05ec7321d12a40227aae9e2bc6dca73f3cb21058999a1df9e193555a9dcc"}, + {file = "charset_normalizer-3.4.7-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cf29836da5119f3c8a8a70667b0ef5fdca3bb12f80fd06487cfa575b3909b393"}, + {file = "charset_normalizer-3.4.7-cp310-cp310-manylinux_2_31_armv7l.whl", hash = "sha256:12d8baf840cc7889b37c7c770f478adea7adce3dcb3944d02ec87508e2dcf153"}, + {file = "charset_normalizer-3.4.7-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d560742f3c0d62afaccf9f41fe485ed69bd7661a241f86a3ef0f0fb8b1a397af"}, + {file = "charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b14b2d9dac08e28bb8046a1a0434b1750eb221c8f5b87a68f4fa11a6f97b5e34"}, + {file = "charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:bc17a677b21b3502a21f66a8cc64f5bfad4df8a0b8434d661666f8ce90ac3af1"}, + {file = "charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:750e02e074872a3fad7f233b47734166440af3cdea0add3e95163110816d6752"}, + {file = "charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:4e5163c14bffd570ef2affbfdd77bba66383890797df43dc8b4cc7d6f500bf53"}, + {file = "charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:6ed74185b2db44f41ef35fd1617c5888e59792da9bbc9190d6c7300617182616"}, + {file = "charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:94e1885b270625a9a828c9793b4d52a64445299baa1fea5a173bf1d3dd9a1a5a"}, + {file = "charset_normalizer-3.4.7-cp310-cp310-win32.whl", hash = "sha256:6785f414ae0f3c733c437e0f3929197934f526d19dfaa75e18fdb4f94c6fb374"}, + {file = "charset_normalizer-3.4.7-cp310-cp310-win_amd64.whl", hash = "sha256:6696b7688f54f5af4462118f0bfa7c1621eeb87154f77fa04b9295ce7a8f2943"}, + {file = "charset_normalizer-3.4.7-cp310-cp310-win_arm64.whl", hash = "sha256:66671f93accb62ed07da56613636f3641f1a12c13046ce91ffc923721f23c008"}, + {file = "charset_normalizer-3.4.7-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7641bb8895e77f921102f72833904dcd9901df5d6d72a2ab8f31d04b7e51e4e7"}, + {file = "charset_normalizer-3.4.7-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:202389074300232baeb53ae2569a60901f7efadd4245cf3a3bf0617d60b439d7"}, + {file = "charset_normalizer-3.4.7-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:30b8d1d8c52a48c2c5690e152c169b673487a2a58de1ec7393196753063fcd5e"}, + {file = "charset_normalizer-3.4.7-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:532bc9bf33a68613fd7d65e4b1c71a6a38d7d42604ecf239c77392e9b4e8998c"}, + {file = "charset_normalizer-3.4.7-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2fe249cb4651fd12605b7288b24751d8bfd46d35f12a20b1ba33dea122e690df"}, + {file = "charset_normalizer-3.4.7-cp311-cp311-manylinux_2_31_armv7l.whl", hash = "sha256:65bcd23054beab4d166035cabbc868a09c1a49d1efe458fe8e4361215df40265"}, + {file = "charset_normalizer-3.4.7-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:08e721811161356f97b4059a9ba7bafb23ea5ee2255402c42881c214e173c6b4"}, + {file = "charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:e060d01aec0a910bdccb8be71faf34e7799ce36950f8294c8bf612cba65a2c9e"}, + {file = "charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:38c0109396c4cfc574d502df99742a45c72c08eff0a36158b6f04000043dbf38"}, + {file = "charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:1c2a768fdd44ee4a9339a9b0b130049139b8ce3c01d2ce09f67f5a68048d477c"}, + {file = "charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:1a87ca9d5df6fe460483d9a5bbf2b18f620cbed41b432e2bddb686228282d10b"}, + {file = "charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:d635aab80466bc95771bb78d5370e74d36d1fe31467b6b29b8b57b2a3cd7d22c"}, + {file = "charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ae196f021b5e7c78e918242d217db021ed2a6ace2bc6ae94c0fc596221c7f58d"}, + {file = "charset_normalizer-3.4.7-cp311-cp311-win32.whl", hash = "sha256:adb2597b428735679446b46c8badf467b4ca5f5056aae4d51a19f9570301b1ad"}, + {file = "charset_normalizer-3.4.7-cp311-cp311-win_amd64.whl", hash = "sha256:8e385e4267ab76874ae30db04c627faaaf0b509e1ccc11a95b3fc3e83f855c00"}, + {file = "charset_normalizer-3.4.7-cp311-cp311-win_arm64.whl", hash = "sha256:d4a48e5b3c2a489fae013b7589308a40146ee081f6f509e047e0e096084ceca1"}, + {file = "charset_normalizer-3.4.7-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:eca9705049ad3c7345d574e3510665cb2cf844c2f2dcfe675332677f081cbd46"}, + {file = "charset_normalizer-3.4.7-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6178f72c5508bfc5fd446a5905e698c6212932f25bcdd4b47a757a50605a90e2"}, + {file = "charset_normalizer-3.4.7-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e1421b502d83040e6d7fb2fb18dff63957f720da3d77b2fbd3187ceb63755d7b"}, + {file = "charset_normalizer-3.4.7-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:edac0f1ab77644605be2cbba52e6b7f630731fc42b34cb0f634be1a6eface56a"}, + {file = "charset_normalizer-3.4.7-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5649fd1c7bade02f320a462fdefd0b4bd3ce036065836d4f42e0de958038e116"}, + {file = "charset_normalizer-3.4.7-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:203104ed3e428044fd943bc4bf45fa73c0730391f9621e37fe39ecf477b128cb"}, + {file = "charset_normalizer-3.4.7-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:298930cec56029e05497a76988377cbd7457ba864beeea92ad7e844fe74cd1f1"}, + {file = "charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:708838739abf24b2ceb208d0e22403dd018faeef86ddac04319a62ae884c4f15"}, + {file = "charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:0f7eb884681e3938906ed0434f20c63046eacd0111c4ba96f27b76084cd679f5"}, + {file = "charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4dc1e73c36828f982bfe79fadf5919923f8a6f4df2860804db9a98c48824ce8d"}, + {file = "charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:aed52fea0513bac0ccde438c188c8a471c4e0f457c2dd20cdbf6ea7a450046c7"}, + {file = "charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:fea24543955a6a729c45a73fe90e08c743f0b3334bbf3201e6c4bc1b0c7fa464"}, + {file = "charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:bb6d88045545b26da47aa879dd4a89a71d1dce0f0e549b1abcb31dfe4a8eac49"}, + {file = "charset_normalizer-3.4.7-cp312-cp312-win32.whl", hash = "sha256:2257141f39fe65a3fdf38aeccae4b953e5f3b3324f4ff0daf9f15b8518666a2c"}, + {file = "charset_normalizer-3.4.7-cp312-cp312-win_amd64.whl", hash = "sha256:5ed6ab538499c8644b8a3e18debabcd7ce684f3fa91cf867521a7a0279cab2d6"}, + {file = "charset_normalizer-3.4.7-cp312-cp312-win_arm64.whl", hash = "sha256:56be790f86bfb2c98fb742ce566dfb4816e5a83384616ab59c49e0604d49c51d"}, + {file = "charset_normalizer-3.4.7-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:f496c9c3cc02230093d8330875c4c3cdfc3b73612a5fd921c65d39cbcef08063"}, + {file = "charset_normalizer-3.4.7-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ea948db76d31190bf08bd371623927ee1339d5f2a0b4b1b4a4439a65298703c"}, + {file = "charset_normalizer-3.4.7-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a277ab8928b9f299723bc1a2dabb1265911b1a76341f90a510368ca44ad9ab66"}, + {file = "charset_normalizer-3.4.7-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3bec022aec2c514d9cf199522a802bd007cd588ab17ab2525f20f9c34d067c18"}, + {file = "charset_normalizer-3.4.7-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e044c39e41b92c845bc815e5ae4230804e8e7bc29e399b0437d64222d92809dd"}, + {file = "charset_normalizer-3.4.7-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:f495a1652cf3fbab2eb0639776dad966c2fb874d79d87ca07f9d5f059b8bd215"}, + {file = "charset_normalizer-3.4.7-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e712b419df8ba5e42b226c510472b37bd57b38e897d3eca5e8cfd410a29fa859"}, + {file = "charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7804338df6fcc08105c7745f1502ba68d900f45fd770d5bdd5288ddccb8a42d8"}, + {file = "charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:481551899c856c704d58119b5025793fa6730adda3571971af568f66d2424bb5"}, + {file = "charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f59099f9b66f0d7145115e6f80dd8b1d847176df89b234a5a6b3f00437aa0832"}, + {file = "charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:f59ad4c0e8f6bba240a9bb85504faa1ab438237199d4cce5f622761507b8f6a6"}, + {file = "charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:3dedcc22d73ec993f42055eff4fcfed9318d1eeb9a6606c55892a26964964e48"}, + {file = "charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:64f02c6841d7d83f832cd97ccf8eb8a906d06eb95d5276069175c696b024b60a"}, + {file = "charset_normalizer-3.4.7-cp313-cp313-win32.whl", hash = "sha256:4042d5c8f957e15221d423ba781e85d553722fc4113f523f2feb7b188cc34c5e"}, + {file = "charset_normalizer-3.4.7-cp313-cp313-win_amd64.whl", hash = "sha256:3946fa46a0cf3e4c8cb1cc52f56bb536310d34f25f01ca9b6c16afa767dab110"}, + {file = "charset_normalizer-3.4.7-cp313-cp313-win_arm64.whl", hash = "sha256:80d04837f55fc81da168b98de4f4b797ef007fc8a79ab71c6ec9bc4dd662b15b"}, + {file = "charset_normalizer-3.4.7-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:c36c333c39be2dbca264d7803333c896ab8fa7d4d6f0ab7edb7dfd7aea6e98c0"}, + {file = "charset_normalizer-3.4.7-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1c2aed2e5e41f24ea8ef1590b8e848a79b56f3a5564a65ceec43c9d692dc7d8a"}, + {file = "charset_normalizer-3.4.7-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:54523e136b8948060c0fa0bc7b1b50c32c186f2fceee897a495406bb6e311d2b"}, + {file = "charset_normalizer-3.4.7-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:715479b9a2802ecac752a3b0efa2b0b60285cf962ee38414211abdfccc233b41"}, + {file = "charset_normalizer-3.4.7-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bd6c2a1c7573c64738d716488d2cdd3c00e340e4835707d8fdb8dc1a66ef164e"}, + {file = "charset_normalizer-3.4.7-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:c45e9440fb78f8ddabcf714b68f936737a121355bf59f3907f4e17721b9d1aae"}, + {file = "charset_normalizer-3.4.7-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3534e7dcbdcf757da6b85a0bbf5b6868786d5982dd959b065e65481644817a18"}, + {file = "charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e8ac484bf18ce6975760921bb6148041faa8fef0547200386ea0b52b5d27bf7b"}, + {file = "charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:a5fe03b42827c13cdccd08e6c0247b6a6d4b5e3cdc53fd1749f5896adcdc2356"}, + {file = "charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:2d6eb928e13016cea4f1f21d1e10c1cebd5a421bc57ddf5b1142ae3f86824fab"}, + {file = "charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:e74327fb75de8986940def6e8dee4f127cc9752bee7355bb323cc5b2659b6d46"}, + {file = "charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:d6038d37043bced98a66e68d3aa2b6a35505dc01328cd65217cefe82f25def44"}, + {file = "charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7579e913a5339fb8fa133f6bbcfd8e6749696206cf05acdbdca71a1b436d8e72"}, + {file = "charset_normalizer-3.4.7-cp314-cp314-win32.whl", hash = "sha256:5b77459df20e08151cd6f8b9ef8ef1f961ef73d85c21a555c7eed5b79410ec10"}, + {file = "charset_normalizer-3.4.7-cp314-cp314-win_amd64.whl", hash = "sha256:92a0a01ead5e668468e952e4238cccd7c537364eb7d851ab144ab6627dbbe12f"}, + {file = "charset_normalizer-3.4.7-cp314-cp314-win_arm64.whl", hash = "sha256:67f6279d125ca0046a7fd386d01b311c6363844deac3e5b069b514ba3e63c246"}, + {file = "charset_normalizer-3.4.7-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:effc3f449787117233702311a1b7d8f59cba9ced946ba727bdc329ec69028e24"}, + {file = "charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fbccdc05410c9ee21bbf16a35f4c1d16123dcdeb8a1d38f33654fa21d0234f79"}, + {file = "charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:733784b6d6def852c814bce5f318d25da2ee65dd4839a0718641c696e09a2960"}, + {file = "charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a89c23ef8d2c6b27fd200a42aa4ac72786e7c60d40efdc76e6011260b6e949c4"}, + {file = "charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6c114670c45346afedc0d947faf3c7f701051d2518b943679c8ff88befe14f8e"}, + {file = "charset_normalizer-3.4.7-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:a180c5e59792af262bf263b21a3c49353f25945d8d9f70628e73de370d55e1e1"}, + {file = "charset_normalizer-3.4.7-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3c9a494bc5ec77d43cea229c4f6db1e4d8fe7e1bbffa8b6f0f0032430ff8ab44"}, + {file = "charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8d828b6667a32a728a1ad1d93957cdf37489c57b97ae6c4de2860fa749b8fc1e"}, + {file = "charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:cf1493cd8607bec4d8a7b9b004e699fcf8f9103a9284cc94962cb73d20f9d4a3"}, + {file = "charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:0c96c3b819b5c3e9e165495db84d41914d6894d55181d2d108cc1a69bfc9cce0"}, + {file = "charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:752a45dc4a6934060b3b0dab47e04edc3326575f82be64bc4fc293914566503e"}, + {file = "charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:8778f0c7a52e56f75d12dae53ae320fae900a8b9b4164b981b9c5ce059cd1fcb"}, + {file = "charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ce3412fbe1e31eb81ea42f4169ed94861c56e643189e1e75f0041f3fe7020abe"}, + {file = "charset_normalizer-3.4.7-cp314-cp314t-win32.whl", hash = "sha256:c03a41a8784091e67a39648f70c5f97b5b6a37f216896d44d2cdcb82615339a0"}, + {file = "charset_normalizer-3.4.7-cp314-cp314t-win_amd64.whl", hash = "sha256:03853ed82eeebbce3c2abfdbc98c96dc205f32a79627688ac9a27370ea61a49c"}, + {file = "charset_normalizer-3.4.7-cp314-cp314t-win_arm64.whl", hash = "sha256:c35abb8bfff0185efac5878da64c45dafd2b37fb0383add1be155a763c1f083d"}, + {file = "charset_normalizer-3.4.7-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e5f4d355f0a2b1a31bc3edec6795b46324349c9cb25eed068049e4f472fb4259"}, + {file = "charset_normalizer-3.4.7-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:16d971e29578a5e97d7117866d15889a4a07befe0e87e703ed63cd90cb348c01"}, + {file = "charset_normalizer-3.4.7-cp38-cp38-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:dca4bbc466a95ba9c0234ef56d7dd9509f63da22274589ebd4ed7f1f4d4c54e3"}, + {file = "charset_normalizer-3.4.7-cp38-cp38-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e80c8378d8f3d83cd3164da1ad2df9e37a666cdde7b1cb2298ed0b558064be30"}, + {file = "charset_normalizer-3.4.7-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:36836d6ff945a00b88ba1e4572d721e60b5b8c98c155d465f56ad19d68f23734"}, + {file = "charset_normalizer-3.4.7-cp38-cp38-manylinux_2_31_armv7l.whl", hash = "sha256:bd9b23791fe793e4968dba0c447e12f78e425c59fc0e3b97f6450f4781f3ee60"}, + {file = "charset_normalizer-3.4.7-cp38-cp38-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:aef65cd602a6d0e0ff6f9930fcb1c8fec60dd2cfcb6facaf4bdb0e5873042db0"}, + {file = "charset_normalizer-3.4.7-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:82b271f5137d07749f7bf32f70b17ab6eaabedd297e75dce75081a24f76eb545"}, + {file = "charset_normalizer-3.4.7-cp38-cp38-musllinux_1_2_armv7l.whl", hash = "sha256:1efde3cae86c8c273f1eb3b287be7d8499420cf2fe7585c41d370d3e790054a5"}, + {file = "charset_normalizer-3.4.7-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:c593052c465475e64bbfe5dbd81680f64a67fdc752c56d7a0ae205dc8aeefe0f"}, + {file = "charset_normalizer-3.4.7-cp38-cp38-musllinux_1_2_riscv64.whl", hash = "sha256:af21eb4409a119e365397b2adbaca4c9ccab56543a65d5dbd9f920d6ac29f686"}, + {file = "charset_normalizer-3.4.7-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:84c018e49c3bf790f9c2771c45e9313a08c2c2a6342b162cd650258b57817706"}, + {file = "charset_normalizer-3.4.7-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:dd915403e231e6b1809fe9b6d9fc55cf8fb5e02765ac625d9cd623342a7905d7"}, + {file = "charset_normalizer-3.4.7-cp38-cp38-win32.whl", hash = "sha256:320ade88cfb846b8cd6b4ddf5ee9e80ee0c1f52401f2456b84ae1ae6a1a5f207"}, + {file = "charset_normalizer-3.4.7-cp38-cp38-win_amd64.whl", hash = "sha256:1dc8b0ea451d6e69735094606991f32867807881400f808a106ee1d963c46a83"}, + {file = "charset_normalizer-3.4.7-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:177a0ba5f0211d488e295aaf82707237e331c24788d8d76c96c5a41594723217"}, + {file = "charset_normalizer-3.4.7-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6e0d51f618228538a3e8f46bd246f87a6cd030565e015803691603f55e12afb5"}, + {file = "charset_normalizer-3.4.7-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:14265bfe1f09498b9d8ec91e9ec9fa52775edf90fcbde092b25f4a33d444fea9"}, + {file = "charset_normalizer-3.4.7-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:87fad7d9ba98c86bcb41b2dc8dbb326619be2562af1f8ff50776a39e55721c5a"}, + {file = "charset_normalizer-3.4.7-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f22dec1690b584cea26fade98b2435c132c1b5f68e39f5a0b7627cd7ae31f1dc"}, + {file = "charset_normalizer-3.4.7-cp39-cp39-manylinux_2_31_armv7l.whl", hash = "sha256:d61f00a0869d77422d9b2aba989e2d24afa6ffd552af442e0e58de4f35ea6d00"}, + {file = "charset_normalizer-3.4.7-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6370e8686f662e6a3941ee48ed4742317cafbe5707e36406e9df792cdb535776"}, + {file = "charset_normalizer-3.4.7-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:a6c5863edfbe888d9eff9c8b8087354e27618d9da76425c119293f11712a6319"}, + {file = "charset_normalizer-3.4.7-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:ed065083d0898c9d5b4bbec7b026fd755ff7454e6e8b73a67f8c744b13986e24"}, + {file = "charset_normalizer-3.4.7-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:2cd4a60d0e2fb04537162c62bbbb4182f53541fe0ede35cdf270a1c1e723cc42"}, + {file = "charset_normalizer-3.4.7-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:813c0e0132266c08eb87469a642cb30aaff57c5f426255419572aaeceeaa7bf4"}, + {file = "charset_normalizer-3.4.7-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:07d9e39b01743c3717745f4c530a6349eadbfa043c7577eef86c502c15df2c67"}, + {file = "charset_normalizer-3.4.7-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:c0f081d69a6e58272819b70288d3221a6ee64b98df852631c80f293514d3b274"}, + {file = "charset_normalizer-3.4.7-cp39-cp39-win32.whl", hash = "sha256:8751d2787c9131302398b11e6c8068053dcb55d5a8964e114b6e196cf16cb366"}, + {file = "charset_normalizer-3.4.7-cp39-cp39-win_amd64.whl", hash = "sha256:12a6fff75f6bc66711b73a2f0addfc4c8c15a20e805146a02d147a318962c444"}, + {file = "charset_normalizer-3.4.7-cp39-cp39-win_arm64.whl", hash = "sha256:bb8cc7534f51d9a017b93e3e85b260924f909601c3df002bcdb58ddb4dc41a5c"}, + {file = "charset_normalizer-3.4.7-py3-none-any.whl", hash = "sha256:3dce51d0f5e7951f8bb4900c257dad282f49190fdbebecd4ba99bcc41fef404d"}, + {file = "charset_normalizer-3.4.7.tar.gz", hash = "sha256:ae89db9e5f98a11a4bf50407d4363e7b09b31e55bc117b4f7d80aab97ba009e5"}, ] [[package]] @@ -337,92 +385,6 @@ files = [ ] markers = {main = "(extra == \"latex\" or extra == \"docs\") and sys_platform == \"win32\"", dev = "sys_platform == \"win32\""} -[[package]] -name = "contourpy" -version = "1.3.0" -description = "Python library for calculating contours of 2D quadrilateral grids" -optional = true -python-versions = ">=3.9" -groups = ["main"] -markers = "python_version < \"3.11\" and extra == \"docs\"" -files = [ - {file = "contourpy-1.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:880ea32e5c774634f9fcd46504bf9f080a41ad855f4fef54f5380f5133d343c7"}, - {file = "contourpy-1.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:76c905ef940a4474a6289c71d53122a4f77766eef23c03cd57016ce19d0f7b42"}, - {file = "contourpy-1.3.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:92f8557cbb07415a4d6fa191f20fd9d2d9eb9c0b61d1b2f52a8926e43c6e9af7"}, - {file = "contourpy-1.3.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:36f965570cff02b874773c49bfe85562b47030805d7d8360748f3eca570f4cab"}, - {file = "contourpy-1.3.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cacd81e2d4b6f89c9f8a5b69b86490152ff39afc58a95af002a398273e5ce589"}, - {file = "contourpy-1.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:69375194457ad0fad3a839b9e29aa0b0ed53bb54db1bfb6c3ae43d111c31ce41"}, - {file = "contourpy-1.3.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:7a52040312b1a858b5e31ef28c2e865376a386c60c0e248370bbea2d3f3b760d"}, - {file = "contourpy-1.3.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3faeb2998e4fcb256542e8a926d08da08977f7f5e62cf733f3c211c2a5586223"}, - {file = "contourpy-1.3.0-cp310-cp310-win32.whl", hash = "sha256:36e0cff201bcb17a0a8ecc7f454fe078437fa6bda730e695a92f2d9932bd507f"}, - {file = "contourpy-1.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:87ddffef1dbe5e669b5c2440b643d3fdd8622a348fe1983fad7a0f0ccb1cd67b"}, - {file = "contourpy-1.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0fa4c02abe6c446ba70d96ece336e621efa4aecae43eaa9b030ae5fb92b309ad"}, - {file = "contourpy-1.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:834e0cfe17ba12f79963861e0f908556b2cedd52e1f75e6578801febcc6a9f49"}, - {file = "contourpy-1.3.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dbc4c3217eee163fa3984fd1567632b48d6dfd29216da3ded3d7b844a8014a66"}, - {file = "contourpy-1.3.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4865cd1d419e0c7a7bf6de1777b185eebdc51470800a9f42b9e9decf17762081"}, - {file = "contourpy-1.3.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:303c252947ab4b14c08afeb52375b26781ccd6a5ccd81abcdfc1fafd14cf93c1"}, - {file = "contourpy-1.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:637f674226be46f6ba372fd29d9523dd977a291f66ab2a74fbeb5530bb3f445d"}, - {file = "contourpy-1.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:76a896b2f195b57db25d6b44e7e03f221d32fe318d03ede41f8b4d9ba1bff53c"}, - {file = "contourpy-1.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e1fd23e9d01591bab45546c089ae89d926917a66dceb3abcf01f6105d927e2cb"}, - {file = "contourpy-1.3.0-cp311-cp311-win32.whl", hash = "sha256:d402880b84df3bec6eab53cd0cf802cae6a2ef9537e70cf75e91618a3801c20c"}, - {file = "contourpy-1.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:6cb6cc968059db9c62cb35fbf70248f40994dfcd7aa10444bbf8b3faeb7c2d67"}, - {file = "contourpy-1.3.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:570ef7cf892f0afbe5b2ee410c507ce12e15a5fa91017a0009f79f7d93a1268f"}, - {file = "contourpy-1.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:da84c537cb8b97d153e9fb208c221c45605f73147bd4cadd23bdae915042aad6"}, - {file = "contourpy-1.3.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0be4d8425bfa755e0fd76ee1e019636ccc7c29f77a7c86b4328a9eb6a26d0639"}, - {file = "contourpy-1.3.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9c0da700bf58f6e0b65312d0a5e695179a71d0163957fa381bb3c1f72972537c"}, - {file = "contourpy-1.3.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eb8b141bb00fa977d9122636b16aa67d37fd40a3d8b52dd837e536d64b9a4d06"}, - {file = "contourpy-1.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3634b5385c6716c258d0419c46d05c8aa7dc8cb70326c9a4fb66b69ad2b52e09"}, - {file = "contourpy-1.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0dce35502151b6bd35027ac39ba6e5a44be13a68f55735c3612c568cac3805fd"}, - {file = "contourpy-1.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:aea348f053c645100612b333adc5983d87be69acdc6d77d3169c090d3b01dc35"}, - {file = "contourpy-1.3.0-cp312-cp312-win32.whl", hash = "sha256:90f73a5116ad1ba7174341ef3ea5c3150ddf20b024b98fb0c3b29034752c8aeb"}, - {file = "contourpy-1.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:b11b39aea6be6764f84360fce6c82211a9db32a7c7de8fa6dd5397cf1d079c3b"}, - {file = "contourpy-1.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:3e1c7fa44aaae40a2247e2e8e0627f4bea3dd257014764aa644f319a5f8600e3"}, - {file = "contourpy-1.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:364174c2a76057feef647c802652f00953b575723062560498dc7930fc9b1cb7"}, - {file = "contourpy-1.3.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:32b238b3b3b649e09ce9aaf51f0c261d38644bdfa35cbaf7b263457850957a84"}, - {file = "contourpy-1.3.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d51fca85f9f7ad0b65b4b9fe800406d0d77017d7270d31ec3fb1cc07358fdea0"}, - {file = "contourpy-1.3.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:732896af21716b29ab3e988d4ce14bc5133733b85956316fb0c56355f398099b"}, - {file = "contourpy-1.3.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d73f659398a0904e125280836ae6f88ba9b178b2fed6884f3b1f95b989d2c8da"}, - {file = "contourpy-1.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c6c7c2408b7048082932cf4e641fa3b8ca848259212f51c8c59c45aa7ac18f14"}, - {file = "contourpy-1.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f317576606de89da6b7e0861cf6061f6146ead3528acabff9236458a6ba467f8"}, - {file = "contourpy-1.3.0-cp313-cp313-win32.whl", hash = "sha256:31cd3a85dbdf1fc002280c65caa7e2b5f65e4a973fcdf70dd2fdcb9868069294"}, - {file = "contourpy-1.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:4553c421929ec95fb07b3aaca0fae668b2eb5a5203d1217ca7c34c063c53d087"}, - {file = "contourpy-1.3.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:345af746d7766821d05d72cb8f3845dfd08dd137101a2cb9b24de277d716def8"}, - {file = "contourpy-1.3.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3bb3808858a9dc68f6f03d319acd5f1b8a337e6cdda197f02f4b8ff67ad2057b"}, - {file = "contourpy-1.3.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:420d39daa61aab1221567b42eecb01112908b2cab7f1b4106a52caaec8d36973"}, - {file = "contourpy-1.3.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4d63ee447261e963af02642ffcb864e5a2ee4cbfd78080657a9880b8b1868e18"}, - {file = "contourpy-1.3.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:167d6c890815e1dac9536dca00828b445d5d0df4d6a8c6adb4a7ec3166812fa8"}, - {file = "contourpy-1.3.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:710a26b3dc80c0e4febf04555de66f5fd17e9cf7170a7b08000601a10570bda6"}, - {file = "contourpy-1.3.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:75ee7cb1a14c617f34a51d11fa7524173e56551646828353c4af859c56b766e2"}, - {file = "contourpy-1.3.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:33c92cdae89ec5135d036e7218e69b0bb2851206077251f04a6c4e0e21f03927"}, - {file = "contourpy-1.3.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a11077e395f67ffc2c44ec2418cfebed032cd6da3022a94fc227b6faf8e2acb8"}, - {file = "contourpy-1.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e8134301d7e204c88ed7ab50028ba06c683000040ede1d617298611f9dc6240c"}, - {file = "contourpy-1.3.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e12968fdfd5bb45ffdf6192a590bd8ddd3ba9e58360b29683c6bb71a7b41edca"}, - {file = "contourpy-1.3.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fd2a0fc506eccaaa7595b7e1418951f213cf8255be2600f1ea1b61e46a60c55f"}, - {file = "contourpy-1.3.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4cfb5c62ce023dfc410d6059c936dcf96442ba40814aefbfa575425a3a7f19dc"}, - {file = "contourpy-1.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:68a32389b06b82c2fdd68276148d7b9275b5f5cf13e5417e4252f6d1a34f72a2"}, - {file = "contourpy-1.3.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:94e848a6b83da10898cbf1311a815f770acc9b6a3f2d646f330d57eb4e87592e"}, - {file = "contourpy-1.3.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:d78ab28a03c854a873787a0a42254a0ccb3cb133c672f645c9f9c8f3ae9d0800"}, - {file = "contourpy-1.3.0-cp39-cp39-win32.whl", hash = "sha256:81cb5ed4952aae6014bc9d0421dec7c5835c9c8c31cdf51910b708f548cf58e5"}, - {file = "contourpy-1.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:14e262f67bd7e6eb6880bc564dcda30b15e351a594657e55b7eec94b6ef72843"}, - {file = "contourpy-1.3.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:fe41b41505a5a33aeaed2a613dccaeaa74e0e3ead6dd6fd3a118fb471644fd6c"}, - {file = "contourpy-1.3.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eca7e17a65f72a5133bdbec9ecf22401c62bcf4821361ef7811faee695799779"}, - {file = "contourpy-1.3.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:1ec4dc6bf570f5b22ed0d7efba0dfa9c5b9e0431aeea7581aa217542d9e809a4"}, - {file = "contourpy-1.3.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:00ccd0dbaad6d804ab259820fa7cb0b8036bda0686ef844d24125d8287178ce0"}, - {file = "contourpy-1.3.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8ca947601224119117f7c19c9cdf6b3ab54c5726ef1d906aa4a69dfb6dd58102"}, - {file = "contourpy-1.3.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:c6ec93afeb848a0845a18989da3beca3eec2c0f852322efe21af1931147d12cb"}, - {file = "contourpy-1.3.0.tar.gz", hash = "sha256:7ffa0db17717a8ffb127efd0c95a4362d996b892c2904db72428d5b52e1938a4"}, -] - -[package.dependencies] -numpy = ">=1.23" - -[package.extras] -bokeh = ["bokeh", "selenium"] -docs = ["furo", "sphinx (>=7.2)", "sphinx-copybutton"] -mypy = ["contourpy[bokeh,docs]", "docutils-stubs", "mypy (==1.11.1)", "types-Pillow"] -test = ["Pillow", "contourpy[test-no-images]", "matplotlib"] -test-no-images = ["pytest", "pytest-cov", "pytest-rerunfailures", "pytest-xdist", "wurlitzer"] - [[package]] name = "contourpy" version = "1.3.3" @@ -430,7 +392,7 @@ description = "Python library for calculating contours of 2D quadrilateral grids optional = true python-versions = ">=3.11" groups = ["main"] -markers = "python_version >= \"3.11\" and extra == \"docs\"" +markers = "extra == \"docs\"" files = [ {file = "contourpy-1.3.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:709a48ef9a690e1343202916450bc48b9e51c049b089c7f79a267b46cffcdaa1"}, {file = "contourpy-1.3.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:23416f38bfd74d5d28ab8429cc4d63fa67d5068bd711a85edb1c3fb0c3e2f381"}, @@ -518,121 +480,120 @@ test-no-images = ["pytest", "pytest-cov", "pytest-rerunfailures", "pytest-xdist" [[package]] name = "coverage" -version = "7.10.7" +version = "7.13.5" description = "Code coverage measurement for Python" optional = false -python-versions = ">=3.9" +python-versions = ">=3.10" groups = ["dev"] files = [ - {file = "coverage-7.10.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:fc04cc7a3db33664e0c2d10eb8990ff6b3536f6842c9590ae8da4c614b9ed05a"}, - {file = "coverage-7.10.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e201e015644e207139f7e2351980feb7040e6f4b2c2978892f3e3789d1c125e5"}, - {file = "coverage-7.10.7-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:240af60539987ced2c399809bd34f7c78e8abe0736af91c3d7d0e795df633d17"}, - {file = "coverage-7.10.7-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:8421e088bc051361b01c4b3a50fd39a4b9133079a2229978d9d30511fd05231b"}, - {file = "coverage-7.10.7-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6be8ed3039ae7f7ac5ce058c308484787c86e8437e72b30bf5e88b8ea10f3c87"}, - {file = "coverage-7.10.7-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e28299d9f2e889e6d51b1f043f58d5f997c373cc12e6403b90df95b8b047c13e"}, - {file = "coverage-7.10.7-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c4e16bd7761c5e454f4efd36f345286d6f7c5fa111623c355691e2755cae3b9e"}, - {file = "coverage-7.10.7-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:b1c81d0e5e160651879755c9c675b974276f135558cf4ba79fee7b8413a515df"}, - {file = "coverage-7.10.7-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:606cc265adc9aaedcc84f1f064f0e8736bc45814f15a357e30fca7ecc01504e0"}, - {file = "coverage-7.10.7-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:10b24412692df990dbc34f8fb1b6b13d236ace9dfdd68df5b28c2e39cafbba13"}, - {file = "coverage-7.10.7-cp310-cp310-win32.whl", hash = "sha256:b51dcd060f18c19290d9b8a9dd1e0181538df2ce0717f562fff6cf74d9fc0b5b"}, - {file = "coverage-7.10.7-cp310-cp310-win_amd64.whl", hash = "sha256:3a622ac801b17198020f09af3eaf45666b344a0d69fc2a6ffe2ea83aeef1d807"}, - {file = "coverage-7.10.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a609f9c93113be646f44c2a0256d6ea375ad047005d7f57a5c15f614dc1b2f59"}, - {file = "coverage-7.10.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:65646bb0359386e07639c367a22cf9b5bf6304e8630b565d0626e2bdf329227a"}, - {file = "coverage-7.10.7-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:5f33166f0dfcce728191f520bd2692914ec70fac2713f6bf3ce59c3deacb4699"}, - {file = "coverage-7.10.7-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:35f5e3f9e455bb17831876048355dca0f758b6df22f49258cb5a91da23ef437d"}, - {file = "coverage-7.10.7-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4da86b6d62a496e908ac2898243920c7992499c1712ff7c2b6d837cc69d9467e"}, - {file = "coverage-7.10.7-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6b8b09c1fad947c84bbbc95eca841350fad9cbfa5a2d7ca88ac9f8d836c92e23"}, - {file = "coverage-7.10.7-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:4376538f36b533b46f8971d3a3e63464f2c7905c9800db97361c43a2b14792ab"}, - {file = "coverage-7.10.7-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:121da30abb574f6ce6ae09840dae322bef734480ceafe410117627aa54f76d82"}, - {file = "coverage-7.10.7-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:88127d40df529336a9836870436fc2751c339fbaed3a836d42c93f3e4bd1d0a2"}, - {file = "coverage-7.10.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ba58bbcd1b72f136080c0bccc2400d66cc6115f3f906c499013d065ac33a4b61"}, - {file = "coverage-7.10.7-cp311-cp311-win32.whl", hash = "sha256:972b9e3a4094b053a4e46832b4bc829fc8a8d347160eb39d03f1690316a99c14"}, - {file = "coverage-7.10.7-cp311-cp311-win_amd64.whl", hash = "sha256:a7b55a944a7f43892e28ad4bc0561dfd5f0d73e605d1aa5c3c976b52aea121d2"}, - {file = "coverage-7.10.7-cp311-cp311-win_arm64.whl", hash = "sha256:736f227fb490f03c6488f9b6d45855f8e0fd749c007f9303ad30efab0e73c05a"}, - {file = "coverage-7.10.7-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7bb3b9ddb87ef7725056572368040c32775036472d5a033679d1fa6c8dc08417"}, - {file = "coverage-7.10.7-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:18afb24843cbc175687225cab1138c95d262337f5473512010e46831aa0c2973"}, - {file = "coverage-7.10.7-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:399a0b6347bcd3822be369392932884b8216d0944049ae22925631a9b3d4ba4c"}, - {file = "coverage-7.10.7-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:314f2c326ded3f4b09be11bc282eb2fc861184bc95748ae67b360ac962770be7"}, - {file = "coverage-7.10.7-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c41e71c9cfb854789dee6fc51e46743a6d138b1803fab6cb860af43265b42ea6"}, - {file = "coverage-7.10.7-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc01f57ca26269c2c706e838f6422e2a8788e41b3e3c65e2f41148212e57cd59"}, - {file = "coverage-7.10.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a6442c59a8ac8b85812ce33bc4d05bde3fb22321fa8294e2a5b487c3505f611b"}, - {file = "coverage-7.10.7-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:78a384e49f46b80fb4c901d52d92abe098e78768ed829c673fbb53c498bef73a"}, - {file = "coverage-7.10.7-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:5e1e9802121405ede4b0133aa4340ad8186a1d2526de5b7c3eca519db7bb89fb"}, - {file = "coverage-7.10.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d41213ea25a86f69efd1575073d34ea11aabe075604ddf3d148ecfec9e1e96a1"}, - {file = "coverage-7.10.7-cp312-cp312-win32.whl", hash = "sha256:77eb4c747061a6af8d0f7bdb31f1e108d172762ef579166ec84542f711d90256"}, - {file = "coverage-7.10.7-cp312-cp312-win_amd64.whl", hash = "sha256:f51328ffe987aecf6d09f3cd9d979face89a617eacdaea43e7b3080777f647ba"}, - {file = "coverage-7.10.7-cp312-cp312-win_arm64.whl", hash = "sha256:bda5e34f8a75721c96085903c6f2197dc398c20ffd98df33f866a9c8fd95f4bf"}, - {file = "coverage-7.10.7-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:981a651f543f2854abd3b5fcb3263aac581b18209be49863ba575de6edf4c14d"}, - {file = "coverage-7.10.7-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:73ab1601f84dc804f7812dc297e93cd99381162da39c47040a827d4e8dafe63b"}, - {file = "coverage-7.10.7-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:a8b6f03672aa6734e700bbcd65ff050fd19cddfec4b031cc8cf1c6967de5a68e"}, - {file = "coverage-7.10.7-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:10b6ba00ab1132a0ce4428ff68cf50a25efd6840a42cdf4239c9b99aad83be8b"}, - {file = "coverage-7.10.7-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c79124f70465a150e89340de5963f936ee97097d2ef76c869708c4248c63ca49"}, - {file = "coverage-7.10.7-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:69212fbccdbd5b0e39eac4067e20a4a5256609e209547d86f740d68ad4f04911"}, - {file = "coverage-7.10.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7ea7c6c9d0d286d04ed3541747e6597cbe4971f22648b68248f7ddcd329207f0"}, - {file = "coverage-7.10.7-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b9be91986841a75042b3e3243d0b3cb0b2434252b977baaf0cd56e960fe1e46f"}, - {file = "coverage-7.10.7-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:b281d5eca50189325cfe1f365fafade89b14b4a78d9b40b05ddd1fc7d2a10a9c"}, - {file = "coverage-7.10.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:99e4aa63097ab1118e75a848a28e40d68b08a5e19ce587891ab7fd04475e780f"}, - {file = "coverage-7.10.7-cp313-cp313-win32.whl", hash = "sha256:dc7c389dce432500273eaf48f410b37886be9208b2dd5710aaf7c57fd442c698"}, - {file = "coverage-7.10.7-cp313-cp313-win_amd64.whl", hash = "sha256:cac0fdca17b036af3881a9d2729a850b76553f3f716ccb0360ad4dbc06b3b843"}, - {file = "coverage-7.10.7-cp313-cp313-win_arm64.whl", hash = "sha256:4b6f236edf6e2f9ae8fcd1332da4e791c1b6ba0dc16a2dc94590ceccb482e546"}, - {file = "coverage-7.10.7-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:a0ec07fd264d0745ee396b666d47cef20875f4ff2375d7c4f58235886cc1ef0c"}, - {file = "coverage-7.10.7-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:dd5e856ebb7bfb7672b0086846db5afb4567a7b9714b8a0ebafd211ec7ce6a15"}, - {file = "coverage-7.10.7-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:f57b2a3c8353d3e04acf75b3fed57ba41f5c0646bbf1d10c7c282291c97936b4"}, - {file = "coverage-7.10.7-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:1ef2319dd15a0b009667301a3f84452a4dc6fddfd06b0c5c53ea472d3989fbf0"}, - {file = "coverage-7.10.7-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:83082a57783239717ceb0ad584de3c69cf581b2a95ed6bf81ea66034f00401c0"}, - {file = "coverage-7.10.7-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:50aa94fb1fb9a397eaa19c0d5ec15a5edd03a47bf1a3a6111a16b36e190cff65"}, - {file = "coverage-7.10.7-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:2120043f147bebb41c85b97ac45dd173595ff14f2a584f2963891cbcc3091541"}, - {file = "coverage-7.10.7-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:2fafd773231dd0378fdba66d339f84904a8e57a262f583530f4f156ab83863e6"}, - {file = "coverage-7.10.7-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:0b944ee8459f515f28b851728ad224fa2d068f1513ef6b7ff1efafeb2185f999"}, - {file = "coverage-7.10.7-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4b583b97ab2e3efe1b3e75248a9b333bd3f8b0b1b8e5b45578e05e5850dfb2c2"}, - {file = "coverage-7.10.7-cp313-cp313t-win32.whl", hash = "sha256:2a78cd46550081a7909b3329e2266204d584866e8d97b898cd7fb5ac8d888b1a"}, - {file = "coverage-7.10.7-cp313-cp313t-win_amd64.whl", hash = "sha256:33a5e6396ab684cb43dc7befa386258acb2d7fae7f67330ebb85ba4ea27938eb"}, - {file = "coverage-7.10.7-cp313-cp313t-win_arm64.whl", hash = "sha256:86b0e7308289ddde73d863b7683f596d8d21c7d8664ce1dee061d0bcf3fbb4bb"}, - {file = "coverage-7.10.7-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:b06f260b16ead11643a5a9f955bd4b5fd76c1a4c6796aeade8520095b75de520"}, - {file = "coverage-7.10.7-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:212f8f2e0612778f09c55dd4872cb1f64a1f2b074393d139278ce902064d5b32"}, - {file = "coverage-7.10.7-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3445258bcded7d4aa630ab8296dea4d3f15a255588dd535f980c193ab6b95f3f"}, - {file = "coverage-7.10.7-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:bb45474711ba385c46a0bfe696c695a929ae69ac636cda8f532be9e8c93d720a"}, - {file = "coverage-7.10.7-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:813922f35bd800dca9994c5971883cbc0d291128a5de6b167c7aa697fcf59360"}, - {file = "coverage-7.10.7-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:93c1b03552081b2a4423091d6fb3787265b8f86af404cff98d1b5342713bdd69"}, - {file = "coverage-7.10.7-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:cc87dd1b6eaf0b848eebb1c86469b9f72a1891cb42ac7adcfbce75eadb13dd14"}, - {file = "coverage-7.10.7-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:39508ffda4f343c35f3236fe8d1a6634a51f4581226a1262769d7f970e73bffe"}, - {file = "coverage-7.10.7-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:925a1edf3d810537c5a3abe78ec5530160c5f9a26b1f4270b40e62cc79304a1e"}, - {file = "coverage-7.10.7-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2c8b9a0636f94c43cd3576811e05b89aa9bc2d0a85137affc544ae5cb0e4bfbd"}, - {file = "coverage-7.10.7-cp314-cp314-win32.whl", hash = "sha256:b7b8288eb7cdd268b0304632da8cb0bb93fadcfec2fe5712f7b9cc8f4d487be2"}, - {file = "coverage-7.10.7-cp314-cp314-win_amd64.whl", hash = "sha256:1ca6db7c8807fb9e755d0379ccc39017ce0a84dcd26d14b5a03b78563776f681"}, - {file = "coverage-7.10.7-cp314-cp314-win_arm64.whl", hash = "sha256:097c1591f5af4496226d5783d036bf6fd6cd0cbc132e071b33861de756efb880"}, - {file = "coverage-7.10.7-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:a62c6ef0d50e6de320c270ff91d9dd0a05e7250cac2a800b7784bae474506e63"}, - {file = "coverage-7.10.7-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:9fa6e4dd51fe15d8738708a973470f67a855ca50002294852e9571cdbd9433f2"}, - {file = "coverage-7.10.7-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:8fb190658865565c549b6b4706856d6a7b09302c797eb2cf8e7fe9dabb043f0d"}, - {file = "coverage-7.10.7-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:affef7c76a9ef259187ef31599a9260330e0335a3011732c4b9effa01e1cd6e0"}, - {file = "coverage-7.10.7-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6e16e07d85ca0cf8bafe5f5d23a0b850064e8e945d5677492b06bbe6f09cc699"}, - {file = "coverage-7.10.7-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:03ffc58aacdf65d2a82bbeb1ffe4d01ead4017a21bfd0454983b88ca73af94b9"}, - {file = "coverage-7.10.7-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:1b4fd784344d4e52647fd7857b2af5b3fbe6c239b0b5fa63e94eb67320770e0f"}, - {file = "coverage-7.10.7-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:0ebbaddb2c19b71912c6f2518e791aa8b9f054985a0769bdb3a53ebbc765c6a1"}, - {file = "coverage-7.10.7-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:a2d9a3b260cc1d1dbdb1c582e63ddcf5363426a1a68faa0f5da28d8ee3c722a0"}, - {file = "coverage-7.10.7-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:a3cc8638b2480865eaa3926d192e64ce6c51e3d29c849e09d5b4ad95efae5399"}, - {file = "coverage-7.10.7-cp314-cp314t-win32.whl", hash = "sha256:67f8c5cbcd3deb7a60b3345dffc89a961a484ed0af1f6f73de91705cc6e31235"}, - {file = "coverage-7.10.7-cp314-cp314t-win_amd64.whl", hash = "sha256:e1ed71194ef6dea7ed2d5cb5f7243d4bcd334bfb63e59878519be558078f848d"}, - {file = "coverage-7.10.7-cp314-cp314t-win_arm64.whl", hash = "sha256:7fe650342addd8524ca63d77b2362b02345e5f1a093266787d210c70a50b471a"}, - {file = "coverage-7.10.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fff7b9c3f19957020cac546c70025331113d2e61537f6e2441bc7657913de7d3"}, - {file = "coverage-7.10.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:bc91b314cef27742da486d6839b677b3f2793dfe52b51bbbb7cf736d5c29281c"}, - {file = "coverage-7.10.7-cp39-cp39-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:567f5c155eda8df1d3d439d40a45a6a5f029b429b06648235f1e7e51b522b396"}, - {file = "coverage-7.10.7-cp39-cp39-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2af88deffcc8a4d5974cf2d502251bc3b2db8461f0b66d80a449c33757aa9f40"}, - {file = "coverage-7.10.7-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c7315339eae3b24c2d2fa1ed7d7a38654cba34a13ef19fbcb9425da46d3dc594"}, - {file = "coverage-7.10.7-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:912e6ebc7a6e4adfdbb1aec371ad04c68854cd3bf3608b3514e7ff9062931d8a"}, - {file = "coverage-7.10.7-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:f49a05acd3dfe1ce9715b657e28d138578bc40126760efb962322c56e9ca344b"}, - {file = "coverage-7.10.7-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:cce2109b6219f22ece99db7644b9622f54a4e915dad65660ec435e89a3ea7cc3"}, - {file = "coverage-7.10.7-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:f3c887f96407cea3916294046fc7dab611c2552beadbed4ea901cbc6a40cc7a0"}, - {file = "coverage-7.10.7-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:635adb9a4507c9fd2ed65f39693fa31c9a3ee3a8e6dc64df033e8fdf52a7003f"}, - {file = "coverage-7.10.7-cp39-cp39-win32.whl", hash = "sha256:5a02d5a850e2979b0a014c412573953995174743a3f7fa4ea5a6e9a3c5617431"}, - {file = "coverage-7.10.7-cp39-cp39-win_amd64.whl", hash = "sha256:c134869d5ffe34547d14e174c866fd8fe2254918cc0a95e99052903bc1543e07"}, - {file = "coverage-7.10.7-py3-none-any.whl", hash = "sha256:f7941f6f2fe6dd6807a1208737b8a0cbcf1cc6d7b07d24998ad2d63590868260"}, - {file = "coverage-7.10.7.tar.gz", hash = "sha256:f4ab143ab113be368a3e9b795f9cd7906c5ef407d6173fe9675a902e1fffc239"}, + {file = "coverage-7.13.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e0723d2c96324561b9aa76fb982406e11d93cdb388a7a7da2b16e04719cf7ca5"}, + {file = "coverage-7.13.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:52f444e86475992506b32d4e5ca55c24fc88d73bcbda0e9745095b28ef4dc0cf"}, + {file = "coverage-7.13.5-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:704de6328e3d612a8f6c07000a878ff38181ec3263d5a11da1db294fa6a9bdf8"}, + {file = "coverage-7.13.5-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:a1a6d79a14e1ec1832cabc833898636ad5f3754a678ef8bb4908515208bf84f4"}, + {file = "coverage-7.13.5-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:79060214983769c7ba3f0cee10b54c97609dca4d478fa1aa32b914480fd5738d"}, + {file = "coverage-7.13.5-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:356e76b46783a98c2a2fe81ec79df4883a1e62895ea952968fb253c114e7f930"}, + {file = "coverage-7.13.5-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0cef0cdec915d11254a7f549c1170afecce708d30610c6abdded1f74e581666d"}, + {file = "coverage-7.13.5-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:dc022073d063b25a402454e5712ef9e007113e3a676b96c5f29b2bda29352f40"}, + {file = "coverage-7.13.5-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:9b74db26dfea4f4e50d48a4602207cd1e78be33182bc9cbf22da94f332f99878"}, + {file = "coverage-7.13.5-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:ad146744ca4fd09b50c482650e3c1b1f4dfa1d4792e0a04a369c7f23336f0400"}, + {file = "coverage-7.13.5-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:c555b48be1853fe3997c11c4bd521cdd9a9612352de01fa4508f16ec341e6fe0"}, + {file = "coverage-7.13.5-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7034b5c56a58ae5e85f23949d52c14aca2cfc6848a31764995b7de88f13a1ea0"}, + {file = "coverage-7.13.5-cp310-cp310-win32.whl", hash = "sha256:eb7fdf1ef130660e7415e0253a01a7d5a88c9c4d158bcf75cbbd922fd65a5b58"}, + {file = "coverage-7.13.5-cp310-cp310-win_amd64.whl", hash = "sha256:3e1bb5f6c78feeb1be3475789b14a0f0a5b47d505bfc7267126ccbd50289999e"}, + {file = "coverage-7.13.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:66a80c616f80181f4d643b0f9e709d97bcea413ecd9631e1dedc7401c8e6695d"}, + {file = "coverage-7.13.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:145ede53ccbafb297c1c9287f788d1bc3efd6c900da23bf6931b09eafc931587"}, + {file = "coverage-7.13.5-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:0672854dc733c342fa3e957e0605256d2bf5934feeac328da9e0b5449634a642"}, + {file = "coverage-7.13.5-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:ec10e2a42b41c923c2209b846126c6582db5e43a33157e9870ba9fb70dc7854b"}, + {file = "coverage-7.13.5-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:be3d4bbad9d4b037791794ddeedd7d64a56f5933a2c1373e18e9e568b9141686"}, + {file = "coverage-7.13.5-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4d2afbc5cc54d286bfb54541aa50b64cdb07a718227168c87b9e2fb8f25e1743"}, + {file = "coverage-7.13.5-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3ad050321264c49c2fa67bb599100456fc51d004b82534f379d16445da40fb75"}, + {file = "coverage-7.13.5-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7300c8a6d13335b29bb76d7651c66af6bd8658517c43499f110ddc6717bfc209"}, + {file = "coverage-7.13.5-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:eb07647a5738b89baab047f14edd18ded523de60f3b30e75c2acc826f79c839a"}, + {file = "coverage-7.13.5-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:9adb6688e3b53adffefd4a52d72cbd8b02602bfb8f74dcd862337182fd4d1a4e"}, + {file = "coverage-7.13.5-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:7c8d4bc913dd70b93488d6c496c77f3aff5ea99a07e36a18f865bca55adef8bd"}, + {file = "coverage-7.13.5-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0e3c426ffc4cd952f54ee9ffbdd10345709ecc78a3ecfd796a57236bfad0b9b8"}, + {file = "coverage-7.13.5-cp311-cp311-win32.whl", hash = "sha256:259b69bb83ad9894c4b25be2528139eecba9a82646ebdda2d9db1ba28424a6bf"}, + {file = "coverage-7.13.5-cp311-cp311-win_amd64.whl", hash = "sha256:258354455f4e86e3e9d0d17571d522e13b4e1e19bf0f8596bcf9476d61e7d8a9"}, + {file = "coverage-7.13.5-cp311-cp311-win_arm64.whl", hash = "sha256:bff95879c33ec8da99fc9b6fe345ddb5be6414b41d6d1ad1c8f188d26f36e028"}, + {file = "coverage-7.13.5-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:460cf0114c5016fa841214ff5564aa4864f11948da9440bc97e21ad1f4ba1e01"}, + {file = "coverage-7.13.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0e223ce4b4ed47f065bfb123687686512e37629be25cc63728557ae7db261422"}, + {file = "coverage-7.13.5-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:6e3370441f4513c6252bf042b9c36d22491142385049243253c7e48398a15a9f"}, + {file = "coverage-7.13.5-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:03ccc709a17a1de074fb1d11f217342fb0d2b1582ed544f554fc9fc3f07e95f5"}, + {file = "coverage-7.13.5-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3f4818d065964db3c1c66dc0fbdac5ac692ecbc875555e13374fdbe7eedb4376"}, + {file = "coverage-7.13.5-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:012d5319e66e9d5a218834642d6c35d265515a62f01157a45bcc036ecf947256"}, + {file = "coverage-7.13.5-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:8dd02af98971bdb956363e4827d34425cb3df19ee550ef92855b0acb9c7ce51c"}, + {file = "coverage-7.13.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f08fd75c50a760c7eb068ae823777268daaf16a80b918fa58eea888f8e3919f5"}, + {file = "coverage-7.13.5-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:843ea8643cf967d1ac7e8ecd4bb00c99135adf4816c0c0593fdcc47b597fcf09"}, + {file = "coverage-7.13.5-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:9d44d7aa963820b1b971dbecd90bfe5fe8f81cff79787eb6cca15750bd2f79b9"}, + {file = "coverage-7.13.5-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:7132bed4bd7b836200c591410ae7d97bf7ae8be6fc87d160b2bd881df929e7bf"}, + {file = "coverage-7.13.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a698e363641b98843c517817db75373c83254781426e94ada3197cabbc2c919c"}, + {file = "coverage-7.13.5-cp312-cp312-win32.whl", hash = "sha256:bdba0a6b8812e8c7df002d908a9a2ea3c36e92611b5708633c50869e6d922fdf"}, + {file = "coverage-7.13.5-cp312-cp312-win_amd64.whl", hash = "sha256:d2c87e0c473a10bffe991502eac389220533024c8082ec1ce849f4218dded810"}, + {file = "coverage-7.13.5-cp312-cp312-win_arm64.whl", hash = "sha256:bf69236a9a81bdca3bff53796237aab096cdbf8d78a66ad61e992d9dac7eb2de"}, + {file = "coverage-7.13.5-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5ec4af212df513e399cf11610cc27063f1586419e814755ab362e50a85ea69c1"}, + {file = "coverage-7.13.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:941617e518602e2d64942c88ec8499f7fbd49d3f6c4327d3a71d43a1973032f3"}, + {file = "coverage-7.13.5-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:da305e9937617ee95c2e39d8ff9f040e0487cbf1ac174f777ed5eddd7a7c1f26"}, + {file = "coverage-7.13.5-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:78e696e1cc714e57e8b25760b33a8b1026b7048d270140d25dafe1b0a1ee05a3"}, + {file = "coverage-7.13.5-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:02ca0eed225b2ff301c474aeeeae27d26e2537942aa0f87491d3e147e784a82b"}, + {file = "coverage-7.13.5-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:04690832cbea4e4663d9149e05dba142546ca05cb1848816760e7f58285c970a"}, + {file = "coverage-7.13.5-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0590e44dd2745c696a778f7bab6aa95256de2cbc8b8cff4f7db8ff09813d6969"}, + {file = "coverage-7.13.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d7cfad2d6d81dd298ab6b89fe72c3b7b05ec7544bdda3b707ddaecff8d25c161"}, + {file = "coverage-7.13.5-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:e092b9499de38ae0fbfbc603a74660eb6ff3e869e507b50d85a13b6db9863e15"}, + {file = "coverage-7.13.5-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:48c39bc4a04d983a54a705a6389512883d4a3b9862991b3617d547940e9f52b1"}, + {file = "coverage-7.13.5-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:2d3807015f138ffea1ed9afeeb8624fd781703f2858b62a8dd8da5a0994c57b6"}, + {file = "coverage-7.13.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ee2aa19e03161671ec964004fb74b2257805d9710bf14a5c704558b9d8dbaf17"}, + {file = "coverage-7.13.5-cp313-cp313-win32.whl", hash = "sha256:ce1998c0483007608c8382f4ff50164bfc5bd07a2246dd272aa4043b75e61e85"}, + {file = "coverage-7.13.5-cp313-cp313-win_amd64.whl", hash = "sha256:631efb83f01569670a5e866ceb80fe483e7c159fac6f167e6571522636104a0b"}, + {file = "coverage-7.13.5-cp313-cp313-win_arm64.whl", hash = "sha256:f4cd16206ad171cbc2470dbea9103cf9a7607d5fe8c242fdf1edf36174020664"}, + {file = "coverage-7.13.5-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0428cbef5783ad91fe240f673cc1f76b25e74bbfe1a13115e4aa30d3f538162d"}, + {file = "coverage-7.13.5-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e0b216a19534b2427cc201a26c25da4a48633f29a487c61258643e89d28200c0"}, + {file = "coverage-7.13.5-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:972a9cd27894afe4bc2b1480107054e062df08e671df7c2f18c205e805ccd806"}, + {file = "coverage-7.13.5-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:4b59148601efcd2bac8c4dbf1f0ad6391693ccf7a74b8205781751637076aee3"}, + {file = "coverage-7.13.5-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:505d7083c8b0c87a8fa8c07370c285847c1f77739b22e299ad75a6af6c32c5c9"}, + {file = "coverage-7.13.5-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:60365289c3741e4db327e7baff2a4aaacf22f788e80fa4683393891b70a89fbd"}, + {file = "coverage-7.13.5-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:1b88c69c8ef5d4b6fe7dea66d6636056a0f6a7527c440e890cf9259011f5e606"}, + {file = "coverage-7.13.5-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:5b13955d31d1633cf9376908089b7cebe7d15ddad7aeaabcbe969a595a97e95e"}, + {file = "coverage-7.13.5-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:f70c9ab2595c56f81a89620e22899eea8b212a4041bd728ac6f4a28bf5d3ddd0"}, + {file = "coverage-7.13.5-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:084b84a8c63e8d6fc7e3931b316a9bcafca1458d753c539db82d31ed20091a87"}, + {file = "coverage-7.13.5-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:ad14385487393e386e2ea988b09d62dd42c397662ac2dabc3832d71253eee479"}, + {file = "coverage-7.13.5-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:7f2c47b36fe7709a6e83bfadf4eefb90bd25fbe4014d715224c4316f808e59a2"}, + {file = "coverage-7.13.5-cp313-cp313t-win32.whl", hash = "sha256:67e9bc5449801fad0e5dff329499fb090ba4c5800b86805c80617b4e29809b2a"}, + {file = "coverage-7.13.5-cp313-cp313t-win_amd64.whl", hash = "sha256:da86cdcf10d2519e10cabb8ac2de03da1bcb6e4853790b7fbd48523332e3a819"}, + {file = "coverage-7.13.5-cp313-cp313t-win_arm64.whl", hash = "sha256:0ecf12ecb326fe2c339d93fc131816f3a7367d223db37817208905c89bded911"}, + {file = "coverage-7.13.5-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:fbabfaceaeb587e16f7008f7795cd80d20ec548dc7f94fbb0d4ec2e038ce563f"}, + {file = "coverage-7.13.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:9bb2a28101a443669a423b665939381084412b81c3f8c0fcfbac57f4e30b5b8e"}, + {file = "coverage-7.13.5-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:bd3a2fbc1c6cccb3c5106140d87cc6a8715110373ef42b63cf5aea29df8c217a"}, + {file = "coverage-7.13.5-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6c36ddb64ed9d7e496028d1d00dfec3e428e0aabf4006583bb1839958d280510"}, + {file = "coverage-7.13.5-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:380e8e9084d8eb38db3a9176a1a4f3c0082c3806fa0dc882d1d87abc3c789247"}, + {file = "coverage-7.13.5-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e808af52a0513762df4d945ea164a24b37f2f518cbe97e03deaa0ee66139b4d6"}, + {file = "coverage-7.13.5-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e301d30dd7e95ae068671d746ba8c34e945a82682e62918e41b2679acd2051a0"}, + {file = "coverage-7.13.5-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:800bc829053c80d240a687ceeb927a94fd108bbdc68dfbe505d0d75ab578a882"}, + {file = "coverage-7.13.5-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:0b67af5492adb31940ee418a5a655c28e48165da5afab8c7fa6fd72a142f8740"}, + {file = "coverage-7.13.5-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:c9136ff29c3a91e25b1d1552b5308e53a1e0653a23e53b6366d7c2dcbbaf8a16"}, + {file = "coverage-7.13.5-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:cff784eef7f0b8f6cb28804fbddcfa99f89efe4cc35fb5627e3ac58f91ed3ac0"}, + {file = "coverage-7.13.5-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:68a4953be99b17ac3c23b6efbc8a38330d99680c9458927491d18700ef23ded0"}, + {file = "coverage-7.13.5-cp314-cp314-win32.whl", hash = "sha256:35a31f2b1578185fbe6aa2e74cea1b1d0bbf4c552774247d9160d29b80ed56cc"}, + {file = "coverage-7.13.5-cp314-cp314-win_amd64.whl", hash = "sha256:2aa055ae1857258f9e0045be26a6d62bdb47a72448b62d7b55f4820f361a2633"}, + {file = "coverage-7.13.5-cp314-cp314-win_arm64.whl", hash = "sha256:1b11eef33edeae9d142f9b4358edb76273b3bfd30bc3df9a4f95d0e49caf94e8"}, + {file = "coverage-7.13.5-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:10a0c37f0b646eaff7cce1874c31d1f1ccb297688d4c747291f4f4c70741cc8b"}, + {file = "coverage-7.13.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:b5db73ba3c41c7008037fa731ad5459fc3944cb7452fc0aa9f822ad3533c583c"}, + {file = "coverage-7.13.5-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:750db93a81e3e5a9831b534be7b1229df848b2e125a604fe6651e48aa070e5f9"}, + {file = "coverage-7.13.5-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:9ddb4f4a5479f2539644be484da179b653273bca1a323947d48ab107b3ed1f29"}, + {file = "coverage-7.13.5-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d8a7a2049c14f413163e2bdabd37e41179b1d1ccb10ffc6ccc4b7a718429c607"}, + {file = "coverage-7.13.5-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e1c85e0b6c05c592ea6d8768a66a254bfb3874b53774b12d4c89c481eb78cb90"}, + {file = "coverage-7.13.5-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:777c4d1eff1b67876139d24288aaf1817f6c03d6bae9c5cc8d27b83bcfe38fe3"}, + {file = "coverage-7.13.5-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:6697e29b93707167687543480a40f0db8f356e86d9f67ddf2e37e2dfd91a9dab"}, + {file = "coverage-7.13.5-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:8fdf453a942c3e4d99bd80088141c4c6960bb232c409d9c3558e2dbaa3998562"}, + {file = "coverage-7.13.5-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:32ca0c0114c9834a43f045a87dcebd69d108d8ffb666957ea65aa132f50332e2"}, + {file = "coverage-7.13.5-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:8769751c10f339021e2638cd354e13adeac54004d1941119b2c96fe5276d45ea"}, + {file = "coverage-7.13.5-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:cec2d83125531bd153175354055cdb7a09987af08a9430bd173c937c6d0fba2a"}, + {file = "coverage-7.13.5-cp314-cp314t-win32.whl", hash = "sha256:0cd9ed7a8b181775459296e402ca4fb27db1279740a24e93b3b41942ebe4b215"}, + {file = "coverage-7.13.5-cp314-cp314t-win_amd64.whl", hash = "sha256:301e3b7dfefecaca37c9f1aa6f0049b7d4ab8dd933742b607765d757aca77d43"}, + {file = "coverage-7.13.5-cp314-cp314t-win_arm64.whl", hash = "sha256:9dacc2ad679b292709e0f5fc1ac74a6d4d5562e424058962c7bb0c658ad25e45"}, + {file = "coverage-7.13.5-py3-none-any.whl", hash = "sha256:34b02417cf070e173989b3db962f7ed56d2f644307b2cf9d5a0f258e13084a61"}, + {file = "coverage-7.13.5.tar.gz", hash = "sha256:c81f6515c4c40141f83f502b07bbfa5c240ba25bbe73da7b33f1e5b6120ff179"}, ] -[package.dependencies] -tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} - [package.extras] toml = ["tomli ; python_full_version <= \"3.11.0a6\""] @@ -681,18 +642,18 @@ files = [ [[package]] name = "deprecated" -version = "1.2.18" +version = "1.3.1" description = "Python @deprecated decorator to deprecate old python classes, functions or methods." optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" groups = ["main"] files = [ - {file = "Deprecated-1.2.18-py2.py3-none-any.whl", hash = "sha256:bd5011788200372a32418f888e326a09ff80d0214bd961147cfed01b5c018eec"}, - {file = "deprecated-1.2.18.tar.gz", hash = "sha256:422b6f6d859da6f2ef57857761bfb392480502a64c3028ca9bbe86085d72115d"}, + {file = "deprecated-1.3.1-py2.py3-none-any.whl", hash = "sha256:597bfef186b6f60181535a29fbe44865ce137a5079f295b479886c82729d5f3f"}, + {file = "deprecated-1.3.1.tar.gz", hash = "sha256:b1b50e0ff0c1fddaa5708a2c6b0a6588bb09b892825ab2b214ac9ea9d92a5223"}, ] [package.dependencies] -wrapt = ">=1.10,<2" +wrapt = ">=1.10,<3" [package.extras] dev = ["PyTest", "PyTest-Cov", "bump2version (<1)", "setuptools ; python_version >= \"3.12\"", "tox"] @@ -710,35 +671,16 @@ files = [ {file = "docutils-0.18.1.tar.gz", hash = "sha256:679987caf361a7539d76e584cbeddc311e3aee937877c87346f31debc63e9d06"}, ] -[[package]] -name = "exceptiongroup" -version = "1.3.0" -description = "Backport of PEP 654 (exception groups)" -optional = false -python-versions = ">=3.7" -groups = ["main", "dev"] -markers = "python_version < \"3.11\"" -files = [ - {file = "exceptiongroup-1.3.0-py3-none-any.whl", hash = "sha256:4d111e6e0c13d0644cad6ddaa7ed0261a0b36971f6d23e7ec9b4b9097da78a10"}, - {file = "exceptiongroup-1.3.0.tar.gz", hash = "sha256:b241f5885f560bc56a59ee63ca4c6a8bfa46ae4ad651af316d4e81817bb9fd88"}, -] - -[package.dependencies] -typing-extensions = {version = ">=4.6.0", markers = "python_version < \"3.13\""} - -[package.extras] -test = ["pytest (>=6)"] - [[package]] name = "execnet" -version = "2.1.1" +version = "2.1.2" description = "execnet: rapid multi-Python deployment" optional = false python-versions = ">=3.8" groups = ["dev"] files = [ - {file = "execnet-2.1.1-py3-none-any.whl", hash = "sha256:26dee51f1b80cebd6d0ca8e74dd8745419761d3bef34163928cbebbdc4749fdc"}, - {file = "execnet-2.1.1.tar.gz", hash = "sha256:5189b52c6121c24feae288166ab41b32549c7e2348652736540b9e6e7d4e72e3"}, + {file = "execnet-2.1.2-py3-none-any.whl", hash = "sha256:67fba928dd5a544b783f6056f449e5e3931a5c378b128bc18501f7ea79e296ec"}, + {file = "execnet-2.1.2.tar.gz", hash = "sha256:63d83bfdd9a23e35b9c6a3261412324f964c2ec8dcd8d3c6916ee9373e0befcd"}, ] [package.extras] @@ -778,84 +720,76 @@ devel = ["colorama", "json-spec", "jsonschema", "pylint", "pytest", "pytest-benc [[package]] name = "fonttools" -version = "4.60.1" +version = "4.62.1" description = "Tools to manipulate font files" optional = true -python-versions = ">=3.9" +python-versions = ">=3.10" groups = ["main"] markers = "extra == \"docs\"" files = [ - {file = "fonttools-4.60.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:9a52f254ce051e196b8fe2af4634c2d2f02c981756c6464dc192f1b6050b4e28"}, - {file = "fonttools-4.60.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c7420a2696a44650120cdd269a5d2e56a477e2bfa9d95e86229059beb1c19e15"}, - {file = "fonttools-4.60.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee0c0b3b35b34f782afc673d503167157094a16f442ace7c6c5e0ca80b08f50c"}, - {file = "fonttools-4.60.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:282dafa55f9659e8999110bd8ed422ebe1c8aecd0dc396550b038e6c9a08b8ea"}, - {file = "fonttools-4.60.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4ba4bd646e86de16160f0fb72e31c3b9b7d0721c3e5b26b9fa2fc931dfdb2652"}, - {file = "fonttools-4.60.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:0b0835ed15dd5b40d726bb61c846a688f5b4ce2208ec68779bc81860adb5851a"}, - {file = "fonttools-4.60.1-cp310-cp310-win32.whl", hash = "sha256:1525796c3ffe27bb6268ed2a1bb0dcf214d561dfaf04728abf01489eb5339dce"}, - {file = "fonttools-4.60.1-cp310-cp310-win_amd64.whl", hash = "sha256:268ecda8ca6cb5c4f044b1fb9b3b376e8cd1b361cef275082429dc4174907038"}, - {file = "fonttools-4.60.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7b4c32e232a71f63a5d00259ca3d88345ce2a43295bb049d21061f338124246f"}, - {file = "fonttools-4.60.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3630e86c484263eaac71d117085d509cbcf7b18f677906824e4bace598fb70d2"}, - {file = "fonttools-4.60.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5c1015318e4fec75dd4943ad5f6a206d9727adf97410d58b7e32ab644a807914"}, - {file = "fonttools-4.60.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e6c58beb17380f7c2ea181ea11e7db8c0ceb474c9dd45f48e71e2cb577d146a1"}, - {file = "fonttools-4.60.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ec3681a0cb34c255d76dd9d865a55f260164adb9fa02628415cdc2d43ee2c05d"}, - {file = "fonttools-4.60.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f4b5c37a5f40e4d733d3bbaaef082149bee5a5ea3156a785ff64d949bd1353fa"}, - {file = "fonttools-4.60.1-cp311-cp311-win32.whl", hash = "sha256:398447f3d8c0c786cbf1209711e79080a40761eb44b27cdafffb48f52bcec258"}, - {file = "fonttools-4.60.1-cp311-cp311-win_amd64.whl", hash = "sha256:d066ea419f719ed87bc2c99a4a4bfd77c2e5949cb724588b9dd58f3fd90b92bf"}, - {file = "fonttools-4.60.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:7b0c6d57ab00dae9529f3faf187f2254ea0aa1e04215cf2f1a8ec277c96661bc"}, - {file = "fonttools-4.60.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:839565cbf14645952d933853e8ade66a463684ed6ed6c9345d0faf1f0e868877"}, - {file = "fonttools-4.60.1-cp312-cp312-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:8177ec9676ea6e1793c8a084a90b65a9f778771998eb919d05db6d4b1c0b114c"}, - {file = "fonttools-4.60.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:996a4d1834524adbb423385d5a629b868ef9d774670856c63c9a0408a3063401"}, - {file = "fonttools-4.60.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a46b2f450bc79e06ef3b6394f0c68660529ed51692606ad7f953fc2e448bc903"}, - {file = "fonttools-4.60.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6ec722ee589e89a89f5b7574f5c45604030aa6ae24cb2c751e2707193b466fed"}, - {file = "fonttools-4.60.1-cp312-cp312-win32.whl", hash = "sha256:b2cf105cee600d2de04ca3cfa1f74f1127f8455b71dbad02b9da6ec266e116d6"}, - {file = "fonttools-4.60.1-cp312-cp312-win_amd64.whl", hash = "sha256:992775c9fbe2cf794786fa0ffca7f09f564ba3499b8fe9f2f80bd7197db60383"}, - {file = "fonttools-4.60.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:6f68576bb4bbf6060c7ab047b1574a1ebe5c50a17de62830079967b211059ebb"}, - {file = "fonttools-4.60.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:eedacb5c5d22b7097482fa834bda0dafa3d914a4e829ec83cdea2a01f8c813c4"}, - {file = "fonttools-4.60.1-cp313-cp313-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b33a7884fabd72bdf5f910d0cf46be50dce86a0362a65cfc746a4168c67eb96c"}, - {file = "fonttools-4.60.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2409d5fb7b55fd70f715e6d34e7a6e4f7511b8ad29a49d6df225ee76da76dd77"}, - {file = "fonttools-4.60.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c8651e0d4b3bdeda6602b85fdc2abbefc1b41e573ecb37b6779c4ca50753a199"}, - {file = "fonttools-4.60.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:145daa14bf24824b677b9357c5e44fd8895c2a8f53596e1b9ea3496081dc692c"}, - {file = "fonttools-4.60.1-cp313-cp313-win32.whl", hash = "sha256:2299df884c11162617a66b7c316957d74a18e3758c0274762d2cc87df7bc0272"}, - {file = "fonttools-4.60.1-cp313-cp313-win_amd64.whl", hash = "sha256:a3db56f153bd4c5c2b619ab02c5db5192e222150ce5a1bc10f16164714bc39ac"}, - {file = "fonttools-4.60.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:a884aef09d45ba1206712c7dbda5829562d3fea7726935d3289d343232ecb0d3"}, - {file = "fonttools-4.60.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8a44788d9d91df72d1a5eac49b31aeb887a5f4aab761b4cffc4196c74907ea85"}, - {file = "fonttools-4.60.1-cp314-cp314-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:e852d9dda9f93ad3651ae1e3bb770eac544ec93c3807888798eccddf84596537"}, - {file = "fonttools-4.60.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:154cb6ee417e417bf5f7c42fe25858c9140c26f647c7347c06f0cc2d47eff003"}, - {file = "fonttools-4.60.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:5664fd1a9ea7f244487ac8f10340c4e37664675e8667d6fee420766e0fb3cf08"}, - {file = "fonttools-4.60.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:583b7f8e3c49486e4d489ad1deacfb8d5be54a8ef34d6df824f6a171f8511d99"}, - {file = "fonttools-4.60.1-cp314-cp314-win32.whl", hash = "sha256:66929e2ea2810c6533a5184f938502cfdaea4bc3efb7130d8cc02e1c1b4108d6"}, - {file = "fonttools-4.60.1-cp314-cp314-win_amd64.whl", hash = "sha256:f3d5be054c461d6a2268831f04091dc82753176f6ea06dc6047a5e168265a987"}, - {file = "fonttools-4.60.1-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:b6379e7546ba4ae4b18f8ae2b9bc5960936007a1c0e30b342f662577e8bc3299"}, - {file = "fonttools-4.60.1-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9d0ced62b59e0430b3690dbc5373df1c2aa7585e9a8ce38eff87f0fd993c5b01"}, - {file = "fonttools-4.60.1-cp314-cp314t-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:875cb7764708b3132637f6c5fb385b16eeba0f7ac9fa45a69d35e09b47045801"}, - {file = "fonttools-4.60.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a184b2ea57b13680ab6d5fbde99ccef152c95c06746cb7718c583abd8f945ccc"}, - {file = "fonttools-4.60.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:026290e4ec76583881763fac284aca67365e0be9f13a7fb137257096114cb3bc"}, - {file = "fonttools-4.60.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:f0e8817c7d1a0c2eedebf57ef9a9896f3ea23324769a9a2061a80fe8852705ed"}, - {file = "fonttools-4.60.1-cp314-cp314t-win32.whl", hash = "sha256:1410155d0e764a4615774e5c2c6fc516259fe3eca5882f034eb9bfdbee056259"}, - {file = "fonttools-4.60.1-cp314-cp314t-win_amd64.whl", hash = "sha256:022beaea4b73a70295b688f817ddc24ed3e3418b5036ffcd5658141184ef0d0c"}, - {file = "fonttools-4.60.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:122e1a8ada290423c493491d002f622b1992b1ab0b488c68e31c413390dc7eb2"}, - {file = "fonttools-4.60.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a140761c4ff63d0cb9256ac752f230460ee225ccef4ad8f68affc723c88e2036"}, - {file = "fonttools-4.60.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0eae96373e4b7c9e45d099d7a523444e3554360927225c1cdae221a58a45b856"}, - {file = "fonttools-4.60.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:596ecaca36367027d525b3b426d8a8208169d09edcf8c7506aceb3a38bfb55c7"}, - {file = "fonttools-4.60.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2ee06fc57512144d8b0445194c2da9f190f61ad51e230f14836286470c99f854"}, - {file = "fonttools-4.60.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:b42d86938e8dda1cd9a1a87a6d82f1818eaf933348429653559a458d027446da"}, - {file = "fonttools-4.60.1-cp39-cp39-win32.whl", hash = "sha256:8b4eb332f9501cb1cd3d4d099374a1e1306783ff95489a1026bde9eb02ccc34a"}, - {file = "fonttools-4.60.1-cp39-cp39-win_amd64.whl", hash = "sha256:7473a8ed9ed09aeaa191301244a5a9dbe46fe0bf54f9d6cd21d83044c3321217"}, - {file = "fonttools-4.60.1-py3-none-any.whl", hash = "sha256:906306ac7afe2156fcf0042173d6ebbb05416af70f6b370967b47f8f00103bbb"}, - {file = "fonttools-4.60.1.tar.gz", hash = "sha256:ef00af0439ebfee806b25f24c8f92109157ff3fac5731dc7867957812e87b8d9"}, + {file = "fonttools-4.62.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ad5cca75776cd453b1b035b530e943334957ae152a36a88a320e779d61fc980c"}, + {file = "fonttools-4.62.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0b3ae47e8636156a9accff64c02c0924cbebad62854c4a6dbdc110cd5b4b341a"}, + {file = "fonttools-4.62.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c9b9e288b4da2f64fd6180644221749de651703e8d0c16bd4b719533a3a7d6e3"}, + {file = "fonttools-4.62.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7bca7a1c1faf235ffe25d4f2e555246b4750220b38de8261d94ebc5ce8a23c23"}, + {file = "fonttools-4.62.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b4e0fcf265ad26e487c56cb12a42dffe7162de708762db951e1b3f755319507d"}, + {file = "fonttools-4.62.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:2d850f66830a27b0d498ee05adb13a3781637b1826982cd7e2b3789ef0cc71ae"}, + {file = "fonttools-4.62.1-cp310-cp310-win32.whl", hash = "sha256:486f32c8047ccd05652aba17e4a8819a3a9d78570eb8a0e3b4503142947880ed"}, + {file = "fonttools-4.62.1-cp310-cp310-win_amd64.whl", hash = "sha256:5a648bde915fba9da05ae98856987ca91ba832949a9e2888b48c47ef8b96c5a9"}, + {file = "fonttools-4.62.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:40975849bac44fb0b9253d77420c6d8b523ac4dcdcefeff6e4d706838a5b80f7"}, + {file = "fonttools-4.62.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9dde91633f77fa576879a0c76b1d89de373cae751a98ddf0109d54e173b40f14"}, + {file = "fonttools-4.62.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6acb4109f8bee00fec985c8c7afb02299e35e9c94b57287f3ea542f28bd0b0a7"}, + {file = "fonttools-4.62.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1c5c25671ce8805e0d080e2ffdeca7f1e86778c5cbfbeae86d7f866d8830517b"}, + {file = "fonttools-4.62.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a5d8825e1140f04e6c99bb7d37a9e31c172f3bc208afbe02175339e699c710e1"}, + {file = "fonttools-4.62.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:268abb1cb221e66c014acc234e872b7870d8b5d4657a83a8f4205094c32d2416"}, + {file = "fonttools-4.62.1-cp311-cp311-win32.whl", hash = "sha256:942b03094d7edbb99bdf1ae7e9090898cad7bf9030b3d21f33d7072dbcb51a53"}, + {file = "fonttools-4.62.1-cp311-cp311-win_amd64.whl", hash = "sha256:e8514f4924375f77084e81467e63238b095abda5107620f49421c368a6017ed2"}, + {file = "fonttools-4.62.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:90365821debbd7db678809c7491ca4acd1e0779b9624cdc6ddaf1f31992bf974"}, + {file = "fonttools-4.62.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:12859ff0b47dd20f110804c3e0d0970f7b832f561630cd879969011541a464a9"}, + {file = "fonttools-4.62.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9c125ffa00c3d9003cdaaf7f2c79e6e535628093e14b5de1dccb08859b680936"}, + {file = "fonttools-4.62.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:149f7d84afca659d1a97e39a4778794a2f83bf344c5ee5134e09995086cc2392"}, + {file = "fonttools-4.62.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0aa72c43a601cfa9273bb1ae0518f1acadc01ee181a6fc60cd758d7fdadffc04"}, + {file = "fonttools-4.62.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:19177c8d96c7c36359266e571c5173bcee9157b59cfc8cb0153c5673dc5a3a7d"}, + {file = "fonttools-4.62.1-cp312-cp312-win32.whl", hash = "sha256:a24decd24d60744ee8b4679d38e88b8303d86772053afc29b19d23bb8207803c"}, + {file = "fonttools-4.62.1-cp312-cp312-win_amd64.whl", hash = "sha256:9e7863e10b3de72376280b515d35b14f5eeed639d1aa7824f4cf06779ec65e42"}, + {file = "fonttools-4.62.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:c22b1014017111c401469e3acc5433e6acf6ebcc6aa9efb538a533c800971c79"}, + {file = "fonttools-4.62.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:68959f5fc58ed4599b44aad161c2837477d7f35f5f79402d97439974faebfebe"}, + {file = "fonttools-4.62.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ef46db46c9447103b8f3ff91e8ba009d5fe181b1920a83757a5762551e32bb68"}, + {file = "fonttools-4.62.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:6706d1cb1d5e6251a97ad3c1b9347505c5615c112e66047abbef0f8545fa30d1"}, + {file = "fonttools-4.62.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:2e7abd2b1e11736f58c1de27819e1955a53267c21732e78243fa2fa2e5c1e069"}, + {file = "fonttools-4.62.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:403d28ce06ebfc547fbcb0cb8b7f7cc2f7a2d3e1a67ba9a34b14632df9e080f9"}, + {file = "fonttools-4.62.1-cp313-cp313-win32.whl", hash = "sha256:93c316e0f5301b2adbe6a5f658634307c096fd5aae60a5b3412e4f3e1728ab24"}, + {file = "fonttools-4.62.1-cp313-cp313-win_amd64.whl", hash = "sha256:7aa21ff53e28a9c2157acbc44e5b401149d3c9178107130e82d74ceb500e5056"}, + {file = "fonttools-4.62.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:fa1d16210b6b10a826d71bed68dd9ec24a9e218d5a5e2797f37c573e7ec215ca"}, + {file = "fonttools-4.62.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:aa69d10ed420d8121118e628ad47d86e4caa79ba37f968597b958f6cceab7eca"}, + {file = "fonttools-4.62.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bd13b7999d59c5eb1c2b442eb2d0c427cb517a0b7a1f5798fc5c9e003f5ff782"}, + {file = "fonttools-4.62.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8d337fdd49a79b0d51c4da87bc38169d21c3abbf0c1aa9367eff5c6656fb6dae"}, + {file = "fonttools-4.62.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:d241cdc4a67b5431c6d7f115fdf63335222414995e3a1df1a41e1182acd4bcc7"}, + {file = "fonttools-4.62.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:c05557a78f8fa514da0f869556eeda40887a8abc77c76ee3f74cf241778afd5a"}, + {file = "fonttools-4.62.1-cp314-cp314-win32.whl", hash = "sha256:49a445d2f544ce4a69338694cad575ba97b9a75fff02720da0882d1a73f12800"}, + {file = "fonttools-4.62.1-cp314-cp314-win_amd64.whl", hash = "sha256:1eecc128c86c552fb963fe846ca4e011b1be053728f798185a1687502f6d398e"}, + {file = "fonttools-4.62.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:1596aeaddf7f78e21e68293c011316a25267b3effdaccaf4d59bc9159d681b82"}, + {file = "fonttools-4.62.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:8f8fca95d3bb3208f59626a4b0ea6e526ee51f5a8ad5d91821c165903e8d9260"}, + {file = "fonttools-4.62.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee91628c08e76f77b533d65feb3fbe6d9dad699f95be51cf0d022db94089cdc4"}, + {file = "fonttools-4.62.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5f37df1cac61d906e7b836abe356bc2f34c99d4477467755c216b72aa3dc748b"}, + {file = "fonttools-4.62.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:92bb00a947e666169c99b43753c4305fc95a890a60ef3aeb2a6963e07902cc87"}, + {file = "fonttools-4.62.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:bdfe592802ef939a0e33106ea4a318eeb17822c7ee168c290273cbd5fabd746c"}, + {file = "fonttools-4.62.1-cp314-cp314t-win32.whl", hash = "sha256:b820fcb92d4655513d8402d5b219f94481c4443d825b4372c75a2072aa4b357a"}, + {file = "fonttools-4.62.1-cp314-cp314t-win_amd64.whl", hash = "sha256:59b372b4f0e113d3746b88985f1c796e7bf830dd54b28374cd85c2b8acd7583e"}, + {file = "fonttools-4.62.1-py3-none-any.whl", hash = "sha256:7487782e2113861f4ddcc07c3436450659e3caa5e470b27dc2177cade2d8e7fd"}, + {file = "fonttools-4.62.1.tar.gz", hash = "sha256:e54c75fd6041f1122476776880f7c3c3295ffa31962dc6ebe2543c00dca58b5d"}, ] [package.extras] -all = ["brotli (>=1.0.1) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=0.8.0) ; platform_python_implementation != \"CPython\"", "lxml (>=4.0)", "lz4 (>=1.7.4.2)", "matplotlib", "munkres ; platform_python_implementation == \"PyPy\"", "pycairo", "scipy ; platform_python_implementation != \"PyPy\"", "skia-pathops (>=0.5.0)", "sympy", "uharfbuzz (>=0.23.0)", "unicodedata2 (>=15.1.0) ; python_version <= \"3.12\"", "xattr ; sys_platform == \"darwin\"", "zopfli (>=0.1.4)"] +all = ["brotli (>=1.0.1) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=0.8.0) ; platform_python_implementation != \"CPython\"", "lxml (>=4.0)", "lz4 (>=1.7.4.2)", "matplotlib", "munkres ; platform_python_implementation == \"PyPy\"", "pycairo", "scipy ; platform_python_implementation != \"PyPy\"", "skia-pathops (>=0.5.0)", "sympy", "uharfbuzz (>=0.45.0)", "unicodedata2 (>=17.0.0) ; python_version <= \"3.14\"", "xattr ; sys_platform == \"darwin\"", "zopfli (>=0.1.4)"] graphite = ["lz4 (>=1.7.4.2)"] interpolatable = ["munkres ; platform_python_implementation == \"PyPy\"", "pycairo", "scipy ; platform_python_implementation != \"PyPy\""] lxml = ["lxml (>=4.0)"] pathops = ["skia-pathops (>=0.5.0)"] plot = ["matplotlib"] -repacker = ["uharfbuzz (>=0.23.0)"] +repacker = ["uharfbuzz (>=0.45.0)"] symfont = ["sympy"] type1 = ["xattr ; sys_platform == \"darwin\""] -unicode = ["unicodedata2 (>=15.1.0) ; python_version <= \"3.12\""] +unicode = ["unicodedata2 (>=17.0.0) ; python_version <= \"3.14\""] woff = ["brotli (>=1.0.1) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=0.8.0) ; platform_python_implementation != \"CPython\"", "zopfli (>=0.1.4)"] [[package]] @@ -878,80 +812,80 @@ testing = ["protobuf (>=4.21.9)"] [[package]] name = "grpcio" -version = "1.75.1" +version = "1.80.0" description = "HTTP/2-based RPC framework" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "grpcio-1.75.1-cp310-cp310-linux_armv7l.whl", hash = "sha256:1712b5890b22547dd29f3215c5788d8fc759ce6dd0b85a6ba6e2731f2d04c088"}, - {file = "grpcio-1.75.1-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:8d04e101bba4b55cea9954e4aa71c24153ba6182481b487ff376da28d4ba46cf"}, - {file = "grpcio-1.75.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:683cfc70be0c1383449097cba637317e4737a357cfc185d887fd984206380403"}, - {file = "grpcio-1.75.1-cp310-cp310-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:491444c081a54dcd5e6ada57314321ae526377f498d4aa09d975c3241c5b9e1c"}, - {file = "grpcio-1.75.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ce08d4e112d0d38487c2b631ec8723deac9bc404e9c7b1011426af50a79999e4"}, - {file = "grpcio-1.75.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:5a2acda37fc926ccc4547977ac3e56b1df48fe200de968e8c8421f6e3093df6c"}, - {file = "grpcio-1.75.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:745c5fe6bf05df6a04bf2d11552c7d867a2690759e7ab6b05c318a772739bd75"}, - {file = "grpcio-1.75.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:259526a7159d39e2db40d566fe3e8f8e034d0fb2db5bf9c00e09aace655a4c2b"}, - {file = "grpcio-1.75.1-cp310-cp310-win32.whl", hash = "sha256:f4b29b9aabe33fed5df0a85e5f13b09ff25e2c05bd5946d25270a8bd5682dac9"}, - {file = "grpcio-1.75.1-cp310-cp310-win_amd64.whl", hash = "sha256:cf2e760978dcce7ff7d465cbc7e276c3157eedc4c27aa6de7b594c7a295d3d61"}, - {file = "grpcio-1.75.1-cp311-cp311-linux_armv7l.whl", hash = "sha256:573855ca2e58e35032aff30bfbd1ee103fbcf4472e4b28d4010757700918e326"}, - {file = "grpcio-1.75.1-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:6a4996a2c8accc37976dc142d5991adf60733e223e5c9a2219e157dc6a8fd3a2"}, - {file = "grpcio-1.75.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b1ea1bbe77ecbc1be00af2769f4ae4a88ce93be57a4f3eebd91087898ed749f9"}, - {file = "grpcio-1.75.1-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:e5b425aee54cc5e3e3c58f00731e8a33f5567965d478d516d35ef99fd648ab68"}, - {file = "grpcio-1.75.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0049a7bf547dafaeeb1db17079ce79596c298bfe308fc084d023c8907a845b9a"}, - {file = "grpcio-1.75.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5b8ea230c7f77c0a1a3208a04a1eda164633fb0767b4cefd65a01079b65e5b1f"}, - {file = "grpcio-1.75.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:36990d629c3c9fb41e546414e5af52d0a7af37ce7113d9682c46d7e2919e4cca"}, - {file = "grpcio-1.75.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b10ad908118d38c2453ade7ff790e5bce36580c3742919007a2a78e3a1e521ca"}, - {file = "grpcio-1.75.1-cp311-cp311-win32.whl", hash = "sha256:d6be2b5ee7bea656c954dcf6aa8093c6f0e6a3ef9945c99d99fcbfc88c5c0bfe"}, - {file = "grpcio-1.75.1-cp311-cp311-win_amd64.whl", hash = "sha256:61c692fb05956b17dd6d1ab480f7f10ad0536dba3bc8fd4e3c7263dc244ed772"}, - {file = "grpcio-1.75.1-cp312-cp312-linux_armv7l.whl", hash = "sha256:7b888b33cd14085d86176b1628ad2fcbff94cfbbe7809465097aa0132e58b018"}, - {file = "grpcio-1.75.1-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:8775036efe4ad2085975531d221535329f5dac99b6c2a854a995456098f99546"}, - {file = "grpcio-1.75.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:bb658f703468d7fbb5dcc4037c65391b7dc34f808ac46ed9136c24fc5eeb041d"}, - {file = "grpcio-1.75.1-cp312-cp312-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:4b7177a1cdb3c51b02b0c0a256b0a72fdab719600a693e0e9037949efffb200b"}, - {file = "grpcio-1.75.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7d4fa6ccc3ec2e68a04f7b883d354d7fea22a34c44ce535a2f0c0049cf626ddf"}, - {file = "grpcio-1.75.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3d86880ecaeb5b2f0a8afa63824de93adb8ebe4e49d0e51442532f4e08add7d6"}, - {file = "grpcio-1.75.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a8041d2f9e8a742aeae96f4b047ee44e73619f4f9d24565e84d5446c623673b6"}, - {file = "grpcio-1.75.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3652516048bf4c314ce12be37423c79829f46efffb390ad64149a10c6071e8de"}, - {file = "grpcio-1.75.1-cp312-cp312-win32.whl", hash = "sha256:44b62345d8403975513af88da2f3d5cc76f73ca538ba46596f92a127c2aea945"}, - {file = "grpcio-1.75.1-cp312-cp312-win_amd64.whl", hash = "sha256:b1e191c5c465fa777d4cafbaacf0c01e0d5278022082c0abbd2ee1d6454ed94d"}, - {file = "grpcio-1.75.1-cp313-cp313-linux_armv7l.whl", hash = "sha256:3bed22e750d91d53d9e31e0af35a7b0b51367e974e14a4ff229db5b207647884"}, - {file = "grpcio-1.75.1-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:5b8f381eadcd6ecaa143a21e9e80a26424c76a0a9b3d546febe6648f3a36a5ac"}, - {file = "grpcio-1.75.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5bf4001d3293e3414d0cf99ff9b1139106e57c3a66dfff0c5f60b2a6286ec133"}, - {file = "grpcio-1.75.1-cp313-cp313-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:9f82ff474103e26351dacfe8d50214e7c9322960d8d07ba7fa1d05ff981c8b2d"}, - {file = "grpcio-1.75.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0ee119f4f88d9f75414217823d21d75bfe0e6ed40135b0cbbfc6376bc9f7757d"}, - {file = "grpcio-1.75.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:664eecc3abe6d916fa6cf8dd6b778e62fb264a70f3430a3180995bf2da935446"}, - {file = "grpcio-1.75.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:c32193fa08b2fbebf08fe08e84f8a0aad32d87c3ad42999c65e9449871b1c66e"}, - {file = "grpcio-1.75.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5cebe13088b9254f6e615bcf1da9131d46cfa4e88039454aca9cb65f639bd3bc"}, - {file = "grpcio-1.75.1-cp313-cp313-win32.whl", hash = "sha256:4b4c678e7ed50f8ae8b8dbad15a865ee73ce12668b6aaf411bf3258b5bc3f970"}, - {file = "grpcio-1.75.1-cp313-cp313-win_amd64.whl", hash = "sha256:5573f51e3f296a1bcf71e7a690c092845fb223072120f4bdb7a5b48e111def66"}, - {file = "grpcio-1.75.1-cp314-cp314-linux_armv7l.whl", hash = "sha256:c05da79068dd96723793bffc8d0e64c45f316248417515f28d22204d9dae51c7"}, - {file = "grpcio-1.75.1-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:06373a94fd16ec287116a825161dca179a0402d0c60674ceeec8c9fba344fe66"}, - {file = "grpcio-1.75.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:4484f4b7287bdaa7a5b3980f3c7224c3c622669405d20f69549f5fb956ad0421"}, - {file = "grpcio-1.75.1-cp314-cp314-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:2720c239c1180eee69f7883c1d4c83fc1a495a2535b5fa322887c70bf02b16e8"}, - {file = "grpcio-1.75.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:07a554fa31c668cf0e7a188678ceeca3cb8fead29bbe455352e712ec33ca701c"}, - {file = "grpcio-1.75.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:3e71a2105210366bfc398eef7f57a664df99194f3520edb88b9c3a7e46ee0d64"}, - {file = "grpcio-1.75.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:8679aa8a5b67976776d3c6b0521e99d1c34db8a312a12bcfd78a7085cb9b604e"}, - {file = "grpcio-1.75.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:aad1c774f4ebf0696a7f148a56d39a3432550612597331792528895258966dc0"}, - {file = "grpcio-1.75.1-cp314-cp314-win32.whl", hash = "sha256:62ce42d9994446b307649cb2a23335fa8e927f7ab2cbf5fcb844d6acb4d85f9c"}, - {file = "grpcio-1.75.1-cp314-cp314-win_amd64.whl", hash = "sha256:f86e92275710bea3000cb79feca1762dc0ad3b27830dd1a74e82ab321d4ee464"}, - {file = "grpcio-1.75.1-cp39-cp39-linux_armv7l.whl", hash = "sha256:c09fba33327c3ac11b5c33dbdd8218eef8990d78f83b1656d628831812a8c0fb"}, - {file = "grpcio-1.75.1-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:7e21400b037be29545704889e72e586c238e346dcb2d08d8a7288d16c883a9ec"}, - {file = "grpcio-1.75.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c12121e509b9f8b0914d10054d24120237d19e870b1cd82acbb8a9b9ddd198a3"}, - {file = "grpcio-1.75.1-cp39-cp39-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:73577a93e692b3474b1bfe84285d098de36705dbd838bb4d6a056d326e4dc880"}, - {file = "grpcio-1.75.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e19e7dfa0d7ca7dea22be464339e18ac608fd75d88c56770c646cdabe54bc724"}, - {file = "grpcio-1.75.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:4e1c28f51c1cf67eccdfc1065e8e866c9ed622f09773ca60947089c117f848a1"}, - {file = "grpcio-1.75.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:030a6164bc2ca726052778c0cf8e3249617a34e368354f9e6107c27ad4af8c28"}, - {file = "grpcio-1.75.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:67697efef5a98d46d5db7b1720fa4043536f8b8e5072a5d61cfca762f287e939"}, - {file = "grpcio-1.75.1-cp39-cp39-win32.whl", hash = "sha256:52015cf73eb5d76f6404e0ce0505a69b51fd1f35810b3a01233b34b10baafb41"}, - {file = "grpcio-1.75.1-cp39-cp39-win_amd64.whl", hash = "sha256:9fe51e4a1f896ea84ac750900eae34d9e9b896b5b1e4a30b02dc31ad29f36383"}, - {file = "grpcio-1.75.1.tar.gz", hash = "sha256:3e81d89ece99b9ace23a6916880baca613c03a799925afb2857887efa8b1b3d2"}, + {file = "grpcio-1.80.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:886457a7768e408cdce226ad1ca67d2958917d306523a0e21e1a2fdaa75c9c9c"}, + {file = "grpcio-1.80.0-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:7b641fc3f1dc647bfd80bd713addc68f6d145956f64677e56d9ebafc0bd72388"}, + {file = "grpcio-1.80.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:33eb763f18f006dc7fee1e69831d38d23f5eccd15b2e0f92a13ee1d9242e5e02"}, + {file = "grpcio-1.80.0-cp310-cp310-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:52d143637e3872633fc7dd7c3c6a1c84e396b359f3a72e215f8bf69fd82084fc"}, + {file = "grpcio-1.80.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c51bf8ac4575af2e0678bccfb07e47321fc7acb5049b4482832c5c195e04e13a"}, + {file = "grpcio-1.80.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:50a9871536d71c4fba24ee856abc03a87764570f0c457dd8db0b4018f379fed9"}, + {file = "grpcio-1.80.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:a72d84ad0514db063e21887fbacd1fd7acb4d494a564cae22227cd45c7fbf199"}, + {file = "grpcio-1.80.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f7691a6788ad9196872f95716df5bc643ebba13c97140b7a5ee5c8e75d1dea81"}, + {file = "grpcio-1.80.0-cp310-cp310-win32.whl", hash = "sha256:46c2390b59d67f84e882694d489f5b45707c657832d7934859ceb8c33f467069"}, + {file = "grpcio-1.80.0-cp310-cp310-win_amd64.whl", hash = "sha256:dc053420fc75749c961e2a4c906398d7c15725d36ccc04ae6d16093167223b58"}, + {file = "grpcio-1.80.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:dfab85db094068ff42e2a3563f60ab3dddcc9d6488a35abf0132daec13209c8a"}, + {file = "grpcio-1.80.0-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:5c07e82e822e1161354e32da2662f741a4944ea955f9f580ec8fb409dd6f6060"}, + {file = "grpcio-1.80.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ba0915d51fd4ced2db5ff719f84e270afe0e2d4c45a7bdb1e8d036e4502928c2"}, + {file = "grpcio-1.80.0-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:3cb8130ba457d2aa09fa6b7c3ed6b6e4e6a2685fce63cb803d479576c4d80e21"}, + {file = "grpcio-1.80.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:09e5e478b3d14afd23f12e49e8b44c8684ac3c5f08561c43a5b9691c54d136ab"}, + {file = "grpcio-1.80.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:00168469238b022500e486c1c33916acf2f2a9b2c022202cf8a1885d2e3073c1"}, + {file = "grpcio-1.80.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8502122a3cc1714038e39a0b071acb1207ca7844208d5ea0d091317555ee7106"}, + {file = "grpcio-1.80.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ce1794f4ea6cc3ca29463f42d665c32ba1b964b48958a66497917fe9069f26e6"}, + {file = "grpcio-1.80.0-cp311-cp311-win32.whl", hash = "sha256:51b4a7189b0bef2aa30adce3c78f09c83526cf3dddb24c6a96555e3b97340440"}, + {file = "grpcio-1.80.0-cp311-cp311-win_amd64.whl", hash = "sha256:02e64bb0bb2da14d947a49e6f120a75e947250aebe65f9629b62bb1f5c14e6e9"}, + {file = "grpcio-1.80.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:c624cc9f1008361014378c9d776de7182b11fe8b2e5a81bc69f23a295f2a1ad0"}, + {file = "grpcio-1.80.0-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:f49eddcac43c3bf350c0385366a58f36bed8cc2c0ec35ef7b74b49e56552c0c2"}, + {file = "grpcio-1.80.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d334591df610ab94714048e0d5b4f3dd5ad1bee74dfec11eee344220077a79de"}, + {file = "grpcio-1.80.0-cp312-cp312-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:0cb517eb1d0d0aaf1d87af7cc5b801d686557c1d88b2619f5e31fab3c2315921"}, + {file = "grpcio-1.80.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4e78c4ac0d97dc2e569b2f4bcbbb447491167cb358d1a389fc4af71ab6f70411"}, + {file = "grpcio-1.80.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2ed770b4c06984f3b47eb0517b1c69ad0b84ef3f40128f51448433be904634cd"}, + {file = "grpcio-1.80.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:256507e2f524092f1473071a05e65a5b10d84b82e3ff24c5b571513cfaa61e2f"}, + {file = "grpcio-1.80.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:9a6284a5d907c37db53350645567c522be314bac859a64a7a5ca63b77bb7958f"}, + {file = "grpcio-1.80.0-cp312-cp312-win32.whl", hash = "sha256:c71309cfce2f22be26aa4a847357c502db6c621f1a49825ae98aa0907595b193"}, + {file = "grpcio-1.80.0-cp312-cp312-win_amd64.whl", hash = "sha256:9fe648599c0e37594c4809d81a9e77bd138cc82eb8baa71b6a86af65426723ff"}, + {file = "grpcio-1.80.0-cp313-cp313-linux_armv7l.whl", hash = "sha256:e9e408fc016dffd20661f0126c53d8a31c2821b5c13c5d67a0f5ed5de93319ad"}, + {file = "grpcio-1.80.0-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:92d787312e613754d4d8b9ca6d3297e69994a7912a32fa38c4c4e01c272974b0"}, + {file = "grpcio-1.80.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8ac393b58aa16991a2f1144ec578084d544038c12242da3a215966b512904d0f"}, + {file = "grpcio-1.80.0-cp313-cp313-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:68e5851ac4b9afe07e7f84483803ad167852570d65326b34d54ca560bfa53fb6"}, + {file = "grpcio-1.80.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:873ff5d17d68992ef6605330127425d2fc4e77e612fa3c3e0ed4e668685e3140"}, + {file = "grpcio-1.80.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:2bea16af2750fd0a899bf1abd9022244418b55d1f37da2202249ba4ba673838d"}, + {file = "grpcio-1.80.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ba0db34f7e1d803a878284cd70e4c63cb6ae2510ba51937bf8f45ba997cefcf7"}, + {file = "grpcio-1.80.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8eb613f02d34721f1acf3626dfdb3545bd3c8505b0e52bf8b5710a28d02e8aa7"}, + {file = "grpcio-1.80.0-cp313-cp313-win32.whl", hash = "sha256:93b6f823810720912fd131f561f91f5fed0fda372b6b7028a2681b8194d5d294"}, + {file = "grpcio-1.80.0-cp313-cp313-win_amd64.whl", hash = "sha256:e172cf795a3ba5246d3529e4d34c53db70e888fa582a8ffebd2e6e48bc0cba50"}, + {file = "grpcio-1.80.0-cp314-cp314-linux_armv7l.whl", hash = "sha256:3d4147a97c8344d065d01bbf8b6acec2cf86fb0400d40696c8bdad34a64ffc0e"}, + {file = "grpcio-1.80.0-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:d8e11f167935b3eb089ac9038e1a063e6d7dbe995c0bb4a661e614583352e76f"}, + {file = "grpcio-1.80.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f14b618fc30de822681ee986cfdcc2d9327229dc4c98aed16896761cacd468b9"}, + {file = "grpcio-1.80.0-cp314-cp314-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:4ed39fbdcf9b87370f6e8df4e39ca7b38b3e5e9d1b0013c7b6be9639d6578d14"}, + {file = "grpcio-1.80.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2dcc70e9f0ba987526e8e8603a610fb4f460e42899e74e7a518bf3c68fe1bf05"}, + {file = "grpcio-1.80.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:448c884b668b868562b1bda833c5fce6272d26e1926ec46747cda05741d302c1"}, + {file = "grpcio-1.80.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:a1dc80fe55685b4a543555e6eef975303b36c8db1023b1599b094b92aa77965f"}, + {file = "grpcio-1.80.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:31b9ac4ad1aa28ffee5503821fafd09e4da0a261ce1c1281c6c8da0423c83b6e"}, + {file = "grpcio-1.80.0-cp314-cp314-win32.whl", hash = "sha256:367ce30ba67d05e0592470428f0ec1c31714cab9ef19b8f2e37be1f4c7d32fae"}, + {file = "grpcio-1.80.0-cp314-cp314-win_amd64.whl", hash = "sha256:3b01e1f5464c583d2f567b2e46ff0d516ef979978f72091fd81f5ab7fa6e2e7f"}, + {file = "grpcio-1.80.0-cp39-cp39-linux_armv7l.whl", hash = "sha256:aacdfb4ed3eb919ca997504d27e03d5dba403c85130b8ed450308590a738f7a4"}, + {file = "grpcio-1.80.0-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:a361c20ec1ccd3c3953d20fb6d7b4125093bdd10dff44c5e2bbb39e58917cedc"}, + {file = "grpcio-1.80.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:43168871f170d1e4ed16ae03d10cd21efa29f190e710a624cee7e5ae07da6f4f"}, + {file = "grpcio-1.80.0-cp39-cp39-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:1b97cd29a8eda100b559b455331c487a80915b6ea6bd91cf3e89836c4ee8d957"}, + {file = "grpcio-1.80.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:bac1d573dfa84ce59a5547073e28fa7326d53352adda6912e362da0b917fcef4"}, + {file = "grpcio-1.80.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:4560cf0e86514595dbbd330cd65b7afad4b5c4b8c4905c041cfffa138d45e6fd"}, + {file = "grpcio-1.80.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:ec0a592e926071b4abad50c1495cd0d0d513324b3ff5e7267067c33ba27506e4"}, + {file = "grpcio-1.80.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:deb10a1528473c11f72a0939eed36d83e847d7cbb63e8cc5611fb7a912d38614"}, + {file = "grpcio-1.80.0-cp39-cp39-win32.whl", hash = "sha256:627fb7312171cdc52828bd6fac8d7028ff2a64b89f1957b6f3416caa2218d141"}, + {file = "grpcio-1.80.0-cp39-cp39-win_amd64.whl", hash = "sha256:05d55e1798756282cddd52d56c896b3e7d673e3a8798c2f1cd05ba249a3bb4de"}, + {file = "grpcio-1.80.0.tar.gz", hash = "sha256:29aca15edd0688c22ba01d7cc01cb000d72b2033f4a3c72a81a19b56fd143257"}, ] [package.dependencies] typing-extensions = ">=4.12,<5.0" [package.extras] -protobuf = ["grpcio-tools (>=1.75.1)"] +protobuf = ["grpcio-tools (>=1.80.0)"] [[package]] name = "h11" @@ -1014,143 +948,55 @@ zstd = ["zstandard (>=0.18.0)"] [[package]] name = "idna" -version = "3.10" +version = "3.18" description = "Internationalized Domain Names in Applications (IDNA)" optional = false -python-versions = ">=3.6" +python-versions = ">=3.9" groups = ["main", "dev"] files = [ - {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, - {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, + {file = "idna-3.18-py3-none-any.whl", hash = "sha256:7f952cbe720b688055e3f87de14f5c3e5fdaa8bc3928985c4077ca689de849a2"}, + {file = "idna-3.18.tar.gz", hash = "sha256:ffb385a7e039654cef1ab9ef32c6fafe283c0c0467bba1d9029738ce4a14a848"}, ] [package.extras] -all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] +all = ["mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] [[package]] name = "imagesize" -version = "1.4.1" -description = "Getting image size from png/jpeg/jpeg2000/gif file" +version = "2.0.0" +description = "Get image size from headers (BMP/PNG/JPEG/JPEG2000/GIF/TIFF/SVG/Netpbm/WebP/AVIF/HEIC/HEIF)" optional = true -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +python-versions = "<3.15,>=3.10" groups = ["main"] markers = "extra == \"docs\"" files = [ - {file = "imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b"}, - {file = "imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a"}, + {file = "imagesize-2.0.0-py2.py3-none-any.whl", hash = "sha256:5667c5bbb57ab3f1fa4bc366f4fbc971db3d5ed011fd2715fd8001f782718d96"}, + {file = "imagesize-2.0.0.tar.gz", hash = "sha256:8e8358c4a05c304f1fccf7ff96f036e7243a189e9e42e90851993c558cfe9ee3"}, ] -[[package]] -name = "importlib-metadata" -version = "8.7.0" -description = "Read metadata from Python packages" -optional = true -python-versions = ">=3.9" -groups = ["main"] -markers = "extra == \"docs\" and python_version == \"3.9\"" -files = [ - {file = "importlib_metadata-8.7.0-py3-none-any.whl", hash = "sha256:e5dd1551894c77868a30651cef00984d50e1002d06942a7101d34870c5f02afd"}, - {file = "importlib_metadata-8.7.0.tar.gz", hash = "sha256:d13b81ad223b890aa16c5471f2ac3056cf76c5f10f82d6f9292f0b415f389000"}, -] - -[package.dependencies] -zipp = ">=3.20" - -[package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] -cover = ["pytest-cov"] -doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] -enabler = ["pytest-enabler (>=2.2)"] -perf = ["ipython"] -test = ["flufl.flake8", "importlib_resources (>=1.3) ; python_version < \"3.9\"", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"] -type = ["pytest-mypy"] - -[[package]] -name = "importlib-resources" -version = "6.5.2" -description = "Read resources from Python packages" -optional = true -python-versions = ">=3.9" -groups = ["main"] -markers = "extra == \"docs\" and python_version == \"3.9\"" -files = [ - {file = "importlib_resources-6.5.2-py3-none-any.whl", hash = "sha256:789cfdc3ed28c78b67a06acb8126751ced69a3d5f79c095a98298cd8a760ccec"}, - {file = "importlib_resources-6.5.2.tar.gz", hash = "sha256:185f87adef5bcc288449d98fb4fba07cea78bc036455dd44c5fc4a2fe78fed2c"}, -] - -[package.dependencies] -zipp = {version = ">=3.1.0", markers = "python_version < \"3.10\""} - -[package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] -cover = ["pytest-cov"] -doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] -enabler = ["pytest-enabler (>=2.2)"] -test = ["jaraco.test (>=5.4)", "pytest (>=6,!=8.1.*)", "zipp (>=3.17)"] -type = ["pytest-mypy"] - [[package]] name = "iniconfig" -version = "2.1.0" +version = "2.3.0" description = "brain-dead simple config-ini parsing" optional = false -python-versions = ">=3.8" +python-versions = ">=3.10" groups = ["dev"] files = [ - {file = "iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760"}, - {file = "iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7"}, -] - -[[package]] -name = "ipython" -version = "8.18.1" -description = "IPython: Productive Interactive Computing" -optional = true -python-versions = ">=3.9" -groups = ["main"] -markers = "python_version < \"3.11\" and extra == \"latex\"" -files = [ - {file = "ipython-8.18.1-py3-none-any.whl", hash = "sha256:e8267419d72d81955ec1177f8a29aaa90ac80ad647499201119e2f05e99aa397"}, - {file = "ipython-8.18.1.tar.gz", hash = "sha256:ca6f079bb33457c66e233e4580ebfc4128855b4cf6370dddd73842a9563e8a27"}, + {file = "iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12"}, + {file = "iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730"}, ] -[package.dependencies] -colorama = {version = "*", markers = "sys_platform == \"win32\""} -decorator = "*" -exceptiongroup = {version = "*", markers = "python_version < \"3.11\""} -jedi = ">=0.16" -matplotlib-inline = "*" -pexpect = {version = ">4.3", markers = "sys_platform != \"win32\""} -prompt-toolkit = ">=3.0.41,<3.1.0" -pygments = ">=2.4.0" -stack-data = "*" -traitlets = ">=5" -typing-extensions = {version = "*", markers = "python_version < \"3.10\""} - -[package.extras] -all = ["black", "curio", "docrepr", "exceptiongroup", "ipykernel", "ipyparallel", "ipywidgets", "matplotlib", "matplotlib (!=3.2.0)", "nbconvert", "nbformat", "notebook", "numpy (>=1.22)", "pandas", "pickleshare", "pytest (<7)", "pytest (<7.1)", "pytest-asyncio (<0.22)", "qtconsole", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "stack-data", "testpath", "trio", "typing-extensions"] -black = ["black"] -doc = ["docrepr", "exceptiongroup", "ipykernel", "matplotlib", "pickleshare", "pytest (<7)", "pytest (<7.1)", "pytest-asyncio (<0.22)", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "stack-data", "testpath", "typing-extensions"] -kernel = ["ipykernel"] -nbconvert = ["nbconvert"] -nbformat = ["nbformat"] -notebook = ["ipywidgets", "notebook"] -parallel = ["ipyparallel"] -qtconsole = ["qtconsole"] -test = ["pickleshare", "pytest (<7.1)", "pytest-asyncio (<0.22)", "testpath"] -test-extra = ["curio", "matplotlib (!=3.2.0)", "nbformat", "numpy (>=1.22)", "pandas", "pickleshare", "pytest (<7.1)", "pytest-asyncio (<0.22)", "testpath", "trio"] - [[package]] name = "ipython" -version = "8.37.0" +version = "8.39.0" description = "IPython: Productive Interactive Computing" optional = true python-versions = ">=3.10" groups = ["main"] -markers = "python_version >= \"3.11\" and extra == \"latex\"" +markers = "extra == \"latex\"" files = [ - {file = "ipython-8.37.0-py3-none-any.whl", hash = "sha256:ed87326596b878932dbcb171e3e698845434d8c61b8d8cd474bf663041a9dcf2"}, - {file = "ipython-8.37.0.tar.gz", hash = "sha256:ca815841e1a41a1e6b73a0b08f3038af9b2252564d01fc405356d34033012216"}, + {file = "ipython-8.39.0-py3-none-any.whl", hash = "sha256:bb3c51c4fa8148ab1dea07a79584d1c854e234ea44aa1283bcb37bc75054651f"}, + {file = "ipython-8.39.0.tar.gz", hash = "sha256:4110ae96012c379b8b6db898a07e186c40a2a1ef5d57a7fa83166047d9da7624"}, ] [package.dependencies] @@ -1179,6 +1025,75 @@ qtconsole = ["qtconsole"] test = ["packaging", "pickleshare", "pytest", "pytest-asyncio (<0.22)", "testpath"] test-extra = ["curio", "ipython[test]", "jupyter_ai", "matplotlib (!=3.2.0)", "nbformat", "numpy (>=1.23)", "pandas", "trio"] +[[package]] +name = "jax" +version = "0.10.0" +description = "Differentiate, compile, and transform Numpy code." +optional = false +python-versions = ">=3.11" +groups = ["main"] +files = [ + {file = "jax-0.10.0-py3-none-any.whl", hash = "sha256:76c42ba163c8db3dc2e449e225b888c0edfb623ded31efdc96d85e0fda1d26e8"}, + {file = "jax-0.10.0.tar.gz", hash = "sha256:0119c767de1645f407df72345d28a3837dc904f1d698911c121d8f2b396fdece"}, +] + +[package.dependencies] +jaxlib = "0.10.0" +ml_dtypes = ">=0.5.0" +numpy = ">=2.0" +opt_einsum = "*" +scipy = ">=1.14" + +[package.extras] +ci = ["jaxlib (==0.9.2)"] +cuda = ["jax-cuda12-plugin[with-cuda] (==0.10.0)", "jaxlib (==0.10.0)"] +cuda12 = ["jax-cuda12-plugin[with-cuda] (==0.10.0)", "jaxlib (==0.10.0)"] +cuda12-local = ["jax-cuda12-plugin (==0.10.0)", "jaxlib (==0.10.0)"] +cuda13 = ["jax-cuda13-plugin[with-cuda] (==0.10.0)", "jaxlib (==0.10.0)"] +cuda13-local = ["jax-cuda13-plugin (==0.10.0)", "jaxlib (==0.10.0)"] +k8s = ["kubernetes"] +minimum-jaxlib = ["jaxlib (==0.10.0)"] +rocm7-local = ["jax-rocm7-plugin (==0.10.0.*)", "jaxlib (==0.10.0)"] +tpu = ["jaxlib (==0.10.0)", "libtpu (==0.0.40.*)", "requests"] +xprof = ["xprof"] + +[[package]] +name = "jaxlib" +version = "0.10.0" +description = "XLA library for JAX" +optional = false +python-versions = ">=3.11" +groups = ["main"] +files = [ + {file = "jaxlib-0.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:277032e9f074c3fd5ffd1e0cb03d4fe66e272de472667cdbc418ad99b21b646a"}, + {file = "jaxlib-0.10.0-cp311-cp311-manylinux_2_27_aarch64.whl", hash = "sha256:3db94ebc859375d955de3504182add7ce1733ce3d30c15e0ef031602cb51a559"}, + {file = "jaxlib-0.10.0-cp311-cp311-manylinux_2_27_x86_64.whl", hash = "sha256:9be229993a41e5b2b84f234ecc19a5de02f35eddb1195cf027bd539e1601e15d"}, + {file = "jaxlib-0.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:421cdf3a4a5c2ee41471035e586954c8dc599d677ce9b11b063c3926a82a7850"}, + {file = "jaxlib-0.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7c1d9b463327c7a2333f210114ecb04f28fefc51ba8233a85a2280cce75bdb42"}, + {file = "jaxlib-0.10.0-cp312-cp312-manylinux_2_27_aarch64.whl", hash = "sha256:aa1d70f1a4e27eb403654e71e2fb28d5786d3e9b77fc1847e8c5389880927ca4"}, + {file = "jaxlib-0.10.0-cp312-cp312-manylinux_2_27_x86_64.whl", hash = "sha256:b0bfb865a07df2e6d7418c0b0c292dd294b5500523b1dd5872b180db2aa480d4"}, + {file = "jaxlib-0.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:25bf167e0d8b594e0ec50783ff4892c0b7ec37236c88b2b425a7c252823f8680"}, + {file = "jaxlib-0.10.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:384635fff55899a295bbc82ee6c6f773a300e787dc472ca92bbe79abfaac8369"}, + {file = "jaxlib-0.10.0-cp313-cp313-manylinux_2_27_aarch64.whl", hash = "sha256:6d8d78b7070b34e4c5bba5f7e10927e7f4aac9b69be17e9b0a5898553a4338f3"}, + {file = "jaxlib-0.10.0-cp313-cp313-manylinux_2_27_x86_64.whl", hash = "sha256:d303dc31b65e8b793d5600f81b1583be03dc9b876a4c10b3e259b6609a1cbe3b"}, + {file = "jaxlib-0.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:3869be623c2f3391be2ee86f8b412372b102492e67cac0a5f0ab1037bbc3a5cc"}, + {file = "jaxlib-0.10.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:9050ce2ae7eeca62b1a235065056cad62cac590ddc035486faa4472a47eed9f6"}, + {file = "jaxlib-0.10.0-cp313-cp313t-manylinux_2_27_aarch64.whl", hash = "sha256:59e07aab3bdfaad9bdd3cf32e0d3d4f228837b9b231c53f5ae1c0fc284481094"}, + {file = "jaxlib-0.10.0-cp313-cp313t-manylinux_2_27_x86_64.whl", hash = "sha256:3088503812cfe49f34a3083d3b7ef5cb3aaf33d89ceb1b3f647fa52713aee59d"}, + {file = "jaxlib-0.10.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:98b26672943672742873f65bc03216819fc55325c99f146590d007c0172bff30"}, + {file = "jaxlib-0.10.0-cp314-cp314-manylinux_2_27_aarch64.whl", hash = "sha256:ad47e072430979ec21637aa487d4dc464028b8e9be27268f37de69536c76e341"}, + {file = "jaxlib-0.10.0-cp314-cp314-manylinux_2_27_x86_64.whl", hash = "sha256:2a42cf04c0f88bc03b150a17fa7ddbb2f40e096667ec8a1b840ed87913e6e735"}, + {file = "jaxlib-0.10.0-cp314-cp314-win_amd64.whl", hash = "sha256:450b771c01b3662c3497e2dceada3f6fc893112ae637ef85ef1dcc7dc68892a8"}, + {file = "jaxlib-0.10.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f62026c9fb1f05998592082a6dcb62f70b466342bc139f711802a9b184ba9a46"}, + {file = "jaxlib-0.10.0-cp314-cp314t-manylinux_2_27_aarch64.whl", hash = "sha256:e66bdc0b57ed5649950799d3f0d67a6bb67f03d06b49ea3fced0bdd6140a9943"}, + {file = "jaxlib-0.10.0-cp314-cp314t-manylinux_2_27_x86_64.whl", hash = "sha256:4dccd9065b30954879869641472d5d12fe4d7914175a5cad56293af8429ce7e0"}, +] + +[package.dependencies] +ml_dtypes = ">=0.5.0" +numpy = ">=2.0" +scipy = ">=1.14" + [[package]] name = "jedi" version = "0.19.2" @@ -1211,6 +1126,7 @@ files = [ {file = "jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67"}, {file = "jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d"}, ] +markers = {main = "extra == \"docs\""} [package.dependencies] MarkupSafe = ">=2.0" @@ -1220,22 +1136,22 @@ i18n = ["Babel (>=2.7)"] [[package]] name = "jsonschema" -version = "4.25.1" +version = "4.26.0" description = "An implementation of JSON Schema validation for Python" optional = true -python-versions = ">=3.9" +python-versions = ">=3.10" groups = ["main"] markers = "extra == \"docs\"" files = [ - {file = "jsonschema-4.25.1-py3-none-any.whl", hash = "sha256:3fba0169e345c7175110351d456342c364814cfcf3b964ba4587f22915230a63"}, - {file = "jsonschema-4.25.1.tar.gz", hash = "sha256:e4a9655ce0da0c0b67a085847e00a3a51449e1157f4f75e9fb5aa545e122eb85"}, + {file = "jsonschema-4.26.0-py3-none-any.whl", hash = "sha256:d489f15263b8d200f8387e64b4c3a75f06629559fb73deb8fdfb525f2dab50ce"}, + {file = "jsonschema-4.26.0.tar.gz", hash = "sha256:0c26707e2efad8aa1bfc5b7ce170f3fccc2e4918ff85989ba9ffa9facb2be326"}, ] [package.dependencies] attrs = ">=22.2.0" -jsonschema-specifications = ">=2023.03.6" +jsonschema-specifications = ">=2023.3.6" referencing = ">=0.28.4" -rpds-py = ">=0.7.1" +rpds-py = ">=0.25.0" [package.extras] format = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3987", "uri-template", "webcolors (>=1.11)"] @@ -1259,45 +1175,44 @@ referencing = ">=0.31.0" [[package]] name = "jupyter-client" -version = "8.6.3" +version = "8.8.0" description = "Jupyter protocol implementation and client libraries" optional = true -python-versions = ">=3.8" +python-versions = ">=3.10" groups = ["main"] markers = "extra == \"docs\"" files = [ - {file = "jupyter_client-8.6.3-py3-none-any.whl", hash = "sha256:e8a19cc986cc45905ac3362915f410f3af85424b4c0905e94fa5f2cb08e8f23f"}, - {file = "jupyter_client-8.6.3.tar.gz", hash = "sha256:35b3a0947c4a6e9d589eb97d7d4cd5e90f910ee73101611f01283732bd6d9419"}, + {file = "jupyter_client-8.8.0-py3-none-any.whl", hash = "sha256:f93a5b99c5e23a507b773d3a1136bd6e16c67883ccdbd9a829b0bbdb98cd7d7a"}, + {file = "jupyter_client-8.8.0.tar.gz", hash = "sha256:d556811419a4f2d96c869af34e854e3f059b7cc2d6d01a9cd9c85c267691be3e"}, ] [package.dependencies] -importlib-metadata = {version = ">=4.8.3", markers = "python_version < \"3.10\""} -jupyter-core = ">=4.12,<5.0.dev0 || >=5.1.dev0" +jupyter-core = ">=5.1" python-dateutil = ">=2.8.2" -pyzmq = ">=23.0" -tornado = ">=6.2" +pyzmq = ">=25.0" +tornado = ">=6.4.1" traitlets = ">=5.3" [package.extras] docs = ["ipykernel", "myst-parser", "pydata-sphinx-theme", "sphinx (>=4)", "sphinx-autodoc-typehints", "sphinxcontrib-github-alt", "sphinxcontrib-spelling"] -test = ["coverage", "ipykernel (>=6.14)", "mypy", "paramiko ; sys_platform == \"win32\"", "pre-commit", "pytest (<8.2.0)", "pytest-cov", "pytest-jupyter[client] (>=0.4.1)", "pytest-timeout"] +orjson = ["orjson"] +test = ["anyio", "coverage", "ipykernel (>=6.14)", "msgpack", "mypy ; platform_python_implementation != \"PyPy\"", "paramiko ; sys_platform == \"win32\"", "pre-commit", "pytest", "pytest-cov", "pytest-jupyter[client] (>=0.6.2)", "pytest-timeout"] [[package]] name = "jupyter-core" -version = "5.8.1" +version = "5.9.1" description = "Jupyter core package. A base package on which Jupyter projects rely." optional = true -python-versions = ">=3.8" +python-versions = ">=3.10" groups = ["main"] markers = "extra == \"docs\"" files = [ - {file = "jupyter_core-5.8.1-py3-none-any.whl", hash = "sha256:c28d268fc90fb53f1338ded2eb410704c5449a358406e8a948b75706e24863d0"}, - {file = "jupyter_core-5.8.1.tar.gz", hash = "sha256:0a5f9706f70e64786b75acba995988915ebd4601c8a52e534a40b51c95f59941"}, + {file = "jupyter_core-5.9.1-py3-none-any.whl", hash = "sha256:ebf87fdc6073d142e114c72c9e29a9d7ca03fad818c5d300ce2adc1fb0743407"}, + {file = "jupyter_core-5.9.1.tar.gz", hash = "sha256:4d09aaff303b9566c3ce657f580bd089ff5c91f5f89cf7d8846c3cdf465b5508"}, ] [package.dependencies] platformdirs = ">=2.5" -pywin32 = {version = ">=300", markers = "sys_platform == \"win32\" and platform_python_implementation != \"PyPy\""} traitlets = ">=5.3" [package.extras] @@ -1319,239 +1234,231 @@ files = [ [[package]] name = "kiwisolver" -version = "1.4.7" -description = "A fast implementation of the Cassowary constraint solver" -optional = true -python-versions = ">=3.8" -groups = ["main"] -markers = "python_version < \"3.11\" and extra == \"docs\"" -files = [ - {file = "kiwisolver-1.4.7-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:8a9c83f75223d5e48b0bc9cb1bf2776cf01563e00ade8775ffe13b0b6e1af3a6"}, - {file = "kiwisolver-1.4.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:58370b1ffbd35407444d57057b57da5d6549d2d854fa30249771775c63b5fe17"}, - {file = "kiwisolver-1.4.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:aa0abdf853e09aff551db11fce173e2177d00786c688203f52c87ad7fcd91ef9"}, - {file = "kiwisolver-1.4.7-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:8d53103597a252fb3ab8b5845af04c7a26d5e7ea8122303dd7a021176a87e8b9"}, - {file = "kiwisolver-1.4.7-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:88f17c5ffa8e9462fb79f62746428dd57b46eb931698e42e990ad63103f35e6c"}, - {file = "kiwisolver-1.4.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88a9ca9c710d598fd75ee5de59d5bda2684d9db36a9f50b6125eaea3969c2599"}, - {file = "kiwisolver-1.4.7-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f4d742cb7af1c28303a51b7a27aaee540e71bb8e24f68c736f6f2ffc82f2bf05"}, - {file = "kiwisolver-1.4.7-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e28c7fea2196bf4c2f8d46a0415c77a1c480cc0724722f23d7410ffe9842c407"}, - {file = "kiwisolver-1.4.7-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e968b84db54f9d42046cf154e02911e39c0435c9801681e3fc9ce8a3c4130278"}, - {file = "kiwisolver-1.4.7-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:0c18ec74c0472de033e1bebb2911c3c310eef5649133dd0bedf2a169a1b269e5"}, - {file = "kiwisolver-1.4.7-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:8f0ea6da6d393d8b2e187e6a5e3fb81f5862010a40c3945e2c6d12ae45cfb2ad"}, - {file = "kiwisolver-1.4.7-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:f106407dda69ae456dd1227966bf445b157ccc80ba0dff3802bb63f30b74e895"}, - {file = "kiwisolver-1.4.7-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:84ec80df401cfee1457063732d90022f93951944b5b58975d34ab56bb150dfb3"}, - {file = "kiwisolver-1.4.7-cp310-cp310-win32.whl", hash = "sha256:71bb308552200fb2c195e35ef05de12f0c878c07fc91c270eb3d6e41698c3bcc"}, - {file = "kiwisolver-1.4.7-cp310-cp310-win_amd64.whl", hash = "sha256:44756f9fd339de0fb6ee4f8c1696cfd19b2422e0d70b4cefc1cc7f1f64045a8c"}, - {file = "kiwisolver-1.4.7-cp310-cp310-win_arm64.whl", hash = "sha256:78a42513018c41c2ffd262eb676442315cbfe3c44eed82385c2ed043bc63210a"}, - {file = "kiwisolver-1.4.7-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d2b0e12a42fb4e72d509fc994713d099cbb15ebf1103545e8a45f14da2dfca54"}, - {file = "kiwisolver-1.4.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2a8781ac3edc42ea4b90bc23e7d37b665d89423818e26eb6df90698aa2287c95"}, - {file = "kiwisolver-1.4.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:46707a10836894b559e04b0fd143e343945c97fd170d69a2d26d640b4e297935"}, - {file = "kiwisolver-1.4.7-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ef97b8df011141c9b0f6caf23b29379f87dd13183c978a30a3c546d2c47314cb"}, - {file = "kiwisolver-1.4.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ab58c12a2cd0fc769089e6d38466c46d7f76aced0a1f54c77652446733d2d02"}, - {file = "kiwisolver-1.4.7-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:803b8e1459341c1bb56d1c5c010406d5edec8a0713a0945851290a7930679b51"}, - {file = "kiwisolver-1.4.7-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f9a9e8a507420fe35992ee9ecb302dab68550dedc0da9e2880dd88071c5fb052"}, - {file = "kiwisolver-1.4.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18077b53dc3bb490e330669a99920c5e6a496889ae8c63b58fbc57c3d7f33a18"}, - {file = "kiwisolver-1.4.7-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6af936f79086a89b3680a280c47ea90b4df7047b5bdf3aa5c524bbedddb9e545"}, - {file = "kiwisolver-1.4.7-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:3abc5b19d24af4b77d1598a585b8a719beb8569a71568b66f4ebe1fb0449460b"}, - {file = "kiwisolver-1.4.7-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:933d4de052939d90afbe6e9d5273ae05fb836cc86c15b686edd4b3560cc0ee36"}, - {file = "kiwisolver-1.4.7-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:65e720d2ab2b53f1f72fb5da5fb477455905ce2c88aaa671ff0a447c2c80e8e3"}, - {file = "kiwisolver-1.4.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3bf1ed55088f214ba6427484c59553123fdd9b218a42bbc8c6496d6754b1e523"}, - {file = "kiwisolver-1.4.7-cp311-cp311-win32.whl", hash = "sha256:4c00336b9dd5ad96d0a558fd18a8b6f711b7449acce4c157e7343ba92dd0cf3d"}, - {file = "kiwisolver-1.4.7-cp311-cp311-win_amd64.whl", hash = "sha256:929e294c1ac1e9f615c62a4e4313ca1823ba37326c164ec720a803287c4c499b"}, - {file = "kiwisolver-1.4.7-cp311-cp311-win_arm64.whl", hash = "sha256:e33e8fbd440c917106b237ef1a2f1449dfbb9b6f6e1ce17c94cd6a1e0d438376"}, - {file = "kiwisolver-1.4.7-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:5360cc32706dab3931f738d3079652d20982511f7c0ac5711483e6eab08efff2"}, - {file = "kiwisolver-1.4.7-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:942216596dc64ddb25adb215c3c783215b23626f8d84e8eff8d6d45c3f29f75a"}, - {file = "kiwisolver-1.4.7-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:48b571ecd8bae15702e4f22d3ff6a0f13e54d3d00cd25216d5e7f658242065ee"}, - {file = "kiwisolver-1.4.7-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ad42ba922c67c5f219097b28fae965e10045ddf145d2928bfac2eb2e17673640"}, - {file = "kiwisolver-1.4.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:612a10bdae23404a72941a0fc8fa2660c6ea1217c4ce0dbcab8a8f6543ea9e7f"}, - {file = "kiwisolver-1.4.7-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9e838bba3a3bac0fe06d849d29772eb1afb9745a59710762e4ba3f4cb8424483"}, - {file = "kiwisolver-1.4.7-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:22f499f6157236c19f4bbbd472fa55b063db77a16cd74d49afe28992dff8c258"}, - {file = "kiwisolver-1.4.7-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:693902d433cf585133699972b6d7c42a8b9f8f826ebcaf0132ff55200afc599e"}, - {file = "kiwisolver-1.4.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4e77f2126c3e0b0d055f44513ed349038ac180371ed9b52fe96a32aa071a5107"}, - {file = "kiwisolver-1.4.7-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:657a05857bda581c3656bfc3b20e353c232e9193eb167766ad2dc58b56504948"}, - {file = "kiwisolver-1.4.7-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4bfa75a048c056a411f9705856abfc872558e33c055d80af6a380e3658766038"}, - {file = "kiwisolver-1.4.7-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:34ea1de54beef1c104422d210c47c7d2a4999bdecf42c7b5718fbe59a4cac383"}, - {file = "kiwisolver-1.4.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:90da3b5f694b85231cf93586dad5e90e2d71b9428f9aad96952c99055582f520"}, - {file = "kiwisolver-1.4.7-cp312-cp312-win32.whl", hash = "sha256:18e0cca3e008e17fe9b164b55735a325140a5a35faad8de92dd80265cd5eb80b"}, - {file = "kiwisolver-1.4.7-cp312-cp312-win_amd64.whl", hash = "sha256:58cb20602b18f86f83a5c87d3ee1c766a79c0d452f8def86d925e6c60fbf7bfb"}, - {file = "kiwisolver-1.4.7-cp312-cp312-win_arm64.whl", hash = "sha256:f5a8b53bdc0b3961f8b6125e198617c40aeed638b387913bf1ce78afb1b0be2a"}, - {file = "kiwisolver-1.4.7-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:2e6039dcbe79a8e0f044f1c39db1986a1b8071051efba3ee4d74f5b365f5226e"}, - {file = "kiwisolver-1.4.7-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a1ecf0ac1c518487d9d23b1cd7139a6a65bc460cd101ab01f1be82ecf09794b6"}, - {file = "kiwisolver-1.4.7-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7ab9ccab2b5bd5702ab0803676a580fffa2aa178c2badc5557a84cc943fcf750"}, - {file = "kiwisolver-1.4.7-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f816dd2277f8d63d79f9c8473a79fe54047bc0467754962840782c575522224d"}, - {file = "kiwisolver-1.4.7-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf8bcc23ceb5a1b624572a1623b9f79d2c3b337c8c455405ef231933a10da379"}, - {file = "kiwisolver-1.4.7-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dea0bf229319828467d7fca8c7c189780aa9ff679c94539eed7532ebe33ed37c"}, - {file = "kiwisolver-1.4.7-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c06a4c7cf15ec739ce0e5971b26c93638730090add60e183530d70848ebdd34"}, - {file = "kiwisolver-1.4.7-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:913983ad2deb14e66d83c28b632fd35ba2b825031f2fa4ca29675e665dfecbe1"}, - {file = "kiwisolver-1.4.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5337ec7809bcd0f424c6b705ecf97941c46279cf5ed92311782c7c9c2026f07f"}, - {file = "kiwisolver-1.4.7-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:4c26ed10c4f6fa6ddb329a5120ba3b6db349ca192ae211e882970bfc9d91420b"}, - {file = "kiwisolver-1.4.7-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c619b101e6de2222c1fcb0531e1b17bbffbe54294bfba43ea0d411d428618c27"}, - {file = "kiwisolver-1.4.7-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:073a36c8273647592ea332e816e75ef8da5c303236ec0167196793eb1e34657a"}, - {file = "kiwisolver-1.4.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:3ce6b2b0231bda412463e152fc18335ba32faf4e8c23a754ad50ffa70e4091ee"}, - {file = "kiwisolver-1.4.7-cp313-cp313-win32.whl", hash = "sha256:f4c9aee212bc89d4e13f58be11a56cc8036cabad119259d12ace14b34476fd07"}, - {file = "kiwisolver-1.4.7-cp313-cp313-win_amd64.whl", hash = "sha256:8a3ec5aa8e38fc4c8af308917ce12c536f1c88452ce554027e55b22cbbfbff76"}, - {file = "kiwisolver-1.4.7-cp313-cp313-win_arm64.whl", hash = "sha256:76c8094ac20ec259471ac53e774623eb62e6e1f56cd8690c67ce6ce4fcb05650"}, - {file = "kiwisolver-1.4.7-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5d5abf8f8ec1f4e22882273c423e16cae834c36856cac348cfbfa68e01c40f3a"}, - {file = "kiwisolver-1.4.7-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:aeb3531b196ef6f11776c21674dba836aeea9d5bd1cf630f869e3d90b16cfade"}, - {file = "kiwisolver-1.4.7-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b7d755065e4e866a8086c9bdada157133ff466476a2ad7861828e17b6026e22c"}, - {file = "kiwisolver-1.4.7-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:08471d4d86cbaec61f86b217dd938a83d85e03785f51121e791a6e6689a3be95"}, - {file = "kiwisolver-1.4.7-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7bbfcb7165ce3d54a3dfbe731e470f65739c4c1f85bb1018ee912bae139e263b"}, - {file = "kiwisolver-1.4.7-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5d34eb8494bea691a1a450141ebb5385e4b69d38bb8403b5146ad279f4b30fa3"}, - {file = "kiwisolver-1.4.7-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9242795d174daa40105c1d86aba618e8eab7bf96ba8c3ee614da8302a9f95503"}, - {file = "kiwisolver-1.4.7-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:a0f64a48bb81af7450e641e3fe0b0394d7381e342805479178b3d335d60ca7cf"}, - {file = "kiwisolver-1.4.7-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:8e045731a5416357638d1700927529e2b8ab304811671f665b225f8bf8d8f933"}, - {file = "kiwisolver-1.4.7-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:4322872d5772cae7369f8351da1edf255a604ea7087fe295411397d0cfd9655e"}, - {file = "kiwisolver-1.4.7-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:e1631290ee9271dffe3062d2634c3ecac02c83890ada077d225e081aca8aab89"}, - {file = "kiwisolver-1.4.7-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:edcfc407e4eb17e037bca59be0e85a2031a2ac87e4fed26d3e9df88b4165f92d"}, - {file = "kiwisolver-1.4.7-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:4d05d81ecb47d11e7f8932bd8b61b720bf0b41199358f3f5e36d38e28f0532c5"}, - {file = "kiwisolver-1.4.7-cp38-cp38-win32.whl", hash = "sha256:b38ac83d5f04b15e515fd86f312479d950d05ce2368d5413d46c088dda7de90a"}, - {file = "kiwisolver-1.4.7-cp38-cp38-win_amd64.whl", hash = "sha256:d83db7cde68459fc803052a55ace60bea2bae361fc3b7a6d5da07e11954e4b09"}, - {file = "kiwisolver-1.4.7-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:3f9362ecfca44c863569d3d3c033dbe8ba452ff8eed6f6b5806382741a1334bd"}, - {file = "kiwisolver-1.4.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e8df2eb9b2bac43ef8b082e06f750350fbbaf2887534a5be97f6cf07b19d9583"}, - {file = "kiwisolver-1.4.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f32d6edbc638cde7652bd690c3e728b25332acbadd7cad670cc4a02558d9c417"}, - {file = "kiwisolver-1.4.7-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:e2e6c39bd7b9372b0be21456caab138e8e69cc0fc1190a9dfa92bd45a1e6e904"}, - {file = "kiwisolver-1.4.7-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:dda56c24d869b1193fcc763f1284b9126550eaf84b88bbc7256e15028f19188a"}, - {file = "kiwisolver-1.4.7-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:79849239c39b5e1fd906556c474d9b0439ea6792b637511f3fe3a41158d89ca8"}, - {file = "kiwisolver-1.4.7-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5e3bc157fed2a4c02ec468de4ecd12a6e22818d4f09cde2c31ee3226ffbefab2"}, - {file = "kiwisolver-1.4.7-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3da53da805b71e41053dc670f9a820d1157aae77b6b944e08024d17bcd51ef88"}, - {file = "kiwisolver-1.4.7-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:8705f17dfeb43139a692298cb6637ee2e59c0194538153e83e9ee0c75c2eddde"}, - {file = "kiwisolver-1.4.7-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:82a5c2f4b87c26bb1a0ef3d16b5c4753434633b83d365cc0ddf2770c93829e3c"}, - {file = "kiwisolver-1.4.7-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:ce8be0466f4c0d585cdb6c1e2ed07232221df101a4c6f28821d2aa754ca2d9e2"}, - {file = "kiwisolver-1.4.7-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:409afdfe1e2e90e6ee7fc896f3df9a7fec8e793e58bfa0d052c8a82f99c37abb"}, - {file = "kiwisolver-1.4.7-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:5b9c3f4ee0b9a439d2415012bd1b1cc2df59e4d6a9939f4d669241d30b414327"}, - {file = "kiwisolver-1.4.7-cp39-cp39-win32.whl", hash = "sha256:a79ae34384df2b615eefca647a2873842ac3b596418032bef9a7283675962644"}, - {file = "kiwisolver-1.4.7-cp39-cp39-win_amd64.whl", hash = "sha256:cf0438b42121a66a3a667de17e779330fc0f20b0d97d59d2f2121e182b0505e4"}, - {file = "kiwisolver-1.4.7-cp39-cp39-win_arm64.whl", hash = "sha256:764202cc7e70f767dab49e8df52c7455e8de0df5d858fa801a11aa0d882ccf3f"}, - {file = "kiwisolver-1.4.7-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:94252291e3fe68001b1dd747b4c0b3be12582839b95ad4d1b641924d68fd4643"}, - {file = "kiwisolver-1.4.7-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:5b7dfa3b546da08a9f622bb6becdb14b3e24aaa30adba66749d38f3cc7ea9706"}, - {file = "kiwisolver-1.4.7-pp310-pypy310_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bd3de6481f4ed8b734da5df134cd5a6a64fe32124fe83dde1e5b5f29fe30b1e6"}, - {file = "kiwisolver-1.4.7-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a91b5f9f1205845d488c928e8570dcb62b893372f63b8b6e98b863ebd2368ff2"}, - {file = "kiwisolver-1.4.7-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40fa14dbd66b8b8f470d5fc79c089a66185619d31645f9b0773b88b19f7223c4"}, - {file = "kiwisolver-1.4.7-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:eb542fe7933aa09d8d8f9d9097ef37532a7df6497819d16efe4359890a2f417a"}, - {file = "kiwisolver-1.4.7-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:bfa1acfa0c54932d5607e19a2c24646fb4c1ae2694437789129cf099789a3b00"}, - {file = "kiwisolver-1.4.7-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:eee3ea935c3d227d49b4eb85660ff631556841f6e567f0f7bda972df6c2c9935"}, - {file = "kiwisolver-1.4.7-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:f3160309af4396e0ed04db259c3ccbfdc3621b5559b5453075e5de555e1f3a1b"}, - {file = "kiwisolver-1.4.7-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:a17f6a29cf8935e587cc8a4dbfc8368c55edc645283db0ce9801016f83526c2d"}, - {file = "kiwisolver-1.4.7-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:10849fb2c1ecbfae45a693c070e0320a91b35dd4bcf58172c023b994283a124d"}, - {file = "kiwisolver-1.4.7-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:ac542bf38a8a4be2dc6b15248d36315ccc65f0743f7b1a76688ffb6b5129a5c2"}, - {file = "kiwisolver-1.4.7-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:8b01aac285f91ca889c800042c35ad3b239e704b150cfd3382adfc9dcc780e39"}, - {file = "kiwisolver-1.4.7-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:48be928f59a1f5c8207154f935334d374e79f2b5d212826307d072595ad76a2e"}, - {file = "kiwisolver-1.4.7-pp39-pypy39_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f37cfe618a117e50d8c240555331160d73d0411422b59b5ee217843d7b693608"}, - {file = "kiwisolver-1.4.7-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:599b5c873c63a1f6ed7eead644a8a380cfbdf5db91dcb6f85707aaab213b1674"}, - {file = "kiwisolver-1.4.7-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:801fa7802e5cfabe3ab0c81a34c323a319b097dfb5004be950482d882f3d7225"}, - {file = "kiwisolver-1.4.7-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:0c6c43471bc764fad4bc99c5c2d6d16a676b1abf844ca7c8702bdae92df01ee0"}, - {file = "kiwisolver-1.4.7.tar.gz", hash = "sha256:9893ff81bd7107f7b685d3017cc6583daadb4fc26e4a888350df530e41980a60"}, -] - -[[package]] -name = "kiwisolver" -version = "1.4.9" +version = "1.5.0" description = "A fast implementation of the Cassowary constraint solver" optional = true python-versions = ">=3.10" groups = ["main"] -markers = "python_version >= \"3.11\" and extra == \"docs\"" -files = [ - {file = "kiwisolver-1.4.9-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b4b4d74bda2b8ebf4da5bd42af11d02d04428b2c32846e4c2c93219df8a7987b"}, - {file = "kiwisolver-1.4.9-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:fb3b8132019ea572f4611d770991000d7f58127560c4889729248eb5852a102f"}, - {file = "kiwisolver-1.4.9-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:84fd60810829c27ae375114cd379da1fa65e6918e1da405f356a775d49a62bcf"}, - {file = "kiwisolver-1.4.9-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:b78efa4c6e804ecdf727e580dbb9cba85624d2e1c6b5cb059c66290063bd99a9"}, - {file = "kiwisolver-1.4.9-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d4efec7bcf21671db6a3294ff301d2fc861c31faa3c8740d1a94689234d1b415"}, - {file = "kiwisolver-1.4.9-cp310-cp310-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:90f47e70293fc3688b71271100a1a5453aa9944a81d27ff779c108372cf5567b"}, - {file = "kiwisolver-1.4.9-cp310-cp310-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8fdca1def57a2e88ef339de1737a1449d6dbf5fab184c54a1fca01d541317154"}, - {file = "kiwisolver-1.4.9-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:9cf554f21be770f5111a1690d42313e140355e687e05cf82cb23d0a721a64a48"}, - {file = "kiwisolver-1.4.9-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fc1795ac5cd0510207482c3d1d3ed781143383b8cfd36f5c645f3897ce066220"}, - {file = "kiwisolver-1.4.9-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:ccd09f20ccdbbd341b21a67ab50a119b64a403b09288c27481575105283c1586"}, - {file = "kiwisolver-1.4.9-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:540c7c72324d864406a009d72f5d6856f49693db95d1fbb46cf86febef873634"}, - {file = "kiwisolver-1.4.9-cp310-cp310-win_amd64.whl", hash = "sha256:ede8c6d533bc6601a47ad4046080d36b8fc99f81e6f1c17b0ac3c2dc91ac7611"}, - {file = "kiwisolver-1.4.9-cp310-cp310-win_arm64.whl", hash = "sha256:7b4da0d01ac866a57dd61ac258c5607b4cd677f63abaec7b148354d2b2cdd536"}, - {file = "kiwisolver-1.4.9-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:eb14a5da6dc7642b0f3a18f13654847cd8b7a2550e2645a5bda677862b03ba16"}, - {file = "kiwisolver-1.4.9-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:39a219e1c81ae3b103643d2aedb90f1ef22650deb266ff12a19e7773f3e5f089"}, - {file = "kiwisolver-1.4.9-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2405a7d98604b87f3fc28b1716783534b1b4b8510d8142adca34ee0bc3c87543"}, - {file = "kiwisolver-1.4.9-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:dc1ae486f9abcef254b5618dfb4113dd49f94c68e3e027d03cf0143f3f772b61"}, - {file = "kiwisolver-1.4.9-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8a1f570ce4d62d718dce3f179ee78dac3b545ac16c0c04bb363b7607a949c0d1"}, - {file = "kiwisolver-1.4.9-cp311-cp311-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:cb27e7b78d716c591e88e0a09a2139c6577865d7f2e152488c2cc6257f460872"}, - {file = "kiwisolver-1.4.9-cp311-cp311-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:15163165efc2f627eb9687ea5f3a28137217d217ac4024893d753f46bce9de26"}, - {file = "kiwisolver-1.4.9-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:bdee92c56a71d2b24c33a7d4c2856bd6419d017e08caa7802d2963870e315028"}, - {file = "kiwisolver-1.4.9-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:412f287c55a6f54b0650bd9b6dce5aceddb95864a1a90c87af16979d37c89771"}, - {file = "kiwisolver-1.4.9-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:2c93f00dcba2eea70af2be5f11a830a742fe6b579a1d4e00f47760ef13be247a"}, - {file = "kiwisolver-1.4.9-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f117e1a089d9411663a3207ba874f31be9ac8eaa5b533787024dc07aeb74f464"}, - {file = "kiwisolver-1.4.9-cp311-cp311-win_amd64.whl", hash = "sha256:be6a04e6c79819c9a8c2373317d19a96048e5a3f90bec587787e86a1153883c2"}, - {file = "kiwisolver-1.4.9-cp311-cp311-win_arm64.whl", hash = "sha256:0ae37737256ba2de764ddc12aed4956460277f00c4996d51a197e72f62f5eec7"}, - {file = "kiwisolver-1.4.9-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:ac5a486ac389dddcc5bef4f365b6ae3ffff2c433324fb38dd35e3fab7c957999"}, - {file = "kiwisolver-1.4.9-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f2ba92255faa7309d06fe44c3a4a97efe1c8d640c2a79a5ef728b685762a6fd2"}, - {file = "kiwisolver-1.4.9-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4a2899935e724dd1074cb568ce7ac0dce28b2cd6ab539c8e001a8578eb106d14"}, - {file = "kiwisolver-1.4.9-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f6008a4919fdbc0b0097089f67a1eb55d950ed7e90ce2cc3e640abadd2757a04"}, - {file = "kiwisolver-1.4.9-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:67bb8b474b4181770f926f7b7d2f8c0248cbcb78b660fdd41a47054b28d2a752"}, - {file = "kiwisolver-1.4.9-cp312-cp312-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2327a4a30d3ee07d2fbe2e7933e8a37c591663b96ce42a00bc67461a87d7df77"}, - {file = "kiwisolver-1.4.9-cp312-cp312-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7a08b491ec91b1d5053ac177afe5290adacf1f0f6307d771ccac5de30592d198"}, - {file = "kiwisolver-1.4.9-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d8fc5c867c22b828001b6a38d2eaeb88160bf5783c6cb4a5e440efc981ce286d"}, - {file = "kiwisolver-1.4.9-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:3b3115b2581ea35bb6d1f24a4c90af37e5d9b49dcff267eeed14c3893c5b86ab"}, - {file = "kiwisolver-1.4.9-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:858e4c22fb075920b96a291928cb7dea5644e94c0ee4fcd5af7e865655e4ccf2"}, - {file = "kiwisolver-1.4.9-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ed0fecd28cc62c54b262e3736f8bb2512d8dcfdc2bcf08be5f47f96bf405b145"}, - {file = "kiwisolver-1.4.9-cp312-cp312-win_amd64.whl", hash = "sha256:f68208a520c3d86ea51acf688a3e3002615a7f0238002cccc17affecc86a8a54"}, - {file = "kiwisolver-1.4.9-cp312-cp312-win_arm64.whl", hash = "sha256:2c1a4f57df73965f3f14df20b80ee29e6a7930a57d2d9e8491a25f676e197c60"}, - {file = "kiwisolver-1.4.9-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a5d0432ccf1c7ab14f9949eec60c5d1f924f17c037e9f8b33352fa05799359b8"}, - {file = "kiwisolver-1.4.9-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efb3a45b35622bb6c16dbfab491a8f5a391fe0e9d45ef32f4df85658232ca0e2"}, - {file = "kiwisolver-1.4.9-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1a12cf6398e8a0a001a059747a1cbf24705e18fe413bc22de7b3d15c67cffe3f"}, - {file = "kiwisolver-1.4.9-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b67e6efbf68e077dd71d1a6b37e43e1a99d0bff1a3d51867d45ee8908b931098"}, - {file = "kiwisolver-1.4.9-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5656aa670507437af0207645273ccdfee4f14bacd7f7c67a4306d0dcaeaf6eed"}, - {file = "kiwisolver-1.4.9-cp313-cp313-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:bfc08add558155345129c7803b3671cf195e6a56e7a12f3dde7c57d9b417f525"}, - {file = "kiwisolver-1.4.9-cp313-cp313-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:40092754720b174e6ccf9e845d0d8c7d8e12c3d71e7fc35f55f3813e96376f78"}, - {file = "kiwisolver-1.4.9-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:497d05f29a1300d14e02e6441cf0f5ee81c1ff5a304b0d9fb77423974684e08b"}, - {file = "kiwisolver-1.4.9-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:bdd1a81a1860476eb41ac4bc1e07b3f07259e6d55bbf739b79c8aaedcf512799"}, - {file = "kiwisolver-1.4.9-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:e6b93f13371d341afee3be9f7c5964e3fe61d5fa30f6a30eb49856935dfe4fc3"}, - {file = "kiwisolver-1.4.9-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d75aa530ccfaa593da12834b86a0724f58bff12706659baa9227c2ccaa06264c"}, - {file = "kiwisolver-1.4.9-cp313-cp313-win_amd64.whl", hash = "sha256:dd0a578400839256df88c16abddf9ba14813ec5f21362e1fe65022e00c883d4d"}, - {file = "kiwisolver-1.4.9-cp313-cp313-win_arm64.whl", hash = "sha256:d4188e73af84ca82468f09cadc5ac4db578109e52acb4518d8154698d3a87ca2"}, - {file = "kiwisolver-1.4.9-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:5a0f2724dfd4e3b3ac5a82436a8e6fd16baa7d507117e4279b660fe8ca38a3a1"}, - {file = "kiwisolver-1.4.9-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:1b11d6a633e4ed84fc0ddafd4ebfd8ea49b3f25082c04ad12b8315c11d504dc1"}, - {file = "kiwisolver-1.4.9-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:61874cdb0a36016354853593cffc38e56fc9ca5aa97d2c05d3dcf6922cd55a11"}, - {file = "kiwisolver-1.4.9-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:60c439763a969a6af93b4881db0eed8fadf93ee98e18cbc35bc8da868d0c4f0c"}, - {file = "kiwisolver-1.4.9-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:92a2f997387a1b79a75e7803aa7ded2cfbe2823852ccf1ba3bcf613b62ae3197"}, - {file = "kiwisolver-1.4.9-cp313-cp313t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a31d512c812daea6d8b3be3b2bfcbeb091dbb09177706569bcfc6240dcf8b41c"}, - {file = "kiwisolver-1.4.9-cp313-cp313t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:52a15b0f35dad39862d376df10c5230155243a2c1a436e39eb55623ccbd68185"}, - {file = "kiwisolver-1.4.9-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a30fd6fdef1430fd9e1ba7b3398b5ee4e2887783917a687d86ba69985fb08748"}, - {file = "kiwisolver-1.4.9-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:cc9617b46837c6468197b5945e196ee9ca43057bb7d9d1ae688101e4e1dddf64"}, - {file = "kiwisolver-1.4.9-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:0ab74e19f6a2b027ea4f845a78827969af45ce790e6cb3e1ebab71bdf9f215ff"}, - {file = "kiwisolver-1.4.9-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:dba5ee5d3981160c28d5490f0d1b7ed730c22470ff7f6cc26cfcfaacb9896a07"}, - {file = "kiwisolver-1.4.9-cp313-cp313t-win_arm64.whl", hash = "sha256:0749fd8f4218ad2e851e11cc4dc05c7cbc0cbc4267bdfdb31782e65aace4ee9c"}, - {file = "kiwisolver-1.4.9-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:9928fe1eb816d11ae170885a74d074f57af3a0d65777ca47e9aeb854a1fba386"}, - {file = "kiwisolver-1.4.9-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:d0005b053977e7b43388ddec89fa567f43d4f6d5c2c0affe57de5ebf290dc552"}, - {file = "kiwisolver-1.4.9-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:2635d352d67458b66fd0667c14cb1d4145e9560d503219034a18a87e971ce4f3"}, - {file = "kiwisolver-1.4.9-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:767c23ad1c58c9e827b649a9ab7809fd5fd9db266a9cf02b0e926ddc2c680d58"}, - {file = "kiwisolver-1.4.9-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:72d0eb9fba308b8311685c2268cf7d0a0639a6cd027d8128659f72bdd8a024b4"}, - {file = "kiwisolver-1.4.9-cp314-cp314-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f68e4f3eeca8fb22cc3d731f9715a13b652795ef657a13df1ad0c7dc0e9731df"}, - {file = "kiwisolver-1.4.9-cp314-cp314-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d84cd4061ae292d8ac367b2c3fa3aad11cb8625a95d135fe93f286f914f3f5a6"}, - {file = "kiwisolver-1.4.9-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:a60ea74330b91bd22a29638940d115df9dc00af5035a9a2a6ad9399ffb4ceca5"}, - {file = "kiwisolver-1.4.9-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:ce6a3a4e106cf35c2d9c4fa17c05ce0b180db622736845d4315519397a77beaf"}, - {file = "kiwisolver-1.4.9-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:77937e5e2a38a7b48eef0585114fe7930346993a88060d0bf886086d2aa49ef5"}, - {file = "kiwisolver-1.4.9-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:24c175051354f4a28c5d6a31c93906dc653e2bf234e8a4bbfb964892078898ce"}, - {file = "kiwisolver-1.4.9-cp314-cp314-win_amd64.whl", hash = "sha256:0763515d4df10edf6d06a3c19734e2566368980d21ebec439f33f9eb936c07b7"}, - {file = "kiwisolver-1.4.9-cp314-cp314-win_arm64.whl", hash = "sha256:0e4e2bf29574a6a7b7f6cb5fa69293b9f96c928949ac4a53ba3f525dffb87f9c"}, - {file = "kiwisolver-1.4.9-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:d976bbb382b202f71c67f77b0ac11244021cfa3f7dfd9e562eefcea2df711548"}, - {file = "kiwisolver-1.4.9-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:2489e4e5d7ef9a1c300a5e0196e43d9c739f066ef23270607d45aba368b91f2d"}, - {file = "kiwisolver-1.4.9-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:e2ea9f7ab7fbf18fffb1b5434ce7c69a07582f7acc7717720f1d69f3e806f90c"}, - {file = "kiwisolver-1.4.9-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b34e51affded8faee0dfdb705416153819d8ea9250bbbf7ea1b249bdeb5f1122"}, - {file = "kiwisolver-1.4.9-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d8aacd3d4b33b772542b2e01beb50187536967b514b00003bdda7589722d2a64"}, - {file = "kiwisolver-1.4.9-cp314-cp314t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7cf974dd4e35fa315563ac99d6287a1024e4dc2077b8a7d7cd3d2fb65d283134"}, - {file = "kiwisolver-1.4.9-cp314-cp314t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:85bd218b5ecfbee8c8a82e121802dcb519a86044c9c3b2e4aef02fa05c6da370"}, - {file = "kiwisolver-1.4.9-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:0856e241c2d3df4efef7c04a1e46b1936b6120c9bcf36dd216e3acd84bc4fb21"}, - {file = "kiwisolver-1.4.9-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:9af39d6551f97d31a4deebeac6f45b156f9755ddc59c07b402c148f5dbb6482a"}, - {file = "kiwisolver-1.4.9-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:bb4ae2b57fc1d8cbd1cf7b1d9913803681ffa903e7488012be5b76dedf49297f"}, - {file = "kiwisolver-1.4.9-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:aedff62918805fb62d43a4aa2ecd4482c380dc76cd31bd7c8878588a61bd0369"}, - {file = "kiwisolver-1.4.9-cp314-cp314t-win_amd64.whl", hash = "sha256:1fa333e8b2ce4d9660f2cda9c0e1b6bafcfb2457a9d259faa82289e73ec24891"}, - {file = "kiwisolver-1.4.9-cp314-cp314t-win_arm64.whl", hash = "sha256:4a48a2ce79d65d363597ef7b567ce3d14d68783d2b2263d98db3d9477805ba32"}, - {file = "kiwisolver-1.4.9-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:4d1d9e582ad4d63062d34077a9a1e9f3c34088a2ec5135b1f7190c07cf366527"}, - {file = "kiwisolver-1.4.9-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:deed0c7258ceb4c44ad5ec7d9918f9f14fd05b2be86378d86cf50e63d1e7b771"}, - {file = "kiwisolver-1.4.9-pp310-pypy310_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0a590506f303f512dff6b7f75fd2fd18e16943efee932008fe7140e5fa91d80e"}, - {file = "kiwisolver-1.4.9-pp310-pypy310_pp73-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e09c2279a4d01f099f52d5c4b3d9e208e91edcbd1a175c9662a8b16e000fece9"}, - {file = "kiwisolver-1.4.9-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:c9e7cdf45d594ee04d5be1b24dd9d49f3d1590959b2271fb30b5ca2b262c00fb"}, - {file = "kiwisolver-1.4.9-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:720e05574713db64c356e86732c0f3c5252818d05f9df320f0ad8380641acea5"}, - {file = "kiwisolver-1.4.9-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:17680d737d5335b552994a2008fab4c851bcd7de33094a82067ef3a576ff02fa"}, - {file = "kiwisolver-1.4.9-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:85b5352f94e490c028926ea567fc569c52ec79ce131dadb968d3853e809518c2"}, - {file = "kiwisolver-1.4.9-pp311-pypy311_pp73-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:464415881e4801295659462c49461a24fb107c140de781d55518c4b80cb6790f"}, - {file = "kiwisolver-1.4.9-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:fb940820c63a9590d31d88b815e7a3aa5915cad3ce735ab45f0c730b39547de1"}, - {file = "kiwisolver-1.4.9.tar.gz", hash = "sha256:c3b22c26c6fd6811b0ae8363b95ca8ce4ea3c202d3d0975b2914310ceb1bcc4d"}, +markers = "extra == \"docs\"" +files = [ + {file = "kiwisolver-1.5.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:32cc0a5365239a6ea0c6ed461e8838d053b57e397443c0ca894dcc8e388d4374"}, + {file = "kiwisolver-1.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:cc0b66c1eec9021353a4b4483afb12dfd50e3669ffbb9152d6842eb34c7e29fd"}, + {file = "kiwisolver-1.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:86e0287879f75621ae85197b0877ed2f8b7aa57b511c7331dce2eb6f4de7d476"}, + {file = "kiwisolver-1.5.0-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:62f59da443c4f4849f73a51a193b1d9d258dcad0c41bc4d1b8fb2bcc04bfeb22"}, + {file = "kiwisolver-1.5.0-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9190426b7aa26c5229501fa297b8d0653cfd3f5a36f7990c264e157cbf886b3b"}, + {file = "kiwisolver-1.5.0-cp310-cp310-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c8277104ded0a51e699c8c3aff63ce2c56d4ed5519a5f73e0fd7057f959a2b9e"}, + {file = "kiwisolver-1.5.0-cp310-cp310-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8f9baf6f0a6e7571c45c8863010b45e837c3ee1c2c77fcd6ef423be91b21fedb"}, + {file = "kiwisolver-1.5.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cff8e5383db4989311f99e814feeb90c4723eb4edca425b9d5d9c3fefcdd9537"}, + {file = "kiwisolver-1.5.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:ebae99ed6764f2b5771c522477b311be313e8841d2e0376db2b10922daebbba4"}, + {file = "kiwisolver-1.5.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:d5cd5189fc2b6a538b75ae45433140c4823463918f7b1617c31e68b085c0022c"}, + {file = "kiwisolver-1.5.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f42c23db5d1521218a3276bb08666dcb662896a0be7347cba864eca45ff64ede"}, + {file = "kiwisolver-1.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:94eff26096eb5395136634622515b234ecb6c9979824c1f5004c6e3c3c85ccd2"}, + {file = "kiwisolver-1.5.0-cp310-cp310-win_arm64.whl", hash = "sha256:dd952e03bfbb096cfe2dd35cd9e00f269969b67536cb4370994afc20ff2d0875"}, + {file = "kiwisolver-1.5.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9eed0f7edbb274413b6ee781cca50541c8c0facd3d6fd289779e494340a2b85c"}, + {file = "kiwisolver-1.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3c4923e404d6bcd91b6779c009542e5647fef32e4a5d75e115e3bbac6f2335eb"}, + {file = "kiwisolver-1.5.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0df54df7e686afa55e6f21fb86195224a6d9beb71d637e8d7920c95cf0f89aac"}, + {file = "kiwisolver-1.5.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2517e24d7315eb51c10664cdb865195df38ab74456c677df67bb47f12d088a27"}, + {file = "kiwisolver-1.5.0-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ff710414307fefa903e0d9bdf300972f892c23477829f49504e59834f4195398"}, + {file = "kiwisolver-1.5.0-cp311-cp311-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6176c1811d9d5a04fa391c490cc44f451e240697a16977f11c6f722efb9041db"}, + {file = "kiwisolver-1.5.0-cp311-cp311-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:50847dca5d197fcbd389c805aa1a1cf32f25d2e7273dc47ab181a517666b68cc"}, + {file = "kiwisolver-1.5.0-cp311-cp311-manylinux_2_39_riscv64.whl", hash = "sha256:01808c6d15f4c3e8559595d6d1fe6411c68e4a3822b4b9972b44473b24f4e679"}, + {file = "kiwisolver-1.5.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:f1f9f4121ec58628c96baa3de1a55a4e3a333c5102c8e94b64e23bf7b2083309"}, + {file = "kiwisolver-1.5.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:b7d335370ae48a780c6e6a6bbfa97342f563744c39c35562f3f367665f5c1de2"}, + {file = "kiwisolver-1.5.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:800ee55980c18545af444d93fdd60c56b580db5cc54867d8cbf8a1dc0829938c"}, + {file = "kiwisolver-1.5.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:c438f6ca858697c9ab67eb28246c92508af972e114cac34e57a6d4ba17a3ac08"}, + {file = "kiwisolver-1.5.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:8c63c91f95173f9c2a67c7c526b2cea976828a0e7fced9cdcead2802dc10f8a4"}, + {file = "kiwisolver-1.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:beb7f344487cdcb9e1efe4b7a29681b74d34c08f0043a327a74da852a6749e7b"}, + {file = "kiwisolver-1.5.0-cp311-cp311-win_arm64.whl", hash = "sha256:ad4ae4ffd1ee9cd11357b4c66b612da9888f4f4daf2f36995eda64bd45370cac"}, + {file = "kiwisolver-1.5.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:4e9750bc21b886308024f8a54ccb9a2cc38ac9fa813bf4348434e3d54f337ff9"}, + {file = "kiwisolver-1.5.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:72ec46b7eba5b395e0a7b63025490d3214c11013f4aacb4f5e8d6c3041829588"}, + {file = "kiwisolver-1.5.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ed3a984b31da7481b103f68776f7128a89ef26ed40f4dc41a2223cda7fb24819"}, + {file = "kiwisolver-1.5.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:bb5136fb5352d3f422df33f0c879a1b0c204004324150cc3b5e3c4f310c9049f"}, + {file = "kiwisolver-1.5.0-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b2af221f268f5af85e776a73d62b0845fc8baf8ef0abfae79d29c77d0e776aaf"}, + {file = "kiwisolver-1.5.0-cp312-cp312-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b0f172dc8ffaccb8522d7c5d899de00133f2f1ca7b0a49b7da98e901de87bf2d"}, + {file = "kiwisolver-1.5.0-cp312-cp312-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6ab8ba9152203feec73758dad83af9a0bbe05001eb4639e547207c40cfb52083"}, + {file = "kiwisolver-1.5.0-cp312-cp312-manylinux_2_39_riscv64.whl", hash = "sha256:cdee07c4d7f6d72008d3f73b9bf027f4e11550224c7c50d8df1ae4a37c1402a6"}, + {file = "kiwisolver-1.5.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7c60d3c9b06fb23bd9c6139281ccbdc384297579ae037f08ae90c69f6845c0b1"}, + {file = "kiwisolver-1.5.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:e315e5ec90d88e140f57696ff85b484ff68bb311e36f2c414aa4286293e6dee0"}, + {file = "kiwisolver-1.5.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:1465387ac63576c3e125e5337a6892b9e99e0627d52317f3ca79e6930d889d15"}, + {file = "kiwisolver-1.5.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:530a3fd64c87cffa844d4b6b9768774763d9caa299e9b75d8eca6a4423b31314"}, + {file = "kiwisolver-1.5.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1d9daea4ea6b9be74fe2f01f7fbade8d6ffab263e781274cffca0dba9be9eec9"}, + {file = "kiwisolver-1.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:f18c2d9782259a6dc132fdc7a63c168cbc74b35284b6d75c673958982a378384"}, + {file = "kiwisolver-1.5.0-cp312-cp312-win_arm64.whl", hash = "sha256:f7c7553b13f69c1b29a5bde08ddc6d9d0c8bfb84f9ed01c30db25944aeb852a7"}, + {file = "kiwisolver-1.5.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:fd40bb9cd0891c4c3cb1ddf83f8bbfa15731a248fdc8162669405451e2724b09"}, + {file = "kiwisolver-1.5.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c0e1403fd7c26d77c1f03e096dc58a5c726503fa0db0456678b8668f76f521e3"}, + {file = "kiwisolver-1.5.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:dda366d548e89a90d88a86c692377d18d8bd64b39c1fb2b92cb31370e2896bbd"}, + {file = "kiwisolver-1.5.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:332b4f0145c30b5f5ad9374881133e5aa64320428a57c2c2b61e9d891a51c2f3"}, + {file = "kiwisolver-1.5.0-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0c50b89ffd3e1a911c69a1dd3de7173c0cd10b130f56222e57898683841e4f96"}, + {file = "kiwisolver-1.5.0-cp313-cp313-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4db576bb8c3ef9365f8b40fe0f671644de6736ae2c27a2c62d7d8a1b4329f099"}, + {file = "kiwisolver-1.5.0-cp313-cp313-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0b85aad90cea8ac6797a53b5d5f2e967334fa4d1149f031c4537569972596cb8"}, + {file = "kiwisolver-1.5.0-cp313-cp313-manylinux_2_39_riscv64.whl", hash = "sha256:d36ca54cb4c6c4686f7cbb7b817f66f5911c12ddb519450bbe86707155028f87"}, + {file = "kiwisolver-1.5.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:38f4a703656f493b0ad185211ccfca7f0386120f022066b018eb5296d8613e23"}, + {file = "kiwisolver-1.5.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:3ac2360e93cb41be81121755c6462cff3beaa9967188c866e5fce5cf13170859"}, + {file = "kiwisolver-1.5.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:c95cab08d1965db3d84a121f1c7ce7479bdd4072c9b3dafd8fecce48a2e6b902"}, + {file = "kiwisolver-1.5.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:fc20894c3d21194d8041a28b65622d5b86db786da6e3cfe73f0c762951a61167"}, + {file = "kiwisolver-1.5.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7a32f72973f0f950c1920475d5c5ea3d971b81b6f0ec53b8d0a956cc965f22e0"}, + {file = "kiwisolver-1.5.0-cp313-cp313-win_amd64.whl", hash = "sha256:0bf3acf1419fa93064a4c2189ac0b58e3be7872bf6ee6177b0d4c63dc4cea276"}, + {file = "kiwisolver-1.5.0-cp313-cp313-win_arm64.whl", hash = "sha256:fa8eb9ecdb7efb0b226acec134e0d709e87a909fa4971a54c0c4f6e88635484c"}, + {file = "kiwisolver-1.5.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:db485b3847d182b908b483b2ed133c66d88d49cacf98fd278fadafe11b4478d1"}, + {file = "kiwisolver-1.5.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:be12f931839a3bdfe28b584db0e640a65a8bcbc24560ae3fdb025a449b3d754e"}, + {file = "kiwisolver-1.5.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:16b85d37c2cbb3253226d26e64663f755d88a03439a9c47df6246b35defbdfb7"}, + {file = "kiwisolver-1.5.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4432b835675f0ea7414aab3d37d119f7226d24869b7a829caeab49ebda407b0c"}, + {file = "kiwisolver-1.5.0-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b0feb50971481a2cc44d94e88bdb02cdd497618252ae226b8eb1201b957e368"}, + {file = "kiwisolver-1.5.0-cp313-cp313t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:56fa888f10d0f367155e76ce849fa1166fc9730d13bd2d65a2aa13b6f5424489"}, + {file = "kiwisolver-1.5.0-cp313-cp313t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:940dda65d5e764406b9fb92761cbf462e4e63f712ab60ed98f70552e496f3bf1"}, + {file = "kiwisolver-1.5.0-cp313-cp313t-manylinux_2_39_riscv64.whl", hash = "sha256:89fc958c702ee9a745e4700378f5d23fddbc46ff89e8fdbf5395c24d5c1452a3"}, + {file = "kiwisolver-1.5.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9027d773c4ff81487181a925945743413f6069634d0b122d0b37684ccf4f1e18"}, + {file = "kiwisolver-1.5.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:5b233ea3e165e43e35dba1d2b8ecc21cf070b45b65ae17dd2747d2713d942021"}, + {file = "kiwisolver-1.5.0-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:ce9bf03dad3b46408c08649c6fbd6ca28a9fce0eb32fdfffa6775a13103b5310"}, + {file = "kiwisolver-1.5.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:fc4d3f1fb9ca0ae9f97b095963bc6326f1dbfd3779d6679a1e016b9baaa153d3"}, + {file = "kiwisolver-1.5.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f443b4825c50a51ee68585522ab4a1d1257fac65896f282b4c6763337ac9f5d2"}, + {file = "kiwisolver-1.5.0-cp313-cp313t-win_arm64.whl", hash = "sha256:893ff3a711d1b515ba9da14ee090519bad4610ed1962fbe298a434e8c5f8db53"}, + {file = "kiwisolver-1.5.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:8df31fe574b8b3993cc61764f40941111b25c2d9fea13d3ce24a49907cd2d615"}, + {file = "kiwisolver-1.5.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:1d49a49ac4cbfb7c1375301cd1ec90169dfeae55ff84710d782260ce77a75a02"}, + {file = "kiwisolver-1.5.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0cbe94b69b819209a62cb27bdfa5dc2a8977d8de2f89dfd97ba4f53ed3af754e"}, + {file = "kiwisolver-1.5.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:80aa065ffd378ff784822a6d7c3212f2d5f5e9c3589614b5c228b311fd3063ac"}, + {file = "kiwisolver-1.5.0-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e7f886f47ab881692f278ae901039a234e4025a68e6dfab514263a0b1c4ae05"}, + {file = "kiwisolver-1.5.0-cp314-cp314-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5060731cc3ed12ca3a8b57acd4aeca5bbc2f49216dd0bec1650a1acd89486bcd"}, + {file = "kiwisolver-1.5.0-cp314-cp314-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7a4aa69609f40fce3cbc3f87b2061f042eee32f94b8f11db707b66a26461591a"}, + {file = "kiwisolver-1.5.0-cp314-cp314-manylinux_2_39_riscv64.whl", hash = "sha256:d168fda2dbff7b9b5f38e693182d792a938c31db4dac3a80a4888de603c99554"}, + {file = "kiwisolver-1.5.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:413b820229730d358efd838ecbab79902fe97094565fdc80ddb6b0a18c18a581"}, + {file = "kiwisolver-1.5.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:5124d1ea754509b09e53738ec185584cc609aae4a3b510aaf4ed6aa047ef9303"}, + {file = "kiwisolver-1.5.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:e4415a8db000bf49a6dd1c478bf70062eaacff0f462b92b0ba68791a905861f9"}, + {file = "kiwisolver-1.5.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:d618fd27420381a4f6044faa71f46d8bfd911bd077c555f7138ed88729bfbe79"}, + {file = "kiwisolver-1.5.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5092eb5b1172947f57d6ea7d89b2f29650414e4293c47707eb499ec07a0ac796"}, + {file = "kiwisolver-1.5.0-cp314-cp314-win_amd64.whl", hash = "sha256:d76e2d8c75051d58177e762164d2e9ab92886534e3a12e795f103524f221dd8e"}, + {file = "kiwisolver-1.5.0-cp314-cp314-win_arm64.whl", hash = "sha256:fa6248cd194edff41d7ea9425ced8ca3a6f838bfb295f6f1d6e6bb694a8518df"}, + {file = "kiwisolver-1.5.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:d1ffeb80b5676463d7a7d56acbe8e37a20ce725570e09549fe738e02ca6b7e1e"}, + {file = "kiwisolver-1.5.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:bc4d8e252f532ab46a1de9349e2d27b91fce46736a9eedaa37beaca66f574ed4"}, + {file = "kiwisolver-1.5.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6783e069732715ad0c3ce96dbf21dbc2235ab0593f2baf6338101f70371f4028"}, + {file = "kiwisolver-1.5.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e7c4c09a490dc4d4a7f8cbee56c606a320f9dc28cf92a7157a39d1ce7676a657"}, + {file = "kiwisolver-1.5.0-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2a075bd7bd19c70cf67c8badfa36cf7c5d8de3c9ddb8420c51e10d9c50e94920"}, + {file = "kiwisolver-1.5.0-cp314-cp314t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:bdd3e53429ff02aa319ba59dfe4ceeec345bf46cf180ec2cf6fd5b942e7975e9"}, + {file = "kiwisolver-1.5.0-cp314-cp314t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3cdcb35dc9d807259c981a85531048ede628eabcffb3239adf3d17463518992d"}, + {file = "kiwisolver-1.5.0-cp314-cp314t-manylinux_2_39_riscv64.whl", hash = "sha256:70d593af6a6ca332d1df73d519fddb5148edb15cd90d5f0155e3746a6d4fcc65"}, + {file = "kiwisolver-1.5.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:377815a8616074cabbf3f53354e1d040c35815a134e01d7614b7692e4bf8acfa"}, + {file = "kiwisolver-1.5.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:0255a027391d52944eae1dbb5d4cc5903f57092f3674e8e544cdd2622826b3f0"}, + {file = "kiwisolver-1.5.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:012b1eb16e28718fa782b5e61dc6f2da1f0792ca73bd05d54de6cb9561665fc9"}, + {file = "kiwisolver-1.5.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:0e3aafb33aed7479377e5e9a82e9d4bf87063741fc99fc7ae48b0f16e32bdd6f"}, + {file = "kiwisolver-1.5.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:e7a116ae737f0000343218c4edf5bd45893bfeaff0993c0b215d7124c9f77646"}, + {file = "kiwisolver-1.5.0-cp314-cp314t-win_amd64.whl", hash = "sha256:1dd9b0b119a350976a6d781e7278ec7aca0b201e1a9e2d23d9804afecb6ca681"}, + {file = "kiwisolver-1.5.0-cp314-cp314t-win_arm64.whl", hash = "sha256:58f812017cd2985c21fbffb4864d59174d4903dd66fa23815e74bbc7a0e2dd57"}, + {file = "kiwisolver-1.5.0-graalpy312-graalpy250_312_native-macosx_10_13_x86_64.whl", hash = "sha256:5ae8e62c147495b01a0f4765c878e9bfdf843412446a247e28df59936e99e797"}, + {file = "kiwisolver-1.5.0-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:f6764a4ccab3078db14a632420930f6186058750df066b8ea2a7106df91d3203"}, + {file = "kiwisolver-1.5.0-graalpy312-graalpy250_312_native-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c31c13da98624f957b0fb1b5bae5383b2333c2c3f6793d9825dd5ce79b525cb7"}, + {file = "kiwisolver-1.5.0-graalpy312-graalpy250_312_native-win_amd64.whl", hash = "sha256:1f1489f769582498610e015a8ef2d36f28f505ab3096d0e16b4858a9ec214f57"}, + {file = "kiwisolver-1.5.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:295d9ffe712caa9f8a3081de8d32fc60191b4b51c76f02f951fd8407253528f4"}, + {file = "kiwisolver-1.5.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:51e8c4084897de9f05898c2c2a39af6318044ae969d46ff7a34ed3f96274adca"}, + {file = "kiwisolver-1.5.0-pp310-pypy310_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b83af57bdddef03c01a9138034c6ff03181a3028d9a1003b301eb1a55e161a3f"}, + {file = "kiwisolver-1.5.0-pp310-pypy310_pp73-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bf4679a3d71012a7c2bf360e5cd878fbd5e4fcac0896b56393dec239d81529ed"}, + {file = "kiwisolver-1.5.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:41024ed50e44ab1a60d3fe0a9d15a4ccc9f5f2b1d814ff283c8d01134d5b81bc"}, + {file = "kiwisolver-1.5.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:ec4c85dc4b687c7f7f15f553ff26a98bfe8c58f5f7f0ac8905f0ba4c7be60232"}, + {file = "kiwisolver-1.5.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:12e91c215a96e39f57989c8912ae761286ac5a9584d04030ceb3368a357f017a"}, + {file = "kiwisolver-1.5.0-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:be4a51a55833dc29ab5d7503e7bcb3b3af3402d266018137127450005cdfe737"}, + {file = "kiwisolver-1.5.0-pp311-pypy311_pp73-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:daae526907e262de627d8f70058a0f64acc9e2641c164c99c8f594b34a799a16"}, + {file = "kiwisolver-1.5.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:59cd8683f575d96df5bb48f6add94afc055012c29e28124fcae2b63661b9efb1"}, + {file = "kiwisolver-1.5.0.tar.gz", hash = "sha256:d4193f3d9dc3f6f79aaed0e5637f45d98850ebf01f7ca20e69457f3e8946b66a"}, +] + +[[package]] +name = "librt" +version = "0.9.0" +description = "Mypyc runtime library" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +markers = "platform_python_implementation != \"PyPy\"" +files = [ + {file = "librt-0.9.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2f8e12706dcb8ff6b3ed57514a19e45c49ad00bcd423e87b2b2e4b5f64578443"}, + {file = "librt-0.9.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4e3dda8345307fd7306db0ed0cb109a63a2c85ba780eb9dc2d09b2049a931f9c"}, + {file = "librt-0.9.0-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:de7dac64e3eb832ffc7b840eb8f52f76420cde1b845be51b2a0f6b870890645e"}, + {file = "librt-0.9.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:22a904cbdb678f7cb348c90d543d3c52f581663d687992fee47fd566dcbf5285"}, + {file = "librt-0.9.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:224b9727eb8bc188bc3bcf29d969dba0cd61b01d9bac80c41575520cc4baabb2"}, + {file = "librt-0.9.0-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e94cbc6ad9a6aeea46d775cbb11f361022f778a9cc8cc90af653d3a594b057ce"}, + {file = "librt-0.9.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:7bc30ad339f4e1a01d4917d645e522a0bc0030644d8973f6346397c93ba1503f"}, + {file = "librt-0.9.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:56d65b583cf43b8cf4c8fbe1e1da20fa3076cc32a1149a141507af1062718236"}, + {file = "librt-0.9.0-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:0a1be03168b2691ba61927e299b352a6315189199ca18a57b733f86cb3cc8d38"}, + {file = "librt-0.9.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:63c12efcd160e1d14da11af0c46c0217473e1e0d2ae1acbccc83f561ea4c2a7b"}, + {file = "librt-0.9.0-cp310-cp310-win32.whl", hash = "sha256:e9002e98dcb1c0a66723592520decd86238ddcef168b37ff6cfb559200b4b774"}, + {file = "librt-0.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:9fcb461fbf70654a52a7cc670e606f04449e2374c199b1825f754e16dacfedd8"}, + {file = "librt-0.9.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:90904fac73c478f4b83f4ed96c99c8208b75e6f9a8a1910548f69a00f1eaa671"}, + {file = "librt-0.9.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:789fff71757facc0738e8d89e3b84e4f0251c1c975e85e81b152cdaca927cc2d"}, + {file = "librt-0.9.0-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:1bf465d1e5b0a27713862441f6467b5ab76385f4ecf8f1f3a44f8aa3c695b4b6"}, + {file = "librt-0.9.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f819e0c6413e259a17a7c0d49f97f405abadd3c2a316a3b46c6440b7dbbedbb1"}, + {file = "librt-0.9.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e0785c2fb4a81e1aece366aa3e2e039f4a4d7d21aaaded5227d7f3c703427882"}, + {file = "librt-0.9.0-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:80b25c7b570a86c03b5da69e665809deb39265476e8e21d96a9328f9762f9990"}, + {file = "librt-0.9.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d4d16b608a1c43d7e33142099a75cd93af482dadce0bf82421e91cad077157f4"}, + {file = "librt-0.9.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:194fc1a32e1e21fe809d38b5faea66cc65eaa00217c8901fbdb99866938adbdb"}, + {file = "librt-0.9.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:8c6bc1384d9738781cfd41d09ad7f6e8af13cfea2c75ece6bd6d2566cdea2076"}, + {file = "librt-0.9.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:15cb151e52a044f06e54ac7f7b47adbfc89b5c8e2b63e1175a9d587c43e8942a"}, + {file = "librt-0.9.0-cp311-cp311-win32.whl", hash = "sha256:f100bfe2acf8a3689af9d0cc660d89f17286c9c795f9f18f7b62dd1a6b247ae6"}, + {file = "librt-0.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:0b73e4266307e51c95e09c0750b7ec383c561d2e97d58e473f6f6a209952fbb8"}, + {file = "librt-0.9.0-cp311-cp311-win_arm64.whl", hash = "sha256:bc5518873822d2faa8ebdd2c1a4d7c8ef47b01a058495ab7924cb65bdbf5fc9a"}, + {file = "librt-0.9.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:9b3e3bc363f71bda1639a4ee593cb78f7fbfeacc73411ec0d4c92f00730010a4"}, + {file = "librt-0.9.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0a09c2f5869649101738653a9b7ab70cf045a1105ac66cbb8f4055e61df78f2d"}, + {file = "librt-0.9.0-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:5ca8e133d799c948db2ab1afc081c333a825b5540475164726dcbf73537e5c2f"}, + {file = "librt-0.9.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:603138ee838ee1583f1b960b62d5d0007845c5c423feb68e44648b1359014e27"}, + {file = "librt-0.9.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f4003f70c56a5addd6aa0897f200dd59afd3bf7bcd5b3cce46dd21f925743bc2"}, + {file = "librt-0.9.0-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:78042f6facfd98ecb25e9829c7e37cce23363d9d7c83bc5f72702c5059eb082b"}, + {file = "librt-0.9.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a361c9434a64d70a7dbb771d1de302c0cc9f13c0bffe1cf7e642152814b35265"}, + {file = "librt-0.9.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:dd2c7e082b0b92e1baa4da28163a808672485617bc855cc22a2fd06978fa9084"}, + {file = "librt-0.9.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:7e6274fd33fc5b2a14d41c9119629d3ff395849d8bcbc80cf637d9e8d2034da8"}, + {file = "librt-0.9.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5093043afb226ecfa1400120d1ebd4442b4f99977783e4f4f7248879009b227f"}, + {file = "librt-0.9.0-cp312-cp312-win32.whl", hash = "sha256:9edcc35d1cae9fd5320171b1a838c7da8a5c968af31e82ecc3dff30b4be0957f"}, + {file = "librt-0.9.0-cp312-cp312-win_amd64.whl", hash = "sha256:3cc2917258e131ae5f958a4d872e07555b51cb7466a43433218061c74ef33745"}, + {file = "librt-0.9.0-cp312-cp312-win_arm64.whl", hash = "sha256:90e6d5420fc8a300518d4d2288154ff45005e920425c22cbbfe8330f3f754bd9"}, + {file = "librt-0.9.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f29b68cd9714531672db62cc54f6e8ff981900f824d13fa0e00749189e13778e"}, + {file = "librt-0.9.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7d5c8a5929ac325729f6119802070b561f4db793dffc45e9ac750992a4ed4d22"}, + {file = "librt-0.9.0-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:756775d25ec8345b837ab52effee3ad2f3b2dfd6bbee3e3f029c517bd5d8f05a"}, + {file = "librt-0.9.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2b8f5d00b49818f4e2b1667db994488b045835e0ac16fe2f924f3871bd2b8ac5"}, + {file = "librt-0.9.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c81aef782380f0f13ead670aae01825eb653b44b046aa0e5ebbb79f76ed4aa11"}, + {file = "librt-0.9.0-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:66b58fed90a545328e80d575467244de3741e088c1af928f0b489ebec3ef3858"}, + {file = "librt-0.9.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e78fb7419e07d98c2af4b8567b72b3eaf8cb05caad642e9963465569c8b2d87e"}, + {file = "librt-0.9.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2c3786f0f4490a5cd87f1ed6cefae833ad6b1060d52044ce0434a2e85893afd0"}, + {file = "librt-0.9.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:8494cfc61e03542f2d381e71804990b3931175a29b9278fdb4a5459948778dc2"}, + {file = "librt-0.9.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:07cf11f769831186eeac424376e6189f20ace4f7263e2134bdb9757340d84d4d"}, + {file = "librt-0.9.0-cp313-cp313-win32.whl", hash = "sha256:850d6d03177e52700af605fd60db7f37dcb89782049a149674d1a9649c2138fd"}, + {file = "librt-0.9.0-cp313-cp313-win_amd64.whl", hash = "sha256:a5af136bfba820d592f86c67affcef9b3ff4d4360ac3255e341e964489b48519"}, + {file = "librt-0.9.0-cp313-cp313-win_arm64.whl", hash = "sha256:4c4d0440a3a8e31d962340c3e1cc3fc9ee7febd34c8d8f770d06adb947779ea5"}, + {file = "librt-0.9.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:3f05d145df35dca5056a8bc3838e940efebd893a54b3e19b2dda39ceaa299bcb"}, + {file = "librt-0.9.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1c587494461ebd42229d0f1739f3aa34237dd9980623ecf1be8d3bcba79f4499"}, + {file = "librt-0.9.0-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:b0a2040f801406b93657a70b72fa12311063a319fee72ce98e1524da7200171f"}, + {file = "librt-0.9.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f38bc489037eca88d6ebefc9c4d41a4e07c8e8b4de5188a9e6d290273ad7ebb1"}, + {file = "librt-0.9.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f3fd278f5e6bf7c75ccd6d12344eb686cc020712683363b66f46ac79d37c799f"}, + {file = "librt-0.9.0-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fcbdf2a9ca24e87bbebb47f1fe34e531ef06f104f98c9ccfc953a3f3344c567a"}, + {file = "librt-0.9.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e306d956cfa027fe041585f02a1602c32bfa6bb8ebea4899d373383295a6c62f"}, + {file = "librt-0.9.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:465814ab157986acb9dfa5ccd7df944be5eefc0d08d31ec6e8d88bc71251d845"}, + {file = "librt-0.9.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:703f4ae36d6240bfe24f542bac784c7e4194ec49c3ba5a994d02891649e2d85b"}, + {file = "librt-0.9.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:3be322a15ee5e70b93b7a59cfd074614f22cc8c9ff18bd27f474e79137ea8d3b"}, + {file = "librt-0.9.0-cp314-cp314-win32.whl", hash = "sha256:b8da9f8035bb417770b1e1610526d87ad4fc58a2804dc4d79c53f6d2cf5a6eb9"}, + {file = "librt-0.9.0-cp314-cp314-win_amd64.whl", hash = "sha256:b8bd70d5d816566a580d193326912f4a76ec2d28a97dc4cd4cc831c0af8e330e"}, + {file = "librt-0.9.0-cp314-cp314-win_arm64.whl", hash = "sha256:fc5758e2b7a56532dc33e3c544d78cbaa9ecf0a0f2a2da2df882c1d6b99a317f"}, + {file = "librt-0.9.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:f24b90b0e0c8cc9491fb1693ae91fe17cb7963153a1946395acdbdd5818429a4"}, + {file = "librt-0.9.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:3fe56e80badb66fdcde06bef81bbaa5bfcf6fbd7aefb86222d9e369c38c6b228"}, + {file = "librt-0.9.0-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:527b5b820b47a09e09829051452bb0d1dd2122261254e2a6f674d12f1d793d54"}, + {file = "librt-0.9.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7d429bdd4ac0ab17c8e4a8af0ed2a7440b16eba474909ab357131018fe8c7e71"}, + {file = "librt-0.9.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7202bdcac47d3a708271c4304a474a8605a4a9a4a709e954bf2d3241140aa938"}, + {file = "librt-0.9.0-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c0d620e74897f8c2613b3c4e2e9c1e422eb46d2ddd07df540784d44117836af3"}, + {file = "librt-0.9.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:d69fc39e627908f4c03297d5a88d9284b73f4d90b424461e32e8c2485e21c283"}, + {file = "librt-0.9.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:c2640e23d2b7c98796f123ffd95cf2022c7777aa8a4a3b98b36c570d37e85eee"}, + {file = "librt-0.9.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:451daa98463b7695b0a30aa56bf637831ea559e7b8101ac2ef6382e8eb15e29c"}, + {file = "librt-0.9.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:928bd06eca2c2bbf4349e5b817f837509b0604342e65a502de1d50a7570afd15"}, + {file = "librt-0.9.0-cp314-cp314t-win32.whl", hash = "sha256:a9c63e04d003bc0fb6a03b348018b9a3002f98268200e22cc80f146beac5dc40"}, + {file = "librt-0.9.0-cp314-cp314t-win_amd64.whl", hash = "sha256:f162af66a2ed3f7d1d161a82ca584efd15acd9c1cff190a373458c32f7d42118"}, + {file = "librt-0.9.0-cp314-cp314t-win_arm64.whl", hash = "sha256:a4b25c6c25cac5d0d9d6d6da855195b254e0021e513e0249f0e3b444dc6e0e61"}, + {file = "librt-0.9.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5112c2fb7c2eefefaeaf5c97fec81343ef44ee86a30dcfaa8223822fba6467b4"}, + {file = "librt-0.9.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a81eea9b999b985e4bacc650c4312805ea7008fd5e45e1bf221310176a7bcb3a"}, + {file = "librt-0.9.0-cp39-cp39-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:eea1b54943475f51698f85fa230c65ccac769f1e603b981be060ac5763d90927"}, + {file = "librt-0.9.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:81107843ed1836874b46b310f9b1816abcb89912af627868522461c3b7333c0f"}, + {file = "librt-0.9.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:aa95738a68cedd3a6f5492feddc513e2e166b50602958139e47bbdd82da0f5a7"}, + {file = "librt-0.9.0-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6788207daa0c19955d2b668f3294a368d19f67d9b5f274553fd073c1260cbb9f"}, + {file = "librt-0.9.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:f48c963a76d71b9d7927eb817b543d0dccd52ab6648b99d37bd54f4cd475d856"}, + {file = "librt-0.9.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:42ff8a962554c350d4a83cf47d9b7b78b0e6ff7943e87df7cdfc97c07f3c016f"}, + {file = "librt-0.9.0-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:657f8ba7b9eaaa82759a104137aed2a3ef7bc46ccfd43e0d89b04005b3e0a4cc"}, + {file = "librt-0.9.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:2d03fa4fd277a7974c1978c92c374c57f44edeee163d147b477b143446ad1bf6"}, + {file = "librt-0.9.0-cp39-cp39-win32.whl", hash = "sha256:d9da80e5b04acce03ced8ba6479a71c2a2edf535c2acc0d09c80d2f80f3bad15"}, + {file = "librt-0.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:54d412e47c21b85865676ed0724e37a89e9593c2eee1e7367adf85bfad56ffb1"}, + {file = "librt-0.9.0.tar.gz", hash = "sha256:a0951822531e7aee6e0dfb556b30d5ee36bbe234faf60c20a16c01be3530869d"}, ] [[package]] @@ -1678,138 +1585,72 @@ files = [ {file = "markupsafe-3.0.3-cp39-cp39-win_arm64.whl", hash = "sha256:38664109c14ffc9e7437e86b4dceb442b0096dfe3541d7864d9cbe1da4cf36c8"}, {file = "markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698"}, ] +markers = {main = "extra == \"docs\""} [[package]] name = "matplotlib" -version = "3.9.4" -description = "Python plotting package" -optional = true -python-versions = ">=3.9" -groups = ["main"] -markers = "python_version < \"3.11\" and extra == \"docs\"" -files = [ - {file = "matplotlib-3.9.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:c5fdd7abfb706dfa8d307af64a87f1a862879ec3cd8d0ec8637458f0885b9c50"}, - {file = "matplotlib-3.9.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d89bc4e85e40a71d1477780366c27fb7c6494d293e1617788986f74e2a03d7ff"}, - {file = "matplotlib-3.9.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ddf9f3c26aae695c5daafbf6b94e4c1a30d6cd617ba594bbbded3b33a1fcfa26"}, - {file = "matplotlib-3.9.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18ebcf248030173b59a868fda1fe42397253f6698995b55e81e1f57431d85e50"}, - {file = "matplotlib-3.9.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:974896ec43c672ec23f3f8c648981e8bc880ee163146e0312a9b8def2fac66f5"}, - {file = "matplotlib-3.9.4-cp310-cp310-win_amd64.whl", hash = "sha256:4598c394ae9711cec135639374e70871fa36b56afae17bdf032a345be552a88d"}, - {file = "matplotlib-3.9.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:d4dd29641d9fb8bc4492420c5480398dd40a09afd73aebe4eb9d0071a05fbe0c"}, - {file = "matplotlib-3.9.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:30e5b22e8bcfb95442bf7d48b0d7f3bdf4a450cbf68986ea45fca3d11ae9d099"}, - {file = "matplotlib-3.9.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2bb0030d1d447fd56dcc23b4c64a26e44e898f0416276cac1ebc25522e0ac249"}, - {file = "matplotlib-3.9.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aca90ed222ac3565d2752b83dbb27627480d27662671e4d39da72e97f657a423"}, - {file = "matplotlib-3.9.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a181b2aa2906c608fcae72f977a4a2d76e385578939891b91c2550c39ecf361e"}, - {file = "matplotlib-3.9.4-cp311-cp311-win_amd64.whl", hash = "sha256:1f6882828231eca17f501c4dcd98a05abb3f03d157fbc0769c6911fe08b6cfd3"}, - {file = "matplotlib-3.9.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:dfc48d67e6661378a21c2983200a654b72b5c5cdbd5d2cf6e5e1ece860f0cc70"}, - {file = "matplotlib-3.9.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:47aef0fab8332d02d68e786eba8113ffd6f862182ea2999379dec9e237b7e483"}, - {file = "matplotlib-3.9.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fba1f52c6b7dc764097f52fd9ab627b90db452c9feb653a59945de16752e965f"}, - {file = "matplotlib-3.9.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:173ac3748acaac21afcc3fa1633924609ba1b87749006bc25051c52c422a5d00"}, - {file = "matplotlib-3.9.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:320edea0cadc07007765e33f878b13b3738ffa9745c5f707705692df70ffe0e0"}, - {file = "matplotlib-3.9.4-cp312-cp312-win_amd64.whl", hash = "sha256:a4a4cfc82330b27042a7169533da7991e8789d180dd5b3daeaee57d75cd5a03b"}, - {file = "matplotlib-3.9.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:37eeffeeca3c940985b80f5b9a7b95ea35671e0e7405001f249848d2b62351b6"}, - {file = "matplotlib-3.9.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3e7465ac859ee4abcb0d836137cd8414e7bb7ad330d905abced457217d4f0f45"}, - {file = "matplotlib-3.9.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f4c12302c34afa0cf061bea23b331e747e5e554b0fa595c96e01c7b75bc3b858"}, - {file = "matplotlib-3.9.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2b8c97917f21b75e72108b97707ba3d48f171541a74aa2a56df7a40626bafc64"}, - {file = "matplotlib-3.9.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0229803bd7e19271b03cb09f27db76c918c467aa4ce2ae168171bc67c3f508df"}, - {file = "matplotlib-3.9.4-cp313-cp313-win_amd64.whl", hash = "sha256:7c0d8ef442ebf56ff5e206f8083d08252ee738e04f3dc88ea882853a05488799"}, - {file = "matplotlib-3.9.4-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:a04c3b00066a688834356d196136349cb32f5e1003c55ac419e91585168b88fb"}, - {file = "matplotlib-3.9.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:04c519587f6c210626741a1e9a68eefc05966ede24205db8982841826af5871a"}, - {file = "matplotlib-3.9.4-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:308afbf1a228b8b525fcd5cec17f246bbbb63b175a3ef6eb7b4d33287ca0cf0c"}, - {file = "matplotlib-3.9.4-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ddb3b02246ddcffd3ce98e88fed5b238bc5faff10dbbaa42090ea13241d15764"}, - {file = "matplotlib-3.9.4-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8a75287e9cb9eee48cb79ec1d806f75b29c0fde978cb7223a1f4c5848d696041"}, - {file = "matplotlib-3.9.4-cp313-cp313t-win_amd64.whl", hash = "sha256:488deb7af140f0ba86da003e66e10d55ff915e152c78b4b66d231638400b1965"}, - {file = "matplotlib-3.9.4-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:3c3724d89a387ddf78ff88d2a30ca78ac2b4c89cf37f2db4bd453c34799e933c"}, - {file = "matplotlib-3.9.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d5f0a8430ffe23d7e32cfd86445864ccad141797f7d25b7c41759a5b5d17cfd7"}, - {file = "matplotlib-3.9.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6bb0141a21aef3b64b633dc4d16cbd5fc538b727e4958be82a0e1c92a234160e"}, - {file = "matplotlib-3.9.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:57aa235109e9eed52e2c2949db17da185383fa71083c00c6c143a60e07e0888c"}, - {file = "matplotlib-3.9.4-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:b18c600061477ccfdd1e6fd050c33d8be82431700f3452b297a56d9ed7037abb"}, - {file = "matplotlib-3.9.4-cp39-cp39-win_amd64.whl", hash = "sha256:ef5f2d1b67d2d2145ff75e10f8c008bfbf71d45137c4b648c87193e7dd053eac"}, - {file = "matplotlib-3.9.4-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:44e0ed786d769d85bc787b0606a53f2d8d2d1d3c8a2608237365e9121c1a338c"}, - {file = "matplotlib-3.9.4-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:09debb9ce941eb23ecdbe7eab972b1c3e0276dcf01688073faff7b0f61d6c6ca"}, - {file = "matplotlib-3.9.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bcc53cf157a657bfd03afab14774d54ba73aa84d42cfe2480c91bd94873952db"}, - {file = "matplotlib-3.9.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:ad45da51be7ad02387801fd154ef74d942f49fe3fcd26a64c94842ba7ec0d865"}, - {file = "matplotlib-3.9.4.tar.gz", hash = "sha256:1e00e8be7393cbdc6fedfa8a6fba02cf3e83814b285db1c60b906a023ba41bc3"}, -] - -[package.dependencies] -contourpy = ">=1.0.1" -cycler = ">=0.10" -fonttools = ">=4.22.0" -importlib-resources = {version = ">=3.2.0", markers = "python_version < \"3.10\""} -kiwisolver = ">=1.3.1" -numpy = ">=1.23" -packaging = ">=20.0" -pillow = ">=8" -pyparsing = ">=2.3.1" -python-dateutil = ">=2.7" - -[package.extras] -dev = ["meson-python (>=0.13.1,<0.17.0)", "numpy (>=1.25)", "pybind11 (>=2.6,!=2.13.3)", "setuptools (>=64)", "setuptools_scm (>=7)"] - -[[package]] -name = "matplotlib" -version = "3.10.6" +version = "3.10.9" description = "Python plotting package" optional = true python-versions = ">=3.10" groups = ["main"] -markers = "python_version >= \"3.11\" and extra == \"docs\"" -files = [ - {file = "matplotlib-3.10.6-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:bc7316c306d97463a9866b89d5cc217824e799fa0de346c8f68f4f3d27c8693d"}, - {file = "matplotlib-3.10.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d00932b0d160ef03f59f9c0e16d1e3ac89646f7785165ce6ad40c842db16cc2e"}, - {file = "matplotlib-3.10.6-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8fa4c43d6bfdbfec09c733bca8667de11bfa4970e8324c471f3a3632a0301c15"}, - {file = "matplotlib-3.10.6-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ea117a9c1627acaa04dbf36265691921b999cbf515a015298e54e1a12c3af837"}, - {file = "matplotlib-3.10.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:08fc803293b4e1694ee325896030de97f74c141ccff0be886bb5915269247676"}, - {file = "matplotlib-3.10.6-cp310-cp310-win_amd64.whl", hash = "sha256:2adf92d9b7527fbfb8818e050260f0ebaa460f79d61546374ce73506c9421d09"}, - {file = "matplotlib-3.10.6-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:905b60d1cb0ee604ce65b297b61cf8be9f4e6cfecf95a3fe1c388b5266bc8f4f"}, - {file = "matplotlib-3.10.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7bac38d816637343e53d7185d0c66677ff30ffb131044a81898b5792c956ba76"}, - {file = "matplotlib-3.10.6-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:942a8de2b5bfff1de31d95722f702e2966b8a7e31f4e68f7cd963c7cd8861cf6"}, - {file = "matplotlib-3.10.6-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a3276c85370bc0dfca051ec65c5817d1e0f8f5ce1b7787528ec8ed2d524bbc2f"}, - {file = "matplotlib-3.10.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9df5851b219225731f564e4b9e7f2ac1e13c9e6481f941b5631a0f8e2d9387ce"}, - {file = "matplotlib-3.10.6-cp311-cp311-win_amd64.whl", hash = "sha256:abb5d9478625dd9c9eb51a06d39aae71eda749ae9b3138afb23eb38824026c7e"}, - {file = "matplotlib-3.10.6-cp311-cp311-win_arm64.whl", hash = "sha256:886f989ccfae63659183173bb3fced7fd65e9eb793c3cc21c273add368536951"}, - {file = "matplotlib-3.10.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:31ca662df6a80bd426f871105fdd69db7543e28e73a9f2afe80de7e531eb2347"}, - {file = "matplotlib-3.10.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1678bb61d897bb4ac4757b5ecfb02bfb3fddf7f808000fb81e09c510712fda75"}, - {file = "matplotlib-3.10.6-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:56cd2d20842f58c03d2d6e6c1f1cf5548ad6f66b91e1e48f814e4fb5abd1cb95"}, - {file = "matplotlib-3.10.6-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:662df55604a2f9a45435566d6e2660e41efe83cd94f4288dfbf1e6d1eae4b0bb"}, - {file = "matplotlib-3.10.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:08f141d55148cd1fc870c3387d70ca4df16dee10e909b3b038782bd4bda6ea07"}, - {file = "matplotlib-3.10.6-cp312-cp312-win_amd64.whl", hash = "sha256:590f5925c2d650b5c9d813c5b3b5fc53f2929c3f8ef463e4ecfa7e052044fb2b"}, - {file = "matplotlib-3.10.6-cp312-cp312-win_arm64.whl", hash = "sha256:f44c8d264a71609c79a78d50349e724f5d5fc3684ead7c2a473665ee63d868aa"}, - {file = "matplotlib-3.10.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:819e409653c1106c8deaf62e6de6b8611449c2cd9939acb0d7d4e57a3d95cc7a"}, - {file = "matplotlib-3.10.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:59c8ac8382fefb9cb71308dde16a7c487432f5255d8f1fd32473523abecfecdf"}, - {file = "matplotlib-3.10.6-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:84e82d9e0fd70c70bc55739defbd8055c54300750cbacf4740c9673a24d6933a"}, - {file = "matplotlib-3.10.6-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:25f7a3eb42d6c1c56e89eacd495661fc815ffc08d9da750bca766771c0fd9110"}, - {file = "matplotlib-3.10.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f9c862d91ec0b7842920a4cfdaaec29662195301914ea54c33e01f1a28d014b2"}, - {file = "matplotlib-3.10.6-cp313-cp313-win_amd64.whl", hash = "sha256:1b53bd6337eba483e2e7d29c5ab10eee644bc3a2491ec67cc55f7b44583ffb18"}, - {file = "matplotlib-3.10.6-cp313-cp313-win_arm64.whl", hash = "sha256:cbd5eb50b7058b2892ce45c2f4e92557f395c9991f5c886d1bb74a1582e70fd6"}, - {file = "matplotlib-3.10.6-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:acc86dd6e0e695c095001a7fccff158c49e45e0758fdf5dcdbb0103318b59c9f"}, - {file = "matplotlib-3.10.6-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e228cd2ffb8f88b7d0b29e37f68ca9aaf83e33821f24a5ccc4f082dd8396bc27"}, - {file = "matplotlib-3.10.6-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:658bc91894adeab669cf4bb4a186d049948262987e80f0857216387d7435d833"}, - {file = "matplotlib-3.10.6-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8913b7474f6dd83ac444c9459c91f7f0f2859e839f41d642691b104e0af056aa"}, - {file = "matplotlib-3.10.6-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:091cea22e059b89f6d7d1a18e2c33a7376c26eee60e401d92a4d6726c4e12706"}, - {file = "matplotlib-3.10.6-cp313-cp313t-win_amd64.whl", hash = "sha256:491e25e02a23d7207629d942c666924a6b61e007a48177fdd231a0097b7f507e"}, - {file = "matplotlib-3.10.6-cp313-cp313t-win_arm64.whl", hash = "sha256:3d80d60d4e54cda462e2cd9a086d85cd9f20943ead92f575ce86885a43a565d5"}, - {file = "matplotlib-3.10.6-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:70aaf890ce1d0efd482df969b28a5b30ea0b891224bb315810a3940f67182899"}, - {file = "matplotlib-3.10.6-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1565aae810ab79cb72e402b22facfa6501365e73ebab70a0fdfb98488d2c3c0c"}, - {file = "matplotlib-3.10.6-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f3b23315a01981689aa4e1a179dbf6ef9fbd17143c3eea77548c2ecfb0499438"}, - {file = "matplotlib-3.10.6-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:30fdd37edf41a4e6785f9b37969de57aea770696cb637d9946eb37470c94a453"}, - {file = "matplotlib-3.10.6-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:bc31e693da1c08012c764b053e702c1855378e04102238e6a5ee6a7117c53a47"}, - {file = "matplotlib-3.10.6-cp314-cp314-win_amd64.whl", hash = "sha256:05be9bdaa8b242bc6ff96330d18c52f1fc59c6fb3a4dd411d953d67e7e1baf98"}, - {file = "matplotlib-3.10.6-cp314-cp314-win_arm64.whl", hash = "sha256:f56a0d1ab05d34c628592435781d185cd99630bdfd76822cd686fb5a0aecd43a"}, - {file = "matplotlib-3.10.6-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:94f0b4cacb23763b64b5dace50d5b7bfe98710fed5f0cef5c08135a03399d98b"}, - {file = "matplotlib-3.10.6-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:cc332891306b9fb39462673d8225d1b824c89783fee82840a709f96714f17a5c"}, - {file = "matplotlib-3.10.6-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee1d607b3fb1590deb04b69f02ea1d53ed0b0bf75b2b1a5745f269afcbd3cdd3"}, - {file = "matplotlib-3.10.6-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:376a624a218116461696b27b2bbf7a8945053e6d799f6502fc03226d077807bf"}, - {file = "matplotlib-3.10.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:83847b47f6524c34b4f2d3ce726bb0541c48c8e7692729865c3df75bfa0f495a"}, - {file = "matplotlib-3.10.6-cp314-cp314t-win_amd64.whl", hash = "sha256:c7e0518e0d223683532a07f4b512e2e0729b62674f1b3a1a69869f98e6b1c7e3"}, - {file = "matplotlib-3.10.6-cp314-cp314t-win_arm64.whl", hash = "sha256:4dd83e029f5b4801eeb87c64efd80e732452781c16a9cf7415b7b63ec8f374d7"}, - {file = "matplotlib-3.10.6-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:13fcd07ccf17e354398358e0307a1f53f5325dca22982556ddb9c52837b5af41"}, - {file = "matplotlib-3.10.6-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:470fc846d59d1406e34fa4c32ba371039cd12c2fe86801159a965956f2575bd1"}, - {file = "matplotlib-3.10.6-pp310-pypy310_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f7173f8551b88f4ef810a94adae3128c2530e0d07529f7141be7f8d8c365f051"}, - {file = "matplotlib-3.10.6-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:f2d684c3204fa62421bbf770ddfebc6b50130f9cad65531eeba19236d73bb488"}, - {file = "matplotlib-3.10.6-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:6f4a69196e663a41d12a728fab8751177215357906436804217d6d9cf0d4d6cf"}, - {file = "matplotlib-3.10.6-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4d6ca6ef03dfd269f4ead566ec6f3fb9becf8dab146fb999022ed85ee9f6b3eb"}, - {file = "matplotlib-3.10.6.tar.gz", hash = "sha256:ec01b645840dd1996df21ee37f208cd8ba57644779fa20464010638013d3203c"}, +markers = "extra == \"docs\"" +files = [ + {file = "matplotlib-3.10.9-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:77210dce9cb8153dffc967efaae990543392563d5a376d4dd8539bebcb0ed217"}, + {file = "matplotlib-3.10.9-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1e7698ac9868428e84d2c967424803b2472ff7167d9d6590d4204ed775343c3b"}, + {file = "matplotlib-3.10.9-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1aa972116abb4c9d201bf245620b433726cb6856f3bef6a78f776a00f5c92d37"}, + {file = "matplotlib-3.10.9-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ae2f11957b27ce53497dd4d7b235c4d4f1faf383dfb39d0c5beb833bff883294"}, + {file = "matplotlib-3.10.9-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b049278ddce116aaa1c1377ebf58adea909132dfce0281cf7e3a1ea9fc2e2c65"}, + {file = "matplotlib-3.10.9-cp310-cp310-win_amd64.whl", hash = "sha256:82834c3c292d24d3a8aae77cd2d20019de69d692a34a970e4fdb8d33e2ea3dda"}, + {file = "matplotlib-3.10.9-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:68cfdcede415f7c8f5577b03303dd94526cdb6d11036cecdc205e08733b2d2bb"}, + {file = "matplotlib-3.10.9-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dfca0129678bd56379db26c52b5d77ed7de314c047492fbdc763aa7501710cfb"}, + {file = "matplotlib-3.10.9-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8e436d155fa8a3399dc62683f8f5d0e2e50d25d0144a73edd73f82eec8f4abfb"}, + {file = "matplotlib-3.10.9-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:56fc0bd271b00025c6edfdc7c2dcd247372c8e1544971d62e1dc7c17367e8bf9"}, + {file = "matplotlib-3.10.9-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a5a6104ed666402ba5106d7f36e0e0cdca4e8d7fa4d39708ca88019e2835a2eb"}, + {file = "matplotlib-3.10.9-cp311-cp311-win_amd64.whl", hash = "sha256:d730e984eddf56974c3e72b6129c7ca462ac38dc624338f4b0b23eb23ecba00f"}, + {file = "matplotlib-3.10.9-cp311-cp311-win_arm64.whl", hash = "sha256:51bf0ddbdc598e060d46c16b5590708f81a1624cefbaaf62f6a81bf9285b8c80"}, + {file = "matplotlib-3.10.9-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f0c3c28d9fbcc1fe7a03be236d73430cf6409c41fb2383a7ac52fe932b072cb1"}, + {file = "matplotlib-3.10.9-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:41cb28c2bd769aa3e98322c6ab09854cbcc52ab69d2759d681bba3e327b2b320"}, + {file = "matplotlib-3.10.9-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ae20801130378b82d647ff5047c07316295b68dc054ca6b3c13519d0ea624285"}, + {file = "matplotlib-3.10.9-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6c63ebcd8b4b169eb2f5c200552ae6b8be8999a005b6b507ed76fb8d7d674fe2"}, + {file = "matplotlib-3.10.9-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d75d11c949914165976c621b2324f9ef162af7ebf4b057ddf95dd1dba7e5edcf"}, + {file = "matplotlib-3.10.9-cp312-cp312-win_amd64.whl", hash = "sha256:d091f9d758b34aaaaa6331d13574bf01891d903b3dec59bfff458ef7551de5d6"}, + {file = "matplotlib-3.10.9-cp312-cp312-win_arm64.whl", hash = "sha256:10cc5ce06d10231c36f40e875f3c7e8050362a4ee8f0ee5d29a6b3277d57bb42"}, + {file = "matplotlib-3.10.9-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b580440f1ff81a0e34122051a3dfabb7e4b7f9e380629929bde0eff9af72165f"}, + {file = "matplotlib-3.10.9-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:b1b745c489cd1a77a0dc1120a05dc87af9798faebc913601feb8c73d89bf2d1e"}, + {file = "matplotlib-3.10.9-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8f3bcac1ca5ed000a6f4337d47ba67dfddf37ed6a46c15fd7f014997f7bf865f"}, + {file = "matplotlib-3.10.9-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7a8d66a55def891c33147ba3ba9bfcabf0b526a43764c818acbb4525e5ed0838"}, + {file = "matplotlib-3.10.9-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d843374407c4017a6403b59c6c81606773d136f3259d5b6da3131bc814542cc2"}, + {file = "matplotlib-3.10.9-cp313-cp313-win_amd64.whl", hash = "sha256:f4399f64b3e94cd500195490972ae1ee81170df1636fa15364d157d5bdd7b921"}, + {file = "matplotlib-3.10.9-cp313-cp313-win_arm64.whl", hash = "sha256:ba7b3b8ef09eab7df0e86e9ae086faa433efbfbdb46afcb3aa16aabf779469a8"}, + {file = "matplotlib-3.10.9-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:09218df8a93712bd6ea133e83a153c755448cf7868316c531cffcc43f69d1cc9"}, + {file = "matplotlib-3.10.9-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:82368699727bfb7b0182e1aa13082e3c08e092fa1a25d3e1fd92405bff96f6d4"}, + {file = "matplotlib-3.10.9-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3225f4e1edcb8c86c884ddf79ebe20ecd0a67d30188f279897554ccd8fded4dc"}, + {file = "matplotlib-3.10.9-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:de2445a0c6690d21b7eb6ce071cebad6d40a2e9bdf10d039074a96ba19797b99"}, + {file = "matplotlib-3.10.9-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:b2b9516251cb89ff618d757daec0e2ed1bf21248013844a853d87ef85ab3081d"}, + {file = "matplotlib-3.10.9-cp313-cp313t-win_amd64.whl", hash = "sha256:e9fae004b941b23ff2edcf1567a857ed77bafc8086ffa258190462328434faf8"}, + {file = "matplotlib-3.10.9-cp313-cp313t-win_arm64.whl", hash = "sha256:6b63d9c7c769b88ab81e10dc86e4e0607cf56817b9f9e6cf24b2a5f1693b8e38"}, + {file = "matplotlib-3.10.9-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:172db52c9e683f5d12eaf57f0f54834190e12581fe1cc2a19595a8f5acb4e77d"}, + {file = "matplotlib-3.10.9-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:97e35e8d39ccc85859095e01a53847432ba9a53ddf7986f7a54a11b73d0e143f"}, + {file = "matplotlib-3.10.9-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:aba1615dabe83188e19d4f75a253c6a08423e04c1425e64039f800050a69de6b"}, + {file = "matplotlib-3.10.9-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:34cf8167e023ad956c15f36302911d5406bd99a9862c1a8499ea6f7c0e015dc2"}, + {file = "matplotlib-3.10.9-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:59476c6d29d612b8e9bb6ce8c5b631be6ba8f9e3a2421f22a02b192c7dd28716"}, + {file = "matplotlib-3.10.9-cp314-cp314-win_amd64.whl", hash = "sha256:336b9acc64d309063126edcdaca00db9373af3c476bb94388fe9c5a53ad13e6f"}, + {file = "matplotlib-3.10.9-cp314-cp314-win_arm64.whl", hash = "sha256:2dc9477819ffd78ad12a20df1d9d6a6bd4fec6aaa9072681465fddca052f1456"}, + {file = "matplotlib-3.10.9-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:da4e09638420548f31c354032a6250e473c68e5a4e96899b4844cf39ddea23fe"}, + {file = "matplotlib-3.10.9-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:345f6f68ecc8da0ca56fad2ea08fde1a115eda530079eca185d50a7bc3e146c6"}, + {file = "matplotlib-3.10.9-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4edcfbd8565339aa62f1cd4012f7180926fdbe71850f7b0d3c379c175cd6b66c"}, + {file = "matplotlib-3.10.9-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6be157fe17fc37cb95ac1d7374cf717ce9259616edec911a78d9d26dae8522d4"}, + {file = "matplotlib-3.10.9-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:4e42042d54db34fda4e95a7bd3e5789c2a995d2dad3eb8850232ee534092fbbf"}, + {file = "matplotlib-3.10.9-cp314-cp314t-win_amd64.whl", hash = "sha256:c27df8b3848f32a83d1767566595e43cfaa4460380974da06f4279a7ec143c39"}, + {file = "matplotlib-3.10.9-cp314-cp314t-win_arm64.whl", hash = "sha256:a49f1eadc84ca85fd72fa4e89e70e61bf86452df6f971af04b12c60761a0772c"}, + {file = "matplotlib-3.10.9-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:1872fb212a05b729e649754a72d5da61d03e0554d76e80303b6f83d1d2c0552b"}, + {file = "matplotlib-3.10.9-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:985f2238880e2e69093f588f5fe2e46771747febf0649f3cf7f7b7480875317f"}, + {file = "matplotlib-3.10.9-pp310-pypy310_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:6640f75af2c6148293caa0a2b39dd806a492dd66c8a8b04035813e33d0fd2585"}, + {file = "matplotlib-3.10.9-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:42fb814efabe95c06c1994d8ab5a8385f43a249e23badd3ba931d4308e5bca20"}, + {file = "matplotlib-3.10.9-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:f76e640a5268850bfda54b5131b1b1941cc685e42c5fa98ed9f2d64038308cba"}, + {file = "matplotlib-3.10.9-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3fc0364dfbe1d07f6d15c5ebd0c5bf89e126916e5a8667dd4a7a6e84c36653d4"}, + {file = "matplotlib-3.10.9.tar.gz", hash = "sha256:fd66508e8c6877d98e586654b608a0456db8d7e8a546eb1e2600efd957302358"}, ] [package.dependencies] @@ -1820,11 +1661,11 @@ kiwisolver = ">=1.3.1" numpy = ">=1.23" packaging = ">=20.0" pillow = ">=8" -pyparsing = ">=2.3.1" +pyparsing = ">=3" python-dateutil = ">=2.7" [package.extras] -dev = ["meson-python (>=0.13.1,<0.17.0)", "pybind11 (>=2.13.2,!=2.13.3)", "setuptools (>=64)", "setuptools_scm (>=7)"] +dev = ["meson-python (>=0.13.1,<0.17.0)", "pybind11 (>=2.13.2,!=2.13.3)", "setuptools (>=64)", "setuptools_scm (>=7,<10)"] [[package]] name = "matplotlib-inline" @@ -1833,6 +1674,7 @@ description = "Inline Matplotlib backend for Jupyter" optional = true python-versions = ">=3.8" groups = ["main"] +markers = "extra == \"latex\" or extra == \"docs\"" files = [ {file = "matplotlib_inline-0.1.7-py3-none-any.whl", hash = "sha256:df192d39a4ff8f21b1895d72e6a13f5fcc5099f00fa84384e0ea28c2cc0653ca"}, {file = "matplotlib_inline-0.1.7.tar.gz", hash = "sha256:8423b23ec666be3d16e16b60bdd8ac4e86e840ebd1dd11a30b9f117f2fa0ab90"}, @@ -1841,27 +1683,6 @@ files = [ [package.dependencies] traitlets = "*" -[[package]] -name = "mdit-py-plugins" -version = "0.4.2" -description = "Collection of plugins for markdown-it-py" -optional = true -python-versions = ">=3.8" -groups = ["main"] -markers = "python_version < \"3.11\" and extra == \"docs\"" -files = [ - {file = "mdit_py_plugins-0.4.2-py3-none-any.whl", hash = "sha256:0c673c3f889399a33b95e88d2f0d111b4447bdfea7f237dab2d488f459835636"}, - {file = "mdit_py_plugins-0.4.2.tar.gz", hash = "sha256:5f2cd1fdb606ddf152d37ec30e46101a60512bc0e5fa1a7002c36647b09e26b5"}, -] - -[package.dependencies] -markdown-it-py = ">=1.0.0,<4.0.0" - -[package.extras] -code-style = ["pre-commit"] -rtd = ["myst-parser", "sphinx-book-theme"] -testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] - [[package]] name = "mdit-py-plugins" version = "0.5.0" @@ -1869,7 +1690,7 @@ description = "Collection of plugins for markdown-it-py" optional = true python-versions = ">=3.10" groups = ["main"] -markers = "python_version >= \"3.11\" and extra == \"docs\"" +markers = "extra == \"docs\"" files = [ {file = "mdit_py_plugins-0.5.0-py3-none-any.whl", hash = "sha256:07a08422fc1936a5d26d146759e9155ea466e842f5ab2f7d2266dd084c8dab1f"}, {file = "mdit_py_plugins-0.5.0.tar.gz", hash = "sha256:f4918cb50119f50446560513a8e311d574ff6aaed72606ddae6d35716fe809c6"}, @@ -1898,151 +1719,217 @@ files = [ [[package]] name = "mistune" -version = "3.1.4" +version = "3.3.2" description = "A sane and fast Markdown parser with useful plugins and renderers" optional = true python-versions = ">=3.8" groups = ["main"] markers = "extra == \"docs\"" files = [ - {file = "mistune-3.1.4-py3-none-any.whl", hash = "sha256:93691da911e5d9d2e23bc54472892aff676df27a75274962ff9edc210364266d"}, - {file = "mistune-3.1.4.tar.gz", hash = "sha256:b5a7f801d389f724ec702840c11d8fc48f2b33519102fc7ee739e8177b672164"}, + {file = "mistune-3.3.2-py3-none-any.whl", hash = "sha256:a678a56387d487db7368ede4647cb2ba1deff22ce61f92343e4ebe0ddfce4f2d"}, + {file = "mistune-3.3.2.tar.gz", hash = "sha256:e12ee4f1e74336e91aa1141e35f913b337c40bdf7c0cc49f21fb853a27e8b62f"}, +] + +[[package]] +name = "ml-dtypes" +version = "0.5.4" +description = "ml_dtypes is a stand-alone implementation of several NumPy dtype extensions used in machine learning." +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "ml_dtypes-0.5.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b95e97e470fe60ed493fd9ae3911d8da4ebac16bd21f87ffa2b7c588bf22ea2c"}, + {file = "ml_dtypes-0.5.4-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b4b801ebe0b477be666696bda493a9be8356f1f0057a57f1e35cd26928823e5a"}, + {file = "ml_dtypes-0.5.4-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:388d399a2152dd79a3f0456a952284a99ee5c93d3e2f8dfe25977511e0515270"}, + {file = "ml_dtypes-0.5.4-cp310-cp310-win_amd64.whl", hash = "sha256:4ff7f3e7ca2972e7de850e7b8fcbb355304271e2933dd90814c1cb847414d6e2"}, + {file = "ml_dtypes-0.5.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6c7ecb74c4bd71db68a6bea1edf8da8c34f3d9fe218f038814fd1d310ac76c90"}, + {file = "ml_dtypes-0.5.4-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bc11d7e8c44a65115d05e2ab9989d1e045125d7be8e05a071a48bc76eb6d6040"}, + {file = "ml_dtypes-0.5.4-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:19b9a53598f21e453ea2fbda8aa783c20faff8e1eeb0d7ab899309a0053f1483"}, + {file = "ml_dtypes-0.5.4-cp311-cp311-win_amd64.whl", hash = "sha256:7c23c54a00ae43edf48d44066a7ec31e05fdc2eee0be2b8b50dd1903a1db94bb"}, + {file = "ml_dtypes-0.5.4-cp311-cp311-win_arm64.whl", hash = "sha256:557a31a390b7e9439056644cb80ed0735a6e3e3bb09d67fd5687e4b04238d1de"}, + {file = "ml_dtypes-0.5.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:a174837a64f5b16cab6f368171a1a03a27936b31699d167684073ff1c4237dac"}, + {file = "ml_dtypes-0.5.4-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a7f7c643e8b1320fd958bf098aa7ecf70623a42ec5154e3be3be673f4c34d900"}, + {file = "ml_dtypes-0.5.4-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9ad459e99793fa6e13bd5b7e6792c8f9190b4e5a1b45c63aba14a4d0a7f1d5ff"}, + {file = "ml_dtypes-0.5.4-cp312-cp312-win_amd64.whl", hash = "sha256:c1a953995cccb9e25a4ae19e34316671e4e2edaebe4cf538229b1fc7109087b7"}, + {file = "ml_dtypes-0.5.4-cp312-cp312-win_arm64.whl", hash = "sha256:9bad06436568442575beb2d03389aa7456c690a5b05892c471215bfd8cf39460"}, + {file = "ml_dtypes-0.5.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8c760d85a2f82e2bed75867079188c9d18dae2ee77c25a54d60e9cc79be1bc48"}, + {file = "ml_dtypes-0.5.4-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ce756d3a10d0c4067172804c9cc276ba9cc0ff47af9078ad439b075d1abdc29b"}, + {file = "ml_dtypes-0.5.4-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:533ce891ba774eabf607172254f2e7260ba5f57bdd64030c9a4fcfbd99815d0d"}, + {file = "ml_dtypes-0.5.4-cp313-cp313-win_amd64.whl", hash = "sha256:f21c9219ef48ca5ee78402d5cc831bd58ea27ce89beda894428bc67a52da5328"}, + {file = "ml_dtypes-0.5.4-cp313-cp313-win_arm64.whl", hash = "sha256:35f29491a3e478407f7047b8a4834e4640a77d2737e0b294d049746507af5175"}, + {file = "ml_dtypes-0.5.4-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:304ad47faa395415b9ccbcc06a0350800bc50eda70f0e45326796e27c62f18b6"}, + {file = "ml_dtypes-0.5.4-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6a0df4223b514d799b8a1629c65ddc351b3efa833ccf7f8ea0cf654a61d1e35d"}, + {file = "ml_dtypes-0.5.4-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:531eff30e4d368cb6255bc2328d070e35836aa4f282a0fb5f3a0cd7260257298"}, + {file = "ml_dtypes-0.5.4-cp313-cp313t-win_amd64.whl", hash = "sha256:cb73dccfc991691c444acc8c0012bee8f2470da826a92e3a20bb333b1a7894e6"}, + {file = "ml_dtypes-0.5.4-cp313-cp313t-win_arm64.whl", hash = "sha256:3bbbe120b915090d9dd1375e4684dd17a20a2491ef25d640a908281da85e73f1"}, + {file = "ml_dtypes-0.5.4-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:2b857d3af6ac0d39db1de7c706e69c7f9791627209c3d6dedbfca8c7e5faec22"}, + {file = "ml_dtypes-0.5.4-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:805cef3a38f4eafae3a5bf9ebdcdb741d0bcfd9e1bd90eb54abd24f928cd2465"}, + {file = "ml_dtypes-0.5.4-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:14a4fd3228af936461db66faccef6e4f41c1d82fcc30e9f8d58a08916b1d811f"}, + {file = "ml_dtypes-0.5.4-cp314-cp314-win_amd64.whl", hash = "sha256:8c6a2dcebd6f3903e05d51960a8058d6e131fe69f952a5397e5dbabc841b6d56"}, + {file = "ml_dtypes-0.5.4-cp314-cp314-win_arm64.whl", hash = "sha256:5a0f68ca8fd8d16583dfa7793973feb86f2fbb56ce3966daf9c9f748f52a2049"}, + {file = "ml_dtypes-0.5.4-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:bfc534409c5d4b0bf945af29e5d0ab075eae9eecbb549ff8a29280db822f34f9"}, + {file = "ml_dtypes-0.5.4-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2314892cdc3fcf05e373d76d72aaa15fda9fb98625effa73c1d646f331fcecb7"}, + {file = "ml_dtypes-0.5.4-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0d2ffd05a2575b1519dc928c0b93c06339eb67173ff53acb00724502cda231cf"}, + {file = "ml_dtypes-0.5.4-cp314-cp314t-win_amd64.whl", hash = "sha256:4381fe2f2452a2d7589689693d3162e876b3ddb0a832cde7a414f8e1adf7eab1"}, + {file = "ml_dtypes-0.5.4-cp314-cp314t-win_arm64.whl", hash = "sha256:11942cbf2cf92157db91e5022633c0d9474d4dfd813a909383bd23ce828a4b7d"}, + {file = "ml_dtypes-0.5.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:d81fdb088defa30eb37bf390bb7dde35d3a83ec112ac8e33d75ab28cc29dd8b0"}, + {file = "ml_dtypes-0.5.4-cp39-cp39-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:88c982aac7cb1cbe8cbb4e7f253072b1df872701fcaf48d84ffbb433b6568f24"}, + {file = "ml_dtypes-0.5.4-cp39-cp39-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a9b61c19040397970d18d7737375cffd83b1f36a11dd4ad19f83a016f736c3ef"}, + {file = "ml_dtypes-0.5.4-cp39-cp39-win_amd64.whl", hash = "sha256:3d277bf3637f2a62176f4575512e9ff9ef51d00e39626d9fe4a161992f355af2"}, + {file = "ml_dtypes-0.5.4.tar.gz", hash = "sha256:8ab06a50fb9bf9666dd0fe5dfb4676fa2b0ac0f31ecff72a6c3af8e22c063453"}, ] [package.dependencies] -typing-extensions = {version = "*", markers = "python_version < \"3.11\""} +numpy = [ + {version = ">=1.23.3", markers = "python_version == \"3.11\""}, + {version = ">=1.26.0", markers = "python_version >= \"3.12\""}, +] + +[package.extras] +dev = ["absl-py", "pyink", "pylint (>=2.6.0)", "pytest", "pytest-xdist"] [[package]] name = "msgpack" -version = "1.1.2" +version = "1.2.1" description = "MessagePack serializer" optional = false -python-versions = ">=3.9" +python-versions = ">=3.10" groups = ["main"] files = [ - {file = "msgpack-1.1.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0051fffef5a37ca2cd16978ae4f0aef92f164df86823871b5162812bebecd8e2"}, - {file = "msgpack-1.1.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a605409040f2da88676e9c9e5853b3449ba8011973616189ea5ee55ddbc5bc87"}, - {file = "msgpack-1.1.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8b696e83c9f1532b4af884045ba7f3aa741a63b2bc22617293a2c6a7c645f251"}, - {file = "msgpack-1.1.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:365c0bbe981a27d8932da71af63ef86acc59ed5c01ad929e09a0b88c6294e28a"}, - {file = "msgpack-1.1.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:41d1a5d875680166d3ac5c38573896453bbbea7092936d2e107214daf43b1d4f"}, - {file = "msgpack-1.1.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:354e81bcdebaab427c3df4281187edc765d5d76bfb3a7c125af9da7a27e8458f"}, - {file = "msgpack-1.1.2-cp310-cp310-win32.whl", hash = "sha256:e64c8d2f5e5d5fda7b842f55dec6133260ea8f53c4257d64494c534f306bf7a9"}, - {file = "msgpack-1.1.2-cp310-cp310-win_amd64.whl", hash = "sha256:db6192777d943bdaaafb6ba66d44bf65aa0e9c5616fa1d2da9bb08828c6b39aa"}, - {file = "msgpack-1.1.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2e86a607e558d22985d856948c12a3fa7b42efad264dca8a3ebbcfa2735d786c"}, - {file = "msgpack-1.1.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:283ae72fc89da59aa004ba147e8fc2f766647b1251500182fac0350d8af299c0"}, - {file = "msgpack-1.1.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:61c8aa3bd513d87c72ed0b37b53dd5c5a0f58f2ff9f26e1555d3bd7948fb7296"}, - {file = "msgpack-1.1.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:454e29e186285d2ebe65be34629fa0e8605202c60fbc7c4c650ccd41870896ef"}, - {file = "msgpack-1.1.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7bc8813f88417599564fafa59fd6f95be417179f76b40325b500b3c98409757c"}, - {file = "msgpack-1.1.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bafca952dc13907bdfdedfc6a5f579bf4f292bdd506fadb38389afa3ac5b208e"}, - {file = "msgpack-1.1.2-cp311-cp311-win32.whl", hash = "sha256:602b6740e95ffc55bfb078172d279de3773d7b7db1f703b2f1323566b878b90e"}, - {file = "msgpack-1.1.2-cp311-cp311-win_amd64.whl", hash = "sha256:d198d275222dc54244bf3327eb8cbe00307d220241d9cec4d306d49a44e85f68"}, - {file = "msgpack-1.1.2-cp311-cp311-win_arm64.whl", hash = "sha256:86f8136dfa5c116365a8a651a7d7484b65b13339731dd6faebb9a0242151c406"}, - {file = "msgpack-1.1.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:70a0dff9d1f8da25179ffcf880e10cf1aad55fdb63cd59c9a49a1b82290062aa"}, - {file = "msgpack-1.1.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:446abdd8b94b55c800ac34b102dffd2f6aa0ce643c55dfc017ad89347db3dbdb"}, - {file = "msgpack-1.1.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c63eea553c69ab05b6747901b97d620bb2a690633c77f23feb0c6a947a8a7b8f"}, - {file = "msgpack-1.1.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:372839311ccf6bdaf39b00b61288e0557916c3729529b301c52c2d88842add42"}, - {file = "msgpack-1.1.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2929af52106ca73fcb28576218476ffbb531a036c2adbcf54a3664de124303e9"}, - {file = "msgpack-1.1.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:be52a8fc79e45b0364210eef5234a7cf8d330836d0a64dfbb878efa903d84620"}, - {file = "msgpack-1.1.2-cp312-cp312-win32.whl", hash = "sha256:1fff3d825d7859ac888b0fbda39a42d59193543920eda9d9bea44d958a878029"}, - {file = "msgpack-1.1.2-cp312-cp312-win_amd64.whl", hash = "sha256:1de460f0403172cff81169a30b9a92b260cb809c4cb7e2fc79ae8d0510c78b6b"}, - {file = "msgpack-1.1.2-cp312-cp312-win_arm64.whl", hash = "sha256:be5980f3ee0e6bd44f3a9e9dea01054f175b50c3e6cdb692bc9424c0bbb8bf69"}, - {file = "msgpack-1.1.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4efd7b5979ccb539c221a4c4e16aac1a533efc97f3b759bb5a5ac9f6d10383bf"}, - {file = "msgpack-1.1.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:42eefe2c3e2af97ed470eec850facbe1b5ad1d6eacdbadc42ec98e7dcf68b4b7"}, - {file = "msgpack-1.1.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1fdf7d83102bf09e7ce3357de96c59b627395352a4024f6e2458501f158bf999"}, - {file = "msgpack-1.1.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fac4be746328f90caa3cd4bc67e6fe36ca2bf61d5c6eb6d895b6527e3f05071e"}, - {file = "msgpack-1.1.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:fffee09044073e69f2bad787071aeec727183e7580443dfeb8556cbf1978d162"}, - {file = "msgpack-1.1.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5928604de9b032bc17f5099496417f113c45bc6bc21b5c6920caf34b3c428794"}, - {file = "msgpack-1.1.2-cp313-cp313-win32.whl", hash = "sha256:a7787d353595c7c7e145e2331abf8b7ff1e6673a6b974ded96e6d4ec09f00c8c"}, - {file = "msgpack-1.1.2-cp313-cp313-win_amd64.whl", hash = "sha256:a465f0dceb8e13a487e54c07d04ae3ba131c7c5b95e2612596eafde1dccf64a9"}, - {file = "msgpack-1.1.2-cp313-cp313-win_arm64.whl", hash = "sha256:e69b39f8c0aa5ec24b57737ebee40be647035158f14ed4b40e6f150077e21a84"}, - {file = "msgpack-1.1.2-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:e23ce8d5f7aa6ea6d2a2b326b4ba46c985dbb204523759984430db7114f8aa00"}, - {file = "msgpack-1.1.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:6c15b7d74c939ebe620dd8e559384be806204d73b4f9356320632d783d1f7939"}, - {file = "msgpack-1.1.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:99e2cb7b9031568a2a5c73aa077180f93dd2e95b4f8d3b8e14a73ae94a9e667e"}, - {file = "msgpack-1.1.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:180759d89a057eab503cf62eeec0aa61c4ea1200dee709f3a8e9397dbb3b6931"}, - {file = "msgpack-1.1.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:04fb995247a6e83830b62f0b07bf36540c213f6eac8e851166d8d86d83cbd014"}, - {file = "msgpack-1.1.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:8e22ab046fa7ede9e36eeb4cfad44d46450f37bb05d5ec482b02868f451c95e2"}, - {file = "msgpack-1.1.2-cp314-cp314-win32.whl", hash = "sha256:80a0ff7d4abf5fecb995fcf235d4064b9a9a8a40a3ab80999e6ac1e30b702717"}, - {file = "msgpack-1.1.2-cp314-cp314-win_amd64.whl", hash = "sha256:9ade919fac6a3e7260b7f64cea89df6bec59104987cbea34d34a2fa15d74310b"}, - {file = "msgpack-1.1.2-cp314-cp314-win_arm64.whl", hash = "sha256:59415c6076b1e30e563eb732e23b994a61c159cec44deaf584e5cc1dd662f2af"}, - {file = "msgpack-1.1.2-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:897c478140877e5307760b0ea66e0932738879e7aa68144d9b78ea4c8302a84a"}, - {file = "msgpack-1.1.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a668204fa43e6d02f89dbe79a30b0d67238d9ec4c5bd8a940fc3a004a47b721b"}, - {file = "msgpack-1.1.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5559d03930d3aa0f3aacb4c42c776af1a2ace2611871c84a75afe436695e6245"}, - {file = "msgpack-1.1.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:70c5a7a9fea7f036b716191c29047374c10721c389c21e9ffafad04df8c52c90"}, - {file = "msgpack-1.1.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:f2cb069d8b981abc72b41aea1c580ce92d57c673ec61af4c500153a626cb9e20"}, - {file = "msgpack-1.1.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:d62ce1f483f355f61adb5433ebfd8868c5f078d1a52d042b0a998682b4fa8c27"}, - {file = "msgpack-1.1.2-cp314-cp314t-win32.whl", hash = "sha256:1d1418482b1ee984625d88aa9585db570180c286d942da463533b238b98b812b"}, - {file = "msgpack-1.1.2-cp314-cp314t-win_amd64.whl", hash = "sha256:5a46bf7e831d09470ad92dff02b8b1ac92175ca36b087f904a0519857c6be3ff"}, - {file = "msgpack-1.1.2-cp314-cp314t-win_arm64.whl", hash = "sha256:d99ef64f349d5ec3293688e91486c5fdb925ed03807f64d98d205d2713c60b46"}, - {file = "msgpack-1.1.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ea5405c46e690122a76531ab97a079e184c0daf491e588592d6a23d3e32af99e"}, - {file = "msgpack-1.1.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9fba231af7a933400238cb357ecccf8ab5d51535ea95d94fc35b7806218ff844"}, - {file = "msgpack-1.1.2-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a8f6e7d30253714751aa0b0c84ae28948e852ee7fb0524082e6716769124bc23"}, - {file = "msgpack-1.1.2-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:94fd7dc7d8cb0a54432f296f2246bc39474e017204ca6f4ff345941d4ed285a7"}, - {file = "msgpack-1.1.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:350ad5353a467d9e3b126d8d1b90fe05ad081e2e1cef5753f8c345217c37e7b8"}, - {file = "msgpack-1.1.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:6bde749afe671dc44893f8d08e83bf475a1a14570d67c4bb5cec5573463c8833"}, - {file = "msgpack-1.1.2-cp39-cp39-win32.whl", hash = "sha256:ad09b984828d6b7bb52d1d1d0c9be68ad781fa004ca39216c8a1e63c0f34ba3c"}, - {file = "msgpack-1.1.2-cp39-cp39-win_amd64.whl", hash = "sha256:67016ae8c8965124fdede9d3769528ad8284f14d635337ffa6a713a580f6c030"}, - {file = "msgpack-1.1.2.tar.gz", hash = "sha256:3b60763c1373dd60f398488069bcdc703cd08a711477b5d480eecc9f9626f47e"}, + {file = "msgpack-1.2.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8c7b398c56ff125feae96c2737abfec5595f1fa0aa186df60c56040b8accb95c"}, + {file = "msgpack-1.2.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1548006a91aa93c5da81f3bdcebc1a0d10cea2d25969754fbe848da622b2b895"}, + {file = "msgpack-1.2.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1dabedcd0f23559f3596428c6589c1cd8c6eaed3a0d720795b07b0225d769203"}, + {file = "msgpack-1.2.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:83efa1c898e0fc5380fc0cabbf75164c52e3b5cbb45973710d75821928380c73"}, + {file = "msgpack-1.2.1-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:01e2dd6c9b19d333a00282330cc8a73d38d8dabc306dc5b42cd668c3ac82e833"}, + {file = "msgpack-1.2.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:350cb813d0af6e65d2f7ef0d729f7ff5be5a8bce03665892f43e5883d4ecc1b8"}, + {file = "msgpack-1.2.1-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:ee1d9ed27d0497b848923746cf762ed2e7db24f4be7eec8e5cbe8c766aa707b7"}, + {file = "msgpack-1.2.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:633727297ed063441fd1cda2288865487f33ad14eeb8831afb5f0c396a62cfce"}, + {file = "msgpack-1.2.1-cp310-cp310-win32.whl", hash = "sha256:298872ecf9e61950f1c6af4ca969b859ee91783bb920ef6e6172697d0c8aad74"}, + {file = "msgpack-1.2.1-cp310-cp310-win_amd64.whl", hash = "sha256:2ff164c1b0bcb740b073b99e945234d0212852fa378e44a208c425379140dbeb"}, + {file = "msgpack-1.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:29a3f6e9667868429d8240dfd063ea5ffdc1321c13d783aa23827a38de0dcb22"}, + {file = "msgpack-1.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:aded5bdf32609dc7987a49bbbd15a8ef096193f96dd8bbeb791de729e650acf5"}, + {file = "msgpack-1.2.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:146ee4e9ce80b365c6d4c47073da9da7bcec473e58194ceee5dd7620ace77e06"}, + {file = "msgpack-1.2.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a28d076ca7c82b9c8728ad90b7147489449557038bed50e4241eb832395169b4"}, + {file = "msgpack-1.2.1-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7d31c0ac0c640f877804c67cb2bc9f4e23dc2db97e96c2e67fa27d38283b41f8"}, + {file = "msgpack-1.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8ff92d7feeaf5bc26c51495b69e2f99ed97ab79346fb6555f44be7dd2ac6503b"}, + {file = "msgpack-1.2.1-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:779197a6513bab3c3632265e3d0f7cb3227e62510841a6f34f1eaa37efbb345e"}, + {file = "msgpack-1.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:67f6dd22fa72a93752643f07889796d62739a13415ee630169a8ce764f86cf9f"}, + {file = "msgpack-1.2.1-cp311-cp311-win32.whl", hash = "sha256:91054a783328e0ea7954b8771095705c8d2243b814743fbaadf14552c9c52c5d"}, + {file = "msgpack-1.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2eda0b7ebb1283a98d3e4492ac933c8af6aff59fd3df1c3ed024f536af4b1dc8"}, + {file = "msgpack-1.2.1-cp311-cp311-win_arm64.whl", hash = "sha256:6ee967f7c7e1df2890c671ff2ee51a28ded0efc95da3e507176dee881ce36c66"}, + {file = "msgpack-1.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2ef59c659f289eddf8aa6623823f19fa2f40a4029266889eac7a2505dd210c35"}, + {file = "msgpack-1.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d3567748a5107cb40cdf66a275430c2f87c07777698f4bfd25c35f44d533258c"}, + {file = "msgpack-1.2.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:60926b75d00c8e816ef98f3034f484a8bc64242d66839cef4cf7e503142316a0"}, + {file = "msgpack-1.2.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:020e881a764b20d8d7ca1a54fc01b8175519d108e3c3f194fddc200bda95951a"}, + {file = "msgpack-1.2.1-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:4202c74688ca06591f78cb18988228bd4cca2cc75d57b60008372892d2f1e6e6"}, + {file = "msgpack-1.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8b267ce94efb76fbd1b3373511420074ee3187f0f7811bf394531de13294735a"}, + {file = "msgpack-1.2.1-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:e4f1d0f8f98ade9634e01fb704a408f9336c0a8f1117b369f5db83dc7551d8b1"}, + {file = "msgpack-1.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f02cf17a6ca1abe29b5f980644f7551f94d71f2011509b26d8625ce038f0df64"}, + {file = "msgpack-1.2.1-cp312-cp312-win32.whl", hash = "sha256:0c0d9802354507bcba62af19c17918e3eb437cc25e6f50657d511b5856a77aac"}, + {file = "msgpack-1.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:5c24aa15d5963051e1a5c62b12c50cd705992502b5ec1f3bece6046f33c9fc24"}, + {file = "msgpack-1.2.1-cp312-cp312-win_arm64.whl", hash = "sha256:4227224aaec8f7fbcbfbd4272319347b2bb4030366502600f8c45588c5187b07"}, + {file = "msgpack-1.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0a70e3cf2804a300d921bb0940426e35f4e489a23adfb77a808892241db0a064"}, + {file = "msgpack-1.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:491cc39455ca765fad51fb451bf2915eb2cf41192ab5801ce8d67c1d614fe056"}, + {file = "msgpack-1.2.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f310233ef7fb9c14e201c93639fe5f5260b005f56f0b29048e999c30935596cc"}, + {file = "msgpack-1.2.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:787c9bebb5833e8f6fc8abca3c0597683d8d87f56a8842b6b89c75a5f3176e2d"}, + {file = "msgpack-1.2.1-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:dc871b997a9370d855b7394465f2f350e847a5b806dd38dcc9c989e7d87da155"}, + {file = "msgpack-1.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:85f57e960d877f2977f6430896191b04a21f8901b3b4baf2e4604329f4db5402"}, + {file = "msgpack-1.2.1-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:1233ee2dd0cefba127583de50ea654677277047d238303521db35def3d7b2e7c"}, + {file = "msgpack-1.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e3dc2feb0876209d9c38aa56cb1de169bd6c4348f1aa48271f241226590993e6"}, + {file = "msgpack-1.2.1-cp313-cp313-win32.whl", hash = "sha256:6d09badf350af2be9d189184e04e64cf54ad93569ab3d96fca58bd3e84aad707"}, + {file = "msgpack-1.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:33f14fba63278b714efe6ad07e50ea5f03d91537aa6a1c5f1ceca4cf44013ca9"}, + {file = "msgpack-1.2.1-cp313-cp313-win_arm64.whl", hash = "sha256:afc5febcd4c99effbc02b528e49d6fd0760b2b7d48c05239e345a5fa6e743d9a"}, + {file = "msgpack-1.2.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:05f340e47e7e47d2da8db9b53e1bb1d294369e9ef45a747441309f6650b8351d"}, + {file = "msgpack-1.2.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:810b916696c86ef0deb3b74588480224df4c1b071136c34183e4a2a4284d7ac7"}, + {file = "msgpack-1.2.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ca0dacff965c47afdc3749a8469d7302a8f801d6a28758d55120d75e66ce6889"}, + {file = "msgpack-1.2.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0e2bf9280bceb5efca998435904b5d3e9fdbcc11d90dc9df30aec7973252b720"}, + {file = "msgpack-1.2.1-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:aa6c4be5d1c02a42b066ca6ddb71adf36432868fdcdb6ee87e634e86e0674190"}, + {file = "msgpack-1.2.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ec0e675d59150a6269ddc9139087c722292664a37d071a849c05c473350f1f2d"}, + {file = "msgpack-1.2.1-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:dd3bfe82d53edfe4b7fc9a7ec9761e23a7a5b1dac22264505af428253c29ed24"}, + {file = "msgpack-1.2.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5ad5467fc3f68b5468e06c5f788d712e9f8ffc8b0cd1bcb160c105c1ee92dae7"}, + {file = "msgpack-1.2.1-cp314-cp314-win32.whl", hash = "sha256:98b58bdb89c46190e4609bb36abe17c6d4105ad13f9c5f8f6f64d320f8ced3fb"}, + {file = "msgpack-1.2.1-cp314-cp314-win_amd64.whl", hash = "sha256:74847557e28ce71bd3c438a447ca90e4b507e997ddbdef8a12a7b283b86c156b"}, + {file = "msgpack-1.2.1-cp314-cp314-win_arm64.whl", hash = "sha256:b50b727bd652bdc37d950336c848ef20ec54a4cafc38dce19b1cd86ad625d0f7"}, + {file = "msgpack-1.2.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:8d00f177ca88a77c1cf848d204a38f249751650b601cb6532acc68805d8a8273"}, + {file = "msgpack-1.2.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5bb9c386f0a329c035ddbab4b72d1028bf9627add8dda41070288563d57ed1b1"}, + {file = "msgpack-1.2.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:20466cca18c49c7292a8984bc15d65857b171e7264bdcb5f96baf8be238791fc"}, + {file = "msgpack-1.2.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:196300e7e5d6e74d50f1607ab9c06c4a1484c383cd22defd727902591f7e8dde"}, + {file = "msgpack-1.2.1-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:575957e79cd51903a4e8495a242442949641e08f1efd5197b43bebd3ea7682b4"}, + {file = "msgpack-1.2.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8c2ed1e48cc0f460bf3c7780e7137ff21a4e18433451916f2442c1b21036cd7d"}, + {file = "msgpack-1.2.1-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:5f6277e5f783c36786a145e0247fc189a03f35f84b251646e53592d2bc12b355"}, + {file = "msgpack-1.2.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:f9389552ecf4784886345ead0647e4edc96bee37cbab05b75540f542f766c48c"}, + {file = "msgpack-1.2.1-cp314-cp314t-win32.whl", hash = "sha256:c1c79a604a2969a868a78b6ebd27a887e00c624f14f66b3038e0590cb23332d1"}, + {file = "msgpack-1.2.1-cp314-cp314t-win_amd64.whl", hash = "sha256:f12038a35fabd52e56a3547bab42401af49a45caa6dd00b34c44de235bc93ee2"}, + {file = "msgpack-1.2.1-cp314-cp314t-win_arm64.whl", hash = "sha256:0adcf06ffde0777c0e1a9b771a2b1c4226ba1bbf748c8efcc02fcdeca3299107"}, + {file = "msgpack-1.2.1.tar.gz", hash = "sha256:04c721c2c7448767e9e3f2520a475663d8ee0f09c31890f6d2bd70fd636a9647"}, ] [[package]] name = "mypy" -version = "1.18.2" +version = "1.20.2" description = "Optional static typing for Python" optional = false -python-versions = ">=3.9" +python-versions = ">=3.10" groups = ["dev"] files = [ - {file = "mypy-1.18.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c1eab0cf6294dafe397c261a75f96dc2c31bffe3b944faa24db5def4e2b0f77c"}, - {file = "mypy-1.18.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7a780ca61fc239e4865968ebc5240bb3bf610ef59ac398de9a7421b54e4a207e"}, - {file = "mypy-1.18.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:448acd386266989ef11662ce3c8011fd2a7b632e0ec7d61a98edd8e27472225b"}, - {file = "mypy-1.18.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f9e171c465ad3901dc652643ee4bffa8e9fef4d7d0eece23b428908c77a76a66"}, - {file = "mypy-1.18.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:592ec214750bc00741af1f80cbf96b5013d81486b7bb24cb052382c19e40b428"}, - {file = "mypy-1.18.2-cp310-cp310-win_amd64.whl", hash = "sha256:7fb95f97199ea11769ebe3638c29b550b5221e997c63b14ef93d2e971606ebed"}, - {file = "mypy-1.18.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:807d9315ab9d464125aa9fcf6d84fde6e1dc67da0b6f80e7405506b8ac72bc7f"}, - {file = "mypy-1.18.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:776bb00de1778caf4db739c6e83919c1d85a448f71979b6a0edd774ea8399341"}, - {file = "mypy-1.18.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1379451880512ffce14505493bd9fe469e0697543717298242574882cf8cdb8d"}, - {file = "mypy-1.18.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1331eb7fd110d60c24999893320967594ff84c38ac6d19e0a76c5fd809a84c86"}, - {file = "mypy-1.18.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3ca30b50a51e7ba93b00422e486cbb124f1c56a535e20eff7b2d6ab72b3b2e37"}, - {file = "mypy-1.18.2-cp311-cp311-win_amd64.whl", hash = "sha256:664dc726e67fa54e14536f6e1224bcfce1d9e5ac02426d2326e2bb4e081d1ce8"}, - {file = "mypy-1.18.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:33eca32dd124b29400c31d7cf784e795b050ace0e1f91b8dc035672725617e34"}, - {file = "mypy-1.18.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a3c47adf30d65e89b2dcd2fa32f3aeb5e94ca970d2c15fcb25e297871c8e4764"}, - {file = "mypy-1.18.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5d6c838e831a062f5f29d11c9057c6009f60cb294fea33a98422688181fe2893"}, - {file = "mypy-1.18.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:01199871b6110a2ce984bde85acd481232d17413868c9807e95c1b0739a58914"}, - {file = "mypy-1.18.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a2afc0fa0b0e91b4599ddfe0f91e2c26c2b5a5ab263737e998d6817874c5f7c8"}, - {file = "mypy-1.18.2-cp312-cp312-win_amd64.whl", hash = "sha256:d8068d0afe682c7c4897c0f7ce84ea77f6de953262b12d07038f4d296d547074"}, - {file = "mypy-1.18.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:07b8b0f580ca6d289e69209ec9d3911b4a26e5abfde32228a288eb79df129fcc"}, - {file = "mypy-1.18.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ed4482847168439651d3feee5833ccedbf6657e964572706a2adb1f7fa4dfe2e"}, - {file = "mypy-1.18.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c3ad2afadd1e9fea5cf99a45a822346971ede8685cc581ed9cd4d42eaf940986"}, - {file = "mypy-1.18.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a431a6f1ef14cf8c144c6b14793a23ec4eae3db28277c358136e79d7d062f62d"}, - {file = "mypy-1.18.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7ab28cc197f1dd77a67e1c6f35cd1f8e8b73ed2217e4fc005f9e6a504e46e7ba"}, - {file = "mypy-1.18.2-cp313-cp313-win_amd64.whl", hash = "sha256:0e2785a84b34a72ba55fb5daf079a1003a34c05b22238da94fcae2bbe46f3544"}, - {file = "mypy-1.18.2-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:62f0e1e988ad41c2a110edde6c398383a889d95b36b3e60bcf155f5164c4fdce"}, - {file = "mypy-1.18.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:8795a039bab805ff0c1dfdb8cd3344642c2b99b8e439d057aba30850b8d3423d"}, - {file = "mypy-1.18.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6ca1e64b24a700ab5ce10133f7ccd956a04715463d30498e64ea8715236f9c9c"}, - {file = "mypy-1.18.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d924eef3795cc89fecf6bedc6ed32b33ac13e8321344f6ddbf8ee89f706c05cb"}, - {file = "mypy-1.18.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:20c02215a080e3a2be3aa50506c67242df1c151eaba0dcbc1e4e557922a26075"}, - {file = "mypy-1.18.2-cp314-cp314-win_amd64.whl", hash = "sha256:749b5f83198f1ca64345603118a6f01a4e99ad4bf9d103ddc5a3200cc4614adf"}, - {file = "mypy-1.18.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:25a9c8fb67b00599f839cf472713f54249a62efd53a54b565eb61956a7e3296b"}, - {file = "mypy-1.18.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c2b9c7e284ee20e7598d6f42e13ca40b4928e6957ed6813d1ab6348aa3f47133"}, - {file = "mypy-1.18.2-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d6985ed057513e344e43a26cc1cd815c7a94602fb6a3130a34798625bc2f07b6"}, - {file = "mypy-1.18.2-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:22f27105f1525ec024b5c630c0b9f36d5c1cc4d447d61fe51ff4bd60633f47ac"}, - {file = "mypy-1.18.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:030c52d0ea8144e721e49b1f68391e39553d7451f0c3f8a7565b59e19fcb608b"}, - {file = "mypy-1.18.2-cp39-cp39-win_amd64.whl", hash = "sha256:aa5e07ac1a60a253445797e42b8b2963c9675563a94f11291ab40718b016a7a0"}, - {file = "mypy-1.18.2-py3-none-any.whl", hash = "sha256:22a1748707dd62b58d2ae53562ffc4d7f8bcc727e8ac7cbc69c053ddc874d47e"}, - {file = "mypy-1.18.2.tar.gz", hash = "sha256:06a398102a5f203d7477b2923dda3634c36727fa5c237d8f859ef90c42a9924b"}, + {file = "mypy-1.20.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:cf5a4db6dca263010e2c7bff081c89383c72d187ba2cf4c44759aac970e2f0c4"}, + {file = "mypy-1.20.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7b0e817b518bff7facd7f85ea05b643ad8bdcce684cf29784987b0a7c8e1f997"}, + {file = "mypy-1.20.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:97d7b9a485b40f8ca425460e89bf1da2814625b2da627c0dcc6aa46c92631d14"}, + {file = "mypy-1.20.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1e1c12f6d2db3d78b909b5f77513c11eb7f2dd2782b96a3ab6dffc7d44575c99"}, + {file = "mypy-1.20.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:89dce27e142d25ffbc154c1819383b69f2e9234dc4ed4766f42e0e8cb264ab5c"}, + {file = "mypy-1.20.2-cp310-cp310-win_amd64.whl", hash = "sha256:f376e37f9bf2a946872fc5fd1199c99310748e3c26c7a26683f13f8bdb756cbd"}, + {file = "mypy-1.20.2-cp310-cp310-win_arm64.whl", hash = "sha256:6e2b469efd811707bc530fd1effef0f5d6eebcb7fe376affae69025da4b979a2"}, + {file = "mypy-1.20.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4077797a273e56e8843d001e9dfe4ba10e33323d6ade647ff260e5cd97d9758c"}, + {file = "mypy-1.20.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:cdecf62abcc4292500d7858aeae87a1f8f1150f4c4dd08fb0b336ee79b2a6df3"}, + {file = "mypy-1.20.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c566c3a88b6ece59b3d70f65bedef17304f48eb52ff040a6a18214e1917b3254"}, + {file = "mypy-1.20.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0deb80d062b2479f2c87ae568f89845afc71d11bc41b04179e58165fd9f31e98"}, + {file = "mypy-1.20.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bba9ad231e92a3e424b3e56b65aa17704993425bba97e302c832f9466bb85bac"}, + {file = "mypy-1.20.2-cp311-cp311-win_amd64.whl", hash = "sha256:baf593f2765fa3a6b1ef95807dbaa3d25b594f6a52adcc506a6b9cb115e1be67"}, + {file = "mypy-1.20.2-cp311-cp311-win_arm64.whl", hash = "sha256:20175a1c0f49863946ec20b7f63255768058ac4f07d2b9ded6a6b46cfb5a9100"}, + {file = "mypy-1.20.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4dbfcf869f6b0517f70cf0030ba6ea1d6645e132337a7d5204a18d8d5636c02b"}, + {file = "mypy-1.20.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4b6481b228d072315b053210b01ac320e1be243dc17f9e5887ef167f23f5fae4"}, + {file = "mypy-1.20.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:34397cdced6b90b836e38182076049fdb41424322e0b0728c946b0939ebdf9f6"}, + {file = "mypy-1.20.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a5da6976f20cae27059ea8d0c86e7cef3de720e04c4bb9ee18e3690fdb792066"}, + {file = "mypy-1.20.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:56908d7e08318d39f85b1f0c6cfd47b0cac1a130da677630dac0de3e0623e102"}, + {file = "mypy-1.20.2-cp312-cp312-win_amd64.whl", hash = "sha256:d52ad8d78522da1d308789df651ee5379088e77c76cb1994858d40a426b343b9"}, + {file = "mypy-1.20.2-cp312-cp312-win_arm64.whl", hash = "sha256:785b08db19c9f214dc37d65f7c165d19a30fcecb48abfa30f31b01b5acaabb58"}, + {file = "mypy-1.20.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:edfbfca868cdd6bd8d974a60f8a3682f5565d3f5c99b327640cedd24c4264026"}, + {file = "mypy-1.20.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e2877a02380adfcdbc69071a0f74d6e9dbbf593c0dc9d174e1f223ffd5281943"}, + {file = "mypy-1.20.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7488448de6007cd5177c6cea0517ac33b4c0f5ee9b5e9f2be51ce75511a85517"}, + {file = "mypy-1.20.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bb9c2fa06887e21d6a3a868762acb82aec34e2c6fd0174064f27c93ede68ad15"}, + {file = "mypy-1.20.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9d56a78b646f2e3daa865bc70cd5ec5a46c50045801ca8ff17a0c43abc97e3ee"}, + {file = "mypy-1.20.2-cp313-cp313-win_amd64.whl", hash = "sha256:2a4102b03bb7481d9a91a6da8d174740c9c8c4401024684b9ca3b7cc5e49852f"}, + {file = "mypy-1.20.2-cp313-cp313-win_arm64.whl", hash = "sha256:a95a9248b0c6fd933a442c03c3b113c3b61320086b88e2c444676d3fd1ca3330"}, + {file = "mypy-1.20.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:419413398fe250aae057fd2fe50166b61077083c9b82754c341cf4fd73038f30"}, + {file = "mypy-1.20.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:e73c07f23009962885c197ccb9b41356a30cc0e5a1d0c2ea8fd8fb1362d7f924"}, + {file = "mypy-1.20.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0c64e5973df366b747646fc98da921f9d6eba9716d57d1db94a83c026a08e0fb"}, + {file = "mypy-1.20.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5a65aa591af023864fd08a97da9974e919452cfe19cb146c8a5dc692626445dc"}, + {file = "mypy-1.20.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:4fef51b01e638974a6e69885687e9bd40c8d1e09a6cd291cca0619625cf1f558"}, + {file = "mypy-1.20.2-cp314-cp314-win_amd64.whl", hash = "sha256:913485a03f1bcf5d279409a9d2b9ed565c151f61c09f29991e5faa14033da4c8"}, + {file = "mypy-1.20.2-cp314-cp314-win_arm64.whl", hash = "sha256:c3bae4f855d965b5453784300c12ffc63a548304ac7f99e55d4dc7c898673aa3"}, + {file = "mypy-1.20.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:2de3dcea53babc1c3237a19002bc3d228ce1833278f093b8d619e06e7cc79609"}, + {file = "mypy-1.20.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:52b176444e2e5054dfcbcb8c75b0b719865c96247b37407184bbfca5c353f2c2"}, + {file = "mypy-1.20.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:688c3312e5dadb573a2c69c82af3a298d43ecf9e6d264e0f95df960b5f6ac19c"}, + {file = "mypy-1.20.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:29752dbbf8cc53f89f6ac096d363314333045c257c9c75cbd189ca2de0455744"}, + {file = "mypy-1.20.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:803203d2b6ea644982c644895c2f78b28d0e208bba7b27d9b921e0ec5eb207c6"}, + {file = "mypy-1.20.2-cp314-cp314t-win_amd64.whl", hash = "sha256:9bcb8aa397ff0093c824182fd76a935a9ba7ad097fcbef80ae89bf6c1731d8ec"}, + {file = "mypy-1.20.2-cp314-cp314t-win_arm64.whl", hash = "sha256:e061b58443f1736f8a37c48978d7ab581636d6ab03e3d4f99e3fa90463bb9382"}, + {file = "mypy-1.20.2-py3-none-any.whl", hash = "sha256:a94c5a76ab46c5e6257c7972b6c8cff0574201ca7dc05647e33e795d78680563"}, + {file = "mypy-1.20.2.tar.gz", hash = "sha256:e8222c26daaafd9e8626dec58ae36029f82585890589576f769a650dd20fd665"}, ] [package.dependencies] +librt = {version = ">=0.8.0", markers = "platform_python_implementation != \"PyPy\""} mypy_extensions = ">=1.0.0" -pathspec = ">=0.9.0" -tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} -typing_extensions = ">=4.6.0" +pathspec = ">=1.0.0" +typing_extensions = {version = ">=4.6.0", markers = "python_version < \"3.15\""} [package.extras] dmypy = ["psutil (>=4.0)"] faster-cache = ["orjson"] install-types = ["pip"] mypyc = ["setuptools (>=50)"] +native-parser = ["ast-serialize (>=0.1.1,<1.0.0)"] reports = ["lxml"] [[package]] @@ -2087,46 +1974,45 @@ testing-docutils = ["pygments", "pytest (>=8,<9)", "pytest-param-files (>=0.6.0, [[package]] name = "nbclient" -version = "0.10.2" +version = "0.10.4" description = "A client library for executing notebooks. Formerly nbconvert's ExecutePreprocessor." optional = true -python-versions = ">=3.9.0" +python-versions = ">=3.10.0" groups = ["main"] markers = "extra == \"docs\"" files = [ - {file = "nbclient-0.10.2-py3-none-any.whl", hash = "sha256:4ffee11e788b4a27fabeb7955547e4318a5298f34342a4bfd01f2e1faaeadc3d"}, - {file = "nbclient-0.10.2.tar.gz", hash = "sha256:90b7fc6b810630db87a6d0c2250b1f0ab4cf4d3c27a299b0cde78a4ed3fd9193"}, + {file = "nbclient-0.10.4-py3-none-any.whl", hash = "sha256:9162df5a7373d70d606527300a95a975a47c137776cd942e52d9c7e29ff83440"}, + {file = "nbclient-0.10.4.tar.gz", hash = "sha256:1e54091b16e6da39e297b0ece3e10f6f29f4ac4e8ee515d29f8a7099bd6553c9"}, ] [package.dependencies] jupyter-client = ">=6.1.12" jupyter-core = ">=4.12,<5.0.dev0 || >=5.1.dev0" -nbformat = ">=5.1" +nbformat = ">=5.1.3" traitlets = ">=5.4" [package.extras] dev = ["pre-commit"] -docs = ["autodoc-traits", "flaky", "ipykernel (>=6.19.3)", "ipython", "ipywidgets", "mock", "moto", "myst-parser", "nbconvert (>=7.1.0)", "pytest (>=7.0,<8)", "pytest-asyncio", "pytest-cov (>=4.0)", "sphinx (>=1.7)", "sphinx-book-theme", "sphinxcontrib-spelling", "testpath", "xmltodict"] -test = ["flaky", "ipykernel (>=6.19.3)", "ipython", "ipywidgets", "nbconvert (>=7.1.0)", "pytest (>=7.0,<8)", "pytest-asyncio", "pytest-cov (>=4.0)", "testpath", "xmltodict"] +docs = ["autodoc-traits", "flaky", "ipykernel (>=6.19.3)", "ipython", "ipywidgets", "mock", "moto", "myst-parser", "nbconvert (>=7.1.0)", "pytest (>=9.0.1,<10)", "pytest-asyncio (>=1.3.0)", "pytest-cov (>=4.0)", "sphinx (>=1.7)", "sphinx-book-theme", "sphinxcontrib-spelling", "testpath", "xmltodict"] +test = ["flaky", "ipykernel (>=6.19.3)", "ipython", "ipywidgets", "nbconvert (>=7.1.0)", "pytest (>=9.0.1,<10)", "pytest-asyncio (>=1.3.0)", "pytest-cov (>=4.0)", "testpath", "xmltodict"] [[package]] name = "nbconvert" -version = "7.16.6" -description = "Converting Jupyter Notebooks (.ipynb files) to other formats. Output formats include asciidoc, html, latex, markdown, pdf, py, rst, script. nbconvert can be used both as a Python library (`import nbconvert`) or as a command line tool (invoked as `jupyter nbconvert ...`)." +version = "7.17.1" +description = "Convert Jupyter Notebooks (.ipynb files) to other formats." optional = true -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["main"] markers = "extra == \"docs\"" files = [ - {file = "nbconvert-7.16.6-py3-none-any.whl", hash = "sha256:1375a7b67e0c2883678c48e506dc320febb57685e5ee67faa51b18a90f3a712b"}, - {file = "nbconvert-7.16.6.tar.gz", hash = "sha256:576a7e37c6480da7b8465eefa66c17844243816ce1ccc372633c6b71c3c0f582"}, + {file = "nbconvert-7.17.1-py3-none-any.whl", hash = "sha256:aa85c087b435e7bf1ffd03319f658e285f2b89eccab33bc1ba7025495ab3e7c8"}, + {file = "nbconvert-7.17.1.tar.gz", hash = "sha256:34d0d0a7e73ce3cbab6c5aae8f4f468797280b01fd8bd2ca746da8569eddd7d2"}, ] [package.dependencies] beautifulsoup4 = "*" bleach = {version = "!=5.0.0", extras = ["css"]} defusedxml = "*" -importlib-metadata = {version = ">=3.6", markers = "python_version < \"3.10\""} jinja2 = ">=3.0" jupyter-core = ">=4.7" jupyterlab-pygments = "*" @@ -2140,8 +2026,8 @@ pygments = ">=2.4.1" traitlets = ">=5.1" [package.extras] -all = ["flaky", "ipykernel", "ipython", "ipywidgets (>=7.5)", "myst-parser", "nbsphinx (>=0.2.12)", "playwright", "pydata-sphinx-theme", "pyqtwebengine (>=5.15)", "pytest (>=7)", "sphinx (==5.0.2)", "sphinxcontrib-spelling", "tornado (>=6.1)"] -docs = ["ipykernel", "ipython", "myst-parser", "nbsphinx (>=0.2.12)", "pydata-sphinx-theme", "sphinx (==5.0.2)", "sphinxcontrib-spelling"] +all = ["flaky", "intersphinx-registry", "ipykernel", "ipython", "ipywidgets (>=7.5)", "myst-parser", "nbsphinx (>=0.2.12)", "playwright", "pydata-sphinx-theme", "pyqtwebengine (>=5.15)", "pytest (>=7)", "sphinx (>=5.0.2)", "sphinxcontrib-spelling", "tornado (>=6.1)"] +docs = ["intersphinx-registry", "ipykernel", "ipython", "myst-parser", "nbsphinx (>=0.2.12)", "pydata-sphinx-theme", "sphinx (>=5.0.2)", "sphinxcontrib-spelling"] qtpdf = ["pyqtwebengine (>=5.15)"] qtpng = ["pyqtwebengine (>=5.15)"] serve = ["tornado (>=6.1)"] @@ -2173,15 +2059,15 @@ test = ["pep440", "pre-commit", "pytest", "testpath"] [[package]] name = "nbsphinx" -version = "0.9.7" +version = "0.9.8" description = "Jupyter Notebook Tools for Sphinx" optional = true -python-versions = ">=3.6" +python-versions = ">=3.8" groups = ["main"] markers = "extra == \"docs\"" files = [ - {file = "nbsphinx-0.9.7-py3-none-any.whl", hash = "sha256:7292c3767fea29e405c60743eee5393682a83982ab202ff98f5eb2db02629da8"}, - {file = "nbsphinx-0.9.7.tar.gz", hash = "sha256:abd298a686d55fa894ef697c51d44f24e53aa312dadae38e82920f250a5456fe"}, + {file = "nbsphinx-0.9.8-py3-none-any.whl", hash = "sha256:92d95ee91784e56bc633b60b767a6b6f23a0445f891e24641ce3c3f004759ccf"}, + {file = "nbsphinx-0.9.8.tar.gz", hash = "sha256:d0765908399a8ee2b57be7ae881cf2ea58d66db3af7bbf33e6eb48f83bea5495"}, ] [package.dependencies] @@ -2189,304 +2075,227 @@ docutils = ">=0.18.1" jinja2 = "*" nbconvert = ">=5.3,<5.4 || >5.4" nbformat = "*" -sphinx = ">=1.8,<8.2" +sphinx = ">=1.8,<8.2.0 || >8.2.0,<8.2.1 || >8.2.1" traitlets = ">=5" [[package]] name = "networkx" -version = "3.2.1" +version = "3.6.1" description = "Python package for creating and manipulating graphs and networks" optional = false -python-versions = ">=3.9" +python-versions = "!=3.14.1,>=3.11" groups = ["main"] -markers = "python_version < \"3.11\"" files = [ - {file = "networkx-3.2.1-py3-none-any.whl", hash = "sha256:f18c69adc97877c42332c170849c96cefa91881c99a7cb3e95b7c659ebdc1ec2"}, - {file = "networkx-3.2.1.tar.gz", hash = "sha256:9f1bb5cf3409bf324e0a722c20bdb4c20ee39bf1c30ce8ae499c8502b0b5e0c6"}, -] - -[package.extras] -default = ["matplotlib (>=3.5)", "numpy (>=1.22)", "pandas (>=1.4)", "scipy (>=1.9,!=1.11.0,!=1.11.1)"] -developer = ["changelist (==0.4)", "mypy (>=1.1)", "pre-commit (>=3.2)", "rtoml"] -doc = ["nb2plots (>=0.7)", "nbconvert (<7.9)", "numpydoc (>=1.6)", "pillow (>=9.4)", "pydata-sphinx-theme (>=0.14)", "sphinx (>=7)", "sphinx-gallery (>=0.14)", "texext (>=0.6.7)"] -extra = ["lxml (>=4.6)", "pydot (>=1.4.2)", "pygraphviz (>=1.11)", "sympy (>=1.10)"] -test = ["pytest (>=7.2)", "pytest-cov (>=4.0)"] - -[[package]] -name = "networkx" -version = "3.5" -description = "Python package for creating and manipulating graphs and networks" -optional = false -python-versions = ">=3.11" -groups = ["main"] -markers = "python_version >= \"3.11\"" -files = [ - {file = "networkx-3.5-py3-none-any.whl", hash = "sha256:0030d386a9a06dee3565298b4a734b68589749a544acbb6c412dc9e2489ec6ec"}, - {file = "networkx-3.5.tar.gz", hash = "sha256:d4c6f9cf81f52d69230866796b82afbccdec3db7ae4fbd1b65ea750feed50037"}, + {file = "networkx-3.6.1-py3-none-any.whl", hash = "sha256:d47fbf302e7d9cbbb9e2555a0d267983d2aa476bac30e90dfbe5669bd57f3762"}, + {file = "networkx-3.6.1.tar.gz", hash = "sha256:26b7c357accc0c8cde558ad486283728b65b6a95d85ee1cd66bafab4c8168509"}, ] [package.extras] +benchmarking = ["asv", "virtualenv"] default = ["matplotlib (>=3.8)", "numpy (>=1.25)", "pandas (>=2.0)", "scipy (>=1.11.2)"] developer = ["mypy (>=1.15)", "pre-commit (>=4.1)"] doc = ["intersphinx-registry", "myst-nb (>=1.1)", "numpydoc (>=1.8.0)", "pillow (>=10)", "pydata-sphinx-theme (>=0.16)", "sphinx (>=8.0)", "sphinx-gallery (>=0.18)", "texext (>=0.6.7)"] -example = ["cairocffi (>=1.7)", "contextily (>=1.6)", "igraph (>=0.11)", "momepy (>=0.7.2)", "osmnx (>=2.0.0)", "scikit-learn (>=1.5)", "seaborn (>=0.13)"] +example = ["cairocffi (>=1.7)", "contextily (>=1.6)", "igraph (>=0.11)", "iplotx (>=0.9.0)", "momepy (>=0.7.2)", "osmnx (>=2.0.0)", "scikit-learn (>=1.5)", "seaborn (>=0.13)"] extra = ["lxml (>=4.6)", "pydot (>=3.0.1)", "pygraphviz (>=1.14)", "sympy (>=1.10)"] +release = ["build (>=0.10)", "changelist (==0.5)", "twine (>=4.0)", "wheel (>=0.40)"] test = ["pytest (>=7.2)", "pytest-cov (>=4.0)", "pytest-xdist (>=3.0)"] test-extras = ["pytest-mpl", "pytest-randomly"] [[package]] name = "numpy" -version = "2.0.2" +version = "2.4.4" description = "Fundamental package for array computing in Python" optional = false -python-versions = ">=3.9" +python-versions = ">=3.11" groups = ["main"] -markers = "python_version < \"3.11\"" -files = [ - {file = "numpy-2.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:51129a29dbe56f9ca83438b706e2e69a39892b5eda6cedcb6b0c9fdc9b0d3ece"}, - {file = "numpy-2.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f15975dfec0cf2239224d80e32c3170b1d168335eaedee69da84fbe9f1f9cd04"}, - {file = "numpy-2.0.2-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:8c5713284ce4e282544c68d1c3b2c7161d38c256d2eefc93c1d683cf47683e66"}, - {file = "numpy-2.0.2-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:becfae3ddd30736fe1889a37f1f580e245ba79a5855bff5f2a29cb3ccc22dd7b"}, - {file = "numpy-2.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2da5960c3cf0df7eafefd806d4e612c5e19358de82cb3c343631188991566ccd"}, - {file = "numpy-2.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:496f71341824ed9f3d2fd36cf3ac57ae2e0165c143b55c3a035ee219413f3318"}, - {file = "numpy-2.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a61ec659f68ae254e4d237816e33171497e978140353c0c2038d46e63282d0c8"}, - {file = "numpy-2.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d731a1c6116ba289c1e9ee714b08a8ff882944d4ad631fd411106a30f083c326"}, - {file = "numpy-2.0.2-cp310-cp310-win32.whl", hash = "sha256:984d96121c9f9616cd33fbd0618b7f08e0cfc9600a7ee1d6fd9b239186d19d97"}, - {file = "numpy-2.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:c7b0be4ef08607dd04da4092faee0b86607f111d5ae68036f16cc787e250a131"}, - {file = "numpy-2.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:49ca4decb342d66018b01932139c0961a8f9ddc7589611158cb3c27cbcf76448"}, - {file = "numpy-2.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:11a76c372d1d37437857280aa142086476136a8c0f373b2e648ab2c8f18fb195"}, - {file = "numpy-2.0.2-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:807ec44583fd708a21d4a11d94aedf2f4f3c3719035c76a2bbe1fe8e217bdc57"}, - {file = "numpy-2.0.2-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:8cafab480740e22f8d833acefed5cc87ce276f4ece12fdaa2e8903db2f82897a"}, - {file = "numpy-2.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a15f476a45e6e5a3a79d8a14e62161d27ad897381fecfa4a09ed5322f2085669"}, - {file = "numpy-2.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13e689d772146140a252c3a28501da66dfecd77490b498b168b501835041f951"}, - {file = "numpy-2.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9ea91dfb7c3d1c56a0e55657c0afb38cf1eeae4544c208dc465c3c9f3a7c09f9"}, - {file = "numpy-2.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c1c9307701fec8f3f7a1e6711f9089c06e6284b3afbbcd259f7791282d660a15"}, - {file = "numpy-2.0.2-cp311-cp311-win32.whl", hash = "sha256:a392a68bd329eafac5817e5aefeb39038c48b671afd242710b451e76090e81f4"}, - {file = "numpy-2.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:286cd40ce2b7d652a6f22efdfc6d1edf879440e53e76a75955bc0c826c7e64dc"}, - {file = "numpy-2.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:df55d490dea7934f330006d0f81e8551ba6010a5bf035a249ef61a94f21c500b"}, - {file = "numpy-2.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8df823f570d9adf0978347d1f926b2a867d5608f434a7cff7f7908c6570dcf5e"}, - {file = "numpy-2.0.2-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:9a92ae5c14811e390f3767053ff54eaee3bf84576d99a2456391401323f4ec2c"}, - {file = "numpy-2.0.2-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:a842d573724391493a97a62ebbb8e731f8a5dcc5d285dfc99141ca15a3302d0c"}, - {file = "numpy-2.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c05e238064fc0610c840d1cf6a13bf63d7e391717d247f1bf0318172e759e692"}, - {file = "numpy-2.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0123ffdaa88fa4ab64835dcbde75dcdf89c453c922f18dced6e27c90d1d0ec5a"}, - {file = "numpy-2.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:96a55f64139912d61de9137f11bf39a55ec8faec288c75a54f93dfd39f7eb40c"}, - {file = "numpy-2.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ec9852fb39354b5a45a80bdab5ac02dd02b15f44b3804e9f00c556bf24b4bded"}, - {file = "numpy-2.0.2-cp312-cp312-win32.whl", hash = "sha256:671bec6496f83202ed2d3c8fdc486a8fc86942f2e69ff0e986140339a63bcbe5"}, - {file = "numpy-2.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:cfd41e13fdc257aa5778496b8caa5e856dc4896d4ccf01841daee1d96465467a"}, - {file = "numpy-2.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9059e10581ce4093f735ed23f3b9d283b9d517ff46009ddd485f1747eb22653c"}, - {file = "numpy-2.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:423e89b23490805d2a5a96fe40ec507407b8ee786d66f7328be214f9679df6dd"}, - {file = "numpy-2.0.2-cp39-cp39-macosx_14_0_arm64.whl", hash = "sha256:2b2955fa6f11907cf7a70dab0d0755159bca87755e831e47932367fc8f2f2d0b"}, - {file = "numpy-2.0.2-cp39-cp39-macosx_14_0_x86_64.whl", hash = "sha256:97032a27bd9d8988b9a97a8c4d2c9f2c15a81f61e2f21404d7e8ef00cb5be729"}, - {file = "numpy-2.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1e795a8be3ddbac43274f18588329c72939870a16cae810c2b73461c40718ab1"}, - {file = "numpy-2.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f26b258c385842546006213344c50655ff1555a9338e2e5e02a0756dc3e803dd"}, - {file = "numpy-2.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5fec9451a7789926bcf7c2b8d187292c9f93ea30284802a0ab3f5be8ab36865d"}, - {file = "numpy-2.0.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:9189427407d88ff25ecf8f12469d4d39d35bee1db5d39fc5c168c6f088a6956d"}, - {file = "numpy-2.0.2-cp39-cp39-win32.whl", hash = "sha256:905d16e0c60200656500c95b6b8dca5d109e23cb24abc701d41c02d74c6b3afa"}, - {file = "numpy-2.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:a3f4ab0caa7f053f6797fcd4e1e25caee367db3112ef2b6ef82d749530768c73"}, - {file = "numpy-2.0.2-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:7f0a0c6f12e07fa94133c8a67404322845220c06a9e80e85999afe727f7438b8"}, - {file = "numpy-2.0.2-pp39-pypy39_pp73-macosx_14_0_x86_64.whl", hash = "sha256:312950fdd060354350ed123c0e25a71327d3711584beaef30cdaa93320c392d4"}, - {file = "numpy-2.0.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26df23238872200f63518dd2aa984cfca675d82469535dc7162dc2ee52d9dd5c"}, - {file = "numpy-2.0.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:a46288ec55ebbd58947d31d72be2c63cbf839f0a63b49cb755022310792a3385"}, - {file = "numpy-2.0.2.tar.gz", hash = "sha256:883c987dee1880e2a864ab0dc9892292582510604156762362d9326444636e78"}, -] - -[[package]] -name = "numpy" -version = "2.3.3" -description = "Fundamental package for array computing in Python" +files = [ + {file = "numpy-2.4.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f983334aea213c99992053ede6168500e5f086ce74fbc4acc3f2b00f5762e9db"}, + {file = "numpy-2.4.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:72944b19f2324114e9dc86a159787333b77874143efcf89a5167ef83cfee8af0"}, + {file = "numpy-2.4.4-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:86b6f55f5a352b48d7fbfd2dbc3d5b780b2d79f4d3c121f33eb6efb22e9a2015"}, + {file = "numpy-2.4.4-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:ba1f4fc670ed79f876f70082eff4f9583c15fb9a4b89d6188412de4d18ae2f40"}, + {file = "numpy-2.4.4-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8a87ec22c87be071b6bdbd27920b129b94f2fc964358ce38f3822635a3e2e03d"}, + {file = "numpy-2.4.4-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:df3775294accfdd75f32c74ae39fcba920c9a378a2fc18a12b6820aa8c1fb502"}, + {file = "numpy-2.4.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0d4e437e295f18ec29bc79daf55e8a47a9113df44d66f702f02a293d93a2d6dd"}, + {file = "numpy-2.4.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6aa3236c78803afbcb255045fbef97a9e25a1f6c9888357d205ddc42f4d6eba5"}, + {file = "numpy-2.4.4-cp311-cp311-win32.whl", hash = "sha256:30caa73029a225b2d40d9fae193e008e24b2026b7ee1a867b7ee8d96ca1a448e"}, + {file = "numpy-2.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:6bbe4eb67390b0a0265a2c25458f6b90a409d5d069f1041e6aff1e27e3d9a79e"}, + {file = "numpy-2.4.4-cp311-cp311-win_arm64.whl", hash = "sha256:fcfe2045fd2e8f3cb0ce9d4ba6dba6333b8fa05bb8a4939c908cd43322d14c7e"}, + {file = "numpy-2.4.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:15716cfef24d3a9762e3acdf87e27f58dc823d1348f765bbea6bef8c639bfa1b"}, + {file = "numpy-2.4.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:23cbfd4c17357c81021f21540da84ee282b9c8fba38a03b7b9d09ba6b951421e"}, + {file = "numpy-2.4.4-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:8b3b60bb7cba2c8c81837661c488637eee696f59a877788a396d33150c35d842"}, + {file = "numpy-2.4.4-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:e4a010c27ff6f210ff4c6ef34394cd61470d01014439b192ec22552ee867f2a8"}, + {file = "numpy-2.4.4-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f9e75681b59ddaa5e659898085ae0eaea229d054f2ac0c7e563a62205a700121"}, + {file = "numpy-2.4.4-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:81f4a14bee47aec54f883e0cad2d73986640c1590eb9bfaaba7ad17394481e6e"}, + {file = "numpy-2.4.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:62d6b0f03b694173f9fcb1fb317f7222fd0b0b103e784c6549f5e53a27718c44"}, + {file = "numpy-2.4.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fbc356aae7adf9e6336d336b9c8111d390a05df88f1805573ebb0807bd06fd1d"}, + {file = "numpy-2.4.4-cp312-cp312-win32.whl", hash = "sha256:0d35aea54ad1d420c812bfa0385c71cd7cc5bcf7c65fed95fc2cd02fe8c79827"}, + {file = "numpy-2.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:b5f0362dc928a6ecd9db58868fca5e48485205e3855957bdedea308f8672ea4a"}, + {file = "numpy-2.4.4-cp312-cp312-win_arm64.whl", hash = "sha256:846300f379b5b12cc769334464656bc882e0735d27d9726568bc932fdc49d5ec"}, + {file = "numpy-2.4.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:08f2e31ed5e6f04b118e49821397f12767934cfdd12a1ce86a058f91e004ee50"}, + {file = "numpy-2.4.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e823b8b6edc81e747526f70f71a9c0a07ac4e7ad13020aa736bb7c9d67196115"}, + {file = "numpy-2.4.4-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:4a19d9dba1a76618dd86b164d608566f393f8ec6ac7c44f0cc879011c45e65af"}, + {file = "numpy-2.4.4-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:d2a8490669bfe99a233298348acc2d824d496dee0e66e31b66a6022c2ad74a5c"}, + {file = "numpy-2.4.4-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:45dbed2ab436a9e826e302fcdcbe9133f9b0006e5af7168afb8963a6520da103"}, + {file = "numpy-2.4.4-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c901b15172510173f5cb310eae652908340f8dede90fff9e3bf6c0d8dfd92f83"}, + {file = "numpy-2.4.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:99d838547ace2c4aace6c4f76e879ddfe02bb58a80c1549928477862b7a6d6ed"}, + {file = "numpy-2.4.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0aec54fd785890ecca25a6003fd9a5aed47ad607bbac5cd64f836ad8666f4959"}, + {file = "numpy-2.4.4-cp313-cp313-win32.whl", hash = "sha256:07077278157d02f65c43b1b26a3886bce886f95d20aabd11f87932750dfb14ed"}, + {file = "numpy-2.4.4-cp313-cp313-win_amd64.whl", hash = "sha256:5c70f1cc1c4efbe316a572e2d8b9b9cc44e89b95f79ca3331553fbb63716e2bf"}, + {file = "numpy-2.4.4-cp313-cp313-win_arm64.whl", hash = "sha256:ef4059d6e5152fa1a39f888e344c73fdc926e1b2dd58c771d67b0acfbf2aa67d"}, + {file = "numpy-2.4.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:4bbc7f303d125971f60ec0aaad5e12c62d0d2c925f0ab1273debd0e4ba37aba5"}, + {file = "numpy-2.4.4-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:4d6d57903571f86180eb98f8f0c839fa9ebbfb031356d87f1361be91e433f5b7"}, + {file = "numpy-2.4.4-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:4636de7fd195197b7535f231b5de9e4b36d2c440b6e566d2e4e4746e6af0ca93"}, + {file = "numpy-2.4.4-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ad2e2ef14e0b04e544ea2fa0a36463f847f113d314aa02e5b402fdf910ef309e"}, + {file = "numpy-2.4.4-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5a285b3b96f951841799528cd1f4f01cd70e7e0204b4abebac9463eecfcf2a40"}, + {file = "numpy-2.4.4-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:f8474c4241bc18b750be2abea9d7a9ec84f46ef861dbacf86a4f6e043401f79e"}, + {file = "numpy-2.4.4-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4e874c976154687c1f71715b034739b45c7711bec81db01914770373d125e392"}, + {file = "numpy-2.4.4-cp313-cp313t-win32.whl", hash = "sha256:9c585a1790d5436a5374bac930dad6ed244c046ed91b2b2a3634eb2971d21008"}, + {file = "numpy-2.4.4-cp313-cp313t-win_amd64.whl", hash = "sha256:93e15038125dc1e5345d9b5b68aa7f996ec33b98118d18c6ca0d0b7d6198b7e8"}, + {file = "numpy-2.4.4-cp313-cp313t-win_arm64.whl", hash = "sha256:0dfd3f9d3adbe2920b68b5cd3d51444e13a10792ec7154cd0a2f6e74d4ab3233"}, + {file = "numpy-2.4.4-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:f169b9a863d34f5d11b8698ead99febeaa17a13ca044961aa8e2662a6c7766a0"}, + {file = "numpy-2.4.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:2483e4584a1cb3092da4470b38866634bafb223cbcd551ee047633fd2584599a"}, + {file = "numpy-2.4.4-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:2d19e6e2095506d1736b7d80595e0f252d76b89f5e715c35e06e937679ea7d7a"}, + {file = "numpy-2.4.4-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:6a246d5914aa1c820c9443ddcee9c02bec3e203b0c080349533fae17727dfd1b"}, + {file = "numpy-2.4.4-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:989824e9faf85f96ec9c7761cd8d29c531ad857bfa1daa930cba85baaecf1a9a"}, + {file = "numpy-2.4.4-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:27a8d92cd10f1382a67d7cf4db7ce18341b66438bdd9f691d7b0e48d104c2a9d"}, + {file = "numpy-2.4.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e44319a2953c738205bf3354537979eaa3998ed673395b964c1176083dd46252"}, + {file = "numpy-2.4.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:e892aff75639bbef0d2a2cfd55535510df26ff92f63c92cd84ef8d4ba5a5557f"}, + {file = "numpy-2.4.4-cp314-cp314-win32.whl", hash = "sha256:1378871da56ca8943c2ba674530924bb8ca40cd228358a3b5f302ad60cf875fc"}, + {file = "numpy-2.4.4-cp314-cp314-win_amd64.whl", hash = "sha256:715d1c092715954784bc79e1174fc2a90093dc4dc84ea15eb14dad8abdcdeb74"}, + {file = "numpy-2.4.4-cp314-cp314-win_arm64.whl", hash = "sha256:2c194dd721e54ecad9ad387c1d35e63dce5c4450c6dc7dd5611283dda239aabb"}, + {file = "numpy-2.4.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2aa0613a5177c264ff5921051a5719d20095ea586ca88cc802c5c218d1c67d3e"}, + {file = "numpy-2.4.4-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:42c16925aa5a02362f986765f9ebabf20de75cdefdca827d14315c568dcab113"}, + {file = "numpy-2.4.4-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:874f200b2a981c647340f841730fc3a2b54c9d940566a3c4149099591e2c4c3d"}, + {file = "numpy-2.4.4-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c9b39d38a9bd2ae1becd7eac1303d031c5c110ad31f2b319c6e7d98b135c934d"}, + {file = "numpy-2.4.4-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b268594bccac7d7cf5844c7732e3f20c50921d94e36d7ec9b79e9857694b1b2f"}, + {file = "numpy-2.4.4-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:ac6b31e35612a26483e20750126d30d0941f949426974cace8e6b5c58a3657b0"}, + {file = "numpy-2.4.4-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:8e3ed142f2728df44263aaf5fb1f5b0b99f4070c553a0d7f033be65338329150"}, + {file = "numpy-2.4.4-cp314-cp314t-win32.whl", hash = "sha256:dddbbd259598d7240b18c9d87c56a9d2fb3b02fe266f49a7c101532e78c1d871"}, + {file = "numpy-2.4.4-cp314-cp314t-win_amd64.whl", hash = "sha256:a7164afb23be6e37ad90b2f10426149fd75aee07ca55653d2aa41e66c4ef697e"}, + {file = "numpy-2.4.4-cp314-cp314t-win_arm64.whl", hash = "sha256:ba203255017337d39f89bdd58417f03c4426f12beed0440cfd933cb15f8669c7"}, + {file = "numpy-2.4.4-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:58c8b5929fcb8287cbd6f0a3fae19c6e03a5c48402ae792962ac465224a629a4"}, + {file = "numpy-2.4.4-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:eea7ac5d2dce4189771cedb559c738a71512768210dc4e4753b107a2048b3d0e"}, + {file = "numpy-2.4.4-pp311-pypy311_pp73-macosx_14_0_arm64.whl", hash = "sha256:51fc224f7ca4d92656d5a5eb315f12eb5fe2c97a66249aa7b5f562528a3be38c"}, + {file = "numpy-2.4.4-pp311-pypy311_pp73-macosx_14_0_x86_64.whl", hash = "sha256:28a650663f7314afc3e6ec620f44f333c386aad9f6fc472030865dc0ebb26ee3"}, + {file = "numpy-2.4.4-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:19710a9ca9992d7174e9c52f643d4272dcd1558c5f7af7f6f8190f633bd651a7"}, + {file = "numpy-2.4.4-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9b2aec6af35c113b05695ebb5749a787acd63cafc83086a05771d1e1cd1e555f"}, + {file = "numpy-2.4.4-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:f2cf083b324a467e1ab358c105f6cad5ea950f50524668a80c486ff1db24e119"}, + {file = "numpy-2.4.4.tar.gz", hash = "sha256:2d390634c5182175533585cc89f3608a4682ccb173cc9bb940b2881c8d6f8fa0"}, +] + +[[package]] +name = "opt-einsum" +version = "3.4.0" +description = "Path optimization of einsum functions." optional = false -python-versions = ">=3.11" +python-versions = ">=3.8" groups = ["main"] -markers = "python_version >= \"3.11\"" -files = [ - {file = "numpy-2.3.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0ffc4f5caba7dfcbe944ed674b7eef683c7e94874046454bb79ed7ee0236f59d"}, - {file = "numpy-2.3.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e7e946c7170858a0295f79a60214424caac2ffdb0063d4d79cb681f9aa0aa569"}, - {file = "numpy-2.3.3-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:cd4260f64bc794c3390a63bf0728220dd1a68170c169088a1e0dfa2fde1be12f"}, - {file = "numpy-2.3.3-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:f0ddb4b96a87b6728df9362135e764eac3cfa674499943ebc44ce96c478ab125"}, - {file = "numpy-2.3.3-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:afd07d377f478344ec6ca2b8d4ca08ae8bd44706763d1efb56397de606393f48"}, - {file = "numpy-2.3.3-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bc92a5dedcc53857249ca51ef29f5e5f2f8c513e22cfb90faeb20343b8c6f7a6"}, - {file = "numpy-2.3.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7af05ed4dc19f308e1d9fc759f36f21921eb7bbfc82843eeec6b2a2863a0aefa"}, - {file = "numpy-2.3.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:433bf137e338677cebdd5beac0199ac84712ad9d630b74eceeb759eaa45ddf30"}, - {file = "numpy-2.3.3-cp311-cp311-win32.whl", hash = "sha256:eb63d443d7b4ffd1e873f8155260d7f58e7e4b095961b01c91062935c2491e57"}, - {file = "numpy-2.3.3-cp311-cp311-win_amd64.whl", hash = "sha256:ec9d249840f6a565f58d8f913bccac2444235025bbb13e9a4681783572ee3caa"}, - {file = "numpy-2.3.3-cp311-cp311-win_arm64.whl", hash = "sha256:74c2a948d02f88c11a3c075d9733f1ae67d97c6bdb97f2bb542f980458b257e7"}, - {file = "numpy-2.3.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:cfdd09f9c84a1a934cde1eec2267f0a43a7cd44b2cca4ff95b7c0d14d144b0bf"}, - {file = "numpy-2.3.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cb32e3cf0f762aee47ad1ddc6672988f7f27045b0783c887190545baba73aa25"}, - {file = "numpy-2.3.3-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:396b254daeb0a57b1fe0ecb5e3cff6fa79a380fa97c8f7781a6d08cd429418fe"}, - {file = "numpy-2.3.3-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:067e3d7159a5d8f8a0b46ee11148fc35ca9b21f61e3c49fbd0a027450e65a33b"}, - {file = "numpy-2.3.3-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1c02d0629d25d426585fb2e45a66154081b9fa677bc92a881ff1d216bc9919a8"}, - {file = "numpy-2.3.3-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d9192da52b9745f7f0766531dcfa978b7763916f158bb63bdb8a1eca0068ab20"}, - {file = "numpy-2.3.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:cd7de500a5b66319db419dc3c345244404a164beae0d0937283b907d8152e6ea"}, - {file = "numpy-2.3.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:93d4962d8f82af58f0b2eb85daaf1b3ca23fe0a85d0be8f1f2b7bb46034e56d7"}, - {file = "numpy-2.3.3-cp312-cp312-win32.whl", hash = "sha256:5534ed6b92f9b7dca6c0a19d6df12d41c68b991cef051d108f6dbff3babc4ebf"}, - {file = "numpy-2.3.3-cp312-cp312-win_amd64.whl", hash = "sha256:497d7cad08e7092dba36e3d296fe4c97708c93daf26643a1ae4b03f6294d30eb"}, - {file = "numpy-2.3.3-cp312-cp312-win_arm64.whl", hash = "sha256:ca0309a18d4dfea6fc6262a66d06c26cfe4640c3926ceec90e57791a82b6eee5"}, - {file = "numpy-2.3.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f5415fb78995644253370985342cd03572ef8620b934da27d77377a2285955bf"}, - {file = "numpy-2.3.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d00de139a3324e26ed5b95870ce63be7ec7352171bc69a4cf1f157a48e3eb6b7"}, - {file = "numpy-2.3.3-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:9dc13c6a5829610cc07422bc74d3ac083bd8323f14e2827d992f9e52e22cd6a6"}, - {file = "numpy-2.3.3-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:d79715d95f1894771eb4e60fb23f065663b2298f7d22945d66877aadf33d00c7"}, - {file = "numpy-2.3.3-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:952cfd0748514ea7c3afc729a0fc639e61655ce4c55ab9acfab14bda4f402b4c"}, - {file = "numpy-2.3.3-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5b83648633d46f77039c29078751f80da65aa64d5622a3cd62aaef9d835b6c93"}, - {file = "numpy-2.3.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b001bae8cea1c7dfdb2ae2b017ed0a6f2102d7a70059df1e338e307a4c78a8ae"}, - {file = "numpy-2.3.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8e9aced64054739037d42fb84c54dd38b81ee238816c948c8f3ed134665dcd86"}, - {file = "numpy-2.3.3-cp313-cp313-win32.whl", hash = "sha256:9591e1221db3f37751e6442850429b3aabf7026d3b05542d102944ca7f00c8a8"}, - {file = "numpy-2.3.3-cp313-cp313-win_amd64.whl", hash = "sha256:f0dadeb302887f07431910f67a14d57209ed91130be0adea2f9793f1a4f817cf"}, - {file = "numpy-2.3.3-cp313-cp313-win_arm64.whl", hash = "sha256:3c7cf302ac6e0b76a64c4aecf1a09e51abd9b01fc7feee80f6c43e3ab1b1dbc5"}, - {file = "numpy-2.3.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:eda59e44957d272846bb407aad19f89dc6f58fecf3504bd144f4c5cf81a7eacc"}, - {file = "numpy-2.3.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:823d04112bc85ef5c4fda73ba24e6096c8f869931405a80aa8b0e604510a26bc"}, - {file = "numpy-2.3.3-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:40051003e03db4041aa325da2a0971ba41cf65714e65d296397cc0e32de6018b"}, - {file = "numpy-2.3.3-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:6ee9086235dd6ab7ae75aba5662f582a81ced49f0f1c6de4260a78d8f2d91a19"}, - {file = "numpy-2.3.3-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:94fcaa68757c3e2e668ddadeaa86ab05499a70725811e582b6a9858dd472fb30"}, - {file = "numpy-2.3.3-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:da1a74b90e7483d6ce5244053399a614b1d6b7bc30a60d2f570e5071f8959d3e"}, - {file = "numpy-2.3.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:2990adf06d1ecee3b3dcbb4977dfab6e9f09807598d647f04d385d29e7a3c3d3"}, - {file = "numpy-2.3.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:ed635ff692483b8e3f0fcaa8e7eb8a75ee71aa6d975388224f70821421800cea"}, - {file = "numpy-2.3.3-cp313-cp313t-win32.whl", hash = "sha256:a333b4ed33d8dc2b373cc955ca57babc00cd6f9009991d9edc5ddbc1bac36bcd"}, - {file = "numpy-2.3.3-cp313-cp313t-win_amd64.whl", hash = "sha256:4384a169c4d8f97195980815d6fcad04933a7e1ab3b530921c3fef7a1c63426d"}, - {file = "numpy-2.3.3-cp313-cp313t-win_arm64.whl", hash = "sha256:75370986cc0bc66f4ce5110ad35aae6d182cc4ce6433c40ad151f53690130bf1"}, - {file = "numpy-2.3.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:cd052f1fa6a78dee696b58a914b7229ecfa41f0a6d96dc663c1220a55e137593"}, - {file = "numpy-2.3.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:414a97499480067d305fcac9716c29cf4d0d76db6ebf0bf3cbce666677f12652"}, - {file = "numpy-2.3.3-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:50a5fe69f135f88a2be9b6ca0481a68a136f6febe1916e4920e12f1a34e708a7"}, - {file = "numpy-2.3.3-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:b912f2ed2b67a129e6a601e9d93d4fa37bef67e54cac442a2f588a54afe5c67a"}, - {file = "numpy-2.3.3-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9e318ee0596d76d4cb3d78535dc005fa60e5ea348cd131a51e99d0bdbe0b54fe"}, - {file = "numpy-2.3.3-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ce020080e4a52426202bdb6f7691c65bb55e49f261f31a8f506c9f6bc7450421"}, - {file = "numpy-2.3.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e6687dc183aa55dae4a705b35f9c0f8cb178bcaa2f029b241ac5356221d5c021"}, - {file = "numpy-2.3.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d8f3b1080782469fdc1718c4ed1d22549b5fb12af0d57d35e992158a772a37cf"}, - {file = "numpy-2.3.3-cp314-cp314-win32.whl", hash = "sha256:cb248499b0bc3be66ebd6578b83e5acacf1d6cb2a77f2248ce0e40fbec5a76d0"}, - {file = "numpy-2.3.3-cp314-cp314-win_amd64.whl", hash = "sha256:691808c2b26b0f002a032c73255d0bd89751425f379f7bcd22d140db593a96e8"}, - {file = "numpy-2.3.3-cp314-cp314-win_arm64.whl", hash = "sha256:9ad12e976ca7b10f1774b03615a2a4bab8addce37ecc77394d8e986927dc0dfe"}, - {file = "numpy-2.3.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9cc48e09feb11e1db00b320e9d30a4151f7369afb96bd0e48d942d09da3a0d00"}, - {file = "numpy-2.3.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:901bf6123879b7f251d3631967fd574690734236075082078e0571977c6a8e6a"}, - {file = "numpy-2.3.3-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:7f025652034199c301049296b59fa7d52c7e625017cae4c75d8662e377bf487d"}, - {file = "numpy-2.3.3-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:533ca5f6d325c80b6007d4d7fb1984c303553534191024ec6a524a4c92a5935a"}, - {file = "numpy-2.3.3-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0edd58682a399824633b66885d699d7de982800053acf20be1eaa46d92009c54"}, - {file = "numpy-2.3.3-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:367ad5d8fbec5d9296d18478804a530f1191e24ab4d75ab408346ae88045d25e"}, - {file = "numpy-2.3.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8f6ac61a217437946a1fa48d24c47c91a0c4f725237871117dea264982128097"}, - {file = "numpy-2.3.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:179a42101b845a816d464b6fe9a845dfaf308fdfc7925387195570789bb2c970"}, - {file = "numpy-2.3.3-cp314-cp314t-win32.whl", hash = "sha256:1250c5d3d2562ec4174bce2e3a1523041595f9b651065e4a4473f5f48a6bc8a5"}, - {file = "numpy-2.3.3-cp314-cp314t-win_amd64.whl", hash = "sha256:b37a0b2e5935409daebe82c1e42274d30d9dd355852529eab91dab8dcca7419f"}, - {file = "numpy-2.3.3-cp314-cp314t-win_arm64.whl", hash = "sha256:78c9f6560dc7e6b3990e32df7ea1a50bbd0e2a111e05209963f5ddcab7073b0b"}, - {file = "numpy-2.3.3-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:1e02c7159791cd481e1e6d5ddd766b62a4d5acf8df4d4d1afe35ee9c5c33a41e"}, - {file = "numpy-2.3.3-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:dca2d0fc80b3893ae72197b39f69d55a3cd8b17ea1b50aa4c62de82419936150"}, - {file = "numpy-2.3.3-pp311-pypy311_pp73-macosx_14_0_arm64.whl", hash = "sha256:99683cbe0658f8271b333a1b1b4bb3173750ad59c0c61f5bbdc5b318918fffe3"}, - {file = "numpy-2.3.3-pp311-pypy311_pp73-macosx_14_0_x86_64.whl", hash = "sha256:d9d537a39cc9de668e5cd0e25affb17aec17b577c6b3ae8a3d866b479fbe88d0"}, - {file = "numpy-2.3.3-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8596ba2f8af5f93b01d97563832686d20206d303024777f6dfc2e7c7c3f1850e"}, - {file = "numpy-2.3.3-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e1ec5615b05369925bd1125f27df33f3b6c8bc10d788d5999ecd8769a1fa04db"}, - {file = "numpy-2.3.3-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:2e267c7da5bf7309670523896df97f93f6e469fb931161f483cd6882b3b1a5dc"}, - {file = "numpy-2.3.3.tar.gz", hash = "sha256:ddc7c39727ba62b80dfdbedf400d1c10ddfa8eefbd7ec8dcb118be8b56d31029"}, +files = [ + {file = "opt_einsum-3.4.0-py3-none-any.whl", hash = "sha256:69bb92469f86a1565195ece4ac0323943e83477171b91d24c35afe028a90d7cd"}, + {file = "opt_einsum-3.4.0.tar.gz", hash = "sha256:96ca72f1b886d148241348783498194c577fa30a8faac108586b14f1ba4473ac"}, ] [[package]] name = "packaging" -version = "25.0" +version = "26.2" description = "Core utilities for Python packages" optional = false python-versions = ">=3.8" groups = ["main", "dev"] files = [ - {file = "packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484"}, - {file = "packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f"}, + {file = "packaging-26.2-py3-none-any.whl", hash = "sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e"}, + {file = "packaging-26.2.tar.gz", hash = "sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661"}, ] [[package]] name = "pandas" -version = "2.3.3" +version = "3.0.2" description = "Powerful data structures for data analysis, time series, and statistics" optional = true -python-versions = ">=3.9" +python-versions = ">=3.11" groups = ["main"] markers = "extra == \"docs\"" files = [ - {file = "pandas-2.3.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:376c6446ae31770764215a6c937f72d917f214b43560603cd60da6408f183b6c"}, - {file = "pandas-2.3.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e19d192383eab2f4ceb30b412b22ea30690c9e618f78870357ae1d682912015a"}, - {file = "pandas-2.3.3-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5caf26f64126b6c7aec964f74266f435afef1c1b13da3b0636c7518a1fa3e2b1"}, - {file = "pandas-2.3.3-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dd7478f1463441ae4ca7308a70e90b33470fa593429f9d4c578dd00d1fa78838"}, - {file = "pandas-2.3.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4793891684806ae50d1288c9bae9330293ab4e083ccd1c5e383c34549c6e4250"}, - {file = "pandas-2.3.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:28083c648d9a99a5dd035ec125d42439c6c1c525098c58af0fc38dd1a7a1b3d4"}, - {file = "pandas-2.3.3-cp310-cp310-win_amd64.whl", hash = "sha256:503cf027cf9940d2ceaa1a93cfb5f8c8c7e6e90720a2850378f0b3f3b1e06826"}, - {file = "pandas-2.3.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:602b8615ebcc4a0c1751e71840428ddebeb142ec02c786e8ad6b1ce3c8dec523"}, - {file = "pandas-2.3.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8fe25fc7b623b0ef6b5009149627e34d2a4657e880948ec3c840e9402e5c1b45"}, - {file = "pandas-2.3.3-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b468d3dad6ff947df92dcb32ede5b7bd41a9b3cceef0a30ed925f6d01fb8fa66"}, - {file = "pandas-2.3.3-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b98560e98cb334799c0b07ca7967ac361a47326e9b4e5a7dfb5ab2b1c9d35a1b"}, - {file = "pandas-2.3.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37b5848ba49824e5c30bedb9c830ab9b7751fd049bc7914533e01c65f79791"}, - {file = "pandas-2.3.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:db4301b2d1f926ae677a751eb2bd0e8c5f5319c9cb3f88b0becbbb0b07b34151"}, - {file = "pandas-2.3.3-cp311-cp311-win_amd64.whl", hash = "sha256:f086f6fe114e19d92014a1966f43a3e62285109afe874f067f5abbdcbb10e59c"}, - {file = "pandas-2.3.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d21f6d74eb1725c2efaa71a2bfc661a0689579b58e9c0ca58a739ff0b002b53"}, - {file = "pandas-2.3.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3fd2f887589c7aa868e02632612ba39acb0b8948faf5cc58f0850e165bd46f35"}, - {file = "pandas-2.3.3-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ecaf1e12bdc03c86ad4a7ea848d66c685cb6851d807a26aa245ca3d2017a1908"}, - {file = "pandas-2.3.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b3d11d2fda7eb164ef27ffc14b4fcab16a80e1ce67e9f57e19ec0afaf715ba89"}, - {file = "pandas-2.3.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a68e15f780eddf2b07d242e17a04aa187a7ee12b40b930bfdd78070556550e98"}, - {file = "pandas-2.3.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:371a4ab48e950033bcf52b6527eccb564f52dc826c02afd9a1bc0ab731bba084"}, - {file = "pandas-2.3.3-cp312-cp312-win_amd64.whl", hash = "sha256:a16dcec078a01eeef8ee61bf64074b4e524a2a3f4b3be9326420cabe59c4778b"}, - {file = "pandas-2.3.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:56851a737e3470de7fa88e6131f41281ed440d29a9268dcbf0002da5ac366713"}, - {file = "pandas-2.3.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bdcd9d1167f4885211e401b3036c0c8d9e274eee67ea8d0758a256d60704cfe8"}, - {file = "pandas-2.3.3-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e32e7cc9af0f1cc15548288a51a3b681cc2a219faa838e995f7dc53dbab1062d"}, - {file = "pandas-2.3.3-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:318d77e0e42a628c04dc56bcef4b40de67918f7041c2b061af1da41dcff670ac"}, - {file = "pandas-2.3.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4e0a175408804d566144e170d0476b15d78458795bb18f1304fb94160cabf40c"}, - {file = "pandas-2.3.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:93c2d9ab0fc11822b5eece72ec9587e172f63cff87c00b062f6e37448ced4493"}, - {file = "pandas-2.3.3-cp313-cp313-win_amd64.whl", hash = "sha256:f8bfc0e12dc78f777f323f55c58649591b2cd0c43534e8355c51d3fede5f4dee"}, - {file = "pandas-2.3.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:75ea25f9529fdec2d2e93a42c523962261e567d250b0013b16210e1d40d7c2e5"}, - {file = "pandas-2.3.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:74ecdf1d301e812db96a465a525952f4dde225fdb6d8e5a521d47e1f42041e21"}, - {file = "pandas-2.3.3-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6435cb949cb34ec11cc9860246ccb2fdc9ecd742c12d3304989017d53f039a78"}, - {file = "pandas-2.3.3-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:900f47d8f20860de523a1ac881c4c36d65efcb2eb850e6948140fa781736e110"}, - {file = "pandas-2.3.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a45c765238e2ed7d7c608fc5bc4a6f88b642f2f01e70c0c23d2224dd21829d86"}, - {file = "pandas-2.3.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c4fc4c21971a1a9f4bdb4c73978c7f7256caa3e62b323f70d6cb80db583350bc"}, - {file = "pandas-2.3.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:ee15f284898e7b246df8087fc82b87b01686f98ee67d85a17b7ab44143a3a9a0"}, - {file = "pandas-2.3.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1611aedd912e1ff81ff41c745822980c49ce4a7907537be8692c8dbc31924593"}, - {file = "pandas-2.3.3-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6d2cefc361461662ac48810cb14365a365ce864afe85ef1f447ff5a1e99ea81c"}, - {file = "pandas-2.3.3-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ee67acbbf05014ea6c763beb097e03cd629961c8a632075eeb34247120abcb4b"}, - {file = "pandas-2.3.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c46467899aaa4da076d5abc11084634e2d197e9460643dd455ac3db5856b24d6"}, - {file = "pandas-2.3.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:6253c72c6a1d990a410bc7de641d34053364ef8bcd3126f7e7450125887dffe3"}, - {file = "pandas-2.3.3-cp314-cp314-win_amd64.whl", hash = "sha256:1b07204a219b3b7350abaae088f451860223a52cfb8a6c53358e7948735158e5"}, - {file = "pandas-2.3.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:2462b1a365b6109d275250baaae7b760fd25c726aaca0054649286bcfbb3e8ec"}, - {file = "pandas-2.3.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:0242fe9a49aa8b4d78a4fa03acb397a58833ef6199e9aa40a95f027bb3a1b6e7"}, - {file = "pandas-2.3.3-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a21d830e78df0a515db2b3d2f5570610f5e6bd2e27749770e8bb7b524b89b450"}, - {file = "pandas-2.3.3-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2e3ebdb170b5ef78f19bfb71b0dc5dc58775032361fa188e814959b74d726dd5"}, - {file = "pandas-2.3.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:d051c0e065b94b7a3cea50eb1ec32e912cd96dba41647eb24104b6c6c14c5788"}, - {file = "pandas-2.3.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:3869faf4bd07b3b66a9f462417d0ca3a9df29a9f6abd5d0d0dbab15dac7abe87"}, - {file = "pandas-2.3.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c503ba5216814e295f40711470446bc3fd00f0faea8a086cbc688808e26f92a2"}, - {file = "pandas-2.3.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a637c5cdfa04b6d6e2ecedcb81fc52ffb0fd78ce2ebccc9ea964df9f658de8c8"}, - {file = "pandas-2.3.3-cp39-cp39-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:854d00d556406bffe66a4c0802f334c9ad5a96b4f1f868adf036a21b11ef13ff"}, - {file = "pandas-2.3.3-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bf1f8a81d04ca90e32a0aceb819d34dbd378a98bf923b6398b9a3ec0bf44de29"}, - {file = "pandas-2.3.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:23ebd657a4d38268c7dfbdf089fbc31ea709d82e4923c5ffd4fbd5747133ce73"}, - {file = "pandas-2.3.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:5554c929ccc317d41a5e3d1234f3be588248e61f08a74dd17c9eabb535777dc9"}, - {file = "pandas-2.3.3-cp39-cp39-win_amd64.whl", hash = "sha256:d3e28b3e83862ccf4d85ff19cf8c20b2ae7e503881711ff2d534dc8f761131aa"}, - {file = "pandas-2.3.3.tar.gz", hash = "sha256:e05e1af93b977f7eafa636d043f9f94c7ee3ac81af99c13508215942e64c993b"}, + {file = "pandas-3.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a727a73cbdba2f7458dc82449e2315899d5140b449015d822f515749a46cbbe0"}, + {file = "pandas-3.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dbbd4aa20ca51e63b53bbde6a0fa4254b1aaabb74d2f542df7a7959feb1d760c"}, + {file = "pandas-3.0.2-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:339dda302bd8369dedeae979cb750e484d549b563c3f54f3922cb8ff4978c5eb"}, + {file = "pandas-3.0.2-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:61c2fd96d72b983a9891b2598f286befd4ad262161a609c92dc1652544b46b76"}, + {file = "pandas-3.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c934008c733b8bbea273ea308b73b3156f0181e5b72960790b09c18a2794fe1e"}, + {file = "pandas-3.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:60a80bb4feacbef5e1447a3f82c33209c8b7e07f28d805cfd1fb951e5cb443aa"}, + {file = "pandas-3.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:ed72cb3f45190874eb579c64fa92d9df74e98fd63e2be7f62bce5ace0ade61df"}, + {file = "pandas-3.0.2-cp311-cp311-win_arm64.whl", hash = "sha256:f12b1a9e332c01e09510586f8ca9b108fd631fd656af82e452d7315ef6df5f9f"}, + {file = "pandas-3.0.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:232a70ebb568c0c4d2db4584f338c1577d81e3af63292208d615907b698a0f18"}, + {file = "pandas-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:970762605cff1ca0d3f71ed4f3a769ea8f85fc8e6348f6e110b8fea7e6eb5a14"}, + {file = "pandas-3.0.2-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:aff4e6f4d722e0652707d7bcb190c445fe58428500c6d16005b02401764b1b3d"}, + {file = "pandas-3.0.2-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ef8b27695c3d3dc78403c9a7d5e59a62d5464a7e1123b4e0042763f7104dc74f"}, + {file = "pandas-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f8d68083e49e16b84734eb1a4dcae4259a75c90fb6e2251ab9a00b61120c06ab"}, + {file = "pandas-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:32cc41f310ebd4a296d93515fcac312216adfedb1894e879303987b8f1e2b97d"}, + {file = "pandas-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:a4785e1d6547d8427c5208b748ae2efb64659a21bd82bf440d4262d02bfa02a4"}, + {file = "pandas-3.0.2-cp312-cp312-win_arm64.whl", hash = "sha256:08504503f7101300107ecdc8df73658e4347586db5cfdadabc1592e9d7e7a0fd"}, + {file = "pandas-3.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b5918ba197c951dec132b0c5929a00c0bf05d5942f590d3c10a807f6e15a57d3"}, + {file = "pandas-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d606a041c89c0a474a4702d532ab7e73a14fe35c8d427b972a625c8e46373668"}, + {file = "pandas-3.0.2-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:710246ba0616e86891b58ab95f2495143bb2bc83ab6b06747c74216f583a6ac9"}, + {file = "pandas-3.0.2-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5d3cfe227c725b1f3dff4278b43d8c784656a42a9325b63af6b1492a8232209e"}, + {file = "pandas-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c3b723df9087a9a9a840e263ebd9f88b64a12075d1bf2ea401a5a42f254f084d"}, + {file = "pandas-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a3096110bf9eac0070b7208465f2740e2d8a670d5cb6530b5bb884eca495fd39"}, + {file = "pandas-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:07a10f5c36512eead51bc578eb3354ad17578b22c013d89a796ab5eee90cd991"}, + {file = "pandas-3.0.2-cp313-cp313-win_arm64.whl", hash = "sha256:5fdbfa05931071aba28b408e59226186b01eb5e92bea2ab78b65863ca3228d84"}, + {file = "pandas-3.0.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:dbc20dea3b9e27d0e66d74c42b2d0c1bed9c2ffe92adea33633e3bedeb5ac235"}, + {file = "pandas-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:b75c347eff42497452116ce05ef461822d97ce5b9ff8df6edacb8076092c855d"}, + {file = "pandas-3.0.2-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d1478075142e83a5571782ad007fb201ed074bdeac7ebcc8890c71442e96adf7"}, + {file = "pandas-3.0.2-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5880314e69e763d4c8b27937090de570f1fb8d027059a7ada3f7f8e98bdcb677"}, + {file = "pandas-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:b5329e26898896f06035241a626d7c335daa479b9bbc82be7c2742d048e41172"}, + {file = "pandas-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:81526c4afd31971f8b62671442a4b2b51e0aa9acc3819c9f0f12a28b6fcf85f1"}, + {file = "pandas-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:7cadd7e9a44ec13b621aec60f9150e744cfc7a3dd32924a7e2f45edff31823b0"}, + {file = "pandas-3.0.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:db0dbfd2a6cdf3770aa60464d50333d8f3d9165b2f2671bcc299b72de5a6677b"}, + {file = "pandas-3.0.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0555c5882688a39317179ab4a0ed41d3ebc8812ab14c69364bbee8fb7a3f6288"}, + {file = "pandas-3.0.2-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:01f31a546acd5574ef77fe199bc90b55527c225c20ccda6601cf6b0fd5ed597c"}, + {file = "pandas-3.0.2-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:deeca1b5a931fdf0c2212c8a659ade6d3b1edc21f0914ce71ef24456ca7a6535"}, + {file = "pandas-3.0.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:0f48afd9bb13300ffb5a3316973324c787054ba6665cda0da3fbd67f451995db"}, + {file = "pandas-3.0.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:6c4d8458b97a35717b62469a4ea0e85abd5ed8687277f5ccfc67f8a5126f8c53"}, + {file = "pandas-3.0.2-cp314-cp314-win_amd64.whl", hash = "sha256:b35d14bb5d8285d9494fe93815a9e9307c0876e10f1e8e89ac5b88f728ec8dcf"}, + {file = "pandas-3.0.2-cp314-cp314-win_arm64.whl", hash = "sha256:63d141b56ef686f7f0d714cfb8de4e320475b86bf4b620aa0b7da89af8cbdbbb"}, + {file = "pandas-3.0.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:140f0cffb1fa2524e874dde5b477d9defe10780d8e9e220d259b2c0874c89d9d"}, + {file = "pandas-3.0.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ae37e833ff4fed0ba352f6bdd8b73ba3ab3256a85e54edfd1ab51ae40cca0af8"}, + {file = "pandas-3.0.2-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4d888a5c678a419a5bb41a2a93818e8ed9fd3172246555c0b37b7cc27027effd"}, + {file = "pandas-3.0.2-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b444dc64c079e84df91baa8bf613d58405645461cabca929d9178f2cd392398d"}, + {file = "pandas-3.0.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:4544c7a54920de8eeacaa1466a6b7268ecfbc9bc64ab4dbb89c6bbe94d5e0660"}, + {file = "pandas-3.0.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:734be7551687c00fbd760dc0522ed974f82ad230d4a10f54bf51b80d44a08702"}, + {file = "pandas-3.0.2-cp314-cp314t-win_amd64.whl", hash = "sha256:57a07209bebcbcf768d2d13c9b78b852f9a15978dac41b9e6421a81ad4cdd276"}, + {file = "pandas-3.0.2-cp314-cp314t-win_arm64.whl", hash = "sha256:5371b72c2d4d415d08765f32d689217a43227484e81b2305b52076e328f6f482"}, + {file = "pandas-3.0.2.tar.gz", hash = "sha256:f4753e73e34c8d83221ba58f232433fca2748be8b18dbca02d242ed153945043"}, ] [package.dependencies] -numpy = [ - {version = ">=1.22.4", markers = "python_version < \"3.11\""}, - {version = ">=1.23.2", markers = "python_version == \"3.11\""}, - {version = ">=1.26.0", markers = "python_version >= \"3.12\""}, -] +numpy = {version = ">=1.26.0", markers = "python_version < \"3.14\""} python-dateutil = ">=2.8.2" -pytz = ">=2020.1" -tzdata = ">=2022.7" +tzdata = {version = "*", markers = "sys_platform == \"win32\" or sys_platform == \"emscripten\""} [package.extras] -all = ["PyQt5 (>=5.15.9)", "SQLAlchemy (>=2.0.0)", "adbc-driver-postgresql (>=0.8.0)", "adbc-driver-sqlite (>=0.8.0)", "beautifulsoup4 (>=4.11.2)", "bottleneck (>=1.3.6)", "dataframe-api-compat (>=0.1.7)", "fastparquet (>=2022.12.0)", "fsspec (>=2022.11.0)", "gcsfs (>=2022.11.0)", "html5lib (>=1.1)", "hypothesis (>=6.46.1)", "jinja2 (>=3.1.2)", "lxml (>=4.9.2)", "matplotlib (>=3.6.3)", "numba (>=0.56.4)", "numexpr (>=2.8.4)", "odfpy (>=1.4.1)", "openpyxl (>=3.1.0)", "pandas-gbq (>=0.19.0)", "psycopg2 (>=2.9.6)", "pyarrow (>=10.0.1)", "pymysql (>=1.0.2)", "pyreadstat (>=1.2.0)", "pytest (>=7.3.2)", "pytest-xdist (>=2.2.0)", "python-calamine (>=0.1.7)", "pyxlsb (>=1.0.10)", "qtpy (>=2.3.0)", "s3fs (>=2022.11.0)", "scipy (>=1.10.0)", "tables (>=3.8.0)", "tabulate (>=0.9.0)", "xarray (>=2022.12.0)", "xlrd (>=2.0.1)", "xlsxwriter (>=3.0.5)", "zstandard (>=0.19.0)"] -aws = ["s3fs (>=2022.11.0)"] -clipboard = ["PyQt5 (>=5.15.9)", "qtpy (>=2.3.0)"] -compression = ["zstandard (>=0.19.0)"] -computation = ["scipy (>=1.10.0)", "xarray (>=2022.12.0)"] -consortium-standard = ["dataframe-api-compat (>=0.1.7)"] -excel = ["odfpy (>=1.4.1)", "openpyxl (>=3.1.0)", "python-calamine (>=0.1.7)", "pyxlsb (>=1.0.10)", "xlrd (>=2.0.1)", "xlsxwriter (>=3.0.5)"] -feather = ["pyarrow (>=10.0.1)"] -fss = ["fsspec (>=2022.11.0)"] -gcp = ["gcsfs (>=2022.11.0)", "pandas-gbq (>=0.19.0)"] -hdf5 = ["tables (>=3.8.0)"] -html = ["beautifulsoup4 (>=4.11.2)", "html5lib (>=1.1)", "lxml (>=4.9.2)"] -mysql = ["SQLAlchemy (>=2.0.0)", "pymysql (>=1.0.2)"] -output-formatting = ["jinja2 (>=3.1.2)", "tabulate (>=0.9.0)"] -parquet = ["pyarrow (>=10.0.1)"] -performance = ["bottleneck (>=1.3.6)", "numba (>=0.56.4)", "numexpr (>=2.8.4)"] -plot = ["matplotlib (>=3.6.3)"] -postgresql = ["SQLAlchemy (>=2.0.0)", "adbc-driver-postgresql (>=0.8.0)", "psycopg2 (>=2.9.6)"] -pyarrow = ["pyarrow (>=10.0.1)"] -spss = ["pyreadstat (>=1.2.0)"] -sql-other = ["SQLAlchemy (>=2.0.0)", "adbc-driver-postgresql (>=0.8.0)", "adbc-driver-sqlite (>=0.8.0)"] -test = ["hypothesis (>=6.46.1)", "pytest (>=7.3.2)", "pytest-xdist (>=2.2.0)"] -xml = ["lxml (>=4.9.2)"] +all = ["PyQt5 (>=5.15.9)", "SQLAlchemy (>=2.0.36)", "adbc-driver-postgresql (>=1.2.0)", "adbc-driver-sqlite (>=1.2.0)", "beautifulsoup4 (>=4.12.3)", "bottleneck (>=1.4.2)", "fastparquet (>=2024.11.0)", "fsspec (>=2024.10.0)", "gcsfs (>=2024.10.0)", "html5lib (>=1.1)", "hypothesis (>=6.116.0)", "jinja2 (>=3.1.5)", "lxml (>=5.3.0)", "matplotlib (>=3.9.3)", "numba (>=0.60.0)", "numexpr (>=2.10.2)", "odfpy (>=1.4.1)", "openpyxl (>=3.1.5)", "psycopg2 (>=2.9.10)", "pyarrow (>=13.0.0)", "pyiceberg (>=0.8.1)", "pymysql (>=1.1.1)", "pyreadstat (>=1.2.8)", "pytest (>=8.3.4)", "pytest-xdist (>=3.6.1)", "python-calamine (>=0.3.0)", "pytz (>=2024.2)", "pyxlsb (>=1.0.10)", "qtpy (>=2.4.2)", "s3fs (>=2024.10.0)", "scipy (>=1.14.1)", "tables (>=3.10.1)", "tabulate (>=0.9.0)", "xarray (>=2024.10.0)", "xlrd (>=2.0.1)", "xlsxwriter (>=3.2.0)", "zstandard (>=0.23.0)"] +aws = ["s3fs (>=2024.10.0)"] +clipboard = ["PyQt5 (>=5.15.9)", "qtpy (>=2.4.2)"] +compression = ["zstandard (>=0.23.0)"] +computation = ["scipy (>=1.14.1)", "xarray (>=2024.10.0)"] +excel = ["odfpy (>=1.4.1)", "openpyxl (>=3.1.5)", "python-calamine (>=0.3.0)", "pyxlsb (>=1.0.10)", "xlrd (>=2.0.1)", "xlsxwriter (>=3.2.0)"] +feather = ["pyarrow (>=13.0.0)"] +fss = ["fsspec (>=2024.10.0)"] +gcp = ["gcsfs (>=2024.10.0)"] +hdf5 = ["tables (>=3.10.1)"] +html = ["beautifulsoup4 (>=4.12.3)", "html5lib (>=1.1)", "lxml (>=5.3.0)"] +iceberg = ["pyiceberg (>=0.8.1)"] +mysql = ["SQLAlchemy (>=2.0.36)", "pymysql (>=1.1.1)"] +output-formatting = ["jinja2 (>=3.1.5)", "tabulate (>=0.9.0)"] +parquet = ["pyarrow (>=13.0.0)"] +performance = ["bottleneck (>=1.4.2)", "numba (>=0.60.0)", "numexpr (>=2.10.2)"] +plot = ["matplotlib (>=3.9.3)"] +postgresql = ["SQLAlchemy (>=2.0.36)", "adbc-driver-postgresql (>=1.2.0)", "psycopg2 (>=2.9.10)"] +pyarrow = ["pyarrow (>=13.0.0)"] +spss = ["pyreadstat (>=1.2.8)"] +sql-other = ["SQLAlchemy (>=2.0.36)", "adbc-driver-postgresql (>=1.2.0)", "adbc-driver-sqlite (>=1.2.0)"] +test = ["hypothesis (>=6.116.0)", "pytest (>=8.3.4)", "pytest-xdist (>=3.6.1)"] +timezone = ["pytz (>=2024.2)"] +xml = ["lxml (>=5.3.0)"] [[package]] name = "pandoc" @@ -2520,33 +2329,38 @@ files = [ [[package]] name = "parso" -version = "0.8.5" +version = "0.8.6" description = "A Python Parser" optional = true python-versions = ">=3.6" groups = ["main"] markers = "extra == \"latex\"" files = [ - {file = "parso-0.8.5-py2.py3-none-any.whl", hash = "sha256:646204b5ee239c396d040b90f9e272e9a8017c630092bf59980beb62fd033887"}, - {file = "parso-0.8.5.tar.gz", hash = "sha256:034d7354a9a018bdce352f48b2a8a450f05e9d6ee85db84764e9b6bd96dafe5a"}, + {file = "parso-0.8.6-py2.py3-none-any.whl", hash = "sha256:2c549f800b70a5c4952197248825584cb00f033b29c692671d3bf08bf380baff"}, + {file = "parso-0.8.6.tar.gz", hash = "sha256:2b9a0332696df97d454fa67b81618fd69c35a7b90327cbe6ba5c92d2c68a7bfd"}, ] [package.extras] -qa = ["flake8 (==5.0.4)", "mypy (==0.971)", "types-setuptools (==67.2.0.1)"] +qa = ["flake8 (==5.0.4)", "types-setuptools (==67.2.0.1)", "zuban (==0.5.1)"] testing = ["docopt", "pytest"] [[package]] name = "pathspec" -version = "0.12.1" +version = "1.1.1" description = "Utility library for gitignore style pattern matching of file paths." optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["dev"] files = [ - {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, - {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, + {file = "pathspec-1.1.1-py3-none-any.whl", hash = "sha256:a00ce642f577bf7f473932318056212bc4f8bfdf53128c78bbd5af0b9b20b189"}, + {file = "pathspec-1.1.1.tar.gz", hash = "sha256:17db5ecd524104a120e173814c90367a96a98d07c45b2e10c2f3919fff91bf5a"}, ] +[package.extras] +hyperscan = ["hyperscan (>=0.7)"] +optional = ["typing-extensions (>=4)"] +re2 = ["google-re2 (>=1.1)"] + [[package]] name = "pexpect" version = "4.9.0" @@ -2554,7 +2368,7 @@ description = "Pexpect allows easy control of interactive console applications." optional = true python-versions = "*" groups = ["main"] -markers = "extra == \"latex\" and sys_platform != \"win32\" and (sys_platform != \"win32\" and sys_platform != \"emscripten\" or python_version < \"3.11\")" +markers = "extra == \"latex\" and sys_platform != \"win32\" and sys_platform != \"emscripten\"" files = [ {file = "pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523"}, {file = "pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f"}, @@ -2565,148 +2379,127 @@ ptyprocess = ">=0.5" [[package]] name = "pillow" -version = "11.3.0" -description = "Python Imaging Library (Fork)" +version = "12.2.0" +description = "Python Imaging Library (fork)" optional = true -python-versions = ">=3.9" +python-versions = ">=3.10" groups = ["main"] markers = "extra == \"docs\"" files = [ - {file = "pillow-11.3.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:1b9c17fd4ace828b3003dfd1e30bff24863e0eb59b535e8f80194d9cc7ecf860"}, - {file = "pillow-11.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:65dc69160114cdd0ca0f35cb434633c75e8e7fad4cf855177a05bf38678f73ad"}, - {file = "pillow-11.3.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7107195ddc914f656c7fc8e4a5e1c25f32e9236ea3ea860f257b0436011fddd0"}, - {file = "pillow-11.3.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:cc3e831b563b3114baac7ec2ee86819eb03caa1a2cef0b481a5675b59c4fe23b"}, - {file = "pillow-11.3.0-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f1f182ebd2303acf8c380a54f615ec883322593320a9b00438eb842c1f37ae50"}, - {file = "pillow-11.3.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4445fa62e15936a028672fd48c4c11a66d641d2c05726c7ec1f8ba6a572036ae"}, - {file = "pillow-11.3.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:71f511f6b3b91dd543282477be45a033e4845a40278fa8dcdbfdb07109bf18f9"}, - {file = "pillow-11.3.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:040a5b691b0713e1f6cbe222e0f4f74cd233421e105850ae3b3c0ceda520f42e"}, - {file = "pillow-11.3.0-cp310-cp310-win32.whl", hash = "sha256:89bd777bc6624fe4115e9fac3352c79ed60f3bb18651420635f26e643e3dd1f6"}, - {file = "pillow-11.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:19d2ff547c75b8e3ff46f4d9ef969a06c30ab2d4263a9e287733aa8b2429ce8f"}, - {file = "pillow-11.3.0-cp310-cp310-win_arm64.whl", hash = "sha256:819931d25e57b513242859ce1876c58c59dc31587847bf74cfe06b2e0cb22d2f"}, - {file = "pillow-11.3.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:1cd110edf822773368b396281a2293aeb91c90a2db00d78ea43e7e861631b722"}, - {file = "pillow-11.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9c412fddd1b77a75aa904615ebaa6001f169b26fd467b4be93aded278266b288"}, - {file = "pillow-11.3.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7d1aa4de119a0ecac0a34a9c8bde33f34022e2e8f99104e47a3ca392fd60e37d"}, - {file = "pillow-11.3.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:91da1d88226663594e3f6b4b8c3c8d85bd504117d043740a8e0ec449087cc494"}, - {file = "pillow-11.3.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:643f189248837533073c405ec2f0bb250ba54598cf80e8c1e043381a60632f58"}, - {file = "pillow-11.3.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:106064daa23a745510dabce1d84f29137a37224831d88eb4ce94bb187b1d7e5f"}, - {file = "pillow-11.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:cd8ff254faf15591e724dc7c4ddb6bf4793efcbe13802a4ae3e863cd300b493e"}, - {file = "pillow-11.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:932c754c2d51ad2b2271fd01c3d121daaa35e27efae2a616f77bf164bc0b3e94"}, - {file = "pillow-11.3.0-cp311-cp311-win32.whl", hash = "sha256:b4b8f3efc8d530a1544e5962bd6b403d5f7fe8b9e08227c6b255f98ad82b4ba0"}, - {file = "pillow-11.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:1a992e86b0dd7aeb1f053cd506508c0999d710a8f07b4c791c63843fc6a807ac"}, - {file = "pillow-11.3.0-cp311-cp311-win_arm64.whl", hash = "sha256:30807c931ff7c095620fe04448e2c2fc673fcbb1ffe2a7da3fb39613489b1ddd"}, - {file = "pillow-11.3.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fdae223722da47b024b867c1ea0be64e0df702c5e0a60e27daad39bf960dd1e4"}, - {file = "pillow-11.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:921bd305b10e82b4d1f5e802b6850677f965d8394203d182f078873851dada69"}, - {file = "pillow-11.3.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:eb76541cba2f958032d79d143b98a3a6b3ea87f0959bbe256c0b5e416599fd5d"}, - {file = "pillow-11.3.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:67172f2944ebba3d4a7b54f2e95c786a3a50c21b88456329314caaa28cda70f6"}, - {file = "pillow-11.3.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:97f07ed9f56a3b9b5f49d3661dc9607484e85c67e27f3e8be2c7d28ca032fec7"}, - {file = "pillow-11.3.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:676b2815362456b5b3216b4fd5bd89d362100dc6f4945154ff172e206a22c024"}, - {file = "pillow-11.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3e184b2f26ff146363dd07bde8b711833d7b0202e27d13540bfe2e35a323a809"}, - {file = "pillow-11.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6be31e3fc9a621e071bc17bb7de63b85cbe0bfae91bb0363c893cbe67247780d"}, - {file = "pillow-11.3.0-cp312-cp312-win32.whl", hash = "sha256:7b161756381f0918e05e7cb8a371fff367e807770f8fe92ecb20d905d0e1c149"}, - {file = "pillow-11.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:a6444696fce635783440b7f7a9fc24b3ad10a9ea3f0ab66c5905be1c19ccf17d"}, - {file = "pillow-11.3.0-cp312-cp312-win_arm64.whl", hash = "sha256:2aceea54f957dd4448264f9bf40875da0415c83eb85f55069d89c0ed436e3542"}, - {file = "pillow-11.3.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:1c627742b539bba4309df89171356fcb3cc5a9178355b2727d1b74a6cf155fbd"}, - {file = "pillow-11.3.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:30b7c02f3899d10f13d7a48163c8969e4e653f8b43416d23d13d1bbfdc93b9f8"}, - {file = "pillow-11.3.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:7859a4cc7c9295f5838015d8cc0a9c215b77e43d07a25e460f35cf516df8626f"}, - {file = "pillow-11.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ec1ee50470b0d050984394423d96325b744d55c701a439d2bd66089bff963d3c"}, - {file = "pillow-11.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7db51d222548ccfd274e4572fdbf3e810a5e66b00608862f947b163e613b67dd"}, - {file = "pillow-11.3.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2d6fcc902a24ac74495df63faad1884282239265c6839a0a6416d33faedfae7e"}, - {file = "pillow-11.3.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f0f5d8f4a08090c6d6d578351a2b91acf519a54986c055af27e7a93feae6d3f1"}, - {file = "pillow-11.3.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c37d8ba9411d6003bba9e518db0db0c58a680ab9fe5179f040b0463644bc9805"}, - {file = "pillow-11.3.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:13f87d581e71d9189ab21fe0efb5a23e9f28552d5be6979e84001d3b8505abe8"}, - {file = "pillow-11.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:023f6d2d11784a465f09fd09a34b150ea4672e85fb3d05931d89f373ab14abb2"}, - {file = "pillow-11.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:45dfc51ac5975b938e9809451c51734124e73b04d0f0ac621649821a63852e7b"}, - {file = "pillow-11.3.0-cp313-cp313-win32.whl", hash = "sha256:a4d336baed65d50d37b88ca5b60c0fa9d81e3a87d4a7930d3880d1624d5b31f3"}, - {file = "pillow-11.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:0bce5c4fd0921f99d2e858dc4d4d64193407e1b99478bc5cacecba2311abde51"}, - {file = "pillow-11.3.0-cp313-cp313-win_arm64.whl", hash = "sha256:1904e1264881f682f02b7f8167935cce37bc97db457f8e7849dc3a6a52b99580"}, - {file = "pillow-11.3.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:4c834a3921375c48ee6b9624061076bc0a32a60b5532b322cc0ea64e639dd50e"}, - {file = "pillow-11.3.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:5e05688ccef30ea69b9317a9ead994b93975104a677a36a8ed8106be9260aa6d"}, - {file = "pillow-11.3.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:1019b04af07fc0163e2810167918cb5add8d74674b6267616021ab558dc98ced"}, - {file = "pillow-11.3.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f944255db153ebb2b19c51fe85dd99ef0ce494123f21b9db4877ffdfc5590c7c"}, - {file = "pillow-11.3.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1f85acb69adf2aaee8b7da124efebbdb959a104db34d3a2cb0f3793dbae422a8"}, - {file = "pillow-11.3.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:05f6ecbeff5005399bb48d198f098a9b4b6bdf27b8487c7f38ca16eeb070cd59"}, - {file = "pillow-11.3.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a7bc6e6fd0395bc052f16b1a8670859964dbd7003bd0af2ff08342eb6e442cfe"}, - {file = "pillow-11.3.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:83e1b0161c9d148125083a35c1c5a89db5b7054834fd4387499e06552035236c"}, - {file = "pillow-11.3.0-cp313-cp313t-win32.whl", hash = "sha256:2a3117c06b8fb646639dce83694f2f9eac405472713fcb1ae887469c0d4f6788"}, - {file = "pillow-11.3.0-cp313-cp313t-win_amd64.whl", hash = "sha256:857844335c95bea93fb39e0fa2726b4d9d758850b34075a7e3ff4f4fa3aa3b31"}, - {file = "pillow-11.3.0-cp313-cp313t-win_arm64.whl", hash = "sha256:8797edc41f3e8536ae4b10897ee2f637235c94f27404cac7297f7b607dd0716e"}, - {file = "pillow-11.3.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:d9da3df5f9ea2a89b81bb6087177fb1f4d1c7146d583a3fe5c672c0d94e55e12"}, - {file = "pillow-11.3.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0b275ff9b04df7b640c59ec5a3cb113eefd3795a8df80bac69646ef699c6981a"}, - {file = "pillow-11.3.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0743841cabd3dba6a83f38a92672cccbd69af56e3e91777b0ee7f4dba4385632"}, - {file = "pillow-11.3.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2465a69cf967b8b49ee1b96d76718cd98c4e925414ead59fdf75cf0fd07df673"}, - {file = "pillow-11.3.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:41742638139424703b4d01665b807c6468e23e699e8e90cffefe291c5832b027"}, - {file = "pillow-11.3.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:93efb0b4de7e340d99057415c749175e24c8864302369e05914682ba642e5d77"}, - {file = "pillow-11.3.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7966e38dcd0fa11ca390aed7c6f20454443581d758242023cf36fcb319b1a874"}, - {file = "pillow-11.3.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:98a9afa7b9007c67ed84c57c9e0ad86a6000da96eaa638e4f8abe5b65ff83f0a"}, - {file = "pillow-11.3.0-cp314-cp314-win32.whl", hash = "sha256:02a723e6bf909e7cea0dac1b0e0310be9d7650cd66222a5f1c571455c0a45214"}, - {file = "pillow-11.3.0-cp314-cp314-win_amd64.whl", hash = "sha256:a418486160228f64dd9e9efcd132679b7a02a5f22c982c78b6fc7dab3fefb635"}, - {file = "pillow-11.3.0-cp314-cp314-win_arm64.whl", hash = "sha256:155658efb5e044669c08896c0c44231c5e9abcaadbc5cd3648df2f7c0b96b9a6"}, - {file = "pillow-11.3.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:59a03cdf019efbfeeed910bf79c7c93255c3d54bc45898ac2a4140071b02b4ae"}, - {file = "pillow-11.3.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f8a5827f84d973d8636e9dc5764af4f0cf2318d26744b3d902931701b0d46653"}, - {file = "pillow-11.3.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ee92f2fd10f4adc4b43d07ec5e779932b4eb3dbfbc34790ada5a6669bc095aa6"}, - {file = "pillow-11.3.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c96d333dcf42d01f47b37e0979b6bd73ec91eae18614864622d9b87bbd5bbf36"}, - {file = "pillow-11.3.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4c96f993ab8c98460cd0c001447bff6194403e8b1d7e149ade5f00594918128b"}, - {file = "pillow-11.3.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:41342b64afeba938edb034d122b2dda5db2139b9a4af999729ba8818e0056477"}, - {file = "pillow-11.3.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:068d9c39a2d1b358eb9f245ce7ab1b5c3246c7c8c7d9ba58cfa5b43146c06e50"}, - {file = "pillow-11.3.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:a1bc6ba083b145187f648b667e05a2534ecc4b9f2784c2cbe3089e44868f2b9b"}, - {file = "pillow-11.3.0-cp314-cp314t-win32.whl", hash = "sha256:118ca10c0d60b06d006be10a501fd6bbdfef559251ed31b794668ed569c87e12"}, - {file = "pillow-11.3.0-cp314-cp314t-win_amd64.whl", hash = "sha256:8924748b688aa210d79883357d102cd64690e56b923a186f35a82cbc10f997db"}, - {file = "pillow-11.3.0-cp314-cp314t-win_arm64.whl", hash = "sha256:79ea0d14d3ebad43ec77ad5272e6ff9bba5b679ef73375ea760261207fa8e0aa"}, - {file = "pillow-11.3.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:48d254f8a4c776de343051023eb61ffe818299eeac478da55227d96e241de53f"}, - {file = "pillow-11.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:7aee118e30a4cf54fdd873bd3a29de51e29105ab11f9aad8c32123f58c8f8081"}, - {file = "pillow-11.3.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:23cff760a9049c502721bdb743a7cb3e03365fafcdfc2ef9784610714166e5a4"}, - {file = "pillow-11.3.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:6359a3bc43f57d5b375d1ad54a0074318a0844d11b76abccf478c37c986d3cfc"}, - {file = "pillow-11.3.0-cp39-cp39-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:092c80c76635f5ecb10f3f83d76716165c96f5229addbd1ec2bdbbda7d496e06"}, - {file = "pillow-11.3.0-cp39-cp39-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cadc9e0ea0a2431124cde7e1697106471fc4c1da01530e679b2391c37d3fbb3a"}, - {file = "pillow-11.3.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:6a418691000f2a418c9135a7cf0d797c1bb7d9a485e61fe8e7722845b95ef978"}, - {file = "pillow-11.3.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:97afb3a00b65cc0804d1c7abddbf090a81eaac02768af58cbdcaaa0a931e0b6d"}, - {file = "pillow-11.3.0-cp39-cp39-win32.whl", hash = "sha256:ea944117a7974ae78059fcc1800e5d3295172bb97035c0c1d9345fca1419da71"}, - {file = "pillow-11.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:e5c5858ad8ec655450a7c7df532e9842cf8df7cc349df7225c60d5d348c8aada"}, - {file = "pillow-11.3.0-cp39-cp39-win_arm64.whl", hash = "sha256:6abdbfd3aea42be05702a8dd98832329c167ee84400a1d1f61ab11437f1717eb"}, - {file = "pillow-11.3.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:3cee80663f29e3843b68199b9d6f4f54bd1d4a6b59bdd91bceefc51238bcb967"}, - {file = "pillow-11.3.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:b5f56c3f344f2ccaf0dd875d3e180f631dc60a51b314295a3e681fe8cf851fbe"}, - {file = "pillow-11.3.0-pp310-pypy310_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:e67d793d180c9df62f1f40aee3accca4829d3794c95098887edc18af4b8b780c"}, - {file = "pillow-11.3.0-pp310-pypy310_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d000f46e2917c705e9fb93a3606ee4a819d1e3aa7a9b442f6444f07e77cf5e25"}, - {file = "pillow-11.3.0-pp310-pypy310_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:527b37216b6ac3a12d7838dc3bd75208ec57c1c6d11ef01902266a5a0c14fc27"}, - {file = "pillow-11.3.0-pp310-pypy310_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:be5463ac478b623b9dd3937afd7fb7ab3d79dd290a28e2b6df292dc75063eb8a"}, - {file = "pillow-11.3.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:8dc70ca24c110503e16918a658b869019126ecfe03109b754c402daff12b3d9f"}, - {file = "pillow-11.3.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:7c8ec7a017ad1bd562f93dbd8505763e688d388cde6e4a010ae1486916e713e6"}, - {file = "pillow-11.3.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:9ab6ae226de48019caa8074894544af5b53a117ccb9d3b3dcb2871464c829438"}, - {file = "pillow-11.3.0-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:fe27fb049cdcca11f11a7bfda64043c37b30e6b91f10cb5bab275806c32f6ab3"}, - {file = "pillow-11.3.0-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:465b9e8844e3c3519a983d58b80be3f668e2a7a5db97f2784e7079fbc9f9822c"}, - {file = "pillow-11.3.0-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5418b53c0d59b3824d05e029669efa023bbef0f3e92e75ec8428f3799487f361"}, - {file = "pillow-11.3.0-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:504b6f59505f08ae014f724b6207ff6222662aab5cc9542577fb084ed0676ac7"}, - {file = "pillow-11.3.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:c84d689db21a1c397d001aa08241044aa2069e7587b398c8cc63020390b1c1b8"}, - {file = "pillow-11.3.0.tar.gz", hash = "sha256:3828ee7586cd0b2091b6209e5ad53e20d0649bbe87164a459d0676e035e8f523"}, + {file = "pillow-12.2.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:a4e8f36e677d3336f35089648c8955c51c6d386a13cf6ee9c189c5f5bd713a9f"}, + {file = "pillow-12.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2e589959f10d9824d39b350472b92f0ce3b443c0a3442ebf41c40cb8361c5b97"}, + {file = "pillow-12.2.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:a52edc8bfff4429aaabdf4d9ee0daadbbf8562364f940937b941f87a4290f5ff"}, + {file = "pillow-12.2.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:975385f4776fafde056abb318f612ef6285b10a1f12b8570f3647ad0d74b48ec"}, + {file = "pillow-12.2.0-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bd9c0c7a0c681a347b3194c500cb1e6ca9cab053ea4d82a5cf45b6b754560136"}, + {file = "pillow-12.2.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:88d387ff40b3ff7c274947ed3125dedf5262ec6919d83946753b5f3d7c67ea4c"}, + {file = "pillow-12.2.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:51c4167c34b0d8ba05b547a3bb23578d0ba17b80a5593f93bd8ecb123dd336a3"}, + {file = "pillow-12.2.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:34c0d99ecccea270c04882cb3b86e7b57296079c9a4aff88cb3b33563d95afaa"}, + {file = "pillow-12.2.0-cp310-cp310-win32.whl", hash = "sha256:b85f66ae9eb53e860a873b858b789217ba505e5e405a24b85c0464822fe88032"}, + {file = "pillow-12.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:673aa32138f3e7531ccdbca7b3901dba9b70940a19ccecc6a37c77d5fdeb05b5"}, + {file = "pillow-12.2.0-cp310-cp310-win_arm64.whl", hash = "sha256:3e080565d8d7c671db5802eedfb438e5565ffa40115216eabb8cd52d0ecce024"}, + {file = "pillow-12.2.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:8be29e59487a79f173507c30ddf57e733a357f67881430449bb32614075a40ab"}, + {file = "pillow-12.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:71cde9a1e1551df7d34a25462fc60325e8a11a82cc2e2f54578e5e9a1e153d65"}, + {file = "pillow-12.2.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f490f9368b6fc026f021db16d7ec2fbf7d89e2edb42e8ec09d2c60505f5729c7"}, + {file = "pillow-12.2.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8bd7903a5f2a4545f6fd5935c90058b89d30045568985a71c79f5fd6edf9b91e"}, + {file = "pillow-12.2.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3997232e10d2920a68d25191392e3a4487d8183039e1c74c2297f00ed1c50705"}, + {file = "pillow-12.2.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e74473c875d78b8e9d5da2a70f7099549f9eb37ded4e2f6a463e60125bccd176"}, + {file = "pillow-12.2.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:56a3f9c60a13133a98ecff6197af34d7824de9b7b38c3654861a725c970c197b"}, + {file = "pillow-12.2.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:90e6f81de50ad6b534cab6e5aef77ff6e37722b2f5d908686f4a5c9eba17a909"}, + {file = "pillow-12.2.0-cp311-cp311-win32.whl", hash = "sha256:8c984051042858021a54926eb597d6ee3012393ce9c181814115df4c60b9a808"}, + {file = "pillow-12.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:6e6b2a0c538fc200b38ff9eb6628228b77908c319a005815f2dde585a0664b60"}, + {file = "pillow-12.2.0-cp311-cp311-win_arm64.whl", hash = "sha256:9a8a34cc89c67a65ea7437ce257cea81a9dad65b29805f3ecee8c8fe8ff25ffe"}, + {file = "pillow-12.2.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2d192a155bbcec180f8564f693e6fd9bccff5a7af9b32e2e4bf8c9c69dbad6b5"}, + {file = "pillow-12.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f3f40b3c5a968281fd507d519e444c35f0ff171237f4fdde090dd60699458421"}, + {file = "pillow-12.2.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:03e7e372d5240cc23e9f07deca4d775c0817bffc641b01e9c3af208dbd300987"}, + {file = "pillow-12.2.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b86024e52a1b269467a802258c25521e6d742349d760728092e1bc2d135b4d76"}, + {file = "pillow-12.2.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7371b48c4fa448d20d2714c9a1f775a81155050d383333e0a6c15b1123dda005"}, + {file = "pillow-12.2.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:62f5409336adb0663b7caa0da5c7d9e7bdbaae9ce761d34669420c2a801b2780"}, + {file = "pillow-12.2.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:01afa7cf67f74f09523699b4e88c73fb55c13346d212a59a2db1f86b0a63e8c5"}, + {file = "pillow-12.2.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fc3d34d4a8fbec3e88a79b92e5465e0f9b842b628675850d860b8bd300b159f5"}, + {file = "pillow-12.2.0-cp312-cp312-win32.whl", hash = "sha256:58f62cc0f00fd29e64b29f4fd923ffdb3859c9f9e6105bfc37ba1d08994e8940"}, + {file = "pillow-12.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:7f84204dee22a783350679a0333981df803dac21a0190d706a50475e361c93f5"}, + {file = "pillow-12.2.0-cp312-cp312-win_arm64.whl", hash = "sha256:af73337013e0b3b46f175e79492d96845b16126ddf79c438d7ea7ff27783a414"}, + {file = "pillow-12.2.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:8297651f5b5679c19968abefd6bb84d95fe30ef712eb1b2d9b2d31ca61267f4c"}, + {file = "pillow-12.2.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:50d8520da2a6ce0af445fa6d648c4273c3eeefbc32d7ce049f22e8b5c3daecc2"}, + {file = "pillow-12.2.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:766cef22385fa1091258ad7e6216792b156dc16d8d3fa607e7545b2b72061f1c"}, + {file = "pillow-12.2.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5d2fd0fa6b5d9d1de415060363433f28da8b1526c1c129020435e186794b3795"}, + {file = "pillow-12.2.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:56b25336f502b6ed02e889f4ece894a72612fe885889a6e8c4c80239ff6e5f5f"}, + {file = "pillow-12.2.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f1c943e96e85df3d3478f7b691f229887e143f81fedab9b20205349ab04d73ed"}, + {file = "pillow-12.2.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:03f6fab9219220f041c74aeaa2939ff0062bd5c364ba9ce037197f4c6d498cd9"}, + {file = "pillow-12.2.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5cdfebd752ec52bf5bb4e35d9c64b40826bc5b40a13df7c3cda20a2c03a0f5ed"}, + {file = "pillow-12.2.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:eedf4b74eda2b5a4b2b2fb4c006d6295df3bf29e459e198c90ea48e130dc75c3"}, + {file = "pillow-12.2.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:00a2865911330191c0b818c59103b58a5e697cae67042366970a6b6f1b20b7f9"}, + {file = "pillow-12.2.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1e1757442ed87f4912397c6d35a0db6a7b52592156014706f17658ff58bbf795"}, + {file = "pillow-12.2.0-cp313-cp313-win32.whl", hash = "sha256:144748b3af2d1b358d41286056d0003f47cb339b8c43a9ea42f5fea4d8c66b6e"}, + {file = "pillow-12.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:390ede346628ccc626e5730107cde16c42d3836b89662a115a921f28440e6a3b"}, + {file = "pillow-12.2.0-cp313-cp313-win_arm64.whl", hash = "sha256:8023abc91fba39036dbce14a7d6535632f99c0b857807cbbbf21ecc9f4717f06"}, + {file = "pillow-12.2.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:042db20a421b9bafecc4b84a8b6e444686bd9d836c7fd24542db3e7df7baad9b"}, + {file = "pillow-12.2.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:dd025009355c926a84a612fecf58bb315a3f6814b17ead51a8e48d3823d9087f"}, + {file = "pillow-12.2.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:88ddbc66737e277852913bd1e07c150cc7bb124539f94c4e2df5344494e0a612"}, + {file = "pillow-12.2.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d362d1878f00c142b7e1a16e6e5e780f02be8195123f164edf7eddd911eefe7c"}, + {file = "pillow-12.2.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2c727a6d53cb0018aadd8018c2b938376af27914a68a492f59dfcaca650d5eea"}, + {file = "pillow-12.2.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:efd8c21c98c5cc60653bcb311bef2ce0401642b7ce9d09e03a7da87c878289d4"}, + {file = "pillow-12.2.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9f08483a632889536b8139663db60f6724bfcb443c96f1b18855860d7d5c0fd4"}, + {file = "pillow-12.2.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:dac8d77255a37e81a2efcbd1fc05f1c15ee82200e6c240d7e127e25e365c39ea"}, + {file = "pillow-12.2.0-cp313-cp313t-win32.whl", hash = "sha256:ee3120ae9dff32f121610bb08e4313be87e03efeadfc6c0d18f89127e24d0c24"}, + {file = "pillow-12.2.0-cp313-cp313t-win_amd64.whl", hash = "sha256:325ca0528c6788d2a6c3d40e3568639398137346c3d6e66bb61db96b96511c98"}, + {file = "pillow-12.2.0-cp313-cp313t-win_arm64.whl", hash = "sha256:2e5a76d03a6c6dcef67edabda7a52494afa4035021a79c8558e14af25313d453"}, + {file = "pillow-12.2.0-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:3adc9215e8be0448ed6e814966ecf3d9952f0ea40eb14e89a102b87f450660d8"}, + {file = "pillow-12.2.0-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:6a9adfc6d24b10f89588096364cc726174118c62130c817c2837c60cf08a392b"}, + {file = "pillow-12.2.0-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:6a6e67ea2e6feda684ed370f9a1c52e7a243631c025ba42149a2cc5934dec295"}, + {file = "pillow-12.2.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:2bb4a8d594eacdfc59d9e5ad972aa8afdd48d584ffd5f13a937a664c3e7db0ed"}, + {file = "pillow-12.2.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:80b2da48193b2f33ed0c32c38140f9d3186583ce7d516526d462645fd98660ae"}, + {file = "pillow-12.2.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:22db17c68434de69d8ecfc2fe821569195c0c373b25cccb9cbdacf2c6e53c601"}, + {file = "pillow-12.2.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7b14cc0106cd9aecda615dd6903840a058b4700fcb817687d0ee4fc8b6e389be"}, + {file = "pillow-12.2.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8cbeb542b2ebc6fcdacabf8aca8c1a97c9b3ad3927d46b8723f9d4f033288a0f"}, + {file = "pillow-12.2.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4bfd07bc812fbd20395212969e41931001fd59eb55a60658b0e5710872e95286"}, + {file = "pillow-12.2.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:9aba9a17b623ef750a4d11b742cbafffeb48a869821252b30ee21b5e91392c50"}, + {file = "pillow-12.2.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:deede7c263feb25dba4e82ea23058a235dcc2fe1f6021025dc71f2b618e26104"}, + {file = "pillow-12.2.0-cp314-cp314-win32.whl", hash = "sha256:632ff19b2778e43162304d50da0181ce24ac5bb8180122cbe1bf4673428328c7"}, + {file = "pillow-12.2.0-cp314-cp314-win_amd64.whl", hash = "sha256:4e6c62e9d237e9b65fac06857d511e90d8461a32adcc1b9065ea0c0fa3a28150"}, + {file = "pillow-12.2.0-cp314-cp314-win_arm64.whl", hash = "sha256:b1c1fbd8a5a1af3412a0810d060a78b5136ec0836c8a4ef9aa11807f2a22f4e1"}, + {file = "pillow-12.2.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:57850958fe9c751670e49b2cecf6294acc99e562531f4bd317fa5ddee2068463"}, + {file = "pillow-12.2.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:d5d38f1411c0ed9f97bcb49b7bd59b6b7c314e0e27420e34d99d844b9ce3b6f3"}, + {file = "pillow-12.2.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5c0a9f29ca8e79f09de89293f82fc9b0270bb4af1d58bc98f540cc4aedf03166"}, + {file = "pillow-12.2.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1610dd6c61621ae1cf811bef44d77e149ce3f7b95afe66a4512f8c59f25d9ebe"}, + {file = "pillow-12.2.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0a34329707af4f73cf1782a36cd2289c0368880654a2c11f027bcee9052d35dd"}, + {file = "pillow-12.2.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8e9c4f5b3c546fa3458a29ab22646c1c6c787ea8f5ef51300e5a60300736905e"}, + {file = "pillow-12.2.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:fb043ee2f06b41473269765c2feae53fc2e2fbf96e5e22ca94fb5ad677856f06"}, + {file = "pillow-12.2.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:f278f034eb75b4e8a13a54a876cc4a5ab39173d2cdd93a638e1b467fc545ac43"}, + {file = "pillow-12.2.0-cp314-cp314t-win32.whl", hash = "sha256:6bb77b2dcb06b20f9f4b4a8454caa581cd4dd0643a08bacf821216a16d9c8354"}, + {file = "pillow-12.2.0-cp314-cp314t-win_amd64.whl", hash = "sha256:6562ace0d3fb5f20ed7290f1f929cae41b25ae29528f2af1722966a0a02e2aa1"}, + {file = "pillow-12.2.0-cp314-cp314t-win_arm64.whl", hash = "sha256:aa88ccfe4e32d362816319ed727a004423aab09c5cea43c01a4b435643fa34eb"}, + {file = "pillow-12.2.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0538bd5e05efec03ae613fd89c4ce0368ecd2ba239cc25b9f9be7ed426b0af1f"}, + {file = "pillow-12.2.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:394167b21da716608eac917c60aa9b969421b5dcbbe02ae7f013e7b85811c69d"}, + {file = "pillow-12.2.0-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5d04bfa02cc2d23b497d1e90a0f927070043f6cbf303e738300532379a4b4e0f"}, + {file = "pillow-12.2.0-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0c838a5125cee37e68edec915651521191cef1e6aa336b855f495766e77a366e"}, + {file = "pillow-12.2.0-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4a6c9fa44005fa37a91ebfc95d081e8079757d2e904b27103f4f5fa6f0bf78c0"}, + {file = "pillow-12.2.0-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:25373b66e0dd5905ed63fa3cae13c82fbddf3079f2c8bf15c6fb6a35586324c1"}, + {file = "pillow-12.2.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:bfa9c230d2fe991bed5318a5f119bd6780cda2915cca595393649fc118ab895e"}, + {file = "pillow-12.2.0.tar.gz", hash = "sha256:a830b1a40919539d07806aa58e1b114df53ddd43213d9c8b75847eee6c0182b5"}, ] [package.extras] docs = ["furo", "olefile", "sphinx (>=8.2)", "sphinx-autobuild", "sphinx-copybutton", "sphinx-inline-tabs", "sphinxext-opengraph"] fpx = ["olefile"] mic = ["olefile"] -test-arrow = ["pyarrow"] -tests = ["check-manifest", "coverage (>=7.4.2)", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout", "pytest-xdist", "trove-classifiers (>=2024.10.12)"] -typing = ["typing-extensions ; python_version < \"3.10\""] +test-arrow = ["arro3-compute", "arro3-core", "nanoarrow", "pyarrow"] +tests = ["check-manifest", "coverage (>=7.4.2)", "defusedxml", "markdown2", "olefile", "packaging", "pyroma (>=5)", "pytest", "pytest-cov", "pytest-timeout", "pytest-xdist", "trove-classifiers (>=2024.10.12)"] xmp = ["defusedxml"] [[package]] name = "platformdirs" -version = "4.4.0" +version = "4.9.6" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." optional = true -python-versions = ">=3.9" +python-versions = ">=3.10" groups = ["main"] markers = "extra == \"docs\"" files = [ - {file = "platformdirs-4.4.0-py3-none-any.whl", hash = "sha256:abd01743f24e5287cd7a5db3752faf1a2d65353f38ec26d98e25a6db65958c85"}, - {file = "platformdirs-4.4.0.tar.gz", hash = "sha256:ca753cf4d81dc309bc67b0ea38fd15dc97bc30ce419a7f58d13eb3bf14c4febf"}, + {file = "platformdirs-4.9.6-py3-none-any.whl", hash = "sha256:e61adb1d5e5cb3441b4b7710bea7e4c12250ca49439228cc1021c00dcfac0917"}, + {file = "platformdirs-4.9.6.tar.gz", hash = "sha256:3bfa75b0ad0db84096ae777218481852c0ebc6c727b3168c1b9e0118e458cf0a"}, ] -[package.extras] -docs = ["furo (>=2024.8.6)", "proselint (>=0.14)", "sphinx (>=8.1.3)", "sphinx-autodoc-typehints (>=3)"] -test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=8.3.4)", "pytest-cov (>=6)", "pytest-mock (>=3.14)"] -type = ["mypy (>=1.14.1)"] - [[package]] name = "pluggy" version = "1.6.0" @@ -2725,25 +2518,22 @@ testing = ["coverage", "pytest", "pytest-benchmark"] [[package]] name = "plumbum" -version = "1.9.0" +version = "1.10.0" description = "Plumbum: shell combinators library" optional = true -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["main"] markers = "extra == \"docs\"" files = [ - {file = "plumbum-1.9.0-py3-none-any.whl", hash = "sha256:9fd0d3b0e8d86e4b581af36edf3f3bbe9d1ae15b45b8caab28de1bcb27aaa7f5"}, - {file = "plumbum-1.9.0.tar.gz", hash = "sha256:e640062b72642c3873bd5bdc3effed75ba4d3c70ef6b6a7b907357a84d909219"}, + {file = "plumbum-1.10.0-py3-none-any.whl", hash = "sha256:9583d737ac901c474d99d030e4d5eec4c4e6d2d7417b1cf49728cf3be34f6dc8"}, + {file = "plumbum-1.10.0.tar.gz", hash = "sha256:f8cbf0ecec0b73ff4e349398b65112a9e3f9300e7dc019001217dcc148d5c97c"}, ] [package.dependencies] pywin32 = {version = "*", markers = "platform_system == \"Windows\" and platform_python_implementation != \"PyPy\""} [package.extras] -dev = ["coverage[toml]", "paramiko", "psutil", "pytest (>=6.0)", "pytest-cov", "pytest-mock", "pytest-timeout"] -docs = ["sphinx (>=4.0.0)", "sphinx-rtd-theme (>=1.0.0)"] ssh = ["paramiko"] -test = ["coverage[toml]", "paramiko", "psutil", "pytest (>=6.0)", "pytest-cov", "pytest-mock", "pytest-timeout"] [[package]] name = "ply" @@ -2781,7 +2571,7 @@ description = "Run a subprocess in a pseudo terminal" optional = true python-versions = "*" groups = ["main"] -markers = "extra == \"latex\" and sys_platform != \"win32\" and (sys_platform != \"win32\" and sys_platform != \"emscripten\" or python_version < \"3.11\")" +markers = "extra == \"latex\" and sys_platform != \"win32\" and sys_platform != \"emscripten\"" files = [ {file = "ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35"}, {file = "ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220"}, @@ -2817,27 +2607,27 @@ files = [ [[package]] name = "pycparser" -version = "2.23" +version = "3.0" description = "C parser in Python" optional = false -python-versions = ">=3.8" +python-versions = ">=3.10" groups = ["main"] markers = "implementation_name == \"pypy\"" files = [ - {file = "pycparser-2.23-py3-none-any.whl", hash = "sha256:e5c6e8d3fbad53479cab09ac03729e0a9faf2bee3db8208a550daf5af81a5934"}, - {file = "pycparser-2.23.tar.gz", hash = "sha256:78816d4f24add8f10a06d6f05b4d424ad9e96cfebf68a4ddc99c65c0720d00c2"}, + {file = "pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992"}, + {file = "pycparser-3.0.tar.gz", hash = "sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29"}, ] [[package]] name = "pygments" -version = "2.19.2" +version = "2.20.0" description = "Pygments is a syntax highlighting package written in Python." optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["main", "dev"] files = [ - {file = "pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b"}, - {file = "pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887"}, + {file = "pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176"}, + {file = "pygments-2.20.0.tar.gz", hash = "sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f"}, ] markers = {main = "extra == \"latex\" or extra == \"docs\""} @@ -2846,15 +2636,15 @@ windows-terminal = ["colorama (>=0.4.6)"] [[package]] name = "pyparsing" -version = "3.2.5" +version = "3.3.2" description = "pyparsing - Classes and methods to define and execute parsing grammars" optional = true python-versions = ">=3.9" groups = ["main"] markers = "extra == \"docs\"" files = [ - {file = "pyparsing-3.2.5-py3-none-any.whl", hash = "sha256:e38a4f02064cf41fe6593d328d0512495ad1f3d8a91c4f73fc401b3079a59a5e"}, - {file = "pyparsing-3.2.5.tar.gz", hash = "sha256:2df8d5b7b2802ef88e8d016a2eb9c7aeaa923529cd251ed0fe4608275d4105b6"}, + {file = "pyparsing-3.3.2-py3-none-any.whl", hash = "sha256:850ba148bd908d7e2411587e247a1e4f0327839c40e2e5e6d05a007ecc69911d"}, + {file = "pyparsing-3.3.2.tar.gz", hash = "sha256:c777f4d763f140633dcb6d8a3eda953bf7a214dc4eff598413c070bcdc117cbc"}, ] [package.extras] @@ -2862,24 +2652,22 @@ diagrams = ["jinja2", "railroad-diagrams"] [[package]] name = "pytest" -version = "8.4.2" +version = "9.1.1" description = "pytest: simple powerful testing with Python" optional = false -python-versions = ">=3.9" +python-versions = ">=3.10" groups = ["dev"] files = [ - {file = "pytest-8.4.2-py3-none-any.whl", hash = "sha256:872f880de3fc3a5bdc88a11b39c9710c3497a547cfa9320bc3c5e62fbf272e79"}, - {file = "pytest-8.4.2.tar.gz", hash = "sha256:86c0d0b93306b961d58d62a4db4879f27fe25513d4b969df351abdddb3c30e01"}, + {file = "pytest-9.1.1-py3-none-any.whl", hash = "sha256:37a86b45efb9a47a61a36449063e8e18d0cab3161329fc099eb21783169c4f0c"}, + {file = "pytest-9.1.1.tar.gz", hash = "sha256:1088fbde8f2b49d95a549a195707afa7a76a3ce9bcadc26b6d71f0ffda5fe313"}, ] [package.dependencies] colorama = {version = ">=0.4", markers = "sys_platform == \"win32\""} -exceptiongroup = {version = ">=1", markers = "python_version < \"3.11\""} -iniconfig = ">=1" -packaging = ">=20" +iniconfig = ">=1.0.1" +packaging = ">=22" pluggy = ">=1.5,<2" pygments = ">=2.7.2" -tomli = {version = ">=1", markers = "python_version < \"3.11\""} [package.extras] dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "requests", "setuptools", "xmlschema"] @@ -3012,76 +2800,83 @@ six = ">=1.5" [[package]] name = "python-rapidjson" -version = "1.21" +version = "1.23" description = "Python wrapper around rapidjson" optional = false python-versions = ">=3.6" groups = ["main"] files = [ - {file = "python_rapidjson-1.21-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7e1c914b2c7952dadc69d1de54a3f147e27e00d45c70c72fe5a35fb58d0c3437"}, - {file = "python_rapidjson-1.21-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:127935afaf90ffaa721836ad64e58a7cd6838ef9d9a3c670f2769e045824aac1"}, - {file = "python_rapidjson-1.21-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0e6d0266549811757523245bcd7ce046cbedf653383f28f412103c759feaf3ef"}, - {file = "python_rapidjson-1.21-cp310-cp310-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5b81bc8189a354de509dc1ca353c847571013dc46dc212660b327dd5303315e5"}, - {file = "python_rapidjson-1.21-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f9f90fe043904efeec69a74787c87db26df657f4c722c19db7bfd0964c352f41"}, - {file = "python_rapidjson-1.21-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:150177509c71e9e782930e88e79078323dcca09acf01440f670f0b2f9c2958d4"}, - {file = "python_rapidjson-1.21-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:7958912ccd14c2e18d53b6ee766c273f8ee2a75a2f2df47c31e16b9b5fe826e9"}, - {file = "python_rapidjson-1.21-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:0ca49342dd54b23ffd5a2b150a43cdc8b2bb10e2c88935e9f30fc58ef6346fd9"}, - {file = "python_rapidjson-1.21-cp310-cp310-win32.whl", hash = "sha256:673c545dd5f8e87ccab51f2af83f30772ec6349295a13aef24b5b3e828fc086c"}, - {file = "python_rapidjson-1.21-cp310-cp310-win_amd64.whl", hash = "sha256:8e5322d8c11d461da1cd2074ee2bce9613b29e5873f46c2b486115d7894e32cd"}, - {file = "python_rapidjson-1.21-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:78103db8a58fbcff54ee475426fc9d3aab5257f6c2eb3ffe1c769b9ad9e0878f"}, - {file = "python_rapidjson-1.21-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7eafd20dfb972d16f7284e1f0a02fc7603b657f847dab3308c129fb41f214030"}, - {file = "python_rapidjson-1.21-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:08433801ddd96f2f2a03d17aeab8121adecb4e8cc119ae5dc05a1c441fb1ef58"}, - {file = "python_rapidjson-1.21-cp311-cp311-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:54ab944647aaf1d0c3fd26a0756edc87904b640a635fd40980b85faa80bdba70"}, - {file = "python_rapidjson-1.21-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9f47e5e126a2052fbb8a2f3e6c40b7ab52334e57ed8665733c277027f6caa6fa"}, - {file = "python_rapidjson-1.21-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:06fc84a13300be41dc075eded0d7bab7d024e8feb0685f43e958892ad4f6e5ad"}, - {file = "python_rapidjson-1.21-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:a8be8d08c33e9e8a6b83a4b34777b900f85ce5e6a951c562cb8c6e0966f2f604"}, - {file = "python_rapidjson-1.21-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:925cc765c2a4ab9aec92a163eca1d0572a4bf5ef7ef2e3d44e3808424454c5ec"}, - {file = "python_rapidjson-1.21-cp311-cp311-win32.whl", hash = "sha256:47a9fc6b1c76946f9af875cfcceb32f08c5a6b1677500174397f042a9bbd5d1e"}, - {file = "python_rapidjson-1.21-cp311-cp311-win_amd64.whl", hash = "sha256:a12c6b412f0b77589912159eea1a3385710ed1dc0d01259f2f467d5ec6b2413b"}, - {file = "python_rapidjson-1.21-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:354ac34828a242eec1896901fabe59d14adab7a5054d09a8bdaed4c6ee510b22"}, - {file = "python_rapidjson-1.21-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ddd846b095fad357e4e4c5071e874f9c100c2e5b9765abcc7d71254820251c20"}, - {file = "python_rapidjson-1.21-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7faf4211354c1db9b21934d723f3c7db1d611ec3013458dc2fe1a732aa100d3e"}, - {file = "python_rapidjson-1.21-cp312-cp312-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:46ea95e9292260fd8174966eca05fad08b43ca0f5df1ccfab9796edb0868d8eb"}, - {file = "python_rapidjson-1.21-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d1cac86399cfbaf24e72aef6eed1114180a72ba0242c7153f89d874a81fb83c6"}, - {file = "python_rapidjson-1.21-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5a4afbf54eb60dc56d4c2b6fac5d24709e8a5928baaeff0669c00950961c7a65"}, - {file = "python_rapidjson-1.21-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:1e8d4d1285c697325ad1b5300866176d190625bbdd85dce963300fad5bfaf166"}, - {file = "python_rapidjson-1.21-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1525778bc9b9cee9b8f3058e0435f0e4ff03e2d8c3883893ebcaf58680316964"}, - {file = "python_rapidjson-1.21-cp312-cp312-win32.whl", hash = "sha256:b5717ddb9788ca00e0f97546cbdd3cfd0c25e52ab3bfed0951c7898e7f37cc60"}, - {file = "python_rapidjson-1.21-cp312-cp312-win_amd64.whl", hash = "sha256:409704e52ad25df661265dbcb3a512a2146c98f72f362adc157e9b595704e1be"}, - {file = "python_rapidjson-1.21-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:863208f50b848b8e10aefcf0da2a67ce38a1044a2ebf83d6355f16f0c7865d4f"}, - {file = "python_rapidjson-1.21-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:608ade2fff2788821f25e70af4b1bfbf9ab2b2dc8ad47c91616935c5d31e3b72"}, - {file = "python_rapidjson-1.21-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1bf6377c4e9636e20b403776585903d1ff52613a419c5f8453032146f0422634"}, - {file = "python_rapidjson-1.21-cp313-cp313-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:74cba817cd9b16a3a54e7ddb3b59980c338c9a28900124502215dcba5db5a276"}, - {file = "python_rapidjson-1.21-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3194bde069c3f03f3c425de7037cca37d337b6c7ac30f42cd2f17e15f1c4da3a"}, - {file = "python_rapidjson-1.21-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b663932dc7548567028fb735bf4092b50dc373822dc9756a9ade6513541013de"}, - {file = "python_rapidjson-1.21-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:fcb094ecca3d892f1dbd318251579ef66e2b2f06a2a24414ad71c31931d7ffff"}, - {file = "python_rapidjson-1.21-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f4cdb4f77b08b7ae121b7ca27ed2d57dcce17f8cd9f1d4593c954c982aba18ba"}, - {file = "python_rapidjson-1.21-cp313-cp313-win32.whl", hash = "sha256:be63c0ef87bf26059ee77f5de21d84a1be3659cb6baa8484c237ffe17a8beb56"}, - {file = "python_rapidjson-1.21-cp313-cp313-win_amd64.whl", hash = "sha256:38307a2ef4fddbdc18806a796201fe668c30e60de7cd795943a3f7fd39535c8b"}, - {file = "python_rapidjson-1.21-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3c629e9ad36c6b37850ccef73ef99e06b6f1622ceb7c1f8e2f316529e5fb664b"}, - {file = "python_rapidjson-1.21-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0d4f99a48ba81e463176384d2b82de3f3480fb6acaf6dc283e5cb27bde4f5bb1"}, - {file = "python_rapidjson-1.21-cp39-cp39-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8f9d9d4d97deef3b9bdc88b661691a56f4aa87f5be2624338534ec939a4c7dec"}, - {file = "python_rapidjson-1.21-cp39-cp39-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:18baeaf6e6a8923e3967030a187791a7c2191a41bf0183d7e56fdc9bdbafc707"}, - {file = "python_rapidjson-1.21-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9a9a51d4b4c25eb0f4b2395bdbac937b0b76ec6997c85407ef63f88d700e7ae3"}, - {file = "python_rapidjson-1.21-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:9f03088e7c400465ecca19bf32df253b07ec5def8f3cb2b8f75b47827b231258"}, - {file = "python_rapidjson-1.21-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:f2c1c1c799138ce924da3e7808304905bacd8ab4ee08e3bafbbd71c83e57839a"}, - {file = "python_rapidjson-1.21-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:fd953da0e05cdf42ed5fc6fcfa7f4900b224ad70ba2ff9cce84f0d60bdabd45c"}, - {file = "python_rapidjson-1.21-cp39-cp39-win32.whl", hash = "sha256:97a02836c859bb71eb6e791ddd35c40d84f809948c7ee212c12842e62887c321"}, - {file = "python_rapidjson-1.21-cp39-cp39-win_amd64.whl", hash = "sha256:af85273105309df8b3fcb71c1919c648e21c8aed4315ea5bae238c7c8cae2281"}, - {file = "python_rapidjson-1.21.tar.gz", hash = "sha256:4d0dd9cf1fcb6f4bf79ee606e6e9be4cfa598f273b91338a6974b6a99309a1e6"}, -] - -[[package]] -name = "pytz" -version = "2025.2" -description = "World timezone definitions, modern and historical" -optional = true -python-versions = "*" -groups = ["main"] -markers = "extra == \"docs\"" -files = [ - {file = "pytz-2025.2-py2.py3-none-any.whl", hash = "sha256:5ddf76296dd8c44c26eb8f4b6f35488f3ccbf6fbbd7adee0b7262d43f0ec2f00"}, - {file = "pytz-2025.2.tar.gz", hash = "sha256:360b9e3dbb49a209c21ad61809c7fb453643e048b38924c765813546746e81c3"}, + {file = "python_rapidjson-1.23-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:dbb0958c5132d3def9d2be178dee45a2c41071573558348091bc539fe2671cd2"}, + {file = "python_rapidjson-1.23-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7f9314d155f342bc95912a0db9c1087a8d043ecceccaff33c21d257de4376f20"}, + {file = "python_rapidjson-1.23-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:332c972ceaaa7faff559d370702682ed20bbe36fdab477679b157a0f3c0391d5"}, + {file = "python_rapidjson-1.23-cp310-cp310-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c9f92eed9120b83acd7be9df932318de1c0d6e40d01594e9d855fef41cdb55c7"}, + {file = "python_rapidjson-1.23-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b79f9a4af92188098008623b17743bf8a300abeacb383d0424d070b4fbfbf83e"}, + {file = "python_rapidjson-1.23-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e60f8aa275d403a407abeb31a6bf319826c9f6b565a569a8dfb930fe0a2cca1a"}, + {file = "python_rapidjson-1.23-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:4c871e878c30f87077249d65ed65c0d5c24dd93c412f487cd95f648bec209909"}, + {file = "python_rapidjson-1.23-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:fc2b0c06b8e25528bbdeef45fda1ea373f62c277eb8f77518646e82ecc9b0718"}, + {file = "python_rapidjson-1.23-cp310-cp310-win32.whl", hash = "sha256:027c2bd096ac505c52a7ff1a7f2860b0ee451bc75e69777b49fd3d842efff544"}, + {file = "python_rapidjson-1.23-cp310-cp310-win_amd64.whl", hash = "sha256:6516b8538b2081bbaf737326dba472b354d1bf873adbb155fc7c735916761afb"}, + {file = "python_rapidjson-1.23-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6da3f5726c4ca30595b9ccab0e0bcfc18b624c61af5f6e3877b80e00bef62995"}, + {file = "python_rapidjson-1.23-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:316e1541c98af3af4bc0908cb9baaa44019dcedcad69a9944e2237607deb3ce5"}, + {file = "python_rapidjson-1.23-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e771315f7abd9c345c95f82dc013b6d60f041b6d708b26585a84e011dd5e092d"}, + {file = "python_rapidjson-1.23-cp311-cp311-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6ea4f914033440f4474931838b93890567f9214e26dd89d741b4260d1e243e56"}, + {file = "python_rapidjson-1.23-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d0702eae42704948320df851a35371db3e7a9494c123896cbb069cc8b56e3c4e"}, + {file = "python_rapidjson-1.23-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0cdc820cf2c7b5f4ac5ba4d697f049e0b93d2e9776b53b02a77051472d38cede"}, + {file = "python_rapidjson-1.23-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:016a35f2d93ee6be13c938ab4bc30dcf525c4ee6e4c3fbd5c75c2320fccea875"}, + {file = "python_rapidjson-1.23-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:40990adbf47bcb7f80b96b586e4ce114b497c19516a8e6a0700d61447ca8d28b"}, + {file = "python_rapidjson-1.23-cp311-cp311-win32.whl", hash = "sha256:8a2dc5faba744b643901489e82f037cef099be92b9d4d0eea597c1d5aea910be"}, + {file = "python_rapidjson-1.23-cp311-cp311-win_amd64.whl", hash = "sha256:6d2db055ed9728071117a8395ebb1552b937c3d5bbcf6f610420f9b6f926c654"}, + {file = "python_rapidjson-1.23-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:56e557fb6a7d7babfeb8ebaa4d096d4ce127477ecf46fe7de7f1edf2e1d8e4d6"}, + {file = "python_rapidjson-1.23-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d8e107121f5c1e98cb4f0e5fde443e0f66b45eadc3269bc2416e31261535f444"}, + {file = "python_rapidjson-1.23-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fc45ef1f725b3a9a27cdedcf9997f1f8c5a523ac03882d3925c6f764b33e5e1b"}, + {file = "python_rapidjson-1.23-cp312-cp312-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f87de7b994d65da2327fffdc5d3d7166782e3ca99c76c0560c8a7f1e109a5b54"}, + {file = "python_rapidjson-1.23-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6067810f0fd57713ec733b0b6ae265ef169e13b2ce04a4938b1807cddd8b4db4"}, + {file = "python_rapidjson-1.23-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:83306643cf31c0833b226d4317e8738b1b5ed4371e310f3c552be994c01a3df0"}, + {file = "python_rapidjson-1.23-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:13797fdcd43e558b81d3344c637bf878878fd6dede84409769d6910f8f6a9024"}, + {file = "python_rapidjson-1.23-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad674edb9dfe8181fb704a14149e5eb30ae179a92021484ebe8935b8d0f88495"}, + {file = "python_rapidjson-1.23-cp312-cp312-win32.whl", hash = "sha256:0c64958048ce714ccc42c659ef954812ed6de79fe4800322b3926ca46f60ffd9"}, + {file = "python_rapidjson-1.23-cp312-cp312-win_amd64.whl", hash = "sha256:cbb0a67a5330d28279a5c3b68068e901deedcd21ade0ec23be1bcc250948ae62"}, + {file = "python_rapidjson-1.23-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f13fd870fad2758c59a64e7b7efb2f037d447f31913a02569cf3e1e0efda1e29"}, + {file = "python_rapidjson-1.23-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7c8e16a0be2cb9736e92e61604bea62db8575da2946732bec63980b990503bfe"}, + {file = "python_rapidjson-1.23-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e7a333587073d576eef6bb685c0d25ea32316ca1dbf44adf6724596dad347658"}, + {file = "python_rapidjson-1.23-cp313-cp313-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:82e8be36193fc2c7769a6ce0678f8c494480f76dc84c8f387a24703f32b4b29b"}, + {file = "python_rapidjson-1.23-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b918f1672bfc08a93680b5bc70f2d60a4c2c001d29d105b3013865235c1d88fa"}, + {file = "python_rapidjson-1.23-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:171b173bfc1ce4896472ebc78c8caf1c77403cd7db5709370f1d49014a64dce4"}, + {file = "python_rapidjson-1.23-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:1ad6f00c3eb7c0b8f7a01efc7d9cc2bb7ee7a50029052950006cf4990f6a1869"}, + {file = "python_rapidjson-1.23-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:3981a92f1557fbaa0e6ae301fd0e27ddc1582a14316575d349a1354b67ff3668"}, + {file = "python_rapidjson-1.23-cp313-cp313-win32.whl", hash = "sha256:0c9d1b98f2def374bc2f29a94230dbb0c17a84b66187ad5183b3d43c68296b2f"}, + {file = "python_rapidjson-1.23-cp313-cp313-win_amd64.whl", hash = "sha256:0ae97758fd48da3ffbb631a4769dafe5a64e0e7091a64be0b8bc081b66a447e3"}, + {file = "python_rapidjson-1.23-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:8c0a23345d2a505e0ba3378b2568ba803eb41e2a78b7847221ab924f16e0cd17"}, + {file = "python_rapidjson-1.23-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:3dcea6b19b3eee6a9dc5d8c1a20c828fa18d9d265893ce4c1858e8a85af944aa"}, + {file = "python_rapidjson-1.23-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8272a1e89afa7b5be0d191c024d832262b5d1b479585253b4ebdec8181f54083"}, + {file = "python_rapidjson-1.23-cp314-cp314-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:0b0d4f24dc252edb73438241719020a57fc367a5607dcb776e80d43e0a22a779"}, + {file = "python_rapidjson-1.23-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bffe4bb5bd0a51a9d937cad299530aaa82b6fe102aab095504963e98b7714a47"}, + {file = "python_rapidjson-1.23-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:950eaa5f54d73b1963b7562888b251ec772812a3be8af157adde927462889fbb"}, + {file = "python_rapidjson-1.23-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:bb8e40cd40ecccd8d36e795eef8f197fee688ba1611f14c30f9b0ae64576a844"}, + {file = "python_rapidjson-1.23-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:c1432c363920e0fd5763cd802eb7797bddb990fc67578bbfd7d0c0b7b4837b2b"}, + {file = "python_rapidjson-1.23-cp314-cp314-win32.whl", hash = "sha256:882e67c03366a5d0a71531cf8e8f89a43fd774081080ce18b9880604696b10aa"}, + {file = "python_rapidjson-1.23-cp314-cp314-win_amd64.whl", hash = "sha256:11fa5f12404220e3bf9931add698ae599083924fb49406a77a59cec62e39378e"}, + {file = "python_rapidjson-1.23-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:8fd85ff2f1f18a310f7f257a3e995d18c2238ee3a637eec4a556fc692c4f8cf7"}, + {file = "python_rapidjson-1.23-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:26a2e3aa6fc2ebc7e587cc571f1c794b1bc58faaebf235d41987cd098ea63e7b"}, + {file = "python_rapidjson-1.23-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:20532558f71b834b971985517a1f63372a5824ca9ff0da421a1a66de8f91bcf4"}, + {file = "python_rapidjson-1.23-cp314-cp314t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a9ebd5aca46a9b53f1a074bcfcc0f9ecc37dace55d328ea6e5e5ff08daf3c710"}, + {file = "python_rapidjson-1.23-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c28b4a8e5e8ea8a9fa7325e5b5130ccb88f78e17b1cbf59a7a2edf6f6b4c55f9"}, + {file = "python_rapidjson-1.23-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:58a5a448ca3131433f8141a5da132ae9ae41629bc38a32709a10c56bba69689e"}, + {file = "python_rapidjson-1.23-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:0b9e3e58fc2ea7e1a06aa8856b0163a60b60e8b12f10678e5fcf8ba982c9de64"}, + {file = "python_rapidjson-1.23-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:965bbe74a1dd930a4e912b5064cf7cf646e2eefbbfba7c3d80dce397b3899db0"}, + {file = "python_rapidjson-1.23-cp314-cp314t-win32.whl", hash = "sha256:f0c520c81d53d7fc0e8d1e9213e2585b0c657513218575fad63421f0166c4aa3"}, + {file = "python_rapidjson-1.23-cp314-cp314t-win_amd64.whl", hash = "sha256:cac6c1166831c7984e27f42048c27befe4ebb3185554f5692e4812c899d94113"}, + {file = "python_rapidjson-1.23-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:938d1bd7448151732aa7c70b251dbe9b7b3246a1b8b80c4110e86903e450a5f9"}, + {file = "python_rapidjson-1.23-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:023ad77d11fd8023c03a038a4dbf734d6d54b74d9fa41414d93d0f696a77437d"}, + {file = "python_rapidjson-1.23-cp39-cp39-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2fe29411bbb63a15ea8c0003cbc82c185a4cf9602527d0fb026ccda10e440893"}, + {file = "python_rapidjson-1.23-cp39-cp39-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b90c2e51b3050062df91992c5fcdaf89def37450c41b067639a19d9aaa5fc85e"}, + {file = "python_rapidjson-1.23-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8956da1444a45ee5c4596b1574c50d6dc09a0874f388b975b726ddd0c4068c16"}, + {file = "python_rapidjson-1.23-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:29270878291beb42b6ac9abead70323780a44494d3451f820ffd81b3812f603c"}, + {file = "python_rapidjson-1.23-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:63c072656336a0046a4176dd2a961dc26fe81a80c39c5908561820763fe3e632"}, + {file = "python_rapidjson-1.23-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:71b0d9eb4529610dec6b23d21056be5a6d3fe143bef177f8434df3a647774716"}, + {file = "python_rapidjson-1.23-cp39-cp39-win32.whl", hash = "sha256:f0ceef3cd234754cc224ef73fcd1e9faa88ea278c5c1a578301ab31816345243"}, + {file = "python_rapidjson-1.23-cp39-cp39-win_amd64.whl", hash = "sha256:d50ba0791f5b2d6525cc6d2bbc9f4dca5d9991271ca57c6d1a82d782ce67e31b"}, + {file = "python_rapidjson-1.23.tar.gz", hash = "sha256:0f845daeb26be147f5720a8c410308235092bb4fbb81ea408aa77203e26296fb"}, ] [[package]] @@ -3091,7 +2886,7 @@ description = "Python for Window Extensions" optional = true python-versions = "*" groups = ["main"] -markers = "(sys_platform == \"win32\" or platform_system == \"Windows\") and platform_python_implementation != \"PyPy\" and extra == \"docs\"" +markers = "platform_system == \"Windows\" and platform_python_implementation != \"PyPy\" and extra == \"docs\"" files = [ {file = "pywin32-311-cp310-cp310-win32.whl", hash = "sha256:d03ff496d2a0cd4a5893504789d4a15399133fe82517455e78bad62efbb7f0a3"}, {file = "pywin32-311-cp310-cp310-win_amd64.whl", hash = "sha256:797c2772017851984b97180b0bebe4b620bb86328e8a884bb626156295a63b3b"}, @@ -3306,33 +3101,29 @@ cffi = {version = "*", markers = "implementation_name == \"pypy\""} [[package]] name = "qcs-api-client-common" -version = "0.11.8" +version = "0.17.5" description = "Contains core QCS client functionality and middleware implementations." optional = false -python-versions = "*" +python-versions = "<4,>=3.10" groups = ["main"] files = [ - {file = "qcs_api_client_common-0.11.8-cp310-cp310-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:52be55375a650d88d16ff9b0fb9a93bdf4272c0ecaba3d0b75ecdfd8a8adec88"}, - {file = "qcs_api_client_common-0.11.8-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:176d06a1758afbf78a4896ebeecfdeee41dce63950b869fdcb0c56b6665bb16f"}, - {file = "qcs_api_client_common-0.11.8-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:72f7d1154778350b64f9a0b5a5fba078d51b8f55fa8401c2a04d8bf470b7c42f"}, - {file = "qcs_api_client_common-0.11.8-cp310-cp310-win_amd64.whl", hash = "sha256:4173ec105b91cd5ecb37bec629334e5696c80382441729b1932f5840ff492209"}, - {file = "qcs_api_client_common-0.11.8-cp311-cp311-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:cb7efcd32cee06c874a70da0de857c0647c6f5a3e8815834ef433caeb4ebdc04"}, - {file = "qcs_api_client_common-0.11.8-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:a013c240d1e0f47805d346cd88294a1cf4a806deea84509e8cb0953ca20fa073"}, - {file = "qcs_api_client_common-0.11.8-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:bafaf69c8f0d4bd5957a38a92fa99d9af2145787d6233eda13015869991507e1"}, - {file = "qcs_api_client_common-0.11.8-cp311-cp311-win_amd64.whl", hash = "sha256:ddadfc820f1647b964bd7b487ef8cce182f835570ec2e80666be947c265a5fc2"}, - {file = "qcs_api_client_common-0.11.8-cp312-cp312-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:580061d2906c8966524a22e7cd68db04fa529103a6e027603830ea00202bdf87"}, - {file = "qcs_api_client_common-0.11.8-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:b64b685fa8bced69a84f143a82b01ddd10e7a64a9ef2320b375558014d30414e"}, - {file = "qcs_api_client_common-0.11.8-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:41a1c3c1ec3b0f0180d7b18fd3a74d8e09a7a816572fbfcd08ceb0b57efab328"}, - {file = "qcs_api_client_common-0.11.8-cp312-cp312-win_amd64.whl", hash = "sha256:b076e621a3e1294523f8e99dff9a48f06ff3ec3cafb45c04d90197d9e5391bc9"}, - {file = "qcs_api_client_common-0.11.8-cp38-cp38-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:3a6d96a76c40c2b5d866d934edce20adaa7ab98e2342aa67fb1ba772acd2dd00"}, - {file = "qcs_api_client_common-0.11.8-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:37491fadbe62a30e032d117cbf011d423beead81c44f5355634b2070f5dfcd15"}, - {file = "qcs_api_client_common-0.11.8-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:f78ac5066070845e541c6e27cb22865bb0d0405cceb0b8e39e4b6e2826e2a6fb"}, - {file = "qcs_api_client_common-0.11.8-cp38-cp38-win_amd64.whl", hash = "sha256:0c4c4c9ea687120256ad79acc007a16ec24afb6513b7e7c023d1e92f6674cee9"}, - {file = "qcs_api_client_common-0.11.8-cp39-cp39-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:2c40adfdf1535c8620e5baa71fcc0f0d1c5127c4322bc5b16c7d78f7b72b9c42"}, - {file = "qcs_api_client_common-0.11.8-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:facbc40de88e23c5775b2d11f34fb4762e463d9a43f1dddddd449fb6957c0a28"}, - {file = "qcs_api_client_common-0.11.8-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:132ba3111893dceca926122c6e58edb6294cedfd809b7e01789d87a64279372c"}, - {file = "qcs_api_client_common-0.11.8-cp39-cp39-win_amd64.whl", hash = "sha256:96ebf74879b707ee5b5d2567a16d1c668ba80327ac8e7116e73b682b9e293cec"}, - {file = "qcs_api_client_common-0.11.8.tar.gz", hash = "sha256:cd255c3759caedf7c641b5ba7b62aa25bdb3b32df040d66014315460dbc33309"}, + {file = "qcs_api_client_common-0.17.5-cp310-cp310-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:4b4fd6f8495eaea3c305ed9708d6d6cc15dd92ec227e1bca43521aa855d92239"}, + {file = "qcs_api_client_common-0.17.5-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:45f17f51344c10ee4dac3486fc6d1753fca0716f79e6de47ccfa76592c11b9a4"}, + {file = "qcs_api_client_common-0.17.5-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:8e9650ed50faed7e3cfeebf4374b27f0f1409acfd022b1fdf525162031eb4983"}, + {file = "qcs_api_client_common-0.17.5-cp310-cp310-win_amd64.whl", hash = "sha256:6a9573dd6ac9a342d8c189386fa2377407d338f25257969911d2136d782d520c"}, + {file = "qcs_api_client_common-0.17.5-cp311-cp311-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:212d41c6a70584c91b46b970a0de3cbd3a1900aba44a92648813a8053615f28d"}, + {file = "qcs_api_client_common-0.17.5-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:101951a8eff305869ebd7575cbf3b02909e8d1bf2da0580f7eb49ed9edf3bfa0"}, + {file = "qcs_api_client_common-0.17.5-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:37aa7f6c76e917890158ba57ef2600b0069aeceefa7d3c74f2c45cfabd6f2fed"}, + {file = "qcs_api_client_common-0.17.5-cp311-cp311-win_amd64.whl", hash = "sha256:deba7add73c9926c5d3589a581f983c05b6a03a65db711c2270342a56d635aac"}, + {file = "qcs_api_client_common-0.17.5-cp312-cp312-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:2f7f563fbb3498b03faef1517358e8c7f56c43bafbc73fa66c4325fd8e6c6ec7"}, + {file = "qcs_api_client_common-0.17.5-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:3103e61a2302d5dfa688315d29ef0f4f7f234aab4f70cfbc75c73318c2eead62"}, + {file = "qcs_api_client_common-0.17.5-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:6136f786bc5262eaa510e0683a7ade1b8bfc6b2b850e7333a45a80d397c03099"}, + {file = "qcs_api_client_common-0.17.5-cp312-cp312-win_amd64.whl", hash = "sha256:92ef06de9948ffa978231a0412daecdde70b26450d1f51618015887675e2e84e"}, + {file = "qcs_api_client_common-0.17.5-cp313-cp313-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:e2f63c4bbad2936a54478c2f4f026fca8d6c76462bee34a01854cb9b1bf1e5da"}, + {file = "qcs_api_client_common-0.17.5-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:c9252fcb6ab258c1878e5f978a6983af7941a12defd38304d4d09383641ee9b9"}, + {file = "qcs_api_client_common-0.17.5-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:9480cc4e5d64b16831383608f6251fda94886a65be17da37fa50a41fc57a51c5"}, + {file = "qcs_api_client_common-0.17.5-cp313-cp313-win_amd64.whl", hash = "sha256:c7aaa84479f326ce13728cba1c0f3c3bcf314c6267b3d45e05f63a51b703b239"}, + {file = "qcs_api_client_common-0.17.5.tar.gz", hash = "sha256:6d39b53cc83f11ffebb5f80a1618c1b3ed5a286dbfedb6a0bc6ed8534f32de47"}, ] [package.dependencies] @@ -3340,43 +3131,40 @@ grpc-interceptor = ">=0.15.0" grpcio = ">=1.63.0" httpx = ">=0.27.0" -[package.extras] -dev = ["furo (>=2024.5.6)", "grpcio-testing (>=1.63.0)", "maturin (>=1.5.1)", "mypy (>=1.4.1)", "pytest (>=8.1.1)", "pytest-asyncio (>=0.23.6)", "pytest-clarity (>=1.0.1)", "pytest-mock (>=3.14.0)", "pytest-sugar (>=1.0.0)", "ruff (>=0.3.5)", "sphinx (>=7.3.7)", "sphinx-autoapi (>=3.0.0)", "syrupy (>=4.0.0)", "watchdog[watchmedo] (>=4.0.0)"] - [[package]] name = "qcs-sdk-python" -version = "0.21.19" +version = "0.21.22" description = "Python interface for the QCS Rust SDK" optional = false python-versions = "*" groups = ["main"] files = [ - {file = "qcs_sdk_python-0.21.19-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:37e9b582f251844bc4c118f80eaf91c2dc449edc519c98493d4af3338ded831a"}, - {file = "qcs_sdk_python-0.21.19-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7c696e97db02eac75a946edd01341fb32658fb4e1c05c72d7149f45b50cd2837"}, - {file = "qcs_sdk_python-0.21.19-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:60c9225ba058bdf2bab8f4bb0016a85d248f8dc974d717c2d36e231693054db4"}, - {file = "qcs_sdk_python-0.21.19-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:506d8cf0447f6e71b3a468bbe2bc60be54eb31bcc8f5165c7792c5df7962d67a"}, - {file = "qcs_sdk_python-0.21.19-cp310-cp310-win_amd64.whl", hash = "sha256:e1dc4d4011323c55acb1b8e37409457f7fdb1e51643c6b33b922588b4b9ca2a8"}, - {file = "qcs_sdk_python-0.21.19-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:9fb1c2d8fdda64dcab431e0c5c4da3d92d8a0ef0cf3c5e366c23ddc5d60b70e0"}, - {file = "qcs_sdk_python-0.21.19-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7ff06a2addac593d54759192d03ac30ef4818f4c3ef3e08db0eba547fafe78b1"}, - {file = "qcs_sdk_python-0.21.19-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:13764abef25716ccc03d0a2499b761eecc8e310110a6dd0d9cd475e45549b2f5"}, - {file = "qcs_sdk_python-0.21.19-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:70674a0fc61e83f00e794972cbf5386b161622497d7609ef0e131aa25c17df8e"}, - {file = "qcs_sdk_python-0.21.19-cp311-cp311-win_amd64.whl", hash = "sha256:f2844c2892d75599f1fb8ffff7e7ee38f7f357dfca57f178be8f5418af5f1ba4"}, - {file = "qcs_sdk_python-0.21.19-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:e604f7559362235ec209d109324a57fe4d3b84e566ec524282d584d2681f6cb7"}, - {file = "qcs_sdk_python-0.21.19-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a2919fb3869d3eca07a4e61dd638ce81e4c5eacaa100c855ec6f13b2c90e552c"}, - {file = "qcs_sdk_python-0.21.19-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:b74ce5b676fb9f84b8a6320e240d5f8afda0e0bd4a8c8ed7b00358f588c3b08b"}, - {file = "qcs_sdk_python-0.21.19-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:36978e53ea4fa3120c8363bf1f81f1915aa095f117c7348c7636ccff1a905aed"}, - {file = "qcs_sdk_python-0.21.19-cp312-cp312-win_amd64.whl", hash = "sha256:5df8a32c4c6f6c042c64e27df964bcfba8b212084e0bcabad28e62e58e8be07e"}, - {file = "qcs_sdk_python-0.21.19-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:b66d25013c2b5fea6eb4d5e04b9d9f78384434ae1ecdc80ba7c33d6b3714665e"}, - {file = "qcs_sdk_python-0.21.19-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7603b4200a3d773c313a85a54d5cfab35f00c3bf43dc0fcfc8a5786262e472bf"}, - {file = "qcs_sdk_python-0.21.19-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:a7814e874dbfd70b4c748e489a88c97b2540ffd09cc4e68427d86bbc943027b3"}, - {file = "qcs_sdk_python-0.21.19-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:5738f6cbf7d3c23938cb4dd98640566819b6c2eda54e58d78158cba9dc81dd5c"}, - {file = "qcs_sdk_python-0.21.19-cp38-cp38-win_amd64.whl", hash = "sha256:f0f907e13dc6afc6ac1ebfd960595fce91e9d94ca0308f1a3d6b4d5a48cb9cef"}, - {file = "qcs_sdk_python-0.21.19-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:39cb2bfb3be9e7a58f138c5d78b15c7d2f4196f62db896d43a8c50bd5079551d"}, - {file = "qcs_sdk_python-0.21.19-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0febee697e78dc0a84aee62cc7b378b2afeaa1dc3bc0c5c5964266831afc910c"}, - {file = "qcs_sdk_python-0.21.19-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:eec825100236f6d4a903cf165d7aca74b86c9b970e2097679990caee626151fb"}, - {file = "qcs_sdk_python-0.21.19-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:6e2830129efb2a71143bf853c4a8411bfbfd4873e1d89cb39fba91abb7257b56"}, - {file = "qcs_sdk_python-0.21.19-cp39-cp39-win_amd64.whl", hash = "sha256:53fb8137b0defeb508a68ebb0d755eeca9101c2c5b877eba1d408b34be7a1f77"}, - {file = "qcs_sdk_python-0.21.19.tar.gz", hash = "sha256:8a1d6a3c13a4becabd0b0c8b063191bfed7e1d5c7de87db7518b79400c2ec601"}, + {file = "qcs_sdk_python-0.21.22-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2cb91c482b0fa9305f54bed35acfc7e75b62b9826d0686dfe21b2db9e96c2535"}, + {file = "qcs_sdk_python-0.21.22-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6096bda4a7901d58505bbf44603b70a95b00d995ef2ddfd4d445f3ce0c746e37"}, + {file = "qcs_sdk_python-0.21.22-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:62aafe936bf60f053f2ad2ecef8399eed6e8a7d49e7f2cb19af0c9e308205cc6"}, + {file = "qcs_sdk_python-0.21.22-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:ca85831da9f25ed837c606b63f9b6ced5cb7b9241bd596451607643766acf158"}, + {file = "qcs_sdk_python-0.21.22-cp310-cp310-win_amd64.whl", hash = "sha256:70f385b3329a6ab621a5aac56e02ed836657816edf8a2082ed04fff45b0f5870"}, + {file = "qcs_sdk_python-0.21.22-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:35e1aef030083c1b58f4b95ad7da89ab939617da3ad98093d87298dbffea8994"}, + {file = "qcs_sdk_python-0.21.22-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:121cd94a8e09ce125bcc287efe56555eee350365be719961d05c51a456af4d00"}, + {file = "qcs_sdk_python-0.21.22-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:1113f957445f1663e5c0431fa1ee1cb6b08ce7574dc519203acb3c39b8a41678"}, + {file = "qcs_sdk_python-0.21.22-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:cf1a6578b1b4382175f0db25d86e21e13c5b7b229396aceb2f5b264dc8b9b317"}, + {file = "qcs_sdk_python-0.21.22-cp311-cp311-win_amd64.whl", hash = "sha256:7a37b473f52bc4006369108e37ccc82357a727566faa7c494859f0a615cc3c4c"}, + {file = "qcs_sdk_python-0.21.22-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:bd3998d7d8a9c09703667e65a72f2994b6e01aeed7f95bf42e828d22d734cdab"}, + {file = "qcs_sdk_python-0.21.22-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:14c1a08550a67570b3965025228489e4400b123593442c677a1d0d8ea0ecb7ed"}, + {file = "qcs_sdk_python-0.21.22-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:e0e55b477d5d053334aede3dd7f1613fb9c547d09d560ae7f43a67cdfe0d09a8"}, + {file = "qcs_sdk_python-0.21.22-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:d8c75597e8b3e87477f48a1554b3a34b8c289133853a2bec5db0c9561e96332c"}, + {file = "qcs_sdk_python-0.21.22-cp312-cp312-win_amd64.whl", hash = "sha256:becdb70a11345a0e13b072b57f5de1630be4495c5972d11d5d5d037500180fdd"}, + {file = "qcs_sdk_python-0.21.22-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:9c37818ca9b511ddbbf80be2ed1c8eb3d0f59f5e3a3e9dc5c4ee017295ca1a80"}, + {file = "qcs_sdk_python-0.21.22-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:bae9af7b1271ae32fccecca6c3dc2b8239d9b9703cc23567a9177ab5650c2010"}, + {file = "qcs_sdk_python-0.21.22-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:54a80a96a3a2f83296aef2766829b9aea4c80eb185e925721711a9dc27873447"}, + {file = "qcs_sdk_python-0.21.22-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:98a8ebb898d2d2ae84ec1ab960afe91af7b08e744c260930782455dad1da9485"}, + {file = "qcs_sdk_python-0.21.22-cp38-cp38-win_amd64.whl", hash = "sha256:22c1d8692eef9c207a50f4c1a66f8b15e87d56adc6ad07d8a8db3388ceb5c818"}, + {file = "qcs_sdk_python-0.21.22-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:133548d3ceaaca71fc93161bb5213b5d843fbb79ff5dfaf2e06eacbd5b79abed"}, + {file = "qcs_sdk_python-0.21.22-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6d4f2585756aec307d461d5465eee8fba3fb6636bfe55b48c1876d9ec075522f"}, + {file = "qcs_sdk_python-0.21.22-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:ad0ee18426c368a960b519d390fe4ca82cf32c3f7be747d41568e4e9fe298623"}, + {file = "qcs_sdk_python-0.21.22-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:353f7a485e40df974d2df19ff30b9b753f466469f611a1e46de9324e8ed90be6"}, + {file = "qcs_sdk_python-0.21.22-cp39-cp39-win_amd64.whl", hash = "sha256:882fb9600f0993027de7de49128bdd25d579acc2faaf4a6b4d7c09eab2806467"}, + {file = "qcs_sdk_python-0.21.22.tar.gz", hash = "sha256:cdedde65abe084f2aa049cbf214dbe45a0f35505147a26f3c9373b598c2ea918"}, ] [package.dependencies] @@ -3431,15 +3219,15 @@ dev = ["maturin (>=1.2.3)", "mypy (>=1.13.0)", "pdoc (>=14.1.0)", "pytest (>=7.2 [[package]] name = "referencing" -version = "0.36.2" +version = "0.37.0" description = "JSON Referencing + Python" optional = true -python-versions = ">=3.9" +python-versions = ">=3.10" groups = ["main"] markers = "extra == \"docs\"" files = [ - {file = "referencing-0.36.2-py3-none-any.whl", hash = "sha256:e8699adbbf8b5c7de96d8ffa0eb5c158b3beafce084968e2ea8bb08c6794dcd0"}, - {file = "referencing-0.36.2.tar.gz", hash = "sha256:df2e89862cd09deabbdba16944cc3f10feb6b3e6f18e902f7cc25609a34775aa"}, + {file = "referencing-0.37.0-py3-none-any.whl", hash = "sha256:381329a9f99628c9069361716891d34ad94af76e461dcb0335825aecc7692231"}, + {file = "referencing-0.37.0.tar.gz", hash = "sha256:44aefc3142c5b842538163acb373e24cce6632bd54bdb01b21ad5863489f50d8"}, ] [package.dependencies] @@ -3449,26 +3237,26 @@ typing-extensions = {version = ">=4.4.0", markers = "python_version < \"3.13\""} [[package]] name = "requests" -version = "2.32.5" +version = "2.33.1" description = "Python HTTP for Humans." optional = true -python-versions = ">=3.9" +python-versions = ">=3.10" groups = ["main"] markers = "extra == \"docs\"" files = [ - {file = "requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6"}, - {file = "requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf"}, + {file = "requests-2.33.1-py3-none-any.whl", hash = "sha256:4e6d1ef462f3626a1f0a0a9c42dd93c63bad33f9f1c1937509b8c5c8718ab56a"}, + {file = "requests-2.33.1.tar.gz", hash = "sha256:18817f8c57c6263968bc123d237e3b8b08ac046f5456bd1e307ee8f4250d3517"}, ] [package.dependencies] -certifi = ">=2017.4.17" +certifi = ">=2023.5.7" charset_normalizer = ">=2,<4" idna = ">=2.5,<4" -urllib3 = ">=1.21.1,<3" +urllib3 = ">=1.26,<3" [package.extras] socks = ["PySocks (>=1.5.6,!=1.5.7)"] -use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] +use-chardet-on-py3 = ["chardet (>=3.0.2,<8)"] [[package]] name = "respx" @@ -3485,6 +3273,24 @@ files = [ [package.dependencies] httpx = ">=0.21.0" +[[package]] +name = "rigetti-quax" +version = "0.6.6" +description = "A high-performance library for quantum information science built on top of JAX" +optional = false +python-versions = "<4.0,>=3.11" +groups = ["main"] +files = [ + {file = "rigetti_quax-0.6.6-py3-none-any.whl", hash = "sha256:9ea85ba2f5703c39010332ceec70fde4421ff601eb371b4403921dda3460e6fb"}, + {file = "rigetti_quax-0.6.6.tar.gz", hash = "sha256:bba5a9a2e7f567e1abfcd4507413a98155410cf7792eae98f7a5c88ef3b19b21"}, +] + +[package.dependencies] +jax = ">=0.8.2" + +[package.extras] +plot = ["plotly (>=5.20)"] + [[package]] name = "rpcq" version = "3.11.0" @@ -3504,254 +3310,147 @@ pyzmq = ">=17" [[package]] name = "rpds-py" -version = "0.27.1" +version = "0.30.0" description = "Python bindings to Rust's persistent data structures (rpds)" optional = true -python-versions = ">=3.9" +python-versions = ">=3.10" groups = ["main"] markers = "extra == \"docs\"" files = [ - {file = "rpds_py-0.27.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:68afeec26d42ab3b47e541b272166a0b4400313946871cba3ed3a4fc0cab1cef"}, - {file = "rpds_py-0.27.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:74e5b2f7bb6fa38b1b10546d27acbacf2a022a8b5543efb06cfebc72a59c85be"}, - {file = "rpds_py-0.27.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9024de74731df54546fab0bfbcdb49fae19159ecaecfc8f37c18d2c7e2c0bd61"}, - {file = "rpds_py-0.27.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:31d3ebadefcd73b73928ed0b2fd696f7fefda8629229f81929ac9c1854d0cffb"}, - {file = "rpds_py-0.27.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b2e7f8f169d775dd9092a1743768d771f1d1300453ddfe6325ae3ab5332b4657"}, - {file = "rpds_py-0.27.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d905d16f77eb6ab2e324e09bfa277b4c8e5e6b8a78a3e7ff8f3cdf773b4c013"}, - {file = "rpds_py-0.27.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:50c946f048209e6362e22576baea09193809f87687a95a8db24e5fbdb307b93a"}, - {file = "rpds_py-0.27.1-cp310-cp310-manylinux_2_31_riscv64.whl", hash = "sha256:3deab27804d65cd8289eb814c2c0e807c4b9d9916c9225e363cb0cf875eb67c1"}, - {file = "rpds_py-0.27.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8b61097f7488de4be8244c89915da8ed212832ccf1e7c7753a25a394bf9b1f10"}, - {file = "rpds_py-0.27.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:8a3f29aba6e2d7d90528d3c792555a93497fe6538aa65eb675b44505be747808"}, - {file = "rpds_py-0.27.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:dd6cd0485b7d347304067153a6dc1d73f7d4fd995a396ef32a24d24b8ac63ac8"}, - {file = "rpds_py-0.27.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:6f4461bf931108c9fa226ffb0e257c1b18dc2d44cd72b125bec50ee0ab1248a9"}, - {file = "rpds_py-0.27.1-cp310-cp310-win32.whl", hash = "sha256:ee5422d7fb21f6a00c1901bf6559c49fee13a5159d0288320737bbf6585bd3e4"}, - {file = "rpds_py-0.27.1-cp310-cp310-win_amd64.whl", hash = "sha256:3e039aabf6d5f83c745d5f9a0a381d031e9ed871967c0a5c38d201aca41f3ba1"}, - {file = "rpds_py-0.27.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:be898f271f851f68b318872ce6ebebbc62f303b654e43bf72683dbdc25b7c881"}, - {file = "rpds_py-0.27.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:62ac3d4e3e07b58ee0ddecd71d6ce3b1637de2d373501412df395a0ec5f9beb5"}, - {file = "rpds_py-0.27.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4708c5c0ceb2d034f9991623631d3d23cb16e65c83736ea020cdbe28d57c0a0e"}, - {file = "rpds_py-0.27.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:abfa1171a9952d2e0002aba2ad3780820b00cc3d9c98c6630f2e93271501f66c"}, - {file = "rpds_py-0.27.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4b507d19f817ebaca79574b16eb2ae412e5c0835542c93fe9983f1e432aca195"}, - {file = "rpds_py-0.27.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:168b025f8fd8d8d10957405f3fdcef3dc20f5982d398f90851f4abc58c566c52"}, - {file = "rpds_py-0.27.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cb56c6210ef77caa58e16e8c17d35c63fe3f5b60fd9ba9d424470c3400bcf9ed"}, - {file = "rpds_py-0.27.1-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:d252f2d8ca0195faa707f8eb9368955760880b2b42a8ee16d382bf5dd807f89a"}, - {file = "rpds_py-0.27.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6e5e54da1e74b91dbc7996b56640f79b195d5925c2b78efaa8c5d53e1d88edde"}, - {file = "rpds_py-0.27.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ffce0481cc6e95e5b3f0a47ee17ffbd234399e6d532f394c8dce320c3b089c21"}, - {file = "rpds_py-0.27.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:a205fdfe55c90c2cd8e540ca9ceba65cbe6629b443bc05db1f590a3db8189ff9"}, - {file = "rpds_py-0.27.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:689fb5200a749db0415b092972e8eba85847c23885c8543a8b0f5c009b1a5948"}, - {file = "rpds_py-0.27.1-cp311-cp311-win32.whl", hash = "sha256:3182af66048c00a075010bc7f4860f33913528a4b6fc09094a6e7598e462fe39"}, - {file = "rpds_py-0.27.1-cp311-cp311-win_amd64.whl", hash = "sha256:b4938466c6b257b2f5c4ff98acd8128ec36b5059e5c8f8372d79316b1c36bb15"}, - {file = "rpds_py-0.27.1-cp311-cp311-win_arm64.whl", hash = "sha256:2f57af9b4d0793e53266ee4325535a31ba48e2f875da81a9177c9926dfa60746"}, - {file = "rpds_py-0.27.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:ae2775c1973e3c30316892737b91f9283f9908e3cc7625b9331271eaaed7dc90"}, - {file = "rpds_py-0.27.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2643400120f55c8a96f7c9d858f7be0c88d383cd4653ae2cf0d0c88f668073e5"}, - {file = "rpds_py-0.27.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:16323f674c089b0360674a4abd28d5042947d54ba620f72514d69be4ff64845e"}, - {file = "rpds_py-0.27.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9a1f4814b65eacac94a00fc9a526e3fdafd78e439469644032032d0d63de4881"}, - {file = "rpds_py-0.27.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7ba32c16b064267b22f1850a34051121d423b6f7338a12b9459550eb2096e7ec"}, - {file = "rpds_py-0.27.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e5c20f33fd10485b80f65e800bbe5f6785af510b9f4056c5a3c612ebc83ba6cb"}, - {file = "rpds_py-0.27.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:466bfe65bd932da36ff279ddd92de56b042f2266d752719beb97b08526268ec5"}, - {file = "rpds_py-0.27.1-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:41e532bbdcb57c92ba3be62c42e9f096431b4cf478da9bc3bc6ce5c38ab7ba7a"}, - {file = "rpds_py-0.27.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f149826d742b406579466283769a8ea448eed82a789af0ed17b0cd5770433444"}, - {file = "rpds_py-0.27.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:80c60cfb5310677bd67cb1e85a1e8eb52e12529545441b43e6f14d90b878775a"}, - {file = "rpds_py-0.27.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:7ee6521b9baf06085f62ba9c7a3e5becffbc32480d2f1b351559c001c38ce4c1"}, - {file = "rpds_py-0.27.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a512c8263249a9d68cac08b05dd59d2b3f2061d99b322813cbcc14c3c7421998"}, - {file = "rpds_py-0.27.1-cp312-cp312-win32.whl", hash = "sha256:819064fa048ba01b6dadc5116f3ac48610435ac9a0058bbde98e569f9e785c39"}, - {file = "rpds_py-0.27.1-cp312-cp312-win_amd64.whl", hash = "sha256:d9199717881f13c32c4046a15f024971a3b78ad4ea029e8da6b86e5aa9cf4594"}, - {file = "rpds_py-0.27.1-cp312-cp312-win_arm64.whl", hash = "sha256:33aa65b97826a0e885ef6e278fbd934e98cdcfed80b63946025f01e2f5b29502"}, - {file = "rpds_py-0.27.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:e4b9fcfbc021633863a37e92571d6f91851fa656f0180246e84cbd8b3f6b329b"}, - {file = "rpds_py-0.27.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1441811a96eadca93c517d08df75de45e5ffe68aa3089924f963c782c4b898cf"}, - {file = "rpds_py-0.27.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55266dafa22e672f5a4f65019015f90336ed31c6383bd53f5e7826d21a0e0b83"}, - {file = "rpds_py-0.27.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d78827d7ac08627ea2c8e02c9e5b41180ea5ea1f747e9db0915e3adf36b62dcf"}, - {file = "rpds_py-0.27.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae92443798a40a92dc5f0b01d8a7c93adde0c4dc965310a29ae7c64d72b9fad2"}, - {file = "rpds_py-0.27.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c46c9dd2403b66a2a3b9720ec4b74d4ab49d4fabf9f03dfdce2d42af913fe8d0"}, - {file = "rpds_py-0.27.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2efe4eb1d01b7f5f1939f4ef30ecea6c6b3521eec451fb93191bf84b2a522418"}, - {file = "rpds_py-0.27.1-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:15d3b4d83582d10c601f481eca29c3f138d44c92187d197aff663a269197c02d"}, - {file = "rpds_py-0.27.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4ed2e16abbc982a169d30d1a420274a709949e2cbdef119fe2ec9d870b42f274"}, - {file = "rpds_py-0.27.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a75f305c9b013289121ec0f1181931975df78738cdf650093e6b86d74aa7d8dd"}, - {file = "rpds_py-0.27.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:67ce7620704745881a3d4b0ada80ab4d99df390838839921f99e63c474f82cf2"}, - {file = "rpds_py-0.27.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9d992ac10eb86d9b6f369647b6a3f412fc0075cfd5d799530e84d335e440a002"}, - {file = "rpds_py-0.27.1-cp313-cp313-win32.whl", hash = "sha256:4f75e4bd8ab8db624e02c8e2fc4063021b58becdbe6df793a8111d9343aec1e3"}, - {file = "rpds_py-0.27.1-cp313-cp313-win_amd64.whl", hash = "sha256:f9025faafc62ed0b75a53e541895ca272815bec18abe2249ff6501c8f2e12b83"}, - {file = "rpds_py-0.27.1-cp313-cp313-win_arm64.whl", hash = "sha256:ed10dc32829e7d222b7d3b93136d25a406ba9788f6a7ebf6809092da1f4d279d"}, - {file = "rpds_py-0.27.1-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:92022bbbad0d4426e616815b16bc4127f83c9a74940e1ccf3cfe0b387aba0228"}, - {file = "rpds_py-0.27.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:47162fdab9407ec3f160805ac3e154df042e577dd53341745fc7fb3f625e6d92"}, - {file = "rpds_py-0.27.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb89bec23fddc489e5d78b550a7b773557c9ab58b7946154a10a6f7a214a48b2"}, - {file = "rpds_py-0.27.1-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e48af21883ded2b3e9eb48cb7880ad8598b31ab752ff3be6457001d78f416723"}, - {file = "rpds_py-0.27.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6f5b7bd8e219ed50299e58551a410b64daafb5017d54bbe822e003856f06a802"}, - {file = "rpds_py-0.27.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:08f1e20bccf73b08d12d804d6e1c22ca5530e71659e6673bce31a6bb71c1e73f"}, - {file = "rpds_py-0.27.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0dc5dceeaefcc96dc192e3a80bbe1d6c410c469e97bdd47494a7d930987f18b2"}, - {file = "rpds_py-0.27.1-cp313-cp313t-manylinux_2_31_riscv64.whl", hash = "sha256:d76f9cc8665acdc0c9177043746775aa7babbf479b5520b78ae4002d889f5c21"}, - {file = "rpds_py-0.27.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:134fae0e36022edad8290a6661edf40c023562964efea0cc0ec7f5d392d2aaef"}, - {file = "rpds_py-0.27.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:eb11a4f1b2b63337cfd3b4d110af778a59aae51c81d195768e353d8b52f88081"}, - {file = "rpds_py-0.27.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:13e608ac9f50a0ed4faec0e90ece76ae33b34c0e8656e3dceb9a7db994c692cd"}, - {file = "rpds_py-0.27.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:dd2135527aa40f061350c3f8f89da2644de26cd73e4de458e79606384f4f68e7"}, - {file = "rpds_py-0.27.1-cp313-cp313t-win32.whl", hash = "sha256:3020724ade63fe320a972e2ffd93b5623227e684315adce194941167fee02688"}, - {file = "rpds_py-0.27.1-cp313-cp313t-win_amd64.whl", hash = "sha256:8ee50c3e41739886606388ba3ab3ee2aae9f35fb23f833091833255a31740797"}, - {file = "rpds_py-0.27.1-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:acb9aafccaae278f449d9c713b64a9e68662e7799dbd5859e2c6b3c67b56d334"}, - {file = "rpds_py-0.27.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:b7fb801aa7f845ddf601c49630deeeccde7ce10065561d92729bfe81bd21fb33"}, - {file = "rpds_py-0.27.1-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fe0dd05afb46597b9a2e11c351e5e4283c741237e7f617ffb3252780cca9336a"}, - {file = "rpds_py-0.27.1-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b6dfb0e058adb12d8b1d1b25f686e94ffa65d9995a5157afe99743bf7369d62b"}, - {file = "rpds_py-0.27.1-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ed090ccd235f6fa8bb5861684567f0a83e04f52dfc2e5c05f2e4b1309fcf85e7"}, - {file = "rpds_py-0.27.1-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bf876e79763eecf3e7356f157540d6a093cef395b65514f17a356f62af6cc136"}, - {file = "rpds_py-0.27.1-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:12ed005216a51b1d6e2b02a7bd31885fe317e45897de81d86dcce7d74618ffff"}, - {file = "rpds_py-0.27.1-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:ee4308f409a40e50593c7e3bb8cbe0b4d4c66d1674a316324f0c2f5383b486f9"}, - {file = "rpds_py-0.27.1-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0b08d152555acf1f455154d498ca855618c1378ec810646fcd7c76416ac6dc60"}, - {file = "rpds_py-0.27.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:dce51c828941973a5684d458214d3a36fcd28da3e1875d659388f4f9f12cc33e"}, - {file = "rpds_py-0.27.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:c1476d6f29eb81aa4151c9a31219b03f1f798dc43d8af1250a870735516a1212"}, - {file = "rpds_py-0.27.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:3ce0cac322b0d69b63c9cdb895ee1b65805ec9ffad37639f291dd79467bee675"}, - {file = "rpds_py-0.27.1-cp314-cp314-win32.whl", hash = "sha256:dfbfac137d2a3d0725758cd141f878bf4329ba25e34979797c89474a89a8a3a3"}, - {file = "rpds_py-0.27.1-cp314-cp314-win_amd64.whl", hash = "sha256:a6e57b0abfe7cc513450fcf529eb486b6e4d3f8aee83e92eb5f1ef848218d456"}, - {file = "rpds_py-0.27.1-cp314-cp314-win_arm64.whl", hash = "sha256:faf8d146f3d476abfee026c4ae3bdd9ca14236ae4e4c310cbd1cf75ba33d24a3"}, - {file = "rpds_py-0.27.1-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:ba81d2b56b6d4911ce735aad0a1d4495e808b8ee4dc58715998741a26874e7c2"}, - {file = "rpds_py-0.27.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:84f7d509870098de0e864cad0102711c1e24e9b1a50ee713b65928adb22269e4"}, - {file = "rpds_py-0.27.1-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a9e960fc78fecd1100539f14132425e1d5fe44ecb9239f8f27f079962021523e"}, - {file = "rpds_py-0.27.1-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:62f85b665cedab1a503747617393573995dac4600ff51869d69ad2f39eb5e817"}, - {file = "rpds_py-0.27.1-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fed467af29776f6556250c9ed85ea5a4dd121ab56a5f8b206e3e7a4c551e48ec"}, - {file = "rpds_py-0.27.1-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f2729615f9d430af0ae6b36cf042cb55c0936408d543fb691e1a9e36648fd35a"}, - {file = "rpds_py-0.27.1-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1b207d881a9aef7ba753d69c123a35d96ca7cb808056998f6b9e8747321f03b8"}, - {file = "rpds_py-0.27.1-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:639fd5efec029f99b79ae47e5d7e00ad8a773da899b6309f6786ecaf22948c48"}, - {file = "rpds_py-0.27.1-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fecc80cb2a90e28af8a9b366edacf33d7a91cbfe4c2c4544ea1246e949cfebeb"}, - {file = "rpds_py-0.27.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:42a89282d711711d0a62d6f57d81aa43a1368686c45bc1c46b7f079d55692734"}, - {file = "rpds_py-0.27.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:cf9931f14223de59551ab9d38ed18d92f14f055a5f78c1d8ad6493f735021bbb"}, - {file = "rpds_py-0.27.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:f39f58a27cc6e59f432b568ed8429c7e1641324fbe38131de852cd77b2d534b0"}, - {file = "rpds_py-0.27.1-cp314-cp314t-win32.whl", hash = "sha256:d5fa0ee122dc09e23607a28e6d7b150da16c662e66409bbe85230e4c85bb528a"}, - {file = "rpds_py-0.27.1-cp314-cp314t-win_amd64.whl", hash = "sha256:6567d2bb951e21232c2f660c24cf3470bb96de56cdcb3f071a83feeaff8a2772"}, - {file = "rpds_py-0.27.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:c918c65ec2e42c2a78d19f18c553d77319119bf43aa9e2edf7fb78d624355527"}, - {file = "rpds_py-0.27.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1fea2b1a922c47c51fd07d656324531adc787e415c8b116530a1d29c0516c62d"}, - {file = "rpds_py-0.27.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bbf94c58e8e0cd6b6f38d8de67acae41b3a515c26169366ab58bdca4a6883bb8"}, - {file = "rpds_py-0.27.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c2a8fed130ce946d5c585eddc7c8eeef0051f58ac80a8ee43bd17835c144c2cc"}, - {file = "rpds_py-0.27.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:037a2361db72ee98d829bc2c5b7cc55598ae0a5e0ec1823a56ea99374cfd73c1"}, - {file = "rpds_py-0.27.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5281ed1cc1d49882f9997981c88df1a22e140ab41df19071222f7e5fc4e72125"}, - {file = "rpds_py-0.27.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2fd50659a069c15eef8aa3d64bbef0d69fd27bb4a50c9ab4f17f83a16cbf8905"}, - {file = "rpds_py-0.27.1-cp39-cp39-manylinux_2_31_riscv64.whl", hash = "sha256:c4b676c4ae3921649a15d28ed10025548e9b561ded473aa413af749503c6737e"}, - {file = "rpds_py-0.27.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:079bc583a26db831a985c5257797b2b5d3affb0386e7ff886256762f82113b5e"}, - {file = "rpds_py-0.27.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:4e44099bd522cba71a2c6b97f68e19f40e7d85399de899d66cdb67b32d7cb786"}, - {file = "rpds_py-0.27.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:e202e6d4188e53c6661af813b46c37ca2c45e497fc558bacc1a7630ec2695aec"}, - {file = "rpds_py-0.27.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:f41f814b8eaa48768d1bb551591f6ba45f87ac76899453e8ccd41dba1289b04b"}, - {file = "rpds_py-0.27.1-cp39-cp39-win32.whl", hash = "sha256:9e71f5a087ead99563c11fdaceee83ee982fd39cf67601f4fd66cb386336ee52"}, - {file = "rpds_py-0.27.1-cp39-cp39-win_amd64.whl", hash = "sha256:71108900c9c3c8590697244b9519017a400d9ba26a36c48381b3f64743a44aab"}, - {file = "rpds_py-0.27.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:7ba22cb9693df986033b91ae1d7a979bc399237d45fccf875b76f62bb9e52ddf"}, - {file = "rpds_py-0.27.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:5b640501be9288c77738b5492b3fd3abc4ba95c50c2e41273c8a1459f08298d3"}, - {file = "rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb08b65b93e0c6dd70aac7f7890a9c0938d5ec71d5cb32d45cf844fb8ae47636"}, - {file = "rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d7ff07d696a7a38152ebdb8212ca9e5baab56656749f3d6004b34ab726b550b8"}, - {file = "rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fb7c72262deae25366e3b6c0c0ba46007967aea15d1eea746e44ddba8ec58dcc"}, - {file = "rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7b002cab05d6339716b03a4a3a2ce26737f6231d7b523f339fa061d53368c9d8"}, - {file = "rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:23f6b69d1c26c4704fec01311963a41d7de3ee0570a84ebde4d544e5a1859ffc"}, - {file = "rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:530064db9146b247351f2a0250b8f00b289accea4596a033e94be2389977de71"}, - {file = "rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7b90b0496570bd6b0321724a330d8b545827c4df2034b6ddfc5f5275f55da2ad"}, - {file = "rpds_py-0.27.1-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:879b0e14a2da6a1102a3fc8af580fc1ead37e6d6692a781bd8c83da37429b5ab"}, - {file = "rpds_py-0.27.1-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:0d807710df3b5faa66c731afa162ea29717ab3be17bdc15f90f2d9f183da4059"}, - {file = "rpds_py-0.27.1-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:3adc388fc3afb6540aec081fa59e6e0d3908722771aa1e37ffe22b220a436f0b"}, - {file = "rpds_py-0.27.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:c796c0c1cc68cb08b0284db4229f5af76168172670c74908fdbd4b7d7f515819"}, - {file = "rpds_py-0.27.1-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:cdfe4bb2f9fe7458b7453ad3c33e726d6d1c7c0a72960bcc23800d77384e42df"}, - {file = "rpds_py-0.27.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:8fabb8fd848a5f75a2324e4a84501ee3a5e3c78d8603f83475441866e60b94a3"}, - {file = "rpds_py-0.27.1-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eda8719d598f2f7f3e0f885cba8646644b55a187762bec091fa14a2b819746a9"}, - {file = "rpds_py-0.27.1-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3c64d07e95606ec402a0a1c511fe003873fa6af630bda59bac77fac8b4318ebc"}, - {file = "rpds_py-0.27.1-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:93a2ed40de81bcff59aabebb626562d48332f3d028ca2036f1d23cbb52750be4"}, - {file = "rpds_py-0.27.1-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:387ce8c44ae94e0ec50532d9cb0edce17311024c9794eb196b90e1058aadeb66"}, - {file = "rpds_py-0.27.1-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aaf94f812c95b5e60ebaf8bfb1898a7d7cb9c1af5744d4a67fa47796e0465d4e"}, - {file = "rpds_py-0.27.1-pp311-pypy311_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:4848ca84d6ded9b58e474dfdbad4b8bfb450344c0551ddc8d958bf4b36aa837c"}, - {file = "rpds_py-0.27.1-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2bde09cbcf2248b73c7c323be49b280180ff39fadcfe04e7b6f54a678d02a7cf"}, - {file = "rpds_py-0.27.1-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:94c44ee01fd21c9058f124d2d4f0c9dc7634bec93cd4b38eefc385dabe71acbf"}, - {file = "rpds_py-0.27.1-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:df8b74962e35c9249425d90144e721eed198e6555a0e22a563d29fe4486b51f6"}, - {file = "rpds_py-0.27.1-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:dc23e6820e3b40847e2f4a7726462ba0cf53089512abe9ee16318c366494c17a"}, - {file = "rpds_py-0.27.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:aa8933159edc50be265ed22b401125c9eebff3171f570258854dbce3ecd55475"}, - {file = "rpds_py-0.27.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:a50431bf02583e21bf273c71b89d710e7a710ad5e39c725b14e685610555926f"}, - {file = "rpds_py-0.27.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:78af06ddc7fe5cc0e967085a9115accee665fb912c22a3f54bad70cc65b05fe6"}, - {file = "rpds_py-0.27.1-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:70d0738ef8fee13c003b100c2fbd667ec4f133468109b3472d249231108283a3"}, - {file = "rpds_py-0.27.1-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e2f6fd8a1cea5bbe599b6e78a6e5ee08db434fc8ffea51ff201c8765679698b3"}, - {file = "rpds_py-0.27.1-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8177002868d1426305bb5de1e138161c2ec9eb2d939be38291d7c431c4712df8"}, - {file = "rpds_py-0.27.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:008b839781d6c9bf3b6a8984d1d8e56f0ec46dc56df61fd669c49b58ae800400"}, - {file = "rpds_py-0.27.1-pp39-pypy39_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:a55b9132bb1ade6c734ddd2759c8dc132aa63687d259e725221f106b83a0e485"}, - {file = "rpds_py-0.27.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a46fdec0083a26415f11d5f236b79fa1291c32aaa4a17684d82f7017a1f818b1"}, - {file = "rpds_py-0.27.1-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:8a63b640a7845f2bdd232eb0d0a4a2dd939bcdd6c57e6bb134526487f3160ec5"}, - {file = "rpds_py-0.27.1-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:7e32721e5d4922deaaf963469d795d5bde6093207c52fec719bd22e5d1bedbc4"}, - {file = "rpds_py-0.27.1-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:2c426b99a068601b5f4623573df7a7c3d72e87533a2dd2253353a03e7502566c"}, - {file = "rpds_py-0.27.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:4fc9b7fe29478824361ead6e14e4f5aed570d477e06088826537e202d25fe859"}, - {file = "rpds_py-0.27.1.tar.gz", hash = "sha256:26a1c73171d10b7acccbded82bf6a586ab8203601e565badc74bbbf8bc5a10f8"}, + {file = "rpds_py-0.30.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:679ae98e00c0e8d68a7fda324e16b90fd5260945b45d3b824c892cec9eea3288"}, + {file = "rpds_py-0.30.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4cc2206b76b4f576934f0ed374b10d7ca5f457858b157ca52064bdfc26b9fc00"}, + {file = "rpds_py-0.30.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:389a2d49eded1896c3d48b0136ead37c48e221b391c052fba3f4055c367f60a6"}, + {file = "rpds_py-0.30.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:32c8528634e1bf7121f3de08fa85b138f4e0dc47657866630611b03967f041d7"}, + {file = "rpds_py-0.30.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f207f69853edd6f6700b86efb84999651baf3789e78a466431df1331608e5324"}, + {file = "rpds_py-0.30.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:67b02ec25ba7a9e8fa74c63b6ca44cf5707f2fbfadae3ee8e7494297d56aa9df"}, + {file = "rpds_py-0.30.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c0e95f6819a19965ff420f65578bacb0b00f251fefe2c8b23347c37174271f3"}, + {file = "rpds_py-0.30.0-cp310-cp310-manylinux_2_31_riscv64.whl", hash = "sha256:a452763cc5198f2f98898eb98f7569649fe5da666c2dc6b5ddb10fde5a574221"}, + {file = "rpds_py-0.30.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e0b65193a413ccc930671c55153a03ee57cecb49e6227204b04fae512eb657a7"}, + {file = "rpds_py-0.30.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:858738e9c32147f78b3ac24dc0edb6610000e56dc0f700fd5f651d0a0f0eb9ff"}, + {file = "rpds_py-0.30.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:da279aa314f00acbb803da1e76fa18666778e8a8f83484fba94526da5de2cba7"}, + {file = "rpds_py-0.30.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7c64d38fb49b6cdeda16ab49e35fe0da2e1e9b34bc38bd78386530f218b37139"}, + {file = "rpds_py-0.30.0-cp310-cp310-win32.whl", hash = "sha256:6de2a32a1665b93233cde140ff8b3467bdb9e2af2b91079f0333a0974d12d464"}, + {file = "rpds_py-0.30.0-cp310-cp310-win_amd64.whl", hash = "sha256:1726859cd0de969f88dc8673bdd954185b9104e05806be64bcd87badbe313169"}, + {file = "rpds_py-0.30.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:a2bffea6a4ca9f01b3f8e548302470306689684e61602aa3d141e34da06cf425"}, + {file = "rpds_py-0.30.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dc4f992dfe1e2bc3ebc7444f6c7051b4bc13cd8e33e43511e8ffd13bf407010d"}, + {file = "rpds_py-0.30.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:422c3cb9856d80b09d30d2eb255d0754b23e090034e1deb4083f8004bd0761e4"}, + {file = "rpds_py-0.30.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:07ae8a593e1c3c6b82ca3292efbe73c30b61332fd612e05abee07c79359f292f"}, + {file = "rpds_py-0.30.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:12f90dd7557b6bd57f40abe7747e81e0c0b119bef015ea7726e69fe550e394a4"}, + {file = "rpds_py-0.30.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:99b47d6ad9a6da00bec6aabe5a6279ecd3c06a329d4aa4771034a21e335c3a97"}, + {file = "rpds_py-0.30.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:33f559f3104504506a44bb666b93a33f5d33133765b0c216a5bf2f1e1503af89"}, + {file = "rpds_py-0.30.0-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:946fe926af6e44f3697abbc305ea168c2c31d3e3ef1058cf68f379bf0335a78d"}, + {file = "rpds_py-0.30.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:495aeca4b93d465efde585977365187149e75383ad2684f81519f504f5c13038"}, + {file = "rpds_py-0.30.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d9a0ca5da0386dee0655b4ccdf46119df60e0f10da268d04fe7cc87886872ba7"}, + {file = "rpds_py-0.30.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8d6d1cc13664ec13c1b84241204ff3b12f9bb82464b8ad6e7a5d3486975c2eed"}, + {file = "rpds_py-0.30.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3896fa1be39912cf0757753826bc8bdc8ca331a28a7c4ae46b7a21280b06bb85"}, + {file = "rpds_py-0.30.0-cp311-cp311-win32.whl", hash = "sha256:55f66022632205940f1827effeff17c4fa7ae1953d2b74a8581baaefb7d16f8c"}, + {file = "rpds_py-0.30.0-cp311-cp311-win_amd64.whl", hash = "sha256:a51033ff701fca756439d641c0ad09a41d9242fa69121c7d8769604a0a629825"}, + {file = "rpds_py-0.30.0-cp311-cp311-win_arm64.whl", hash = "sha256:47b0ef6231c58f506ef0b74d44e330405caa8428e770fec25329ed2cb971a229"}, + {file = "rpds_py-0.30.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a161f20d9a43006833cd7068375a94d035714d73a172b681d8881820600abfad"}, + {file = "rpds_py-0.30.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6abc8880d9d036ecaafe709079969f56e876fcf107f7a8e9920ba6d5a3878d05"}, + {file = "rpds_py-0.30.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca28829ae5f5d569bb62a79512c842a03a12576375d5ece7d2cadf8abe96ec28"}, + {file = "rpds_py-0.30.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a1010ed9524c73b94d15919ca4d41d8780980e1765babf85f9a2f90d247153dd"}, + {file = "rpds_py-0.30.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f8d1736cfb49381ba528cd5baa46f82fdc65c06e843dab24dd70b63d09121b3f"}, + {file = "rpds_py-0.30.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d948b135c4693daff7bc2dcfc4ec57237a29bd37e60c2fabf5aff2bbacf3e2f1"}, + {file = "rpds_py-0.30.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47f236970bccb2233267d89173d3ad2703cd36a0e2a6e92d0560d333871a3d23"}, + {file = "rpds_py-0.30.0-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:2e6ecb5a5bcacf59c3f912155044479af1d0b6681280048b338b28e364aca1f6"}, + {file = "rpds_py-0.30.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a8fa71a2e078c527c3e9dc9fc5a98c9db40bcc8a92b4e8858e36d329f8684b51"}, + {file = "rpds_py-0.30.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:73c67f2db7bc334e518d097c6d1e6fed021bbc9b7d678d6cc433478365d1d5f5"}, + {file = "rpds_py-0.30.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:5ba103fb455be00f3b1c2076c9d4264bfcb037c976167a6047ed82f23153f02e"}, + {file = "rpds_py-0.30.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7cee9c752c0364588353e627da8a7e808a66873672bcb5f52890c33fd965b394"}, + {file = "rpds_py-0.30.0-cp312-cp312-win32.whl", hash = "sha256:1ab5b83dbcf55acc8b08fc62b796ef672c457b17dbd7820a11d6c52c06839bdf"}, + {file = "rpds_py-0.30.0-cp312-cp312-win_amd64.whl", hash = "sha256:a090322ca841abd453d43456ac34db46e8b05fd9b3b4ac0c78bcde8b089f959b"}, + {file = "rpds_py-0.30.0-cp312-cp312-win_arm64.whl", hash = "sha256:669b1805bd639dd2989b281be2cfd951c6121b65e729d9b843e9639ef1fd555e"}, + {file = "rpds_py-0.30.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:f83424d738204d9770830d35290ff3273fbb02b41f919870479fab14b9d303b2"}, + {file = "rpds_py-0.30.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e7536cd91353c5273434b4e003cbda89034d67e7710eab8761fd918ec6c69cf8"}, + {file = "rpds_py-0.30.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2771c6c15973347f50fece41fc447c054b7ac2ae0502388ce3b6738cd366e3d4"}, + {file = "rpds_py-0.30.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0a59119fc6e3f460315fe9d08149f8102aa322299deaa5cab5b40092345c2136"}, + {file = "rpds_py-0.30.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:76fec018282b4ead0364022e3c54b60bf368b9d926877957a8624b58419169b7"}, + {file = "rpds_py-0.30.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:692bef75a5525db97318e8cd061542b5a79812d711ea03dbc1f6f8dbb0c5f0d2"}, + {file = "rpds_py-0.30.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9027da1ce107104c50c81383cae773ef5c24d296dd11c99e2629dbd7967a20c6"}, + {file = "rpds_py-0.30.0-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:9cf69cdda1f5968a30a359aba2f7f9aa648a9ce4b580d6826437f2b291cfc86e"}, + {file = "rpds_py-0.30.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a4796a717bf12b9da9d3ad002519a86063dcac8988b030e405704ef7d74d2d9d"}, + {file = "rpds_py-0.30.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5d4c2aa7c50ad4728a094ebd5eb46c452e9cb7edbfdb18f9e1221f597a73e1e7"}, + {file = "rpds_py-0.30.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ba81a9203d07805435eb06f536d95a266c21e5b2dfbf6517748ca40c98d19e31"}, + {file = "rpds_py-0.30.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:945dccface01af02675628334f7cf49c2af4c1c904748efc5cf7bbdf0b579f95"}, + {file = "rpds_py-0.30.0-cp313-cp313-win32.whl", hash = "sha256:b40fb160a2db369a194cb27943582b38f79fc4887291417685f3ad693c5a1d5d"}, + {file = "rpds_py-0.30.0-cp313-cp313-win_amd64.whl", hash = "sha256:806f36b1b605e2d6a72716f321f20036b9489d29c51c91f4dd29a3e3afb73b15"}, + {file = "rpds_py-0.30.0-cp313-cp313-win_arm64.whl", hash = "sha256:d96c2086587c7c30d44f31f42eae4eac89b60dabbac18c7669be3700f13c3ce1"}, + {file = "rpds_py-0.30.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:eb0b93f2e5c2189ee831ee43f156ed34e2a89a78a66b98cadad955972548be5a"}, + {file = "rpds_py-0.30.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:922e10f31f303c7c920da8981051ff6d8c1a56207dbdf330d9047f6d30b70e5e"}, + {file = "rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cdc62c8286ba9bf7f47befdcea13ea0e26bf294bda99758fd90535cbaf408000"}, + {file = "rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:47f9a91efc418b54fb8190a6b4aa7813a23fb79c51f4bb84e418f5476c38b8db"}, + {file = "rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1f3587eb9b17f3789ad50824084fa6f81921bbf9a795826570bda82cb3ed91f2"}, + {file = "rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:39c02563fc592411c2c61d26b6c5fe1e51eaa44a75aa2c8735ca88b0d9599daa"}, + {file = "rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51a1234d8febafdfd33a42d97da7a43f5dcb120c1060e352a3fbc0c6d36e2083"}, + {file = "rpds_py-0.30.0-cp313-cp313t-manylinux_2_31_riscv64.whl", hash = "sha256:eb2c4071ab598733724c08221091e8d80e89064cd472819285a9ab0f24bcedb9"}, + {file = "rpds_py-0.30.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6bdfdb946967d816e6adf9a3d8201bfad269c67efe6cefd7093ef959683c8de0"}, + {file = "rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c77afbd5f5250bf27bf516c7c4a016813eb2d3e116139aed0096940c5982da94"}, + {file = "rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:61046904275472a76c8c90c9ccee9013d70a6d0f73eecefd38c1ae7c39045a08"}, + {file = "rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4c5f36a861bc4b7da6516dbdf302c55313afa09b81931e8280361a4f6c9a2d27"}, + {file = "rpds_py-0.30.0-cp313-cp313t-win32.whl", hash = "sha256:3d4a69de7a3e50ffc214ae16d79d8fbb0922972da0356dcf4d0fdca2878559c6"}, + {file = "rpds_py-0.30.0-cp313-cp313t-win_amd64.whl", hash = "sha256:f14fc5df50a716f7ece6a80b6c78bb35ea2ca47c499e422aa4463455dd96d56d"}, + {file = "rpds_py-0.30.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:68f19c879420aa08f61203801423f6cd5ac5f0ac4ac82a2368a9fcd6a9a075e0"}, + {file = "rpds_py-0.30.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:ec7c4490c672c1a0389d319b3a9cfcd098dcdc4783991553c332a15acf7249be"}, + {file = "rpds_py-0.30.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f251c812357a3fed308d684a5079ddfb9d933860fc6de89f2b7ab00da481e65f"}, + {file = "rpds_py-0.30.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ac98b175585ecf4c0348fd7b29c3864bda53b805c773cbf7bfdaffc8070c976f"}, + {file = "rpds_py-0.30.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3e62880792319dbeb7eb866547f2e35973289e7d5696c6e295476448f5b63c87"}, + {file = "rpds_py-0.30.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4e7fc54e0900ab35d041b0601431b0a0eb495f0851a0639b6ef90f7741b39a18"}, + {file = "rpds_py-0.30.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47e77dc9822d3ad616c3d5759ea5631a75e5809d5a28707744ef79d7a1bcfcad"}, + {file = "rpds_py-0.30.0-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:b4dc1a6ff022ff85ecafef7979a2c6eb423430e05f1165d6688234e62ba99a07"}, + {file = "rpds_py-0.30.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4559c972db3a360808309e06a74628b95eaccbf961c335c8fe0d590cf587456f"}, + {file = "rpds_py-0.30.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:0ed177ed9bded28f8deb6ab40c183cd1192aa0de40c12f38be4d59cd33cb5c65"}, + {file = "rpds_py-0.30.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:ad1fa8db769b76ea911cb4e10f049d80bf518c104f15b3edb2371cc65375c46f"}, + {file = "rpds_py-0.30.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:46e83c697b1f1c72b50e5ee5adb4353eef7406fb3f2043d64c33f20ad1c2fc53"}, + {file = "rpds_py-0.30.0-cp314-cp314-win32.whl", hash = "sha256:ee454b2a007d57363c2dfd5b6ca4a5d7e2c518938f8ed3b706e37e5d470801ed"}, + {file = "rpds_py-0.30.0-cp314-cp314-win_amd64.whl", hash = "sha256:95f0802447ac2d10bcc69f6dc28fe95fdf17940367b21d34e34c737870758950"}, + {file = "rpds_py-0.30.0-cp314-cp314-win_arm64.whl", hash = "sha256:613aa4771c99f03346e54c3f038e4cc574ac09a3ddfb0e8878487335e96dead6"}, + {file = "rpds_py-0.30.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:7e6ecfcb62edfd632e56983964e6884851786443739dbfe3582947e87274f7cb"}, + {file = "rpds_py-0.30.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a1d0bc22a7cdc173fedebb73ef81e07faef93692b8c1ad3733b67e31e1b6e1b8"}, + {file = "rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d08f00679177226c4cb8c5265012eea897c8ca3b93f429e546600c971bcbae7"}, + {file = "rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5965af57d5848192c13534f90f9dd16464f3c37aaf166cc1da1cae1fd5a34898"}, + {file = "rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9a4e86e34e9ab6b667c27f3211ca48f73dba7cd3d90f8d5b11be56e5dbc3fb4e"}, + {file = "rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e5d3e6b26f2c785d65cc25ef1e5267ccbe1b069c5c21b8cc724efee290554419"}, + {file = "rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:626a7433c34566535b6e56a1b39a7b17ba961e97ce3b80ec62e6f1312c025551"}, + {file = "rpds_py-0.30.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:acd7eb3f4471577b9b5a41baf02a978e8bdeb08b4b355273994f8b87032000a8"}, + {file = "rpds_py-0.30.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fe5fa731a1fa8a0a56b0977413f8cacac1768dad38d16b3a296712709476fbd5"}, + {file = "rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:74a3243a411126362712ee1524dfc90c650a503502f135d54d1b352bd01f2404"}, + {file = "rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:3e8eeb0544f2eb0d2581774be4c3410356eba189529a6b3e36bbbf9696175856"}, + {file = "rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:dbd936cde57abfee19ab3213cf9c26be06d60750e60a8e4dd85d1ab12c8b1f40"}, + {file = "rpds_py-0.30.0-cp314-cp314t-win32.whl", hash = "sha256:dc824125c72246d924f7f796b4f63c1e9dc810c7d9e2355864b3c3a73d59ade0"}, + {file = "rpds_py-0.30.0-cp314-cp314t-win_amd64.whl", hash = "sha256:27f4b0e92de5bfbc6f86e43959e6edd1425c33b5e69aab0984a72047f2bcf1e3"}, + {file = "rpds_py-0.30.0-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:c2262bdba0ad4fc6fb5545660673925c2d2a5d9e2e0fb603aad545427be0fc58"}, + {file = "rpds_py-0.30.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:ee6af14263f25eedc3bb918a3c04245106a42dfd4f5c2285ea6f997b1fc3f89a"}, + {file = "rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3adbb8179ce342d235c31ab8ec511e66c73faa27a47e076ccc92421add53e2bb"}, + {file = "rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:250fa00e9543ac9b97ac258bd37367ff5256666122c2d0f2bc97577c60a1818c"}, + {file = "rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9854cf4f488b3d57b9aaeb105f06d78e5529d3145b1e4a41750167e8c213c6d3"}, + {file = "rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:993914b8e560023bc0a8bf742c5f303551992dcb85e247b1e5c7f4a7d145bda5"}, + {file = "rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58edca431fb9b29950807e301826586e5bbf24163677732429770a697ffe6738"}, + {file = "rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:dea5b552272a944763b34394d04577cf0f9bd013207bc32323b5a89a53cf9c2f"}, + {file = "rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ba3af48635eb83d03f6c9735dfb21785303e73d22ad03d489e88adae6eab8877"}, + {file = "rpds_py-0.30.0-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:dff13836529b921e22f15cb099751209a60009731a68519630a24d61f0b1b30a"}, + {file = "rpds_py-0.30.0-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:1b151685b23929ab7beec71080a8889d4d6d9fa9a983d213f07121205d48e2c4"}, + {file = "rpds_py-0.30.0-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:ac37f9f516c51e5753f27dfdef11a88330f04de2d564be3991384b2f3535d02e"}, + {file = "rpds_py-0.30.0.tar.gz", hash = "sha256:dd8ff7cf90014af0c0f787eea34794ebf6415242ee1d6fa91eaba725cc441e84"}, ] [[package]] name = "ruamel-yaml" -version = "0.18.15" +version = "0.19.1" description = "ruamel.yaml is a YAML parser/emitter that supports roundtrip preservation of comments, seq/map flow style, and map key order" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["main"] files = [ - {file = "ruamel.yaml-0.18.15-py3-none-any.whl", hash = "sha256:148f6488d698b7a5eded5ea793a025308b25eca97208181b6a026037f391f701"}, - {file = "ruamel.yaml-0.18.15.tar.gz", hash = "sha256:dbfca74b018c4c3fba0b9cc9ee33e53c371194a9000e694995e620490fd40700"}, + {file = "ruamel_yaml-0.19.1-py3-none-any.whl", hash = "sha256:27592957fedf6e0b62f281e96effd28043345e0e66001f97683aa9a40c667c93"}, + {file = "ruamel_yaml-0.19.1.tar.gz", hash = "sha256:53eb66cd27849eff968ebf8f0bf61f46cdac2da1d1f3576dd4ccee9b25c31993"}, ] -[package.dependencies] -"ruamel.yaml.clib" = {version = ">=0.2.7", markers = "platform_python_implementation == \"CPython\" and python_version < \"3.14\""} - [package.extras] docs = ["mercurial (>5.7)", "ryd"] jinja2 = ["ruamel.yaml.jinja2 (>=0.2)"] - -[[package]] -name = "ruamel-yaml-clib" -version = "0.2.14" -description = "C version of reader, parser and emitter for ruamel.yaml derived from libyaml" -optional = false -python-versions = ">=3.9" -groups = ["main"] -markers = "platform_python_implementation == \"CPython\"" -files = [ - {file = "ruamel.yaml.clib-0.2.14-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f8b2acb0ffdd2ce8208accbec2dca4a06937d556fdcaefd6473ba1b5daa7e3c4"}, - {file = "ruamel.yaml.clib-0.2.14-cp310-cp310-macosx_13_0_arm64.whl", hash = "sha256:aef953f3b8bd0b50bd52a2e52fb54a6a2171a1889d8dea4a5959d46c6624c451"}, - {file = "ruamel.yaml.clib-0.2.14-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:a0ac90efbc7a77b0d796c03c8cc4e62fd710b3f1e4c32947713ef2ef52e09543"}, - {file = "ruamel.yaml.clib-0.2.14-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9bf6b699223afe6c7fe9f2ef76e0bfa6dd892c21e94ce8c957478987ade76cd8"}, - {file = "ruamel.yaml.clib-0.2.14-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d73a0187718f6eec5b2f729b0f98e4603f7bd9c48aa65d01227d1a5dcdfbe9e8"}, - {file = "ruamel.yaml.clib-0.2.14-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:81f6d3b19bc703679a5705c6a16dabdc79823c71d791d73c65949be7f3012c02"}, - {file = "ruamel.yaml.clib-0.2.14-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:b28caeaf3e670c08cb7e8de221266df8494c169bd6ed8875493fab45be9607a4"}, - {file = "ruamel.yaml.clib-0.2.14-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:94f3efb718f8f49b031f2071ec7a27dd20cbfe511b4dfd54ecee54c956da2b31"}, - {file = "ruamel.yaml.clib-0.2.14-cp310-cp310-win32.whl", hash = "sha256:27c070cf3888e90d992be75dd47292ff9aa17dafd36492812a6a304a1aedc182"}, - {file = "ruamel.yaml.clib-0.2.14-cp310-cp310-win_amd64.whl", hash = "sha256:4f4a150a737fccae13fb51234d41304ff2222e3b7d4c8e9428ed1a6ab48389b8"}, - {file = "ruamel.yaml.clib-0.2.14-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5bae1a073ca4244620425cd3d3aa9746bde590992b98ee8c7c8be8c597ca0d4e"}, - {file = "ruamel.yaml.clib-0.2.14-cp311-cp311-macosx_13_0_arm64.whl", hash = "sha256:0a54e5e40a7a691a426c2703b09b0d61a14294d25cfacc00631aa6f9c964df0d"}, - {file = "ruamel.yaml.clib-0.2.14-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:10d9595b6a19778f3269399eff6bab642608e5966183abc2adbe558a42d4efc9"}, - {file = "ruamel.yaml.clib-0.2.14-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dba72975485f2b87b786075e18a6e5d07dc2b4d8973beb2732b9b2816f1bad70"}, - {file = "ruamel.yaml.clib-0.2.14-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:29757bdb7c142f9595cc1b62ec49a3d1c83fab9cef92db52b0ccebaad4eafb98"}, - {file = "ruamel.yaml.clib-0.2.14-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:557df28dbccf79b152fe2d1b935f6063d9cc431199ea2b0e84892f35c03bb0ee"}, - {file = "ruamel.yaml.clib-0.2.14-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:26a8de280ab0d22b6e3ec745b4a5a07151a0f74aad92dd76ab9c8d8d7087720d"}, - {file = "ruamel.yaml.clib-0.2.14-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e501c096aa3889133d674605ebd018471bc404a59cbc17da3c5924421c54d97c"}, - {file = "ruamel.yaml.clib-0.2.14-cp311-cp311-win32.whl", hash = "sha256:915748cfc25b8cfd81b14d00f4bfdb2ab227a30d6d43459034533f4d1c207a2a"}, - {file = "ruamel.yaml.clib-0.2.14-cp311-cp311-win_amd64.whl", hash = "sha256:4ccba93c1e5a40af45b2f08e4591969fa4697eae951c708f3f83dcbf9f6c6bb1"}, - {file = "ruamel.yaml.clib-0.2.14-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:6aeadc170090ff1889f0d2c3057557f9cd71f975f17535c26a5d37af98f19c27"}, - {file = "ruamel.yaml.clib-0.2.14-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:5e56ac47260c0eed992789fa0b8efe43404a9adb608608631a948cee4fc2b052"}, - {file = "ruamel.yaml.clib-0.2.14-cp312-cp312-manylinux2014_aarch64.whl", hash = "sha256:a911aa73588d9a8b08d662b9484bc0567949529824a55d3885b77e8dd62a127a"}, - {file = "ruamel.yaml.clib-0.2.14-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a05ba88adf3d7189a974b2de7a9d56731548d35dc0a822ec3dc669caa7019b29"}, - {file = "ruamel.yaml.clib-0.2.14-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fb04c5650de6668b853623eceadcdb1a9f2fee381f5d7b6bc842ee7c239eeec4"}, - {file = "ruamel.yaml.clib-0.2.14-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:df3ec9959241d07bc261f4983d25a1205ff37703faf42b474f15d54d88b4f8c9"}, - {file = "ruamel.yaml.clib-0.2.14-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:fbc08c02e9b147a11dfcaa1ac8a83168b699863493e183f7c0c8b12850b7d259"}, - {file = "ruamel.yaml.clib-0.2.14-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c099cafc1834d3c5dac305865d04235f7c21c167c8dd31ebc3d6bbc357e2f023"}, - {file = "ruamel.yaml.clib-0.2.14-cp312-cp312-win32.whl", hash = "sha256:b5b0f7e294700b615a3bcf6d28b26e6da94e8eba63b079f4ec92e9ba6c0d6b54"}, - {file = "ruamel.yaml.clib-0.2.14-cp312-cp312-win_amd64.whl", hash = "sha256:a37f40a859b503304dd740686359fcf541d6fb3ff7fc10f539af7f7150917c68"}, - {file = "ruamel.yaml.clib-0.2.14-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:7e4f9da7e7549946e02a6122dcad00b7c1168513acb1f8a726b1aaf504a99d32"}, - {file = "ruamel.yaml.clib-0.2.14-cp313-cp313-macosx_15_0_arm64.whl", hash = "sha256:dd7546c851e59c06197a7c651335755e74aa383a835878ca86d2c650c07a2f85"}, - {file = "ruamel.yaml.clib-0.2.14-cp313-cp313-manylinux2014_aarch64.whl", hash = "sha256:1c1acc3a0209ea9042cc3cfc0790edd2eddd431a2ec3f8283d081e4d5018571e"}, - {file = "ruamel.yaml.clib-0.2.14-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2070bf0ad1540d5c77a664de07ebcc45eebd1ddcab71a7a06f26936920692beb"}, - {file = "ruamel.yaml.clib-0.2.14-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bd8fe07f49c170e09d76773fb86ad9135e0beee44f36e1576a201b0676d3d1d"}, - {file = "ruamel.yaml.clib-0.2.14-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ff86876889ea478b1381089e55cf9e345707b312beda4986f823e1d95e8c0f59"}, - {file = "ruamel.yaml.clib-0.2.14-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:1f118b707eece8cf84ecbc3e3ec94d9db879d85ed608f95870d39b2d2efa5dca"}, - {file = "ruamel.yaml.clib-0.2.14-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b30110b29484adc597df6bd92a37b90e63a8c152ca8136aad100a02f8ba6d1b6"}, - {file = "ruamel.yaml.clib-0.2.14-cp313-cp313-win32.whl", hash = "sha256:f4e97a1cf0b7a30af9e1d9dad10a5671157b9acee790d9e26996391f49b965a2"}, - {file = "ruamel.yaml.clib-0.2.14-cp313-cp313-win_amd64.whl", hash = "sha256:090782b5fb9d98df96509eecdbcaffd037d47389a89492320280d52f91330d78"}, - {file = "ruamel.yaml.clib-0.2.14-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:7df6f6e9d0e33c7b1d435defb185095386c469109de723d514142632a7b9d07f"}, - {file = "ruamel.yaml.clib-0.2.14-cp314-cp314-macosx_15_0_arm64.whl", hash = "sha256:70eda7703b8126f5e52fcf276e6c0f40b0d314674f896fc58c47b0aef2b9ae83"}, - {file = "ruamel.yaml.clib-0.2.14-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:a0cb71ccc6ef9ce36eecb6272c81afdc2f565950cdcec33ae8e6cd8f7fc86f27"}, - {file = "ruamel.yaml.clib-0.2.14-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:e7cb9ad1d525d40f7d87b6df7c0ff916a66bc52cb61b66ac1b2a16d0c1b07640"}, - {file = "ruamel.yaml.clib-0.2.14-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:18c041b28f3456ddef1f1951d4492dbebe0f8114157c1b3c981a4611c2020792"}, - {file = "ruamel.yaml.clib-0.2.14-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:d8354515ab62f95a07deaf7f845886cc50e2f345ceab240a3d2d09a9f7d77853"}, - {file = "ruamel.yaml.clib-0.2.14-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:275f938692013a3883edbd848edde6d9f26825d65c9a2eb1db8baa1adc96a05d"}, - {file = "ruamel.yaml.clib-0.2.14-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:16a60d69f4057ad9a92f3444e2367c08490daed6428291aa16cefb445c29b0e9"}, - {file = "ruamel.yaml.clib-0.2.14-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5ac5ff9425d8acb8f59ac5b96bcb7fd3d272dc92d96a7c730025928ffcc88a7a"}, - {file = "ruamel.yaml.clib-0.2.14-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:e1d1735d97fd8a48473af048739379975651fab186f8a25a9f683534e6904179"}, - {file = "ruamel.yaml.clib-0.2.14-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:83bbd8354f6abb3fdfb922d1ed47ad8d1db3ea72b0523dac8d07cdacfe1c0fcf"}, - {file = "ruamel.yaml.clib-0.2.14-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:808c7190a0fe7ae7014c42f73897cf8e9ef14ff3aa533450e51b1e72ec5239ad"}, - {file = "ruamel.yaml.clib-0.2.14-cp39-cp39-win32.whl", hash = "sha256:6d5472f63a31b042aadf5ed28dd3ef0523da49ac17f0463e10fda9c4a2773352"}, - {file = "ruamel.yaml.clib-0.2.14-cp39-cp39-win_amd64.whl", hash = "sha256:8dd3c2cc49caa7a8d64b67146462aed6723a0495e44bf0aa0a2e94beaa8432f6"}, - {file = "ruamel.yaml.clib-0.2.14.tar.gz", hash = "sha256:803f5044b13602d58ea378576dd75aa759f52116a0232608e8fdada4da33752e"}, -] +libyaml = ["ruamel.yaml.clibz (>=0.3.7) ; platform_python_implementation == \"CPython\""] +oldlibyaml = ["ruamel.yaml.clib ; platform_python_implementation == \"CPython\""] [[package]] name = "ruff" @@ -3782,126 +3481,81 @@ files = [ [[package]] name = "scipy" -version = "1.13.1" -description = "Fundamental algorithms for scientific computing in Python" -optional = false -python-versions = ">=3.9" -groups = ["main"] -markers = "python_version < \"3.11\"" -files = [ - {file = "scipy-1.13.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:20335853b85e9a49ff7572ab453794298bcf0354d8068c5f6775a0eabf350aca"}, - {file = "scipy-1.13.1-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:d605e9c23906d1994f55ace80e0125c587f96c020037ea6aa98d01b4bd2e222f"}, - {file = "scipy-1.13.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cfa31f1def5c819b19ecc3a8b52d28ffdcc7ed52bb20c9a7589669dd3c250989"}, - {file = "scipy-1.13.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f26264b282b9da0952a024ae34710c2aff7d27480ee91a2e82b7b7073c24722f"}, - {file = "scipy-1.13.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:eccfa1906eacc02de42d70ef4aecea45415f5be17e72b61bafcfd329bdc52e94"}, - {file = "scipy-1.13.1-cp310-cp310-win_amd64.whl", hash = "sha256:2831f0dc9c5ea9edd6e51e6e769b655f08ec6db6e2e10f86ef39bd32eb11da54"}, - {file = "scipy-1.13.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:27e52b09c0d3a1d5b63e1105f24177e544a222b43611aaf5bc44d4a0979e32f9"}, - {file = "scipy-1.13.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:54f430b00f0133e2224c3ba42b805bfd0086fe488835effa33fa291561932326"}, - {file = "scipy-1.13.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e89369d27f9e7b0884ae559a3a956e77c02114cc60a6058b4e5011572eea9299"}, - {file = "scipy-1.13.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a78b4b3345f1b6f68a763c6e25c0c9a23a9fd0f39f5f3d200efe8feda560a5fa"}, - {file = "scipy-1.13.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:45484bee6d65633752c490404513b9ef02475b4284c4cfab0ef946def50b3f59"}, - {file = "scipy-1.13.1-cp311-cp311-win_amd64.whl", hash = "sha256:5713f62f781eebd8d597eb3f88b8bf9274e79eeabf63afb4a737abc6c84ad37b"}, - {file = "scipy-1.13.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5d72782f39716b2b3509cd7c33cdc08c96f2f4d2b06d51e52fb45a19ca0c86a1"}, - {file = "scipy-1.13.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:017367484ce5498445aade74b1d5ab377acdc65e27095155e448c88497755a5d"}, - {file = "scipy-1.13.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:949ae67db5fa78a86e8fa644b9a6b07252f449dcf74247108c50e1d20d2b4627"}, - {file = "scipy-1.13.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de3ade0e53bc1f21358aa74ff4830235d716211d7d077e340c7349bc3542e884"}, - {file = "scipy-1.13.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2ac65fb503dad64218c228e2dc2d0a0193f7904747db43014645ae139c8fad16"}, - {file = "scipy-1.13.1-cp312-cp312-win_amd64.whl", hash = "sha256:cdd7dacfb95fea358916410ec61bbc20440f7860333aee6d882bb8046264e949"}, - {file = "scipy-1.13.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:436bbb42a94a8aeef855d755ce5a465479c721e9d684de76bf61a62e7c2b81d5"}, - {file = "scipy-1.13.1-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:8335549ebbca860c52bf3d02f80784e91a004b71b059e3eea9678ba994796a24"}, - {file = "scipy-1.13.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d533654b7d221a6a97304ab63c41c96473ff04459e404b83275b60aa8f4b7004"}, - {file = "scipy-1.13.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:637e98dcf185ba7f8e663e122ebf908c4702420477ae52a04f9908707456ba4d"}, - {file = "scipy-1.13.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a014c2b3697bde71724244f63de2476925596c24285c7a637364761f8710891c"}, - {file = "scipy-1.13.1-cp39-cp39-win_amd64.whl", hash = "sha256:392e4ec766654852c25ebad4f64e4e584cf19820b980bc04960bca0b0cd6eaa2"}, - {file = "scipy-1.13.1.tar.gz", hash = "sha256:095a87a0312b08dfd6a6155cbbd310a8c51800fc931b8c0b84003014b874ed3c"}, -] - -[package.dependencies] -numpy = ">=1.22.4,<2.3" - -[package.extras] -dev = ["cython-lint (>=0.12.2)", "doit (>=0.36.0)", "mypy", "pycodestyle", "pydevtool", "rich-click", "ruff", "types-psutil", "typing_extensions"] -doc = ["jupyterlite-pyodide-kernel", "jupyterlite-sphinx (>=0.12.0)", "jupytext", "matplotlib (>=3.5)", "myst-nb", "numpydoc", "pooch", "pydata-sphinx-theme (>=0.15.2)", "sphinx (>=5.0.0)", "sphinx-design (>=0.4.0)"] -test = ["array-api-strict", "asv", "gmpy2", "hypothesis (>=6.30)", "mpmath", "pooch", "pytest", "pytest-cov", "pytest-timeout", "pytest-xdist", "scikit-umfpack", "threadpoolctl"] - -[[package]] -name = "scipy" -version = "1.16.2" +version = "1.17.1" description = "Fundamental algorithms for scientific computing in Python" optional = false python-versions = ">=3.11" groups = ["main"] -markers = "python_version >= \"3.11\"" -files = [ - {file = "scipy-1.16.2-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:6ab88ea43a57da1af33292ebd04b417e8e2eaf9d5aa05700be8d6e1b6501cd92"}, - {file = "scipy-1.16.2-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:c95e96c7305c96ede73a7389f46ccd6c659c4da5ef1b2789466baeaed3622b6e"}, - {file = "scipy-1.16.2-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:87eb178db04ece7c698220d523c170125dbffebb7af0345e66c3554f6f60c173"}, - {file = "scipy-1.16.2-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:4e409eac067dcee96a57fbcf424c13f428037827ec7ee3cb671ff525ca4fc34d"}, - {file = "scipy-1.16.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:e574be127bb760f0dad24ff6e217c80213d153058372362ccb9555a10fc5e8d2"}, - {file = "scipy-1.16.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f5db5ba6188d698ba7abab982ad6973265b74bb40a1efe1821b58c87f73892b9"}, - {file = "scipy-1.16.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ec6e74c4e884104ae006d34110677bfe0098203a3fec2f3faf349f4cb05165e3"}, - {file = "scipy-1.16.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:912f46667d2d3834bc3d57361f854226475f695eb08c08a904aadb1c936b6a88"}, - {file = "scipy-1.16.2-cp311-cp311-win_amd64.whl", hash = "sha256:91e9e8a37befa5a69e9cacbe0bcb79ae5afb4a0b130fd6db6ee6cc0d491695fa"}, - {file = "scipy-1.16.2-cp311-cp311-win_arm64.whl", hash = "sha256:f3bf75a6dcecab62afde4d1f973f1692be013110cad5338007927db8da73249c"}, - {file = "scipy-1.16.2-cp312-cp312-macosx_10_14_x86_64.whl", hash = "sha256:89d6c100fa5c48472047632e06f0876b3c4931aac1f4291afc81a3644316bb0d"}, - {file = "scipy-1.16.2-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:ca748936cd579d3f01928b30a17dc474550b01272d8046e3e1ee593f23620371"}, - {file = "scipy-1.16.2-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:fac4f8ce2ddb40e2e3d0f7ec36d2a1e7f92559a2471e59aec37bd8d9de01fec0"}, - {file = "scipy-1.16.2-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:033570f1dcefd79547a88e18bccacff025c8c647a330381064f561d43b821232"}, - {file = "scipy-1.16.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ea3421209bf00c8a5ef2227de496601087d8f638a2363ee09af059bd70976dc1"}, - {file = "scipy-1.16.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f66bd07ba6f84cd4a380b41d1bf3c59ea488b590a2ff96744845163309ee8e2f"}, - {file = "scipy-1.16.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5e9feab931bd2aea4a23388c962df6468af3d808ddf2d40f94a81c5dc38f32ef"}, - {file = "scipy-1.16.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:03dfc75e52f72cf23ec2ced468645321407faad8f0fe7b1f5b49264adbc29cb1"}, - {file = "scipy-1.16.2-cp312-cp312-win_amd64.whl", hash = "sha256:0ce54e07bbb394b417457409a64fd015be623f36e330ac49306433ffe04bc97e"}, - {file = "scipy-1.16.2-cp312-cp312-win_arm64.whl", hash = "sha256:2a8ffaa4ac0df81a0b94577b18ee079f13fecdb924df3328fc44a7dc5ac46851"}, - {file = "scipy-1.16.2-cp313-cp313-macosx_10_14_x86_64.whl", hash = "sha256:84f7bf944b43e20b8a894f5fe593976926744f6c185bacfcbdfbb62736b5cc70"}, - {file = "scipy-1.16.2-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:5c39026d12edc826a1ef2ad35ad1e6d7f087f934bb868fc43fa3049c8b8508f9"}, - {file = "scipy-1.16.2-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:e52729ffd45b68777c5319560014d6fd251294200625d9d70fd8626516fc49f5"}, - {file = "scipy-1.16.2-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:024dd4a118cccec09ca3209b7e8e614931a6ffb804b2a601839499cb88bdf925"}, - {file = "scipy-1.16.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7a5dc7ee9c33019973a470556081b0fd3c9f4c44019191039f9769183141a4d9"}, - {file = "scipy-1.16.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c2275ff105e508942f99d4e3bc56b6ef5e4b3c0af970386ca56b777608ce95b7"}, - {file = "scipy-1.16.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:af80196eaa84f033e48444d2e0786ec47d328ba00c71e4299b602235ffef9acb"}, - {file = "scipy-1.16.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9fb1eb735fe3d6ed1f89918224e3385fbf6f9e23757cacc35f9c78d3b712dd6e"}, - {file = "scipy-1.16.2-cp313-cp313-win_amd64.whl", hash = "sha256:fda714cf45ba43c9d3bae8f2585c777f64e3f89a2e073b668b32ede412d8f52c"}, - {file = "scipy-1.16.2-cp313-cp313-win_arm64.whl", hash = "sha256:2f5350da923ccfd0b00e07c3e5cfb316c1c0d6c1d864c07a72d092e9f20db104"}, - {file = "scipy-1.16.2-cp313-cp313t-macosx_10_14_x86_64.whl", hash = "sha256:53d8d2ee29b925344c13bda64ab51785f016b1b9617849dac10897f0701b20c1"}, - {file = "scipy-1.16.2-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:9e05e33657efb4c6a9d23bd8300101536abd99c85cca82da0bffff8d8764d08a"}, - {file = "scipy-1.16.2-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:7fe65b36036357003b3ef9d37547abeefaa353b237e989c21027b8ed62b12d4f"}, - {file = "scipy-1.16.2-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:6406d2ac6d40b861cccf57f49592f9779071655e9f75cd4f977fa0bdd09cb2e4"}, - {file = "scipy-1.16.2-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ff4dc42bd321991fbf611c23fc35912d690f731c9914bf3af8f417e64aca0f21"}, - {file = "scipy-1.16.2-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:654324826654d4d9133e10675325708fb954bc84dae6e9ad0a52e75c6b1a01d7"}, - {file = "scipy-1.16.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:63870a84cd15c44e65220eaed2dac0e8f8b26bbb991456a033c1d9abfe8a94f8"}, - {file = "scipy-1.16.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:fa01f0f6a3050fa6a9771a95d5faccc8e2f5a92b4a2e5440a0fa7264a2398472"}, - {file = "scipy-1.16.2-cp313-cp313t-win_amd64.whl", hash = "sha256:116296e89fba96f76353a8579820c2512f6e55835d3fad7780fece04367de351"}, - {file = "scipy-1.16.2-cp313-cp313t-win_arm64.whl", hash = "sha256:98e22834650be81d42982360382b43b17f7ba95e0e6993e2a4f5b9ad9283a94d"}, - {file = "scipy-1.16.2-cp314-cp314-macosx_10_14_x86_64.whl", hash = "sha256:567e77755019bb7461513c87f02bb73fb65b11f049aaaa8ca17cfaa5a5c45d77"}, - {file = "scipy-1.16.2-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:17d9bb346194e8967296621208fcdfd39b55498ef7d2f376884d5ac47cec1a70"}, - {file = "scipy-1.16.2-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:0a17541827a9b78b777d33b623a6dcfe2ef4a25806204d08ead0768f4e529a88"}, - {file = "scipy-1.16.2-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:d7d4c6ba016ffc0f9568d012f5f1eb77ddd99412aea121e6fa8b4c3b7cbad91f"}, - {file = "scipy-1.16.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:9702c4c023227785c779cba2e1d6f7635dbb5b2e0936cdd3a4ecb98d78fd41eb"}, - {file = "scipy-1.16.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d1cdf0ac28948d225decdefcc45ad7dd91716c29ab56ef32f8e0d50657dffcc7"}, - {file = "scipy-1.16.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:70327d6aa572a17c2941cdfb20673f82e536e91850a2e4cb0c5b858b690e1548"}, - {file = "scipy-1.16.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5221c0b2a4b58aa7c4ed0387d360fd90ee9086d383bb34d9f2789fafddc8a936"}, - {file = "scipy-1.16.2-cp314-cp314-win_amd64.whl", hash = "sha256:f5a85d7b2b708025af08f060a496dd261055b617d776fc05a1a1cc69e09fe9ff"}, - {file = "scipy-1.16.2-cp314-cp314-win_arm64.whl", hash = "sha256:2cc73a33305b4b24556957d5857d6253ce1e2dcd67fa0ff46d87d1670b3e1e1d"}, - {file = "scipy-1.16.2-cp314-cp314t-macosx_10_14_x86_64.whl", hash = "sha256:9ea2a3fed83065d77367775d689401a703d0f697420719ee10c0780bcab594d8"}, - {file = "scipy-1.16.2-cp314-cp314t-macosx_12_0_arm64.whl", hash = "sha256:7280d926f11ca945c3ef92ba960fa924e1465f8d07ce3a9923080363390624c4"}, - {file = "scipy-1.16.2-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:8afae1756f6a1fe04636407ef7dbece33d826a5d462b74f3d0eb82deabefd831"}, - {file = "scipy-1.16.2-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:5c66511f29aa8d233388e7416a3f20d5cae7a2744d5cee2ecd38c081f4e861b3"}, - {file = "scipy-1.16.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:efe6305aeaa0e96b0ccca5ff647a43737d9a092064a3894e46c414db84bc54ac"}, - {file = "scipy-1.16.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7f3a337d9ae06a1e8d655ee9d8ecb835ea5ddcdcbd8d23012afa055ab014f374"}, - {file = "scipy-1.16.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:bab3605795d269067d8ce78a910220262711b753de8913d3deeaedb5dded3bb6"}, - {file = "scipy-1.16.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:b0348d8ddb55be2a844c518cd8cc8deeeb8aeba707cf834db5758fc89b476a2c"}, - {file = "scipy-1.16.2-cp314-cp314t-win_amd64.whl", hash = "sha256:26284797e38b8a75e14ea6631d29bda11e76ceaa6ddb6fdebbfe4c4d90faf2f9"}, - {file = "scipy-1.16.2-cp314-cp314t-win_arm64.whl", hash = "sha256:d2a4472c231328d4de38d5f1f68fdd6d28a615138f842580a8a321b5845cf779"}, - {file = "scipy-1.16.2.tar.gz", hash = "sha256:af029b153d243a80afb6eabe40b0a07f8e35c9adc269c019f364ad747f826a6b"}, +files = [ + {file = "scipy-1.17.1-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:1f95b894f13729334fb990162e911c9e5dc1ab390c58aa6cbecb389c5b5e28ec"}, + {file = "scipy-1.17.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:e18f12c6b0bc5a592ed23d3f7b891f68fd7f8241d69b7883769eb5d5dfb52696"}, + {file = "scipy-1.17.1-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:a3472cfbca0a54177d0faa68f697d8ba4c80bbdc19908c3465556d9f7efce9ee"}, + {file = "scipy-1.17.1-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:766e0dc5a616d026a3a1cffa379af959671729083882f50307e18175797b3dfd"}, + {file = "scipy-1.17.1-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:744b2bf3640d907b79f3fd7874efe432d1cf171ee721243e350f55234b4cec4c"}, + {file = "scipy-1.17.1-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:43af8d1f3bea642559019edfe64e9b11192a8978efbd1539d7bc2aaa23d92de4"}, + {file = "scipy-1.17.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:cd96a1898c0a47be4520327e01f874acfd61fb48a9420f8aa9f6483412ffa444"}, + {file = "scipy-1.17.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4eb6c25dd62ee8d5edf68a8e1c171dd71c292fdae95d8aeb3dd7d7de4c364082"}, + {file = "scipy-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:d30e57c72013c2a4fe441c2fcb8e77b14e152ad48b5464858e07e2ad9fbfceff"}, + {file = "scipy-1.17.1-cp311-cp311-win_arm64.whl", hash = "sha256:9ecb4efb1cd6e8c4afea0daa91a87fbddbce1b99d2895d151596716c0b2e859d"}, + {file = "scipy-1.17.1-cp312-cp312-macosx_10_14_x86_64.whl", hash = "sha256:35c3a56d2ef83efc372eaec584314bd0ef2e2f0d2adb21c55e6ad5b344c0dcb8"}, + {file = "scipy-1.17.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:fcb310ddb270a06114bb64bbe53c94926b943f5b7f0842194d585c65eb4edd76"}, + {file = "scipy-1.17.1-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:cc90d2e9c7e5c7f1a482c9875007c095c3194b1cfedca3c2f3291cdc2bc7c086"}, + {file = "scipy-1.17.1-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:c80be5ede8f3f8eded4eff73cc99a25c388ce98e555b17d31da05287015ffa5b"}, + {file = "scipy-1.17.1-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e19ebea31758fac5893a2ac360fedd00116cbb7628e650842a6691ba7ca28a21"}, + {file = "scipy-1.17.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:02ae3b274fde71c5e92ac4d54bc06c42d80e399fec704383dcd99b301df37458"}, + {file = "scipy-1.17.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8a604bae87c6195d8b1045eddece0514d041604b14f2727bbc2b3020172045eb"}, + {file = "scipy-1.17.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f590cd684941912d10becc07325a3eeb77886fe981415660d9265c4c418d0bea"}, + {file = "scipy-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:41b71f4a3a4cab9d366cd9065b288efc4d4f3c0b37a91a8e0947fb5bd7f31d87"}, + {file = "scipy-1.17.1-cp312-cp312-win_arm64.whl", hash = "sha256:f4115102802df98b2b0db3cce5cb9b92572633a1197c77b7553e5203f284a5b3"}, + {file = "scipy-1.17.1-cp313-cp313-macosx_10_14_x86_64.whl", hash = "sha256:5e3c5c011904115f88a39308379c17f91546f77c1667cea98739fe0fccea804c"}, + {file = "scipy-1.17.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:6fac755ca3d2c3edcb22f479fceaa241704111414831ddd3bc6056e18516892f"}, + {file = "scipy-1.17.1-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:7ff200bf9d24f2e4d5dc6ee8c3ac64d739d3a89e2326ba68aaf6c4a2b838fd7d"}, + {file = "scipy-1.17.1-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:4b400bdc6f79fa02a4d86640310dde87a21fba0c979efff5248908c6f15fad1b"}, + {file = "scipy-1.17.1-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2b64ca7d4aee0102a97f3ba22124052b4bd2152522355073580bf4845e2550b6"}, + {file = "scipy-1.17.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:581b2264fc0aa555f3f435a5944da7504ea3a065d7029ad60e7c3d1ae09c5464"}, + {file = "scipy-1.17.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:beeda3d4ae615106d7094f7e7cef6218392e4465cc95d25f900bebabfded0950"}, + {file = "scipy-1.17.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6609bc224e9568f65064cfa72edc0f24ee6655b47575954ec6339534b2798369"}, + {file = "scipy-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:37425bc9175607b0268f493d79a292c39f9d001a357bebb6b88fdfaff13f6448"}, + {file = "scipy-1.17.1-cp313-cp313-win_arm64.whl", hash = "sha256:5cf36e801231b6a2059bf354720274b7558746f3b1a4efb43fcf557ccd484a87"}, + {file = "scipy-1.17.1-cp313-cp313t-macosx_10_14_x86_64.whl", hash = "sha256:d59c30000a16d8edc7e64152e30220bfbd724c9bbb08368c054e24c651314f0a"}, + {file = "scipy-1.17.1-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:010f4333c96c9bb1a4516269e33cb5917b08ef2166d5556ca2fd9f082a9e6ea0"}, + {file = "scipy-1.17.1-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:2ceb2d3e01c5f1d83c4189737a42d9cb2fc38a6eeed225e7515eef71ad301dce"}, + {file = "scipy-1.17.1-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:844e165636711ef41f80b4103ed234181646b98a53c8f05da12ca5ca289134f6"}, + {file = "scipy-1.17.1-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:158dd96d2207e21c966063e1635b1063cd7787b627b6f07305315dd73d9c679e"}, + {file = "scipy-1.17.1-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:74cbb80d93260fe2ffa334efa24cb8f2f0f622a9b9febf8b483c0b865bfb3475"}, + {file = "scipy-1.17.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:dbc12c9f3d185f5c737d801da555fb74b3dcfa1a50b66a1a93e09190f41fab50"}, + {file = "scipy-1.17.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:94055a11dfebe37c656e70317e1996dc197e1a15bbcc351bcdd4610e128fe1ca"}, + {file = "scipy-1.17.1-cp313-cp313t-win_amd64.whl", hash = "sha256:e30bdeaa5deed6bc27b4cc490823cd0347d7dae09119b8803ae576ea0ce52e4c"}, + {file = "scipy-1.17.1-cp313-cp313t-win_arm64.whl", hash = "sha256:a720477885a9d2411f94a93d16f9d89bad0f28ca23c3f8daa521e2dcc3f44d49"}, + {file = "scipy-1.17.1-cp314-cp314-macosx_10_14_x86_64.whl", hash = "sha256:a48a72c77a310327f6a3a920092fa2b8fd03d7deaa60f093038f22d98e096717"}, + {file = "scipy-1.17.1-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:45abad819184f07240d8a696117a7aacd39787af9e0b719d00285549ed19a1e9"}, + {file = "scipy-1.17.1-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:3fd1fcdab3ea951b610dc4cef356d416d5802991e7e32b5254828d342f7b7e0b"}, + {file = "scipy-1.17.1-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:7bdf2da170b67fdf10bca777614b1c7d96ae3ca5794fd9587dce41eb2966e866"}, + {file = "scipy-1.17.1-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:adb2642e060a6549c343603a3851ba76ef0b74cc8c079a9a58121c7ec9fe2350"}, + {file = "scipy-1.17.1-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:eee2cfda04c00a857206a4330f0c5e3e56535494e30ca445eb19ec624ae75118"}, + {file = "scipy-1.17.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:d2650c1fb97e184d12d8ba010493ee7b322864f7d3d00d3f9bb97d9c21de4068"}, + {file = "scipy-1.17.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:08b900519463543aa604a06bec02461558a6e1cef8fdbb8098f77a48a83c8118"}, + {file = "scipy-1.17.1-cp314-cp314-win_amd64.whl", hash = "sha256:3877ac408e14da24a6196de0ddcace62092bfc12a83823e92e49e40747e52c19"}, + {file = "scipy-1.17.1-cp314-cp314-win_arm64.whl", hash = "sha256:f8885db0bc2bffa59d5c1b72fad7a6a92d3e80e7257f967dd81abb553a90d293"}, + {file = "scipy-1.17.1-cp314-cp314t-macosx_10_14_x86_64.whl", hash = "sha256:1cc682cea2ae55524432f3cdff9e9a3be743d52a7443d0cba9017c23c87ae2f6"}, + {file = "scipy-1.17.1-cp314-cp314t-macosx_12_0_arm64.whl", hash = "sha256:2040ad4d1795a0ae89bfc7e8429677f365d45aa9fd5e4587cf1ea737f927b4a1"}, + {file = "scipy-1.17.1-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:131f5aaea57602008f9822e2115029b55d4b5f7c070287699fe45c661d051e39"}, + {file = "scipy-1.17.1-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:9cdc1a2fcfd5c52cfb3045feb399f7b3ce822abdde3a193a6b9a60b3cb5854ca"}, + {file = "scipy-1.17.1-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6e3dcd57ab780c741fde8dc68619de988b966db759a3c3152e8e9142c26295ad"}, + {file = "scipy-1.17.1-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a9956e4d4f4a301ebf6cde39850333a6b6110799d470dbbb1e25326ac447f52a"}, + {file = "scipy-1.17.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:a4328d245944d09fd639771de275701ccadf5f781ba0ff092ad141e017eccda4"}, + {file = "scipy-1.17.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:a77cbd07b940d326d39a1d1b37817e2ee4d79cb30e7338f3d0cddffae70fcaa2"}, + {file = "scipy-1.17.1-cp314-cp314t-win_amd64.whl", hash = "sha256:eb092099205ef62cd1782b006658db09e2fed75bffcae7cc0d44052d8aa0f484"}, + {file = "scipy-1.17.1-cp314-cp314t-win_arm64.whl", hash = "sha256:200e1050faffacc162be6a486a984a0497866ec54149a01270adc8a59b7c7d21"}, + {file = "scipy-1.17.1.tar.gz", hash = "sha256:95d8e012d8cb8816c226aef832200b1d45109ed4464303e997c5b13122b297c0"}, ] [package.dependencies] -numpy = ">=1.25.2,<2.6" +numpy = ">=1.26.4,<2.7" [package.extras] -dev = ["cython-lint (>=0.12.2)", "doit (>=0.36.0)", "mypy (==1.10.0)", "pycodestyle", "pydevtool", "rich-click", "ruff (>=0.0.292)", "types-psutil", "typing_extensions"] -doc = ["intersphinx_registry", "jupyterlite-pyodide-kernel", "jupyterlite-sphinx (>=0.19.1)", "jupytext", "linkify-it-py", "matplotlib (>=3.5)", "myst-nb (>=1.2.0)", "numpydoc", "pooch", "pydata-sphinx-theme (>=0.15.2)", "sphinx (>=5.0.0,<8.2.0)", "sphinx-copybutton", "sphinx-design (>=0.4.0)"] +dev = ["click (<8.3.0)", "cython-lint (>=0.12.2)", "mypy (==1.10.0)", "pycodestyle", "ruff (>=0.12.0)", "spin", "types-psutil", "typing_extensions"] +doc = ["intersphinx_registry", "jupyterlite-pyodide-kernel", "jupyterlite-sphinx (>=0.19.1)", "jupytext", "linkify-it-py", "matplotlib (>=3.5)", "myst-nb (>=1.2.0)", "numpydoc", "pooch", "pydata-sphinx-theme (>=0.15.2)", "sphinx (>=5.0.0,<8.2.0)", "sphinx-copybutton", "sphinx-design (>=0.4.0)", "tabulate"] test = ["Cython", "array-api-strict (>=2.3.1)", "asv", "gmpy2", "hypothesis (>=6.30)", "meson", "mpmath", "ninja ; sys_platform != \"emscripten\"", "pooch", "pytest (>=8.0.0)", "pytest-cov", "pytest-timeout", "pytest-xdist", "scikit-umfpack", "threadpoolctl"] [[package]] @@ -3962,18 +3616,6 @@ files = [ {file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"}, ] -[[package]] -name = "sniffio" -version = "1.3.1" -description = "Sniff out which async library your code is running under" -optional = false -python-versions = ">=3.7" -groups = ["main", "dev"] -files = [ - {file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"}, - {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"}, -] - [[package]] name = "snowballstemmer" version = "3.0.1" @@ -3989,15 +3631,15 @@ files = [ [[package]] name = "soupsieve" -version = "2.8" +version = "2.8.3" description = "A modern CSS selector implementation for Beautiful Soup." optional = true python-versions = ">=3.9" groups = ["main"] markers = "extra == \"docs\"" files = [ - {file = "soupsieve-2.8-py3-none-any.whl", hash = "sha256:0cc76456a30e20f5d7f2e14a98a4ae2ee4e5abdc7c5ea0aafe795f344bc7984c"}, - {file = "soupsieve-2.8.tar.gz", hash = "sha256:e2dd4a40a628cb5f28f6d4b0db8800b8f581b65bb380b97de22ba5ca8d72572f"}, + {file = "soupsieve-2.8.3-py3-none-any.whl", hash = "sha256:ed64f2ba4eebeab06cc4962affce381647455978ffc1e36bb79a545b91f45a95"}, + {file = "soupsieve-2.8.3.tar.gz", hash = "sha256:3267f1eeea4251fb42728b6dfb746edc9acaffc4a45b27e19450b676586e8349"}, ] [[package]] @@ -4019,7 +3661,6 @@ babel = ">=2.9" colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} docutils = ">=0.18.1,<0.22" imagesize = ">=1.3" -importlib-metadata = {version = ">=4.8", markers = "python_version < \"3.10\""} Jinja2 = ">=3.0" packaging = ">=21.0" Pygments = ">=2.14" @@ -4031,7 +3672,6 @@ sphinxcontrib-htmlhelp = ">=2.0.0" sphinxcontrib-jsmath = "*" sphinxcontrib-qthelp = "*" sphinxcontrib-serializinghtml = ">=1.1.9" -tomli = {version = ">=2", markers = "python_version < \"3.11\""} [package.extras] docs = ["sphinxcontrib-websupport"] @@ -4204,18 +3844,18 @@ tests = ["cython", "littleutils", "pygments", "pytest", "typeguard"] [[package]] name = "syrupy" -version = "4.9.1" +version = "5.3.3" description = "Pytest Snapshot Test Utility" optional = false -python-versions = ">=3.8.1" +python-versions = ">=3.10" groups = ["dev"] files = [ - {file = "syrupy-4.9.1-py3-none-any.whl", hash = "sha256:b94cc12ed0e5e75b448255430af642516842a2374a46936dd2650cfb6dd20eda"}, - {file = "syrupy-4.9.1.tar.gz", hash = "sha256:b7d0fcadad80a7d2f6c4c71917918e8ebe2483e8c703dfc8d49cdbb01081f9a4"}, + {file = "syrupy-5.3.3-py3-none-any.whl", hash = "sha256:94b7c41192295fa03ec6de281d8075900e03739f45943935f7854d61824670d7"}, + {file = "syrupy-5.3.3.tar.gz", hash = "sha256:6c065cd1ba9374e810ad3826feb466f4d43e6f1a7168703178e56b47902c736f"}, ] [package.dependencies] -pytest = ">=7.0.0,<9.0.0" +pytest = ">=8.0.0" [[package]] name = "tinycss2" @@ -4241,78 +3881,34 @@ test = ["pytest", "ruff"] name = "toml" version = "0.10.2" description = "Python Library for Tom's Obvious, Minimal Language" -optional = false +optional = true python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" -groups = ["dev"] +groups = ["main"] +markers = "extra == \"docs\"" files = [ {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, ] -[[package]] -name = "tomli" -version = "2.2.1" -description = "A lil' TOML parser" -optional = false -python-versions = ">=3.8" -groups = ["main", "dev"] -files = [ - {file = "tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249"}, - {file = "tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6"}, - {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a"}, - {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee"}, - {file = "tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e"}, - {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4"}, - {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106"}, - {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8"}, - {file = "tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff"}, - {file = "tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b"}, - {file = "tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea"}, - {file = "tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8"}, - {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192"}, - {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222"}, - {file = "tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77"}, - {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6"}, - {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd"}, - {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e"}, - {file = "tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98"}, - {file = "tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4"}, - {file = "tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7"}, - {file = "tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c"}, - {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13"}, - {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281"}, - {file = "tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272"}, - {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140"}, - {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2"}, - {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744"}, - {file = "tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec"}, - {file = "tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69"}, - {file = "tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc"}, - {file = "tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff"}, -] -markers = {main = "python_version < \"3.11\" and extra == \"docs\"", dev = "python_version < \"3.11\""} - [[package]] name = "tornado" -version = "6.5.2" +version = "6.5.7" description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed." optional = true python-versions = ">=3.9" groups = ["main"] markers = "extra == \"docs\"" files = [ - {file = "tornado-6.5.2-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:2436822940d37cde62771cff8774f4f00b3c8024fe482e16ca8387b8a2724db6"}, - {file = "tornado-6.5.2-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:583a52c7aa94ee046854ba81d9ebb6c81ec0fd30386d96f7640c96dad45a03ef"}, - {file = "tornado-6.5.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b0fe179f28d597deab2842b86ed4060deec7388f1fd9c1b4a41adf8af058907e"}, - {file = "tornado-6.5.2-cp39-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b186e85d1e3536d69583d2298423744740986018e393d0321df7340e71898882"}, - {file = "tornado-6.5.2-cp39-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e792706668c87709709c18b353da1f7662317b563ff69f00bab83595940c7108"}, - {file = "tornado-6.5.2-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:06ceb1300fd70cb20e43b1ad8aaee0266e69e7ced38fa910ad2e03285009ce7c"}, - {file = "tornado-6.5.2-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:74db443e0f5251be86cbf37929f84d8c20c27a355dd452a5cfa2aada0d001ec4"}, - {file = "tornado-6.5.2-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b5e735ab2889d7ed33b32a459cac490eda71a1ba6857b0118de476ab6c366c04"}, - {file = "tornado-6.5.2-cp39-abi3-win32.whl", hash = "sha256:c6f29e94d9b37a95013bb669616352ddb82e3bfe8326fccee50583caebc8a5f0"}, - {file = "tornado-6.5.2-cp39-abi3-win_amd64.whl", hash = "sha256:e56a5af51cc30dd2cae649429af65ca2f6571da29504a07995175df14c18f35f"}, - {file = "tornado-6.5.2-cp39-abi3-win_arm64.whl", hash = "sha256:d6c33dc3672e3a1f3618eb63b7ef4683a7688e7b9e6e8f0d9aa5726360a004af"}, - {file = "tornado-6.5.2.tar.gz", hash = "sha256:ab53c8f9a0fa351e2c0741284e06c7a45da86afb544133201c5cc8578eb076a0"}, + {file = "tornado-6.5.7-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:148b2eb15c2c765a50796172c1e499649b35f30d2e3c3d3e15913cfa56bfb163"}, + {file = "tornado-6.5.7-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:9da38de27f1da3b78a966f0dae12b5a1ea9afe72ca805d84ff06508272ddf100"}, + {file = "tornado-6.5.7-cp39-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:8d759e71906ee783f8867b93bf26a265743da4c1e2f4a018464c1ba019862972"}, + {file = "tornado-6.5.7-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8a46347a18f23fb92b396beebe0fb78f61dda0cc302445202c16203d8a18848b"}, + {file = "tornado-6.5.7-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:7778b30bef919231265e91c69963ce0f49a1e9c07ac900bbe75b19ce2575ba92"}, + {file = "tornado-6.5.7-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:e726f0c75da7726eec023aa62751ff8878bd2737e34fbdd33b1ae5897d2200f5"}, + {file = "tornado-6.5.7-cp39-abi3-win32.whl", hash = "sha256:f8de3bf12d3efdd0cbe7c8887868198f8a91415e3f29fcf258d9b8eb7b1d9ae4"}, + {file = "tornado-6.5.7-cp39-abi3-win_amd64.whl", hash = "sha256:de942f843533a039ef9fa3d9c88c7cd8a7c94553fb5ad0154270989b3d99a2c4"}, + {file = "tornado-6.5.7-cp39-abi3-win_arm64.whl", hash = "sha256:ff934fce95643af5f11efdae618eaa73d469dc588641e5c8d19295a0c65c4796"}, + {file = "tornado-6.5.7.tar.gz", hash = "sha256:66c513a76cda70d53907bc27cf1447557699c2e95aa48ba27a442ff61c3ddfc2"}, ] [[package]] @@ -4322,6 +3918,7 @@ description = "Traitlets Python configuration system" optional = true python-versions = ">=3.8" groups = ["main"] +markers = "extra == \"latex\" or extra == \"docs\"" files = [ {file = "traitlets-5.14.3-py3-none-any.whl", hash = "sha256:b74e89e397b1ed28cc831db7aea759ba6640cb3de13090ca145426688ff1ac4f"}, {file = "traitlets-5.14.3.tar.gz", hash = "sha256:9ed0579d3502c94b4b3732ac120375cda96f923114522847de4b3bb98b96b6b7"}, @@ -4333,14 +3930,14 @@ test = ["argcomplete (>=3.0.3)", "mypy (>=1.7.0)", "pre-commit", "pytest (>=7.0, [[package]] name = "types-deprecated" -version = "1.2.15.20250304" +version = "1.3.1.20260408" description = "Typing stubs for Deprecated" optional = false -python-versions = ">=3.9" +python-versions = ">=3.10" groups = ["main"] files = [ - {file = "types_deprecated-1.2.15.20250304-py3-none-any.whl", hash = "sha256:86a65aa550ea8acf49f27e226b8953288cd851de887970fbbdf2239c116c3107"}, - {file = "types_deprecated-1.2.15.20250304.tar.gz", hash = "sha256:c329030553029de5cc6cb30f269c11f4e00e598c4241290179f63cda7d33f719"}, + {file = "types_deprecated-1.3.1.20260408-py3-none-any.whl", hash = "sha256:b64e1eab560d4fa9394a27a3099211344b0e0f2f3ac8026d825c86e70d65cdd5"}, + {file = "types_deprecated-1.3.1.20260408.tar.gz", hash = "sha256:62d6a86d0cc754c14bb2de31162d069b1c6a07ce11ee65e5258f8f75308eb3a3"}, ] [[package]] @@ -4357,47 +3954,47 @@ files = [ [[package]] name = "tzdata" -version = "2025.2" +version = "2026.2" description = "Provider of IANA time zone data" optional = true python-versions = ">=2" groups = ["main"] -markers = "extra == \"docs\"" +markers = "(sys_platform == \"win32\" or sys_platform == \"emscripten\") and extra == \"docs\"" files = [ - {file = "tzdata-2025.2-py2.py3-none-any.whl", hash = "sha256:1a403fada01ff9221ca8044d701868fa132215d84beb92242d9acd2147f667a8"}, - {file = "tzdata-2025.2.tar.gz", hash = "sha256:b60a638fcc0daffadf82fe0f57e53d06bdec2f36c4df66280ae79bce6bd6f2b9"}, + {file = "tzdata-2026.2-py2.py3-none-any.whl", hash = "sha256:bbe9af844f658da81a5f95019480da3a89415801f6cc966806612cc7169bffe7"}, + {file = "tzdata-2026.2.tar.gz", hash = "sha256:9173fde7d80d9018e02a662e168e5a2d04f87c41ea174b139fbef642eda62d10"}, ] [[package]] name = "urllib3" -version = "2.5.0" +version = "2.7.0" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = true -python-versions = ">=3.9" +python-versions = ">=3.10" groups = ["main"] markers = "extra == \"docs\"" files = [ - {file = "urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc"}, - {file = "urllib3-2.5.0.tar.gz", hash = "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760"}, + {file = "urllib3-2.7.0-py3-none-any.whl", hash = "sha256:9fb4c81ebbb1ce9531cce37674bbc6f1360472bc18ca9a553ede278ef7276897"}, + {file = "urllib3-2.7.0.tar.gz", hash = "sha256:231e0ec3b63ceb14667c67be60f2f2c40a518cb38b03af60abc813da26505f4c"}, ] [package.extras] -brotli = ["brotli (>=1.0.9) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=0.8.0) ; platform_python_implementation != \"CPython\""] +brotli = ["brotli (>=1.2.0) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=1.2.0.0) ; platform_python_implementation != \"CPython\""] h2 = ["h2 (>=4,<5)"] socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] -zstd = ["zstandard (>=0.18.0)"] +zstd = ["backports-zstd (>=1.0.0) ; python_version < \"3.14\""] [[package]] name = "wcwidth" -version = "0.2.14" +version = "0.6.0" description = "Measures the displayed width of unicode strings in a terminal" optional = true -python-versions = ">=3.6" +python-versions = ">=3.8" groups = ["main"] markers = "extra == \"latex\"" files = [ - {file = "wcwidth-0.2.14-py2.py3-none-any.whl", hash = "sha256:a7bb560c8aee30f9957e5f9895805edd20602f2d7f720186dfd906e82b4982e1"}, - {file = "wcwidth-0.2.14.tar.gz", hash = "sha256:4d478375d31bc5395a3c55c40ccdf3354688364cd61c4f6adacaa9215d0b3605"}, + {file = "wcwidth-0.6.0-py3-none-any.whl", hash = "sha256:1a3a1e510b553315f8e146c54764f4fb6264ffad731b3d78088cdb1478ffbdad"}, + {file = "wcwidth-0.6.0.tar.gz", hash = "sha256:cdc4e4262d6ef9a1a57e018384cbeb1208d8abbc64176027e2c2455c81313159"}, ] [[package]] @@ -4415,121 +4012,112 @@ files = [ [[package]] name = "wrapt" -version = "1.17.3" +version = "2.1.2" description = "Module for decorators, wrappers and monkey patching." optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "wrapt-1.17.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:88bbae4d40d5a46142e70d58bf664a89b6b4befaea7b2ecc14e03cedb8e06c04"}, - {file = "wrapt-1.17.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e6b13af258d6a9ad602d57d889f83b9d5543acd471eee12eb51f5b01f8eb1bc2"}, - {file = "wrapt-1.17.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd341868a4b6714a5962c1af0bd44f7c404ef78720c7de4892901e540417111c"}, - {file = "wrapt-1.17.3-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f9b2601381be482f70e5d1051a5965c25fb3625455a2bf520b5a077b22afb775"}, - {file = "wrapt-1.17.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:343e44b2a8e60e06a7e0d29c1671a0d9951f59174f3709962b5143f60a2a98bd"}, - {file = "wrapt-1.17.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:33486899acd2d7d3066156b03465b949da3fd41a5da6e394ec49d271baefcf05"}, - {file = "wrapt-1.17.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e6f40a8aa5a92f150bdb3e1c44b7e98fb7113955b2e5394122fa5532fec4b418"}, - {file = "wrapt-1.17.3-cp310-cp310-win32.whl", hash = "sha256:a36692b8491d30a8c75f1dfee65bef119d6f39ea84ee04d9f9311f83c5ad9390"}, - {file = "wrapt-1.17.3-cp310-cp310-win_amd64.whl", hash = "sha256:afd964fd43b10c12213574db492cb8f73b2f0826c8df07a68288f8f19af2ebe6"}, - {file = "wrapt-1.17.3-cp310-cp310-win_arm64.whl", hash = "sha256:af338aa93554be859173c39c85243970dc6a289fa907402289eeae7543e1ae18"}, - {file = "wrapt-1.17.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:273a736c4645e63ac582c60a56b0acb529ef07f78e08dc6bfadf6a46b19c0da7"}, - {file = "wrapt-1.17.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5531d911795e3f935a9c23eb1c8c03c211661a5060aab167065896bbf62a5f85"}, - {file = "wrapt-1.17.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0610b46293c59a3adbae3dee552b648b984176f8562ee0dba099a56cfbe4df1f"}, - {file = "wrapt-1.17.3-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b32888aad8b6e68f83a8fdccbf3165f5469702a7544472bdf41f582970ed3311"}, - {file = "wrapt-1.17.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8cccf4f81371f257440c88faed6b74f1053eef90807b77e31ca057b2db74edb1"}, - {file = "wrapt-1.17.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8a210b158a34164de8bb68b0e7780041a903d7b00c87e906fb69928bf7890d5"}, - {file = "wrapt-1.17.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:79573c24a46ce11aab457b472efd8d125e5a51da2d1d24387666cd85f54c05b2"}, - {file = "wrapt-1.17.3-cp311-cp311-win32.whl", hash = "sha256:c31eebe420a9a5d2887b13000b043ff6ca27c452a9a22fa71f35f118e8d4bf89"}, - {file = "wrapt-1.17.3-cp311-cp311-win_amd64.whl", hash = "sha256:0b1831115c97f0663cb77aa27d381237e73ad4f721391a9bfb2fe8bc25fa6e77"}, - {file = "wrapt-1.17.3-cp311-cp311-win_arm64.whl", hash = "sha256:5a7b3c1ee8265eb4c8f1b7d29943f195c00673f5ab60c192eba2d4a7eae5f46a"}, - {file = "wrapt-1.17.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:ab232e7fdb44cdfbf55fc3afa31bcdb0d8980b9b95c38b6405df2acb672af0e0"}, - {file = "wrapt-1.17.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:9baa544e6acc91130e926e8c802a17f3b16fbea0fd441b5a60f5cf2cc5c3deba"}, - {file = "wrapt-1.17.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6b538e31eca1a7ea4605e44f81a48aa24c4632a277431a6ed3f328835901f4fd"}, - {file = "wrapt-1.17.3-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:042ec3bb8f319c147b1301f2393bc19dba6e176b7da446853406d041c36c7828"}, - {file = "wrapt-1.17.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3af60380ba0b7b5aeb329bc4e402acd25bd877e98b3727b0135cb5c2efdaefe9"}, - {file = "wrapt-1.17.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0b02e424deef65c9f7326d8c19220a2c9040c51dc165cddb732f16198c168396"}, - {file = "wrapt-1.17.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:74afa28374a3c3a11b3b5e5fca0ae03bef8450d6aa3ab3a1e2c30e3a75d023dc"}, - {file = "wrapt-1.17.3-cp312-cp312-win32.whl", hash = "sha256:4da9f45279fff3543c371d5ababc57a0384f70be244de7759c85a7f989cb4ebe"}, - {file = "wrapt-1.17.3-cp312-cp312-win_amd64.whl", hash = "sha256:e71d5c6ebac14875668a1e90baf2ea0ef5b7ac7918355850c0908ae82bcb297c"}, - {file = "wrapt-1.17.3-cp312-cp312-win_arm64.whl", hash = "sha256:604d076c55e2fdd4c1c03d06dc1a31b95130010517b5019db15365ec4a405fc6"}, - {file = "wrapt-1.17.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a47681378a0439215912ef542c45a783484d4dd82bac412b71e59cf9c0e1cea0"}, - {file = "wrapt-1.17.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:54a30837587c6ee3cd1a4d1c2ec5d24e77984d44e2f34547e2323ddb4e22eb77"}, - {file = "wrapt-1.17.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:16ecf15d6af39246fe33e507105d67e4b81d8f8d2c6598ff7e3ca1b8a37213f7"}, - {file = "wrapt-1.17.3-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6fd1ad24dc235e4ab88cda009e19bf347aabb975e44fd5c2fb22a3f6e4141277"}, - {file = "wrapt-1.17.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ed61b7c2d49cee3c027372df5809a59d60cf1b6c2f81ee980a091f3afed6a2d"}, - {file = "wrapt-1.17.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:423ed5420ad5f5529db9ce89eac09c8a2f97da18eb1c870237e84c5a5c2d60aa"}, - {file = "wrapt-1.17.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e01375f275f010fcbf7f643b4279896d04e571889b8a5b3f848423d91bf07050"}, - {file = "wrapt-1.17.3-cp313-cp313-win32.whl", hash = "sha256:53e5e39ff71b3fc484df8a522c933ea2b7cdd0d5d15ae82e5b23fde87d44cbd8"}, - {file = "wrapt-1.17.3-cp313-cp313-win_amd64.whl", hash = "sha256:1f0b2f40cf341ee8cc1a97d51ff50dddb9fcc73241b9143ec74b30fc4f44f6cb"}, - {file = "wrapt-1.17.3-cp313-cp313-win_arm64.whl", hash = "sha256:7425ac3c54430f5fc5e7b6f41d41e704db073309acfc09305816bc6a0b26bb16"}, - {file = "wrapt-1.17.3-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:cf30f6e3c077c8e6a9a7809c94551203c8843e74ba0c960f4a98cd80d4665d39"}, - {file = "wrapt-1.17.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:e228514a06843cae89621384cfe3a80418f3c04aadf8a3b14e46a7be704e4235"}, - {file = "wrapt-1.17.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:5ea5eb3c0c071862997d6f3e02af1d055f381b1d25b286b9d6644b79db77657c"}, - {file = "wrapt-1.17.3-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:281262213373b6d5e4bb4353bc36d1ba4084e6d6b5d242863721ef2bf2c2930b"}, - {file = "wrapt-1.17.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dc4a8d2b25efb6681ecacad42fca8859f88092d8732b170de6a5dddd80a1c8fa"}, - {file = "wrapt-1.17.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:373342dd05b1d07d752cecbec0c41817231f29f3a89aa8b8843f7b95992ed0c7"}, - {file = "wrapt-1.17.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d40770d7c0fd5cbed9d84b2c3f2e156431a12c9a37dc6284060fb4bec0b7ffd4"}, - {file = "wrapt-1.17.3-cp314-cp314-win32.whl", hash = "sha256:fbd3c8319de8e1dc79d346929cd71d523622da527cca14e0c1d257e31c2b8b10"}, - {file = "wrapt-1.17.3-cp314-cp314-win_amd64.whl", hash = "sha256:e1a4120ae5705f673727d3253de3ed0e016f7cd78dc463db1b31e2463e1f3cf6"}, - {file = "wrapt-1.17.3-cp314-cp314-win_arm64.whl", hash = "sha256:507553480670cab08a800b9463bdb881b2edeed77dc677b0a5915e6106e91a58"}, - {file = "wrapt-1.17.3-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:ed7c635ae45cfbc1a7371f708727bf74690daedc49b4dba310590ca0bd28aa8a"}, - {file = "wrapt-1.17.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:249f88ed15503f6492a71f01442abddd73856a0032ae860de6d75ca62eed8067"}, - {file = "wrapt-1.17.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5a03a38adec8066d5a37bea22f2ba6bbf39fcdefbe2d91419ab864c3fb515454"}, - {file = "wrapt-1.17.3-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:5d4478d72eb61c36e5b446e375bbc49ed002430d17cdec3cecb36993398e1a9e"}, - {file = "wrapt-1.17.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:223db574bb38637e8230eb14b185565023ab624474df94d2af18f1cdb625216f"}, - {file = "wrapt-1.17.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e405adefb53a435f01efa7ccdec012c016b5a1d3f35459990afc39b6be4d5056"}, - {file = "wrapt-1.17.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:88547535b787a6c9ce4086917b6e1d291aa8ed914fdd3a838b3539dc95c12804"}, - {file = "wrapt-1.17.3-cp314-cp314t-win32.whl", hash = "sha256:41b1d2bc74c2cac6f9074df52b2efbef2b30bdfe5f40cb78f8ca22963bc62977"}, - {file = "wrapt-1.17.3-cp314-cp314t-win_amd64.whl", hash = "sha256:73d496de46cd2cdbdbcce4ae4bcdb4afb6a11234a1df9c085249d55166b95116"}, - {file = "wrapt-1.17.3-cp314-cp314t-win_arm64.whl", hash = "sha256:f38e60678850c42461d4202739f9bf1e3a737c7ad283638251e79cc49effb6b6"}, - {file = "wrapt-1.17.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:70d86fa5197b8947a2fa70260b48e400bf2ccacdcab97bb7de47e3d1e6312225"}, - {file = "wrapt-1.17.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:df7d30371a2accfe4013e90445f6388c570f103d61019b6b7c57e0265250072a"}, - {file = "wrapt-1.17.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:caea3e9c79d5f0d2c6d9ab96111601797ea5da8e6d0723f77eabb0d4068d2b2f"}, - {file = "wrapt-1.17.3-cp38-cp38-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:758895b01d546812d1f42204bd443b8c433c44d090248bf22689df673ccafe00"}, - {file = "wrapt-1.17.3-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:02b551d101f31694fc785e58e0720ef7d9a10c4e62c1c9358ce6f63f23e30a56"}, - {file = "wrapt-1.17.3-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:656873859b3b50eeebe6db8b1455e99d90c26ab058db8e427046dbc35c3140a5"}, - {file = "wrapt-1.17.3-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:a9a2203361a6e6404f80b99234fe7fb37d1fc73487b5a78dc1aa5b97201e0f22"}, - {file = "wrapt-1.17.3-cp38-cp38-win32.whl", hash = "sha256:55cbbc356c2842f39bcc553cf695932e8b30e30e797f961860afb308e6b1bb7c"}, - {file = "wrapt-1.17.3-cp38-cp38-win_amd64.whl", hash = "sha256:ad85e269fe54d506b240d2d7b9f5f2057c2aa9a2ea5b32c66f8902f768117ed2"}, - {file = "wrapt-1.17.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:30ce38e66630599e1193798285706903110d4f057aab3168a34b7fdc85569afc"}, - {file = "wrapt-1.17.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:65d1d00fbfb3ea5f20add88bbc0f815150dbbde3b026e6c24759466c8b5a9ef9"}, - {file = "wrapt-1.17.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a7c06742645f914f26c7f1fa47b8bc4c91d222f76ee20116c43d5ef0912bba2d"}, - {file = "wrapt-1.17.3-cp39-cp39-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:7e18f01b0c3e4a07fe6dfdb00e29049ba17eadbc5e7609a2a3a4af83ab7d710a"}, - {file = "wrapt-1.17.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0f5f51a6466667a5a356e6381d362d259125b57f059103dd9fdc8c0cf1d14139"}, - {file = "wrapt-1.17.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:59923aa12d0157f6b82d686c3fd8e1166fa8cdfb3e17b42ce3b6147ff81528df"}, - {file = "wrapt-1.17.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:46acc57b331e0b3bcb3e1ca3b421d65637915cfcd65eb783cb2f78a511193f9b"}, - {file = "wrapt-1.17.3-cp39-cp39-win32.whl", hash = "sha256:3e62d15d3cfa26e3d0788094de7b64efa75f3a53875cdbccdf78547aed547a81"}, - {file = "wrapt-1.17.3-cp39-cp39-win_amd64.whl", hash = "sha256:1f23fa283f51c890eda8e34e4937079114c74b4c81d2b2f1f1d94948f5cc3d7f"}, - {file = "wrapt-1.17.3-cp39-cp39-win_arm64.whl", hash = "sha256:24c2ed34dc222ed754247a2702b1e1e89fdbaa4016f324b4b8f1a802d4ffe87f"}, - {file = "wrapt-1.17.3-py3-none-any.whl", hash = "sha256:7171ae35d2c33d326ac19dd8facb1e82e5fd04ef8c6c0e394d7af55a55051c22"}, - {file = "wrapt-1.17.3.tar.gz", hash = "sha256:f66eb08feaa410fe4eebd17f2a2c8e2e46d3476e9f8c783daa8e09e0faa666d0"}, -] - -[[package]] -name = "zipp" -version = "3.23.0" -description = "Backport of pathlib-compatible object wrapper for zip files" -optional = true python-versions = ">=3.9" groups = ["main"] -markers = "extra == \"docs\" and python_version == \"3.9\"" files = [ - {file = "zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e"}, - {file = "zipp-3.23.0.tar.gz", hash = "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166"}, + {file = "wrapt-2.1.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4b7a86d99a14f76facb269dc148590c01aaf47584071809a70da30555228158c"}, + {file = "wrapt-2.1.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a819e39017f95bf7aede768f75915635aa8f671f2993c036991b8d3bfe8dbb6f"}, + {file = "wrapt-2.1.2-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:5681123e60aed0e64c7d44f72bbf8b4ce45f79d81467e2c4c728629f5baf06eb"}, + {file = "wrapt-2.1.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2b8b28e97a44d21836259739ae76284e180b18abbb4dcfdff07a415cf1016c3e"}, + {file = "wrapt-2.1.2-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cef91c95a50596fcdc31397eb6955476f82ae8a3f5a8eabdc13611b60ee380ba"}, + {file = "wrapt-2.1.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:dad63212b168de8569b1c512f4eac4b57f2c6934b30df32d6ee9534a79f1493f"}, + {file = "wrapt-2.1.2-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:d307aa6888d5efab2c1cde09843d48c843990be13069003184b67d426d145394"}, + {file = "wrapt-2.1.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c87cf3f0c85e27b3ac7d9ad95da166bf8739ca215a8b171e8404a2d739897a45"}, + {file = "wrapt-2.1.2-cp310-cp310-win32.whl", hash = "sha256:d1c5fea4f9fe3762e2b905fdd67df51e4be7a73b7674957af2d2ade71a5c075d"}, + {file = "wrapt-2.1.2-cp310-cp310-win_amd64.whl", hash = "sha256:d8f7740e1af13dff2684e4d56fe604a7e04d6c94e737a60568d8d4238b9a0c71"}, + {file = "wrapt-2.1.2-cp310-cp310-win_arm64.whl", hash = "sha256:1c6cc827c00dc839350155f316f1f8b4b0c370f52b6a19e782e2bda89600c7dc"}, + {file = "wrapt-2.1.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:96159a0ee2b0277d44201c3b5be479a9979cf154e8c82fa5df49586a8e7679bb"}, + {file = "wrapt-2.1.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:98ba61833a77b747901e9012072f038795de7fc77849f1faa965464f3f87ff2d"}, + {file = "wrapt-2.1.2-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:767c0dbbe76cae2a60dd2b235ac0c87c9cccf4898aef8062e57bead46b5f6894"}, + {file = "wrapt-2.1.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9c691a6bc752c0cc4711cc0c00896fcd0f116abc253609ef64ef930032821842"}, + {file = "wrapt-2.1.2-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f3b7d73012ea75aee5844de58c88f44cf62d0d62711e39da5a82824a7c4626a8"}, + {file = "wrapt-2.1.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:577dff354e7acd9d411eaf4bfe76b724c89c89c8fc9b7e127ee28c5f7bcb25b6"}, + {file = "wrapt-2.1.2-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:3d7b6fd105f8b24e5bd23ccf41cb1d1099796524bcc6f7fbb8fe576c44befbc9"}, + {file = "wrapt-2.1.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:866abdbf4612e0b34764922ef8b1c5668867610a718d3053d59e24a5e5fcfc15"}, + {file = "wrapt-2.1.2-cp311-cp311-win32.whl", hash = "sha256:5a0a0a3a882393095573344075189eb2d566e0fd205a2b6414e9997b1b800a8b"}, + {file = "wrapt-2.1.2-cp311-cp311-win_amd64.whl", hash = "sha256:64a07a71d2730ba56f11d1a4b91f7817dc79bc134c11516b75d1921a7c6fcda1"}, + {file = "wrapt-2.1.2-cp311-cp311-win_arm64.whl", hash = "sha256:b89f095fe98bc12107f82a9f7d570dc83a0870291aeb6b1d7a7d35575f55d98a"}, + {file = "wrapt-2.1.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ff2aad9c4cda28a8f0653fc2d487596458c2a3f475e56ba02909e950a9efa6a9"}, + {file = "wrapt-2.1.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6433ea84e1cfacf32021d2a4ee909554ade7fd392caa6f7c13f1f4bf7b8e8748"}, + {file = "wrapt-2.1.2-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:c20b757c268d30d6215916a5fa8461048d023865d888e437fab451139cad6c8e"}, + {file = "wrapt-2.1.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:79847b83eb38e70d93dc392c7c5b587efe65b3e7afcc167aa8abd5d60e8761c8"}, + {file = "wrapt-2.1.2-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f8fba1bae256186a83d1875b2b1f4e2d1242e8fac0f58ec0d7e41b26967b965c"}, + {file = "wrapt-2.1.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e3d3b35eedcf5f7d022291ecd7533321c4775f7b9cd0050a31a68499ba45757c"}, + {file = "wrapt-2.1.2-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:6f2c5390460de57fa9582bc8a1b7a6c86e1a41dfad74c5225fc07044c15cc8d1"}, + {file = "wrapt-2.1.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7dfa9f2cf65d027b951d05c662cc99ee3bd01f6e4691ed39848a7a5fffc902b2"}, + {file = "wrapt-2.1.2-cp312-cp312-win32.whl", hash = "sha256:eba8155747eb2cae4a0b913d9ebd12a1db4d860fc4c829d7578c7b989bd3f2f0"}, + {file = "wrapt-2.1.2-cp312-cp312-win_amd64.whl", hash = "sha256:1c51c738d7d9faa0b3601708e7e2eda9bf779e1b601dce6c77411f2a1b324a63"}, + {file = "wrapt-2.1.2-cp312-cp312-win_arm64.whl", hash = "sha256:c8e46ae8e4032792eb2f677dbd0d557170a8e5524d22acc55199f43efedd39bf"}, + {file = "wrapt-2.1.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:787fd6f4d67befa6fe2abdffcbd3de2d82dfc6fb8a6d850407c53332709d030b"}, + {file = "wrapt-2.1.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4bdf26e03e6d0da3f0e9422fd36bcebf7bc0eeb55fdf9c727a09abc6b9fe472e"}, + {file = "wrapt-2.1.2-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:bbac24d879aa22998e87f6b3f481a5216311e7d53c7db87f189a7a0266dafffb"}, + {file = "wrapt-2.1.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:16997dfb9d67addc2e3f41b62a104341e80cac52f91110dece393923c0ebd5ca"}, + {file = "wrapt-2.1.2-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:162e4e2ba7542da9027821cb6e7c5e068d64f9a10b5f15512ea28e954893a267"}, + {file = "wrapt-2.1.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f29c827a8d9936ac320746747a016c4bc66ef639f5cd0d32df24f5eacbf9c69f"}, + {file = "wrapt-2.1.2-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:a9dd9813825f7ecb018c17fd147a01845eb330254dff86d3b5816f20f4d6aaf8"}, + {file = "wrapt-2.1.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6f8dbdd3719e534860d6a78526aafc220e0241f981367018c2875178cf83a413"}, + {file = "wrapt-2.1.2-cp313-cp313-win32.whl", hash = "sha256:5c35b5d82b16a3bc6e0a04349b606a0582bc29f573786aebe98e0c159bc48db6"}, + {file = "wrapt-2.1.2-cp313-cp313-win_amd64.whl", hash = "sha256:f8bc1c264d8d1cf5b3560a87bbdd31131573eb25f9f9447bb6252b8d4c44a3a1"}, + {file = "wrapt-2.1.2-cp313-cp313-win_arm64.whl", hash = "sha256:3beb22f674550d5634642c645aba4c72a2c66fb185ae1aebe1e955fae5a13baf"}, + {file = "wrapt-2.1.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0fc04bc8664a8bc4c8e00b37b5355cffca2535209fba1abb09ae2b7c76ddf82b"}, + {file = "wrapt-2.1.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a9b9d50c9af998875a1482a038eb05755dfd6fe303a313f6a940bb53a83c3f18"}, + {file = "wrapt-2.1.2-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2d3ff4f0024dd224290c0eabf0240f1bfc1f26363431505fb1b0283d3b08f11d"}, + {file = "wrapt-2.1.2-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3278c471f4468ad544a691b31bb856374fbdefb7fee1a152153e64019379f015"}, + {file = "wrapt-2.1.2-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a8914c754d3134a3032601c6984db1c576e6abaf3fc68094bb8ab1379d75ff92"}, + {file = "wrapt-2.1.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:ff95d4264e55839be37bafe1536db2ab2de19da6b65f9244f01f332b5286cfbf"}, + {file = "wrapt-2.1.2-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:76405518ca4e1b76fbb1b9f686cff93aebae03920cc55ceeec48ff9f719c5f67"}, + {file = "wrapt-2.1.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c0be8b5a74c5824e9359b53e7e58bef71a729bacc82e16587db1c4ebc91f7c5a"}, + {file = "wrapt-2.1.2-cp313-cp313t-win32.whl", hash = "sha256:f01277d9a5fc1862f26f7626da9cf443bebc0abd2f303f41c5e995b15887dabd"}, + {file = "wrapt-2.1.2-cp313-cp313t-win_amd64.whl", hash = "sha256:84ce8f1c2104d2f6daa912b1b5b039f331febfeee74f8042ad4e04992bd95c8f"}, + {file = "wrapt-2.1.2-cp313-cp313t-win_arm64.whl", hash = "sha256:a93cd767e37faeddbe07d8fc4212d5cba660af59bdb0f6372c93faaa13e6e679"}, + {file = "wrapt-2.1.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:1370e516598854e5b4366e09ce81e08bfe94d42b0fd569b88ec46cc56d9164a9"}, + {file = "wrapt-2.1.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:6de1a3851c27e0bd6a04ca993ea6f80fc53e6c742ee1601f486c08e9f9b900a9"}, + {file = "wrapt-2.1.2-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:de9f1a2bbc5ac7f6012ec24525bdd444765a2ff64b5985ac6e0692144838542e"}, + {file = "wrapt-2.1.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:970d57ed83fa040d8b20c52fe74a6ae7e3775ae8cff5efd6a81e06b19078484c"}, + {file = "wrapt-2.1.2-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3969c56e4563c375861c8df14fa55146e81ac11c8db49ea6fb7f2ba58bc1ff9a"}, + {file = "wrapt-2.1.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:57d7c0c980abdc5f1d98b11a2aa3bb159790add80258c717fa49a99921456d90"}, + {file = "wrapt-2.1.2-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:776867878e83130c7a04237010463372e877c1c994d449ca6aaafeab6aab2586"}, + {file = "wrapt-2.1.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:fab036efe5464ec3291411fabb80a7a39e2dd80bae9bcbeeca5087fdfa891e19"}, + {file = "wrapt-2.1.2-cp314-cp314-win32.whl", hash = "sha256:e6ed62c82ddf58d001096ae84ce7f833db97ae2263bff31c9b336ba8cfe3f508"}, + {file = "wrapt-2.1.2-cp314-cp314-win_amd64.whl", hash = "sha256:467e7c76315390331c67073073d00662015bb730c566820c9ca9b54e4d67fd04"}, + {file = "wrapt-2.1.2-cp314-cp314-win_arm64.whl", hash = "sha256:da1f00a557c66225d53b095a97eace0fc5349e3bfda28fa34ffae238978ee575"}, + {file = "wrapt-2.1.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:62503ffbc2d3a69891cf29beeaccdb4d5e0a126e2b6a851688d4777e01428dbb"}, + {file = "wrapt-2.1.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c7e6cd120ef837d5b6f860a6ea3745f8763805c418bb2f12eeb1fa6e25f22d22"}, + {file = "wrapt-2.1.2-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:3769a77df8e756d65fbc050333f423c01ae012b4f6731aaf70cf2bef61b34596"}, + {file = "wrapt-2.1.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a76d61a2e851996150ba0f80582dd92a870643fa481f3b3846f229de88caf044"}, + {file = "wrapt-2.1.2-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6f97edc9842cf215312b75fe737ee7c8adda75a89979f8e11558dfff6343cc4b"}, + {file = "wrapt-2.1.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:4006c351de6d5007aa33a551f600404ba44228a89e833d2fadc5caa5de8edfbf"}, + {file = "wrapt-2.1.2-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:a9372fc3639a878c8e7d87e1556fa209091b0a66e912c611e3f833e2c4202be2"}, + {file = "wrapt-2.1.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:3144b027ff30cbd2fca07c0a87e67011adb717eb5f5bd8496325c17e454257a3"}, + {file = "wrapt-2.1.2-cp314-cp314t-win32.whl", hash = "sha256:3b8d15e52e195813efe5db8cec156eebe339aaf84222f4f4f051a6c01f237ed7"}, + {file = "wrapt-2.1.2-cp314-cp314t-win_amd64.whl", hash = "sha256:08ffa54146a7559f5b8df4b289b46d963a8e74ed16ba3687f99896101a3990c5"}, + {file = "wrapt-2.1.2-cp314-cp314t-win_arm64.whl", hash = "sha256:72aaa9d0d8e4ed0e2e98019cea47a21f823c9dd4b43c7b77bba6679ffcca6a00"}, + {file = "wrapt-2.1.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5e0fa9cc32300daf9eb09a1f5bdc6deb9a79defd70d5356ba453bcd50aef3742"}, + {file = "wrapt-2.1.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:710f6e5dfaf6a5d5c397d2d6758a78fecd9649deb21f1b645f5b57a328d63050"}, + {file = "wrapt-2.1.2-cp39-cp39-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:305d8a1755116bfdad5dda9e771dcb2138990a1d66e9edd81658816edf51aed1"}, + {file = "wrapt-2.1.2-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f0d8fc30a43b5fe191cf2b1a0c82bab2571dadd38e7c0062ee87d6df858dd06e"}, + {file = "wrapt-2.1.2-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a5d516e22aedb7c9c1d47cba1c63160b1a6f61ec2f3948d127cd38d5cfbb556f"}, + {file = "wrapt-2.1.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:45914e8efbe4b9d5102fcf0e8e2e3258b83a5d5fba9f8f7b6d15681e9d29ffe0"}, + {file = "wrapt-2.1.2-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:478282ebd3795a089154fb16d3db360e103aa13d3b2ad30f8f6aac0d2207de0e"}, + {file = "wrapt-2.1.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:3756219045f73fb28c5d7662778e4156fbd06cf823c4d2d4b19f97305e52819c"}, + {file = "wrapt-2.1.2-cp39-cp39-win32.whl", hash = "sha256:b8aefb4dbb18d904b96827435a763fa42fc1f08ea096a391710407a60983ced8"}, + {file = "wrapt-2.1.2-cp39-cp39-win_amd64.whl", hash = "sha256:e5aeab8fe15c3dff75cfee94260dcd9cded012d4ff06add036c28fae7718593b"}, + {file = "wrapt-2.1.2-cp39-cp39-win_arm64.whl", hash = "sha256:f069e113743a21a3defac6677f000068ebb931639f789b5b226598e247a4c89e"}, + {file = "wrapt-2.1.2-py3-none-any.whl", hash = "sha256:b8fd6fa2b2c4e7621808f8c62e8317f4aae56e59721ad933bac5239d913cf0e8"}, + {file = "wrapt-2.1.2.tar.gz", hash = "sha256:3996a67eecc2c68fd47b4e3c564405a5777367adfd9b8abb58387b63ee83b21e"}, ] [package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] -cover = ["pytest-cov"] -doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] -enabler = ["pytest-enabler (>=2.2)"] -test = ["big-O", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more_itertools", "pytest (>=6,!=8.1.*)", "pytest-ignore-flaky"] -type = ["pytest-mypy"] +dev = ["pytest", "setuptools"] [extras] -docs = ["Sphinx", "matplotlib", "myst-parser", "nbsphinx", "pandoc", "seaborn", "sphinx-rtd-theme"] +docs = ["Sphinx", "matplotlib", "matplotlib-inline", "myst-parser", "nbsphinx", "pandoc", "seaborn", "sphinx-rtd-theme", "toml"] latex = ["ipython"] [metadata] lock-version = "2.1" -python-versions = "^3.9,<3.13" -content-hash = "7497c2e55515737cc5b451578890b87969f1899daf957fe71ffe1b1b9eac7974" +python-versions = ">=3.11, <3.13" +content-hash = "2b96409c96f43f18016b7fe078e8dff2b5f2851903018e4f359f01c1f158d582" diff --git a/pyproject.toml b/pyproject.toml index b1dc24166..4d9342571 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "pyquil" -version = "4.17.1-rc.0" +version = "4.19.0-rc.3" description = "A Python library for creating Quantum Instruction Language (Quil) programs." authors = ["Rigetti Computing "] readme = "README.md" @@ -10,8 +10,6 @@ license = "Apache-2.0" classifiers = [ "Development Status :: 5 - Production/Stable", "License :: OSI Approved :: Apache Software License", - "Programming Language :: Python :: 3.9", - "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Operating System :: OS Independent", @@ -22,7 +20,7 @@ exclude = ["pyquil/conftest.py"] [tool.poetry.dependencies] # TODO(#1816): Loosen this bound once we've resolved support for Python 3.13+. -python = "^3.9,<3.13" +python = ">=3.11, <3.13" numpy = ">=1.26,<3" scipy = "^1.11" rpcq = "^3.11.0" @@ -45,26 +43,27 @@ pandoc = {version = "2.4b0", optional = true} matplotlib = {version = "^3.9.0", optional = true} matplotlib-inline = {version = "^0.1.7", optional = true} seaborn = {version = "^0.13.2", optional = true} +toml = {version = "^0.10.2", optional = true} +rigetti-quax = ">=0.6.6" [tool.poetry.extras] latex = ["ipython"] -docs = ["Sphinx", "sphinx-rtd-theme", "nbsphinx", "myst-parser", "pandoc", "matplotlib", "matlotlib-inline", "seaborn", "toml"] +docs = ["Sphinx", "sphinx-rtd-theme", "nbsphinx", "myst-parser", "pandoc", "matplotlib", "matplotlib-inline", "seaborn", "toml"] [tool.poetry.group.dev.dependencies] typing-extensions = "^4.12.0" setuptools = {version = "^78.0.0", python = ">=3.12"} ruff = "^0.4.6" -pytest = "^8.2.0" +pytest = ">=8.2.0,<10" pytest-cov = "^5.0.0" mypy = "^1.10.0" -toml = "^0.10.2" pytest-xdist = "^3.6.1" pytest-rerunfailures = "^14.0.0" pytest-timeout = "^2.3.1" pytest-mock = "^3.14.0" pytest-benchmark = "4.0.0" respx = "^0.21.1" -syrupy = "^4.6.1" +syrupy = "^5.3.3" jinja2 = {version = ">=3.1.6", optional = true} # see: https://osv.dev/vulnerability/GHSA-gmj6-6f8f-6699 and https://osv.dev/vulnerability/GHSA-q2x7-8rv6-6q7h and https://osv.dev/GHSA-cpwx-vrp4-4pq7 h11 = {version = ">=0.16.0", optional = true} # see: https://osv.dev/vulnerability/GHSA-vqfr-h8mv-ghfj @@ -100,7 +99,7 @@ exclude = [ ] line-length = 120 indent-width = 4 -target-version = "py39" +target-version = "py311" [tool.ruff.lint] select = ["D", "E4", "E7", "E9", "F", "I", "B", "S", "UP", "W", "NPY201"] @@ -108,6 +107,8 @@ ignore = [ "D105", # Allow missing documentation in dunder method. "D203", # This conflicts with D211. "D213", # This conflicts with D212. + "UP007", # Optional[X] → X | None is not applied in files with from __future__ import annotations. + "UP038", # Deprecated: isinstance(x, (A, B)) → isinstance(x, A | B) has a runtime cost. ] # Allow fix for all enabled rules (when `--fix`) is provided. fixable = ["ALL"] diff --git a/pyquil/api/_abstract_compiler.py b/pyquil/api/_abstract_compiler.py index 880845054..83d1e2692 100644 --- a/pyquil/api/_abstract_compiler.py +++ b/pyquil/api/_abstract_compiler.py @@ -19,7 +19,7 @@ from abc import ABC, abstractmethod from collections.abc import Sequence from dataclasses import dataclass, field, fields -from typing import Any, Optional, Union +from typing import Any, Union from deprecated.sphinx import deprecated from qcs_sdk import QCSClient @@ -100,8 +100,8 @@ def __init__( *, quantum_processor: AbstractQuantumProcessor, timeout: float, - client_configuration: Optional[QCSClient] = None, - quilc_client: Optional[QuilcClient] = None, + client_configuration: QCSClient | None = None, + quilc_client: QuilcClient | None = None, ) -> None: self.quantum_processor = quantum_processor self._timeout = timeout @@ -121,7 +121,7 @@ def get_version_info(self) -> dict[str, Any]: """ return {"quilc": self._compiler_client.get_version()} - def quil_to_native_quil(self, program: Program, *, protoquil: Optional[bool] = None) -> Program: + def quil_to_native_quil(self, program: Program, *, protoquil: bool | None = None) -> Program: """Convert a Quil program into native Quil, which is supported for execution on a QPU.""" result = self._compile_with_quilc( program.out(calibrations=False), @@ -134,7 +134,7 @@ def quil_to_native_quil(self, program: Program, *, protoquil: Optional[bool] = N return native_program - def _compile_with_quilc(self, input: str, options: Optional[CompilerOpts] = None) -> CompilationResult: + def _compile_with_quilc(self, input: str, options: CompilerOpts | None = None) -> CompilationResult: self._connect() # convert the pyquil ``TargetDevice`` to the qcs_sdk ``TargetDevice`` @@ -186,7 +186,7 @@ def _check_quilc_version(version: str) -> None: major, minor, _ = map(int, version.split(".")) if major == 1 and minor < 8: raise QuilcVersionMismatch( - "Must use quilc >= 1.8.0 with pyquil >= 2.8.0, but you " f"have quilc {version} and pyquil {pyquil_version}" + f"Must use quilc >= 1.8.0 with pyquil >= 2.8.0, but you have quilc {version} and pyquil {pyquil_version}" ) @@ -208,8 +208,8 @@ def generate_rb_sequence( self, depth: int, gateset: Sequence[Gate], - seed: Optional[int] = None, - interleaver: Optional[Program] = None, + seed: int | None = None, + interleaver: Program | None = None, ) -> list[Program]: r"""Construct a randomized benchmarking experiment on the given qubits, decomposing into gateset. diff --git a/pyquil/api/_benchmark.py b/pyquil/api/_benchmark.py index 253bf7a51..6e1eaf512 100644 --- a/pyquil/api/_benchmark.py +++ b/pyquil/api/_benchmark.py @@ -14,7 +14,7 @@ # limitations under the License. ############################################################################## from collections.abc import Sequence -from typing import Optional, cast +from typing import cast from qcs_sdk import QCSClient from qcs_sdk.compiler.quilc import ( @@ -35,7 +35,7 @@ class BenchmarkConnection(AbstractBenchmarker): """Represents a connection to a server that generates benchmarking data.""" - def __init__(self, *, timeout: float = 10.0, client_configuration: Optional[QCSClient] = None): + def __init__(self, *, timeout: float = 10.0, client_configuration: QCSClient | None = None): """Client to communicate with the benchmarking data endpoint. :param timeout: Time limit for requests, in seconds. @@ -60,7 +60,7 @@ def apply_clifford_to_pauli(self, clifford: Program, pauli_in: PauliTerm) -> Pau if is_identity(pauli_in): return pauli_in - indices_and_terms = list(zip(*list(pauli_in.operations_as_set()))) + indices_and_terms = list(zip(*list(pauli_in.operations_as_set()), strict=False)) request = ConjugateByCliffordRequest( pauli=QuilcPauliTerm( @@ -87,8 +87,8 @@ def generate_rb_sequence( self, depth: int, gateset: Sequence[Gate], - seed: Optional[int] = None, - interleaver: Optional[Program] = None, + seed: int | None = None, + interleaver: Program | None = None, ) -> list[Program]: """Construct a randomized benchmarking experiment on the given qubits, decomposing into gateset. @@ -124,7 +124,7 @@ def generate_rb_sequence( gateset_as_program = address_qubits(sum(gateset, Program())) # type: ignore qubits = len(gateset_as_program.get_qubits()) gateset_for_api = gateset_as_program.out().splitlines() - interleaver_out: Optional[str] = None + interleaver_out: str | None = None if interleaver: if not isinstance(interleaver, Program): raise ValueError("interleaver must be a Program") diff --git a/pyquil/api/_compiler.py b/pyquil/api/_compiler.py index 0ce1e9228..41465aaef 100644 --- a/pyquil/api/_compiler.py +++ b/pyquil/api/_compiler.py @@ -13,7 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. ############################################################################## -from typing import Any, Optional +from typing import Any, TypeAlias from warnings import warn from qcs_sdk import QCSClient @@ -27,7 +27,6 @@ TranslationOptions as _TranslationOptions, ) from rpcq.messages import ParameterSpec -from typing_extensions import TypeAlias from pyquil.api._abstract_compiler import AbstractCompiler, EncryptedProgram, QuantumExecutable from pyquil.quantum_processor import AbstractQuantumProcessor @@ -75,7 +74,7 @@ def _collect_memory_descriptors(program: Program) -> dict[str, ParameterSpec]: class QPUCompiler(AbstractCompiler): """Client to communicate with the compiler and translation service.""" - api_options: Optional[QPUCompilerAPIOptions] + api_options: QPUCompilerAPIOptions | None def __init__( self, @@ -83,9 +82,9 @@ def __init__( quantum_processor_id: str, quantum_processor: AbstractQuantumProcessor, timeout: float = 10.0, - client_configuration: Optional[QCSClient] = None, - api_options: Optional[QPUCompilerAPIOptions] = None, - quilc_client: Optional[QuilcClient] = None, + client_configuration: QCSClient | None = None, + api_options: QPUCompilerAPIOptions | None = None, + quilc_client: QuilcClient | None = None, ) -> None: """Instantiate a new QPU compiler client. @@ -104,10 +103,10 @@ def __init__( self.api_options = api_options self.quantum_processor_id = quantum_processor_id - self._calibration_program: Optional[Program] = None + self._calibration_program: Program | None = None def native_quil_to_executable( - self, nq_program: Program, *, api_options: Optional[QPUCompilerAPIOptions] = None, **kwargs: Any + self, nq_program: Program, *, api_options: QPUCompilerAPIOptions | None = None, **kwargs: Any ) -> QuantumExecutable: """Convert a native Quil program into an executable binary which can be executed by a QPU. @@ -182,8 +181,8 @@ def __init__( *, quantum_processor: AbstractQuantumProcessor, timeout: float = 10.0, - client_configuration: Optional[QCSClient] = None, - quilc_client: Optional[QuilcClient] = None, + client_configuration: QCSClient | None = None, + quilc_client: QuilcClient | None = None, ) -> None: """Client to communicate with compiler. @@ -210,7 +209,7 @@ class IncompatibleBackendForQuantumProcessorIDWarning(Warning): def select_backend_for_quantum_processor_id( - quantum_processor_id: str, backend: Optional[TranslationBackend] + quantum_processor_id: str, backend: TranslationBackend | None ) -> TranslationBackend: """Check that the translation backend is supported for the quantum processor. diff --git a/pyquil/api/_compiler_client.py b/pyquil/api/_compiler_client.py index b8fa87644..d12b29048 100644 --- a/pyquil/api/_compiler_client.py +++ b/pyquil/api/_compiler_client.py @@ -15,7 +15,6 @@ ############################################################################## import json from dataclasses import dataclass -from typing import Optional from qcs_sdk import QCSClient from qcs_sdk.compiler.quilc import ( @@ -45,7 +44,7 @@ class CompileToNativeQuilRequest: target_quantum_processor: TargetQuantumProcessor """Quantum processor to target.""" - protoquil: Optional[bool] + protoquil: bool | None """Whether or not to restrict to protoquil. Overrides server default when provided.""" @@ -56,25 +55,25 @@ class NativeQuilMetadataResponse: final_rewiring: list[int] """Output qubit index relabeling due to SWAP insertion.""" - gate_depth: Optional[int] + gate_depth: int | None """Maximum number of successive gates in the native Quil program.""" - gate_volume: Optional[int] + gate_volume: int | None """Total number of gates in the native Quil program.""" - multiqubit_gate_depth: Optional[int] + multiqubit_gate_depth: int | None """Maximum number of successive two-qubit gates in the native Quil program.""" - program_duration: Optional[float] + program_duration: float | None """Rough estimate of native Quil program length in nanoseconds.""" - program_fidelity: Optional[float] + program_fidelity: float | None """Rough estimate of the fidelity of the full native Quil program.""" - topological_swaps: Optional[int] + topological_swaps: int | None """Total number of SWAPs in the native Quil program.""" - qpu_runtime_estimation: Optional[float] + qpu_runtime_estimation: float | None """ The estimated runtime (milliseconds) on a Rigetti QPU (protoquil program). Available only for protoquil-compliant programs. @@ -88,7 +87,7 @@ class CompileToNativeQuilResponse: native_program: str """Native Quil program.""" - metadata: Optional[NativeQuilMetadata] + metadata: NativeQuilMetadata | None """Metadata for the returned Native Quil.""" @@ -100,7 +99,7 @@ def __init__( *, client_configuration: QCSClient, request_timeout: float = 10.0, - quilc_client: Optional[QuilcClient] = None, + quilc_client: QuilcClient | None = None, ) -> None: """Instantiate a new compiler client. diff --git a/pyquil/api/_qam.py b/pyquil/api/_qam.py index e49c83589..95d0808ad 100644 --- a/pyquil/api/_qam.py +++ b/pyquil/api/_qam.py @@ -17,7 +17,7 @@ from collections.abc import Iterable, Mapping, Sequence from dataclasses import dataclass from datetime import timedelta -from typing import Any, Generic, Optional, TypeVar, Union +from typing import Any, Generic, TypeVar import numpy as np from deprecated import deprecated @@ -35,7 +35,7 @@ class QAMError(RuntimeError): T = TypeVar("T") """A generic parameter describing the opaque job handle returned from QAM#execute and subclasses.""" -MemoryMap = Mapping[str, Union[Sequence[int], Sequence[float]]] +MemoryMap = Mapping[str, Sequence[int] | Sequence[float]] """A mapping of memory regions to a list containing the values to be written into that memory region.""" @@ -51,7 +51,7 @@ class QAMExecutionResult: to get at the data in a more convenient format. """ - def get_register_map(self) -> dict[str, Optional[np.ndarray]]: + def get_register_map(self) -> dict[str, np.ndarray | None]: """Map a register name (ie. "ro") to a ``np.ndarray`` containing the values for the register. Raises a ``RegisterMatrixConversionError`` if the inner execution data for any of the @@ -74,7 +74,7 @@ def get_register_map(self) -> dict[str, Optional[np.ndarray]]: register_map = self.data.result_data.to_register_map() return {key: matrix.to_ndarray() for key, matrix in register_map.items()} - def get_raw_readout_data(self) -> Union[RawQVMReadoutData, RawQPUReadoutData]: + def get_raw_readout_data(self) -> RawQVMReadoutData | RawQPUReadoutData: """Get the raw result data. This will be a flattened structure derived @@ -108,12 +108,12 @@ def get_memory_values(self) -> Mapping[str, MemoryValues]: "and will be removed in future versions. Use the `get_register_map()` method instead." ), ) - def readout_data(self) -> Mapping[str, Optional[np.ndarray]]: + def readout_data(self) -> Mapping[str, np.ndarray | None]: """Readout data returned from the QAM, keyed on the name of the readout register or post-processing node.""" return self.get_register_map() @property - def execution_duration_microseconds(self) -> Optional[float]: + def execution_duration_microseconds(self) -> float | None: """Duration job held exclusive hardware access. Defaults to ``None`` when information is not available.""" if isinstance(self.data.duration, timedelta): return self.data.duration.total_seconds() * 1e6 @@ -127,7 +127,7 @@ class QAM(ABC, Generic[T]): def execute( self, executable: QuantumExecutable, - memory_map: Optional[MemoryMap] = None, + memory_map: MemoryMap | None = None, **kwargs: Any, ) -> T: """Run an executable on a QAM, returning a handle to be used to retrieve results. @@ -160,7 +160,7 @@ def get_result(self, execute_response: T) -> QAMExecutionResult: """ def run( - self, executable: QuantumExecutable, memory_map: Optional[MemoryMap] = None, **kwargs: Any + self, executable: QuantumExecutable, memory_map: MemoryMap | None = None, **kwargs: Any ) -> QAMExecutionResult: """Run an executable to completion on the QAM.""" return self.get_result(self.execute(executable, memory_map, **kwargs)) diff --git a/pyquil/api/_qpu.py b/pyquil/api/_qpu.py index 0fc6fae6d..1537d5e9e 100644 --- a/pyquil/api/_qpu.py +++ b/pyquil/api/_qpu.py @@ -17,7 +17,7 @@ from collections.abc import Iterable from dataclasses import dataclass from datetime import timedelta -from typing import Any, Optional, Union +from typing import Any import numpy as np from numpy.typing import NDArray @@ -41,7 +41,7 @@ ) -def decode_buffer(buffer: ExecutionResult) -> Union[NDArray[np.complex64], NDArray[np.int32]]: +def decode_buffer(buffer: ExecutionResult) -> NDArray[np.complex64] | NDArray[np.int32]: """Translate a DataBuffer into a numpy array. :param buffer: Dictionary with 'data' byte array, 'dtype', and 'shape' fields @@ -97,8 +97,7 @@ def alloc(spec: ParameterSpec) -> np.ndarray: region_width = memory_descriptors[mref.name].length if end > region_width: raise ValueError( - f"Attempted to fill {mref.name}[{mref.offset}, {end})" - f"but the declared region has width {region_width}." + f"Attempted to fill {mref.name}[{mref.offset}, {end})but the declared region has width {region_width}." ) regions[mref.name][:, mref.offset : end] = buf @@ -110,7 +109,7 @@ def alloc(spec: ParameterSpec) -> np.ndarray: class QPUExecuteResponse: job_id: str _executable: EncryptedProgram - execution_options: Optional[ExecutionOptions] + execution_options: ExecutionOptions | None class QPU(QAM[QPUExecuteResponse]): @@ -119,10 +118,10 @@ def __init__( *, quantum_processor_id: str, priority: int = 1, - timeout: Optional[float] = 30.0, - client_configuration: Optional[QCSClient] = None, - endpoint_id: Optional[str] = None, - execution_options: Optional[ExecutionOptions] = None, + timeout: float | None = 30.0, + client_configuration: QCSClient | None = None, + endpoint_id: str | None = None, + execution_options: ExecutionOptions | None = None, ) -> None: """Connect to the QPU. @@ -141,7 +140,7 @@ def __init__( self._client_configuration = client_configuration or QCSClient.load() self._last_results: dict[str, np.ndarray] = {} - self._memory_results: dict[str, Optional[np.ndarray]] = defaultdict(lambda: None) + self._memory_results: dict[str, np.ndarray | None] = defaultdict(lambda: None) self._quantum_processor_id = quantum_processor_id if execution_options is None: execution_options_builder = ExecutionOptionsBuilder.default() @@ -160,8 +159,8 @@ def quantum_processor_id(self) -> str: def execute( self, executable: QuantumExecutable, - memory_map: Optional[MemoryMap] = None, - execution_options: Optional[ExecutionOptions] = None, + memory_map: MemoryMap | None = None, + execution_options: ExecutionOptions | None = None, **__: Any, ) -> QPUExecuteResponse: """Enqueue a job for execution on the QPU. @@ -186,7 +185,7 @@ def execute_with_memory_map_batch( self, executable: QuantumExecutable, memory_maps: Iterable[MemoryMap], - execution_options: Optional[ExecutionOptions] = None, + execution_options: ExecutionOptions | None = None, **__: Any, ) -> list[QPUExecuteResponse]: """Execute a compiled program on a QPU with multiple sets of `memory_maps`. diff --git a/pyquil/api/_quantum_computer.py b/pyquil/api/_quantum_computer.py index 63926f60e..83e900189 100644 --- a/pyquil/api/_quantum_computer.py +++ b/pyquil/api/_quantum_computer.py @@ -23,8 +23,6 @@ from math import log, pi from typing import ( Any, - Optional, - Union, cast, ) @@ -102,7 +100,7 @@ def qubits(self) -> list[int]: """ return self.compiler.quantum_processor.qubits() - def qubit_topology(self) -> nx.graph: + def qubit_topology(self) -> nx.Graph: """Return a NetworkX graph representation of this QuantumComputer's quantum_processor's qubit connectivity. See :py:func:`AbstractQuantumProcessor.qubit_topology` for more. @@ -117,7 +115,7 @@ def to_compiler_isa(self) -> CompilerISA: return self.compiler.quantum_processor.to_compiler_isa() def run( - self, executable: QuantumExecutable, memory_map: Optional[MemoryMap] = None, **kwargs: Any + self, executable: QuantumExecutable, memory_map: MemoryMap | None = None, **kwargs: Any ) -> QAMExecutionResult: """Run a quil executable. @@ -157,7 +155,7 @@ def calibrate(self, experiment: Experiment) -> list[ExperimentResult]: def run_experiment( self, experiment: Experiment, - memory_map: Optional[MemoryMap] = None, + memory_map: MemoryMap | None = None, ) -> list[ExperimentResult]: """Run an ``Experiment`` on a QVM or QPU backend. @@ -249,7 +247,7 @@ def run_experiment( std_errs = np.std(expectations, axis=0, ddof=1) / np.sqrt(len(expectations)) joint_results = [] - for qubit_subset, mean, std_err in zip(joint_expectations, means, std_errs): + for qubit_subset, mean, std_err in zip(joint_expectations, means, std_errs, strict=False): out_operator = PauliTerm.from_list([(setting.out_operator[i], i) for i in qubit_subset]) s = ExperimentSetting( in_state=setting.in_state, @@ -274,7 +272,7 @@ def run_symmetrized_readout( program: Program, trials: int, symm_type: int = 3, - meas_qubits: Optional[Sequence[QubitDesignator]] = None, + meas_qubits: Sequence[QubitDesignator] | None = None, ) -> np.ndarray: r"""Run a quil program in such a way that the readout error is made symmetric. @@ -329,9 +327,7 @@ def run_symmetrized_readout( :return: A numpy array of shape (trials, len(ro-register)) that contains 0s and 1s. """ if not isinstance(symm_type, int): - raise ValueError( - "Symmetrization options are indicated by an int. See " "the docstrings for more information." - ) + raise ValueError("Symmetrization options are indicated by an int. See the docstrings for more information.") if meas_qubits is None: meas_qubits = list(program.get_qubit_indices()) @@ -363,7 +359,7 @@ def compile( to_native_gates: bool = True, optimize: bool = True, *, - protoquil: Optional[bool] = None, + protoquil: bool | None = None, ) -> QuantumExecutable: """Provide a high-level interface for program compilation. @@ -408,7 +404,7 @@ def list_quantum_computers( qpus: bool = True, qvms: bool = True, timeout: float = 10.0, - client_configuration: Optional[QCSClient] = None, + client_configuration: QCSClient | None = None, ) -> list[str]: """List the names of available quantum computers. @@ -428,22 +424,22 @@ def list_quantum_computers( return qc_names -def _parse_name(name: str, as_qvm: Optional[bool], noisy: Optional[bool]) -> tuple[str, Optional[str], bool]: +def _parse_name(name: str, as_qvm: bool | None, noisy: bool | None) -> tuple[str, str | None, bool]: """Try to figure out whether we're getting a (noisy) qvm, and the associated qpu name. See :py:func:`get_qc` for examples of valid names + flags. """ - qvm_type: Optional[str] + qvm_type: str | None parts = name.split("-") if len(parts) >= 2 and parts[-2] == "noisy" and parts[-1] in ["qvm", "pyqvm"]: if as_qvm is not None and (not as_qvm): raise ValueError( - "The provided qc name indicates you are getting a noisy QVM, " "but you have specified `as_qvm=False`" + "The provided qc name indicates you are getting a noisy QVM, but you have specified `as_qvm=False`" ) if noisy is not None and (not noisy): raise ValueError( - "The provided qc name indicates you are getting a noisy QVM, " "but you have specified `noisy=False`" + "The provided qc name indicates you are getting a noisy QVM, but you have specified `noisy=False`" ) qvm_type = parts[-1] @@ -454,7 +450,7 @@ def _parse_name(name: str, as_qvm: Optional[bool], noisy: Optional[bool]) -> tup if len(parts) >= 1 and parts[-1] in ["qvm", "pyqvm"]: if as_qvm is not None and (not as_qvm): raise ValueError( - "The provided qc name indicates you are getting a QVM, " "but you have specified `as_qvm=False`" + "The provided qc name indicates you are getting a QVM, but you have specified `as_qvm=False`" ) qvm_type = parts[-1] if noisy is None: @@ -473,7 +469,7 @@ def _parse_name(name: str, as_qvm: Optional[bool], noisy: Optional[bool]) -> tup return name, qvm_type, noisy -def _canonicalize_name(prefix: str, qvm_type: Optional[str], noisy: bool) -> str: +def _canonicalize_name(prefix: str, qvm_type: str | None, noisy: bool) -> str: """Take the output of _parse_name to create a canonical name.""" if noisy: noise_suffix = "-noisy" @@ -496,11 +492,11 @@ def _canonicalize_name(prefix: str, qvm_type: Optional[str], noisy: bool) -> str def _get_qvm_or_pyqvm( *, qvm_type: str, - qvm_client: Optional[QVMClient], - noise_model: Optional[NoiseModel], - quantum_processor: Optional[AbstractQuantumProcessor], + qvm_client: QVMClient | None, + noise_model: NoiseModel | None, + quantum_processor: AbstractQuantumProcessor | None, execution_timeout: float, -) -> Union[QVM, PyQVM]: +) -> QVM | PyQVM: if qvm_type == "qvm": return QVM(noise_model=noise_model, timeout=execution_timeout, client=qvm_client) elif qvm_type == "pyqvm": @@ -519,9 +515,9 @@ def _get_qvm_qc( quantum_processor: AbstractQuantumProcessor, compiler_timeout: float, execution_timeout: float, - noise_model: Optional[NoiseModel], - quilc_client: Optional[QuilcClient] = None, - qvm_client: Optional[QVMClient] = None, + noise_model: NoiseModel | None, + quilc_client: QuilcClient | None = None, + qvm_client: QVMClient | None = None, ) -> QuantumComputer: """Construct a QuantumComputer backed by a QVM. @@ -563,8 +559,8 @@ def _get_qvm_with_topology( qvm_type: str, compiler_timeout: float, execution_timeout: float, - quilc_client: Optional[QuilcClient] = None, - qvm_client: Optional[QVMClient] = None, + quilc_client: QuilcClient | None = None, + qvm_client: QVMClient | None = None, ) -> QuantumComputer: """Construct a QVM with the provided topology. @@ -583,9 +579,7 @@ def _get_qvm_with_topology( # Note to developers: consider making this function public and advertising it. quantum_processor = NxQuantumProcessor(topology=topology) if noisy: - noise_model: Optional[NoiseModel] = decoherence_noise_with_asymmetric_ro( - isa=quantum_processor.to_compiler_isa() - ) + noise_model: NoiseModel | None = decoherence_noise_with_asymmetric_ro(isa=quantum_processor.to_compiler_isa()) else: noise_model = None return _get_qvm_qc( @@ -609,8 +603,8 @@ def _get_9q_square_qvm( qvm_type: str, compiler_timeout: float, execution_timeout: float, - quilc_client: Optional[QuilcClient] = None, - qvm_client: Optional[QVMClient] = None, + quilc_client: QuilcClient | None = None, + qvm_client: QVMClient | None = None, ) -> QuantumComputer: """Nine-qubit 3x3 square lattice. @@ -648,8 +642,8 @@ def _get_unrestricted_qvm( qvm_type: str, compiler_timeout: float, execution_timeout: float, - quilc_client: Optional[QuilcClient] = None, - qvm_client: Optional[QVMClient] = None, + quilc_client: QuilcClient | None = None, + qvm_client: QVMClient | None = None, ) -> QuantumComputer: """QVM with a fully-connected topology. @@ -687,8 +681,8 @@ def _get_qvm_based_on_real_quantum_processor( qvm_type: str, compiler_timeout: float, execution_timeout: float, - quilc_client: Optional[QuilcClient] = None, - qvm_client: Optional[QVMClient] = None, + quilc_client: QuilcClient | None = None, + qvm_client: QVMClient | None = None, ) -> QuantumComputer: """QVM based on a real quantum_processor. @@ -724,14 +718,14 @@ def _get_qvm_based_on_real_quantum_processor( def get_qc( name: str, *, - as_qvm: Optional[bool] = None, - noisy: Optional[bool] = None, + as_qvm: bool | None = None, + noisy: bool | None = None, compiler_timeout: float = 30.0, execution_timeout: float = 30.0, - client_configuration: Optional[QCSClient] = None, - endpoint_id: Optional[str] = None, - quilc_client: Optional[QuilcClient] = None, - qvm_client: Optional[QVMClient] = None, + client_configuration: QCSClient | None = None, + endpoint_id: str | None = None, + quilc_client: QuilcClient | None = None, + qvm_client: QVMClient | None = None, ) -> QuantumComputer: """Get a quantum computer. @@ -914,7 +908,7 @@ def local_forest_runtime( qvm_port: int = 5000, quilc_port: int = 5555, use_protoquil: bool = False, -) -> Iterator[tuple[Optional[subprocess.Popen], Optional[subprocess.Popen]]]: +) -> Iterator[tuple[subprocess.Popen | None, subprocess.Popen | None]]: """Context manager for local QVM and QUIL compiler. You must first have installed the `qvm` and `quilc` executables from @@ -956,8 +950,8 @@ def local_forest_runtime( ports is in use, the process won't be started and the respective value in the tuple will be ``None``. """ - qvm: Optional[subprocess.Popen] = None - quilc: Optional[subprocess.Popen] = None + qvm: subprocess.Popen | None = None + quilc: subprocess.Popen | None = None # If the host we should listen to is 0.0.0.0, we replace it # with 127.0.0.1 to use a valid IP when checking if the port is in use. @@ -1006,7 +1000,7 @@ def _flip_array_to_prog(flip_array: tuple[bool], qubits: Sequence[QubitDesignato raise ValueError("Mismatch of qubits and operations") prog = Program() - for qubit, flip_output in zip(qubits, flip_array): + for qubit, flip_output in zip(qubits, flip_array, strict=False): if flip_output == 0: continue elif flip_output == 1: @@ -1088,7 +1082,7 @@ def _consolidate_symmetrization_outputs(outputs: list[np.ndarray], flip_arrays: raise ValueError("The length of outputs must equal the length of flip_arrays") output = [] - for bitarray, flip_array in zip(outputs, flip_arrays): + for bitarray, flip_array in zip(outputs, flip_arrays, strict=False): if len(flip_array) == 0: output.append(bitarray) else: diff --git a/pyquil/api/_qvm.py b/pyquil/api/_qvm.py index 24438c3fc..89c289b1e 100644 --- a/pyquil/api/_qvm.py +++ b/pyquil/api/_qvm.py @@ -15,7 +15,7 @@ ############################################################################## from collections.abc import Iterable, Sequence from dataclasses import dataclass -from typing import Any, Optional +from typing import Any import numpy as np from qcs_sdk import ExecutionData, QCSClient, ResultData, qvm @@ -43,7 +43,7 @@ def check_qvm_version(version: str) -> None: major, minor = map(int, version.split(".")[:2]) if major == 1 and minor < 8: raise QVMVersionMismatch( - "Must use QVM >= 1.8.0 with pyquil >= 2.8.0, but you " f"have QVM {version} and pyquil {pyquil_version}" + f"Must use QVM >= 1.8.0 with pyquil >= 2.8.0, but you have QVM {version} and pyquil {pyquil_version}" ) @@ -60,12 +60,12 @@ def memory(self) -> dict[str, np.ndarray]: class QVM(QAM[QVMExecuteResponse]): def __init__( self, - noise_model: Optional[NoiseModel] = None, - gate_noise: Optional[tuple[float, float, float]] = None, - measurement_noise: Optional[tuple[float, float, float]] = None, - random_seed: Optional[int] = None, + noise_model: NoiseModel | None = None, + gate_noise: tuple[float, float, float] | None = None, + measurement_noise: tuple[float, float, float] | None = None, + random_seed: int | None = None, timeout: float = 10.0, - client: Optional[QVMClient] = None, + client: QVMClient | None = None, ) -> None: """Return a virtual machine that classically emulates the execution of Quil programs. @@ -138,7 +138,7 @@ def execute_with_memory_map_batch( def execute( self, executable: QuantumExecutable, - memory_map: Optional[MemoryMap] = None, + memory_map: MemoryMap | None = None, **__: Any, ) -> QVMExecuteResponse: """Execute the input program to completion.""" @@ -180,7 +180,7 @@ def get_version_info(self) -> str: return qvm.api.get_version_info(self._client, options=QVMOptions(timeout_seconds=self.timeout)) -def validate_noise_probabilities(noise_parameter: Optional[tuple[float, float, float]]) -> None: +def validate_noise_probabilities(noise_parameter: tuple[float, float, float] | None) -> None: """Validate the noise probabilities. This function checks that the provided noise parameters are in the correct format and within the expected ranges. diff --git a/pyquil/api/_wavefunction_simulator.py b/pyquil/api/_wavefunction_simulator.py index 6b44937e6..682372185 100644 --- a/pyquil/api/_wavefunction_simulator.py +++ b/pyquil/api/_wavefunction_simulator.py @@ -13,7 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. ############################################################################## -from typing import Optional, Union, cast +from typing import cast import numpy as np from qcs_sdk import QCSClient, qvm @@ -34,11 +34,11 @@ class WavefunctionSimulator: def __init__( self, *, - gate_noise: Optional[tuple[float, float, float]] = None, - measurement_noise: Optional[tuple[float, float, float]] = None, - random_seed: Optional[int] = None, + gate_noise: tuple[float, float, float] | None = None, + measurement_noise: tuple[float, float, float] | None = None, + random_seed: int | None = None, timeout: float = 10.0, - client_configuration: Optional[QCSClient] = None, + client_configuration: QCSClient | None = None, ) -> None: """Return a simulator that propagates a wavefunction representation of a quantum state. @@ -67,7 +67,7 @@ def __init__( self._client = client_configuration or QCSClient.load() self._qvm_client = qvm.QVMClient.new_http(self._client.qvm_url) - def wavefunction(self, quil_program: Program, memory_map: Optional[MemoryMap] = None) -> Wavefunction: + def wavefunction(self, quil_program: Program, memory_map: MemoryMap | None = None) -> Wavefunction: """Simulate a Quil program and return the wavefunction. .. note:: If your program contains measurements or noisy gates, this method may not do what @@ -102,9 +102,9 @@ def wavefunction(self, quil_program: Program, memory_map: Optional[MemoryMap] = def expectation( self, prep_prog: Program, - pauli_terms: Union[PauliSum, list[PauliTerm]], - memory_map: Optional[dict[str, list[Union[int, float]]]] = None, - ) -> Union[float, np.ndarray]: + pauli_terms: PauliSum | list[PauliTerm], + memory_map: dict[str, list[int | float]] | None = None, + ) -> float | np.ndarray: """Calculate the expectation value of Pauli operators given a state prepared by prep_program. If ``pauli_terms`` is a ``PauliSum`` then the returned value is a single ``float``, @@ -151,9 +151,9 @@ def expectation( def run_and_measure( self, quil_program: Program, - qubits: Optional[list[int]] = None, + qubits: list[int] | None = None, trials: int = 1, - memory_map: Optional[MemoryMap] = None, + memory_map: MemoryMap | None = None, ) -> np.ndarray: """Run a Quil program once to determine the final wavefunction, and measure multiple times. diff --git a/pyquil/control_flow_graph.py b/pyquil/control_flow_graph.py index ff100bae6..25c3ab490 100644 --- a/pyquil/control_flow_graph.py +++ b/pyquil/control_flow_graph.py @@ -1,9 +1,9 @@ """Classes that represent the control flow graph of a Quil program.""" -from typing import Optional +from typing import Self from quil import program as quil_rs -from typing_extensions import Self, override +from typing_extensions import override from pyquil.quilbase import ( AbstractInstruction, @@ -30,7 +30,7 @@ def instructions(self) -> list[AbstractInstruction]: # type: ignore[override] return _convert_to_py_instructions(super().instructions()) @override - def terminator(self) -> Optional[AbstractInstruction]: # type: ignore[override] + def terminator(self) -> AbstractInstruction | None: # type: ignore[override] inst = super().terminator() if inst is None: return None diff --git a/pyquil/experiment/_group.py b/pyquil/experiment/_group.py index 0ece4766e..b552297cb 100644 --- a/pyquil/experiment/_group.py +++ b/pyquil/experiment/_group.py @@ -17,7 +17,7 @@ import itertools from collections.abc import Iterable, Sequence from operator import mul -from typing import Union, cast +from typing import cast import networkx as nx from networkx.algorithms.approximation.clique import clique_removal @@ -91,9 +91,7 @@ def merge_disjoint_experiments(experiments: list[Experiment], group_merged_setti used_qubits: set[int] = set() for expt in experiments: if expt.program.get_qubits().intersection(used_qubits): - raise ValueError( - "Experiment programs act on some shared set of qubits and cannot be " "merged unambiguously." - ) + raise ValueError("Experiment programs act on some shared set of qubits and cannot be merged unambiguously.") used_qubits = used_qubits.union(cast(set[int], expt.program.get_qubits())) # get a flat list of all settings, to be regrouped later @@ -115,7 +113,7 @@ def merge_disjoint_experiments(experiments: list[Experiment], group_merged_setti def construct_tpb_graph(experiments: Experiment) -> nx.Graph: """Construct a graph where an edge signifies two experiments are diagonal in a TPB.""" - g = nx.Graph() + g: nx.Graph = nx.Graph() for expt in experiments: if len(expt) != 1: raise ValueError("There must be a single set of ExperimentSettings for each Experiment.") @@ -169,7 +167,7 @@ def group_settings_clique_removal(experiments: Experiment) -> Experiment: ) -def _max_weight_operator(ops: Iterable[PauliTerm]) -> Union[None, PauliTerm]: +def _max_weight_operator(ops: Iterable[PauliTerm]) -> None | PauliTerm: """Construct a PauliTerm operator by taking the non-identity single-qubit operator at each qubit position. This function will return ``None`` if the input operators do not share a natural tensor product basis. @@ -191,7 +189,7 @@ def _max_weight_operator(ops: Iterable[PauliTerm]) -> Union[None, PauliTerm]: return op -def _max_weight_state(states: Iterable[TensorProductState]) -> Union[None, TensorProductState]: +def _max_weight_state(states: Iterable[TensorProductState]) -> None | TensorProductState: """Construct a TensorProductState by taking the single-qubit state at each qubit position. This function will return ``None`` if the input states are not compatible diff --git a/pyquil/experiment/_main.py b/pyquil/experiment/_main.py index 21ff055df..332933272 100644 --- a/pyquil/experiment/_main.py +++ b/pyquil/experiment/_main.py @@ -22,13 +22,10 @@ import json import logging import warnings -from collections.abc import Generator, Mapping, Sequence +from collections.abc import Callable, Generator, Mapping, Sequence from json import JSONEncoder from typing import ( Any, - Callable, - Optional, - Union, cast, ) @@ -136,7 +133,7 @@ class Experiment: def __init__( self, - settings: Union[list[ExperimentSetting], list[list[ExperimentSetting]]], + settings: list[ExperimentSetting] | list[list[ExperimentSetting]], program: Program, *, symmetrization: int = SymmetrizationLevel.EXHAUSTIVE, @@ -193,7 +190,7 @@ def __reversed__(self) -> Generator[list[ExperimentSetting], None, None]: def __contains__(self, item: list[ExperimentSetting]) -> bool: return item in self._settings - def append(self, expts: Union[ExperimentSetting, list[ExperimentSetting]]) -> None: + def append(self, expts: ExperimentSetting | list[ExperimentSetting]) -> None: if not isinstance(expts, list): expts = [expts] self._settings.append(expts) @@ -219,7 +216,7 @@ def remove(self, expt: list[ExperimentSetting]) -> None: def reverse(self) -> None: self._settings.reverse() - def sort(self, key: Optional[Callable[[list[ExperimentSetting]], Any]] = None, reverse: bool = False) -> None: + def sort(self, key: Callable[[list[ExperimentSetting]], Any] | None = None, reverse: bool = False) -> None: return self._settings.sort(key=key, reverse=reverse) def setting_strings(self) -> Generator[str, None, None]: @@ -228,7 +225,7 @@ def setting_strings(self) -> Generator[str, None, None]: for i, settings in enumerate(self._settings) ) - def settings_string(self, abbrev_after: Optional[int] = None) -> str: + def settings_string(self, abbrev_after: int | None = None) -> str: setting_strs = list(self.setting_strings()) if abbrev_after is not None and len(setting_strs) > abbrev_after: first_n = abbrev_after // 2 @@ -273,7 +270,7 @@ def get_meas_qubits(self) -> list[int]: meas_qubits.update(cast(list[int], settings[0].out_operator.get_qubits())) return sorted(meas_qubits) - def get_meas_registers(self, qubits: Optional[Sequence[int]] = None) -> list[int]: + def get_meas_registers(self, qubits: Sequence[int] | None = None) -> list[int]: """Return the sorted list of memory registers corresponding to the list of qubits provided. If no qubits are provided, just returns the list of numbers from 0 to n-1 where n is the @@ -414,7 +411,7 @@ def build_symmetrization_memory_maps( zeros = np.zeros(num_meas_registers) for idx, r in enumerate(symm_registers): zeros[r] = a[idx] - memory_maps.append({f"{label}": list(zeros)}) + memory_maps.append({f"{label}": zeros.tolist()}) return memory_maps def generate_calibration_experiment(self) -> "Experiment": @@ -495,7 +492,7 @@ def to_json(fn: str, obj: Any) -> str: return fn -def _operator_object_hook(obj: Mapping[str, Any]) -> Union[Mapping[str, Any], Experiment]: +def _operator_object_hook(obj: Mapping[str, Any]) -> Mapping[str, Any] | Experiment: if "type" in obj and obj["type"] in ["Experiment", "TomographyExperiment"]: # I bet this doesn't work for grouped experiment settings settings = [[ExperimentSetting.from_str(s) for s in stt] for stt in obj["settings"]] diff --git a/pyquil/experiment/_result.py b/pyquil/experiment/_result.py index 5885c99cc..950434cba 100644 --- a/pyquil/experiment/_result.py +++ b/pyquil/experiment/_result.py @@ -21,7 +21,7 @@ import logging from dataclasses import dataclass -from typing import Any, Optional, Union +from typing import Any import numpy as np @@ -40,28 +40,28 @@ class ExperimentResult: """ setting: ExperimentSetting - expectation: Union[float, complex] + expectation: float | complex total_counts: int - std_err: Optional[Union[float, complex]] = None - raw_expectation: Optional[Union[float, complex]] = None - raw_std_err: Optional[float] = None - calibration_expectation: Optional[Union[float, complex]] = None - calibration_std_err: Optional[Union[float, complex]] = None - calibration_counts: Optional[int] = None - additional_results: Optional[list["ExperimentResult"]] = None + std_err: float | complex | None = None + raw_expectation: float | complex | None = None + raw_std_err: float | None = None + calibration_expectation: float | complex | None = None + calibration_std_err: float | complex | None = None + calibration_counts: int | None = None + additional_results: list["ExperimentResult"] | None = None def __init__( self, setting: ExperimentSetting, - expectation: Union[float, complex], + expectation: float | complex, total_counts: int, - std_err: Optional[Union[float, complex]] = None, - raw_expectation: Optional[Union[float, complex]] = None, - raw_std_err: Optional[Union[float, complex]] = None, - calibration_expectation: Optional[Union[float, complex]] = None, - calibration_std_err: Optional[Union[float, complex]] = None, - calibration_counts: Optional[int] = None, - additional_results: Optional[list["ExperimentResult"]] = None, + std_err: float | complex | None = None, + raw_expectation: float | complex | None = None, + raw_std_err: float | complex | None = None, + calibration_expectation: float | complex | None = None, + calibration_std_err: float | complex | None = None, + calibration_counts: int | None = None, + additional_results: list["ExperimentResult"] | None = None, ): object.__setattr__(self, "setting", setting) object.__setattr__(self, "expectation", expectation) @@ -95,9 +95,7 @@ def serializable(self) -> dict[str, Any]: } -def bitstrings_to_expectations( - bitstrings: np.ndarray, joint_expectations: Optional[list[list[int]]] = None -) -> np.ndarray: +def bitstrings_to_expectations(bitstrings: np.ndarray, joint_expectations: list[list[int]] | None = None) -> np.ndarray: """Given an array of bitstrings, map them to expectation values and return the desired joint expectation values. If no joint expectations are desired, then just the 1 -> -1, 0 -> 1 mapping is performed. @@ -157,7 +155,8 @@ def correct_experiment_result( len_calibration = len(calibration.additional_results) raise ValueError(f"Length of results ({len_result}) should match calibration ({len_calibration}).") additional_results = [ - correct_experiment_result(r, c) for r, c in zip(result.additional_results, calibration.additional_results) + correct_experiment_result(r, c) + for r, c in zip(result.additional_results, calibration.additional_results, strict=False) ] return ExperimentResult( @@ -175,11 +174,11 @@ def correct_experiment_result( def ratio_variance( - a: Union[float, complex, np.number, np.ndarray], - var_a: Union[float, complex, np.number, np.ndarray], - b: Union[float, complex, np.number, np.ndarray], - var_b: Union[float, complex, np.number, np.ndarray], -) -> Union[float, complex, np.number, np.ndarray]: + a: float | complex | np.number | np.ndarray, + var_a: float | complex | np.number | np.ndarray, + b: float | complex | np.number | np.ndarray, + var_b: float | complex | np.number | np.ndarray, +) -> float | complex | np.number | np.ndarray: r"""Compute the variance on the ratio Y = A/B. Given random variables 'A' and 'B', compute the variance on the ratio Y = A/B. Denote the diff --git a/pyquil/experiment/_setting.py b/pyquil/experiment/_setting.py index faf443927..64f776a63 100644 --- a/pyquil/experiment/_setting.py +++ b/pyquil/experiment/_setting.py @@ -23,7 +23,7 @@ import re from collections.abc import Generator, Iterable from dataclasses import dataclass -from typing import Any, Optional, cast +from typing import Any, cast from pyquil.paulis import PauliTerm, sI @@ -60,7 +60,7 @@ class TensorProductState: states: list[_OneQState] - def __init__(self, states: Optional[Iterable[_OneQState]] = None): + def __init__(self, states: Iterable[_OneQState] | None = None): if states is None: states = [] object.__setattr__(self, "states", list(states)) @@ -172,13 +172,13 @@ class ExperimentSetting: in_state: TensorProductState out_operator: PauliTerm - additional_expectations: Optional[list[list[int]]] = None + additional_expectations: list[list[int]] | None = None def __init__( self, in_state: TensorProductState, out_operator: PauliTerm, - additional_expectations: Optional[list[list[int]]] = None, + additional_expectations: list[list[int]] | None = None, ): object.__setattr__(self, "in_state", in_state) object.__setattr__(self, "out_operator", out_operator) diff --git a/pyquil/external/rpcq.py b/pyquil/external/rpcq.py index 25890dfeb..29ca6ea0d 100644 --- a/pyquil/external/rpcq.py +++ b/pyquil/external/rpcq.py @@ -2,7 +2,7 @@ import json from dataclasses import dataclass, field -from typing import Literal, Optional, Union +from typing import Literal, Union from deprecated.sphinx import deprecated from rpcq.messages import TargetDevice as TargetQuantumProcessor @@ -14,9 +14,9 @@ class Operator: """Operator class for representing a quantum gate or measurement.""" - operator: Optional[str] = None - duration: Optional[float] = None - fidelity: Optional[float] = None + operator: str | None = None + duration: float | None = None + fidelity: float | None = None def __post_init__(self): self.duration = float(self.duration) if self.duration is not None else None @@ -57,8 +57,8 @@ def parse_obj(cls, dictionary: dict): class MeasureInfo(Operator): """MeasureInfo class for representing a measurement operation.""" - qubit: Optional[Union[int, str]] = None - target: Optional[Union[int, str]] = None + qubit: int | str | None = None + target: int | str | None = None operator_type: Literal["measure"] = "measure" def __post_init__(self): @@ -107,8 +107,8 @@ def parse_obj(cls, dictionary: dict): class GateInfo(Operator): """GateInfo class for representing a quantum gate operation.""" - parameters: list[Union[float, str]] = field(default_factory=list) - arguments: list[Union[int, str]] = field(default_factory=list) + parameters: list[float | str] = field(default_factory=list) + arguments: list[int | str] = field(default_factory=list) operator_type: Literal["gate"] = "gate" def _dict(self) -> dict[str, JsonValue]: @@ -149,7 +149,7 @@ def parse_obj(cls, dictionary: dict): return cls._parse_obj(dictionary) -def _parse_operator(dictionary: dict) -> Union[GateInfo, MeasureInfo]: +def _parse_operator(dictionary: dict) -> GateInfo | MeasureInfo: operator_type = dictionary["operator_type"] if operator_type == "measure": return MeasureInfo._parse_obj(dictionary) @@ -163,8 +163,8 @@ class Qubit: """Qubit class for representing a qubit in a quantum processor.""" id: int - dead: Optional[bool] = False - gates: list[Union[GateInfo, MeasureInfo]] = field(default_factory=list) + dead: bool | None = False + gates: list[GateInfo | MeasureInfo] = field(default_factory=list) def _dict(self) -> dict[str, JsonValue]: encoding = dict(id=self.id, gates=[g._dict() for g in self.gates]) @@ -203,7 +203,7 @@ class Edge: """Edge class for representing a connection between two qubits.""" ids: list[int] - dead: Optional[bool] = False + dead: bool | None = False gates: list[GateInfo] = field(default_factory=list) def _dict(self) -> dict[str, JsonValue]: @@ -296,7 +296,7 @@ def add_qubit(quantum_processor: CompilerISA, node_id: int) -> Qubit: return quantum_processor.qubits[str(node_id)] -def get_qubit(quantum_processor: CompilerISA, node_id: int) -> Optional[Qubit]: +def get_qubit(quantum_processor: CompilerISA, node_id: int) -> Qubit | None: """Get a qubit from the quantum processor ISA.""" return quantum_processor.qubits.get(str(node_id)) @@ -314,7 +314,7 @@ def add_edge(quantum_processor: CompilerISA, qubit1: int, qubit2: int) -> Edge: return quantum_processor.edges[edge_id] -def get_edge(quantum_processor: CompilerISA, qubit1: int, qubit2: int) -> Optional[Edge]: +def get_edge(quantum_processor: CompilerISA, qubit1: int, qubit2: int) -> Edge | None: """Get an Edge between two qubit IDs.""" edge_id = make_edge_id(qubit1, qubit2) return quantum_processor.edges.get(edge_id) diff --git a/pyquil/gates.py b/pyquil/gates.py index 9709e13b3..248ae4602 100644 --- a/pyquil/gates.py +++ b/pyquil/gates.py @@ -15,9 +15,9 @@ ############################################################################## """Standard Gate definitions to use in a ``Program``.""" -from collections.abc import Mapping, Sequence +from collections.abc import Callable, Mapping, Sequence from numbers import Real -from typing import Callable, Optional, Union, no_type_check +from typing import no_type_check import numpy as np from deprecated.sphinx import deprecated, versionadded @@ -82,8 +82,8 @@ def unpack_reg_val_pair( classical_reg1: MemoryReferenceDesignator, - classical_reg2: Union[MemoryReferenceDesignator, int, float], -) -> tuple[MemoryReference, Union[MemoryReference, int, float]]: + classical_reg2: MemoryReferenceDesignator | int | float, +) -> tuple[MemoryReference, MemoryReference | int | float]: """Type check/coerce arguments to constructors for binary classical operators. :param classical_reg1: Specifier for the classical memory address to be modified. @@ -100,8 +100,8 @@ def unpack_reg_val_pair( def prepare_ternary_operands( classical_reg1: MemoryReferenceDesignator, classical_reg2: MemoryReferenceDesignator, - classical_reg3: Union[MemoryReferenceDesignator, int, float], -) -> tuple[MemoryReference, MemoryReference, Union[MemoryReference, int, float]]: + classical_reg3: MemoryReferenceDesignator | int | float, +) -> tuple[MemoryReference, MemoryReference, MemoryReference | int | float]: """Type check/coerce arguments to constructors for ternary classical operators. :param classical_reg1: Specifier for the classical memory address to be modified. @@ -420,7 +420,7 @@ def CPHASE10(angle: ParameterDesignator, control: QubitDesignator, target: Qubit # Cannot resolve forward reference in type annotations of "pyquil.gates.CPHASE": # name 'Expression' is not defined def CPHASE( - angle: Union[Expression, MemoryReference, np.int_, int, float, complex], + angle: Expression | MemoryReference | np.int_ | int | float | complex, control: QubitDesignator, target: QubitDesignator, ) -> Gate: @@ -651,7 +651,7 @@ def RYY(phi: ParameterDesignator, q1: QubitDesignator, q2: QubitDesignator) -> G """ -def RESET(qubit_index: Optional[QubitDesignator] = None) -> Union[Reset, ResetQubit]: +def RESET(qubit_index: QubitDesignator | None = None) -> Reset | ResetQubit: """Reset all qubits or just one specific qubit. :param qubit_index: The qubit to reset. @@ -685,8 +685,8 @@ def DECLARE( name: str, memory_type: str = "BIT", memory_size: int = 1, - shared_region: Optional[str] = None, - offsets: Optional[Sequence[tuple[int, str]]] = None, + shared_region: str | None = None, + offsets: Sequence[tuple[int, str]] | None = None, ) -> Declare: return Declare( name=name, @@ -697,7 +697,7 @@ def DECLARE( ) -def MEASURE(qubit: QubitDesignator, classical_reg: Optional[MemoryReferenceDesignator]) -> Measurement: +def MEASURE(qubit: QubitDesignator, classical_reg: MemoryReferenceDesignator | None) -> Measurement: """Produce a MEASURE instruction. :param qubit: The qubit to measure. @@ -730,9 +730,7 @@ def NOT(classical_reg: MemoryReferenceDesignator) -> ClassicalNot: return ClassicalNot(unpack_classical_reg(classical_reg)) -def AND( - classical_reg1: MemoryReferenceDesignator, classical_reg2: Union[MemoryReferenceDesignator, int] -) -> ClassicalAnd: +def AND(classical_reg1: MemoryReferenceDesignator, classical_reg2: MemoryReferenceDesignator | int) -> ClassicalAnd: """Produce an AND instruction. NOTE: The order of operands was reversed in pyQuil <=1.9 . @@ -748,7 +746,7 @@ def AND( def IOR( - classical_reg1: MemoryReferenceDesignator, classical_reg2: Union[MemoryReferenceDesignator, int] + classical_reg1: MemoryReferenceDesignator, classical_reg2: MemoryReferenceDesignator | int ) -> ClassicalInclusiveOr: """Produce an inclusive OR instruction. @@ -763,7 +761,7 @@ def IOR( def XOR( - classical_reg1: MemoryReferenceDesignator, classical_reg2: Union[MemoryReferenceDesignator, int] + classical_reg1: MemoryReferenceDesignator, classical_reg2: MemoryReferenceDesignator | int ) -> ClassicalExclusiveOr: """Produce an exclusive OR instruction. @@ -779,7 +777,7 @@ def XOR( def MOVE( classical_reg1: MemoryReferenceDesignator, - classical_reg2: Union[MemoryReferenceDesignator, int, float], + classical_reg2: MemoryReferenceDesignator | int | float, ) -> ClassicalMove: """Produce a MOVE instruction. @@ -819,7 +817,7 @@ def LOAD( def STORE( region_name: str, offset_reg: MemoryReferenceDesignator, - source: Union[MemoryReferenceDesignator, int, float], + source: MemoryReferenceDesignator | int | float, ) -> ClassicalStore: """Produce a STORE instruction. @@ -843,7 +841,7 @@ def CONVERT(classical_reg1: MemoryReferenceDesignator, classical_reg2: MemoryRef return ClassicalConvert(unpack_classical_reg(classical_reg1), unpack_classical_reg(classical_reg2)) -def ADD(classical_reg: MemoryReferenceDesignator, right: Union[MemoryReferenceDesignator, int, float]) -> ClassicalAdd: +def ADD(classical_reg: MemoryReferenceDesignator, right: MemoryReferenceDesignator | int | float) -> ClassicalAdd: """Produce an ADD instruction. :param classical_reg: Left operand for the arithmetic operation. Also serves as the store @@ -855,7 +853,7 @@ def ADD(classical_reg: MemoryReferenceDesignator, right: Union[MemoryReferenceDe return ClassicalAdd(left, right) -def SUB(classical_reg: MemoryReferenceDesignator, right: Union[MemoryReferenceDesignator, int, float]) -> ClassicalSub: +def SUB(classical_reg: MemoryReferenceDesignator, right: MemoryReferenceDesignator | int | float) -> ClassicalSub: """Produce a SUB instruction. :param classical_reg: Left operand for the arithmetic operation. Also serves as the store @@ -867,7 +865,7 @@ def SUB(classical_reg: MemoryReferenceDesignator, right: Union[MemoryReferenceDe return ClassicalSub(left, right) -def MUL(classical_reg: MemoryReferenceDesignator, right: Union[MemoryReferenceDesignator, int, float]) -> ClassicalMul: +def MUL(classical_reg: MemoryReferenceDesignator, right: MemoryReferenceDesignator | int | float) -> ClassicalMul: """Produce a MUL instruction. :param classical_reg: Left operand for the arithmetic operation. Also serves as the store @@ -879,7 +877,7 @@ def MUL(classical_reg: MemoryReferenceDesignator, right: Union[MemoryReferenceDe return ClassicalMul(left, right) -def DIV(classical_reg: MemoryReferenceDesignator, right: Union[MemoryReferenceDesignator, int, float]) -> ClassicalDiv: +def DIV(classical_reg: MemoryReferenceDesignator, right: MemoryReferenceDesignator | int | float) -> ClassicalDiv: """Produce an DIV instruction. :param classical_reg: Left operand for the arithmetic operation. Also serves as the store @@ -894,7 +892,7 @@ def DIV(classical_reg: MemoryReferenceDesignator, right: Union[MemoryReferenceDe def EQ( classical_reg1: MemoryReferenceDesignator, classical_reg2: MemoryReferenceDesignator, - classical_reg3: Union[MemoryReferenceDesignator, int, float], + classical_reg3: MemoryReferenceDesignator | int | float, ) -> ClassicalEqual: """Produce an EQ instruction. @@ -913,7 +911,7 @@ def EQ( def LT( classical_reg1: MemoryReferenceDesignator, classical_reg2: MemoryReferenceDesignator, - classical_reg3: Union[MemoryReferenceDesignator, int, float], + classical_reg3: MemoryReferenceDesignator | int | float, ) -> ClassicalLessThan: """Produce an LT instruction. @@ -931,7 +929,7 @@ def LT( def LE( classical_reg1: MemoryReferenceDesignator, classical_reg2: MemoryReferenceDesignator, - classical_reg3: Union[MemoryReferenceDesignator, int, float], + classical_reg3: MemoryReferenceDesignator | int | float, ) -> ClassicalLessEqual: """Produce an LE instruction. @@ -949,7 +947,7 @@ def LE( def GT( classical_reg1: MemoryReferenceDesignator, classical_reg2: MemoryReferenceDesignator, - classical_reg3: Union[MemoryReferenceDesignator, int, float], + classical_reg3: MemoryReferenceDesignator | int | float, ) -> ClassicalGreaterThan: """Produce an GT instruction. @@ -967,7 +965,7 @@ def GT( def GE( classical_reg1: MemoryReferenceDesignator, classical_reg2: MemoryReferenceDesignator, - classical_reg3: Union[MemoryReferenceDesignator, int, float], + classical_reg3: MemoryReferenceDesignator | int | float, ) -> ClassicalGreaterEqual: """Produce an GE instruction. @@ -1101,7 +1099,7 @@ def RAW_CAPTURE( # with a float, and everything in between should be of a particular # type T. @no_type_check -def DELAY(*args) -> Union[DelayFrames, DelayQubits]: +def DELAY(*args) -> DelayFrames | DelayQubits: """Produce a DELAY instruction. Note: There are two variants of DELAY. One applies to specific frames on some @@ -1133,7 +1131,7 @@ def DELAY(*args) -> Union[DelayFrames, DelayQubits]: ) -def FENCE(*qubits: Union[int, Qubit, FormalArgument]) -> Union[FenceAll, Fence]: +def FENCE(*qubits: int | Qubit | FormalArgument) -> FenceAll | Fence: """Produce a FENCE instruction. Note: If no qubits are specified, then this is interpreted as a global FENCE. @@ -1200,7 +1198,7 @@ def FENCE(*qubits: Union[int, Qubit, FormalArgument]) -> Union[FenceAll, Fence]: Dictionary of Quil-T AST construction functions keyed by instruction name. """ -STANDARD_INSTRUCTIONS: Mapping[str, Union[AbstractInstruction, Callable[..., AbstractInstruction]]] = { +STANDARD_INSTRUCTIONS: Mapping[str, AbstractInstruction | Callable[..., AbstractInstruction]] = { "WAIT": WAIT, "RESET": RESET, "DECLARE": DECLARE, diff --git a/pyquil/latex/_diagram.py b/pyquil/latex/_diagram.py index ca0867739..0f7b4ab87 100644 --- a/pyquil/latex/_diagram.py +++ b/pyquil/latex/_diagram.py @@ -17,7 +17,7 @@ from collections import defaultdict from collections.abc import Iterable, Mapping, Sequence from dataclasses import dataclass, replace -from typing import Optional, cast +from typing import cast from warnings import warn from pyquil.quil import Program @@ -144,23 +144,23 @@ def TIKZ_MEASURE() -> str: return r"\meter{}" -def _format_parameter(param: ParameterDesignator, settings: Optional[DiagramSettings] = None) -> str: +def _format_parameter(param: ParameterDesignator, settings: DiagramSettings | None = None) -> str: formatted: str = format_parameter(param) if settings and settings.texify_numerical_constants: formatted = formatted.replace("pi", r"\pi") return formatted -def _format_parameters(params: Iterable[ParameterDesignator], settings: Optional[DiagramSettings] = None) -> str: +def _format_parameters(params: Iterable[ParameterDesignator], settings: DiagramSettings | None = None) -> str: return "(" + ",".join(_format_parameter(param, settings) for param in params) + ")" def TIKZ_GATE( name: str, size: int = 1, - params: Optional[Sequence[ParameterDesignator]] = None, + params: Sequence[ParameterDesignator] | None = None, dagger: bool = False, - settings: Optional[DiagramSettings] = None, + settings: DiagramSettings | None = None, ) -> str: cmd = r"\gate" rotations = ["RX", "RY", "RZ"] @@ -221,7 +221,7 @@ def append(self, qubit: int, op: str) -> None: """Add an operation to the rightmost edge of the specified qubit line.""" self.lines[qubit].append(op) - def append_diagram(self, diagram: "DiagramState", group: Optional[str] = None) -> "DiagramState": + def append_diagram(self, diagram: "DiagramState", group: str | None = None) -> "DiagramState": """Add all operations represented by the given diagram to their corresponding qubit lines in this diagram. If group is not None, then a TIKZ_GATE_GROUP is created with the label indicated by group. @@ -286,7 +286,7 @@ def split_on_terminal_measures( elif isinstance(instr, Pragma): if instr.command == PRAGMA_END_GROUP: warn( - "Alignment of terminal MEASURE operations may" "conflict with gate group declaration.", + "Alignment of terminal MEASURE operations mayconflict with gate group declaration.", stacklevel=2, ) in_group = True @@ -306,13 +306,13 @@ def __init__(self, circuit: Program, settings: DiagramSettings): self.circuit = circuit self.settings = settings # instructions currently being processed - self.working_instructions: Optional[list[AbstractInstruction]] = None + self.working_instructions: list[AbstractInstruction] | None = None # index into working instructions. we maintain the invariant that # working_instructions[0:index] has been processed, with the diagram # updated accordingly self.index = 0 # partially constructed diagram - self.diagram: Optional[DiagramState] = None + self.diagram: DiagramState | None = None def build(self) -> DiagramState: """Build the diagram.""" @@ -347,7 +347,7 @@ def build(self) -> DiagramState: self._build_measure() elif isinstance(instr, Gate): if "FORKED" in instr.modifiers: - raise ValueError("LaTeX output does not currently support" f"FORKED modifiers: {instr}.") + raise ValueError(f"LaTeX output does not currently supportFORKED modifiers: {instr}.") # the easy case is 1q operations if len(instr.qubits) == 1: self._build_1q_unitary() @@ -357,7 +357,7 @@ def build(self) -> DiagramState: else: self._build_generic_unitary() elif isinstance(instr, UNSUPPORTED_INSTRUCTION_CLASSES): - raise ValueError("LaTeX output does not currently support" f"the following instruction: {instr.out()}") + raise ValueError(f"LaTeX output does not currently supportthe following instruction: {instr.out()}") else: self.index += 1 diff --git a/pyquil/latex/_ipython.py b/pyquil/latex/_ipython.py index 9ebe0994d..7ee58d258 100644 --- a/pyquil/latex/_ipython.py +++ b/pyquil/latex/_ipython.py @@ -18,7 +18,7 @@ import shutil import subprocess import tempfile -from typing import Any, Optional +from typing import Any from IPython.display import Image @@ -27,7 +27,7 @@ from pyquil.quil import Program -def display(circuit: Program, settings: Optional[DiagramSettings] = None, **image_options: Any) -> Image: +def display(circuit: Program, settings: DiagramSettings | None = None, **image_options: Any) -> Image: """Display a PyQuil circuit as an IPython image object. .. note:: diff --git a/pyquil/latex/_main.py b/pyquil/latex/_main.py index c45d91646..b59aed4a9 100644 --- a/pyquil/latex/_main.py +++ b/pyquil/latex/_main.py @@ -15,13 +15,11 @@ ############################################################################## """The main entry point to the LaTeX generation functionality in pyQuil.""" -from typing import Optional - from pyquil.latex._diagram import DiagramBuilder, DiagramSettings from pyquil.quil import Program -def to_latex(circuit: Program, settings: Optional[DiagramSettings] = None) -> str: +def to_latex(circuit: Program, settings: DiagramSettings | None = None) -> str: """Translate a given pyQuil Program to a TikZ picture in a LaTeX document. Here are some high points of the generation procedure (see ``pyquil/latex/_diagram.py``): diff --git a/pyquil/latex/latex_generation.py b/pyquil/latex/latex_generation.py index 815c0ae96..c4faec872 100644 --- a/pyquil/latex/latex_generation.py +++ b/pyquil/latex/latex_generation.py @@ -18,8 +18,6 @@ Note: this is a deprecated module: Import from pyquil.latex instead. """ -from typing import Optional - from deprecated.classic import deprecated from pyquil.latex._diagram import DiagramSettings @@ -30,7 +28,7 @@ version="4.0", reason="This module has been moved -- please import it as 'from pyquil.latex import to_latex' going forward", ) -def to_latex(circuit: Program, settings: Optional[DiagramSettings] = None) -> str: +def to_latex(circuit: Program, settings: DiagramSettings | None = None) -> str: """Produce a circuit diagram in LaTeX for a given pyQuil Program.""" from pyquil.latex._main import to_latex diff --git a/pyquil/noise/__init__.py b/pyquil/noise/__init__.py new file mode 100644 index 000000000..bace195bf --- /dev/null +++ b/pyquil/noise/__init__.py @@ -0,0 +1,76 @@ +"""pyquil.noise — Noise modeling for quantum simulators. + +This package provides: + +- **Noise model** (``_legacy_noise``): Kraus-map based noise construction + for the QVM, including ``KrausModel``, ``NoiseModel``, and decoherence + helpers. + +- **Channel classes** (``_channels``): quax-backed ``Channel``, + ``MeasurementChannel``, ``ResetChannel``, and ``CycleChannel`` dataclasses + for fine-grained noise modeling. These are private and not re-exported. + +- **New noise model** (``_noise_model``): The quax-based ``NoiseModel`` + container and program-level fidelity estimation utilities. These are + private and not re-exported; they will become the public API in the next + major version. +""" + +# ── Noise model (Kraus-map based) ─────────────────────────────────────── +from pyquil.noise._legacy_noise import ( + ANGLE_TOLERANCE, + INFINITY, + NO_NOISE, + KrausModel, + NoiseModel, + NoisyGateUndefined, + _bitstring_probs_by_qubit, # noqa: F401 + _check_kraus_ops, # noqa: F401 + _create_kraus_pragmas, # noqa: F401 + _decoherence_noise_model, # noqa: F401 + _get_program_gates, # noqa: F401 + _noise_model_program_header, # noqa: F401 + _run, # noqa: F401 + add_decoherence_noise, + append_kraus_to_gate, + apply_noise_model, + bitstring_probs_to_z_moments, + combine_kraus_maps, + correct_bitstring_probs, + corrupt_bitstring_probs, + damping_after_dephasing, + damping_kraus_map, + decoherence_noise_with_asymmetric_ro, + dephasing_kraus_map, + estimate_assignment_probs, + estimate_bitstring_probs, + get_noisy_gate, + pauli_kraus_map, + tensor_kraus_maps, +) + +__all__ = [ + # Noise model + "ANGLE_TOLERANCE", + "INFINITY", + "KrausModel", + "NO_NOISE", + "NoiseModel", + "NoisyGateUndefined", + "add_decoherence_noise", + "append_kraus_to_gate", + "apply_noise_model", + "bitstring_probs_to_z_moments", + "combine_kraus_maps", + "correct_bitstring_probs", + "corrupt_bitstring_probs", + "damping_after_dephasing", + "damping_kraus_map", + "decoherence_noise_with_asymmetric_ro", + "dephasing_kraus_map", + "estimate_assignment_probs", + "estimate_bitstring_probs", + "get_noisy_gate", + "pauli_kraus_map", + "tensor_kraus_maps", +] diff --git a/pyquil/noise/_channels.py b/pyquil/noise/_channels.py new file mode 100644 index 000000000..597057c67 --- /dev/null +++ b/pyquil/noise/_channels.py @@ -0,0 +1,1654 @@ +############################################################################## +# Copyright 2016-2026 Rigetti Computing +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +############################################################################## +"""Noise channel classes and gate-resolution utilities. + +This module defines ``Channel``, ``MeasurementChannel``, ``ResetChannel``, and +``CycleChannel`` dataclasses for representing noise in quantum circuits, along +with helper functions for resolving gate unitaries and extracting custom gate +definitions from Quil programs. +""" + +from __future__ import annotations + +import itertools +import json +import logging +from collections.abc import Callable +from dataclasses import dataclass, replace +from functools import cached_property, reduce +from itertools import product +from typing import TYPE_CHECKING, Any + +import jax.numpy as jnp +import numpy as np +import quax as qx +from jax import Array +from quil.expression import Expression as QuilExpression +from quil.program import Program as RSProgram +from scipy.linalg import expm as scipy_expm +from scipy.linalg import fractional_matrix_power +from scipy.linalg import logm as scipy_logm + +from pyquil.quilatom import Expression, FormalArgument, Parameter, substitute +from pyquil.quilbase import DefCircuit, DefGate, Gate, Measurement, Reset, ResetQubit + +if TYPE_CHECKING: + from plotly.graph_objs import Figure + + from pyquil import Program + +logger = logging.getLogger(__name__) + +# Type alias for the custom-gate lookup map used throughout the Channel constructors. +CustomGateMap = dict[str, qx.Unitary | Callable[..., qx.Unitary]] + + +def _parse_quil_instruction(quil_str: str) -> Gate | Measurement | Reset: + """Parse a single Quil instruction string into a pyquil instruction object. + + Uses the ``quil`` Rust parser directly, avoiding a dependency on ``pyquil.Program``. + """ + rs_inst = RSProgram.parse(quil_str).body_instructions[0] + if rs_inst.is_gate(): + return Gate._from_rs_gate(rs_inst.to_gate()) + elif rs_inst.is_measurement(): + return Measurement._from_rs_measurement(rs_inst.to_measurement()) + elif rs_inst.is_reset(): + reset = rs_inst.to_reset() + if reset.qubit is None: + return Reset._from_rs_reset(reset) + return ResetQubit._from_rs_reset(reset) + raise ValueError(f"Unsupported instruction type in: {quil_str}") + + +def _pack_complex_array(array: Array | np.ndarray) -> dict[str, Any]: + """Pack a complex array into JSON-compatible real/imaginary pairs.""" + np_array = np.asarray(array) + return { + "_complex_array": [[float(value.real), float(value.imag)] for value in np_array.flat], + "shape": list(np_array.shape), + } + + +def _unpack_complex_array(data: dict[str, Any]) -> Array: + """Unpack a complex array from :func:`_pack_complex_array` data.""" + shape = tuple(data["shape"]) + return jnp.array([complex(pair[0], pair[1]) for pair in data["_complex_array"]], dtype=complex).reshape(shape) + + +def _pack_dims(dims: tuple[tuple[int, ...], tuple[int, ...]]) -> list[list[int]]: + """Pack quax operator dims into JSON-compatible lists.""" + return [list(dims[0]), list(dims[1])] + + +def _unpack_dims(data: list[list[int]]) -> tuple[tuple[int, ...], tuple[int, ...]]: + """Unpack quax operator dims from JSON-compatible lists.""" + if len(data) != 2: + raise ValueError(f"Serialized operator dims must contain output and input dims, got {data}.") + return (tuple(int(dim) for dim in data[0]), tuple(int(dim) for dim in data[1])) + + +def _infer_legacy_qubit_dims(shape: tuple[int, ...], *, superoperator: bool) -> tuple[tuple[int, ...], tuple[int, ...]]: + """Infer qubit-only dims for data serialized before dims were stored.""" + hilbert_dim = int(round(np.sqrt(shape[0]))) if superoperator else int(shape[0]) + num_qubits = int(round(np.log2(hilbert_dim))) + if 2**num_qubits != hilbert_dim: + raise ValueError( + "Serialized operator data does not include dims and its shape is not compatible with qubit-only dims." + ) + return ((2,) * num_qubits, (2,) * num_qubits) + + +def _pack_operator(operator: qx.SuperOp | qx.Unitary | qx.Choi) -> dict[str, Any]: + """Pack a quax operator matrix with explicit dimension metadata.""" + data = _pack_complex_array(operator.matrix) + data["dims"] = _pack_dims(operator.dims) + return data + + +def _unpack_operator_dims( + data: dict[str, Any], shape: tuple[int, ...], *, superoperator: bool +) -> tuple[tuple[int, ...], tuple[int, ...]]: + """Read explicit dims, falling back to the legacy qubit-only inference.""" + if "dims" in data: + return _unpack_dims(data["dims"]) + return _infer_legacy_qubit_dims(shape, superoperator=superoperator) + + +def _resolve_params(params: list) -> list[float]: + """Resolve gate parameters to concrete float values. + + :param params: The gate parameters (may include symbolic Parameters or Expressions). + :return: A list of concrete float values. + :raises ValueError: If any parameter is symbolic and cannot be evaluated to a number. + """ + fixed_params = [] + for p in params: + if isinstance(p, (Parameter, Expression)): + evaluated = p._evaluate() + if isinstance(evaluated, (Parameter, Expression)): + raise ValueError( + f"Cannot resolve symbolic parameter {p}. Provide a gate with concrete numeric parameters." + ) + fixed_params.append(float(evaluated)) + elif isinstance(p, QuilExpression): + result = p.evaluate({}, {}) + fixed_params.append(float(result.to_number()) if hasattr(result, "to_number") else float(result)) # type: ignore[arg-type] + else: + fixed_params.append(float(p.real)) + return fixed_params + + +def get_custom_gates_from_program(program: Program) -> CustomGateMap: + """Extract custom gate definitions from a Quil program. + + Returns a dictionary mapping gate names to unitary matrices (for fixed gates) or callables + (for parametric gates). Does not include the standard gate set — use this to augment + the standard ``qx.gates.QUANTUM_GATES`` when resolving instructions with custom gates. + + :param program: A Quil program containing DefGate definitions. + :return: A dictionary of custom gate names to unitary matrices or callables. + """ + custom_gates: CustomGateMap = {} + for defgate in program.defined_gates: + if defgate.parameters: + + def parametric_gate(*args: float, defgate: DefGate = defgate) -> qx.Unitary: + parameter_map = {Parameter(p.name): arg for p, arg in zip(defgate.parameters, args, strict=False)} + matrix = jnp.asarray( + [[substitute(element, parameter_map) for element in row] for row in defgate.matrix], # type: ignore[arg-type] + dtype=complex, + ) + num_qubits = int(jnp.round(jnp.log2(matrix.shape[0]))) + return qx.Unitary.from_matrix(matrix, ((2,) * num_qubits, (2,) * num_qubits)) + + custom_gates[defgate.name] = parametric_gate + else: + matrix = jnp.asarray(defgate.matrix, dtype=complex) + num_qubits = int(jnp.round(jnp.log2(matrix.shape[0]))) + custom_gates[defgate.name] = qx.Unitary.from_matrix(matrix, ((2,) * num_qubits, (2,) * num_qubits)) + return custom_gates + + +def get_instruction_unitary( + inst: Gate, + custom_gates: CustomGateMap | None = None, +) -> qx.Unitary: + """Get the unitary matrix associated with a gate instruction. + + Looks up the gate by name — first in ``custom_gates`` (if provided), then in the + standard quax gate table ``qx.gates.QUANTUM_GATES``. Parametric gates are supported + provided all parameters are concrete numeric values. + + :param inst: The gate instruction. + :param custom_gates: Optional dictionary of additional gate definitions (e.g. from + :func:`get_custom_gates_from_program`). Takes precedence over the standard gate set. + :return: The unitary matrix. + :raises ValueError: If any gate parameter is symbolic. + :raises KeyError: If the gate name is not found in either the custom or standard gate set. + """ + name = inst.name + + # Look up gate definition: custom gates take precedence + if custom_gates is not None and name in custom_gates: + gate_def = custom_gates[name] + elif name in qx.gates.QUANTUM_GATES: + gate_def = qx.gates.QUANTUM_GATES[name] + else: + raise KeyError(f"Unknown gate '{name}'. Provide it via custom_gates (e.g. custom_gates={{'{name}': matrix}}).") + + if inst.params: + fixed_params = _resolve_params(list(inst.params)) + if not callable(gate_def): + raise ValueError(f"Gate '{name}' is not parametric but parameters were provided.") + result = gate_def(*fixed_params) + else: + if callable(gate_def): + result = gate_def() + else: + result = gate_def + + # quax parametric gates may return Operator instead of Unitary; wrap if needed + if not isinstance(result, qx.Unitary): + result = qx.Unitary.from_matrix(result.matrix, result.dims) + return result + + +@dataclass(frozen=True) +class Channel: + """A noise channel attaches a superoperator to a specific gate. + + The superoperator *includes* the gate unitary, so the channel replaces the gate + rather than being applied after it. + + The ``process`` field is a ``qx.SuperOp`` which can be converted to alternative + representations (Choi, Kraus, Pauli-Liouville) via ``quax``. + + Fidelity metrics are computed relative to the ideal gate unitary stored in + ``target_unitary``. For standard gates use the class methods (e.g. + :meth:`from_gate_fidelity`) which resolve the unitary automatically. + """ + + inst: Gate + """Quil gate to which the channel applies.""" + + process: qx.SuperOp + """The noisy process (superoperator) for the gate, including the gate unitary.""" + + target_unitary: qx.Unitary + """The noiseless unitary of the gate.""" + + @cached_property + def unitary(self) -> qx.Unitary: + """The noiseless unitary of the gate.""" + return self.target_unitary + + @cached_property + def qubits(self) -> list[int]: + """The qubits which the channel applies to.""" + return self.inst.get_qubit_indices() + + @cached_property + def num_qubits(self) -> int: + """The number of qubits the channel acts on.""" + return len(self.qubits) + + # ────────────────────────────────────────────── + # Constructors + # ────────────────────────────────────────────── + + @classmethod + def from_gate_fidelity( + cls: type[Channel], + inst: Gate, + fidelity: float, + custom_gates: CustomGateMap | None = None, + ) -> Channel: + r"""Create a depolarizing noise channel from an average gate fidelity. + + The resulting channel is the composition of the ideal gate unitary with a + depolarizing channel calibrated to the specified fidelity: + :math:`\\mathcal{E} = \\mathcal{D}_p \\circ \\mathcal{U}` + + :param inst: The gate to which the channel applies. + :param fidelity: The average gate fidelity, :math:`F_{\\mathrm{avg}} \\in [0, 1]`. + :param custom_gates: Optional dictionary of custom gate definitions. + :return: A Channel instance. + """ + unitary = get_instruction_unitary(inst, custom_gates) + p = qx.average_fidelity_to_depolarizing_constant(fidelity, unitary.dims[0]) + return cls.from_depolarizing_constant(inst, p, custom_gates) + + @classmethod + def from_pauli_fidelity( + cls: type[Channel], + inst: Gate, + pauli_fidelity: float, + custom_gates: CustomGateMap | None = None, + ) -> Channel: + r"""Create a depolarizing noise channel from a process (Pauli) fidelity. + + The process fidelity :math:`F_e` is related to the average gate fidelity by + :math:`F_{\\mathrm{avg}} = (d \\cdot F_e + 1) / (d + 1)`. + + :param inst: The gate to which the channel applies. + :param pauli_fidelity: The process fidelity (entanglement fidelity), :math:`F_e \\in [0, 1]`. + :param custom_gates: Optional dictionary of custom gate definitions. + :return: A Channel instance. + """ + unitary = get_instruction_unitary(inst, custom_gates) + p = qx.process_fidelity_to_depolarizing_constant(pauli_fidelity, unitary.dims[0]) + return cls.from_depolarizing_constant(inst, p, custom_gates) + + @classmethod + def from_depolarizing_constant( + cls: type[Channel], + inst: Gate, + depolarizing_constant: float, + custom_gates: CustomGateMap | None = None, + ) -> Channel: + r"""Create a depolarizing noise channel from a depolarization constant. + + The depolarizing constant :math:`p` parameterizes the channel as + :math:`\\mathcal{D}_p(\\rho) = p \\, \\rho + (1-p) \\, I/d`. + + :param inst: The gate to which the channel applies. + :param depolarizing_constant: The depolarization constant, e.g. 0.98 for 2% depolarization. + :param custom_gates: Optional dictionary of custom gate definitions. + :return: A Channel instance. + """ + unitary = get_instruction_unitary(inst, custom_gates) + depolarizing_superop = qx.depolarizing_channel_superoperator(1 - depolarizing_constant, unitary.dims[0]) + combined_superop = depolarizing_superop @ unitary + return cls(inst=inst, process=qx.to_superop(combined_superop), target_unitary=unitary) + + @classmethod + def from_pauli_noise( + cls: type[Channel], + inst: Gate, + pauli_noise: dict[str, float], + custom_gates: CustomGateMap | None = None, + ) -> Channel: + """Create a stochastic Pauli noise channel from Pauli error rates. + + The noise is specified as a dictionary mapping Pauli strings to error probabilities, + e.g. ``{"XX": 0.03, "ZI": 0.001}``. The probabilities must sum to at most 1.0; + any remainder is assigned to the identity (no-error) term. + + :param inst: The gate to which the channel applies. + :param pauli_noise: Pauli error rates, e.g. ``{"IX": 0.01, "ZZ": 0.02}``. + :param custom_gates: Optional dictionary of custom gate definitions. + :return: A Channel instance. + """ + unitary = get_instruction_unitary(inst, custom_gates) + num_qubits = len(unitary.dims[0]) + + total_error_rate = 0.0 + for pauli, error_rate in pauli_noise.items(): + if error_rate < 0.0: + raise ValueError(f"Pauli term '{pauli}' has negative error rate {error_rate}.") + total_error_rate += error_rate + if total_error_rate > 1.0: + raise ValueError(f"Pauli error rates must sum to at most 1.0, got {total_error_rate}.") + + for pauli in pauli_noise: + if len(pauli) != num_qubits: + raise ValueError(f"Pauli term '{pauli}' has length {len(pauli)}, expected {num_qubits}.") + + all_pauli_terms = tuple("".join(term) for term in product("IXYZ", repeat=num_qubits)) + + pauli_error_rates: list[float] = [] + for term in reversed(all_pauli_terms): + if term in pauli_noise: + error_rate = pauli_noise[term] + elif all(p == "I" for p in term): + error_rate = 1 - sum(pauli_error_rates) + else: + error_rate = 0 + pauli_error_rates.append(error_rate) + if not jnp.isclose(1.0, sum(pauli_error_rates)): + raise ValueError("Pauli error rates plus the implicit identity rate must sum to 1.0.") + pauli_error_rates = list(reversed(pauli_error_rates)) + + # Build the 4**num_qubits Pauli operators as tensor (Kronecker) products of the + # single-qubit Paulis, in lexicographic (I, X, Y, Z) order matching pauli_error_rates. + single_pauli_matrices = qx.ensembles.PAULIS.matrix # (4, 2, 2): I, X, Y, Z + pauli_op_matrices = jnp.stack( + [reduce(jnp.kron, paulis) for paulis in product(single_pauli_matrices, repeat=num_qubits)] + ) # (4**num_qubits, 2**num_qubits, 2**num_qubits) + + # Scale each Pauli by sqrt(probability) to form Kraus operators + coeffs = jnp.sqrt(jnp.array(pauli_error_rates, dtype=float)) + kraus_matrices = coeffs[:, None, None] * pauli_op_matrices + kraus_map = qx.KrausMap.from_matrix(kraus_matrices, unitary.dims) + + process_superop = qx.to_superop(kraus_map @ unitary) + return cls(inst=inst, process=process_superop, target_unitary=unitary) + + @classmethod + def from_random_coherent_error( + cls: type[Channel], + inst: Gate, + process_fidelity: float, + rng: np.random.Generator | None = None, + custom_gates: CustomGateMap | None = None, + ) -> Channel: + r"""Create a channel with a random coherent (unitary) error at the specified process fidelity. + + A random unitary close to identity is generated with the given process fidelity, + then composed with the ideal gate. + + :param inst: The gate to which the channel applies. + :param process_fidelity: The process fidelity of the coherent error, :math:`F_e \\in [0, 1]`. + :param rng: NumPy random number generator for reproducibility. + :param custom_gates: Optional dictionary of custom gate definitions. + :return: A Channel instance. + """ + if rng is None: + rng = np.random.default_rng() + + ideal = get_instruction_unitary(inst, custom_gates) + num_qubits = len(ideal.dims[0]) + d = 2**num_qubits + + # Generate a random unitary error with the specified process fidelity + # using Pauli generator decomposition + angle = jnp.arccos(2 * process_fidelity - 1) / (2 * jnp.pi) + id_coeff = 1 - float(angle) + coeffs = rng.random(4**num_qubits - 1) + coeffs = (1 - id_coeff) / np.sqrt(np.sum(np.square(coeffs))) * coeffs + + # Build Pauli generator sum using quax Pauli matrices + pauli_matrices = qx.ensembles.PAULIS.matrix # shape (4, 2, 2) + pauli_sum = jnp.eye(d, dtype=complex) * id_coeff + pauli_products = list(itertools.product(pauli_matrices, repeat=num_qubits))[1:] + for paulis, coefficient in zip(pauli_products, coeffs, strict=False): + pauli_sum = pauli_sum + reduce(jnp.kron, paulis) * coefficient + + from jax.scipy.linalg import expm as jax_expm + + error_unitary = jax_expm(-1j * jnp.pi * pauli_sum) + # Fix global phase + phase = jnp.exp(-1j * jnp.angle(error_unitary[0, 0])) + error_unitary = error_unitary * phase + + error_u = qx.Unitary.from_matrix(error_unitary, ideal.dims) + noisy_superop = qx.to_superop(error_u @ ideal) + return cls(inst=inst, process=noisy_superop, target_unitary=ideal) + + @classmethod + def from_mixture( + cls: type[Channel], + inst: Gate, + constituents: list[qx.Unitary], + probabilities: list[float], + custom_gates: CustomGateMap | None = None, + ) -> Channel: + r"""Create a mixture channel from a set of unitary errors with given probabilities. + + The channel is :math:`\\mathcal{E}(\\rho) = (1-\\sum p_i) U\\rho U^\\dagger + \\sum p_i V_i U \\rho U^\\dagger V_i^\\dagger` + where :math:`U` is the ideal gate and :math:`V_i` are the error unitaries. + + :param inst: The gate to which the channel applies. + :param constituents: Unitary error operators to mix. + :param probabilities: Probability of each unitary error. Must sum to at most 1.0. + :param custom_gates: Optional dictionary of custom gate definitions. + :return: A Channel instance. + """ + ideal = get_instruction_unitary(inst, custom_gates) + + if len(constituents) != len(probabilities): + raise ValueError("The number of constituents and probabilities must match.") + error_prob = sum(probabilities) + if error_prob > 1.0: + raise ValueError(f"The sum of probabilities ({error_prob}) must be at most 1.0.") + + # Build the mixture superop: (1-p_total) S(U) + sum p_i S(V_i @ U) + p0 = 1.0 - error_prob + noisy_superop_matrix = p0 * qx.to_superop(ideal).matrix + for p, v in zip(probabilities, constituents, strict=False): + composed = v @ ideal + noisy_superop_matrix = noisy_superop_matrix + p * qx.to_superop(composed).matrix + noisy_superop = qx.SuperOp.from_matrix(noisy_superop_matrix, ideal.dims) + return cls(inst=inst, process=noisy_superop, target_unitary=ideal) + + @classmethod + def from_coherence_times( + cls: type[Channel], + inst: Gate, + gate_duration: float, + t1s: list[float], + t2s: list[float] | None = None, + custom_gates: CustomGateMap | None = None, + ) -> Channel: + """Create a decoherence Channel based on the coherence times. + + In this construction, decoherence is applied _after_ the ideal gate unitary. + + :param inst: The target instruction. + :param gate_duration: The duration of the gate. + :param t1s: The t1 time(s) of the qubits + :param t2s: The t2 time(s) of the qubits. Default to 2*t1. + """ + unitary = get_instruction_unitary(inst, custom_gates) + qubits = inst.get_qubit_indices() + num_sys = len(qubits) + if num_sys != len(t1s): + raise ValueError(f"Expected {num_sys} T1 values for {inst.out()}, got {len(t1s)}.") + if t2s is None: + t2s = [2 * t1 for t1 in t1s] + else: + if num_sys != len(t2s): + raise ValueError(f"Expected {num_sys} T2 values for {inst.out()}, got {len(t2s)}.") + + t1_array = jnp.asarray(t1s) + tphi_array = 1 / (1 / jnp.asarray(t2s) - 1 / t1_array) + + choi = qx.thermal_relaxation_choi(t1s=t1_array, tphis=tphi_array, duration=gate_duration) + process = qx.to_superop(choi @ unitary) + return cls( + inst=inst, + process=process, + target_unitary=unitary, + ) + + @classmethod + def from_superoperator( + cls: type[Channel], + inst: Gate, + process: qx.SuperOp, + target_unitary: qx.Unitary | None = None, + custom_gates: CustomGateMap | None = None, + ) -> Channel: + """Create a Channel from a pre-built superoperator. + + If ``target_unitary`` is not provided it is inferred from the gate + instruction using the standard gate set (and ``custom_gates`` if given). + + :param inst: The gate to which the channel applies. + :param process: The noisy process superoperator (includes the gate unitary). + :param target_unitary: The ideal gate unitary. Resolved automatically + when omitted. + :param custom_gates: Optional dictionary of custom gate definitions, + used only when ``target_unitary`` is ``None``. + :return: A Channel instance. + """ + if target_unitary is None: + target_unitary = get_instruction_unitary(inst, custom_gates) + return cls(inst=inst, process=process, target_unitary=target_unitary) + + # ────────────────────────────────────────────── + # Cached representation conversions + # ────────────────────────────────────────────── + + @cached_property + def noise_process(self) -> qx.SuperOp: + r"""The noise-only channel with the ideal gate unitary factored out. + + If the full channel is :math:`\\mathcal{E} = \\Lambda \\circ \\mathcal{U}`, this + returns :math:`\\Lambda`. + """ + return qx.to_superop(self.process @ self.unitary.h) + + # ────────────────────────────────────────────── + # Fidelity properties + # ────────────────────────────────────────────── + + @cached_property + def fidelity(self) -> float: + r"""Average gate fidelity :math:`F_{\\mathrm{avg}}` of the channel relative to the ideal gate.""" + return float(qx.process_fidelity_to_average_fidelity(self.pauli_fidelity, dims=self.unitary.dims[0])) + + @cached_property + def infidelity(self) -> float: + r"""Average gate infidelity :math:`1 - F_{\\mathrm{avg}}`.""" + return 1.0 - self.fidelity + + @cached_property + def pauli_fidelity(self) -> float: + """Process fidelity (entanglement fidelity) :math:`F_e` relative to the ideal gate.""" + process, unitary = qx.promote_hilbert_space(self.process, qx.to_superop(self.unitary)) + return float(qx.process_fidelity(process, unitary)) + + @cached_property + def pauli_infidelity(self) -> float: + """Process infidelity :math:`1 - F_e`.""" + return 1.0 - self.pauli_fidelity + + @cached_property + def stochastic_infidelity(self) -> float: + """Stochastic (incoherent) component of the process infidelity.""" + return float(qx.stochastic_infidelity(self.noise_process)) + + @cached_property + def stochastic_fidelity(self) -> float: + """Stochastic fidelity :math:`1 - e_S`.""" + return 1.0 - self.stochastic_infidelity + + @cached_property + def coherent_infidelity(self) -> float: + """Coherent component of the process infidelity: :math:`e_C = e - e_S`.""" + return self.pauli_infidelity - self.stochastic_infidelity + + @cached_property + def coherent_fidelity(self) -> float: + """Coherent fidelity :math:`1 - e_C`.""" + return 1.0 - self.coherent_infidelity + + @cached_property + def unitarity(self) -> float: + """Unitarity of the channel.""" + return float(qx.unitarity(self.noise_process)) + + # ────────────────────────────────────────────── + # Channel analysis methods + # ────────────────────────────────────────────── + + def pauli_twirl(self) -> Channel: + """Return a Pauli-twirled version of this channel. + + Pauli twirling projects the channel onto the Pauli diagonal, eliminating + off-diagonal coherences in the Pauli-Liouville representation. The + resulting channel is a stochastic Pauli channel with the same diagonal + error rates. + """ + ptm = qx.to_pauli_liouville(self.process) + # Keep only the diagonal of the PTM + twirled_ptm_matrix = jnp.diag(jnp.diag(ptm.matrix)) + twirled_superop = qx.to_superop(qx.PauliLiouville.from_matrix(twirled_ptm_matrix, self.process.dims)) + return replace(self, process=twirled_superop) + + @cached_property + def _unitary_error_component(self) -> Array: + """Extract the dominant unitary from the noise-only channel. + + Uses eigendecomposition + SVD polar decomposition to find the closest + unitary to the noise channel. + """ + choi_matrix = qx.to_choi(self.noise_process).matrix + d = 2**self.num_qubits + + # Dominant eigenvector of the Choi matrix + eigenvalues, eigenvectors = jnp.linalg.eigh(choi_matrix) + dominant_eigenvector = eigenvectors[:, jnp.argmax(jnp.abs(eigenvalues))] + + # SVD polar decomposition to extract the closest unitary + u, _, vh = jnp.linalg.svd(dominant_eigenvector.reshape(d, d).T) + return u @ vh + + def to_coherent_channel(self) -> Channel: + """Isolate the coherent (unitary) component of the noise. + + Extracts the dominant unitary from the noise Choi matrix via polar + decomposition and returns a channel consisting of that unitary error + composed with the ideal gate. + """ + u_error = self._unitary_error_component + u_error_qx = qx.Unitary.from_matrix(u_error, self.process.dims) + coherent_superop = qx.to_superop(u_error_qx @ self.unitary) + return replace(self, process=coherent_superop) + + def to_stochastic_channel(self) -> Channel: + r"""Isolate the stochastic (incoherent) component of the noise. + + The full channel decomposes as + :math:`\\mathcal{E} = \\mathcal{S} \\circ \\mathcal{U}_{\\mathrm{err}} \\circ \\mathcal{U}_{\\mathrm{gate}}`. + This method factors out the coherent unitary error and returns + :math:`\\mathcal{S} \\circ \\mathcal{U}_{\\mathrm{gate}}`. + """ + u_error = self._unitary_error_component + # Get the noise-only superoperator and compose with U_err† + noise_superop = self.noise_process.matrix + u_err_inv_superop = jnp.kron(u_error.conj(), u_error.conj().T) + stochastic_noise_superop = noise_superop @ u_err_inv_superop + # Recompose with the ideal gate + ideal_superop = jnp.kron(self.unitary.matrix, self.unitary.matrix.conj()) + stochastic_superop = stochastic_noise_superop @ ideal_superop + return replace(self, process=qx.SuperOp.from_matrix(stochastic_superop, self.process.dims)) + + def is_pauli(self) -> bool: + """Check if the noise channel is a Pauli (stochastic Pauli) channel. + + A Pauli channel has a diagonal Pauli transfer matrix (noise-only part). + """ + ptm = qx.to_pauli_liouville(self.noise_process).matrix + mask = ~jnp.eye(ptm.shape[0], dtype=bool) + return bool(jnp.allclose(ptm[mask], 0)) + + def to_pauli_vector(self) -> Array: + """Convert the noise channel to a Pauli error probability vector. + + Returns the vector of probabilities for each Pauli error in lexicographic + order (II, IX, IY, IZ, XI, XX, ...). The vector sums to 1.0. + """ + noise_superop = self.noise_process.matrix + num_qubits = self.num_qubits + dim = noise_superop.shape[0] + + # Build all Pauli operators and their superoperators + pauli_matrices = qx.ensembles.PAULIS.matrix # (4, 2, 2): I, X, Y, Z + all_pauli_products = list(product(pauli_matrices, repeat=num_qubits)) + pauli_error_rates = [] + for pauli_tuple in all_pauli_products: + pauli_op = reduce(jnp.kron, pauli_tuple) + pauli_superop = jnp.kron(pauli_op, pauli_op.conj()) + rate = float(jnp.abs(jnp.trace(noise_superop @ pauli_superop) / dim)) + pauli_error_rates.append(rate) + + return jnp.array(pauli_error_rates, dtype=float) + + @cached_property + def pauli_vector(self) -> Array: + """The Pauli error probability vector of the noise channel.""" + return self.to_pauli_vector() + + # ────────────────────────────────────────────── + # Visualization + # ────────────────────────────────────────────── + + def plot(self, only_noise: bool = True, show_identity: bool = False) -> Figure: + """Plot the Pauli transfer matrix of the channel. + + :param only_noise: If True, plot the noise-only channel (gate unitary factored out). + If False, plot the full channel including the gate unitary. + :param show_identity: If True, include the identity component in the noise-only plot. + If False (default), visualize the generator of the noise channel via the matrix + logarithm of the PTM. For near-identity noise this approximates PTM - I, but + correctly captures the Lie-algebraic structure of the channel. + Only applies when ``only_noise=True``. + :return: A Plotly Figure. + """ + if only_noise: + channel = self.noise_process + if not show_identity: + ptm = qx.to_pauli_liouville(channel) + log_ptm = scipy_logm(np.asarray(ptm.matrix)) + channel = qx.PauliLiouville.from_matrix(jnp.array(log_ptm), channel.dims) + title_prefix = "Noise Channel" + else: + channel = self.process + title_prefix = "Full Channel" + + fig = qx.plot(channel) + fig.update_layout( + title=( + f"{title_prefix} for {self.inst.out()}
" + f"𝜀={self.pauli_infidelity * 100:.2f}%, " + f"𝜀u={self.coherent_infidelity * 100:.2f}%, " + f"𝜀s={self.stochastic_infidelity * 100:.2f}%" + ) + ) + return fig + + # ────────────────────────────────────────────── + # Serialization + # ────────────────────────────────────────────── + + def to_json(self) -> str: + """Serialize Channel to a JSON string. + + :return: JSON string representation. + """ + data = { + "schema_version": 1, + "inst": self.inst.out(), + "superop": _pack_operator(self.process), + } + data["target_unitary"] = _pack_operator(self.target_unitary) + + return json.dumps(data) + + @classmethod + def from_json(cls: type[Channel], json_str: str) -> Channel: + """Deserialize a Channel from a JSON string. + + :param json_str: JSON string as produced by :meth:`to_json`. + :return: Channel instance. + """ + data = json.loads(json_str) + inst = _parse_quil_instruction(data["inst"]) + if not isinstance(inst, Gate): + raise TypeError(f"Channel JSON must contain a gate instruction, got {type(inst).__name__}.") + + superop_data = data["superop"] + shape = tuple(superop_data["shape"]) + superop_array = _unpack_complex_array(superop_data) + dims = _unpack_operator_dims(superop_data, shape, superoperator=True) + superop = qx.SuperOp.from_matrix(superop_array, dims) + + if "target_unitary" in data: + u_data = data["target_unitary"] + u_shape = tuple(u_data["shape"]) + u_array = _unpack_complex_array(u_data) + u_dims = _unpack_operator_dims(u_data, u_shape, superoperator=False) + target_unitary = qx.Unitary.from_matrix(u_array, u_dims) + else: + target_unitary = get_instruction_unitary(inst) + + return cls(inst=inst, process=superop, target_unitary=target_unitary) + + # ────────────────────────────────────────────── + # Dunder methods + # ────────────────────────────────────────────── + + def __str__(self) -> str: + """Return a simplified string representation showing the gate and process fidelity.""" + return f"<{self.inst.out()} ~ ({100 * self.pauli_fidelity:.2f}%)>" + + def __eq__(self, other: object) -> bool: + """Check equality by instruction and exact process and ideal-gate matrices. + + Equality is exact (no fidelity tolerance): two channels are equal only if they + share the same instruction and bit-for-bit identical process and target-unitary + matrices. Making tolerance decisions on the user's behalf is deliberately avoided. + """ + if not isinstance(other, Channel): + return False + if self.inst != other.inst: + return False + return bool( + jnp.array_equal(self.process.matrix, other.process.matrix) + and jnp.array_equal(self.target_unitary.matrix, other.target_unitary.matrix) + ) + + __hash__ = None # type: ignore[assignment] + + def __matmul__(self, other: Channel) -> Channel: + r"""Compose two channels: ``channel_B @ channel_A``. + + Both channels share the same gate instruction. The composition factors + out one copy of the gate unitary so the result represents the sequential + application of the two noisy processes: + + :math:`\\mathcal{E}_B \\circ \\mathcal{U}^\\dagger \\circ \\mathcal{E}_A` + + This is the natural composition: if ``channel_A`` already includes the + gate, applying ``channel_B`` after it should not double-count the gate. + """ + if not isinstance(other, Channel): + return NotImplemented + if self.inst != other.inst: + raise ValueError(f"Cannot compose channels for different gates: {self.inst.out()} vs {other.inst.out()}") + # E_B @ U† @ E_A (factor out one gate unitary between the two channels) + u_dag_superop = qx.to_superop(self.unitary.h) + composed_superop = qx.to_superop(self.process @ u_dag_superop @ other.process) + return replace(self, process=composed_superop) + + def __pow__(self, power: float) -> Channel: + r"""Raise the channel's noise to a fractional ``power``, preserving the gate. + + With the channel written :math:`\\mathcal{E} = \\Lambda \\circ \\mathcal{U}`, this returns + :math:`\\Lambda^{power} \\circ \\mathcal{U}`: only the noise :math:`\\Lambda` is raised to the + fractional matrix power, while the ideal gate is kept. So ``power = 0`` yields the noiseless + gate, ``1`` leaves the channel unchanged, and ``> 1`` strengthens the noise. This is the knob + used to sweep noise strength. + """ + if not isinstance(power, (int, float)): + return NotImplemented + powered_noise_matrix = fractional_matrix_power(np.asarray(self.noise_process.matrix), power) + powered_noise = qx.SuperOp.from_matrix(jnp.asarray(powered_noise_matrix), self.noise_process.dims) + process = qx.to_superop(powered_noise @ qx.to_superop(self.unitary)) + return replace(self, process=process) + + def __or__(self, other: Channel | MeasurementChannel) -> CycleChannel: + """Tensor product of two channels on disjoint qubits, producing a CycleChannel. + + The result represents a cycle containing both operations acting in parallel + on disjoint qubits. The DefCircuit encodes the parallel operations as + formal instructions. + + :param other: Another Channel or MeasurementChannel on disjoint qubits. + :return: A CycleChannel representing the tensor product. + """ + if not isinstance(other, (Channel, MeasurementChannel)): + return NotImplemented + + # Validate disjoint qubits + self_qubits = set(self.qubits) + other_qubits = set(other.qubits) + if self_qubits & other_qubits: + raise ValueError(f"Cannot tensor channels with overlapping qubits: {self_qubits & other_qubits}") + + return _build_cycle_channel([self, other]) + + +@dataclass(frozen=True) +class MeasurementChannel: + """A measurement noise channel attaches a quantum instrument to a specific measurement operation. + + The ``process`` field is a ``qx.QuantumInstrument`` which models both classification + errors and post-measurement back-action. + """ + + inst: Measurement + """The measurement operation to which the channel applies.""" + + process: qx.QuantumInstrument + """A quantum instrument representation of the noisy measurement.""" + + @cached_property + def qubits(self) -> list[int]: + """The qubits which the measurement applies to.""" + qubit = self.inst.qubit + return [qubit.index if hasattr(qubit, "index") else int(qubit)] + + # ────────────────────────────────────────────── + # Constructors + # ────────────────────────────────────────────── + + @classmethod + def from_readout_fidelity( + cls: type[MeasurementChannel], + inst: Measurement, + fidelity: float, + asymmetry: float = 0.0, + dim: int = 2, + ) -> MeasurementChannel: + """Create a readout quantum instrument with optional asymmetry. + + Produces a perfectly QND measurement with the given classification fidelity. + Error is distributed only between adjacent levels: P(j+1|j) and P(j|j+1). + Non-adjacent confusion is zero. + + :param inst: The measurement instruction. + :param fidelity: The average readout fidelity. + :param asymmetry: Value between -1 and +1. Zero is symmetric. + Positive biases toward upward confusion P(j+1|j), negative toward downward P(j|j+1). + :param dim: The dimension of the measured system (2 for qubits, 3 for qutrits, etc.). + :return: A MeasurementChannel instance. + """ + # Compute per-pair error factor so that the average diagonal equals fidelity. + # Each adjacent pair (j, j+1) contributes error_factor*(1+a) + error_factor*(1-a) + # = 2*error_factor to total off-diagonal sum. With (dim-1) pairs, the average + # column error is 2*(dim-1)*error_factor/dim, which we set equal to (1-fidelity). + error_factor = dim * (1 - fidelity) / (2 * (dim - 1)) + + confusion = jnp.zeros((dim, dim)) + for j in range(dim - 1): + confusion = confusion.at[j + 1, j].set(error_factor * (1 + asymmetry)) + confusion = confusion.at[j, j + 1].set(error_factor * (1 - asymmetry)) + # Set diagonal so each column sums to 1 + col_sums = confusion.sum(axis=0) + confusion = confusion + jnp.diag(1 - col_sums) + + transition = jnp.eye(dim) + instrument = qx.instrument_from_confusion_and_transition( + confusion_matrix=confusion, + transition_matrix=transition, + dims=(dim,), + measured_qudits=(0,), + ) + return cls(inst=inst, process=instrument) + + @classmethod + def from_confusion_and_transition( + cls: type[MeasurementChannel], + inst: Measurement, + confusion_matrix: Array, + transition_matrix: Array, + ) -> MeasurementChannel: + """Create a MeasurementChannel from a confusion matrix and a transition matrix. + + Provides independent control over measurement classification accuracy + and post-measurement quantum state evolution. + + **Matrix Conventions (column-stochastic):** + + - ``confusion_matrix[i, j]``: P(outcome i | prepared j) + - ``transition_matrix[k, j]``: P(ending in k | input j) + - Columns sum to 1.0 + + :param inst: The measurement instruction. + :param confusion_matrix: A (d, d) classification matrix. + :param transition_matrix: A (d, d) post-measurement transition matrix. + :return: A MeasurementChannel instance. + """ + confusion = jnp.asarray(confusion_matrix) + dim = confusion.shape[0] + instrument = qx.instrument_from_confusion_and_transition( + confusion_matrix=confusion, + transition_matrix=jnp.asarray(transition_matrix), + dims=(dim,), + measured_qudits=(0,), + ) + return cls(inst=inst, process=instrument) + + @classmethod + def from_axis( + cls: type[MeasurementChannel], + inst: Measurement, + theta: float = 0.0, + phi: float = 0.0, + sharpness: float = 1.0, + ) -> MeasurementChannel: + """Create a MeasurementChannel from a Bloch sphere measurement axis. + + The angles refer to the standard Bloch sphere notation. + Theta=0, phi=0 is the Z axis (computational basis measurement). + + :param inst: The measurement instruction. + :param theta: The colatitude with respect to the z-axis. + :param phi: The longitude with respect to the x-axis. + :param sharpness: The sharpness of the measurement. 1.0 is projective, + 0.0 is no measurement. 0 < s < 1 is a weak measurement. + :return: A MeasurementChannel instance. + """ + instrument = qx.instrument_from_axis( + theta=theta, + phi=phi, + sharpness=sharpness, + ) + return cls(inst=inst, process=instrument) + + @classmethod + def from_binary_discriminator( + cls: type[MeasurementChannel], + inst: Measurement, + dim: int, + threshold: int, + fidelity: float = 1.0, + ) -> MeasurementChannel: + """Create a MeasurementChannel for a binary discriminator. + + Models a measurement that reports a single classical *bit* for a + ``dim``-level system by thresholding: levels ``[0, threshold)`` yield + outcome ``0`` and levels ``[threshold, dim)`` yield outcome ``1``. The + resulting instrument therefore always has exactly two outcomes, so leaked + levels are lumped in with whichever side of the threshold they fall on + (the usual case being a "dark" ground state vs. everything "bright"). + + For example, ``threshold=1, dim=2`` is an ordinary qubit readout + (``{0}`` -> 0, ``{1}`` -> 1); ``threshold=1, dim=3`` discriminates + ``{0}`` vs ``{1, 2}`` (ground vs. excited-or-leaked); ``threshold=2, + dim=3`` discriminates ``{0, 1}`` vs ``{2}`` (i.e. flags leakage only). + + An optional ``fidelity`` parameter degrades the ideal discriminator with + uniform classification noise. + + :param inst: The measurement instruction. + :param dim: The dimension of the measured system. + :param threshold: The split point: levels below it report 0, levels at or + above it report 1. Must satisfy ``1 <= threshold < dim``. + :param fidelity: Additional classification fidelity applied on top of the + discrimination (1.0 = perfect discriminator). + :return: A MeasurementChannel instance. + """ + if not (1 <= threshold < dim): + raise ValueError(f"threshold must satisfy 1 <= threshold < dim, got threshold={threshold}, dim={dim}") + + # Ideal two-outcome confusion matrix of shape (num_outcomes=2, dim): + # column j (prepared level j) puts all its weight on outcome 0 if + # j < threshold, else on outcome 1. Two rows so the instrument has + # exactly two outcomes (never a phantom, zero-probability outcome). + confusion = jnp.zeros((2, dim)) + for j in range(dim): + confusion = confusion.at[int(j >= threshold), j].set(1.0) + + # Optionally degrade with uniform noise across the two outcomes. + if fidelity < 1.0: + confusion = fidelity * confusion + (1 - fidelity) * jnp.ones((2, dim)) / 2 + + transition = jnp.eye(dim) + instrument = qx.instrument_from_confusion_and_transition( + confusion_matrix=confusion, + transition_matrix=transition, + dims=(dim,), + measured_qudits=(0,), + ) + return cls(inst=inst, process=instrument) + + # ────────────────────────────────────────────── + # Properties + # ────────────────────────────────────────────── + + @cached_property + def confusion_matrix(self) -> Array: + """The confusion matrix of the measurement. + + Shape ``(num_outcomes, d_measured)``. + Entry ``[i, j]`` is P(outcome i | prepared j). + """ + return self.process.confusion_matrix # type: ignore[no-any-return] + + @cached_property + def transition_matrix(self) -> Array: + """The post-measurement transition matrix. + + Shape ``(d, d)``. Entry ``[k, j]`` is P(ending in k | input j), + marginalized over all measurement outcomes. + """ + return self.process.transition_matrix # type: ignore[no-any-return] + + @cached_property + def non_demolition_fidelity(self) -> float: + """Quantum non-demolition (QND) fidelity. + + Measures how well the measurement preserves computational basis states, + averaged over outcomes and input states. + """ + return float(qx.non_demolition_fidelity(self.process)) + + @cached_property + def instrument_fidelity(self) -> float: + """Overall instrument fidelity w.r.t. ideal QND measurement. + + Accounts for both classification errors and post-measurement state disturbance. + """ + return float(qx.instrument_fidelity(self.process)) + + @cached_property + def classification_fidelity(self) -> float: + """Classification fidelity: average probability of correctly identifying the measurement outcome.""" + return float(qx.classification_fidelity(self.process)) + + # ────────────────────────────────────────────── + # Visualization + # ────────────────────────────────────────────── + + def plot(self) -> Figure: + """Plot the quantum instrument using the quax visualization. + + Shows per-outcome superoperator matrices and the total CPTP channel. + + :return: A Plotly Figure. + """ + fig = qx.plot(self.process) + fig.update_layout( + title=( + f"Quantum Instrument MEASURE {self.qubits[0]}
" + f"Cls: {100 * self.classification_fidelity:.2f}%, " + f"QND: {100 * self.non_demolition_fidelity:.2f}%, " + f"Instrument: {100 * self.instrument_fidelity:.2f}%" + ) + ) + return fig + + # ────────────────────────────────────────────── + # Serialization + # ────────────────────────────────────────────── + + def to_json(self) -> str: + """Serialize MeasurementChannel to a JSON string. + + :return: JSON string representation. + """ + # Store per-outcome superoperator matrices. + instrument_data = [] + for i in range(self.process.num_outcomes): + superop_i, _ = self.process.outcome_superop(i) + instrument_data.append(_pack_operator(superop_i)) + + data = { + "schema_version": 1, + "inst": self.inst.out(), + "instruments": instrument_data, + "measured_qudits": list(self.process.measured_qudits), + } + return json.dumps(data) + + @classmethod + def from_json(cls: type[MeasurementChannel], json_str: str) -> MeasurementChannel: + """Deserialize a MeasurementChannel from a JSON string. + + :param json_str: JSON string as produced by :meth:`to_json`. + :return: MeasurementChannel instance. + """ + data = json.loads(json_str) + inst = _parse_quil_instruction(data["inst"]) + if not isinstance(inst, Measurement): + raise TypeError( + f"MeasurementChannel JSON must contain a measurement instruction, got {type(inst).__name__}." + ) + measured_qudits = tuple(data["measured_qudits"]) + + superop_matrices = [] + instrument_dims = None + for inst_data in data["instruments"]: + shape = tuple(inst_data["shape"]) + arr = _unpack_complex_array(inst_data) + op_dims = _unpack_operator_dims(inst_data, shape, superoperator=True) + if instrument_dims is None: + instrument_dims = op_dims + elif instrument_dims != op_dims: + raise ValueError("All serialized measurement outcomes must have the same dims.") + superop_matrices.append(arr) + + if instrument_dims is None: + raise ValueError("MeasurementChannel JSON must contain at least one outcome superoperator.") + + instrument = qx.QuantumInstrument.from_matrix(jnp.stack(superop_matrices), instrument_dims, measured_qudits) + return cls(inst=inst, process=instrument) + + # ────────────────────────────────────────────── + # Dunder methods + # ────────────────────────────────────────────── + + def __str__(self) -> str: + """Return a simplified string representation.""" + return f"" + + def __eq__(self, other: object) -> bool: + """Check equality by instruction and exact instrument matrix (no tolerance).""" + if not isinstance(other, MeasurementChannel): + return False + if self.inst != other.inst: + return False + return bool(jnp.array_equal(self.process.matrix, other.process.matrix)) + + __hash__ = None # type: ignore[assignment] + + def __matmul__(self, other: MeasurementChannel) -> MeasurementChannel: + """Compose two measurement channels on the same qubit. + + Models sequential application: ``channel_B @ channel_A`` means + apply ``channel_A`` first, then ``channel_B``. + """ + if not isinstance(other, MeasurementChannel): + return NotImplemented + if self.inst != other.inst: + raise ValueError( + f"Cannot compose measurement channels for different qubits: {self.inst.out()} vs {other.inst.out()}" + ) + composed = self.process @ other.process + return replace(self, process=composed) + + def __pow__(self, power: float) -> MeasurementChannel: + r"""Raise the measurement's classification noise to a fractional ``power``. + + The confusion and transition matrices are column-stochastic, so a fractional power is + only well-defined through their *generator*: with :math:`M = e^{G}` (where :math:`G` has + zero column sums), the principled power is :math:`M^{p} = e^{p G}`, which is again + column-stochastic. This avoids the non-physical (e.g. negative) entries that a naive + ``fractional_matrix_power`` of a stochastic matrix can produce. + + So ``power = 0`` is an ideal (noiseless) measurement, ``1`` leaves it unchanged, and + ``> 1`` degrades it. Mirrors :meth:`Channel.__pow__` for sweeping readout-noise strength. + + :raises ValueError: If the matrix is not embeddable (no real generator) so that the + powered matrix is not a valid stochastic matrix. + """ + if not isinstance(power, (int, float)): + return NotImplemented + + def _powered_stochastic(matrix: Array) -> Array: + m = np.asarray(matrix, dtype=complex) + # Generator G = log(M); M**power = exp(power * G). Zero column sums of G are + # preserved under scaling, so exp(power * G) stays column-stochastic. + powered = scipy_expm(power * scipy_logm(m)) + if np.max(np.abs(powered.imag)) > 1e-9: + raise ValueError( + f"MeasurementChannel ** {power} has no real generator; the matrix is not " + "embeddable, so the powered measurement is not a valid stochastic matrix." + ) + powered = powered.real + if np.any(powered < -1e-9) or not np.allclose(powered.sum(axis=0), 1.0, atol=1e-6): + raise ValueError( + f"MeasurementChannel ** {power} is not a valid (non-negative, column-stochastic) " + "matrix; the underlying confusion/transition matrix is not embeddable for this power." + ) + # Clip away sub-tolerance numerical negatives validated above. + return jnp.asarray(np.clip(powered, 0.0, None)) + + confusion = _powered_stochastic(self.confusion_matrix) + transition = _powered_stochastic(self.transition_matrix) + return MeasurementChannel.from_confusion_and_transition(self.inst, confusion, transition) + + def __or__(self, other: Channel | MeasurementChannel) -> CycleChannel: + """Tensor product of two channels on disjoint qubits, producing a CycleChannel. + + :param other: Another Channel or MeasurementChannel on disjoint qubits. + :return: A CycleChannel representing the tensor product. + """ + if not isinstance(other, (Channel, MeasurementChannel)): + return NotImplemented + + self_qubits = set(self.qubits) + other_qubits = set(other.qubits) + if self_qubits & other_qubits: + raise ValueError(f"Cannot tensor channels with overlapping qubits: {self_qubits & other_qubits}") + + return _build_cycle_channel([self, other]) + + +@dataclass(frozen=True) +class ResetChannel: + """A reset noise channel attaches a superoperator to a specific reset operation. + + The ``process`` field is a ``qx.SuperOp`` which *includes* the ideal reset, so the channel + replaces the reset instruction rather than being applied after it. + """ + + inst: ResetQubit + """The reset operation to which the channel applies.""" + + process: qx.SuperOp + """A superoperator representation of the noisy reset (including ideal reset).""" + + def __post_init__(self) -> None: + """Validate that ResetChannel is attached to a targeted reset.""" + if not isinstance(self.inst, ResetQubit): + raise TypeError("ResetChannel only supports targeted ResetQubit instructions.") + + # ────────────────────────────────────────────── + # Constructors + # ────────────────────────────────────────────── + + @classmethod + def from_reset_fidelity( + cls: type[ResetChannel], + inst: ResetQubit, + fidelity: float, + dim: int = 2, + ) -> ResetChannel: + r"""Create a ResetChannel with depolarizing noise scaled to the given process fidelity. + + The ideal reset channel maps every state to :math:`|0\\rangle\\langle 0|`. Noise is + modelled as a depolarising channel applied after the ideal reset. + + :param inst: The reset instruction. + :param fidelity: Process fidelity of the reset channel, :math:`F \\in [0, 1]`. + 1.0 yields an ideal reset; values below 1 introduce depolarising noise. + :param dim: Hilbert-space dimension (2 for qubits). + :return: A ResetChannel instance. + """ + if not isinstance(inst, ResetQubit): + raise TypeError("ResetChannel only supports targeted ResetQubit instructions.") + + ideal_superop = qx.gates.RESET(dim=dim) + p = 1.0 - fidelity + d2 = dim * dim + # Depolarising channel in superop form: (1-p)*S_ideal + p*(I/d) for all inputs + # The completely depolarising superop maps everything to I/d: + # its rows are all zero except the diagonal entries corresponding to + # the trace extraction (maps vec(ρ) → vec(I/d) = vec(I)/d). + depol_superop_matrix = jnp.zeros((d2, d2), dtype=complex) + # vec(I/d) has value 1/d at positions 0, d+1, 2(d+1), ... i.e. diagonal entries + vec_identity_over_d = jnp.zeros(d2, dtype=complex) + for i in range(dim): + vec_identity_over_d = vec_identity_over_d.at[i * dim + i].set(1.0 / dim) + # The trace functional extracts sum of diagonal: positions 0, d+1, ... + trace_row = jnp.zeros(d2, dtype=complex) + for i in range(dim): + trace_row = trace_row.at[i * dim + i].set(1.0) + # Depolarising superop: each row of output is vec(I/d) * Tr(ρ) + depol_superop_matrix = jnp.outer(vec_identity_over_d, trace_row) + noisy_superop_matrix = (1.0 - p) * ideal_superop.matrix + p * depol_superop_matrix + noisy_superop = qx.SuperOp.from_matrix(noisy_superop_matrix, ideal_superop.dims) + return cls(inst=inst, process=noisy_superop) + + # ────────────────────────────────────────────── + # Properties + # ────────────────────────────────────────────── + + @cached_property + def qubits(self) -> list[int]: + """The qubit(s) that the reset applies to.""" + qubit = self.inst.qubit + if qubit is None: + return [] + return [qubit.index if hasattr(qubit, "index") else int(qubit)] + + @cached_property + def fidelity(self) -> float: + r"""Process fidelity of the reset channel relative to the ideal reset. + + Defined as :math:`F = \\mathrm{Tr}[\\Lambda_{\\mathrm{ideal}}^\\dagger \\Lambda] / d^2` + where :math:`\\Lambda` is the Choi matrix of the noisy channel and + :math:`\\Lambda_{\\mathrm{ideal}}` is the ideal-reset Choi. + """ + dim = self.process.dims[0][0] + ideal_choi = qx.to_choi(qx.gates.RESET(dim=dim)) + noisy_choi = qx.to_choi(self.process) + # Process fidelity = Tr[ideal_choi† @ noisy_choi] / d^2 + d2 = float(dim * dim) + return float(jnp.real(jnp.trace(ideal_choi.matrix.conj().T @ noisy_choi.matrix)) / d2) + + @cached_property + def noise_process(self) -> qx.SuperOp: + """The noise-only channel (ideal reset factored out). + + For a reset channel the noise framing is less natural than for unitary gates; + this property returns the full process superoperator. + """ + return self.process + + # ────────────────────────────────────────────── + # Visualization + # ────────────────────────────────────────────── + + def plot(self) -> Figure: + """Plot the Pauli transfer matrix of the reset channel. + + :return: A Plotly Figure. + """ + fig = qx.plot(self.process) + qubit_str = str(self.qubits[0]) if self.qubits else "?" + fig.update_layout(title=(f"Reset Channel RESET {qubit_str}
F_\u03c7={self.fidelity * 100:.2f}%")) + return fig + + # ────────────────────────────────────────────── + # Serialization + # ────────────────────────────────────────────── + + def to_json(self) -> str: + """Serialize ResetChannel to a JSON string. + + :return: JSON string representation. + """ + data = { + "schema_version": 1, + "inst": self.inst.out(), + "superop": _pack_operator(self.process), + } + return json.dumps(data) + + @classmethod + def from_json(cls: type[ResetChannel], json_str: str) -> ResetChannel: + """Deserialize a ResetChannel from a JSON string. + + :param json_str: JSON string as produced by :meth:`to_json`. + :return: ResetChannel instance. + """ + data = json.loads(json_str) + inst = _parse_quil_instruction(data["inst"]) + if not isinstance(inst, ResetQubit): + raise TypeError(f"ResetChannel JSON must contain a targeted reset instruction, got {type(inst).__name__}.") + superop_data = data["superop"] + shape = tuple(superop_data["shape"]) + arr = _unpack_complex_array(superop_data) + dims = _unpack_operator_dims(superop_data, shape, superoperator=True) + process = qx.SuperOp.from_matrix(arr, dims) + return cls(inst=inst, process=process) + + # ────────────────────────────────────────────── + # Dunder methods + # ────────────────────────────────────────────── + + def __str__(self) -> str: + """Return a simplified string representation.""" + qubit_str = str(self.qubits[0]) if self.qubits else "?" + return f"" + + def __eq__(self, other: object) -> bool: + """Check equality by instruction and exact process matrix (no tolerance).""" + if not isinstance(other, ResetChannel): + return False + if self.inst != other.inst: + return False + return bool(jnp.array_equal(self.process.matrix, other.process.matrix)) + + __hash__ = None # type: ignore[assignment] + + +@dataclass(frozen=True) +class CycleChannel: + """A cycle noise channel attaches superoperators to a specific cycle. + + Cycles can include gates and measurements. The constituent channels are stored + directly, allowing fidelity metrics and serialization to be derived from them. + """ + + inst: Gate + """The cycle to which the channel applies.""" + + defcircuit: DefCircuit + """The DefCircuit representing the logical cycle to which instruction represents.""" + + channels: tuple[Channel | MeasurementChannel, ...] + """Constituent channels (one per operation in the cycle) on disjoint qubits.""" + + def __post_init__(self) -> None: + """Validate that every instruction in the cycle body has a corresponding channel. + + Downstream consumers (the resolver, the stim converter) use only ``channels`` and + ignore ``defcircuit``; a missing channel would silently drop that operation's noise. + Operations are matched by identity (name, params, concrete qubits), independent of + the DefCircuit's formal-argument naming. + """ + if len(self.expanded_instructions) != len(self.channels): + raise ValueError( + "CycleChannel is incomplete: every instruction in the cycle's DefCircuit " + "body must have a corresponding channel. " + f"\nDefCircuit body: {self.expanded_instructions}" + f"\nChannels: {self.channels}" + ) + for instruction, channel in zip(self.expanded_instructions, self.channels, strict=True): + if str(instruction) != str(channel.inst): + raise ValueError( + "CycleChannel is incomplete: every instruction in the cycle's DefCircuit " + "body must have a corresponding channel. " + f"\nDefCircuit body: {instruction}" + f"\nChannels: {channel.inst}" + ) + + # ────────────────────────────────────────────── + # Derived properties + # ────────────────────────────────────────────── + + @cached_property + def expanded_instructions(self) -> list[Gate | Measurement | ResetQubit]: + """Return the expanded instructions of the defcircuit.""" + qarg_to_qubit = dict(zip(self.defcircuit.qubit_variables, self.inst.get_qubit_indices(), strict=False)) + instructions: list[Gate | Measurement | ResetQubit] = [] + for inst in self.defcircuit.instructions: + match inst: + case Measurement(): + instructions.append(Measurement(qubit=qarg_to_qubit[inst.qubit], classical_reg=inst.classical_reg)) # type: ignore[index] + case ResetQubit(): + instructions.append(ResetQubit(qarg_to_qubit[inst.qubit])) # type: ignore[index] + case Gate(): + instructions.append(Gate(inst.name, inst.params, [qarg_to_qubit[q] for q in inst.qubits])) # type: ignore[index] + case _: + raise TypeError(f"Unsupported instruction type in defcircuit: {type(inst).__name__}") + return instructions + + @cached_property + def operator(self) -> tuple[qx.SuperOp | qx.QuantumInstrument, ...]: + """Tuple of process superoperators, one per constituent channel.""" + return tuple(ch.process for ch in self.channels) + + @cached_property + def qubits(self) -> list[int]: + """All qubits in the cycle, derived from the instruction.""" + return self.inst.get_qubit_indices() + + @cached_property + def pauli_fidelity(self) -> float: + """Product of process (Pauli) fidelities over all gate channels in the cycle. + + Measurement channels do not contribute a gate fidelity and are skipped. + For near-ideal noise the product approximation is exact since constituent + channels act on disjoint subsystems. + """ + f = 1.0 + for ch in self.channels: + if isinstance(ch, Channel): + f *= ch.pauli_fidelity + return f + + @cached_property + def fidelity(self) -> float: + """Product of average gate fidelities over all gate channels in the cycle. + + Measurement channels do not contribute a gate fidelity and are skipped. + """ + f = 1.0 + for ch in self.channels: + if isinstance(ch, Channel): + f *= ch.fidelity + return f + + @cached_property + def infidelity(self) -> float: + """``1 - fidelity``.""" + return 1.0 - self.fidelity + + @cached_property + def pauli_infidelity(self) -> float: + """``1 - pauli_fidelity``.""" + return 1.0 - self.pauli_fidelity + + # ────────────────────────────────────────────── + # Serialization + # ────────────────────────────────────────────── + + def to_json(self) -> str: + """Serialize CycleChannel to a JSON string. + + :return: JSON string representation. + """ + ch_data = [] + for ch in self.channels: + ch_data.append({"type": type(ch).__name__, "data": ch.to_json()}) + data = { + "channels": ch_data, + } + return json.dumps(data) + + @classmethod + def from_json(cls: type[CycleChannel], json_str: str) -> CycleChannel: + """Deserialize a CycleChannel from a JSON string. + + The ``inst`` and ``defcircuit`` fields are reconstructed from the constituent + channels, consistent with how :func:`_build_cycle_channel` builds them. + + :param json_str: JSON string as produced by :meth:`to_json`. + :return: CycleChannel instance. + """ + data = json.loads(json_str) + _type_map: dict[str, type[Channel | MeasurementChannel]] = { + "Channel": Channel, + "MeasurementChannel": MeasurementChannel, + } + constituent_channels: list[Channel | MeasurementChannel] = [ + _type_map[ch_data["type"]].from_json(ch_data["data"]) for ch_data in data["channels"] + ] + return _build_cycle_channel(constituent_channels) + + # ────────────────────────────────────────────── + # Dunder methods + # ────────────────────────────────────────────── + + def __str__(self) -> str: + """Return a simplified string representation showing the gate and process fidelity.""" + return f"<{self.inst.out()} ~ ({100 * self.pauli_fidelity:.2f}%)>" + + def __eq__(self, other: object) -> bool: + """Check equality based on instruction and constituent channels.""" + if not isinstance(other, CycleChannel): + return False + if self.inst != other.inst: + return False + return self.channels == other.channels + + __hash__ = None # type: ignore[assignment] + + +def _channel_to_formal_inst(channel: Channel | MeasurementChannel) -> Gate | Measurement: + """Convert a channel's instruction to use formal arguments for DefCircuit.""" + if isinstance(channel, Channel): + inst = channel.inst + return Gate( + name=inst.name, + params=inst.params, + qubits=[FormalArgument(f"q{q}") for q in inst.get_qubit_indices()], + modifiers=inst.modifiers, # type: ignore[arg-type] + ) + elif isinstance(channel, MeasurementChannel): + qubit_idx = channel.qubits[0] + return Measurement( + qubit=FormalArgument(f"q{qubit_idx}"), + classical_reg=None, + ) + raise TypeError(f"Unsupported channel type: {type(channel)}") + + +def _build_cycle_channel( + channels: list[Channel | MeasurementChannel], +) -> CycleChannel: + """Build a CycleChannel from a list of Channel/MeasurementChannel on disjoint qubits.""" + all_qubits = sorted(q for ch in channels for q in ch.qubits) + cycle_name = "CYCLE" + formal_insts = [_channel_to_formal_inst(ch) for ch in channels] + + defcircuit = DefCircuit( + name=cycle_name, + parameters=[], + qubits=[FormalArgument(f"q{q}") for q in all_qubits], + instructions=list(formal_insts), + ) + inst = Gate(name=cycle_name, params=[], qubits=all_qubits) + return CycleChannel(inst=inst, defcircuit=defcircuit, channels=tuple(channels)) diff --git a/pyquil/noise.py b/pyquil/noise/_legacy_noise.py similarity index 97% rename from pyquil/noise.py rename to pyquil/noise/_legacy_noise.py index d794c4f78..730a0b8a0 100644 --- a/pyquil/noise.py +++ b/pyquil/noise/_legacy_noise.py @@ -18,9 +18,10 @@ import sys from collections import namedtuple from collections.abc import Iterable, Sequence -from typing import TYPE_CHECKING, Any, Optional, Union, cast +from typing import TYPE_CHECKING, Any, Optional, cast import numpy as np +from deprecated import deprecated from pyquil.external.rpcq import CompilerISA from pyquil.gates import MEASURE, RX, I @@ -41,6 +42,9 @@ class KrausModel(_KrausModel): """Encapsulate a single gate's noise model. + .. deprecated:: + Use :class:`pyquil.noise.Channel` for quax-based noise modeling. + :ivar str gate: The name of the gate. :ivar Sequence[float] params: Optional parameters for the gate. :ivar Sequence[int] targets: The target qubit ids. @@ -50,7 +54,7 @@ class KrausModel(_KrausModel): """ @staticmethod - def unpack_kraus_matrix(m: Union[list[Any], np.ndarray]) -> np.ndarray: + def unpack_kraus_matrix(m: list[Any] | np.ndarray) -> np.ndarray: """Unpack a JSON compatible representation of a complex Kraus matrix. :param m: The representation of a Kraus operator. Either a complex @@ -117,6 +121,11 @@ def __eq__(self, other: object) -> bool: _NoiseModel = namedtuple("_NoiseModel", ["gates", "assignment_probs"]) +@deprecated( + version="4.17.0", + reason="Use the quax-based noise model in pyquil.noise._noise_model instead. " + "This class will be removed in the next major version of pyquil.", +) class NoiseModel(_NoiseModel): """Encapsulate the QPU noise model containing information about the noisy gates. @@ -208,9 +217,7 @@ def _create_kraus_pragmas(name: str, qubit_indices: Sequence[int], kraus_ops: Se return pragmas -def append_kraus_to_gate( - kraus_ops: Sequence[np.ndarray], gate_matrix: np.ndarray -) -> list[Union[np.number, np.ndarray]]: +def append_kraus_to_gate(kraus_ops: Sequence[np.ndarray], gate_matrix: np.ndarray) -> list[np.number | np.ndarray]: """Follow a gate ``gate_matrix`` by a Kraus map described by ``kraus_ops``. :param kraus_ops: The Kraus operators. @@ -256,7 +263,7 @@ def pauli_kraus_map(probabilities: Sequence[float]) -> list[np.ndarray]: else: operators = np.kron(paulis, paulis) # type: ignore - return [coeff * op for coeff, op in zip(np.sqrt(probabilities), operators)] + return [coeff * op for coeff, op in zip(np.sqrt(probabilities), operators, strict=False)] def damping_kraus_map(p: float = 0.10) -> list[np.ndarray]: @@ -374,7 +381,7 @@ def get_noisy_gate(gate_name: str, params: Iterable[ParameterDesignator]) -> tup return np.diag([1, 1, 1, -1]), "NOISY-CZ" raise NoisyGateUndefined( - f"Undefined gate and params: {gate_name}{params}\n" "Please restrict yourself to I, RX(+/-pi), RX(+/-pi/2), CZ" + f"Undefined gate and params: {gate_name}{params}\nPlease restrict yourself to I, RX(+/-pi), RX(+/-pi/2), CZ" ) @@ -389,11 +396,11 @@ def _get_program_gates(prog: "Program") -> list[Gate]: def _decoherence_noise_model( gates: Sequence[Gate], - T1: Union[dict[int, float], float] = 30e-6, - T2: Union[dict[int, float], float] = 30e-6, + T1: dict[int, float] | float = 30e-6, + T2: dict[int, float] | float = 30e-6, gate_time_1q: float = 50e-9, gate_time_2q: float = 150e-09, - ro_fidelity: Union[dict[int, float], float] = 0.95, + ro_fidelity: dict[int, float] | float = 0.95, ) -> NoiseModel: """Return default noise model. @@ -557,11 +564,11 @@ def apply_noise_model(prog: "Program", noise_model: NoiseModel) -> "Program": def add_decoherence_noise( prog: "Program", - T1: Union[dict[int, float], float] = 30e-6, - T2: Union[dict[int, float], float] = 30e-6, + T1: dict[int, float] | float = 30e-6, + T2: dict[int, float] | float = 30e-6, gate_time_1q: float = 50e-9, gate_time_2q: float = 150e-09, - ro_fidelity: Union[dict[int, float], float] = 0.95, + ro_fidelity: dict[int, float] | float = 0.95, ) -> "Program": """Add generic damping and dephasing noise to a program. diff --git a/pyquil/noise/_noise_model.py b/pyquil/noise/_noise_model.py new file mode 100644 index 000000000..5bd1682ae --- /dev/null +++ b/pyquil/noise/_noise_model.py @@ -0,0 +1,489 @@ +############################################################################## +# Copyright 2016-2026 Rigetti Computing +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +############################################################################## +"""Noise model container and program-level fidelity estimation. + +This module defines: + +- ``NoiseModelLike``: A ``typing.Protocol`` defining the interface that all noise + models must satisfy (a single ``get_channel`` method). +- ``NoiseModel``: The primary concrete implementation — a frozen dataclass that + collects per-instruction noise channels. +- ``DepolarizingNoiseModel``: A convenience implementation that returns a + depolarizing channel for any gate. +- ``CompositeNoiseModel``: Chains multiple noise models, returning the first + non-None channel. +- Program-level fidelity estimation utilities. +""" + +from __future__ import annotations + +import json +import logging +from collections.abc import Iterable, Mapping, Sequence +from dataclasses import dataclass +from functools import reduce +from operator import mul +from types import MappingProxyType +from typing import ( + TYPE_CHECKING, + Protocol, + overload, + runtime_checkable, +) + +from pyquil.external.rpcq import CompilerISA +from pyquil.noise._channels import Channel, CycleChannel, MeasurementChannel, ResetChannel +from pyquil.quilbase import Gate, Measurement, ResetQubit + +if TYPE_CHECKING: + from pyquil import Program + from pyquil.paulis import PauliSum, PauliTerm + +logger = logging.getLogger(__name__) + +# ────────────────────────────────────────────────────────── +# Protocol +# ────────────────────────────────────────────────────────── + +# Channel union type returned by get_channel +ChannelType = Channel | MeasurementChannel | ResetChannel | CycleChannel +NoiseInstruction = Gate | Measurement | ResetQubit + + +@runtime_checkable +class NoiseModelLike(Protocol): + """Protocol defining the noise model interface. + + Any object that implements ``get_channel`` with the correct signature can be + used wherever a noise model is expected. This enables alternative noise model + strategies (depolarizing, dynamic, crosstalk-aware) without modifying + consumers. + + The standard concrete implementation is :class:`NoiseModel`. + """ + + @overload + def get_channel(self, inst: Gate) -> Channel | CycleChannel | None: ... + + @overload + def get_channel(self, inst: Measurement) -> MeasurementChannel | None: ... + + @overload + def get_channel(self, inst: ResetQubit) -> ResetChannel | None: ... + + def get_channel(self, inst: Gate | Measurement | ResetQubit) -> ChannelType | None: + """Retrieve the noise channel for a specific instruction. + + :param inst: A gate, measurement, or reset instruction. + :return: The associated noise channel, or ``None`` if the instruction + should be treated as ideal (noiseless). + """ + ... + + +@dataclass(frozen=True) +class NoiseModel: + """A noise model collects all the noise channels for a given quantum program. + + This includes gate channels, measurement channels, reset channels, and cycle channels. + + The constructor accepts a mapping from instruction to channel. Use + :meth:`from_channels` when constructing from a list, tuple, set, generator, + or other channel iterable. + """ + + channels: Mapping[NoiseInstruction, ChannelType] + """Immutable mapping from instruction to its noise channel.""" + + def __init__( + self, + channels: Mapping[NoiseInstruction, ChannelType] | None = None, + ) -> None: + if channels is None: + channels = {} + if not isinstance(channels, Mapping): + raise TypeError("NoiseModel channels must be a mapping. Use NoiseModel.from_channels(...) for iterables.") + + channel_map: dict[NoiseInstruction, ChannelType] = {} + for inst, channel in channels.items(): + if channel.inst != inst: + raise ValueError( + f"NoiseModel channel key {inst!r} does not match channel instruction {channel.inst!r}." + ) + channel_map[inst] = channel + object.__setattr__(self, "channels", MappingProxyType(channel_map)) + + def __getstate__(self) -> dict[str, dict[NoiseInstruction, ChannelType]]: + # ``channels`` is a MappingProxyType (unpicklable); serialize it as a plain dict. + return {"channels": dict(self.channels)} + + def __setstate__(self, state: Mapping[str, Mapping[NoiseInstruction, ChannelType]]) -> None: + object.__setattr__(self, "channels", MappingProxyType(dict(state["channels"]))) + + @overload + def get_channel(self, inst: Gate) -> Channel | CycleChannel | None: ... + + @overload + def get_channel(self, inst: Measurement) -> MeasurementChannel | None: ... + + @overload + def get_channel(self, inst: ResetQubit) -> ResetChannel | None: ... + + def get_channel( + self, inst: Gate | Measurement | ResetQubit + ) -> Channel | MeasurementChannel | ResetChannel | CycleChannel | None: + """Retrieve the noise channel associated with a specific instruction. + + :param inst: The instruction (gate, measurement, or reset) for which to retrieve the noise channel. + :return: The noise channel associated with the instruction, or None if no channel is found. + """ + return self.channels.get(inst) + + # ────────────────────────────────────────────── + # Constructors + # ────────────────────────────────────────────── + + @classmethod + def from_channels(cls: type[NoiseModel], channels: Iterable[ChannelType] = ()) -> NoiseModel: + """Create a noise model from an iterable of channels. + + :param channels: Noise channels to include in the model. + :return: A NoiseModel keyed by each channel's instruction. + :raises ValueError: If more than one channel targets the same instruction. + """ + channel_map: dict[NoiseInstruction, ChannelType] = {} + for channel in channels: + if channel.inst in channel_map: + raise ValueError(f"Duplicate noise channel for instruction {channel.inst!r}.") + channel_map[channel.inst] = channel + return cls(channels=channel_map) + + @classmethod + def from_isa(cls: type[NoiseModel], compiler_isa: CompilerISA) -> NoiseModel: + """Create a noise model from an instruction set architecture. + + Gate fidelities are converted to depolarizing channels and measurement + errors are symmetric. Only gates with concrete numeric parameters are + included. + + .. note:: + Two-qubit gate channels are keyed by the ISA edge's operand order + (e.g. ``CZ 0 1``). A program that issues the gate with the operands + reversed (``CZ 1 0``) will not match this channel, even for symmetric + gates. Issue gates in the ISA operand order, or build channels + explicitly for both orderings. + + :param compiler_isa: The compiler ISA. + :return: A NoiseModel with channels according to the provided fidelities. + """ + from pyquil.external.rpcq import GateInfo, MeasureInfo + from pyquil.quilatom import Qubit as QuilQubit + + channels: dict[NoiseInstruction, ChannelType] = {} + seen_measure_qubits: set[int] = set() + + for qubit_label, qubit in compiler_isa.qubits.items(): + for op_info in qubit.gates: + if isinstance(op_info, GateInfo): + qubits = [int(qubit_label)] + gate_name = op_info.operator + fidelity = op_info.fidelity + params = op_info.parameters + + if gate_name is None: + continue + # Skip gates with non-numeric parameters + if not all(isinstance(p, (float, int, complex)) for p in params): + continue + + numeric_params: list[float] = [float(p) for p in params if isinstance(p, (float, int, complex))] + inst = Gate(name=gate_name, params=numeric_params, qubits=qubits) + if fidelity is not None and fidelity < 1.0: + channels[inst] = Channel.from_gate_fidelity(inst=inst, fidelity=fidelity) + + elif isinstance(op_info, MeasureInfo): + if op_info.qubit is None: + continue + # Use qubit_label from the enclosing section when qubit is a wildcard + qubit_str = op_info.qubit if op_info.qubit != "_" else qubit_label + try: + qubit_idx = int(qubit_str) + except (ValueError, TypeError): + continue + fidelity = op_info.fidelity + if qubit_idx in seen_measure_qubits: + continue + if fidelity is None: + # Don't mark the qubit seen on a fidelity-less entry; a later + # MeasureInfo for the same qubit may carry a usable fidelity. + continue + seen_measure_qubits.add(qubit_idx) + m_inst = Measurement(qubit=QuilQubit(qubit_idx), classical_reg=None) + channels[m_inst] = MeasurementChannel.from_readout_fidelity(inst=m_inst, fidelity=fidelity) + + for edge_label, edge in compiler_isa.edges.items(): + for op_info in edge.gates: + if isinstance(op_info, GateInfo): + qubits = [int(q) for q in edge_label.split("-")] + gate_name = op_info.operator + fidelity = op_info.fidelity + params = op_info.parameters + + if gate_name is None: + continue + if not all(isinstance(p, (float, int, complex)) for p in params): + continue + + numeric_params = [float(p) for p in params if isinstance(p, (float, int, complex))] + inst = Gate(name=gate_name, params=numeric_params, qubits=qubits) + if fidelity is not None and fidelity < 1.0: + channels[inst] = Channel.from_gate_fidelity(inst=inst, fidelity=fidelity) + + return cls(channels=channels) + + # ────────────────────────────────────────────── + # Serialization + # ────────────────────────────────────────────── + + def to_json(self) -> str: + """Serialize NoiseModel to a JSON string. + + :return: JSON string representation. + """ + channel_data = [] + for ch in self.channels.values(): + if isinstance(ch, (Channel, MeasurementChannel, ResetChannel, CycleChannel)): + channel_data.append({"type": type(ch).__name__, "data": ch.to_json()}) + else: + logger.warning(f"Skipping serialization of {type(ch).__name__} (not yet supported).") + return json.dumps({"channels": channel_data}) + + @classmethod + def from_json(cls: type[NoiseModel], json_str: str) -> NoiseModel: + """Deserialize a NoiseModel from a JSON string. + + :param json_str: JSON string as produced by :meth:`to_json`. + :return: NoiseModel instance. + """ + data = json.loads(json_str) + _type_map = { + "Channel": Channel, + "MeasurementChannel": MeasurementChannel, + "ResetChannel": ResetChannel, + "CycleChannel": CycleChannel, + } + channels: list[Channel | MeasurementChannel | ResetChannel | CycleChannel] = [] + for ch_data in data["channels"]: + ch_cls = _type_map.get(ch_data["type"]) + if ch_cls is None: + raise ValueError(f"Unknown channel type: {ch_data['type']}") + channels.append(ch_cls.from_json(ch_data["data"])) # type: ignore[attr-defined] + return cls.from_channels(channels) + + # ────────────────────────────────────────────── + # Dunder methods + # ────────────────────────────────────────────── + + def __eq__(self, other: object) -> bool: + """Check if two NoiseModels contain equivalent channel maps.""" + if not isinstance(other, NoiseModel): + return False + return dict(self.channels) == dict(other.channels) + + # Unhashable: its channels hold jax arrays and are themselves unhashable, and an + # ``id``-based hash would be inconsistent with the value-based ``__eq__``. + __hash__ = None # type: ignore[assignment] + + def __add__(self, other: NoiseModel) -> NoiseModel: + """Combine two NoiseModels into their disjoint union. + + The two models must not both define a channel for the same instruction. + Addition is a union of channels, not a composition: overlapping channels are + a conflict, not an "addition". Compose channels explicitly + (``channel_a @ channel_b``) if that is what you intend. + + :raises ValueError: If both models define a channel for the same instruction. + """ + if not isinstance(other, NoiseModel): + return NotImplemented + + overlap = set(self.channels) & set(other.channels) + if overlap: + insts = ", ".join(repr(inst) for inst in overlap) + raise ValueError( + f"Cannot add NoiseModels: both define a channel for the same instruction(s): {insts}. " + "Addition is a disjoint union; compose the channels explicitly if that is intended." + ) + + return NoiseModel(channels={**self.channels, **other.channels}) + + def with_channels(self, channels: Iterable[ChannelType]) -> NoiseModel: + """Return a new model with additional channels. + + :param channels: New channels to add. + :return: A NoiseModel containing the existing and new channels. + :raises ValueError: If a new channel targets an instruction already in the model. + """ + combined = dict(self.channels) + for channel in channels: + if channel.inst in combined: + raise ValueError(f"Duplicate noise channel for instruction {channel.inst!r}.") + combined[channel.inst] = channel + return NoiseModel(channels=combined) + + +# ────────────────────────────────────────────────────────── +# Convenience NoiseModelLike implementations +# ────────────────────────────────────────────────────────── + + +@dataclass(frozen=True) +class DepolarizingNoiseModel: + r"""A noise model that applies uniform depolarizing noise to every gate. + + For any ``Gate`` instruction, returns a :class:`Channel` with the specified + depolarizing constant. Measurements and resets are treated as ideal. + + :param depolarizing_constant: The depolarization constant :math:`p` where + :math:`\\mathcal{D}_p(\\rho) = p \\, \\rho + (1-p) \\, I/d`. + A value of 1.0 means no noise; 0.0 means full depolarization. + """ + + depolarizing_constant: float + + @overload + def get_channel(self, inst: Gate) -> Channel | CycleChannel | None: ... + + @overload + def get_channel(self, inst: Measurement) -> MeasurementChannel | None: ... + + @overload + def get_channel(self, inst: ResetQubit) -> ResetChannel | None: ... + + def get_channel(self, inst: Gate | Measurement | ResetQubit) -> ChannelType | None: + """Return a depolarizing channel for gates; ``None`` for measurements/resets.""" + if isinstance(inst, Gate): + return Channel.from_depolarizing_constant(inst, self.depolarizing_constant) + return None + + +@dataclass(frozen=True) +class CompositeNoiseModel: + """A noise model that chains multiple models, returning the first non-None channel. + + Models are queried in order. The first model that returns a non-None channel + for a given instruction wins. + + :param models: Sequence of noise models to query in priority order. + """ + + models: tuple[NoiseModelLike, ...] + + def __init__(self, models: Sequence[NoiseModelLike]) -> None: + object.__setattr__(self, "models", tuple(models)) + + @overload + def get_channel(self, inst: Gate) -> Channel | CycleChannel | None: ... + + @overload + def get_channel(self, inst: Measurement) -> MeasurementChannel | None: ... + + @overload + def get_channel(self, inst: ResetQubit) -> ResetChannel | None: ... + + def get_channel(self, inst: Gate | Measurement | ResetQubit) -> ChannelType | None: + """Query each model in order, returning the first non-None result.""" + for model in self.models: + channel = model.get_channel(inst) + if channel is not None: + return channel + return None + + +# ────────────────────────────────────────────────────────── +# Program-level fidelity estimation +# ────────────────────────────────────────────────────────── + + +def estimate_program_fidelity(program: Program, noise_model: NoiseModelLike) -> float: + """Estimate the program fidelity for a given noise model. + + Works by multiplying the gate process fidelities together. Readout noise + is not considered. + + :param program: The program of interest. + :param noise_model: A noise model. + :return: The estimated process fidelity. + """ + gate_fidelities = [1.0] + for inst in program.instructions: + if isinstance(inst, Gate): + channel = noise_model.get_channel(inst) + if isinstance(channel, (Channel, CycleChannel)): + gate_fidelities.append(channel.pauli_fidelity) + + return reduce(mul, gate_fidelities) + + +def _light_cone_program(program: Program, qubits: list[int]) -> Program: + """Return a sub-program containing only gates in the backward light cone of *qubits*. + + Walks backward through the program's gate instructions. Any gate that + acts on a qubit currently in the light-cone set is included, and all of + its qubits are added to the set (because earlier gates on those qubits + are now causally relevant). + """ + from pyquil import Program as _Program + + gate_instructions = [inst for inst in program.instructions if isinstance(inst, Gate)] + relevant_qubits = set(qubits) + included: list[Gate] = [] + for inst in reversed(gate_instructions): + inst_qubits = {q.index for q in inst.qubits} # type: ignore[union-attr] + if inst_qubits & relevant_qubits: + included.append(inst) + relevant_qubits |= inst_qubits + reduced = _Program() + for inst in reversed(included): + reduced += inst + return reduced + + +def estimate_program_observable_fidelity( + program: Program, + noise_model: NoiseModelLike, + observable: PauliSum | PauliTerm, +) -> float: + """Estimate program fidelity restricted to the backward light cone of *observable*. + + Reduces the program to only the gates causally connected to the + observable qubits, then multiplies gate process fidelities together. + Readout noise is not considered. + + :param program: The program of interest. + :param noise_model: A noise model. + :param observable: A ``PauliTerm`` or ``PauliSum`` whose qubits define + the light cone. + :return: The estimated process fidelity for the light-cone-reduced program. + """ + from pyquil.paulis import PauliSum, PauliTerm + + if isinstance(observable, PauliTerm): + observable = PauliSum(terms=[observable]) + + qubits = [int(q) for term in observable.terms for q, _ in term.operations_as_set()] # type: ignore[arg-type] + reduced_program = _light_cone_program(program, qubits) + return estimate_program_fidelity(reduced_program, noise_model) diff --git a/pyquil/noise_gates.py b/pyquil/noise_gates.py index 7ba5db6e2..db699c740 100644 --- a/pyquil/noise_gates.py +++ b/pyquil/noise_gates.py @@ -1,7 +1,6 @@ """Utility functions for generating noise gates compatible with a QVM's instruction set architecture.""" import logging -from typing import Optional from pyquil.external.rpcq import CompilerISA, Edge, GateInfo, Supported1QGate, Supported2QGate from pyquil.quilatom import Parameter, unpack_qubit @@ -45,7 +44,7 @@ def _get_qvm_noise_supported_gates(isa: CompilerISA) -> list[Gate]: return gates -def _transform_rpcq_qubit_gate_info_to_qvm_noise_supported_gate(qubit_id: int, gate: GateInfo) -> Optional[Gate]: +def _transform_rpcq_qubit_gate_info_to_qvm_noise_supported_gate(qubit_id: int, gate: GateInfo) -> Gate | None: if gate.operator == Supported1QGate.RX: if len(gate.parameters) == 1 and gate.parameters[0] == 0.0: return None diff --git a/pyquil/operator_estimation.py b/pyquil/operator_estimation.py index 50a2e97da..ed2182374 100644 --- a/pyquil/operator_estimation.py +++ b/pyquil/operator_estimation.py @@ -1,10 +1,10 @@ """Tools for estimating the expectation value of operators on a quantum computer.""" import logging -from collections.abc import Generator, Mapping +from collections.abc import Callable, Generator, Mapping from math import pi from numbers import Complex -from typing import Callable, Optional, cast +from typing import cast import numpy as np @@ -187,8 +187,8 @@ def _generate_experiment_programs( def measure_observables( qc: QuantumComputer, tomo_experiment: Experiment, - progress_callback: Optional[Callable[[int, int], None]] = None, - calibrate_readout: Optional[str] = "plus-eig", + progress_callback: Callable[[int, int], None] | None = None, + calibrate_readout: str | None = "plus-eig", ) -> Generator[ExperimentResult, None, None]: """Measure all the observables in a TomographyExperiment. @@ -209,14 +209,12 @@ def measure_observables( # calibration readout only works with symmetrization turned on if calibrate_readout is not None and symmetrization != SymmetrizationLevel.EXHAUSTIVE: - raise ValueError( - "Readout calibration only currently works with exhaustive readout " "symmetrization turned on." - ) + raise ValueError("Readout calibration only currently works with exhaustive readout symmetrization turned on.") # generate programs for each group of simultaneous settings. programs, meas_qubits = _generate_experiment_programs(tomo_experiment, reset) - for i, (prog, qubits, settings) in enumerate(zip(programs, meas_qubits, tomo_experiment)): + for i, (prog, qubits, settings) in enumerate(zip(programs, meas_qubits, tomo_experiment, strict=False)): log.info(f"Collecting bitstrings for the {len(settings)} settings: {settings}") # we don't need to do any actual measurement if the combined operator is simply the diff --git a/pyquil/paulis.py b/pyquil/paulis.py index 78720a2ae..79f863a28 100644 --- a/pyquil/paulis.py +++ b/pyquil/paulis.py @@ -19,13 +19,11 @@ import re import warnings from collections import OrderedDict -from collections.abc import Hashable, Iterable, Iterator, Sequence +from collections.abc import Callable, Hashable, Iterable, Iterator, Sequence from functools import reduce from itertools import product from numbers import Complex, Number from typing import ( - Callable, - Optional, Union, cast, ) @@ -117,7 +115,7 @@ def __init__(self, *args: object, **kwargs: object): """ -def _valid_qubit(index: Optional[Union[PauliTargetDesignator, QubitPlaceholder]]) -> bool: +def _valid_qubit(index: PauliTargetDesignator | QubitPlaceholder | None) -> bool: return ( (isinstance(index, integer_types) and index >= 0) or isinstance(index, QubitPlaceholder) @@ -131,7 +129,7 @@ class PauliTerm: def __init__( self, op: str, - index: Optional[PauliTargetDesignator], + index: PauliTargetDesignator | None, coefficient: ExpressionDesignator = 1.0, ): """Create a new Pauli Term with a Pauli operator at a particular index and a leading coefficient. @@ -152,7 +150,7 @@ def __init__( self._ops[index] = op if isinstance(coefficient, Number): - self.coefficient: Union[complex, Expression] = complex(coefficient) + self.coefficient: complex | Expression = complex(coefficient) else: self.coefficient = coefficient @@ -286,7 +284,7 @@ def _multiply_factor(self, factor: str, index: PauliTargetDesignator) -> "PauliT return new_term - def __mul__(self, term: Union[PauliDesignator, ExpressionDesignator]) -> PauliDesignator: + def __mul__(self, term: PauliDesignator | ExpressionDesignator) -> PauliDesignator: """Multiply this Pauli Term with another PauliTerm, PauliSum, or number according to the Pauli algebra rules. :param term: (PauliTerm or PauliSum or Number) A term to multiply by. @@ -333,7 +331,7 @@ def __pow__(self, power: int) -> "PauliTerm": result = cast(PauliTerm, result * self) return result - def __add__(self, other: Union[PauliDesignator, ExpressionDesignator]) -> "PauliSum": + def __add__(self, other: PauliDesignator | ExpressionDesignator) -> "PauliSum": """Add this PauliTerm with another one. :param other: A PauliTerm object, a PauliSum object, or a Number @@ -405,9 +403,7 @@ def from_list( :return: PauliTerm """ if not all([isinstance(op, tuple) for op in terms_list]): - raise TypeError( - "The type of terms_list should be a list of (name, index) " "tuples suitable for PauliTerm()." - ) + raise TypeError("The type of terms_list should be a list of (name, index) tuples suitable for PauliTerm().") pterm = PauliTerm("I", 0) if not all([op[0] in PAULI_OPS for op in terms_list]): @@ -450,7 +446,7 @@ def from_compact_str(cls, str_pauli_term: str) -> "PauliTerm": # parse the coefficient into either a float or complex str_coef = str_coef.replace(" ", "") try: - coef: Union[float, complex] = float(str_coef) + coef: float | complex = float(str_coef) except ValueError: try: coef = complex(str_coef) @@ -475,7 +471,7 @@ def from_compact_str(cls, str_pauli_term: str) -> "PauliTerm": raise ValueError(f"Expected operation to be PauliTerm, got {type(op)}.") return op - def pauli_string(self, qubits: Optional[Iterable[int]] = None) -> str: + def pauli_string(self, qubits: Iterable[int] | None = None) -> str: """Return a string representation of this PauliTerm without its coefficient and with implicit qubit indices. If an iterable of qubits is provided, each character in the resulting string represents @@ -508,7 +504,7 @@ def ZERO() -> PauliTerm: return PauliTerm("I", 0, 0) -def sI(q: Optional[int] = None) -> PauliTerm: +def sI(q: int | None = None) -> PauliTerm: """Return the identity operator, optionally on a particular qubit. This can be specified without a qubit. @@ -581,7 +577,7 @@ def __init__(self, terms: Sequence[PauliTerm]): def _from_rs_pauli_sum(cls, pauli_sum: quil_rs.PauliSum) -> "PauliSum": return cls([PauliTerm._from_rs_pauli_term(term) for term in pauli_sum.terms]) - def _to_rs_pauli_sum(self, arguments: Optional[list[PauliTargetDesignator]] = None) -> quil_rs.PauliSum: + def _to_rs_pauli_sum(self, arguments: list[PauliTargetDesignator] | None = None) -> quil_rs.PauliSum: rs_arguments: list[str] if arguments is None: argument_set: dict[str, None] = {} @@ -632,7 +628,7 @@ def __iter__(self) -> Iterator[PauliTerm]: """Iterate over the PauliTerms in the sum.""" return self.terms.__iter__() - def __mul__(self, other: Union[PauliDesignator, ExpressionDesignator]) -> "PauliSum": + def __mul__(self, other: PauliDesignator | ExpressionDesignator) -> "PauliSum": """Multiply and simplify this PauliSum with another PauliSum, PauliTerm or Number object. :param other: a PauliSum, PauliTerm or Number object @@ -641,7 +637,7 @@ def __mul__(self, other: Union[PauliDesignator, ExpressionDesignator]) -> "Pauli if not isinstance(other, (Expression, Number, PauliTerm, PauliSum)): raise ValueError("Cannot multiply PauliSum by term that is not a Number, PauliTerm, or PauliSum") - other_terms: list[Union[PauliTerm, ExpressionDesignator]] = [] + other_terms: list[PauliTerm | ExpressionDesignator] = [] if isinstance(other, PauliSum): other_terms += other.terms else: @@ -688,7 +684,7 @@ def __pow__(self, power: int) -> "PauliSum": result *= self return result - def __add__(self, other: Union[PauliDesignator, ExpressionDesignator]) -> "PauliSum": + def __add__(self, other: PauliDesignator | ExpressionDesignator) -> "PauliSum": """Add and simplify this PauliSum with another PauliSum, PauliTerm or Number objects. :param other: a PauliSum, PauliTerm or Number object @@ -717,7 +713,7 @@ def __radd__(self, other: ExpressionDesignator) -> "PauliSum": raise TypeError(f"Expected a Number object, got {type(other)}") return self + other - def __sub__(self, other: Union[PauliDesignator, ExpressionDesignator]) -> "PauliSum": + def __sub__(self, other: PauliDesignator | ExpressionDesignator) -> "PauliSum": """Subtract and simplify this PauliSum with another PauliSum, PauliTerm or Number object. :param other: a PauliSum, PauliTerm or Number object @@ -725,7 +721,7 @@ def __sub__(self, other: Union[PauliDesignator, ExpressionDesignator]) -> "Pauli """ return self + -1.0 * other - def __rsub__(self, other: Union[PauliDesignator, ExpressionDesignator]) -> "PauliSum": + def __rsub__(self, other: PauliDesignator | ExpressionDesignator) -> "PauliSum": """Subtract and simplify this PauliSum with another PauliSum, PauliTerm or Number object. :param other: a PauliSum, PauliTerm or Number object @@ -935,7 +931,7 @@ def combined_exp_wrap(param: float) -> Program: def exponentiate_pauli_sum( - pauli_sum: Union[PauliSum, PauliTerm], + pauli_sum: PauliSum | PauliTerm, ) -> NDArray[np.complex128]: r"""Exponentiate a sequence of PauliTerms, which may or may not commute. diff --git a/pyquil/pyqvm.py b/pyquil/pyqvm.py index d0e59a5b9..0f9e5c236 100644 --- a/pyquil/pyqvm.py +++ b/pyquil/pyqvm.py @@ -18,7 +18,7 @@ import logging from abc import ABC, abstractmethod from collections.abc import Iterable, Sequence -from typing import Any, Optional, Union +from typing import Any import numpy as np from numpy.random.mtrand import RandomState @@ -71,7 +71,7 @@ class AbstractQuantumSimulator(ABC): """An abstract interface for a quantum simulator.""" @abstractmethod - def __init__(self, n_qubits: int, rs: Optional[RandomState]): + def __init__(self, n_qubits: int, rs: RandomState | None): """Initialize. :param n_qubits: Number of qubits to simulate. @@ -115,7 +115,7 @@ def do_measurement(self, qubit: int) -> int: """ @abstractmethod - def expectation(self, operator: Union[PauliTerm, PauliSum]) -> complex: + def expectation(self, operator: PauliTerm | PauliSum) -> complex: """Compute the expectation of an operator. :param operator: The operator @@ -156,9 +156,9 @@ class PyQVM(QAM["PyQVM"]): def __init__( self, n_qubits: int, - quantum_simulator_type: Optional[type[AbstractQuantumSimulator]] = None, - seed: Optional[int] = None, - post_gate_noise_probabilities: Optional[dict[str, float]] = None, + quantum_simulator_type: type[AbstractQuantumSimulator] | None = None, + seed: int | None = None, + post_gate_noise_probabilities: dict[str, float] | None = None, ): """PyQuil's built-in Quil virtual machine. @@ -189,19 +189,19 @@ def __init__( quantum_simulator_type = ReferenceDensitySimulator self.n_qubits = n_qubits - self.ram: dict[str, list[Union[float, int]]] = {} + self.ram: dict[str, list[float | int]] = {} if post_gate_noise_probabilities is None: post_gate_noise_probabilities = {} self.post_gate_noise_probabilities = post_gate_noise_probabilities - self.program: Optional[Program] = None + self.program: Program | None = None self.program_counter: int = 0 self.defined_gates: dict[str, np.ndarray] = dict() # private implementation details - self._qubit_to_ram: Optional[dict[int, int]] = None - self._ro_size: Optional[int] = None + self._qubit_to_ram: dict[int, int] | None = None + self._ro_size: int | None = None self._memory_results = {} # type: ignore self.rs = np.random.RandomState(seed=seed) @@ -227,7 +227,7 @@ def execute_with_memory_map_batch( "PyQVM does not support batch execution as the state of the instance is reset at the start of each execute." ) - def execute(self, executable: QuantumExecutable, memory_map: Optional[MemoryMap] = None, **__: Any) -> "PyQVM": + def execute(self, executable: QuantumExecutable, memory_map: MemoryMap | None = None, **__: Any) -> "PyQVM": """Execute a program on the PyQVM. Note that the state of the instance is reset on each call to ``execute``. @@ -287,7 +287,7 @@ def read_memory(self, *, region_name: str) -> np.ndarray: raise ValueError("No memory results available.") return np.asarray(self._memory_results[region_name]) - def find_label(self, label: Union[Label, LabelPlaceholder]) -> int: + def find_label(self, label: Label | LabelPlaceholder) -> int: """Iterate over the program and find a JumpTarget that has a Label matching the input label. :param label: Label object to search for in program @@ -332,7 +332,7 @@ def transition(self) -> bool: elif isinstance(instruction, Measurement): measured_val = self.wf_simulator.do_measurement(qubit=instruction.get_qubit_indices().pop()) - meas_reg: Optional[MemoryReference] = instruction.classical_reg + meas_reg: MemoryReference | None = instruction.classical_reg if meas_reg is None: raise ValueError("Measurement instruction must have a classical register.") self.ram[meas_reg.name][meas_reg.offset] = measured_val @@ -361,7 +361,7 @@ def transition(self) -> bool: elif isinstance(instruction, (JumpWhen, JumpUnless)): # JumpWhen/Unless; check classical reg - jump_reg: Optional[MemoryReference] = instruction.condition + jump_reg: MemoryReference | None = instruction.condition if jump_reg is None: raise ValueError("JumpWhen/Unless instruction must have a classical register.") cond = self.ram[jump_reg.name][jump_reg.offset] @@ -411,7 +411,7 @@ def transition(self) -> bool: if isinstance(instruction, ClassicalAnd): if not isinstance(left_val, int) or not isinstance(right_val, int): raise ValueError("AND requires a data type of INTEGER; not {type(left_val)} and {type(right_val)}") - new_val: Union[int, float] = left_val & right_val + new_val: int | float = left_val & right_val elif isinstance(instruction, ClassicalInclusiveOr): if not isinstance(left_val, int) or not isinstance(right_val, int): raise ValueError("OR requires a data type of INTEGER; not {type(left_val)} and {type(right_val)}") diff --git a/pyquil/quantum_processor/graph.py b/pyquil/quantum_processor/graph.py index d9eb49287..eff8546e2 100644 --- a/pyquil/quantum_processor/graph.py +++ b/pyquil/quantum_processor/graph.py @@ -1,6 +1,6 @@ """An implementation of an AbstractQuantumProcessor based on a NetworkX graph topology.""" -from typing import Any, Optional +from typing import Any import networkx as nx @@ -19,8 +19,8 @@ class NxQuantumProcessor(AbstractQuantumProcessor): def __init__( self, topology: nx.Graph, - gates_1q: Optional[list[str]] = None, - gates_2q: Optional[list[str]] = None, + gates_1q: list[str] | None = None, + gates_2q: list[str] | None = None, ) -> None: """Initialize a new NxQuantumProcessor. diff --git a/pyquil/quantum_processor/qcs.py b/pyquil/quantum_processor/qcs.py index f00101020..b23fe11b9 100644 --- a/pyquil/quantum_processor/qcs.py +++ b/pyquil/quantum_processor/qcs.py @@ -1,7 +1,5 @@ """An implementation of AbstractQuantumProcessor based on an InstructionSetArchitecture returned from the QCS API.""" -from typing import Optional - import networkx as nx from qcs_sdk import QCSClient from qcs_sdk.qpu.isa import InstructionSetArchitecture, get_instruction_set_architecture @@ -20,13 +18,13 @@ class QCSQuantumProcessor(AbstractQuantumProcessor): quantum_processor_id: str _isa: InstructionSetArchitecture - noise_model: Optional[NoiseModel] + noise_model: NoiseModel | None def __init__( self, quantum_processor_id: str, isa: InstructionSetArchitecture, - noise_model: Optional[NoiseModel] = None, + noise_model: NoiseModel | None = None, ): """Initialize a new QCSQuantumProcessor. @@ -61,7 +59,7 @@ def __repr__(self) -> str: def get_qcs_quantum_processor( quantum_processor_id: str, - client_configuration: Optional[QCSClient] = None, + client_configuration: QCSClient | None = None, timeout: float = 10.0, ) -> QCSQuantumProcessor: """Retrieve an instruction set architecture for the specified QPU ID and initialize a ``QCSQuantumProcessor`` with it. diff --git a/pyquil/quantum_processor/transformers/graph_to_compiler_isa.py b/pyquil/quantum_processor/transformers/graph_to_compiler_isa.py index ae0b1a31c..75ceabb04 100644 --- a/pyquil/quantum_processor/transformers/graph_to_compiler_isa.py +++ b/pyquil/quantum_processor/transformers/graph_to_compiler_isa.py @@ -1,6 +1,6 @@ """Transformers for converting between NetworkX graphs and CompilerISAs.""" -from typing import Optional, Union, cast +from typing import cast import networkx as nx import numpy as np @@ -28,7 +28,7 @@ def graph_to_compiler_isa( - graph: nx.Graph, gates_1q: Optional[list[str]] = None, gates_2q: Optional[list[str]] = None + graph: nx.Graph, gates_1q: list[str] | None = None, gates_2q: list[str] | None = None ) -> CompilerISA: """Generate an ``CompilerISA`` object from a NetworkX graph and list of 1Q and 2Q gates. @@ -108,17 +108,17 @@ def _make_wildcard_1q_gates() -> list[GateInfo]: def _transform_qubit_operation_to_gates( operation_name: str, -) -> list[Union[GateInfo, MeasureInfo]]: +) -> list[GateInfo | MeasureInfo]: if operation_name == Supported1QGate.I: - return cast(list[Union[GateInfo, MeasureInfo]], _make_i_gates()) + return cast(list[GateInfo | MeasureInfo], _make_i_gates()) elif operation_name == Supported1QGate.RX: - return cast(list[Union[GateInfo, MeasureInfo]], _make_rx_gates()) + return cast(list[GateInfo | MeasureInfo], _make_rx_gates()) elif operation_name == Supported1QGate.RZ: - return cast(list[Union[GateInfo, MeasureInfo]], _make_rz_gates()) + return cast(list[GateInfo | MeasureInfo], _make_rz_gates()) elif operation_name == Supported1QGate.MEASURE: - return cast(list[Union[GateInfo, MeasureInfo]], _make_measure_gates()) + return cast(list[GateInfo | MeasureInfo], _make_measure_gates()) elif operation_name == Supported1QGate.WILDCARD: - return cast(list[Union[GateInfo, MeasureInfo]], _make_wildcard_1q_gates()) + return cast(list[GateInfo | MeasureInfo], _make_wildcard_1q_gates()) else: raise GraphGateError(f"Unsupported graph qubit operation: {operation_name}") diff --git a/pyquil/quantum_processor/transformers/qcs_isa_to_compiler_isa.py b/pyquil/quantum_processor/transformers/qcs_isa_to_compiler_isa.py index c26b0c484..aca806322 100644 --- a/pyquil/quantum_processor/transformers/qcs_isa_to_compiler_isa.py +++ b/pyquil/quantum_processor/transformers/qcs_isa_to_compiler_isa.py @@ -2,7 +2,7 @@ from collections import defaultdict from collections.abc import Sequence -from typing import Optional, Union, cast +from typing import cast import numpy as np from qcs_sdk.qpu.isa import Characteristic, InstructionSetArchitecture, Operation @@ -46,13 +46,12 @@ def qcs_isa_to_compiler_isa(isa: InstructionSetArchitecture) -> CompilerISA: if operation.node_count == 1: if len(site.node_ids) != 1: raise QCSISAParseError( - f"operation {operation.name} has node count 1, but " f"site has {len(site.node_ids)} node_ids" + f"operation {operation.name} has node count 1, but site has {len(site.node_ids)} node_ids" ) operation_qubit = get_qubit(device, site.node_ids[0]) if operation_qubit is None: raise QCSISAParseError( - f"operation {operation.name} has node {site.node_ids[0]} " - "but node not declared in architecture" + f"operation {operation.name} has node {site.node_ids[0]} but node not declared in architecture" ) if operation.name in qubit_operations_seen[operation_qubit.id]: @@ -71,7 +70,7 @@ def qcs_isa_to_compiler_isa(isa: InstructionSetArchitecture) -> CompilerISA: elif operation.node_count == 2: if len(site.node_ids) != 2: QCSISAParseError( - f"operation {operation.name} has node count 2, but site " f"has {len(site.node_ids)} node_ids" + f"operation {operation.name} has node count 2, but site has {len(site.node_ids)} node_ids" ) operation_edge = get_edge(device, site.node_ids[0], site.node_ids[1]) @@ -191,7 +190,7 @@ def _make_rz_gates(node_id: int) -> list[GateInfo]: ] -def _get_frb_sim_1q(node_id: int, benchmarks: Sequence[Operation]) -> Optional[float]: +def _get_frb_sim_1q(node_id: int, benchmarks: Sequence[Operation]) -> float | None: frb_sim_1q = next( (benchmark for benchmark in benchmarks if benchmark.name == "randomized_benchmark_simultaneous_1q"), None ) @@ -231,15 +230,15 @@ def _transform_qubit_operation_to_gates( node_id: int, characteristics: Sequence[Characteristic], benchmarks: Sequence[Operation], -) -> list[Union[GateInfo, MeasureInfo]]: +) -> list[GateInfo | MeasureInfo]: if operation_name == Supported1QGate.RX: - return cast(list[Union[GateInfo, MeasureInfo]], _make_rx_gates(node_id, benchmarks)) + return cast(list[GateInfo | MeasureInfo], _make_rx_gates(node_id, benchmarks)) elif operation_name == Supported1QGate.RZ: - return cast(list[Union[GateInfo, MeasureInfo]], _make_rz_gates(node_id)) + return cast(list[GateInfo | MeasureInfo], _make_rz_gates(node_id)) elif operation_name == Supported1QGate.MEASURE: - return cast(list[Union[GateInfo, MeasureInfo]], _make_measure_gates(node_id, characteristics)) + return cast(list[GateInfo | MeasureInfo], _make_measure_gates(node_id, characteristics)) elif operation_name == Supported1QGate.WILDCARD: - return cast(list[Union[GateInfo, MeasureInfo]], _make_wildcard_1q_gates(node_id)) + return cast(list[GateInfo | MeasureInfo], _make_wildcard_1q_gates(node_id)) elif operation_name in {"I", "RESET"}: return [] else: diff --git a/pyquil/quil.py b/pyquil/quil.py index 43a5126f6..e74121939 100644 --- a/pyquil/quil.py +++ b/pyquil/quil.py @@ -19,11 +19,10 @@ import types import warnings from collections import defaultdict -from collections.abc import Generator, Iterable, Iterator, Sequence +from collections.abc import Callable, Generator, Iterable, Iterator, Sequence from copy import deepcopy from typing import ( Any, - Callable, Optional, TypeVar, Union, @@ -139,7 +138,7 @@ def __init__(self, *instructions: InstructionDesignator): # default number of shots to loop through self.num_shots = 1 - self.native_quil_metadata: Optional[NativeQuilMetadata] = None + self.native_quil_metadata: NativeQuilMetadata | None = None # The following properties are cached on the first call and won't be re-built unless cleared. # Any method that mutates the state program should use the `@_invalidates_cached_properties` @@ -224,7 +223,7 @@ def instructions(self, instructions: list[AbstractInstruction]) -> None: self._program = new_program._program @_invalidates_cached_properties - def inst(self, *instructions: Union[InstructionDesignator, RSProgram]) -> "Program": + def inst(self, *instructions: InstructionDesignator | RSProgram) -> "Program": """Mutates the Program object by appending new instructions. This function accepts a number of different valid forms, e.g. @@ -314,8 +313,8 @@ def with_loop( self, num_iterations: int, iteration_count_reference: MemoryReference, - start_label: Union[Label, LabelPlaceholder], - end_label: Union[Label, LabelPlaceholder], + start_label: Label | LabelPlaceholder, + end_label: Label | LabelPlaceholder, ) -> "Program": r"""Return a copy of the ``Program`` wrapped in a Quil loop that will execute ``num_iterations`` times. @@ -364,8 +363,8 @@ def resolve_placeholders(self) -> None: def resolve_placeholders_with_custom_resolvers( self, *, - label_resolver: Optional[Callable[[LabelPlaceholder], Optional[str]]] = None, - qubit_resolver: Optional[Callable[[QubitPlaceholder], Optional[int]]] = None, + label_resolver: Callable[[LabelPlaceholder], str | None] | None = None, + qubit_resolver: Callable[[QubitPlaceholder], int | None] | None = None, ) -> None: r"""Resolve ``LabelPlaceholder``\\s and ``QubitPlaceholder``\\s within the program using a function. @@ -380,13 +379,13 @@ def resolve_placeholders_with_custom_resolvers( rs_qubit_resolver = None if qubit_resolver is not None: - def rs_qubit_resolver(placeholder: quil_rs.QubitPlaceholder) -> Optional[int]: + def rs_qubit_resolver(placeholder: quil_rs.QubitPlaceholder) -> int | None: return qubit_resolver(QubitPlaceholder(placeholder=placeholder)) rs_label_resolver = None if label_resolver is not None: - def rs_label_resolver(placeholder: quil_rs.TargetPlaceholder) -> Optional[str]: + def rs_label_resolver(placeholder: quil_rs.TargetPlaceholder) -> str | None: return label_resolver(LabelPlaceholder(placeholder=placeholder)) self._program.resolve_placeholders_with_custom_resolvers( @@ -402,7 +401,7 @@ def resolve_qubit_placeholders(self) -> None: def resolve_qubit_placeholders_with_mapping(self, qubit_mapping: dict[QubitPlaceholder, int]) -> None: r"""Resolve all qubit placeholders using a mapping of ``QubitPlaceholder``\\s to the index they resolve to.""" - def qubit_resolver(placeholder: quil_rs.QubitPlaceholder) -> Optional[int]: + def qubit_resolver(placeholder: quil_rs.QubitPlaceholder) -> int | None: return qubit_mapping.get(QubitPlaceholder(placeholder), None) def label_resolver(_: quil_rs.TargetPlaceholder) -> None: @@ -502,7 +501,7 @@ def gate( self, name: str, params: Sequence[ParameterDesignator], - qubits: Sequence[Union[Qubit, QubitPlaceholder]], + qubits: Sequence[Qubit | QubitPlaceholder], ) -> "Program": """Add a gate to the program. @@ -523,8 +522,8 @@ def gate( def defgate( self, name: str, - matrix: Union[list[list[Any]], np.ndarray, np.matrix], - parameters: Optional[list[Parameter]] = None, + matrix: list[list[Any]] | np.ndarray | np.matrix, + parameters: list[Parameter] | None = None, ) -> "Program": """Define a new static gate. @@ -563,7 +562,7 @@ def define_noisy_gate(self, name: str, qubit_indices: Sequence[int], kraus_ops: _check_kraus_ops(len(qubit_indices), kraus_ops) return self.inst(_create_kraus_pragmas(name, tuple(qubit_indices), kraus_ops)) - def define_noisy_readout(self, qubit: Union[int], p00: float, p11: float) -> "Program": + def define_noisy_readout(self, qubit: int, p00: float, p11: float) -> "Program": """For this program define a classical bit flip readout error channel parametrized by ``p00`` and ``p11``. This models the effect of thermal noise that corrupts the readout signal **after** it has interrogated the @@ -598,7 +597,7 @@ def no_noise(self) -> "Program": """ return self.inst(Pragma("NO-NOISE")) - def measure(self, qubit: QubitDesignator, classical_reg: Optional[MemoryReferenceDesignator]) -> "Program": + def measure(self, qubit: QubitDesignator, classical_reg: MemoryReferenceDesignator | None) -> "Program": """Measures a qubit at qubit_index and puts the result in classical_reg. :param qubit: The qubit to measure. @@ -608,7 +607,7 @@ def measure(self, qubit: QubitDesignator, classical_reg: Optional[MemoryReferenc """ return self.inst(MEASURE(qubit, classical_reg)) - def reset(self, qubit_index: Optional[int] = None) -> "Program": + def reset(self, qubit_index: int | None = None) -> "Program": """Reset all qubits or just a specific qubit at qubit_index. :param qubit_index: The address of the qubit to reset. @@ -618,7 +617,7 @@ def reset(self, qubit_index: Optional[int] = None) -> "Program": """ return self.inst(RESET(qubit_index)) - def measure_all(self, *qubit_reg_pairs: tuple[QubitDesignator, Optional[MemoryReferenceDesignator]]) -> "Program": + def measure_all(self, *qubit_reg_pairs: tuple[QubitDesignator, MemoryReferenceDesignator | None]) -> "Program": """Measures many qubits into their specified classical bits, in the order they were entered. If no qubit/register pairs are provided, measure all qubits present in the program into classical addresses of @@ -738,8 +737,8 @@ def declare( name: str, memory_type: str = "BIT", memory_size: int = 1, - shared_region: Optional[str] = None, - offsets: Optional[Sequence[tuple[int, str]]] = None, + shared_region: str | None = None, + offsets: Sequence[tuple[int, str]] | None = None, ) -> MemoryReference: """DECLARE a quil variable. @@ -789,7 +788,7 @@ def wrap_in_numshots_loop(self, shots: int) -> "Program": self.num_shots = shots return self - def out(self, *, calibrations: Optional[bool] = True) -> str: + def out(self, *, calibrations: bool | None = True) -> str: """Serialize the Quil program to a string suitable for submitting to the QVM or QPU.""" if calibrations: return self._program.to_quil() @@ -802,7 +801,7 @@ def out(self, *, calibrations: Optional[bool] = True) -> str: version="4.0", reason="The indices flag will be removed. Use get_qubit_indices() instead.", ) - def get_qubits(self, indices: bool = True) -> Union[set[QubitDesignator], set[int]]: + def get_qubits(self, indices: bool = True) -> set[QubitDesignator] | set[int]: """Return all of the qubit indices used in this program, including gate applications and allocated qubits. For example: @@ -834,7 +833,7 @@ def get_qubit_indices(self) -> set[int]: """ return {q.to_fixed() for q in self._program.get_used_qubits()} - def match_calibrations(self, instr: AbstractInstruction) -> Optional[CalibrationMatch]: + def match_calibrations(self, instr: AbstractInstruction) -> CalibrationMatch | None: """Attempt to match a calibration to the provided instruction. Note: preference is given to later calibrations, i.e. in a program with @@ -866,7 +865,7 @@ def match_calibrations(self, instr: AbstractInstruction) -> Optional[Calibration return None - def get_calibration(self, instr: AbstractInstruction) -> Optional[Union[DefCalibration, DefMeasureCalibration]]: + def get_calibration(self, instr: AbstractInstruction) -> DefCalibration | DefMeasureCalibration | None: """Get the calibration corresponding to the provided instruction. :param instr: An instruction. @@ -881,7 +880,7 @@ def get_calibration(self, instr: AbstractInstruction) -> Optional[Union[DefCalib def calibrate( self, instruction: AbstractInstruction, - previously_calibrated_instructions: Optional[set[AbstractInstruction]] = None, + previously_calibrated_instructions: set[AbstractInstruction] | None = None, ) -> list[AbstractInstruction]: """Expand an instruction into its calibrated definition. @@ -948,7 +947,7 @@ def __iadd__(self, other: InstructionDesignator) -> "Program": self.inst(other) return self - def __getitem__(self, index: Union[slice, int]) -> Union[AbstractInstruction, "Program"]: + def __getitem__(self, index: slice | int) -> Union[AbstractInstruction, "Program"]: """Get the instruction at the given index, or a Program from a slice.""" return Program(self.instructions[index]) if isinstance(index, slice) else self.instructions[index] @@ -1046,7 +1045,7 @@ def get_classical_addresses_from_program(program: Program) -> dict[str, list[int return flattened_addresses -def address_qubits(program: Program, qubit_mapping: Optional[dict[QubitPlaceholder, int]] = None) -> Program: +def address_qubits(program: Program, qubit_mapping: dict[QubitPlaceholder, int] | None = None) -> Program: """Take a program which contains placeholders and assigns the all defined values. Either all qubits must be defined or all undefined. If qubits are diff --git a/pyquil/quilatom.py b/pyquil/quilatom.py index fd7b7f026..d7d782771 100644 --- a/pyquil/quilatom.py +++ b/pyquil/quilatom.py @@ -16,15 +16,14 @@ """Classes that represent the atomic building blocks of Quil expressions.""" import inspect -from collections.abc import Iterable, Mapping, Sequence +from collections.abc import Callable, Iterable, Mapping, Sequence from fractions import Fraction from numbers import Number from typing import ( Any, - Callable, ClassVar, NoReturn, - Optional, + Self, Union, cast, ) @@ -33,7 +32,6 @@ import quil.expression as quil_rs_expr import quil.instructions as quil_rs from deprecated.sphinx import deprecated -from typing_extensions import Self class QuilAtom: @@ -127,7 +125,7 @@ class QubitPlaceholder(QuilAtom): Qubit placeholders must be resolved to actual qubits before they can be used in a program. """ - def __init__(self, placeholder: Optional[quil_rs.QubitPlaceholder] = None): + def __init__(self, placeholder: quil_rs.QubitPlaceholder | None = None): """Initialize a qubit placeholder, or get a new handle for an existing placeholder.""" if placeholder is not None: self._placeholder = placeholder @@ -191,7 +189,7 @@ def __lt__(self, other: object) -> bool: QubitDesignator = Union[Qubit, QubitPlaceholder, FormalArgument, int] -def _convert_to_rs_qubit(qubit: Union[QubitDesignator, quil_rs.Qubit, QubitPlaceholder]) -> quil_rs.Qubit: +def _convert_to_rs_qubit(qubit: QubitDesignator | quil_rs.Qubit | QubitPlaceholder) -> quil_rs.Qubit: if isinstance(qubit, quil_rs.Qubit): return qubit if isinstance(qubit, Qubit): @@ -209,7 +207,7 @@ def _convert_to_rs_qubits(qubits: Iterable[QubitDesignator]) -> list[quil_rs.Qub return [_convert_to_rs_qubit(qubit) for qubit in qubits] -def _convert_to_py_qubit(qubit: Union[QubitDesignator, quil_rs.Qubit, quil_rs.QubitPlaceholder]) -> QubitDesignator: +def _convert_to_py_qubit(qubit: QubitDesignator | quil_rs.Qubit | quil_rs.QubitPlaceholder) -> QubitDesignator: if isinstance(qubit, quil_rs.Qubit): if qubit.is_fixed(): return Qubit(qubit.to_fixed()) @@ -222,11 +220,11 @@ def _convert_to_py_qubit(qubit: Union[QubitDesignator, quil_rs.Qubit, quil_rs.Qu raise ValueError(f"{type(qubit)} is not a valid QubitDesignator") -def _convert_to_py_qubits(qubits: Iterable[Union[QubitDesignator, quil_rs.Qubit]]) -> list[QubitDesignator]: +def _convert_to_py_qubits(qubits: Iterable[QubitDesignator | quil_rs.Qubit]) -> list[QubitDesignator]: return [_convert_to_py_qubit(qubit) for qubit in qubits] -def unpack_qubit(qubit: Union[QubitDesignator, FormalArgument]) -> Union[Qubit, QubitPlaceholder, FormalArgument]: +def unpack_qubit(qubit: QubitDesignator | FormalArgument) -> Qubit | QubitPlaceholder | FormalArgument: """Get a qubit from an object. :param qubit: the qubit designator to unpack. @@ -339,7 +337,7 @@ class LabelPlaceholder(QuilAtom): All placeholders must be resolved to actual labels before they can be used in a program. """ - def __init__(self, prefix: str = "L", *, placeholder: Optional[quil_rs.TargetPlaceholder] = None): + def __init__(self, prefix: str = "L", *, placeholder: quil_rs.TargetPlaceholder | None = None): """Initialize a new label placeholder.""" if placeholder: self.target = quil_rs.Target.from_placeholder(placeholder) @@ -378,7 +376,7 @@ def __hash__(self) -> int: def _convert_to_rs_expression( - parameter: Union[ParameterDesignator, quil_rs_expr.Expression], + parameter: ParameterDesignator | quil_rs_expr.Expression, ) -> quil_rs_expr.Expression: if isinstance(parameter, quil_rs_expr.Expression): return parameter @@ -390,7 +388,7 @@ def _convert_to_rs_expression( def _convert_to_rs_expressions( - parameters: Sequence[Union[ParameterDesignator, quil_rs_expr.Expression]], + parameters: Sequence[ParameterDesignator | quil_rs_expr.Expression], ) -> list[quil_rs_expr.Expression]: return [_convert_to_rs_expression(parameter) for parameter in parameters] @@ -448,13 +446,11 @@ def format_parameter(element: ParameterDesignator) -> str: def _convert_to_py_expression( - expression: Union[ - ParameterDesignator, - ExpressionDesignator, - ExpressionValueDesignator, - quil_rs_expr.Expression, - quil_rs.MemoryReference, - ], + expression: ParameterDesignator + | ExpressionDesignator + | ExpressionValueDesignator + | quil_rs_expr.Expression + | quil_rs.MemoryReference, ) -> ExpressionDesignator: if isinstance(expression, (Expression, Number)): return expression @@ -494,7 +490,7 @@ def _convert_to_py_expression( def _convert_to_py_expressions( expressions: Sequence[ - Union[ParameterDesignator, ExpressionDesignator, quil_rs_expr.Expression, quil_rs.MemoryReference] + ParameterDesignator | ExpressionDesignator | quil_rs_expr.Expression | quil_rs.MemoryReference ], ) -> Sequence[ExpressionDesignator]: return [_convert_to_py_expression(expression) for expression in expressions] @@ -582,7 +578,7 @@ def __float__(self) -> float: raise ValueError(f"Cannot convert complex value with non-zero imaginary value to float: {value}") return float(value.real) - def __array__(self, dtype: Optional[np.dtype] = None) -> np.ndarray: + def __array__(self, dtype: np.dtype | None = None) -> np.ndarray: """Implement the numpy array protocol for this expression. If the dtype is not object, then there will be an attempt to simplify the expression to a @@ -621,7 +617,7 @@ def substitute(expr: ExpressionDesignator, d: ParameterSubstitutionsMapDesignato return expr -def substitute_array(a: Union[Sequence[Expression], np.ndarray], d: ParameterSubstitutionsMapDesignator) -> np.ndarray: +def substitute_array(a: Sequence[Expression] | np.ndarray, d: ParameterSubstitutionsMapDesignator) -> np.ndarray: """Apply ``substitute`` to all elements of an array ``a`` and return the resulting array. :param a: The array of expressions whose parameters or memory references are to be substituted. @@ -955,7 +951,7 @@ class MemoryReference(QuilAtom, Expression): the declared variable is of length >1 or 1, resp. """ - def __init__(self, name: str, offset: int = 0, declared_size: Optional[int] = None): + def __init__(self, name: str, offset: int = 0, declared_size: int | None = None): """Initialize a new memory reference.""" if not isinstance(offset, int) or offset < 0: raise TypeError("MemoryReference offset must be a non-negative int") @@ -1069,7 +1065,7 @@ def __str__(self) -> str: class WaveformInvocation(quil_rs.WaveformInvocation, QuilAtom): """A waveform invocation.""" - def __new__(cls, name: str, parameters: Optional[dict[str, ParameterDesignator]] = None) -> Self: + def __new__(cls, name: str, parameters: dict[str, ParameterDesignator] | None = None) -> Self: """Initialize a new waveform invocation.""" if parameters is None: parameters = {} @@ -1108,7 +1104,7 @@ def __new__(cls, name: str) -> Self: def _template_waveform_property( - name: str, *, dtype: Optional[Union[type[int], type[float]]] = None, doc: Optional[str] = None + name: str, *, dtype: type[int] | type[float] | None = None, doc: str | None = None ) -> property: """Initialize a getters, setters, and deleter for a parameter on a ``TemplateWaveform``. @@ -1121,7 +1117,7 @@ def _template_waveform_property( :param doc: Docstring for the property. """ - def fget(self: "TemplateWaveform") -> Optional[ParameterDesignator]: + def fget(self: "TemplateWaveform") -> ParameterDesignator | None: parameter = self.get_parameter(name) if parameter is None or dtype is None: return parameter @@ -1161,7 +1157,7 @@ def __new__( name: str, *, duration: float, - **kwargs: Union[Optional[ParameterDesignator], Optional[ExpressionDesignator]], + **kwargs: ParameterDesignator | None | ExpressionDesignator | None, ) -> Self: """Initialize a new TemplateWaveform.""" rs_parameters = {key: _convert_to_rs_expression(value) for key, value in kwargs.items() if value is not None} @@ -1172,14 +1168,14 @@ def out(self) -> str: """Return the waveform as a valid Quil string.""" return str(self) - def get_parameter(self, name: str) -> Optional[ParameterDesignator]: + def get_parameter(self, name: str) -> ParameterDesignator | None: """Get a parameter in the waveform by name.""" parameter = super().parameters.get(name, None) if parameter is None: return None return _convert_to_py_expression(parameter) - def set_parameter(self, name: str, value: Optional[ParameterDesignator]) -> None: + def set_parameter(self, name: str, value: ParameterDesignator | None) -> None: """Set a parameter with a value.""" parameters = super().parameters if value is None: @@ -1265,9 +1261,9 @@ def __str__(self) -> str: def _update_envelope( iqs: np.ndarray, rate: float, - scale: Optional[float], - phase: Optional[float], - detuning: Optional[float], + scale: float | None, + phase: float | None, + detuning: float | None, ) -> np.ndarray: """Update a pulse envelope by optional shape parameters. @@ -1278,7 +1274,7 @@ def _update_envelope( :return: The updated pulse envelope. """ - def default(obj: Optional[float], val: float) -> float: + def default(obj: float | None, val: float) -> float: return obj if obj is not None else val scale = default(scale, 1.0) diff --git a/pyquil/quilbase.py b/pyquil/quilbase.py index 768c4c8e2..5f15345b6 100644 --- a/pyquil/quilbase.py +++ b/pyquil/quilbase.py @@ -16,20 +16,17 @@ """Contains the core pyQuil objects that correspond to Quil instructions.""" import abc -from collections.abc import Container, Iterable, Sequence +from collections.abc import Callable, Container, Iterable, Sequence from typing import ( TYPE_CHECKING, Any, - Callable, ClassVar, - Optional, + Self, TypeVar, - Union, ) import numpy as np from deprecated.sphinx import deprecated -from typing_extensions import Self from pyquil.quilatom import ( Expression, @@ -59,10 +56,37 @@ if TYPE_CHECKING: # avoids circular import from pyquil.paulis import PauliSum +import math + import quil.expression as quil_rs_expr import quil.instructions as quil_rs +def _is_perfect_power(n: int) -> bool: + """Check if n is a prime power (p^k for prime p, k >= 1). + + This ensures the matrix dimension can be interpreted as k qudits of + dimension p. Composite non-prime-power dimensions like 6 = 2*3 are + ambiguous and rejected. + """ + if n < 2: + return False + # Find the smallest prime factor. + factor = 0 + for p in range(2, int(math.isqrt(n)) + 1): + if n % p == 0: + factor = p + break + if factor == 0: + # n is prime → n = n^1, valid single-qudit dimension. + return True + # Check that n is a power of this smallest prime factor. + val = factor + while val < n: + val *= factor + return val == n + + class _InstructionMeta(abc.ABCMeta): """A metaclass that allows us to group all instruction types from quil-rs and pyQuil as an `AbstractInstruction`. @@ -121,7 +145,7 @@ def __reduce__(self: Any) -> tuple[Callable[[Any], AbstractInstruction], tuple[A return cls -def _convert_to_rs_instruction(instr: Union[AbstractInstruction, quil_rs.Instruction]) -> quil_rs.Instruction: +def _convert_to_rs_instruction(instr: AbstractInstruction | quil_rs.Instruction) -> quil_rs.Instruction: if isinstance(instr, quil_rs.Instruction): return instr if isinstance(instr, quil_rs.Arithmetic): @@ -348,7 +372,7 @@ def __new__( cls, name: str, params: Sequence[ParameterDesignator], - qubits: Sequence[Union[Qubit, QubitPlaceholder, FormalArgument, int]], + qubits: Sequence[Qubit | QubitPlaceholder | FormalArgument | int], modifiers: Sequence[quil_rs.GateModifier] = [], ) -> Self: """Initialize a new gate instruction.""" @@ -377,7 +401,7 @@ def qubits(self) -> list[QubitDesignator]: return self.get_qubits(indices=False) # type: ignore @qubits.setter # type: ignore[override] - def qubits(self, qubits: Sequence[Union[Qubit, QubitPlaceholder, FormalArgument]]) -> None: + def qubits(self, qubits: Sequence[Qubit | QubitPlaceholder | FormalArgument]) -> None: quil_rs.Gate.qubits.__set__(self, _convert_to_rs_qubits(qubits)) # type: ignore @property @@ -395,7 +419,7 @@ def modifiers(self) -> list[str]: return [str(modifier).upper() for modifier in super().modifiers] @modifiers.setter # type: ignore[override] - def modifiers(self, modifiers: Union[list[str], list[quil_rs.GateModifier]]) -> None: + def modifiers(self, modifiers: list[str] | list[quil_rs.GateModifier]) -> None: modifiers = [ self._to_rs_gate_modifier(modifier) if isinstance(modifier, str) else modifier for modifier in modifiers ] @@ -417,11 +441,7 @@ def get_qubit_indices(self) -> list[int]: def controlled( self, - control_qubit: Union[ - quil_rs.Qubit, - QubitDesignator, - Sequence[Union[QubitDesignator, quil_rs.Qubit]], - ], + control_qubit: quil_rs.Qubit | QubitDesignator | Sequence[QubitDesignator | quil_rs.Qubit], ) -> "Gate": """Add the CONTROLLED modifier to the gate with the given control qubit or Sequence of control qubits.""" if isinstance(control_qubit, Sequence): @@ -434,8 +454,8 @@ def controlled( def forked( self, - fork_qubit: Union[quil_rs.Qubit, QubitDesignator], - alt_params: Union[Sequence[ParameterDesignator], Sequence[quil_rs_expr.Expression]], + fork_qubit: quil_rs.Qubit | QubitDesignator, + alt_params: Sequence[ParameterDesignator] | Sequence[quil_rs_expr.Expression], ) -> "Gate": """Add the FORKED modifier to the gate with the given fork qubit and given additional parameters.""" forked = super().forked(_convert_to_rs_qubit(fork_qubit), _convert_to_rs_expressions(alt_params)) @@ -472,7 +492,7 @@ def __deepcopy__(self, memo: dict) -> "Gate": return Gate._from_rs_gate(super().__deepcopy__(memo)) -def _strip_modifiers(gate: Gate, limit: Optional[int] = None) -> Gate: +def _strip_modifiers(gate: Gate, limit: int | None = None) -> Gate: """Remove modifiers from :py:class:`Gate`. This function removes up to ``limit`` gate modifiers from the given gate, @@ -517,14 +537,14 @@ class Measurement(quil_rs.Measurement, AbstractInstruction): def __new__( cls, qubit: QubitDesignator, - classical_reg: Optional[MemoryReference], + classical_reg: MemoryReference | None, ) -> Self: """Initialize a new measurement instruction.""" target = cls._reg_to_target(classical_reg) return super().__new__(cls, _convert_to_rs_qubit(qubit), target) @classmethod - def _reg_to_target(cls, classical_reg: Optional[MemoryReference]) -> Optional[quil_rs.MemoryReference]: + def _reg_to_target(cls, classical_reg: MemoryReference | None) -> quil_rs.MemoryReference | None: if isinstance(classical_reg, quil_rs.MemoryReference): return classical_reg @@ -547,7 +567,7 @@ def qubit(self, qubit: QubitDesignator) -> None: quil_rs.Measurement.qubit.__set__(self, _convert_to_rs_qubit(qubit)) # type: ignore[attr-defined] @property - def classical_reg(self) -> Optional[MemoryReference]: + def classical_reg(self) -> MemoryReference | None: """Get the MemoryReference that this instruction writes to, if any.""" target = super().target if target is None: @@ -555,7 +575,7 @@ def classical_reg(self) -> Optional[MemoryReference]: return MemoryReference._from_rs_memory_reference(target) @classical_reg.setter - def classical_reg(self, classical_reg: Optional[MemoryReference]) -> None: + def classical_reg(self, classical_reg: MemoryReference | None) -> None: target = self._reg_to_target(classical_reg) quil_rs.Measurement.target.__set__(self, target) # type: ignore[attr-defined] @@ -563,7 +583,7 @@ def classical_reg(self, classical_reg: Optional[MemoryReference]) -> None: version="4.0", reason="The indices flag will be removed, use get_qubit_indices() instead.", ) - def get_qubits(self, indices: bool = True) -> Union[set[QubitDesignator], set[int]]: + def get_qubits(self, indices: bool = True) -> set[QubitDesignator] | set[int]: """Get the qubit this instruction measures.""" if indices: return self.get_qubit_indices() @@ -592,9 +612,9 @@ def __deepcopy__(self, memo: dict) -> "Measurement": class Reset(quil_rs.Reset, AbstractInstruction): """The RESET instruction.""" - def __new__(cls, qubit: Optional[Union[Qubit, QubitPlaceholder, FormalArgument, int]] = None) -> Self: + def __new__(cls, qubit: Qubit | QubitPlaceholder | FormalArgument | int | None = None) -> Self: """Initialize a new reset instruction.""" - rs_qubit: Optional[quil_rs.Qubit] = None + rs_qubit: quil_rs.Qubit | None = None if qubit is not None: rs_qubit = _convert_to_rs_qubit(qubit) return super().__new__(cls, rs_qubit) @@ -611,7 +631,7 @@ def out(self) -> str: version="4.0", reason="The indices flag will be removed, use get_qubit_indices() instead.", ) - def get_qubits(self, indices: bool = True) -> Optional[set[QubitDesignator]]: + def get_qubits(self, indices: bool = True) -> set[QubitDesignator] | None: """Get the qubit this instruction resets.""" if super().qubit is None: return None @@ -619,22 +639,22 @@ def get_qubits(self, indices: bool = True) -> Optional[set[QubitDesignator]]: return self.get_qubit_indices() # type: ignore return {_convert_to_py_qubit(super().qubit)} # type: ignore - def get_qubit_indices(self) -> Optional[set[int]]: + def get_qubit_indices(self) -> set[int] | None: """Get the qubit this instruction resets, as an integer index.""" if super().qubit is None: return None return {super().qubit.to_fixed()} # type: ignore @property # type: ignore[override] - def qubit(self) -> Optional[QubitDesignator]: + def qubit(self) -> QubitDesignator | None: """Get the qubit this instruction resets, if any.""" if super().qubit: return _convert_to_py_qubit(super().qubit) # type: ignore return None @qubit.setter - def qubit(self, qubit: Optional[QubitDesignator]) -> None: - rs_qubit: Optional[quil_rs.Qubit] = None + def qubit(self, qubit: QubitDesignator | None) -> None: + rs_qubit: quil_rs.Qubit | None = None if qubit is not None: rs_qubit = _convert_to_rs_qubit(qubit) quil_rs.Reset.qubit.__set__(self, rs_qubit) # type: ignore[attr-defined] @@ -654,7 +674,7 @@ def __deepcopy__(self, memo: dict) -> "Reset": class ResetQubit(Reset): """A targeted RESET instruction.""" - def __new__(cls, qubit: Union[Qubit, QubitPlaceholder, FormalArgument, int]) -> Self: + def __new__(cls, qubit: Qubit | QubitPlaceholder | FormalArgument | int) -> Self: """Initialize a new reset instruction, with a target qubit.""" if qubit is None: raise TypeError("qubit should not be None") @@ -675,8 +695,8 @@ class DefGate(quil_rs.GateDefinition, AbstractInstruction): def __new__( cls, name: str, - matrix: Union[list[list[Expression]], np.ndarray, np.matrix], - parameters: Optional[list[Parameter]] = None, + matrix: list[list[Expression]] | np.ndarray | np.matrix, + parameters: list[Parameter] | None = None, ) -> Self: """Initialize a new gate definition. @@ -695,15 +715,13 @@ def _from_rs_gate_definition(cls, gate_definition: quil_rs.GateDefinition) -> Se @staticmethod def _convert_to_matrix_specification( - matrix: Union[list[list[Expression]], np.ndarray, np.matrix], + matrix: list[list[Expression]] | np.ndarray | np.matrix, ) -> quil_rs.GateSpecification: to_rs_matrix = np.vectorize(_convert_to_rs_expression, otypes=["O"]) return quil_rs.GateSpecification.from_matrix(to_rs_matrix(np.asarray(matrix))) @staticmethod - def _validate_matrix( - matrix: Union[list[list[Expression]], np.ndarray, np.matrix], contains_parameters: bool - ) -> None: + def _validate_matrix(matrix: list[list[Expression]] | np.ndarray | np.matrix, contains_parameters: bool) -> None: if isinstance(matrix, list): rows = len(matrix) if not all([len(row) == rows for row in matrix]): @@ -715,8 +733,10 @@ def _validate_matrix( else: raise TypeError("Matrix argument must be a list or NumPy array/matrix") - if 0 != rows & (rows - 1): - raise ValueError(f"Dimension of matrix must be a power of 2, got {rows}") + if not _is_perfect_power(rows): + raise ValueError( + f"Dimension of matrix must be a perfect power of an integer (e.g. 2, 3, 4, 8, 9, ...), got {rows}" + ) if not contains_parameters: np_matrix = np.asarray(matrix) @@ -728,22 +748,32 @@ def out(self) -> str: """Return the Gate as a valid Quil string.""" return super().to_quil() - def get_constructor(self) -> Union[Callable[..., Gate], Callable[..., Callable[..., Gate]]]: + def get_constructor(self) -> Callable[..., Gate] | Callable[..., Callable[..., Gate]]: """Return a function that constructs this gate on variable qubit indices. For example, `mygate.get_constructor()(1) applies the gate to qubit 1.` """ if self.parameters: - return lambda *params: lambda *qubits: Gate( - name=self.name, params=list(params), qubits=list(map(unpack_qubit, qubits)) + return lambda *params: ( + lambda *qubits: Gate(name=self.name, params=list(params), qubits=list(map(unpack_qubit, qubits))) ) else: return lambda *qubits: Gate(name=self.name, params=[], qubits=list(map(unpack_qubit, qubits))) def num_args(self) -> int: - """Get the number of qubit arguments the gate takes.""" + """Get the number of qudit arguments the gate takes. + + For a matrix of dimension d^k, returns k where d is the smallest + integer base >= 2 such that rows = d^k. + """ rows = len(self.matrix) - return int(np.log2(rows)) + if rows < 2: + return 0 + for base in range(2, rows + 1): + k = int(round(math.log(rows, base))) + if base**k == rows: + return k + return 1 @property def matrix(self) -> np.ndarray: @@ -761,7 +791,7 @@ def parameters(self) -> list[Parameter]: return [Parameter(name) for name in super().parameters] @parameters.setter # type: ignore[override] - def parameters(self, parameters: Optional[list[Parameter]]) -> None: + def parameters(self, parameters: list[Parameter] | None) -> None: quil_rs.GateDefinition.parameters.__set__(self, [param.name for param in parameters or []]) # type: ignore[attr-defined] # noqa def __hash__(self) -> int: @@ -780,14 +810,14 @@ def __deepcopy__(self, memo: dict) -> "DefGate": class DefPermutationGate(DefGate): """A gate defined by a permutation of numbers.""" - def __new__(cls, name: str, permutation: Union[list[int], np.ndarray]) -> Self: + def __new__(cls, name: str, permutation: list[int] | np.ndarray) -> Self: """Initialize a new gate definition with a permutation.""" specification = DefPermutationGate._convert_to_permutation_specification(permutation) gate_definition = quil_rs.GateDefinition(name, [], specification) return super()._from_rs_gate_definition(gate_definition) @staticmethod - def _convert_to_permutation_specification(permutation: Union[list[int], np.ndarray]) -> quil_rs.GateSpecification: + def _convert_to_permutation_specification(permutation: list[int] | np.ndarray) -> quil_rs.GateSpecification: return quil_rs.GateSpecification.from_permutation([int(x) for x in permutation]) @property @@ -869,7 +899,7 @@ def __str__(self) -> str: class JumpTarget(quil_rs.Label, AbstractInstruction): """Representation of a target that can be jumped to.""" - def __new__(cls, label: Union[Label, LabelPlaceholder]) -> Self: + def __new__(cls, label: Label | LabelPlaceholder) -> Self: """Initialize a new target.""" return super().__new__(cls, label.target) @@ -878,7 +908,7 @@ def _from_rs_label(cls, label: quil_rs.Label) -> "JumpTarget": return super().__new__(cls, label.target) @property - def label(self) -> Union[Label, LabelPlaceholder]: + def label(self) -> Label | LabelPlaceholder: """Get the target as a label.""" if super().target.is_placeholder(): return LabelPlaceholder._from_rs_target(super().target) @@ -902,7 +932,7 @@ def __deepcopy__(self, memo: dict) -> "JumpTarget": class JumpWhen(quil_rs.JumpWhen, AbstractInstruction): """The JUMP-WHEN instruction.""" - def __new__(cls, target: Union[Label, LabelPlaceholder], condition: MemoryReference) -> Self: + def __new__(cls, target: Label | LabelPlaceholder, condition: MemoryReference) -> Self: """Initialize a new JumpWhen instruction. :param target: The target to jump to if the condition is true. @@ -930,14 +960,14 @@ def condition(self, condition: MemoryReference) -> None: quil_rs.JumpWhen.condition.__set__(self, condition._to_rs_memory_reference()) # type: ignore[attr-defined] @property # type: ignore[override] - def target(self) -> Union[Label, LabelPlaceholder]: + def target(self) -> Label | LabelPlaceholder: """Get the target the instruction will jump to if the condition bit is not 1.""" if super().target.is_placeholder(): return LabelPlaceholder._from_rs_target(super().target) return Label._from_rs_target(super().target) @target.setter - def target(self, target: Union[Label, LabelPlaceholder]) -> None: + def target(self, target: Label | LabelPlaceholder) -> None: quil_rs.JumpWhen.target.__set__(self, target) # type: ignore[attr-defined] def __str__(self) -> str: @@ -954,7 +984,7 @@ def __deepcopy__(self, memo: dict) -> "JumpWhen": class JumpUnless(quil_rs.JumpUnless, AbstractInstruction): """The JUMP-UNLESS instruction.""" - def __new__(cls, target: Union[Label, LabelPlaceholder], condition: MemoryReference) -> Self: + def __new__(cls, target: Label | LabelPlaceholder, condition: MemoryReference) -> Self: """Initialize a new JumpUnless instruction. :param target: The target to jump to if the condition is true. @@ -982,14 +1012,14 @@ def condition(self, condition: MemoryReference) -> None: quil_rs.JumpUnless.condition.__set__(self, condition._to_rs_memory_reference()) # type: ignore[attr-defined] @property # type: ignore[override] - def target(self) -> Union[Label, LabelPlaceholder]: + def target(self) -> Label | LabelPlaceholder: """Get the target the instruction will jump to if the condition bit is not 1.""" if super().target.is_placeholder(): return LabelPlaceholder._from_rs_target(super().target) return Label._from_rs_target(super().target) @target.setter - def target(self, target: Union[Label, LabelPlaceholder]) -> None: + def target(self, target: Label | LabelPlaceholder) -> None: quil_rs.JumpUnless.target.__set__(self, target) # type: ignore[attr-defined] def __str__(self) -> str: @@ -1096,7 +1126,7 @@ class LogicalBinaryOp(quil_rs.BinaryLogic, AbstractInstruction): op: ClassVar[quil_rs.BinaryOperator] - def __new__(cls, left: MemoryReference, right: Union[MemoryReference, int]) -> Self: + def __new__(cls, left: MemoryReference, right: MemoryReference | int) -> Self: """Initialize the operands of the binary logical instruction.""" destination = left._to_rs_memory_reference() source = cls._to_rs_binary_operand(right) @@ -1107,13 +1137,13 @@ def _from_rs_binary_logic(cls, binary_logic: quil_rs.BinaryLogic) -> "LogicalBin return super().__new__(cls, binary_logic.operator, binary_logic.destination, binary_logic.source) @staticmethod - def _to_rs_binary_operand(operand: Union[MemoryReference, int]) -> quil_rs.BinaryOperand: + def _to_rs_binary_operand(operand: MemoryReference | int) -> quil_rs.BinaryOperand: if isinstance(operand, MemoryReference): return quil_rs.BinaryOperand.from_memory_reference(operand._to_rs_memory_reference()) return quil_rs.BinaryOperand.from_literal_integer(operand) @staticmethod - def _to_py_binary_operand(operand: quil_rs.BinaryOperand) -> Union[MemoryReference, int]: + def _to_py_binary_operand(operand: quil_rs.BinaryOperand) -> MemoryReference | int: if operand.is_literal_integer(): return operand.to_literal_integer() return MemoryReference._from_rs_memory_reference(operand.to_memory_reference()) @@ -1129,12 +1159,12 @@ def left(self, left: MemoryReference) -> None: quil_rs.BinaryLogic.destination.__set__(self, destination) # type: ignore[attr-defined] @property - def right(self) -> Union[MemoryReference, int]: + def right(self) -> MemoryReference | int: """The right hand side of the binary expression.""" return self._to_py_binary_operand(super().source) @right.setter - def right(self, right: Union[MemoryReference, int]) -> None: + def right(self, right: MemoryReference | int) -> None: source = self._to_rs_binary_operand(right) quil_rs.BinaryLogic.source.__set__(self, source) # type: ignore[attr-defined] @@ -1178,7 +1208,7 @@ class ArithmeticBinaryOp(quil_rs.Arithmetic, AbstractInstruction): op: ClassVar[quil_rs.ArithmeticOperator] - def __new__(cls, left: MemoryReference, right: Union[MemoryReference, int, float]) -> Self: + def __new__(cls, left: MemoryReference, right: MemoryReference | int | float) -> Self: """Initialize the operands of the binary arithmetic instruction.""" right_operand = _to_rs_arithmetic_operand(right) return super().__new__(cls, cls.op, left._to_rs_memory_reference(), right_operand) @@ -1199,12 +1229,12 @@ def left(self, left: MemoryReference) -> None: ) @property - def right(self) -> Union[MemoryReference, int, float]: + def right(self) -> MemoryReference | int | float: """The left hand side of the binary expression.""" return _to_py_arithmetic_operand(super().source) @right.setter - def right(self, right: Union[MemoryReference, int, float]) -> None: + def right(self, right: MemoryReference | int | float) -> None: quil_rs.Arithmetic.source.__set__(self, _to_rs_arithmetic_operand(right)) # type: ignore[attr-defined] def out(self) -> str: @@ -1251,7 +1281,7 @@ class ClassicalDiv(ArithmeticBinaryOp): class ClassicalMove(quil_rs.Move, AbstractInstruction): """The MOVE instruction.""" - def __new__(cls, left: MemoryReference, right: Union[MemoryReference, int, float]) -> "ClassicalMove": + def __new__(cls, left: MemoryReference, right: MemoryReference | int | float) -> "ClassicalMove": """Initialize a new MOVE instruction.""" return super().__new__(cls, left._to_rs_memory_reference(), _to_rs_arithmetic_operand(right)) @@ -1269,12 +1299,12 @@ def left(self, left: MemoryReference) -> None: quil_rs.Move.destination.__set__(self, left._to_rs_memory_reference()) # type: ignore @property - def right(self) -> Union[MemoryReference, int, float]: + def right(self) -> MemoryReference | int | float: """The right hand side (or "source") of the move instruction.""" return _to_py_arithmetic_operand(super().source) @right.setter - def right(self, right: Union[MemoryReference, int, float]) -> None: + def right(self, right: MemoryReference | int | float) -> None: quil_rs.Move.source.__set__(self, _to_rs_arithmetic_operand(right)) # type: ignore def out(self) -> str: @@ -1436,7 +1466,7 @@ def __deepcopy__(self, memo: dict) -> "ClassicalLoad": return ClassicalLoad._from_rs_load(super().__deepcopy__(memo)) -def _to_rs_arithmetic_operand(operand: Union[MemoryReference, int, float]) -> quil_rs.ArithmeticOperand: +def _to_rs_arithmetic_operand(operand: MemoryReference | int | float) -> quil_rs.ArithmeticOperand: if isinstance(operand, MemoryReference): return quil_rs.ArithmeticOperand.from_memory_reference(operand._to_rs_memory_reference()) if isinstance(operand, int): @@ -1446,7 +1476,7 @@ def _to_rs_arithmetic_operand(operand: Union[MemoryReference, int, float]) -> qu raise TypeError(f"{type(operand)} is not a valid ArithmeticOperand") -def _to_py_arithmetic_operand(operand: quil_rs.ArithmeticOperand) -> Union[MemoryReference, int, float]: +def _to_py_arithmetic_operand(operand: quil_rs.ArithmeticOperand) -> MemoryReference | int | float: if not isinstance(operand, quil_rs.ArithmeticOperand): raise TypeError(f"{type(operand)} is not an ArithmeticOperand") inner = operand.inner() @@ -1459,7 +1489,7 @@ def _to_py_arithmetic_operand(operand: quil_rs.ArithmeticOperand) -> Union[Memor class ClassicalStore(quil_rs.Store, AbstractInstruction): """The STORE instruction.""" - def __new__(cls, target: str, left: MemoryReference, right: Union[MemoryReference, int, float]) -> "ClassicalStore": + def __new__(cls, target: str, left: MemoryReference, right: MemoryReference | int | float) -> "ClassicalStore": """Initialize a new STORE instruction.""" rs_right = _to_rs_arithmetic_operand(right) return super().__new__(cls, target, left._to_rs_memory_reference(), rs_right) @@ -1487,12 +1517,12 @@ def left(self, left: MemoryReference) -> None: quil_rs.Store.offset.__set__(self, left._to_rs_memory_reference()) # type: ignore @property - def right(self) -> Union[MemoryReference, int, float]: + def right(self) -> MemoryReference | int | float: """The left hand side of the STORE instruction.""" return _to_py_arithmetic_operand(super().source) @right.setter - def right(self, right: Union[MemoryReference, int, float]) -> None: + def right(self, right: MemoryReference | int | float) -> None: quil_rs.Store.source.__set__(self, _to_rs_arithmetic_operand(right)) # type: ignore def out(self) -> str: @@ -1519,7 +1549,7 @@ def __new__( cls, target: MemoryReference, left: MemoryReference, - right: Union[MemoryReference, int, float], + right: MemoryReference | int | float, ) -> "ClassicalComparison": """Initialize a new comparison instruction.""" rs_target, rs_left, rs_right = ( @@ -1534,7 +1564,7 @@ def _from_rs_comparison(cls, comparison: quil_rs.Comparison) -> Self: return super().__new__(cls, comparison.operator, comparison.destination, comparison.lhs, comparison.rhs) @staticmethod - def _to_comparison_operand(operand: Union[MemoryReference, int, float]) -> quil_rs.ComparisonOperand: + def _to_comparison_operand(operand: MemoryReference | int | float) -> quil_rs.ComparisonOperand: if isinstance(operand, MemoryReference): return quil_rs.ComparisonOperand.from_memory_reference(operand._to_rs_memory_reference()) elif isinstance(operand, int): @@ -1544,7 +1574,7 @@ def _to_comparison_operand(operand: Union[MemoryReference, int, float]) -> quil_ raise TypeError(f"{type(operand)} is not a valid ComparisonOperand") @staticmethod - def _to_py_operand(operand: quil_rs.ComparisonOperand) -> Union[MemoryReference, int, float]: + def _to_py_operand(operand: quil_rs.ComparisonOperand) -> MemoryReference | int | float: if not isinstance(operand, quil_rs.ComparisonOperand): raise TypeError(f"{type(operand)} is not an ComparisonOperand") inner = operand.inner() @@ -1571,7 +1601,7 @@ def left(self, left: MemoryReference) -> None: quil_rs.Comparison.lhs.__set__(self, left._to_rs_memory_reference()) # type: ignore @property - def right(self) -> Union[MemoryReference, int, float]: + def right(self) -> MemoryReference | int | float: """The right hand side of the comparison.""" return self._to_py_operand(super().rhs) @@ -1629,7 +1659,7 @@ class ClassicalGreaterEqual(ClassicalComparison): class Jump(quil_rs.Jump, AbstractInstruction): """Representation of an unconditional jump instruction (JUMP).""" - def __new__(cls, target: Union[Label, LabelPlaceholder]) -> Self: + def __new__(cls, target: Label | LabelPlaceholder) -> Self: """Initialize a new jump instruction.""" return super().__new__(cls, target.target) @@ -1638,14 +1668,14 @@ def _from_rs_jump(cls, jump: quil_rs.Jump) -> Self: return super().__new__(cls, jump.target) @property # type: ignore[override] - def target(self) -> Union[Label, LabelPlaceholder]: + def target(self) -> Label | LabelPlaceholder: """Get the target of the jump.""" if super().target.is_placeholder(): return LabelPlaceholder._from_rs_target(super().target) return Label._from_rs_target(super().target) @target.setter - def target(self, target: Union[Label, LabelPlaceholder]) -> None: + def target(self, target: Label | LabelPlaceholder) -> None: quil_rs.Jump.target.__set__(self, target.target) # type: ignore[attr-defined] def out(self) -> str: @@ -1675,7 +1705,7 @@ class Pragma(quil_rs.Pragma, AbstractInstruction): def __new__( cls, command: str, - args: Sequence[Union[Qubit, FormalArgument, int, str]] = (), + args: Sequence[Qubit | FormalArgument | int | str] = (), freeform_string: str = "", ) -> Self: """Initialize a new PRAGMA instruction.""" @@ -1687,7 +1717,7 @@ def _from_rs_pragma(cls, pragma: quil_rs.Pragma) -> "Pragma": return super().__new__(cls, pragma.name, pragma.arguments, pragma.data) @staticmethod - def _to_pragma_arguments(args: Sequence[Union[QubitDesignator, str]]) -> list[quil_rs.PragmaArgument]: + def _to_pragma_arguments(args: Sequence[QubitDesignator | str]) -> list[quil_rs.PragmaArgument]: pragma_arguments = [] for arg in args: if isinstance(arg, Qubit): @@ -1732,7 +1762,7 @@ def args(self) -> tuple[QubitDesignator]: return tuple(Pragma._to_py_arguments(super().arguments)) # type: ignore[return-value] @args.setter - def args(self, args: Sequence[Union[QubitDesignator, str]]) -> None: + def args(self, args: Sequence[QubitDesignator | str]) -> None: quil_rs.Pragma.arguments.__set__(self, Pragma._to_pragma_arguments(args)) # type: ignore[attr-defined] @property @@ -1765,8 +1795,8 @@ def __new__( name: str, memory_type: str, memory_size: int = 1, - shared_region: Optional[str] = None, - offsets: Optional[Sequence[tuple[int, str]]] = None, + shared_region: str | None = None, + offsets: Sequence[tuple[int, str]] | None = None, ) -> Self: """Initialize a new DECLARE directive.""" vector = quil_rs.Vector(Declare._memory_type_to_scalar_type(memory_type), memory_size) @@ -1793,7 +1823,7 @@ def _memory_type_to_scalar_type(memory_type: str) -> quil_rs.ScalarType: raise ValueError(f"{memory_type} is not a valid scalar type.") @staticmethod - def _to_rs_offsets(offsets: Optional[Sequence[tuple[int, str]]]) -> list[quil_rs.Offset]: + def _to_rs_offsets(offsets: Sequence[tuple[int, str]] | None) -> list[quil_rs.Offset]: if offsets is None: return [] return [ @@ -1823,7 +1853,7 @@ def memory_size(self, memory_size: int) -> None: quil_rs.Declaration.size.__set__(self, vector) # type: ignore[attr-defined] @property - def shared_region(self) -> Optional[str]: + def shared_region(self) -> str | None: """Get the memory region this declaration is sharing with, if any.""" sharing = super().sharing if sharing is None: @@ -1831,7 +1861,7 @@ def shared_region(self) -> Optional[str]: return sharing.name @shared_region.setter - def shared_region(self, shared_region: Optional[str]) -> None: + def shared_region(self, shared_region: str | None) -> None: sharing = None if not shared_region else quil_rs.Sharing(shared_region, []) current_sharing = super().sharing if sharing and isinstance(current_sharing, quil_rs.Sharing): @@ -1847,14 +1877,14 @@ def offsets(self) -> list[tuple[int, str]]: return [(offset.offset, str(offset.data_type).upper()) for offset in sharing.offsets] @offsets.setter - def offsets(self, offsets: Optional[list[tuple[int, str]]]) -> None: + def offsets(self, offsets: list[tuple[int, str]] | None) -> None: sharing = super().sharing if sharing is None: raise ValueError("DECLARE without a shared region cannot use offsets") sharing.offsets = Declare._to_rs_offsets(offsets) quil_rs.Declaration.sharing.__set__(self, sharing) # type: ignore[attr-defined] - def asdict(self) -> dict[str, Union[Sequence[tuple[int, str]], Optional[str], int]]: + def asdict(self) -> dict[str, Sequence[tuple[int, str]] | str | None | int]: """Get the DECLARE directive as a dictionary.""" return { "name": self.name, @@ -1923,7 +1953,7 @@ def __str__(self) -> str: version="4.0", reason="The indices flag will be removed, use get_qubit_indices() instead.", ) - def get_qubits(self, indices: bool = True) -> Union[set[QubitDesignator], set[int]]: + def get_qubits(self, indices: bool = True) -> set[QubitDesignator] | set[int]: """Get the qubits the pulse operates on.""" if indices: return self.get_qubit_indices() @@ -2009,7 +2039,7 @@ def freq(self, freq: ParameterDesignator) -> None: version="4.0", reason="The indices flag will be removed, use get_qubit_indices() instead.", ) - def get_qubits(self, indices: bool = True) -> Union[set[QubitDesignator], set[int]]: + def get_qubits(self, indices: bool = True) -> set[QubitDesignator] | set[int]: """Get the qubits the frequency is set on.""" if indices: return self.get_qubit_indices() @@ -2067,7 +2097,7 @@ def freq(self, freq: ParameterDesignator) -> None: version="4.0", reason="The indices flag will be removed, use get_qubit_indices() instead.", ) - def get_qubits(self, indices: bool = True) -> Union[set[QubitDesignator], set[int]]: + def get_qubits(self, indices: bool = True) -> set[QubitDesignator] | set[int]: """Get the qubits the frequency is shifted on.""" if indices: return self.get_qubit_indices() @@ -2125,7 +2155,7 @@ def phase(self, phase: ParameterDesignator) -> None: version="4.0", reason="The indices flag will be removed, use get_qubit_indices() instead.", ) - def get_qubits(self, indices: bool = True) -> Union[set[QubitDesignator], set[int]]: + def get_qubits(self, indices: bool = True) -> set[QubitDesignator] | set[int]: """Get the quibts the phase is set on.""" if indices: return self.get_qubit_indices() @@ -2183,7 +2213,7 @@ def phase(self, phase: ParameterDesignator) -> None: version="4.0", reason="The indices flag will be removed, use get_qubit_indices() instead.", ) - def get_qubits(self, indices: bool = True) -> Union[set[QubitDesignator], set[int]]: + def get_qubits(self, indices: bool = True) -> set[QubitDesignator] | set[int]: """Get the qubits the phase is shifted on.""" if indices: return self.get_qubit_indices() @@ -2241,7 +2271,7 @@ def __str__(self) -> str: version="4.0", reason="The indices flag will be removed, use get_qubit_indices() instead.", ) - def get_qubits(self, indices: bool = True) -> Union[set[QubitDesignator], set[int]]: + def get_qubits(self, indices: bool = True) -> set[QubitDesignator] | set[int]: """Get the qubits the swap-phases instruction operates on.""" if indices: return self.get_qubit_indices() @@ -2299,7 +2329,7 @@ def scale(self, scale: ParameterDesignator) -> None: version="4.0", reason="The indices flag will be removed, use get_qubit_indices() instead.", ) - def get_qubits(self, indices: bool = True) -> Union[set[QubitDesignator], set[int]]: + def get_qubits(self, indices: bool = True) -> set[QubitDesignator] | set[int]: """Get the qubits the scale is set on.""" if indices: return self.get_qubit_indices() @@ -2413,7 +2443,7 @@ def __str__(self) -> str: version="4.0", reason="The indices flag will be removed, use get_qubit_indices() instead.", ) - def get_qubits(self, indices: bool = True) -> Union[set[QubitDesignator], set[int]]: + def get_qubits(self, indices: bool = True) -> set[QubitDesignator] | set[int]: """Get the qubits the capture instruction operates on.""" if indices: return self.get_qubit_indices() @@ -2502,7 +2532,7 @@ def __str__(self) -> str: version="4.0", reason="The indices flag will be removed, use get_qubit_indices() instead.", ) - def get_qubits(self, indices: bool = True) -> Union[set[QubitDesignator], set[int]]: + def get_qubits(self, indices: bool = True) -> set[QubitDesignator] | set[int]: """Get the qubits the raw-capture instruction operates on.""" if indices: return self.get_qubit_indices() @@ -2524,7 +2554,7 @@ def __deepcopy__(self, memo: dict) -> "RawCapture": class Delay(quil_rs.Delay, AbstractInstruction): """The DELAY instruction.""" - def __new__(cls, frames: list[Frame], qubits: Sequence[Union[int, Qubit, FormalArgument]], duration: float) -> Self: + def __new__(cls, frames: list[Frame], qubits: Sequence[int | Qubit | FormalArgument], duration: float) -> Self: """Initialize a new DELAY instruction.""" frame_names = [frame.name for frame in frames] rs_qubits = _convert_to_rs_qubits(Delay._join_frame_qubits(frames, list(qubits))) @@ -2537,8 +2567,8 @@ def _from_rs_delay(cls, delay: quil_rs.Delay) -> "Delay": @staticmethod def _join_frame_qubits( - frames: Sequence[Frame], qubits: Sequence[Union[int, Qubit, FormalArgument]] - ) -> list[Union[int, Qubit, FormalArgument]]: + frames: Sequence[Frame], qubits: Sequence[int | Qubit | FormalArgument] + ) -> list[int | Qubit | FormalArgument]: merged_qubits = set(qubits) for frame in frames: merged_qubits.update(frame.qubits) # type: ignore @@ -2557,7 +2587,7 @@ def qubits(self) -> list[QubitDesignator]: return _convert_to_py_qubits(super().qubits) @qubits.setter # type: ignore[override] - def qubits(self, qubits: Sequence[Union[int, Qubit, FormalArgument]]) -> None: + def qubits(self, qubits: Sequence[int | Qubit | FormalArgument]) -> None: quil_rs.Delay.qubits.__set__(self, _convert_to_rs_qubits(qubits)) # type: ignore @property @@ -2606,7 +2636,7 @@ def _from_rs_delay(cls, delay: quil_rs.Delay) -> "DelayFrames": class DelayQubits(Delay): """Initialize a new DELAY instruction that operates on qubits.""" - def __new__(cls, qubits: Sequence[Union[Qubit, FormalArgument]], duration: float) -> Self: + def __new__(cls, qubits: Sequence[Qubit | FormalArgument], duration: float) -> Self: """Initialize a new DELAY instruction that operates on qubits.""" return super().__new__(cls, [], qubits, duration) @@ -2619,7 +2649,7 @@ def _from_rs_delay(cls, delay: quil_rs.Delay) -> "DelayQubits": class Fence(quil_rs.Fence, AbstractInstruction): """The FENCE instruction.""" - def __new__(cls, qubits: list[Union[Qubit, FormalArgument]]) -> Self: + def __new__(cls, qubits: list[Qubit | FormalArgument]) -> Self: """Initialize a new FENCE instruction.""" return super().__new__(cls, _convert_to_rs_qubits(qubits)) @@ -2643,7 +2673,7 @@ def qubits(self) -> list[QubitDesignator]: return _convert_to_py_qubits(super().qubits) @qubits.setter # type: ignore[override] - def qubits(self, qubits: list[Union[Qubit, FormalArgument]]) -> None: + def qubits(self, qubits: list[Qubit | FormalArgument]) -> None: quil_rs.Fence.qubits.__set__(self, _convert_to_rs_qubits(qubits)) # type: ignore[attr-defined] def __copy__(self) -> Self: @@ -2669,7 +2699,7 @@ def __new__( cls, name: str, parameters: list[Parameter], - entries: list[Union[complex, Expression]], + entries: list[complex | Expression], ) -> Self: """Initialize a new waveform definition.""" rs_waveform = DefWaveform._build_rs_waveform(parameters, entries) @@ -2680,7 +2710,7 @@ def _from_rs_waveform_definition(cls, waveform_definition: quil_rs.WaveformDefin return super().__new__(cls, waveform_definition.name, waveform_definition.definition) @staticmethod - def _build_rs_waveform(parameters: list[Parameter], entries: list[Union[complex, Expression]]) -> quil_rs.Waveform: + def _build_rs_waveform(parameters: list[Parameter], entries: list[complex | Expression]) -> quil_rs.Waveform: rs_parameters = [parameter.name for parameter in parameters] return quil_rs.Waveform(_convert_to_rs_expressions(entries), rs_parameters) @@ -2708,7 +2738,7 @@ def entries(self) -> Sequence[ParameterDesignator]: return _convert_to_py_expressions(super().definition.matrix) @entries.setter - def entries(self, entries: list[Union[complex, Expression]]) -> None: + def entries(self, entries: list[complex | Expression]) -> None: waveform = super().definition waveform.matrix = _convert_to_rs_expressions(entries) quil_rs.WaveformDefinition.definition.__set__(self, waveform) # type: ignore[attr-defined] @@ -2799,9 +2829,9 @@ def __new__( cls, name: str, parameters: Sequence[ParameterDesignator], - qubits: Sequence[Union[Qubit, FormalArgument]], + qubits: Sequence[Qubit | FormalArgument], instrs: Sequence[AbstractInstruction], - modifiers: Optional[list[quil_rs.GateModifier]] = None, + modifiers: list[quil_rs.GateModifier] | None = None, ) -> Self: """Initialize a new calibration definition.""" return super().__new__( @@ -2890,7 +2920,7 @@ class DefMeasureCalibration(quil_rs.MeasureCalibrationDefinition, AbstractInstru def __new__( cls, - qubit: Optional[Union[Qubit, FormalArgument]], + qubit: Qubit | FormalArgument | None, memory_reference: MemoryReference, instrs: list[AbstractInstruction], ) -> Self: @@ -2909,7 +2939,7 @@ def _from_rs_measure_calibration_definition( return super().__new__(cls, calibration.identifier, calibration.instructions) @property # type: ignore[override] - def qubit(self) -> Optional[QubitDesignator]: + def qubit(self) -> QubitDesignator | None: """Get the qubit this calibration matches.""" qubit = super().identifier.qubit if not qubit: @@ -2923,7 +2953,7 @@ def qubit(self, qubit: QubitDesignator) -> None: quil_rs.MeasureCalibrationDefinition.identifier.__set__(self, identifier) # type: ignore[attr-defined] # noqa @property - def memory_reference(self) -> Optional[MemoryReference]: + def memory_reference(self) -> MemoryReference | None: """Get the memory reference this calibration writes to.""" return MemoryReference._from_parameter_str(super().identifier.parameter) @@ -2972,13 +3002,13 @@ class DefFrame(quil_rs.FrameDefinition, AbstractInstruction): def __new__( cls, frame: Frame, - direction: Optional[str] = None, - initial_frequency: Optional[float] = None, - hardware_object: Optional[str] = None, - sample_rate: Optional[float] = None, - center_frequency: Optional[float] = None, - enable_raw_capture: Optional[str] = None, - channel_delay: Optional[float] = None, + direction: str | None = None, + initial_frequency: float | None = None, + hardware_object: str | None = None, + sample_rate: float | None = None, + center_frequency: float | None = None, + enable_raw_capture: str | None = None, + channel_delay: float | None = None, ) -> Self: """Get the frame definition.""" attributes = { @@ -3002,6 +3032,7 @@ def __new__( enable_raw_capture, channel_delay, ], + strict=False, ) if value is not None } @@ -3018,7 +3049,7 @@ def _from_rs_attribute_values( return super().__new__(cls, frame, attributes) @staticmethod - def _to_attribute_value(value: Union[str, float]) -> quil_rs.AttributeValue: + def _to_attribute_value(value: str | float) -> quil_rs.AttributeValue: if isinstance(value, str): return quil_rs.AttributeValue.from_string(value) if isinstance(value, (int, float, complex)): @@ -3041,13 +3072,13 @@ def frame(self) -> Frame: def frame(self, frame: Frame) -> None: quil_rs.FrameDefinition.identifier.__set__(self, frame) # type: ignore[attr-defined] - def set_attribute(self, name: str, value: Union[str, float]) -> None: + def set_attribute(self, name: str, value: str | float) -> None: """Set an attribute on the frame definition.""" updated = super().attributes updated.update({name: DefFrame._to_attribute_value(value)}) quil_rs.FrameDefinition.attributes.__set__(self, updated) # type: ignore[attr-defined] - def get_attribute(self, name: str) -> Optional[Union[str, float]]: + def get_attribute(self, name: str) -> str | float | None: """Get an attribute's value on the frame definition.""" value = super().attributes.get(name, None) if value is None: @@ -3056,7 +3087,7 @@ def get_attribute(self, name: str) -> Optional[Union[str, float]]: return value.to_string() return value.to_expression().to_number().real - def __getitem__(self, name: str) -> Union[str, float]: + def __getitem__(self, name: str) -> str | float: if not isinstance(name, str): raise TypeError("Frame attribute keys must be strings") value = self.get_attribute(name) @@ -3064,7 +3095,7 @@ def __getitem__(self, name: str) -> Union[str, float]: raise AttributeError(f"Attribute {name} not found") return value - def __setitem__(self, name: str, value: Union[str, float]) -> None: + def __setitem__(self, name: str, value: str | float) -> None: if not isinstance(name, str): raise TypeError("Frame attribute keys must be strings") self.set_attribute(name, value) @@ -3074,7 +3105,7 @@ def __setitem__(self, name: str, value: Union[str, float]) -> None: version="4.0", reason="Quil now supports generic key/value pairs in DEFFRAMEs. Use get_attribute('DIRECTION') instead.", ) - def direction(self) -> Optional[str]: + def direction(self) -> str | None: """Get the DIRECTION attribute of the frame.""" return self.get_attribute("DIRECTION") # type: ignore @@ -3091,7 +3122,7 @@ def direction(self, direction: str) -> None: version="4.0", reason="Quil now supports generic key/value pairs in DEFFRAMEs. Use set_attribute('INITIAL-FREQUENCY') instead.", # noqa: E501 ) - def initial_frequency(self) -> Optional[float]: + def initial_frequency(self) -> float | None: """Get the INITIAL-FREQUENCY attribute of the frame.""" return self.get_attribute("INITIAL-FREQUENCY") # type: ignore @@ -3108,7 +3139,7 @@ def initial_frequency(self, initial_frequency: float) -> None: version="4.0", reason="Quil now supports generic key/value pairs in DEFFRAMEs. Use get_attribute('HARDWARE-OBJECT') instead.", ) - def hardware_object(self) -> Optional[str]: + def hardware_object(self) -> str | None: """Get the HARDWARE-OBJECT attribute of the frame.""" return self.get_attribute("HARDWARE-OBJECT") # type: ignore diff --git a/pyquil/quiltcalibrations.py b/pyquil/quiltcalibrations.py index 2614bb673..e35bd7eb2 100644 --- a/pyquil/quiltcalibrations.py +++ b/pyquil/quiltcalibrations.py @@ -2,7 +2,7 @@ from collections.abc import Sequence from dataclasses import dataclass -from typing import Any, Optional, Union +from typing import Any import quil.expression as quil_expr import quil.instructions as quil_rs @@ -39,20 +39,20 @@ class CalibrationDoesntMatch(CalibrationError): class CalibrationMatch: """A match between a calibration definition and an instruction.""" - cal: Union[DefCalibration, DefMeasureCalibration] - settings: dict[Union[QubitDesignator, ExpressionDesignator], Any] + cal: DefCalibration | DefMeasureCalibration + settings: dict[QubitDesignator | ExpressionDesignator, Any] def _convert_to_calibration_match( - instruction: Union[quil_rs.Gate, quil_rs.Measurement], - calibration: Optional[Union[quil_rs.Calibration, quil_rs.MeasureCalibrationDefinition]], -) -> Optional[CalibrationMatch]: + instruction: quil_rs.Gate | quil_rs.Measurement, + calibration: quil_rs.Calibration | quil_rs.MeasureCalibrationDefinition | None, +) -> CalibrationMatch | None: if isinstance(instruction, quil_rs.Gate) and isinstance(calibration, quil_rs.Calibration): target_qubits = instruction.qubits - target_values: Sequence[Union[quil_expr.Expression, MemoryReference]] = instruction.parameters + target_values: Sequence[quil_expr.Expression | MemoryReference] = instruction.parameters parameter_qubits = calibration.identifier.qubits - parameter_values: Sequence[Union[quil_expr.Expression, MemoryReference]] = calibration.identifier.parameters - py_calibration: Union[DefCalibration, DefMeasureCalibration] = DefCalibration._from_rs_calibration(calibration) + parameter_values: Sequence[quil_expr.Expression | MemoryReference] = calibration.identifier.parameters + py_calibration: DefCalibration | DefMeasureCalibration = DefCalibration._from_rs_calibration(calibration) elif isinstance(instruction, quil_rs.Measurement) and isinstance(calibration, quil_rs.MeasureCalibrationDefinition): target_qubits = [instruction.qubit] target_values = ( @@ -65,15 +65,15 @@ def _convert_to_calibration_match( else: return None - settings: dict[Union[QubitDesignator, ExpressionDesignator], Union[QubitDesignator, ExpressionDesignator]] = { + settings: dict[QubitDesignator | ExpressionDesignator, QubitDesignator | ExpressionDesignator] = { _convert_to_py_qubit(param): _convert_to_py_qubit(qubit) - for param, qubit in zip(parameter_qubits, target_qubits) + for param, qubit in zip(parameter_qubits, target_qubits, strict=False) if isinstance(param, MemoryReference) or param.is_variable() } settings.update( { _convert_to_py_expression(param): _convert_to_py_expression(value) - for param, value in zip(parameter_values, target_values) + for param, value in zip(parameter_values, target_values, strict=False) if isinstance(param, MemoryReference) or param.is_variable() } ) @@ -82,8 +82,8 @@ def _convert_to_calibration_match( def match_calibration( - instr: AbstractInstruction, cal: Union[DefCalibration, DefMeasureCalibration] -) -> Optional[CalibrationMatch]: + instr: AbstractInstruction, cal: DefCalibration | DefMeasureCalibration +) -> CalibrationMatch | None: """Match a calibration definition to an instruction. On a successful match, return a (possibly empty) dictionary mapping calibration @@ -97,7 +97,7 @@ def match_calibration( instruction = _convert_to_rs_instruction(instr) gate = instruction.to_gate() calibration_set = CalibrationSet([calibration.to_calibration_definition()], []) - matched_calibration: Optional[Union[quil_rs.Calibration, quil_rs.MeasureCalibrationDefinition]] = ( + matched_calibration: quil_rs.Calibration | quil_rs.MeasureCalibrationDefinition | None = ( calibration_set.get_match_for_gate(gate) ) return _convert_to_calibration_match(gate, matched_calibration) diff --git a/pyquil/quiltwaveforms.py b/pyquil/quiltwaveforms.py index 834265032..06a522e8b 100644 --- a/pyquil/quiltwaveforms.py +++ b/pyquil/quiltwaveforms.py @@ -1,10 +1,9 @@ """Waveform templates that are commonly useful when working with pulse programs.""" -from typing import Optional +from typing import Self import numpy as np from scipy.special import erf -from typing_extensions import Self from pyquil.quilatom import ( TemplateWaveform, @@ -22,9 +21,9 @@ def __new__( cls, duration: float, iq: complex, - scale: Optional[float] = None, - phase: Optional[float] = None, - detuning: Optional[float] = None, + scale: float | None = None, + phase: float | None = None, + detuning: float | None = None, ) -> Self: """Initialize a new FlatWaveform.""" return super().__new__(cls, cls.NAME, duration=duration, iq=iq, scale=scale, phase=phase, detuning=detuning) @@ -50,9 +49,9 @@ def __new__( duration: float, fwhm: float, t0: float, - scale: Optional[float] = None, - phase: Optional[float] = None, - detuning: Optional[float] = None, + scale: float | None = None, + phase: float | None = None, + detuning: float | None = None, ) -> Self: """Initialize a new GaussianWaveform.""" return super().__new__( @@ -89,9 +88,9 @@ def __new__( t0: float, anh: float, alpha: float, - scale: Optional[float] = None, - phase: Optional[float] = None, - detuning: Optional[float] = None, + scale: float | None = None, + phase: float | None = None, + detuning: float | None = None, ) -> Self: """Initialize a new DragGaussianWaveform.""" return super().__new__( @@ -149,9 +148,9 @@ def __new__( anh: float, alpha: float, second_order_hrm_coeff: float, - scale: Optional[float] = None, - phase: Optional[float] = None, - detuning: Optional[float] = None, + scale: float | None = None, + phase: float | None = None, + detuning: float | None = None, ) -> Self: """Initialize a new HrmGaussianWaveform.""" return super().__new__( @@ -216,9 +215,9 @@ def __new__( risetime: float, pad_left: float, pad_right: float, - scale: Optional[float] = None, - phase: Optional[float] = None, - detuning: Optional[float] = None, + scale: float | None = None, + phase: float | None = None, + detuning: float | None = None, ) -> Self: """Initialize a new ErfSquareWaveform.""" return super().__new__( @@ -273,9 +272,9 @@ class BoxcarAveragerKernel(TemplateWaveform): def __new__( cls, duration: float, - scale: Optional[float] = None, - phase: Optional[float] = None, - detuning: Optional[float] = None, + scale: float | None = None, + phase: float | None = None, + detuning: float | None = None, ) -> Self: """Initialize a new BoxcarAveragerKernel.""" return super().__new__(cls, cls.NAME, duration=duration, scale=scale, phase=phase, detuning=detuning) diff --git a/pyquil/simulation/_numpy.py b/pyquil/simulation/_numpy.py index 0bba502eb..aa602899c 100644 --- a/pyquil/simulation/_numpy.py +++ b/pyquil/simulation/_numpy.py @@ -14,7 +14,7 @@ # limitations under the License. ############################################################################## from collections.abc import Sequence -from typing import Any, Optional, Union, cast +from typing import Any, cast import numpy as np from numpy.random.mtrand import RandomState @@ -75,7 +75,7 @@ def targeted_einsum(gate: np.ndarray, wf: np.ndarray, wf_target_inds: list[int]) used_data_indices = tuple(data_indices[q] for q in wf_target_inds) input_indices = work_indices + used_data_indices output_indices = list(data_indices) - for w, t in zip(work_indices, wf_target_inds): + for w, t in zip(work_indices, wf_target_inds, strict=False): output_indices[t] = w # TODO: `out` does not work if input matrices share memory with outputs, as is usually @@ -122,7 +122,7 @@ def targeted_tensordot(gate: np.ndarray, wf: np.ndarray, wf_target_inds: Sequenc where_td_put_them = where_td_put_them[sorty] sorted_targets = np.asarray(wf_target_inds)[sorty] # now that everything is sorted, we can do the insertion. - for target_ind, from_ind in zip(sorted_targets, where_td_put_them): + for target_ind, from_ind in zip(sorted_targets, where_td_put_them, strict=False): axes_ordering.insert(target_ind, from_ind) # A quick call to transpose gives us the right thing. @@ -177,7 +177,7 @@ def _term_expectation(wf: np.ndarray, term: PauliTerm) -> Any: class NumpyWavefunctionSimulator(AbstractQuantumSimulator): - def __init__(self, n_qubits: int, rs: Optional[RandomState] = None): + def __init__(self, n_qubits: int, rs: RandomState | None = None): """Initialize a wavefunction simulator that uses numpy's tensordot or einsum to update a state vector. Please consider using @@ -276,7 +276,7 @@ def do_gate_matrix(self, matrix: np.ndarray, qubits: Sequence[int]) -> "NumpyWav self.wf = targeted_tensordot(gate=tensor, wf=self.wf, wf_target_inds=qubits) return self - def expectation(self, operator: Union[PauliTerm, PauliSum]) -> float: + def expectation(self, operator: PauliTerm | PauliSum) -> float: """Compute the expectation of an operator. :param operator: The operator diff --git a/pyquil/simulation/_reference.py b/pyquil/simulation/_reference.py index fe5a72936..573d03d22 100644 --- a/pyquil/simulation/_reference.py +++ b/pyquil/simulation/_reference.py @@ -15,7 +15,7 @@ ############################################################################## import warnings from collections.abc import Sequence -from typing import Any, Optional, Union +from typing import Any import numpy as np from numpy.random.mtrand import RandomState @@ -70,7 +70,7 @@ def _is_valid_quantum_state(state_matrix: np.ndarray, rtol: float = 1e-05, atol: class ReferenceWavefunctionSimulator(AbstractQuantumSimulator): - def __init__(self, n_qubits: int, rs: Optional[RandomState] = None): + def __init__(self, n_qubits: int, rs: RandomState | None = None): """Initialize a wavefunction simulator that prioritizes readability over performance. Please consider using @@ -160,7 +160,7 @@ def do_measurement(self, qubit: int) -> int: self.wf = unitary.dot(self.wf) return 1 - def expectation(self, operator: Union[PauliTerm, PauliSum]) -> float: + def expectation(self, operator: PauliTerm | PauliSum) -> float: """Compute the expectation of an operator. :param operator: The operator @@ -210,7 +210,7 @@ class ReferenceDensitySimulator(AbstractQuantumSimulator): doing anything stochastic. A value of ``None`` disallows doing anything stochastic. """ - def __init__(self, n_qubits: int, rs: Optional[RandomState] = None): + def __init__(self, n_qubits: int, rs: RandomState | None = None): self.n_qubits = n_qubits self.rs = rs self.density: np.ndarray @@ -241,7 +241,7 @@ def set_initial_state(self, state_matrix: np.ndarray) -> "ReferenceDensitySimula self.initial_density = state_matrix else: raise ValueError( - "The state matrix is not valid. It must be Hermitian, trace one, " "and have non-negative eigenvalues." + "The state matrix is not valid. It must be Hermitian, trace one, and have non-negative eigenvalues." ) return self @@ -320,7 +320,7 @@ def do_measurement(self, qubit: int) -> int: self.density = unitary.dot(self.density).dot(np.conj(unitary.T)) return 1 - def expectation(self, operator: Union[PauliTerm, PauliSum]) -> complex: + def expectation(self, operator: PauliTerm | PauliSum) -> complex: raise NotImplementedError("To implement") def reset(self) -> "AbstractQuantumSimulator": diff --git a/pyquil/simulation/_resolver.py b/pyquil/simulation/_resolver.py new file mode 100644 index 000000000..b1dfa1971 --- /dev/null +++ b/pyquil/simulation/_resolver.py @@ -0,0 +1,957 @@ +############################################################################## +# Copyright 2016-2026 Rigetti Computing +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +############################################################################## +"""Shared infrastructure for the density-matrix and state-vector simulators. + +This module provides the simulation preprocessing pipeline: + +1. **Expander** — expands a program into a flat list of operators and physical + qubit tuples, resolving noise channels, custom gates, and DEFCIRCUIT + bodies. Fixed (non-parameterized) operations are returned as concrete + quax types; parameterized gates are returned as callables. +2. **Resolver** — converts a parameter vector into a list of + ``(operator, subsystem)`` pairs using native quax types. +3. **Adapters** — convert resolved operations into the form expected by each + simulator backend (``SuperOp`` for density matrices; ``Unitary``/``KrausMap``/ + ``QuantumInstrument`` for state-vector trajectories). +4. **Compressor** — merges adjacent operators via greedy edge contraction. +""" + +from __future__ import annotations + +import heapq +import logging +from collections.abc import Callable, Iterator, Mapping +from copy import deepcopy +from typing import Any, Literal, NamedTuple, TypeAlias, cast + +import jax.numpy as jnp +import networkx as nx +import quax as qx +from jax import Array + +from pyquil.noise._channels import ( + Channel, + CustomGateMap, + CycleChannel, + MeasurementChannel, + ResetChannel, + get_custom_gates_from_program, + get_instruction_unitary, +) +from pyquil.noise._noise_model import ( + NoiseModelLike, +) +from pyquil.quil import Program +from pyquil.quilatom import MemoryReference, substitute +from pyquil.quilbase import DefCircuit, Gate, Measurement, Reset, ResetQubit + +logger = logging.getLogger(__name__) + +# ────────────────────────────────────────────────────────── +# Type aliases +# ────────────────────────────────────────────────────────── + +# A fixed (non-parameterized) operator — the most specific native quax type. +FixedOp: TypeAlias = qx.Unitary | qx.SuperOp | qx.KrausMap | qx.QuantumInstrument + +# How ``MEASURE`` is represented during expansion. The grad-able simulators +# (state-vector, density-matrix) treat a measurement as a plain dephasing +# ``SuperOp`` (``"superop"``); the trajectory simulators keep it as a sampled +# ``QuantumInstrument`` (``"instrument"``). See :func:`resolve_for_gradable` and +# :func:`resolve_for_trajectory`. +MeasurementMode: TypeAlias = Literal["superop", "instrument"] + + +class ParametricGate: + """A parametric gate whose matrix depends on runtime parameters. + + Instances are callable: ``gate(params) -> qx.Unitary``. They also expose + the gate constructor and parameter layout so that the simulator can group + gates by type and use ``jax.vmap`` for efficient batch construction. + """ + + __slots__ = ("gate_fn", "param_indices", "concrete_values") + + def __init__( + self, + gate_fn: Callable[..., qx.Unitary], + param_indices: tuple[int, ...], + concrete_values: tuple[float, ...], + ) -> None: + #: The quax gate constructor (e.g. ``qx.gates.RX``). + self.gate_fn = gate_fn + #: Per-argument index into the flat parameter vector, or ``-1`` + #: when that argument is a compile-time constant. + self.param_indices = param_indices + #: Per-argument concrete value (``nan`` for runtime-parametric slots). + self.concrete_values = concrete_values + + def __call__(self, params: Array) -> qx.Unitary: + resolved: list[Any] = [] + for pi, cv in zip(self.param_indices, self.concrete_values, strict=False): + if pi >= 0: + resolved.append(params[pi]) + else: + resolved.append(cv) + result = self.gate_fn(*resolved) + if not isinstance(result, qx.Unitary): + result = cast(Any, result) + result = qx.Unitary.from_matrix(result.matrix, result.dims) + return result + + +# An expanded item is either a fixed operator or a ParametricGate that +# resolves parameters into a Unitary. +ExpandedOp: TypeAlias = FixedOp | ParametricGate + +# Resolved operations retain the most specific native quax type. +ResolvedOp = tuple[FixedOp, tuple[int, ...]] + +# Trajectory operations for the state-vector simulator. +TrajectoryOp = tuple[qx.Unitary | qx.KrausMap | qx.QuantumInstrument, tuple[int, ...]] + +# Density-matrix operations. +DensityMatrixOp = tuple[qx.SuperOp, tuple[int, ...]] + + +# ────────────────────────────────────────────────────────── +# DEFCIRCUIT expansion +# ────────────────────────────────────────────────────────── + + +def expand_defcircuit_body( + inst: Gate, + defcircuit: DefCircuit, + circuit_definitions: dict[str, DefCircuit], +) -> Iterator[Gate | Measurement | ResetQubit | Reset]: + """Yield concrete instructions from a DEFCIRCUIT invocation. + + Substitutes formal qubit/parameter arguments with the concrete values + from ``inst``. Handles nested DEFCIRCUITs via recursion. + + :param inst: The Gate that invokes the DEFCIRCUIT. + :param defcircuit: The DefCircuit definition to expand. + :param circuit_definitions: All known DEFCIRCUIT definitions (for nested expansion). + :yields: Concrete instructions with physical qubits and resolved parameters. + """ + qarg_to_arg_map = {qarg: q for q, qarg in zip(inst.qubits, defcircuit.qubit_variables, strict=False)} + parg_to_arg_map = {parg: param for param, parg in zip(inst.params, defcircuit.parameters, strict=False)} + + for circuit_inst in defcircuit.instructions: + if isinstance(circuit_inst, Gate): + circuit_inst = deepcopy(circuit_inst) + circuit_inst.qubits = [qarg_to_arg_map[qarg] for qarg in circuit_inst.qubits] # type: ignore[index,misc] + if hasattr(circuit_inst, "params"): + circuit_inst.params = [substitute(param, parg_to_arg_map) for param in circuit_inst.params] # type: ignore[arg-type] + if circuit_inst.name in circuit_definitions: + yield from expand_defcircuit_body( + circuit_inst, circuit_definitions[circuit_inst.name], circuit_definitions + ) + else: + yield circuit_inst + elif isinstance(circuit_inst, Measurement): + circuit_inst = deepcopy(circuit_inst) + circuit_inst.qubit = qarg_to_arg_map[circuit_inst.qubit] # type: ignore[index] + yield circuit_inst + elif isinstance(circuit_inst, ResetQubit): + circuit_inst = deepcopy(circuit_inst) + circuit_inst.qubit = qarg_to_arg_map[circuit_inst.qubit] # type: ignore[index] + yield circuit_inst + else: + yield deepcopy(circuit_inst) # type: ignore[misc] + + +# ══════════════════════════════════════════════════════════ +# Expander +# ══════════════════════════════════════════════════════════ + + +def _measure_registers(program: Program) -> set[str]: + """Return the set of register names that are targets of MEASURE instructions.""" + regs: set[str] = set() + for inst in program.instructions: + if isinstance(inst, Measurement): + cr = inst.classical_reg + if cr is not None: + regs.add(cr.name) + return regs + + +class _ExpansionContext(NamedTuple): + """Program-level derivations that are independent of qubit dimensions. + + Computing these once lets :func:`resolve_program` expand a program twice + (dimension inference, then final expansion) without re-deriving the custom + gates, circuit definitions, etc. on each pass. + """ + + circuit_definitions: dict[str, DefCircuit] + custom_gates: CustomGateMap | None + measure_regs: set[str] + all_qubits: list[int] + + +def _build_expansion_context(program: Program) -> _ExpansionContext: + """Derive the dimension-independent expansion context for a program.""" + circuit_definitions: dict[str, DefCircuit] = { + inst.name: inst for inst in program.instructions if isinstance(inst, DefCircuit) + } + custom_gates = get_custom_gates_from_program(program) or None + measure_regs = _measure_registers(program) + all_qubits = sorted(program.get_qubit_indices()) + return _ExpansionContext(circuit_definitions, custom_gates, measure_regs, all_qubits) + + +def expand_program( + program: Program, + noise_model: NoiseModelLike | None = None, + qubit_dimensions: Mapping[int, int] | None = None, + *, + context: _ExpansionContext | None = None, + measurement: MeasurementMode = "instrument", +) -> tuple[list[ExpandedOp], list[tuple[int, ...]], list[tuple[str, int]]]: + """Expand a program into operators and physical qubit tuples. + + Fixed (non-parameterized) operations are returned as concrete quax types + (``Unitary``, ``SuperOp``, ``QuantumInstrument``). Only parameterized + gates are returned as ``Callable[[Array], Unitary]``. + + DEFCIRCUIT invocations are expanded: + + * If a cycle invocation matches a :class:`CycleChannel` in the noise + model, the cycle is expanded using the channel's constituent operators. + * Otherwise the DEFCIRCUIT body is expanded via qubit/param substitution + and each resulting instruction is resolved individually. + + The noise model is fully resolved during expansion: noisy gates become + ``SuperOp``, noisy measurements become ``QuantumInstrument``, and noisy + resets become ``SuperOp``. + + :param program: Quil program (may contain DEFCIRCUITs). + :param noise_model: Optional noise model. + :param qubit_dimensions: Optional mapping from physical qubit id to its + Hilbert-space dimension. Used for ideal measurement and reset operators, + whose quax constructors otherwise default to qubit dimension. + :param context: Optional precomputed :class:`_ExpansionContext`. When omitted + it is derived from the program; pass it to avoid recomputing the + dimension-independent derivations across multiple expansion passes. + :param measurement: How to represent ``MEASURE`` instructions. ``"instrument"`` + (default) keeps a sampled ``QuantumInstrument``; ``"superop"`` emits the + measurement's dephasing total channel as a ``SuperOp`` so no instrument + (and hence no compressor barrier) is produced — used by the grad-able + simulators. + :return: Tuple of ``(ops, qubit_tuples, param_refs)`` where each op is + either a concrete quax operator or a ``Callable[[Array], Unitary]`` + for parameterized gates, each qubit tuple contains physical qubit + IDs, and ``param_refs`` is a list of ``(register_name, offset)`` + pairs for each scalar parameter in program order. + """ + # Derive (or reuse) the dimension-independent program context. + if context is None: + context = _build_expansion_context(program) + circuit_definitions = context.circuit_definitions + custom_gates = context.custom_gates + measure_regs = context.measure_regs + all_qubits = context.all_qubits + + ops: list[ExpandedOp] = [] + qubit_tuples: list[tuple[int, ...]] = [] + param_refs: list[tuple[str, int]] = [] + param_counter = 0 + + def _emit_op(op: ExpandedOp, qubits: tuple[int, ...]) -> None: + ops.append(op) + qubit_tuples.append(qubits) + + def _resolve_gate(inst: Gate) -> tuple[ExpandedOp, tuple[int, ...]]: + """Resolve a single gate instruction to an operator or callable.""" + nonlocal param_counter + qubits = tuple(inst.get_qubit_indices()) + + # Check noise model first. + channel = noise_model.get_channel(inst) if noise_model is not None else None + if isinstance(channel, Channel): + return channel.process, qubits + if isinstance(channel, CycleChannel): + raise ValueError(f"CycleChannel for {inst.name} was not expanded before gate resolution.") + + # Parameterized gate → callable that resolves params at call time. + if any(isinstance(p, MemoryReference) for p in inst.params): + gate_name = inst.name + if custom_gates is not None and gate_name in custom_gates: + gate_def = custom_gates[gate_name] + elif gate_name in qx.gates.QUANTUM_GATES: + gate_def = qx.gates.QUANTUM_GATES[gate_name] + else: + raise KeyError(f"Unknown gate '{gate_name}'.") + + param_indices: list[int] = [] + concrete_values: list[float] = [] + for p in inst.params: + if isinstance(p, MemoryReference) and p.name not in measure_regs: + param_indices.append(param_counter) + concrete_values.append(float("nan")) + param_refs.append((p.name, p.offset)) + param_counter += 1 + else: + param_indices.append(-1) + concrete_values.append(float(p.real) if hasattr(p, "real") else float(p)) + + return ParametricGate(gate_def, tuple(param_indices), tuple(concrete_values)), qubits + + # Fixed gate → resolve to Unitary now. + unitary = get_instruction_unitary(inst, custom_gates=custom_gates) + return unitary, qubits + + def _dimension_for(qubit: int) -> int: + return qubit_dimensions.get(qubit, 2) if qubit_dimensions is not None else 2 + + def _resolve_measurement(inst: Measurement) -> tuple[FixedOp, tuple[int, ...]]: + """Resolve a measurement instruction. + + Under ``measurement="instrument"`` the result is a ``QuantumInstrument`` + (sampled by the trajectory simulators). Under ``measurement="superop"`` + the instrument's dephasing total channel is returned as a ``SuperOp`` so + the grad-able pipeline never has to carry — or merge — an instrument. + """ + qubits = tuple(inst.get_qubit_indices()) + channel = noise_model.get_channel(inst) if noise_model is not None else None + instrument = ( + channel.process + if isinstance(channel, MeasurementChannel) + else qx.gates.MEASURE(dim=_dimension_for(qubits[0])) + ) + if measurement == "superop": + return instrument.total_channel(), qubits + return instrument, qubits + + def _resolve_reset_qubit(inst: ResetQubit) -> tuple[FixedOp, tuple[int, ...]]: + """Resolve a targeted reset instruction.""" + qubits = tuple(inst.get_qubit_indices()) # type: ignore[arg-type] + channel = noise_model.get_channel(inst) if noise_model is not None else None + if isinstance(channel, ResetChannel): + return channel.process, qubits + return qx.gates.RESET(dim=_dimension_for(qubits[0])), qubits + + def _emit_instruction(inst: Gate | Measurement | ResetQubit | Reset) -> None: + """Resolve and emit a single instruction.""" + match inst: + case Gate(): + op, qubits = _resolve_gate(inst) + _emit_op(op, qubits) + case Measurement(): + op, qubits = _resolve_measurement(inst) + _emit_op(op, qubits) + case ResetQubit(): + op, qubits = _resolve_reset_qubit(inst) + _emit_op(op, qubits) + case Reset(): + for q in all_qubits: + _emit_op(qx.gates.RESET(dim=_dimension_for(q)), (q,)) + + for inst in program.instructions: + if isinstance(inst, DefCircuit): + continue + + if isinstance(inst, Gate) and inst.name in circuit_definitions: + channel = noise_model.get_channel(inst) if noise_model is not None else None + + if isinstance(channel, CycleChannel): + # Expand using the channel's constituent operators. + for sub_ch in channel.channels: + sub_qubits = tuple(sub_ch.inst.get_qubit_indices()) + _emit_op(sub_ch.process, sub_qubits) + else: + # Expand DEFCIRCUIT body and resolve each instruction. + for expanded_inst in expand_defcircuit_body(inst, circuit_definitions[inst.name], circuit_definitions): + _emit_instruction(expanded_inst) + elif isinstance(inst, (Gate, Measurement, ResetQubit, Reset)): + _emit_instruction(inst) + + return ops, qubit_tuples, param_refs + + +# ══════════════════════════════════════════════════════════ +# DAG construction & qubit remapping +# ══════════════════════════════════════════════════════════ + + +def remap_qubits( + qubit_tuples: list[tuple[int, ...]], + qubit_indices: dict[int, int], +) -> list[tuple[int, ...]]: + """Remap physical qubit IDs to 0-based indices. + + :param qubit_tuples: List of physical qubit tuples from :func:`expand_program`. + :param qubit_indices: Mapping from physical qubit id → 0-based index. + :return: Remapped qubit tuples. + """ + return [tuple(qubit_indices[q] for q in qubits) for qubits in qubit_tuples] + + +def build_dag(qubit_tuples: list[tuple[int, ...]]) -> nx.DiGraph: + """Build a dependency DAG from qubit tuples. + + Each node corresponds to one operation (indexed 0..N-1). An edge + ``(u, v)`` exists when ``u`` and ``v`` act on a shared qubit and + ``u`` precedes ``v`` in program order. + + :param qubit_tuples: Remapped qubit tuples (0-based indices). + :return: DAG with node attribute ``"qubits"`` storing each node's qubit tuple. + """ + dag: nx.DiGraph = nx.DiGraph() + last_on_qubit: dict[int, int] = {} + + for idx, qubits in enumerate(qubit_tuples): + dag.add_node(idx, qubits=qubits) + for q in qubits: + if q in last_on_qubit: + dag.add_edge(last_on_qubit[q], idx) + last_on_qubit[q] = idx + + return dag + + +def _infer_dims(resolved: list[ResolvedOp], n_qubits: int) -> tuple[int, ...]: + """Infer per-qudit dimensions from resolved operators.""" + dims = [2] * n_qubits + for op, subsystem in resolved: + for q, d in zip(subsystem, op.dims[1], strict=False): + dims[q] = max(dims[q], d) + return tuple(dims) + + +# ══════════════════════════════════════════════════════════ +# Resolver +# ══════════════════════════════════════════════════════════ + + +def _freeze_resolver( + ops: list[ExpandedOp], + subsystems: list[tuple[int, ...]], +) -> Callable[[Array], list[ResolvedOp]]: + """Build the closure that turns a parameter vector into resolved operations. + + Fixed operators pass straight through; ``ParametricGate`` items are called + with the parameter vector to produce a concrete ``Unitary``. + """ + frozen = list(zip(ops, subsystems, strict=False)) + + def resolve(params: Array) -> list[ResolvedOp]: + return [ + (cast(Callable[[Array], qx.Unitary], item)(params) if callable(item) else item, subsystem) + for item, subsystem in frozen + ] + + return resolve + + +class Resolution(NamedTuple): + """Everything the simulators need from a program after expansion. + + :param dims: Inferred per-qudit dimensions (e.g. ``(2, 2, 3)``). + :param ops: Expanded operators, one per DAG node, in program order. + :param subsystems: 0-based qubit tuple each operator acts on. + :param param_refs: ``(register_name, offset)`` for each scalar parameter. + :param resolve: Closure mapping a parameter vector to ``(operator, subsystem)`` pairs. + """ + + dims: tuple[int, ...] + ops: list[ExpandedOp] + subsystems: list[tuple[int, ...]] + param_refs: list[tuple[str, int]] + resolve: Callable[[Array], list[ResolvedOp]] + + +def resolve_program( + program: Program, + noise_model: NoiseModelLike | None = None, + qubits: list[int] | None = None, + dims: tuple[int, ...] | None = None, + *, + measurement: MeasurementMode = "instrument", +) -> Resolution: + """Expand a program and build its parameter-resolving closure. + + Operators are returned in their most specific native type: + + * Ideal gates → ``qx.Unitary`` (parametric gates as a ``ParametricGate`` callable) + * Noisy gates (``Channel``) → ``qx.SuperOp`` + * Expanded cycle gates with ``CycleChannel`` noise → constituent ``qx.SuperOp`` + * Measurements → ``qx.QuantumInstrument`` + * Noisy/ideal resets → ``qx.SuperOp`` + + The program is expanded twice: first with default (qubit) register + dimensions to infer each register's true dimension from the gates and noisy + channels, then again with those dimensions so ideal measurement/reset + instruments use the correct dimension. Passing *dims* skips the first pass. + + :param program: Quil program (may contain DEFCIRCUITs and DEFGATEs). + :param noise_model: Optional noise model. + :param qubits: Optional explicit qubit list. If ``None``, inferred from the + program. Use this when the simulator knows about qubits that don't + appear in the program. + :param dims: Optional pre-determined per-qudit dimensions. + :param measurement: Measurement representation — see :func:`expand_program`. + Prefer the :func:`resolve_for_gradable` / :func:`resolve_for_trajectory` + entry points, which pin the correct mode for each simulator family. + :return: A :class:`Resolution`. + """ + if qubits is None: + qubits = sorted(program.get_qubit_indices()) + qubit_indices = {q: i for i, q in enumerate(qubits)} + + # Derive the dimension-independent context once and reuse it across both passes. + context = _build_expansion_context(program) + + def expand( + qubit_dimensions: Mapping[int, int] | None, + ) -> tuple[list[ExpandedOp], list[tuple[int, ...]], list[tuple[str, int]]]: + ops, phys_qubits, param_refs = expand_program( + program, noise_model, qubit_dimensions=qubit_dimensions, context=context, measurement=measurement + ) + return ops, remap_qubits(phys_qubits, qubit_indices), param_refs + + if dims is None: + ops, subsystems, param_refs = expand(None) + resolve = _freeze_resolver(ops, subsystems) + dims = _infer_dims(resolve(jnp.zeros(len(param_refs))), len(qubits)) + + qubit_dimensions = {q: dims[i] for q, i in qubit_indices.items()} + ops, subsystems, param_refs = expand(qubit_dimensions) + return Resolution(dims, ops, subsystems, param_refs, _freeze_resolver(ops, subsystems)) + + +def resolve_for_gradable( + program: Program, + noise_model: NoiseModelLike | None = None, + qubits: list[int] | None = None, + dims: tuple[int, ...] | None = None, +) -> Resolution: + """Resolve a program for the grad-able (state-vector / density-matrix) simulators. + + Measurements become dephasing ``SuperOp`` operators, so the resolved program + contains only ``Unitary``/``SuperOp`` operators and no compressor barriers are + needed. Thin wrapper over :func:`resolve_program` with ``measurement="superop"``. + """ + return resolve_program(program, noise_model, qubits, dims, measurement="superop") + + +def resolve_for_trajectory( + program: Program, + noise_model: NoiseModelLike | None = None, + qubits: list[int] | None = None, + dims: tuple[int, ...] | None = None, +) -> Resolution: + """Resolve a program for the trajectory simulators. + + Measurements remain sampled ``QuantumInstrument`` operators (kept out of merges + by compressor barriers). Thin wrapper over :func:`resolve_program` with + ``measurement="instrument"``. + """ + return resolve_program(program, noise_model, qubits, dims, measurement="instrument") + + +def enumerate_bases( + emit_order: list[tuple[int, list[int], tuple[int, ...]]], +) -> tuple[list[tuple[int, ...]], tuple[int, ...]]: + """Enumerate the distinct base subsystems produced by a compressor. + + The compressor's ``emit_order`` (see :func:`compressor_from_dag`) lists one + ``(root, nodes, subsystem)`` entry per emitted group, in application order. The + merge structure depends only on the DAG, not on parameter values, so the base + subsystems can be read straight off ``emit_order`` — no ``resolve``/``compress`` + probe is required. + + The grad-able simulators dispatch each compressed operation through a + ``jax.lax.switch`` keyed by its base, so the number of *distinct* bases (rather + than the number of operations) sets the size of the compiled graph. + + :param emit_order: The ``emit_order`` attribute of a compressor closure. + :return: ``(bases, op_index)`` where ``bases`` is the distinct subsystems in + first-seen order and ``op_index[k]`` is the base index of the ``k``-th + emitted operation. + """ + bases: list[tuple[int, ...]] = [] + sub_to_branch: dict[tuple[int, ...], int] = {} + op_index: list[int] = [] + for _, _, subsystem in emit_order: + if subsystem not in sub_to_branch: + sub_to_branch[subsystem] = len(bases) + bases.append(subsystem) + op_index.append(sub_to_branch[subsystem]) + return bases, tuple(op_index) + + +# ══════════════════════════════════════════════════════════ +# Adapters +# +# These adapters live outside the resolver intentionally. The resolver produces +# operators in their most specific native type (Unitary, SuperOp, KrausMap, +# QuantumInstrument). Each simulator backend then adapts these to its required +# representation. This separation keeps the resolver backend-agnostic and the +# per-op conversion cost (type dispatch + matrix reshape) is negligible compared +# to the actual simulation. +# ══════════════════════════════════════════════════════════ + + +def adapt_for_density_matrix( + ops: list[ResolvedOp], +) -> list[DensityMatrixOp]: + """Convert resolved operations to ``(SuperOp, subsystem)`` pairs for density-matrix simulation. + + * ``Unitary`` → ``qx.to_superop(op)`` + * ``SuperOp`` → pass through + * ``KrausMap`` → ``qx.to_superop(op)`` + * ``QuantumInstrument`` → ``qx.to_superop(op.total_channel())`` + + :param ops: Resolved operations from :func:`build_resolver`. + :return: List of ``(SuperOp, subsystem)`` pairs. + """ + result: list[DensityMatrixOp] = [] + for op, subsystem in ops: + # ``qx.to_superop`` is single-dispatch and idempotent on SuperOp, so it + # covers Unitary/SuperOp/KrausMap directly; only an instrument needs its + # total channel taken first. + channel = op.total_channel() if isinstance(op, qx.QuantumInstrument) else op + result.append((qx.to_superop(channel), subsystem)) + return result + + +def adapt_for_trajectory( + ops: list[ResolvedOp], + kraus_truncation_threshold: float = 1e-6, +) -> list[TrajectoryOp]: + """Convert resolved operations to trajectory-compatible types. + + * ``Unitary`` → pass through + * ``SuperOp`` → ``truncate_kraus(to_kraus(op))`` → ``KrausMap`` + * ``KrausMap`` → pass through + * ``QuantumInstrument`` → pass through + + :param ops: Resolved operations from :func:`build_resolver`. + :param kraus_truncation_threshold: Threshold for Kraus truncation. + :return: List of ``(Unitary | KrausMap | QuantumInstrument, subsystem)`` pairs. + """ + result: list[TrajectoryOp] = [] + for op, subsystem in ops: + match op: + case qx.SuperOp(): + km = qx.truncate_kraus(qx.to_kraus(op), atol=kraus_truncation_threshold) + result.append((km, subsystem)) + case qx.Unitary() | qx.KrausMap() | qx.QuantumInstrument(): + result.append((op, subsystem)) + case _: + raise TypeError(f"Cannot adapt operator of type {type(op).__name__} for trajectory simulation.") + return result + + +# ══════════════════════════════════════════════════════════ +# Compressor (greedy edge contraction) +# ══════════════════════════════════════════════════════════ + + +def _merge_ops( + ops_with_subsystems: list[ResolvedOp], + merged_subsystem: tuple[int, ...], + dims: tuple[int, ...], +) -> ResolvedOp: + """Merge a sequence of operators into a single operator on the union subsystem. + + Each operator is embedded into the merged Hilbert space with :func:`quax.embed` + and composed sequentially with ``@``. Quax's operator ``@`` promotes mixed + types automatically (requires ``rigetti-quax >= 0.6.5``), so an all-``Unitary`` + group yields a ``Unitary`` while a group containing any channel promotes to a + ``SuperOp``. Downstream adapters handle final conversion (e.g. to ``KrausMap`` + for trajectories). + + :param ops_with_subsystems: Ordered list of ``(operator, subsystem)`` pairs + to merge (applied in order: first element is applied first). + :param merged_subsystem: Sorted tuple of qubit indices for the merged operator. + :param dims: Global per-qudit dimensions tuple. + :return: A single ``(operator, merged_subsystem)`` pair. + """ + target_dims = tuple(dims[q] for q in merged_subsystem) + + accumulated: FixedOp | None = None + for op, subsystem in ops_with_subsystems: + positions = tuple(merged_subsystem.index(q) for q in subsystem) + embedded = qx.embed(op, target_dims=target_dims, positions=positions) + accumulated = embedded if accumulated is None else embedded @ accumulated + + if accumulated is None: + raise ValueError("Cannot merge an empty operation group.") + return accumulated, merged_subsystem + + +class _UnionFind: + """Simple union-find (disjoint set) data structure for node grouping.""" + + def __init__(self) -> None: + self._parent: dict[int, int] = {} + self._rank: dict[int, int] = {} + + def make_set(self, x: int) -> None: + self._parent[x] = x + self._rank[x] = 0 + + def find(self, x: int) -> int: + while self._parent[x] != x: + self._parent[x] = self._parent[self._parent[x]] # path compression + x = self._parent[x] + return x + + def union(self, x: int, y: int) -> int: + rx, ry = self.find(x), self.find(y) + if rx == ry: + return rx + if self._rank[rx] < self._rank[ry]: + rx, ry = ry, rx + self._parent[ry] = rx + if self._rank[rx] == self._rank[ry]: + self._rank[rx] += 1 + return rx + + +def compressor_from_dag( + dag: nx.DiGraph, + max_subsystem_size: int, + dims: tuple[int, ...] = (), + *, + barrier_nodes: set[int] | None = None, +) -> Callable[[list[ResolvedOp]], list[ResolvedOp]]: + """Build a compressor that merges operators via greedy edge contraction. + + The algorithm prioritises merging small gates into larger groups, which + reduces the number of distinct subsystem shapes and therefore JIT + compilation time. + + 1. Classify each node as *mergeable* or *barrier* (e.g. measurement). + Barrier nodes are never merged. + 2. Build a priority queue of candidate edge merges sorted by resulting + subsystem size (ascending), so 1-qubit gates are absorbed into + neighbouring multi-qubit groups first. + 3. Greedily contract edges while the merged subsystem fits within + ``max_subsystem_size``. + 4. Return a closure that receives the resolved operator list and produces + a compressed operator list. + + :param dag: Program dependency DAG (nodes indexed 0..N-1, each with + a ``"qubits"`` attribute). + :param max_subsystem_size: Maximum number of qubits in a merged group. + 0 disables merging entirely. + :param dims: Per-qudit dimensions tuple for embedding during merge. + :param barrier_nodes: Optional set of node indices that should not be + merged (e.g. measurement nodes). If ``None``, all nodes are mergeable. + :return: A closure ``compress(ops) -> list[ResolvedOp]``. + """ + n_original = dag.number_of_nodes() + + if max_subsystem_size == 0 or n_original == 0: + passthrough_emit_order = [ + (nk, [nk], tuple(dag.nodes[nk]["qubits"])) for nk in nx.lexicographical_topological_sort(dag) + ] + + def compress_passthrough(ops: list[ResolvedOp]) -> list[ResolvedOp]: + return ops + + compress_passthrough.emit_order = passthrough_emit_order # type: ignore[attr-defined] + logger.info( + "Compressor: %d ops (no merging), max_subsystem_size=0", + n_original, + ) + return compress_passthrough + + if barrier_nodes is None: + barrier_nodes = set() + + # --- Priority-queue based greedy edge contraction --- + uf = _UnionFind() + group_qubits: dict[int, set[int]] = {} + + for nk in dag.nodes: + uf.make_set(nk) + group_qubits[nk] = set(dag.nodes[nk]["qubits"]) + + # Quotient graph over current group roots, kept in lock-step with the + # union-find structure. It starts as a copy of the dependency DAG and is + # contracted whenever two groups merge. It is the authority on whether a + # candidate merge is *convex*: contracting two groups must not reorder any + # operation that lies topologically between them (see ``_contraction_cycles``). + quotient: nx.DiGraph = nx.DiGraph() + quotient.add_nodes_from(dag.nodes) + quotient.add_edges_from(dag.edges) + + def _contraction_cycles(root_a: int, root_b: int) -> bool: + """Return ``True`` if merging two groups would create a cycle. + + The quotient graph is always a DAG, so contracting ``root_a`` and + ``root_b`` introduces a cycle iff there is a directed path of length + ``>= 2`` between them in *either* direction — i.e. some other group is + sandwiched on a dependency path from one to the other. Merging across + such a node would force it to be reordered relative to the merged group, + which is exactly what must be forbidden for barriers (measurements) and + for any non-commuting gate. A direct edge ``root_a -> root_b`` alone is + fine; only an *indirect* path is a problem. + """ + for src, dst in ((root_a, root_b), (root_b, root_a)): + stack = [s for s in quotient.successors(src) if s != dst] + seen = set(stack) + while stack: + node = stack.pop() + if node == dst: + return True + for nxt in quotient.successors(node): + if nxt not in seen: + seen.add(nxt) + stack.append(nxt) + return False + + def _contract_quotient(keep: int, drop: int) -> None: + """Contract ``drop`` into ``keep`` in the quotient graph.""" + for pred in list(quotient.predecessors(drop)): + if pred != keep: + quotient.add_edge(pred, keep) + for succ in list(quotient.successors(drop)): + if succ != keep: + quotient.add_edge(keep, succ) + quotient.remove_node(drop) + + # Build initial candidate heap: (union_size, u, v) + # Smaller union sizes are processed first. + heap: list[tuple[int, int, int]] = [] + for u_node, v_node in dag.edges: + if u_node in barrier_nodes or v_node in barrier_nodes: + continue + union_size = len(group_qubits[u_node] | group_qubits[v_node]) + if union_size <= max_subsystem_size: + heapq.heappush(heap, (union_size, u_node, v_node)) + + while heap: + _, u_node, v_node = heapq.heappop(heap) + ru = uf.find(u_node) + rv = uf.find(v_node) + if ru == rv: + continue + union_qubits = group_qubits[ru] | group_qubits[rv] + if len(union_qubits) > max_subsystem_size: + continue + # Reject merges that would move a barrier (measurement) or a + # non-commuting gate across the merged group. Without this check the + # compressor can fuse two gates that straddle a mid-circuit measurement, + # silently reordering the measurement and corrupting the result. + if _contraction_cycles(ru, rv): + continue + new_root = uf.union(ru, rv) + group_qubits[new_root] = union_qubits + old_root = rv if new_root == ru else ru + if old_root in group_qubits: + del group_qubits[old_root] + _contract_quotient(new_root, old_root) + + # Re-enqueue edges from the newly merged group to its neighbours. + for neighbour in ( + set(dag.successors(u_node)) + | set(dag.predecessors(u_node)) + | set(dag.successors(v_node)) + | set(dag.predecessors(v_node)) + ): + rn = uf.find(neighbour) + if rn == new_root or neighbour in barrier_nodes: + continue + new_union_size = len(group_qubits[new_root] | group_qubits[rn]) + if new_union_size <= max_subsystem_size: + heapq.heappush(heap, (new_union_size, u_node, neighbour)) + + # --- Build merge plan --- + # Order the *members within each group* by a lexicographical topological + # sort of the original DAG (= program order), so ``_merge_ops`` composes + # them in the order they appear in the program. + topo_order = list(nx.lexicographical_topological_sort(dag)) + + root_to_nodes: dict[int, list[int]] = {} + for nk in topo_order: + root = uf.find(nk) + root_to_nodes.setdefault(root, []).append(nk) + + root_to_subsystem: dict[int, tuple[int, ...]] = {} + for root, qubits in group_qubits.items(): + root_to_subsystem[root] = tuple(sorted(qubits)) + + # Emit the *groups* in a topological order of the **quotient** graph rather + # than the original DAG. A merged group can legitimately contain an op that + # precedes a barrier (e.g. a measurement) in program order *together* with + # an op that depends on that barrier — the merge is valid because the + # pre-barrier op commutes with the barrier, so it may be applied after it. + # But emitting the group at its earliest member's position (as an earlier + # version did, by walking the original DAG) would place the *whole* group — + # including the post-barrier op — before the barrier, silently applying + # post-measurement gates before the measurement and corrupting its outcome. + # A quotient topological sort respects every inter-group dependency, so a + # group is emitted only after all groups it depends on. The lexicographic + # key — each group's minimum original node index — keeps barrier singletons + # (measurements are never merged) emitted in program order, so the order of + # ``QuantumInstrument`` ops in the compressed list (and hence the + # measurement-outcome columns in :func:`_apply_trajectory_operations`) + # matches the order of ``MEASURE`` instructions in the program: any ancestor + # group of a measurement has a member preceding it in program order and thus + # a strictly smaller minimum index, so a later measurement can never be + # emitted before an earlier one. + group_min_index: dict[int, int] = {} + for nk in dag.nodes: + root = uf.find(nk) + if root not in group_min_index or nk < group_min_index[root]: + group_min_index[root] = nk + + emit_order: list[tuple[int, list[int], tuple[int, ...]]] = [] + for root in nx.lexicographical_topological_sort(quotient, key=lambda r: group_min_index[r]): + emit_order.append((root, root_to_nodes[root], root_to_subsystem[root])) + + # --- Log the compression statistics --- + n_groups = len(emit_order) + n_multi = sum(1 for _, nodes, _ in emit_order if len(nodes) > 1) + subsystem_sizes = [len(sub) for _, _, sub in emit_order] + avg_subsystem = sum(subsystem_sizes) / len(subsystem_sizes) if subsystem_sizes else 0.0 + max_sub = max(subsystem_sizes) if subsystem_sizes else 0 + + logger.info( + "Compressor: %d ops → %d groups (ratio=%.2f), " + "%d merged groups, avg_subsystem=%.2f, max_subsystem=%d, max_subsystem_size=%d", + n_original, + n_groups, + n_groups / n_original if n_original else 1.0, + n_multi, + avg_subsystem, + max_sub, + max_subsystem_size, + ) + + # --- Build compress closure --- + def compress(ops: list[ResolvedOp]) -> list[ResolvedOp]: + result: list[ResolvedOp] = [] + for _, nodes, subsystem in emit_order: + if len(nodes) == 1: + result.append(ops[nodes[0]]) + else: + group_ops = [(ops[nk][0], ops[nk][1]) for nk in nodes] + merged = _merge_ops(group_ops, subsystem, dims) + result.append(merged) + return result + + # Expose merge recipe: for each group, (nodes_in_topo_order, merged_subsystem). + compress.emit_order = emit_order # type: ignore[attr-defined] + + return compress diff --git a/pyquil/simulation/_simulator.py b/pyquil/simulation/_simulator.py new file mode 100644 index 000000000..0c2c6e7a5 --- /dev/null +++ b/pyquil/simulation/_simulator.py @@ -0,0 +1,1260 @@ +############################################################################## +# Copyright 2016-2026 Rigetti Computing +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +############################################################################## +"""Program simulators backed by quax. + +All simulators share the preprocessing in :class:`ProgramSimulator` (expansion, +dimension inference, ``linearize``/``resolve``/``compress``) and then split into +two families with different execution models: + +* **Grad-able** (:class:`_GradableSimulator`) — jit/grad-friendly evolution of a + compressed ``Unitary``/``SuperOp`` stack; measurements are dephasing SuperOps and + there are no compressor barriers: + + * :class:`PureStateVectorSimulator` — gate-only programs (no noise, measurements, + or resets). + * :class:`DensityMatrixSimulator` — any program, optionally with noise. + +* **Trajectory** (:class:`_TrajectorySimulator`) — Monte Carlo sampling of programs + with measurements and resets; measurements stay as sampled QuantumInstruments: + + * :class:`TrajectorySimulator` — fixed-dimension, vectorized/batched trajectories. + * :class:`DynamicTrajectorySimulator` — eager, per-trajectory dynamic qudit dims. + +The ``compute`` method is the main entry point; for the grad-able family it can be +passed directly to ``jax.jit`` or ``jax.grad``. +""" + +from __future__ import annotations + +import math +from collections.abc import Callable +from dataclasses import dataclass, field +from typing import Any, cast + +import jax +import jax.numpy as jnp +import numpy as np +import quax as qx +from jax import Array +from quax._apply import _sample_kraus_map_trajectory + +from pyquil.api import MemoryMap +from pyquil.noise._noise_model import NoiseModelLike +from pyquil.quil import Program +from pyquil.quilbase import Measurement, Reset, ResetQubit +from pyquil.simulation._resolver import ( + MeasurementMode, + ParametricGate, + ResolvedOp, + TrajectoryOp, + adapt_for_density_matrix, + adapt_for_trajectory, + build_dag, + compressor_from_dag, + enumerate_bases, + resolve_for_gradable, + resolve_for_trajectory, +) + + +def _pad_matrix(mat: Array, *target: int) -> Array: + """Zero-pad the trailing dimensions of *mat* up to *target* sizes. + + Only the last ``len(target)`` axes are padded (top-left aligned); any + leading (ensemble/stack) axes are left untouched. + """ + if all(mat.shape[-len(target) + i] == t for i, t in enumerate(target)): + return mat + pad = [(0, 0)] * (mat.ndim - len(target)) + [ + (0, t - mat.shape[mat.ndim - len(target) + i]) for i, t in enumerate(target) + ] + return jnp.pad(mat, pad) + + +# ══════════════════════════════════════════════════════════ +# Base class +# ══════════════════════════════════════════════════════════ + + +class ProgramSimulator: + """Shared preprocessing base for program simulators. + + Handles the pipeline common to every backend: circuit expansion, qubit + ordering, dimension inference, and building the ``linearize``/``resolve``/ + ``compress`` closures. Two family bases specialise it — :class:`_GradableSimulator` + (state-vector, density-matrix) and :class:`_TrajectorySimulator` (trajectory, + dynamic trajectory) — supplying only the execution machinery each needs. + + Subclasses override :meth:`_validate` and :meth:`compute`. + + Instances are treated as immutable after construction. + """ + + def __init__( + self, + program: Program, + qubits: list[int] | None = None, + *, + noise_model: NoiseModelLike | None = None, + max_subsystem_size: int = 2, + dims: tuple[int, ...] | None = None, + measurement: MeasurementMode = "instrument", + ) -> None: + self._validate(program) + + if qubits is None: + qubits = sorted(program.get_qubit_indices()) + self.qubits = qubits + self.n_qubits = len(qubits) + + # Expand the program into operators, inferring register dimensions when not + # supplied. The measurement mode selects the resolver family: the grad-able + # simulators resolve measurements to dephasing SuperOps, the trajectory + # simulators keep them as sampled QuantumInstruments. + if measurement == "superop": + res = resolve_for_gradable(program, noise_model, qubits, dims) + else: + res = resolve_for_trajectory(program, noise_model, qubits, dims) + self.dims = res.dims + self._resolve_fn = res.resolve + self._expanded_ops = tuple(res.ops) + self._raw_subsystems = tuple(res.subsystems) + self._n_params = len(res.param_refs) + param_refs = res.param_refs + + # Build linearizer from parameter references discovered during expansion. + def linearize(memory_map: MemoryMap) -> Array: + if not param_refs: + return jnp.array([], dtype=float) + values = [float(memory_map[name][offset]) for name, offset in param_refs] + return jnp.array(values, dtype=float) + + self._linearize_fn = linearize + + dag = build_dag(res.subsystems) + + # Measurements (QuantumInstrument) must not be merged by the compressor. + # Under the grad-able ("superop") mode no instruments are produced, so this + # is naturally empty and the compressor is free to merge every operation. + barrier_nodes = {i for i, op in enumerate(res.ops) if isinstance(op, qx.QuantumInstrument)} + + self._compress_fn = compressor_from_dag( + dag, + max_subsystem_size, + dims=self.dims, + barrier_nodes=barrier_nodes, + ) + + # -- hook for subclass validation --------------------- + + def _validate(self, program: Program) -> None: + """Override to reject unsupported instructions.""" + + # -- public pipeline methods -------------------------- + + def linearize(self, memory_map: MemoryMap) -> Array: + """Convert a memory map to a flat JAX parameter vector.""" + return self._linearize_fn(memory_map) + + def _default_params(self, params: Array | None) -> Array: + """Return *params*, or the empty-memory-map vector when ``None``. + + Lets callers omit ``params`` for parameter-free programs (where the + vector is empty). For a parametric program the empty memory map raises + on the missing register, which is clearer than silently using zeros. + """ + return self.linearize({}) if params is None else params + + def resolve(self, params: Array) -> list[ResolvedOp]: + """Resolve parameters into one operator per DAG node.""" + return self._resolve_fn(params) + + def compress(self, resolved: list[ResolvedOp]) -> list[ResolvedOp]: + """Merge operators via greedy edge contraction.""" + return self._compress_fn(resolved) + + def compute(self, params: Array | None = None, **kwargs: Any) -> Any: + """Compute the simulation result. Subclasses must override.""" + raise NotImplementedError + + +# ══════════════════════════════════════════════════════════ +# Grad-able family base (state-vector / density-matrix) +# ══════════════════════════════════════════════════════════ + + +class _GradableSimulator(ProgramSimulator): + """Base for the jit/grad-friendly state-vector and density-matrix simulators. + + Adds the compressed-stack evolution machinery. It enumerates the distinct + *base subsystems* the compressor emits (:func:`enumerate_bases`) and applies the + operator stack with a :func:`jax.lax.scan` whose body dispatches each operator to + the :func:`jax.lax.switch` branch for its base (``self._branches``, keyed by + ``self._idx_arr``), so the compiled graph size scales with the number of distinct + base subsystems rather than the number of operations. + + Measurements are dephasing SuperOps (``measurement="superop"``) and the compressor + runs with no barriers, so former-measurement operators merge freely. + """ + + def __init__( + self, + program: Program, + qubits: list[int] | None = None, + *, + noise_model: NoiseModelLike | None = None, + max_subsystem_size: int = 2, + dims: tuple[int, ...] | None = None, + ) -> None: + super().__init__( + program, + qubits, + noise_model=noise_model, + max_subsystem_size=max_subsystem_size, + dims=dims, + measurement="superop", + ) + + # The merge structure depends only on the DAG (not on parameter values), so + # the base subsystems can be read straight off the compressor's emit order — + # no ``resolve``/``compress`` probe is required. + self.bases, self.op_index = enumerate_bases(self._compress_fn.emit_order) # type: ignore[attr-defined] + self.base_dims = [tuple(self.dims[q] for q in base) for base in self.bases] + self.base_total_dim = [math.prod(d) for d in self.base_dims] + self.d_max = max(self.base_total_dim) if self.base_total_dim else 1 + self._idx_arr = jnp.asarray(self.op_index, dtype=jnp.int32) + + # Whether any gate matrix depends on a runtime parameter. When it does not, + # the compressed operator stack is a compile-time constant and can be + # materialised eagerly (outside the traced graph), which avoids XLA + # constant-folding/autotuning a large ``compose_operator`` subgraph — the + # dominant JIT cost on accelerators for deep, literal-angle programs. + self._has_params = self._n_params > 0 + + def apply(self, state: Any, op_stack: Array) -> Any: + """Apply a stack of operator matrices to *state* via a scan + switch. + + Each operator is dispatched to the switch branch for its base subsystem + (``self._branches``, keyed by ``self._idx_arr``), so the compiled graph size + scales with the number of distinct base subsystems rather than the number of + operations. The state-vector and density-matrix simulators differ only in + their branch and state types. + """ + branches = self._branches # type: ignore[attr-defined] + + def body(state: Any, xs: tuple[Array, Array]) -> tuple[Any, None]: + op_mat, sidx = xs + return jax.lax.switch(sidx, branches, op_mat, state), None + + state, _ = jax.lax.scan(body, state, (op_stack, self._idx_arr)) + return state + + +# ══════════════════════════════════════════════════════════ +# Vectorized gate construction +# ══════════════════════════════════════════════════════════ + + +def _embed_unitary_to_group( + op: qx.Unitary, + target_dims: tuple[int, ...], + positions: tuple[int, ...], + d_max: int, +) -> Array: + """Embed *op* into a merge group and pad to the uniform stack width ``d_max``. + + :func:`quax.embed` places ``op`` (whose qudits map to ``positions`` within the + group) into the group Hilbert space ``target_dims``; the trailing pad to + ``d_max`` — the stack width shared by every group — is plain array padding with + no quax equivalent. This is traceable, so it serves both the eager + constant-gate path and the vmapped parametric path. + """ + embedded = qx.embed(op, target_dims=target_dims, positions=positions).matrix + return jnp.pad(embedded, [(0, d_max - s) for s in embedded.shape]) + + +@dataclass +class _GateBatch: + """A set of gates sharing one constructor, concrete layout, and embedding. + + Members differ only in which entries of the parameter vector feed their + free arguments, so all of them are built with a single ``jax.vmap``. This + keeps the traced graph proportional to the number of distinct gate *kinds* + rather than the number of gates. + """ + + gate_fn: Callable[..., qx.Unitary] + n_args: int + #: ``(slot, value)`` for each compile-time-constant argument. + concrete_args: tuple[tuple[int, float], ...] + #: Per-qudit dimensions of the merge group each member embeds into. + target_dims: tuple[int, ...] + #: Positions within the group occupied by the gate's qudits. + group_positions: tuple[int, ...] + #: Uniform stack width every embedded matrix is padded to. + d_max: int + #: Sorted-array positions this batch fills, one per member. + positions: list[int] = field(default_factory=list) + #: Parameter-vector index for each free argument, one list per member. + param_indices: list[list[int]] = field(default_factory=list) + + def builder(self) -> Callable[[Array], Array]: + """Return ``params -> (n_members, d_max, d_max)`` embedded gate matrices.""" + concrete = {slot for slot, _ in self.concrete_args} + free_slots = [j for j in range(self.n_args) if j not in concrete] + gate_fn, n_args, concrete_args = self.gate_fn, self.n_args, self.concrete_args + target_dims, group_positions, d_max = self.target_dims, self.group_positions, self.d_max + param_indices = jnp.asarray(self.param_indices) # (n_members, n_free) + + def single(free_values: Array) -> Array: + args: list[Any] = [None] * n_args + for slot, val in concrete_args: + args[slot] = val + for k, slot in enumerate(free_slots): + args[slot] = free_values[k] + return _embed_unitary_to_group(gate_fn(*args), target_dims, group_positions, d_max) + + batched = jax.vmap(single) + return lambda params: batched(params[param_indices]) + + +def _make_group_fold(group_start: list[int], n_ops: int, d_max: int) -> Callable[[Array], Array]: + """Build the per-group matrix-product fold. + + ``fold(raw)`` takes ``(n_ops, d_max, d_max)`` embedded gate matrices laid out + in group order and returns ``(n_groups, d_max, d_max)`` — the ordered matrix + product of each group's gates. Groups are gathered into a padded + ``(n_groups, max_size, ...)`` array (short groups padded with an identity + sentinel) so every group folds under a single ``jax.vmap``. + """ + n_groups = len(group_start) - 1 + sizes = np.diff(group_start) + max_size = int(sizes.max()) if n_groups else 1 + + # gather[g, k] = sorted position of group g's k-th gate, or n_ops (the + # identity sentinel appended in ``fold``) for padding. + gather = np.full((n_groups, max_size), n_ops, dtype=np.int32) + for g in range(n_groups): + gather[g, : sizes[g]] = np.arange(group_start[g], group_start[g + 1]) + gather_jax = jnp.asarray(gather) + eye = jnp.eye(d_max, dtype=complex) + + def group_product(mats: Array) -> Array: + final, _ = jax.lax.scan(lambda acc, m: (m @ acc, None), eye, mats) + return final + + def fold(raw: Array) -> Array: + padded = jnp.concatenate([raw, eye[None]], axis=0)[gather_jax] # (n_groups, max_size, d, d) + return jax.vmap(group_product)(padded) + + return fold + + +def _build_vectorized_unitary_constructor( + expanded_ops: tuple[Any, ...], + raw_subsystems: tuple[tuple[int, ...], ...], + emit_order: list[tuple[int, list[int], tuple[int, ...]]], + dims: tuple[int, ...], + d_max: int, +) -> Callable[[Array], Array]: + """Build a JIT-friendly constructor for the compressed unitary stack. + + Returns ``build(params) -> (n_groups, d_max, d_max)``: one matrix per merge + group, equal to ``compress(resolve(params))`` but assembled so the traced + graph scales with the number of distinct gate *kinds* rather than the number + of gates. Each gate is embedded into its merge group's Hilbert space, then + the gates of every group are folded together via :func:`_make_group_fold`. + """ + n_ops = len(expanded_ops) + + # Lay raw ops out in group order: group g occupies sorted positions + # [group_start[g], group_start[g + 1]). + sorted_indices: list[int] = [] + group_subsystems: list[tuple[int, ...]] = [] # merge subsystem per sorted position + group_start: list[int] = [0] + for _, nodes, subsystem in emit_order: + for nk in nodes: + sorted_indices.append(nk) + group_subsystems.append(subsystem) + group_start.append(len(sorted_indices)) + + # Plan how each op's embedded matrix is produced: parametric gates are + # collected into vmapped batches; constant gates are embedded eagerly. + batches: dict[tuple, _GateBatch] = {} + const_positions: list[int] = [] + const_mats: list[Array] = [] + for pos, raw_idx in enumerate(sorted_indices): + op = expanded_ops[raw_idx] + op_sub = raw_subsystems[raw_idx] + grp_sub = group_subsystems[pos] + # Where the op's qudits sit within the merge group, and the group's dims. + target_dims = tuple(dims[q] for q in grp_sub) + group_positions = tuple(grp_sub.index(q) for q in op_sub) + if isinstance(op, ParametricGate): + # Key by embedding *type* (op dims + group dims + positions), not + # physical qubits: embeddings that trace to the same graph share a vmap. + embed_key = (tuple(dims[q] for q in op_sub), target_dims, group_positions) + concrete_args = tuple((j, op.concrete_values[j]) for j, pi in enumerate(op.param_indices) if pi < 0) + key = (id(op.gate_fn), concrete_args, embed_key) + batch = batches.get(key) + if batch is None: + batch = _GateBatch( + gate_fn=op.gate_fn, + n_args=len(op.param_indices), + concrete_args=concrete_args, + target_dims=target_dims, + group_positions=group_positions, + d_max=d_max, + ) + batches[key] = batch + batch.positions.append(pos) + batch.param_indices.append([pi for pi in op.param_indices if pi >= 0]) + else: + const_positions.append(pos) + const_mats.append(_embed_unitary_to_group(op, target_dims, group_positions, d_max)) + + builders = [(np.asarray(b.positions), b.builder()) for b in batches.values()] + const_pos_arr = np.asarray(const_positions) if const_positions else None + const_stack = jnp.stack(const_mats) if const_mats else None + + fold = _make_group_fold(group_start, n_ops, d_max) + + def build(params: Array) -> Array: + raw = jnp.zeros((n_ops, d_max, d_max), dtype=complex) + for positions, builder in builders: + raw = raw.at[positions].set(builder(params)) + if const_stack is not None: + raw = raw.at[const_pos_arr].set(const_stack) + return fold(raw) + + return build + + +# ══════════════════════════════════════════════════════════ +# Pure state-vector simulator +# ══════════════════════════════════════════════════════════ + + +class PureStateVectorSimulator(_GradableSimulator): + """Simulator for gate-only programs (no noise, measurements, or resets). + + All methods are jit- and grad-friendly:: + + sim = PureStateVectorSimulator(program) + params = sim.linearize(memory_map) + psi = jax.jit(sim.compute)(params) + U = jax.jit(sim.unitary)(params) + """ + + def __init__( + self, + program: Program, + qubits: list[int] | None = None, + *, + max_subsystem_size: int = 2, + ) -> None: + super().__init__(program, qubits, noise_model=None, max_subsystem_size=max_subsystem_size) + self._psi0 = qx.zero_state_vector(dims=self.dims) + + # Vectorized gate construction (vmap per gate type) followed by a + # segmented matmul scan for compression. This gives both fast + # compilation (small traced graph) AND fast runtime (compressed + # op count in the state-evolution scan). + emit_order = getattr(self._compress_fn, "emit_order", []) + self._vmapped_build_fn = _build_vectorized_unitary_constructor( + self._expanded_ops, + self._raw_subsystems, + emit_order, + self.dims, + self.d_max, + ) + + # One switch branch per distinct base subsystem: it rebuilds a Unitary + # from the padded matrix slice for its base and applies it to the state. + def unitary_branch( + base: tuple[int, ...], base_dims: tuple[int, ...], db: int + ) -> Callable[[Array, qx.StateVector], qx.StateVector]: + def branch(op_mat: Array, psi: qx.StateVector) -> qx.StateVector: + unitary = qx.Unitary.from_matrix(op_mat[:db, :db], (base_dims, base_dims)) + return qx.targeted_apply_unitary(unitary, psi, base) + + return branch + + self._branches = [ + unitary_branch(base, base_dims, db) + for base, base_dims, db in zip(self.bases, self.base_dims, self.base_total_dim, strict=True) + ] + + def _validate(self, program: Program) -> None: + for inst in program.instructions: + if isinstance(inst, Measurement): + raise ValueError(f"PureStateVectorSimulator does not support measurements. Found: {inst}") + if isinstance(inst, (Reset, ResetQubit)): + raise ValueError(f"PureStateVectorSimulator does not support resets. Found: {inst}") + + def compute(self, params: Array | None = None) -> qx.StateVector: # type: ignore[override] + """Compute the final state vector. + + Operators are stacked into a single array and applied with a + :func:`jax.lax.scan` whose body dispatches each operator to the right + base subsystem via :func:`jax.lax.switch`. This keeps the traced graph + size proportional to the number of distinct base subsystems rather than + the number of operations, dramatically reducing JIT compilation time + for large programs. + + :param params: Flat parameter vector from :meth:`linearize`. Omit (or + pass ``None``) for a parameter-free program. + :return: The final state vector. + """ + # No operations (e.g. empty program) → the initial state is the result. + if not self._branches: + return self._psi0 + + # Vectorized construction: build embedded matrices via vmap, then + # compose within each merge group via a parallel fold. + op_stack = self._vmapped_build_fn(self._default_params(params)) + return self.apply(self._psi0, op_stack) + + def __call__(self, params: Array | None = None) -> qx.StateVector: + return self.compute(params) + + def unitary(self, params: Array | None = None) -> qx.Unitary: + """Compute the full program unitary. + + :param params: Flat parameter vector from :meth:`linearize`. Omit (or + pass ``None``) for a parameter-free program. + :return: The full unitary matrix. + """ + resolved = self.resolve(self._default_params(params)) + compressed = self.compress(resolved) + + accumulated: qx.Unitary | None = None + for op, subsystem in compressed: + embedded = qx.embed(op, target_dims=self.dims, positions=subsystem) + if accumulated is None: + accumulated = embedded + else: + accumulated = embedded @ accumulated + + if accumulated is None: + d = math.prod(self.dims) + return qx.Unitary.from_matrix(jnp.eye(d, dtype=complex), (self.dims, self.dims)) + + return accumulated + + +# ══════════════════════════════════════════════════════════ +# Density-matrix simulator +# ══════════════════════════════════════════════════════════ + + +class DensityMatrixSimulator(_GradableSimulator): + """Density-matrix simulator for any program, optionally with noise. + + All methods are jit- and grad-friendly:: + + sim = DensityMatrixSimulator(program, noise_model=noise_model) + params = sim.linearize(memory_map) + rho = jax.jit(sim.compute)(params) + """ + + def __init__( + self, + program: Program, + qubits: list[int] | None = None, + *, + noise_model: NoiseModelLike | None = None, + max_subsystem_size: int = 2, + ) -> None: + super().__init__(program, qubits, noise_model=noise_model, max_subsystem_size=max_subsystem_size) + self._rho0 = qx.zero_state_matrix(dims=self.dims) + + # One switch branch per distinct base subsystem: it rebuilds a SuperOp + # from the padded matrix slice for its base and applies it to the state. + def superop_branch( + base: tuple[int, ...], base_dims: tuple[int, ...], db2: int + ) -> Callable[[Array, qx.DensityMatrix], qx.DensityMatrix]: + def branch(op_mat: Array, rho: qx.DensityMatrix) -> qx.DensityMatrix: + superop = qx.SuperOp.from_matrix(op_mat[:db2, :db2], (base_dims, base_dims)) + return qx.targeted_apply_superop(superop, rho, base) + + return branch + + self._branches = [ + superop_branch(base, base_dims, db * db) + for base, base_dims, db in zip(self.bases, self.base_dims, self.base_total_dim, strict=True) + ] + + # See :class:`PureStateVectorSimulator`: for parameter-free programs the + # superoperator stack is constant, so it can be built once and reused to keep + # the traced graph to just the scan over a concrete array. It is materialised + # lazily on the first :meth:`compute` call rather than eagerly here, so + # constructing the simulator stays cheap. + self._const_op_stack: Array | None = None + + def _stack_superops(self, resolved: list[ResolvedOp]) -> Array: + """Compress, promote each op to a SuperOp, and stack.""" + compressed = self.compress(resolved) + superops = adapt_for_density_matrix(compressed) + d_max2 = self.d_max * self.d_max + mats = [_pad_matrix(superop.matrix, d_max2, d_max2) for superop, _ in superops] + return jnp.stack(mats, axis=0) + + def compute(self, params: Array | None = None) -> qx.DensityMatrix: # type: ignore[override] + """Compute the final density matrix. + + Superoperators are stacked and applied with a :func:`jax.lax.scan` + whose body dispatches to the correct base subsystem via + :func:`jax.lax.switch`, keeping the compiled graph size proportional to + the number of distinct base subsystems. + + :param params: Flat parameter vector from :meth:`linearize`. Omit (or + pass ``None``) for a parameter-free program. + :return: The final density matrix. + """ + if not self._has_params and self.op_index: + # Parameter-free program: build the constant superop stack once, then reuse. + if self._const_op_stack is None: + self._const_op_stack = self._stack_superops(self.resolve(jnp.zeros(0))) + op_stack = self._const_op_stack + else: + resolved = self.resolve(self._default_params(params)) + if not resolved: + return self._rho0 + op_stack = self._stack_superops(resolved) + return self.apply(self._rho0, op_stack) + + def __call__(self, params: Array | None = None) -> qx.DensityMatrix: + return self.compute(params) + + +# ══════════════════════════════════════════════════════════ +# Trajectory family base +# ══════════════════════════════════════════════════════════ + + +class _TrajectorySimulator(ProgramSimulator): + """Base for the Monte-Carlo trajectory simulators. + + Resolves measurements as sampled ``QuantumInstrument`` operators + (``measurement="instrument"``), keeps them out of merges via compressor + barriers, and adapts the compressed operators to the trajectory-native + ``Unitary``/``KrausMap``/``QuantumInstrument`` types. Unlike the grad-able + family it builds no base-subsystem switch table — each concrete simulator + builds its own trajectory kernel. + """ + + def __init__( + self, + program: Program, + qubits: list[int] | None = None, + *, + noise_model: NoiseModelLike | None = None, + max_subsystem_size: int = 2, + kraus_truncation_threshold: float = 1e-6, + dims: tuple[int, ...] | None = None, + ) -> None: + super().__init__( + program, + qubits, + noise_model=noise_model, + max_subsystem_size=max_subsystem_size, + dims=dims, + measurement="instrument", + ) + self._kraus_truncation_threshold = kraus_truncation_threshold + + def adapt(self, compressed: list[ResolvedOp]) -> list[TrajectoryOp]: + """Convert compressed ops to trajectory-compatible types.""" + return adapt_for_trajectory(compressed, self._kraus_truncation_threshold) + + +# ══════════════════════════════════════════════════════════ +# Trajectory simulator +# ══════════════════════════════════════════════════════════ + + +class TrajectorySimulator(_TrajectorySimulator): + """Monte Carlo trajectory simulator for programs with measurements and resets. + + The ``compute`` method requires a JAX PRNG key. The number of + trajectories is determined by the key shape: a scalar key runs one + trajectory; a batch of keys ``jax.random.split(key, n)`` runs *n* + trajectories in parallel:: + + sim = TrajectorySimulator(program, noise_model=noise_model) + params = sim.linearize(memory_map) + + # Single trajectory + key = jax.random.key(0) + psi, outcomes = sim.compute(params, key) + + # Batched trajectories + keys = jax.random.split(jax.random.key(0), 100) + psi_batch, outcomes_batch = sim.compute(params, keys) + + The ``sample`` method is a convenience wrapper that runs trajectories + in batches and discards state vectors, returning only measurement + outcomes. + """ + + def __init__( + self, + program: Program, + qubits: list[int] | None = None, + *, + noise_model: NoiseModelLike | None = None, + max_subsystem_size: int = 2, + kraus_truncation_threshold: float = 1e-6, + devices: list[jax.Device] | None = None, + dims: tuple[int, ...] | None = None, + ) -> None: + super().__init__( + program, + qubits, + noise_model=noise_model, + max_subsystem_size=max_subsystem_size, + kraus_truncation_threshold=kraus_truncation_threshold, + dims=dims, + ) + self._devices = devices if devices is not None else jax.devices() + + def compute( # type: ignore[override] + self, + params: Array | None = None, + key: Array | None = None, + ) -> tuple[qx.StateVector, Array]: + """Run trajectory simulation. + + :param params: Flat parameter vector from :meth:`linearize`. Omit (or + pass ``None``) for a parameter-free program. + :param key: JAX PRNG key (required). Scalar key → single trajectory. + Batch of keys (from ``jax.random.split``) → batched trajectories. + :return: Tuple of ``(state_vector, measurement_outcomes)``. + """ + if key is None: + raise ValueError("TrajectorySimulator.compute requires a JAX PRNG key.") + operations = self.adapt(self.compress(self.resolve(self._default_params(params)))) + + if key.ndim == 0: + psi = qx.zero_state_vector(dims=self.dims) + else: + n_traj = key.shape[0] + psi = qx.zero_state_vector(dims=self.dims, ensemble_size=(n_traj,)) + + return _build_trajectory_kernel(operations, self.dims)(psi, key) + + def __call__(self, params: Array | None = None, key: Array | None = None) -> tuple[qx.StateVector, Array]: + return self.compute(params, key) + + def sample( + self, + params: Array | None = None, + num_trajectories: int = 1000, + batch_size: int = 250, + random_seed: int = 0, + ) -> Array: + """Run trajectory simulation in batches, returning only measurement outcomes. + + State vectors are discarded after each batch, making this scalable + to arbitrarily many trajectories. When multiple devices are + available the batch is run **data-parallel** via :func:`jax.pmap`: each + device runs an independent replica of the trajectory kernel on its own + slice of trajectories, with no cross-device communication. + + :param params: Flat parameter vector from :meth:`linearize`. Omit (or + pass ``None``) for a parameter-free program. + :param num_trajectories: Total number of trajectories to simulate. + :param batch_size: Trajectories per device per batch. With ``n`` devices + each batch runs ``n * batch_size`` trajectories concurrently; on a + single device this is simply the batch size. + :param random_seed: Seed for the JAX PRNG. + :return: Measurement outcomes with shape ``(num_trajectories, n_measurements)``. + """ + operations = self.adapt(self.compress(self.resolve(self._default_params(params)))) + + _, all_outcomes = _run_batched_trajectories( + operations, + num_trajectories, + batch_size, + random_seed, + keep_states=False, + dims=self.dims, + devices=self._devices, + ) + + if len(all_outcomes) == 1: + return all_outcomes[0] + return jnp.concatenate(all_outcomes, axis=0) + + +# ══════════════════════════════════════════════════════════ +# Trajectory simulation internals +# ══════════════════════════════════════════════════════════ + + +def _op_to_kraus_matrix( + op: qx.Unitary | qx.KrausMap | qx.QuantumInstrument, +) -> tuple[Array, int, bool]: + """Convert a single trajectory operator to a padded Kraus matrix. + + Every trajectory operator is expressed as a Kraus map so that a single, + uniform ``jax.lax.switch`` branch (Kraus trajectory sampling) can handle + all operation types: + + - ``qx.Unitary`` → a one-operator Kraus map. + - ``qx.KrausMap`` → itself. + - ``qx.QuantumInstrument`` → its outcome and Kraus axes are merged into a + single Kraus axis (replicating the flattening in + :func:`quax.targeted_apply_instrument_to_state_vector`). The returned + *divisor* is the number of Kraus operators per outcome, so the sampled + Kraus index ``k`` decodes to the measurement outcome ``k // divisor``. + + :param op: The operator (already acting on its base subsystem). + :return: ``(matrix, divisor, is_measurement)`` where ``matrix`` has shape + ``(n_kraus, d, d)``. + """ + match op: + case qx.Unitary(): + return qx.to_kraus(op).matrix, 1, False + case qx.KrausMap(): + return op.matrix, 1, False + case qx.QuantumInstrument(): + kraus_mats = [qx.superop_to_kraus(op.outcome_superop(i)[0]).matrix for i in range(op.num_outcomes)] + n_kraus_per_outcome = kraus_mats[0].shape[-3] + merged = jnp.concatenate(kraus_mats, axis=-3) + return merged, n_kraus_per_outcome, True + case _: + raise TypeError(f"Unsupported operator type: {type(op)}") + + +TrajectoryRun = Callable[[qx.StateVector, Array], tuple[qx.StateVector, Array]] + +#: A ``run(op_mat, psi, key) -> (psi, sampled_index)`` switch branch for one subsystem. +KrausBranch = Callable[[Array, qx.StateVector, Array], tuple[qx.StateVector, Array]] + + +@dataclass +class _KrausOpStack: + """The batch-invariant, scan-ready form of a trajectory operation sequence. + + Every operation is expressed as a zero-padded Kraus matrix so the scan can index + a single homogeneous stack (quax has no ragged/heterogeneous KrausMap stacking; + operators live on different subsystems with different Kraus counts). The padding + to a uniform ``(max_k, d_max, d_max)`` is inherent to that single-``lax.scan`` + design — zero-padded Kraus operators carry zero Born probability and are simply + never sampled — and each :data:`KrausBranch` re-slices ``[:, :db, :db]`` back to + its subsystem before rebuilding a ``KrausMap``. + """ + + #: ``(n_ops, max_k, d_max, d_max)`` padded Kraus matrices, in application order. + op_stack: Array + #: ``(n_ops,)`` int32 — the :attr:`branches` index for each operation. + branch_arr: Array + #: One switch branch per distinct subsystem (Kraus trajectory sampling). + branches: list[KrausBranch] + #: Per-operation Kraus-count divisor; measurement outcome = ``sampled_index // divisor``. + divisors: list[int] + #: Indices (into the op sequence) of the measurement operations, in program order. + measure_positions: list[int] + + @property + def n_ops(self) -> int: + return len(self.divisors) + + +def _build_kraus_op_stack(operations: list[TrajectoryOp], dims: tuple[int, ...]) -> _KrausOpStack: + """Convert a trajectory operation sequence into a scan-ready :class:`_KrausOpStack`. + + This is all the batch-invariant work: enumerating distinct subsystems (one switch + branch each), promoting each operator to its register dimension, converting it to a + Kraus matrix (:func:`_op_to_kraus_matrix`), and padding everything to one uniform + stack. Separated from :func:`_build_trajectory_kernel` so the kernel itself only + holds the scan / key-folding / outcome-decoding logic. + + :param operations: Ordered list of ``(operator, subsystem)`` pairs. + :param dims: Per-qudit register dimensions (must match the ``psi`` passed to the kernel). + """ + distinct_subsystems: list[tuple[int, ...]] = [] + sub_to_branch: dict[tuple[int, ...], int] = {} + for _, subsystem in operations: + if subsystem not in sub_to_branch: + sub_to_branch[subsystem] = len(distinct_subsystems) + distinct_subsystems.append(subsystem) + + def make_branch(base: tuple[int, ...]) -> KrausBranch: + base_dims = tuple(dims[q] for q in base) + db = math.prod(base_dims) + + def branch(op_mat: Array, psi: qx.StateVector, key: Array) -> tuple[qx.StateVector, Array]: + kraus_map = qx.KrausMap.from_matrix(op_mat[:, :db, :db], (base_dims, base_dims)) + return cast(tuple[qx.StateVector, Array], _sample_kraus_map_trajectory(kraus_map, psi, key, base)) + + return branch + + branches = [make_branch(subsystem) for subsystem in distinct_subsystems] + + kraus_mats: list[Array] = [] + divisors: list[int] = [] + measure_positions: list[int] = [] + branch_index: list[int] = [] + for i, (op, subsystem) in enumerate(operations): + # Promote each operator to the register dimension on its subsystem (identity on the + # higher levels). Without this, an op authored at a lower dimension than the register + # (e.g. a qubit-dimension channel on a register promoted to qutrits by a leakage model) + # would be zero-padded to ``d_max`` instead — silently wrong on the high levels, and a + # reshape error when ``d_max`` is below the branch's dimension. + target_dims = tuple(dims[q] for q in subsystem) + if op.dims[0] != target_dims: + op = qx.promote(op, target_dims) + mat, divisor, is_measure = _op_to_kraus_matrix(op) + kraus_mats.append(mat) + divisors.append(divisor) + branch_index.append(sub_to_branch[subsystem]) + if is_measure: + measure_positions.append(i) + + max_k = max(mat.shape[0] for mat in kraus_mats) + d_max = max(mat.shape[-1] for mat in kraus_mats) + op_stack = jnp.stack([_pad_matrix(mat, max_k, d_max, d_max) for mat in kraus_mats], axis=0) + return _KrausOpStack( + op_stack=op_stack, + branch_arr=jnp.asarray(branch_index, dtype=jnp.int32), + branches=branches, + divisors=divisors, + measure_positions=measure_positions, + ) + + +def _build_trajectory_kernel(operations: list[TrajectoryOp], dims: tuple[int, ...]) -> TrajectoryRun: + """Build a reusable, jitted trajectory kernel from *operations*. + + All batch-invariant work happens once in :func:`_build_kraus_op_stack`. The + returned ``run(psi, key)`` wraps the scan over that padded Kraus stack in + :func:`jax.jit`, so repeated calls with matching ``psi``/``key`` shapes (e.g. one + per sampling batch) reuse a single compilation instead of re-tracing — this is + what turns the previously spiky, recompile-per-batch GPU usage into a single + upfront compile. + + Measurements are handled uniformly by flattening a quantum instrument so that + sampling a Kraus index also selects an outcome (``index // divisor``). + Per-operation keys are derived lazily via ``jax.random.fold_in`` so the key array + is never materialised in full (sharding-friendly). + + :param operations: Ordered list of ``(operator, subsystem)`` pairs. + :param dims: Per-qudit register dimensions (must match the ``psi`` passed to + ``run``). + :return: ``run(psi, key) -> (final_state_vector, measurement_outcomes)`` where + ``measurement_outcomes`` has shape ``(*ensemble, n_measurements)``, + dtype int32. ``key`` is a scalar PRNG key or a per-trajectory key vector. + """ + if not operations: + + def run_empty(psi: qx.StateVector, key: Array) -> tuple[qx.StateVector, Array]: + return psi, jnp.empty((*psi.ensemble_size, 0), dtype=jnp.int32) + + return run_empty + + stack = _build_kraus_op_stack(operations, dims) + branches = stack.branches + op_stack = stack.op_stack + branch_arr = stack.branch_arr + divisors = stack.divisors + measure_positions = stack.measure_positions + op_indices = jnp.arange(stack.n_ops, dtype=jnp.int32) + + @jax.jit + def run(psi: qx.StateVector, key: Array) -> tuple[qx.StateVector, Array]: + ensemble_size = psi.ensemble_size + if ensemble_size: + per_traj_keys = key if key.ndim > 0 else jax.random.split(key, ensemble_size[0]) + else: + per_traj_keys = None + + def body(psi_c: qx.StateVector, xs: tuple[Array, Array, Array]) -> tuple[qx.StateVector, Array]: + op_mat, bidx, i = xs + if per_traj_keys is not None: + op_key = jax.vmap(lambda k: jax.random.fold_in(k, i))(per_traj_keys) + else: + op_key = jax.random.fold_in(key, i) + psi_c, sampled_idx = jax.lax.switch(bidx, branches, op_mat, psi_c, op_key) + return psi_c, sampled_idx.astype(jnp.int32) + + psi_out, sampled = jax.lax.scan(body, psi, (op_stack, branch_arr, op_indices)) + + if measure_positions: + outcomes = jnp.stack([sampled[p] // divisors[p] for p in measure_positions], axis=-1) + else: + outcomes = jnp.empty((*ensemble_size, 0), dtype=jnp.int32) + return psi_out, outcomes + + return run + + +def _apply_trajectory_operations( + operations: list[TrajectoryOp], + psi: qx.StateVector, + key: Array, +) -> tuple[qx.StateVector, Array]: + """Build a one-off trajectory kernel for *operations* and apply it to *psi*. + + Convenience wrapper around :func:`_build_trajectory_kernel` for callers that + run a single (batch of) trajectories; batched sampling reuses one kernel + across batches instead — see :func:`_run_batched_trajectories`. + """ + return _build_trajectory_kernel(operations, psi.dims)(psi, key) + + +def _run_batched_trajectories( + operations: list[TrajectoryOp], + num_trajectories: int, + batch_size: int, + random_seed: int, + keep_states: bool = True, + *, + dims: tuple[int, ...], + devices: list[jax.Device] | None = None, +) -> tuple[list[qx.StateVector] | None, list[Array]]: + """Run trajectory simulation in batches, data-parallel across devices. + + When *devices* contains more than one device the batch is run **data-parallel** + via :func:`jax.pmap`: each device runs an independent replica of the trajectory + kernel on its own slice of trajectories. Trajectories are statistically + independent, so no cross-device communication is required — per-device memory + equals a single-device run and the broken-collectives / all-gather failure modes + of SPMD sharding are avoided entirely. + + ``batch_size`` is interpreted **per device**: with ``n`` devices each call runs + ``n * batch_size`` trajectories. Every call runs at the same width + (:func:`_build_trajectory_kernel` compiled once, wrapped in a single ``pmap``, + reused across calls) so the GPU sees one upfront compile rather than a recompile + spike per batch; the final short call is padded up to that width and its extra + rows are sliced off. + + Each device's zero state is built *inside* the mapped function so the full + ``(n_devices, per_device, hilbert)`` array is never allocated on one device. + When ``keep_states`` is false only the measurement outcomes are returned from the + mapped function, so the large final state vectors are freeable intermediates and + are never gathered back to the host. + + :param dims: Per-qudit register dimensions of the simulated system. + """ + devices = devices if devices is not None else jax.devices() + n_devices = len(devices) + per_device = batch_size + per_call = n_devices * per_device + + kernel = _build_trajectory_kernel(operations, dims) + + def run_replica(device_keys: Array) -> Any: + # Build only this device's slice; keeps per-device memory at a single-device run. + psi = qx.zero_state_vector(dims=dims, ensemble_size=(per_device,)) + psi_out, outcomes = kernel(psi, device_keys) + return (psi_out, outcomes) if keep_states else outcomes + + pkernel = jax.pmap(run_replica, devices=devices) + + key = jax.random.key(random_seed) + all_psis: list[qx.StateVector] = [] + all_outcomes: list[Array] = [] + + remaining = num_trajectories + while remaining > 0: + this_call = min(remaining, per_call) + key, batch_key = jax.random.split(key) + # Fixed width every call (tail padded, extra rows sliced) → compile once. + batch_keys = jax.random.split(batch_key, per_call).reshape(n_devices, per_device) + + result = pkernel(batch_keys) + outcomes = result[1] if keep_states else result + # pmap re-adds the leading device axis: (n_devices, per_device, n_meas). + # Flatten it back to a 1-D ensemble to preserve the return contract. + outcomes = outcomes.reshape(per_call, -1)[:this_call] + all_outcomes.append(outcomes) + + if keep_states: + mat = result[0].matrix.reshape(per_call, -1)[:this_call] + all_psis.append(qx.StateVector.from_matrix(mat, dims)) + + remaining -= this_call + + return (all_psis if keep_states else None), all_outcomes + + +# ══════════════════════════════════════════════════════════ +# Dynamic-shape trajectory simulator +# ══════════════════════════════════════════════════════════ + + +def _dyn_apply( + op: qx.Unitary | qx.KrausMap | qx.QuantumInstrument, + psi: qx.StateVector, + subsystem: tuple[int, ...], + key: Array, + squeeze_tol: float, +) -> tuple[qx.StateVector, Array | None]: + """Apply one trajectory operator with dynamic per-subsystem dimensions. + + The reconciliation is *grow state → apply → squeeze state*: + + 1. The state is grown via :func:`quax.promote` only where the operator exceeds it. + Operators are applied at their authored dimension — they are never squeezed, since + squeezing an operator is ill-defined: it acts non-trivially on levels the state may + never populate (see :mod:`quax._squeeze`). + 2. The matching ``quax`` kernel is applied; it promotes the operator up to the state's + dimensions, never shrinking it. + 3. The *state* is squeezed (a well-defined operation) to reclaim any leakage level the + operator left empty. So an ideal gate authored on a qutrit register transiently + grows the state and then squeezes straight back, while a genuine leakage op leaves + population behind that survives the squeeze. The squeeze is skipped while every qudit + is already at the qubit floor, so a purely no-leakage trajectory pays nothing for it. + + :param squeeze_tol: Tolerance for squeezing emptied leakage levels out of the state. + :return: ``(state, outcome)`` where ``outcome`` is the sampled measurement + result for an instrument, else ``None``. + """ + current = tuple(psi.dims[q] for q in subsystem) + target = tuple(max(c, e) for c, e in zip(current, op.dims[0], strict=True)) + if target != current: + grown = list(psi.dims) + for q, t in zip(subsystem, target, strict=True): + grown[q] = t + psi = qx.promote(psi, tuple(grown)) + + if isinstance(op, qx.Unitary): + psi, outcome = qx.targeted_apply_unitary(op, psi, subsystem), None + elif isinstance(op, qx.KrausMap): + psi, _ = _sample_kraus_map_trajectory(op, psi, key, subsystem) + outcome = None + elif isinstance(op, qx.QuantumInstrument): + psi, outcome = qx.targeted_apply_instrument_to_state_vector(op, psi, key, subsystem) + else: + raise TypeError(f"DynamicTrajectorySimulator cannot apply operator of type {type(op).__name__}.") + + # Reclaim any leakage level the operator left empty by squeezing the *state*. No qudit + # can shrink below the qubit floor, so skip the work entirely while none has leaked. + if any(d > 2 for d in psi.dims): + psi = cast(qx.StateVector, qx.squeeze(psi, squeeze_tol)) + return psi, outcome + + +class DynamicTrajectorySimulator(_TrajectorySimulator): + """Single-trajectory simulator with dynamically-sized qudit dimensions. + + Targets the **largest** leakage-aware registers. Where the other simulators + fix a global Hilbert-space shape, this one keeps a per-subsystem dimension + vector that drifts at runtime: a qudit is grown to dimension 3 only when an + operator can populate its leakage level, and squeezed back to 2 once that + level empties (via :func:`quax.squeeze`). In the realistic low-leakage + regime only a handful of qudits occupy ``|2>`` at once, so the stored state + stays far below the full ``3**n``. + + The simulation is **eager** — it applies one operator at a time and cannot be + ``jax.jit``/``jax.grad``-compiled (the shapes are data-dependent) — and runs a + single trajectory per :meth:`compute` call (scalar PRNG key, no ensemble). + Squeeze is tolerance-based, so results carry a bounded truncation error set by + ``squeeze_tol``. + + ``max_subsystem_size`` defaults to 1: chains of single-qudit gates on one line + are still fused (no extra qudits pinned), but multi-qudit gates are left + un-merged so that a leakage channel never pins its neighbours to dimension 3. + + Example:: + + sim = DynamicTrajectorySimulator(program, noise_model=leakage_model) + params = sim.linearize(memory_map) + psi, outcomes = sim.compute(params, jax.random.key(0)) + shots = sim.sample(params, num_trajectories=1000) + """ + + def __init__( + self, + program: Program, + qubits: list[int] | None = None, + *, + noise_model: NoiseModelLike | None = None, + max_subsystem_size: int = 1, + kraus_truncation_threshold: float = 1e-6, + squeeze_tol: float = 1e-9, + ) -> None: + super().__init__( + program, + qubits, + noise_model=noise_model, + max_subsystem_size=max_subsystem_size, + kraus_truncation_threshold=kraus_truncation_threshold, + ) + self._squeeze_tol = squeeze_tol + + def _validate(self, program: Program) -> None: + """Measurements, resets, and noise are all supported (like TrajectorySimulator).""" + + def compute( # type: ignore[override] + self, + params: Array | None = None, + key: Array | None = None, + ) -> tuple[qx.StateVector, Array]: + """Run a single dynamic-shape trajectory. + + :param params: Flat parameter vector from :meth:`linearize`. Omit (or + pass ``None``) for a parameter-free program. + :param key: Scalar JAX PRNG key for this trajectory (required). + :return: ``(state_vector, measurement_outcomes)``. The state's per-qudit + dimensions reflect whatever leakage survived squeezing; outcomes has + shape ``(n_measurements,)`` in program order. + """ + if key is None: + raise ValueError("DynamicTrajectorySimulator.compute requires a JAX PRNG key.") + operations = self.adapt(self.compress(self.resolve(self._default_params(params)))) + + psi = qx.zero_state_vector(dims=(2,) * self.n_qubits) + outcomes: list[Array] = [] + # The state's shape changes as qudits grow and squeeze, so quax's jitted apply kernels + # compile once per distinct (shape, subsystem) they see. In the low-leakage regime that + # set is small and bounded — the deterministic no-leak shape sequence plus a few + # one-qubit-leaked shapes — so the compilation is a fixed upfront cost that amortizes + # over the many trajectories a sampling run takes. We therefore keep jit enabled. + for idx, (op, subsystem) in enumerate(operations): + psi, outcome = _dyn_apply(op, psi, tuple(subsystem), jax.random.fold_in(key, idx), self._squeeze_tol) + if outcome is not None: + outcomes.append(outcome) + + if outcomes: + return psi, jnp.stack(outcomes, axis=-1).astype(jnp.int32) + return psi, jnp.empty((0,), dtype=jnp.int32) + + def __call__(self, params: Array | None = None, key: Array | None = None) -> tuple[qx.StateVector, Array]: + return self.compute(params, key) + + def sample( + self, + params: Array | None = None, + num_trajectories: int = 1000, + random_seed: int = 0, + ) -> Array: + """Run trajectories sequentially, returning only measurement outcomes. + + Dynamic per-trajectory shapes preclude ``vmap`` batching, so trajectories + run one at a time. + + :param params: Flat parameter vector from :meth:`linearize`. + :param num_trajectories: Number of trajectories to simulate. + :param random_seed: Seed for the JAX PRNG. + :return: Measurement outcomes with shape ``(num_trajectories, n_measurements)``. + """ + key = jax.random.key(random_seed) + outcomes = [self.compute(params, jax.random.fold_in(key, t))[1] for t in range(num_trajectories)] + return jnp.stack(outcomes, axis=0) diff --git a/pyquil/simulation/tools.py b/pyquil/simulation/tools.py index 433bf84ca..8418cb2a0 100644 --- a/pyquil/simulation/tools.py +++ b/pyquil/simulation/tools.py @@ -16,7 +16,7 @@ """Miscellaneous tools that are helpful for simulation.""" from collections.abc import Sequence -from typing import Union, cast +from typing import cast import numpy as np @@ -306,7 +306,7 @@ def _gate_matrix(gate: Gate) -> np.ndarray: elif mod == "CONTROLLED": child = _strip_modifiers(gate, limit=1) matrix = _gate_matrix(child) - return np.kron(zero, np.eye(*matrix.shape)) + np.kron(one, matrix) # type: ignore + return np.kron(zero, np.eye(*matrix.shape)) + np.kron(one, matrix) elif mod == "FORKED": if len(gate.params) % 2 != 0: raise ValueError("FORKED gates must have an even number of parameters.") @@ -348,7 +348,7 @@ def program_unitary(program: Program, n_qubits: int) -> np.ndarray: return umat -def lifted_pauli(pauli_sum: Union[PauliSum, PauliTerm], qubits: list[int]) -> np.ndarray: +def lifted_pauli(pauli_sum: PauliSum | PauliTerm, qubits: list[int]) -> np.ndarray: """Return a matrix corresponding to the tensor representation of the given PauliSum and qubits. Useful for generating the full Hamiltonian after a particular fermion to @@ -389,7 +389,7 @@ def lifted_pauli(pauli_sum: Union[PauliSum, PauliTerm], qubits: list[int]) -> np return result_hilbert -def tensor_up(pauli_sum: Union[PauliSum, PauliTerm], qubits: list[int]) -> np.ndarray: +def tensor_up(pauli_sum: PauliSum | PauliTerm, qubits: list[int]) -> np.ndarray: """Return a matrix corresponding to the tensor representation of the given PauliSum and qubits. This is the same as :py:func:`lifted_pauli`. Nick R originally wrote this functionality diff --git a/pyquil/wavefunction.py b/pyquil/wavefunction.py index c6161a68d..c53e645b2 100644 --- a/pyquil/wavefunction.py +++ b/pyquil/wavefunction.py @@ -17,7 +17,7 @@ import itertools from collections.abc import Iterator, Sequence -from typing import Optional, cast +from typing import cast import numpy as np @@ -48,7 +48,7 @@ def __init__(self, amplitude_vector: np.ndarray): self.amplitudes: np.ndarray = np.asarray(amplitude_vector) sumprob = np.sum(self.probabilities()) if not np.isclose(sumprob, 1.0): - raise ValueError("The wavefunction is not normalized. " f"The probabilities sum to {sumprob} instead of 1") + raise ValueError(f"The wavefunction is not normalized. The probabilities sum to {sumprob} instead of 1") @staticmethod def zeros(qubit_num: int) -> "Wavefunction": @@ -143,7 +143,7 @@ def pretty_print(self, decimal_digits: int = 2) -> str: pp_string = pp_string[:-3] # remove the dangling + if it is there return pp_string - def plot(self, qubit_subset: Optional[Sequence[int]] = None) -> None: + def plot(self, qubit_subset: Sequence[int] | None = None) -> None: """Plot a bar chart with bitstring on the x-axis and probability on the y-axis. :param qubit_subset: Optional parameter used for plotting a subset of the Hilbert space. diff --git a/test/benchmarks/fixtures/surface_17_depth_5_no_reset.quil b/test/benchmarks/fixtures/surface_17_depth_5_no_reset.quil new file mode 100644 index 000000000..064cff716 --- /dev/null +++ b/test/benchmarks/fixtures/surface_17_depth_5_no_reset.quil @@ -0,0 +1,289 @@ +DEFCIRCUIT SZ_INIT q65 q66 q74 q75 q76 q77 q82 q83 q84 q85 q86 q91 q92 q93 q94 q102 q103: + RZ(pi/2) q103 + RZ(pi/2) q93 + RZ(pi/2) q85 + RZ(pi/2) q77 + RZ(pi/2) q91 + RZ(pi/2) q83 + RZ(pi/2) q75 + RZ(pi/2) q65 + RZ(pi/2) q82 + RZ(pi/2) q102 + RZ(pi/2) q66 + RZ(pi/2) q86 + RZ(pi/2) q84 + I q92 + I q74 + I q94 + I q76 + +DEFCIRCUIT SX_INIT q65 q66 q74 q75 q76 q77 q82 q83 q84 q85 q86 q91 q92 q93 q94 q102 q103: + RX(pi/2) q103 + RX(pi/2) q93 + RX(pi/2) q85 + RX(pi/2) q77 + RX(pi/2) q91 + RX(pi/2) q83 + RX(pi/2) q75 + RX(pi/2) q65 + RX(pi/2) q82 + RX(pi/2) q102 + RX(pi/2) q66 + RX(pi/2) q86 + RX(pi/2) q84 + I q92 + I q74 + I q94 + I q76 + +DEFCIRCUIT CZ_0 q65 q66 q74 q75 q76 q77 q82 q83 q84 q85 q86 q91 q92 q93 q94 q102 q103: + CZ q83 q84 + CZ q85 q86 + CZ q65 q66 + CZ q94 q93 + CZ q76 q75 + CZ q92 q91 + I q77 + I q74 + I q82 + I q102 + I q103 + +DEFCIRCUIT SZ_DATA q65 q66 q74 q75 q76 q77 q82 q83 q84 q85 q86 q91 q92 q93 q94 q102 q103: + RZ(pi/2) q102 + RZ(pi/2) q94 + RZ(pi/2) q86 + RZ(pi/2) q92 + RZ(pi/2) q84 + RZ(pi/2) q76 + RZ(pi/2) q82 + RZ(pi/2) q74 + RZ(pi/2) q66 + I q77 + I q93 + I q75 + I q103 + I q65 + I q83 + I q85 + I q91 + +DEFCIRCUIT SX_DATA q65 q66 q74 q75 q76 q77 q82 q83 q84 q85 q86 q91 q92 q93 q94 q102 q103: + RX(pi/2) q102 + RX(pi/2) q94 + RX(pi/2) q86 + RX(pi/2) q92 + RX(pi/2) q84 + RX(pi/2) q76 + RX(pi/2) q82 + RX(pi/2) q74 + RX(pi/2) q66 + I q77 + I q93 + I q75 + I q103 + I q65 + I q83 + I q85 + I q91 + +DEFCIRCUIT CZ_1 q65 q66 q74 q75 q76 q77 q82 q83 q84 q85 q86 q91 q92 q93 q94 q102 q103: + CZ q83 q92 + CZ q85 q94 + CZ q65 q74 + CZ q84 q93 + CZ q66 q75 + CZ q82 q91 + I q77 + I q102 + I q86 + I q103 + I q76 + +DEFCIRCUIT CZ_2 q65 q66 q74 q75 q76 q77 q82 q83 q84 q85 q86 q91 q92 q93 q94 q102 q103: + CZ q83 q74 + CZ q85 q76 + CZ q103 q94 + CZ q102 q93 + CZ q84 q75 + CZ q86 q77 + I q82 + I q66 + I q65 + I q92 + I q91 + +DEFCIRCUIT CZ_3 q65 q66 q74 q75 q76 q77 q82 q83 q84 q85 q86 q91 q92 q93 q94 q102 q103: + CZ q83 q82 + CZ q85 q84 + CZ q103 q102 + CZ q92 q93 + CZ q74 q75 + CZ q76 q77 + I q66 + I q94 + I q86 + I q65 + I q91 + +DEFCIRCUIT SZ_ANCILLA q65 q66 q74 q75 q76 q77 q82 q83 q84 q85 q86 q91 q92 q93 q94 q102 q103: + RZ(pi/2) q103 + RZ(pi/2) q93 + RZ(pi/2) q85 + RZ(pi/2) q77 + RZ(pi/2) q91 + RZ(pi/2) q83 + RZ(pi/2) q75 + RZ(pi/2) q65 + I q74 + I q82 + I q102 + I q66 + I q94 + I q86 + I q84 + I q92 + I q76 + +DEFCIRCUIT SX_ANCILLA_ECHO q65 q66 q74 q75 q76 q77 q82 q83 q84 q85 q86 q91 q92 q93 q94 q102 q103: + RX(pi/2) q103 + RX(pi/2) q93 + RX(pi/2) q85 + RX(pi/2) q77 + RX(pi/2) q91 + RX(pi/2) q83 + RX(pi/2) q75 + RX(pi/2) q65 + RX(pi) q102 + RX(pi) q94 + RX(pi) q86 + RX(pi) q92 + RX(pi) q84 + RX(pi) q76 + RX(pi) q82 + RX(pi) q74 + RX(pi) q66 + +DEFCIRCUIT MEASURE_ANCILLA q65 q66 q74 q75 q76 q77 q82 q83 q84 q85 q86 q91 q92 q93 q94 q102 q103: + MEASURE q103 + MEASURE q93 + MEASURE q85 + MEASURE q77 + MEASURE q91 + MEASURE q83 + MEASURE q75 + MEASURE q65 + I q74 + I q82 + I q102 + I q66 + I q94 + I q86 + I q84 + I q92 + I q76 + +DEFCIRCUIT MEASURE_ALL q65 q66 q74 q75 q76 q77 q82 q83 q84 q85 q86 q91 q92 q93 q94 q102 q103: + MEASURE q103 + MEASURE q102 + MEASURE q94 + MEASURE q86 + MEASURE q93 + MEASURE q85 + MEASURE q77 + MEASURE q92 + MEASURE q84 + MEASURE q76 + MEASURE q91 + MEASURE q83 + MEASURE q75 + MEASURE q82 + MEASURE q74 + MEASURE q66 + MEASURE q65 + +SZ_INIT 65 66 74 75 76 77 82 83 84 85 86 91 92 93 94 102 103 +SX_INIT 65 66 74 75 76 77 82 83 84 85 86 91 92 93 94 102 103 +SZ_INIT 65 66 74 75 76 77 82 83 84 85 86 91 92 93 94 102 103 +CZ_0 65 66 74 75 76 77 82 83 84 85 86 91 92 93 94 102 103 +SZ_DATA 65 66 74 75 76 77 82 83 84 85 86 91 92 93 94 102 103 +SX_DATA 65 66 74 75 76 77 82 83 84 85 86 91 92 93 94 102 103 +SZ_DATA 65 66 74 75 76 77 82 83 84 85 86 91 92 93 94 102 103 +CZ_1 65 66 74 75 76 77 82 83 84 85 86 91 92 93 94 102 103 +CZ_2 65 66 74 75 76 77 82 83 84 85 86 91 92 93 94 102 103 +SZ_DATA 65 66 74 75 76 77 82 83 84 85 86 91 92 93 94 102 103 +SX_DATA 65 66 74 75 76 77 82 83 84 85 86 91 92 93 94 102 103 +SZ_DATA 65 66 74 75 76 77 82 83 84 85 86 91 92 93 94 102 103 +CZ_3 65 66 74 75 76 77 82 83 84 85 86 91 92 93 94 102 103 +SZ_ANCILLA 65 66 74 75 76 77 82 83 84 85 86 91 92 93 94 102 103 +SX_ANCILLA_ECHO 65 66 74 75 76 77 82 83 84 85 86 91 92 93 94 102 103 +SZ_ANCILLA 65 66 74 75 76 77 82 83 84 85 86 91 92 93 94 102 103 +MEASURE_ANCILLA 65 66 74 75 76 77 82 83 84 85 86 91 92 93 94 102 103 +SZ_ANCILLA 65 66 74 75 76 77 82 83 84 85 86 91 92 93 94 102 103 +SX_ANCILLA_ECHO 65 66 74 75 76 77 82 83 84 85 86 91 92 93 94 102 103 +SZ_ANCILLA 65 66 74 75 76 77 82 83 84 85 86 91 92 93 94 102 103 +CZ_0 65 66 74 75 76 77 82 83 84 85 86 91 92 93 94 102 103 +SZ_DATA 65 66 74 75 76 77 82 83 84 85 86 91 92 93 94 102 103 +SX_DATA 65 66 74 75 76 77 82 83 84 85 86 91 92 93 94 102 103 +SZ_DATA 65 66 74 75 76 77 82 83 84 85 86 91 92 93 94 102 103 +CZ_1 65 66 74 75 76 77 82 83 84 85 86 91 92 93 94 102 103 +CZ_2 65 66 74 75 76 77 82 83 84 85 86 91 92 93 94 102 103 +SZ_DATA 65 66 74 75 76 77 82 83 84 85 86 91 92 93 94 102 103 +SX_DATA 65 66 74 75 76 77 82 83 84 85 86 91 92 93 94 102 103 +SZ_DATA 65 66 74 75 76 77 82 83 84 85 86 91 92 93 94 102 103 +CZ_3 65 66 74 75 76 77 82 83 84 85 86 91 92 93 94 102 103 +SZ_ANCILLA 65 66 74 75 76 77 82 83 84 85 86 91 92 93 94 102 103 +SX_ANCILLA_ECHO 65 66 74 75 76 77 82 83 84 85 86 91 92 93 94 102 103 +SZ_ANCILLA 65 66 74 75 76 77 82 83 84 85 86 91 92 93 94 102 103 +MEASURE_ANCILLA 65 66 74 75 76 77 82 83 84 85 86 91 92 93 94 102 103 +SZ_ANCILLA 65 66 74 75 76 77 82 83 84 85 86 91 92 93 94 102 103 +SX_ANCILLA_ECHO 65 66 74 75 76 77 82 83 84 85 86 91 92 93 94 102 103 +SZ_ANCILLA 65 66 74 75 76 77 82 83 84 85 86 91 92 93 94 102 103 +CZ_0 65 66 74 75 76 77 82 83 84 85 86 91 92 93 94 102 103 +SZ_DATA 65 66 74 75 76 77 82 83 84 85 86 91 92 93 94 102 103 +SX_DATA 65 66 74 75 76 77 82 83 84 85 86 91 92 93 94 102 103 +SZ_DATA 65 66 74 75 76 77 82 83 84 85 86 91 92 93 94 102 103 +CZ_1 65 66 74 75 76 77 82 83 84 85 86 91 92 93 94 102 103 +CZ_2 65 66 74 75 76 77 82 83 84 85 86 91 92 93 94 102 103 +SZ_DATA 65 66 74 75 76 77 82 83 84 85 86 91 92 93 94 102 103 +SX_DATA 65 66 74 75 76 77 82 83 84 85 86 91 92 93 94 102 103 +SZ_DATA 65 66 74 75 76 77 82 83 84 85 86 91 92 93 94 102 103 +CZ_3 65 66 74 75 76 77 82 83 84 85 86 91 92 93 94 102 103 +SZ_ANCILLA 65 66 74 75 76 77 82 83 84 85 86 91 92 93 94 102 103 +SX_ANCILLA_ECHO 65 66 74 75 76 77 82 83 84 85 86 91 92 93 94 102 103 +SZ_ANCILLA 65 66 74 75 76 77 82 83 84 85 86 91 92 93 94 102 103 +MEASURE_ANCILLA 65 66 74 75 76 77 82 83 84 85 86 91 92 93 94 102 103 +SZ_ANCILLA 65 66 74 75 76 77 82 83 84 85 86 91 92 93 94 102 103 +SX_ANCILLA_ECHO 65 66 74 75 76 77 82 83 84 85 86 91 92 93 94 102 103 +SZ_ANCILLA 65 66 74 75 76 77 82 83 84 85 86 91 92 93 94 102 103 +CZ_0 65 66 74 75 76 77 82 83 84 85 86 91 92 93 94 102 103 +SZ_DATA 65 66 74 75 76 77 82 83 84 85 86 91 92 93 94 102 103 +SX_DATA 65 66 74 75 76 77 82 83 84 85 86 91 92 93 94 102 103 +SZ_DATA 65 66 74 75 76 77 82 83 84 85 86 91 92 93 94 102 103 +CZ_1 65 66 74 75 76 77 82 83 84 85 86 91 92 93 94 102 103 +CZ_2 65 66 74 75 76 77 82 83 84 85 86 91 92 93 94 102 103 +SZ_DATA 65 66 74 75 76 77 82 83 84 85 86 91 92 93 94 102 103 +SX_DATA 65 66 74 75 76 77 82 83 84 85 86 91 92 93 94 102 103 +SZ_DATA 65 66 74 75 76 77 82 83 84 85 86 91 92 93 94 102 103 +CZ_3 65 66 74 75 76 77 82 83 84 85 86 91 92 93 94 102 103 +SZ_ANCILLA 65 66 74 75 76 77 82 83 84 85 86 91 92 93 94 102 103 +SX_ANCILLA_ECHO 65 66 74 75 76 77 82 83 84 85 86 91 92 93 94 102 103 +SZ_ANCILLA 65 66 74 75 76 77 82 83 84 85 86 91 92 93 94 102 103 +MEASURE_ANCILLA 65 66 74 75 76 77 82 83 84 85 86 91 92 93 94 102 103 +SZ_ANCILLA 65 66 74 75 76 77 82 83 84 85 86 91 92 93 94 102 103 +SX_ANCILLA_ECHO 65 66 74 75 76 77 82 83 84 85 86 91 92 93 94 102 103 +SZ_ANCILLA 65 66 74 75 76 77 82 83 84 85 86 91 92 93 94 102 103 +CZ_0 65 66 74 75 76 77 82 83 84 85 86 91 92 93 94 102 103 +SZ_DATA 65 66 74 75 76 77 82 83 84 85 86 91 92 93 94 102 103 +SX_DATA 65 66 74 75 76 77 82 83 84 85 86 91 92 93 94 102 103 +SZ_DATA 65 66 74 75 76 77 82 83 84 85 86 91 92 93 94 102 103 +CZ_1 65 66 74 75 76 77 82 83 84 85 86 91 92 93 94 102 103 +CZ_2 65 66 74 75 76 77 82 83 84 85 86 91 92 93 94 102 103 +SZ_DATA 65 66 74 75 76 77 82 83 84 85 86 91 92 93 94 102 103 +SX_DATA 65 66 74 75 76 77 82 83 84 85 86 91 92 93 94 102 103 +SZ_DATA 65 66 74 75 76 77 82 83 84 85 86 91 92 93 94 102 103 +CZ_3 65 66 74 75 76 77 82 83 84 85 86 91 92 93 94 102 103 +SZ_INIT 65 66 74 75 76 77 82 83 84 85 86 91 92 93 94 102 103 +SX_INIT 65 66 74 75 76 77 82 83 84 85 86 91 92 93 94 102 103 +SZ_INIT 65 66 74 75 76 77 82 83 84 85 86 91 92 93 94 102 103 +MEASURE_ALL 65 66 74 75 76 77 82 83 84 85 86 91 92 93 94 102 103 diff --git a/test/benchmarks/test_state_vector.py b/test/benchmarks/test_state_vector.py new file mode 100644 index 000000000..8916881d6 --- /dev/null +++ b/test/benchmarks/test_state_vector.py @@ -0,0 +1,514 @@ +"""Benchmarks for the quax-backed state vector and trajectory simulators.""" + +from pathlib import Path + +import jax + +jax.config.update("jax_enable_x64", True) + +import numpy as np +import pytest +import quax as qx + +from pyquil.gates import CNOT, RX, RZ +from pyquil.noise._channels import Channel, CycleChannel, MeasurementChannel +from pyquil.noise._noise_model import NoiseModel +from pyquil.quil import Program +from pyquil.quilbase import DefCircuit, ResetQubit +from pyquil.quilbase import Gate as QuilGate +from pyquil.quilbase import Measurement as QuilMeasurement +from pyquil.quilbase import Reset as QuilReset +from pyquil.simulation._simulator import ( + DensityMatrixSimulator, + PureStateVectorSimulator, + TrajectorySimulator, +) +from pyquil.simulation._simulator import ( + _apply_trajectory_operations as apply_trajectory_operations, +) + +_EMPTY_PARAMS = np.array([], dtype=float) +_FIXTURES_DIR = Path(__file__).parent / "fixtures" +_SURFACE17_FIXTURE = _FIXTURES_DIR / "surface_17_depth_5_no_reset.quil" +_SURFACE17_QUBITS = (65, 66, 74, 75, 76, 77, 82, 83, 84, 85, 86, 91, 92, 93, 94, 102, 103) +_SURFACE17_CYCLES = { + "SZ_INIT", + "SX_INIT", + "CZ_0", + "SZ_DATA", + "SX_DATA", + "CZ_1", + "CZ_2", + "CZ_3", + "SZ_ANCILLA", + "SX_ANCILLA_ECHO", + "MEASURE_ANCILLA", + "MEASURE_ALL", +} +_DEFAULT_NUM_QUBITS = 15 +_DEFAULT_NUM_LAYERS = 10 +_DEFAULT_NUM_TRAJECTORIES = 128 +_DEFAULT_BATCH_SIZE = 32 +_DEFAULT_MAX_SUBSYSTEM_SIZE = 1 + + +def _build_noisy_program_and_model(num_qubits, num_layers, seed=4867): + """Build a layered noisy circuit and matching noise model for shared scaling benchmarks.""" + edges_0 = [(i, i + 1) for i in range(0, num_qubits - 1, 2)] + edges_1 = [(i, i + 1) for i in range(1, num_qubits - 1, 2)] + rng = np.random.default_rng(seed) + + t1s, t2s = {}, {} + for q in range(num_qubits): + t1 = np.clip(rng.normal(30, 10), 10, 50) + t2 = np.clip(rng.normal(30, 20), 5, 2 * t1) + t1s[q], t2s[q] = t1, t2 + + channels = [ + Channel.from_coherence_times( + CNOT(*edge), gate_duration=0.1, t1s=[t1s[q] for q in edge], t2s=[t2s[q] for q in edge] + ) + for edge in edges_0 + edges_1 + ] + [ + Channel.from_coherence_times(RX(np.pi / 2, q), gate_duration=0.04, t1s=[t1s[q]], t2s=[t2s[q]]) + for q in range(num_qubits) + ] + noise_model = NoiseModel.from_channels(channels) + + program = Program() + for _ in range(num_layers): + for edges in [edges_0, edges_1]: + program += [RZ(rng.uniform(-np.pi, np.pi), idx) for idx in range(num_qubits)] + program += [RX(np.pi / 2, idx) for idx in range(num_qubits)] + program += [RZ(rng.uniform(-np.pi, np.pi), idx) for idx in range(num_qubits)] + program += [RX(np.pi / 2, idx) for idx in range(num_qubits)] + program += [RZ(rng.uniform(-np.pi, np.pi), idx) for idx in range(num_qubits)] + program += [CNOT(*edge) for edge in edges] + + return program, noise_model + + +def _surface17_defcircuits(program): + return {inst.name: inst for inst in program.instructions if isinstance(inst, DefCircuit)} + + +def _surface17_program_variant(variant="full"): + program = Program(_SURFACE17_FIXTURE.read_text()) + if variant == "full": + return program + + if variant == "first_measurement": + prefix_program = Program() + for inst in program.instructions: + prefix_program += inst + if isinstance(inst, QuilGate) and inst.name == "MEASURE_ANCILLA": + break + return prefix_program + + if variant == "no_measurements": + keep_gates, keep_measurements = True, False + elif variant == "measurements_only": + keep_gates, keep_measurements = False, True + else: + raise ValueError(f"Unknown surface-17 variant: {variant}") + + original_defcircuits = _surface17_defcircuits(program) + filtered_defcircuits = {} + filtered_program = Program() + + for inst in program.instructions: + if isinstance(inst, DefCircuit): + instructions = [ + cycle_inst + for cycle_inst in inst.instructions + if (keep_gates and isinstance(cycle_inst, QuilGate)) + or (keep_measurements and isinstance(cycle_inst, QuilMeasurement)) + ] + if instructions: + defcircuit = DefCircuit(inst.name, inst.parameters, inst.qubit_variables, instructions) + filtered_defcircuits[inst.name] = defcircuit + filtered_program += defcircuit + elif isinstance(inst, QuilGate) and inst.name in original_defcircuits: + if inst.name in filtered_defcircuits: + filtered_program += inst + else: + filtered_program += inst + + return filtered_program + + +def _build_surface17_cycle_noise_model(program, depolarizing_constant=0.99, readout_fidelity=1.0): + defcircuits = _surface17_defcircuits(program) + cycle_channels = {} + + for inst in program.instructions: + if not isinstance(inst, QuilGate) or inst.name not in defcircuits: + continue + if inst in cycle_channels: + continue + + defcircuit = defcircuits[inst.name] + qubit_map = dict(zip(defcircuit.qubit_variables, inst.qubits)) + channels = [] + + for cycle_inst in defcircuit.instructions: + if isinstance(cycle_inst, QuilGate): + concrete_gate = QuilGate( + cycle_inst.name, + list(cycle_inst.params), + [qubit_map[qubit] for qubit in cycle_inst.qubits], + ) + channels.append(Channel.from_depolarizing_constant(concrete_gate, depolarizing_constant)) + elif isinstance(cycle_inst, QuilMeasurement): + concrete_measurement = QuilMeasurement(qubit=qubit_map[cycle_inst.qubit], classical_reg=None) + channels.append( + MeasurementChannel.from_readout_fidelity(concrete_measurement, fidelity=readout_fidelity) + ) + + cycle_channels[inst] = CycleChannel(inst=inst, defcircuit=defcircuit, channels=tuple(channels)) + + return NoiseModel(channels=cycle_channels) + + +def _prepare_trajectory_operations(program, noise_model, max_subsystem_size=0): + sim = TrajectorySimulator(program, noise_model=noise_model, max_subsystem_size=max_subsystem_size) + params = sim.linearize({}) + resolved = sim.resolve(params) + compressed = sim.compress(resolved) + operations = sim.adapt(compressed) + return sim, resolved, compressed, operations + + +def _operation_counts(operations): + return { + "unitary_ops": sum(1 for op, _ in operations if isinstance(op, qx.Unitary)), + "kraus_ops": sum(1 for op, _ in operations if isinstance(op, qx.KrausMap)), + "instrument_ops": sum(1 for op, _ in operations if isinstance(op, qx.QuantumInstrument)), + } + + +def _record_counts(benchmark, resolved, compressed, operations): + if hasattr(benchmark, "extra_info"): + benchmark.extra_info.update( + { + "resolved_ops": len(resolved), + "compressed_ops": len(compressed), + "trajectory_ops": len(operations), + **_operation_counts(operations), + } + ) + + +def _block_until_ready(matrix, outcomes): + matrix.block_until_ready() + outcomes.block_until_ready() + + +def _benchmark_trajectory_operations( + benchmark, + sim, + operations, + *, + num_trajectories, + batch_size, + random_seed=0, + use_jit=False, +): + if use_jit: + + def apply_matrix(matrix, key): + psi = qx.StateVector.from_matrix(matrix, sim.dims) + psi_out, outcomes = apply_trajectory_operations(operations, psi, key) + return psi_out.matrix, outcomes + + apply_batch = jax.jit(apply_matrix) + else: + + def apply_batch(matrix, key): + psi = qx.StateVector.from_matrix(matrix, sim.dims) + psi_out, outcomes = apply_trajectory_operations(operations, psi, key) + return psi_out.matrix, outcomes + + warmup_psi = qx.zero_state_vector(dims=sim.dims, ensemble_size=(batch_size,)) + _block_until_ready(*apply_batch(warmup_psi.matrix, jax.random.key(random_seed))) + + def thunk(): + key = jax.random.key(random_seed) + remaining = num_trajectories + while remaining > 0: + this_batch = min(remaining, batch_size) + key, batch_key = jax.random.split(key) + psi = qx.zero_state_vector(dims=sim.dims, ensemble_size=(this_batch,)) + _block_until_ready(*apply_batch(psi.matrix, batch_key)) + remaining -= this_batch + + benchmark.pedantic(thunk, iterations=1, rounds=1) + + +def _run_perf_benchmark( + benchmark, + num_qubits=_DEFAULT_NUM_QUBITS, + num_layers=_DEFAULT_NUM_LAYERS, + num_trajectories=_DEFAULT_NUM_TRAJECTORIES, + batch_size=_DEFAULT_BATCH_SIZE, + max_subsystem_size=_DEFAULT_MAX_SUBSYSTEM_SIZE, +): + program, noise_model = _build_noisy_program_and_model(num_qubits, num_layers) + sim, resolved, compressed, operations = _prepare_trajectory_operations(program, noise_model, max_subsystem_size) + _record_counts(benchmark, resolved, compressed, operations) + _benchmark_trajectory_operations( + benchmark, + sim, + operations, + num_trajectories=num_trajectories, + batch_size=batch_size, + ) + + +def _run_surface17_benchmark( + benchmark, + *, + variant="full", + num_trajectories=128, + batch_size=16, + max_subsystem_size=2, + depolarizing_constant=0.99, + readout_fidelity=1.0, + use_jit=False, +): + program = _surface17_program_variant(variant) + noise_model = _build_surface17_cycle_noise_model( + program, + depolarizing_constant=depolarizing_constant, + readout_fidelity=readout_fidelity, + ) + sim, resolved, compressed, operations = _prepare_trajectory_operations(program, noise_model, max_subsystem_size) + _record_counts(benchmark, resolved, compressed, operations) + if hasattr(benchmark, "extra_info"): + benchmark.extra_info.update( + { + "variant": variant, + "num_trajectories": num_trajectories, + "batch_size": batch_size, + "max_subsystem_size": max_subsystem_size, + "use_jit": use_jit, + } + ) + _benchmark_trajectory_operations( + benchmark, + sim, + operations, + num_trajectories=num_trajectories, + batch_size=batch_size, + use_jit=use_jit, + ) + + +def test_surface17_fixture_structure(): + program = Program(_SURFACE17_FIXTURE.read_text()) + defcircuits = {inst.name: inst for inst in program.instructions if isinstance(inst, DefCircuit)} + invocations = [inst for inst in program.instructions if isinstance(inst, QuilGate)] + invocation_names = [inst.name for inst in invocations] + + assert _SURFACE17_FIXTURE.exists() + assert set(defcircuits) == _SURFACE17_CYCLES + assert set(program.get_qubit_indices()) == set(_SURFACE17_QUBITS) + assert not any(isinstance(inst, (QuilReset, ResetQubit)) for inst in program.instructions) + assert invocation_names.count("MEASURE_ANCILLA") == 4 + assert invocation_names[-1] == "MEASURE_ALL" + + +def test_surface17_cycle_noise_model_preserves_measurements(): + program = Program(_SURFACE17_FIXTURE.read_text()) + noise_model = _build_surface17_cycle_noise_model(program, depolarizing_constant=1.0) + sim, _, _, _ = _prepare_trajectory_operations(program, noise_model, max_subsystem_size=0) + resolved = sim.resolve(_EMPTY_PARAMS) + + assert sum(1 for op, _ in resolved if isinstance(op, qx.QuantumInstrument)) == 49 + + +@pytest.mark.parametrize("variant", ["full", "no_measurements", "measurements_only"]) +def test_surface17_cycle_noise_compression_preserves_instruments(variant): + program = _surface17_program_variant(variant) + noise_model = _build_surface17_cycle_noise_model(program) + counts = [] + + for max_subsystem_size in (0, 1, 2): + _, resolved, compressed, operations = _prepare_trajectory_operations(program, noise_model, max_subsystem_size) + counts.append((len(resolved), len(compressed), len(operations), _operation_counts(operations))) + + assert counts[0][0] == counts[1][0] == counts[2][0] + if variant == "measurements_only": + assert counts[0] == counts[1] == counts[2] + else: + assert counts[2][1] < counts[1][1] < counts[0][1] + assert counts[0][3]["instrument_ops"] == counts[1][3]["instrument_ops"] == counts[2][3]["instrument_ops"] + + +class TestPerformance: + """Trajectory simulator performance benchmarks.""" + + @pytest.mark.parametrize( + "num_qubits", + [ + pytest.param(3, id="3q"), + pytest.param(6, id="6q"), + pytest.param(9, id="9q"), + pytest.param(12, id="12q"), + pytest.param(15, id="15q"), + ], + ) + def test_scaling_qubits(self, benchmark, num_qubits): + _run_perf_benchmark(benchmark, num_qubits=num_qubits) + + @pytest.mark.parametrize( + "num_layers", + [ + pytest.param(1, id="1L"), + pytest.param(3, id="3L"), + pytest.param(10, id="10L"), + pytest.param(20, id="20L"), + ], + ) + def test_scaling_depth(self, benchmark, num_layers): + _run_perf_benchmark(benchmark, num_layers=num_layers) + + @pytest.mark.parametrize( + "batch_size", + [ + pytest.param(8, id="b8"), + pytest.param(16, id="b16"), + pytest.param(64, id="b64"), + ], + ) + def test_scaling_batch_size(self, benchmark, batch_size): + _run_perf_benchmark(benchmark, batch_size=batch_size) + + @pytest.mark.parametrize( + "max_subsystem_size", + [ + pytest.param(0, id="s0"), + pytest.param(1, id="s1"), + ], + ) + def test_scaling_subsystem_size(self, benchmark, max_subsystem_size): + _run_perf_benchmark(benchmark, max_subsystem_size=max_subsystem_size) + + @pytest.mark.parametrize( + "batch_size", + [ + pytest.param(8, id="b8"), + pytest.param(16, id="b16"), + pytest.param(64, id="b64"), + ], + ) + def test_17q_batch_size(self, benchmark, batch_size): + _run_perf_benchmark(benchmark, num_qubits=17, batch_size=batch_size) + + def test_surface17_depth5_cycle_noise(self, benchmark): + _run_surface17_benchmark(benchmark) + + def test_surface17_depth5_cycle_noise_low_trajectory(self, benchmark): + _run_surface17_benchmark(benchmark, num_trajectories=4, batch_size=4) + + def test_surface17_depth5_cycle_noise_micro(self, benchmark): + _run_surface17_benchmark(benchmark, variant="first_measurement", num_trajectories=1, batch_size=1) + + def test_surface17_depth5_cycle_noise_micro_jit(self, benchmark): + _run_surface17_benchmark(benchmark, variant="first_measurement", num_trajectories=1, batch_size=1, use_jit=True) + + def test_surface17_depth5_cycle_noise_no_measurements_micro(self, benchmark): + _run_surface17_benchmark(benchmark, variant="no_measurements", num_trajectories=4, batch_size=4) + + def test_surface17_depth5_cycle_noise_measurements_only_micro(self, benchmark): + _run_surface17_benchmark(benchmark, variant="measurements_only", num_trajectories=4, batch_size=4) + + +def _build_gate_program(num_qubits, num_layers, seed=4867): + """Build a layered gate-only (noise-free) brickwork circuit.""" + rng = np.random.default_rng(seed) + program = Program() + for _ in range(num_layers): + for q in range(num_qubits): + program += RX(rng.uniform(-np.pi, np.pi), q) + program += RZ(rng.uniform(-np.pi, np.pi), q) + for q in range(0, num_qubits - 1, 2): + program += CNOT(q, q + 1) + for q in range(1, num_qubits - 1, 2): + program += CNOT(q, q + 1) + return program + + +def _benchmark_compile_time(benchmark, sim, params, extra=None): + """Benchmark the JIT compile time of ``sim.compute``. + + A fresh ``jax.jit`` wrapper is created on every round so the XLA compilation + cache is bypassed and the full lower+compile cost is measured each time. + """ + if hasattr(benchmark, "extra_info") and extra: + benchmark.extra_info.update(extra) + + def thunk(): + return jax.jit(lambda p: sim.compute(p)).lower(params).compile() + + benchmark.pedantic(thunk, iterations=1, rounds=1) + + +class TestJitCompileTime: + """JIT compile-time benchmarks for the lax-loop simulators. + + These measure how compilation time scales with program depth. The lax-loop + ``compute`` traces a single loop body plus one switch branch per distinct + base subsystem, so the compiled graph size is bounded by the number of + distinct subsystems rather than the number of operations. + """ + + @pytest.mark.parametrize( + "num_layers", + [ + pytest.param(10, id="10L"), + pytest.param(40, id="40L"), + pytest.param(80, id="80L"), + ], + ) + def test_state_vector_compile_depth(self, benchmark, num_layers): + num_qubits = 10 + program = _build_gate_program(num_qubits, num_layers) + sim = PureStateVectorSimulator(program, qubits=list(range(num_qubits))) + params = sim.linearize({}) + _benchmark_compile_time( + benchmark, + sim, + params, + extra={ + "num_qubits": num_qubits, + "num_layers": num_layers, + "num_ops": len(program.instructions), + "num_bases": len(sim.bases), + }, + ) + + @pytest.mark.parametrize( + "num_layers", + [ + pytest.param(5, id="5L"), + pytest.param(20, id="20L"), + pytest.param(40, id="40L"), + ], + ) + def test_density_matrix_compile_depth(self, benchmark, num_layers): + num_qubits = 6 + program = _build_gate_program(num_qubits, num_layers) + sim = DensityMatrixSimulator(program, qubits=list(range(num_qubits))) + params = sim.linearize({}) + _benchmark_compile_time( + benchmark, + sim, + params, + extra={ + "num_qubits": num_qubits, + "num_layers": num_layers, + "num_ops": len(program.instructions), + "num_bases": len(sim.bases), + }, + ) diff --git a/test/unit/__snapshots__/test_compiler.ambr b/test/unit/__snapshots__/test_compiler.ambr deleted file mode 100644 index fc4c291a0..000000000 --- a/test/unit/__snapshots__/test_compiler.ambr +++ /dev/null @@ -1,10 +0,0 @@ -# serializer version: 1 -# name: test_transpile_qasm_2 - ''' - DECLARE ro BIT[2] - MEASURE 0 ro[0] - MEASURE 1 ro[1] - HALT - - ''' -# --- diff --git a/test/unit/conftest.py b/test/unit/conftest.py index fcb8d236a..445c6562c 100644 --- a/test/unit/conftest.py +++ b/test/unit/conftest.py @@ -1,6 +1,10 @@ import os from typing import Any, Dict +import jax + +jax.config.update("jax_enable_x64", True) + import numpy as np import pytest from qcs_sdk import QCSClient diff --git a/test/unit/test_compiler.py b/test/unit/test_compiler.py index c44dea0f2..611154681 100644 --- a/test/unit/test_compiler.py +++ b/test/unit/test_compiler.py @@ -3,7 +3,6 @@ import pytest from qcs_sdk.qpu.translation import TranslationBackend -from syrupy.assertion import SnapshotAssertion from pyquil import Program from pyquil.api._compiler import ( @@ -36,10 +35,16 @@ def test_compile_with_quilt_calibrations(compiler: QPUCompiler): assert compilation_result == program -def test_transpile_qasm_2(compiler: QPUCompiler, snapshot: SnapshotAssertion): +def test_transpile_qasm_2(compiler: QPUCompiler): qasm = 'OPENQASM 2.0;\nqreg q[3];\ncreg ro[2];\nmeasure q[0] -> ro[0];\nmeasure q[1] -> ro[1];' program = compiler.transpile_qasm_2(qasm) - assert program.out() == snapshot + lines = [line for line in program.out().splitlines() if line] + assert lines[0] == "DECLARE ro BIT[2]" + instructions = lines[1:] + if instructions[-1:] == ["HALT"]: + instructions = instructions[:-1] + assert len(instructions) == 2 + assert set(instructions) == {"MEASURE 0 ro[0]", "MEASURE 1 ro[1]"} @pytest.mark.parametrize( @@ -60,4 +65,3 @@ def test_translation_backend_validation(quantum_processor_id: str, backend: Opti else: actual = select_backend_for_quantum_processor_id(quantum_processor_id, backend) assert actual == expected - diff --git a/test/unit/test_dynamic_trajectory.py b/test/unit/test_dynamic_trajectory.py new file mode 100644 index 000000000..9e0ce3202 --- /dev/null +++ b/test/unit/test_dynamic_trajectory.py @@ -0,0 +1,130 @@ +# Copyright 2026 Rigetti Computing +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Unit tests for the dynamic-shape trajectory simulator.""" + +import jax +import jax.numpy as jnp +import numpy as np +import quax as qx + +from pyquil.gates import CNOT, MEASURE, RX, H, X +from pyquil.noise._channels import Channel +from pyquil.noise._noise_model import NoiseModel +from pyquil.quil import Program +from pyquil.quilatom import MemoryReference +from pyquil.quilbase import Declare +from pyquil.simulation._simulator import ( + DensityMatrixSimulator, + DynamicTrajectorySimulator, + PureStateVectorSimulator, + _dyn_apply, +) + +_EMPTY = jnp.array([], dtype=float) + + +def _overlap(a: qx.StateVector, b: qx.StateVector) -> float: + return float(jnp.abs(jnp.vdot(a.matrix, b.matrix)) ** 2) + + +# -------------------------------------------------------------------------- +# Deterministic correctness against the exact simulators +# -------------------------------------------------------------------------- + + +def test_matches_pure_state_vector_and_stays_qubit(): + """A noiseless gate-only program reproduces the exact state and never grows.""" + program = Program(X(0), CNOT(0, 1), RX(0.7, 2)) + reference = PureStateVectorSimulator(program).compute(_EMPTY) + + sim = DynamicTrajectorySimulator(program) + psi, outcomes = sim.compute(_EMPTY, jax.random.key(0)) + + assert psi.dims == (2, 2, 2) # no leakage -> no growth + assert outcomes.shape == (0,) + assert _overlap(psi, reference) > 1 - 1e-6 + + +def test_bell_measurement_is_correlated(): + """Bell-state measurements are perfectly correlated and roughly balanced.""" + program = Program( + Declare("ro", "BIT", 2), + H(0), + CNOT(0, 1), + MEASURE(0, MemoryReference("ro", 0)), + MEASURE(1, MemoryReference("ro", 1)), + ) + + shots = DynamicTrajectorySimulator(program).sample(_EMPTY, num_trajectories=200, random_seed=1) + assert shots.shape == (200, 2) + # the two qubits always agree + assert np.all(np.asarray(shots)[:, 0] == np.asarray(shots)[:, 1]) + # and both outcomes occur + assert set(np.asarray(shots)[:, 0].tolist()) == {0, 1} + + +# -------------------------------------------------------------------------- +# Dynamic growth / squeeze of the live state +# -------------------------------------------------------------------------- + + +def test_dyn_apply_grows_then_squeezes(): + """A 1<->2 raising gate grows the state to dim 3; returning empties and squeezes it.""" + psi = qx.StateVector.from_matrix(jnp.array([0.0, 1.0], dtype=complex), (2,)) # |1> + swap_12 = qx.Unitary.from_matrix(jnp.array([[1, 0, 0], [0, 0, 1], [0, 1, 0]], dtype=complex), ((3,), (3,))) + + grown, outcome = _dyn_apply(swap_12, psi, (0,), jax.random.key(0), 1e-9) + assert outcome is None + assert grown.dims == (3,) # state was grown to host |2> + assert float(jnp.abs(grown.matrix[2]) ** 2) > 0.99 + assert qx.squeeze(grown, 1e-9).dims == (3,) # population in |2> is retained + + # Apply the raising gate again: population returns to |1>, so squeeze shrinks back. + returned, _ = _dyn_apply(swap_12, grown, (0,), jax.random.key(1), 1e-9) + squeezed = qx.squeeze(returned, 1e-9) + assert squeezed.dims == (2,) + assert float(jnp.abs(squeezed.matrix[1]) ** 2) > 0.99 + + +# -------------------------------------------------------------------------- +# Noise: trajectory average reproduces the density matrix +# -------------------------------------------------------------------------- + + +def test_depolarizing_trajectory_average_matches_density_matrix(): + """Averaging |psi> 0.95 + + def test_from_gate_fidelity(self): + """Channel.from_gate_fidelity produces correct fidelity.""" + inst = RX(np.pi / 2, 0) + ch = Channel.from_gate_fidelity(inst=inst, fidelity=0.99) + assert abs(ch.fidelity - 0.99) < 0.001 + + def test_from_pauli_fidelity(self): + """Channel.from_pauli_fidelity produces a valid channel.""" + inst = X(0) + ch = Channel.from_pauli_fidelity(inst=inst, pauli_fidelity=0.97) + assert isinstance(ch.process, qx.SuperOp) + assert ch.pauli_fidelity == pytest.approx(0.97, abs=0.001) + + def test_from_pauli_noise(self): + """Channel.from_pauli_noise produces a valid Pauli noise channel.""" + inst = RX(0.5, 0) + ch = Channel.from_pauli_noise(inst=inst, pauli_noise={"X": 0.01, "Z": 0.02}) + assert isinstance(ch.process, qx.SuperOp) + assert ch.fidelity < 1.0 + + def test_from_coherence_times(self): + """Channel.from_coherence_times produces a valid decoherence channel.""" + inst = RX(np.pi / 4, 0) + ch = Channel.from_coherence_times(inst=inst, gate_duration=40e-9, t1s=[30e-6], t2s=[20e-6]) + assert isinstance(ch.process, qx.SuperOp) + assert ch.fidelity < 1.0 + assert ch.fidelity > 0.99 # short gate relative to T1/T2 + + def test_qubits(self): + """Channel.qubits reflects the instruction's qubits.""" + ch = Channel.from_depolarizing_constant(inst=RX(0.1, 3), depolarizing_constant=0.99) + assert ch.qubits == [3] + + def test_num_qubits(self): + """Channel.num_qubits is correct for 2Q gates.""" + ch = Channel.from_depolarizing_constant(inst=CNOT(0, 1), depolarizing_constant=0.95) + assert ch.num_qubits == 2 + + def test_fidelity_properties(self): + """Fidelity, infidelity, pauli_fidelity, pauli_infidelity are consistent.""" + ch = Channel.from_gate_fidelity(inst=RX(0.3, 0), fidelity=0.98) + assert ch.fidelity + ch.infidelity == pytest.approx(1.0) + assert ch.pauli_fidelity + ch.pauli_infidelity == pytest.approx(1.0) + assert ch.stochastic_fidelity + ch.stochastic_infidelity == pytest.approx(1.0) + assert ch.coherent_fidelity + ch.coherent_infidelity == pytest.approx(1.0) + + def test_noise_process(self): + """noise_process factors out the ideal gate unitary.""" + ch = Channel.from_depolarizing_constant(inst=RX(np.pi / 4, 0), depolarizing_constant=0.99) + noise = ch.noise_process + assert isinstance(noise, qx.SuperOp) + + def test_is_pauli(self): + """A depolarizing channel on a Clifford gate should be a Pauli channel.""" + ch = Channel.from_depolarizing_constant(inst=X(0), depolarizing_constant=0.98) + assert ch.is_pauli() + + def test_pauli_twirl(self): + """Pauli twirl of a channel on a Clifford gate should be a Pauli channel.""" + ch = Channel.from_random_coherent_error(inst=X(0), process_fidelity=0.97, rng=np.random.default_rng(42)) + twirled = ch.pauli_twirl() + assert twirled.is_pauli() + + def test_unitarity(self): + """A depolarizing channel should have unitarity < 1.""" + ch = Channel.from_depolarizing_constant(inst=RX(0.5, 0), depolarizing_constant=0.95) + assert 0 < ch.unitarity < 1.0 + + def test_pauli_vector_sums_to_one(self): + """Pauli error probability vector should sum to 1.""" + ch = Channel.from_depolarizing_constant(inst=X(0), depolarizing_constant=0.97) + pv = ch.pauli_vector + assert float(jnp.sum(pv)) == pytest.approx(1.0, abs=1e-8) + + def test_perfect_channel(self): + """A depolarizing constant of 1.0 should give fidelity 1.0.""" + ch = Channel.from_depolarizing_constant(inst=RX(np.pi, 0), depolarizing_constant=1.0) + assert ch.fidelity == pytest.approx(1.0, abs=1e-10) + + def test_from_pauli_noise_rejects_invalid_probabilities(self): + """Pauli error rates must be probabilities with total error no greater than 1.""" + with pytest.raises(ValueError, match="negative"): + Channel.from_pauli_noise(inst=RX(0.5, 0), pauli_noise={"X": -0.1}) + + with pytest.raises(ValueError, match="at most 1.0"): + Channel.from_pauli_noise(inst=RX(0.5, 0), pauli_noise={"X": 0.6, "Z": 0.5}) + + def test_from_pauli_noise_two_qubit(self): + """from_pauli_noise builds the correct 16-term Pauli channel for a 2Q gate (regression).""" + pauli_noise = {"IX": 0.01, "XI": 0.005, "ZZ": 0.02} + ch = Channel.from_pauli_noise(inst=CNOT(0, 1), pauli_noise=pauli_noise) + pv = np.asarray(ch.pauli_vector) + assert pv.size == 16 + assert float(jnp.sum(ch.pauli_vector)) == pytest.approx(1.0, abs=1e-3) + terms = [a + b for a in "IXYZ" for b in "IXYZ"] + rates = dict(zip(terms, pv, strict=True)) + for term, rate in pauli_noise.items(): + assert rates[term] == pytest.approx(rate, abs=1e-3) + assert rates["II"] == pytest.approx(1.0 - sum(pauli_noise.values()), abs=1e-3) + + def test_pow_scales_noise(self): + """Channel ** power scales the noise while preserving the gate.""" + ch = Channel.from_depolarizing_constant(inst=RX(np.pi / 2, 0), depolarizing_constant=0.99) + assert (ch**0.0).pauli_infidelity == pytest.approx(0.0, abs=1e-3) + assert (ch**1.0).pauli_infidelity == pytest.approx(ch.pauli_infidelity, abs=1e-3) + assert (ch**2.0).pauli_infidelity > ch.pauli_infidelity + # The ideal gate is preserved. + assert (ch**2.0).qubits == ch.qubits + assert jnp.allclose((ch**2.0).target_unitary.matrix, ch.target_unitary.matrix) + + def test_json_roundtrip_preserves_qutrit_dims(self): + """Channel JSON includes explicit dims for non-qubit operators.""" + qutrit_x = jnp.array( + [ + [0.0, 0.0, 1.0], + [1.0, 0.0, 0.0], + [0.0, 1.0, 0.0], + ], + dtype=complex, + ) + target_unitary = qx.Unitary.from_matrix(qutrit_x, ((3,), (3,))) + channel = Channel( + inst=Gate("TX", [], [0]), process=qx.to_superop(target_unitary), target_unitary=target_unitary + ) + + restored = Channel.from_json(channel.to_json()) + + assert restored.inst == channel.inst + assert restored.process.dims == ((3,), (3,)) + assert restored.target_unitary.dims == ((3,), (3,)) + assert jnp.allclose(restored.process.matrix, channel.process.matrix) + + +# ────────────────────────────────────────────────────────── +# MeasurementChannel tests +# ────────────────────────────────────────────────────────── + + +class TestMeasurementChannel: + def test_from_readout_fidelity(self): + """MeasurementChannel.from_readout_fidelity produces a valid quantum instrument.""" + # Use the pyquil MEASURE gate to get qubit + prog = Program(MEASURE(0, None)) + meas_inst = [i for i in prog if isinstance(i, Measurement)][0] + ch = MeasurementChannel.from_readout_fidelity(inst=meas_inst, fidelity=0.95) + assert isinstance(ch.process, qx.QuantumInstrument) + + def test_from_readout_fidelity_with_asymmetry(self): + """MeasurementChannel with asymmetry produces asymmetric confusion.""" + prog = Program(MEASURE(0, None)) + meas_inst = [i for i in prog if isinstance(i, Measurement)][0] + ch = MeasurementChannel.from_readout_fidelity(inst=meas_inst, fidelity=0.95, asymmetry=0.5) + assert isinstance(ch.process, qx.QuantumInstrument) + + def test_qubits(self): + """MeasurementChannel.qubits returns the correct qubit.""" + prog = Program(MEASURE(5, None)) + meas_inst = [i for i in prog if isinstance(i, Measurement)][0] + ch = MeasurementChannel.from_readout_fidelity(inst=meas_inst, fidelity=0.99) + assert ch.qubits == [5] + + @pytest.mark.parametrize("asymmetry", [0.0, 0.5]) + def test_pow_scales_readout_noise(self, asymmetry): + """MeasurementChannel ** power scales readout noise via the stochastic generator.""" + prog = Program(MEASURE(0, None)) + meas_inst = [i for i in prog if isinstance(i, Measurement)][0] + ch = MeasurementChannel.from_readout_fidelity(inst=meas_inst, fidelity=0.95, asymmetry=asymmetry) + + def bitflip(channel): + cm = np.asarray(channel.confusion_matrix) + return 1.0 - 0.5 * (float(cm[0, 0]) + float(cm[1, 1])) + + assert bitflip(ch**0.0) == pytest.approx(0.0, abs=1e-3) + assert bitflip(ch**1.0) == pytest.approx(bitflip(ch), abs=1e-3) + assert bitflip(ch**2.0) > bitflip(ch) + # The generator construction keeps the result exactly column-stochastic and non-negative. + powered = np.asarray((ch**1.5).confusion_matrix) + assert np.all(powered >= -1e-9) + assert np.allclose(powered.sum(axis=0), 1.0, atol=1e-6) + + def test_pow_rejects_non_embeddable_measurement(self): + """A confusion matrix with no real generator cannot be fractionally powered.""" + prog = Program(MEASURE(0, None)) + meas_inst = [i for i in prog if isinstance(i, Measurement)][0] + # Near-complete bit flip: eigenvalue ~ -0.8, so the matrix is not embeddable. + confusion = jnp.array([[0.1, 0.9], [0.9, 0.1]]) + ch = MeasurementChannel.from_confusion_and_transition(meas_inst, confusion, jnp.eye(2)) + with pytest.raises(ValueError, match="not embeddable|not a valid"): + _ = ch**0.5 + + def test_from_binary_discriminator_qubit_is_faithful_readout(self): + """Regression: dim=2/threshold=1 is a real qubit readout, not an always-0 collapse. + + The previous implementation mapped both |0> and |1> to outcome 0 for a qubit, + silently erasing all measurement information. + """ + prog = Program(MEASURE(0, None)) + meas_inst = [i for i in prog if isinstance(i, Measurement)][0] + ch = MeasurementChannel.from_binary_discriminator(inst=meas_inst, dim=2, threshold=1) + cm = np.asarray(ch.confusion_matrix) + assert cm.shape == (2, 2) # two reachable outcomes + assert np.allclose(cm, np.eye(2)) # |0> -> 0, |1> -> 1 + + def test_from_binary_discriminator_qutrit_split(self): + """dim=3 splits into exactly two outcomes at the threshold.""" + prog = Program(MEASURE(0, None)) + meas_inst = [i for i in prog if isinstance(i, Measurement)][0] + # threshold=2: {0,1} -> 0, {2} -> 1 (flag leakage only) + cm2 = np.asarray(MeasurementChannel.from_binary_discriminator(inst=meas_inst, dim=3, threshold=2).confusion_matrix) + assert np.allclose(cm2, [[1, 1, 0], [0, 0, 1]]) + # threshold=1: {0} -> 0, {1,2} -> 1 (ground vs excited-or-leaked) + cm1 = np.asarray(MeasurementChannel.from_binary_discriminator(inst=meas_inst, dim=3, threshold=1).confusion_matrix) + assert np.allclose(cm1, [[1, 0, 0], [0, 1, 1]]) + + def test_from_binary_discriminator_fidelity_degrades(self): + """Sub-unit fidelity stays column-stochastic and keeps both outcomes reachable.""" + prog = Program(MEASURE(0, None)) + meas_inst = [i for i in prog if isinstance(i, Measurement)][0] + cm = np.asarray( + MeasurementChannel.from_binary_discriminator(inst=meas_inst, dim=2, threshold=1, fidelity=0.9).confusion_matrix + ) + assert cm.shape == (2, 2) + assert np.allclose(cm.sum(axis=0), 1.0) + assert cm[1, 1] > cm[0, 1] # |1> still most likely reads as outcome 1 + + def test_from_binary_discriminator_rejects_bad_threshold(self): + """threshold must satisfy 1 <= threshold < dim.""" + prog = Program(MEASURE(0, None)) + meas_inst = [i for i in prog if isinstance(i, Measurement)][0] + with pytest.raises(ValueError, match="threshold"): + MeasurementChannel.from_binary_discriminator(inst=meas_inst, dim=2, threshold=2) + + def test_json_roundtrip_preserves_qutrit_dims(self): + """MeasurementChannel JSON includes explicit dims for non-qubit instruments.""" + prog = Program(MEASURE(0, None)) + meas_inst = [i for i in prog if isinstance(i, Measurement)][0] + channel = MeasurementChannel.from_readout_fidelity(inst=meas_inst, fidelity=0.95, dim=3) + + restored = MeasurementChannel.from_json(channel.to_json()) + + assert restored.inst == channel.inst + assert restored.process.dims == channel.process.dims + assert restored.process.measured_qudits == channel.process.measured_qudits + assert jnp.allclose(restored.process.matrix, channel.process.matrix) + + +# ────────────────────────────────────────────────────────── +# NoiseModel tests +# ────────────────────────────────────────────────────────── + + +class TestNoiseModel: + def test_empty_model(self): + """An empty NoiseModel has no channels.""" + nm = NoiseModel() + assert nm.get_channel(RX(0.5, 0)) is None + + def test_constructor_accepts_instruction_mapping(self): + """NoiseModel stores channels keyed by instruction.""" + inst = RX(np.pi / 4, 0) + ch = Channel.from_depolarizing_constant(inst=inst, depolarizing_constant=0.98) + nm = NoiseModel(channels={inst: ch}) + assert nm.channels[inst] is ch + assert nm.get_channel(inst) is ch + + def test_constructor_rejects_channel_iterable(self): + """Sequence construction should go through from_channels.""" + inst = RX(np.pi / 4, 0) + ch = Channel.from_depolarizing_constant(inst=inst, depolarizing_constant=0.98) + with pytest.raises(TypeError, match="from_channels"): + NoiseModel(channels=[ch]) # type: ignore[arg-type] + + def test_pickle_roundtrip(self): + """NoiseModel survives pickling (its MappingProxyType channels would otherwise block it). + + This is what lets a model be shipped to multiprocessing workers. + """ + import pickle + + gate = RX(np.pi / 4, 0) + prog = Program(MEASURE(0, None)) + meas_inst = [i for i in prog if isinstance(i, Measurement)][0] + nm = NoiseModel.from_channels( + [ + Channel.from_depolarizing_constant(inst=gate, depolarizing_constant=0.98), + MeasurementChannel.from_readout_fidelity(inst=meas_inst, fidelity=0.95), + ] + ) + restored = pickle.loads(pickle.dumps(nm)) + assert set(restored.channels) == set(nm.channels) + assert isinstance(restored.get_channel(gate), Channel) + # Channels survive the round-trip intact (exercises value-based __eq__). + assert restored == nm + + def test_constructor_rejects_mismatched_mapping_key(self): + """Mapping keys must match the instruction stored on each channel.""" + inst = RX(np.pi / 4, 0) + ch = Channel.from_depolarizing_constant(inst=inst, depolarizing_constant=0.98) + with pytest.raises(ValueError, match="does not match"): + NoiseModel(channels={RY(np.pi / 2, 0): ch}) + + def test_from_channels_rejects_duplicates(self): + """Duplicate instruction channels are ambiguous and rejected.""" + inst = RX(np.pi / 4, 0) + ch1 = Channel.from_depolarizing_constant(inst=inst, depolarizing_constant=0.98) + ch2 = Channel.from_depolarizing_constant(inst=inst, depolarizing_constant=0.97) + with pytest.raises(ValueError, match="Duplicate noise channel"): + NoiseModel.from_channels([ch1, ch2]) + + def test_get_channel_gate(self): + """NoiseModel.get_channel returns the correct Channel for a gate.""" + inst = RX(np.pi / 4, 0) + ch = Channel.from_depolarizing_constant(inst=inst, depolarizing_constant=0.98) + nm = NoiseModel.from_channels([ch]) + retrieved = nm.get_channel(inst) + assert retrieved is ch + + def test_get_channel_returns_none_for_missing(self): + """get_channel returns None for instructions not in the model.""" + inst = RX(np.pi / 4, 0) + ch = Channel.from_depolarizing_constant(inst=inst, depolarizing_constant=0.98) + nm = NoiseModel.from_channels([ch]) + other_inst = RY(np.pi / 2, 1) + assert nm.get_channel(other_inst) is None + + def test_multiple_channels(self): + """NoiseModel with multiple channels retrieves each correctly.""" + inst1 = RX(0.5, 0) + inst2 = RY(0.3, 1) + inst3 = CNOT(0, 1) + ch1 = Channel.from_depolarizing_constant(inst=inst1, depolarizing_constant=0.99) + ch2 = Channel.from_depolarizing_constant(inst=inst2, depolarizing_constant=0.97) + ch3 = Channel.from_depolarizing_constant(inst=inst3, depolarizing_constant=0.95) + nm = NoiseModel.from_channels([ch1, ch2, ch3]) + assert nm.get_channel(inst1) is ch1 + assert nm.get_channel(inst2) is ch2 + assert nm.get_channel(inst3) is ch3 + + def test_json_roundtrip(self): + """NoiseModel JSON keeps the existing channel-list wire format.""" + ch = Channel.from_depolarizing_constant(inst=RX(np.pi / 4, 0), depolarizing_constant=0.98) + meas_ch = MeasurementChannel.from_readout_fidelity(inst=MEASURE(1, None), fidelity=0.95) + nm = NoiseModel.from_channels([ch, meas_ch]) + + restored = NoiseModel.from_json(nm.to_json()) + + assert restored == nm + assert set(restored.channels) == {ch.inst, meas_ch.inst} + + def test_add_combines_disjoint_channels(self): + """NoiseModel addition preserves disjoint channels from both operands.""" + ch1 = Channel.from_depolarizing_constant(inst=RX(np.pi / 4, 0), depolarizing_constant=0.98) + ch2 = Channel.from_depolarizing_constant(inst=RY(np.pi / 4, 1), depolarizing_constant=0.97) + + combined = NoiseModel.from_channels([ch1]) + NoiseModel.from_channels([ch2]) + + assert combined.get_channel(ch1.inst) == ch1 + assert combined.get_channel(ch2.inst) == ch2 + + def test_add_rejects_overlapping_channels(self): + """Addition is a disjoint union; a shared instruction is a conflict, not a composition.""" + inst = RX(np.pi / 4, 0) + ch1 = Channel.from_depolarizing_constant(inst=inst, depolarizing_constant=0.98) + ch2 = Channel.from_depolarizing_constant(inst=inst, depolarizing_constant=0.97) + + with pytest.raises(ValueError, match="same instruction"): + _ = NoiseModel.from_channels([ch1]) + NoiseModel.from_channels([ch2]) + + def test_with_channels_returns_extended_model(self): + """with_channels returns a new model and rejects duplicate instructions.""" + ch1 = Channel.from_depolarizing_constant(inst=RX(np.pi / 4, 0), depolarizing_constant=0.98) + ch2 = Channel.from_depolarizing_constant(inst=RY(np.pi / 4, 1), depolarizing_constant=0.97) + nm = NoiseModel.from_channels([ch1]) + + extended = nm.with_channels([ch2]) + + assert nm.get_channel(ch2.inst) is None + assert extended.get_channel(ch1.inst) is ch1 + assert extended.get_channel(ch2.inst) is ch2 + with pytest.raises(ValueError, match="Duplicate noise channel"): + nm.with_channels([ch1]) + + def test_noise_model_is_unhashable(self): + """NoiseModel is unhashable (consistent with value-based equality).""" + nm = NoiseModel.from_channels([Channel.from_depolarizing_constant(RX(0.5, 0), 0.98)]) + with pytest.raises(TypeError): + hash(nm) + + +# ────────────────────────────────────────────────────────── +# get_instruction_unitary tests +# ────────────────────────────────────────────────────────── + + +class TestGetInstructionUnitary: + def test_standard_gate(self): + """get_instruction_unitary resolves standard gates.""" + u = get_instruction_unitary(RX(np.pi / 2, 0)) + assert isinstance(u, qx.Unitary) + assert u.matrix.shape == (2, 2) + + def test_two_qubit_gate(self): + """get_instruction_unitary resolves 2Q gates.""" + u = get_instruction_unitary(CNOT(0, 1)) + assert isinstance(u, qx.Unitary) + assert u.matrix.shape == (4, 4) + + def test_custom_gate(self): + """get_instruction_unitary resolves custom gates from custom_gates dict.""" + custom_matrix = np.array([[0, 1], [1, 0]], dtype=complex) + inst = Gate("MY_GATE", [], [0]) + u = get_instruction_unitary(inst, custom_gates={"MY_GATE": qx.Unitary.from_matrix(custom_matrix, ((2,), (2,)))}) + assert isinstance(u, qx.Unitary) + assert np.allclose(np.asarray(u.matrix), custom_matrix) + + +# ────────────────────────────────────────────────────────── +# ResetChannel tests +# ────────────────────────────────────────────────────────── + + +class TestResetChannel: + def test_from_reset_fidelity_perfect(self): + """A perfect reset (fidelity=1.0) should produce a valid superoperator.""" + inst = RESET(0) + ch = ResetChannel.from_reset_fidelity(inst=inst, fidelity=1.0) + assert isinstance(ch.process, qx.SuperOp) + + def test_from_reset_fidelity_noisy(self): + """A noisy reset should have lower fidelity than a perfect one.""" + inst = RESET(0) + ch_perfect = ResetChannel.from_reset_fidelity(inst=inst, fidelity=1.0) + ch_noisy = ResetChannel.from_reset_fidelity(inst=inst, fidelity=0.95) + assert isinstance(ch_noisy.process, qx.SuperOp) + assert ch_noisy.fidelity < ch_perfect.fidelity + + def test_qubits(self): + """ResetChannel.qubits returns the correct qubit.""" + inst = RESET(3) + ch = ResetChannel.from_reset_fidelity(inst=inst, fidelity=0.99) + assert ch.qubits == [3] + + def test_ideal_reset_maps_excited_to_ground(self): + """An ideal reset on an excited qubit should produce |0><0|.""" + inst = RESET(0) + ch = ResetChannel.from_reset_fidelity(inst=inst, fidelity=1.0) + noise_model = NoiseModel.from_channels([ch]) + # Prepare |1> then reset + program = Program(X(0), RESET(0)) + rho = _dm(program, noise_model=noise_model) + target_rho = qx.zero_state_matrix(1) + assert qx.fidelity(rho, target_rho) > 0.9999 + + def test_ideal_reset_maps_superposition_to_ground(self): + """An ideal reset on a superposition state should produce |0><0|.""" + inst = RESET(0) + ch = ResetChannel.from_reset_fidelity(inst=inst, fidelity=1.0) + noise_model = NoiseModel.from_channels([ch]) + # Prepare |+> then reset + program = Program(RX(np.pi / 2, 0), RESET(0)) + rho = _dm(program, noise_model=noise_model) + target_rho = qx.zero_state_matrix(1) + assert qx.fidelity(rho, target_rho) > 0.9999 + + def test_noisy_reset_reduces_fidelity(self): + """A noisy reset should produce a state with fidelity < 1 relative to |0><0|.""" + inst = RESET(0) + ch = ResetChannel.from_reset_fidelity(inst=inst, fidelity=0.90) + noise_model = NoiseModel.from_channels([ch]) + program = Program(X(0), RESET(0)) + rho = _dm(program, noise_model=noise_model) + target_rho = qx.zero_state_matrix(1) + fid = float(qx.fidelity(rho, target_rho)) + # Should be less than perfect but still high + assert 0.85 < fid < 1.0 + + def test_reset_in_multi_qubit_circuit(self): + """Reset on one qubit should not affect the other qubit.""" + inst = RESET(0) + ch = ResetChannel.from_reset_fidelity(inst=inst, fidelity=1.0) + noise_model = NoiseModel.from_channels([ch]) + # Prepare |11> then reset qubit 0 + program = Program(X(0), X(1), RESET(0)) + rho = _dm(program, noise_model=noise_model) + # Expected state: |0> on qubit 0, |1> on qubit 1 → |01> + target_rho = (qx.gates.I | qx.gates.X) @ qx.zero_state_matrix(2) + assert qx.fidelity(rho, target_rho) > 0.9999 + + def test_global_reset(self): + """A global RESET (no qubit specified) resets all qubits to |0>.""" + program = Program(X(0), X(1), RESET()) + rho = _dm(program) + target_rho = qx.zero_state_matrix(2) + assert qx.fidelity(rho, target_rho) > 0.9999 + + def test_global_reset_channel_rejected(self): + """ResetChannel is intentionally scoped to targeted resets.""" + with pytest.raises(TypeError, match="targeted"): + ResetChannel.from_reset_fidelity(inst=RESET(), fidelity=1.0) + + def test_json_roundtrip_preserves_qutrit_dims(self): + """ResetChannel JSON includes explicit dims for non-qubit resets.""" + channel = ResetChannel.from_reset_fidelity(inst=ResetQubit(0), fidelity=0.9, dim=3) + + restored = ResetChannel.from_json(channel.to_json()) + + assert restored.inst == channel.inst + assert restored.process.dims == ((3,), (3,)) + assert jnp.allclose(restored.process.matrix, channel.process.matrix) + + +# ────────────────────────────────────────────────────────── +# Channel equality / hashing semantics +# ────────────────────────────────────────────────────────── + + +class TestChannelEqualityAndHashing: + def test_channels_are_unhashable(self): + """Channels hold jax arrays and are intentionally unhashable.""" + ch = Channel.from_depolarizing_constant(RX(0.5, 0), 0.98) + with pytest.raises(TypeError): + hash(ch) + + def test_channel_equality_is_exact(self): + """Channel equality is exact: identical builds are equal, different ones are not.""" + ch1 = Channel.from_depolarizing_constant(RX(0.5, 0), 0.98) + ch2 = Channel.from_depolarizing_constant(RX(0.5, 0), 0.98) + ch3 = Channel.from_depolarizing_constant(RX(0.5, 0), 0.97) + assert ch1 == ch2 + assert ch1 != ch3 + assert ch1 != "not a channel" + + def test_channel_inequality_on_different_instruction(self): + """Channels on different instructions are never equal.""" + ch_a = Channel.from_depolarizing_constant(RX(0.5, 0), 0.98) + ch_b = Channel.from_depolarizing_constant(RX(0.5, 1), 0.98) + assert ch_a != ch_b + + +# ────────────────────────────────────────────────────────── +# Channel construction / analysis coverage +# ────────────────────────────────────────────────────────── + + +class TestChannelAnalysis: + def test_from_mixture(self): + """from_mixture builds a noisy channel from unitary errors with probabilities.""" + z = qx.Unitary.from_matrix(jnp.array([[1, 0], [0, -1]], dtype=complex), ((2,), (2,))) + ch = Channel.from_mixture(X(0), constituents=[z], probabilities=[0.1]) + assert isinstance(ch.process, qx.SuperOp) + assert ch.pauli_infidelity > 0.0 + + def test_coherent_and_stochastic_decomposition(self): + """to_coherent_channel / to_stochastic_channel split the noise into components.""" + ch = Channel.from_random_coherent_error(X(0), process_fidelity=0.95, rng=np.random.default_rng(0)) + coherent = ch.to_coherent_channel() + stochastic = ch.to_stochastic_channel() + assert isinstance(coherent, Channel) + assert isinstance(stochastic, Channel) + # Coherent + stochastic infidelity decomposition is non-negative and finite. + assert np.isfinite(ch.coherent_infidelity) + assert np.isfinite(ch.stochastic_infidelity) + + def test_pauli_twirl_is_pauli(self): + """Twirling a coherent-error channel yields a stochastic Pauli channel.""" + ch = Channel.from_random_coherent_error(X(0), process_fidelity=0.95, rng=np.random.default_rng(1)) + twirled = ch.pauli_twirl() + assert twirled.is_pauli() + + +# ────────────────────────────────────────────────────────── +# NoiseModel.from_isa +# ────────────────────────────────────────────────────────── + + +class TestFromIsa: + @staticmethod + def _isa() -> CompilerISA: + return CompilerISA.parse_obj( + { + "1Q": { + "0": { + "id": 0, + "gates": [ + { + "operator_type": "gate", + "operator": "RX", + "parameters": [1.5707963267948966], + "arguments": ["_"], + "fidelity": 0.99, + }, + # A fidelity-less measurement entry must not mask the real one below. + {"operator_type": "measure", "qubit": "0", "fidelity": None}, + {"operator_type": "measure", "qubit": "0", "fidelity": 0.95}, + ], + }, + "1": {"id": 1, "gates": []}, + }, + "2Q": { + "0-1": { + "ids": [0, 1], + "gates": [ + { + "operator_type": "gate", + "operator": "CZ", + "parameters": [], + "arguments": ["_", "_"], + "fidelity": 0.9, + } + ], + } + }, + } + ) + + def test_builds_gate_and_edge_channels(self): + nm = NoiseModel.from_isa(self._isa()) + assert isinstance(nm.get_channel(Gate("RX", [1.5707963267948966], [0])), Channel) + assert isinstance(nm.get_channel(Gate("CZ", [], [0, 1])), Channel) + + def test_measurement_dedup_prefers_real_fidelity(self): + """A None-fidelity measure entry must not block a later usable one (dedup ordering).""" + nm = NoiseModel.from_isa(self._isa()) + channel = nm.get_channel(Measurement(qubit=Qubit(0), classical_reg=None)) + assert isinstance(channel, MeasurementChannel) + + +class TestCycleChannel: + def test_complete_cycle_constructs(self): + """A CycleChannel whose channels cover every DefCircuit body instruction is valid.""" + channels = tuple( + Channel.from_depolarizing_constant(inst, depolarizing_constant=0.99) for inst in (RX(0.1, 0), RZ(0.2, 1)) + ) + cycle = _build_cycle_channel(list(channels)) + assert cycle.channels == channels + + def test_incomplete_cycle_rejected(self): + """A body instruction with no corresponding channel is a footgun and must raise.""" + q0, q1 = FormalArgument("q0"), FormalArgument("q1") + # DefCircuit body has two gates, but only one channel is supplied for q0. + defcircuit = DefCircuit("CYCLE", [], [q0, q1], [RX(0.1, q0), RZ(0.2, q1)]) + cycle_inst = Gate("CYCLE", [], [Qubit(0), Qubit(1)]) + channels = (Channel.from_depolarizing_constant(RX(0.1, 0), depolarizing_constant=0.99),) + + with pytest.raises(ValueError, match="incomplete"): + _ = CycleChannel(inst=cycle_inst, defcircuit=defcircuit, channels=channels) diff --git a/test/unit/test_qutrit_simulation.py b/test/unit/test_qutrit_simulation.py new file mode 100644 index 000000000..22dac99f7 --- /dev/null +++ b/test/unit/test_qutrit_simulation.py @@ -0,0 +1,716 @@ +"""Unit tests for qutrit (d=3) and mixed qubit/qutrit simulation.""" + +import jax +import jax.numpy as jnp +import numpy as np +import pytest +import quax as qx + +from pyquil.gates import H, X +from pyquil.noise._channels import Channel, MeasurementChannel, ResetChannel +from pyquil.noise._noise_model import NoiseModel +from pyquil.quil import Program +from pyquil.quilatom import MemoryReference, Qubit +from pyquil.quilbase import Declare, DefGate, Gate, Measurement +from pyquil.simulation._simulator import ( + DensityMatrixSimulator, + PureStateVectorSimulator, + TrajectorySimulator, +) + +_EMPTY_PARAMS = jnp.array([], dtype=float) + + +def _sv(program, qubits=None): + """Compute pure state vector for a gate-only program.""" + sim = PureStateVectorSimulator(program, qubits=qubits) + return sim.compute(_EMPTY_PARAMS) + + +def _dm(program, qubits=None, noise_model=None): + """Compute density matrix.""" + sim = DensityMatrixSimulator(program, qubits=qubits, noise_model=noise_model) + return sim.compute(_EMPTY_PARAMS) + + +def _sample(program, qubits=None, noise_model=None, num_trajectories=1000, + batch_size=250, random_seed=0): + """Run trajectory sampling, returning outcomes.""" + sim = TrajectorySimulator(program, qubits=qubits, noise_model=noise_model) + return sim.sample(_EMPTY_PARAMS, num_trajectories=num_trajectories, + batch_size=batch_size, random_seed=random_seed) + + +# ══════════════════════════════════════════════════════════ +# Test: Apply qutrit channels to programs +# ══════════════════════════════════════════════════════════ + + +class TestQutritProgramSimulation: + """Test that qutrit gates in programs produce correct state vectors.""" + + def test_tx_gate_cycles(self): + """TX (cyclic shift) maps |0> -> |2> -> |1> -> |0>.""" + # Apply TX once: |0> -> |2> + p = Program() + p += Gate("TX", [], [0]) + psi = _sv(p, qubits=[0]) + expected = qx.StateVector.from_matrix( + jnp.array([0, 0, 1], dtype=complex), dims=(3,) + ) + assert qx.fidelity(psi, expected) > 0.9999 + + def test_tx_gate_double(self): + """TX^2 maps |0> -> |1>.""" + p = Program() + p += Gate("TX", [], [0]) + p += Gate("TX", [], [0]) + psi = _sv(p, qubits=[0]) + expected = qx.StateVector.from_matrix( + jnp.array([0, 1, 0], dtype=complex), dims=(3,) + ) + assert qx.fidelity(psi, expected) > 0.9999 + + def test_tx_gate_triple_identity(self): + """TX^3 = I for qutrits.""" + p = Program() + p += Gate("TX", [], [0]) + p += Gate("TX", [], [0]) + p += Gate("TX", [], [0]) + psi = _sv(p, qubits=[0]) + expected = qx.StateVector.from_matrix( + jnp.array([1, 0, 0], dtype=complex), dims=(3,) + ) + assert qx.fidelity(psi, expected) > 0.9999 + + def test_th_creates_superposition(self): + """TH (qutrit Hadamard/QFT) creates uniform superposition from |0>.""" + p = Program() + p += Gate("TH", [], [0]) + psi = _sv(p, qubits=[0]) + # QFT on |0> = (|0> + |1> + |2>) / sqrt(3) + expected = qx.StateVector.from_matrix( + jnp.array([1, 1, 1], dtype=complex) / jnp.sqrt(3), dims=(3,) + ) + assert qx.fidelity(psi, expected) > 0.9999 + + def test_tz_clock_matrix(self): + """TZ (clock matrix) adds phases: |k> -> omega^k |k>.""" + # TX|0> = |2>, then TZ on |2> should give omega^2 * |2> where omega = exp(2*pi*i/3) + p = Program() + p += Gate("TX", [], [0]) + p += Gate("TZ", [], [0]) + psi = _sv(p, qubits=[0]) + omega = jnp.exp(2j * jnp.pi / 3) + expected = qx.StateVector.from_matrix( + jnp.array([0, 0, omega**2], dtype=complex), dims=(3,) + ) + assert qx.fidelity(psi, expected) > 0.9999 + + def test_parametric_trx01(self): + """TRX01(pi) acts as a pi rotation in the |0>-|1> subspace.""" + p = Program() + p += Gate("TRX01", [np.pi], [0]) + psi = _sv(p, qubits=[0]) + # RX(pi)|0> = -i|1> in the 0-1 subspace, |2> untouched + expected = qx.StateVector.from_matrix( + jnp.array([0, -1j, 0], dtype=complex), dims=(3,) + ) + assert qx.fidelity(psi, expected) > 0.9999 + + def test_parametric_trx02(self): + """TRX02(pi) rotates between |0> and |2> subspace.""" + p = Program() + p += Gate("TRX02", [np.pi], [0]) + psi = _sv(p, qubits=[0]) + # RX(pi) in 0-2 subspace: |0> -> -i|2> + expected = qx.StateVector.from_matrix( + jnp.array([0, 0, -1j], dtype=complex), dims=(3,) + ) + assert qx.fidelity(psi, expected) > 0.9999 + + def test_two_qutrit_tswap(self): + """TSWAP swaps two qutrit registers.""" + # Prepare |2>|0> (TX maps |0>->|2>) then swap -> |0>|2> + p = Program() + p += Gate("TX", [], [0]) + p += Gate("TSWAP", [], [0, 1]) + psi = _sv(p, qubits=[0, 1]) + # After swap: qubit 0 in |0>, qubit 1 in |2> + # State: |0>|2> in (3,3) space -> index 0*3 + 2 = 2 in 9-dim + expected_vec = jnp.zeros(9, dtype=complex).at[2].set(1.0) + expected = qx.StateVector.from_matrix(expected_vec, dims=(3, 3)) + assert qx.fidelity(psi, expected) > 0.9999 + + def test_multi_qutrit_independence(self): + """Two independent qutrit operations on separate registers.""" + p = Program() + p += Gate("TX", [], [0]) # |0> -> |2> + p += Gate("TH", [], [1]) # |0> -> (|0>+|1>+|2>)/sqrt(3) + psi = _sv(p, qubits=[0, 1]) + assert psi.dims == (3, 3) + # Product state: |2> ⊗ (|0>+|1>+|2>)/sqrt(3) + q0 = jnp.array([0, 0, 1], dtype=complex) + q1 = jnp.array([1, 1, 1], dtype=complex) / jnp.sqrt(3) + expected_vec = jnp.kron(q0, q1) + expected = qx.StateVector.from_matrix(expected_vec, dims=(3, 3)) + assert qx.fidelity(psi, expected) > 0.9999 + + +# ══════════════════════════════════════════════════════════ +# Test: All quax qutrit gates through the pure-state simulator +# ══════════════════════════════════════════════════════════ + + +def _quax_qutrit_gates(): + """Yield ``(name, gate)`` for every unitary qutrit gate in quax. + + Projectors (TP0/TP1/TP2) are excluded because they are not unitary and + therefore not valid for the pure-state simulator. + """ + for name, gate in qx.gates.QUANTUM_GATES.items(): + if name.startswith("TP"): + continue # projectors are not unitary + if callable(gate): + # Parametric gates accept one or more angles; probe arity. + unitary = None + for n_args in (1, 2, 3): + try: + unitary = gate(*([0.0] * n_args)) + break + except TypeError: + continue + if unitary is None: + continue + else: + unitary = gate + if any(d == 3 for d in unitary.dims[1]): + yield name, gate + + +class TestAllQutritGates: + """Every unitary qutrit gate in quax simulates correctly on |0...0>.""" + + # Single-qutrit fixed gates (non-parametric, dims == (3,)). + SINGLE_FIXED = [ + name + for name, gate in _quax_qutrit_gates() + if not callable(gate) and gate.dims[1] == (3,) + ] + # Parametric single-qutrit rotations (callable, dims == (3,)). + SINGLE_PARAM = [ + name + for name, gate in _quax_qutrit_gates() + if callable(gate) + ] + + def test_gate_inventory_is_nonempty(self): + """Sanity check: quax exposes the expected qutrit gate families.""" + # Clock/shift, Hadamard, Pauli-like, and Weyl operators are all present. + for expected in ("TX", "TY", "TZ", "TH", "TSHIFT", "TSWAP", "W00", "W22"): + assert expected in qx.gates.QUANTUM_GATES + assert set(self.SINGLE_PARAM) >= { + "TRX01", "TRY01", "TRZ01", + "TRX02", "TRY02", "TRZ02", + "TRX12", "TRY12", "TRZ12", + } + + @pytest.mark.parametrize("name", SINGLE_FIXED) + def test_single_qutrit_fixed_gate(self, name): + """Each fixed single-qutrit gate produces the expected |0> column.""" + p = Program(Gate(name, [], [0])) + psi = _sv(p, qubits=[0]) + assert psi.dims == (3,) + # The output equals the gate's first column (its action on |0>). + expected = qx.gates.QUANTUM_GATES[name].matrix[:, 0] + np.testing.assert_allclose(psi.matrix, expected, atol=1e-6) + # Unitary gates preserve normalization. + np.testing.assert_allclose( + float(jnp.sum(jnp.abs(psi.matrix) ** 2)), 1.0, atol=1e-6 + ) + + @pytest.mark.parametrize("name", SINGLE_PARAM) + def test_single_qutrit_parametric_gate(self, name): + """Each parametric single-qutrit rotation simulates and is unitary.""" + angle = np.pi / 3 + p = Program(Gate(name, [angle], [0])) + sim = PureStateVectorSimulator(p, qubits=[0]) + psi = sim.compute(jnp.array([], dtype=float)) + assert psi.dims == (3,) + expected = qx.gates.QUANTUM_GATES[name](angle).matrix[:, 0] + np.testing.assert_allclose(psi.matrix, expected, atol=1e-6) + np.testing.assert_allclose( + float(jnp.sum(jnp.abs(psi.matrix) ** 2)), 1.0, atol=1e-6 + ) + + def test_parametric_qutrit_via_memory_map(self): + """A parametric qutrit rotation resolves a runtime memory parameter.""" + p = Program() + p += Declare("theta", "REAL", 1) + p += Gate("TRX01", [MemoryReference("theta")], [0]) + sim = PureStateVectorSimulator(p, qubits=[0]) + params = sim.linearize({"theta": [np.pi]}) + psi = sim.compute(params) + # TRX01(pi)|0> = -i|1> (pi rotation in the 0-1 subspace). + expected = qx.StateVector.from_matrix( + jnp.array([0, -1j, 0], dtype=complex), dims=(3,) + ) + assert qx.fidelity(psi, expected) > 0.9999 + + def test_two_qutrit_tswap_at_position_one(self): + """A single-qutrit gate merged at the high slot of a TSWAP pair. + + Exercises embedding a qutrit gate at a non-zero position within a + two-qutrit merge group (TH on slot 1 alongside TSWAP on (0, 1)). + """ + p = Program() + p += Gate("TX", [], [0]) # |0> -> |2> on slot 0 + p += Gate("TH", [], [1]) # superposition on slot 1 + p += Gate("TSWAP", [], [0, 1]) # swap the two qutrits + psi = _sv(p, qubits=[0, 1]) + assert psi.dims == (3, 3) + # After swap: slot 0 holds TH|0>, slot 1 holds |2>. + q0 = jnp.array([1, 1, 1], dtype=complex) / jnp.sqrt(3) + q1 = jnp.array([0, 0, 1], dtype=complex) + expected = qx.StateVector.from_matrix(jnp.kron(q0, q1), dims=(3, 3)) + assert qx.fidelity(psi, expected) > 0.9999 + + def test_jit_and_grad_qutrit(self): + """The qutrit pure-state simulator is jit- and grad-friendly.""" + p = Program() + p += Declare("theta", "REAL", 1) + p += Gate("TRX01", [MemoryReference("theta")], [0]) + sim = PureStateVectorSimulator(p, qubits=[0]) + + def excited_population(theta): + params = jnp.array([theta], dtype=float) + psi = sim.compute(params) + return jnp.abs(psi.matrix[1]) ** 2 + + # jit produces the same result as eager execution. + val_eager = float(excited_population(np.pi / 2)) + val_jit = float(jax.jit(excited_population)(np.pi / 2)) + np.testing.assert_allclose(val_jit, val_eager, atol=1e-6) + # grad is finite and well-defined. + g = float(jax.grad(excited_population)(np.pi / 2)) + assert np.isfinite(g) + + +# ══════════════════════════════════════════════════════════ +# Test: Mixed qubit/qutrit systems +# ══════════════════════════════════════════════════════════ + + +class TestMixedQubitQutrit: + """Test that mixed qubit/qutrit registers are handled correctly.""" + + def test_dimension_inference_mixed(self): + """Dimension inference correctly identifies qubit vs qutrit registers.""" + # Qubit gate on register 0, qutrit gate on register 1 + p = Program() + p += X(0) # qubit gate on q0 + p += Gate("TX", [], [1]) # qutrit gate on q1 + sim = PureStateVectorSimulator(p, qubits=[0, 1]) + assert sim.dims == (2, 3) + + def test_mixed_state_vector_dims(self): + """State vector from mixed system has correct dims.""" + p = Program() + p += X(0) # qubit: |0> -> |1> + p += Gate("TX", [], [1]) # qutrit: |0> -> |2> + psi = _sv(p, qubits=[0, 1]) + assert psi.dims == (2, 3) + # State should be |1>⊗|2> in (2,3) space = index 1*3 + 2 = 5 in 6-dim + expected_vec = jnp.zeros(6, dtype=complex).at[5].set(1.0) + expected = qx.StateVector.from_matrix(expected_vec, dims=(2, 3)) + assert qx.fidelity(psi, expected) > 0.9999 + + def test_mixed_density_matrix_dims(self): + """Density matrix simulator also handles mixed qubit/qutrit.""" + p = Program() + p += X(0) # qubit on q0 + p += Gate("TX", [], [1]) # qutrit on q1 + rho = _dm(p, qubits=[0, 1]) + assert rho.dims == (2, 3) + + def test_mixed_three_registers(self): + """Three registers: qubit, qutrit, qubit.""" + p = Program() + p += X(0) # qubit + p += Gate("TX", [], [1]) # qutrit + p += H(2) # qubit + psi = _sv(p, qubits=[0, 1, 2]) + assert psi.dims == (2, 3, 2) + + def test_mixed_qubit_qutrit_entanglement_via_defgate(self): + """Test entanglement with qutrit gates. + + Note: DefGate requires matrix dimensions that are a perfect power + of an integer (2, 3, 4, 8, 9, 16, 25, 27, ...). Mixed qubit/qutrit + custom gates (e.g. 6x6) cannot use DefGate since 6 is not a + perfect power. We test entanglement using built-in qutrit gates. + """ + # DefGate rejects non-perfect-power matrices (6 = 2*3) + mat = np.eye(6, dtype=complex) + with pytest.raises(ValueError, match="perfect power"): + DefGate("BAD_GATE", mat) + + # Test entanglement using built-in gates instead: + # Use TSWAP to entangle two qutrits + p = Program() + p += Gate("TH", [], [0]) # superposition on q0 + p += Gate("TSWAP", [], [0, 1]) # entangle q0 and q1 + p += Gate("TH", [], [0]) # further evolve q0 + psi = _sv(p, qubits=[0, 1]) + assert psi.dims == (3, 3) + # The state should NOT be a product state (entangled) + # Check by verifying reduced purity < 1 + rho = _dm( + Program([ + Gate("TH", [], [0]), + Gate("TSWAP", [], [0, 1]), + Gate("TH", [], [0]), + ]), + qubits=[0, 1], + ) + full_purity = float(jnp.real(jnp.trace(rho.matrix @ rho.matrix))) + assert full_purity > 0.9999 # pure state + + def test_dimension_inference_density_matrix(self): + """Density matrix preprocess_program infers mixed dims correctly.""" + p = Program([ + H(0), # qubit + Gate("TH", [], [1]), # qutrit + ]) + sim = DensityMatrixSimulator(p, qubits=[0, 1]) + assert sim.dims == (2, 3) + + +# ══════════════════════════════════════════════════════════ +# Test: Qutrit measurements +# ══════════════════════════════════════════════════════════ + + +class TestQutritMeasurements: + """Test that qutrit measurements produce correct outcome distributions.""" + + def test_qutrit_measure_ground_state(self): + """Measuring a qutrit in |0> always yields outcome 0.""" + # Use the identity-like approach: TRX01(0) is identity but establishes dim=3 + p_ground = Program() + p_ground += Gate("TRX01", [0.0], [0]) # identity rotation + p_ground += Measurement(qubit=Qubit(0), classical_reg=None) + + outcomes = _sample( + p_ground, qubits=[0], num_trajectories=100, random_seed=42 + ) + # All outcomes should be 0 (ground state) + assert jnp.all(outcomes == 0) + + def test_qutrit_measure_excited_state(self): + """Measuring a qutrit in |2> (via TX) always yields outcome 2.""" + p = Program() + p += Gate("TX", [], [0]) + p += Measurement(qubit=Qubit(0), classical_reg=None) + + outcomes = _sample( + p, qubits=[0], num_trajectories=100, random_seed=42 + ) + assert jnp.all(outcomes == 2) + + def test_qutrit_measure_second_excited(self): + """Measuring a qutrit in |1> (via TX^2) always yields outcome 1.""" + p = Program() + p += Gate("TX", [], [0]) + p += Gate("TX", [], [0]) + p += Measurement(qubit=Qubit(0), classical_reg=None) + + outcomes = _sample( + p, qubits=[0], num_trajectories=100, random_seed=42 + ) + assert jnp.all(outcomes == 1) + + def test_qutrit_ideal_reset(self): + """An ideal qutrit reset returns any qutrit level to |0>.""" + from pyquil.quilbase import ResetQubit + + p = Program() + p += Gate("TX", [], [0]) + p += ResetQubit(Qubit(0)) + p += Measurement(qubit=Qubit(0), classical_reg=None) + + outcomes = _sample( + p, qubits=[0], num_trajectories=100, random_seed=42 + ) + assert jnp.all(outcomes == 0) + + def test_qutrit_measure_superposition_statistics(self): + """TH|0> = (|0>+|1>+|2>)/sqrt(3) gives uniform measurement distribution.""" + p = Program() + p += Gate("TH", [], [0]) + p += Measurement(qubit=Qubit(0), classical_reg=None) + + n_traj = 3000 + outcomes = _sample( + p, qubits=[0], num_trajectories=n_traj, random_seed=123 + ) + # Each outcome should appear ~1/3 of the time + counts = jnp.bincount(outcomes.flatten(), length=3) + freqs = counts / n_traj + np.testing.assert_allclose(freqs, [1 / 3, 1 / 3, 1 / 3], atol=0.05) + + def test_qutrit_noisy_measurement_channel(self): + """Noisy qutrit measurement with confusion matrix.""" + meas_inst = Measurement(qubit=Qubit(0), classical_reg=None) + meas_ch = MeasurementChannel.from_readout_fidelity( + inst=meas_inst, fidelity=0.9, dim=3 + ) + noise_model = NoiseModel.from_channels([meas_ch]) + + # Prepare |2> (TX|0>=|2>) and measure with noise + p = Program() + p += Gate("TX", [], [0]) + p += Measurement(qubit=Qubit(0), classical_reg=None) + + n_traj = 2000 + outcomes = _sample( + p, noise_model=noise_model, qubits=[0], + num_trajectories=n_traj, random_seed=42, + ) + counts = jnp.bincount(outcomes.flatten(), length=3) + # Should be mostly outcome 2 (the prepared state), with some errors + assert counts[2] / n_traj > 0.7 # majority correct + + def test_mixed_qubit_qutrit_measurement(self): + """Measure both a qubit and qutrit in the same program.""" + p = Program() + p += X(0) # qubit |0> -> |1> + p += Gate("TX", [], [1]) # qutrit |0> -> |2> + p += Measurement(qubit=Qubit(0), classical_reg=None) + p += Measurement(qubit=Qubit(1), classical_reg=None) + + outcomes = _sample( + p, qubits=[0, 1], num_trajectories=50, random_seed=0 + ) + # qubit measurement should give 1, qutrit measurement should give 2 + assert outcomes.shape == (50, 2) + assert jnp.all(outcomes[:, 0] == 1) + assert jnp.all(outcomes[:, 1] == 2) + + +# ══════════════════════════════════════════════════════════ +# Test: Dimension inference +# ══════════════════════════════════════════════════════════ + + +class TestDimensionInference: + """Test the mechanism for deciding initial register dimension.""" + + def test_all_qubit_program(self): + """A program with only qubit gates infers dims=(2,2).""" + p = Program(H(0), X(1)) + sim = PureStateVectorSimulator(p, qubits=[0, 1]) + assert sim.dims == (2, 2) + + def test_single_qutrit_program(self): + """A program with a single qutrit gate infers dims=(3,).""" + p = Program(Gate("TX", [], [0])) + sim = PureStateVectorSimulator(p, qubits=[0]) + assert sim.dims == (3,) + + def test_mixed_dims_from_operations(self): + """Operations on different registers infer heterogeneous dims.""" + p = Program( + H(0), # dim=2 on slot 0 + Gate("TX", [], [1]), # dim=3 on slot 1 + X(2), # dim=2 on slot 2 + ) + sim = PureStateVectorSimulator(p, qubits=[0, 1, 2]) + assert sim.dims == (2, 3, 2) + + def test_dimension_upgrade_takes_max(self): + """If a slot sees both dim=2 and dim=3 ops, dim=3 wins.""" + p = Program( + Gate("TX", [], [0]), # dim=3 + ) + sim = PureStateVectorSimulator(p, qubits=[0]) + assert sim.dims == (3,) + + def test_density_matrix_dimension_inference_consistency(self): + """State vector and density matrix simulators infer same dims.""" + p = Program( + X(0), + Gate("TH", [], [1]), + Gate("TX", [], [2]), + H(3), + ) + # Density matrix path + dm_sim = DensityMatrixSimulator(p, qubits=[0, 1, 2, 3]) + dm_dims = dm_sim.dims + + # State vector path + sv_sim = PureStateVectorSimulator(p, qubits=[0, 1, 2, 3]) + sv_dims = sv_sim.dims + + assert dm_dims == sv_dims == (2, 3, 3, 2) + + def test_two_qutrit_gate_infers_both_slots(self): + """A two-qutrit gate (TSWAP) upgrades both slots to dim=3.""" + p = Program(Gate("TSWAP", [], [0, 1])) + sim = PureStateVectorSimulator(p, qubits=[0, 1]) + assert sim.dims == (3, 3) + + def test_custom_defgate_qutrit_dimensions(self): + """DefGate accepts 3x3 unitary matrices for single-qutrit gates.""" + # 3x3 identity is a valid qutrit gate + mat = np.eye(3, dtype=complex) + dg = DefGate("MY_QUTRIT_GATE", mat) + assert dg.num_args() == 1 + + # Built-in qutrit gates also work and infer dim=3 + p = Program(Gate("TX", [], [0])) + psi = _sv(p, qubits=[0]) + assert psi.dims == (3,) + + def test_custom_defgate_two_qutrit(self): + """DefGate accepts 9x9 unitary matrices for two-qutrit gates.""" + mat = np.eye(9, dtype=complex) + dg = DefGate("TWO_QUTRIT_ID", mat) + assert dg.num_args() == 2 + + # Built-in TSWAP also works for two-qutrit systems + p = Program(Gate("TSWAP", [], [0, 1])) + psi = _sv(p, qubits=[0, 1]) + assert psi.matrix.shape[-1] == 9 + assert psi.dims == (3, 3) + + def test_custom_defgate_rejects_non_perfect_power(self): + """DefGate rejects matrices whose dimension is not a perfect power.""" + # 6 = 2*3 is not a perfect power of any integer + mat = np.eye(6, dtype=complex) + with pytest.raises(ValueError, match="perfect power"): + DefGate("BAD_GATE", mat) + + +# ══════════════════════════════════════════════════════════ +# Test: Qutrit noise channels +# ══════════════════════════════════════════════════════════ + + +class TestQutritNoiseChannels: + """Test noisy qutrit simulation via NoiseModel.""" + + def test_qutrit_depolarizing_channel(self): + """A depolarizing channel on a qutrit gate mixes the state.""" + inst = Gate("TX", [], [0]) + channel = Channel.from_gate_fidelity(inst=inst, fidelity=0.8) + noise_model = NoiseModel.from_channels([channel]) + + # Density matrix should show mixed state + p = Program(Gate("TX", [], [0])) + rho = _dm(p, noise_model=noise_model, qubits=[0]) + assert rho.dims == (3,) + # Purity < 1 indicates noise + purity = float(jnp.real(jnp.trace(rho.matrix @ rho.matrix))) + assert purity < 0.99 + + def test_qutrit_depolarizing_trajectory(self): + """Trajectory simulation with qutrit depolarizing noise.""" + inst = Gate("TX", [], [0]) + channel = Channel.from_gate_fidelity(inst=inst, fidelity=0.9) + noise_model = NoiseModel.from_channels([channel]) + + p = Program() + p += Gate("TX", [], [0]) + p += Measurement(qubit=Qubit(0), classical_reg=None) + + n_traj = 2000 + outcomes = _sample( + p, noise_model=noise_model, qubits=[0], + num_trajectories=n_traj, random_seed=7, + ) + counts = jnp.bincount(outcomes.flatten(), length=3) + # Most should be outcome 2 (ideal TX|0>=|2>), with some noise + assert counts[2] / n_traj > 0.7 + + def test_qutrit_reset_channel(self): + """Noisy qutrit reset via ResetChannel.""" + from pyquil.quilbase import ResetQubit + + reset_inst = ResetQubit(Qubit(0)) + reset_ch = ResetChannel.from_reset_fidelity(inst=reset_inst, fidelity=0.9, dim=3) + noise_model = NoiseModel.from_channels([reset_ch]) + + # Prepare |1> (TX^2|0>=|1>), then reset — should mostly go to |0> + p = Program() + p += Gate("TX", [], [0]) + p += Gate("TX", [], [0]) # |0> -> |1> + p += ResetQubit(Qubit(0)) + p += Measurement(qubit=Qubit(0), classical_reg=None) + + n_traj = 1000 + outcomes = _sample( + p, noise_model=noise_model, qubits=[0], + num_trajectories=n_traj, random_seed=55, + ) + counts = jnp.bincount(outcomes.flatten(), length=3) + # Majority should be reset to |0> + assert counts[0] / n_traj > 0.7 + + def test_mixed_noise_qubit_and_qutrit(self): + """Noise model with channels for both qubit and qutrit gates.""" + # Noisy qubit X gate + ch_qubit = Channel.from_gate_fidelity( + inst=Gate("X", [], [0]), fidelity=0.95 + ) + # Noisy qutrit TX gate + ch_qutrit = Channel.from_gate_fidelity( + inst=Gate("TX", [], [1]), fidelity=0.95 + ) + noise_model = NoiseModel.from_channels([ch_qubit, ch_qutrit]) + + p = Program() + p += X(0) + p += Gate("TX", [], [1]) + rho = _dm(p, noise_model=noise_model, qubits=[0, 1]) + assert rho.dims == (2, 3) + # Both registers should have purity < 1 due to noise + purity = float(jnp.real(jnp.trace(rho.matrix @ rho.matrix))) + assert purity < 0.99 + + +# ══════════════════════════════════════════════════════════ +# Test: Qutrit state vector trajectories (batched) +# ══════════════════════════════════════════════════════════ + + +class TestQutritTrajectoryBatching: + """Test that batched trajectory simulation works for qutrits.""" + + def test_noiseless_qutrit_batch(self): + """Batched noiseless qutrit simulation produces identical trajectories.""" + p = Program(Gate("TX", [], [0])) + sim = TrajectorySimulator(p, qubits=[0]) + keys = jax.random.split(jax.random.key(0), 10) + psi, _ = sim.compute(_EMPTY_PARAMS, keys) + # All 10 trajectories should be identical (noiseless) + assert psi.ensemble_size == (10,) + assert psi.dims == (3,) + expected = jnp.array([0, 0, 1], dtype=complex) # TX|0> = |2> + for i in range(10): + fid = float(jnp.abs(jnp.vdot(psi.matrix[i], expected)) ** 2) + assert fid > 0.9999 + + def test_qutrit_trajectory_outcomes_shape(self): + """Trajectory outcomes have correct shape for qutrit programs.""" + p = Program() + p += Gate("TH", [], [0]) + p += Measurement(qubit=Qubit(0), classical_reg=None) + + outcomes = _sample( + p, qubits=[0], num_trajectories=500, batch_size=100, random_seed=0 + ) + assert outcomes.shape == (500, 1) + # Outcomes should be in {0, 1, 2} + assert jnp.all(outcomes >= 0) + assert jnp.all(outcomes <= 2) diff --git a/test/unit/test_reference_density.py b/test/unit/test_reference_density.py index 265f3d335..a85ca36cf 100644 --- a/test/unit/test_reference_density.py +++ b/test/unit/test_reference_density.py @@ -1,3 +1,7 @@ +# TODO(pyquil-5.0): Remove this file. These tests exercise ReferenceDensitySimulator/PyQVM, +# which are superseded by DensityMatrixSimulator. Equivalent coverage lives in +# test_state_vector.py and test_noise_model.py. + import networkx as nx import numpy as np import pytest diff --git a/test/unit/test_reference_wavefunction.py b/test/unit/test_reference_wavefunction.py index 938d95bc1..a77e45e6d 100644 --- a/test/unit/test_reference_wavefunction.py +++ b/test/unit/test_reference_wavefunction.py @@ -1,3 +1,7 @@ +# TODO(pyquil-5.0): Remove this file. These tests exercise ReferenceWavefunctionSimulator/PyQVM, +# which are superseded by PureStateVectorSimulator and TrajectorySimulator. +# Equivalent coverage lives in test_state_vector.py. + import functools import inspect import random diff --git a/test/unit/test_resolver.py b/test/unit/test_resolver.py new file mode 100644 index 000000000..e6e8bfacc --- /dev/null +++ b/test/unit/test_resolver.py @@ -0,0 +1,209 @@ +"""Unit tests for the resolver pipeline.""" + +import jax.numpy as jnp +import numpy as np +import quax as qx + +from pyquil.gates import CNOT, MEASURE, RESET, RX, RZ, H, X +from pyquil.noise._channels import Channel, CycleChannel +from pyquil.noise._noise_model import NoiseModel +from pyquil.quil import Program +from pyquil.quilatom import FormalArgument, MemoryReference, Qubit +from pyquil.quilbase import ( + Declare, + DefCircuit, + Gate, + Measurement, +) +from pyquil.simulation._resolver import ( + build_dag, + expand_program, + remap_qubits, + resolve_program, +) + +_EMPTY_PARAMS = jnp.array([], dtype=float) + + +# ────────────────────────────────────────────────────────── +# expand_program +# ────────────────────────────────────────────────────────── + + +class TestExpandProgram: + def test_simple_gates(self): + p = Program(H(0), X(1), CNOT(0, 1)) + ops, qubit_tuples, _ = expand_program(p) + assert len(ops) == 3 + assert len(qubit_tuples) == 3 + # Physical qubit IDs + assert qubit_tuples[0] == (0,) + assert qubit_tuples[1] == (1,) + assert qubit_tuples[2] == (0, 1) + + def test_fixed_gates_are_concrete(self): + p = Program(H(0), X(1)) + ops, _, _ = expand_program(p) + for op in ops: + assert isinstance(op, qx.Unitary) + + def test_measurement_emitted(self): + p = Program(Declare("ro", "BIT", 1), H(0), MEASURE(0, MemoryReference("ro", 0))) + ops, qubit_tuples, _ = expand_program(p) + assert len(ops) == 2 + # Measurement should be a concrete QuantumInstrument + assert isinstance(ops[1], qx.QuantumInstrument) + + def test_reset_emitted(self): + p = Program(RESET(), H(0)) + ops, qubit_tuples, _ = expand_program(p) + # Bare RESET expands to one reset per qubit (only qubit 0 in this program) + assert len(ops) == 2 + assert isinstance(ops[0], qx.SuperOp) + + def test_noise_channel_resolved(self): + p = Program(X(0)) + ch = Channel.from_gate_fidelity(inst=X(0), fidelity=0.99) + nm = NoiseModel.from_channels([ch]) + ops, _, _ = expand_program(p, nm) + assert isinstance(ops[0], qx.SuperOp) + + def test_defcircuit_expansion_no_cycle_channel(self): + q0, q1 = FormalArgument("q0"), FormalArgument("q1") + dc = DefCircuit("MY_CYCLE", [], [q0, q1], [H(q0), CNOT(q0, q1)]) + p = Program(dc, Gate("MY_CYCLE", [], [Qubit(0), Qubit(1)])) + ops, qubit_tuples, _ = expand_program(p) + assert len(ops) == 2 + + def test_cycle_channel_expansion(self): + """CycleChannel constituents are emitted instead of DEFCIRCUIT body.""" + q0 = FormalArgument("q") + dc = DefCircuit("SQC", [], [q0], [RX(0.1, q0), RZ(0.2, q0)]) + cycle_inst = Gate("SQC", [], [Qubit(0)]) + channels = tuple( + Channel.from_depolarizing_constant(inst, depolarizing_constant=0.99) for inst in (RX(0.1, 0), RZ(0.2, 0)) + ) + nm = NoiseModel.from_channels([CycleChannel(inst=cycle_inst, defcircuit=dc, channels=channels)]) + p = Program(dc, cycle_inst) + ops, qubit_tuples, _ = expand_program(p, nm) + assert len(ops) == 2 + # All should be concrete noisy SuperOps + for op in ops: + assert isinstance(op, qx.SuperOp) + + def test_cycle_channel_applies_process_faithfully(self): + """A ``CycleChannel`` constituent is simulated as its ``process`` verbatim. + + ``Channel.process`` is the source of truth for a cycle constituent: it must + carry the gate (composed with any noise). This pins that contract — a + noiseless gate channel's ``process`` is emitted unchanged and equals the gate + superoperator — so a noise model that puts an identity in ``process`` for a + real gate (which silently drops it) is a *noise-model* error, caught at the + point of construction, not something the resolver second-guesses. + """ + q0 = FormalArgument("q") + dc = DefCircuit("VCYC", [], [q0], [X(q0)]) + cycle_inst = Gate("VCYC", [], [Qubit(0)]) + # Noiseless X channel: the gate lives in the process (target_unitary == process action). + gate_channel = Channel(X(0), qx.to_superop(qx.gates.X), target_unitary=qx.gates.X) + nm = NoiseModel.from_channels([CycleChannel(inst=cycle_inst, defcircuit=dc, channels=(gate_channel,))]) + p = Program(dc, cycle_inst) + ops, _, _ = expand_program(p, nm) + assert len(ops) == 1 + # Emitted verbatim: the process (an X superoperator) is what gets applied. + assert jnp.allclose(qx.to_superop(ops[0]).matrix, qx.to_superop(qx.gates.X).matrix, atol=1e-6) + + def test_parameterized_gate_produces_callable(self): + p = Program(Declare("theta", "REAL", 1), RZ(MemoryReference("theta", 0), 0)) + ops, _, _ = expand_program(p) + assert len(ops) == 1 + assert callable(ops[0]) + result = ops[0](jnp.array([1.23])) + assert isinstance(result, qx.Unitary) + + +# ────────────────────────────────────────────────────────── +# remap_qubits & build_dag +# ────────────────────────────────────────────────────────── + + +class TestRemapAndDag: + def test_remap_qubits(self): + qubit_tuples = [(3,), (5,), (3, 5)] + qubit_indices = {3: 0, 5: 1} + result = remap_qubits(qubit_tuples, qubit_indices) + assert result == [(0,), (1,), (0, 1)] + + def test_build_dag_single_qubit_chain(self): + qubit_tuples = [(0,), (0,), (0,)] + dag = build_dag(qubit_tuples) + assert dag.has_edge(0, 1) + assert dag.has_edge(1, 2) + assert not dag.has_edge(0, 2) + + def test_build_dag_independent_qubits(self): + qubit_tuples = [(0,), (1,)] + dag = build_dag(qubit_tuples) + assert dag.number_of_edges() == 0 + + def test_build_dag_multi_qubit(self): + qubit_tuples = [(0,), (1,), (0, 1)] + dag = build_dag(qubit_tuples) + assert dag.has_edge(0, 2) + assert dag.has_edge(1, 2) + assert not dag.has_edge(0, 1) + + +# ────────────────────────────────────────────────────────── +# resolve_program (integration) +# ────────────────────────────────────────────────────────── + + +class TestResolveProgram: + def test_basic_roundtrip(self): + p = Program(H(0), CNOT(0, 1), X(1)) + res = resolve_program(p) + ops = res.resolve(_EMPTY_PARAMS) + assert len(ops) == 3 + assert all(isinstance(op, qx.Unitary) for op, _ in ops) + assert res.dims == (2, 2) + + def test_with_noise(self): + p = Program(X(0), H(1)) + ch = Channel.from_gate_fidelity(inst=X(0), fidelity=0.99) + nm = NoiseModel.from_channels([ch]) + res = resolve_program(p, nm) + ops = res.resolve(_EMPTY_PARAMS) + assert len(ops) == 2 + assert isinstance(ops[0][0], qx.SuperOp) + assert isinstance(ops[1][0], qx.Unitary) + + def test_parameterized(self): + p = Program(Declare("theta", "REAL", 1), RZ(MemoryReference("theta", 0), 0)) + res = resolve_program(p) + params = jnp.array([np.pi / 4]) + ops = res.resolve(params) + assert len(ops) == 1 + assert isinstance(ops[0][0], qx.Unitary) + + def test_dag_structure(self): + p = Program(H(0), X(0), CNOT(0, 1)) + dag = build_dag(resolve_program(p).subsystems) + assert dag.has_edge(0, 1) + assert dag.has_edge(1, 2) + + def test_measurement_and_reset(self): + p = Program(Declare("ro", "BIT", 1), H(0), MEASURE(0, MemoryReference("ro", 0))) + res = resolve_program(p) + ops = res.resolve(_EMPTY_PARAMS) + assert len(ops) == 2 + assert isinstance(ops[0][0], qx.Unitary) + assert isinstance(ops[1][0], qx.QuantumInstrument) + + def test_qutrit_measurement_dimensions(self): + p = Program(Gate("TX", [], [0]), Measurement(Qubit(0), None)) + res = resolve_program(p) + ops = res.resolve(_EMPTY_PARAMS) + assert res.dims == (3,) + assert isinstance(ops[1][0], qx.QuantumInstrument) + assert ops[1][0].dims == ((3,), (3,)) diff --git a/test/unit/test_state_vector.py b/test/unit/test_state_vector.py new file mode 100644 index 000000000..ace424401 --- /dev/null +++ b/test/unit/test_state_vector.py @@ -0,0 +1,1170 @@ +"""Unit tests for the quax-based state vector simulator.""" + +import jax +import jax.numpy as jnp +import numpy as np +import pytest +import quax as qx + +from pyquil.gates import CNOT, CPHASE, CZ, MEASURE, RESET, RX, RY, RZ, H, X +from pyquil.noise._channels import Channel, CycleChannel, MeasurementChannel, ResetChannel +from pyquil.noise._noise_model import NoiseModel +from pyquil.quil import Program +from pyquil.quilatom import FormalArgument, MemoryReference, Qubit +from pyquil.quilbase import ( + Declare, + DefCircuit, + DefGate, + ResetQubit, +) +from pyquil.quilbase import ( + Gate as QuilGate, +) +from pyquil.quilbase import ( + Measurement as QuilMeasurement, +) +from pyquil.simulation._simulator import ( + PureStateVectorSimulator, + TrajectorySimulator, + _run_batched_trajectories, +) +from pyquil.simulation._simulator import ( + _apply_trajectory_operations as apply_trajectory_operations, +) + +_EMPTY_PARAMS = jnp.array([], dtype=float) + + +def _sv(program, qubits=None, memory_map=None): + """Compute pure state vector for a gate-only program.""" + sim = PureStateVectorSimulator(program, qubits=qubits) + if memory_map: + params = sim.linearize(memory_map) + else: + params = _EMPTY_PARAMS + return sim.compute(params) + + +def _simulate_trajectories(program, noise_model=None, qubits=None, num_trajectories=1, batch_size=256, random_seed=0): + """Helper: build + compress + run trajectories, returning (psi, outcomes).""" + sim = TrajectorySimulator(program, noise_model=noise_model, qubits=qubits) + resolved = sim.resolve(_EMPTY_PARAMS) + compressed = sim.compress(resolved) + operations = sim.adapt(compressed) + all_psis, all_outcomes = _run_batched_trajectories( + operations, + num_trajectories, + batch_size, + random_seed, + keep_states=True, + dims=sim.dims, + ) + assert all_psis is not None + if len(all_psis) == 1: + return all_psis[0], all_outcomes[0] + combined_data = jnp.concatenate([p.matrix for p in all_psis], axis=0) + combined_psi = qx.StateVector.from_matrix(combined_data, all_psis[0].dims) + combined_outcomes = jnp.concatenate(all_outcomes, axis=0) + return combined_psi, combined_outcomes + + +class TestSingleQubitGates: + def test_identity(self): + p = Program() + psi = _sv(p, qubits=[0]) + target = qx.StateVector.from_matrix(jnp.array([1.0, 0.0], dtype=complex), dims=(2,)) + assert qx.fidelity(psi, target) > 0.9999 + + def test_x_gate(self): + p = Program(X(0)) + psi = _sv(p, qubits=[0]) + target = qx.StateVector.from_matrix(jnp.array([0.0, 1.0], dtype=complex), dims=(2,)) + assert qx.fidelity(psi, target) > 0.9999 + + def test_hadamard(self): + p = Program(H(0)) + psi = _sv(p, qubits=[0]) + target = qx.StateVector.from_matrix(jnp.array([1.0, 1.0], dtype=complex) / jnp.sqrt(2), dims=(2,)) + assert qx.fidelity(psi, target) > 0.9999 + + @pytest.mark.parametrize("angle", [0.0, np.pi / 4, np.pi / 2, np.pi, 3 * np.pi / 2]) + def test_rx_gate(self, angle): + p = Program(RX(angle, 0)) + psi = _sv(p, qubits=[0]) + expected = jnp.asarray(qx.gates.RX(angle).matrix) @ jnp.array([1.0, 0.0], dtype=complex) + target = qx.StateVector.from_matrix(expected, dims=(2,)) + assert qx.fidelity(psi, target) > 0.9999 + + @pytest.mark.parametrize("angle", [0.0, np.pi / 4, np.pi / 2, np.pi]) + def test_ry_gate(self, angle): + p = Program(RY(angle, 0)) + psi = _sv(p, qubits=[0]) + expected = jnp.asarray(qx.gates.RY(angle).matrix) @ jnp.array([1.0, 0.0], dtype=complex) + target = qx.StateVector.from_matrix(expected, dims=(2,)) + assert qx.fidelity(psi, target) > 0.9999 + + @pytest.mark.parametrize("angle", [0.0, np.pi / 4, np.pi / 2, np.pi]) + def test_rz_gate(self, angle): + p = Program(RZ(angle, 0)) + psi = _sv(p, qubits=[0]) + expected = jnp.asarray(qx.gates.RZ(angle).matrix) @ jnp.array([1.0, 0.0], dtype=complex) + target = qx.StateVector.from_matrix(expected, dims=(2,)) + assert qx.fidelity(psi, target) > 0.9999 + + +class TestMultiQubitGates: + def test_bell_state(self): + p = Program(H(0), CNOT(0, 1)) + psi = _sv(p, qubits=[0, 1]) + target = qx.StateVector.from_matrix(jnp.array([1.0, 0.0, 0.0, 1.0], dtype=complex) / jnp.sqrt(2), dims=(2, 2)) + assert qx.fidelity(psi, target) > 0.9999 + + def test_ghz_state_3q(self): + p = Program(H(0), CNOT(0, 1), CNOT(1, 2)) + psi = _sv(p, qubits=[0, 1, 2]) + target = qx.StateVector.from_matrix( + jnp.array([1.0, 0, 0, 0, 0, 0, 0, 1.0], dtype=complex) / jnp.sqrt(2), dims=(2, 2, 2) + ) + assert qx.fidelity(psi, target) > 0.9999 + + def test_qubit_ordering(self): + """State vector should respect the provided qubit ordering.""" + p = Program(X(5)) + psi = _sv(p, qubits=[5, 6]) + # qubit 5 is index 0, qubit 6 is index 1 + # X on qubit 5 → |10> → state [0, 0, 1, 0] + target = qx.StateVector.from_matrix(jnp.array([0.0, 0.0, 1.0, 0.0], dtype=complex), dims=(2, 2)) + assert qx.fidelity(psi, target) > 0.9999 + + +class TestParameterizedPrograms: + def test_parameterized_rx(self): + p = Program( + Declare("theta", "REAL"), + RX(MemoryReference("theta"), 0), + ) + angle = np.pi / 3 + psi = _sv(p, qubits=[0], memory_map={"theta": [angle]}) + expected = jnp.asarray(qx.gates.RX(angle).matrix) @ jnp.array([1.0, 0.0], dtype=complex) + target = qx.StateVector.from_matrix(expected, dims=(2,)) + assert qx.fidelity(psi, target) > 0.9999 + + +class TestCustomGates: + def test_defgate(self): + """Test that DefGate-defined gates work correctly.""" + cnot_matrix = np.asarray(qx.gates.CNOT.matrix) + p = Program() + p += DefGate("MY_CNOT", cnot_matrix) + p += QuilGate("MY_CNOT", [], [Qubit(0), Qubit(1)]) + # Prepare |1,0> first + p2 = Program(X(0)) + p + psi = _sv(p2, qubits=[0, 1]) + # X(0) gives |10>, then CNOT gives |11> + target = qx.StateVector.from_matrix(jnp.array([0.0, 0.0, 0.0, 1.0], dtype=complex), dims=(2, 2)) + assert qx.fidelity(psi, target) > 0.9999 + + +class TestAutoQubitDetection: + def test_auto_qubits(self): + """When qubits=None, should auto-detect from program.""" + p = Program(H(2), CNOT(2, 5)) + psi = _sv(p) + # Should use qubits [2, 5] in sorted order + target = qx.StateVector.from_matrix(jnp.array([1.0, 0.0, 0.0, 1.0], dtype=complex) / jnp.sqrt(2), dims=(2, 2)) + assert qx.fidelity(psi, target) > 0.9999 + + +# ────────────────────────────────────────────────────────────────────────────── +# Trajectory simulator tests +# ────────────────────────────────────────────────────────────────────────────── + + +class TestTrajectoryNoiseless: + """Test that the trajectory simulator preserves noiseless behavior.""" + + def test_single_gate_noiseless(self): + """Without noise, trajectory simulation matches unitary simulation.""" + p = Program(H(0)) + psi_noiseless = _sv(p, qubits=[0]) + psi_traj, outcomes = _simulate_trajectories(p, noise_model=None, qubits=[0], num_trajectories=1) + assert qx.fidelity(psi_noiseless, psi_traj) > 0.9999 + + def test_bell_state_noiseless(self): + """Multi-qubit noiseless trajectory.""" + p = Program(H(0), CNOT(0, 1)) + psi_noiseless = _sv(p, qubits=[0, 1]) + psi_traj, outcomes = _simulate_trajectories(p, noise_model=None, qubits=[0, 1], num_trajectories=1) + assert qx.fidelity(psi_noiseless, psi_traj) > 0.9999 + + def test_multiple_trajectories_noiseless_deterministic(self): + """Multiple noiseless trajectories should all give same result.""" + p = Program(X(0)) + psi_batch, outcomes = _simulate_trajectories(p, noise_model=None, qubits=[0], num_trajectories=8) + # Each trajectory should be |1⟩ + target = qx.StateVector.from_matrix(jnp.array([0.0, 1.0], dtype=complex), dims=(2,)) + probs = qx.probabilities(psi_batch) + # All trajectories: prob of |1⟩ = 1 + assert jnp.allclose(probs[:, 1], 1.0, atol=1e-6) + + +class TestTrajectoryNoisy: + """Test noisy trajectory simulation with known analytical results.""" + + def _make_bitflip_noise_model(self, p_error: float, qubit: int = 0) -> NoiseModel: + """Create a noise model with a bit-flip channel on X gate.""" + # Bit-flip channel applied AFTER the gate: E(rho) = (1-p) U rho U† + p X U rho U† X + # As a combined superop that includes the gate: + inst = X(qubit) + unitary = qx.gates.X + # Build noisy superop: (1-p)|U> NoiseModel: + """Create a noise model with depolarizing noise on X gate.""" + inst = X(qubit) + channel = Channel.from_gate_fidelity(inst=inst, fidelity=fidelity) + return NoiseModel.from_channels([channel]) + + def test_noiseless_gate_with_noise_model(self): + """A noise model that doesn't cover the applied gate should leave it noiseless.""" + # Noise model only covers X gate, but we apply H + noise_model = self._make_bitflip_noise_model(0.1, qubit=0) + p = Program(H(0)) + psi, outcomes = _simulate_trajectories(p, noise_model=noise_model, qubits=[0], num_trajectories=1) + target = qx.StateVector.from_matrix(jnp.array([1.0, 1.0], dtype=complex) / jnp.sqrt(2), dims=(2,)) + assert qx.fidelity(psi, target) > 0.9999 + + def test_bitflip_statistics(self): + """Bit-flip noise should produce correct outcome statistics.""" + p_error = 0.3 + noise_model = self._make_bitflip_noise_model(p_error, qubit=0) + # X gate with bit-flip noise: X|0⟩=|1⟩, then bit-flip with p=0.3 + # So final state: (1-p)|1⟩ + p|0⟩ in trajectory picture + p = Program(X(0)) + num_traj = 2048 + psi_batch, outcomes = _simulate_trajectories( + p, + noise_model=noise_model, + qubits=[0], + num_trajectories=num_traj, + batch_size=256, + random_seed=42, + ) + # Get probabilities for each trajectory + probs = qx.probabilities(psi_batch) # shape (num_traj, 2) + # Each trajectory should collapse to either |0⟩ or |1⟩ + # Count how many ended in |0⟩ (bit-flipped from |1⟩) + in_zero = jnp.sum(probs[:, 0] > 0.5) + observed_flip_rate = float(in_zero) / num_traj + # Expected: p_error fraction should flip to |0⟩ + assert abs(observed_flip_rate - p_error) < 0.05, f"Expected flip rate ~{p_error}, got {observed_flip_rate}" + + def test_depolarizing_statistics(self): + """Depolarizing noise on identity-like circuit should produce mixed results.""" + fidelity_val = 0.9 + noise_model = self._make_depolarizing_noise_model(fidelity_val, qubit=0) + p = Program(X(0)) + num_traj = 2048 + psi_batch, outcomes = _simulate_trajectories( + p, + noise_model=noise_model, + qubits=[0], + num_trajectories=num_traj, + batch_size=256, + random_seed=123, + ) + probs = qx.probabilities(psi_batch) + # Average probability of |1⟩ across trajectories should be close to + # the expected value from depolarizing channel on |1⟩: + # p(|1⟩) = F + (1-F)/d where d=2 for single qubit depol + # Actually for depol with constant p: output prob depends on p + avg_prob_1 = float(jnp.mean(probs[:, 1])) + # For depolarizing channel with fidelity F on a single qubit: + # After X|0⟩=|1⟩, depol: prob(|1⟩) = (2F-1) * 1/2 + 1/2 = F + # (since F_avg = (d*p + 1)/(d+1) and rho_out = p*rho + (1-p)*I/d) + assert abs(avg_prob_1 - fidelity_val) < 0.05 + + def test_two_qubit_noise(self): + """Test that noise applies independently to separate qubits.""" + p_error = 0.2 + inst_q0 = X(0) + inst_q1 = X(1) + ch0 = Channel.from_pauli_noise(inst=inst_q0, pauli_noise={"X": p_error}) + ch1 = Channel.from_pauli_noise(inst=inst_q1, pauli_noise={"X": p_error}) + noise_model = NoiseModel.from_channels([ch0, ch1]) + + prog = Program(X(0), X(1)) + num_traj = 2048 + psi_batch, _ = _simulate_trajectories( + prog, + noise_model=noise_model, + qubits=[0, 1], + num_trajectories=num_traj, + batch_size=256, + random_seed=7, + ) + probs = qx.probabilities(psi_batch) # shape (num_traj, 4) + # State |11⟩ = index 3. Both flipped: p_error^2 gives |00⟩ + # Expected: P(|11⟩) ≈ (1-p)^2, P(|00⟩) ≈ p^2 + avg_prob_11 = float(jnp.mean(probs[:, 3])) + expected_prob_11 = (1 - p_error) ** 2 + assert abs(avg_prob_11 - expected_prob_11) < 0.05 + + +class TestCycleChannelGateApplication: + """A ``CycleChannel`` gate constituent is applied as its ``process``. + + ``Channel.process`` carries the gate (composed with any noise); the resolver + expands a cycle by applying each constituent's ``process`` verbatim. This + guards, end to end, that a cycle gate whose unitary lives in its ``process`` + actually acts on the state — the surface-code kraus/stim mismatch was caused by + a noise model that put an identity in ``process`` for a real gate, dropping it, + which is a noise-model error rather than a simulator one. + """ + + def test_trajectory_applies_cycle_gate(self): + """The cycle's ``X`` (carried in the channel's process) flips the qubit.""" + q = FormalArgument("q") + dc = DefCircuit("CX", [], [q], [X(q)]) + cyc = QuilGate("CX", [], [Qubit(0)]) + program = Program(dc, cyc, Declare("ro", "BIT", 1), QuilMeasurement(Qubit(0), MemoryReference("ro", 0))) + gate_channel = Channel(X(0), qx.to_superop(qx.gates.X), target_unitary=qx.gates.X) + nm = NoiseModel.from_channels([CycleChannel(inst=cyc, defcircuit=dc, channels=(gate_channel,))]) + sim = TrajectorySimulator(program, noise_model=nm, qubits=[0]) + outcomes = np.asarray(sim.sample(sim.linearize({}), num_trajectories=16, batch_size=16, random_seed=0)) + assert outcomes.shape == (16, 1) + assert np.all(outcomes == 1) + + +class TestTrajectoryMeasurement: + """Test mid-circuit measurement in trajectory simulation.""" + + def test_measurement_records_outcome(self): + """Measurement should record classical outcome.""" + p = Program(H(0), MEASURE(0, None)) + psi, outcomes = _simulate_trajectories( + p, + noise_model=None, + qubits=[0], + num_trajectories=100, + batch_size=100, + random_seed=42, + ) + # outcomes shape should be (100, 1) — one measurement + assert outcomes.shape == (100, 1) + # Outcomes should be 0 or 1 + assert jnp.all((outcomes == 0) | (outcomes == 1)) + # Roughly 50/50 from H|0⟩ + frac_0 = float(jnp.mean(outcomes == 0)) + assert 0.3 < frac_0 < 0.7 + + def test_measurement_collapses_state(self): + """After measurement, state should be consistent with outcome.""" + p = Program(H(0), MEASURE(0, None)) + psi, outcomes = _simulate_trajectories( + p, + noise_model=None, + qubits=[0], + num_trajectories=64, + batch_size=64, + random_seed=99, + ) + probs = qx.probabilities(psi) # (64, 2) + # For each trajectory, the state should be collapsed + for i in range(64): + outcome = int(outcomes[i, 0]) + assert probs[i, outcome] > 0.999 + + def test_noisy_measurement(self): + """Noisy measurement with confusion should produce errors.""" + # Prepare |0⟩, measure with 80% fidelity + qubit = Qubit(0) + m_inst = QuilMeasurement(qubit=qubit, classical_reg=None) + meas_ch = MeasurementChannel.from_readout_fidelity(inst=m_inst, fidelity=0.8) + noise_model = NoiseModel.from_channels([meas_ch]) + + p = Program(MEASURE(0, None)) + psi, outcomes = _simulate_trajectories( + p, + noise_model=noise_model, + qubits=[0], + num_trajectories=1024, + batch_size=256, + random_seed=55, + ) + # Prepared in |0⟩, ideal measurement gives 0, but with 20% error → ~20% ones + frac_1 = float(jnp.mean(outcomes == 1)) + assert 0.1 < frac_1 < 0.3 + + +class TestTrajectoryReset: + """Test reset operations in trajectory simulation.""" + + def test_reset_to_ground(self): + """Reset should bring qubit to |0⟩.""" + p = Program(X(0), ResetQubit(Qubit(0))) + psi, _ = _simulate_trajectories( + p, + noise_model=None, + qubits=[0], + num_trajectories=1, + ) + target = qx.StateVector.from_matrix(jnp.array([1.0, 0.0], dtype=complex), dims=(2,)) + assert qx.fidelity(psi, target) > 0.9999 + + def test_global_reset(self): + """Global RESET should reset all qubits.""" + p = Program(X(0), X(1), RESET()) + psi, _ = _simulate_trajectories( + p, + noise_model=None, + qubits=[0, 1], + num_trajectories=1, + ) + target = qx.StateVector.from_matrix(jnp.array([1.0, 0.0, 0.0, 0.0], dtype=complex), dims=(2, 2)) + assert qx.fidelity(psi, target) > 0.9999 + + def test_noisy_reset(self): + """Noisy reset should have imperfect fidelity.""" + qubit = Qubit(0) + reset_inst = ResetQubit(qubit) + reset_ch = ResetChannel.from_reset_fidelity(inst=reset_inst, fidelity=0.9) + noise_model = NoiseModel.from_channels([reset_ch]) + + # Start in |1⟩, apply noisy reset + p = Program(X(0), ResetQubit(Qubit(0))) + num_traj = 2048 + psi, _ = _simulate_trajectories( + p, + noise_model=noise_model, + qubits=[0], + num_trajectories=num_traj, + batch_size=256, + random_seed=13, + ) + probs = qx.probabilities(psi) # (num_traj, 2) + # With 90% reset fidelity, ~90% should end in |0⟩ + avg_prob_0 = float(jnp.mean(probs[:, 0])) + assert avg_prob_0 > 0.85 + + +class TestTrajectoryBatching: + """Test that batch processing works correctly.""" + + def test_batch_size_smaller_than_trajectories(self): + """Multiple batches should produce same statistics as single batch.""" + p = Program(H(0)) + noise_model = None + + # Single batch + psi_1, outcomes_1 = _simulate_trajectories( + p, + noise_model=noise_model, + qubits=[0], + num_trajectories=64, + batch_size=64, + random_seed=42, + ) + # Multiple batches (same seed) + psi_2, outcomes_2 = _simulate_trajectories( + p, + noise_model=noise_model, + qubits=[0], + num_trajectories=64, + batch_size=16, + random_seed=42, + ) + # Note: different batching may produce different results due to key splitting, + # but shapes should match + assert psi_1.matrix.shape == psi_2.matrix.shape + assert outcomes_1.shape == outcomes_2.shape + + +class TestComputeProgramStateVectorWithNoise: + """Test the TrajectorySimulator with noise_model parameter.""" + + def test_noise_model_none_unchanged(self): + """With noise_model=None, behavior is identical to original.""" + p = Program(H(0), CNOT(0, 1)) + psi = _sv(p, qubits=[0, 1]) + target = qx.StateVector.from_matrix(jnp.array([1.0, 0.0, 0.0, 1.0], dtype=complex) / jnp.sqrt(2), dims=(2, 2)) + assert qx.fidelity(psi, target) > 0.9999 + + def test_noise_model_single_trajectory(self): + """With noise_model provided, runs a single trajectory.""" + inst = X(0) + channel = Channel.from_gate_fidelity(inst=inst, fidelity=1.0) + noise_model = NoiseModel.from_channels([channel]) + p = Program(X(0)) + sim = TrajectorySimulator(p, noise_model=noise_model, qubits=[0]) + psi, _ = sim.compute(_EMPTY_PARAMS, jax.random.key(0)) + # Perfect fidelity channel → same as noiseless + target = qx.StateVector.from_matrix(jnp.array([0.0, 1.0], dtype=complex), dims=(2,)) + assert qx.fidelity(psi, target) > 0.999 + + +class TestSampleProgramTrajectories: + """Test the scalable TrajectorySimulator.sample function.""" + + def test_returns_outcomes_only(self): + """Should return measurement outcomes without state vectors.""" + p = Program(H(0), MEASURE(0, None)) + sim = TrajectorySimulator(p, noise_model=None, qubits=[0]) + outcomes = sim.sample( + _EMPTY_PARAMS, + num_trajectories=100, + batch_size=32, + random_seed=42, + ) + assert outcomes.shape == (100, 1) + assert jnp.all((outcomes == 0) | (outcomes == 1)) + + def test_no_measurements_empty_outcomes(self): + """Without measurements, outcomes array should have zero columns.""" + p = Program(H(0)) + sim = TrajectorySimulator(p, noise_model=None, qubits=[0]) + outcomes = sim.sample( + _EMPTY_PARAMS, + num_trajectories=10, + ) + assert outcomes.shape == (10, 0) + + def test_bitflip_statistics(self): + """Outcome statistics should match noise model.""" + p_error = 0.3 + inst = X(0) + ch = Channel.from_pauli_noise(inst=inst, pauli_noise={"X": p_error}) + noise_model = NoiseModel.from_channels([ch]) + + p = Program(X(0), MEASURE(0, None)) + sim = TrajectorySimulator(p, noise_model=noise_model, qubits=[0]) + outcomes = sim.sample( + _EMPTY_PARAMS, + num_trajectories=2048, + batch_size=256, + random_seed=42, + ) + # X|0⟩ = |1⟩, then bit-flip with p=0.3 → ~30% get |0⟩ + # Measurement outcome reflects the final state + frac_0 = float(jnp.mean(outcomes == 0)) + assert abs(frac_0 - p_error) < 0.05 + + def test_batch_size_does_not_affect_shape(self): + """Different batch sizes should produce same output shape.""" + p = Program(H(0), MEASURE(0, None)) + sim = TrajectorySimulator(p, qubits=[0]) + outcomes_small = sim.sample( + _EMPTY_PARAMS, + num_trajectories=100, + batch_size=10, + ) + outcomes_large = sim.sample( + _EMPTY_PARAMS, + num_trajectories=100, + batch_size=100, + ) + assert outcomes_small.shape == outcomes_large.shape == (100, 1) + + +# ────────────────────────────────────────────────────────────────────────────── +# Linearizer / Compressor architecture tests +# ────────────────────────────────────────────────────────────────────────────── + + +class TestBuildSimulationLinearizer: + """Tests for the simulator linearizer closure.""" + + def test_no_params_returns_empty(self): + p = Program(H(0), CNOT(0, 1), MEASURE(0, MemoryReference("ro", 0))) + p += Declare("ro", "BIT", 1) + sim = TrajectorySimulator(p) + params = sim.linearize({}) + assert params.shape == (0,) + assert sim.n_qubits == 2 + + def test_single_param(self): + p = Program() + p += Declare("theta", "REAL", 1) + p += Declare("ro", "BIT", 1) + p += RZ(MemoryReference("theta", 0), 0) + p += MEASURE(0, MemoryReference("ro", 0)) + sim = TrajectorySimulator(p) + params = sim.linearize({"theta": [1.23]}) + assert params.shape == (1,) + np.testing.assert_allclose(float(params[0]), 1.23) + + def test_multiple_params_ordering(self): + p = Program() + p += Declare("alpha", "REAL", 1) + p += Declare("beta", "REAL", 2) + p += Declare("ro", "BIT", 2) + p += RZ(MemoryReference("alpha", 0), 0) + p += RX(MemoryReference("beta", 0), 0) + p += RY(MemoryReference("beta", 1), 1) + p += MEASURE(0, MemoryReference("ro", 0)) + p += MEASURE(1, MemoryReference("ro", 1)) + sim = TrajectorySimulator(p) + params = sim.linearize({"alpha": [0.1], "beta": [0.2, 0.3]}) + assert params.shape == (3,) + np.testing.assert_allclose(float(params[0]), 0.1) + np.testing.assert_allclose(float(params[1]), 0.2) + np.testing.assert_allclose(float(params[2]), 0.3) + + def test_ro_register_excluded(self): + """Ensure 'ro' register is not treated as a parameter register.""" + p = Program() + p += Declare("theta", "REAL", 1) + p += Declare("ro", "BIT", 1) + p += RZ(MemoryReference("theta", 0), 0) + p += MEASURE(0, MemoryReference("ro", 0)) + sim = TrajectorySimulator(p) + params = sim.linearize({"theta": [np.pi]}) + assert params.shape == (1,) + + +class TestCompressor: + """Tests for the compressor at various max_subsystem_size settings.""" + + # ── max_subsystem_size=0 (no merging) ── + + def test_no_merge_pure_state_compute_matches_compressed(self): + """PureStateVectorSimulator should support disabling compression.""" + p = Program(H(0), CNOT(0, 1), RZ(0.5, 0)) + compressed = PureStateVectorSimulator(p) + uncompressed = PureStateVectorSimulator(p, max_subsystem_size=0) + + psi_compressed = compressed.compute(_EMPTY_PARAMS) + psi_uncompressed = uncompressed.compute(_EMPTY_PARAMS) + + assert qx.fidelity(psi_uncompressed, psi_compressed) > 0.9999 + + def test_parametric_non_contiguous_gate_embeds_in_larger_group(self): + """Parametric multi-qubit gates should embed correctly into larger groups.""" + p = Program() + p += Declare("theta", "REAL", 1) + p += H(0) + p += H(1) + p += H(2) + p += CPHASE(MemoryReference("theta", 0), 0, 2) + p += CPHASE(0.37, 0, 1) + + compressed = PureStateVectorSimulator(p, qubits=[0, 1, 2], max_subsystem_size=3) + uncompressed = PureStateVectorSimulator(p, qubits=[0, 1, 2], max_subsystem_size=0) + params = compressed.linearize({"theta": [0.91]}) + + assert compressed.bases == [(0, 1, 2)] + assert qx.fidelity(compressed.compute(params), uncompressed.compute(params)) > 0.9999 + + def test_no_merge_noiseless_matches_direct(self): + """max_subsystem_size=0 compressor output should match direct computation.""" + p = Program(H(0), CNOT(0, 1), RZ(0.5, 0)) + psi_direct = _sv(p) + + sim = TrajectorySimulator(p, max_subsystem_size=0) + ops = sim.adapt(sim.compress(sim.resolve(_EMPTY_PARAMS))) + + psi = qx.zero_state_vector(sim.n_qubits) + for op, subsystem in ops: + if isinstance(op, qx.Unitary): + psi = qx.targeted_apply_unitary(op, psi, subsystem) + + assert qx.fidelity(psi, psi_direct) > 0.9999 + + def test_no_merge_parameterized_gate(self): + """max_subsystem_size=0 should handle parameterized gates via the param vector.""" + p = Program() + p += Declare("theta", "REAL", 1) + p += Declare("ro", "BIT", 1) + p += RZ(MemoryReference("theta", 0), 0) + p += MEASURE(0, MemoryReference("ro", 0)) + + sim = TrajectorySimulator(p, max_subsystem_size=0) + params = sim.linearize({"theta": [np.pi]}) + ops = sim.adapt(sim.compress(sim.resolve(params))) + + psi = qx.zero_state_vector(sim.n_qubits) + for op, subsystem in ops: + if isinstance(op, qx.Unitary): + psi = qx.targeted_apply_unitary(op, psi, subsystem) + elif isinstance(op, qx.QuantumInstrument): + key = jax.random.key(0) + psi, _ = qx.targeted_apply_instrument_to_state_vector(op, psi, key, subsystem) + + psi_direct = _sv(Program(RZ(np.pi, 0))) + assert qx.fidelity(psi, psi_direct) > 0.9999 + + def test_no_merge_noisy_ops_count(self): + """max_subsystem_size=0 noisy: should have exactly one op per instruction.""" + p = Program(RX(np.pi / 2, 0), CNOT(0, 1), MEASURE(0, MemoryReference("ro", 0))) + p += Declare("ro", "BIT", 1) + + channels = [ + Channel.from_coherence_times(RX(np.pi / 2, 0), gate_duration=0.04, t1s=[30.0], t2s=[20.0]), + ] + noise_model = NoiseModel.from_channels(channels) + + sim = TrajectorySimulator(p, noise_model=noise_model, max_subsystem_size=0) + ops = sim.adapt(sim.compress(sim.resolve(_EMPTY_PARAMS))) + + # RX(noisy) + CNOT(noiseless) + MEASURE = 3 ops + assert len(ops) == 3 + + # ── max_subsystem_size=1 (1Q gate merging) ── + + def test_merges_consecutive_1q_gates(self): + """Three consecutive 1Q gates on qubit 0 should merge into one op.""" + p = Program(RZ(0.1, 0), RX(0.2, 0), RZ(0.3, 0)) + + sim = TrajectorySimulator(p, max_subsystem_size=1) + ops = sim.adapt(sim.compress(sim.resolve(_EMPTY_PARAMS))) + + assert len(ops) == 1 + + psi_direct = _sv(p) + psi = qx.zero_state_vector(sim.n_qubits) + for op, subsystem in ops: + if isinstance(op, qx.Unitary): + psi = qx.targeted_apply_unitary(op, psi, subsystem) + assert qx.fidelity(psi, psi_direct) > 0.9999 + + def test_2q_gate_breaks_run(self): + """A 2Q gate should break the 1Q run.""" + p = Program(RZ(0.1, 0), RX(0.2, 0), CNOT(0, 1), RZ(0.3, 0)) + + sim = TrajectorySimulator(p, max_subsystem_size=1) + ops = sim.adapt(sim.compress(sim.resolve(_EMPTY_PARAMS))) + + assert len(ops) == 3 + + psi_direct = _sv(p) + psi = qx.zero_state_vector(sim.n_qubits) + for op, subsystem in ops: + if isinstance(op, qx.Unitary): + psi = qx.targeted_apply_unitary(op, psi, subsystem) + assert qx.fidelity(psi, psi_direct) > 0.9999 + + def test_independent_qubit_runs(self): + """1Q gates on different qubits should form separate runs.""" + p = Program( + RZ(0.1, 0), + RX(0.2, 0), + RZ(0.3, 1), + RX(0.4, 1), + ) + + sim = TrajectorySimulator(p, max_subsystem_size=1) + ops = sim.adapt(sim.compress(sim.resolve(_EMPTY_PARAMS))) + + assert len(ops) == 2 + + psi_direct = _sv(p) + psi = qx.zero_state_vector(sim.n_qubits) + for op, subsystem in ops: + if isinstance(op, qx.Unitary): + psi = qx.targeted_apply_unitary(op, psi, subsystem) + assert qx.fidelity(psi, psi_direct) > 0.9999 + + def test_parameterized_merge(self): + """Parameterized gates in a 1Q run should merge correctly.""" + p = Program() + p += Declare("theta", "REAL", 2) + p += Declare("ro", "BIT", 1) + p += RZ(MemoryReference("theta", 0), 0) + p += RX(MemoryReference("theta", 1), 0) + p += MEASURE(0, MemoryReference("ro", 0)) + + sim = TrajectorySimulator(p, max_subsystem_size=1) + + theta_vals = [np.pi / 4, np.pi / 2] + params = sim.linearize({"theta": theta_vals}) + ops = sim.adapt(sim.compress(sim.resolve(params))) + + assert len(ops) == 2 + + psi_direct = _sv(Program(RZ(theta_vals[0], 0), RX(theta_vals[1], 0))) + psi = qx.zero_state_vector(sim.n_qubits) + for op, subsystem in ops: + if isinstance(op, qx.Unitary): + psi = qx.targeted_apply_unitary(op, psi, subsystem) + assert qx.fidelity(psi, psi_direct) > 0.9999 + + def test_noisy_1q_merge(self): + """Noisy 1Q gates should merge via SuperOp composition.""" + p = Program(RX(np.pi / 2, 0), RZ(0.5, 0)) + channels = [ + Channel.from_coherence_times(RX(np.pi / 2, 0), gate_duration=0.04, t1s=[30.0], t2s=[20.0]), + ] + noise_model = NoiseModel.from_channels(channels) + + sim0 = TrajectorySimulator(p, noise_model=noise_model, max_subsystem_size=0) + sim1 = TrajectorySimulator(p, noise_model=noise_model, max_subsystem_size=1) + + ops0 = sim0.adapt(sim0.compress(sim0.resolve(_EMPTY_PARAMS))) + ops1 = sim1.adapt(sim1.compress(sim1.resolve(_EMPTY_PARAMS))) + + assert len(ops0) == 2 + assert len(ops1) == 1 + assert isinstance(ops1[0][0], qx.KrausMap) + + def test_measurement_breaks_run(self): + """A MEASURE should break 1Q runs.""" + p = Program() + p += Declare("ro", "BIT", 1) + p += RZ(0.1, 0) + p += MEASURE(0, MemoryReference("ro", 0)) + p += RZ(0.2, 0) + + sim = TrajectorySimulator(p, max_subsystem_size=1) + ops = sim.adapt(sim.compress(sim.resolve(_EMPTY_PARAMS))) + + assert len(ops) == 3 + + def test_typical_circuit_compression_ratio(self): + """A typical layered circuit should have < 1.0 compression ratio.""" + n_q = 4 + p = Program() + for _ in range(3): + for q in range(n_q): + p += RZ(np.random.uniform(-np.pi, np.pi), q) + p += RX(np.pi / 2, q) + p += RZ(np.random.uniform(-np.pi, np.pi), q) + for i in range(0, n_q - 1, 2): + p += CNOT(i, i + 1) + + sim0 = TrajectorySimulator(p, max_subsystem_size=0) + sim1 = TrajectorySimulator(p, max_subsystem_size=1) + n0 = len(sim0.adapt(sim0.compress(sim0.resolve(_EMPTY_PARAMS)))) + n1 = len(sim1.adapt(sim1.compress(sim1.resolve(_EMPTY_PARAMS)))) + assert n1 < n0 + + +class TestBuildSimulationIntegration: + """Integration tests: TrajectorySimulator pipeline flows through to trajectory simulation.""" + + def test_noisy_trajectory_via_simulator(self): + """Full pipeline: TrajectorySimulator resolve + compress + adapt + apply_trajectory_operations.""" + p = Program(H(0), CNOT(0, 1), MEASURE(0, MemoryReference("ro", 0)), MEASURE(1, MemoryReference("ro", 1))) + p += Declare("ro", "BIT", 2) + + channels = [ + Channel.from_coherence_times(CNOT(0, 1), gate_duration=0.1, t1s=[30.0, 30.0], t2s=[20.0, 20.0]), + ] + noise_model = NoiseModel.from_channels(channels) + + sim = TrajectorySimulator(p, noise_model=noise_model, max_subsystem_size=0) + ops = sim.adapt(sim.compress(sim.resolve(_EMPTY_PARAMS))) + + n_traj = 16 + psi = qx.zero_state_vector(sim.n_qubits, ensemble_size=(n_traj,)) + key = jax.random.key(42) + psi_out, outcomes = apply_trajectory_operations(ops, psi, key) + assert outcomes.shape == (n_traj, 2) + assert set(int(v) for v in jnp.unique(outcomes)) <= {0, 1} + + def test_parameterized_trajectory(self): + """Parameterized circuit through TrajectorySimulator → trajectory sim.""" + p = Program() + p += Declare("theta", "REAL", 1) + p += Declare("ro", "BIT", 1) + p += RX(MemoryReference("theta", 0), 0) + p += MEASURE(0, MemoryReference("ro", 0)) + + sim = TrajectorySimulator(p, max_subsystem_size=0) + params = sim.linearize({"theta": [np.pi]}) + ops = sim.adapt(sim.compress(sim.resolve(params))) + + n_traj = 32 + psi = qx.zero_state_vector(sim.n_qubits, ensemble_size=(n_traj,)) + key = jax.random.key(0) + _, outcomes = apply_trajectory_operations(ops, psi, key) + assert jnp.all(outcomes == 1) + + +# ────────────────────────────────────────────────────────────────────────────── +# Compressor op-count tests +# ────────────────────────────────────────────────────────────────────────────── + + +def _op_count(program, max_subsystem_size, noise_model=None): + """Return the number of compressed ops for a program.""" + sim = TrajectorySimulator( + program, + noise_model=noise_model, + max_subsystem_size=max_subsystem_size, + ) + return len(sim.adapt(sim.compress(sim.resolve(sim.linearize({}))))) + + +class TestCompressorOpCounts: + """Tests that verify the compressor produces the expected number of ops.""" + + def test_cycle_channel_expands_and_compresses(self): + formal_qubit = FormalArgument("q") + defcircuit = DefCircuit( + "SINGLE_QUBIT_CYCLE", + [], + [formal_qubit], + [RX(0.1, formal_qubit), RZ(0.2, formal_qubit), RX(0.3, formal_qubit)], + ) + cycle_inst = QuilGate("SINGLE_QUBIT_CYCLE", [], [0]) + channels = tuple( + Channel.from_depolarizing_constant(inst, depolarizing_constant=0.99) + for inst in (RX(0.1, 0), RZ(0.2, 0), RX(0.3, 0)) + ) + noise_model = NoiseModel.from_channels([CycleChannel(inst=cycle_inst, defcircuit=defcircuit, channels=channels)]) + program = Program(defcircuit, cycle_inst) + + sim = TrajectorySimulator(program, noise_model=noise_model, max_subsystem_size=1) + resolved = sim.resolve(_EMPTY_PARAMS) + compressed = sim.compress(resolved) + + assert len(resolved) == 3 + assert all(isinstance(op, qx.SuperOp) for op, _ in resolved) + assert len(compressed) == 1 + + def test_expanded_cycle_without_cycle_channel_uses_gate_channels(self): + formal_qubit = FormalArgument("q") + defcircuit = DefCircuit( + "INDIVIDUAL_NOISE_CYCLE", + [], + [formal_qubit], + [RX(0.1, formal_qubit), RZ(0.2, formal_qubit)], + ) + cycle_inst = QuilGate("INDIVIDUAL_NOISE_CYCLE", [], [0]) + noise_model = NoiseModel.from_channels([Channel.from_depolarizing_constant(RX(0.1, 0), 0.99)]) + program = Program(defcircuit, cycle_inst) + + sim = TrajectorySimulator(program, noise_model=noise_model, max_subsystem_size=0) + resolved = sim.resolve(_EMPTY_PARAMS) + + assert len(resolved) == 2 + assert isinstance(resolved[0][0], qx.SuperOp) + assert isinstance(resolved[1][0], qx.Unitary) + + def test_single_qubit_sequence_merges_to_one(self): + """RZ-RX-RZ-RX-RZ on one qubit → 1 op at max_size ≥ 1.""" + p = Program(RZ(0.1, 0), RX(0.2, 0), RZ(0.3, 0), RX(0.4, 0), RZ(0.5, 0)) + assert _op_count(p, max_subsystem_size=0) == 5 + assert _op_count(p, max_subsystem_size=1) == 1 + + # Verify correctness + sim = TrajectorySimulator(p, max_subsystem_size=1) + ops = sim.adapt(sim.compress(sim.resolve(_EMPTY_PARAMS))) + psi = qx.zero_state_vector(sim.n_qubits) + for op, sub in ops: + psi = qx.targeted_apply_unitary(op, psi, sub) + assert qx.fidelity(psi, _sv(p)) > 0.9999 + + def test_two_qubit_layer_max_size_1(self): + """ZXZXZ on q0, ZXZXZ on q1, CZ 0 1, repeated 2×. + + With max_size=1: 1Q runs merge within each qubit between CZs, but CZ + can't merge into a size-1 group. Structure per repetition: + merged(5×q0) + merged(5×q1) + CZ = 3 ops; ×2 reps = 6 ops. + """ + p = Program() + for _ in range(2): + for q in (0, 1): + p += RZ(0.1, q) + p += RX(0.2, q) + p += RZ(0.3, q) + p += RX(0.4, q) + p += RZ(0.5, q) + p += CZ(0, 1) + + assert _op_count(p, max_subsystem_size=1) == 6 + + def test_two_qubit_layer_max_size_2(self): + """Same circuit as above, but with max_size=2 → everything merges to 1.""" + p = Program() + for _ in range(2): + for q in (0, 1): + p += RZ(0.1, q) + p += RX(0.2, q) + p += RZ(0.3, q) + p += RX(0.4, q) + p += RZ(0.5, q) + p += CZ(0, 1) + + assert _op_count(p, max_subsystem_size=2) == 1 + + # Verify correctness + sim = TrajectorySimulator(p, max_subsystem_size=2) + ops = sim.adapt(sim.compress(sim.resolve(_EMPTY_PARAMS))) + psi = qx.zero_state_vector(sim.n_qubits) + for op, sub in ops: + psi = qx.targeted_apply_unitary(op, psi, sub) + assert qx.fidelity(psi, _sv(p)) > 0.9999 + + def test_cnot_pair_merge(self): + """CNOT 0 1, CNOT 1 0 should merge into 1 op at max_size ≥ 2.""" + p = Program(CNOT(0, 1), CNOT(1, 0)) + + assert _op_count(p, max_subsystem_size=0) == 2 + assert _op_count(p, max_subsystem_size=1) == 2 # both are 2Q, can't fit in size 1 + assert _op_count(p, max_subsystem_size=2) == 1 + + # Verify correctness + sim = TrajectorySimulator(p, max_subsystem_size=2) + ops = sim.adapt(sim.compress(sim.resolve(_EMPTY_PARAMS))) + assert len(ops) == 1 + assert ops[0][1] == (0, 1) + psi = qx.zero_state_vector(sim.n_qubits) + for op, sub in ops: + psi = qx.targeted_apply_unitary(op, psi, sub) + assert qx.fidelity(psi, _sv(p)) > 0.9999 + + @pytest.mark.parametrize("num_qubits", [4, 8, 12]) + @pytest.mark.parametrize("max_subsystem_size", [0, 1, 2, 3]) + def test_random_circuit_compression(self, num_qubits, max_subsystem_size): + """Random layered circuits should compress monotonically with max_size.""" + rng = np.random.default_rng(42) + n_layers = 5 + + p = Program() + for _ in range(n_layers): + # 1Q layer + for q in range(num_qubits): + gate = rng.choice([RZ, RX, RY]) + p += gate(rng.uniform(-np.pi, np.pi), q) + # 2Q layer (linear chain, even edges) + for i in range(0, num_qubits - 1, 2): + p += CNOT(i, i + 1) + # 1Q layer + for q in range(num_qubits): + gate = rng.choice([RZ, RX, RY]) + p += gate(rng.uniform(-np.pi, np.pi), q) + # 2Q layer (odd edges) + for i in range(1, num_qubits - 1, 2): + p += CNOT(i, i + 1) + + n_ops = _op_count(p, max_subsystem_size) + n_uncompressed = _op_count(p, 0) + + # Compression should never increase op count + assert n_ops <= n_uncompressed, f"max_size={max_subsystem_size}: {n_ops} ops > {n_uncompressed} uncompressed" + + # With max_size > 0, we expect at least some compression for this circuit + if max_subsystem_size > 0: + assert n_ops < n_uncompressed + + def test_random_circuit_compression_summary(self, capsys): + """Print a summary table of compression ratios for various configs.""" + rng = np.random.default_rng(42) + + configs = [ + (4, 5), + (8, 5), + (12, 5), + (16, 3), + ] + max_sizes = [0, 1, 2, 3, 4] + + rows = [] + for num_qubits, n_layers in configs: + p = Program() + for _ in range(n_layers): + for q in range(num_qubits): + p += RZ(rng.uniform(-np.pi, np.pi), q) + p += RX(np.pi / 2, q) + for i in range(0, num_qubits - 1, 2): + p += CNOT(i, i + 1) + for q in range(num_qubits): + p += RZ(rng.uniform(-np.pi, np.pi), q) + for i in range(1, num_qubits - 1, 2): + p += CNOT(i, i + 1) + + counts = {s: _op_count(p, s) for s in max_sizes} + rows.append((num_qubits, n_layers, counts)) + + # Print table + header = f"{'qubits':>6} {'layers':>6}" + "".join(f" {'s=' + str(s):>8}" for s in max_sizes) + print(f"\n{'Compression op counts':=^{len(header)}}") + print(header) + print("-" * len(header)) + for nq, nl, counts in rows: + line = f"{nq:>6} {nl:>6}" + for s in max_sizes: + ratio = counts[s] / counts[0] if counts[0] > 0 else 0 + line += f" {counts[s]:>4} ({ratio:.2f})" + # line += f" {counts[s]:>8}" + print(line) + + +# ────────────────────────────────────────────────────────────────────────────── +# Multi-device / sharding tests +# ────────────────────────────────────────────────────────────────────────────── + + +class TestMultiDeviceTrajectory: + """Tests that exercise the multi-device code paths. + + On a single-CPU host these still validate the padding/unpadding logic + and the ``devices`` parameter plumbing. On a multi-device host they + exercise real data-parallel ``jax.pmap`` execution (one replica per device). + """ + + def test_devices_parameter_accepted(self): + """TrajectorySimulator should accept a ``devices`` keyword.""" + p = Program(H(0), MEASURE(0, None)) + sim = TrajectorySimulator(p, qubits=[0], devices=jax.devices()) + outcomes = sim.sample(_EMPTY_PARAMS, num_trajectories=10) + assert outcomes.shape == (10, 1) + + def test_sample_results_match_single_device(self): + """Outcomes shape and value range must be the same regardless of device list.""" + p = Program(H(0), MEASURE(0, None)) + sim_default = TrajectorySimulator(p, qubits=[0]) + sim_explicit = TrajectorySimulator(p, qubits=[0], devices=jax.devices()) + + out_default = sim_default.sample(_EMPTY_PARAMS, num_trajectories=64, batch_size=16, random_seed=99) + out_explicit = sim_explicit.sample(_EMPTY_PARAMS, num_trajectories=64, batch_size=16, random_seed=99) + + assert out_default.shape == out_explicit.shape + assert jnp.all((out_default == 0) | (out_default == 1)) + assert jnp.all((out_explicit == 0) | (out_explicit == 1)) + + def test_padding_stripped_correctly(self): + """When num_trajectories is not a multiple of n_devices, padding must be removed.""" + p = Program(H(0), MEASURE(0, None)) + sim = TrajectorySimulator(p, qubits=[0], devices=jax.devices()) + # 7 is unlikely to be a multiple of any device count > 1 + outcomes = sim.sample(_EMPTY_PARAMS, num_trajectories=7, batch_size=7) + assert outcomes.shape == (7, 1) + + def test_batched_trajectories_with_devices(self): + """_run_batched_trajectories should accept and use devices parameter.""" + p = Program(H(0), MEASURE(0, None)) + sim = TrajectorySimulator(p, qubits=[0]) + resolved = sim.resolve(_EMPTY_PARAMS) + compressed = sim.compress(resolved) + operations = sim.adapt(compressed) + + _, outcomes = _run_batched_trajectories( + operations, + num_trajectories=20, + batch_size=8, + random_seed=42, + keep_states=False, + dims=sim.dims, + devices=jax.devices(), + ) + total = sum(o.shape[0] for o in outcomes) + assert total == 20 + + def test_noisy_sample_with_devices(self): + """Multi-device path should work with noise models.""" + p_error = 0.3 + ch = Channel.from_pauli_noise(inst=X(0), pauli_noise={"X": p_error}) + noise_model = NoiseModel.from_channels([ch]) + p = Program(X(0), MEASURE(0, None)) + sim = TrajectorySimulator(p, noise_model=noise_model, qubits=[0], devices=jax.devices()) + outcomes = sim.sample(_EMPTY_PARAMS, num_trajectories=1024, batch_size=256, random_seed=7) + assert outcomes.shape == (1024, 1) + frac_0 = float(jnp.mean(outcomes == 0)) + assert abs(frac_0 - p_error) < 0.05 diff --git a/test/unit/test_trajectory_compression.py b/test/unit/test_trajectory_compression.py new file mode 100644 index 000000000..073008054 --- /dev/null +++ b/test/unit/test_trajectory_compression.py @@ -0,0 +1,882 @@ +"""Correctness tests for the compressor used by ``TrajectorySimulator``. + +The simulators in :mod:`pyquil.simulation._simulator` share a *compressor* +(see :func:`pyquil.simulation._resolver.compressor_from_dag` and +``_merge_ops``) which merges adjacent operations into multi-qudit groups when +``max_subsystem_size >= 2``. Compression must be a no-op on the *physics*: the +state produced by a compressed simulation has to match the state produced by an +uncompressed one (and an independent oracle). + +These tests pin that invariant down for the :class:`TrajectorySimulator` across +the full matrix of cases requested: + +* qubit (d=2) and qutrit (d=3) registers, +* gate-only and gate+measurement circuits, +* noiseless and noisy circuits, +* small (2-register) and larger (5-register) circuits. + +All tests run at ``max_subsystem_size=2`` and use **non-sequential** qubit +indices so that the physical-to-logical remapping is exercised. Several +circuits deliberately use multi-qudit gates whose qubit arguments are *not* +sorted (e.g. ``CNOT(2, 5)``, ``TSWAP(2, 5)``) to exercise the embedding of an +operator at non-trivial positions inside a merged subsystem — the path most +likely to be dimension-sensitive. + +Verification strategy +--------------------- +The ground-truth ("oracle") is always produced **without** compression +(``max_subsystem_size=0``), which the user has confirmed to be correct: + +* gate-only oracles use :class:`PureStateVectorSimulator` (pure state) and + :class:`DensityMatrixSimulator` (density matrix); +* noisy / measurement oracles use :class:`DensityMatrixSimulator`. + +For the compressed :class:`TrajectorySimulator` we reconstruct the simulated +density matrix as the Monte-Carlo average of the pure trajectory projectors, + + rho_est = (1 / N) * sum_i |psi_i>ij", m, jnp.conj(m)) / n + return qx.DensityMatrix.from_matrix(rho, dims) + + +def _oracle_density(program, qubits, noise_model=None): + """Ground-truth density matrix via the uncompressed density-matrix simulator.""" + sim = DensityMatrixSimulator( + program, qubits=qubits, noise_model=noise_model, max_subsystem_size=0 + ) + return sim.compute(sim.linearize({})) + + +def _assert_real_compression(program, qubits, noise_model=None): + """Assert the compressor actually merges ops at ``MAX_SUBSYSTEM_SIZE``. + + A test of compression correctness is meaningless if no merging happens, so + every test first confirms the compressed op count is strictly smaller than + the resolved op count. + """ + sim = TrajectorySimulator( + program, qubits=qubits, noise_model=noise_model, + max_subsystem_size=MAX_SUBSYSTEM_SIZE, + ) + params = sim.linearize({}) + resolved = sim.resolve(params) + compressed = sim.compress(resolved) + assert len(compressed) < len(resolved), ( + f"compression did not merge any ops " + f"({len(resolved)} resolved -> {len(compressed)} compressed)" + ) + + +def _trajectory_density(program, qubits, dims, n_traj, *, noise_model=None, + seed=0, max_subsystem_size=MAX_SUBSYSTEM_SIZE): + """Run ``n_traj`` trajectories and reconstruct the simulated density matrix. + + The compressor preserves program order for measurement nodes, so the columns + of ``outcomes`` correspond to the ``MEASURE`` instructions in program order + regardless of ``max_subsystem_size``. No re-ordering is applied here — the + raw column order is part of what these tests verify (see + :func:`test_measurement_outcome_column_order_under_compression`). + + :return: ``(rho_est, outcomes)`` where ``rho_est`` is the Monte-Carlo + density matrix and ``outcomes`` has shape ``(n_traj, n_measurements)``. + """ + sim = TrajectorySimulator( + program, qubits=qubits, noise_model=noise_model, + max_subsystem_size=max_subsystem_size, + ) + params = sim.linearize({}) + keys = jax.random.split(jax.random.key(seed), n_traj) + psi, outcomes = sim.compute(params, keys) + return _density_from_states(psi.matrix, dims), np.asarray(outcomes) + + +def _outcome_distribution(outcomes, dims): + """Empirical joint distribution of measurement outcomes as a length-``d`` vector. + + Each trajectory's per-measurement outcomes (in program / qubit-slot order) + are interpreted as digits in a row-major mixed-radix number with the given + ``dims`` and histogrammed. The resulting index ordering matches the + canonical basis ordering of the density matrix, so the distribution can be + compared directly against the oracle diagonal. + """ + out = np.asarray(outcomes) + n_traj, n_meas = out.shape + radices = np.asarray(dims[:n_meas]) + # Mixed-radix encoding: index = sum_k outcome_k * prod(radices[k+1:]). + weights = np.ones(n_meas, dtype=np.int64) + for k in range(n_meas - 2, -1, -1): + weights[k] = weights[k + 1] * radices[k + 1] + indices = (out * weights).sum(axis=1) + counts = np.bincount(indices, minlength=int(np.prod(radices))) + return counts / n_traj + + +def _total_variation(p, q): + """Total-variation distance between two probability vectors.""" + return 0.5 * float(np.abs(np.asarray(p) - np.asarray(q)).sum()) + + +def _sampler_total_channel(program, qubits, *, noise_model=None, max_subsystem_size): + """Full superoperator the trajectory *sampler* implements, as a dense matrix. + + This composes the per-operation channels exactly as the trajectory sampler + sees them: each operation is converted to its padded Kraus matrices via + :func:`pyquil.simulation._simulator._op_to_kraus_matrix` (the very matrices + fed into the Monte-Carlo sampling loop), promoted to a superoperator, and + embedded into the full register before being composed. + + Building the channel from ``pyquil.simulation._simulator._op_to_kraus_matrix`` (rather than from the + high-level operator objects) means this exercises the padding, outcome/Kraus + axis flattening, and dimension handling that only the trajectory path uses. + The result is deterministic and independent of any sampling, so it is a + far more sensitive probe of compression correctness than a Monte-Carlo + fidelity: a *subtly* wrong error rate at ``max_subsystem_size=2`` shows up + here as a non-zero channel difference even though it would hide under the + statistical noise of a fidelity estimate. + + :return: A dense ``(D**2, D**2)`` superoperator matrix. + """ + sim = TrajectorySimulator( + program, qubits=qubits, noise_model=noise_model, + max_subsystem_size=max_subsystem_size, + ) + operations = sim.adapt(sim.compress(sim.resolve(sim.linearize({})))) + dims = tuple(sim.dims) + dimension = int(np.prod(dims)) + + channel = np.eye(dimension * dimension, dtype=complex) + for op, subsystem in operations: + matrix, _divisor, _is_measure = _op_to_kraus_matrix(op) + kraus = qx.KrausMap.from_matrix( + np.asarray(matrix), (tuple(dims[i] for i in subsystem),) * 2 + ) + embedded = qx.embed( + qx.to_superop(kraus), target_dims=dims, positions=tuple(subsystem) + ) + channel = np.asarray(embedded.matrix) @ channel + return channel + + + +# ══════════════════════════════════════════════════════════ +# Circuit builders +# +# Each builder returns ``(program, qubits, dims)`` and (optionally) a matching +# noise model. Single-qudit gates flanking the entanglers are absorbable by +# the compressor, which is what forces real 2-qudit merges to occur. +# ══════════════════════════════════════════════════════════ + + +def _qubit_circuit_2(): + """2-qubit gate-only circuit on non-sequential qubits [5, 2]. + + ``CNOT(2, 5)`` is intentionally control>target so the two-qubit gate is + embedded at a non-sorted position inside the merged subsystem. + """ + p = Program() + p += H(5) + p += CNOT(2, 5) # control on the high index -> non-sorted subsystem + p += X(5) + p += H(2) + return p, QUBITS_2, (2, 2) + + +def _qubit_circuit_5(): + """5-qubit gate-only circuit on non-sequential qubits [7, 2, 9, 4, 0].""" + p = Program() + for q in QUBITS_5: + p += H(q) + p += CZ(7, 2) + p += CZ(9, 4) + for q, a in zip(QUBITS_5, (0.3, 0.7, 1.1, 0.5, 0.9)): + p += RX(a, q) + p += CNOT(2, 9) # non-sorted relative to qubit ordering + p += CNOT(4, 0) + for q, a in zip(QUBITS_5, (0.2, 0.6, 0.4, 0.8, 1.0)): + p += RZ(a, q) + p += CNOT(0, 7) # spans the two ends -> forces another merge + return p, QUBITS_5, (2, 2, 2, 2, 2) + + +def _qutrit_circuit_2(): + """2-qutrit gate-only circuit on non-sequential qubits [5, 2]. + + ``TSWAP(2, 5)`` is non-sorted, which is the case the user reports as broken + under compression. + """ + p = Program() + p += Gate("TH", [], [5]) + p += Gate("TSWAP", [], [2, 5]) # non-sorted two-qutrit gate + p += Gate("TRX01", [0.9], [2]) + p += Gate("TX", [], [5]) + return p, QUBITS_2, (3, 3) + + +def _qutrit_circuit_5(): + """5-qutrit gate-only circuit on non-sequential qubits [7, 2, 9, 4, 0].""" + p = Program() + for q in QUBITS_5: + p += Gate("TH", [], [q]) + p += Gate("TSWAP", [], [2, 7]) # non-sorted + p += Gate("TSWAP", [], [9, 4]) + for q, a in zip(QUBITS_5, (0.3, 0.7, 1.1, 0.5, 0.9)): + p += Gate("TRX01", [a], [q]) + p += Gate("TSWAP", [], [4, 0]) + p += Gate("TX", [], [7]) + p += Gate("TX", [], [9]) + return p, QUBITS_5, (3, 3, 3, 3, 3) + + +def _depolarizing_model(insts, fidelity): + """Build a depolarizing noise model for the given instructions.""" + return NoiseModel.from_channels( + [Channel.from_gate_fidelity(inst=inst, fidelity=fidelity) for inst in insts] + ) + + +# ══════════════════════════════════════════════════════════ +# 1-2. Gate-only, no noise +# ══════════════════════════════════════════════════════════ + + +def test_compression_two_qubit_gates_no_noise(): + """2-qubit gate-only circuit: compressed trajectory == uncompressed oracle.""" + program, qubits, dims = _qubit_circuit_2() + _assert_real_compression(program, qubits) + + oracle = _oracle_density(program, qubits) + rho_est, _ = _trajectory_density(program, qubits, dims, n_traj=8) + assert float(qx.fidelity(rho_est, oracle)) > 0.9999 + + # Compressed and uncompressed trajectories must agree exactly (noiseless). + rho_uncompressed, _ = _trajectory_density( + program, qubits, dims, n_traj=8, max_subsystem_size=0 + ) + assert float(qx.fidelity(rho_est, rho_uncompressed)) > 0.9999 + + +def test_compression_five_qubit_gates_no_noise(): + """5-qubit gate-only circuit: compressed trajectory == uncompressed oracle.""" + program, qubits, dims = _qubit_circuit_5() + _assert_real_compression(program, qubits) + + oracle = _oracle_density(program, qubits) + rho_est, _ = _trajectory_density(program, qubits, dims, n_traj=8) + assert float(qx.fidelity(rho_est, oracle)) > 0.9999 + + rho_uncompressed, _ = _trajectory_density( + program, qubits, dims, n_traj=8, max_subsystem_size=0 + ) + assert float(qx.fidelity(rho_est, rho_uncompressed)) > 0.9999 + + +# ══════════════════════════════════════════════════════════ +# 3-4. Gate-only, with noise +# ══════════════════════════════════════════════════════════ + + +def test_compression_two_qubit_gates_with_noise(): + """2-qubit noisy circuit: reconstructed density matrix matches oracle.""" + program, qubits, dims = _qubit_circuit_2() + noise_model = _depolarizing_model( + [CNOT(2, 5), X(5), H(2), H(5)], fidelity=0.97 + ) + _assert_real_compression(program, qubits, noise_model) + + oracle = _oracle_density(program, qubits, noise_model) + rho_est, _ = _trajectory_density( + program, qubits, dims, n_traj=8000, noise_model=noise_model, seed=1 + ) + assert float(qx.fidelity(rho_est, oracle)) > 0.99 + + +def test_compression_five_qubit_gates_with_noise(): + """5-qubit noisy circuit: reconstructed density matrix matches oracle.""" + program, qubits, dims = _qubit_circuit_5() + noise_model = _depolarizing_model( + [CZ(7, 2), CZ(9, 4), CNOT(2, 9), CNOT(4, 0), CNOT(0, 7)], fidelity=0.99 + ) + _assert_real_compression(program, qubits, noise_model) + + oracle = _oracle_density(program, qubits, noise_model) + rho_est, _ = _trajectory_density( + program, qubits, dims, n_traj=12000, noise_model=noise_model, seed=2 + ) + assert float(qx.fidelity(rho_est, oracle)) > 0.98 + + +# ══════════════════════════════════════════════════════════ +# 5-6. Qutrit gate-only, no noise +# ══════════════════════════════════════════════════════════ + + +def test_compression_two_qutrit_gates_no_noise(): + """2-qutrit gate-only circuit: the qutrit compression case under test.""" + program, qubits, dims = _qutrit_circuit_2() + _assert_real_compression(program, qubits) + + oracle = _oracle_density(program, qubits) + rho_est, _ = _trajectory_density(program, qubits, dims, n_traj=8) + assert float(qx.fidelity(rho_est, oracle)) > 0.9999 + + rho_uncompressed, _ = _trajectory_density( + program, qubits, dims, n_traj=8, max_subsystem_size=0 + ) + assert float(qx.fidelity(rho_est, rho_uncompressed)) > 0.9999 + + +def test_compression_five_qutrit_gates_no_noise(): + """5-qutrit gate-only circuit: compressed trajectory == uncompressed oracle.""" + program, qubits, dims = _qutrit_circuit_5() + _assert_real_compression(program, qubits) + + oracle = _oracle_density(program, qubits) + rho_est, _ = _trajectory_density(program, qubits, dims, n_traj=8) + assert float(qx.fidelity(rho_est, oracle)) > 0.9999 + + rho_uncompressed, _ = _trajectory_density( + program, qubits, dims, n_traj=8, max_subsystem_size=0 + ) + assert float(qx.fidelity(rho_est, rho_uncompressed)) > 0.9999 + + +# ══════════════════════════════════════════════════════════ +# 7-8. Qutrit gate-only, with noise +# ══════════════════════════════════════════════════════════ + + +def test_compression_two_qutrit_gates_with_noise(): + """2-qutrit noisy circuit: reconstructed density matrix matches oracle.""" + program, qubits, dims = _qutrit_circuit_2() + noise_model = _depolarizing_model( + [Gate("TSWAP", [], [2, 5]), Gate("TX", [], [5]), Gate("TH", [], [5])], + fidelity=0.98, + ) + _assert_real_compression(program, qubits, noise_model) + + oracle = _oracle_density(program, qubits, noise_model) + rho_est, _ = _trajectory_density( + program, qubits, dims, n_traj=8000, noise_model=noise_model, seed=3 + ) + assert float(qx.fidelity(rho_est, oracle)) > 0.99 + + +def test_compression_five_qutrit_gates_with_noise(): + """5-qutrit noisy circuit: reconstructed density matrix matches oracle. + + Noise is kept light (per-gate fidelity 0.99 on two gates only) so the + output state stays close to pure and is reconstructable from a tractable + number of trajectories despite the d=243 Hilbert space. + """ + program, qubits, dims = _qutrit_circuit_5() + noise_model = _depolarizing_model( + [Gate("TSWAP", [], [2, 7]), Gate("TSWAP", [], [9, 4])], fidelity=0.99 + ) + _assert_real_compression(program, qubits, noise_model) + + oracle = _oracle_density(program, qubits, noise_model) + rho_est, _ = _trajectory_density( + program, qubits, dims, n_traj=8000, noise_model=noise_model, seed=4 + ) + assert float(qx.fidelity(rho_est, oracle)) > 0.97 + + +# ══════════════════════════════════════════════════════════ +# 9-10. Gates + measurements, no noise +# ══════════════════════════════════════════════════════════ + + +def test_compression_two_qubit_measurements_no_noise(): + """2-qubit Bell circuit with measurements: outcomes are perfectly correlated.""" + program = Program() + program += H(5) + program += CNOT(5, 2) + program += Measurement(qubit=Qubit(5), classical_reg=None) + program += Measurement(qubit=Qubit(2), classical_reg=None) + qubits, dims = QUBITS_2, (2, 2) + + _assert_real_compression(program, qubits) + + oracle = _oracle_density(program, qubits) + rho_est, outcomes = _trajectory_density(program, qubits, dims, n_traj=6000, seed=5) + # Trajectory-averaged (post-measurement) state reproduces the oracle mixture. + assert float(qx.fidelity(rho_est, oracle)) > 0.99 + + # Bell state -> only 00 and 11 ever occur, each ~50%. + dist = _outcome_distribution(outcomes, dims) + oracle_diag = np.real(np.diag(np.asarray(oracle.matrix))) + assert _total_variation(dist, oracle_diag) < 0.05 + assert dist[1] < 1e-9 and dist[2] < 1e-9 # |01> and |10> never measured + + +def test_compression_five_qubit_measurements_no_noise(): + """5-qubit circuit with terminal measurements on all qubits.""" + program, qubits, dims = _qubit_circuit_5() + for q in qubits: + program += Measurement(qubit=Qubit(q), classical_reg=None) + + _assert_real_compression(program, qubits) + + oracle = _oracle_density(program, qubits) + rho_est, outcomes = _trajectory_density(program, qubits, dims, n_traj=8000, seed=6) + assert float(qx.fidelity(rho_est, oracle)) > 0.98 + + dist = _outcome_distribution(outcomes, dims) + oracle_diag = np.real(np.diag(np.asarray(oracle.matrix))) + assert _total_variation(dist, oracle_diag) < 0.06 + + +# ══════════════════════════════════════════════════════════ +# 11-12. Gates + measurements, with noise +# ══════════════════════════════════════════════════════════ + + +def test_compression_two_qubit_measurements_with_noise(): + """2-qubit Bell circuit with gate noise and readout error.""" + program = Program() + program += H(5) + program += CNOT(5, 2) + meas5 = Measurement(qubit=Qubit(5), classical_reg=None) + meas2 = Measurement(qubit=Qubit(2), classical_reg=None) + program += meas5 + program += meas2 + qubits, dims = QUBITS_2, (2, 2) + + noise_model = NoiseModel.from_channels( + [ + Channel.from_gate_fidelity(inst=CNOT(5, 2), fidelity=0.97), + Channel.from_gate_fidelity(inst=H(5), fidelity=0.98), + MeasurementChannel.from_readout_fidelity(inst=meas5, fidelity=0.95), + MeasurementChannel.from_readout_fidelity(inst=meas2, fidelity=0.95), + ] + ) + _assert_real_compression(program, qubits, noise_model) + + oracle = _oracle_density(program, qubits, noise_model) + rho_est, outcomes = _trajectory_density( + program, qubits, dims, n_traj=8000, noise_model=noise_model, seed=7 + ) + assert float(qx.fidelity(rho_est, oracle)) > 0.99 + + # Readout confusion error perturbs the reported outcomes (not the QND + # post-measurement state), so the outcome distribution is compared against + # the uncompressed run rather than the density-matrix diagonal. + dist = _outcome_distribution(outcomes, dims) + _, outcomes_uncompressed = _trajectory_density( + program, qubits, dims, n_traj=8000, noise_model=noise_model, seed=7, + max_subsystem_size=0, + ) + dist_uncompressed = _outcome_distribution(outcomes_uncompressed, dims) + assert _total_variation(dist, dist_uncompressed) < 0.04 + + +def test_compression_five_qubit_measurements_with_noise(): + """5-qubit circuit with gate noise, readout error, and terminal measurements.""" + program, qubits, dims = _qubit_circuit_5() + measurements = [] + for q in qubits: + m = Measurement(qubit=Qubit(q), classical_reg=None) + measurements.append(m) + program += m + + noise_model = NoiseModel.from_channels( + [ + Channel.from_gate_fidelity(inst=CZ(7, 2), fidelity=0.99), + Channel.from_gate_fidelity(inst=CZ(9, 4), fidelity=0.99), + Channel.from_gate_fidelity(inst=CNOT(2, 9), fidelity=0.99), + Channel.from_gate_fidelity(inst=CNOT(4, 0), fidelity=0.99), + Channel.from_gate_fidelity(inst=CNOT(0, 7), fidelity=0.99), + ] + + [MeasurementChannel.from_readout_fidelity(inst=m, fidelity=0.96) for m in measurements] + ) + _assert_real_compression(program, qubits, noise_model) + + oracle = _oracle_density(program, qubits, noise_model) + rho_est, outcomes = _trajectory_density( + program, qubits, dims, n_traj=12000, noise_model=noise_model, seed=8 + ) + assert float(qx.fidelity(rho_est, oracle)) > 0.97 + + # Readout confusion error perturbs the reported outcomes (not the QND + # post-measurement state), so the outcome distribution is compared against + # the uncompressed run rather than the density-matrix diagonal. + dist = _outcome_distribution(outcomes, dims) + _, outcomes_uncompressed = _trajectory_density( + program, qubits, dims, n_traj=12000, noise_model=noise_model, seed=8, + max_subsystem_size=0, + ) + dist_uncompressed = _outcome_distribution(outcomes_uncompressed, dims) + assert _total_variation(dist, dist_uncompressed) < 0.05 + + +# ══════════════════════════════════════════════════════════ +# Regression: measurement-outcome column order under compression +# ══════════════════════════════════════════════════════════ + + +def test_measurement_outcome_column_order_under_compression(): + """Outcome columns follow ``MEASURE`` program order even when gates merge. + + The compressor merges gates into multi-qudit groups, which reorders the + topological emission of operations. Measurement nodes must nonetheless be + emitted in program order so that ``outcomes[:, i]`` corresponds to the + *i*-th ``MEASURE`` instruction. This pins down that invariant directly by + preparing a distinct, deterministic basis state on every qubit and checking + each column independently — at ``max_subsystem_size=2`` (compressed) against + both the analytic expectation and the uncompressed (``max=0``) run. + """ + qubits = QUBITS_5 # [7, 2, 9, 4, 0] + dims = (2, 2, 2, 2, 2) + + # X on qubits 7, 9, 0 -> per-slot states [1, 0, 1, 0, 1] for slots 0..4. + program = Program() + program += X(7) + program += X(9) + program += X(0) + # Single-qubit gates flanking an entangler force real merges around the + # measurement barriers without changing the deterministic outcomes. + program += H(2) + program += H(2) # H*H = I on slot 1, but creates a mergeable group + program += CNOT(7, 9) # both already |1>: CNOT leaves 7->1, 9 flips 1->0 + program += CNOT(7, 9) # flip back: 9 -> 1, restoring [1,0,1,0,1] + # Measure in program order: qubits 7, 2, 9, 4, 0 == slots 0, 1, 2, 3, 4. + for q in qubits: + program += Measurement(qubit=Qubit(q), classical_reg=None) + + _assert_real_compression(program, qubits) + + expected = np.array([1, 0, 1, 0, 1]) + + _, outcomes_compressed = _trajectory_density( + program, qubits, dims, n_traj=16, seed=0, max_subsystem_size=2 + ) + _, outcomes_uncompressed = _trajectory_density( + program, qubits, dims, n_traj=16, seed=0, max_subsystem_size=0 + ) + + # Every trajectory is deterministic; every column must match program order. + assert outcomes_compressed.shape == (16, 5) + assert np.all(outcomes_compressed == expected) + assert np.all(outcomes_uncompressed == expected) + # Compressed and uncompressed must agree column-for-column. + assert np.array_equal(outcomes_compressed, outcomes_uncompressed) + + +# ══════════════════════════════════════════════════════════ +# Tightened: deterministic total-channel equality across sizes +# ══════════════════════════════════════════════════════════ + + +def _noisy_channel_cases(): + """Yield ``(id, program, qubits, noise_model)`` for every noisy circuit. + + These reuse the noisy circuits exercised by the Monte-Carlo tests above for + a deterministic, sampling-free channel comparison. The 5-qutrit circuit is + deliberately omitted: its dense superoperator is ``243**2 x 243**2`` (~52 + GiB), which is intractable. That case stays covered by the Monte-Carlo + fidelity test; the cases below already span qubit and qutrit registers, + non-sorted multi-qudit gates, and 2- and 5-register sizes. + """ + p2, q2, _ = _qubit_circuit_2() + yield ( + "qubit2", + p2, + q2, + _depolarizing_model([CNOT(2, 5), X(5), H(2), H(5)], fidelity=0.97), + ) + + p5, q5, _ = _qubit_circuit_5() + yield ( + "qubit5", + p5, + q5, + _depolarizing_model( + [CZ(7, 2), CZ(9, 4), CNOT(2, 9), CNOT(4, 0), CNOT(0, 7)], fidelity=0.99 + ), + ) + + pq2, qq2, _ = _qutrit_circuit_2() + yield ( + "qutrit2", + pq2, + qq2, + _depolarizing_model( + [Gate("TSWAP", [], [2, 5]), Gate("TX", [], [5]), Gate("TH", [], [5])], + fidelity=0.98, + ), + ) + + +@pytest.mark.parametrize("case", tuple(_noisy_channel_cases()), ids=lambda c: c[0]) +def test_compression_preserves_total_channel_exactly(case): + """Compression must not change the *channel* the sampler implements. + + The Monte-Carlo fidelity tests above can only resolve an error rate to + within their statistical noise (~1e-2). A *subtly* wrong error rate at + ``max_subsystem_size=2`` — exactly the symptom under investigation — would + slip beneath that floor. This test removes the sampling entirely: it builds + the exact dense superoperator the trajectory sampler implements at each + ``max_subsystem_size`` and asserts the compressed channel equals the + uncompressed one to machine precision. + + Because the channel is reconstructed from + :func:`pyquil.simulation._simulator._op_to_kraus_matrix`, this directly + exercises the merged-Kraus padding/embedding path that only compression + triggers, for both qubit (d=2) and qutrit (d=3) registers including + non-sorted multi-qudit gates. + """ + _id, program, qubits, noise_model = case + + reference = _sampler_total_channel( + program, qubits, noise_model=noise_model, max_subsystem_size=0 + ) + for size in (1, 2): + channel = _sampler_total_channel( + program, qubits, noise_model=noise_model, max_subsystem_size=size + ) + max_abs_diff = float(np.abs(channel - reference).max()) + assert max_abs_diff < 1e-10, ( + f"compression at max_subsystem_size={size} changed the channel " + f"(max |ΔS| = {max_abs_diff:.2e})" + ) + + +# ══════════════════════════════════════════════════════════ +# Tightened: per-qubit error rate is mapped to the correct column +# ══════════════════════════════════════════════════════════ + + +def test_asymmetric_readout_error_rate_column_mapping_under_compression(): + """Each qubit's readout error rate stays on its own outcome column. + + A column-permutation bug under compression would swap which qubit a measured + error rate is attributed to. A *symmetric* readout model cannot detect such + a swap, so this test gives the two measured qubits **very different** error + rates (one ~30%, one ~1%) and interleaves a mergeable gate block between the + two ``MEASURE`` instructions. At ``max_subsystem_size=2`` the gate block + collapses to a single merged operator sitting between the measurement + barriers — the configuration most likely to disturb measurement emission + order — yet each column must still report its own qubit's error rate. + """ + qubits = QUBITS_2 # [5, 2] + + program = Program() + program += X(2) # slot 1 (qubit 2) -> |1>, slot 0 (qubit 5) -> |0> + meas2 = Measurement(qubit=Qubit(2), classical_reg=None) # program-order col 0 + meas5 = Measurement(qubit=Qubit(5), classical_reg=None) # program-order col 1 + program += meas2 + # Mergeable gate block between the two measurements (identity on the state: + # H(5) H(5) = I, CNOT(2,5) CNOT(2,5) = I) but it forces a real 2-qubit merge. + program += H(5) + program += CNOT(2, 5) + program += CNOT(2, 5) + program += H(5) + program += meas5 + + # qubit 2 measured |1> with 30% flip (fidelity 0.70); qubit 5 measured |0> + # with 1% flip (fidelity 0.99). The two error rates are deliberately far + # apart so any column swap is unmistakable. + noise_model = NoiseModel.from_channels( + [ + MeasurementChannel.from_readout_fidelity(inst=meas2, fidelity=0.70), + MeasurementChannel.from_readout_fidelity(inst=meas5, fidelity=0.99), + ] + ) + _assert_real_compression(program, qubits, noise_model) + + n_traj = 20000 + dims = (2, 2) + for size in (0, 1, 2): + _, outcomes = _trajectory_density( + program, qubits, dims, n_traj, noise_model=noise_model, + seed=0, max_subsystem_size=size, + ) + # Column 0 == qubit 2 (|1>, 30% flip to 0) -> P(=1) ~ 0.70. + # Column 1 == qubit 5 (|0>, 1% flip to 1) -> P(=1) ~ 0.01. + p_col0_one = outcomes[:, 0].mean() + p_col1_one = outcomes[:, 1].mean() + assert abs(p_col0_one - 0.70) < 0.02, ( + f"size {size}: qubit-2 error rate landed on the wrong column " + f"(col0 P(=1)={p_col0_one:.3f}, expected ~0.70)" + ) + assert abs(p_col1_one - 0.01) < 0.01, ( + f"size {size}: qubit-5 error rate landed on the wrong column " + f"(col1 P(=1)={p_col1_one:.3f}, expected ~0.01)" + ) + + +# ══════════════════════════════════════════════════════════ +# Regression: compression must not reorder mid-circuit measurements +# ══════════════════════════════════════════════════════════ + + +def test_compression_does_not_merge_gates_across_mid_circuit_measurement(): + """Gates straddling a mid-circuit measurement must not be fused. + + This pins down the bug that produced a *different logical error rate* under + compression in repetition-code experiments. Two gates that act on the same + qubits but sit on opposite sides of a mid-circuit ``MEASURE`` form a DAG + edge (they share a qubit), so a size-only merge check happily fuses them — + which silently moves the measurement to *after* both gates. In a circuit + with repeated syndrome extraction this corrupts every round and inflates the + logical error rate. + + The compressor must keep the merged group *convex*: it may only fuse two + operations if no barrier (or any other operation) lies on a dependency path + between them. Here a non-trivial unitary surrounds a mid-circuit + measurement, so the merged ``emit_order`` at ``max_subsystem_size=2`` must + still place the measurement between the two gate groups, and the sampled + outcome distribution must match the uncompressed run. + """ + qubits = QUBITS_2 # [5, 2] + dims = (2, 2) + + program = Program() + program += H(5) + program += CNOT(5, 2) # entangle 5 and 2 + mid = Measurement(qubit=Qubit(2), classical_reg=None) # collapse slot 1 + program += mid + program += H(2) + program += CNOT(5, 2) # acts on the same pair again, *after* the measurement + program += Measurement(qubit=Qubit(5), classical_reg=None) + program += Measurement(qubit=Qubit(2), classical_reg=None) + + _assert_real_compression(program, qubits) + + # The mid-circuit measurement must remain sandwiched between the two + # two-qubit gate groups; the two CNOTs must NOT be fused into one operator. + sim = TrajectorySimulator( + program, qubits=qubits, max_subsystem_size=MAX_SUBSYSTEM_SIZE + ) + emitted = sim.compress(sim.resolve(sim.linearize({}))) + op_types = [type(op).__name__ for op, _ in emitted] + # Exactly three QuantumInstruments (one mid-circuit + two terminal) and the + # first must appear before the second gate group — i.e. there is a gate + # operation emitted *after* the first measurement. + first_measure = op_types.index("QuantumInstrument") + assert any( + name != "QuantumInstrument" for name in op_types[first_measure + 1 :] + ), f"a gate group must follow the mid-circuit measurement, got {op_types}" + + # The sampled outcome statistics must be independent of compression. There + # are three measurements (mid-circuit on slot 1, then terminal on slots 0 + # and 1), so the joint distribution is histogrammed over 2**3 = 8 patterns. + def _joint(max_subsystem_size): + _, outcomes = _trajectory_density( + program, qubits, dims, 40000, seed=0, + max_subsystem_size=max_subsystem_size, + ) + codes = outcomes[:, 0] * 4 + outcomes[:, 1] * 2 + outcomes[:, 2] + return np.bincount(codes, minlength=8) / len(codes) + + assert _total_variation(_joint(0), _joint(2)) < 0.02 + + +def test_compression_does_not_move_pre_measurement_gate_across_a_measurement(): + """A pre-measurement gate must not be pulled *after* a measurement it precedes. + + This is the subtler sibling of the convexity bug above. A gate that sits + *before* a mid-circuit ``MEASURE`` but acts on a *disjoint* qubit can be + validly fused with a gate that sits *after* the measurement (they share a + qubit and the pre-measurement gate commutes with the barrier). The merge + itself is legal, but the merged group must be *emitted after* the barrier — + otherwise the post-measurement gate is applied before the measurement, + corrupting its outcome. + + An earlier emit order walked the *original* DAG and emitted each group at + its earliest member, which placed such a group before the barrier. The + correct emit order is a topological sort of the *quotient* (contracted) + graph. Here ``X`` on the data qubit precedes the ancilla ``MEASURE`` and is + fused with the ``CNOT`` that follows it; the circuit is fully deterministic, + so every measurement column must equal the uncompressed result at every + ``max_subsystem_size``. + """ + ancilla, data = QUBITS_2 # [5, 2]; measure the ancilla mid-circuit + qubits = QUBITS_2 + + program = Program() + program += X(data) # data -> |1> + program += Measurement(qubit=Qubit(ancilla), classical_reg=None) # ancilla |0> -> 0 + program += CNOT(data, ancilla) # control |1> flips ancilla -> |1> + program += Measurement(qubit=Qubit(ancilla), classical_reg=None) # -> 1 + program += Measurement(qubit=Qubit(data), classical_reg=None) # -> 1 + + _assert_real_compression(program, qubits) + + # X(data) and CNOT(data, ancilla) share the data qubit and fuse into one + # group; that group must still be emitted *after* the first ancilla + # measurement, so a gate op appears after the first QuantumInstrument. + sim = TrajectorySimulator(program, qubits=qubits, max_subsystem_size=MAX_SUBSYSTEM_SIZE) + op_types = [type(op).__name__ for op, _ in sim.compress(sim.resolve(sim.linearize({})))] + first_measure = op_types.index("QuantumInstrument") + assert any(name != "QuantumInstrument" for name in op_types[first_measure + 1 :]), ( + f"a gate group must follow the first mid-circuit measurement, got {op_types}" + ) + + # Fully deterministic: columns are [ancilla(round 1), ancilla(round 2), data]. + expected = np.array([0, 1, 1]) + dims = (2, 2) + reference = None + for size in (0, 1, 2): + _, outcomes = _trajectory_density(program, qubits, dims, 4, seed=0, max_subsystem_size=size) + assert np.all(outcomes == expected), ( + f"size {size}: deterministic outcome {outcomes[0].tolist()} != {expected.tolist()}" + ) + if reference is None: + reference = outcomes + else: + assert np.array_equal(outcomes, reference) + +