Skill Tracer
Skills › Scientific Agent Skills › qiskit › migration.md

migration.md

355 lines · skills/qiskit/references/migration.md at 5f2bc03

Open on GitHub

Most marks come from matching words in the file, so treat them as pointers.

1# Migration to Qiskit 2.5 and Runtime 0.50
2
3Cloud authentication, QPU, session, and batch snippets are illustrative and were not executed in this refresh. Local evidence is listed in [sources.md](sources.md).
4Use this guide when adapting code written for Qiskit 0.x, Qiskit 1.x, or early Qiskit Runtime releases.
5

Migration to Qiskit 2.5 and Runtime 0.50

Cloud authentication, QPU, session, and batch snippets are illustrative and were not executed in this refresh. Local evidence is listed in sources.md. Use this guide when adapting code written for Qiskit 0.x, Qiskit 1.x, or early Qiskit Runtime releases.

Raw lines 1 to 5
6## Runtime 0.50 client-side primitives
7
8The top-level `qiskit_ibm_runtime.SamplerV2`/`EstimatorV2` and their aliases are deprecated, not removed. Replace their imports with:
9
10```python
11from qiskit_ibm_runtime.executor_sampler import Sampler
12from qiskit_ibm_runtime.executor_estimator import Estimator
13from qiskit_ibm_runtime.options_models import SamplerOptions, EstimatorOptions
14```
16PUBs and result fields remain V2. The new implementations prepare and process Executor workloads locally. Recheck option defaults with `finalize_options().model_dump()`, keep ISA compilation and observable layout mapping, and test local noise-management behavior. Server `dry_run=True` returns mock values and does not establish circuit correctness.

Runtime 0.50 client-side primitives

The top-level qiskit_ibm_runtime.SamplerV2/EstimatorV2 and their aliases are deprecated, not removed. Replace their imports with:

from qiskit_ibm_runtime.executor_sampler import Sampler
from qiskit_ibm_runtime.executor_estimator import Estimator
from qiskit_ibm_runtime.options_models import SamplerOptions, EstimatorOptions

PUBs and result fields remain V2. The new implementations prepare and process Executor workloads locally. Recheck option defaults with finalize_options().model_dump(), keep ISA compilation and observable layout mapping, and test local noise-management behavior. Server dry_run=True returns mock values and does not establish circuit correctness.

Raw lines 6 to 17
18## Start with a Clean Environment
20Do not upgrade an environment containing both old `qiskit-terra` and modern `qiskit`.
22```bash
23uv venv --python 3.13 .venv-qiskit-2
24source .venv-qiskit-2/bin/activate
25uv pip install \
26 "qiskit==2.5.2" \
27 "qiskit-ibm-runtime==0.50.0" \
28 "qiskit-aer==0.17.2"
29```
31Run:
33```bash
34python scripts/check_environment.py --require-runtime --require-aer
35```

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.2" \
  "qiskit-ibm-runtime==0.50.0" \
  "qiskit-aer==0.17.2"

Run:

