| title | description |
|---|---|
qiskit.algorithms migration guide |
How to update your code to use the new interface for qiskit.algorithms. |
This guide precedes the introduction of the V2 primitives interface. Following the introduction of the V2 primitives, some providers have deprecated V1 primitive implementations in favor of the V2 alternatives. If you are interested in following this guide, we recommend combining it with the Migrate to V2 primitives guide to bring your code to the most updated state.
In Qiskit 0.44 and later releases, the qiskit.algorithms module has been superseded by a new standalone library, qiskit_algorithms,
available on GitHub and
PyPi. The qiskit.algorithms module was migrated to a
separate package in order to clarify the purpose of Qiskit and make a distinction between the tools and libraries built on top of it.
If your code used qiskit.algorithms, follow these steps:
- Check your code for any uses of the
qiskit.algorithmsmodule. If you are, follow this guide to migrate to the primitives-based implementation. - After updating your code, run
pip install qiskit-algorithmsand update your imports fromqiskit.algorithmstoqiskit_algorithms.
The qiskit.algorithms module was originally built on top of the qiskit.opflow library and the
qiskit.utils.QuantumInstance utility. The development of the qiskit.primitives
introduced a higher-level execution paradigm, with the Estimator for computing expectation values for observables, and Sampler for executing circuits and returning probability distributions. These tools allowed the qiskit.algorithms module to be refactored, after which,
qiskit.opflow and qiskit.utils.QuantumInstance were deprecated.
For further background and detailed migration steps, see these guides:
The qiskit.algorithms module has been fully refactored to use the qiskit.primitives, for circuit execution, instead of the qiskit.utils.QuantumInstance, which is now deprecated.
There have been three types of refactoring:
- Algorithms that were refactored in a new location to support
qiskit.primitives. These algorithms have the same class names as theqiskit.utils.QuantumInstance-based ones but are in a new subpackage.
- Algorithms that were refactored in-place (same namespace) to support both
qiskit.utils.QuantumInstanceandqiskit.primitives. In the future,qiskit.utils.QuantumInstancewill be removed.
- Algorithms that were deprecated and are now removed entirely from
qiskit.algorithms. These are algorithms that do not serve as building blocks for applications and are only valuable for education, as described in the following tutorials:
This migration guide focuses on the algorithms with migration alternatives within
qiskit.algorithms, that is, refactoring types 1 and 2.
The classes in
qiskit.algorithms are initialized with any implementation of qiskit.primitives.BaseSampler or qiskit.primitives.BaseEstimator.
Once you know which primitive you want to use, choose the primitive implementation that meets your needs. For example:
-
For quick prototyping, use the reference implementations of primitives included in Qiskit:
qiskit.primitives.Samplerandqiskit.primitives.Estimator. -
For finer algorithm tuning, use a local simulator such as the primitive implementation in Aer:
qiskit_aer.primitives.Samplerandqiskit_aer.primitives.Estimator. -
For running on quantum hardware choose from these options:
- Access services with native primitive implementations, such as the Qiskit Runtime service, by using
qiskit_ibm_runtime.Samplerandqiskit_ibm_runtime.Estimator. - Wrap any QPU (quantum processing unti) with
Backendprimitives (qiskit.primitives.BackendSamplerandqiskit.primitives.BackendEstimator). These wrappers implement a primitive interface on top of a backend that only supportsbackend.run().
- Access services with native primitive implementations, such as the Qiskit Runtime service, by using
For detailed information and examples, particularly on the use of the Backend primitives, refer to
the QuantumInstance migration guide.
This guide describes these common configurations for algorithms that determine which primitive import to use:
- Running an algorithm with a statevector simulator when you want the ideal outcome without shot noise. For example, using the
qiskit.opflowlegacyqiskit.opflow.expectations.MatrixExpectation:
- Reference Primitives with default configuration. See QAOA for an example.
from qiskit.primitives import Sampler, Estimator- Aer Primitives with statevector simulator. See QAOA for an example.
from qiskit_aer.primitives import Sampler, Estimator
sampler = Sampler(backend_options={"method": "statevector"})
estimator = Estimator(backend_options={"method": "statevector"})- Running an algorithm using a simulator or device with shot noise. For example, using the
qiskit.opflowlegacyqiskit.opflow.expectations.PauliExpectation:
- Reference primitives with shots. See the VQE examples.
from qiskit.primitives import Sampler, Estimator
sampler = Sampler(options={"shots": 100})
estimator = Estimator(options={"shots": 100})
# or...
sampler = Sampler()
job = sampler.run(circuits, shots=100)
estimator = Estimator()
job = estimator.run(circuits, observables, shots=100)- Aer primitives with default configuration. See the VQE examples.
from qiskit_aer.primitives import Sampler, Estimator- IBM Qiskit Runtime primitives with default configuration. See VQD for an example.
from qiskit_ibm_runtime import Sampler, Estimator- Running an algorithm on an Aer simulator using a custom instruction. For example, using the
qiskit.opflowlegacyqiskit.opflow.expectations.AerPauliExpectation.
- Aer Primitives with default options. See TrotterQRTE for examples.
from qiskit_aer.primitives import Sampler, Estimator
sampler = Sampler()
estimator = Estimator()
The minimum eigensolver algorithms were refactored in a new location.
Instead of a qiskit.utils.QuantumInstance, qiskit.algorithms.minimum_eigensolvers are now initialized
by using an instance of the qiskit.primitives.Sampler or qiskit.primitives.Estimator primitive, depending
on the algorithm. The legacy classes can still be found in qiskit.algorithms.minimum_eigen_solvers.
- Old import (QuantumInstance-based):
from qiskit.algorithms import VQE, QAOA, NumPyMinimumEigensolver - New import (Primitives-based):
from qiskit.algorithms.minimum_eigensolvers import VQE, SamplingVQE, QAOA, NumPyMinimumEigensolver
The legacy qiskit.algorithms.minimum_eigen_solvers.VQE class has now been split according to the use case:
- For general-purpose Hamiltonians, use the Estimator-based
qiskit.algorithms.minimum_eigensolvers.VQEclass. - If you have a diagonal Hamiltonian and want the algorithm to return a sampling of the state, use
the new Sampler-based
qiskit.algorithms.minimum_eigensolvers.SamplingVQEalgorithm. Previously, this was done by using the legacyqiskit.algorithms.minimum_eigen_solvers.VQEwithqiskit.opflow.expectations.CVaRExpectation.
- The
expectationandinclude_customparameters have been removed, as this functionality is now defined at the Estimator level. - The
gradientparameter now takes in an instance of a primitive-based gradient class fromqiskit.algorithms.gradientsinstead of the legacyqiskit.opflow.gradients.Gradientclass. - The
max_evals_groupedparameter has been removed, as it can be set directly on the optimizer class. - The
estimator,ansatzandoptimizerare the only parameters that can be defined positionally (and in this order). All others have become keyword-only arguments.
[Legacy] Using QuantumInstance:
from qiskit.algorithms import VQE
from qiskit.algorithms.optimizers import SPSA
from qiskit.circuit.library import TwoLocal
from qiskit.opflow import PauliSumOp
from qiskit.utils import QuantumInstance
from qiskit_aer import AerSimulator
ansatz = TwoLocal(2, 'ry', 'cz')
opt = SPSA(maxiter=50)
# shot-based simulation
backend = AerSimulator()
qi = QuantumInstance(backend=backend, shots=2048, seed_simulator=42)
vqe = VQE(ansatz, optimizer=opt, quantum_instance=qi)
hamiltonian = PauliSumOp.from_list([("XX", 1), ("XY", 1)])
result = vqe.compute_minimum_eigenvalue(hamiltonian)
print(result.eigenvalue)(-0.9775390625+0j)[Updated] Using primitives:
from qiskit.algorithms.minimum_eigensolvers import VQE # new import!!!
from qiskit.algorithms.optimizers import SPSA
from qiskit.circuit.library import TwoLocal
from qiskit.quantum_info import SparsePauliOp
from qiskit.primitives import Estimator
from qiskit_aer.primitives import Estimator as AerEstimator
ansatz = TwoLocal(2, 'ry', 'cz')
opt = SPSA(maxiter=50)
# shot-based simulation
estimator = Estimator(options={"shots": 2048})
vqe = VQE(estimator, ansatz, opt)
# another option
aer_estimator = AerEstimator(run_options={"shots": 2048, "seed": 42})
vqe = VQE(aer_estimator, ansatz, opt)
hamiltonian = SparsePauliOp.from_list([("XX", 1), ("XY", 1)])
result = vqe.compute_minimum_eigenvalue(hamiltonian)
print(result.eigenvalue)-0.986328125[Legacy] Using QuantumInstance:
from qiskit.algorithms import VQE
from qiskit.algorithms.optimizers import SLSQP
from qiskit.circuit.library import TwoLocal
from qiskit.opflow import PauliSumOp, CVaRExpectation
from qiskit.utils import QuantumInstance
from qiskit_aer import AerSimulator
ansatz = TwoLocal(2, 'ry', 'cz')
opt = SLSQP(maxiter=50)
# shot-based simulation
backend = AerSimulator()
qi = QuantumInstance(backend=backend, shots=2048)
expectation = CVaRExpectation(alpha=0.2)
vqe = VQE(ansatz, optimizer=opt, expectation=expectation, quantum_instance=qi)
# diagonal Hamiltonian
hamiltonian = PauliSumOp.from_list([("ZZ",1), ("IZ", -0.5), ("II", 0.12)])
result = vqe.compute_minimum_eigenvalue(hamiltonian)
print(result.eigenvalue.real)-1.38[Updated] Using primitives:
from qiskit.algorithms.minimum_eigensolvers import SamplingVQE # new import!!!
from qiskit.algorithms.optimizers import SPSA
from qiskit.circuit.library import TwoLocal
from qiskit.quantum_info import SparsePauliOp
from qiskit.primitives import Sampler
from qiskit_aer.primitives import Sampler as AerSampler
ansatz = TwoLocal(2, 'ry', 'cz')
opt = SPSA(maxiter=50)
# shot-based simulation
sampler = Sampler(options={"shots": 2048})
vqe = SamplingVQE(sampler, ansatz, opt, aggregation=0.2)
# another option
aer_sampler = AerSampler(run_options={"shots": 2048, "seed": 42})
vqe = SamplingVQE(aer_sampler, ansatz, opt, aggregation=0.2)
# diagonal Hamiltonian
hamiltonian = SparsePauliOp.from_list([("ZZ",1), ("IZ", -0.5), ("II", 0.12)])
result = vqe.compute_minimum_eigenvalue(hamiltonian)
print(result.eigenvalue.real)-1.38For complete code examples, see the following updated tutorials:
The new QAOA only supports diagonal operators. This is because the legacy qiskit.algorithms.minimum_eigen_solvers.QAOA class extended
qiskit.algorithms.minimum_eigen_solvers.VQE, but now, qiskit.algorithms.minimum_eigensolvers.QAOA
extends qiskit.algorithms.minimum_eigensolvers.SamplingVQE.
- The
expectationandinclude_customparameters have been removed and theaggregationparameter has been added. This was previously defined through a customexpectationparameter. - The
gradientparameter now takes in an instance of a primitive-based gradient class fromqiskit.algorithms.gradientsinstead of the legacyqiskit.opflow.gradients.Gradientclass. - The
max_evals_groupedparameter has been removed, as it can be set directly on the optimizer class. - The
samplerandoptimizerparameters are the only parameters that can be defined positionally (and in this order). All others have become keyword-only arguments.
[Legacy] Using QuantumInstance:
from qiskit.algorithms import QAOA
from qiskit.algorithms.optimizers import COBYLA
from qiskit.opflow import PauliSumOp
from qiskit.utils import QuantumInstance
from qiskit_aer import AerSimulator
# exact statevector simulation
backend = AerSimulator()
qi = QuantumInstance(backend=backend, shots=None,
seed_simulator = 42, seed_transpiler = 42,
backend_options={"method": "statevector"})
optimizer = COBYLA()
qaoa = QAOA(optimizer=optimizer, reps=2, quantum_instance=qi)
# diagonal operator
qubit_op = PauliSumOp.from_list([("ZIII", 1),("IZII", 1), ("IIIZ", 1), ("IIZI", 1)])
result = qaoa.compute_minimum_eigenvalue(qubit_op)
print(result.eigenvalue.real)-4.0[Updated] Using primitives:
from qiskit.algorithms.minimum_eigensolvers import QAOA
from qiskit.algorithms.optimizers import COBYLA
from qiskit.quantum_info import SparsePauliOp
from qiskit.primitives import Sampler
from qiskit_aer.primitives import Sampler as AerSampler
# exact statevector simulation
sampler = Sampler()
# another option
sampler = AerSampler(backend_options={"method": "statevector"},
run_options={"shots": None, "seed": 42})
optimizer = COBYLA()
qaoa = QAOA(sampler, optimizer, reps=2)
# diagonal operator
qubit_op = SparsePauliOp.from_list([("ZIII", 1),("IZII", 1), ("IIIZ", 1), ("IIZI", 1)])
result = qaoa.compute_minimum_eigenvalue(qubit_op)
print(result.eigenvalue)-3.999999832366272For complete code examples, see the updated QAOA tutorial.
Because this is a classical solver, the workflow has not changed between the old and new implementation.
However, the import has changed from qiskit.algorithms.minimum_eigen_solvers.NumPyMinimumEigensolver
to qiskit.algorithms.minimum_eigensolvers.NumPyMinimumEigensolver to conform to the new interfaces
and result classes.
[Legacy] Using QuantumInstance:
from qiskit.algorithms import NumPyMinimumEigensolver
from qiskit.opflow import PauliSumOp
solver = NumPyMinimumEigensolver()
hamiltonian = PauliSumOp.from_list([("XX", 1), ("XY", 1)])
result = solver.compute_minimum_eigenvalue(hamiltonian)
print(result.eigenvalue)-1.4142135623730958[Updated] Using primitives:
from qiskit.algorithms.minimum_eigensolvers import NumPyMinimumEigensolver
from qiskit.quantum_info import SparsePauliOp
solver = NumPyMinimumEigensolver()
hamiltonian = SparsePauliOp.from_list([("XX", 1), ("XY", 1)])
result = solver.compute_minimum_eigenvalue(hamiltonian)
print(result.eigenvalue)-1.414213562373095For complete code examples, see the updated VQE, callback, gradients, initial point tutorial.
The eigensolver algorithms were refactored in a new location. Instead of using
qiskit.utils.QuantumInstance, qiskit.algorithms.eigensolvers are now initialized
using an instance of the qiskit.primitives.Sampler or qiskit.primitives.Estimator primitive, or
a primitive-based subroutine, depending on the algorithm. The legacy classes can still be found
in qiskit.algorithms.eigen_solvers.
- Old import path (QuantumInstance):
from qiskit.algorithms import VQD, NumPyEigensolver - New import path (primitives):
from qiskit.algorithms.eigensolvers import VQD, NumPyEigensolver
The new qiskit.algorithms.eigensolvers.VQD class is initialized with an instance of the
qiskit.primitives.Estimator primitive instead of a qiskit.utils.QuantumInstance.
It also takes an instance of a state fidelity class from mod:qiskit.algorithms.state_fidelities,
such as the qiskit.primitives.Sampler-based qiskit.algorithms.state_fidelities.ComputeUncompute.
- The
expectationandinclude_customparameters have been removed, as this functionality is now defined at the Estimator level. - The custom
fidelityparameter has been added and the customgradientparameter has been removed because current classes inqiskit.algorithms.gradientscannot use state fidelity gradients. - The
max_evals_groupedparameter has been removed because it can be set directly on theoptimizerclass. - The
estimator,fidelity,ansatzandoptimizerparameters are the only parameters that can be defined positionally (and in this order). All others have become keyword-only arguments.
[Legacy] Using QuantumInstance:
from qiskit import IBMQ
from qiskit.algorithms import VQD
from qiskit.algorithms.optimizers import SLSQP
from qiskit.circuit.library import TwoLocal
from qiskit.opflow import PauliSumOp
from qiskit.utils import QuantumInstance
ansatz = TwoLocal(3, rotation_blocks=["ry", "rz"], entanglement_blocks="cz", reps=1)
optimizer = SLSQP(maxiter=10)
hamiltonian = PauliSumOp.from_list([("XXZ", 1), ("XYI", 1)])
# example executing in cloud simulator
provider = IBMQ.load_account()
backend = provider.get_backend("ibmq_qasm_simulator")
qi = QuantumInstance(backend=backend)
vqd = VQD(ansatz, k=3, optimizer=optimizer, quantum_instance=qi)
result = vqd.compute_eigenvalues(operator=hamiltonian)
print(result.eigenvalues)[ 0.01765114+0.0e+00j -0.58507654+0.0e+00j -0.15003642-2.8e-17j][Updated] Using primitives:
The following code block will return an error for users on the Open Plan, because it uses sessions. Workloads on the Open Plan can run only in [job mode](/docs/guides/execution-modes#job-mode) or [batch mode](/docs/guides/execution-modes#batch-mode).from qiskit_ibm_runtime import Sampler, Estimator, QiskitRuntimeService, Session
from qiskit.algorithms.eigensolvers import VQD
from qiskit.algorithms.optimizers import SLSQP
from qiskit.algorithms.state_fidelities import ComputeUncompute
from qiskit.circuit.library import TwoLocal
from qiskit.quantum_info import SparsePauliOp
ansatz = TwoLocal(3, rotation_blocks=["ry", "rz"], entanglement_blocks="cz", reps=1)
optimizer = SLSQP(maxiter=10)
hamiltonian = SparsePauliOp.from_list([("XXZ", 1), ("XYI", 1)])
# example executing on a QPU
service = QiskitRuntimeService()
backend = service.backend(<backend_name>)
with Session(backend=backend) as session:
estimator = Estimator()
sampler = Sampler()
fidelity = ComputeUncompute(sampler)
vqd = VQD(estimator, fidelity, ansatz, optimizer, k=3)
result = vqd.compute_eigenvalues(operator=hamiltonian)
print(result.eigenvalues)[ 0.01765114+0.0e+00j -0.58507654+0.0e+00j -0.15003642-2.8e-17j]For complete code examples, see the updated VQD tutorial.
Similarly to its minimum eigensolver counterpart, because this is a classical solver, the workflow has not changed
between the old and new implementation.
However, the import has changed from qiskit.algorithms.eigen_solvers.NumPyEigensolver
to qiskit.algorithms.eigensolvers.NumPyEigensolver to conform to the new interfaces and result classes.
[Legacy]:
from qiskit.algorithms import NumPyEigensolver
from qiskit.opflow import PauliSumOp
solver = NumPyEigensolver(k=2)
hamiltonian = PauliSumOp.from_list([("XX", 1), ("XY", 1)])
result = solver.compute_eigenvalues(hamiltonian)
print(result.eigenvalues)[-1.41421356 -1.41421356][Updated]:
from qiskit.algorithms.eigensolvers import NumPyEigensolver
from qiskit.quantum_info import SparsePauliOp
solver = NumPyEigensolver(k=2)
hamiltonian = SparsePauliOp.from_list([("XX", 1), ("XY", 1)])
result = solver.compute_eigenvalues(hamiltonian)
print(result.eigenvalues)[-1.41421356 -1.41421356]
The time evolvers were refactored in a new location.
Instead of using a qiskit.utils.QuantumInstance, qiskit.algorithms.time_evolvers are now initialized
using a qiskit.primitives.Estimator primitive instance. The legacy classes can still be found
in qiskit.algorithms.evolvers.
In addition to the migration, the module has been substantially expanded to include Variational Quantum Time Evolution
(qiskit.algorithms.time_evolvers.VarQTE) solvers.
- Old import path (QuantumInstance):
from qiskit.algorithms import TrotterQRTE - New import path (Primitives):
from qiskit.algorithms.time_evolvers import TrotterQRTE
- The
expectationparameter has been removed, as this functionality is now defined at the Estimator level. - The
num_timestepsparameter has been added so you can define how many steps to divide the full evolution time in to.
[Legacy] Using QuantumInstance:
from qiskit.algorithms import EvolutionProblem, TrotterQRTE
from qiskit.circuit import QuantumCircuit
from qiskit.opflow import PauliSumOp, AerPauliExpectation
from qiskit.utils import QuantumInstance
from qiskit_aer import AerSimulator
operator = PauliSumOp.from_list([("X", 1),("Z", 1)])
initial_state = QuantumCircuit(1) # zero
time = 1
evolution_problem = EvolutionProblem(operator, 1, initial_state)
# Aer simulator using custom instruction
backend = AerSimulator()
quantum_instance = QuantumInstance(backend=backend)
expectation = AerPauliExpectation()
# LieTrotter with 1 rep
trotter_qrte = TrotterQRTE(expectation=expectation, quantum_instance=quantum_instance)
evolved_state = trotter_qrte.evolve(evolution_problem).evolved_state
print(evolved_state)CircuitStateFn(
┌─────────────────────┐
q: ┤ exp(-it (X + Z))(1) ├
└─────────────────────┘
)
[Updated] Using primitives:
from qiskit.algorithms.time_evolvers import TimeEvolutionProblem, TrotterQRTE # note new import!!!
from qiskit.circuit import QuantumCircuit
from qiskit.quantum_info import SparsePauliOp
from qiskit_aer.primitives import Estimator as AerEstimator
operator = SparsePauliOp.from_list([("X", 1),("Z", 1)])
initial_state = QuantumCircuit(1) # zero
time = 1
evolution_problem = TimeEvolutionProblem(operator, 1, initial_state)
# Aer simulator using custom instruction
estimator = AerEstimator()
# LieTrotter with 1 rep
trotter_qrte = TrotterQRTE(estimator=estimator)
evolved_state = trotter_qrte.evolve(evolution_problem).evolved_state
print(evolved_state.decompose()) ┌───────────┐┌───────────┐
q: ┤ exp(it X) ├┤ exp(it Z) ├
└───────────┘└───────────┘
The amplitude amplifier algorithms were refactored in-place.
Instead of a qiskit.utils.QuantumInstance, qiskit.algorithms.amplitude_amplifiers are now initialized
using an instance of any Sampler primitive. That is, qiskit.primitives.Sampler.
[Legacy] Using QuantumInstance:
from qiskit.algorithms import Grover
from qiskit.utils import QuantumInstance
qi = QuantumInstance(backend=backend)
grover = Grover(quantum_instance=qi)[Updated] Using primitives:
from qiskit.algorithms import Grover
from qiskit.primitives import Sampler
grover = Grover(sampler=Sampler())For complete code examples, see the following updated tutorials:
Similarly to the amplitude amplifiers, the amplitude estimators were refactored in-place.
Instead of a qiskit.utils.QuantumInstance, qiskit.algorithms.amplitude_estimators are now initialized
using an instance of any Sampler primitive. That is, qiskit.primitives.Sampler.
[Legacy] Using QuantumInstance:
from qiskit.algorithms import IterativeAmplitudeEstimation
from qiskit.utils import QuantumInstance
qi = QuantumInstance(backend=backend)
iae = IterativeAmplitudeEstimation(
epsilon_target=0.01, # target accuracy
alpha=0.05, # width of the confidence interval
quantum_instance=qi
)[Updated] Using primitives:
from qiskit.algorithms import IterativeAmplitudeEstimation
from qiskit.primitives import Sampler
iae = IterativeAmplitudeEstimation(
epsilon_target=0.01, # target accuracy
alpha=0.05, # width of the confidence interval
sampler=Sampler()
)For a complete code example, see the updated Amplitude Estimation tutorial.
The phase estimators were refactored in-place.
Instead of a qiskit.utils.QuantumInstance, qiskit.algorithms.phase_estimators are now initialized by
using an instance of any Sampler primitive. That is, qiskit.primitives.Sampler.
[Legacy] Using QuantumInstance:
from qiskit.algorithms import IterativePhaseEstimation
from qiskit.utils import QuantumInstance
qi = QuantumInstance(backend=backend)
ipe = IterativePhaseEstimation(
num_iterations=num_iter,
quantum_instance=qi
)[Updated] Using primitives:
from qiskit.algorithms import IterativePhaseEstimation
from qiskit.primitives import Sampler
ipe = IterativePhaseEstimation(
num_iterations=num_iter,
sampler=Sampler()
)For a complete code examples, see the updated Iterative phase estimation tutorial.