Skip to main content
IBM Quantum Platform

Qiskit SDK 2.5 release notes


2.5.0

Prelude

Qiskit v2.5.0 is a new feature release of the Qiskit SDK. The highlights of this release are:

  • Expanded C API and increased access. The C API contains many new functions and types (see the “C API Features” section below), most notably ones to inspect control-flow and classical exprsesions. qiskit.capi also gained ctypes bindings to the C API, for convenient debug access.
  • Initial multi-IR staged pass-manager support. A new MultiStagePassManager allows compilations that progressively lower through multiple IRs. Future releases will expand Qiskit’s multi-IR capabilities.
  • Preset pass-managers for Clifford+T and PBC. Two new functions, generate_preset_clifford_t_pass_manager() and generate_preset_pbc_pass_manager(), provide pre-configured pipelines for targetting Clifford+T and Pauli-based computation (PBC) bases, respectively. This is an early step towards more powerful fault-tolerant compilation in Qiskit.
  • New default two-qubit transpiler optimizations. Qiskit’s default two-qubit resynthesis optimization (enabled at levels 2 and 3 in the preset pipelines) now uses a new TwoQubitPeepholeOptimization pass. Compared to the previous combination of ConsolidateBlocks and UnitarySynthesis, this is more performant and better at selecting the highest-fidelity decomposition.
  • Transpiler performance improvements. Speed-ups of 2-3x across benchpress circuits are typical. This stems from several transpiler passes gaining multithreading suport, and many other improvements such as the new two-qubit synthesis above, algorithmic improvements in SabreLayout, optimizations in rustworkx, and more efficient hashing, allocation and linear-algebra routines.

C API Features

Circuits Features

  • Specialized LinearFunction.inverse() so that it returns a LinearFunction representing the inverse transformation, instead of failing to construct an opaque Instruction.

  • QuantumCircuit.for_loop() now accepts an expr.Var of types.Uint type as the loop_parameter argument. When used in context-manager form, the loop variable is automatically declared as an input variable of the loop body and is available for use within the loop scope. For example:

    from qiskit.circuit import QuantumCircuit, ClassicalRegister
    from qiskit.circuit.classical import expr, types
    
    qc = QuantumCircuit(1, 1)
    cr = ClassicalRegister(5, "reps")
    qc.add_register(cr)
    
    with qc.for_loop(range(5), expr.Var.new("a", types.Uint(32))) as v:
        qc.measure(0, 0)
        qc.store(expr.index(cr, v), qc.clbits[0])
  • Added QuantumCircuit.estimate_fidelity(), which is used to estimate the fidelity of running a circuit on a given Target.

  • ClassicalRegister objects with more than 65,535 bits can now be used with the qiskit.circuit.classical expression system.

  • ParameterVector now accepts an optional uuid constructor argument, and exposes its base UUID through its uuid attribute. This can be used to reconstruct vectors whose elements compare equal.

Quantum Information Features

Transpiler Features

Visualization Features

Transpiler Upgrade Notes

  • The transpile() function now uses a different, more efficient compiler workflow when compiling to a Clifford+T basis. In this workflow, it no longer uses the generic unitary synthesis plugins to synthesize single-qubit unitaries into Clifford+T sequences. Newly, it relies on compiling to RZR_Z gates and directly synthesizing these, which allows for better quality and runtime performance. As the result, the "unitary_synthesis_method" and "unitary_synthesis_plugin_config" arguments no longer affect Clifford+T synthesis in transpile(). Instead, the accuracy can either be set globally by setting the approximation_degree, or by using generate_preset_clifford_t_pass_manager() which exposes rz_synthesis_config argument to pass a configuration for the RZR_Z synthesis.

Miscellaneous Upgrade Notes

  • The minimum required version of NumPy is now 2.0, consistent with SPEC 0 recommendations.

  • Qiskit now has a formal dependency-version policy, tracked in the repository’s contributing documentation. This is:

    • NumPy and SciPy follow the scientific Python community’s SPEC 0 (two years of support);
    • All other Python and Rust dependencies have no constraints beyond platform support;
    • Optional, build-only, and developer-only Python dependencies have no constraints, and do not need to support all runtime versions of Python or host platforms.

