Skip to main content
IBM Quantum Platform

Qiskit SDK 2.2 release notes


2.2.3

Prelude

Qiskit 2.2.3 fixes a bug that was introduced by Qiskit 2.2.2 when using the run() method on a PassManager for more than one circuit.

Upgrade Notes

  • ConsolidateBlocks now reads a PropertySet key ConsolidateBlocks_qubit_map on entry. This key and its value are not public and should not be read or written to by other passes.

Bug Fixes

  • Fixed re-use of the same ConsolidateBlocks instance on multiple circuits, including calls to transpile() with more than one circuit and no process-based parallelization. A bug introduced in Qiskit 2.2.2 caused the pass to panic or produce invalid output if the same instance was re-used on differing circuits.

2.2.2

Prelude

Qiskit 2.2.2 is a bugfix release for the v2.2 minor version series.

Bug Fixes

  • Fixed the implementation of __deepcopy__() in QuantumCircuit which did not deep-copy circuit parameters. As a consequence, mutating a BoxOp in a copied circuit no longer affects the original circuit.

  • Fixed an issue in the Optimize1qGatesDecomposition pass when the pass is initialized with a Target containing 1-qubit gates with fixed angle parameters. Previously, the pass would potentially output gates not included in the target as it did not check whether the gates in the target supported arbitrary parameter values. Fixed #14743.

  • Fixed incorrect behavior in the BasisTranslator pass where a multi-qubit gate within a ControlFlowOp block would track using its local qubit indices instead of using the absolute indices from the source circuit.

  • The ConsolidateBlocks transpiler pass will now correctly evaluate whether a given gate is hardware-supported while recursing into control-flow operations.

  • qpy.dump() can now handle writing out to .gz files opened using the standard-library gzip module with QPY versions 16 or greater. See #15157 for details.

  • Fixed the method MCPhaseGate.inverse() to preserve the control states of open-controlled gates when computing their inverses.

  • ConsolidateBlocks will now return a Python-space exception instead of panicking when it detects invalid or out-of-date analysis in the legacy run_list or block_list PropertySet keys. Fixed #14646.

  • Optimize1qGatesDecomposition will now raise a TranspilerError instead of a Rust-space panic when attempting to run on a circuit that is too large for the Target. Fixed #15116.

  • The scheduling passes, ALAPScheduleAnalysis and ASAPScheduleAnalysis, will now correctly handle circuits with no operations in them. Previously they raised a TranspilerError falsely claiming “No durations provided”. Fixed #15145.

  • Fixed a failure in the circuit text drawer, which could occur when circuit blocks inside control flow operations were defined on different registers than the outer circuit. This situation could for example happen when appending ControlFlowOp operations directly, or for circuits after transpilation.


2.2.1

Prelude

Qiskit 2.2.1 is a small patch release that fixes several bugs identified in the 2.2.0 release.

Transpiler Upgrade Notes

  • The maximum call and trial limits for the exact-matching run of VF2PostLayout at optimization_level=3 have been reduced to avoid excessive runtimes for highly symmetric trial circuits being mapped to large coupling maps.

Bug Fixes

  • DAGCircuit.apply_operation_back(), apply_operation_back() and circuit_to_dag() will now add new edges in a deterministic order. The previous behavior could cause certain transpiler passes (such as SabreSwap) to traverse the DAG in non-deterministic orders.

  • DAGCircuit.apply_operation_front() can no longer insert invalid self loops when handling nodes that include classical conditions.

  • Fixed an issue with pickle support for the SabreSwap where a SabreSwap instance would error when being pickled after the SabreSwap.run() method was run. Fixed #15071.

  • Fixed an issue where is_unitary() was not properly respecting the input tolerance values when checking if an operator is unitary. The method now correctly uses the provided atol and rtol parameters when simplifying the operator and checking if it equals the identity. This fixes #14107.


2.2.0

Prelude

The Qiskit v2.2 adds several enhancements for the C API and the transpiler as well as many other improvements and bug fixes. The major features of this new release are:

  • C API transpile function: The C API now includes a function for transpiling a quantum circuit: qk_transpile(). This function is equivalent to the Python transpile() function for a standalone C context.
  • Litinski transformation pass: A new transformation pass LitinskiTransformation that implements the transform described arXiv:1808.02892. This is pass is typically used in compilation for fault tolerant architectures.
  • Angle bound support for targets: The Target class now supports specifying bounds for the allowed values for parameterized operations added to the target. When an instruction is added to the Target you can add the optional argument angle_bounds to specify the higher and lower bounds for parameterized rotation gates. A new transpiler pass WrapAngles was added to enforce the angle constraints as part of a transpilation pipeline.

