דף זה טרם תורגם. התוכן מוצג באנגלית.
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
tool to detect removed import paths in your code and suggest alternatives.
- Run with pipx
- Run with venv
If you have pipx 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.
If you don't want to use pipx, you can manually create a new
environment for the tool. This approach also lets you use
nbqa to check code examples in
Jupyter notebooks. Delete the environment when you're finished.
# Make new environment and install
python -m venv .flake8-qiskit-migration-venv
source .flake8-qiskit-migration-venv/bin/activate
pip install flake8-qiskit-migration
# Run plugin on Python code
flake8 --select QKT100 <path-to-source-directory> # e.g. `src/`
# (Optional) run plugin on notebooks
pip install nbqa
nbqa flake8 ./**/*.ipynb --select QKT100
# Deactivate and delete environment
deactivate
rm -r .flake8-qiskit-migration-venv
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.
Global instances and functions
Aer
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
BasicAer
The qiskit.BasicAer object is not available in Qiskit 1.0. See the
basicaer migration section for
migration options.
execute
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
backend.run().
# Legacy path
from qiskit import execute
job = execute(circuit, backend)
# New path
from qiskit import transpile
new_circuit = transpile(circuit, backend)
job = backend.run(new_circuit)
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 = sampler.run(circuit)
qiskit.circuit
QuantumCircuit.qasm
The QuantumCircuit.qasm method has been removed. Instead, use
qasm2.dump or
qasm2.dumps.
For Pygments-formatted output, look at the standalone
openqasm-pygments 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:
| Removed | Alternative |
|---|---|
QuantumCircuit.cnot | QuantumCircuit.cx |
QuantumCircuit.toffoli | QuantumCircuit.ccx |
QuantumCircuit.fredkin | QuantumCircuit.cswap |
QuantumCircuit.mct | QuantumCircuit.mcx |
QuantumCircuit.i | QuantumCircuit.id |
QuantumCircuit.squ | QuantumCircuit.unitary |
The following circuit methods have been removed. Instead, these gates can be
applied to a circuit with QuantumCircuit.append.
| Removed | Alternative (append) |
|---|---|
QuantumCircuit.diagonal | DiagonalGate |
QuantumCircuit.hamiltonian | HamiltonianGate |
QuantumCircuit.isometry | Isometry |
QuantumCircuit.iso | Isometry |
QuantumCircuit.uc | UCGate |
QuantumCircuit.ucrx | UCRXGate |
QuantumCircuit.ucry | UCRYGate |
QuantumCircuit.ucrz | UCRZGate |
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:
| Removed | Alternative |
|---|---|
QuantumCircuit.bind_parameters | QuantumCircuit.assign_parameters |
QuantumCircuit.snapshot | qiskit-aer's save instructions |
qiskit.converters
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"))
qiskit.extensions
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:
DiagonalGateHamiltonianGateInitializeIsometryqiskit.circuit.library.generalized_gates.mcg_up_diag.MCGupDiagUCGateUCPauliRotGateUCRXGateUCRYGateUCRZGateUnitaryGate
The following classes have been removed from the codebase, as their
functions were either redundant or linked to the extensions module:
| Removed | Alternative |
|---|---|
SingleQubitUnitary | qiskit.circuit.library.UnitaryGate |
Snapshot | Use qiskit-aer's save instructions |
ExtensionError | A relevant error class |
qiskit.primitives
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 from | Replace with |
|---|---|
BaseEstimator | BaseEstimatorV2 |
BaseSampler | BaseSamplerV2 |
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 from | Replace with |
|---|---|
qiskit.primitives.Estimator | qiskit.primitives.StatevectorEstimator |
qiskit.primitives.Sampler | qiskit.primitives.StatevectorSampler |
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 (
Sampler,
StatevectorSampler,
Estimator,
StatevectorEstimator,
)
estimator_v1 = Estimator()
sampler_v1 = Sampler()
estimator_v2 = StatevectorEstimator()
sampler_v2 = StatevectorSampler()
# define circuits, observables and parameter values
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 ofnparameter sets,nobservables, 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):
- Estimator, 1 circuit, 4 observables
- Sampler, 1 circuit, 3 parameter sets
- Estimator, 1 circuit, 4 observables, 2 parameter sets
# executing 1 circuit with 4 observables using Estimator V1
job = estimator_v1.run([circuit] * 4, [obs1, obs2, obs3, obs4])
evs = job.result().values
# executing 1 circuit with 4 observables using Estimator V2
job = estimator_v2.run([(circuit, [obs1, obs2, obs3, obs4])])
evs = job.result()[0].data.evs
# executing 1 circuit with 3 parameter sets using Sampler V1
job = sampler_v1.run([circuit] * 3, [vals1, vals2, vals3])
dists = job.result().quasi_dists
# executing 1 circuit with 3 parameter sets using Sampler V2
job = sampler_v2.run([(circuit, [vals1, vals2, vals3])])
counts = job.result()[0].data.meas.get_counts()
# executing 1 circuit with 4 observables and 2 parameter sets using Estimator V1
job = estimator_v1.run([circuit] * 8, [obs1, obs2, obs3, obs4] * 2, [vals1, vals2] * 4)
evs = job.result().values
# executing 1 circuit with 4 observables and 2 parameter sets using Estimator V2
job = estimator_v2.run([(circuit, [[obs1, obs2, obs3, obs4]], [[vals1], [vals2]])])
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:
- Sampler, 2 circuits, 1 parameter set
- Estimator, 2 circuits, 2 different observables
# executing 2 circuits with 1 parameter set using Sampler V1
job = sampler_v1.run([circuit1, circuit2], [vals1] * 2)
dists = job.result().quasi_dists
# executing 2 circuits with 1 parameter set using Sampler V2
job = sampler_v2.run([(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)
# executing 2 circuits with 2 different observables using Estimator V1
job = estimator_v1.run([circuit1, circuit2] , [obs1, obs2])
evs = job.result().values
# executing 2 circuits with 2 different observables using Estimator V2
job = estimator_v2.run([(circuit1, obs1), (circuit2, obs2)])
evs1 = job.result()[0].data.evs # result for pub 1 (circuit 1)
evs2 = job.result()[1].data.evs # result for pub 2 (circuit 2)
-
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)
circuit.cx(0, 1)
circuit.measure_all()
circuit.draw()┌───┐ ░ ┌─┐
q_0: ┤ H ├──■───░─┤M├───
└───┘┌─┴─┐ ░ └╥┘┌─┐
q_1: ─────┤ X ├─░──╫─┤M├
└───┘ ░ ║ └╥┘
meas: 2/══════════════╩══╩═
0 1Default classical register nameIn 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 = sampler_v1.run(circuit).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 = sampler_v2.run([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} -
Sampler and Estimator: The sampling overhead, commonly exposed by V1 implementations through theshotsrun option, is now an argument of the primitivesrun()method that can be specified at the PUB level. The V2 base classes expose the arguments in formats different from the V1 API:-
BaseSamplerV2.runexposes ashotsargument (similar to the previous workflow):# Sample two circuits at 128 shots each.
sampler_v2.run([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.
sampler_v2.run([(circuit1, None, 123), (circuit2, None, 456)]) -
EstimatorV2.runintroduces aprecisionargument 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.
estimator_v2.run([(circuit1, obs_array1), (circuit2, obs_array_2)], precision=0.05)
-
Updates in the V1 interface
-
Implicit conversion from a dense
BaseOperatorto aSparsePauliOpinEstimatorobservable arguments is no longer allowed. You should explicitly convert to aSparsePauliOpby usingSparsePauliOp.from_operator(operator)instead. -
Using a
PauliListin Estimator observable arguments is no longer allowed. Instead you should explicitly convert the argument by usingSparsePauliOp(pauli_list)first.
qiskit.providers
basicaer
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:
| Removed | Alternative |
|---|---|
qiskit.providers.basicaer | qiskit.providers.basic_provider |
BasicAerProvider | BasicProvider |
BasicAerJob | BasicProviderJob |
QasmSimulatorPy | BasicSimulator |
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:
| Removed | Alternative |
|---|---|
UnitarySimulatorPy | quantum_info.Operator |
StatevectorSimulatorPy | quantum_info.Statevector |
The following examples show the migration paths of the basicaer simulators.
- Statevector simulator
- Unitary simulator
- QASM simulator
from qiskit import QuantumCircuit
qc = QuantumCircuit(3)
qc.h(0)
qc.h(1)
qc.cx(1, 2)
qc.measure_all()
# Previous
from qiskit import BasicAer
backend = BasicAer.get_backend("statevector_simulator")
statevector = backend.run(qc).result().get_statevector()
# Current
qc.remove_final_measurements() # no measurements allowed
from qiskit.quantum_info import Statevector
statevector = Statevector(qc)
from qiskit import QuantumCircuit
qc = QuantumCircuit(3)
qc.h(0)
qc.h(1)
qc.cx(1, 2)
qc.measure_all()
# Previous
from qiskit import BasicAer
backend = BasicAer.get_backend("unitary_simulator")
result = backend.run(qc).result()
# Current
qc.remove_final_measurements() # no measurements allowed
from qiskit.quantum_info import Operator
result = Operator(qc).data
from qiskit import QuantumCircuit
qc = QuantumCircuit(3)
qc.h(0)
qc.h(1)
qc.cx(1, 2)
qc.measure_all()
# Previous
from qiskit import BasicAer
backend = BasicAer.get_backend("qasm_simulator")
result = backend.run(qc).result()
# One current option
from qiskit.providers.basic_provider import BasicProvider
backend = BasicProvider().get_backend("basic_simulator")
result = backend.run(qc).result()
# Another current option is to specify it directly
from qiskit.providers.basic_provider import BasicSimulator
backend = BasicSimulator()
result = backend.run(qc).result()
fake_provider
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.
- Fake Backends
- Fake Providers
- Any class in
qiskit.providers.fake_provider.backends fake_provider.fake_backend.FakeBackendfake_provider.fake_backend.FakeBackendV2
fake_provider.FakeProviderfake_provider.FakeProviderForBackendV2fake_provider.FakeProviderFactory
To migrate to the new path:
-
Install
qiskit-ibm-runtime0.17.1or later:pip install 'qiskit-ibm-runtime>=0.17.1' -
Replace instances of
qiskit.providers.fake_providerin your code withqiskit_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:
| Removed | Alternative |
|---|---|
qiskit.providers.fake_provider.FakeQasmBackend | qiskit_ibm_runtime.fake_provider.fake_qasm_backend.FakeQasmBackend |
qiskit.providers.fake_provider.FakePulseBackend | qiskit_ibm_runtime.fake_provider.fake_pulse_backend.FakePulseBackend |
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):
qiskit.providers.fake_provider.Fake5QV1qiskit.providers.fake_provider.Fake20QV1qiskit.providers.fake_provider.Fake7QPulseV1qiskit.providers.fake_provider.Fake27QPulseV1qiskit.providers.fake_provider.Fake127QPulseV1
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.
| Removed | Alternative |
|---|---|
fake_provider.FakeBackendV2 | fake_provider.GenericBackendV2 |
fake_provider.FakeBackend5QV2 | fake_provider.GenericBackendV2 |
fake_provider.FakeBackendV2LegacyQubitProps | fake_provider.GenericBackendV2 |
fake_provider.FakeBackendSimple | fake_provider.GenericBackendV2 |
fake_provider.ConfigurableFakeBackend | fake_provider.GenericBackendV2 |
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.aeris no longer possible. Instead, import fromqiskit_aer, which is a drop-in replacement. To installqiskit_aer, run:pip install qiskit-aer -
Support for running pulse jobs on backends from
qiskit.providers.fake_providerhas 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.
qiskit.pulse
ParametricPulse
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 pulse.build() as schedule:
pulse.play(pulse.Gaussian(100, 0.1, 25), pulse.DriveChannel(0))
with open('schedule.qpy', 'wb') as fd:
qpy.dump(schedule, fd)
| Removed | Alternative |
|---|---|
pulse.library.parametric_pulses.ParametricPulse | qiskit.pulse.SymbolicPulse |
pulse.library.parametric_pulses.Constant | pulse.library.symbolic_pulses.Constant |
pulse.library.parametric_pulses.Drag | pulse.library.symbolic_pulses.Drag |
pulse.library.parametric_pulses.Gaussian | pulse.library.symbolic_pulses.Gaussian |
qiskit.pulse.library.parametric_pulses.GaussianSquare | pulse.library.symbolic_pulses.GaussianSquare |
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 pulse.build() as schedule:
angle = Parameter("θ")
pulse.play(pulse.Gaussian(100, 0.1, 25, angle=angle), pulse.DriveChannel(0))
schedule.assign_parameters({angle: pi})