Skip to main contentIBM Quantum Documentation

Qiskit 1.0 feature changes

This guide describes migration paths for the most important feature changes in Qiskit 1.0, organized by module. Use the table of contents on the right side to navigate to the module you are interested in.

Qiskit 1.0 migration tool

To ease the migration process, you can use the flake8-qiskit-migration(opens in a new tab) tool to detect removed import paths in your code and suggest alternatives.

If you have pipx(opens in a new tab) installed, simply run the following command.

pipx run flake8-qiskit-migration <path-to-source-directory> 

This will install the package to a temporary virtual environment and run it on your code.


This tool only detects removed import paths. It does not detect the use of removed methods (such as QuantumCircuit.qasm) or arguments. It also can't keep track of assignments such as qk = qiskit, although it can handle aliases such as import qiskit as qk.

For more information, see the project's repository(opens in a new tab).

Global instances and functions


The qiskit.Aer object is not available in Qiskit 1.0. Instead, use the same object from the qiskit_aer namespace, which is a drop-in replacement. To install qiskit_aer, run:

pip install qiskit-aer


The qiskit.BasicAer object is not available in Qiskit 1.0. See the basicaer migration section for migration options.


The qiskit.execute function is not available in Qiskit 1.0. This function served as a high-level wrapper around the transpile and run functions in Qiskit. Instead of qiskit.execute, use the transpile function followed by

# Legacy path
from qiskit import execute
job = execute(circuit, backend)
# New path
from qiskit import transpile
new_circuit = transpile(circuit, backend)
job =

Alternatively, the Sampler primitive is semantically equivalent to the removed qiskit.execute function. The class BackendSampler is a generic wrapper for backends that do not support primitives:

from qiskit.primitives import BackendSampler
sampler = BackendSampler(backend)
job =



The QuantumCircuit.qasm method has been removed. Instead, use qasm2.dump or qasm2.dumps.

For Pygments-formatted output, look at the standalone openqasm-pygments(opens in a new tab) package, as qasm2.dump and qasm2.dumps do not provide Pygments-colored output.

from qiskit import QuantumCircuit
qc = QuantumCircuit(1)
# Old
qasm_str = qc.qasm()
# Alternative
from qiskit.qasm2 import dumps
qasm_str = dumps(qc)
# Alternative: Write to file
from qiskit.qasm2 import dump
with open("my_file.qasm", "w") as f:
    dump(qc, f)

QuantumCircuit gates

The following gate methods have been removed in favor of more established methods that append the same gates:


The following circuit methods have been removed. Instead, these gates can be applied to a circuit with QuantumCircuit.append.

RemovedAlternative (append)

For example, for a DiagonalGate:

from qiskit.circuit import QuantumCircuit
from qiskit.circuit.library import DiagonalGate  # new location in the circuit library
circuit = QuantumCircuit(2)
circuit.h([0, 1])  # some initial state
gate = DiagonalGate([1, -1, -1, 1])
qubits = [0, 1]  # qubit indices on which to apply the gate
circuit.append(gate, qubits)  # apply the gate

The following QuantumCircuit methods have also been removed:

QuantumCircuit.snapshotqiskit-aer's save instructions(opens in a new tab)


The qiskit.converters.ast_to_dag function has been removed from Qiskit. It converted the abstract syntax tree generated by the legacy OpenQASM 2 parser to a DAGCircuit. As the legacy OpenQASM 2 parser has been removed (see qiskit.qasm), this function no longer serves a purpose. Instead, parse your OpenQASM 2 files into a QuantumCircuit using the QuantumCircuit.from_qasm_file or QuantumCircuit.from_qasm_str constructor methods (or the qiskit.qasm2 module), then convert that QuantumCircuit into a DAGCircuit with circuit_to_dag.

# Previous
from qiskit.converters import ast_to_dag
from qiskit.qasm import Qasm
dag = ast_to_dag(Qasm(filename="myfile.qasm").parse())
# Current alternative
import qiskit.qasm2
from qiskit.converters import circuit_to_dag
dag = circuit_to_dag(qiskit.qasm2.load("myfile.qasm"))


The qiskit.extensions module is no longer available. Most of its objects have been integrated into the circuit library (qiskit.circuit.library). To migrate to the new location, simply replace qiskit.extensions with qiskit.circuit.library in the object import path. This is a drop-in replacement.

# Previous
from qiskit.extensions import DiagonalGate
# Current alternative
from qiskit.circuit.library import DiagonalGate