The v2.2 release series is the final minor release series with support Python 3.9. The minimum supported Rust version use to build Qiskit from source is now Rust v1.85, raised from 1.79 in 2.1.0. For more information about the above and other changes made, please see the release notes below and review the updated documentation.

C API Features

Circuits Features

OpenQASM Features

  • Added partial support for defcal symbols in the OpenQASM3 exporter. This enables downstream packages to export custom instructions that operate on both quantum and classical bits using qiskit.qasm3.dumps(). Users can now define custom instructions (e.g., a CustomMeasure that acts on a qubit and returns a classical bit) and specify their behavior using DefcalInstruction. These defcals are passed to the exporter via the implicit_defcals argument in qiskit.qasm3.dumps().

    For example:

    from qiskit.circuit import Instruction, QuantumCircuit
    from qiskit.qasm3 import dumps
    from qiskit.qasm3.exporter import DefcalInstruction, types
     
    custom_measure = Instruction("measure_2", 1, 1, [])
    qc = QuantumCircuit(1, 1)
    qc.h(0)
    qc.append(custom_measure, [0], [0])
    qc.measure(0, 0)
     
    defcals = {
        "measure_2": DefcalInstruction("measure_2", 0, 1, types.Bool()),
    }
     
    out_qasm = dumps(qc, implicit_defcals=defcals)
    print(out_qasm)

    Would output the following valid OpenQASM3 string:

    OPENQASM 3.0;
    bit[1] c;
    qubit[1] q;
    h q[0];
    c[0] = measure_2 q[0];
    c[0] = measure q[0];

    This approach assumes that the grammar definition for the defcal is provided externally (e.g., in a header file), although such a file is not strictly required for the exporter to function.

  • The functions qasm3.loads() and qasm3.load() now have an extra argument called num_qubits. If provided, the functions will return circuits that will have qubits equal to num_qubits. If not provided, the returned circuit will have qubits equal to the maximum index seen in the serialized circuit. Refer to #14435 for more details

QPY Features

  • Added a setting named min_qpy_version in the user configuration file. When set, it defines the minimum allowed QPY version for qpy.load(). If the format version of a QPY file is lower than the minimum_qpy_version than the minimum_qpy_version setting it will raise an exception.

  • Introduced QPY format version 16. This new version introduces a new circuit start table to the file header which contains the byte offsets of the start of each circuit in a QPY file. This allows for a potentially more efficient loading of circuits from QPY files, and a potentially multi-threaded Rust implementation in the future. Additionally, the new format version adds support for the new DURATION variant of picoseconds.

Quantum Information Features

Synthesis Features

