Skip to content

Latest commit

 

History

History
338 lines (241 loc) · 9.58 KB

File metadata and controls

338 lines (241 loc) · 9.58 KB

Migration to Qiskit 2.5 and Runtime 0.48

Use this guide when adapting code written for Qiskit 0.x, Qiskit 1.x, or early Qiskit Runtime releases.

Start with a Clean Environment

Do not upgrade an environment containing both old qiskit-terra and modern qiskit.

uv venv --python 3.13 .venv-qiskit-2
source .venv-qiskit-2/bin/activate
uv pip install \
  "qiskit==2.5.0" \
  "qiskit-ibm-runtime==0.48.0" \
  "qiskit-aer==0.17.2"

Run:

python scripts/check_environment.py --require-runtime --require-aer

High-Level API Map

Legacy pattern Qiskit 2.5 pattern
Install qiskit-terra Install qiskit
from qiskit import Aer from qiskit_aer import AerSimulator
execute(circuit, backend) V2 primitive, or provider-specific backend only when necessary
QuantumInstance Primitive implementation plus explicit transpilation
qiskit.opflow qiskit.quantum_info.SparsePauliOp and primitive PUBs
circuit.bind_parameters(...) circuit.assign_parameters(...), or pass values in PUBs
V1 Sampler / Estimator StatevectorSampler / StatevectorEstimator, Runtime SamplerV2 / EstimatorV2
Parallel V1 input lists One or more PUB tuples
result.quasi_dists result[i].data.<register>.get_counts()
result.values result[i].data.evs
Runtime shared Options() SamplerOptions, EstimatorOptions, dict, or .options.update()
Primitive backend= / session= Primitive mode=
Runtime auto-transpilation Explicit backend-specific ISA circuit
Logical observable submitted unchanged observable.apply_layout(isa_circuit.layout)
backend.configuration() / .properties() BackendV2 direct attributes and backend.target
channel="ibm_quantum" channel="ibm_quantum_platform"
qiskit.pulse IBM fractional gates or Qiskit Dynamics, depending on the goal
QFT(...) blueprint class QFTGate(...) or synth_qft_full(...)
Blueprint ansatz classes Function constructors such as efficient_su2(...)
instruction.c_if(...) Structured circuit control flow such as if_test(...)

Migrate V1 Sampler

Legacy shape:

# Legacy; do not use
# sampler = Sampler()
# result = sampler.run(circuits, parameter_values).result()
# quasi_distribution = result.quasi_dists[0]

Current local V2:

from qiskit.primitives import StatevectorSampler

sampler = StatevectorSampler(seed=41)
pub_result = sampler.run(
    [(measured_circuit, parameter_values)],
    shots=1024,
).result()[0]

counts = pub_result.data.meas.get_counts(0)

Key changes:

  • measured shot data replace V1 quasi-distributions,
  • output is organized by classical register,
  • parameter sweeps retain array shape,
  • a PUB contains one circuit and its parameter values.

Migrate V1 Estimator

Legacy shape:

# Legacy; do not use
# estimator = Estimator()
# result = estimator.run(circuits, observables, values).result()
# expectation_value = result.values[0]

Current local V2:

from qiskit.primitives import StatevectorEstimator

estimator = StatevectorEstimator()
pub_result = estimator.run(
    [(circuit, observable, parameter_values)]
).result()[0]

expectation_values = pub_result.data.evs
standard_deviations = pub_result.data.stds

Migrate Runtime Execution

Legacy Runtime:

# Legacy; do not use
# options = Options()
# options.resilience_level = 2
# estimator = Estimator(session=session, options=options)

Current Runtime:

from qiskit_ibm_runtime import EstimatorV2 as Estimator

estimator = Estimator(
    mode=session,
    options={"resilience_level": 2},
)

Use current mode syntax:

sampler = Sampler(mode=backend)
sampler = Sampler(mode=batch)
sampler = Sampler(mode=session)

Do not pass backend=backend to a primitive inside a batch or session; that selects job mode.

Migrate to ISA Circuits

Legacy Runtime examples often submitted logical circuits and relied on service-side transpilation. V2 Runtime requires ISA circuits:

from qiskit.transpiler import generate_preset_pass_manager

pass_manager = generate_preset_pass_manager(
    backend=backend,
    optimization_level=1,
    seed_transpiler=41,
)
isa_circuit = pass_manager.run(logical_circuit)

Estimator observables must follow the layout:

isa_observable = logical_observable.apply_layout(
    isa_circuit.layout
)

Failing to map observables can silently change the physical qubits being measured or produce a width error.

Migrate BackendV1 Access

Legacy:

# Legacy; do not use
# basis_gates = backend.configuration().basis_gates
# coupling_map = backend.configuration().coupling_map
# properties = backend.properties()

Current:

