Qrisp 0.10#

Qrisp 0.10 continues to push the boundaries of high-level quantum programming. This release brings a streamlined contribution experience with automated changelog enforcement.

New Features#

  • CUDA-Q interface. Qrisp programs can now be compiled to and run on CUDA-Q, NVIDIA’s platform for hybrid quantum-classical computing. The interface ships as the separate package qrisp-cudaq; install it with pip install "qrisp[cudaq]" (PR #943).

    • cudaq_kernel turns a Qrisp function into a CUDA-Q kernel, executable with cudaq.run or, with execution_mode="sample", with cudaq.sample.

    • Hybrid control flow lowers natively to Quake, including mid-circuit measurement with feed-forward, q_while_loop, q_cond and q_switch.

    • Kernels accept scalar (int, float, bool) and array parameters via FixedShapeNDArray, and can return multiple values.

  • sample() and expectation_value() now accept arbitrary return values Sampling kernels (the functions passed to sample() and expectation_value()) may now return classical values from mid-circuit measurements, QuantumVariables, or a mixture of both. Previously only QuantumVariable returns were supported. QuantumVariables in the return are automatically measured and decoded; classical values are interleaved in-place.

    Terminal sampling (decorator and Japify with terminal_sampling=True) rejects kernels that return classical values with a descriptive error — use terminal_sampling=False (the default) for those cases.

  • Backend-based sampling via @backend_sampler The new backend_sampler() decorator routes sample() and expectation_value() calls through a real quantum backend instead of the Jaspify simulator. The quantum circuit is extracted once, executed on the backend for all shots, and the classical post-processing (decoding, accumulator updates, expectation-value computation) is replayed via the Jaspr’s own while-loop — compiled through jax.jit().

    Key capabilities:

    • Supports any backend implementing the Backend Interface.

    • Handles multiple sample() / expectation_value() calls in the same decorated function, each independently routed.

    • Propagates the backend interception through JAX control-flow primitives (fori_loop, while_loop, cond, scan, nested jit / pjit).

    • Raises RuntimeError for kernels containing real-time feedback (mid-circuit measurements whose outcomes control subsequent gates).

    • Raises RuntimeError when quantum operations are used without a surrounding sample() / expectation_value() call.

    • Raises ValueError for a non-positive shot count — including a dynamic one, which only becomes concrete once the backend runs.

Improvements#

  • Cheaper tracing for q_switch The "tree" method resolves conditionals whose predicate is already known at trace time instead of tracing both arms and discarding one. The emitted circuit is unchanged; the traced program is roughly half the size and compiles about twice as fast for larger switches (2807 to 1308 equations and 1.71 s to 0.77 s for 16 branches). Predicates that genuinely depend on run-time values, such as a branch_amount that is itself traced, still go through q_cond.

  • QiskitJob and AQTJob now skip the live provider query and return the cached status once a job is done, cancelled, or errored. The Job base class docstring for status()/refresh() now consistently describes this as an optional optimization implementations may take advantage of, rather than a guarantee (PR #806).

  • Updated docstrings for sample(), expectation_value(), and terminal_sampling() to use “sampling kernel” terminology and document the new arbitrary-return-value capability.

  • Sped up BigInteger.__lshift__/__rshift__ (~3x faster at 2048-bit width), added doctest-verified Examples, and added test coverage across the BigInteger/Jasp-Montgomery backend and Shor’s algorithm/RSA, previously untested (PR #827).

  • Expanded the count_ops (Jasp resource-estimation) test suite with small, exact-value edge-case tests, and improved the docstrings of the jaspification module (jaspify, simulate_jaspr, stimulate) (PR #827).

  • Decorators qache, custom_inversion, custom_control, RUS, auto_uncompute now propagate the wrapped function’s docstring, name, and signature via functools.wraps, removing the need for manual docstring-copy workarounds at call sites. Functions decorated with RUS can now also be called with keyword arguments. (PR #803).

  • Faster COLD/LCD circuit compilation and Hamiltonian construction compile_U_cold and run() no longer recompute Trotter term grouping on every timestep, and Ising-type Hamiltonians (identity, single-qubit Pauli, or \(Z \otimes Z\) terms only — the case for every built-in QUBO Hamiltonian) are now Trotterized via a new native-gate fast path (fast_trotterization), falling back automatically for any other operator. Separately, a new QubitOperator.sum classmethod builds large QUBO Hamiltonians (create_COLD_instance, create_LCD_instance) in a single pass instead of via Python’s built-in sum(), which no longer costs \(\mathcal{O}(N^4)\) for a dense \(N\)-qubit QUBO.

  • Improved the simulator’s circuit preprocessing: circuit reordering is faster, and gate grouping for circuits with 63+ qubits now stays on the fast Numba-jitted path (via chunked qubit bitmasks) instead of falling back to a slower, non-jitted implementation. All functions and classes used for simulator preprocessing are strictly internal and marked with a leading underscore. (PR #704)

  • Added type hints across QuantumFloat, fixed stale docstring examples, and sped up significant(), init_from(), and encode(..., rounding=True) (now O(1)) (PR #846).

  • Qrisp now ships a py.typed marker (PEP 561), so type checkers such as mypy and pyright use Qrisp’s inline type annotations when checking code that imports Qrisp, instead of treating the package as untyped (PR #656).

  • The num_qubits resource estimator no longer limits the number of qubit allocations and deallocations. It now keeps four running counters instead of recording every event in a fixed-size buffer, so large programs with millions of allocations no longer overflow, and the result is no longer replayed event by event in Python. The returned dictionary is unchanged (PR #917).

  • Fewer qubits for reflection: reflection no longer bundles its operations into a single gate. The bundling kept every ancilla of the state_function allocated for the whole reflection, so compiled circuits could not reuse these qubits. Circuits built with amplitude_amplification, QAE or IQAE now need fewer qubits whenever the state function uses ancillas. Printed circuits show the individual operations instead of a single reflection gate. Jasp mode is unaffected (PR #947).

Other New Features#

  • Added an RYYGate for Ising-YY couplings to accompany the existing RXXGate and RZZGate (PR #797).

  • Dicke state preparation via divide-and-conquer dicke_state() now accepts a method keyword argument. In addition to the existing "deterministic" method (arXiv:1904.07358), the new "divide-and-conquer" method (arXiv:2112.12435) prepares the two halves of the variable on disjoint qubits, roughly halving the circuit depth. It requires the input to have Hamming weight exactly k, whereas "deterministic" implements the full Dicke state unitary and therefore also accepts any input weight l <= k, preparing D(n, l). The default is "deterministic", so existing code is unaffected (PR #767).

  • Added an AI policy note to the issue templates All issue templates now state that the project does not accept AI-generated pull requests and that automated agents should not submit PRs or post comments. Human contributors may use LLMs as an aid, provided they fully understand and take responsibility for the changes implemented (PR #816).

Bug Fixes#

  • Fixed the GQSP angle computation for polynomials with complex coefficients. Unless \(|p(e^{i\theta})| = |p(e^{-i\theta})|\) for all \(\theta\), the angles implemented a different polynomial, so GQSP, GQET, GQSVT and other functions taking user-supplied coefficients applied the wrong transformation, and fourier_series_loader prepared inaccurate states for signals such as an off-centre Gaussian. For example, \(p(z) = (1+iz)/2\) applied to \(U = \mathrm{diag}(1, i)\) now correctly suppresses the eigenvalue \(i\). Real coefficients and hamiltonian_simulation were not affected (PR #953).

  • Fixed a failure when a function decorated with custom_inversion was inverted twice, which raised Automatic loop inversion is only supported for jrange-based loops. An inverted Jaspr keeps a back-pointer to the Jaspr it inverts, and the second inversion follows it instead of deriving an inverse. Inverting can reclassify arguments as constants, and folding them back rewrapped the Jaspr without carrying the back-pointer over, so the second inversion fell back to inverting the body, which is the derivation custom_inversion exists to avoid. This affected, for instance, inverting a prepare whose amplitudes are only known at run time, as produced by a BlockEncoding simulation with traced coefficients.

  • Fixed two issues in q_switch affecting branch lists of odd length. The padding branch the "tree" method appends now accepts every operand, so an odd branch list no longer raises a TypeError in Jasp mode when more than one operand is passed, and the padding is appended to a copy rather than to the caller’s list.

  • Fixed the precision of get_unitary. Unitary matrices are now computed in complex128 precision, removing the spurious ~1e-7 off-diagonal entries that previously appeared where a unitary should vanish exactly (e.g. phase-tolerant controlled gates). (PR #787).

  • Fixed a bug where dot failed with a TypeError: 'QuantumArrayIterator' object is not iterable (PR #642).

  • Updated Qiskit example in documentation to use AerSimulator instead of the deprecated Aer.get_backend() API (PR #690).

  • Fixed Cirq FutureWarning by explicitly setting use_repetition_ids=True in CircuitOperation calls (PR #709).

  • Fixed jasp-mode crashes when tracing a while/scan loop with a single carried value, uncovered while removing duplicated interpreter code across four execution backends (PR #770).

  • Fixed QiskitBackend failing with OverflowError: int too big to convert — or, for small classical registers, silently returning wrong counts — on providers that report measurement results as binary rather than hexadecimal strings, such as qiskit-iqm. Circuits are now submitted through the wrapped backend’s own run() method and counts are read via Result.get_counts(), instead of going through Qiskit’s BackendSamplerV2 primitive, which rebuilds counts from the hex-encoded memory field. QiskitRuntimeBackend continues to use SamplerV2, which IBM Runtime requires. Passing a real IBM Quantum backend to QiskitBackend now raises a TypeError pointing at QiskitRuntimeBackend; IBM fake backends are unaffected (PR #788).

  • Fixed a bug where prepare with method="qswitch" raised a ValueError when used inside an invert or control environment in Jasp mode (PR #769).

  • Fixed the custom assembly format of jasp.create_qubits in the TableGen definition of the Jasp dialect, which still listed the operand types in the order !jasp.QuantumState, tensor<i64> while Qrisp prints them in operand order, tensor<i64>, !jasp.QuantumState. MLIR-based consumers that build a parser from dialect_definition/JaspOps.td could not parse the MLIR emitted by to_mlir (Issue #783).

  • Fixed jasp.quantum_gate and jasp.parity being printed in a syntax that could not be parsed back: both had a hand-written printer and no matching parser, so re-reading a printed module failed with Operation jasp.quantum_gate does not have a custom format. Both now use the same declarative assembly format as the remaining operations, which also removes the stray whitespace around ( and , those printers emitted and stops attributes from being dropped.

  • Removed reduant imports in the top-level qrisp package. (PR #796).

  • Fixed a bug where QuantumModulus constructed with a traced (Jasp-dynamic) modulus leaked a stale JAX tracer into subsequent, independent jaspify/make_jaspr calls, raising jax.errors.UnexpectedTracerError on the second and later calls. The modulus is now threaded through the QuantumVariable pytree as a proper traced attribute instead of being passed into static auxiliary data (PR #802).

  • Updated broken link in TSP tutorial to point to the correct archived Qiskit textbook. (PR #804).

  • Fixed dicke_state for k = 0, which emitted a ladder of identity-acting Split & Cyclic Shift blocks and traced a negative-length loop range under Jasp (PR #767).

  • Fixed several crashes and a correctness bug in the BigInteger/Shor’s algorithm backend: continued-fraction period recovery, mixed-type Montgomery encode/decode, a broken traced egcd, QuantumArray matrix multiplication against a classical matrix, and order-finding verifying candidates against a mutated value instead of the original base (which also masked an exponential-time candidate-combination fallback) (PR #827).

  • Fixed OverflowErrors from numpy’s fixed-width gcd/lcm on large integers in Shor’s algorithm, a silent wrong-result bug when Montgomery-encoding mismatched BigInteger widths, and a bare IndexError when order-finding exhausts all measurement outcomes (PR #827).

  • Fix a code typo in the Jasp tutorial which printed the wrong variable when checking which variables are dynamic. (PR #828).

  • Removed a stray double blank line in QubitOperator.simulate, left behind by an import-hoisting cleanup, which broke ruff format --check on main right after merge.

  • Renamed the CD agp_type option "order1" (a sum of single-qubit Y operators) to "local", to avoid confusion with the first-order nested commutator ansatz "nc". "order1" still works but emits a QrispDeprecationWarning and will be removed in version 0.11. The "order2" option listed in the old docstrings never existed; it has been removed from the docs, and an unknown agp_type now raises a ValueError instead of a KeyError. (PR #893).

  • Fixed two AGP coefficient shape bugs in create_LCD_instance with agp_type="nc": the uniform and non-uniform coefficient builders each wrapped their result one list level too deep, handing a whole per-qubit array where a single coefficient was expected.

  • Fixed a bug where QuantumFloat add/sub with different exponents silently produced a jax.Array exponent instead of a plain int outside tracing, crashing later negative 2**exponent calls (PR #846).

  • Fixed a bug where the catalyst_interpreter failed to compile JAXPRs with constants, by passing the constants to eval_jaxpr (PR #750).

  • Fixed DCQOProblem.run’s COLD method with objective="exp_value", which could be dramatically slower and converge to worse results than in qrisp 0.8: an unused exponential-size cost table was built on every call, the fast statevector path used the wrong cost function, and the optimization-pulse ansatz divided by zero for scheduling functions with vanishing derivative at the domain endpoints.

  • DCQOProblem.run’s COLD method now raises a clear ValueError for N_opt < 1 or the default N_opt=None, instead of failing deep inside range() or SciPy with confusing, inconsistent error messages (#877).

  • Corrected the adiabatic gauge potential (AGP) coefficients used by COLD and LCD. Every closed-form expression deviated from the exact minimizer of the action \(S = \mathrm{Tr}[G_\lambda^2]\), with \(G_\lambda = \partial_\lambda H + i[A_\lambda, H]\) and the AGP \(A_\lambda\), through a mix of wrong exponents, sums used where sums of squares belong, a dropped sign, and a sign convention mismatch between AGP_params and the evolving circuit. (PR #893).

  • Fixed the inverse scheduling function g(lam) in DCQOProblem, which stored the dimensionful time t rather than the normalised t/T of Eq. (18) of COLD, and its derivative as dt/dlambda rather than dg/dlambda. Both feed the chain rule for the control-pulse derivative, so it ran at a frequency T times too high with an amplitude T times too large. The two errors cancel exactly at T = 1, at any other T the AGP drive alternated sign between timesteps. (PR #893).

  • The COLD control pulses now use the basis \(\sin(2\pi k\, g(\lambda))\), instead of \(\sin(\pi k\, g(\lambda))\), and COLD-CRAB randomizes the frequencies as \(k \to k(1 + r_k)\) instead of \(k \to k + r_k\). Optimized parameters from earlier versions do not carry over. (PR #893).

  • Fixed COLD-CRAB (CRAB=True) in DCQOProblem.run. The random frequencies \(r_k\) were redrawn on every call, and the final circuit was built without them. (PR #893).

  • Fixed the agp_coeff_magnitude objective, which summed the AGP coefficients without weighting by lamdot. Since the coefficients diverge as 1/lamdot through the control-pulse derivative while the circuit applies them as dt * lamdot * alpha, the sum was dominated by the first and last timestep and grew as N_steps**3. It also hardcoded uniform=True when scoring, so a non-uniform problem was scored with uniform coefficients. The objective still scores a generic second-order AGP template, not the operators the circuit applies, and is now documented as experimental. (PR #893).

  • solve_QUBO() now raises a ValueError for an unrecognised method instead of failing with an UnboundLocalError, and the ValueError rejecting an unknown objective no longer prints a literal {objective} from a missing f-string prefix. (PR #893).

  • Corrected the local fields of the QUBO-to-Ising conversion used by COLD and LCD. \(h_i\) was computed as \(-\frac{1}{2} Q_{ii} - \frac{1}{2} \sum_j Q_{ij}\), but the row sum already contains \(Q_{ii}\), so the diagonal was counted twice and the problem Hamiltonian encoded \(x^T Q x + \mathrm{diag}(Q) \cdot x\). Its ground state was not the QUBO optimum for roughly a third of random instances. h is now \(-\frac{1}{2} \sum_j Q_{ij}\) and the full spectrum of H_prob reproduces \(x^T Q x\) up to a constant. The docstrings of DCQOProblem, create_COLD_instance(), create_LCD_instance() and solve_QUBO() now state that Q must be symmetric, which both identities require.

  • Fixed several bugs in the Jasp resource estimators count_ops, depth and num_qubits:

    • count_ops and num_qubits now compute the size of a sliced qubit array with Python slicing semantics. Negative bounds were taken literally, so for example x(qv[:-1]) counted no gates, and qubits allocated with the size of such a slice were miscounted.

    • Fusing a qubit array with a single qubit no longer raises a TypeError in count_ops and num_qubits.

    • depth now resolves negative qubit indices such as qv[-1] correctly for registers whose size is only known at run time.

    • depth and num_qubits now number the measurements consistently across loop iterations, branches and subroutine calls, as count_ops does. With a random meas_behavior, they reused the same outcome for every measurement of a loop, and could follow different branches than count_ops for the same program.

    (PR #917).

  • Fixed simulation of composite operations in statevector_array() and run(): circuits now transpile composite operations before simulation to avoid computing wide unitary matrices, while preserving narrow controlled operations (such as Toffoli and MCX gates within the maximum gate group size) so they retain their closed-form unitaries for fast simulation. Also fixed a bug in transpile() when transpiling circuits containing classical bits without qubits (PR #975).

Compatibility#

  • dicke_state() now raises a ValueError for an unrecognized method, and for a k outside 0 <= k <= len(qv) when both are plain Python integers (i.e. outside of Jasp tracing). The latter previously produced an incorrect state silently (PR #767).

  • QuantumFloat methods now raise specific exception types (TypeError, ValueError, NotImplementedError) instead of a generic Exception. Code using except Exception: is unaffected (PR #846).

New Tutorials/ Updated Documentation#

  • CUDA-Q tutorial - Compiling and running Qrisp kernels on NVIDIA’s CUDA-Q platform, from a Bell-state example to hybrid quantum-classical workflows (PR #943).

  • Fixed outdated or inaccurate docstrings and examples across the Jasp module (control flow, sampling, simulators, optimization tools, BigInteger, and Jaspr MLIR/QIR export) (PR #805).

  • Added a Community Day page announcing the first Eclipse Qrisp Community Day (Berlin, October 29th, 2026) with registration link and agenda.

  • Fixed the installation verification command in the getting started documentation (PR #935).

API Changes#

  • IQMBackend is now a delegation shim that re-exports IQMBackend from iqm.qrisp_iqm (IQM client). The backend implementation and its tests live in the IQM client repository. The Qrisp-side module provides a placeholder with a helpful ImportError when the iqm-client[qrisp] package is not installed. (PR #757).

  • Renamed five internal qrisp.shor helper functions to be private, and removed the unused BigInteger.__sub_alt__ method as none were part of the documented public API (PR #827).

  • Renamed the Split & Cyclic Shift helper used by dicke_state() from split_cycle_shift to _split_cycle_shift, marking it private. Its parameters were renamed from highIndex/lowIndex to n/k to match the notation of arXiv:1904.07358. The unitary implemented is unchanged (PR #814).

  • DCQOProblem.run now defaults to objective="exp_value" instead of "agp_coeff_magnitude". The latter is an experimental proxy that does not run the circuit and can select controls that perform worse than no control at all. It remains available as an opt-in for cases where the simulation cost of exp_value is prohibitive. The undocumented and never-implemented agp_coeff_amplitude option has been removed from the docstrings.

  • create_COLD_instance() and solve_QUBO() now accept agp_type for the COLD method, which previously only reached LCD, so the nested-commutator ansatz was unreachable from COLD. COLD requires uniform_AGP_coeffs=True for agp_type="nc" and raises NotImplementedError otherwise: the non-uniform coefficients have no closed form and their solver cannot consume the symbolic control pulse COLD compiles into the circuit. LCD still supports the combination.

  • qrisp.algorithms.cold no longer exports the QUBO test fixtures Q3–Q10 and solution3–solution10. These were leaked into the public namespace by a star import and were consumed only by the test suite; they now live alongside it. The helpers of AGP_params and cold_benchmark (e.g. solve_alpha, approx_ratio) are now private (leading underscore).

  • Gate-application functions (cx, cy, cz, h, x, y, z, mcx, mcz, mcp, p, cp, rx, ry, rz, crz, s, t, s_dg, t_dg, sx, sx_dg, gphase, xxyy, rzz, rxx, ryy, u3, barrier, and swap) no longer return the qubits/QuantumVariables they were applied to; they now consistently return None, reflecting that they act by side effect. Code relying on the previous return values (e.g. result = h(qv)) must use the original argument instead.

  • The max_allocations argument of num_qubits, Jaspr.num_qubits and BlockEncoding.resources is deprecated and has no effect: passing it emits a QrispDeprecationWarning. Programs that exceeded the old limit no longer raise ValueError (PR #917).

  • Removed jasp_mod_adder. It was not used anywhere in Qrisp and duplicated mod_adder, which also works in Jasp, needs no spare qubit in the target register and is cheaper when controlled. Replace jasp_mod_adder(a, b, N, inpl_adder=adder) with mod_adder(a, b, adder, N) (PR #965).

Development#

  • Added jaspr_to_static_register_jaspr, which rewrites a Jaspr so that all qubits come from a single register of fixed size allocated at program start. The CUDA-Q backend in qrisp-cudaq builds on it (PR #937).

  • GQSP indexes its angle arrays from the end instead of reversing them first, so the traced program contains no reversing slice, which the CUDA-Q lowering does not support. The emitted circuit is unchanged (PR #938).

  • Refactored the Jasp resource estimators: the slice, fuse and register handlers of count_ops and num_qubits, the measurement sampling and the parity handler now have a single implementation shared by the metrics, and the metric modules are clean under ruff and pyright. The tests of resource estimation with Jasp now live in tests/jax_tests/resource_estimation_tests, which includes tests on textbook circuits (GHZ, Grover, Toffoli, Bernstein-Vazirani, teleportation, repetition code) whose resources are known in closed form (PR #917).

  • Added Dependabot configuration for automated dependency updates (grouped by type, with labels applied automatically).

  • Added a changelog reminder workflow, the make release-notes script, a developer changelog (changelog-dev.rst), a release guide, fixed the workflow permissions to allow posting PR comments, and skipped the check for Dependabot PRs. The check now compares against the pull request’s head commit instead of GitHub’s synthetic merge commit, which also contains the latest main and therefore made other pull requests’ changelog entries count as the current pull request’s (PR #658, PR #715, PR #727, PR #983).

  • Added pip dependency caching to the CI test workflow (PR #685).

  • Added pytest coverage reporting to the CI test workflow and selective coverage reporting for the qrisp package. (PR #712, PR #774).

  • Added StepSecurity Harden-Runner to the CI test workflow and pinned GitHub Actions version to a full-length commit SHA. (PR #531).

  • Performed a large-scale refactoring of the jasp (JAX-tracing) interpreter subsystem, consolidating control-flow, equation-copying, and caching logic that had been independently duplicated across the Catalyst, classical-simulation, profiling, and post-processing backends into shared helper functions (PR #770).

  • Added an all optional dependency group that installs the base package plus every other extra except aqt (qiskit, iqm, catalyst, xdsl, docs, and dev) and updated the Development Guide’s installation instructions to reference it (PR #807).

  • Added a reviewdog-based CI workflow that runs ruff on pull requests and surfaces lint findings as annotations on the GitHub Checks tab of newly added lines instead of as inline review comments on the PR (PR #639, PR #812).

  • Converted every source file’s Eclipse Public License header from a module-level docstring into a #-prefixed comment block, and gave every file that lacked one a real one-line module docstring. The header had been written as a docstring, which pydocstyle interpreted as the module’s documentation and flagged for style violations (D205, D212, …) on essentially every file; it was never meant to be read as documentation. Existing rich module-level documentation was preserved verbatim, only reformatted to satisfy D205 (PR #820). (PR #866).

  • Extended the ruff ignore list in pyproject.toml with the docstring style rules D209, D212, D401, D402, D404, and D416 (relaxing pydocstyle conventions), plus PLC0415 (function-level imports used to avoid circular imports) and E402 (module-level imports placed after a module docstring) (PR #811).

  • Added type hints across BlockEncoding and the QubitOperator/ Hamiltonian operator algebra. This exposed two latent bugs: BlockEncoding’s constructor methods (from_lcu, from_operator, etc.) had their cls parameter typed as an instance rather than type[BlockEncoding], and Hamiltonian’s abstract methods were typed as returning None, breaking every subclass override (PR #817).

  • Consolidated the ruff reviewdog.yml and ruff_checks.yml workflows into a single code_style.yml, with the ruff format --check gate now running on both pull requests and pushes to main (PR #836).

  • Added a docs build workflow that builds the Sphinx documentation on pull requests and treats any warning as an error. Building it fresh surfaced and fixed several issues: numpydoc-unknown docstring sections (Properties, Decompositions, Example), a duplicate Attributes section, a document listed in two toctrees, the pygment_light_style theme option (renamed to pygments_light_style), and stim being unavailable in the docs environment (now mocked via autodoc_mock_imports) (PR #915).

  • GitHub Actions pinned to commit SHA in CI workflows (PR #829).

  • Added Python 3.13 support to the CI pipeline according to (Issue #823) (PR #847).

  • Removed the CodeFactor status badge from the README. It was frequently broken due to upstream rate limiting and its AI-review functionality is already covered by other tooling (PR #920).

  • Removed the duplicate PyPI badge from the README, keeping a single version badge linked to the PyPI project page (PR #921).

Dependency Upgrades#

  • Bumped myst-parser from 5.0.0 to 5.1.0 (PR #729).

  • Bumped ipykernel from 7.2.0 to 7.3.0 (PR #734).

  • Bumped pytest from 9.1.0 to 9.1.1 (PR #723).

  • Replaced pinned IQM dependencies with iqm-client[qrisp] in the iqm optional dependency group. (PR #757).

  • Bumped actions/setup-python from 6 to 7 (PR #760).

  • Pinned ruff to 0.15.18 in the dev-code-style dependency group and updated the reviewdog CI workflow to install the version specified in pyproject.toml (PR #819).

  • Bumped ruff from 0.15.18 to 0.16.4. On 0.15.x, ruff format --check --output-format=rdjson silently required --preview mode just to emit structured output at all — an unrelated concern from preview formatting rules, which the project does not use — causing the reviewdog formatter check to crash. 0.16.0 stabilized structured output formats for the formatter, fixing this without changing which formatting rules apply (PR #820).

  • Removed unused documentation dependencies: sphinx-toolbox, myst-parser and sphinx-new-tab-link, along with the sphinx.ext.coverage, sphinx.ext.todo and sphinx_toolbox.sidebar_links Sphinx extensions (PR #961).

  • Pinned qiskit to <2.6. With qiskit 2.6.0 on Linux, importing qiskit_aer, pyscf or PennyLane Lightning after qrisp fails with cannot allocate memory in static TLS block (PR #976).

First Time Contributors 🎉#