Transpiler Features

  • Added new high-level-synthesis plugin for synthesizing a ModularAdderGate:

    The ModularAdderSynthesisDefault has also been updated to follow the following sequence of modular adder synthesizers: "ModularAdder.qft_d00" when the number of qubits is 4\leq 4, "ModularAdder.modular_v17" in all other cases.

  • Added a new transpiler pass LitinskiTransformation that implements the transform described in arXiv:1808.02892.

    The input to the pass is a circuit with Clifford and single-qubit RZ-rotation gates, and the output is a circuit with multi-qubit Pauli rotations (implemented as PauliEvolutionGate gates) followed by Clifford gates. The pass raises a TranspilerError exception if the circuit contains non-supported gates.

    The pass supports all of the Clifford gates in the list returned by get_clifford_gate_names(), namely ["id", "x", "y", "z", "h", "s", "sdg", "sx", "sxdg", "cx", "cz", "cy", "swap", "iswap", "ecr", "dcx"]. The list of supported RZ-rotations is ["t", "tdg", "rz"] (we automatically convert T and Tdg gates to RZ-rotations).

    In addition, the LitinskiTransformation constructor accepts an argument fix_clifford. When False (non-default), the returned circuit contains only PauliEvolutionGate gates, with the final Clifford gates omitted. Note that in this case the operators of the original and synthesized circuits will generally not be equivalent.

    For example:

    from qiskit.circuit import QuantumCircuit
    from qiskit.transpiler.passes import LitinskiTransformation
    from qiskit.quantum_info import Operator
     
    # The following quantum circuit consists of 5 Clifford gates
    # and two single-qubits RZ-rotation gates (note that Tdg is
    # an RZ-rotation).
    qc = QuantumCircuit(2)
    qc.cx(0, 1)
    qc.rz(0.1, 0)
    qc.cz(0, 1)
    qc.tdg(1)
    qc.h(1)
    qc.s(1)
    qc.cz(1, 0)
     
    # The transformed circuit consists of two PauliEvolution gates
    # and the same Clifford gates as in the original circuit.
    qct = LitinskiTransformation()(qc)
     
    # The circuits before and after the transformation are equivalent
    assert Operator(qc) == Operator(qct)
  • Added a new high-level synthesis plugin MCXSynthesisNoAuxHP24 for MCXGate objects. Furthermore, the default synthesis plugin MCXSynthesisDefault for MCXGate objects was updated to always choose the best synthesis method depending on the available number of auxiliary qubits.

  • Added support for a Target to specify angle bounds on instructions. Prior to this release a Target could specify that an operation that took a parameter either would allow any valid value or a specific value. For example, if RZGate(Parameter("theta")) were added to the target that would indicate an RZGate with any value for theta were allowed. While if RZGate(math.pi) were added to the target that would indicate RZGate that only π\pi is the only allowed value on the target. This new feature enables restricting the allowed angles to be any float value between a an inclusive bound. For example, you can add RZGate(math.pi) to a Target and restrict the angle value between the values 0 and 2π2\pi.

    There are several methods available for working with the angle bounds on the target. The first is Target.add_instruction() which has a new angle_bounds keyword argument that is used to add an angle bound to an instruction in the Target. To work with angle bounds you will also want to register a callback function to the global WRAP_ANGLE_REGISTRY registry that will tell the transpiler and WrapAngles pass how to adjust gates for angle bounds. The callback function will take a list of arbitrary float values representing the gate angles from the circuit, as well as the qubit indices in the circuit the gate was operating on and it will return a DAGCircuit that represents an equivalent circuit for the gate with that angle but respecting the angle bounds and other Target constraints. For example:

    import math
     
    from qiskit.dagcircuit import DAGCircuit
    from qiskit.transpiler import Target
    from qiskit.transpiler.passes.utils.wrap_angles import WRAP_ANGLE_REGISTRY
     
    target = Target(num_qubits=1)
    target.add_instruction(RZGate(Parameter("theta")), angle_bounds=[(-math.pi, math.pi)])
     
    def callback(angles: List[float], qubits: List[int]) -> DAGCircuit:
        """Callback function to wrap RZ gate angles
     
        Args:
            angles: The list of floating point parameter values for the instance of RZGate in
                the circuit
            qubits: The physical qubit indices that this gate is operating on
     
        Returns:
            The DAGCircuit of the equivalent circuit"""
     
        angle = angles[0]
        dag = DAGCircuit()
        dag.add_qubits([Qubit()])
        if angle > 0:
            divisor = math.pi
        else:
            divisor = -math.pi
        gate_counts = int(angles[0] // divisor)
        rem = angles[0] % divisor
        for _ in range(gate_counts):
            dag.apply_operation_back(RZGate(math.pi), [dag.qubits[0]], check=True)
        dag.apply_operation_back(rem, [dag.qubits[0]], check=True)
     
    WRAP_ANGLE_REGISTRY.add_wrapper("rz", callback)

    Target.has_angle_bounds() can be used to check whether there are any angle bounds set in the target, and Target.gate_has_angle_bounds().

    If you want to apply the angle bounds from a target to any gates in a circuit the WrapAngles transpiler pass should be used to do this.

  • Added a new kwarg check_angle_bounds to the Target.instruction_supported() method. When set to True (the default) the method will check if the specified parameters also conforms to any angle bounds that may exist for the instruction being queried.

  • Added a new transpiler pass WrapAngles which is used to apply angle constraints on gates in the Target. If a Target has defined angle bounds this pass will analyze all the parameters for the gates in the circuit and check that against the bounds specified in the target. For example, if a target contains a custom gate that has angle bounds the pass will decompose that gate into a gate which conforms to the bounds:

    from qiskit.circuit import Gate, Parameter, QuantumCircuit, Qubit
    from qiskit.dagcircuit import DAGCircuit
    from qiskit.transpiler import Target, WrapAngleRegistry
    from qiskit.transpiler.passes import WrapAngles
     
    class MyCustomGate(Gate):
     
        def __init__(self, angle):
            super().__init__("my_custom", 1, [angle])
     
    param = Parameter("a")
    circuit = QuantumCircuit(1)
    circuit.append(MyCustomGate(6.0), [0])
    target = Target(num_qubits=1)
    target.add_instruction(MyCustomGate(param), angle_bounds=[(0, 0.5)])
     
    def callback(angles, _qubits):
        angle = angles[0]
        if angle > 0:
            number_of_gates = angle / 0.5
        else:
            number_of_gates = (6.28 - angle) / 0.5
        dag = DAGCircuit()
        dag.add_qubits([Qubit()])
        for _ in range(int(number_of_gates)):
            dag.apply_operation_back(MyCustomGate(0.5), [dag.qubits[0]])
        return dag
     
    registry = WrapAngleRegistry()
    registry.add_wrapper("my_custom", callback)
    wrap_pass = WrapAngles(target, registry)
    res = wrap_pass(circuit)
    res.draw('mpl')
    _images/release_notes-1.png
  • DAGCircuit now has a manual implementation of __deepcopy__(). This is orders of magnitude faster than the previous implicit implementation from the pickle protocol, especially for large circuits. This also directly benefits compilation performance when running transpile() or running the preset pass managers returned by generate_preset_pass_manager() at optimization_level=3 as optimization level 3 internally deep copies the DAGCircuit for each iteration of the optimization loop in the optimization stage.

  • A new method, DAGCircuit.make_physical(), is provided, which efficiently replaces the qubits in the DAGCircuit with the canonical physical-qubit register, potentially including expansion. A similar method, QuantumCircuit.ensure_physical() is also available.

  • A new method, DAGCircuit.structurally_equal(), can be used to if two DAGCircuit instances have been created and modified in the exact same order. This is a much stronger test than the standard semantic equivalence check of the == overload, and can be used by transpiler-pass authors to verify that their modification orders are deterministic.

  • Custom subclasses of BasePassManager can now modify their property_set attribute during their _passmanager_frontend() method, to seed initial properties. This provides symmetry, as it was previously only possible to read the final properties during _passmanager_backend().

  • Added a new class OptimizationMetric which specifies the optimization criterion in the HighLevelSynthesis pass. Currently the two supported metrics are:

    • COUNT_2Q: optimizes the number of two-qubit gates.
    • COUNT_T: optimizes the number of T-gates, when transpiling into a Clifford+T basis set.

    The transpiler automatically selects the target metric based on the basis gate set, e.g. it will use COUNT_T if a Clifford+T basis set is detected. However, this class can not be currently set manually when running transpile() or running a preset pass manager generated by generate_preset_pass_manager().

  • Added a new argument optimization_metric to the constructor for HighLevelSynthesis transpiler pass which takes an OptimizationMetric object. When set to COUNT_T, the pass chooses decompositions that are more suitable for the Clifford+T gate set.

  • The default high-level synthesis plugins for ModularAdderGate and MultiplierGate produce better T-counts when transpiling into Clifford+T basis set.

  • The default high-level synthesis plugin for MCXGate produces better T-counts when transpiling into Clifford+T basis set, provided at least 1 ancilla qubit is available.

  • Added a default set of inverse gates for the InverseCancellation transpiler pass. Previously, an explicit list of gates or gate pairs to cancel was a required argument for the constructor of the pass object. Now this list is optional and if no list is provided the self inverse gates are:

    and the inverse pairs:

    will be cancelled by the pass.

  • Added a new argument to the InverseCancellation constructor, run_default, which when set to True will run the new default inverse cancellation gate list in addition to the any custom gates provided. This is intended for use cases where you want to run a custom set of inverse cancellations in addition to the default gates.

  • TranspileLayout has two new methods: from_property_set() and write_into_property_set(), which formalize the current ad-hoc structure of transpilation properties, and how they are converted into a TranspileLayout. This makes it possible for passes during a transpiler pipeline to access what the TranspileLayout will be, modify it in the fully structured form, and then write it back out in canonical form.

    It is expected that in the future version 3.0 of Qiskit, the TranspileLayout (or something akin to it) will be a direct attribute of the DAGCircuit transpiler intermediate representation, and required by passes to be kept in sync with the rest of the DAGCircuit.

  • Re-enabled running VF2PostLayout transpiler pass in the optimization stage for optimization_level=3 with the transpile() function and the generated pass manager objects returned by the generate_preset_pass_manager() function. The pass runs with strict_direction=True after all the physical optimizations performed in the stage to attempt and improve the layout one final time with the exact output circuit. This was previously enabled in Qiskit v2.1.0 but was reverted in 2.1.2 due to issues with the initial implementation. These issues have been fixed and the layout will be properly applied if a better one is found.

Visualization Features

  • A new option, measure_arrows, has been added to the mpl and text backends for the circuit drawer qiskit.visualization.circuit_drawer() and qiskit.circuit.QuantumCircuit.draw(). When this option is set to True, the drawer will draw an arrow from the measure box to the classical bits below. This was the previous behavior in the drawers. If it is set to False, no arrows will be drawn and instead the classical register and bit being used by the measure will be indicated inside the measure box. This allows measure boxes to be stacked within a vertical layer.

    If no option is set, the default is True. The user can change the default in the user config file, by default in the ~/.qiskit directory, in the file settings.conf. Under the [Default] heading, a user can enter circuit_measure_arrows = False to change the default.

Upgrade Notes

  • The minimum support Rust version for building Qiskit from source (including building the C API in standalone mode) has been increased from Rust 1.79 to Rust 1.85. This change was necessary to enable using a newer version of the Rust linear algebra library faer that is used inside Qiskit. There were issues identified when running Qiskit on Windows with the previous version of faer. These were fixed in the newer release, however a newer version of the Rust compiler is required to build the newer version of faer.

Circuits Upgrade Notes

C API Upgrade Notes

QPY Upgrade Notes

  • The default version of QPY emitted by qpy.dump() is now QPY format version 16. If you require a different format version you can use the version argument to specify an earlier format version.

Quantum Information Upgrade Notes

Synthesis Upgrade Notes

  • The default values of the arguments opt_a1 and opt_a2 of qs_decomposition() are now opt_a1 = None and opt_a2 = None. The new defaults will choose the optimal value based on whether the input unitary for the mat argument is controlled or not to result in the minimal CXGate count by default. If you require the previous behavior you can explicitly call opt_a1 = True and opt_a2 = True to maintain the same behavior as previous releases.

Transpiler Upgrade Notes

  • The circuit PassManager now always sets the properties original_circuit_indices and num_input_qubits before execution starts on individual passes. These are properties of the input circuit, which it was previously up to individual passes to set, often as a side effect of their primary purpose.

Circuits Deprecations

Bug Fixes

  • ApplyLayout will now correctly handle the case of applying a zero-qubit Layout. Previously, it would claim that no layout had been set, even if the "layout" field of the PropertySet was equal to Layout().

  • Fixed memory leakage issues during the creation of a QkOpCounts instance and during any calls to qk_opcounts_clear() whenever an empty instance is passed.

  • Previously one could define an invalid PauliEvolutionGate from a list of operators, where the operators were not all defined on the same number of qubits. This is now fixed, and we now raise an error when the gate is defined:

    from qiskit.quantum_info import Pauli, SparsePauliOp
    from qiskit.circuit.library import PauliEvolutionGate
     
    pauli = Pauli("XYZ")  # 3 qubits
    op = SparsePauliOp(["XYIZ"], [1])  # 4 qubits
    evo = PauliEvolutionGate([pauli, op], time=1)
  • Fixed an issue in the VF2Layout transpiler pass where even with a fixed seed set the output from the pass was potentially non-deterministic; specifically if the input circuit had any active qubits that only contained single qubit operations. Fixed #14729

  • Fixed a bug in PassManager.run() where the callback function was not invoked when running with multiple circuits. The callback is now correctly triggered for each pass on each circuit, including in parallel execution.

  • Fixed several problems in the CommutativeInverseCancellation transpiler pass. The pass now works correctly on circuits containing Clifford operations, control-flow operations, and non-invertible operations such as Initialize.

    In addition, the pass now always performs a syntactic (non-matrix-based) check first, when identifying inverse gate pairs. If the gates are not syntactically equal, the argument matrix_based is set to True, and the operation does not act on more than max_qubits qubits, then a matrix-based check is also performed. This slightly improves the reduction potential of the pass.

    Fixed #14407, #14635, and #14645.

  • Fixed several issues in the CommutativeCancellation transpiler pass (and thereby in transpile()), where the global phase of the circuit was not updated correctly. In particular, merging an X-gate and an RX-gate introduced a phase mismatch, while removing a Pauli rotation gate with angle of the form (2+4k)π(2 + 4k)\pi, kZk \in \mathbb Z incorrectly produced a phase shift of 1-1.

  • Fixed a problem in CommutationChecker, where standard controlled gates were not handled correctly if they were controlled on something other than the all-ones state. Fixed #14974

  • Fixed a non-determinism in CommutativeCancellation. This did not affect the order returned by DAGCircuit.topological_nodes() or topological_op_nodes(), which typically should be used when node-order determinism is important, due to their built-in canonical sorting function. However, if inspecting the nodes by arbitrary order (DAGCircuit.op_nodes()) or the edge structure (DAGCircuit.edges()), the iteration order would be non-deterministic after a call to CommutativeCancellation.

  • Fixed a bug in MCXGate.control(), where adding more controls to an open-controlled MCXGate did not take the ctrl_state of the controlled MCX gate into account, thus leading to an incorrect ctrl_state of the extended MCX gate. Note that the explicit MCX classes CXGate, CCXGate, C3XGate, and C4XGate were already handled correctly.

  • Fixed the deprecation warning for Python 3.9 so that it correctly is identified as being caused by user code when importing Qiskit. Previously, it would not be identified as being caused by user code and this meant that Python’s default warning filters would not display the warning to the user.

  • Fixed a bug in PhaseOracle, PhaseOracleGate and BitFlipOracleGate where trying to load from dimacs file raised a TypeError.

  • Fixed a bug in the ElidePermutations transpiler pass, where the qubit mapping was not updated correctly in the presence of PermutationGates, leading to incorrect circuits and updates to the pass manager’s property set.

  • Built-in transpiler passes that set the final_layout property will now correctly handle updating this field if it was already set. This can be observed as the method TranspileLayout.routing_permutation now returning a correct permutation after running more than one pass that sets final_layout.

    This did not affect any normal calls to transpile() or generate_preset_pass_manager() using Qiskit’s built-in plugins; no pipeline constructed in this form would attempt to set final_layout more than once.

  • Fixed a bug in the HighLevelSynthesis pass where, if the circuit contained high level objects with classical registers, these would get mapped to the relative index in the object instead of the corresponding index in the outer circuit. The classical registers are now correctly mapped to the outer circuit index.

  • Fixed a bug in Target.instruction_supported() where the check of instruction’s qubit order was skipped when the method was called with operation_name and parameters arguments that matched an existing instruction.

  • Fixed the behavior of the max_trials argument for VF2Layout when set to None or a negative number. The pass was documented as limiting the search to being based on the size of the circuit or target if the option was set to None and as accepting negative values to specify an unbounded search. However in the 2.1.0 this behavior was incorrectly changed so that None ran an unbounded search and trying to use a negative number would raise an error. These oversights have been corrected so that the pass behaves as documented and is consistent with previous releases.

  • Fixed a problem in high-level synthesis plugins MCXSynthesis1DirtyKG24 and MCXSynthesis2DirtyKG24 for MCXGate, where the plugins did not consider available clean auxiliary qubits as available dirty auxiliary qubits. In particular, the plugin MCXSynthesis2DirtyKG24 did not apply when one clean and one dirty auxiliary qubits were available.

  • The PauliEvolutionGate.to_matrix() method now returns the exact matrix exponential exp(itH)\exp(-it H), where HH is the operator and tt the time passed to the gate. This fixes an unexpected behavior, since the PauliEvolutionGate is documented to represent the exact time evolution, but previously the matrix was dependent on how the compiler approximates the time evolution. The to_matrix method is now consistent with the documentation.

  • Fixed a performance regression when incrementally building ParameterExpression from combining a large number of sub-expressions. Fixed #14653

  • Fixed a correctness bug when exporting circuits with delay instructions using ‘ps’ units to QASM3.

  • Fixed an edge case in the display of the QFT circuit. Previously, when QFT.inverse() was called and then the attributes of the QFT circuit were modified, the QFT was displayed as "IQFT_dg". This was incorrect, and now it correctly shows "IQFT". Fixed #14758.

  • Fixed a bug in the PauliEvolutionSynthesisRustiq plugin that produced incorrect circuits in the case that the operator of a PauliEvolutionGate contains objects of type SparseObservable.

    For example:

    from qiskit.circuit.library import PauliEvolutionGate
    from qiskit.quantum_info import SparseObservable, Operator
    from qiskit.transpiler.passes.synthesis.hls_plugins import PauliEvolutionSynthesisRustiq
     
    obs = SparseObservable.from_sparse_list([("1+XY", (0, 1, 2, 3), 1.5)], num_qubits=4)
    evo = PauliEvolutionGate(obs, 1)
    qct = PauliEvolutionSynthesisRustiq().run(evo)
    assert Operator(qct) == Operator(evo)
  • Fixed an issue with the generate_preset_pass_manager() function where it would incorrectly ignore the timing constraints data contained in a Target object provided when the backend argument was not set. Fixed #14329

  • Fixed a bug in ObservablesArray.coerce_observable() where an exception that an observable was not Hermitian was over-triggered. Observables equal to zero are not allowed and now invoke a graceful failure.

  • qiskit.circuit.library.quantum_volume() was updated to handle a numpy.random.Generator as input for its seed argument. Previously, such a generator argument would result in a TypeError.

  • QuantumCircuit.compose() will now correctly remap anyway variables and stretches used in Delay instructions when the var_remap argument is specified.

  • SabreLayout uses a “dense subset” layout as one of its trials, following the same algorithm as DenseLayout. Previously, however, the version used by Sabre was assigning all virtual qubits, including dummy ancillas, to a physical qubit, compromising the effectiveness of the algorithm, but not its correctness. Sabre will now only use the virtual qubits defined by the user for this initial trial, which may result in small improvements in layout selection when averaged over large classes of circuit.

  • Fixed a bug in SparsePauliOp.simplify() where the method removed Pauli terms based on their coefficients’ magnitudes before combining duplicates. This caused incorrect behavior in two key edge cases:

    1. When multiple identical Pauli terms had small coefficients that were individually below the simplification threshold but whose sum exceeded the threshold, those terms were incorrectly removed.
    2. When multiple identical Pauli terms had coefficients above the threshold but summed to near-zero, they were incorrectly kept.

    See #14194 for more detail.

  • TranspileLayout.initial_index_layout() will now correctly handle the filter_ancillas=True argument if the virtual qubits in the initial_layout were not specified by the constructor in index order.

  • VF2Layout and VF2PostLayout will now correctly include (arbitrary) layout assignments for completely idle qubits. Previously this might have been observed by calls to TranspileLayout.initial_index_layout() failing after a compilation.

  • Fixed an issue in the VF2PostLayout transpiler pass. In certain situations when the pass is being run with the argument strict_direction=True there was a potential runtime scaling issue when the interaction graph of the circuit contained any qubits that only had 1 qubit operations. These mapping problems result in a combinatorial complexity for scoring that would lead to the pass almost always hitting the scoring timeout and typically not improving the layout. In strict_direction=False mode there is an optimized search implementation for these problems, but the additional constraints for strict_direction=True don’t make this approach viable. Instead in these cases VF2PostLayout will now skip the search since the layout problem isn’t viable for the pass.

Other Notes

  • The implementation of Sabre routing used by SabreLayout and SabreSwap now compresses runs of nodes that will automatically become eligible for routing at the same time within its internal virtual-interaction representation. This improves the efficiency of the routing, reduces intermediate memory use, and avoids runs of 2q gates biasing the lookahead heuristic components.

2.1.0rc1

Deprecation Notes

  • Support for running Qiskit with Python 3.9 has been deprecated and will be removed in the Qiskit 2.3.0 release. The 2.3.0 is the first release after Python 3.9 goes end of life and is no longer supported. [1] This means that starting in the 2.3.0 release you will need to upgrade the Python version you’re using to Python 3.9 or above.

    [1] https://devguide.python.org/versions/

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