python scripts/check_environment.py --require-runtime --require-aer
Raw lines 18 to 36
Runs code bin/activate :24
Runs code python scripts/check_environment.py :34
37## High-Level API Map
39| Legacy pattern | Qiskit 2.5 pattern |
40|---|---|
41| Install `qiskit-terra` | Install `qiskit` |
42| `from qiskit import Aer` | `from qiskit_aer import AerSimulator` |
43| `execute(circuit, backend)` | V2 primitive, or provider-specific backend only when necessary |
44| `QuantumInstance` | Primitive implementation plus explicit transpilation |
45| `qiskit.opflow` | `qiskit.quantum_info.SparsePauliOp` and primitive PUBs |
46| `circuit.bind_parameters(...)` | `circuit.assign_parameters(...)`, or pass values in PUBs |
47| V1 `Sampler` / `Estimator` | `StatevectorSampler` / `StatevectorEstimator`, Runtime client-side `Sampler` / `Estimator` (V2 API) |
48| Parallel V1 input lists | One or more PUB tuples |
49| `result.quasi_dists` | `result[i].data.<register>.get_counts()` |
50| `result.values` | `result[i].data.evs` |
51| Runtime shared `Options()` | `SamplerOptions`, `EstimatorOptions`, dict, or `.options.update()` |
52| Primitive `backend=` / `session=` | Primitive `mode=` |
53| Runtime auto-transpilation | Explicit backend-specific ISA circuit |
54| Logical observable submitted unchanged | `observable.apply_layout(isa_circuit.layout)` |
55| `backend.configuration()` / `.properties()` | BackendV2 direct attributes and `backend.target` |
56| `channel="ibm_quantum"` | `channel="ibm_quantum_platform"` |
57| `qiskit.pulse` | IBM fractional gates for hardware; isolated legacy simulation for archived Dynamics |
58| `QFT(...)` blueprint class | `QFTGate(...)` or `synth_qft_full(...)` |
59| Blueprint ansatz classes | Function constructors such as `efficient_su2(...)` |
60| `instruction.c_if(...)` | Structured circuit control flow such as `if_test(...)` |

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 client-side Sampler / Estimator (V2 API)
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 for hardware; isolated legacy simulation for archived Dynamics
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(...)
Raw lines 37 to 61
62## Migrate V1 Sampler
64Legacy shape:
66```python
67# Legacy; do not use
68# sampler = Sampler()
69# result = sampler.run(circuits, parameter_values).result()
70# quasi_distribution = result.quasi_dists[0]
71```
73Current local V2:
75```python
76from qiskit.primitives import StatevectorSampler
78sampler = StatevectorSampler(seed=41)
79pub_result = sampler.run(
80 [(measured_circuit, parameter_values)],
81 shots=1024,
82).result()[0]
84counts = pub_result.data.meas.get_counts(0)
85```
87Key changes:
89- measured shot data replace V1 quasi-distributions,
90- output is organized by classical register,
91- parameter sweeps retain array shape,
92- a PUB contains one circuit and its parameter values.

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.
Raw lines 62 to 93
94## Migrate V1 Estimator
96Legacy shape:
98```python
99# Legacy; do not use
100# estimator = Estimator()
101# result = estimator.run(circuits, observables, values).result()
102# expectation_value = result.values[0]
103```
105Current local V2:
107```python
108from qiskit.primitives import StatevectorEstimator
110estimator = StatevectorEstimator()
111pub_result = estimator.run(
112 [(circuit, observable, parameter_values)]
113).result()[0]
115expectation_values = pub_result.data.evs
116standard_deviations = pub_result.data.stds
117```

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
Raw lines 94 to 118
119## Migrate Runtime Execution
121Legacy Runtime:
123```python
124# Legacy; do not use
125# options = Options()
126# options.resilience_level = 2
127# estimator = Estimator(session=session, options=options)
128```
130Current Runtime:
132```python
133from qiskit_ibm_runtime.executor_estimator import Estimator
135estimator = Estimator(
136 mode=session,
137 options={"resilience_level": 2},
139```
141Use current mode syntax:
143```python
144sampler = Sampler(mode=backend)
145sampler = Sampler(mode=batch)
146sampler = Sampler(mode=session)
147```
149The old `backend=` constructor keyword is unsupported. Use `mode=backend` for job mode or `mode=session` / `mode=batch` explicitly.

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.executor_estimator import 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)

The old backend= constructor keyword is unsupported. Use mode=backend for job mode or mode=session / mode=batch explicitly.

Raw lines 119 to 150
151## Migrate to ISA Circuits
153Legacy Runtime examples often submitted logical circuits and relied on service-side transpilation. V2 Runtime requires ISA circuits:
155```python
156from qiskit.transpiler import generate_preset_pass_manager
158pass_manager = generate_preset_pass_manager(
159 backend=backend,
160 optimization_level=1,
161 seed_transpiler=41,
163isa_circuit = pass_manager.run(logical_circuit)
164```
166Estimator observables must follow the layout:
168```python
169isa_observable = logical_observable.apply_layout(
170 isa_circuit.layout
172```
174Failing to map observables can silently change the physical qubits being measured or produce a width error.

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.

Raw lines 151 to 175
176## Migrate BackendV1 Access
178Legacy:
180```python
181# Legacy; do not use
182# basis_gates = backend.configuration().basis_gates
183# coupling_map = backend.configuration().coupling_map
184# properties = backend.properties()
185```
187Current:
189```python
190basis_operations = backend.operation_names
191coupling_map = backend.coupling_map
192target = backend.target
193num_qubits = backend.num_qubits
194```
196Query 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 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.

Raw lines 176 to 197
198## Migrate IBM Account Configuration
200The IBM Quantum Platform Classic channel is retired.
202Current trusted-machine setup:
204```python
205import os
206from qiskit_ibm_runtime import QiskitRuntimeService
208QiskitRuntimeService.save_account(
209 channel="ibm_quantum_platform",
210 token=os.environ["IBM_QUANTUM_API_KEY"],
211 instance=os.environ.get("IBM_QUANTUM_INSTANCE"),
212 set_as_default=True,
213 overwrite=True,
215```
217Do not paste a key into source or a notebook. See [setup.md](setup.md).

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.

Raw lines 198 to 218
219## Migrate Pulse Code
221`qiskit.pulse` was removed in Qiskit 2.0 without a drop-in replacement.
223Choose based on intent:
225- To execute supported continuous-angle one- and two-qubit rotations on IBM hardware, request a backend target with fractional gates.
226- For historical driven-system models, Qiskit Dynamics is archived and no longer maintained (October 2025). Treat it as a separately pinned legacy dependency, not a current Qiskit 2.x pulse replacement; choose a maintained dynamics solver for new research.
227- 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.
229Do not copy `pulse.build`, `ScheduleBlock`, or pulse-drawer examples into Qiskit 2.x code.
231QPY files containing `ScheduleBlock` objects cannot be loaded by Qiskit 2.x.

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.
  • For historical driven-system models, Qiskit Dynamics is archived and no longer maintained (October 2025). Treat it as a separately pinned legacy dependency, not a current Qiskit 2.x pulse replacement; choose a maintained dynamics solver for new research.
  • 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.