basis_operations = backend.operation_names
coupling_map = backend.coupling_map
target = backend.target
num_qubits = backend.num_qubits

Query gate errors, durations, and qubit support through the Target entries. Do not combine a backend with manually copied basis and coupling data unless constructing a deliberately synthetic target.

Migrate IBM Account Configuration

The IBM Quantum Platform Classic channel is retired.

Current trusted-machine setup:

import os
from qiskit_ibm_runtime import QiskitRuntimeService

QiskitRuntimeService.save_account(
    channel="ibm_quantum_platform",
    token=os.environ["IBM_QUANTUM_API_KEY"],
    instance=os.environ.get("IBM_QUANTUM_INSTANCE"),
    set_as_default=True,
    overwrite=True,
)

Do not paste a key into source or a notebook. See setup.md.

Migrate Pulse Code

qiskit.pulse was removed in Qiskit 2.0 without a drop-in replacement.

Choose based on intent:

  • To execute supported continuous-angle one- and two-qubit rotations on IBM hardware, request a backend target with fractional gates.
  • To model driven quantum systems and pulse-level dynamics, use the independently released Qiskit Dynamics project.
  • To keep a historical pulse workflow unchanged, isolate it in a legacy Qiskit 1.x environment only for archival reproducibility; do not mix it with Qiskit 2.x.

Do not copy pulse.build, ScheduleBlock, or pulse-drawer examples into Qiskit 2.x code.

QPY files containing ScheduleBlock objects cannot be loaded by Qiskit 2.x.

Migrate Circuit-Library Blueprints

Several mutable blueprint classes are deprecated in favor of eagerly built functions or gates:

from qiskit.circuit.library import (
    QFTGate,
    efficient_su2,
    real_amplitudes,
    zz_feature_map,
)

qft_gate = QFTGate(4)
ansatz = efficient_su2(4, reps=2)
real_ansatz = real_amplitudes(4, reps=2)
feature_map = zz_feature_map(4, reps=2)

The old QFT class is deprecated as of 2.1 and scheduled for removal in 3.0.

Function constructors can differ in mutability and construction timing from blueprint classes. Test parameter order and circuit metadata after migration.

Migrate Classical Conditions

Legacy per-instruction conditions were removed:

# Legacy; do not use
# circuit.x(0).c_if(classical_register, 1)

Use structured control flow:

with circuit.if_test((classical_bit, True)):
    circuit.x(0)

Then verify that the selected backend target supports the corresponding control-flow instruction.

Migrate Qiskit Algorithms

Old quantum_instance= constructors are not current.

from qiskit.primitives import StatevectorSampler
from qiskit_algorithms import PhaseEstimation

phase_estimation = PhaseEstimation(
    num_evaluation_qubits=4,
    sampler=StatevectorSampler(seed=41),
)

Current VQE takes a V2 Estimator; current QAOA takes a V2 Sampler.

Use optimizer objects:

from qiskit_algorithms.optimizers import COBYLA

optimizer = COBYLA(maxiter=100)

Do not use strings such as optimizer="COBYLA" unless a specific current package API documents that form.

Migrate Qiskit Machine Learning

Since Qiskit Machine Learning 0.8, several features moved out of qiskit_algorithms:

# Current package locations
from qiskit_machine_learning.optimizers import COBYLA
from qiskit_machine_learning.state_fidelities import ComputeUncompute
from qiskit_machine_learning.utils import algorithm_globals

Check the package's 0.8 migration guide for gradients, optimizers, fidelities, and utilities. Do not assume an import path from a Qiskit Machine Learning 0.7 tutorial still works.

Migrate Qiskit Nature

Use mapper classes directly:

from qiskit_nature.second_q.mappers import JordanWignerMapper

mapper = JordanWignerMapper()
qubit_operator = mapper.map(fermionic_operator)

QubitConverter is obsolete. Current application code lives primarily under qiskit_nature.second_q.

Serialization Migration

Prefer:

  • QPY for Qiskit-native circuit persistence,
  • OpenQASM for supported interchange,
  • explicit JSON-compatible experiment metadata.

Avoid Python pickle for untrusted artifacts. QPY is forward-compatible but not backward-compatible: newer Qiskit normally reads older QPY, not vice versa.

Record the Qiskit version that wrote each QPY file.

Migration Validation

After each migration:

  1. Run imports with deprecation warnings visible.
  2. Compare a small logical circuit's ideal state or operator.
  3. Verify parameter order and PUB output shape.
  4. Verify count-string and Pauli-label ordering.
  5. Compile against a fake BackendV2.
  6. Confirm all Estimator observables use the compiled layout.
  7. Compare application-level outputs, not circuit text alone.
  8. Run one bounded noisy simulation before a QPU.
  9. Record new package pins and update the experiment manifest.

Do not silence deprecation warnings globally. Treat them as scheduled migration work before Qiskit 3.0.