The classes moved to qiskit.circuit.library are:

The following classes have been removed from the codebase, as their functions were either redundant or linked to the extensions module:

SnapshotUse qiskit-aer's save instructions(opens in a new tab)
ExtensionErrorA relevant error class


The most notable change in the qiskit.primitives module is the introduction of the new primitives V2 interface. This section shows how to migrate your workflow from primitives V1 to primitives V2, as well as the few changes that have taken place in the inputs accepted by the V1 interface.


Starting with the 1.0 release, we will refer to the pre-1.0 primitives interface as "primitives V1".

Migrate from V1 to V2

The formal distinction between the primitives V1 and V2 APIs are the base classes from which primitives implementations inherit. To transition to the new base classes, you can maintain the original import path from qiskit.primitives:

Migrate fromReplace with

The names of the qiskit core implementations of the V2 primitives (those importable from qiskit.primitives), have been modified to clarify their purpose as implementations that can be run locally with a statevector simulator backend. The new names do not include the -V2 suffix.

Migrate fromReplace with

There are some conceptual differences to think about when migrating from V1 to V2. These differences are dictated by the base class, but are shown in the following examples using the statevector implementations found in qiskit.primitives:


For the following examples, assume the following imports and primitive initializations:

from qiskit.primitives import (
estimator_v1 = Estimator()
sampler_v1 = Sampler()
estimator_v2 = StatevectorEstimator()
sampler_v2 = StatevectorSampler()
# define circuits, observables and parameter values
  1. Sampler and Estimator: The new V2 primitives are designed to accept vectorized inputs, where single circuits can be grouped with array-valued specifications. That is, one circuit can be executed for arrays of n parameter sets, n observables, or both (in the case of the estimator). Each group is called a primitive unified bloc (pub), and can be represented as a tuple: (1 x circuit, [n x observables], [n x parameters]). The V1 interface didn't allow for the same flexibility. Instead, the number of input circuits had to match the number of observables and parameter sets, as shown in the following examples (select a tab to see each example):
# executing 1 circuit with 4 observables using Estimator V1
job =[circuit] * 4, [obs1, obs2, obs3, obs4])
evs = job.result().values
# executing 1 circuit with 4 observables using Estimator V2
job =[(circuit, [obs1, obs2, obs3, obs4])])
evs = job.result()[0].data.evs

V2 primitives accept multiple PUBs as inputs, and each pub gets its own result. This lets you run different circuits with various parameter/observable combinations, which was not always possible in the V1 interface:

# executing 2 circuits with 1 parameter set using Sampler V1
job =[circuit1, circuit2], [vals1] * 2)
dists = job.result().quasi_dists
# executing 2 circuits with 1 parameter set using Sampler V2
job =[(circuit1, vals1), (circuit2, vals1)])
counts1 = job.result()[0].data.meas.get_counts()  # result for pub 1 (circuit 1)
counts2 = job.result()[1].data.meas.get_counts()  # result for pub 2 (circuit 2)
  1. Sampler: The V2 sampler now returns measurement outcome samples in the form of bitstrings or counts, instead of the quasi-probability distributions from the V1 interface. The bitstrings show the measurement outcomes, preserving the shot order in which they were measured. The V2 sampler result objects organize data in terms of their input circuits' classical register names, for compatibility with dynamic circuits.

    # Define quantum circuit with 2 qubits
    circuit = QuantumCircuit(2)
    circuit.h(0), 1)
            ┌───┐      ░ ┌─┐
       q_0: ┤ H ├──■───░─┤M├───
            └───┘┌─┴─┐ ░ └╥┘┌─┐
       q_1: ─────┤ X ├─░──╫─┤M├
                 └───┘ ░  ║ └╥┘
    meas: 2/══════════════╩══╩═
                          0  1
    Default classical register name

    In the circuit above, notice that the name of the classical register defaults to "meas". This name will be used later to access the measurement bitstrings.

    # Run using V1 sampler
    result =
    quasi_dist = result.quasi_dists[0]
    print(f"The quasi-probability distribution is: {quasi_dist}")
    The quasi-probability distribution is: {0: 0.5, 3: 0.5}
    # Run using V2 sampler
    result =[circuit]).result()
    # Access result data for pub 0
    data_pub = result[0].data
    # Access bitstrings for the classical register "meas"
    bitstrings = data_pub.meas.get_bitstrings()
    print(f"The number of bitstrings is: {len(bitstrings)}")
    # Get counts for the classical register "meas"
    counts = data_pub.meas.get_counts()
    print(f"The counts are: {counts}")
    The number of bitstrings is: 1024
    The counts are: {'00': 523, '11': 501}
  2. Sampler and Estimator: The sampling overhead, commonly exposed by V1 implementations through the shots run option, is now an argument of the primitives run() method that can be specified at the PUB level. The V2 base classes expose the arguments in formats different from the V1 API:

    • exposes a shots argument (similar to the previous workflow):

      # Sample two circuits at 128 shots each.[circuit1, circuit2], shots=128)
      # Sample two circuits at different amounts of shots. The "None"s are necessary
      # as placeholders
      # for the lack of parameter values in this example.[(circuit1, None, 123), (circuit2, None, 456)])
    • introduces a precision argument that specifies the error bars that the primitive implementation should target for expectation values estimates:

      # Estimate expectation values for two PUBs, both with 0.05 precision.[(circuit1, obs_array1), (circuit2, obs_array_2)], precision=0.05)