Circuits Deprecations

  • The CommutationChecker no longer maintains an internal cache of commutation relationships. The following items are therefore now deprecated, and will be removed in Qiskit v3.0:

Synthesis Deprecations

  • Several behaviors of arguments of qs_decomposition() are now deprecated and will be removed in Qiskit v3.0.

    • Setting opt_a1 and opt_a2 is no longer necessary; the function automatically determines whether the optimizations can apply.
    • decomposer_1q and decomposer_2q should be instances of OneQubitEulerDecomposer or TwoQubitBasisDecomposer, respectively; using arbitrary Python callables is non-performant compared to the Rust core.

Build System Changes

  • The in-repository bindings-generation tool was renamed from qiskit-bindgen-c to qiskit-bindgen-cli. The preferred Makefile recipe make c is unaffected.

  • qiskit-bindgen-cli gained a check-abi command, which can check that two slots listings (either from qiskit-bindgen-cli show-slots or compiled into qiskit-cext-vtable) are semver-compatible for the relevant version numbers.

  • qiskit-bindgen-cli gained the show-slots command, which outputs a list of the slot assignments for runtime linking to the C API (such as via the Python-space Qiskit package).

  • The minimum version of the build dependency setuptools-rust was bumped to 1.13, to use the new Extension(generated_files=...) argument.

  • Qiskit can now be optionally built using mimalloc as the global allocator. To opt-in to using mimalloc, set the environment variable QISKIT_BUILD_WITH_MIMALLOC=1. This option can be used when either building the Python package or when using the Makefile (make c) to build a standalone shared library.

  • The minimum supported Rust version (MSRV) for building Qiskit from source has been increased from 1.85 to 1.87. Rust 1.87 was required to update to the latest versions of our upstream dependencies. Specifically, nalgebra 0.34 requires Rust 1.87.

  • The minimum required version of stestr in the test requirements has been raised from 2.0 to 3.2.0. This was done to leverage new stestr features, mainly the stestr history command, in continuous integration Python test jobs.

Known Issues

  • Qiskit v2.2 and v2.3 can generate unreadable QPY files for circuits containing a ParameterExpression where all the contained Parameter instances were cancelled out of the final expression. For example, if the expression 0*x + 1.5 appears in a circuit, Qiskit v2.2 and v2.3 will both produce a QPY file that cannot be read by any version of Qiskit, including themselves and v2.4, due to errors in the QPY generation.

    This error will typically appear (in Qiskit v2.4, at least) as a QpyError with a message such as “malformed expression: stack was empty before expression completed”.

  • QPY files generated by qiskit-terra v0.24 and prior may define ParameterVector instances whose elements have disconnected Parameter.uuid attributes, which cannot be completely round-tripped. Prior to this version of Qiskit, QPY would emit a warning if it detected an implicit ParameterVector that had not been completely reconstructed. QPY will no longer warn in this situation.

    In order to observe the dangerous behavior, all the following are required:

    • you load two separate QPY circuit payloads (in the same file, or separate files) generated by qiskit-terra v0.24 or earlier.
    • the two circuits use ParameterVectorElement instances derived from the same ParameterVector.
    • one circuit uses a particular element from the vector, and the other does not use it.
    • you attempt to compare the directly deserialized ParameterVectorElement from the circuit that used it to the same element index, but retrieved through the ParameterVectorElement.vector backreference of a different element from the circuit that does not use the compared element.

    The warning has been removed because in modern Qiskit it is a false positive far more often than it indicates an observed problem. Qiskit v2.5 onwards in fact will create an implicit ParameterVector whose elements compare equal between circuits.

Security Issues

  • The OpenQASM 2 parsers (qasm2.load() and qasm2.loads()) will now exit with a RecursionError when attempting to evaluate an excessively deep expression. The maximum allowed depth is taken from Python’s sys.getrecursionlimit() at the point of entry to the parsers.

    Previously, the parsers could recurse arbitrarily in Rust space, eventually exceeding the maximum stack space and segfaulting the process.

