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
ConsolidateBlocksnow reads aPropertySetkeyConsolidateBlocks_qubit_mapon 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
ConsolidateBlocksinstance on multiple circuits, including calls totranspile()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__()inQuantumCircuitwhich did not deep-copy circuit parameters. As a consequence, mutating aBoxOpin a copied circuit no longer affects the original circuit. -
Fixed an issue in the
Optimize1qGatesDecompositionpass when the pass is initialized with aTargetcontaining 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
BasisTranslatorpass where a multi-qubit gate within aControlFlowOpblock would track using its local qubit indices instead of using the absolute indices from the source circuit. -
The
ConsolidateBlockstranspiler 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.gzfiles opened using the standard-librarygzipmodule 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. -
ConsolidateBlockswill now return a Python-space exception instead of panicking when it detects invalid or out-of-date analysis in the legacyrun_listorblock_listPropertySetkeys. Fixed #14646. -
Optimize1qGatesDecompositionwill now raise aTranspilerErrorinstead of a Rust-space panic when attempting to run on a circuit that is too large for theTarget. Fixed #15116. -
The scheduling passes,
ALAPScheduleAnalysisandASAPScheduleAnalysis, will now correctly handle circuits with no operations in them. Previously they raised aTranspilerErrorfalsely 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
ControlFlowOpoperations 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
VF2PostLayoutatoptimization_level=3have 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()andcircuit_to_dag()will now add new edges in a deterministic order. The previous behavior could cause certain transpiler passes (such asSabreSwap) 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
picklesupport for theSabreSwapwhere aSabreSwapinstance would error when being pickled after theSabreSwap.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 providedatolandrtolparameters 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 Pythontranspile()function for a standalone C context.- Litinski transformation pass: A new transformation pass
LitinskiTransformationthat 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
Targetclass now supports specifying bounds for the allowed values for parameterized operations added to the target. When an instruction is added to theTargetyou can add the optional argument angle_bounds to specify the higher and lower bounds for parameterized rotation gates. A new transpiler passWrapAngleswas 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
-
Added new standalone transpiler pass functions to the C API. These functions take in a
QkCircuitto run the pass on and are equivalent to calling an instantiated pass. -
The
QkTargettype can now represent targets that support parameterizedQkGatetypes that accept any parameter value. Previously, these gates could only be added to the target with a fixed angle value supported by usingqk_target_entry_new_fixed()to create theQkTargetEntry. Now, theqk_target_entry_new()function can be used with parameterized gates. When it is used with parameterized gates, this function indicates that the gate in the target supports any value for all of the gate’s parameters. For example:#include <qiskit.h> QkTarget *target = qk_target_new(5); QkTargetEntry *rz_entry = qk_target_entry_new(QkGate_RZ); for (uint32_t i = 0; i < 5; i++) { uint32_t qargs[1] = {i}; qk_target_entry_add_property(rz_entry, qargs, 1, 1.2e-6, 1.3e-9); } qk_target_add_instruction(target, rz_entry); // Clean up after using target qk_target_free(target);This creates a 5 qubit target that will accept an RZ gate on any qubit with any parameter value being supported by that gate.
-
Added
qk_obs_apply_layout()to apply new qubit layouts to aQkObsobservable. This function takes the observable, the new qubit indices, and an output number of qubits. Importantly, this function allows applying transpile layouts, usually given asQkTranspileLayoutby a transpiler pass, to an observable. For example:// get the number of output qubits -- transpile_layout is a QkTranspileLayout* uint32_t num_output_qubits = qk_transpile_layout_num_output_qubits(transpile_layout); // get the layout including the ancillas (hence the ``false`` in the function call) uint32_t *layout = malloc(sizeof(uint32_t) * num_output_qubits); qk_transpile_layout_final_layout(transpile_layout, false, layout); // apply the layout -- obs is a QkObs* int exit = qk_obs_apply_layout(obs, layout, num_output_qubits); // free the layout array free(layout); -
Added a new function
qk_transpile()to the Qiskit C API. This function is used for transpiling quantum circuits in a standalone C context without using Python. This is the last major component needed in the C API for typical hardware execution workflows using Qiskit.This function mirrors the preset pass managers that are used for the Python transpiler except for some passes and functionality is skipped if it is not relevant for circuits constructed using the C API. This makes the function only suitable for standalone C contexts.
-
Added a new type
QkTranspileLayoutto the C API. This type is used for reasoning about the permutations caused by the transpiler.
Circuits Features
-
A new method,
QuantumCircuit.ensure_physical(), is provided to ensure that a circuit is defined over physical qubits, with the qubit indices referring to physical qubits. See the new discussion on abstract- and physical-circuit representations in the documentation for more detail on the metadata concepts. The concepts of “abstract” and “physical” circuits are not at all new to Qiskit, just the explicit documentation. -
The
Durationclass has gained a newpsvariant, which can be used to represent a duration in picoseconds. -
Improved
PauliEvolutionGate.control(),PauliEvolutionGate.power()andPauliEvolutionGate.inverse()to return more efficient representations in terms of aPauliEvolutionGate. For computing the controlled and exponentiated versions of the evolution gate this change leads to significantly shallower circuits and lower gate counts compared to the previously used generic mechanisms. The inverse decomposition does not change but is now generated more efficiently and allows for better compiler optimizations. -
A new fast-path method
ParameterExpression.bind_all()is added to support the use-case of binding many differentParameterExpressioninstances to numeric values using the same mappings dictionary. The existingbind()method has a large amount of overhead and unnecessary allocations, since it always returns aParameterExpressionfor typing consistency.
OpenQASM Features
-
Added partial support for
defcalsymbols in the OpenQASM3 exporter. This enables downstream packages to export custom instructions that operate on both quantum and classical bits usingqiskit.qasm3.dumps(). Users can now define custom instructions (e.g., aCustomMeasurethat acts on a qubit and returns a classical bit) and specify their behavior usingDefcalInstruction. These defcals are passed to the exporter via theimplicit_defcalsargument inqiskit.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()andqasm3.load()now have an extra argument callednum_qubits. If provided, the functions will return circuits that will have qubits equal tonum_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_versionin the user configuration file. When set, it defines the minimum allowed QPY version forqpy.load(). If the format version of a QPY file is lower than theminimum_qpy_versionthan theminimum_qpy_versionsetting 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
DURATIONvariant of picoseconds.
Quantum Information Features
-
Added the
PauliLindbladMap.drop_qubits()andPauliLindbladMap.keep_qubits()methods to trace subsystems out of Pauli Lindblad maps. -
Added methods
QubitSparsePauli.to_pauli()andQubitSparsePauliList.to_pauli_list(), which convert the sparse objects into the corresponding dense versions,PauliandPauliList, respectively. -
Added the
PhasedQubitSparsePauliandPhasedQubitSparsePauliListclasses. These classes represent a single Pauli operator and a list of Pauli operators respectively stored in qubit-sparse format. These classes are very similar toQubitSparsePauliandQubitSparsePauliList, except they additionally contain phase information. -
Added the method
QubitSparsePauliList.to_dense_array(). This returns the list as an array of integers with the values ofPauli.
Synthesis Features
-
Added new synthesis algorithm for
ModularAdderGatethat requires no ancillary qubits and has better CX count compared toadder_qft_d00(): -
Added a new synthesis algorithm,
synth_mcx_noaux_hp24(), forMCXGatethat does not require any auxiliary qubits. This method produces a linear, rather than a quadratic number of CX gates, compared to the existingsynth_mcx_noaux_v24()algorithm. In particular, the new method is better when the number of control qubits is greater than five. The algorithm is based on the paper “Compiling Conditional Quantum Gates without Using Helper Qubits” by Huang and Palsberg (https://dl.acm.org/doi/10.1145/3656436). -
Improved the
qs_decomposition()function which was originally based on Shende et. al. (https://arxiv.org/abs/quant-ph/0406176). The new synthesis method is based on Krol and Al-Ars (https://arxiv.org/abs/2403.13692), and reduce the total number ofCXGates of a general n-qubit unitary byCXGates. With the improved decomposition, a general 3-qubit unitary can be decomposed using 19CXGates (rather than 20). -
Added
synth_mcmt_xgate()to synthesize the multi-control multi-target gate when the base gate isXGate. It has a decomposition in linear number of CX gates and 0 ancilla qubits along with the high-level synthesis pluginMCMTSynthesisXGate.
Transpiler Features
-
Added new high-level-synthesis plugin for synthesizing a
ModularAdderGate:ModularAdderSynthesisV17, based onadder_modular_v17().
The
ModularAdderSynthesisDefaulthas also been updated to follow the following sequence of modular adder synthesizers:"ModularAdder.qft_d00"when the number of qubits is ,"ModularAdder.modular_v17"in all other cases. -
Added a new transpiler pass
LitinskiTransformationthat 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
PauliEvolutionGategates) followed by Clifford gates. The pass raises aTranspilerErrorexception 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
LitinskiTransformationconstructor accepts an argumentfix_clifford. WhenFalse(non-default), the returned circuit contains onlyPauliEvolutionGategates, 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
MCXSynthesisNoAuxHP24forMCXGateobjects. Furthermore, the default synthesis pluginMCXSynthesisDefaultforMCXGateobjects was updated to always choose the best synthesis method depending on the available number of auxiliary qubits. -
Added support for a
Targetto specify angle bounds on instructions. Prior to this release aTargetcould specify that an operation that took a parameter either would allow any valid value or a specific value. For example, ifRZGate(Parameter("theta"))were added to the target that would indicate anRZGatewith any value for theta were allowed. While ifRZGate(math.pi)were added to the target that would indicateRZGatethat only is the only allowed value on the target. This new feature enables restricting the allowed angles to be anyfloatvalue between a an inclusive bound. For example, you can addRZGate(math.pi)to aTargetand restrict the angle value between the values 0 and .There are several methods available for working with the angle bounds on the target. The first is
Target.add_instruction()which has a newangle_boundskeyword argument that is used to add an angle bound to an instruction in theTarget. To work with angle bounds you will also want to register a callback function to the globalWRAP_ANGLE_REGISTRYregistry that will tell the transpiler andWrapAnglespass how to adjust gates for angle bounds. The callback function will take a list of arbitraryfloatvalues 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 aDAGCircuitthat represents an equivalent circuit for the gate with that angle but respecting the angle bounds and otherTargetconstraints. 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, andTarget.gate_has_angle_bounds().If you want to apply the angle bounds from a target to any gates in a circuit the
WrapAnglestranspiler pass should be used to do this. -
Added a new kwarg
check_angle_boundsto theTarget.instruction_supported()method. When set toTrue(the default) the method will check if the specifiedparametersalso conforms to any angle bounds that may exist for the instruction being queried. -
Added a new transpiler pass
WrapAngleswhich is used to apply angle constraints on gates in theTarget. If aTargethas 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')
-
DAGCircuitnow 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 runningtranspile()or running the preset pass managers returned bygenerate_preset_pass_manager()atoptimization_level=3as optimization level 3 internally deep copies theDAGCircuitfor each iteration of the optimization loop in theoptimizationstage. -
A new method,
DAGCircuit.make_physical(), is provided, which efficiently replaces the qubits in theDAGCircuitwith 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 twoDAGCircuitinstances 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
BasePassManagercan now modify theirproperty_setattribute 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
OptimizationMetricwhich specifies the optimization criterion in theHighLevelSynthesispass. 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_Tif a Clifford+T basis set is detected. However, this class can not be currently set manually when runningtranspile()or running a preset pass manager generated bygenerate_preset_pass_manager(). -
Added a new argument
optimization_metricto the constructor forHighLevelSynthesistranspiler pass which takes anOptimizationMetricobject. When set toCOUNT_T, the pass chooses decompositions that are more suitable for the Clifford+T gate set. -
The default high-level synthesis plugins for
ModularAdderGateandMultiplierGateproduce better T-counts when transpiling into Clifford+T basis set. -
The default high-level synthesis plugin for
MCXGateproduces 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
InverseCancellationtranspiler 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
InverseCancellationconstructor,run_default, which when set toTruewill 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. -
TranspileLayouthas two new methods:from_property_set()andwrite_into_property_set(), which formalize the current ad-hoc structure of transpilation properties, and how they are converted into aTranspileLayout. This makes it possible for passes during a transpiler pipeline to access what theTranspileLayoutwill 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 theDAGCircuittranspiler intermediate representation, and required by passes to be kept in sync with the rest of theDAGCircuit. -
Re-enabled running
VF2PostLayouttranspiler pass in theoptimizationstage foroptimization_level=3with thetranspile()function and the generated pass manager objects returned by thegenerate_preset_pass_manager()function. The pass runs withstrict_direction=Trueafter 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 themplandtextbackends for the circuit drawerqiskit.visualization.circuit_drawer()andqiskit.circuit.QuantumCircuit.draw(). When this option is set toTrue, the drawer will draw an arrow from themeasurebox to the classical bits below. This was the previous behavior in the drawers. If it is set toFalse, no arrows will be drawn and instead the classical register and bit being used by themeasurewill be indicated inside themeasurebox. This allowsmeasureboxes 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~/.qiskitdirectory, in the filesettings.conf. Under the[Default]heading, a user can entercircuit_measure_arrows = Falseto 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
-
The circuit definition of
ModularAdderGatehas been upgraded to useadder_modular_v17(). To obtain the old behaviour, use the definition ofadder_qft_d00()instead. -
The methods
XGate.control(),CXGate.control(),CCXGate.control(),C3XGate.control(),C4XGate.control(), andMCXGate.control()no longer return anAnnotatedOperationwhen the argumentannotatedis set toTrue, and instead return the same explicitMCXGatethat one would get whenannotatedisFalse. This is consistent with how the argumentannotatedis used across the standard circuit library, where we generally avoid introducing anAnnotatedOperationwhen a native gate class is already available. In practice, this leads to more efficient decompositions of control-annotated gates in the MCX family.
C API Upgrade Notes
-
Most usage of
uintptr_thas been changed tosize_t. Specifically the following usages have been changed:QkOpCount.countQkOpCounts.lenQkObsTerm.len- The return type of
qk_circuit_num_instructions(). - The
indexargument inqk_circuit_get_instruction(). - The
boundariesargument inqk_obs_new(). - The return types of
qk_obs_num_terms(),qk_obs_len(), andqk_obs_boundaries(). - The return type of
qk_target_entry_num_properties(), andqk_target_num_instructions().
This change was necessary because the tool used to generate the C header files from Qiskit’s rust code, cbindgen was using
uintptr_tby default in places wheresize_tmore accurately aligned with the type usage in Rust. This change was made to make it more clear how the values in the C API are intended to be used.
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 theversionargument to specify an earlier format version.
Quantum Information Upgrade Notes
SparsePauliOp.assign_parameters()will now set the dtype of the outputcoeffsarray tocomplexif all parameters are fully bound to numeric values.
Synthesis Upgrade Notes
- The default values of the arguments
opt_a1andopt_a2ofqs_decomposition()are nowopt_a1 = Noneandopt_a2 = None. The new defaults will choose the optimal value based on whether the input unitary for thematargument is controlled or not to result in the minimalCXGatecount by default. If you require the previous behavior you can explicitly callopt_a1 = Trueandopt_a2 = Trueto maintain the same behavior as previous releases.
Transpiler Upgrade Notes
- The circuit
PassManagernow always sets the propertiesoriginal_circuit_indicesandnum_input_qubitsbefore 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
-
The following classes in the circuit library are deprecated as of Qiskit 2.2 and will be removed in Qiskit 3.0. They have been replaced with modern gate equivalents.
Bug Fixes
-
ApplyLayoutwill now correctly handle the case of applying a zero-qubitLayout. Previously, it would claim that no layout had been set, even if the"layout"field of thePropertySetwas equal toLayout(). -
Fixed memory leakage issues during the creation of a
QkOpCountsinstance and during any calls toqk_opcounts_clear()whenever an empty instance is passed. -
Previously one could define an invalid
PauliEvolutionGatefrom 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
VF2Layouttranspiler 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 thecallbackfunction 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
CommutativeInverseCancellationtranspiler pass. The pass now works correctly on circuits containingCliffordoperations, control-flow operations, and non-invertible operations such asInitialize.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_basedis set toTrue, and the operation does not act on more thanmax_qubitsqubits, then a matrix-based check is also performed. This slightly improves the reduction potential of the pass. -
Fixed several issues in the
CommutativeCancellationtranspiler pass (and thereby intranspile()), 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 , incorrectly produced a phase shift of . -
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 byDAGCircuit.topological_nodes()ortopological_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 toCommutativeCancellation. -
Fixed a bug in
MCXGate.control(), where adding more controls to an open-controlledMCXGatedid not take thectrl_stateof the controlled MCX gate into account, thus leading to an incorrectctrl_stateof the extended MCX gate. Note that the explicit MCX classesCXGate,CCXGate,C3XGate, andC4XGatewere 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,PhaseOracleGateandBitFlipOracleGatewhere trying to load from dimacs file raised aTypeError. -
Fixed a bug in the
ElidePermutationstranspiler pass, where the qubit mapping was not updated correctly in the presence ofPermutationGates, leading to incorrect circuits and updates to the pass manager’s property set. -
Built-in transpiler passes that set the
final_layoutproperty will now correctly handle updating this field if it was already set. This can be observed as the methodTranspileLayout.routing_permutationnow returning a correct permutation after running more than one pass that setsfinal_layout.This did not affect any normal calls to
transpile()orgenerate_preset_pass_manager()using Qiskit’s built-in plugins; no pipeline constructed in this form would attempt to setfinal_layoutmore than once. -
Fixed a bug in the
HighLevelSynthesispass 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 withoperation_nameandparametersarguments that matched an existing instruction. -
Fixed the behavior of the
max_trialsargument forVF2Layoutwhen set toNoneor 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 toNoneand as accepting negative values to specify an unbounded search. However in the 2.1.0 this behavior was incorrectly changed so thatNoneran 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
MCXSynthesis1DirtyKG24andMCXSynthesis2DirtyKG24forMCXGate, where the plugins did not consider available clean auxiliary qubits as available dirty auxiliary qubits. In particular, the pluginMCXSynthesis2DirtyKG24did not apply when one clean and one dirty auxiliary qubits were available. -
The
PauliEvolutionGate.to_matrix()method now returns the exact matrix exponential , where is theoperatorand thetimepassed to the gate. This fixes an unexpected behavior, since thePauliEvolutionGateis documented to represent the exact time evolution, but previously the matrix was dependent on how the compiler approximates the time evolution. Theto_matrixmethod is now consistent with the documentation. -
Fixed a performance regression when incrementally building
ParameterExpressionfrom 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
QFTcircuit. Previously, whenQFT.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
PauliEvolutionSynthesisRustiqplugin that produced incorrect circuits in the case that the operator of aPauliEvolutionGatecontains objects of typeSparseObservable.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 aTargetobject provided when thebackendargument 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 tozeroare not allowed and now invoke a graceful failure. -
qiskit.circuit.library.quantum_volume()was updated to handle anumpy.random.Generatoras input for itsseedargument. Previously, such a generator argument would result in aTypeError. -
QuantumCircuit.compose()will now correctly remap anyway variables and stretches used inDelayinstructions when thevar_remapargument is specified. -
SabreLayoutuses a “dense subset” layout as one of its trials, following the same algorithm asDenseLayout. 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:- 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.
- 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 thefilter_ancillas=Trueargument if the virtual qubits in theinitial_layoutwere not specified by the constructor in index order. -
VF2LayoutandVF2PostLayoutwill now correctly include (arbitrary) layout assignments for completely idle qubits. Previously this might have been observed by calls toTranspileLayout.initial_index_layout()failing after a compilation. -
Fixed an issue in the
VF2PostLayouttranspiler pass. In certain situations when the pass is being run with the argumentstrict_direction=Truethere 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. Instrict_direction=Falsemode there is an optimized search implementation for these problems, but the additional constraints forstrict_direction=Truedon’t make this approach viable. Instead in these casesVF2PostLayoutwill now skip the search since the layout problem isn’t viable for the pass.
Other Notes
- The implementation of Sabre routing used by
SabreLayoutandSabreSwapnow 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 thelookaheadheuristic 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.