Updates in the V1 interface



Most of the functionality in the qiskit.providers.basicaer module has been replaced with the new qiskit.providers.basic_provider module, except for the UnitarySimulatorPy and StatevectorSimulatorPy classes, which have been removed; their functionality was already contained in the quantum_info module.

The migration to the new paths is straightforward. You can replace most classes in qiskit.providers.basicaer with their qiskit.providers.basic_provider counterpart (drop-in replacement). Note that the following classes have new paths and names:

Global instances

Be aware of any global instances when migrating to the new module. There is no replacement for the BasicAer global instance that could be directly imported as qiskit.BasicAer. This means that from qiskit import BasicProvider is no longer a valid import. Instead, the provider class must be imported from its submodule and instantiated by the user:

# Previous
from qiskit import BasicAer
backend = BasicAer.get_backend("backend_name")
# Current
from qiskit.providers.basic_provider import BasicProvider
backend = BasicProvider().get_backend("backend_name")

The unitary and statevector simulators can be replaced with different quantum_info classes. This is not a drop-in replacement, but the changes are minimal. See the following migration examples:


The following examples show the migration paths of the basicaer simulators.

from qiskit import QuantumCircuit
qc = QuantumCircuit(3)
qc.h(1), 2)
# Previous
from qiskit import BasicAer
backend = BasicAer.get_backend("statevector_simulator")
statevector =
# Current
qc.remove_final_measurements()  # no measurements allowed
from qiskit.quantum_info import Statevector
statevector = Statevector(qc)


Most of the user-facing qiskit.providers.fake_provider components have been migrated to the qiskit-ibm-runtime Python package. This includes the fake provider classes, all of the device-specific fake backends (such as FakeVigo, FakeNairobiV2, and FakeSherbrooke), and the fake backend base classes. Click through the following tabs to see the affected classes.

  • Any class in qiskit.providers.fake_provider.backends
  • fake_provider.fake_backend.FakeBackend
  • fake_provider.fake_backend.FakeBackendV2

To migrate to the new path:

  1. Install qiskit-ibm-runtime 0.17.1 or later:

    pip install 'qiskit-ibm-runtime>=0.17.1'
  2. Replace instances of qiskit.providers.fake_provider in your code with qiskit_ibm_runtime.fake_provider. For example:

    # Old
    from qiskit.providers.fake_provider import FakeProvider
    backend1 = FakeProvider().get_backend("fake_ourense")
    from qiskit.providers.fake_provider import FakeSherbrooke
    backend2 = FakeSherbrooke()
    # Alternative
    from qiskit_ibm_runtime.fake_provider import FakeProvider
    backend1 = FakeProvider().get_backend("fake_ourense")
    from qiskit_ibm_runtime.fake_provider import FakeSherbrooke
    backend2 = FakeSherbrooke()

The fake backend base classes have also been migrated, but have some differences in the import path:


If you depend on fake backends for unit testing a downstream library and have conflicts with the qiskit-ibm-runtime dependency, you can also find new Qiskit-native generic fake backend alternatives. These include the following BackendV1 classes (drop-in replacements):

This is a configurable class that returns BackendV2 instances:

fake_provider (special testing backends)

The fake backend classes for special testing purposes in qiskit.providers.fake_provider have not been migrated to qiskit_ibm_runtime.fake_provider. The recommended migration path is to use the new GenericBackendV2 class to configure a backend with similar properties or to build a custom target.


Example: Migrate to the new GenericBackendV2 class:

# Legacy path
from qiskit.providers.fake_provider import FakeBackend5QV2
backend = FakeBackend5QV2()
# New path
from qiskit.providers.fake_provider import GenericBackendV2
backend = GenericBackendV2(num_qubits=5)
# Note that this class generates a 5q backend with generic
# properties that serves the same purpose as FakeBackend5QV2
# but will not be identical.

Other migration tips

  • Importing from qiskit.providers.aer is no longer possible. Instead, import from qiskit_aer, which is a drop-in replacement. To install qiskit_aer, run:

    pip install qiskit-aer
  • Support for running pulse jobs on backends from qiskit.providers.fake_provider has been removed in Qiskit 1.0. This is because Qiskit Aer removed its simulation functionality for such jobs. For low-level Hamiltonian-simulation workloads, consider using a specialized library such as Qiskit Dynamics(opens in a new tab).



The qiskit.pulse.library.parametric_pulses.ParametricPulse base class and pulse library have been superseded by qiskit.pulse.SymbolicPulse and the corresponding pulse library. SymbolicPulse supports QPY serialization:

from qiskit import pulse, qpy
with as schedule:, 0.1, 25), pulse.DriveChannel(0))
with open('schedule.qpy', 'wb') as fd:
    qpy.dump(schedule, fd)

Complex-valued amplitude

Complex-valued pulse amplitude (amp) is replaced by an (amp, angle) duo. This representation is more intuitive, especially for some calibration tasks such as angle calibration:

from qiskit import pulse
from qiskit.circuit import Parameter
from math import pi
with as schedule:
    angle = Parameter("θ"), 0.1, 25, angle=angle), pulse.DriveChannel(0))
schedule.assign_parameters({angle: pi})

Injecting circuit gate operations

Injecting circuit gate operations into the pulse builder context through is no longer possible. This removal affects input arguments of type QuantumCircuit, as well as the following functions:

  • qiskit.pulse.builder.call_gate
  • qiskit.pulse.builder.u1
  • qiskit.pulse.builder.u2
  • qiskit.pulse.builder.u3
  • qiskit.pulse.builder.x

If you still want to inject backend-calibrated schedules, use the following pattern instead of calling gate commands.

from qiskit.providers.fake_provider import GenericBackendV2
from qiskit import pulse
backend = GenericBackendV2(num_qubits=5)
sched =["x"][(qubit,)].calibration
with as only_pulse_scheds:

Similarly, a QuantumCircuit can be injected in the builder context by manually transpiling and scheduling the object.

from math import pi
from qiskit.compiler import schedule, transpile
qc = QuatumCircuit(2)
qc.rz(pi / 2, 0)
qc.rz(pi / 2, 0), 1)
qc_t = transpile(qc, backend)
sched = schedule(qc_t, backend)
with as only_pulse_scheds:

We recommend writing a minimum pulse program with the builder and attaching it to QuantumCircuit through the QuantumCircuit.add_calibration method as a microcode of a gate instruction, rather than writing the entire program with the pulse model.

The following arguments in have been removed with no alternative.

  • default_transpiler_settings
  • default_circuit_scheduler_settings

These functions have also been removed:

  • qiskit.pulse.builder.active_transpiler_settings
  • qiskit.pulse.builder.active_circuit_scheduler_settings
  • qiskit.pulse.builder.transpiler_settings
  • qiskit.pulse.builder.circuit_scheduler_settings

This is because it's no longer possible to inject circuit objects into the builder context (see Injecting circuit gate operations); these settings were for converting injected objects into pulse representations.


The discrete pulse library has been removed from the code base. This includes:

  • qiskit.pulse.library.constant
  • qiskit.pulse.library.square
  • qiskit.pulse.library.sawtooth
  • qiskit.pulse.library.triangle
  • qiskit.pulse.library.cos
  • qiskit.pulse.library.sin
  • qiskit.pulse.library.gaussian
  • qiskit.pulse.library.gaussian_deriv
  • qiskit.pulse.library.sech
  • qiskit.pulse.library.sech_deriv
  • qiskit.pulse.library.gaussian_square
  • qiskit.pulse.library.drag

Instead, use the corresponding qiskit.pulse.SymbolicPulse, with SymbolicPulse.get_waveform(). For example, instead of pulse.gaussian(100,0.5,10), use pulse.Gaussian(100,0.5,10).get_waveform(). Note that the phase of both Sawtooth and Square is defined such that a phase of 2\\pi shifts by a full cycle, contrary to the discrete counterpart. Also note that complex amplitudes are no longer supported in the symbolic pulse library; use float, amp, and angle instead.


