Qiskit SDK 2.2 release notes
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
atoptimization_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()
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
pickle
support for theSabreSwap
where aSabreSwap
instance 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 providedatol
andrtol
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 Pythontranspile()
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 theTarget
you can add the optional argument angle_bounds to specify the higher and lower bounds for parameterized rotation gates. A new transpiler passWrapAngles
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
-
Added new standalone transpiler pass functions to the C API. These functions take in a
QkCircuit
to run the pass on and are equivalent to calling an instantiated pass. -
The
QkTarget
type can now represent targets that support parameterizedQkGate
types 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 aQkObs
observable. This function takes the observable, the new qubit indices, and an output number of qubits. Importantly, this function allows applying transpile layouts, usually given asQkTranspileLayout
by 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
QkTranspileLayout
to 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
Duration
class has gained a newps
variant, 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 differentParameterExpression
instances to numeric values using the same mappings dictionary. The existingbind()
method has a large amount of overhead and unnecessary allocations, since it always returns aParameterExpression
for typing consistency.
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 usingqiskit.qasm3.dumps()
. Users can now define custom instructions (e.g., aCustomMeasure
that acts on a qubit and returns a classical bit) and specify their behavior usingDefcalInstruction
. These defcals are passed to the exporter via theimplicit_defcals
argument 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_version
in 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_version
than theminimum_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
-
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,Pauli
andPauliList
, respectively. -
Added the
PhasedQubitSparsePauli
andPhasedQubitSparsePauliList
classes. These classes represent a single Pauli operator and a list of Pauli operators respectively stored in qubit-sparse format. These classes are very similar toQubitSparsePauli
andQubitSparsePauliList
, 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
ModularAdderGate
that requires no ancillary qubits and has better CX count compared toadder_qft_d00()
: -
Added a new synthesis algorithm,
synth_mcx_noaux_hp24()
, forMCXGate
that 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 ofCXGate
s of a general n-qubit unitary byCXGate
s. With the improved decomposition, a general 3-qubit unitary can be decomposed using 19CXGate
s (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
ModularAdderSynthesisDefault
has 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
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 aTranspilerError
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 argumentfix_clifford
. WhenFalse
(non-default), the returned circuit contains onlyPauliEvolutionGate
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
forMCXGate
objects. Furthermore, the default synthesis pluginMCXSynthesisDefault
forMCXGate
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 aTarget
could 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 anRZGate
with any value for theta were allowed. While ifRZGate(math.pi)
were added to the target that would indicateRZGate
that only is the only allowed value on the target. This new feature enables restricting the allowed angles to be anyfloat
value between a an inclusive bound. For example, you can addRZGate(math.pi)
to aTarget
and 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_bounds
keyword 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_REGISTRY
registry that will tell the transpiler andWrapAngles
pass how to adjust gates for angle bounds. The callback function will take a list of arbitraryfloat
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 aDAGCircuit
that represents an equivalent circuit for the gate with that angle but respecting the angle bounds and otherTarget
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, andTarget.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 theTarget.instruction_supported()
method. When set toTrue
(the default) the method will check if the specifiedparameters
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 theTarget
. If aTarget
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')
-
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 runningtranspile()
or running the preset pass managers returned bygenerate_preset_pass_manager()
atoptimization_level=3
as optimization level 3 internally deep copies theDAGCircuit
for each iteration of the optimization loop in theoptimization
stage. -
A new method,
DAGCircuit.make_physical()
, is provided, which efficiently replaces the qubits in theDAGCircuit
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 twoDAGCircuit
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 theirproperty_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 theHighLevelSynthesis
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 runningtranspile()
or running a preset pass manager generated bygenerate_preset_pass_manager()
. -
Added a new argument
optimization_metric
to the constructor forHighLevelSynthesis
transpiler pass which takes anOptimizationMetric
object. 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
ModularAdderGate
andMultiplierGate
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 toTrue
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()
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 theTranspileLayout
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 theDAGCircuit
transpiler intermediate representation, and required by passes to be kept in sync with the rest of theDAGCircuit
. -
Re-enabled running
VF2PostLayout
transpiler pass in theoptimization
stage foroptimization_level=3
with thetranspile()
function and the generated pass manager objects returned by thegenerate_preset_pass_manager()
function. The pass runs withstrict_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 thempl
andtext
backends for the circuit drawerqiskit.visualization.circuit_drawer()
andqiskit.circuit.QuantumCircuit.draw()
. When this option is set toTrue
, the drawer will draw an arrow from themeasure
box 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 themeasure
will be indicated inside themeasure
box. This allowsmeasure
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 filesettings.conf
. Under the[Default]
heading, a user can entercircuit_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
-
The circuit definition of
ModularAdderGate
has 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 anAnnotatedOperation
when the argumentannotated
is set toTrue
, and instead return the same explicitMCXGate
that one would get whenannotated
isFalse
. This is consistent with how the argumentannotated
is used across the standard circuit library, where we generally avoid introducing anAnnotatedOperation
when 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_t` has been changed to
size_t
. Specifically the following usages have been changed:QkOpCount::count
QkOpCounts::len
QkObsTerm::len
- The return type of
qk_circuit_num_instructions()
. - The
index
argument inqk_circuit_get_instruction()
. - The
boundaries
argument 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_t
by default in places wheresize_t
more 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 theversion
argument to specify an earlier format version.
Quantum Information Upgrade Notes
SparsePauliOp.assign_parameters()
will now set the dtype of the outputcoeffs
array tocomplex
if all parameters are fully bound to numeric values.
Synthesis Upgrade Notes
- The default values of the arguments
opt_a1
andopt_a2
ofqs_decomposition()
are nowopt_a1 = None
andopt_a2 = None
. The new defaults will choose the optimal value based on whether the input unitary for themat
argument is controlled or not to result in the minimalCXGate
count by default. If you require the previous behavior you can explicitly callopt_a1 = True
andopt_a2 = True
to maintain the same behavior as previous releases.
Transpiler Upgrade Notes
- The circuit
PassManager
now always sets the propertiesoriginal_circuit_indices
andnum_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
-
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
-
ApplyLayout
will 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 thePropertySet
was equal toLayout()
. -
Fixed memory leakage issues during the creation of a
QkOpCounts
instance and during any calls toqk_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 thecallback
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 containingClifford
operations, 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_based
is set toTrue
, and the operation does not act on more thanmax_qubits
qubits, then a matrix-based check is also performed. This slightly improves the reduction potential of the pass. -
Fixed several issues in the
CommutativeCancellation
transpiler 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-controlledMCXGate
did not take thectrl_state
of the controlled MCX gate into account, thus leading to an incorrectctrl_state
of the extended MCX gate. Note that the explicit MCX classesCXGate
,CCXGate
,C3XGate
, andC4XGate
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
andBitFlipOracleGate
where trying to load from dimacs file raised aTypeError
. -
Fixed a bug in the
ElidePermutations
transpiler pass, where the qubit mapping was not updated correctly in the presence ofPermutationGate
s, 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 methodTranspileLayout.routing_permutation
now 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_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 withoperation_name
andparameters
arguments that matched an existing instruction. -
Fixed the behavior of the
max_trials
argument forVF2Layout
when set toNone
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 toNone
and as accepting negative values to specify an unbounded search. However in the 2.1.0 this behavior was incorrectly changed so thatNone
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
andMCXSynthesis2DirtyKG24
forMCXGate
, where the plugins did not consider available clean auxiliary qubits as available dirty auxiliary qubits. In particular, the pluginMCXSynthesis2DirtyKG24
did 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 theoperator
and thetime
passed to the gate. This fixes an unexpected behavior, since thePauliEvolutionGate
is documented to represent the exact time evolution, but previously the matrix was dependent on how the compiler approximates the time evolution. Theto_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, 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
PauliEvolutionSynthesisRustiq
plugin that produced incorrect circuits in the case that the operator of aPauliEvolutionGate
contains 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 aTarget
object provided when thebackend
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 tozero
are not allowed and now invoke a graceful failure. -
qiskit.circuit.library.quantum_volume()
was updated to handle anumpy.random.Generator
as input for itsseed
argument. Previously, such a generator argument would result in aTypeError
. -
QuantumCircuit.compose()
will now correctly remap anyway variables and stretches used inDelay
instructions when thevar_remap
argument is specified. -
SabreLayout
uses 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=True
argument if the virtual qubits in theinitial_layout
were not specified by the constructor in index order. -
VF2Layout
andVF2PostLayout
will 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
VF2PostLayout
transpiler pass. In certain situations when the pass is being run with the argumentstrict_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. Instrict_direction=False
mode there is an optimized search implementation for these problems, but the additional constraints forstrict_direction=True
don’t make this approach viable. Instead in these casesVF2PostLayout
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
andSabreSwap
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 thelookahead
heuristic components.
2.1.0rc1
New Features
-
Added C++ support for the C API. The generated header now allows calling objects and functions from C++ directly. For example, a 100-qubit observable with the term XYZ on the first 3 qubits can be constructed as
#include <iostream> #include <complex> #include <vector> #include <qiskit.h> int main() { uint32_t num_qubits = 100; // Use smart pointer with custom deleter to manage QkObs memory QkObs *obs = qk_obs_zero(num_qubits); // Construct the observable term std::complex<double> coeff = 2.0; std::vector<QkBitTerm> bit_terms = {QkBitTerm_X, QkBitTerm_Y, QkBitTerm_Z}; std::vector<uint32_t> indices = {0, 1, 2}; QkObsTerm term { .coeff = coeff, .len = bit_terms.size(), .bit_terms = bit_terms.data(), .indices = indices.data(), .num_qubits = num_qubits }; qk_obs_add_term(obs.get(), &term); // Print observable properties std::cout << "num_qubits: " << qk_obs_num_qubits(obs) << "\n"; std::cout << "num_terms: " << qk_obs_num_terms(obs) << "\n"; qk_obs_free(obs); return 0; }
Note that
std::complex<double>
is compatible withQkComplex64
. -
Improved synthesis of multi-controlled
CZGate
gates. -
Improved the default plugin for synthesizing
AnnotatedOperation
objects. The improvement is especially useful when creating and transpiling controlled circuits with controlled gates within them. For example:from qiskit.circuit import QuantumCircuit from qiskit.circuit.library import CXGate from qiskit.compiler import transpile inner = QuantumCircuit(5) inner.append(CXGate().control(3, annotated=True), [0, 1, 2, 3, 4]) controlled_inner_gate = inner.to_gate().control(2, annotated=True) qc = QuantumCircuit(15) qc.append(controlled_inner_gate, [0, 1, 2, 3, 4, 5, 6]) qct = transpile(qc, basis_gates=["cx", "u"])
This code creates a quantum circuit
qc
that contains a 2-controlled quantum circuit with a 3-controlled CX-gate within it. With the improvement, the number of CX-gates in the transpiled circuit is reduced from378
to30
. Note that by specifyingannotated=True
when defining control logic, the controlled gates are created as annotated operations. This avoids eager synthesis, allows the transpiler to detect thatcontrolled_inner_gate
is equivalent to a 6-controlled X-gate, and to choose the best synthesis method available for multi-controlled X-gates, in particular utilizing available ancilla qubits. -
The function
adder_qft_d00()
used for synthesizeModularAdderGate
andHalfAdderGate
gates now accepts an addional parameterannotated
. IfTrue
, the inverse-QFT-gate within the adders is implemented as an annotated operations, allowing the transpiler to apply additional optimizations. -
Incorporated the
SparseObservable
class with theObservablesArray
class. * There is no change in behavior of existing methods ofObservablesArray
, except for the methodcoerce_observable()
, whose return type is changed to SparseObservable. * The new methodapply_layout()
performs an apply_layout operation on each observable in the array. * The new methodequivalent()
returns True if two arrays store equivalent observables. * The new methodsget_sparse_observable()
andsparse_observables_array()
return information about the contents of the array. * Estimator pubs can now be defined usingSparseObservable
objects, in addition to the existing options of str, Pauli, SparsePauliOp, and Mapping[Union[str, Pauli], float]. However projective observables are not yet supported when executing circuits. Projective observables are 0, 1, +, -, r, l. Currently only Pauli’s are supported: I, X, Y, Z. -
Introduces the
QubitSparsePauli
,QubitSparsePauliList
, andPauliLindbladMap
classes. These classes represent, respectively, a single phase-less Pauli operator, a list of phase-less Pauli operators, and a Pauli Lindblad map, all stored in qubit-sparse format.
C API Features
-
Added support for querying the Qiskit version via the C API using the following definitions:
QISKIT_VERSION
: The version as human readablechar*
(e.g."2.2.0"
).QISKIT_VERSION_HEX
: The version as HEX.QISKIT_VERSION_MAJOR
: The major version number.QISKIT_VERSION_MINOR
: The minor version number.QISKIT_VERSION_PATCH
: The patch version number.QISKIT_RELEASE_LEVEL
: The release level, e.g.0xF
for final or0xC
for release candidates.QISKIT_RELEASE_SERIAL
: The release serial, e.g.1
for the first release candidate.QISKIT_GET_VERSION_HEX
: A macro returning the version a HEX, given major, minor, patch, level, and serial.
For example, to check if the current version is at least 2.2.0, you can use:
if (QISKIT_VERSION_HEX >= QISKIT_GET_VERSION_HEX(2, 2, 0, 0xF, 0)) { // Code for version 2.2.0 (final) or later }
Circuits Features
-
Added a function
random_circuit_from_graph()
that generates a random circuit that induces the same interaction graph as the interaction graph specified by interaction_graph.The probability of randomly drawing an edge from the interaction graph as a two-qubit gate can be set by the user in the weight attribute of an edge in the input interaction graph. If the user does not set the probability, each edge is drawn uniformly, i.e. each two-qubit gate represented by an edge in the interaction graph has the same probability of getting added to the random circuit. If only a subset of edge probabilities are set, ValueError will be raised.
This is an example where ‘cp_map’ is a list of edges with some arbitrary weights.
from qiskit.circuit.random.utils import random_circuit_from_graph import rustworkx as rx pydi_graph = rx.PyDiGraph() n_q = 5 cp_map = [(0, 1, 0.18), (1, 2, 0.15), (2, 3, 0.15), (3, 4, 0.22)] pydi_graph.add_nodes_from(range(n_q)) pydi_graph.add_edges_from(cp_map) # cp_map can be passed in directly as interaction_graph qc = random_circuit_from_graph(interaction_graph = pydi_graph, min_2q_gate_per_edge = 1, max_operands = 2, measure = True, conditional = True, reset = True, seed = 0, insert_1q_oper = True, prob_conditional = 0.21, prob_reset = 0.1) qc.draw(output='mpl')
-
Added
QuantumCircuit.has_control_flow()
to check if a circuit contains any control flow operations. -
A new module
qiskit.circuit.annotation
and principle objectAnnotation
have been added.Annotations are a way of tagging instructions (currently only
BoxOp
, in the initial implementation) with local, user-custom data. This data is intended to be consumed by custom transpiler passes. Annotations provide a way to attach data to specific instructions, rather than using the global-context objectPropertySet
during compilation.All
Annotation
objects have anamespace
. This string key is used for lookups, so consumers can tell if they handle a particular annotation or not. There are currently no methods for querying any abstract semantics of anAnnotation
subclass, but these are expected to expand in the future.See
qiskit.circuit.annotation
for a full discussion of the capabilities and use cases. -
BoxOp
instances (created byQuantumCircuit.box()
) can now be annotated with customAnnotation
instances. The equality of two boxes depends on the annotations being equal.Typically, this is achieved by passing a list of annotations as the sole positional argument when using
QuantumCircuit.box()
in context-manager form:from qiskit.circuit import annotation, QuantumCircuit class MyAnnotation(annotation.Annotation): namespace = "my.annotation" def __eq__(self, other): return isinstance(other, MyAnnotation) qc = QuantumCircuit() with qc.box([MyAnnotation()]): pass
-
The C API for Qiskit has been extended with support for building and interacting with quantum circuits.
-
Improve the synthesis of a multi-controlled
U1Gate
, so that it will not grow exponentially with the number of controls. -
The
BoxOp.duration
field can now be anexpr.Expr
node with typeDuration
, just likeDelay.duration
. This includes stretches. -
Adds an equivalence between an
XXPlusYYGate
and its decomposition into anRXXGate
followed by anRYYGate
to the equivalence library.
Primitives Features
- To make
PrimitiveJob
serializable,DataBin
has been updated to be pickleable. As a result,PrimitiveResult
is now also pickleable.
OpenQASM Features
-
qasm3.dump()
andqasm3.dumps()
have a newannotation_handlers
argument, which is used to provide instances ofannotation.OpenQASM3Serializer
to the OpenQASM 3 export process, which can serialize customAnnotation
objects to OpenQASM 3. -
When
qiskit_qasm3_import>=0.6.0
is installed,qasm3.load()
andqasm3.loads()
have a newannotation_handlers
argument, which is used to provide instances ofannotation.OpenQASM3Serializer
to the OpenQASM 3 import process, which can deserialize customAnnotation
objects from OpenQASM 3. This support is currently limited tobox
statements, as this is the only place Qiskit can represent annotations in its data model.
QPY Features
-
QPY version 15 is released, including support for the new
Annotation
objects, with support from external serializers and deserializers. The format allows such serializers to be stateful, and safe places in the binary format are allocated for the custom state objects and custom annotation representations. -
qpy.dump()
andqpy.load()
now have an optionalannotation_factories
argument, which is used to provide constructor functions ofannotation.QPYSerializer
objects to handleAnnotation
subclasses. These must be supplied by the user, similar tometadata_serializer
, as in general, Qiskit cannot know about all possible externally-definedAnnotation
objects. -
Added a new function
get_qpy_version()
to theqpy()
module. This function will inspect a QPY file and retrieve the QPY format version used in the payload. The version is returned as an integer, which can be used for logging or debugging purposes. see #14201.
Synthesis Features
-
Added new synthesis algorithm for
HalfAdderGate
that requires no ancillary qubits and has better CX count compared toadder_qft_d00()
: -
Added new decompositions for
MCXGate
utilizing clean ancillae, improving circuit depth and efficiency:synth_mcx_1_clean_kg24()
, using 1 additional clean ancilla qubitsynth_mcx_1_dirty_kg24()
, using 1 additional dirty ancilla qubitsynth_mcx_2_clean_kg24()
, using 2 additional clean ancillary qubitssynth_mcx_2_dirty_kg24()
, using 2 additional dirty ancillary qubits
Example usage:
from qiskit.synthesis.multi_controlled import synth_mcx_1_clean_kg24 n_ctrls = 10 qc = synth_mcx_1_clean_kg24(n_ctrls) qc.draw()
-
The Quantum Shannon Decomposition (
qs_decomposition()
) includes an optimization that reduces theCXGate
count in the case that the input unitary happens to be a controlled unitary. -
The synthesis function
synth_mcx_1_clean_b95()
now produces a circuit with fewer CX-gates. -
The
SolovayKitaevDecomposition
class has additional arguments in the initializer, which allows it to be directly constructed from a set ofbasis_gates
and adepth
for the basic approximations. -
Added
SolovayKitaevDecomposition.save_basic_approximations()
to save the set of basic approximations the class uses into a binary format. This change, in combination with the new initializer arguments, allows to skip the explicit use ofgenerate_basic_approximations()
and only rely onSolovayKitaevDecomposition
. -
The standard equivalence library has additional equivalences for
CSGate
andCSdgGate
. When transpiling to the [“cx”, “u”] basis set, theBasisTranslator
transpiler pass now uses 2CXGate
gates instead of 3 forCSdgGate
, and in addition reduces the number of single-qubit gates in both cases.
Transpiler Features
-
The function
generate_preset_pass_manager()
now generates a special pass manager when the basis set consists of Clifford+T gates only. Formally, a Clifford+T basis set must contain either aTGate
orTdgGate
(or both), and only Clifford gates in addition. The full list of supported Clifford gates can be obtained usingget_clifford_gate_names()
.For example:
from qiskit.circuit import QuantumCircuit from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager from qiskit.quantum_info import get_clifford_gate_names basis_gates = get_clifford_gate_names() + ["t", "tdg"] pm = generate_preset_pass_manager(basis_gates=basis_gates) qc = QuantumCircuit(1) qc.rx(0.8, 0) qct = pm.run(qc) print(qct.count_ops())
Previously, the generated pass manager was not able to handle the example above, as it was not able to decompose single-qubit rotation gates of type
UGate
into Clifford+T gates. Instead, the new pass manager uses the the Solovay-Kitaev decomposition to approximate single-qubit rotation gates usingH
,T
andTdg
gates, and calls theBasisTranslator
transpiler pass to further translate the gates into the target basis set. The new pass manager also has other changes as to enable a more efficient translation into Clifford+T gates.It is important to note that the specified Clifford+T basis gate set should be universal, or else transpilation might not succeed. While the gate set
["h", "t", "tdg"]``or even ``["h", "t"]
is sufficient for universality, it is recommended to add more Clifford gates to the set if possible, as otherwise the translation might be less efficient. For example, not including S-gate might trigger decomposing S-gates into pairs of T-gates (that is, decomposing Clifford gates into non-Clifford gates, which might not be the desired behavior).Here is an additional slightly larger example:
from qiskit.circuit import QuantumCircuit from qiskit.circuit.library import QFTGate from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager qc = QuantumCircuit(4) qc.append(QFTGate(4), [0, 1, 2, 3]) basis_gates = ["cx", "s", "sdg", "h", "t", "tdg"] pm = generate_preset_pass_manager(basis_gates=basis_gates, optimization_level=2) qc = QuantumCircuit(4) qc.append(QFTGate(4), [0, 1, 2, 3]) qct = pm.run(qc) print(qct.count_ops())
-
Added new high-level-synthesis plugin for synthesizing a
HalfAdderGate
:HalfAdderSynthesisR25
, based onadder_ripple_rv_25()
.
The
HalfAdderSynthesisDefault
has also been updated to follow the following sequence of half adder synthesizers:"HalfAdder.ripple_r25"
when the number of qubits is ,"HalfAdder.ripple_c04"
when 1 ancillary qubit is available,"HalfAdder.ripple_r25"
in all remaining cases. -
Added multiple high-level-synthesis plugins for synthesizing an
MCXGate
:MCXSynthesis1CleanKG24
, based onsynth_mcx_1_clean_kg24()
.MCXSynthesis1DirtyKG24
, based onsynth_mcx_1_dirty_kg24()
.MCXSynthesis2CleanKG24
, based onsynth_mcx_2_clean_kg24()
.MCXSynthesis2DirtyKG24
, based onsynth_mcx_2_dirty_kg24()
.
The
MCXSynthesisDefault
has also been updated to follow the following sequence of MCX synthesizers: “mcx.2_clean_kg24”, “mcx.1_clean_kg24”, “mcx.n_clean_m15”, “mcx.n_dirty_i15”, “mcx.2_dirty_kg24”, “mcx.1_dirty_kg24” “mcx.1_clean_b95”, “mcx.noaux_v24”. -
VF2PostLayout
has been added at the end of the default optimization stage when using optimization level 3. -
Support for creation and interaction with the
Target
have been added to the Qiskit C API.Here’s an example of how it is used:
#include <qiskit.h> #include <math.h> // Create a Target with space for 2 qubit operations QkTarget *target = qk_target_new(2); // Create a Target Entry for a CRX Gate with fixed parameters double crx_params[1] = {3.14}; QkTargetEntry *cx_entry = qk_target_entry_new_fixed(QkGate_CRX, crx_params); // Add mapping between (0, 1) and properties duration of 1.93e-9 and error 3.17e-10. uint32_t qargs[2] = {0, 1}; qk_target_entry_add(entry, qargs, 2, 1.93e-9, 3.17e-10); // Add mapping between (1, 0) and properties duration of 1.27e-9 and no error. uint32_t rev_qargs[2] = {1, 0}; qk_target_entry_add(entry, rev_qargs, 2, 1.27e-9, NAN); // Add CRX entry to the target. QkExitCode result_crx = qk_target_add_instruction(target, entry); // Add global Y gate entry to the target QkExitCode result_crx = qk_target_add_instruction(target, qk_target_entry_new(QkGate_Y));
-
Added a new
OptimizeCliffordT
transpiler optimization pass that merges pairs of consecutiveT
-gates intoS
-gates and pairs of consecutiveTdg
-gates intoSdg
-gates. This optimization is particularly effective for reducing T-count following Solovay-Kitaev decomposition, which produces multiple consecutiveT
orTdg
gates. For example:from qiskit.circuit import QuantumCircuit from qiskit.transpiler.passes import SolovayKitaev, OptimizeCliffordT qc = QuantumCircuit(1) qc.rx(0.8, 0) # Run Solovay-Kitaev pass on qc transpiled = SolovayKitaev()(qc) print(transpiled.count_ops().get("t", 0) + transpiled.count_ops().get("tdg", 0)) # Run Clifford+T optimization optimized = OptimizeCliffordT()(transpiled) print(optimized.count_ops().get("t", 0) + optimized.count_ops().get("tdg", 0))
-
Added the
ContextAwareDynamicalDecoupling
pass, which implements a context-aware dynamical decoupling based on Walsh-Hadamard sequences. The inserted delay sequences will be mutually orthogonal to sequences on neighboring qubits, and take into account control/target spectators of CX and ECR gates. See arXiv:2403.06852 for more information.Example:
from qiskit.circuit.library import QFT from qiskit.transpiler import PassManager from qiskit.transpiler.passes import ALAPScheduleAnalysis, ContextAwareDynamicalDecoupling from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager from qiskit_ibm_runtime.fake_provider import FakeSherbrooke num_qubits = 10 circuit = QFT(num_qubits) circuit.measure_all() target = FakeSherbrooke().target pm = generate_preset_pass_manager(optimization_level=2, target=target) dd = PassManager([ ALAPScheduleAnalysis(target=target), ContextAwareDynamicalDecoupling(target=target), ]) transpiled = pm.run(circuit) with_dd = dd.run(transpiled) print(with_dd.draw(idle_wires=False))
-
Adds
DAGCircuit
attributesnum_stretches
,num_captured_stretches
, andnum_declared_stretches
. -
Added a new unitary synthesis plugin
CliffordUnitarySynthesis
that attempts to syntesize a given unitary gate by checking if it can be represented by a Clifford, in which case it returns a circuit implementing this unitary and consisting only of Clifford gates.The plugin is invoked by the
UnitarySynthesis
transpiler pass when the parametermethod
is set to"clifford"
.In addition, the parameter
plugin_config
ofUnitarySynthesis
can be used to pass the following plugin-specific parameters:- min_qubits: the minumum number of qubits to consider (the default value is 1).
- max_qubits: the maximum number of qubits to consider (the default value is 3).
For example:
import math from qiskit.circuit import QuantumCircuit from qiskit.circuit.library import UnitaryGate from qiskit.quantum_info import Operator from qiskit.transpiler.passes import UnitarySynthesis # clifford unitary over 2 qubits c2 = QuantumCircuit(2) c2.h(0) c2.rz(math.pi / 4, 1) c2.rz(math.pi / 4, 1) c2.sdg(1) uc2 = UnitaryGate(Operator(c2).data) # non-clifford unitary over 2 qubits n2 = QuantumCircuit(2) n2.h(0) n2.rz(math.pi / 4, 1) n2.sdg(1) un2 = UnitaryGate(Operator(n2).data) # quantum circuit with two unitary gates qc = QuantumCircuit(3) qc.append(uc2, [2, 1]) qc.append(un2, [0, 2]) transpiled = UnitarySynthesis(method="clifford")(qc)
Executing the code above resynthesized the first unitary gate into Clifford gates, while the second gate remains unchanged.
If we modify the example above as follows:
config = {"min_qubits": 3} transpiled = UnitarySynthesis(method="clifford", plugin_config=config)(qc)
then both unitary gates remain unchanged.
Visualization Features
-
Introduced custom styles for the dag_drawer() function. This allows you to pass a dictionary to the style parameter with custom attributes that changes the style of the DAG the function returns. For example:
from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit from qiskit.converters import circuit_to_dag from qiskit.visualization import dag_drawer q = QuantumRegister(3, 'q') c = ClassicalRegister(3, 'c') circ = QuantumCircuit(q, c) circ.h(q[0]) circ.cx(q[0], q[1]) circ.measure(q[0], c[0]) circ.rz(0.5, q[1]).c_if(c, 2) dag = circuit_to_dag(circ) style = { "inputnodecolor": "pink", "outputnodecolor": "lightblue", "opnodecolor": "red", } dag_drawer(dag, style=style)
-
Box instructions whose vertical spans do not overlap are now rendered in the same vertical slice, when possible.
Upgrade Notes
-
The
python-dateutil
library is no longer a dependency of Qiskit. Since Qiskit 2.0.0 nothing in the library was actively using the library anymore and Qiskit didn’t actually depend on the library anymore. This release removes it from the dependency list so it is not automatically installed as a prerequisite anymore. If you were relying on Qiskit to install dateutil for you as a dependency you will now need to ensure you’re manually installing it (which is best practice for direct dependencies). -
sympy
is no longer a requirement for installing Qiskit. After the migration to a Rust based symbolic engine for theParameterExpression
class the uses of SymPy are isolated to some visualization utilities, theTemplateOptimization
transpiler pass,ParameterExpression.sympify()
(which is explicitly for SymPy interoperability) andSparsePauliOp.simplify()
if using parameterized coefficients. This functionality is not the most commonly used so SymPy is now treated as an optional dependency and those functions will raise aMissingOptionalLibrary
exception if they’re used and SymPy is not installed. -
The dependency on
symengine
which was used for buildingParameterExpression
objects has been removed. It has been replaced by a internal symbolic engine and is no longer required for core functionality in Qiskit. The only exception is that symengine was embedded into QPY formats 10, 11, and 12 so it is still required if you are deserializing those formats. The dependency on symengine forqpy.load()
was made explicitly optional in 2.0.0, but if you were previously relying on symengine getting installed by default for this functionality you will now need to manually install it to load the payload.
Circuits Upgrade Notes
-
The circuit definition of
HalfAdderGate
has been upgraded to useadder_ripple_r25()
. To obtain the old behaviour, use the definition ofadder_qft_d00()
instead. -
excitation_preserving()
andExcitationPreserving
now use a singleXXPlusYYGate
where before it used anRXXGate
followed by anRYYGate
. -
The control method of
UnitaryGate
now usesqs_decomposition()
instead ofIsometry
for decomposition. This change reduces the number ofCXGate
by approximately 2x. -
The definitions of certain standard gates have been updated according to the following principles:
- When available, a definition using Clifford gates is preferred over one that includes non-Clifford gates.
- When available, a definition using Clifford+T gates is preferred over one that includes
UGate
. - The use of
PhaseGate
is preferred overU1Gate
. - The use of
UGate
is preferred overU2Gate
andU3Gate
.
Crucially, the following invariant still holds: by recursively expanding gate definitions, any gate can be ultimately expressed using only the
["cx", "u"]
basis. -
Qiskit now uses its own, Rust-based symbolic expression library to implement the internals of
ParameterExpression
andParameter
. As this is a new implementation of the core symbolic math engine used forParameterExpression
there might be minor differences in the exact behavior of some functionality. It should always produce equivalent results for the documented API. Please open an issue if there are any problems with correctness found.
Synthesis Upgrade Notes
-
The synthesis function
synth_mcx_1_clean_b95()
now produces a circuit with fewer layers of wrappings. -
The serialization format in for basic approximations in the Solovay Kitaev algorithms has been changed from
.npy
to another binary format, based on Rust’sserde
andbincode
. All routines loading basic approximations (such asgenerate_basic_approximations()
,SolovayKitaevDecomposition.load_basic_approximations()
or the initializer ofSolovayKitaev
) still support loading the legacy format. Any new file, however, will be stored in the new format. If you relied on the old format, downgrade Qiskit to <2.2 and store the required files. -
The default values for
SolovayKitaev
(and related classes) have increased todepth=12
andreps=5
. This is due to the underlying implementation now being in Rust, which allows us to increase the default precision, while still being significantly faster than the previous Python version.
Transpiler Upgrade Notes
-
The built-in layout plugins for the present pass managers will no longer contain their principal component (e.g. a
SabreLayout
instance for the “sabre” stage) if no coupling constraints are provided. Previously, the plugins would construct invalid instances of their layout passes, under an assumption that separate logic would prevent the passes from executing and raising exceptions.This should have no meaningful effect on the use of the preset pass managers or the plugins, since it was already never valid to call the passes in an invalid state .
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.
C API Deprecations
- Deprecated the macro
QISKIT_VERSION_NUMERIC
in favor of the direct replacementQISKIT_GET_VERSION_HEX
. This macro can be used to generate a hexanumeric version format from decimal version numbers.
Circuits Deprecations
-
The circuit library underwent a refactoring in the Qiskit 1.3 release, in which alternatives for objects of type
QuantumCircuit
were provided that are either aInstruction
or a Python function for construction. This refactoring allows the compiler to reason about high-level instructions, and reduces the overhead for circuits that do not require high-level optimizations.All
QuantumCircuit
subclasses are now deprecated in favor of their alternatives introduced in Qiskit 1.3. As part of this, theBlueprintCircuit
is also deprecated. All have an extended deprecation period and will only be removed for Qiskit 3.0.The
BlueprintCircuit
does not have a direct replacement, instead use aQuantumCircuit
directly or a function that generates circuits. Seeqiskit.circuit.library
and the migration guide for more details, but some common circuits have the following replacements:QFT
-->
QFTGate
TwoLocal
-->
n_local()
(this is not a typo, this function covers theNLocal
andTwoLocal
functionality)EfficientSU2
-->
efficient_su2()
RealAmplitudes
-->
real_amplitudes()
ZZFeatureMap
-->
zz_feature_map()
QuantumVolume
-->
quantum_volume()
EvolvedOperatorAnsatz
-->
evolved_operator_ansatz()
MCXGrayCode
-->
synth_mcx_gray_code()
MCXRecursive
-->
synth_mcx_n_dirty_i15()
MCXVChain
-->
synth_mcx_n_clean_m15()
-
The deprecated tuple-like interface for
CircuitInstruction
was not removed in this release as originally planned. It will be removed in Qiskit 3.0.0 instead. Instead, use theoperation
,qubits
, andclbits
named attributes.
Security Issues
- Fixed a security vulnerability in
qpy.load()
when loading payloads that usesympy
to serializeParameterExpression
objects and other symbolic expressions. This potentially includes any QPY payload using QPY version < 10, and optionally 10, 11, and 12 depending on the symbolic encoding used in the serialization step (qpy.dump()
).
Bug Fixes
-
Fixes a bug where style=plain did not show circuit labels for the nodes of the DAG.
-
Fixed a visualization bug in the text circuit drawer where post-transpilation control-flow operations could have their closing “bracket” rendered vertically out of order. See #14271 for more detail.
-
Fixed a bug in the
UnitarySynthesis
transpiler pass, where the pass ignored thesynth_gates
parameter and only synthesized theunitary
gates. The pass now correctly synthesizes all gates specified in thesynth_gates
parameter. Fixed #14343. -
Fixed edge-cases in the
Makefile
configuration for Windows, where the pre-defined environment variableOS
did not match the output of theuname -s
command. -
Fixed the name of the
OrGate
, that was set to “and” instead of “or”, and could have possibly led to several problems. -
Fixed an issue in the
ConsolidateBlocks
transpiler pass where it would fail to consolidate some blocks if the KAK gate selected (either directly or via the target) is supercontrolled and notCXGate
. Fixed #14413 -
Fixed a bug in
DAGCircuit
that would cause outputVar
nodes to become input nodes duringdeepcopy
and pickling. -
Fixed an oversight in the
Target
class where setting a new value for thedt
attribute and subsequently callingtarget.durations()
would not show the updateddt
value in the returnedInstructionDurations
object. This is now fixed through an invalidation of the internal target instruction durations cache in thedt
setter. -
Added missing
repr
support forDuration
. -
Added missing support for Python pickling of
Duration
. This was preventing parallel transpilation of circuits withdelay()
instructions that use duration expressions. -
Fixed a problem in
BasisTranslator
transpiler pass, where the global phase of the DAG was not updated correctly. Fixed #14074. -
Fixed a problem in
HighLevelSynthesis
transpiler pass that caused it to erroneously terminate early on certain circuits with control flow operations. Fixed #14338 -
Fixed the construction of Instantaneous Quantum Polynomial time (IQP) circuits in
IQP
and byiqp()
. The previous implementation incorrectly used powers of the gate instead of powers of the gate. -
Fixed a bug in the
PauliEvolutionSynthesisDefault
andPauliEvolutionSynthesisRustiq
plugins that modified the .synthesis attribute of the original circuit when settingpreserve_order=False
. The behavior of the plugins has been restored and the original circuit is now preserved throughout the transpilation pipeline. -
Fixed a bug in meth
qpy.load
where it could fail to deserialize circuits whose parameters had been re-assigned to parameters with the same names. Fixed #13720, #13720, and #13720. -
Fixed a bug in QPY (
qiskit.qpy
) where circuits containing gates of classMCMTGate
would fail to serialize. See #13965. -
SabreLayout
andSabreSwap
will no longer panic when applying the routing result to a circuit that usesexpr.Var
orStretch
objects in a nested control-flow scope. -
SabreLayout
will now correctly propagate a circuit’sname
andmetadata
fields when performing as a joint layout and routing pass. -
Fixed a problem in the
SolovayKitaevSynthesis
unitary synthesis plugin, where repeatedly running the plugin with different basis gates incorrectly reused the basis gates from the first run only. The problem was due to ignoring the basis gates when caching basic approximations, and is now fixed. -
Fixed a problem in the
SolovayKitaev
transpiler pass where the pass could crash due to encountering a 180 degree rotation in the internal recursion, which was not handled correctly. -
Fixed a problem in the
SolovayKitaev
transpiler pass where the generated approximation could have a phase that differs by from the correct value. This resulted due to the internal representation, which requires additional handling to obtain the correct sign of the qubit gate matrix. Fixed #9552 -
GenericBackendV2
now correctly allowsBoxOp
whencontrol_flow=True
. -
When synthesizing an
MCXGate
gate with 3 controls, the synthesis functonsynth_mcx_n_dirty_i15()
used to require one auxiliary qubit, producing a circuit with 5 qubits (3 control, 1 target, and 1 auxiliary). However, the actual synthesis algorithm does not make use of this auxiliary qubit. This behavior is now fixed: the synthesized circuit is over 4 qubits (3 control and 1 target), allowing to apply the synthesis function in a slighty larger number of cases. -
Circuits containing delays with stretches (see
QuantumCircuit.add_stretch()
) can now successfully compile using the preset pass-managers (transpile()
andgenerate_preset_pass_manager()
) when targetting a backend that has alignment constraints, for example IBM Quantum Eagle devices likeibm_sherbrooke
. -
The fallback error heuristic in
VF2Layout
andVF2PostLayout
, used when there were no reported error rates, could previously assign errors greater than one, and have unpredictable effects on the resulting layout scores.
Other Notes
-
A new optional extra dependency variant
qpy-compat
was added. This target should be installed if you plan to load qpy files as it installs extra requirements used for loading QPY files using format versions < 13. If you are only using newer qpy format you do no need to install this. By defaultqpy.dump()
only generates QPY >=13. You can install this new optional variant withpip install qiskit[qpy-compat]`
-
The relative weights of the “basic” and “lookahead” components of the
SabreSwap
andSabreLayout
heuristics have been modified when extended-set tracking is active (as it always is inSabreLayout
, and is by default inSabreSwap
). The heuristic component relating to the distance between qubits in an individual gate in the front layer now no longer weakens proportional to the number of gates in the front layer; this behavior was a historical choice, but at large circuit sizes, has the accidental effect of causing the front layer to be nearly ignored, which is disastrous for efficiency.The resulting routing improvements should be most noticeable for circuits that can frequently be stratified into layers of more than 20 parallel two-qubit gates.
-
The Matplotlib circuit drawer will now insert less extraneous space inside the left edge when drawing
BoxOp
instances in circuits.
2.0.0rc1
Prelude
This release adds support for stretch
variables to QuantumCircuit
which are used to express relationships between instruction durations. For example, in order to ensure a sequence of gates between two barriers will be left-aligned, whatever their actual durations may be, we can do the following:
from qiskit import QuantumCircuit
from numpy import pi
qc = QuantumCircuit(5)
qc.barrier()
qc.cx(0, 1)
qc.u(pi/4, 0, pi/2, 2)
qc.cx(3, 4)
a = qc.add_stretch("a")
b = qc.add_stretch("b")
c = qc.add_stretch("c")
# Use the stretches as Delay duration.
qc.delay(a, [0, 1])
qc.delay(b, 2)
qc.delay(c, [3, 4])
qc.barrier()
For additional context and examples, refer to the OpenQASM 3 language specification.
New Features
-
Support for the Linux aarch64 platform has been promoted to tier 1 support as documented in:
/guides/install-qiskit#operating-system-support
from its previous support level of tier 2 in the 1.x release series.
-
Introduced a C API to build and interact with sparse observables. While the API surface in this release is fairly small, just covering the
SparseObservable
class this new API lays the foundation for Qiskit’s C interface.The detailed syntax and more information is available under the C API docs, but a minimal example to construct the 100-qubit observable
X0 Y1 Z1
is:#include <complex.h> #include <qiskit.h> #include <stdint.h> #include <stdio.h> int main(int argc, char *argv[]) { // build a 100-qubit empty observable uint32_t num_qubits = 100; QkObs *obs = qk_obs_zero(num_qubits); // add the term 2 * (X0 Y1 Z2) to the observable complex double coeff = 2; QkBitTerm bit_terms[3] = {QkBitTerm_X, QkBitTerm_Y, QkBitTerm_Z}; uint32_t indices[3] = {0, 1, 2}; QkObsTerm term = {coeff, 3, bit_terms, indices, num_qubits}; qk_obs_add_term(obs, &term); // print some properties printf("num_qubits: %u\n", qk_obs_num_qubits(obs)); printf("num_terms: %lu\n", qk_obs_num_terms(obs)); // free the memory allocated for the observable qk_obs_free(obs); return 0; }
Circuits Features
-
Added a new
get_control_flow_name_mapping()
convenience function that returns a mapping of Qiskit’s control-flow operation names to their corresponding class. Example usage:from qiskit.circuit import get_control_flow_name_mapping ctrl_flow_name_map = get_control_flow_name_mapping() if_else_object = ctrl_flow_name_map["if_else"] print(if_else_object)
<class 'qiskit.circuit.controlflow.if_else.IfElseOp'>
-
Added a new method,
QuantumCircuit.estimate_duration()
, to compute the estimated duration of a scheduled circuit output from thetranspiler
. This should be used if you need an estimate of the full circuit duration instead of the deprecatedQuantumCircuit.duration
attribute. -
The new
BitFlipOracleGate
andPhaseOracleGate
have the same interface asPhaseOracle
(except the evaluate_bitstring method). Bit-flip oracle gate synthesizes a bit flip oracle instead of a phase flip oracle, meaning it acts on one additional qubit and can be seen as applying a controlled X operation, where the control is the value of the expression encoded by the oracle.from qiskit import QuantumCircuit from qiskit.circuit.library.bit_flip_oracle import BitFlipOracleGate from qiskit.circuit.library.phase_oracle import PhaseOracleGate qc = QuantumCircuit(5) bool_expr = "(x0 & x1 | ~x2) & x4" oracle = BitFlipOracleGate(bool_expr) qc.compose(oracle, inplace=True) print(qc)
┌─────────────────────┐ q_0: ┤0 ├ │ │ q_1: ┤1 ├ │ │ q_2: ┤2 (x0 & x1 | ~x2)... ├ │ │ q_3: ┤3 ├ │ │ q_4: ┤4 ├ └─────────────────────┘
print(qc.decompose())
q_0: ──o────■────■── │ │ │ q_1: ──┼────o────■── │ │ │ q_2: ──o────o────┼── │ │ │ q_3: ──■────■────■── ┌─┴─┐┌─┴─┐┌─┴─┐ q_4: ┤ X ├┤ X ├┤ X ├ └───┘└───┘└───┘
qc = QuantumCircuit(5) bool_expr = "(x0 & x1 | ~x2) & x4" oracle = PhaseOracleGate(bool_expr) qc.compose(oracle, inplace=True) print(qc)
┌───────────────┐ q_0: ┤0 ├ │ │ q_1: ┤1 ├ │ Phase oracle │ q_2: ┤2 ├ │ │ q_3: ┤3 ├ └───────────────┘ q_4: ─────────────────
print(qc.decompose())
q_0: ─o──■──■─ │ │ │ q_1: ─┼──o──■─ │ │ │ q_2: ─o──o──┼─ │ │ │ q_3: ─■──■──■─ q_4: ─────────
-
Added a new argument
approximation_degree
toCommutationChecker.commute()
andCommutationChecker.commute_nodes()
, which allows to set the approximation threshold for when gates are said to commute. See the docstring ofCommutationChecker
for more detail. -
A new method,
QuantumCircuit.noop()
, allows qubits to be marked as explicitly used within a control-flow builder scope, without adding a corresponding operation to them. -
The classical realtime-expressions module
qiskit.circuit.classical
can now represent constant expressions. TheExpr
class now has a boolconst
attribute which indicates the expression’s const-ness. This allows us to enforce that expressions in certain contexts must be possible to evaluate at compile time.All
Var
expressions are considered to be non-const, while allValue
expressions are const.An expression comprised only of other const expressions is also const:
from qiskit.circuit.classical import expr assert expr.bit_and(5, 6).const
An expression that contains any non-const expression is non-const:
from qiskit.circuit.classical import expr, types assert not expr.bit_and(5, expr.Var.new("a", types.Uint(5)).const
-
The classical realtime-expressions module
qiskit.circuit.classical
can now represent duration using the new typesDuration
.The module
qiskit.circuit
also has a newDuration
class which can be used as a literal value within classical expressions.The
lift()
function can be used to create a value expression from aDuration
instance:from qiskit.circuit import Duration from qiskit.circuit.classical import expr expr.lift(Duration.dt(1000)) # Value(Duration.dt(1000), Duration())
-
The classical realtime-expressions module
qiskit.circuit.classical
can now represent IEEE-754 double-precision floating point values using the new typeFloat
.The
lift()
function can be used to create a value expression from a Python float:from qiskit.circuit.classical import expr expr.lift(5.0) # >>> Value(5.0, Float())
This type is intended primarily for use in timing-related (duration and stretch) expressions. It is not compatible with bitwise or logical operations, though it can be used (dangerously) with these if first explicitly cast to something else.
-
Reduce the number of two-qubit gates when decomposing some multi-controlled single-qubit unitary gates. For example,
- For multi-controlled
YGate
on 10 qubits, we reduce theCXGate
count by 56%, - For multi-controlled
HGate
on 10 qubits, we reduce theCXGate
count by 56%, - For multi-controlled
SXGate
andSXdgGate
on 10 qubits, we reduce theCXGate
count by 80%, - For multi-controlled
UGate
on 10 qubits, we reduce theCXGate
count by 31%.
- For multi-controlled
-
The classical realtime-expressions module
qiskit.circuit.classical
can now represent arithmetic operationsadd()
,sub()
,mul()
, anddiv()
on numeric and timing operands.For example:
from qiskit.circuit import QuantumCircuit, ClassicalRegister, Duration from qiskit.circuit.classical import expr # Subtract two integers cr = ClassicalRegister(4, "cr") qc = QuantumCircuit(cr) with qc.if_test(expr.equal(expr.sub(cr, 2), 3)): pass # Multiply a Duration by a Float with qc.if_test(expr.less(expr.mul(Duration.dt(200), 2.0), Duration.ns(500))): pass # Divide a Duration by a Duration to get a Float with qc.if_test(expr.greater(expr.div(Duration.dt(200), Duration.dt(400)), 0.5)): pass
For additional examples, see the module-level documentation linked above.
-
UCGate
now includes amux_simp
boolean attribute that enables the search for simplifications of Carvalho et al., implemented in_simplify()
. This optimization, enabled by default, identifies and removes unnecessary controls from the multiplexer, reducing the number of CX gates and circuit depth, especially in separable state preparation withInitialize
. -
The
PauliEvolutionGate
now natively supportsSparseObservable
s as input. This efficiently allows to handle evolution under projectors, which are implemented as controls of a phase rotation and require less gates than explicitly expanding the projector in terms of Paulis. For example:from qiskit.circuit.library import PauliEvolutionGate from qiskit.quantum_info import SparseObservable obs = SparseObservable("001") evo_proj = PauliEvolutionGate(obs, time=1) print(evo_proj.definition.draw())
-
A new expression node
Stretch
has been added to the classical expression system to representstretch
variables. To create a newstretch` variable, you can use :meth:`.QuantumCircuit.add_stretch`. The resulting expression is a constant expression of type :class:`~.types.Duration`, which can currently be used as the ``duration
argument of adelay()
.The
Stretch
expression is most similar to the existingVar
expression used to represent classical variables in a circuit, except it is constant and is always of typeDuration
. It can be used in other expressions (e.g. you can multiply it by a numeric constant) andQuantumCircuit
provides full scoping support for it (e.g. it can be captured by or declared within a control flow scope). -
Added
Gate
versions of the single-register arithmetic gates, which allow the compiler to perform high-level optimizations compared to theirQuantumCircuit
variants. These are:ExactReciprocalGate
(replacingExactReciprocal
)IntegerComparatorGate
(replacingIntegerComparator
)LinearPauliRotationsGate
(replacingLinearPauliRotations
)PiecewiseLinearPauliRotationsGate
(replacingPiecewiseLinearPauliRotations
)PiecewiseChebyshevGate
(replacingPiecewiseChebyshev
)PiecewisePolynomialPauliRotationsGate
(replacingPiecewisePolynomialPauliRotations
)PolynomialPauliRotationsGate
(replacingPolynomialPauliRotations
)LinearAmplitudeFunctionGate
(replacingLinearAmplitudeFunction
)QuadraticFormGate
(replacingQuadraticForm
)WeightedSumGate
(replacingWeightedAdder
)
Primitives Features
-
Added
to_bool_array()
method toBitArray
class that returns the bit array as a boolean NumPy array. Theorder
argument can be used to specify the endianness of the output array. -
Expanded the docstring of
ObservablesArray.tolist()
to make it clear it might return a scalar in the case the observables array is of dimension 0.
Providers Features
-
Added the ability to set the
dt
property ofGenericBackendV2
in the class initializer with a newdt
argument. Example usage:from qiskit.providers.fake_provider import GenericBackendV2 backend = GenericBackendV2( num_qubits=5, basis_gates=["cx", "id", "rz", "sx", "x"], dt= 2.22*e-10, seed=42)
Quantum Information Features
-
Added
SparseObservable.to_sparse_list()
to obtain a sparse list representation of aSparseObservable
. For example:from qiskit.quantum_info import SparseObservable obs = SparseObservable.from_list([("+II", 1), ("-II", 1)]) print(obs.to_sparse_list()) # [("+", [2], 1), ("-", [2], 1)]
-
Added
SparseObservable.as_paulis()
to express a sparse observable in terms of Paulis only by expanding all projectors. For example:from qiskit.quantum_info import SparseObservable obs = SparseObservable("+-") obs_paulis = obs.as_paulis() # 1/4 ( II + XI - IX - XX )
-
Support construction of a
SparsePauliOp
from aSparseObservable
via the new methodSparsePauliOp.from_sparse_observable
. It is important to remember thatSparseObservable
s can efficiently represent projectors, which require an exponential number of terms in theSparsePauliOp
. -
SparseObservable
now supports operator composition using thecompose()
method, similar to otherquantum_info
classes. This is analagous to matrix multiplication, though the method is entirely matrix free. -
SparseObservable.BitTerm
as a new attribute,label
, which contains the single-character Python string used to represent the term in string labels. -
The method
StabilizerState.expectation_value()
can now accept an operator of typeSparsePauliOp
.
Synthesis Features
-
Add a
TwoQubitControlledUDecomposer
that decomposes any two-qubit unitary in terms of basis two-qubit fractional gates, such asRZZGate
(or two-gates gates which are locally equivalent toRZZGate
up to single qubit gates).For example:
from qiskit.circuit.library import RZZGate from qiskit.synthesis import TwoQubitControlledUDecomposer from qiskit.quantum_info import random_unitary unitary = random_unitary(4, seed=1) decomposer = TwoQubitControlledUDecomposer(RZZGate, euler_basis="ZXZ") circ = decomposer(unitary) circ.draw(output='mpl')
-
Added synthesis functions
synth_integer_comparator_2s()
andsynth_integer_comparator_greedy()
to compile gates implementing an integer comparison, such as represented byIntegerComparatorGate
. The corresponding high-level synthesis plugins areIntComparatorSynthesis2s
andIntComparatorSynthesisNoAux
. To let the compiler select the optimal decomposition based on the availably auxiliary qubits, useIntComparatorSynthesisDefault
. -
Added
synth_weighted_sum_carry()
to synthesizeWeightedSumGate
objects. This is currently the only available synthesis method, with the corresponding high-level synthesis pluginWeightedSumSynthesisDefault
.
Transpiler Features
-
Added support for two-qubit fractional basis gates, such as
RZZGate
, to theConsolidateBlocks
transpiler pass. The decomposition itself is done using theTwoQubitControlledUDecomposer
.For example:
from qiskit import QuantumCircuit from qiskit.transpiler import generate_preset_pass_manager from qiskit.transpiler.passes import ConsolidateBlocks qc = QuantumCircuit(2) qc.rzz(0.1, 0, 1) qc.rzz(0.2, 0, 1) consolidate_pass = ConsolidateBlocks(basis_gates=["rz", "rzz", "sx", "x", "rx"]) block = consolidate_pass(qc) # consolidate the circuit into a single unitary block block.draw(output='mpl') pm = generate_preset_pass_manager( optimization_level=2, basis_gates=["rz", "rzz", "sx", "x", "rx"] ) tqc = pm.run(qc) # synthesizing the circuit into basis gates tqc.draw(output='mpl')
-
Added support for two-qubit fractional basis gates, such as
RZZGate
, to theUnitarySynthesis
transpiler pass. The decomposition is done using theTwoQubitControlledUDecomposer
, and supports both standard and custom basis gates.For example:
from qiskit import QuantumCircuit from qiskit.quantum_info import random_unitary from qiskit.transpiler.passes import UnitarySynthesis from qiskit.converters import circuit_to_dag, dag_to_circuit unitary = random_unitary(4, seed=1) qc = QuantumCircuit(2) qc.append(unitary, [0, 1]) dag = circuit_to_dag(qc) circ = UnitarySynthesis(basis_gates=['rzz', 'rx', 'rz']).run(dag) dag_to_circuit(circ).draw(output='mpl')
-
Added a new transpiler pass,
LightCone
that is used to get the lightcone of a circuit when measuring a subset of qubits or a specific Pauli string. For example if you had a circuit like:running the pass would eliminate the gates that do not affect the outcome.
from qiskit.transpiler.passes.optimization.light_cone import LightCone from qiskit.transpiler.passmanager import PassManager from qiskit.circuit import QuantumCircuit qc = QuantumCircuit(3,1) qc.h(range(3)) qc.cx(0,1) qc.cx(2,1) qc.h(range(3)) qc.measure(0,0) pm = PassManager([LightCone()]) new_circuit = pm.run(qc) new_circuit.draw("mpl")
-
Added a new argument
max_block_width
to the classBlockCollector
and to the transpiler passesCollectLinearFunctions
andCollectCliffords
. This argument allows to restrict the maximum number of qubits over which a block of nodes is defined.For example:
from qiskit.circuit import QuantumCircuit from qiskit.transpiler.passes import CollectLinearFunctions qc = QuantumCircuit(5) qc.h(0) qc.cx(0, 1) qc.cx(1, 2) qc.cx(2, 3) qc.cx(3, 4) # Collects all CX-gates into a single block qc1 = CollectLinearFunctions()(qc) qc1.draw(output='mpl') # Collects CX-gates into two blocks of width 3 qc2 = CollectLinearFunctions(max_block_width=3)(qc) qc2.draw(output='mpl')
-
Added a new option,
collect_from_back
, toCollectMultiQBlocks
. When set toTrue
, the blocks are collected in the reverse direction, from the outputs towards the inputs of the circuit. The blocks are still reported following the normal topological order. This leads to an additional flexibility provided by the pass, and additional optimization opportunities when combined with a circuit resynthesis method. -
Added a new argument
approximation_degree
toCommutationAnalysis
, which allows setting the approximation threshold for when gates are said to commute. See the class docstring for more information. -
A new transpiler pass,
ContractIdleWiresInControlFlow
, is available fromqiskit.transpiler.passes
. This pass removes qubits from control-flow blocks if the semantics allow this, and the qubit is idle throughout the control-flow operation. Previously, the routing stage of the preset pass managers might have done this as an accidental side-effect of how they worked, but the behavior is now more properly placed in an optimization pass. -
A new routing plugin stage is added, called
"default"
. In Qiskit 2.0, this is simply an alias for the previous default"sabre"
. The underlying default algorithm may change over the course of the Qiskit 2.x series for some or all targets, but you can always setrouting_method="sabre"
explicitly to maintain the current behavior. -
A new translation plugin stage is added, called
"default"
. In Qiskit 2.0, this is simply an alias for the previous default"translator"
. The underlying default algorithm may change over the course of the Qiskit 2.x series for some or all targets, but you can always settranslation_method="translator"
explicitly to maintain the current behavior. -
The
HighLevelSynthesis
transpiler pass now synthesizes objects of typeAnnotatedOperation
via the plugin interface. -
PassManager.run()
now accepts aproperty_set
argument, which can be set to aMapping
-like object to provide the initial values of the pipeline’sPropertySet
. This can be used to recommence a partially applied compilation, or to reuse certain analysis from a prior compilation in a new place. -
The scheduling passes
PadDelay
andPadDynamicalDecoupling
has new arguments on their constructorstarget
anddurations
these are used to specify theTarget
orInstructionDurations
respectively. This is required to provide the durations for the instructions in the circuit when this pass is run. -
Added a new method to the
Target
class has a new method,seconds_to_dt()
. This is used to translate a duration in seconds to a number of discretized time steps of the system time resolution specified in theTarget.dt
attribute. This is typically useful for converting theInstructionProperties.duration
value to units ofdt
. -
The
synth_cnot_depth_line_kms()
pass was ported into rust, with preliminary benchmarks pointing at a factor of 20x speedup. -
The
synth_cx_cz_depth_line_my()
pass was ported into rust, with preliminary benchmarks pointing at a factor of 70x speedup. -
The
Split2QUnitaries
transpiler pass has been upgraded to handle the case where the unitary in consideration can be written as a SWAP gate and two 1-qubit gates. In this case, it splits the unitary and also applies virtual swapping similar to what is done inElidePermutations
. This functionality can be controlled with a new argument,split_swap
, on the constructor of :class`.Split2QUnitaries` which can be used to disable splitting swap equivalent gates.
Misc. Features
-
qiskit.utils
now contains utilities to provide better control and inspection of Qiskit’smultiprocessing
parallelization settings. In particular, one can now useshould_run_in_parallel()
to query whetherparallel_map()
(and pass managers) will launch subprocesses for suitable inputs, and use the context managershould_run_in_parallel.override()
to temporarily override most system and user configuration around this decision.An additional function,
default_num_processes()
reads the default maximum number of subprocesses that Qiskit will use for process-based parallelism. -
A new environment variable,
QISKIT_IGNORE_USER_SETTINGS
, now controls whether to read the user settings file onimport qiskit
. If set to the stringtrue
, the settings file will not be read. This is useful for isolating certain instances of Qiskit from the system environment, such as for testing.
Upgrade Notes
-
Qiskit no longer supports Linux i686 and 32 bit Windows. Starting in Qiskit 2.0 a 64 bit platform is needed to run Qiskit. The user base for 32bit architectures is relatively small and as Qiskit continues to focus on improving performance to handle the increased scale in the complexity of quantum computing hardware maintaining support for 32 bit platforms is proving increasingly difficult. This coupled with the larger scientific/numeric Python community’s trend away from 32 bit platform support maintaining support for 32bit platforms is no longer viable. Qiskit 1.x will still continue to support 32 bit platforms, but starting in this 2.0.0 release Qiskit no longer supports these platforms, will not publish pre-compiled binaries for these platforms any longer, and there is no longer any guarantee of being able to build from source on 32 bit platforms.
-
The minimum supported Rust version for building Qiskit from source is now 1.79. This has been raised from the previous minimum supported Rust version of 1.70 in the Qiskit 1.x release series.
-
Increased the minimum threshold for when gates are assumed to be the identity in
RemoveIdentityEquivalent
from machine epsilon to1e-12
to account for round-off errors in the fidelity calculation and for consistency with the other classes, such asCommutationAnalysis
andTwoQubitWeylDecomposition
. -
Updated the metric used to check commutations in
CommutationChecker
. Two gates are assumed to commute if the average gate fidelity of the commutation is above1 - 1e-12
. This value is chosen to account for round-off errors in the fidelity calculation and for consistency withRemoveIdentityEquivalent
andTwoQubitWeylDecomposition
. See the class docstring for more information. -
BlueprintCircuit.copy_empty_like()
now returns an emptyQuantumCircuit
with the same number of qubits/clbits and the same metadata as the original circuit, instead of aBlueprintCircuit
. This change is to mitigate unexpected behavior where dealing with an “empty” copy of a blueprint circuit would re-build the circuit data. Note thatBlueprintCircuit.copy()
still returns aBlueprintCircuit
. Related: #13535 -
Qiskit Pulse has been completely removed in this release, following its deprecation in Qiskit 1.3. This include all pulse module files, pulse visualization functionality, support for
ScheduleBlock
and pulse-gate serialization/deserialization capability in QPY, calibrations management inQuantumCircuit
,Target
andDAGCircuit
and pulse-based fake backends. For more details about the removed components related to pulse, see the corresponding sections below.Note that Pulse migration to Qiskit Dynamics, as was the initial plan following the deprecation of Pulse, has been put on hold due to Qiskit Dynamics development priorities. Users wanting to use Qiskit Pulse as a frontend to supporting backends or in other uses-cases can still use it via Qiskit versions prior to 2.0, which include Pulse functionality.
-
The functions
sequence
andschedule
from thecompiler
module have been removed following their deprecation in Qiskit 1.3. They relied on being able to translate circuits to pulse using backend definitions, a capability that is no longer present. For this reason they have been removed with no proposed alternative. Note that this removals relate to the Pulse package which is also being removed in Qiskit 2.0.
Circuits Upgrade Notes
-
Bit
andRegister
as well as their subclassess are no longer guaranteed to be comparable usingis
checks, due to conversions to and from Python which may re-allocate each instance exposed to Python. -
Bit
andRegister
(and their subclasses) can no longer be subclassed. This was never intended to be supported behavior, and doing so would cause unspecified behavior in Qiskit. It is no longer possible to do this as an implementation detail of the classes. -
It is no longer possible to create instances of the base
Bit
andRegister
classes is no longer possible. Directly instantiating these classes was clearly documented as something that was not supported and being able to do it was was just an implementation artifact of how the class heirarchy in previous releases. Starting in this release it is no longer possible to do this. -
The
PhaseOracle
no longer relies on the tweedledum library but might not be synthesized as effectively as before.BitFlipOracleGate
was added as an alternative to directly synthesizingBooleanExpression
, as this class is removed in Qiskit 2.0. APhaseOracleGate
was added and will replacePhaseOracle
in Qiskit 3.0.The interface of
PhaseOracle
was simplified; it no longer accepts a synthesizer parameter, and the expression parameter can only be a string; ClassicalElement has been deprecated in Qiskit 1.4.PhaseOracle
is used exactly as before:from qiskit.circuit.library.phase_oracle import PhaseOracle bool_expr = "(x0 & x1 | ~x2) & x4" oracle = PhaseOracle(bool_expr) print(oracle)
q_0: ─o──■──■─ │ │ │ q_1: ─┼──o──■─ │ │ │ q_2: ─o──o──┼─ │ │ │ q_3: ─■──■──■─
-
The method
QuantumCircuit.measure_active()
has changed the name of the classical register it creates, as the previous name conflicted with an OpenQASM reserved word. Instead ofmeasure
, it is now calledmeas
, aligning with the register name used bymeasure_all()
. -
DAGCircuit.control_flow_op_nodes()
now always returns a list, even if empty. Previously, it returnedNone
if empty, and never returned the empty list, which required special handling. If you need to explicitly test for emptiness in both Qiskit 1.x and 2.x, you can do:control_flow_nodes = dag.control_flow_op_nodes() if not control_flow_nodes: # There are no control-flow nodes. pass
-
The generic control method for gates now avoids attempting to translate gates into a supported basis, if the gate is already supported. This can slightly change the synthesis of the controlled gate, although it should not increase the two-qubit gate count.
-
Converting a quantum circuit to a gate with
converters.circuit_to_instruction()
fails properly when given circuit contains control flow instructions. -
The internal function
qiskit.circuit.add_control.add_control
was removed, as it is not part of the public API. It had fragile preconditions to uphold and was a common source of bugs. Uses ofadd_control(SomeGate(...), ...)
should change toSomeGate(...).control(...)
usingGate.control()
instead, which is far safer. -
The
ParameterExpression.sympify()
method can now raise aMissingOptionalLibrary
exception ifsympy
is not installed. In the Qiskit 1.x releases sympy was always guaranteed to be installed, but starting in 2.x this is no longer a hard requirement and may only be needed if you are using this method. As this functionality explicitly requiressympy
you will need to ensure you havesympy
installed to use the method. -
The deprecated argument
.dag
forDAGNode
and its subclassesDAGOpNode
,DAGOutNode
, andDAGInNode
.dag
was optional parameter when constructing these objects that was unused and ignored since the 1.3.0 release. The parameter was deprecated in 1.4 and has now been is removed. -
The
QuantumCircuit
methods:cast
cbit_argument_conversion
cls_instances
cls_prefix
qbit_argument_conversion
have been removed, following their deprecation in Qiskit 1.2. These methods were internal helper functions, and never intended to be public API. No replacement is provided.
-
The deprecated attributes for
Instruction
andGate
:duration
andunit
have been removed. This includes setting theunit
orduration
arguments for anyqiskit.circuit.Instruction
or subclass. These attributes were deprecated in Qiskit 1.3.0 and were used to attach a custom execution duration and unit for that duration to an individual instruction. However, the source of truth of the duration of a gate is theBackendV2
Target
which contains the duration for each instruction supported on the backend. The duration of an instruction is not something that’s typically user adjustable and is an immutable property of the backend. If you were previously using this capability to experiment with different durations for gates you can mutate theInstructionProperties.duration
field in a givenTarget
to set a custom duration for an instruction on a backend (the unit is always in seconds in theTarget
). -
The deprecated attribute for
qiskit.circuit.Instruction
andGate
:condition
has been removed. These attributes were deprecated in the 1.3.0 release. This functionality has been superseded by theIfElseOp
class which can be used to describe a classical condition in a circuit. -
The deprecated methods for
Instruction
andGate
:c_if
andcondition_bits
have been removed. These methods were deprecated in the 1.3.0 release. This functionality has been superseded by theIfElseOp
class which can be used to describe a classical condition in a circuit. For example, a circuit previously usingInstruction.c_if()
like:from qiskit.circuit import QuantumCircuit qc = QuantumCircuit(2, 2) qc.h(0) qc.x(0).c_if(0, 1) qc.z(1.c_if(1, 0) qc.measure(0, 0) qc.measure(1, 1)
can be rewritten as:
qc = QuantumCircuit(2, 2) qc.h(0) with expected.if_test((expected.clbits[0], True)): qc.x(0) with expected.if_test((expected.clbits[1], False)): qc.z(1) qc.measure(0, 0) qc.measure(1, 1)
-
The deprecated method
InstructionSet.c_if
has been removed. This method was deprecated in the 1.3.0 release. This functionality has been superseded by theIfElseOp
class which can be used to describe a classical condition in a circuit. -
As part of Pulse removal in Qiskit 2.0, the
calibrations
property has been removed from theQuantumCircuit
,DAGCircuit
andDAGDependency
classes. In addition, the methodhas_calibration_for
has been removed from theQuantumCircuit
andDAGCircuit
classes andadd_calibration
has been removed fromQuantumCircuit
. -
The
classical_function
was removed. This module was dependent on the tweedledum library which is no longer compatible with newer versions of Python. As an alternative, thePhaseOracleGate
andBitFlipOracleGate
classes can be used to generate circuits from boolean expressions. -
The internal representation of
UnitaryGate
has changed when they’re added to aQuantumCircuit
. The object stored in the circuit will not necessarily share a common reference to the object added to the circuit anymore. This was never guaranteed to be the case and mutating theUnitaryGate
object directly or by reference was unsound and always likely to corrupt the circuit, especially if you changed the matrix. If you need to mutate an element in the circuit (which is not recommended as it’s inefficient and error prone) you should ensure that you do something like:from qiskit.circuit import QuantumCircuit from qiskit.quantum_info import random_unitary from qiskit.circuit.library import UnitaryGate import numpy as np qc = QuantumCircuit(2) qc.unitary(np.eye(2, dtype=complex)) new_op = UnitaryGate(random_unitary(2)) qc.data[0] = qc.data[0].replace(operation=new_op)
This also applies to
DAGCircuit
too, but you can useDAGCircuit.substitute_node()
instead. -
The
CircuitInstruction.params
attribute for aCircuitInstruction
that contains anUnitaryGate
for itsoperation
will no longer contain the underlying unitary matrix for the gate. This is because the internal representation of the gate no longer treats the matrix object as a parameter. If you need to access the matrix of the gate you can do this either via theCircuitInstruction.matrix
or theUnitaryGate.params
field of theCircuitInstruction.operation
.
Primitives Upgrade Notes
-
As a consequence of the removal of the
BackendV1
model, theBackendSamplerV2
andBackendEstimatorV2
classes no longer accept inputs of typeBackendV1
in theirbackend
input argument. -
Primitive V1 implementations and V1-exclusive non-versioned type aliases, deprecated in Qiskit 1.2, have been removed. These interfaces have been superseded by their V2 counterparts. The removal includes the following classes implementing V1 interfaces:
Estimator
, in favor of the V2 equivalent,StatevectorEstimator
Sampler
, in favor of the V2 equivalent,StatevectorSampler
BackendEstimator
, in favor of the V2 equivalent,BackendEstimatorV2
BackendSampler
, in favor of the V2 equivalent,BackendSamplerV2
As well as the following non-versioned type aliases:
BaseEstimator
, alias forBaseEstimatorV1
BaseSampler
, alias forBaseSamplerV1
This removal does NOT affect the explicitly-versioned
BaseEstimatorV1
andBaseSamplerV1
abstract interface definitions or related result and job classes, which have been kept to maintain backward compatibitily.In addition, the following utility functions have been removed. These functions were only used in Primitive V1 implementations:
init_circuit()
, to initialize a circuit from aStatevector
, useQuantumCircuit.initialize()
instead,init_observable()
, use the constructor ofSparsePauliOp
instead,final_measurement_mapping()
, useQuantumCircuit.layout()
andSparsePauliOp.apply_layout()
to adjust an operator for a layout. Otherwise, usemthree.utils.final_measurement_mapping
. See Mthree Utility functions for details.
Providers Upgrade Notes
-
The
configuration
method ofBasicSimulator
has been removed following its deprecation in Qiskit 1.3. This method returned aBackendConfiguration
instance, and this class was part of the deprecatedBackendV1
workflow. The individual configuration elements can be retrieved directly from the backend or from the containedTarget
instance(backend.target)
. -
The
run_experiment
method ofBasicSimulator
has also been removed. This method took an instance of theQasmQobjExperiment
class as an input argument, and the class has been deprecated since Qiskit 1.2. -
The
BackendV1
model has been removed following its deprecation in Qiskit 1.2.0. This includes theBackendV1
class as well as related modules and utils, as they have been superseded by theBackendV2
model. The list of removed items includes:-
BackendV1
class: the core of the removed model -
All elements in qiskit/providers/models, as they were used to represent components of the
BackendV1
model:BackendConfiguration
BackendProperties
BackendStatus
QasmBackendConfiguration
PulseBackendConfiguration
UchannelLO
GateConfig
PulseDefaults
PulseQobjDef
Command
GateProperties
Nduv
JobStatus
: This class has been superseded by the more widely usedJobStatus
PulseDefaults
-
BackendV2Converter
class: used to convert fromBackendV1
toBackendV2
-
convert_to_target
function: used to build aTarget
instance from legacyBackendV1
components (such asBackendConfiguration
orBackendProperties
) -
BackendPropertyError
andBackendConfigurationError
: exceptions linked to removed classes
-
-
The
BasicSimulator
backend no longer can simulate classical control flow. It only supported using.c_if()
/.condition
for modeling control flow, as this has now been removed from Qiskit’s data model it no longer can support control flow. -
All fake backend classes based on the deprecated
BackendV1
have been removed from theproviders.fake_provider
module. These classes have been deprecated since Qiskit 1.2 and were part of the deprecatedBackendV1
workflow. Their use in tests has been replaced with theGenericBackendV2
class, which allows to create custom instances ofBackendV2
that implement a simulatedBackendV2.run()
. The removal affects:-
Base classes:
FakeBackend
FakePulseBackend
FakeQasmBackend
-
Fake backends for special testing purposes: *
Fake1Q
*FakeOpenPulse2Q
*FakeOpenPulse3Q
-
Legacy fake backends:
Fake5QV1
Fake20QV1
Fake7QPulseV1
Fake27QPulseV1
Fake127QPulseV1
-
-
As part of Pulse removal in Qiskit 2.0, the following methods have been removed:
qiskit.providers.BackendV2.instruction_schedule_map
qiskit.providers.BackendV2.drive_channel
qiskit.providers.BackendV2.measure_channel
qiskit.providers.BackendV2.acquire_channel
qiskit.providers.BackendV2.control_channel
-
As part of Pulse removal in Qiskit 2.0, pulse support has been removed from
GenericBackendV2
. This includes the ability to initialize the backend with custom calibrations (calibrate_instructions
argument) and pulse channels information. -
Removed the abstract base classes
Provider
andProviderV1
which have been deprecated since Qiskit 1.1.0. The abstraction provided by these interface definitions were not providing a huge value, solely just the attributesname
,backends
, and aget_backend()
method. A _provider_, as a concept, will continue existing as a collection of backends. If you’re implementing a provider currently you can adjust your code by simply removingProviderV1
as the parent class of your implementation. As part of this you probably would want to add an implementation ofget_backend
for backwards compatibility. For example:def get_backend(self, name=None, **kwargs): backend = self.backends(name, **kwargs) if len(backends) > 1: raise QiskitBackendNotFoundError("More than one backend matches the criteria") if not backends: raise QiskitBackendNotFoundError("No backend matches the criteria") return backends[0]
QPY Upgrade Notes
-
The
qpy.load()
function can now raise aMissingOptionalLibrary
exception if a QPY v10, v11, or v12 payload is passed in that is using symengine symbolic expressions and symengine is not installed. Or if sympy is not installed for any other QPY payload < v13. In the Qiskit 1.x releases symengine and sympy were always guaranteed to be installed, but starting in 2.x this is no longer a hard requirement and may only be needed if you’re deserializing a QPY file that was generated using symengine. Parsing these QPY payloads requires symengine (0.11.0 or 0.13.0) as it’s usage is baked into the format specification for QPY v10, v11, and v12 so if the payload requires it there is no option but to install a compatible version of symengine. Similarly, sympy was was used forParameterExpression
encoding for all QPY versions from 1 through 12. -
The minimum QPY compatibility version,
QPY_COMPATIBILITY_VERSION
, has been raised to 13 from 10 in the 1.x release. This version controls the minimum version of QPY that can be emitted by theqpy.dump()
function. This meansqpy.dump()
can only emit QPY v13 and v14 in this release. QPY v13 is still compatible with Qiskit 1.3.x and 1.4.x which means payloads can be generated in Qiskit 2.x that can be loaded with the Qiskit 1.x release series still.This change was necessary as QPY versions 10 through 12 requires either the sympy and symengine libraries to generate a serialization for
ParameterExpression
objects, but in Qiskit 2.x neither library is required for theParameterExpression
object. -
With the removal of Pulse in Qiskit 2.0, support for serializing
ScheduleBlock
programs via theqiskit.qpy.dump()
function has been removed. Users can still load payloads containing pulse gates using theqiskit.qpy.load()
function, however those will be treated as opaque custom instructions. LoadingScheduleBlock
payloads is not supported anymore and will result with aQpyError
exception.
Synthesis Upgrade Notes
-
The plugins for
LinearFunction
no longer raise an error if another object thanLinearFunction
is passed into therun
method. Instead,None
is returned, which is consistent with the other plugins. If you relied on this error being raised, you can manually perform an instance-check. -
The
atomic_evolution
argument toProductFormula
(and its subclassesQDrift
,LieTrotter
andSuzukiTrotter
) has a new function signature. Rather than taking some Pauli operator and time coefficient and returning the evolution circuit, the new function takes in an existing circuit and should append the evolution of the provided Pauli and given time to this circuit. This new implementation benefits from significantly better performance.
Transpiler Upgrade Notes
-
The routing plugin stage name
default
is now reserved for the Qiskit built-in plugin of the same name. -
The default routing plugin stage is now
"default"
. In Qiskit 2.0, this is simply an alias for the previous default"sabre"
. The underlying default algorithm may change over the course of the Qiskit 2.x series for some or all targets, but you can always setrouting_method="sabre"
explicitly to maintain the current behavior. -
The translation plugin stage name
default
is now reserved for the Qiskit built-in plugin of the same name. -
The default translation plugin stage is now
"default"
. In Qiskit 2.0, this is simply an alias for the previous default"translator"
. The underlying default algorithm may change over the course of the Qiskit 2.x series for some or all targets, but you can always settranslation_method="translator"
explicitly to maintain the current behavior. -
The deprecated transpiler passes
ASAPSchedule
,ALAPSchedule
,DynamicalDecoupling
, andAlignMeasures
have been removed. These passes were marked as deprecated. They have been replaced by theALAPScheduleAnalysis
,ASAPScheduleAnalysis
,PadDynamicalDecoupling
, andConstrainedReschedule
passes respectively which can be used instead. -
In the case that neither a target nor a set of basis gates are specified, the
HighLevelSynthesis
transpiler pass synthesizes circuits with annotated operations with fewer layers of wrappings than before (this happens, for instance, for the circuit produced bymultiplier_cumulative_h18()
). -
The keyword argument
property_set
is now reserved inBasePassManager.run()
, and cannot be used as akwarg
that will be forwarded to the subclass’s conversion from the front-end representation to the internal representation. -
The following deprecated uses of the
BackendProperties
object in the transpilation pipeline have been removed in Qiskit 2.0:backend_properties
input argument intranspile()
backend_properties
input argument inPassManagerConfig
backend_properties
input argument ingenerate_preset_pass_manager()
backend_properties
input argument ingenerate_routing_passmanager()
backend_properties
input argument ingenerate_translation_passmanager()
backend_properties
input argumentTarget.from_configuration()
The following passes have also been updated to only accept a
target
instead of:backend_prop
input argument inDenseLayout
properties
input argument inVF2Layout
properties
andcoupling_map
input arguments inVF2PostLayout
backend_props
input argument inUnitarySynthesis
The
BackendProperties
class has been deprecated since Qiskit 1.2, as it was part of the BackendV1 workflow. Specific instruction properties such as gate errors or durations can be added to aTarget
upon construction through theTarget.add_instruction()
method, and communicated to the relevant transpiler passes through the target input argument. -
As a consequence of the removal of the
BackendV1
model, the accepted input types of the following transpiler objects have been updated:- The
generate_preset_pass_manager()
andtranspile()
functions no longer accept inputs of typeBackendV1
in theirbackend
input argument. - The
Target.from_configuration()
method no longer accepts abackend_properties
argument - The
Target.target_to_backend_properties()
method has been removed
- The
-
The
ResetAfterMeasureSimplification
transpiler pass now will use anIfElseOp
to condition the execution of theXGate
instead of setting acondition
attribute on the gate. This is because thecondition
attribute has been removed from the Qiskit data model. -
The deprecated
ConvertConditionsToIfOps
transpiler pass has been removed. The underlyingcondition
attribute ofInstruction
class has been removed so this transpiler pass no longer had anything to convert from. Instead you should directly useIfElseOp
to classically condition the execution of an operation. -
The
PadDelay
andPadDynamicalDecoupling
transpiler passes now require a new argument when constructed. Eithertarget
ordurations
need to be specified with aTarget
orInstructionDurations
respectively. Without these the passes will not be able to determine the duration of instructions in the circuit and will error. Previously these passes would determine these values from the now removedduration
attribute ofInstruction
objects. -
The previously deprecated argument
propagate_condition
on theDAGCircuit
methodssubstitute_node_with_dag()
andsubstitute_node()
has been removed. These arguments were deprecated in Qiskit 1.4.0 and were removed because the larger removal ofInstruction.condition
it no longer served a purpose. -
The previously deprecated
AlignMeasures
transpiler pass has been removed. This pass was deprecated in Qiskit 1.1.0. Instead theConstrainedReschedule
pass should be used.ConstrainedReschedule
performs the same function and also supports aligning to additional timing constraints. -
When calling
generate_preset_pass_manager()
ortranspile()
and specifying theinstruction_durations
argument with a list instead of anInstructionDurations
object and the list specifies durations in units ofdt
adt
parameter is required if scheduling the circuit. -
Removed the deprecated
DAGNode.sort_key
attribute. This attribute was deprecated in the Qiskit 1.4.0 release. As the lexicographical topological sorting is done internally and rust and the sort key attribute was unused this was removed to remove the overhead from DAG node creation. If you were relying on the sort key you can reproduce it from a given node using something like:def get_sort_key(node: DAGNode): if isinstance(node, (DAGInNode, DAGOutNode)): return str(node.wire) return ",".join( f"{dag.find_bit(q).index:04d}" for q in itertools.chain(node.qargs, node.cargs) )
-
The following
transpile()
andgenerate_preset_pass_manager()
input arguments, deprecated since Qiskit 1.3 , have been removed from the API:- instruction_durations
- timing_constraints
In addition to this, the specification of custom basis gates through the
basis
gate argument oftranspile()
andgenerate_preset_pass_manager()
, also deprecated in Qiskit 1.3, is no longer allowed, and aValueError
will be raised in these cases.The information formerly provided through these can still be specified via the
backend
ortarget
arguments. You can build a Target instance with defined instruction durations doing:Target.from_configuration(..., instruction_durations=...)
For specific timing constraints:
Target.from_configuration(..., timing_constraints=...)
And for custom basis gates, you can manually add them to the target or use
.from_configuration
with a custom name mapping, for example:from qiskit.circuit.library import XGate from qiskit.transpiler.target import Target basis_gates = ["my_x", "cx"] custom_name_mapping = {"my_x": XGate()} target = Target.from_configuration( basis_gates=basis_gates, num_qubits=2, custom_name_mapping=custom_name_mapping )
-
The
transpile()
andgenerate_preset_pass_manager()
interfaces now raise aUserWarning
when providing acoupling_map
and/orbasis_gates
along with abackend
. In these cases there are multiple sources of truth, the user intentions are not always clear, and there can be conflicts thatgenerate_preset_pass_manager()
may not know how to resolve. In these cases, we highly encourage the creation of a custom target that combines the chosen constraints.One of these situations is the specification of a gate with 3 or more qubits in backend or basis_gates together with a custom coupling map. The coupling map does not provide the necessary connectivity details to be able to determine the action of the gate. In these cases,
transpile()
andgenerate_preset_pass_manager()
now raise aValueError
. -
As part of Pulse removal in Qiskit 2.0, all pulse and calibration related functionality in the transpiler has been removed. This includes the following:
The following passes and function have been removed:
qiskit.transpiler.passes.PulseGates
passqiskit.transpiler.passes.ValidatePulseGates
passqiskit.transpiler.passes.RXCalibrationBuilder
passqiskit.transpiler.passes.RZXCalibrationBuilder
passqiskit.transpiler.passes.RZXCalibrationBuilderNoEcho
passqiskit.transpiler.passes.EchoRZXWeylDecomposition
passqiskit.transpiler.passes.NoramlizeRXAngle
passqiskit.transpiler.passes.rzx_templates()
function
The
inst_map
argument has been removed from the following elements:- The
generate_preset_pass_manager()
andtranspile()
functions - The
Target.from_configuration()
method - The constructor of the
PassManagerConfig
class
Calibration support has been removed:
calibration
has been removed from theInstructionProperties
‘s constructor and is no longer a property of that class.- The
has_calibration
,get_calibration
,instruction_schedule_map
andupdate_from_instruction_schedule_map
methods have been removed from theTarget
class.
-
The deprecated
StochasticSwap
transpiler pass, and its associated built-in routing stage plugin “stochastic”, have been removed. These were marked as deprecated in the Qiskit 1.3.0 release. The pass has been superseded by theSabreSwap
which should be used instead as it offers better performance and output quality. For example if the pass was previously invoked via the transpile function such as with:from qiskit import transpile from qiskit.circuit import QuantumCircuit from qiskit.transpiler import CouplingMap from qiskit.providers.fake_provider import GenericBackendV2 qc = QuantumCircuit(4) qc.h(0) qc.cx(0, range(1, 4)) qc.measure_all() cmap = CouplingMap.from_heavy_hex(3) backend = GenericBackendV2(num_qubits=cmap.size(), coupling_map=cmap) tqc = transpile( qc, routing_method="stochastic", layout_method="dense", seed_transpiler=12342, target=backend.target )
this should be replaced with:
tqc = transpile( qc, routing_method="sabre", layout_method="dense", seed_transpiler=12342, target=backend.target )
-
The pass
qiskit.transpiler.passes.CXCancellation
was removed. It was deprecated in favor of class:.InverseCancellation, which is more generic.CXCancellation
is fully semantically equivalent toInverseCancellation([CXGate()])
. -
The
SolovayKitaev
transpiler pass no longer raises an exception on circuits that contain single-qubit operations without ato_matrix
method (such as measures, barriers, control-flow operations) or parameterized single-qubit operations, but will leave them unchanged. -
Plugins for the translation stage of the preset compiler are now required to respect gate directionality in the
Target
in their output. Previously,transpile()
andgenerate_preset_pass_manager()
would generate aPassManager
that contained fix-up passes if needed. You must now include these in your own custom stage, if your stage does not guarantee that it respects directionality.You can use the
GateDirection
pass to perform the same fix-ups that Qiskit used to do. For example:from qiskit.transpiler import PassManager from qiskit.transpiler.passes import GateDirection from qiskit.transpiler.preset_passmanagers.plugin import PassManagerStagePlugin class YourTranslationPlugin(PassManagerStagePlugin): def pass_manager(self, pass_manager_config, optimization_level): pm = PassManager([ # ... whatever your current setup is ... ]) # Add the two-qubit directionality-fixing pass. pm.append(GateDirection( pass_manager_config.coupling_map, pass_manager_config.target, )) return pm
-
The preset pass managers no longer populate the implicit
pre_optimization
stage of their outputStagedPassManager
. You can now safely assign your ownPassManager
to this field. You could previously only append to the existingPassManager
. -
The default value for the
generate_routing_passmanager()
argumentseed_transpiler
has changed fromNone
to-1
. This was done because this flag was only used to configure theVF2PostLayout
transpiler pass in the output, and for that pass in particular the randomization typically only hurts performance and is not desirable. If you were relying on the previous default value you can restore this behavior by explicitly setting the argumentseed_transpiler=None
. If you were explicitly setting a seed value for this parameter there is no change in behavior.
Visualization Upgrade Notes
-
The
idle_wires
parameter in all circuit drawers has been extended with a new option,"auto"
, which is now the default behavior. If you still want to display wires without instructions, explicitly setidle_wires=True
.When set to
"auto"
, the behavior is as follows: - If the circuit has a defined.layout
attribute,idle_wires
is automatically set toFalse
(hiding idle wires). - Otherwise,idle_wires
remainsTrue
(showing all wires, as previous default).Here an example. A circuit without a layout, using
idle_wires="auto"
:qr_0: ──────── ┌───┐┌─┐ qr_1: ┤ H ├┤M├ └───┘└╥┘ cr_0: ══════╬═ ║ cr_1: ══════╩═
Once a layout is applied,
idle_wires="auto"
setsidle_wires
toFalse
, hiding idle wires:┌───┐┌─┐ qr_1 -> 1 ┤ H ├┤M├ └───┘└╥┘ cr_1: ══════╩═
If you want to display all wires in a laid-out circuit, set
idle_wires=True
explicitly:qr_0 -> 0 ──────── ┌───┐┌─┐ qr_1 -> 1 ┤ H ├┤M├ └───┘└╥┘ ancilla_0 -> 2 ──────╫─ ║ cr_0: ══════╬═ ║ cr_1: ══════╩═
As quantum computers scale to more qubits, even small circuits can produce large circuit representations after transpilation. The
"auto"
setting helps improve readability by hiding unnecessary wires when possible. -
The
array_to_latex()
andOperator.draw()
methods can now raise aMissingOptionalLibrary
exception if thesympy
library is not installed. In the Qiskit 1.x releases symengine and sympy were always guaranteed to be installed, but starting in 2.x this is no longer a hard requirement. The latex visualization for a matrix relies on the sympy library, so if you’re using this functionality you should ensure that you have sympy installed. -
As a consequence of the removal of the
BackendV1
model, theplot_gate_map()
,plot_error_map()
andplot_circuit_layout()
functions no longer accept inputs of typeBackendV1
in theirbackend
input argument. -
The timeline drawer now requires the target argument is specified when called. As instructions no longer contain duration attributes this extra argument is required to specify the durations for all the supported instructions. Without the argument the timeline drawer does not have access to this information.
-
As part of the Pulse removal in Qiskit 2.0, support for pulse drawing via
qiskit.visualization.pulse_drawer
has been removed.
Misc. Upgrade Notes
-
The
deprecate_function
anddeprecate_arguments
decorators had been deprecated since 0.24, released on May 2023, and have been removed in 2.0. Currentdeprecate_func`()
replaces@deprecate_function
and currentdeprecate_arg()
replaces@deprecate_arguments
. -
The
assemble
function and related capabilities (contained in theassembler
module) have been removed from the codebase following their deprecation in Qiskit 1.2.assemble
was used to generateQobj
in the context of the deprecatedBackendV1
workflow. The conversion is no longer necessary as the transpilation and primitives pipeline handles quantum circuits directly, rendering theQobj
obsolete.The removal includes the following public API components:
qiskit.compiler.assemble
functionqiskit.assembler.assemble_circuits
functionqiskit.assembler.assemble_schedules
functionqiskit.assembler.disassemble
functionqiskit.assembler.RunConfig
classqiskit.circuit.Instruction.assemble
method
-
The
Qobj
structure and related classes, deprecated in Qiskit 1.2.0, have been removed. They were introduced as part of theBackendV1
workflow and are no longer necessary for interacting withBackendV2
backends. This removal includes:QobjExperimentHeader
QobjHeader
QasmQobj
QasmQobjInstruction
QasmQobjExperimentConfig
QasmQobjExperiment
QasmQobjConfig
QasmExperimentCalibrations
GateCalibration
PulseQobj
PulseQobjInstruction
PulseQobjExperimentConfig
PulseQobjExperiment
PulseQobjConfig
QobjMeasurementOption
PulseLibraryItem
-
The
MeasLevel
and :class`.MeasReturnType` classes, previously defined inqobj/utils.py
, have been migrated toresult/models.py
following the removal of theqobj
module. These classes were not part of the public API. The import path has been updated from:from qiskit.qobj.utils import MeasLevel, MeasReturnType
to:from qiskit.result import MeasLevel, MeasReturnType
. -
The use of positional arguments in the constructor of
Result
has been disabled. Please set all arguments using kwarg syntax, i.e:Result(backend_name="name", ....)
. In addition to this, theqobj_id
argument will no longer be used in the construction of theResult
internals. It is still possible to setqobj_id
as a generic kwarg, which will land in the metadata field with the other generic kwargs. -
As part of Pulse removal in Qiskit 2.0, the
sequence
andschedule_circuit
functions fromqiskit.scheduler
together with theScheduleConfig
class have been removed. -
The
qiskit.result.mitigation
module has been removed following its deprecation in Qiskit 1.3. The removal includes theLocalReadoutMitigator
andCorrelatedReadoutMitigator
classes as well as the associated utils.There is no alternative path in Qiskit, as their functionality had been superseded by the mthree package, found in https://github.com/Qiskit/qiskit-addon-mthree.
Circuits Deprecations
-
The deprecated
QuantumCircuit.duration
attribute was not removed in this release as originally planned, it will be removed as part of the Qiskit 3.0.0 release instead. This functionality has been superseded by theQuantumCircuit.estimate_duration()
method which should be used instead. -
The Multiple-Control-Multiple-Target in
MCMT
is now deprecated and replaced byMCMTGate
, which is a properGate
subclass. Using a gate instead of a circuit allows the compiler to reason about the object at a higher level of abstraction and allows for multiple synthesis plugins.
Transpiler Deprecations
-
The deprecated
DAGCircuit.duration
attribute was not removed in this release as originally planned, it will be removed as part of the Qiskit 3.0.0 release instead. This functionality has been superseded by theQuantumCircuit.estimate_duration()
method which should be used instead. -
The
propagate_condition
argument ofDAGCircuit.substitute_node()
andDAGCircuit.substitute_node_with_dag()
has been deprecated. With the removal ofInstruction.condition
from the Qiskit data model this option no longer serves a purpose. If it is set it no longer has any effect. It is not removed from the signature to maintain compatibility during the migration from Qiskit 1.x -> 2.0. This option will be removed in Qiskit 3.0. -
The function
generate_pre_op_passmanager()
is deprecated. It is no longer used in the Qiskit preset pass managers, and its purpose is defunct; it originally generated a fix-up stage for translation plugins that did not respect ISA directionality. Translation stages are now required to respect directionality, so the functionality is not needed, and most likely, no replacement is required.
Bug Fixes
-
Fixed a bug where the barrier labels were incorrectly positioned when using the
reverse_bits = True
parameter in theQuantumCircuit.draw()
method. The bug caused the labels on barrier operations to be misaligned, leading to potential confusion in circuit visualizations. Fixed #13609. -
Fixed an inconsistency in dealing with close-to-identity gates in the transpilation process, where gates that are close to the identity were assumed to commute with everything, but not removed. The underlying issue were different metrics used in
RemoveIdentityEquivalent
andCommutationAnalysis
(and, by extension,CommutativeInverseCancellation
). Both now use the average gate fidelity and the same threshold to assess whether a gate should be treated as identity (such as a rotation gate with very small angle). See either of the aforementioned classes’ docstrings for more information. Fixed #13547. -
Applied a small regularisation factor against ill-conditioned Hermitian matrices in super-operator representations.
-
Commutation relations of
Instruction
s with float-onlyparams
were eagerly cached by theCommutationChecker
, using theparams
as key to query the relation. This could lead to faulty results, if the instruction’s definition depended on additional information that just theparams
attribute, such as e.g. the case forPauliEvolutionGate
. This behavior is now fixed, and the commutation checker only conservatively caches commutations for Qiskit-native standard gates. This can incur a performance cost if you were relying on your custom gates being cached, however, we cannot guarantee safe caching for custom gates, as they might rely on information beyondparams
. -
Fixed a bug in the
CommmutationChecker
, where checking commutation of instruction with non-numeric values in theparams
attribute (such as thePauliGate
) could raise an error. Fixed #13570. -
Fixed a bug where calling
QuantumCircuit.decompose()
on an instruction that had no definition inside ac_if
block would raise an error. Fixed #13493. -
Operations inside a control flow (e.g.
QuantumCircuit.for_loop()
) were not correctly decomposed when callingQuantumCircuit.decompose()
. This behavior is now fixed and instructions are unrolled. Fixed #13544. -
Comparisons of
Delay
instructions, including within circuits, now require the units to be equal as well as the duration value. -
The
CommutationChecker
did not handle commutations of theCRXGate
,CRYGate
andCRZGate
correctly for angles for . In these cases, the controlled rotations were falsely assumed to commute with any gate. Now these gates correctly commute with any gate if the rotation angle is a multiple of . -
Added default definitions for
FullAdderGate
,HalfAdderGate
,ModularAdderGate
andMultiplierGate
gates, allowing to contructOperator
s from quantum circuits containing these gates. -
Fixed the number of clean ancilla qubits required by
FullAdderSynthesisV95
,HalfAdderSynthesisV95
, andModularAdderSynthesisV95
plugins. -
Added missing
FullAdderSynthesisDefault
plugin that chooses the best decomposition forFullAdderGate
based on the number of clean ancilla qubits available. -
Fixed
HalfAdderSynthesisDefault
andModularAdderSynthesisDefault
plugins, forHalfAdderGate
andModularAdderGate
respectively, to choose the best decomposition based on the number of clean ancilla qubits available. -
Fix incorrect behavior in
CircuitData
in which, upon parameter assignment, we attempted to modify the cached operation inside of aPackedInstruction
. Now we instead discard said cache prompting thePackedInstruction
to build a new Python operation should it be needed. -
Fixed a bug in
QuantumCircuit.assign_parameters()
, occurring when assigning parameters to standard gates whose definition has already been triggered. In this case, the new values were not properly propagated to the gate instances. While the circuit itself was still compiled as expected, inspecting the individual operations would still show the old parameter.For example:
from qiskit.circuit.library import EfficientSU2 circuit = EfficientSU2(2, flatten=True) circuit.assign_parameters([1.25] * circuit.num_parameters, inplace=True) print(circuit.data[0].operation.params) # would print θ[0] instead of 1.25
Fixed #13478.
-
Fixed a bug in the
CommutationChecker
which could fail upon checking the commutation relation of a two-qubit Pauli rotation with a gate that is not in the commutation cache. For example:import numpy as np from qiskit.circuit.library import RXXGate, RGate from qiskit.circuit.commutation_library import SessionCommutationChecker as scc res = scc.commute(RGate(2, 2), [1], [], RXXGate(np.pi / 2), [0, 1], [])
This behavior is now resolved and the commutation relation correctly computed. Fixed #13742.
-
Fixed a bug that caused the circuit library functions
efficient_su2()
,real_amplitudes()
,excitation_preserving()
andpauli_two_design()
to error out when constructed fornum_qubits==1
. For a single qubit these circuits will not contain any 2-qubit gates. -
Fixed a series of bugs when processing circuit with parameterized global phases, where upon assignment the global phase was not correctly assigned. Known cases this affected include:
- assigning parameters after calling
QuantumCircuit.decompose()
on a circuit, where the decomposition introduces a global phase - assigning parameters on a circuit constructed from a DAG via
dag_to_circuit()
- assigning parameters on circuits created with
pauli_twirl_2q_gates()
, where the circuit to be twirled had a parameterized global phase
Fixed #13534.
- assigning parameters after calling
-
Previously the
HighLevelSynthesis
transpiler pass synthesized an instruction for which a synthesis plugin is available, regardless of whether the instruction is already supported by the target or a part of the explicitly passedbasis_gates
. This behavior is now fixed, so that such already supported instructions are no longer synthesized. -
An issue where
QuantumCircuit.qubit_stop_time()
andQuantumCircuit.qubit_duration()
returned incorrect time (duration) was fixed. It was triggered when some qubits have instructions but other qubits are idle. Fixes #8729. -
The transpilation pass
InverseCancellation
now runs inside of flow controlled blocks. Previously, it ignored the pairs of gates in classical blocks that could be cancelled. Refer to #13437 for more details. -
Fixed a bug where any instruction called
"mcmt"
would be passed into the high-level synthesis routine for aMCMTGate
, which causes a failure or invalid result. In particular, this could happen accidentally when handling theMCMT
_circuit_, named"mcmt"
, and implicitly converting it into an instruction e.g. when appending it to a circuit. Fixed #13563. -
Fixed a bug in
RZGate.control()
for more than 1 control qubit, which used an unnecessarily expensive decomposition. Fixed #13473. -
Fixed a bug in the basis approximation generation for
SolovayKitaev
. Previously, generating discrete basis approximations usinggenerate_basis_approximations
for a basis containing"sx"
or"sxdg"
gates would fail. This has now been fixed. -
Fix a bug in the multi-controlled rotation circuit methods
QuantumCircuit.mcrx()
,QuantumCircuit.mcry()
, andQuantumCircuit.mcrz()
, when the user provides an unbounded parameter, as well as when callingRXGate.control()
,RYGate.control()
orRZGate.control()
where the rotation angle is aParameterExpression
. Previously, the user got an error that this gate cannot be synthesized with unbound parameter, and now these multi-controlled rotation circuits can be synthesized without raising an error. -
The
PauliEvolutionGate
, if used with a product formula synthesis (this is the default), did not correctly handle all-identity terms in the operator. The all-identity term should introduce a global phase equal to-evolution_time
, but was off by a factor of 2 and could break for parameterized times. This behavior is now fixed. Fixed #13625. -
Fixed an inconsistency in the circuit generated by a Pauli evolution synthesis with
SuzukiTrotter
orLieTrotter
(the default) method. For parameterized evolution times, the resulting circuits contained parameters with a spurious, zero complex part, which affected the output ofParameterExpression.sympify()
. The output now correctly is only real. Fixed #13642. -
Fixed a bug that caused
PauliList.insert()
withqubit=True
to produce a phase attribute with the wrong shape when the original object was length 1. Fixed #13623. -
Fixed a bug in the
RemoveIdentityEquivalent
transpiler pass, where gates close to identity up to a global phase were removed from the circuit, but the global phase of the circuit was not updated. In particular,RemoveIdentityEquivalent
now removes non-parameterizedGlobalPhaseGate
gates. -
Circuits compiled using a preset passmanager constructed by
generate_preset_pass_manager()
will now correctly retain theirname
attribute, as they do withtranspile()
. -
Fix a bug in
qasm3.Exporter
that caused the exporter to crash when handling a unitary gate due to incorrect processing of itsparams
field. -
Fixed a bug in
random_clifford()
that stopped it from sampling the full Clifford group. -
Fixed a bug in the
Target.instruction_supported()
method where targets withself.num_qubits==None
would always returnfalse
independently of the supported basis set. -
Fixed a bug in the
UnitarySynthesis
transpiler pass, where blocks ofUnitaryGate
s on 3 qubits or more were not correctly synthesized. This led, e.g., to the circuit being overwritten with the last processed block or to internal panics when encountering measurements after such a block. Fixed #13586. -
Fixed a bug in the
UnitarySynthesis
transpiler pass where non-2-qubit gates would be included in the available 2 qubit basis, causing theTwoQubitWeylDecomposition
to panic because of the dimension mismatch. -
Fixed a bug that caused
Statevector.expectation_value()
to yield incorrect results for the identity operator when the statevector was not normalized. -
Calling an
AnalysisPass
or aTransformationPass
like a function (as inpass_ = MyPass(); pass_(qc)
) will now respect any requirements that the pass might have. For example, scheduling passes such asALAPScheduleAnalysis
require thatTimeUnitConversion
runs before them. Running the pass via aPassManager
always respected this requirement, but until this note, it was not respected by calling the pass directly. -
When a
TranspilerError
subclass is raised by a pass inside a call toPassManger.run()
, the exception will now be propagated through losslessly, rather than becoming a chained exception with an erased type. -
SabreSwap
will no longer contract idle qubit wires out of control-flow blocks during routing. This was generally a valid optimization, but not an expected side effect of a routing pass. You can now use theContractIdleWiresInControlFlow
pass to perform this contraction. -
When
SabreLayout
is used to do both layout and routing simultaneously (as is the case for the default options totranspile()
andgenerate_preset_pass_manager()
) on aTarget
orCouplingMap
with disjoint connectivity, and the input circuit fits into a single component of the coupling map, the routing permutation will now be tracked correctly.Previously, any qubits in the coupling map that were not connected, even indirectly, to a qubit used by the routed circuit would not be included in the final routing permutation. This could cause surprising behaviour a long way from the point of failure, even if compilation appeared to succeed, such as calls to
TranspileLayout.final_index_layout()
raisingKeyError
.This bug did not affect backends that were fully connected, as most are.
-
Fixed a bug where a initializing
SparsePauliOp
with a large number of Pauli-Y
terms (typically ) and no explicitcoeffs
would result in a coefficient close to 1 but with a floating point error. The coefficient is now correctly 1 per default. Fixed #13522. -
Fixed a per-process based non-determinism in SparsePauliOp.to_matrix. The exact order of the floating-point operations in the summation would previously vary per process, but will now be identical between different invocations of the same script. See #13413.
1.3.0rc1
New Features
- Support for level 1 data was added to
BackendSamplerV2
as was support for passing options through to therun()
method of the wrappedBackendV2
. The run options can be specified using a"run_options"
entry inside of theoptions
dicitonary passed toBackendSamplerV2
. The"run_options"
entry should be a dictionary mapping argument names to values for passing to the backend’srun()
method. When a"meas_level"
option with a value of 1 is set in the run options, the results from the backend will be treated as level 1 results rather as bit arrays (the level 2 format).
Circuits Features
-
Improved the functionality of
CommutationChecker
to include support for the following parameterized gates with free parameters:RXXGate
,:class:.RYYGate,:class:.RZZGate,:class:.RZXGate,RXGate
,:class:.RYGate,:class:.RZGate,:class:.PhaseGate,U1Gate
,:class:.CRXGate,:class:.CRYGate,:class:.CRZGate,CPhaseGate
.Before these were only supported with bound parameters.
-
Added a new function
quantum_volume()
for generating a quantum volumeQuantumCircuit
object as defined in A. Cross et al. Validating quantum computers using randomized model circuits, Phys. Rev. A 100, 032328 (2019) https://link.aps.org/doi/10.1103/PhysRevA.100.032328. This new function differs from the existingQuantumVolume
class in that it returns aQuantumCircuit
object instead of building a subclass object. The second is that this new function is multithreaded and implemented in rust so it generates the output circuit ~10x faster than theQuantumVolume
class. -
Improved the runtime performance of constructing the
QuantumVolume
class with theclassical_permutation
argument set toTrue
. Internally it now calls thequantum_volume()
function which is written in Rust which is ~10x faster to generate a quantum volume circuit. -
Added a new circuit manipulation function
pauli_twirl_2q_gates()
that can be used to apply Pauli twirling to a given circuit. This only works for twirling a fixed set of two-qubit gates, currentlyCXGate
,ECRGate
,CZGate
,iSwapGate
. For example:from qiskit.circuit import QuantumCircuit, pauli_twirl_2q_gates qc = QuantumCircuit(2) qc.cx(0, 1) twirled_circuit = pauli_twirl_2q_gates(qc, seed=123456) twirled_circuit.draw("mpl")
-
Added binary arithmetic gates for inplace addition two -qubit registers, that is . The
ModularAdderGate
implements addition modulo , theAdderGate
implements standard addition including a carry-out, and theFullAdderGate
includes a carry-in qubit. See the respective documentations for details and examples.In contrast to the existing library circuits, such as
CDKMRippleCarryAdder
, handling the abstract gate allows the compiler (or user) to select the optimal gate synthesis, depending on the circuit’s context. -
Added the
MultiplierGate
for multiplication of two -qubit registers, that is . See the class documentations for details and examples. -
Quantum circuits in
qiskit.circuit.library.boolean_logic
now have equivalent representations asGate
objects:AndGate
, representingAND
,OrGate
, representingOR
,BitwiseXorGate
, representingXOR
,InnerProductGate
, representingInnerProduct
.
-
Specialized implementations of
__eq__()
have been added for all standard-library circuit gates. Most of the standard gates already specialized this method, but a few did not, and could cause significant slowdowns in unexpected places. -
Added
evolved_operator_ansatz()
,hamiltonian_variational_ansatz()
, andqaoa_ansatz()
to the circuit library to implement variational circuits based on operator evolutions.evolved_operator_ansatz()
andqaoa_ansatz()
are functionally equivalent toEvolvedOperatorAnsatz
andQAOAAnsatz
, but generally more performant.The
hamiltonian_variational_ansatz()
is designed to take a single Hamiltonian and automatically split it into commuting terms to implement a Hamiltonian variational ansatz. This could already be achieved manually by using theEvolvedOperatorAnsatz
, but is now more convenient to use. -
Added
grover_operator()
to construct a Grover operator circuit, used in e.g. Grover’s algorithm and amplitude estimation/amplification. This function is similar toGroverOperator
, but does not require choosing the implementation of the multi-controlled X gate a-priori and let’s the compiler choose the optimal decomposition instead. In addition to this, it does not wrap the circuit into an opaque gate and is faster as less decompositions are required to transpile.Example:
from qiskit.circuit import QuantumCircuit from qiskit.circuit.library import grover_operator oracle = QuantumCircuit(2) oracle.z(0) # good state = first qubit is |1> grover_op = grover_operator(oracle, insert_barriers=True) print(grover_op.draw())
-
Added a new argument
"apply_synthesis"
toDecompose
, which allows the transpiler pass to apply high-level synthesis to decompose objects that are only defined by a synthesis routine. For example:from qiskit import QuantumCircuit from qiskit.quantum_info import Clifford from qiskit.transpiler.passes import Decompose cliff = Clifford(HGate()) circuit = QuantumCircuit(1) circuit.append(cliff, [0]) # Clifford has no .definition, it is only defined by synthesis nothing_happened = Decompose()(circuit) # this internally runs the HighLevelSynthesis pass to decompose the Clifford decomposed = Decompose(apply_synthesis=True)(circuit)
-
Added the
iqp()
function to construct Instantaneous Quantum Polynomial time (IQP) circuits. In addition to the existingIQP
class, the function also allows construction of random IQP circuits:from qiskit.circuit.library import iqp random_iqp = iqp(num_qubits=4) print(random_iqp.draw())
-
Added the
MCMTGate
to represent a multi-control multi-target operation as a gate. This gate representation of the existingMCMT
circuit allows the compiler to select the best available implementation according to the number and the state of auxiliary qubits present in the circuit.Specific implementation can be chosen by specifying the high-level synthesis plugin:
from qiskit import QuantumCircuit, transpile from qiskit.circuit.library import MCMTGate, HGate from qiskit.transpiler.passes import HLSConfig # used for the synthesis config mcmt = MCMTGate(HGate(), num_ctrl_qubits=5, num_target_qubits=3) circuit = QuantumCircuit(20) circuit.append(mcmt, range(mcmt.num_qubits)) config = HLSConfig(mcmt=["vchain"]) # alternatively use the "noaux" method synthesized = transpile(circuit, hls_config=config)
The
MCMTGate
in addition also supports custom (i.e., open) control states of the control qubits. -
As a part of circuit library modernization, each of the following quantum circuits is either also represented as a
Gate
object or can be constructed using a synthesis method:GraphState
is represented byGraphStateGate
,FourierChecking
can be constructed usingfourier_checking()
,UnitaryOverlap
can be constructed usingunitary_overlap()
,HiddenLinearFunction
can be constructed usinghidden_linear_function()
,PhaseEstimation
can be constructed usingphase_estimation()
.
-
Added circuit library functions
pauli_feature_map()
,z_feature_map()
,zz_feature_map()
to construct Pauli feature map circuits. These functions are approximately 8x faster than the current circuit library objects,PauliFeatureMap
,ZFeatureMap
, andZZFeatureMap
, and will replace them in the future. Note, that the new functions return a plainQuantumCircuit
instead of aBlueprintCircuit
.The functions can be used as drop-in replacement:
from qiskit.circuit.library import pauli_feature_map, PauliFeatureMap fm = pauli_feature_map(20, paulis=["z", "xx", "yyy"]) also_fm = PauliFeatureMap(20, paulis=["z", "xx", "yyy"]).decompose()
OpenQASM Features
- The class
qasm3.CustomGate
is now inspectable programmatically. Itsconstructor
,name
,num_params
andnum_qubits
can now be viewed from Python after the object has been constructed. This allows you to inspect the contents of provided data attributes likeSTDGATES_INC_GATES
.
QPY Features
- Added a new QPY format version 13 that adds a Qiskit native representation of
ParameterExpression
objects.
Quantum Information Features
-
The performance of
SparsePauliOp.from_operator()
has been optimized on top of the algorithm improvements methods introduced in Qiskit 1.0. It is now approximately five times faster than before for fully dense matrices, taking approximately 40ms to decompose a 10q operator involving all Pauli terms. -
Added a new argument
assume_unitary
toqiskit.quantum_info.Operator.power()
. WhenTrue
, we use a faster method based on Schur’s decomposition to raise anOperator
to a fractional power. -
Added
SparsePauliOperator.to_sparse_list()
to convert an operator into a sparse list format. This works inversely toSparsePauliOperator.from_sparse_list()
. For example:from qiskit.quantum_info import SparsePauliOp op = SparsePauliOp(["XIII", "IZZI"], coeffs=[1, 2]) sparse = op.to_sparse_list() # [("X", [3], 1), ("ZZ", [1, 2], 2)] other = SparsePauliOp.from_sparse_list(sparse, op.num_qubits) print(other == op) # True
-
The performance of
Pauli.to_label()
has significantly improved for large Paulis. -
The method
Operator.power()
has a new parameterbranch_cut_rotation
. This can be used to shift the branch-cut point of the root around, which can affect which matrix is chosen as the principal root. By default, it is set to a small positive rotation to make roots of operators with a real-negative eigenvalue (like Pauli operators) more stable against numerical precision differences. -
A new observable class has been added.
SparseObservable
represents observables as a sum of terms, similar toSparsePauliOp
, but with two core differences:- Each complete term is stored as (effectively) a series of
(qubit, bit_term)
pairs, without storing qubits that undergo the identity for that term. This significantly improves the memory usage of observables such as the weighted sum of Paulis . - The single-qubit term alphabet is overcomplete for the operator space; it can represent Pauli operators (like
SparsePauliOp
), but also projectors onto the eigenstates of the Pauli operators, like . Such projectors can be measured on hardware equally as efficiently as their corresponding Pauli operator, butSparsePauliOp
would require an exponential number of terms to represent over qubits, whileSparseObservable
needs only a single term.
You can construct and manipulate
SparseObservable
using an interface familiar to users ofSparsePauliOp
:from qiskit.quantum_info import SparseObservable obs = SparseObservable.from_sparse_list([ ("XZY", (2, 1, 0), 1.5j), ("+-", (100, 99), 0.5j), ("01", (50, 49), 0.5), ])
SparseObservable
is not currently supported as an input format to the primitives (qiskit.primitives
), but we expect to expand these interfaces to include them in the future. - Each complete term is stored as (effectively) a series of
Synthesis Features
-
Added
adder_qft_d00()
,adder_ripple_c04()
, andadder_ripple_v95()
to synthesize the adder gates,ModularAdderGate
,AdderGate
, andFullAdderGate
. -
Added
multiplier_cumulative_h18()
andmultiplier_qft_r17()
to synthesize theMultiplierGate
. -
Added
synth_mcmt_vchain()
to synthesize the multi-control multi-target gate with a linear number of Toffoli gates and k-1 auxiliary qubits for k control qubits, along with the high-level synthesis pluginMCMTSynthesisVChain
. -
Added a high-level synthesis plugin structure for the
MCMTGate
, including theMCMTSynthesisNoAux
(for no auxiliary qubits), the aforementionedMCMTSynthesisVChain
(usingnum_control - 1
auxiliary qubits), and theMCMTSynthesisDefault
to let the compiler choose the optimal decomposition. -
Added
ProductFormula.expand()
which allows to view the expansion of a product formula in a sparse Pauli format. -
Added the plugin structure for the
PauliEvolutionGate
. The default plugin,PauliEvolutionSynthesisDefault
, constructs circuit as before, but faster as it internally uses Rust. The larger the circuit (e.g. by the Hamiltonian size, the number of timesteps, or the Suzuki-Trotter order), the higher the speedup. For example, a 100-qubit Heisenberg Hamiltonian with 10 timesteps and a 4th-order Trotter formula is now constructed ~9.4x faster. The new plugin,PauliEvolutionSynthesisRustiq
, uses the synthesis algorithm that is described in the paper “Faster and shorter synthesis of Hamiltonian simulation circuits” by de Brugière and Martiel (https://arxiv.org/abs/2404.03280) and is implemented in https://github.com/smartiel/rustiq-core. For example:from qiskit.circuit import QuantumCircuit from qiskit.quantum_info import SparsePauliOp from qiskit.circuit.library import PauliEvolutionGate from qiskit.compiler import transpile from qiskit.transpiler.passes import HLSConfig op = SparsePauliOp(["XXX", "YYY", "IZZ"]) qc = QuantumCircuit(4) qc.append(PauliEvolutionGate(op), [0, 1, 3]) config = HLSConfig(PauliEvolution=[("rustiq", {"upto_phase": False})]) tqc = transpile(qc, basis_gates=["cx", "u"], hls_config=config) tqc.draw(output='mpl')
This code snippet uses the
"rustiq"
plugin to synthesizePauliEvolutionGate
objects in the quantum circuit qc. The plugin is called with the additional option"upto_phase" = False
allowing to obtain smaller circuits at the expense of possibly not preserving the global phase. For the full list of supported options, see documentation forPauliEvolutionSynthesisRustiq
. -
Added a new argument
preserve_order
toProductFormula
, which allows re-ordering the Pauli terms in the Hamiltonian before the product formula expansion, to compress the final circuit depth. By setting this toFalse
, a term of form
will be re-ordered to
which will lead to the RZZ
and RYY
rotations being applied in parallel, instead of three sequential rotations in the first part.
This option can be set via the plugin interface:
from qiskit import QuantumCircuit, transpile
from qiskit.circuit.library import PauliEvolutionGate
from qiskit.quantum_info import SparsePauliOp
from qiskit.synthesis.evolution import SuzukiTrotter
from qiskit.transpiler.passes import HLSConfig
op = SparsePauliOp(["XXII", "IYYI", "IIZZ"])
time, reps = 0.1, 1
synthesis = SuzukiTrotter(order=2, reps=reps)
hls_config = HLSConfig(PauliEvolution=[("default", {"preserve_order": False})])
circuit = QuantumCircuit(op.num_qubits)
circuit.append(PauliEvolutionGate(op, time), circuit.qubits)
tqc = transpile(circuit, basis_gates=["u", "cx"], hls_config=hls_config)
print(tqc.draw())
Transpiler Features
-
Add an argument
matrix_based
to theCollectCliffords()
transpiler pass. If the new parametermatrix_based=True
, theCollectCliffords()
transpiler pass can collectRZGate(np.pi/2)
gates and other unitary gates that areClifford()
gates for certain parameters. -
The
RemoveIdentityEquivalent
transpiler pass is now run as part of the preset pass managers at optimization levels 2 and 3. The pass is run in theinit
stage and theoptimization
stage, because the optimizations it applies are valid in both stages and the pass is fast to execute. -
The
SabreLayout
transpiler pass has been updated to run an additional 2 or 3 layout trials by default independently of thelayout_trials
keyword argument’s value. A trivial layout and its reverse are included for all backends, just like theDenseLayout
trial that was added in 1.2.0. In addition to this, the largest rings on an IBM backend heavy hex connectivity graph are added if the backends are 127, 133, or 156 qubits. This can provide a good starting point for some circuits on these commonly run backends, while for all others it’s just an additional “random trial”. -
Improved handling of ancilla qubits in the
HighLevelSynthesis
transpiler pass. For example, a circuit may have custom gates whose definitions includeMCXGate
s. Now the synthesis algorithms for the inner MCX-gates can use the ancilla qubits available on the global circuit but outside the custom gates’ definitions. -
Port most of the logic of the transpiler pass
ElidePermutations
to Rust. -
Added a new transpiler pass,
RemoveIdentityEquivalent
that is used to remove gates that are equivalent to an identity up to some tolerance. For example if you had a circuit like:running the pass would eliminate the
CPhaseGate
:from qiskit.circuit import QuantumCircuit from qiskit.transpiler.passes import RemoveIdentityEquivalent qc = QuantumCircuit(2) qc.cp(1e-20, 0, 1) removal_pass = RemoveIdentityEquivalent() result = removal_pass(qc) result.draw("mpl")
-
The
ConsolidateGates
pass will now run the equivalent of theCollect2qBlocks
pass internally if it was not run in a pass manager prior to the pass. Previously it was required thatCollect2qBlocks
orCollect1qRuns
were run prior toConsolidateBlocks
forConsolidateBlocks
to do anything. By doing the collection internally the overhead of the pass is reduced. IfCollect2qBlocks
orCollect1qRuns
are run prior toConsolidateBlocks
the collected runs by those passes from the property set are used and there is no change in behavior for the pass.
Visualization Features
- The
timeline_drawer()
visualization function has a new argumenttarget
, used to specify aTarget
object for the visualization. By default the function used theInstruction.duration
to get the duration of a given instruction, but specifying the target will leverage the timing details inside the target instead.
Known Issues
-
Versions of Qiskit before 1.2.4 will not be able to load QPY files dumped using
qpy.dump()
, even withversion
set appropriately, if:- there are unbound
ParameterExpression
s in the QPY file, - the
use_symengine=True
flag was set (which is the default in Qiskit >= 1.0.0) inqpy.dump()
, - the version of
symengine
installed in the generating and loading environments are not within the same minor version.
This applies regardless of the version of Qiskit used in the generation (at least up to Qiskit 1.2.4 inclusive).
If you want to maximize compatibility with older versions of Qiskit, you should set
use_symengine=False
. Newer versions of Qiskit should not require this. - there are unbound
-
QPY files from the Qiskit 0.45 series can, under a very specific and unlikely set of circumstances, fail to load with any newer version of Qiskit, including Qiskit 1.2.4. The criteria are:
- the
QuantumCircuit
orScheduleBlock
to be dumped contained unboundParameterExpression
objects, - the installed version of
symengine
was in the 0.9 series (which was the most recent release during the support window of Qiskit 0.45), - the
use_symengine=True
flag was set (which was not the default).
Later versions of Qiskit used during generation are not affected, because they required newer versions than
symengine
0.9.In this case, you can recover the QPY file by reloading it with an environment with Qiskit 0.45.3 and
symengine
0.9.2 installed. Then, useqpy.dump()
withuse_symengine=False
to re-export the file. This will then be readable by any newer version of Qiskit. - the
-
When using QPY formats 10, 11, or 12 there is a dependency on the version of
symengine
installed in the payload for serializedParamerExpression
if there is mismatched version of the installedsymengine
package between the environment that generated the payload withqpy.dump()
and the installed version that is trying to load the payload withqpy.load()
. If this is encountered you will need to install the symengine version from the error message emitted to load the payload. QPY format version >= 13 (or < 10) will not have this issue and it is recommended if you’re serializingParameterExpression
objects as part of your circuit or anyScheduleBlock
objects you use version 13 to avoid this issue in the future.
Upgrade Notes
-
The supported versions of symengine have been pre-emptively capped at < 0.14.0 (which is expected to be the next minor version, as of this release of Qiskit). This has been done to protect against a potential incompatibility in
qpy
when serializingParameterExpression
objects. The serialization used in QPY Format versions 10, 11, and 12 forParameterExpression
objects is tied to the symengine version used to generate it, and there is the potential for a future symengine release to not be compatible. This upper version cap is to prevent a future release of symengine causing incompatibilities when trying to load QPY files usingqpy.load
. -
When using
BackendSamplerV2
, circuit metadata is no longer cleared before passing circuits to therun()
method of the wrappedBackendV2
instance. -
The following classes now use the operation to diagonalize the Pauli-Y operator:
PauliEvolutionGate
,EvolvedOperatorAnsatz
,PauliFeatureMap
. Previously, these classes used either or as basis transformation. Using the operation, represented by theSXGate
is more efficient as it uses only a single gate implemented as singleton.
Circuits Upgrade Notes
- The
QuantumVolume
class will generate circuits with different unitary matrices and permutations for a given seed value from the previous Qiskit release. This is due to using a new internal random number generator for the circuit generation that will generate the circuit more quickly. If you need an exact circuit with the same seed you can use the previous release of Qiskit and generate the circuit with theflatten=True
argument and export the circuit withqpy.dump()
and then load it with this release.
QPY Upgrade Notes
- The
qpy.dump()
function will now emit format version 13 by default. This means payloads generated with this function by default will only be compatible with Qiskit >= 1.3.0. If you need for the payload to be loaded by a older version of Qiskit you can use theversion
flag onqpy.dump()
to emit a version compatible with earlier releases of Qiskit. You can refer to QPY Compatibility for more details on this.
Deprecation Notes
-
The Qiskit Pulse package is being deprecated and will be removed in Qiskit 2.0.0. Pulse-level access is currently only supported on a subset of Eagle devices and not supported on the Heron architecture. Furthermore, newer IBM Quantum architectures will not support pulse-level access. As a consequence, supporting Pulse as a first-class citizen frontend in the Qiskit SDK itself makes little sense going forward. The deprecation includes all pulse code in
qiskit.pulse
as well as functionality dependant or related to pulse such as pulse visualization, serialization and custom calibration support. For more details see the deprecation sections.The Pulse package as a whole, along with directly related components in Qiskit, will be moved to the Qiskit Dynamics repository to further enable pulse and low-level control simulation.
Circuits Deprecations
-
Deprecated the
Instruction.condition
attribute and theInstruction.c_if()
method. They will be removed in Qiskit 2.0, along with any uses in the Qiskit data model. This functionality has been superseded by theIfElseOp
class which can be used to describe a classical condition in a circuit. For example, a circuit usingInstruction.c_if()
like:from qiskit.circuit import QuantumCircuit qc = QuantumCircuit(2, 2) qc.h(0) qc.x(0).c_if(0, 1) qc.z(1.c_if(1, 0) qc.measure(0, 0) qc.measure(1, 1)
can be rewritten as:
qc = QuantumCircuit(2, 2) qc.h(0) with expected.if_test((expected.clbits[0], True)): qc.x(0) with expected.if_test((expected.clbits[1], False)): qc.z(1) qc.measure(0, 0) qc.measure(1, 1)
The now deprecated
ConvertConditionsToIfOps
transpiler pass can be used to automate this conversion for existing circuits. -
As part of the Qiskit Pulse package deprecation, the following dependencies are deprecated as well:
qiskit.circuit.QuantumCircuit.calibrations
qiskit.circuit.QuantumCircuit.has_calibration_for()
qiskit.circuit.QuantumCircuit.add_calibration()
qiskit.dagcircuit.DAGCircuit.calibrations
qiskit.dagcircuit.DAGCircuit.has_calibration_for()
qiskit.dagcircuit.DAGCircuit.add_calibration()
qiskit.dagcircuit.DAGDependency.calibrations
-
The
QuantumCircuit.unit
andQuantumCircuit.duration
attributes have been deprecated and will be removed in Qiskit 2.0.0. These attributes were used to track the estimated duration and unit of that duration to execute on the circuit. However, the values of these attributes were always limited, as they would only be properly populated if the transpiler were run with the correct settings. The duration was also only a guess based on the longest path on the sum of the duration ofDAGCircuit
and wouldn’t ever correctly account for control flow or conditionals in the circuit. -
The
DAGCircuit.unit
andDAGCircuit.duration
attributes have been deprecated and will be removed in Qiskit 2.0.0. These attributes were used to track the estimated duration and unit of that duration to execute on the circuit. However, the values of these attributes were always limited, as they would only be properly populated if the transpiler were run with the correct settings. The duration was also only a guess based on the longest path on the sum of the duration ofDAGCircuit
and wouldn’t ever correctly account for control flow or conditionals in the circuit. -
The
Instruction.duration
andInstruction.unit
attributes have been deprecated and will be removed in Qiskit 2.0.0. This includes setting theunit
orduration
arguments for anyqiskit.circuit.Instruction
or subclass. These attributes were used to attach a custom execution duration and unit for that duration to an individual instruction. However, the source of truth of the duration of a gate is theBackendV2
Target
which contains the duration for each instruction supported on the backend. The duration of an instruction is not something that’s typically user adjustable and is an immutable property of the backend. If you were previously using this capability to experiment with different durations for gates you can mutate theInstructionProperties.duration
field in a givenTarget
to set a custom duration for an instruction on a backend (the unit is always in seconds in theTarget
).
Providers Deprecations
-
The
BasicSimulator.configuration()
method is deprecated and will be removed in 2.0.0. This method returned a legacyproviders.models.BackendConfiguration
instance which is part of the deprecatedBackendV1
model. This model has been replaced withBackendV2
, where the constraints are stored directly in the backend instance or the underlyingTarget
(backend.target
).Here is a quick guide for accessing the most common
BackendConfiguration
attributes in theBackendV2
model:””BackendV1 model (deprecated) ————> BackendV2 model —————————- ————— backend.configuration().backend_name backend.name backend.configuration().backend_version backend.backend_version backend.configuration().n_qubits backend.num_qubits backend.configuration().num_qubits backend.num_qubits backend.configuration().basis_gates backend.target.operation_names (*) backend.configuration().coupling_map backend.target.build_coupling_map() backend.configuration().local No representation backend.configuration().simulator No representation backend.configuration().conditional No representation backend.configuration().open_pulse No representation backend.configuration().memory No representation backend.configuration().max_shots No representation
(*) Note that
backend.target.operation_names
includesbasis_gates
and additional non-gate instructions, in some implementations it might be necessary to filter the output.See this guide for more information on migrating to the
BackendV2
model. -
As part of the Qiskit Pulse package deprecation, all pulse-related functionality in
qiskit.providers.BackendV2
class is being deprecated. This includes the following methods:instruction_schedule_map()
drive_channel()
measure_channel()
acquire_channel()
control_channel()
Consequently, the corresponding channel methods in the
qiskit.providers.BackendV2Converter
andqiskit.providers.fake_provider.GenericBackendV2
classes are being deprecated as well.In addition, the pulse_channels and calibrate_instructions arguments in the
BackendV2
initializer method are being deprecated. -
The defaults argument is being deprecated from the
qiskit.providers.backend_compat.convert_to_target()
function.
QPY Deprecations
- As part of the Qiskit Pulse package deprecation, serializing a
qiskit.pulse.ScheduleBlock
-based payloads is being deprecated. In particular, passingqiskit.pulse.ScheduleBlock
objects to the programs argument in theqiskit.qpy.dump()
function is being deprecated.
Transpiler Deprecations
-
The transpiler pass
ConvertConditionsToIfOps
has been deprecated and will be removed in Qiskit 2.0.0. This class is now deprecated because the underlying data model forInstruction.condition
which this pass is converting from has been deprecated and will be removed in 2.0.0. -
Providing custom gates through the
basis_gates
argument is deprecated for bothtranspile()
andgenerate_preset_pass_manager()
, this functionality will be removed in Qiskit 2.0. Custom gates are still supported in theTarget
model, and can be provided through thetarget
argument. One can build aTarget
instance from scratch or use theTarget.from_configuration()
method with thecustom_name_mapping
argument. For example:from qiskit.circuit.library import XGate from qiskit.transpiler.target import Target basis_gates = ["my_x", "cx"] custom_name_mapping = {"my_x": XGate()} target = Target.from_configuration( basis_gates=basis_gates, num_qubits=2, custom_name_mapping=custom_name_mapping )
-
As part of the Qiskit Pulse package deprecation, pulse-related aspects in the
qiskit.transpiler.Target
class are being deprecated. These include:calibration
update_from_instruction_schedule_map()
has_calibration()
get_calibration()
instruction_schedule_map()
In addition the following transpiler passer are also being deprecated:
PulseGates
ValidatePulseGates
RXCalibrationBuilder
RZXCalibrationBuilder
EchoRZXWeylDecomposition
-
The inst_map argument in
generate_preset_pass_manager()
,from_configuration()
,PassManagerConfig
initializer andgenerate_scheduling()
is being deprecated. -
The calibration argument in
InstructionProperties()
initializer methods is being deprecated. -
The following
transpile()
andgenerate_preset_pass_manager()
arguments are deprecated in favor of defining a customTarget
:instruction_durations
,timing_constraints
, andbackend_properties
. These arguments can be used to build a target withTarget.from_configuration()
:Target.from_configuration( ... backend_properties = backend_properties, instruction_durations = instruction_durations, timing_constraints = timing_constraints )
-
The method
PassManagerConfig.from_backend()
will stop supporting inputs of typeBackendV1
in the backend parameter in a future release no earlier than 2.0.BackendV1
is deprecated and implementations should move toBackendV2
.
Visualization Deprecations
- As part of the Qiskit Pulse package deprecation, pulse drawing via
qiskit.visualization.pulse_drawer()
is being deprecated.
Misc. Deprecations
-
The
qiskit.result.mitigation
module has been deprecated and will be removed in the 2.0 release. The deprecation includes theLocalReadoutMitigator
andCorrelatedReadoutMitigator
classes as well as the associated utils. Their functionality has been superseded by the mthree package, found in https://github.com/Qiskit/qiskit-addon-mthree. -
As part of the Qiskit Pulse package deprecation, the following functions and class are being deprecated as well:
qiskit.compiler.schedule()
qiskit.compiler.sequence()
qiskit.assembler.assemble_schedules()
qiskit.scheduler.methods.as_soon_as_possible()
qiskit.scheduler.methods.as_late_as_possible()
qiskit.scheduler.schedule_circuit.schedule_circuit()
qiskit.scheduler.ScheduleConfig
Bug Fixes
-
Fixed a bug in the transpiler pass
ElidePermutations
where the qubit mapping was not updated correctly in the presence ofPermutationGate
s. -
Fixed a potential source of non-determinism in
DenseLayout
(and by extensionSabreLayout
) when targeting aCouplingMap
orTarget
that has more than one subgraph with the same degree of connectivity. In these case the exact output layout from the pass could previously fluctuate based on the number of local CPUs and thread execution speed. -
The
HighLevelSynthesis
transpiler pass no longer raises an exception when encountering a custom gate that is called “qft” but is not an instance ofQFTGate
. Instead, the synthesis plugins for QFT gates ignore such a gate, and the gate’s definition is used (if provided). -
Fixed an issue introduced in the now yanked 1.2.3 bugfix release that would cause an exception with the error message “Qiskit doesn’t support loading a symengine payload generated with symengine >= 1.0” to be raised whenever loading a QPY file that was generated with a different symengine version from the version installed by the loading. This issue could only occur in 1.2.3.
-
Fixed an issue with
qpy.load()
when loading a QPY file containing aParameterExpression
, if the versions ofsymengine
installed in the generating and loading environments were not the same. For example, if a QPY file containingParameterExpression
s was generated using Qiskit 1.2.2 withsymengine==0.11.0
installed, Qiskit 1.2.2 withsyengine==0.13.0
installed would be unable to load it.Previously, an error would have been raised by
symengine
around this version mismatch. This has been worked around forsymengine
0.11 and 0.13 (there was no 0.12), but if you’re trying to use different versions ofsymengine
and there is a mismatch, this version of Qiskit still might not work. -
Fixed an issue when running
transpile()
orrun()
on a pass manager generated bygenerate_preset_pass_manager()
usingoptimization_level
2 or 3 when therouting_method
argument is set to"none"
to explicitly disable routing. Previously under these conditions the transpiler would run theElidePermutation
pass as part of the init stage as under normal conditions this is a useful optimization to removeSwapGate
andPermutationGate
instances from the circuit. But whenrouting_method="none"
this optimization wasn’t expected as it permutes the circuit in a similar manner to routing which shouldn’t be performed whenrouting_method="none"
. This has been fixed by no longer runningElidePermutation
ifrouting_method="none"
is set. Fixed #13144 -
The OpenQASM 2 importer previously would output incorrect
Gate
instances for gate calls referring to agate
definition that followed a priorgate
definition that was being treated as a built-in operation by aCustomInstruction
. See #13339 for more detail. -
The OpenQASM 3 exporter has restored its behavior of accepting non-standard-library include files in the
includes
argument toqasm3.dump()
,dumps()
, andExporter
. These will insert a suitableinclude
statement into the output as before, and the exporter remains unaware of the intended gates in that include file; you should pass the gates you expect it to define in thebasis_gates
argument to the same functions.We expect to improve the export mechanism against non-standard include files in a future release of Qiskit.
-
Fixed a performance regression in
QuantumCircuit.assign_parameters()
introduced in Qiskit 1.2.0 when calling the method in a tight loop, binding only a small number of parameters out of a heavily parametric circuit on each iteration. If possible, it is still more performant to callassign_parameters()
only once, with all assignments at the same time, as this reduces the proportion of time spent on input normalization and error-checking overhead. -
For
BasicSimulator
, thebasis_gates
entry in the configuration instance returned by theconfiguration()
is now a list rather than adict_keys
instance, matching the expected type and allowing for configuration instance to be deep copied. -
Fixed a bug in
QuantumCircuit.decompose()
where objects that could be synthesized withHighLevelSynthesis
were first synthesized and then decomposed immediately (i.e., they were decomposed twice instead of once). This affected, e.g.,MCXGate
orClifford
, among others. -
Fixed a bug in
QuantumCircuit.decompose()
, where high-level objects without a definition were not decomposed if they were explicitly set via the"gates_to_decompose"
argument. For example, previously the following did not perform a decomposition but now works as expected:from qiskit import QuantumCircuit from qiskit.quantum_info import Clifford from qiskit.transpiler.passes import Decompose cliff = Clifford(HGate()) circuit = QuantumCircuit(1) circuit.append(cliff, [0]) decomposed = Decompose(gates_to_decompose=["clifford"])(circuit)
-
Fixed
Operator.power()
when called with non-integer powers on a matrix whose Schur form is not diagonal (for example, most non-unitary matrices). -
Operator.power()
will now more reliably return the expected principal value from a fractional matrix power of a unitary matrix with a eigenvalue. This is tricky in general, because floating-point rounding effects can cause a matrix to _truly_ have an eigenvalue on the negative side of the branch cut (even if its exact mathematical relation would not), and imprecision in various BLAS calls can falsely find the wrong side of the branch cut.Operator.power()
now shifts the branch-cut location for matrix powers to be a small complex rotation away from . This does not solve the problem, it just shifts it to a place where it is far less likely to be noticeable for the types of operators that usually appear. Use the newbranch_cut_rotation
parameter to have more control over this.See #13305.
1.3.0b1
New Features
-
Added a new class
QFTGate
for natively representing Quantum Fourier Transforms (QFTs). The older way of representing QFTs via quantum circuits, seeQFT
, remains for backward compatibility. The new way of representing a QFT via a gate avoids synthesizing its definition circuit when the gate is declared, delaying the actual synthesis to the transpiler. It also allows to easily choose between several different algorithms for synthesizing QFTs, which are available as high-level-synthesis plugins. -
Added a synthesis method
synth_qft_full()
for constructing a QFT circuit assuming a fully-connected architecture. -
Added two high-level-synthesis plugins for synthesizing a
QFTGate
. The classQFTSynthesisFull
is based onsynth_qft_full()
and synthesizes a QFT gate assuming all-to-all connectivity. The classQFTSynthesisLine
is based onsynth_qft_line()
and synthesizes a QFT gate assuming linear nearest neighbor connectivity. -
MCXVChain
has two new Boolean parameters relative_phase and action_only. If action_only the circuit does not clean the dirty qubits. If relative_phase the gate is implemented up to a global phase. Both parameters are used to optimize the decomposition of MCXVChain. -
Added two parameters to
GenericBackendV2
to exclude error (noise_info) and pulse channel information (pulse_channels) from the construction of the backend. These parameters are true by default, replicating the initial default behavior of the constructor. A memory-sensitive user may set these options to False to reduce the memory overhead by 40x when transpiling on large- scaleGenericBackendV2
. -
The
StabilizerState
class now has a new methodprobabilities_dict_from_bitstring()
allowing the user to pass single bitstring to measure an outcome for. Previouslly theprobabilities_dict()
would be utilized and would at worst case calculate (2^n) number of probability calculations (depending on the state), even if a user wanted a single result. With this new method the user can calculate just the single outcome bitstring value a user passes to measure the probability for. As the number of qubits increases, the more prevelant the performance enhancement may be (depending on the state) as only 1 bitstring result is measured. -
Implemented
UniformSuperpositionGate
class, which allows the creation of a uniform superposition state using the Shukla-Vedula algorithm. This feature facilitates the creation of quantum circuits that produce a uniform superposition state , where is a positive integer representing the number of computational basis states with an amplitude of . This implementation supports the efficient creation of uniform superposition states, requiring only qubits and gates. Usage example:from qiskit import QuantumCircuit from qiskit.circuit.library.data_preparation import UniformSuperpositionGate M = 5 num_qubits = 3 usp_gate = UniformSuperpositionGate(M, num_qubits) qc = QuantumCircuit(num_qubits) qc.append(usp_gate, list(range(num_qubits))) qc.draw()
Circuits Features
-
Added support for
AnnotatedOperation.params()
andAnnotatedOperation.validate_parameter()
, which enable circuit-level parameter handling (such as binding parameters) for annotated operations. -
CircuitInstruction
andDAGOpNode
each have new methods to query various properties of their internalOperation
, without necessarily needing to access it. These methods are:CircuitInstruction.is_standard_gate()
andDAGOpNode.is_standard_gate()
,CircuitInstruction.is_controlled_gate()
andDAGOpNode.is_controlled_gate()
,CircuitInstruction.is_directive()
andDAGOpNode.is_directive()
,CircuitInstruction.is_control_flow()
andDAGOpNode.is_control_flow()
, andCircuitInstruction.is_parameterized()
andDAGOpNode.is_parameterized()
.
If applicable, using any of these methods is significantly faster than querying
CircuitInstruction.operation
orDAGOpNode.op
directly, especially if the instruction or node represents a Qiskit standard gate. This is because the standard gates are stored natively in Rust, and their Python representation is only created when requested. -
A native rust representation of Qiskit’s standard gate library has been added. When a standard gate is added to a
QuantumCircuit
orDAGCircuit
it is now represented in a more efficient manner directly in Rust seamlessly. Accessing that gate object from a circuit or dag will return a new Python object representing the standard gate. This leads to faster and more efficient transpilation and manipulation of circuits for functionality written in Rust. -
Improved performance of the method
DAGCircuit.quantum_causal_cone()
by not examining the same non-directive node multiple times when reached from different paths. -
Replacing the internal synthesis algorithm of
StatePreparation
andInitialize
of Shende et al. by the algorithm given inIsometry
of Iten et al. The new algorithm reduces the number of CX gates and the circuit depth by a factor of 2. -
ParameterExpression
now supports the unary+
operator. -
Added a new function to
qiskit.circuit.random
that allows to generate a pseudo-random Clifford circuit with gates from the standard library:random_clifford_circuit()
. Example usage:from qiskit.circuit.random import random_clifford_circuit circ = random_clifford_circuit(num_qubits=2, num_gates=6) circ.draw(output='mpl')
-
A new data attribute,
qiskit.circuit.CONTROL_FLOW_OP_NAMES
, is available to easily find and check whether a givenInstruction
is a control-flow operation by name. -
The standard equivalence library (
SessionEquivalenceLibrary
) now has rules that can directly convert between Qiskit’s standard-library 2q continuous Ising-type interactions (e.g.CPhaseGate
,RZZGate
,RZXGate
, and so on) using local equivalence relations. Previously, several of these conversions would go via a 2-CX form, which resulted in less efficient circuit generation.NoteIn general, the
BasisTranslator
is not guaranteed to find the “best” equivalence relation for a givenTarget
, but will always find an equivalence if one exists. We rely on more expensive resynthesis and gate-optimization passes in the transpiler to improve the output. These passes are currently not as effective for basis sets with a continuously parametrized two-qubit interaction as they are for discrete super-controlled two-qubit interactions. -
The random_circuit function from qiskit.circuit.random.utils has a new feature where users can specify a distribution num_operand_distribution (a dict) that specifies the ratio of 1-qubit, 2-qubit, 3-qubit, and 4-qubit gates in the random circuit. For example, if num_operand_distribution = {1: 0.25, 2: 0.25, 3: 0.25, 4: 0.25} is passed to the function then the generated circuit will have approximately 25% of 1-qubit, 2-qubit, 3-qubit, and 4-qubit gates (The order in which the dictionary is passed does not matter i.e. you can specify num_operand_distribution = {3: 0.5, 1: 0.0, 4: 0.3, 2: 0.2} and the function will still work as expected). Also it should be noted that the if num_operand_distribution is not specified then max_operands will default to 4 and a random circuit with a random gate distribution will be generated. If both num_operand_distribution and max_operands are specified at the same time then num_operand_distribution will be used to generate the random circuit. Example usage:
from qiskit.circuit.random import random_circuit circ = random_circuit(num_qubits=6, depth=5, num_operand_distribution = {1: 0.25, 2: 0.25, 3: 0.25, 4: 0.25}) circ.draw(output='mpl')
-
PauliFeatureMap
andZZFeatureMap
now support specifying the entanglement as a dictionary where the keys represent the number of qubits, and the values are lists of integer tuples that define which qubits are entangled with one another. This allows for more flexibility in constructing feature maps tailored to specific quantum algorithms. Example usage:from qiskit.circuit.library import PauliFeatureMap entanglement = { 1: [(0,), (2,)], 2: [(0, 1), (1, 2)], 3: [(0, 1, 2)], } qc = PauliFeatureMap(3, reps=2, paulis=['Z', 'ZZ', 'ZZZ'], entanglement=entanglement, insert_barriers=True) qc.decompose().draw('mpl')
-
The
count_ops()
method inQuantumCircuit
has been re-written in Rust. It now runs between 3 and 9 times faster. -
Added the
insert_barriers
keyword argument to therepeat()
method. Setting it toTrue
will insert barriers between circuit repetitions. -
QuantumCircuit
has several new methods to work with and inspect manualVar
variables.See Working with real-time typed classical data for more in-depth discussion on all of these.
The new methods are:
add_var()
add_input()
add_capture()
add_uninitialized_var()
get_var()
has_var()
iter_vars()
iter_declared_vars()
iter_captured_vars()
iter_input_vars()
store()
In addition, there are several new dynamic attributes on
QuantumCircuit
surrounding these variables: -
ControlFlowOp
and its subclasses now have aiter_captured_vars()
method, which will return an iterator over the unique variables captured in any of its immediate blocks. -
DAGCircuit
has several new methods to work with and inspect manualVar
variables. These are largely equivalent to theirQuantumCircuit
counterparts, except that theDAGCircuit
ones are optimized for programmatic access with already defined objects, while theQuantumCircuit
methods are more focussed on interactive human use.The new methods are:
add_input_var()
add_captured_var()
add_declared_var()
has_var()
iter_vars()
iter_declared_vars()
iter_captured_vars()
iter_input_vars()
There are also new public attributes:
-
DAGCircuit.wires
will now also contain anyVar
manual variables in the circuit as well, as these are also classical data flow. -
A new method,
Var.new()
, is added to manually construct a real-time classical variable that owns its memory. -
QuantumCircuit.compose()
has two need keyword arguments,var_remap
andinline_captures
to better support real-time classical variables.var_remap
can be used to rewriteVar
nodes in the circuit argument as its instructions are inlined onto the base circuit. This can be used to avoid naming conflicts.inline_captures
can be set toTrue
(defaults toFalse
) to link allVar
nodes tracked as “captures” in the argument circuit with the sameVar
nodes in the base circuit, without attempting to redeclare the variables. This can be used, in combination withQuantumCircuit.copy_empty_like()
’svars_mode="captures"
handling, to build up a circuit layer by layer, containing variables. -
DAGCircuit.compose()
has a new keyword argument,inline_captures
, which can be set toTrue
to inline “captured”Var
nodes on the argument circuit onto the base circuit without redeclaring them. In conjunction with thevars_mode="captures"
option to severalDAGCircuit
methods, this can be used to combine DAGs that operate on the same variables. -
QuantumCircuit.copy_empty_like()
andDAGCircuit.copy_empty_like()
have a new keyword argument,vars_mode
which controls how any memory-owningVar
nodes are tracked in the output. By default ("alike"
), the variables are declared in the same input/captured/local mode as the source. This can be set to"captures"
to convert all variables to captures (useful withcompose()
) or"drop"
to remove them. -
A new
vars_mode
keyword argument has been added to theDAGCircuit
methods:which has the same meaning as it does for
copy_empty_like()
.
Primitives Features
-
Added a new method
BitArray.postselect()
that returns all shots containing specified bit values. Example usage:from qiskit.primitives.containers import BitArray ba = BitArray.from_counts({'110': 2, '100': 4, '000': 3}) print(ba.postselect([0,2], [0,1]).get_counts()) # {'110': 2, '100': 4}
-
The metadata of Primitives V2 implementations, i.e.,
StatevectorSampler
,StatevectorEstimator
,BackendSamplerV2
andBackendEstimatorV2
, has been updated to match that of IBM quantum devices.version
andcircuit_metadata
are added for all V2 implementationsshots
is added forBackendSamplerV2
andBackendEstimatorV2
precision
is renamed withtarget_precision
forStatevectorEstimator
Note that metadata of
StatevectorEstimator
does not haveshots
because the class computes expectation values withStatevector
and shots are not used. -
Estimator
andStatevectorEstimator
return expectation values in a stochastic way if the input circuit includes a reset for a some subsystems. The result was not reproducible, but it is now reproducible if a random seed is set. For example:from qiskit.primitives import StatevectorEstimator estimator = StatevectorEstimator(seed=123)
or:
from qiskit.primitives import Estimator estimator = Estimator(options={"seed":123})
OpenQASM Features
-
The internal symbol table of the OpenQASM 3 exporter (
qiskit.qasm3
) has been rewritten, which should result in cleaner outputs when using Qiskit standard-library gates that are not in the OpenQASM 3 standard-library headers, and more deterministic outputs. For example, using severalRZXGate
s will now result in only a single parametric definition, and when naming collisions occur, the symbol table will assign a deterministic counter to make names unique, rather than a non-deterministic integer (previously, the object identity was used). -
The vendored version of the OpenQASM 3.0 standard library has been updated to match the state as of commit 4ca1d79383. This should generally have no effect on your use of Qiskit, unless you were retrieving our vendored file for your own use.
-
The OpenQASM 3 exporter supports manual-storage
Var
nodes on circuits.
QPY Features
- QPY (
qiskit.qpy
) format version 12 has been added, which includes support for memory-owningVar
variables. See Version 12 for more detail on the format changes.
Synthesis Features
-
Port internal binary matrix utils from Python to Rust, including binary matrix multiplication, gaussian elimination, rank calculation, binary matrix inversion, and random invertible binary matrix generation. These functions are not part of the Qiskit API, and porting them to rust improves the performance of certain synthesis methods.
-
MCXRecursive
with control qubits and a single clean auxiliary qubit now requires at most CX gates. -
Port
synth_permutation_acg()
, used to synthesize qubit permutations, to Rust. This produces an approximate 3x performance improvement on 1000 qubit circuits. -
Port
synth_permutation_basic()
, used to synthesize qubit permutations, to Rust. -
Port
synth_cnot_full_pmh()
, used to synthesize a linear function into a CX network, to Rust. This produces approximately 44x speedup, as measured on 100 qubit circuits. -
The function
synth_cnot_full_pmh()
now allows choosing the (heuristically) optimalsection_size
by setting it toNone
. Then, a value is chosen which attempts to minimize the upper bound on the number of CX gates, that is where is the number of qubits and . -
The function
synth_clifford_bm()
was ported to Rust. Recall that this function optimally synthesizesClifford
operators on 1, 2 or 3 qubits with respect to the number of CX-gates. This leads to a significant increase in performance. For Cliffords over 3 qubits, the speedup in on the order of 80 times. -
The function
synth_clifford_greedy()
that synthesizesClifford
operators was ported to Rust, leading to a significant increase in performance for all numbers of qubits. For Cliffords over 50 qubits, the speedup is on the order of 1000 times. -
Port
synth_permutation_depth_lnn_kms()
, used to synthesize permutations for linear connectivity, to Rust. -
Added synthesis functions
synth_mcx_gray_code()
andsynth_mcx_noaux_v24()
that synthesize multi-controlled X gates. These functions do not require additional ancilla qubits. -
Added synthesis functions
synth_c3x()
andsynth_c4x()
that synthesize 3-controlled and 4-controlled X-gates respectively. -
Add a synthesis function
synth_mcx_n_dirty_i15()
that synthesizes a multi-controlled X gate with controls using dirty ancillary qubits producing a circuit with at most CX gates, by Iten et. al. (arXiv:1501.06911). -
Add a synthesis function
synth_mcx_n_clean_m15()
that synthesizes a multi-controlled X gate with controls using clean ancillary qubits producing a circuit with at most CX gates, by Maslov (arXiv:1508.03273). -
Add a synthesis function
synth_mcx_1_clean_b95()
that synthesizes a multi-controlled X gate with controls using a single clean ancillary qubit producing a circuit with at most CX gates, by Barenco et al. (arXiv:quant-ph/9503016). -
The function
random_clifford()
was ported to Rust, improving the runtime by a factor of 3. -
Port :func: .synth_cz_depth_line_mr to Rust. This function synthesizes a CZ circuit for linear nearest neighbor (LNN) connectivity, based on the Maslov and Roetteler method. On a 350x350 binary matrix, the Rust implementation yields a speedup of about 30 times.
-
Port :func: .synth_permutation_reverse_lnn_kms to Rust, which synthesizes a reverse permutation for linear nearest-neighbor architecture using the Kutin, Moulton, Smithline method.
-
Added the
wrap
keyword argument to theProductFormula
classes which (when enabled) wraps individual Pauli evolution terms. This can be useful when visualizing circuits.
Transpiler Features
-
Added a new import path option for
generate_preset_pass_manager()
, so that it can now be imported as:from qiskit import generate_preset_pass_manager
instead of having to type the full path:
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
The function is also importable from the
qiskit.transpiler
module as:from qiskit.transpiler import generate_preset_pass_manager
-
Added a new user config file option
sabre_all_threads
and a corresponding environment variableQISKIT_SABRE_ALL_THREADS
. When this flag is set the preset pass managers will run theSabreLayout
andSabreSwap
transpiler passes using all the available CPUs on the local system. Using this option is a tradeoff between determinism of output between different computers and potentially better output with fewerSwapGate
s.These transpiler passes run multiple random trials in parallel and pick the output which results in the fewest
SwapGate
s. As a rule of thumb, if you run more trials, this provides the algorithm more opportunities to find a better result. By default, the preset pass managers use a fixed number of trials, in this release 5 trials for levels 0 and 1, and 20 trials for levels 2 and 3, but these numbers may change in future releases (and were different in historical releases). Using a fixed number of trials results in deterministic results regardless of the local system, because even with a fixed seed if you were to default to the number of local CPUs available the results would different when running between different computers.If the default number of trials for a given optimization level is higher than the number of local CPUs it will use the optimization level default which is higher.
-
The
optimization_level
argument for thegenerate_preset_pass_manager()
function is now optional. If it’s not specified it will default to using optimization level 2. As the argument is now optional, the first positional argument has been expanded to enable passing aTarget
or aBackendV2
as the first argument for more convenient construction. For example:from qiskit.transpiler.preset_passmanager import generate_preset_pass_manager from qiskit.providers.fake_provider import GenericBackendV2 backend = GenericBackendV2(100) generate_preset_pass_manager(backend.Target)
will construct a default pass manager for the 100 qubit :class`.GenericBackendV2` instance.
-
Added a new pass
Split2QUnitaries
that iterates over all two-qubit gates or unitaries in a circuit and replaces them with two single-qubit unitaries, if possible without introducing errors, i.e. the two-qubit gate/unitary is actually a (kronecker) product of single-qubit unitaries. -
The passes
Collect2qBlocks
,ConsolidateBlocks
andSplit2QUnitaries
have been added to theinit
stage of the preset pass managers with optimization level 2 and optimization level 3. The modification of the init stage should allow for a more efficient routing for quantum circuits that either:- contain two-qubit unitaries/gates that are actually a product of single-qubit gates
- contain multiple two-qubit gates in a continuous block of two-qubit gates.
In the former case, the routing of the two-qubit gate can simply be skipped as no real interaction between a pair of qubits occurs. In the latter case, the lookahead space of routing algorithms is not ‘polluted’ by superfluous two-qubit gates, i.e. for routing it is sufficient to only consider one single two-qubit gate per continuous block of two-qubit gates. These passes are not run if the pass managers target a
Target
that has a discrete basis gate set, i.e. all basis gates have are not parameterized. -
Port part of the logic from the
StarPrerouting
, used to find a star graph connectivity subcircuit and replaces it with a linear routing equivalent. -
The function
star_preroute()
now performs the heavily lifting to transform the dag by in the rust space by taking advantage of the functions_build_sabre_dag()
and_apply_sabre_result()
. -
A new
dt
argument has been added togenerate_preset_pass_manager()
to match the set of arguments oftranspile()
. This will allow for the internal conversion of transpilation constraints to aTarget
representation. -
Added multiple high-level-synthesis plugins for synthesizing an
MCXGate
:MCXSynthesisNCleanM15
, based onsynth_mcx_n_clean_m15()
.MCXSynthesisNDirtyI15
, based onsynth_mcx_n_dirty_i15()
.MCXSynthesis1CleanB95
, based onsynth_mcx_1_clean_b95()
.MCXSynthesisNoAuxV24
, based onsynth_mcx_noaux_v24()
.MCXSynthesisGrayCode
, based onsynth_mcx_gray_code()
.
As well:
MCXSynthesisDefault
, choosing the most efficient synthesis method based on the number of clean and dirty ancilla qubits available.
As an example, consider how the transpilation of the following circuit:
from qiskit.circuit import QuantumCircuit from qiskit.compiler import transpile qc = QuantumCircuit(7) qc.x(0) qc.mcx([0, 1, 2, 3], [4]) qc.mcx([0, 1, 2, 3, 4], [5]) qc.mcx([0, 1, 2, 3, 4, 5], [6]) transpile(qc)
For the first MCX gate, qubits
5
and6
can be used as clean ancillas, and the best available synthesis methodsynth_mcx_n_clean_m15
will get chosen. For the second MCX gate, qubit6
can be used as a clean ancilla, the methodsynth_mcx_n_clean_m15
no longer applies, so the methodsynth_mcx_1_clean_b95
will get chosen. For the third MCX gate, there are no ancilla qubits, and the methodsynth_mcx_noaux_v24
will get chosen. -
The implementation of the
DAGCircuit
has been rewritten in Rust. This rewrite of the Python class should be fully API compatible with the previous Python implementation of the class. While the class was previously implemented using rustworkx and its underlying data graph structure existed in Rust, the implementation of the class and all the data was stored in Python. This new version ofDAGCircuit
stores a Rust native representation for all its data and is fully implemented in Rust. This new implementation should be more efficient in memory usage as it compresses the qubit and clbit representation for instructions at rest. It also enables speed up for transpiler passes as they can fully manipulate aDAGCircuit
from Rust. -
A new argument
qubits_initially_zero
has been added toqiskit.compiler.transpile()
,generate_preset_pass_manager()
, and toPassManagerConfig
. If set toTrue
, the qubits are assumed to be initially in the state , potentially allowing additional optimization opportunities for individual transpiler passes. -
The constructor for
HighLevelSynthesis
transpiler pass now accepts an additional argumentqubits_initially_zero
. If set toTrue
, the pass assumes that the qubits are initially in the state . In addition, the pass keeps track of clean and dirty auxiliary qubits throughout the run, and passes this information to plugins via kwargsnum_clean_ancillas
andnum_dirty_ancillas
. -
Added a new method
DAGCircuit.control_flow_ops()
which provides a fast path to get all theDAGOpNode
in aDAGCircuit
that contain aControlFlowOp
. This was possible before using theDAGCircuit.op_nodes()
method and passing theControlFlowOp
class as a filter, but this new function will perform the operation faster. -
Ported the entirety of the
Optimize1qGatesDecomposition
transpiler pass to Rust. This improves the runtime performance of the pass between 5x to 10x. -
Added a Rust implementation of
CommutationAnalysis
inanalyze_commutations()
. -
The the
CommutationChecker
class has been reimplemented in Rust. This retains the same functionality as before but is now significantly in most cases. -
The
RemoveDiagonalGatesBeforeMeasure
transpiler pass has been upgraded to include more diagonal gates:PhaseGate
,CPhaseGate
,CSGate
,CSdgGate
andCCZGate
. In addition, the code of theRemoveDiagonalGatesBeforeMeasure
was ported to Rust, and is now x20 faster for a 20 qubit circuit.
Visualization Features
-
The user configuration file has a new option
circuit_idle_wires
, which takes a Boolean value. This allows users to set their preferred default behavior of theidle_wires
option of the circuit drawersQuantumCircuit.draw()
andcircuit_drawer()
. For example, adding a section to~/.qiskit/settings.conf
with:[default] circuit_idle_wires = false
will change the default to display the bits in reverse order.
-
The text and Matplotlib circuit drawers (
QuantumCircuit.draw()
) have minimal support for displaying expressions involving manual real-time variables. TheStore
operation and the variable initializations are not yet supported; for large-scale dynamic circuits, we recommend using the OpenQASM 3 export capabilities (qasm3.dumps()
) to get a textual representation of a circuit.
Misc. Features
- Added a new build-time environment variable
QISKIT_NO_CACHE_GATES
which when set to a value of1
(i.e.QISKIT_NO_CACHE_GATES=1
) which decreases the memory overhead of aCircuitInstruction
andDAGOpNode
object at the cost of decreased runtime on multiple accesses toCircuitInstruction.operation
andDAGOpNode.op
. If this environment variable is set when building the Qiskit python package from source the caching of the return of these attributes will be disabled.
Upgrade Notes
- The minimum supported version of Python is now 3.9, this has been raised from the previous minimum support version of 3.8. This change was necessary because the upstream cPython project no longer supports Python 3.8.
Circuits Upgrade Notes
-
The
annotated
argument of theGate.control()
method is nowNone
, which allows Qiskit to choose whether to annotate a controlled operation. If the concrete implementation (annotated=False
) is available, it will be returned by default. Otherwise, the annotated implementation will be returned (annotated=True
). This allows, for example, to defer the synthesis of controlled, parameterized gates. -
The
Operation
instances ofDAGOpNode.op
being returned will not necessarily share a common reference to the underlying object anymore. This was never guaranteed to be the case and mutating theop
directly by reference was unsound and always likely to corrupt the dag’s internal state tracking Due to the internal refactor of theQuantumCircuit
andDAGCircuit
to store standard gates in rust the output object fromDAGOpNode.op
will now likely be a copy instead of a shared instance. If you need to mutate an element should ensure that you either do:op = dag_node.op op.params[0] = 3.14159 dag_node.op = op
or:
op = dag_node.op op.params[0] = 3.14159 dag.substitute_node(dag_node, op)
instead of doing something like:
dag_node.op.params[0] = 3.14159
which will not work for any standard gates in this release. It would have likely worked by chance in a previous release but was never an API guarantee.
-
The
Operation
instances ofCircuitInstruction.operation
being returned will not necessarily share a common reference to the underlying object anymore. This was never guaranteed to be the case and mutating theoperation
directly by reference was unsound and always likely to corrupt the circuit, especially when parameters were in use. Due to the internal refactor of the QuantumCircuit to store standard gates in rust the output object fromCircuitInstruction.operation
will now likely be a copy instead of a shared instance. If you need to mutate an element in the circuit (which is strongly not recommended as it’s inefficient and error prone) you should ensure that you do:from qiskit.circuit import QuantumCircuit qc = QuantumCircuit(1) qc.p(0) op = qc.data[0].operation op.params[0] = 3.14 qc.data[0] = qc.data[0].replace(operation=op)
instead of doing something like:
from qiskit.circuit import QuantumCircuit qc = QuantumCircuit(1) qc.p(0) qc.data[0].operation.params[0] = 3.14
which will not work for any standard gates in this release. It would have likely worked by chance in a previous release but was never an API guarantee.
Primitives Upgrade Notes
-
BitArray.slice_bits()
andBitArray.slice_shots()
will now raiseIndexError
when indices are out of bounds. They used to raiseValueError
in the case. -
BitArray.__getitem__()
will now raiseIndexError
when indices are out of bounds or the number of dimensions of indices does not match that of BitArray. They used to raiseValueError
in the case.
Providers Upgrade Notes
-
Implementations of
BackendV2
(andBackendV1
) may desire to update theirrun()
methods to eagerly reject inputs containing typed classical variables (seeqiskit.circuit.classical
) and theStore
instruction, if they do not have support for them. The newStore
instruction is treated by the transpiler as an always-available “directive” (likeBarrier
); if your backends do not support this won’t be caught by thetranspiler
.See Real-time variables for more information.
QPY Upgrade Notes
- The value of
qiskit.qpy.QPY_VERSION
is now 12.QPY_COMPATIBILITY_VERSION
is unchanged at 10.
Synthesis Upgrade Notes
-
The
atomic_evolution
argument toProductFormula
(and its subclasses) has a new function signature. Rather than taking some Pauli operator and time coefficient and returning the evolution circuit, the new function takes in an existing circuit and should append the evolution of the provided Pauli and given time to this circuit. This new implementation benefits from significantly better performance. -
LieTrotter
andSuzukiTrotter
no longer wrap the individually evolved Pauli terms into gate definitions. If you rely on a certain decomposition level of your circuit, you have to remove one level ofdecompose()
or add thewrap=True
keyword argument to your synthesis object.
Transpiler Upgrade Notes
-
The default
optimization_level
used by thetranspile()
function when one is not specified has been changed to level 2. This makes it consistent with the default used bygenerate_preset_pass_manager()
which is used internally bytranspile()
. Optimization level 2 provides a much better balance between the run time of the function and the optimizations it performs, it’s a better tradeoff to use by default.The API of
transpile()
remains unchanged because, fundamentally, level 2 and level 1 have the same semantics. If you were previously relying on the implicit default of level 1, you can simply set the argumentoptimization_level=1
when you calltranspile()
. Similarly you can change the default back in your local environment by using a user config file and setting thetranspile_optimization_level
field to 1.The only potential issue is that your transpilation workflow may be relying on an implicit trivial layout (where qubit 0 in the circuit passed to
transpile()
is mapped to qubit 0 on the target backend/coupling, 1->1, 2->2, etc.) without specifyingoptimization_level=1
,layout_method="trivial"
, or explicitly settinginitial_layout
when callingtranspile()
. This behavior was a side effect of the preset pass manager construction in optimization level 1 and is not mirrored in level 2. If you need this behavior you can use any of the three options listed previously to make this behavior explicit.Similarly, if you were targeting a discrete basis gate set you may encounter an issue using the new default with optimization level 2 (or running explicitly optimization level 3), as the additional optimization passes that run in level 2 and 3 don’t work in all cases with a discrete basis. You can explicitly set
optimization_level=1
manually in this case. In general the transpiler does not currently fully support discrete basis sets and if you’re relying on this you should likely construct a pass manager manually to build a compilation pipeline that will work with your target. -
The default routing pass used by optimization level 0 for
generate_preset_pass_manager()
andtranspile()
has been changed fromStochasticSwap
toSabreSwap
. TheSabreSwap
pass performs exactly the same function but performs better in both runtime and output quality (in number of swap gates and depth) compared toStochasticSwap
. Foroptimization_level=0
this shouldn’t matter because it’s not expected to run routing for the typical use case of level 0.If you were relying on the previous default routing algorithm for any reason you can use the
routing_method
argument fortranspile()
andgenerate_preset_pass_manager()
to"stochastic"
to use theStochasticSwap
pass. -
The
generate_preset_pass_manager()
function has been upgraded to, when possible, internally convert transpiler constraints into aTarget
instance. If a backend input of typeBackendV1
is provided, it will be converted toBackendV2
to expose itsTarget
. This change does not require any user action. -
DAGNode
objects (and its subclassesDAGInNode
,DAGOutNode
, andDAGOpNode
) no longer return references to the same underlying object fromDAGCircuit
methods. This was never a guarantee before that all returned nodes would be shared reference to the same object, but with the migration of theDAGCircuit
to Rust when aDAGNode
a newDAGNode
instance is generated on the fly when a node is returned to Python. These objects will evaluate as equal using==
or similar checks that rely on__eq__
but will no longer identify as the same object. -
The
DAGOpNode
instances returned from theDAGCircuit
are no longer shared references to the underlying data stored on the DAG. In previous release it was possible to do something like:for node in dag.op_nodes(): node.op = new_op
however this type of mutation was always unsound as it could break the DAG’s internal caching and cause corruption of the data structure. Instead you should use the API provided by
DAGCircuit
for mutation such asDAGCircuit.substitute_node()
,DAGCircuit.substitute_node_with_dag()
, orDAGCircuit.contract_node()
. For example the above code block would become:for node in dag.op_nodes(): dag.substitute_node(node, new_op)
This is similar to an upgrade note from 1.2.0 where this was noted on for mutation of the
DAGOpNode.op
attribute, not theDAGOpNode
itself. However in 1.3 this extends to the entire object, not just it’s innerop
attribute. In general this type of mutation was always unsound and not supported, but could previously have potentially worked in some cases. -
The
transpile()
now assumes that the qubits are initially in the state . To avoid this assumption, one can set the argumentqubits_initially_zero
toFalse
.
Misc. Upgrade Notes
- The minimum version of rustworkx required to run this release has been increased from 0.14.0 to 0.15.0. This is required because Qiskit is now using new functionality added in the rustworkx 0.15.0 release which improves performance.
Circuits Deprecations
-
The following circuit methods were not intended for public use, but were accidentally left documented in the public API during the 1.0 release. They are now deprecated from Qiskit 1.2 and will be removed in Qiskit 2.0:
QuantumCircuit.cast
QuantumCircuit.cls_instances
QuantumCircuit.cls_prefix
QuantumCircuit.cbit_argument_conversion
QuantumCircuit.qbit_argument_conversion
-
Treating
CircuitInstruction
as a tuple-like iterable is deprecated, and this legacy path way will be removed in Qiskit 2.0. You should use the attribute-access fieldsoperation
,qubits
, andclbits
instead. For example:from qiskit.circuit import QuantumCircuit qc = QuantumCircuit(2, 2) qc.h(0) qc.cx(0, 1) qc.measure([0, 1], [0, 1]) # Deprecated. for op, qubits, clbits in qc.data: pass # New style. for instruction in qc.data: op = instruction.operation qubits = instruction.qubits clbits = instruction.clbits
Primitives Deprecations
-
Primitive V1 implementations and V1-exclusive non-versioned type aliases are now deprecated in favor of their V2 counterparts. The deprecation is extended to the following classes implementing V1 interfaces:
Estimator
, in favor of the V2 equivalent,StatevectorEstimator
Sampler
, in favor of the V2 equivalent,StatevectorSampler
BackendEstimator
, in favor of the V2 equivalent,BackendEstimatorV2
BackendSampler
, in favor of the V2 equivalent,BackendSamplerV2
As well as the following non-versioned type aliases:
BaseEstimator
, alias forBaseEstimatorV1
BaseSampler
, alias forBaseSamplerV1
This deprecation does NOT affect the explicitly-versioned
BaseEstimatorV1
andBaseSamplerV1
abstract interface definitions or related result and job classes.In addition, the following utility functions are deprecated:
init_circuit()
, to initialize a circuit from aStatevector
, useQuantumCircuit.initialize()
instead,init_observable()
, use the constructor ofSparsePauliOp
instead,final_measurement_mapping()
, useQuantumCircuit.layout()
andSparsePauliOp.apply_layout()
to adjust an operator for a layout. Otherwise, usemthree.utils.final_measurement_mapping
. See Mthree Utility functions for details.
Providers Deprecations
-
The
BackendV1
class is deprecated and it will be removed not earlier than the next major release. There are several migration paths available depending on the main purpose of the backend object:- To expose backend information with no access to execution (just a hardware description), consider constructing a :class:`.Target` directly. - To provides access to execution capabilities, consider relying on the primitives interfaces instead. - Alternatively, to continue providing simultaneous :class:`.Target` (hardware information) and ``run`` (execution) capabilities, consider moving to :class:`.BackendV2` (see <https://qisk.it/backendV1-to-V2>).
-
The models in
qiskit.providers.models
are part of the deprecatedBackendV1
workflow and no longer necessary forBackendV2
. If a user workflow requires these representations it likely relies on deprecated functionality and should be updated to useBackendV2
. -
The Qobj structure and related classes are now deprecated, they were introduced as part of the BackendV1 workflow and are no longer necessary for interacting with
BackendV2
backends. Remote backend interaction should be done via QPY or OpenQASM instead.
Transpiler Deprecations
-
The assemble function is now deprecated and will be removed in the 2.0 release. The function was primarily used to create a Qobj, which is no longer necessary in
BackendV2
-based workflows. It was also used for binding parameters, a functionality fully covered byassign_parameters()
. -
Deprecated
StochasticSwap
which has been superseded bySabreSwap
. If the class is called from the transpile function, the change would be, for example:from qiskit import transpile from qiskit.circuit import QuantumCircuit from qiskit.transpiler import CouplingMap from qiskit.providers.fake_provider import GenericBackendV2 qc = QuantumCircuit(4) qc.h(0) qc.cx(0, range(1, 4)) qc.measure_all() cmap = CouplingMap.from_heavy_hex(3) backend = GenericBackendV2(num_qubits=cmap.size(), coupling_map=cmap) tqc = transpile( qc, routing_method="stochastic", layout_method="dense", seed_transpiler=12342, target=backend.target )
to:
tqc = transpile( qc, routing_method="sabre", layout_method="sabre", seed_transpiler=12342, target=backend.target )
While for a pass manager, the change would be:
passmanager = PassManager(StochasticSwap(coupling, 20, 13)) new_qc = passmanager.run(qc)
to:
passmanager = PassManager(SabreSwap(backend.target, "basic")) new_qc = passmanager.run(qc)
Visualization Deprecations
-
The
transition_visualization()
function has been deprecated and will be removed in the 2.0.0 release. This function had a number of limitations which limited it’s utility to only very specific use cases and didn’t fit in with the rest of the Qiskit visualization module. -
The
justify
argument ofcircuit_drawer()
orQuantumCircuit.draw()
, will no longer support invalid values (previously changing them to the default), and in a future release they will error. Valid justify values are"left"
,"right"
or"none"
.
Bug Fixes
-
Fixes an issue with the visualizations of some backends/coupling maps that showed as folded on their own. The default ``neato` setting <https://graphviz.org/docs/layouts/neato/>`_ works well in most cases. However,
prism
overlap returns a more regular layout for other scenarios. -
Fixed a series of issues when controlling parameterized standard gates. The controlled version of some gates (e.g.
RXXGate
orRYGate
for more than 1 control) cannot be synthesized if they contain unbound parameters. Previously, calling.control()
but now we create anAnnotatedOperation
as placeholder. This allows to insert the controlled gate into a circuit, bind the parameters at a later stage, and then synthesize the operation. Fixes #10311, #10697, and #12135. -
The
SGate
andSdgGate
now correctly return aCSGate
, resp.CSdgGate
, if they are controlled on a single control qubit. -
Parametric controlled standard-library gates (such as
CRXGate
) will now get correctly extracted to a Rust-space standard gate when usingQuantumCircuit.append()
and the gate object. Previously there was a discrepancy where using theQuantumCircuit.crx()
method would cause a correct extraction in Rust space, but theappend()
form would not. The bug should generally not have caused any unsoundness from Python. -
Fixed a bug in
TwoQubitBasisDecomposer
where the Rust-based code would panic if the given KAK gate wasn’t a Rust-spaceStandardGate
. -
Fixed a bug where
InstructionDurations.from_backend()
did not work forBackendV2
backends. Fixed #12760 <https://github.com/Qiskit/qiskit/issues/12760>. -
Fixed a bug in
BitArray.from_counts()
andBitArray.from_samples()
. Previously these would raise an error if given data containing only zeros, and no value for the optional argumentnum_bits
. Now they produce aBitArray
withBitArray.num_bits
set to 1. -
Fixed a bug in the
ConsolidateBlocks
transpiler pass, when the input circuit contains a custom opaque gate and neither thebasis_gates
ortarget
options are set the pass would raise aQiskitError
and fail. This has been corrected so that the in these situations the transpiler pass will not consolidate the block identified containing a custom gate instead of failing. -
Fixed the definition of the
CUGate
matrix in Rust-space. While this was not noticable while handling theCUGate
purely on Python side, this had knock-on effects when transpiler passes were using the Rust representation, such as could happen inConsolidate2qBlocks
. Fixed #13118. -
Fixed a bug in
PadDynamicalDecoupling
, which previously did not correctly display the error message that a delay is not pulse-aligned, if the previous or following node was an input/output node. Now, the error message is correctly displayed. -
Fixed a bug in
HoareOptimizer
where a controlled gate was simplified by removing its controls but the new gate was not handled correctly. -
The keyword argument
order
of the functionBitArray.from_bool_array()
should be ‘little’ or ‘big’. Added checks to raise error if an invalid value is entered. -
Improve the decomposition of the gate generated by
QuantumCircuit.mcx()
without using ancilla qubits, so that the number ofCXGate
will grow quadratically in the number of qubits and not exponentially. -
Fixed the behavior of
generate_preset_pass_manager()
to raise a ValueError exception if not provided with a non-negative integer seed_transpiler argument. -
Fixed an edge case in
SabreLayout
, where in rare cases on large devices and challenging circuits, the routing would fail. This was due to the release valve making more than one two-qubit gate routable, where only one was expected. Fixed #13081. -
Fixed a bug in
Split2QUnitaries
where it would fail to run on circuits with custom gates that didn’t implement__array__()
. See #12984. -
Fixed a bug in
StatePreparation
where thenormalize
argument was ignored for input arrays. Fixed #12984. -
Fixed a bug of
StatevectorSampler
that ignored gates withc_if
. It will raise an error becauseStatevector
cannot handlec_if
. -
Fixed a bug where various synthesis methods created circuits without quantum or classical registers. This also affected functions that internally used the synthesis methods, such as
Clifford.to_circuit()
. Fixed #13041. -
Fix a bug that caused the method
Initialize.gates_to_uncompute()
fail. -
MCXVChain
with k controls and k-2 dirty auxiliary qubits now requires 8k-6 cx gates. -
Fixed a bug in
synth_cnot_full_pmh()
where providing asection_size
that did not divide the number of qubits without remainder could lead to wrong results. Now anysection_size
(at most equal to the number of qubits) synthesizes the correct circuit. For a (heuristically) optimal value, setsection_size=None
. -
The OpenQASM 3 exporter will now correctly error when asked to use a keyword or other invalid identifier as a “basis gate”, as it has no way of putting out correct output in these cases.
-
The OpenQASM 3 exporter (
qiskit.qasm3
) will now correctly export multiple instances ofPauliEvolutionGate
from a circuit. Previously, only a single instance would be exported, and all other instances would silently use the same (incorrect) version. -
The OpenQASM 3 exporter (
qiskit.qasm3
) will now correctly escape gate names. Previously, a gate whose name was an invalid OpenQASM 3 identifier would cause invalid OpenQASM 3 to be generated. -
Fixed an edge case when transpiling a circuit with
optimization_level
2 or 3 with an incomplete 1-qubit basis gate set on a circuit containing 2-qubit gates, that can be implemented as a product of single qubit gates. This bug is resolved by restrictingSplit2QUnitaries
to consider onlyUnitaryGate
objects. Fixed #12970. -
A series of input-handling inconsistencies between
transpile()
andgenerate_preset_pass_manager()
have been fixed. These inconsistencies would lead to different transpilation outputs for the same inputs, orgenerate_preset_pass_manager()
failing for certain input combinations accepted bytranspile()
. -
Changes the way in which the
BackendEstimatorV2
class calculates thestd
to ensure that it matches the correct formula. -
Fixed an issue where
circuit_drawer()
or theQuantumCircuit.draw()
method would not raise a warning when an invalid value was passed to thejustify
argument, before changing it to the default. Now, it will raise a warning if an invalid value is passed. Valid justify values are"left"
,"right"
or"none"
. Refer to #12089 <https://github.com/Qiskit/qiskit/issues/12089> for more details. -
Fixed an issue with
DAGCircuit.apply_operation_back()
andDAGCircuit.apply_operation_front()
where previously if you set aClbit
object to the input for theqargs
argument it would silently be accepted. This has been fixed so the type mismatch is correctly identified and an exception is raised. -
Fixed
SparsePauliOp.apply_layout()
andPauli.apply_layout()
to raiseQiskitError
if duplicate indices or negative indices are provided as part of a layout. -
Fixed a missing decorator in
C3SXGate
that made it fail ifGate.to_matrix()
was called. The gate matrix is now return as expected. -
Fixed a bug with the
"circular"
and"sca"
entanglement forNLocal
circuits and its derivatives. For entanglement blocks of more than 2 qubits, the circular entanglement was previously missing some connections. For example, for 4 qubits and a block size of 3 the code previously used:[(2, 3, 0), (0, 1, 2), (1, 2, 3)]
but now is correctly adding the
(3, 0, 1)
connections, that is:[(2, 3, 0), (3, 0, 1), (0, 1, 2), (1, 2, 3)]
As such, the
"circular"
and"sca"
entanglements usenum_qubits
entangling blocks per layer. -
Add more Clifford gates to the
CollectCliffords()
transpiler pass. In particular, we have added the gatesECRGate()
,DCXGate()
,iSWAPGate()
,SXGate()
andSXdgGate()
to this transpiler pass. -
Fix a bug in
Isometry
due to an unnecessary assertion, that led to an error inUnitaryGate.control()
whenUnitaryGate
had more that two qubits. -
The
QuantumCircuit.parameters
attribute will now correctly be empty when usingQuantumCircuit.copy_empty_like()
on a parametric circuit. Previously, an internal cache would be copied over without invalidation. Fix #12617. -
QuantumCircuit.depth()
will now correctly handle operations that do not have operands, such asGlobalPhaseGate
. -
QuantumCircuit.depth()
will now count the variables and clbits used in real-time expressions as part of the depth calculation. -
Fix the
SolovayKitaev
transpiler pass when loading basic approximations from an exising.npy
file. Previously, loading a stored approximation which allowed for further reductions (e.g. due to gate cancellations) could cause a runtime failure. Additionally, the global phase difference of the U(2) gate product and SO(3) representation was lost during a save-reload procedure. Fixes Qiskit/qiskit#12576. -
Fixed
SparsePauliOp.apply_layout()
to work correctly with zero-qubit operators. For example, if you previously created a 0 qubit and applied a layout like:op = SparsePauliOp("") op.apply_layout(None, 3)
this would have previously raised an error. Now this will correctly return an operator of the form:
SparsePauliOp(['III'], coeffs=[1.+0.j])
-
Fixed a bug when
SparsePauliOp.paulis
is set to be aPauliList
with nonzero phase, where subsequent calls to severalSparsePauliOp
methods would produce incorrect results. Now whenSparsePauliOp.paulis
is set to aPauliList
with nonzero phase, the phase is absorbed intoSparsePauliOp.coeffs
, and the phase of the inputPauliList
is set to zero. -
Fixed an oversight in the
Commuting2qGateRouter
transpiler pass where the qreg permutations were not added to the pass property set, so they would have to be tracked manually by the user. Now it’s possible to access the permutation through the output circuit’slayout
property and plug the pass into any transpilation pipeline without loss of information. -
Fixed a floating-point imprecision when scaling certain pulse units between seconds and nanoseconds. If the pulse was symbolically defined, an unnecessary floating-point error could be introduced by the scaling for certain builds of
symengine
, which could manifest in unexpected results once the symbols were fully bound. See #12392. -
Fixed a bug in
qiskit.visualization.pulse_v2.interface.draw()
that didn’t draw pulse schedules when the draw function was called with aBackendV2
argument. Because the V2 backend doesn’t report hardware channel frequencies, the generated drawing will show ‘no freq.’ below each channel label. -
Fixed an issue with
dag_drawer()
andDAGCircuit.draw()
when attempting to visualize aDAGCircuit
instance that containedVar
wires. The visualizer would raise an exception trying to do this which has been fixed so the expected visualization will be generated. -
The
VF2Layout
pass would raise an exception when provided with aTarget
instance without connectivity constraints. This would be the case with targets from Aer 0.13. The issue is now fixed. -
Fixes an error when calling the method
UnitaryGate.repeat()
. Refer to #11990 for more details. -
The constructor
GenericBackendV2
was allowing to create malformed backends because it accepted basis gates that couldn’t be allocated in the backend size . That is, a backend with a single qubit should not accept a basis with two-qubit gates. -
PassManager.run()
will no longer waste time serializing itself when given multiple inputs if it is only going to work in serial. -
ParameterExpression
was updated so that fully bound instances that compare equal to instances of Python’s built-in numeric types (likefloat
andint
) also have hash values that match those of the other instances. This change ensures that these types can be used interchangeably as dictionary keys. See #12488. -
Fixed that the entanglement in
PauliFeatureMap
andZZFeatureMap
could be given asList[int]
orList[List[int]]
, which was incompatible with the fact that entanglement blocks of different sizes are used. Instead, the entanglement can be given as dictionary with{block_size: entanglement}
pairs. -
Fixed a bug in
plot_coupling_map()
that caused the edges of the coupling map to be colored incorrectly. See https://github.com/Qiskit/qiskit/pull/12369 for details. -
The OpenQASM 2 parser (
qiskit.qasm2
) can now handle conditionals with integers that do not fit within a 64-bit integer. Fixed #12773. -
The OpenQASM 2.0 parser (
qasm2.load()
andqasm2.loads()
) can now evaluate gate-angle expressions including integer operands that would overflow the system-size integer. These will be evaluated in a double-precision floating-point context, just like the rest of the expression always has been. Beware: an arbitrarily large integer will not necessarily be exactly representable in double-precision floating-point, so there is a chance that however the circuit was generated, it had already lost all numerical precision modulo . -
Custom gates (those stemming from a
gate
statement) in imported OpenQASM 2 programs will now have anGate.to_matrix()
implementation. Previously they would have no matrix definition, meaning that roundtrips through OpenQASM 2 could needlessly lose the ability to derive the gate matrix. Note, though, that the matrix is calculated by recursively finding the matrices of the inner gate definitions, asOperator
does, which might be less performant than before the round-trip. -
Previously,
DAGCircuit.replace_block_with_op()
allowed to place ann
-qubit operation onto a block ofm
qubits, leaving the DAG in an invalid state. This behavior has been fixed, and the attempt will raise aDAGCircuitError
. -
Target.has_calibration()
has been updated so that it does not raise an exception for an instruction that has been added to the target withNone
for its instruction properties. Fixes #12525.
1.1.0rc1
New Features
-
Extended the commutation analysis performed by
CommutationChecker
to only operate on hardware circuits to also work with abstract circuits, i.e. each operation in the input quantum circuit is now checked for its matrix representation before proceeding to the analysis. In addition, the operation is now checked for its ability to be cached in the session commutation library. For example, this now enables computing whetherAnnotatedOperation
commute. This enables transpiler passes that rely onCommutationChecker
internally, such asCommutativeCancellation
, during earlier stages of a default transpilation pipeline (prior to basis translation). -
The methods
power()
,power()
, as well as the similar methods of subclasses ofGate
(such as ofSGate
) all have an additional argumentannotated
. The default value ofFalse
corresponds to the existing behavior. Furthermore, for standard gates with an explicitly definedpower
method, the argumentannotated
has no effect, for example bothSGate().power(1.5, annotated=False)
andSGate().power(1.5, annotated=True)
return aPhaseGate
. The difference manifests for gates without an explicitly defined power method. The value ofFalse
returns aUnitaryGate
, just as before, while the value ofTrue
returns anAnnotatedOperation
that represents the instruction modified with the “power modifier”. -
The implementation
BackendEstimatorV2
ofBaseEstimatorV2
was added. This estimator supportsBackendV1
andBackendV2
.import numpy as np from qiskit import transpile from qiskit.circuit.library import IQP from qiskit.primitives import BackendEstimatorV2 from qiskit.providers.fake_provider import Fake7QPulseV1 from qiskit.quantum_info import SparsePauliOp, random_hermitian backend = Fake7QPulseV1() estimator = BackendEstimatorV2(backend=backend) n_qubits = 5 mat = np.real(random_hermitian(n_qubits, seed=1234)) circuit = IQP(mat) observable = SparsePauliOp("Z" * n_qubits) isa_circuit = transpile(circuit, backend=backend, optimization_level=1) isa_observable = observable.apply_layout(isa_circuit.layout) job = estimator.run([(isa_circuit, isa_observable)], precision=0.01) result = job.result() print(f"> Expectation value: {result[0].data.evs}") print(f"> Standard error: {result[0].data.stds}") print(f"> Metadata: {result[0].metadata}")
-
The implementation
BackendSamplerV2
ofBaseSamplerV2
was added. This sampler supportsBackendV1
andBackendV2
that allowmemory
option to compute bitstrings.import numpy as np from qiskit import transpile from qiskit.circuit.library import IQP from qiskit.primitives import BackendSamplerV2 from qiskit.providers.fake_provider import Fake7QPulseV1 from qiskit.quantum_info import random_hermitian backend = Fake7QPulseV1() sampler = BackendSamplerV2(backend=backend) n_qubits = 5 mat = np.real(random_hermitian(n_qubits, seed=1234)) circuit = IQP(mat) circuit.measure_all() isa_circuit = transpile(circuit, backend=backend, optimization_level=1) job = sampler.run([isa_circuit], shots=100) result = job.result() print(f"> bitstrings: {result[0].data.meas.get_bitstrings()}") print(f"> counts: {result[0].data.meas.get_counts()}") print(f"> Metadata: {result[0].metadata}")
-
Added
ctrl_state
parameter toQuantumCircuit.mcp()
andMCPhaseGate()
.The
QuantumCircuit.mcp()
function andMCPhaseGate()
have been updated to include actrl_state
parameter. This enhancement allows users to specify the control state of the multi-controlled phase gate. The parameter can accept either a decimal value or a bitstring and defaults to controlling the ‘1’ state if not provided.from qiskit import QuantumCircuit qc = QuantumCircuit(4) qc.mcp(0.2,[0,1,2],3,ctrl_state=2)
-
The transpiler pass
ElidePermutations
runs by default with optimization level 2 and 3. Intuitively, removingSwapGate
s andPermutationGate
s in a virtual circuit is almost always beneficial, as it makes the circuit shorter and easier to route. AsOptimizeSwapBeforeMeasure
is a special case ofElidePermutations
, it has been removed from optimization level 3. -
Added a new optimization transpiler pass,
ElidePermutations
, which is designed to run prior to the Layout stage and will optimize away anySwapGate
s andPermutationGate
s in a circuit by permuting virtual qubits. For example, taking a circuit withSwapGate
s:will remove the swaps when the pass is run:
from qiskit.transpiler.passes import ElidePermutations from qiskit.circuit import QuantumCircuit qc = QuantumCircuit(3) qc.h(0) qc.swap(0, 1) qc.swap(2, 0) qc.cx(1, 0) qc.measure_all() ElidePermutations()(qc).draw("mpl")
The pass also sets the
virtual_permutation_layout
property set, storing the permutation of the virtual qubits that was optimized away. -
The
KMSSynthesisLinearFunction
plugin for synthesizingLinearFunction
objects now accepts two additional optionsuse_inverted
anduse_transposed
. These option modify the matrix on which the underlying synthesis algorithm runs by possibly inverting and/or transposing it, and then suitably adjust the synthesized circuit. By varying these options, we generally get different synthesized circuits, and in cases may obtain better results than for their default values. -
The
PMHSynthesisLinearFunction
plugin for synthesizingLinearFunction
objects now accepts several additional options. The optionsection_size
is passed to the underlying synthesis method. The optionsuse_inverted
anduse_transposed
modify the matrix on which the underlying synthesis algorithm runs by possibly inverting and/or transposing it, and then suitably adjust the synthesized circuit. By varying these options, we generally get different synthesized circuits, and in cases may obtain better results than for their default values. -
The
HLSConfig
now has two additional optional arguments. The argumentplugin_selection
can be set either to"sequential"
or to"all"
. If set to “sequential” (default), for every higher-level-object theHighLevelSynthesis
pass will consider the specified methods sequentially, in the order they appear in the list, stopping at the first method that is able to synthesize the object. If set to “all”, all the specified methods will be considered, and the best synthesized circuit, according toplugin_evaluation_fn
will be chosen. The argumentplugin_evaluation_fn
is an optional callable that evaluates the quality of the synthesized quantum circuit; a smaller value means a better circuit. When set toNone
, the quality of the circuit is its size (i.e. the number of gates that it contains).The following example illustrates the new functionality:
from qiskit import QuantumCircuit from qiskit.circuit.library import LinearFunction from qiskit.synthesis.linear import random_invertible_binary_matrix from qiskit.transpiler.passes import HighLevelSynthesis, HLSConfig # Create a circuit with a linear function mat = random_invertible_binary_matrix(7, seed=37) qc = QuantumCircuit(7) qc.append(LinearFunction(mat), [0, 1, 2, 3, 4, 5, 6]) # Run different methods with different parameters, # choosing the best result in terms of depth. hls_config = HLSConfig( linear_function=[ ("pmh", {}), ("pmh", {"use_inverted": True}), ("pmh", {"use_transposed": True}), ("pmh", {"use_inverted": True, "use_transposed": True}), ("pmh", {"section_size": 1}), ("pmh", {"section_size": 3}), ("kms", {}), ("kms", {"use_inverted": True}), ], plugin_selection="all", plugin_evaluation_fn=lambda circuit: circuit.depth(), ) # synthesize qct = HighLevelSynthesis(hls_config=hls_config)(qc)
In the example, we run multiple synthesis methods with different parameters, choosing the best circuit in terms of depth. Note that optimizing
circuit.size()
instead would pick a different circuit. -
Added ctrl_state parameter to
QuantumCircuit.mcx()
.The
QuantumCircuit.mcx()
function in the quantum circuit library has been enhanced to include a ctrl_state parameter, allowing users to specify the control state of the multi-controlled X gate. This parameter can accept either a decimal value or a bitstring and defaults to controlling the ‘1’ state if not provided.from qiskit import QuantumCircuit qc = QuantumCircuit(3, 3) qc.mcx([0, 1], 2, ctrl_state="00")
-
Added the
CommutativeCancellation
pass to theinit
stage of the preset pass managers for optimization levels 2 and 3. This enables the preset pass managers to cancel additional logical operations at the beginning of the compilation pipeline. -
Added a new method
Layout.inverse()
which is used for taking the inverse of aLayout
object. Added a new methodLayout.compose()
which is used for composing twoLayout
objects together. Added a new methodLayout.to_permutation()
which is used for creating a permutation corresponding to aLayout
object. -
This release of Qiskit finalizes support for NumPy 2.0. Qiskit will continue to support both Numpy 1.x and 2.x for the foreseeable future.
-
Added a new reduction to the
OptimizeAnnotated
transpiler pass. This reduction looks for annotated operations (objects of typeAnnotatedOperation
that consist of a base operationB
and a listM
of control, inverse and power modifiers) with the following properties:- the base operation
B
needs to be synthesized (i.e. it’s not already supported by the target or belongs to the equivalence library) - the definition circuit for
B
can be expressed asP -- Q -- R
with
In this case the modifiers can be moved to the
Q
-part only. As a specific example, controlled QFT-based adders have the formcontrol - [QFT -- U -- IQFT]
, which can be simplified toQFT -- control-[U] -- IQFT
. By removing the controls overQFT
andIQFT
parts of the circuit, one obtains significantly fewer gates in the transpiled circuit. - the base operation
-
Added two new methods to the
DAGCircuit
class:qiskit.dagcircuit.DAGCircuit.op_successors()
returns an iterator toDAGOpNode
successors of a node, andqiskit.dagcircuit.DAGCircuit.op_successors()
returns an iterator toDAGOpNode
predecessors of a node. -
Added a new transpiler pass,
RemoveFinalReset
, which will remove anyReset
operation which is the final instruction on a qubit wire. For example, taking a circuit with finalReset
s:will remove the final resets when the pass is run:
from qiskit.transpiler.passes import RemoveFinalReset from qiskit.circuit import QuantumCircuit qc = QuantumCircuit(3, 1) qc.reset(0) qc.h(range(3)) qc.cx(1, 0) qc.measure(0, 0) qc.reset(range(3)) RemoveFinalReset()(qc).draw("mpl")
-
The performance of
SparsePauliOp.to_matrix()
has been greatly improved for both dense and sparse forms. By default, both will now take advantage of threaded parallelism available on your system, subject to theRAYON_NUM_THREADS
environment variable. You can temporarily force serial execution using the newforce_serial
Boolean argument toto_matrix()
. -
Added a new transpiler pass
StarPreRouting
which is designed to identify star connectivity subcircuits and then replace them with an optimal linear routing. This is useful for certain circuits that are composed of this circuit connectivity such as Bernstein Vazirani and QFT. For example:
Circuits Features
-
A
QuantumCircuit
can now contain typed classical variables:from qiskit.circuit import QuantumCircuit, ClassicalRegister, QuantumRegister from qiskit.circuit.classical import expr, types qr = QuantumRegister(2, "q") cr = ClassicalRegister(2, "c") qc = QuantumCircuit(qr, cr) # Add two input variables to the circuit with different types. a = qc.add_input("a", types.Bool()) mask = qc.add_input("mask", types.Uint(2)) # Test whether the input variable was true at runtime. with qc.if_test(a) as else_: qc.x(0) with else_: qc.h(0) qc.cx(0, 1) qc.measure(qr, cr) # Add a typed variable manually, initialized to the same value as the classical register. b = qc.add_var("b", expr.lift(cr)) qc.reset([0, 1]) qc.h(0) qc.cx(0, 1) qc.measure(qr, cr) # Store some calculated value into the `b` variable. qc.store(b, expr.bit_and(b, cr)) # Test whether we had equality, up to a mask. with qc.if_test(expr.equal(expr.bit_and(b, mask), mask)): qc.x(0)
These variables can be specified either as inputs to the circuit, or as scoped variables. The circuit object does not yet have support for representing typed classical-variable outputs, but this will be added later when hardware and the result interfaces are in more of a position to support it. Circuits that represent a block of an inner scope may also capture variables from outer scopes.
A variable is a
Var
node, which can now contain an arbitrary type, and represents a unique memory location within its live range when added to a circuit. These can be constructed in a circuit usingQuantumCircuit.add_var()
andadd_input()
, or at a lower level usingVar.new()
.Variables can be manually stored to, using the
Store
instruction and its corresponding circuit methodQuantumCircuit.store()
. This includes writing toClbit
andClassicalRegister
instances wrapped inVar
nodes.Variables can be used wherever classical expressions (see
qiskit.circuit.classical.expr
) are valid. Currently this is the target expressions of control-flow operations, though we plan to expand this to gate parameters in the future, as the type and expression system are expanded.See Real-time classical computation for more discussion of these variables, and the associated data model.
These are supported throughout the transpiler, through QPY serialization (
qiskit.qpy
), OpenQASM 3 export (qiskit.qasm3
), and have initial support through the circuit visualizers (seeQuantumCircuit.draw()
).NoteThe new classical variables and storage will take some time to become supported on hardware and simulator backends. They are not supported in the primitives interfaces (
qiskit.primitives
), but will likely inform those interfaces as they evolve. -
The classical realtime-expressions module
qiskit.circuit.classical
can now represent indexing and bitshifting of unsigned integers and bitlikes (e.g.ClassicalRegister
). For example, it is now possible to compare one register with the bitshift of another:from qiskit.circuit import QuantumCircuit, ClassicalRegister from qiskit.circuit.classical import expr cr1 = ClassicalRegister(4, "cr1") cr2 = ClassicalRegister(4, "cr2") qc = QuantumCircuit(cr1, cr2) with qc.if_test(expr.equal(cr1, expr.shift_left(cr2, 2))): pass
Qiskit can also represent a condition that dynamically indexes into a register:
with qc.if_test(expr.index(cr1, cr2)): pass
-
The construction performance of
NLocal
and its derived circuit-library subclasses (e.g.EfficientSU2
andRealAmplitudes
) has significantly improved, when the rotation and/or entanglement subblocks are simple applications of a single Qiskit standard-library gate. Since these circuits are constructed lazily, you might not see the improvement immediately on instantiation of the class, but instead on first access to its internal structure. Performance improvements are on the order of ten times faster. -
QuantumCircuit.append()
now has acopy
keyword argument, which defaults toTrue
. When an instruction with runtime parameters (ParameterExpression
s) is appended to a circuit, by default, the circuit has always created a copy of the instruction so that ifQuantumCircuit.assign_parameters()
attempts to mutate the instruction in place, it does not affect other references to the same instruction. Now, settingcopy=False
allows you to override this, so you can avoid the copy penalty if you know your instructions will not be used in other locations. -
QuantumCircuit.compose()
now has acopy
keyword argument, which defaults toTrue
. By default,compose()
copies all instructions, so that mutations from one circuit do not affect any other. Ifcopy=False
, then instructions from the other circuit will become directly owned by the new circuit, which may involve mutating them in place. The other circuit must not be used afterwards, in this case. -
Construction time for
QuantumVolume
circuits has been significantly improved, on the order of 10x or a bit more. The internal SU4 gates will now also use more bits of randomness during their generation, leading to more representative volume circuits, especially at large widths and depths. -
QuantumVolume
now has aflatten
keyword argument. This defaults toFalse
, where the constructed circuit contains a single instruction that in turn contains the actual volume structure. If setTrue
, the circuit will directly have the volumetric SU4 matrices. -
UnitaryGate
now accepts an optionalnum_qubits
argument. The only effect of this is to skip the inference of the qubit count, which can be helpful for performance when many gates are being constructed. -
All of the “standard gates” in the circuit library (
qiskit.circuit.library
) can now be specified by string name for the entangling operations inTwoLocal
circuits, such asRealAmplitudes
andEfficientSU2
.
Primitives Features
-
Version 2 of the primitives is introduced via a new base class for both the sampler and the estimator, along with new types for their inputs and outputs. The emphasis of this new version is on performing vectorized calls to the primitive
run()
methods, so that sweeps over parameter value sets and observables can be efficiently specified. SeeStatevectorSampler
andStatevectorEstimator
for reference implementations of the V2 primitives.Moreover, the estimator has gained a
precision
argument in therun()
method that specifies the targeted precision of the expectation value estimates. Analogously, the sampler has movedshots
out of the options and into the arguments of therun()
method. The sampler has also been changed to return the outputs (e.g. bitstrings) from every shot, rather than providing aCounts
-like return, and also to store data from separateClassicalRegister
s . This enables derived classes to implement sampler support for circuits with classical control flow.The primitive V2 base classes are:
The new types which are used for inputs and outputs are:
SamplerPubLike
: primitive unified bloc (PUB) of sampler inputs; a union type of allowed inputs to a samplerEstimatorPubLike
: Primitive unified bloc (PUB) of estimator inputs; a union type of allowed inputs to an estimatorPubResult
: the data and metadata resulting from a single PUB’s executionDataBin
: A namespace to hold data from a single PUB’s executionBitArray
: an array-valued collection of bit values in a dense formatPrimitiveResult
: an iterable ofPubResult
s along with metadata
-
The reference implementation
StatevectorEstimator
ofBaseEstimatorV2
was added. As seen in the example below, this estimator (and all V2 estimators) supports providing arrays of observables and/or arrays of parameter value sets that are attached to particular circuits.Each tuple of
(circuit, observables, <optional> parameter values, <optional> precision)
, called an estimator primitive unified bloc (PUB), produces its own array-based result. Therun()
method can be given many pubs at once.from qiskit.circuit import Parameter, QuantumCircuit from qiskit.primitives import StatevectorEstimator from qiskit.quantum_info import Pauli, SparsePauliOp import matplotlib.pyplot as plt import numpy as np # Define a circuit with two parameters. circuit = QuantumCircuit(2) circuit.h(0) circuit.cx(0, 1) circuit.ry(Parameter("a"), 0) circuit.rz(Parameter("b"), 0) circuit.cx(0, 1) circuit.h(0) # Define a sweep over parameter values, where the second axis is over # the two parameters in the circuit. params = np.vstack([ np.linspace(-np.pi, np.pi, 100), np.linspace(-4 * np.pi, 4 * np.pi, 100) ]).T # Define three observables. Many formats are supported here including # classes such as qiskit.quantum_info.SparsePauliOp. The inner length-1 # lists cause this array of observables to have shape (3, 1), rather # than shape (3,) if they were omitted. observables = [ [SparsePauliOp(["XX", "IY"], [0.5, 0.5])], [Pauli("XX")], [Pauli("IY")] ] # Instantiate a new statevector simulation based estimator object. estimator = StatevectorEstimator() # Estimate the expectation value for all 300 combinations of # observables and parameter values, where the pub result will have # shape (3, 100). This shape is due to our array of parameter # bindings having shape (100,), combined with our array of observables # having shape (3, 1) pub = (circuit, observables, params) job = estimator.run([pub]) # Extract the result for the 0th pub (this example only has one pub). result = job.result()[0] # Error-bar information is also available, but the error is 0 # for this StatevectorEstimator. result.data.stds # Pull out the array-based expectation value estimate data from the # result and plot a trace for each observable. for idx, pauli in enumerate(observables): plt.plot(result.data.evs[idx], label=pauli) plt.legend()
-
The reference implementation
StatevectorSampler
ofBaseSamplerV2
was added. As seen in the example below, this sampler (and all V2 samplers) supports providing arrays of parameter value sets to bind against a single circuit.Each tuple of
(circuit, <optional> parameter values, <optional> shots)
, called a sampler primitive unified bloc (PUB), produces its own array-based result. Therun()
method can be given many pubs at once.from qiskit.circuit import ( Parameter, QuantumCircuit, ClassicalRegister, QuantumRegister ) from qiskit.primitives import StatevectorSampler import matplotlib.pyplot as plt import numpy as np # Define our circuit registers, including classical registers # called 'alpha' and 'beta'. qreg = QuantumRegister(3) alpha = ClassicalRegister(2, "alpha") beta = ClassicalRegister(1, "beta") # Define a quantum circuit with two parameters. circuit = QuantumCircuit(qreg, alpha, beta) circuit.h(0) circuit.cx(0, 1) circuit.cx(1, 2) circuit.ry(Parameter("a"), 0) circuit.rz(Parameter("b"), 0) circuit.cx(1, 2) circuit.cx(0, 1) circuit.h(0) circuit.measure([0, 1], alpha) circuit.measure([2], beta) # Define a sweep over parameter values, where the second axis is over. # the two parameters in the circuit. params = np.vstack([ np.linspace(-np.pi, np.pi, 100), np.linspace(-4 * np.pi, 4 * np.pi, 100) ]).T # Instantiate a new statevector simulation based sampler object. sampler = StatevectorSampler() # Start a job that will return shots for all 100 parameter value sets. pub = (circuit, params) job = sampler.run([pub], shots=256) # Extract the result for the 0th pub (this example only has one pub). result = job.result()[0] # There is one BitArray object for each ClassicalRegister in the # circuit. Here, we can see that the BitArray for alpha contains data # for all 100 sweep points, and that it is indeed storing data for 2 # bits over 256 shots. assert result.data.alpha.shape == (100,) assert result.data.alpha.num_bits == 2 assert result.data.alpha.num_shots == 256 # We can work directly with a binary array in performant applications. raw = result.data.alpha.array # For small registers where it is anticipated to have many counts # associated with the same bitstrings, we can turn the data from, # for example, the 22nd sweep index into a dictionary of counts. counts = result.data.alpha.get_counts(22) # Or, convert into a list of bitstrings that preserve shot order. bitstrings = result.data.alpha.get_bitstrings(22) print(bitstrings)
-
Added methods to join multiple
BitArray
objects along various axes.concatenate()
: join arrays along an existing axis of the arrays.concatenate_bits()
: join arrays along the bit axis.concatenate_shots()
: join arrays along the shots axis.
ba = BitArray.from_samples(['00', '11']) print(ba) # BitArray(<shape=(), num_shots=2, num_bits=2>) # reshape the bit array because `concatenate` requires an axis. ba_ = ba.reshape(1, 2) print(ba_) # BitArray(<shape=(1,), num_shots=2, num_bits=2>) ba2 = BitArray.concatenate([ba_, ba_]) print(ba2.get_bitstrings()) # ['00', '11', '00', '11'] # `concatenate_bits` and `concatenates_shots` do not require any axis. ba3 = BitArray.concatenate_bits([ba, ba]) print(ba3.get_bitstrings()) # ['0000', '1111'] ba4 = BitArray.concatenate_shots([ba, ba]) print(ba4.get_bitstrings()) # ['00', '11', '00', '11']
-
Added methods to generate a subset of
BitArray
object by slicing along various axes.__getitem__()
: slice the array along an existing axis of the array.slice_bits()
: slice the array along the bit axis.slice_shots()
: slice the array along the shot axis.
ba = BitArray.from_samples(['0000', '0001', '0010', '0011'], 4) print(ba) # BitArray(<shape=(), num_shots=4, num_bits=4>) print(ba.get_bitstrings()) # ['0000', '0001', '0010', '0011'] ba2 = ba.reshape(2, 2) print(ba2) # BitArray(<shape=(2,), num_shots=2, num_bits=2>) print(ba2[0].get_bitstrings()) # ['0000', '0001'] print(ba2[1].get_bitstrings()) # ['0010', '0011'] ba3 = ba.slice_bits([0, 2]) print(ba3.get_bitstrings()) # ['00', '01', '00', '01'] ba4 = ba.slice_shots([0, 2]) print(ba3.get_bitstrings()) # ['0000', '0010']
-
Added a method
transpose()
to transpose aBitArray
.ba = BitArray.from_samples(['00', '11']).reshape(2, 1, 1) print(ba) # BitArray(<shape=(2, 1), num_shots=1, num_bits=2>) print(ba.transpose()) # BitArray(<shape=(1, 2), num_shots=1, num_bits=2>)
-
Added a method
expectation_values()
to compute expectation values of diagonal operators.ba = BitArray.from_samples(['01', '11']) print(ba.expectation_values(["IZ", "ZI", "01"])) # [-1. 0. 0.5]
-
qiskit.primitives.containers.DataBin now satisfies the qiskit.primitives.containers.Shaped protocol. This means that every DataBin instance now has the additional attributes * shape: tuple[int, …] the leading shape of every entry in the instance * ndim: int the length of shape * size: int the product of the entries of shape The shape can be passed to the constructor.
-
Added mapping-like features to
DataBin
, i.e.,__getitem__
,__contains__
,__iter__
,keys()
,values()
, anditems()
.from qiskit import QuantumCircuit from qiskit.primitives import StatevectorSampler circuit = QuantumCircuit(1) circuit.h(0) circuit.measure_all() sampler = StatevectorSampler() result = sampler.run([circuit]).result() databin = result[0].data for creg, arr in databin.items(): print(creg, arr) for creg in databin: print(creg, databin[creg])
-
The subclass
SamplerPubResult
ofPubResult
was added, whichBaseSamplerV2
implementations can return. The main feature added in this new subclass isjoin_data()
, which joins together (a subset of) the contents ofdata
into a single object. This enables the following patterns:job_result = sampler.run([pub1, pub2, pub3], shots=123).result() # assuming all returned data entries are BitArrays counts1 = job_result[0].join_data().get_counts() bistrings2 = job_result[1].join_data().get_bitstrings() array3 = job_result[2].join_data().array
Providers Features
- The
BasicSimulator
python-based simulator included inqiskit.providers.basic_provider
now includes all the standard gates (qiskit.circuit.library .standard_gates
) up to 3 qubits.
Pulse Features
-
It is now possible to assign parameters to pulse
Schedule
andScheduleBlock
objects by specifying the parameter name as a string. The parameter name can be used to assign values to all parameters within the Schedule or ScheduleBlock that have the same name. Moreover, the parameter name of a ParameterVector can be used to assign all values of the vector simultaneously (the list of values should therefore match the length of the vector). -
The
assign_parameters
methods ofSchedule
andScheduleBlock
now support assigning aParameterVector
to a list of parameter values simultaneously in addition to assigning individualParameter
instances to individual values.
Quantum Information Features
-
Added a new
apply_layout()
method that is equivalent toapply_layout()
. This method is used to apply aTranspileLayout
layout from the transpiler to aPauli
observable that was built for an input circuit. This enables working withBaseEstimator
/BaseEstimatorV2
implementations and local transpilation when the input is of typePauli
. For example:from qiskit.circuit.library import RealAmplitudes from qiskit.primitives import BackendEstimatorV2 from qiskit.providers.fake_provider import GenericBackendV2 from qiskit.quantum_info import Pauli from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager psi = RealAmplitudes(num_qubits=2, reps=2) H1 = Pauli("XI") backend = GenericBackendV2(num_qubits=7) estimator = BackendEstimatorV2(backend=backend) thetas = [0, 1, 1, 2, 3, 5] pm = generate_preset_pass_manager(optimization_level=3, backend=backend) transpiled_psi = pm.run(psi) permuted_op = H1.apply_layout(transpiled_psi.layout) res = estimator.run([(transpiled_psi, permuted_op, thetas)]).result()
where an input circuit is transpiled locally before it’s passed to
run
. Transpilation expands the original circuit from 2 to 7 qubits (the size ofbackend
) and permutes its layout, which is then applied toH1
usingapply_layout()
to reflect the transformations performed bypm.run()
. -
Adds the
PauliList.noncommutation_graph()
andSparsePauliOp.noncommutation_graph()
methods, exposing the construction of non-commutation graphs, recasting the measurement operator grouping problem into a graph coloring problem. This permits users to work with these graphs directly, for example to explore coloring algorithms other than the one used bySparsePauliOp.group_commuting()
.
Synthesis Features
-
Added a new argument,
use_dag
, to theTwoQubitBasisDecomposer.__call__()
andXXDecomposer.__call__()
methods. This argument is used to control whether aDAGCircuit
is returned when calling aTwoQubitBasisDecomposer
orXXDecomposer
instance instead of the defaultQuantumCircuit
. For example:from qiskit.circuit.library import CXGate from qiskit.quantum_info import random_unitary from qiskit.synthesis import TwoQubitBasisDecomposer decomposer = TwoQubitBasisDecomposer(CXGate(), euler_basis="PSX") decomposer(random_unitary(4), use_dag=True)
will return a
DAGCircuit
when calling theTwoQubitBasisDecomposer
instancedecomposer
. -
LieTrotter.synthesize()
now usesQuantumCircuit.repeat()
to generate additional reps after the first. This reduces the number ofQuantumCircuit.compose()
calls by a factor ofreps
and significantly reduces the runtime for larger operators. -
Add a new synthesis method
synth_permutation_reverse_lnn_kms()
of reverse permutations for linear nearest-neighbor architectures using Kutin, Moulton, Smithline method. This algorithm synthesizes the reverse permutation on qubits over a linear nearest-neighbor architecture using CX gates with depth . -
The
TwoQubitBasisDecomposer
class has been rewritten in Rust which greatly improves the runtime performance. -
The
TwoQubitWeylDecomposition
synthesis class has been rewritten in Rust for better performance.
Transpiler Features
-
The following analysis passes now accept constraints encoded in a
Target
thanks to a newtarget
input argument:InstructionDurationCheck
ConstrainedReschedule
ValidatePulseGates
The target constraints will have priority over user-provided constraints, for coherence with the rest of the transpiler pipeline.
Upgrade Notes
-
Removes the hard-coding of style options for
plot_histogram()
. This allows Matplotlib style files to be faithfully applied to the figures. Users looking to go beyond the defaults set by Matplotlib can make their own style files, or pass a MatplotlibAxes
object toplot_histogram
and post-apply any customizations. -
The
transpile()
function has been upgraded to internally convert backend inputs of typeBackendV1
toBackendV2
, which allows the transpilation pipeline to now access the backend constraints through aTarget
. This change does not require any user action.
Circuits Upgrade Notes
-
The random-number usage of
QuantumVolume
has changed, so you will get a different circuit for a fixed seed between older versions of Qiskit and this version. The random-unitary generation now uses more bits of entropy, so large circuits will be less biased. -
The internal
UnitaryGate
instances in the definition of aQuantumVolume
circuit will no longer have alabel
field set. Previously this was set to the stringsu4_<seed>
where<seed>
was a three-digit number denoting the seed of an internal Numpy pRNG instance for that gate. Doing this was a serious performance problem, and the seed ought not to have been useful; if you need to retrieve the matrix from the gate, simply use theGate.to_matrix()
method.
Primitives Upgrade Notes
- The function qiskit.primitives.containers.make_data_bin() no longer creates and returns a qiskit.primitives.containers.DataBin subclass. It instead always returns the DataBin class. However, it continues to exist for backwards compatibility, though will eventually be deprecated. All users should migrate to construct DataBin instances directly, instead of instantiating subclasses as output by make_data_bin().
Synthesis Upgrade Notes
- The
TwoQubitWeylDecomposition
no longer will self-specialize into a subclass on creation. This was an internal detail of theTwoQubitWeylDecomposition
previously, and was not a documented public behavior as all the subclasses behaved the same and were only used for internal dispatch. However, as it was discoverable behavior this release note is to document that this will no longer occur and all instances ofTwoQubitWeylDecomposition
will be of the same type. There is no change in behavior for public methods of the class.
Transpiler Upgrade Notes
- The preset
StagedPassManager
returned for optimization level 2 bygenerate_preset_pass_manager()
andlevel_2_pass_manager()
have been reworked to provide a better balance between runtime and optimization. This means the output circuits will change compared to earlier releases. If you need an exact pass manager from level 2 in earlier releases you can either build it manually or use it from an earlier release and save the circuits withqpy
to load with a newer release.
Deprecation Notes
-
Support for running Qiskit with Python 3.8 has been deprecated and will be removed in the Qiskit 1.3.0 release. The 1.3.0 is the first release after Python 3.8 goes end of life and is no longer supported. [1] This means that starting in the 1.3.0 release you will need to upgrade the Python version you’re using to Python 3.9 or above.
-
The parameters
show_idle
andshow_barrier
in the timeline drawers had been replaced byidle_wires
andplot_barriers
respectively to match the circuit drawer parameters. Their previous names are now deprecated and will be removed in the next major release. The new parameters are fully equivalent.
Providers Deprecations
-
The abstract base classes
Provider
andProviderV1
are now deprecated and will be removed in Qiskit 2.0.0. The abstraction provided by these interface definitions were not providing a huge value. solely just the attributesname
,backends
, and aget_backend()
. A _provider_, as a concept, will continue existing as a collection of backends. If you’re implementing a provider currently you can adjust your code by simply removingProviderV1
as the parent class of your implementation. As part of this you probably would want to add an implementation ofget_backend
for backwards compatibility. For example:def get_backend(self, name=None, **kwargs): backend = self.backends(name, **kwargs) if len(backends) > 1: raise QiskitBackendNotFoundError("More than one backend matches the criteria") if not backends: raise QiskitBackendNotFoundError("No backend matches the criteria") return backends[0]
Synthesis Deprecations
- The
TwoQubitWeylDecomposition.specialize()
method is now deprecated and will be removed in the Qiskit 2.0.0 release. This method never had a public purpose and was unsafe for an end user to call as it would mutate the calculated decomposition in the object and produce invalid fields in the object. It was only used internally to construct a newTwoQubitWeylDecomposition
object. Despite this it was still a documented part of the public API for the class and is now being deprecated without any potential replacement. This release it always will raise aNotImplementedError
when called because the specialization subclassing has been removed as part of the Rust rewrite of the class.
Transpiler Deprecations
-
The pass
qiskit.transpiler.passes.CXCancellation
was deprecated in favor of class:.InverseCancellation, which is more generic.CXCancellation
is fully semantically equivalent toInverseCancellation([CXGate()])
. -
The transpilation pass
qiskit.transpiler.passes.ALAPSchedule
is now deprecated. It was pending for deprecation since Qiskit 0.37 (with Terra 0.21), released on June 2022. The pass is replaced byALAPScheduleAnalysis
, which is an analysis pass. -
The transpilation pass
qiskit.transpiler.passes.ASAPSchedule
is now deprecated. It was pending for deprecation since Qiskit 0.37 (with Terra 0.21), released on June 2022. It has been superseded byASAPScheduleAnalysis
and the new scheduling workflow. -
The transpilation pass
qiskit.transpiler.passes.DynamicalDecoupling
is now deprecated. It was pending for deprecation since Qiskit 0.37 (with Terra 0.21), released on June 2022. Instead, usePadDynamicalDecoupling
, which performs the same function but requires scheduling and alignment analysis passes to run prior to it. -
The transpilation pass
qiskit.transpiler.passes.AlignMeasures
is now deprecated. It was pending for deprecation since Qiskit 0.37 (with Terra 0.21), released on June 2022. Instead, useConstrainedReschedule
, which performs the same function and also supports aligning to additional timing constraints.
Bug Fixes
-
Fixed an issue with the
qpy.dump()
function where, when theuse_symengine
flag was set to a truthy object that evaluated toTrue
but was not actually the booleanTrue
, the generated QPY payload would be corrupt. For example, if you setuse_symengine
toHAS_SYMENGINE
, this object evaluates toTrue
when cast as a bool, but isn’t actuallyTrue
. -
Fixed an issue with the
circuit_drawer()
function andQuantumCircuit.draw()
method when loading a matplotlib style via the user configuration file. -
Fixed an issue where the
ConstrainedReschedule
transpiler pass would previously error if the circuit contained aReset
instruction. This has been corrected so that the pass no longer errors, however an actual hardware may behave differently from what Qiskit scheduler assumes especially for mid-circuit measurements and resets. Qiskit scheduler raisesRuntimeWarning
if it encounters circuit containing either. Fixed #10354 -
Fixed an issue with the
CommutationChecker
class where it would error if a gate’sname
attribute was UTF8 encoded. Previously only gate names with ascii encoding would work. Fixed #12501 -
Fixed a performance issue in the
BackendSamplerV2
andBackendEstimatorV2
. Fixed #12290 -
Fixed an issue with the
convert_to_target()
where the converter would incorrectly ignore control flow instructions if they were specified in theBackendConfiguration.supported_instructions
attribute which is the typical location that control flow instructions are specified in aBackendConfiguration
object. Fixed #11872. -
Fixed an issue with the
circuit_drawer()
orQuantumCircuit.draw()
when using thempl
output option where the program would hang if the circuit being drawn had a ControlFlow operation in it and thefold
option was set to -1 (meaning no fold). Fixed #12012. -
Fixed a bug in the conversion of custom pulse instructions to the legacy
qiskit.qobj
format. The bug was introduced in Qiskit 1.0.0 and caused conversion of instructions with custom pulse shapes to raise an error. After the fix, the conversion is carried out correctly, and the custom pulse is converted topulse.Waveform
as it should. Fixed #11828. -
A bug in
transpile()
has been fixed where custominstruction_durations
,dt
andbackend_properties
constraints would be ignored when provided at the same time as a backend of typeBackendV2
. The behavior after the fix is now independent of whether the provided backend is of typeBackendV1
or typeBackendV2
. Similarly, customtiming_constraints
are now overridden bytarget
inputs but take precedence overBackendV1
andBackendV2
inputs. -
Calling
EquivalenceLibrary.set_entry()
will now correctly update the internal graph object of the library. Previously, the metadata would be updated, but the graph structure would be unaltered, meaning that users likeBasisTranslator
would still use the old rules. Fixed #11958. -
The
EvolvedOperatorAnsatz
now correctly handles the case where the operators argument is an empty list. Previously, this would result in an error. -
From now on,
EvolvedOperatorAnsatz
will not have any qregs when thera are zero qubits, instead of having aQuantumRegister
instance with zero qubits. This behavior aligns more consistently with its superclassQuantumCircuit
. -
The method
Instruction.repeat()
now moves a setcondition
to the outer returnedInstruction
and leave the inner gates of its definition unconditional. Previously, the method would leaveClassicalRegister
instances within the inner definition, which was an invalid state, and would manifest itself as seemingly unrelated bugs later, such as during transpilation or export. Fixed #11935. -
Fixed an issue in the
InverseCancellation
transpiler pass where in some cases it would incorrectly cancel a self-inverse parameterized gate even if the parameter value didn’t match. Fixed #11815 -
Improve the decomposition of the gates
MCXGate
andMCPhaseGate
without using ancilla qubits, so that the number ofCXGate
will grow quadratically in the number of qubits and not exponentially. -
A bug that crashes the
convert_to_target()
function when qubit properties (either T1, T2 or frequency) are missing was fixed. The missing property values inQubitProperties
are filled withNone
. -
BasePassManager.run()
will no longer leak the previousPropertySet
into new workflows when called more than once. Previously, the samePropertySet
as before would be used to initialize follow-on runs, which could mean that invalid property information was being given to tasks. The behavior now matches that of Qiskit 0.44. Fixed #11784. -
Pauli.evolve()
now correctly handles quantum circuits containing ECR gates. Formerly they were not recognized as Clifford gates, and an error was raised. -
Fixed a bug in
Pauli.evolve()
where evolving by a circuit with a name matching certain Clifford gates (‘cx’, ‘cz’, etc) would evolve the Pauli according to the name of the circuit, not by the contents of the circuit. This bug occurred only with the non-default optionframe='s'
. -
Fixed a performance issue in the
qpy.load()
function when deserializing QPY payloads with large numbers of qubits or clbits in a circuit. -
Fixed a bug where
qiskit.primitives.containers.estimator_pub.EstimatorPub.coerce()
andqiskit.primitives.containers.sampler_pub.SamplerPub.coerce()
improperly handle the case where the parameter values are aBindingsArray
instance, giving rise to aValueError
whenever it is attempted. -
Fixed a bug in the handling of
default_alignment
argument ofbuild()
. Inputs of typeAlignmentKind
are now correctly processed as default alignments. -
Fixed a bug in
qiskit.pulse.utils.format_parameter_value()
function that unintentionally converts large enough integer numbers into float values or causes unexpected rounding. See qiskit/#11971 for details. -
Fix incorrect implemention of qDRIFT, negative coefficients of the Hamiltonian are now added back whereas they were always forced to be positive.
-
A bug has been fixed in
convert_durations_to_dt()
where the function would blindly apply a conversion from seconds todt
on circuit durations, independently of the original units of the attribute. This could lead to wrong orders of magnitude in the reported circuit durations. -
The preset pass managers of
transpile()
will no longer fail on circuits with control flow, if no hardware target or basis-gate set is specified. They will now treat such abstract targets as permitting all control-flow operations. Fixed #11906. -
The method
qiskit.instruction.Instruction.soft_compare()
is meant to compare whether two gates match in their name, number of qubits, number of clbits, and the number of parameters. However, there was a typo where it would not check the number of qubits and number of clbits for a match. This resolves the apparent typo. -
The default
init
plugin was not properly raising aTranspilerError
when called with an invalid optimization level. -
Fixed qiskit.primitives.containers.observables_array.ObservablesArray.coerce() so that it returns a 0-d array when the input is a single, unnested observable. Previously, it erroneously upgraded to a single dimension, with shape (1,).
-
Fixed an issue with the
Operator.from_circuit()
constructor method where it would incorrectly interpret the final layout permutation resulting in an invalid Operator being constructed. Previously, the final layout was processed without regards for the initial layout, i.e. the initialization was incorrect for all quantum circuits that have a non-trivial initial layout. -
Parameter
was updated so that instances that compare equal always have the same hash. Previously, only theParameter.uuid
was compared, soParamemter
instances with different names could compare equal if they had been constructed using a common value for theuuid
parameter (which is usually not passed explicitly). -
Parameter
instances used as stand-ins forinput
variables in OpenQASM 3 programs will now have their names escaped to avoid collisions with built-in gates during the export to OpenQASM 3. Previously there could be a naming clash, and the exporter would generate invalid OpenQASM 3. -
Fixed bug in
QuantumCircuit.draw()
that was causing custom style dictionaries for the Matplotlib drawer to be modified upon execution. -
QuantumCircuit.append()
withcopy=True
(its default) will now correctly copy instructions parametrized byParameterExpression
instances, and not just byParameter
instances. -
The internal handling of custom circuit calibrations and
InstructionDurations
has been offloaded from thetranspile()
function to the individual transpiler passes:qiskit.transpiler.passes.scheduling.DynamicalDecoupling
,qiskit.transpiler.passes.scheduling.padding.DynamicalDecoupling
. Before, instruction durations from circuit calibrations would not be taken into account unless they were manually incorporated into instruction_durations input argument, but the passes that need it now analyze the circuit and pick the most relevant duration value according to the following priority order: target > custom input > circuit calibrations. -
Fixed a bug in
transpile()
where thenum_processes
argument would only be used ifdt
orinstruction_durations
were provided.
Other Notes
-
Support for the arm64 macOS platform has been promoted from Tier 3 to Tier 1. Previously the platform was at Tier 3 because there was no available CI environment for testing Qiskit on the platform. Now that Github has made an arm64 macOS environment available to open source projects [1] we’re testing the platform along with the other Tier 1 supported platforms.
[1]