Qrisp 0.10#
Warning
🚧🧪 IN DEVELOPMENT — NOT RELEASED YET 🧪🚧
This changelog covers an in-development version of Qrisp. It has not been released yet, and its contents are subject to change.
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_kernelturns a Qrisp function into a CUDA-Q kernel, executable withcudaq.runor, withexecution_mode="sample", withcudaq.sample.Hybrid control flow lowers natively to Quake, including mid-circuit measurement with feed-forward,
q_while_loop,q_condandq_switch.Kernels accept scalar (
int,float,bool) and array parameters viaFixedShapeNDArray, and can return multiple values.
sample() and expectation_value() now accept arbitrary return values Sampling kernels (the functions passed to
sample()andexpectation_value()) may now return classical values from mid-circuit measurements,QuantumVariables, or a mixture of both. Previously onlyQuantumVariablereturns 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 — useterminal_sampling=False(the default) for those cases.Backend-based sampling via
@backend_samplerThe newbackend_sampler()decorator routessample()andexpectation_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 throughjax.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, nestedjit/pjit).Raises
RuntimeErrorfor kernels containing real-time feedback (mid-circuit measurements whose outcomes control subsequent gates).Raises
RuntimeErrorwhen quantum operations are used without a surroundingsample()/expectation_value()call.Raises
ValueErrorfor a non-positive shot count — including a dynamic one, which only becomes concrete once the backend runs.
Improvements#
Cheaper tracing for
q_switchThe"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 abranch_amountthat is itself traced, still go throughq_cond.QiskitJobandAQTJobnow skip the live provider query and return the cached status once a job is done, cancelled, or errored. TheJobbase class docstring forstatus()/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(), andterminal_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-verifiedExamples, and added test coverage across theBigInteger/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 thejaspificationmodule (jaspify,simulate_jaspr,stimulate) (PR #827).Decorators
qache,custom_inversion,custom_control,RUS,auto_uncomputenow propagate the wrapped function’s docstring, name, and signature viafunctools.wraps, removing the need for manual docstring-copy workarounds at call sites. Functions decorated withRUScan now also be called with keyword arguments. (PR #803).Faster COLD/LCD circuit compilation and Hamiltonian construction
compile_U_coldandrun()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 newQubitOperator.sumclassmethod builds large QUBO Hamiltonians (create_COLD_instance,create_LCD_instance) in a single pass instead of via Python’s built-insum(), 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 upsignificant(),init_from(), andencode(..., rounding=True)(now O(1)) (PR #846).Qrisp now ships a
py.typedmarker (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).
Refactored quantum_variable.py by adding more typing, explicit attributes and delegating name generation to QuantumSession in a central place. (PR #882).
Faster resource estimation of modular multiplications in Jasp The classical constants of the multiplication of a
QuantumModulusby a classical factor are now computed by doubling moduloNand with machine integers, instead of a long division per partial product and aBigIntegerinverse modulo a power of two. The newQuantumModulus.inpl_multalso accepts the inverse of the factor moduloN, the most expensive classical step, when it is known in advance, as for the powers of Shor’s algorithm. The circuit is unchanged. For a controlled multiplication of a 512-bit register,count_opstakes 1.6 s instead of 2.2 s withqm *= X, and 0.04 s withqm.inpl_mult(X, inverse)(PR #918).Fewer qubits for
reflection:reflectionno longer bundles its operations into a single gate. The bundling kept every ancilla of thestate_functionallocated for the whole reflection, so compiled circuits could not reuse these qubits. Circuits built withamplitude_amplification,QAEor IQAE now need fewer qubits whenever the state function uses ancillas. Printed circuits show the individual operations instead of a singlereflectiongate. Jasp mode is unaffected (PR #947).
Other New Features#
Added an
RYYGatefor Ising-YY couplings to accompany the existingRXXGateandRZZGate(PR #797).Dicke state preparation via divide-and-conquer
dicke_state()now accepts amethodkeyword 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 exactlyk, whereas"deterministic"implements the full Dicke state unitary and therefore also accepts any input weightl <= k, preparingD(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).
Semiclassical order finding for Shor’s algorithm
semiclassical_order_finding()runs the quantum part of order finding in Jasp mode with a single control qubit, measured after every step of the phase estimation, instead of a phase register of2nqubits followed by the inverse QFT. The outcome has the same distribution, while the circuit needs2n - 1fewer qubits (3n + 2 ceil(log2 n) + 2with the default adder) and2nsingle-qubit rotations instead of the controlled phase gates of the inverse QFT. The resource estimators count the circuit without simulating it (PR #932).
Bug Fixes#
Fixed the lowering of unsigned integers by
Jaspr.to_mlirwithlower_stablehlo=True. An unsigned constant kept its unsigned value behind a signless result type, so the module failed verification and MLIR parsers rejected it. SinceQuantumModuluscomputes withuint64in Jasp mode (PR #918), this brokeQuantumModulusarithmetic in the CUDA-Q interface. Unsigned remainders, comparisons and widening conversions were also lowered to their signedarithcounterparts, which gives wrong results for values with the top bit set (Issue #1008).Fixed the handling of the Montgomery shift
mofQuantumModulusin Jasp mode (Issue #925).mis now a traced attribute. Previously it was stored as static data thatqacheignored, which caused three failures:A
qached function multiplying twoQuantumModulusraisedUnexpectedTracerError.A
qached function called with differing shifts silently reused the trace of the first call and returned wrong results.+=with differing shifts raisedTracerIntegerConversionError.
Addition and subtraction (
+,-,+=,-=) now support operands with differing shifts in both Jasp and static mode, and no longer raiseValueError. The other operand is converted into the target’s representation, and the result keeps the left operand’s shift. AQuantumFloatoperand with exponenteis treated as having shift-e, so its bit weights are taken into account. SignedQuantumFloatoperands now raiseTypeError. In Jasp mode, encoding a classical value into aQuantumModuluswith a negative shift (e.g. afterc = a * b; c += 1) no longer raisesValueError: negative shift count. (PR #958).Fixed
cuccaro_adderto support arbitrary quantum inputs, in particularlist[Qubit]andDynamicQubitArraytargets. Previously, a classical addend with alist[Qubit]target raisedAttributeError: 'list' object has no attribute 'duplicate', soQuantumModulus(..., inpl_adder=cuccaro_adder)failed. Classical addends larger than the target register are now truncated modulo2**len(b)(PR #848, issue #839).Fixed the
GQSP angle computationfor polynomials with complex coefficients. Unless \(|p(e^{i\theta})| = |p(e^{-i\theta})|\) for all \(\theta\), the angles implemented a different polynomial, soGQSP,GQET,GQSVTand other functions taking user-supplied coefficients applied the wrong transformation, andfourier_series_loaderprepared 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 andhamiltonian_simulationwere not affected (PR #953).Fixed a failure when a function decorated with
custom_inversionwas inverted twice, which raisedAutomatic 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 derivationcustom_inversionexists to avoid. This affected, for instance, inverting apreparewhose amplitudes are only known at run time, as produced by aBlockEncodingsimulation with traced coefficients.Fixed two issues in
q_switchaffecting branch lists of odd length. The padding branch the"tree"method appends now accepts every operand, so an odd branch list no longer raises aTypeErrorin 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 incomplex128precision, 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
dotfailed with aTypeError: 'QuantumArrayIterator' object is not iterable(PR #642).Updated Qiskit example in documentation to use
AerSimulatorinstead of the deprecatedAer.get_backend()API (PR #690).Fixed Cirq
FutureWarningby explicitly settinguse_repetition_ids=TrueinCircuitOperationcalls (PR #709).Fixed jasp-mode crashes when tracing a
while/scanloop with a single carried value, uncovered while removing duplicated interpreter code across four execution backends (PR #770).Fixed
QiskitBackendfailing withOverflowError: 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 asqiskit-iqm. Circuits are now submitted through the wrapped backend’s ownrun()method and counts are read viaResult.get_counts(), instead of going through Qiskit’sBackendSamplerV2primitive, which rebuilds counts from the hex-encodedmemoryfield.QiskitRuntimeBackendcontinues to useSamplerV2, which IBM Runtime requires. Passing a real IBM Quantum backend toQiskitBackendnow raises aTypeErrorpointing atQiskitRuntimeBackend; IBM fake backends are unaffected (PR #788).Fixed a bug where
preparewithmethod="qswitch"raised aValueErrorwhen used inside aninvertorcontrolenvironment in Jasp mode (PR #769).Fixed the custom assembly format of
jasp.create_qubitsin 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 fromdialect_definition/JaspOps.tdcould not parse the MLIR emitted byto_mlir(Issue #783).Fixed
jasp.quantum_gateandjasp.paritybeing 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 withOperation 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
qrisppackage. (PR #796).Fixed a bug where
QuantumModulusconstructed with a traced (Jasp-dynamic) modulus leaked a stale JAX tracer into subsequent, independentjaspify/make_jasprcalls, raisingjax.errors.UnexpectedTracerErroron the second and later calls. The modulus is now threaded through theQuantumVariablepytree 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_statefork = 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 tracedegcd,QuantumArraymatrix 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 fromnumpy’s fixed-widthgcd/lcmon large integers in Shor’s algorithm, a silent wrong-result bug when Montgomery-encoding mismatchedBigIntegerwidths, and a bareIndexErrorwhen 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 brokeruff format --checkonmainright after merge.Renamed the CD
agp_typeoption"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 aQrispDeprecationWarningand 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 unknownagp_typenow raises aValueErrorinstead of aKeyError. (PR #893).Fixed two AGP coefficient shape bugs in
create_LCD_instancewithagp_type="nc": theuniformand 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
QuantumFloatadd/sub with different exponents silently produced ajax.Arrayexponent instead of a plainintoutside tracing, crashing later negative2**exponentcalls (PR #846).Fixed a bug where the
catalyst_interpreterfailed to compile JAXPRs with constants, by passing the constants toeval_jaxpr(PR #750).Fixed
DCQOProblem.run’s COLD method withobjective="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 clearValueErrorforN_opt < 1or the defaultN_opt=None, instead of failing deep insiderange()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_paramsand the evolving circuit. (PR #893).Fixed the inverse scheduling function
g(lam)inDCQOProblem, which stored the dimensionful timetrather than the normalisedt/Tof Eq. (18) of COLD, and its derivative asdt/dlambdarather thandg/dlambda. Both feed the chain rule for the control-pulse derivative, so it ran at a frequencyTtimes too high with an amplitudeTtimes too large. The two errors cancel exactly atT = 1, at any otherTthe 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) inDCQOProblem.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_magnitudeobjective, which summed the AGP coefficients without weighting bylamdot. Since the coefficients diverge as1/lamdotthrough the control-pulse derivative while the circuit applies them asdt * lamdot * alpha, the sum was dominated by the first and last timestep and grew asN_steps**3. It also hardcodeduniform=Truewhen 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 aValueErrorfor an unrecognisedmethodinstead of failing with anUnboundLocalError, and theValueErrorrejecting an unknownobjectiveno 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.
his now \(-\frac{1}{2} \sum_j Q_{ij}\) and the full spectrum ofH_probreproduces \(x^T Q x\) up to a constant. The docstrings ofDCQOProblem,create_COLD_instance(),create_LCD_instance()andsolve_QUBO()now state thatQmust be symmetric, which both identities require.Fixed several bugs in the Jasp resource estimators count_ops, depth and num_qubits:
count_opsandnum_qubitsnow compute the size of a sliced qubit array with Python slicing semantics. Negative bounds were taken literally, so for examplex(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
TypeErrorincount_opsandnum_qubits.depthnow resolves negative qubit indices such asqv[-1]correctly for registers whose size is only known at run time.depthandnum_qubitsnow number the measurements consistently across loop iterations, branches and subroutine calls, ascount_opsdoes. With a randommeas_behavior, they reused the same outcome for every measurement of a loop, and could follow different branches thancount_opsfor the same program.
(PR #917).
The multiplication of a
QuantumModulusby a classical factor (qm *= Xandqm * X, andQuantumArray @ matrix) now chooses the Montgomery shift from the number of qubits of the register, as the Montgomery reduction requires, instead of from the value of the factor. For large factors the shift was about as large as the register, so a controlled multiplication of an n-qubit register used 3 to 4 times more T gates (for n between 32 and 512) and about 5n instead of 3n qubits. For integer moduli of 32 bits or more, computing the shift also overflowed, which gave wrong results.best_montgomery_shiftno longer overflows for traced moduli close to 2**63 (PR #918).The modular arithmetic helpers behind
QuantumModulusare exact for large integer moduli under tracing.pow2_mod_N,montgomery_encoderandmontgomery_decodermultiplied in int64, which overflowed for moduli above 2**31.5, so for example aQuantumModuluswith a non-zero Montgomery shift decoded wrong values.smallest_power_of_twoused a floating-point logarithm, which gave one bit too few from 2**49 on.qm * Xnow also accepts the same classical factors asqm *= X, including JAX integers (PR #918).In Jasp mode, the multiplication of a
QuantumModuluswith an integer modulus by a classical factor computed its partial products as(X << j) % Nin int64, which overflowed for moduli above about 33 bits and gave wrong results. The partial products are now computed by doubling moduloN, which is exact for moduli below 2**63 (PR #918).The multiplication of a
QuantumModulusby a classical factor reduces a factor with more bits than the modulus modulo it, instead of truncating it to the limbs of a BigInteger modulus (or to 64 bits under tracing). A BigInteger factor with a different number of limbs than the modulus is accepted, also with an integer modulus, and unsigned JAX factors are reduced in unsigned arithmetic. A JAX array that is not an integer scalar raises aTypeError(PR #918).IQPE applied the powers
U**(2**t), …,U**2of the unitary instead ofU**(2**(t - 1)), …,U, wheretis the precision. It therefore returned twice the phase modulo 1 and lost its most significant bit: a phase of 0.75 was estimated as 0.5. It now returns the phase, as documented, so code that relied on the previous values (twice the phase) needs to be updated (PR #932).IQPE released its control qubit after measuring it, without resetting it. Interpreters that reuse released qubits, such as the Catalyst interpreter, could therefore start the next step with the qubit in
|1>and give a wrong estimate. The control qubit is now reset before it is released (PR #932).Fixed simulation of composite operations in
statevector_array()andrun(): 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 intranspile()when transpiling circuits containing classical bits without qubits (PR #975).Fixed a bug in
resolve_naming_collisionswhere in the “qv_0 is newer” conditional branch,qv_1.is_fixed_name(calleduser_given_nameearlier) was checked when it should have beenqv_0’s. As a result, merging two sessions could rename a variable with an explicitly given (fixed) name. Now a fixed name is never renamed. (PR #882).Fixed a bug where calls of
duplicate()of the formduplicate(name="foo*")caused the variable being duplicated to change. There,self.is_fixed_name(calleduser_given_nameearlier) was set toFalse, instead of the duplicate’sis_fixed_name. This is fixed now. (PR #882).Fixed a bug where a QuantumVariable created at the top level of a plain Python script (e.g.
qv = QuantumVariable(2)) was not named after its Python variable, but received a generic name such asqv_0instead.find_calling_linepicked the wrong stack frame whenever the declaring line was the bottom of the call stack. Inside functions, pytest or Jupyter the inferred name was already correct; at module level, scripts now get the inferred name too, which changes the names shown e.g. in printed circuits. (PR #882).Fixed the name inferred for a QuantumVariable declared with a type annotation or through tuple unpacking.
x: QuantumFloat = QuantumFloat(3)was namedx:anda, b = QuantumFloat(2), QuantumFloat(2)named its first variablea,; they are now namedxandarespectively. (PR #882).Fixed a bug where deleting a QuantumVariable could remove a different variable with the same name, living in another QuantumSession, from
QuantumVariable.live_qvs. Names are only unique within a single QuantumSession, so variables are now matched by their creation time instead. Since theauto_uncomputedecorator useslive_qvsto find the variables a function created, it could previously miss such a variable. (PR #882).Fixed
reseton a single qubit, such asreset(qv[0]), failing to compile with qjit. Resetting a whole QuantumVariable was not affected (PR #941).Fixed the matrices of several standard gates and their conversions:
RGate(theta, phi)now implementsRZ(phi) RX(theta) RZ(-phi), matching the standard R gate. Its PennyLane conversion is nowqml.Rot(pi/2 - phi, theta, phi - pi/2).SXGateandSXDGGatenow carry the global phase±pi/4, so they are the exact square roots ofXinstead ofRX(±pi/2). This phase is also kept when they are controlled. They are now converted toqml.SXin PennyLane.U1Gatenow converts to Qiskit’sRZGateinstead ofPhaseGate, since Qrisp’sU1is an RZ rotation.
(PR #949).
Compatibility#
dicke_state()now raises aValueErrorfor an unrecognizedmethod, and for akoutside0 <= 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).QuantumFloatmethods now raise specific exception types (TypeError,ValueError,NotImplementedError) instead of a genericException. Code usingexcept Exception:is unaffected (PR #846).IQPE returns the phase instead of twice the phase modulo 1 (see Bug Fixes). It raises a
TypeErrorfor a precision that is not an integer and, unless the precision is traced, aValueErrorfor a precision above 63, for which the number of calls of the unitary overflowed a 64-bit integer (PR #932).RGateandQuantumCircuit.rare deprecated in favor of the identicalPRXGateand the newQuantumCircuit.prx. Both still work but emit aQrispDeprecationWarning.RGate(theta, phi)now builds aPRXGate(theta, phi), so the resulting operation is named"prx"instead of"r"and stores its angles asalphaandbeta.PRXGatecan now also be imported fromqrisp.circuit.standard_operations(PR #949).
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, andJasprMLIR/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#
IQMBackendis now a delegation shim that re-exportsIQMBackendfromiqm.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 helpfulImportErrorwhen theiqm-client[qrisp]package is not installed. (PR #757).Renamed five internal
qrisp.shorhelper functions to be private, and removed the unusedBigInteger.__sub_alt__method as none were part of the documented public API (PR #827).Renamed the Split & Cyclic Shift helper used by
dicke_state()fromsplit_cycle_shiftto_split_cycle_shift, marking it private. Its parameters were renamed fromhighIndex/lowIndexton/kto match the notation of arXiv:1904.07358. The unitary implemented is unchanged (PR #814).DCQOProblem.runnow defaults toobjective="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 ofexp_valueis prohibitive. The undocumented and never-implementedagp_coeff_amplitudeoption has been removed from the docstrings.create_COLD_instance()andsolve_QUBO()now acceptagp_typefor the COLD method, which previously only reached LCD, so the nested-commutator ansatz was unreachable from COLD. COLD requiresuniform_AGP_coeffs=Trueforagp_type="nc"and raisesNotImplementedErrorotherwise: 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.coldno longer exports the QUBO test fixturesQ3–Q10andsolution3–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 ofAGP_paramsandcold_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, andswap) no longer return the qubits/QuantumVariables they were applied to; they now consistently returnNone, 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_allocationsargument of num_qubits,Jaspr.num_qubitsandBlockEncoding.resourcesis deprecated and has no effect: passing it emits aQrispDeprecationWarning. Programs that exceeded the old limit no longer raiseValueError(PR #917).Removed
jasp_mod_adder. It was not used anywhere in Qrisp and duplicatedmod_adder, which also works in Jasp, needs no spare qubit in the target register and is cheaper when controlled. Replacejasp_mod_adder(a, b, N, inpl_adder=adder)withmod_adder(a, b, adder, N)(PR #965).Renamed the
QuantumVariableattributeuser_given_nametois_fixed_name. Code readingqv.user_given_namemust useqv.is_fixed_nameinstead (PR #882).QuantumVariable.creation_counteris now a plainintinstead of a one-element NumPy array. Code reading it asint(QuantumVariable.creation_counter[0])must useQuantumVariable.creation_counterinstead (PR #882).
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).GQSPindexes 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_opsandnum_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 intests/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-notesscript, 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 latestmainand 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
qrisppackage. (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
alloptional dependency group that installs the base package plus every other extra exceptaqt(qiskit,iqm,catalyst,xdsl,docs, anddev) and updated the Development Guide’s installation instructions to reference it (PR #807).Added a
reviewdog-based CI workflow that runsruffon 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 satisfyD205(PR #820). (PR #866).Extended the
ruffignore list inpyproject.tomlwith the docstring style rulesD209,D212,D401,D402,D404, andD416(relaxing pydocstyle conventions), plusPLC0415(function-level imports used to avoid circular imports) andE402(module-level imports placed after a module docstring) (PR #811).Added type hints across
BlockEncodingand theQubitOperator/Hamiltonianoperator algebra. This exposed two latent bugs:BlockEncoding’s constructor methods (from_lcu,from_operator, etc.) had theirclsparameter typed as an instance rather thantype[BlockEncoding], andHamiltonian’s abstract methods were typed as returningNone, breaking every subclass override (PR #817).Consolidated the
ruffreviewdog.ymlandruff_checks.ymlworkflows into a singlecode_style.yml, with theruff format --checkgate now running on both pull requests and pushes tomain(PR #836).Added a
docs buildworkflow 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 duplicateAttributessection, a document listed in two toctrees, thepygment_light_styletheme option (renamed topygments_light_style), andstimbeing unavailable in the docs environment (now mocked viaautodoc_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 theiqmoptional dependency group. (PR #757).Bumped
actions/setup-pythonfrom 6 to 7 (PR #760).Pinned
ruffto0.15.18in thedev-code-styledependency group and updated thereviewdogCI workflow to install the version specified inpyproject.toml(PR #819).Bumped
rufffrom0.15.18to0.16.4. On0.15.x,ruff format --check --output-format=rdjsonsilently required--previewmode just to emit structured output at all — an unrelated concern from preview formatting rules, which the project does not use — causing thereviewdogformatter check to crash.0.16.0stabilized structured output formats for the formatter, fixing this without changing which formatting rules apply (PR #820).Removed unused documentation dependencies:
sphinx-toolbox,myst-parserandsphinx-new-tab-link, along with thesphinx.ext.coverage,sphinx.ext.todoandsphinx_toolbox.sidebar_linksSphinx extensions (PR #961).Pinned
qiskitto<2.6. With qiskit 2.6.0 on Linux, importingqiskit_aer,pyscfor PennyLane Lightning afterqrispfails withcannot allocate memory in static TLS block(PR #976).Required
iqm-client[qrisp]>=35.0.0in theiqmanddev-testingdependency groups. Older releases do not provide theqrispextra, so pip could fall back to one of them and install no IQM backend. This happened on Python 3.13, where Sphinx 9.2.0 requirespackaging>=25while recentiqm-clientreleases requirepackaging<25, andpip install ".[all]"resolved toiqm-client15.3 (PR #995).