Bug Fixes

  • Fixed ConstrainedReschedule raising a TranspilerError when rescheduling a circuit containing a Barrier. Fixed #16135.

  • synth_cz_depth_line_mr() will now raise a Python-space ValueError on mis-shaped inputs, instead of a Rust-space panic. Fixed #16152.

  • Fixed a bug in ElidePermutations and StarPreRouting when composing virtual permutation layouts (implicit permutations applied at the end of circuit).

  • ConstrainedReschedule will no longer raise AttributeError if the optional target argument is not provided. See #16245.

  • Fixed a bug in CommutationChecker when evaluating commutativity relations between controlled-rotation gates CRXGate, CRYGate and CRZGate and other standard gates. In particular, fixed the behavior of CommutationAnalysis, CommutativeCancellation, CommutativeOptimization and qk_transpiler_pass_standalone_commutative_cancellation() passes in the presence of such gates.

    Fixed #16164.

  • Fixed a bug in InverseCancellation where CSGate and CSdgGate pairs with reversed qubit arguments were inconsistently cancelled. Previously, a sequence of CSCSCS-CS^\dagger (with reversed qubit args) was cancelled, but CSCSCS^\dagger-CS was not. Now both are consistently cancelled.

    Fixed #15855.

  • Fixed MultiplierGate so that its default decomposition preserves the requested number of result qubits. Previously, it ignored this argument and always decomposed using twice the number of state qubits for the result qubits. Fixed #16168.

  • Fixed a panic in RemoveIdentityEquivalent when the global phase of the circuit contained ParameterVectorElement objects. The message was:

    Cannot clone pointer into Python heap without the thread being attached.

    See #16053.

  • The circuit visualization no longer prints a redundant leading 1 for angles within tolerance of π/k\pi/k for integer kk (for example 1π/2 is now π/2), including latex, mpl, and qasm output modes.

    Fixes #16170.

  • Fixed qpy.load() for QPY versions >=13, where Delay instructions with integer durations could deserialize to incorrect types. This could cause later code to raise errors, such as qasm3.dumps_experimental() returning an error saying “Failed to parse parameter value”. See #16076 for more detail.

  • Fixed Statevector.evolve() modifying the input statevector in-place when the instruction has a global phase. See #15750.

  • Ensure no subtraction panic happens via underflow in ConstrainedReschedule. Fixed #16231.

  • Fixed a bug in UnrollForLoops where unrolling a static QuantumCircuit.for_loop() whose body has a nonzero QuantumCircuit.global_phase added one extra copy of the body phase.

    Fixed #16185.

  • VF2Layout and VF2PostLayout will no longer panic saying “float scores must not return nan” when encountering instructions with errors reported as 1.0.

  • Fixed LinearFunction.inverse() always raising CircuitError on call. See #16184.

  • Fixed an issue where the transpile_optimization_level user-configuration file field would not be respected when calling generate_preset_pass_manager(). It previously only affected transpile().

  • plot_bloch_vector() no longer mutates list inputs.

  • CouplingMap will now ignore edges that join a qubit to itself, and issue a warning about it. Previously these were incorrectly silently accepted, which could cause long-range errors in code depending on the coupling graph.

  • Fixed dagdependency_to_circuit() to correctly preserve the circuit’s global phase during conversion. Fixed #16166.

  • Fixed BitArray.bitcount() so that it ignores unused padding bits in the packed byte storage. See #16173.

  • Fixed a bug in CommutationChecker where certain Rust-backed gates, such as the UnitaryGate, could circumvent the matrix size check and cause the calculation of large matrices.

  • Fixed circuit_to_dagdependency() failing to copy the global_phase. See #14537.

  • Fixed clifford_6_4(), rzx_xz(), rzx_zz1(), rzx_zz2(), and rzx_zz3() templates so that TemplateOptimization now accepts and applies them. Each of these templates implements the identity only up to a global phase in their gate content but was missing the compensating global_phase field, causing TemplateOptimization to silently reject them on every run. See #14538.

  • Fixed CommutativeCancellation incorrectly shifting the global phase by π-\pi when accumulating a 2π2\pi rotation using a PhaseGate or U1Gate. Fixed #16377.

  • Fixed Commuting2qGateRouter so it preserves the input circuit’s global phase instead of adding it again when composing accumulated non-routed operations. Fixed #16205.

  • Fixed a problem in ConsolidateBlocks where basis gate selection was not always deterministic in the case that the basis set contained multiple parametric or multiple non-parametric 2-qubit gates. This could lead to different transpiled circuits across runs.

  • Fixed ForLoopOp with negative integers in its index set. Previously, an OverflowError was raised. See #16412.

  • In Qiskit v2.5.0rc1 only, the C API type QkLoopElements erroneously listed its elements member as const size_t *. This is now const ptrdiff_t * to account for negative values.

  • Fixed a bug in ConsolidateBlocks when force_consolidate=True, where the blocks and runs were not consolidated if the basis gates did not allow selecting a decomposer.

  • Fixed Initialize.gates_to_uncompute() to return a circuit with the correct number of qubits, when the gate was constructed using an integer form (e.g. Initialize(1, num_qubits=2)). Fixed #16413.

  • Fixed an unclear error message in Initialize.inverse(), which now immediately raises an error when called since the instruction is not invertible.

    See #15595.

  • Fixed an issue with the qk_transpile() and qk_transpile_stage_optimization() C API functions when using optimization_level 3 in the QkTranspileOptions. Previously the internal minimum-point tracking logic was not updating the DAG even after finding a better circuit. This resulted in returning a circuit that had not been correctly optimized depth/size.

  • Fixed a bug in the OptimizeAnnotated transpiler pass where control modifiers with open controls were not combined correctly. See #16410.

  • ParameterExpression instances with cancelled-out variables (like x - x) will now retain the reference to x after a round-trip through QPY and pickle.

  • ParameterExpression instances that evaluated to a bare value (like 0*x + 2) will now successfully round-trip through QPY and pickle.

  • Fixed commutativity checks between two PauliProductMeasurement instructions that measure to the same classical bit, which previously did not respect the observable write order.

  • Fixed an issue with the serialization to QPY of PauliProductMeasurement and PauliProductRotationGate where the Paulis were treated incorrectly, breaking compatibility with previous Qiskit versions. See #16099.

  • Fixed qasm3.dump_experimental() and dumps_experimental() incorrectly outputting a us suffix for delays specified in ms. Fixed #16097.

  • Fixed the conversion to ns in qasm3.dump_experimental() and dumps_experimental() when outputting delays specified in ps. Fixed #16097.

  • Fixed synth_qft_line() to correctly synthesize circuits with 32 or more qubits.

  • Fixed an issue where running the SynthesizeRZRotations transpiler pass multiple times with different error budgets could produce incorrect results or trigger internal panics.

    See #16385.

  • Fixed global phase handling in TemplateOptimization, which previously silently reset to input circuit’s global phase to zero and didn’t correct for global phases of the templates.

    Fixes #14537.

  • Fixed QuantumCircuit.compose() raising ValueError when front=True was used with an explicit qubits mapping. See #15834.

  • Fixed a bug in the CommutativeOptimization transpiler pass where pairs of XXPlusYYGate gates acting on the same qubits but in reversed order could be incorrectly simplified. See #16161.

  • Fixed error in BasisTranslator in which a circuit with nested control flow operations would have its blocks skip the calculated transformations.

    See #13162, #14025, and #15734.

  • QPY deserialization no longer emits a warning when loading a circuit that uses some, but not all, elements of a ParameterVector.

  • Fixes an issue where the LightCone pass would fail with index errors or out-of-memory panics when encountering large or complex operations. Fixed #15021 and #13828.

  • Fixed in issue in Schur decomposition for certain matrices by reimplementing it using faer instead of nalgebra. The issue could occur when running the quantum Shannon decomposition via qs_decomposition(), as well as via the UnitarySynthesis transpiler pass or via transpile().

    See #15870.

  • QuantumCircuit.remove_final_measurements() will no longer emit a spurious warning about trying to add registers when called on a circuit with a set layout.

  • Circuits with Reset instructions now can be scheduled against a GenericBackendV2.

  • Fixed a non-determinism in random_clifford_circuit(), even when seeded. See #16138.

Performance Improvements

Was this page helpful?
Report a bug, typo, or request content on GitHub.