Raw lines 219 to 232
233## Migrate Circuit-Library Blueprints
235Several mutable blueprint classes are deprecated in favor of eagerly built functions or gates:
237```python
238from qiskit.circuit.library import (
239 QFTGate,
240 efficient_su2,
241 real_amplitudes,
242 zz_feature_map,
245qft_gate = QFTGate(4)
246ansatz = efficient_su2(4, reps=2)
247real_ansatz = real_amplitudes(4, reps=2)
248feature_map = zz_feature_map(4, reps=2)
249```
251The old `QFT` class is deprecated as of 2.1 and scheduled for removal in 3.0.
253Function constructors can differ in mutability and construction timing from blueprint classes. Test parameter order and circuit metadata after migration.

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.

Raw lines 233 to 254
255## Migrate Classical Conditions
257Legacy per-instruction conditions were removed:
259```python
260# Legacy; do not use
261# circuit.x(0).c_if(classical_register, 1)
262```
264Use structured control flow:
266```python
267with circuit.if_test((classical_bit, True)):
268 circuit.x(0)
269```
271Then verify that the selected backend target supports the corresponding control-flow instruction.

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.

Raw lines 255 to 272
273## Migrate Qiskit Algorithms
275Old `quantum_instance=` constructors are not current.
277```python
278from qiskit.primitives import StatevectorSampler
279from qiskit_algorithms import PhaseEstimation
281phase_estimation = PhaseEstimation(
282 num_evaluation_qubits=4,
283 sampler=StatevectorSampler(seed=41),
285```
287Current `VQE` takes a V2 Estimator; current `QAOA` takes a V2 Sampler.
289Use optimizer objects:
291```python
292from qiskit_algorithms.optimizers import COBYLA
294optimizer = COBYLA(maxiter=100)
295```
297Do not use strings such as `optimizer="COBYLA"` unless a specific current package API documents that form.

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.

Raw lines 273 to 298
299## Migrate Qiskit Machine Learning
301Since Qiskit Machine Learning 0.8, several features moved out of `qiskit_algorithms`:
303```python
304# Current package locations
305from qiskit_machine_learning.optimizers import COBYLA
306from qiskit_machine_learning.state_fidelities import ComputeUncompute
307from qiskit_machine_learning.utils import algorithm_globals
308```
310Check 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 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.

Raw lines 299 to 311
312## Migrate Qiskit Optimization 0.7
314Use `qiskit_optimization.minimum_eigensolvers.QAOA`, `qiskit_optimization.optimizers`, and `qiskit_optimization.utils.algorithm_globals` with `MinimumEigenOptimizer`. Its result handling uses the package's own eigensolver/result classes. The stand-alone Algorithms package remains separate.

Migrate Qiskit Optimization 0.7

Use qiskit_optimization.minimum_eigensolvers.QAOA, qiskit_optimization.optimizers, and qiskit_optimization.utils.algorithm_globals with MinimumEigenOptimizer. Its result handling uses the package's own eigensolver/result classes. The stand-alone Algorithms package remains separate.

Raw lines 312 to 315
316## Migrate Qiskit Nature
318Use mapper classes directly:
320```python
321from qiskit_nature.second_q.mappers import JordanWignerMapper
323mapper = JordanWignerMapper()
324qubit_operator = mapper.map(fermionic_operator)
325```
327`QubitConverter` is obsolete. Current application code lives primarily under `qiskit_nature.second_q`.

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.

Raw lines 316 to 328
329## Serialization Migration
331Prefer:
333- QPY for Qiskit-native circuit persistence,
334- OpenQASM for supported interchange,
335- explicit JSON-compatible experiment metadata.
337Avoid Python pickle for untrusted artifacts. QPY is forward-compatible but not backward-compatible: newer Qiskit normally reads older QPY, not vice versa.
339Record the Qiskit version that wrote each QPY file.

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.

Raw lines 329 to 340
341## Migration Validation
343After each migration:
3451. Run imports with deprecation warnings visible.
3462. Compare a small logical circuit's ideal state or operator.
3473. Verify parameter order and PUB output shape.
3484. Verify count-string and Pauli-label ordering.
3495. Compile against a fake `BackendV2`.
3506. Confirm all Estimator observables use the compiled layout.
3517. Compare application-level outputs, not circuit text alone.
3528. Run one bounded noisy simulation before a QPU.
3539. Record new package pins and update the experiment manifest.
355Do not silence deprecation warnings globally. Treat them as scheduled migration work before Qiskit 3.0.

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.

Raw lines 341 to 355