Back to Claude Scientific Skills

Migration to Qiskit 2.5 and Runtime 0.48

skills/qiskit/references/migration.md

2.55.09.6 KB
Original Source

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.

bash
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:

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

High-Level API Map

Legacy patternQiskit 2.5 pattern
Install qiskit-terraInstall qiskit
from qiskit import Aerfrom qiskit_aer import AerSimulator
execute(circuit, backend)V2 primitive, or provider-specific backend only when necessary
QuantumInstancePrimitive implementation plus explicit transpilation
qiskit.opflowqiskit.quantum_info.SparsePauliOp and primitive PUBs
circuit.bind_parameters(...)circuit.assign_parameters(...), or pass values in PUBs
V1 Sampler / EstimatorStatevectorSampler / StatevectorEstimator, Runtime SamplerV2 / EstimatorV2
Parallel V1 input listsOne or more PUB tuples
result.quasi_distsresult[i].data.<register>.get_counts()
result.valuesresult[i].data.evs
Runtime shared Options()SamplerOptions, EstimatorOptions, dict, or .options.update()
Primitive backend= / session=Primitive mode=
Runtime auto-transpilationExplicit backend-specific ISA circuit
Logical observable submitted unchangedobservable.apply_layout(isa_circuit.layout)
backend.configuration() / .properties()BackendV2 direct attributes and backend.target
channel="ibm_quantum"channel="ibm_quantum_platform"
qiskit.pulseIBM fractional gates or Qiskit Dynamics, depending on the goal
QFT(...) blueprint classQFTGate(...) or synth_qft_full(...)
Blueprint ansatz classesFunction constructors such as efficient_su2(...)
instruction.c_if(...)Structured circuit control flow such as if_test(...)

Migrate V1 Sampler

Legacy shape:

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

Current local V2:

python
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:

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

Current local V2:

python
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:

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

Current Runtime:

python
from qiskit_ibm_runtime import EstimatorV2 as Estimator

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

Use current mode syntax:

python
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:

python
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:

python
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:

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

Current:

python
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:

python
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:

python
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:

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

Use structured control flow:

python
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.

python
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:

python
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:

python
# 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:

python
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.