It is no longer possible to load library qiskit.pulse.ScalableSymbolicPulse objects with a complex amp parameter from version 5 or earlier qpy files (Qiskit Terra < 0.23.0). No migration action is required, as complex amp will automatically be converted to float (amp, angle).

This change applies to these pulses:


The legacy OpenQASM 2 parser module previously in qiskit.qasm has been superseded by the qiskit.qasm2 module, which provides a faster and more accurate parser for OpenQASM 2. The high level QuantumCircuit methods from_qasm_file() and from_qasm_str() remain the same, but will use the new parser internally. However, the public interface for the qasm2 module is not the same. While the qiskit.qasm module provided an interface to an abstract syntax tree returned by the ply parser library, qiskit.qasm2 does not expose the AST or any lower level implementation details about the parser. It instead takes OpenQASM 2 input and outputs a QuantumCircuit object.

For example, if you were previously running something like this:

import qiskit.qasm
from qiskit.converters import ast_to_dag, dag_to_circuit
ast = qiskit.qasm.Qasm(filename="myfile.qasm").parse()
dag = ast_to_dag(ast)
qasm_circ = dag_to_circuit(dag)

Replace it with the following:

import qiskit.qasm2
qasm_circ = qiskit.qasm2.load("myfile.qasm")


The qiskit.quantum_info.synthesis module has been migrated to various locations in the codebase, mostly qiskit.synthesis.


This move has not affected the usual import path of Quaternion, but you can no longer access it through qiskit.quantum_info.synthesis.

Finally, cnot_rxx_decompose has been removed.


The qiskit.test module is no longer a public module. This was never intended to be public, nor to be used outside of the Qiskit test suite. All functionality was specific to Qiskit and no alternative is provided; if you needed similar functionality, you should include it in your own test harnesses.

The module was removed in Qiskit 1.0. Most of this functionality was either replaced by similar functionality in other packages or removed with no alternative. The primary exception is the function, which has been relocated to the qiskit.utils module. It can be used from this new location instead. For example:

If you were previously running:

# Previous
from import parallel_map
parallel_map(func, input)
# Current
from qiskit.utils import parallel_map
parallel_map(func, input)


The submodule has been removed because the functionality in this module is tied to the legacy qiskit-ibmq-provider package, which is no longer supported. It also only supported BackendV1 and not the newer BackendV2 interface. For similar functionality, see qiskit-ibm-provider.ibm_jupyter.


The submodule has been removed as it was tied to the legacy qiskit-ibmq-provider package, which is no longer supported (it also only supported BackendV1 interface and not the newer BackendV2 interface). There is no alternative provided for this functionality.


The submodule has been removed. This module was a legacy redirect from the original location of the Qiskit visualization module and was moved to qiskit.visualization in Qiskit 0.8.0. If you're still using this path, update your imports from to qiskit.visualization.

# Previous
from import plot_histogram
# Current
from qiskit.visualization import plot_histogram


The module and the progressbar() utility it exposed have been removed. This module's functionality was not widely used and is better covered by dedicated packages such as tqdm(opens in a new tab).



The items in qiskit.transpiler.synthesis module have been migrated to new locations:

qiskit.transpiler.synthesis.aqc (except for AQCSynthesisPlugin)qiskit.synthesis.unitary.aqc


The NoiseAdaptiveLayout transpiler pass has been superseded by VF2Layout and VF2PostLayout, which set a layout based on the reported noise characteristics of a backend. Both the pass and the corresponding "noise_adaptive" layout stage plugin have been removed from Qiskit.

The CrosstalkAdaptiveSchedule transpiler pass has been removed from the code base. This pass was no longer usable because its internal operation was dependent on custom properties being set in the BackendProperties payload of a BackendV1 instance. As no backends are setting these fields, the pass has been removed.


The append methods of the ConditionalController, FlowControllerLinear, and DoWhileController classes have been removed. Instead, all tasks must be provided when the controller objects are constructed.


The following tools in qiskit.utils have been removed with no replacement:

  • qiskit.utils.arithmetic
  • qiskit.utils.circuit_utils
  • qiskit.utils.entangler_map
  • qiskit.utils.name_unnamed_args

These functions were used exclusively in the qiskit.algorithms and qiskit.opflow modules, which have also been removed.


The qiskit.visualization.qcstyle module has been removed. Use qiskit.visualization.circuit.qcstyle as direct replacement.

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