From dc0fae866ac0f53808ba2556e528274469847ed2 Mon Sep 17 00:00:00 2001 From: ElePT Date: Wed, 8 Feb 2023 11:59:18 +0100 Subject: [PATCH 01/18] Add draft --- docs/index.rst | 3 +- docs/migration_guides/index.rst | 10 + docs/migration_guides/opflow_migration.rst | 696 +++++++++++++++++++++ 3 files changed, 708 insertions(+), 1 deletion(-) create mode 100644 docs/migration_guides/index.rst create mode 100644 docs/migration_guides/opflow_migration.rst diff --git a/docs/index.rst b/docs/index.rst index 27af89033ee7..69c065eb9fef 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -8,7 +8,8 @@ Qiskit Terra documentation API References Release Notes - + Migration Guides + .. Hiding - Indices and tables :ref:`genindex` :ref:`modindex` diff --git a/docs/migration_guides/index.rst b/docs/migration_guides/index.rst new file mode 100644 index 000000000000..0ae8193ca5bc --- /dev/null +++ b/docs/migration_guides/index.rst @@ -0,0 +1,10 @@ +.. module:: qiskit + +======================= +Qiskit Migration Guides +======================= + +.. toctree:: + :maxdepth: 1 + + opflow_migration diff --git a/docs/migration_guides/opflow_migration.rst b/docs/migration_guides/opflow_migration.rst new file mode 100644 index 000000000000..efc0564b9895 --- /dev/null +++ b/docs/migration_guides/opflow_migration.rst @@ -0,0 +1,696 @@ +======================= +Opflow Migration Guide +======================= + +*Jump to* `TL;DR`_. + +Background +---------- + +The :mod:`~qiskit.opflow` module was originally introduced as a layer between circuits and algorithms, a series of building blocks +for quantum algorithms research and development. The core design of opflow was based on the assumption that the +point of access to backends (real devices or simulators) was a ``backend.run()`` +type of method: a method that takes in a circuit and returns its measurement results. +Under this assumption, all the tasks related to operator handling and building expectation value +computations were left to the user to manage. Opflow helped bridge that gap, it allowed to wrap circuits and +observables into operator classes that could be algebraically manipulated, so that the final result's expectation +values could be easily computed following different methods. + +This basic opflow functionality is covered by its core submodules: the ``operators`` submodule +(including :mod:`~qiskit.opflow.operator_globals`, :mod:`~qiskit.opflow.list_ops`, :mod:`~qiskit.opflow.primitive_ops`, and :mod:`~qiskit.opflow.state_fns`), +the :mod:`~qiskit.opflow.converters` submodule, and the :mod:`~qiskit.opflow.expectations` submodule. +Following this reference framework of ``operators``, :mod:`~qiskit.opflow.converters` and :mod:`~qiskit.opflow.expectations`, opflow includes more +algorithm-specific functionality, which can be found in the :mod:`~qiskit.opflow.evolutions` submodule (specific for hamiltonian +simulation algorithms), as well as the :mod:`~qiskit.opflow.gradients` submodule (applied in multiple machine learning and optimization +use-cases). Some classes from the core modules mentioned above are also algorithm or application-specific, +for example the :obj:`~CVarMeasurement` or the :obj:`~Z2Symmetries`. + +.. With the introduction of the primitives we have a new mechanism that allows.... efficient... error mitigation... + +The recent introduction of the :mod:`~qiskit.primitives` provided a new interface for interacting with backends. Now, instead of +preparing a circuit to execute with a ``backend.run()`` type of method, the algorithms can leverage the :class:`~Sampler` and +:class:`~Estimator` primitives, send parametrized circuits and observables, and directly receive quasi-probability distributions or +expectation values (respectively). This workflow simplifies considerably the pre-processing and post-processing steps +that previously relied on opflow. For example, the :class:`~Estimator` primitive returns expectation values from a series of +circuit-observable pairs, superseding most of the functionality of the :mod:`~qiskit.opflow.expectations` submodule. Without the need for +building opflow expectations, most of the components in ``operators`` also became redundant, as they commonly wrapped +elements from :mod:`~qiskit.quantum_info`. + +Higher-level opflow sub-modules, such as the :mod:`~qiskit.opflow.gradients` sub-module, were refactored to take full advantage +of the primitives interface. They can now be accessed as part of the :mod:`~qiskit.algorithms` module, +together with other primitive-based subroutines. Similarly, the :mod:`~qiskit.opflow.evolutions` sub-module got refactored, and now +can be easily integrated into a primitives-based workflow (as seen in the new :mod:`~qiskit.algorithms.time_evolvers` algorithms). + +All of these reasons have encouraged us to move away from opflow, and find new paths of developing algorithms based on +the :mod:`~qiskit.primitives` interface and the :mod:`~qiskit.quantum_info` module, which is a powerful tool for representing +and manipulating quantum operators. + +This guide traverses the opflow submodules and provides either a direct alternative +(i.e. using :mod:`~qiskit.quantum_info`), or an explanation of how to replace their functionality in algorithms. + +TL;DR +----- +The new :mod:`~qiskit.primitives` have superseded most of the :mod:`~qiskit.opflow` functionality. Thus, the latter is being deprecated. + +Index +----- +This guide covers the migration from these opflow sub-modules: + +**Operators** + +- `Operator Base Class`_ +- `Operator Globals`_ +- `Primitive and List Ops`_ +- `State Functions`_ + +**Converters** + +- `Converters`_ +- `Evolutions`_ +- `Expectations`_ + +**Gradients** + +- `Gradients`_ + + +Operator Base Class +------------------- + +The :class:`~opflow.OperatorBase` abstract class can generally be replaced with :class:`~quantum_info.BaseOperator`, keeping in +mind that :class:`~quantum_info.BaseOperator` is more generic than its opflow counterpart. In particular, you should consider that: + +1. :class:`~opflow.OperatorBase` implements a broader algebra mixin. Some operator overloads are not available in +:class:`~quantum_info.BaseOperator`. + +2. :class:`~opflow.OperatorBase` also implements methods such as ``.to_matrix()`` or ``.to_spmatrix()``, which are only found +in some of the :class:`~quantum_info.BaseOperator` subclasses. + +.. list-table:: Migration of ``qiskit.opflow.operator_base`` + :header-rows: 1 + + * - opflow + - alternative + - notes + * - :class:`~opflow.OperatorBase` + - :class:`~quantum_info.BaseOperator` + - For more information, check the :class:`~quantum_info.BaseOperator` source code. + +Operator Globals +---------------- +Opflow provided shortcuts to define common single qubit states, operators, and common non-parametrized gates in the +:mod:`~qiskit.opflow.operator_globals` module. These were mainly used for didactic purposes and can easily be replaced by their corresponding +:mod:`~qiskit.quantum_info` class: :class:`~qiskit.quantum_info.Pauli`, :class:`~qiskit.quantum_info.Clifford` or :class:`~qiskit.quantum_info.Statevector`. + +1-Qubit Paulis +~~~~~~~~~~~~~~ + +.. list-table:: Migration of ``qiskit.opflow.operator_globals`` (1/3) + :header-rows: 1 + + * - opflow + - alternative + - notes + * - :class:`~qiskit.opflow.X`, :class:`~qiskit.opflow.Y`, :class:`~qiskit.opflow.Z`, :class:`~qiskit.opflow.I` + - :class:`~qiskit.quantum_info.Pauli` + - For direct compatibility with classes in :mod:`~qiskit.algorithms`, wrap in :class:`~qiskit.quantum_info.SparsePauliOp`. + * - + + .. code-block:: python + + from qiskit.opflow import X + operator = X ^ X + + - + + .. code-block:: python + + from qiskit.quantum_info import Pauli + X = Pauli('X') + op = X ^ X + + - + + .. code-block:: python + + from qiskit.quantum_info import Pauli, SparsePauliOp + op = Pauli('X') ^ Pauli('X') + + # equivalent to: + op = SparsePauliOp('XX') + +Common non-parametrized gates (Clifford) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.. list-table:: Migration of ``qiskit.opflow.operator_globals`` (2/3) + :header-rows: 1 + + * - opflow + - alternative + - notes + + * - :class:`~qiskit.opflow.CX`, :class:`~qiskit.opflow.S`, :class:`~qiskit.opflow.H`, :class:`~qiskit.opflow.T`, :class:`~qiskit.opflow.CZ`, :class:`~qiskit.opflow.Swap` + - Append corresponding gate to :class:`~qiskit.QuantumCircuit` + :class:`~qiskit.quantum_info.Clifford` + ``.to_operator()`` + - + + * - + + .. code-block:: python + + from qiskit.opflow import H + op = H ^ H + + - + + .. code-block:: python + + from qiskit import QuantumCircuit + from qiskit.quantum_info import Clifford + qc = QuantumCircuit(2) + qc.h(0) + qc.h(1) + op = Clifford(qc).to_operator() + + # or... + qc = QuantumCircuit(1) + qc.h(0) + H = Clifford(qc).to_operator() + op = H ^ H + + - + +1-Qubit States +~~~~~~~~~~~~~~ +.. list-table:: Migration of ``qiskit.opflow.operator_globals`` (3/3) + :header-rows: 1 + + * - opflow + - alternative + - notes + + * - :class:`~qiskit.opflow.Zero`, :class:`~qiskit.opflow.One`, :class:`~qiskit.opflow.Plus`, :class:`~qiskit.opflow.Minus` + - :class:`~qiskit.quantum_info.Statevector` or :class:`~qiskit.QuantumCircuit` directly + - + + * - + + .. code-block:: python + + from qiskit.opflow import Zero, One, Plus, Minus + + state1 = Zero ^ One + state2 = Plus ^ Minus + + - + + .. code-block:: python + + from qiskit import QuantumCircuit + from qiskit.quantum_info import Statevector + + qc_zero = QuantumCircuit(1) + qc_one = copy(qc_zero) + qc_one.x(0) + state1 = Statevector(qc_zero) ^ Statevector(qc_one) + + qc_plus = copy(qc_zero) + qc_plus.h(0) + qc_minus = copy(qc_one) + qc_minus.h(0) + state2 = Statevector(qc_plus) ^ Statevector(qc_minus) + - + + + +Primitive and List Ops +---------------------- +Most of the workflows that previously relied in components from :mod:`~qiskit.opflow.primitive_ops` and :mod:`~qiskit.opflow.list_ops` can now +leverage elements from :mod:`~qiskit.quantum_info.operators` instead. Some of these classes do not require a 1-1 replacement because +they were created to interface with other opflow components. + +PrimitiveOps +~~~~~~~~~~~~~~ + +.. list-table:: Migration of ``qiskit.opflow.primitive_ops`` + :header-rows: 1 + + * - opflow + - alternative + - notes + + * - :class:`~qiskit.opflow.PrimitiveOp` + - No replacement needed + - Can directly use :class:`~qiskit.quantum_info.Operator`` + * - :class:`~qiskit.opflow.CircuitOp` + - No replacement needed + - Can directly use :class:`~qiskit.QuantumCircuit` + * - :class:`~qiskit.opflow.MatrixOp` + - :class:`~qiskit.quantum_info.Operator`` + - + * - :class:`~qiskit.opflow.PauliOp` + - :class:`~qiskit.quantum_info.Pauli` + - For direct compatibility with classes in :mod:`~qiskit.algorithms`, wrap in :class:`~qiskit.quantum_info.SparsePauliOp` + * - :class:`~qiskit.opflow.PauliSumOp` + - :class:`~qiskit.quantum_info.SparsePauliOp` + - See example below + * - :class:`~qiskit.opflow.TaperedPauliSumOp` + - This class was used to combine a :class:`~PauliSumOp` with its identified symmetries in one object. It has been deprecated without replacement + - See ``Z2Symmetries`` example for updated workflow + * - :class:`~qiskit.opflow.Z2Symmetries` + - :class:`~qiskit.quantum_info.Z2Symmetries` + - See example below + + +PrimitiveOps Examples +~~~~~~~~~~~~~~~~~~~~~ +.. list-table:: + :header-rows: 1 + + * - opflow + - alternative + - notes + + * - ``PauliSumOp`` **Example:** + + .. code-block:: python + + from qiskit.opflow import PuliSumOp + from qiskit.quantum_info import SparsePauliOp, Pauli + + qubit_op = PauliSumOp(SparsePauliOp(Pauli("XYZY"), coeffs=[2]), coeff=-3j) + + - + + .. code-block:: python + + from qiskit.quantum_info import SparsePauliOp, Pauli + + qubit_op = SparsePauliOp(Pauli("XYZY")), coeff=-6j) + + - + * - ``Z2Symmetries`` **and** ``TaperedPauliSumOp`` **Example:** + + .. code-block:: python + + from qiskit.opflow import PuliSumOp, Z2Symmetries, TaperedPauliSumOp + + qubit_op = PauliSumOp.from_list( + [ + ("II", -1.0537076071291125), + ("IZ", 0.393983679438514), + ("ZI", -0.39398367943851387), + ("ZZ", -0.01123658523318205), + ("XX", 0.1812888082114961), + ] + ) + z2_symmetries = Z2Symmetries.find_Z2_symmetries(qubit_op) + tapered_op = z2_symmetries.taper(qubit_op) + # can be represented as: + tapered_op = TaperedPauliSumOp(primitive, z2_symmetries) + - + + .. code-block:: python + + from qiskit.quantum_info import SparsePauliOp, Z2Symmetries + + qubit_op = SparsePauliOp.from_list( + [ + ("II", -1.0537076071291125), + ("IZ", 0.393983679438514), + ("ZI", -0.39398367943851387), + ("ZZ", -0.01123658523318205), + ("XX", 0.1812888082114961), + ] + ) + z2_symmetries = Z2Symmetries.find_z2_symmetries(qubit_op) + tapered_op = z2_symmetries.taper(qubit_op) + - + + +ListOps +~~~~~~~ +.. list-table:: Migration of ``qiskit.opflow.list_ops`` + :header-rows: 1 + + * - opflow + - alternative + - notes + + * - :class:`~qiskit.opflow.ListOp` + - No replacement needed. This class was used internally within opflow. + - + + * - :class:`~qiskit.opflow.ComposedOp` + - No replacement needed. This class was used internally within opflow. + - + + * - :class:`~qiskit.opflow.SummedOp` + - No replacement needed. This class was used internally within opflow. + - + + * - :class:`~qiskit.opflow.TensoredOp` + - No replacement needed. This class was used internally within opflow. + - + +State Functions +--------------- + +This module can be generally replaced by :class:`~qiskit.quantum_info.QuantumState`, with some differences to keep in mind: + +1. The primitives-based workflow does not rely on constructing state functions as opflow did +2. The equivalence is, once again, not 1-1. +3. Algorithm-specific functionality has been migrated to the respective algorithm's module + +TODO: ADD EXAMPLE! + +.. list-table:: Migration of ``qiskit.opflow.state_fns`` + :header-rows: 1 + + * - opflow + - alternative + - notes + + * - :class:`~qiskit.opflow.StateFn` + - No replacement needed. This class was used internally within opflow. + - + + * - :class:`~qiskit.opflow.CircuitStateFn` + - No replacement needed. This class was used internally within opflow. + - + + * - :class:`~qiskit.opflow.DictStateFn` + - No replacement needed. This class was used internally within opflow. + - + + * - :class:`~qiskit.opflow.VectorStateFn` + - This class was used internally within opflow, but there exists a :mod:`~qiskit.quantum_info` replacement. There's the :class:`~qiskit.quantum_info.Statevector` class and the :class:`~qiskit.quantum_info.StabilizerState` (Clifford based vector). + - + + * - :class:`~qiskit.opflow.SparseVectorStateFn` + - No replacement needed. This class was used internally within opflow. + - See :class:`~qiskit.opflow.VectorStateFn` + + * - :class:`~qiskit.opflow.OperatorStateFn` + - No replacement needed. This class was used internally within opflow. + - + * - :class:`~qiskit.opflow.CVaRMeasurement` + - Used in :class:`~qiskit.opflow.CVaRExpectation`. Functionality now covered by :class:`~SamplingEstimator`. See example in expectations. + - + +Converters +---------- + +The role of this sub-module was to convert the operators into other opflow operator classes (:class:`~qiskit.opflow.TwoQubitReduction`, :class:`~qiskit.opflow.PauliBasisChange`...). +In the case of the :class:`~qiskit.opflow.CircuitSampler`, it traversed an operator and outputted approximations of its state functions using a quantum backend. +Notably, this functionality has been replaced by the :mod:`~qiskit.primitives`. + +Circuit Sampler +~~~~~~~~~~~~~~~ + +.. list-table:: Migration of ``qiskit.opflow.CircuitSampler`` + :header-rows: 1 + + * - opflow + - alternative + - notes + + * - :class:`~qiskit.opflow.CircuitSampler` + - :class:`~primitives.Estimator` + - + + * - + + .. code-block:: python + + from qiskit import QuantumCircuit + from qiskit.opflow import X, Z, StateFn, CircuitStateFn, CircuitSampler + from qiskit.providers.aer import AerSimulator + + qc = QuantumCircuit(1) + qc.h(0) + state = CircuitStateFn(qc) + hamiltonian = X + Z + + expr = StateFn(hamiltonian, is_measurement=True).compose(state) + backend = AerSimulator() + sampler = CircuitSampler(backend) + expectation = sampler.convert(expr) + expectation_value = expectation.eval().real + + - + + .. code-block:: python + + from qiskit import QuantumCircuit + from qiskit.primitives import Estimator + from qiskit.quantum_info import SparsePauliOp + + state = QuantumCircuit(1) + state.h(0) + hamiltonian = SparsePauliOp.from_list([('X', 1), ('Z',1)]) + + estimator = Estimator() + expectation_value = estimator.run(state, hamiltonian).result().values + + - + +Two Qubit Reduction +~~~~~~~~~~~~~~~~~~~~ +.. list-table:: Migration of ``qiskit.opflow.TwoQubitReduction`` + :header-rows: 1 + + * - opflow + - alternative + - notes + + * - :class:`~qiskit.opflow.TwoQubitReduction` + - This class used to implement a chemistry-specific reduction. It has been directly integrated in to the parity mapper class in ``qiskit-nature`` and has no replacement in ``qiskit``. + - + +Other Converters +~~~~~~~~~~~~~~~~~ + +.. list-table:: Migration of ``qiskit.opflow.converters`` + :header-rows: 1 + + * - opflow + - alternative + - notes + + * - :class:`~qiskit.opflow.AbelianGrouper` + - No replacement needed. This class was used internally within opflow. + - + * - :class:`~qiskit.opflow.DictToCircuitSum` + - No replacement needed. This class was used internally within opflow. + - + * - :class:`~qiskit.opflow.PauliBasisChange` + - No replacement needed. This class was used internally within opflow. + - + +Evolutions +---------- + +The :mod:`~qiskit.opflow.evolutions` sub-module was created to provide building blocks for hamiltonian simulation algorithms, +including various methods for trotterization. The original opflow workflow for hamiltonian simulation did not allow for +delayed synthesis of the gates or efficient transpilation of the circuits, so this functionality was migrated to the +:mod:`~qiskit.synthesis.evolution` module. + +The :class:`~qiskit.opflow.PauliTrotterEvolution` class computes evolutions for exponentiated sums of Paulis by changing them each to the +Z basis, rotating with an RZ, changing back, and trotterizing following the desired scheme. Within its ``.convert`` method, +the class follows a recursive strategy that involves creating :class:`~qiskit.opflow.EvolvedOp` placeholders for the operators, +constructing :class:`~PauliEvolutionGate`\s out of the operator primitives and supplying one of the desired synthesis methods to +perform the trotterization (either via a ``string``\, which is then inputted into a :class:`~qiskit.opflow.TrotterizationFactory`, +or by supplying a method instance of :class:`~qiskit.opflow.Trotter`, :class:`~qiskit.opflow.Suzuki` or :class:`~qiskit.opflow.QDrift`). + +The different trotterization methods that extend :class:`~qiskit.opflow.TrotterizationBase` were migrated to :mod:`~qiskit.synthesis`, +and now extend the :class:`~qiskit.synthesis.evolution.ProductFormula` base class. They no longer contain a ``.convert()`` method for +standalone use, but now are designed to be plugged into the :class:`~qiskit.synthesis.PauliEvolutionGate` and called via ``.synthesize()``. +In this context, the job of the :class:`~qiskit.opflow.PauliTrotterEvolution` class can now be handled directly by the algorithms +(for example, :class:`~qiskit.algorithms.time_evolvers.TrotterQRTE`\), as shown in the following example: + +.. list-table:: Migration of ``qiskit.opflow.evolutions (1/2)`` + :header-rows: 1 + + * - opflow + - alternative + + * - + + .. code-block:: python + + from qiskit.opflow import Trotter, PauliTrotterEvolution, PauliSumOp + + hamiltonian = PauliSumOp.from_list([('X', 1), ('Z',1)]) + evolution = PauliTrotterEvolution(trotter_mode=Trotter(), reps=1) + evol_result = evolution.convert(hamiltonian.exp_i()) + evolved_state = evol_result.to_circuit() + - + + .. code-block:: python + + from qiskit.quantum_info import SparsePauliOp + from qiskit.synthesis import SuzukiTrotter + from qiskit.circuit.library import PauliEvolutionGate + from qiskit import QuantumCircuit + + hamiltonian = SparsePauliOp.from_list([('X', 1), ('Z',1)]) + evol_gate = PauliEvolutionGate(hamiltonian, 1, synthesis=SuzukiTrotter()) + evolved_state = QuantumCircuit(1) + evolved_state.append(evol_gate, [0]) + +In a similar manner, the :class:`~qiskit.opflow.MatrixEvolution` class performs evolution by classical matrix exponentiation, +constructing a circuit with :class:`~UnitaryGate`\s or :class:`~HamiltonianGate`\s containing the exponentiation of the operator. +This class is no longer necessary, as the :class:`~HamiltonianGate`\s can be directly handled by the algorithms. + +.. list-table:: Migration of ``qiskit.opflow.evolutions (2/2)`` + :header-rows: 1 + + * - opflow + - alternative + + * - + + .. code-block:: python + + from qiskit.opflow import MatrixEvolution, MatrixOp + + hamiltonian = MatrixOp([[0, 1], [1, 0]]) + evolution = MatrixEvolution() + evol_result = evolution.convert(hamiltonian.exp_i()) + evolved_state = evol_result.to_circuit() + - + + .. code-block:: python + + from qiskit.quantum_info import SparsePauliOp + from qiskit.extensions import HamiltonianGate + from qiskit import QuantumCircuit + + evol_gate = HamiltonianGate([[0, 1], [1, 0]], 1) + evolved_state = QuantumCircuit(1) + evolved_state.append(evol_gate, [0]) + +To summarize: + +.. list-table:: Migration of ``qiskit.opflow.evolutions.trotterizations`` + :header-rows: 1 + + * - opflow + - alternative + - notes + + * - :class:`~qiskit.opflow.TrotterizationFactory` + - This class is no longer necessary. + - + * - :class:`~qiskit.opflow.Trotter` + - :class:`~synthesis.SuzukiTrotter` or :class:`~synthesis.LieTrotter` + - + * - :class:`~qiskit.opflow.Suzuki` + - `:class:`~synthesis.SuzukiTrotter` + - + * - :class:`~qiskit.opflow.QDrift` + - :class:`~synthesis.QDrift` + - + +.. list-table:: Migration of ``qiskit.opflow.evolutions.evolutions`` + :header-rows: 1 + + * - opflow + - alternative + - notes + + * - :class:`~qiskit.opflow.EvolutionFactory` + - This class is no longer necessary. + - + * - :class:`~qiskit.opflow.EvolvedOp` + - :class:`~synthesis.SuzukiTrotter` + - This class is no longer necessary + * - :class:`~qiskit.opflow.MatrixEvolution` + - :class:`~HamiltonianGate` + - + * - :class:`~qiskit.opflow.PauliTrotterEvolution` + - :class:`~PauliEvolutionGate` + - + +Expectations +------------ +Expectations are converters which enable the computation of the expectation value of an observable with respect to some state function. +This functionality can now be found in the estimator primitive. + +Algorithm-Agnostic Expectations +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. list-table:: Migration of ``qiskit.opflow.expectations`` + :header-rows: 1 + + * - opflow + - alternative + - notes + * - :class:`~qiskit.opflow.ExpectationFactory` + - No replacement needed. + - + * - :class:`~qiskit.opflow.AerPauliExpectation` + - Use :class:`~Estimator` primitive from ``qiskit_aer`` instead. + - + * - :class:`~qiskit.opflow.MatrixExpectation` + - Use :class:`~Estimator` primitive from ``qiskit`` instead (uses Statevector). + - + * - :class:`~qiskit.opflow.PauliExpectation` + - Use any :class:`~Estimator` primitive. + - + +TODO: ADD EXAMPLE! + +CVarExpectation +~~~~~~~~~~~~~~~ + +.. list-table:: Migration of ``qiskit.opflow.expectations.CVaRExpectation`` + :header-rows: 1 + + * - opflow + - alternative + - notes + + * - :class:`~qiskit.opflow.expectations.CVaRExpectation` + - Functionality absorbed into corresponding VQE algorithm: :class:`~qiskit.algorithms.minimum_eigensolvers.SamplingVQE` + - + * - + + .. code-block:: python + + from qiskit.opflow import CVaRExpectation, PauliSumOp + + from qiskit.algorithms import VQE + from qiskit.algorithms.optimizers import SLSQP + from qiskit.circuit.library import TwoLocal + from qiskit_aer import AerSimulator + backend = AerSimulator() + ansatz = TwoLocal(2, 'ry', 'cz') + op = PauliSumOp.from_list([('ZZ',1), ('IZ',1), ('II',1)]) + alpha=0.2 + cvar_expectation = CVaRExpectation(alpha=alpha) + opt = SLSQP(maxiter=1000) + vqe = VQE(ansatz, expectation=cvar_expectation, optimizer=opt, quantum_instance=backend) + result = vqe.compute_minimum_eigenvalue(op) + + - + + .. code-block:: python + + from qiskit.quantum_info import SparsePauliOp + + from qiskit.algorithms.minimum_eigensolvers import SamplingVQE + from qiskit.algorithms.optimizers import SLSQP + from qiskit.circuit.library import TwoLocal + from qiskit.primitives import Sampler + ansatz = TwoLocal(2, 'ry', 'cz') + op = SparsePauliOp.from_list([('ZZ',1), ('IZ',1), ('II',1)]) + opt = SLSQP(maxiter=1000) + alpha=0.2 + vqe = SamplingVQE(Sampler(), ansatz, optm, aggregation=alpha) + result = vqe.compute_minimum_eigenvalue(op) + - + +**Gradients** +------------- +Replaced by new gradients module (link) (link to new tutorial). + From d728473789a6848343d64f502ade0fccd65f5272 Mon Sep 17 00:00:00 2001 From: ElePT Date: Wed, 8 Feb 2023 13:14:16 +0100 Subject: [PATCH 02/18] Remove qiskit --- docs/migration_guides/index.rst | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/migration_guides/index.rst b/docs/migration_guides/index.rst index 0ae8193ca5bc..38cea61c2a75 100644 --- a/docs/migration_guides/index.rst +++ b/docs/migration_guides/index.rst @@ -1,5 +1,3 @@ -.. module:: qiskit - ======================= Qiskit Migration Guides ======================= From 311d9bcd9d0acf9659f664e8767e720890ef1e71 Mon Sep 17 00:00:00 2001 From: ElePT Date: Wed, 8 Feb 2023 17:25:49 +0100 Subject: [PATCH 03/18] Apply review --- docs/migration_guides/opflow_migration.rst | 48 +++++++++++++++++----- 1 file changed, 38 insertions(+), 10 deletions(-) diff --git a/docs/migration_guides/opflow_migration.rst b/docs/migration_guides/opflow_migration.rst index efc0564b9895..5e21c53d0037 100644 --- a/docs/migration_guides/opflow_migration.rst +++ b/docs/migration_guides/opflow_migration.rst @@ -17,7 +17,7 @@ observables into operator classes that could be algebraically manipulated, so th values could be easily computed following different methods. This basic opflow functionality is covered by its core submodules: the ``operators`` submodule -(including :mod:`~qiskit.opflow.operator_globals`, :mod:`~qiskit.opflow.list_ops`, :mod:`~qiskit.opflow.primitive_ops`, and :mod:`~qiskit.opflow.state_fns`), +(including :mod:`~qiskit.opflow.operator_globals`, :mod:`~qiskit.opflow.list_ops`, :mod:`~qiskit.opflow.primitive_ops`, and :mod:`~qiskit.opflow.state_fns`), the :mod:`~qiskit.opflow.converters` submodule, and the :mod:`~qiskit.opflow.expectations` submodule. Following this reference framework of ``operators``, :mod:`~qiskit.opflow.converters` and :mod:`~qiskit.opflow.expectations`, opflow includes more algorithm-specific functionality, which can be found in the :mod:`~qiskit.opflow.evolutions` submodule (specific for hamiltonian @@ -134,8 +134,8 @@ Opflow provided shortcuts to define common single qubit states, operators, and c .. code-block:: python from qiskit.quantum_info import Pauli, SparsePauliOp - op = Pauli('X') ^ Pauli('X') - + op = Pauli('X') ^ Pauli('X') + # equivalent to: op = SparsePauliOp('XX') @@ -188,8 +188,8 @@ Common non-parametrized gates (Clifford) - notes * - :class:`~qiskit.opflow.Zero`, :class:`~qiskit.opflow.One`, :class:`~qiskit.opflow.Plus`, :class:`~qiskit.opflow.Minus` - - :class:`~qiskit.quantum_info.Statevector` or :class:`~qiskit.QuantumCircuit` directly - - + - :class:`~qiskit.quantum_info.StabilizerState` or :class:`~qiskit.quantum_info.Statevector` or :class:`~qiskit.QuantumCircuit`, depending on the use case + - In principle, :class:`~qiskit.quantum_info.StabilizerState` is the most efficient replacement for :class:`~qiskit.opflow` states, but the functionality is not identical. See API ref. for more info. * - @@ -205,18 +205,18 @@ Common non-parametrized gates (Clifford) .. code-block:: python from qiskit import QuantumCircuit - from qiskit.quantum_info import Statevector + from qiskit.quantum_info import StabilizerState qc_zero = QuantumCircuit(1) qc_one = copy(qc_zero) qc_one.x(0) - state1 = Statevector(qc_zero) ^ Statevector(qc_one) + state1 = StabilizerState(qc_zero) ^ StabilizerState(qc_one) qc_plus = copy(qc_zero) qc_plus.h(0) qc_minus = copy(qc_one) qc_minus.h(0) - state2 = Statevector(qc_plus) ^ Statevector(qc_minus) + state2 = StabilizerState(qc_plus) ^ StabilizerState(qc_minus) - @@ -360,8 +360,6 @@ This module can be generally replaced by :class:`~qiskit.quantum_info.QuantumSta 2. The equivalence is, once again, not 1-1. 3. Algorithm-specific functionality has been migrated to the respective algorithm's module -TODO: ADD EXAMPLE! - .. list-table:: Migration of ``qiskit.opflow.state_fns`` :header-rows: 1 @@ -396,6 +394,36 @@ TODO: ADD EXAMPLE! - Used in :class:`~qiskit.opflow.CVaRExpectation`. Functionality now covered by :class:`~SamplingEstimator`. See example in expectations. - +StateFn Examples +~~~~~~~~~~~~~~~~~ + +.. list-table:: + :header-rows: 1 + + * - opflow + - alternative + - notes + + * - ``StateFn`` **Example:** + + .. code-block:: python + + from qiskit.opflow import PuliSumOp + from qiskit.quantum_info import SparsePauliOp, Pauli + + qubit_op = PauliSumOp(SparsePauliOp(Pauli("XYZY"), coeffs=[2]), coeff=-3j) + + - + + .. code-block:: python + + from qiskit.quantum_info import SparsePauliOp, Pauli + + qubit_op = SparsePauliOp(Pauli("XYZY")), coeff=-6j) + + - + + Converters ---------- From 7579e61c196ec4bdfde4265be9efdc85dd2159bb Mon Sep 17 00:00:00 2001 From: ElePT Date: Wed, 8 Feb 2023 17:28:02 +0100 Subject: [PATCH 04/18] Add algos guide --- docs/migration_guides/algorithms_migration.rst | 12 ++++++++++++ docs/migration_guides/index.rst | 1 + 2 files changed, 13 insertions(+) create mode 100644 docs/migration_guides/algorithms_migration.rst diff --git a/docs/migration_guides/algorithms_migration.rst b/docs/migration_guides/algorithms_migration.rst new file mode 100644 index 000000000000..91c7d74b4e09 --- /dev/null +++ b/docs/migration_guides/algorithms_migration.rst @@ -0,0 +1,12 @@ +========================== +Algorithms Migration Guide +========================== + +*Jump to* `TL;DR`_. + +Background +---------- + + +TL;DR +----- diff --git a/docs/migration_guides/index.rst b/docs/migration_guides/index.rst index 38cea61c2a75..161bd6d5bd0a 100644 --- a/docs/migration_guides/index.rst +++ b/docs/migration_guides/index.rst @@ -6,3 +6,4 @@ Qiskit Migration Guides :maxdepth: 1 opflow_migration + algorithms_migration From ca899efc439ffc8b56895a64135a074bda37cb51 Mon Sep 17 00:00:00 2001 From: ElePT Date: Fri, 10 Feb 2023 10:24:42 +0100 Subject: [PATCH 05/18] Add skeleton --- .../migration_guides/algorithms_migration.rst | 123 ++++++++++++++++++ 1 file changed, 123 insertions(+) diff --git a/docs/migration_guides/algorithms_migration.rst b/docs/migration_guides/algorithms_migration.rst index 91c7d74b4e09..321b5d30f2a7 100644 --- a/docs/migration_guides/algorithms_migration.rst +++ b/docs/migration_guides/algorithms_migration.rst @@ -6,7 +6,130 @@ Algorithms Migration Guide Background ---------- +Before: algorithms based on QuantumInstance +Now: algorithms based on Primitives +How to choose backends now: + +1. Select algorithmic abstraction: sampler/estimator +2. Select backend --> where to import sampler/estimator from. How to define settings for each primitive. + +Link to primitives tutorial for further info. TL;DR ----- + +Algorithms have been refactored to use the primitives instead of QI. + +3 types of refactoring + +1. New algos in new place (old algos in old place will be deprecated). Careful with import paths!! + These must imported directly from the new folders, since names conflict with existing ones that + are still importable from qiskit.algorithms + + - `Minimum Eigensolvers`_ + - `Eigensolvers`_ + - `Time Evolvers`_ + +2. Algos refactored in-place to support both QI and primitives (use of QI will be deprecated). + + - `Amplitude Amplifiers`_ + - `Amplitude Estimators`_ + - `Phase Estimators`_ + +3. Algos deprecated entirely in this repo --> Having an encapsulated algorithm class is not very useful. + These are kept as educational material for the textbook. No code examples in the guide. + + - `Linear Solvers`_ (HHL) --> Add Link + - `Factorizers`_ (Shor) --> Add Link + + +Minimum Eigensolvers +-------------------- + +``VQE`` (:class:`~qiskit.algorithms.minimum_eigensolvers.VQE`\) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +(!) IMPORT CHANGE: ``from qiskit.algorithms.minimum_eigensolvers import VQE`` (new) instead of ``from qiskit.algorithms import VQE`` (old) + +- 3 different common configs/uses (which apply to others like VQD) + +1. StateVector backend (ie. using opflow Matrix Expectation) -> Terra Estimator + +2. Qasm simulator/device (i.e. using PauliExpectation) -> AerEstimator, Run Estimator, or Terra Estimator with shots + +3. Aer simulator using custom instruction (ie. using AerPauliExpectation - include_custom for VQE using expectation factory) -> AerEstimator shots=None, approximation=True + +* Note: New result does not have state in it any more + + +``VQE`` + ``CVARExpectation`` -> (:class:`~qiskit.algorithms.minimum_eigensolvers.SamplingVQE`\) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +(!) NEW IMPORT: ``from qiskit.algorithms.minimum_eigensolvers import SamplingVQE`` (new) instead of ``from qiskit.algorithms import VQE`` (old) + +``QAOA`` -> (:class:`~qiskit.algorithms.minimum_eigensolvers.QAOA`\) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +(!) NEW IMPORT: ``from qiskit.algorithms.minimum_eigensolvers import QAOA`` (new) instead of ``from qiskit.algorithms import QAOA`` (old) + +This used to be based off VQE but new is based off SamplingVQE. +As such the Sampler selection now determines exact behavior or not. +The new one only supports diagonal operator. +If you want the old QAOA you can use QAOAAnsatz with VQE but bear in mind there is no state result - +if one needed counts one could use the optimal cct with a Sampler. + +``NumPyMinimumEigensolver`` -> (:class:`~qiskit.algorithms.minimum_eigensolvers.NumPyMinimumEigensolver`\) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +(!) NEW IMPORT + +Eigensolvers +------------ + +``VQD``-> (:class:`~qiskit.algorithms.eigensolvers.VQD`\) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +(!) NEW IMPORT + + +``NumPyEigensolver`` -> (:class:`~qiskit.algorithms.eigensolvers.NumPyEigensolver`\) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +(!) NEW IMPORT + +Time Evolvers +------------- + +``TrotterQRTE``-> (:class:`~qiskit.algorithms.time_evolvers.TrotterQRTE`\) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +(!) NEW IMPORT + +(this is the only evolvers algo that was shipped before the primitives change) +See the rest of the time evolvers here. + +Amplitude Amplifiers +--------------------- +Inplace Algos: The QI or Sampler is a common theme in these. But maybe we show at least one algo in each category + +Grover +~~~~~~ +Grover + QI -> Grover + Sampler + +Amplitude Estimators +-------------------- +Inplace Algos: The QI or Sampler is a common theme in these. But maybe we show at least one algo in each category + +(x)AE +~~~~~ +(x)AE + QI -> (x)AE + Sampler + +Since AE variants are similar in the old to new way maybe we only need to show one exmaple and state this fact + +Phase Estimators +---------------- +Inplace Algos: The QI or Sampler is a common theme in these. But maybe we show at least one algo in each category + +PhaseEstimation +~~~~~~~~~~~~~~~~ +PhaseEstimation + QI -> PhaseEstimation + Sampler + +Similar to AE + + From d59b8a1c0ca8df03605808e5a56d33a34abc62d8 Mon Sep 17 00:00:00 2001 From: ElePT Date: Thu, 23 Feb 2023 11:46:59 +0100 Subject: [PATCH 06/18] Add content --- .../migration_guides/algorithms_migration.rst | 797 ++++++++++++++++-- docs/migration_guides/index.rst | 1 - docs/migration_guides/opflow_migration.rst | 724 ---------------- 3 files changed, 726 insertions(+), 796 deletions(-) delete mode 100644 docs/migration_guides/opflow_migration.rst diff --git a/docs/migration_guides/algorithms_migration.rst b/docs/migration_guides/algorithms_migration.rst index 321b5d30f2a7..ae4c8356c3c2 100644 --- a/docs/migration_guides/algorithms_migration.rst +++ b/docs/migration_guides/algorithms_migration.rst @@ -2,134 +2,789 @@ Algorithms Migration Guide ========================== -*Jump to* `TL;DR`_. - -Background ----------- -Before: algorithms based on QuantumInstance -Now: algorithms based on Primitives - -How to choose backends now: - -1. Select algorithmic abstraction: sampler/estimator -2. Select backend --> where to import sampler/estimator from. How to define settings for each primitive. - -Link to primitives tutorial for further info. - TL;DR ----- -Algorithms have been refactored to use the primitives instead of QI. +The :mod:`qiskit.algorithms` module has been fully refactored to use the :mod:`~qiskit.primitives` instead of the +:class:`~qiskit.utils.QuantumInstance` for circuit execution. -3 types of refactoring +There have been **3 types of refactoring**: -1. New algos in new place (old algos in old place will be deprecated). Careful with import paths!! - These must imported directly from the new folders, since names conflict with existing ones that - are still importable from qiskit.algorithms +1. Algorithms refactored in a new location to support :mod:`~qiskit.primitives`. These algorithms have the same + class names as the :class:`~qiskit.utils.QuantumInstance`\-based ones but are in a new namespace. + **Careful with import paths!!** The old algorithms are still importable from :mod:`qiskit.algorithms` + until the legacy path is deprecated. - `Minimum Eigensolvers`_ - `Eigensolvers`_ - `Time Evolvers`_ -2. Algos refactored in-place to support both QI and primitives (use of QI will be deprecated). +2. Algorithms refactored in-place (same namespace) to support both :class:`~qiskit.utils.QuantumInstance` and + :mod:`~qiskit.primitives`. In the future, the use of :class:`~qiskit.utils.QuantumInstance` will be deprecated, + but the namespace will not change. - `Amplitude Amplifiers`_ - `Amplitude Estimators`_ - `Phase Estimators`_ -3. Algos deprecated entirely in this repo --> Having an encapsulated algorithm class is not very useful. - These are kept as educational material for the textbook. No code examples in the guide. - - `Linear Solvers`_ (HHL) --> Add Link - - `Factorizers`_ (Shor) --> Add Link +3. Algorithms deprecated entirely in :mod:`qiskit.algorithms`. These are algorithms that do not currently serve + as building blocks for applications. Their main value is educational, and as such, will be kept as tutorials + in the qiskit textbook. You can consult the tutorials in the following links: + + - `Linear Solvers (HHL) `_ + - `Factorizers (Shor) `_ + +Background +---------- +*Back to* `TL;DR`_ + +The :mod:`qiskit.algorithms` module was originally built on top of the :mod:`qiskit.opflow` library and the +:class:`~qiskit.utils.QuantumInstance` utility. The introduction of the :mod:`~qiskit.primitives` superseded +these two tools, which are now soon to be deprecated, and allowed to refactor the :mod:`qiskit.algorithms` module +to make all algorithms **primitive-based**. + +.. attention:: + + The transition away from :mod:`qiskit.opflow` affects the classes that algorithms take as part of the problem + setup. As a rule of thumb, most :mod:`qiskit.opflow` dependencies have a direct :mod:`qiskit.quantum_info` + replacement. One common example is the class :mod:`qiskit.opflow.PauliSumOp`, used to define hamiltonian + operators (for example, to plug into VQE), that can be replaced by :mod:`qiskit.quantum_info.SparsePauliOp`. + For information on how to migrate other :mod:`~qiskit.opflow` objects, you can refer to the + `Opflow migration guide `_. + +For further background and detailed migration steps, see the: + +* `Opflow migration guide `_ +* `Quantum Instance migration guide `_ + + +Choosing the Right Primitive for your Algorithm +----------------------------------------------- +*Back to* `TL;DR`_ + +There are 3 different common configurations for algorithms that determine **which primitive import** you should +be selecting: + +1. Running an algorithm with a statevector simulator + (ie. using :mod:`qiskit.opflow`\'s legacy :class:`.MatrixExpectation`): + + - Reference Primitives (see `QAOA`_ example): + + .. code-block:: python + + from qiskit.primitives import Sampler, Estimator + +2. Running an algorithm using a "qasm" simulator/device (i.e. using :mod:`qiskit.opflow`\'s legacy :class:`.PauliExpectation`): + + - Reference Primitives **with shots** (see `VQE`_ examples): + + .. code-block:: python + + 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) + + - Runtime Primitives (see `VQD`_ example): + + .. code-block:: python + + from qiskit_ibm_runtime import Sampler, Estimator + + - Aer Primitives (see `VQE`_ examples): + + .. code-block:: python + + from qiskit_aer.primitives import Sampler, Estimator + + +3. Running an algorithm on an Aer simulator using custom instruction (ie. using :mod:`qiskit.opflow`\'s legacy +:class:`.AerPauliExpectation`): + + - Aer Primitives with ``shots=None``, ``approximation=True`` (see `TrotterQRTE`_ example): + + .. code-block:: python + + from qiskit_aer.primitives import Sampler, Estimator + + sampler = Sampler(run_options={"approximation": True, "shots": None}) + estimator = Estimator(run_options={"approximation": True, "shots": None}) + + +.. note:: + + In general, in order to know which primitive to use instead of :class:`~qiskit.utils.QuantumInstance`, + you should ask yourself two questions: + + 1. What is the minimal unit of information used by your algorithm? + a. **Expectation value** - you will need an ``Estimator`` + b. **Probability distribution** (from sampling the device) - you will need a ``Sampler`` + + 2. How do you want to execute your circuits? + a. Using **local** statevector simulators for quick prototyping: **Reference Primitives** + b. Using **local** noisy simulations for finer algorithm tuning: **Aer Primitives** + c. Accessing **runtime-enabled backends** (or cloud simulators): **Runtime Primitives** + d. Accessing **non runtime-enabled backends** : **Backend Primitives** + + For more information and examples, see the `Quantum Instance migration guide `_. Minimum Eigensolvers -------------------- +*Back to* `TL;DR`_ + +Instead of a :class:`~qiskit.utils.QuantumInstance`, :mod:`qiskit.algorithms.minimum_eigensolvers` are now initialized +using an instance of the :mod:`~qiskit.primitives.Sampler` or :mod:`~qiskit.primitives.Estimator` primitive, depending +on the algorithm. + +.. attention:: + + For the :mod:`qiskit.algorithms.minimum_eigensolvers` classes, depending on the import path, + you will access either the primitive-based or the quantum-instance-based + implementation. You have to be extra-careful, because the class name does not change. + + * Old import path (Quantum Instance): ``from qiskit.algorithms import VQE, QAOA, NumPyMinimumEigensolver`` + * New import path (Primitives): ``from qiskit.algorithms.minimum_eigensolvers import VQE, SamplingVQE, QAOA, NumPyMinimumEigensolver`` + +VQE +~~~ + +The legacy :class:`qiskit.algorithms.minimum_eigen_solvers.VQE` class has now been split according to the use-case: + +- For general-purpose hamiltonians, you can use the Estimator-based :class:`qiskit.algorithms.minimum_eigensolvers.VQE` + class. +- If you have a diagonal hamiltonian, and would like the algorithm to return a sampling of the state, you can use + the new Sampler-based :class:`qiskit.algorithms.minimum_eigensolvers.SamplingVQE` algorithm. This could formerly + be realized using the legacy :class:`~qiskit.algorithms.minimum_eigen_solvers.VQE` with + :class:`~qiskit.opflow.expectations.CVaRExpectation`. + +.. note:: + + The new :class:`~qiskit.algorithms.minimum_eigensolvers.VQEResult` class does not include the state anymore, as + this output was only useful in the case of diagonal operators. However, if it is available as part of the new + :class:`~qiskit.algorithms.minimum_eigensolvers.SamplingVQE` :class:`~qiskit.algorithms.minimum_eigensolvers.SamplingVQEResult`. + +.. raw:: html + +
+ VQE Example +
+ +**[Legacy] Using Quantum Instance:** + +.. code-block:: python + + from qiskit.algorithms import VQE + from qiskit.algorithms.optimizers import SLSQP + from qiskit.circuit.library import TwoLocal + from qiskit.opflow import PauliSumOp + from qiskit_aer import AerSimulator + + ansatz = TwoLocal(2, 'ry', 'cz') + opt = SLSQP(maxiter=1000) + + # shot-based simulation + backend = AerSimulator() + qi = QuantumInstance(backend=backend, shots=2048) + vqe = VQE(ansatz, optimizer=opt, quantum_instance=qi) + + hamiltonian = PauliSumOp.from_list([("XX", 1), ("XY", 1)] + result = vqe.compute_minimum_eigenvalue(hamiltonian) + +**[Updated] Using Primitives:** + +.. code-block:: python + + # new import!!! + from qiskit.algorithms.minimum_eigensolvers import VQE + from qiskit.algorithms.optimizers import SLSQP + 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 = SLSQP(maxiter=1000) + + # shot-based simulation + estimator = Estimator(options={"shots": 2048}) + vqe = VQE(estimator, ansatz, opt) + + # another option + aer_estimator = AerEstimator(run_options={"shots": 2048}) + vqe = VQE(aer_estimator, ansatz, opt) + + hamiltonian = SparsePauliOp.from_list([("XX", 1), ("XY", 1)] + result = vqe.compute_minimum_eigenvalue(hamiltonian) + +.. raw:: html + +
+
+ +.. raw:: html + +
+ SamplingVQE Example +
+ +**[Legacy] Using Quantum Instance:** + +.. code-block:: python + + 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_aer import AerSimulator + + ansatz = TwoLocal(2, 'ry', 'cz') + opt = SLSQP(maxiter=1000) + + # shot-based simulation + backend = AerSimulator() + qi = QuantumInstance(backend=backend, shots=2048) + expectation = CVaRExpectation() + 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) + +**[Updated] Using Primitives:** + +.. code-block:: python + + # new import!!! + from qiskit.algorithms.minimum_eigensolvers import SamplingVQE + from qiskit.algorithms.optimizers import SLSQP + 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 = SLSQP(maxiter=1000) + + # shot-based simulation + sampler = Sampler(options={"shots": 2048}) + vqe = SamplingVQE(sampler, ansatz, opt) + + # another option + aer_sampler = AerSampler(run_options={"shots": 2048}) + vqe = VQE(aer_sampler, ansatz, opt) + + # diagonal hamiltonian + hamiltonian = SparsePauliOp.from_list([("XX", 1), ("XY", 1)] + result = vqe.compute_minimum_eigenvalue(hamiltonian) + +.. raw:: html + +
+
+ +For complete code examples, see the following updated tutorials: + +- `VQE Introduction `_ +- `VQE, Callback, Gradients, Initial Point `_ +- `VQE with Aer Primitives `_ + +QAOA +~~~~ + +The legacy :class:`qiskit.algorithms.minimum_eigen_solvers.QAOA` class used to extend +:class:`qiskit.algorithms.minimum_eigen_solvers.VQE`, but now, :class:`qiskit.algorithms.minimum_eigensolvers.QAOA` +extends :class:`qiskit.algorithms.minimum_eigensolvers.SamplingVQE`. +For this reason, **the new QAOA only supports diagonal operators**. + +.. note:: + + If you want to run QAOA on a non-diagonal operator, you can use the :class:`.QAOAAnsatz` with + :class:`qiskit.algorithms.minimum_eigensolvers.VQE`, but bear in mind there will be no state result. + If your application requires the final probability distribution, you can instantiate a ``Sampler`` + and run it with the optimal circuit after :class:`~qiskit.algorithms.minimum_eigensolvers.VQE`. + +.. raw:: html + +
+ QAOA Example +
+ +**[Legacy] Using Quantum Instance:** + +.. code-block:: python + + 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(method="statevector") + qi = QuantumInstance(backend=backend) + optimizer = COBYLA() + qaoa = QAOA(optimizer= optimizer, reps= 2, quantum_instance= qi) + + # diagonal operator + qubit_op = PauliSumOp.from_list([("IIIX", 1), ("IIXI", 1), ("IXII", 1), ("XIII", 1)]) + result = qaoa.compute_minimum_eigenvalue(qubit_op) + +**[Updated] Using Primitives:** + +.. code-block:: python + + from qiskit.algorithms.minimum_eigensolvers import QAOA + from qiskit.algorithms.optimizers import COBYLA + from qiskit.quantum_info import SparsePauliOp + from qiskit.primitives import Sampler + + # exact statevector simulation + sampler = Sampler() + optimizer = COBYLA() + qaoa = QAOA(sampler, optimizer, reps=2) + + # diagonal operator + qubit_op = SparsePauliOp.from_list([("IIIX", 1), ("IIXI", 1), ("IXII", 1), ("XIII", 1)]) + result = qaoa.compute_minimum_eigenvalue(qubit_op) + +.. raw:: html + +
+
-``VQE`` (:class:`~qiskit.algorithms.minimum_eigensolvers.VQE`\) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +For complete code examples, see the following updated tutorials: -(!) IMPORT CHANGE: ``from qiskit.algorithms.minimum_eigensolvers import VQE`` (new) instead of ``from qiskit.algorithms import VQE`` (old) +- `QAOA `_ -- 3 different common configs/uses (which apply to others like VQD) +NumPyMinimumEigensolver +~~~~~~~~~~~~~~~~~~~~~~~~~ +Because this is a classical solver, the workflow has not changed between the old and new implementation. +The import has however changed from :class:`qiskit.algorithms.minimum_eigen_solvers.NumPyMinimumEigensolver` +to :class:`qiskit.algorithms.minimum_eigensolvers.NumPyMinimumEigensolver` for consistency. -1. StateVector backend (ie. using opflow Matrix Expectation) -> Terra Estimator +.. raw:: html -2. Qasm simulator/device (i.e. using PauliExpectation) -> AerEstimator, Run Estimator, or Terra Estimator with shots +
+ NumPyMinimumEigensolver Example +
-3. Aer simulator using custom instruction (ie. using AerPauliExpectation - include_custom for VQE using expectation factory) -> AerEstimator shots=None, approximation=True +**[Legacy]:** -* Note: New result does not have state in it any more +.. code-block:: python + from qiskit.algorithms import NumpyMinimumEigensolver + from qiskit.algorithms.optimizers import SLSQP + from qiskit.opflow import PauliSumOp -``VQE`` + ``CVARExpectation`` -> (:class:`~qiskit.algorithms.minimum_eigensolvers.SamplingVQE`\) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + opt = SLSQP(maxiter=1000) + solver = NumpyMinimumEigensolver(optimizer=opt) -(!) NEW IMPORT: ``from qiskit.algorithms.minimum_eigensolvers import SamplingVQE`` (new) instead of ``from qiskit.algorithms import VQE`` (old) + hamiltonian = PauliSumOp.from_list([("XX", 1), ("XY", 1)] + result = solver.compute_minimum_eigenvalue(hamiltonian) -``QAOA`` -> (:class:`~qiskit.algorithms.minimum_eigensolvers.QAOA`\) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -(!) NEW IMPORT: ``from qiskit.algorithms.minimum_eigensolvers import QAOA`` (new) instead of ``from qiskit.algorithms import QAOA`` (old) +**[Updated]:** -This used to be based off VQE but new is based off SamplingVQE. -As such the Sampler selection now determines exact behavior or not. -The new one only supports diagonal operator. -If you want the old QAOA you can use QAOAAnsatz with VQE but bear in mind there is no state result - -if one needed counts one could use the optimal cct with a Sampler. +.. code-block:: python -``NumPyMinimumEigensolver`` -> (:class:`~qiskit.algorithms.minimum_eigensolvers.NumPyMinimumEigensolver`\) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -(!) NEW IMPORT + from qiskit.algorithms.minimum_eigensolvers import NumpyMinimumEigensolver + from qiskit.algorithms.optimizers import SLSQP + from qiskit.quantum_info import SparsePauliOp + + opt = SLSQP(maxiter=1000) + solver = NumpyMinimumEigensolver(optimizer=opt) + + hamiltonian = SparsePauliOp.from_list([("XX", 1), ("XY", 1)] + result = solver.compute_minimum_eigenvalue(hamiltonian) + +.. raw:: html + +
+
+ +For complete code examples, see the following updated tutorials: + +- `VQE, Callback, Gradients, Initial Point `_ Eigensolvers ------------ +*Back to* `TL;DR`_ + +Instead of a :class:`~qiskit.utils.QuantumInstance`, :mod:`qiskit.algorithms.eigensolvers` are now initialized +using an instance of the :class:`~qiskit.primitives.Sampler` or :class:`~qiskit.primitives.Estimator` primitive, or +**a primitive-based subroutine**, depending on the algorithm. + +.. attention:: + + For the :mod:`qiskit.algorithms.eigensolvers` classes, depending on the import path, + you will access either the primitive-based or the quantum-instance-based + implementation. You have to be extra-careful, because the class name does not change. + + * Old import path (Quantum Instance): ``from qiskit.algorithms import VQD, NumPyEigensolver`` + * New import path (Primitives): ``from qiskit.algorithms.eigensolvers import VQD, NumPyEigensolver`` + +VQD +~~~~ + +The new :class:`qiskit.algorithms.eigensolvers.VQD` class is initialized with an :class:`~qiskit.primitives.Estimator` +primitive, as well as a :class:`~qiskit.primitives.Sampler`\-based fidelity class +from :mod:`qiskit.algorithms.state_fidelities`. + +.. note:: + + Similarly to VQE, the new :class:`~qiskit.algorithms.eigensolvers.VQDResult` class does not include + the state anymore. If your application requires the final probability distribution, you can instantiate + a ``Sampler`` and run it with the optimal circuit after :class:`~qiskit.algorithms.eigensolvers.VQD`. + +.. raw:: html + +
+ VQD Example +
+ +**[Legacy] Using Quantum Instance:** + +.. code-block:: python + + from qiskit_ibm_provider import IBMProvider + 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() + hamiltonian = PauliSumOp.from_list([("XXZ", 1), ("XYI", 1)] + + # example executing in cloud simulator + provider = IBMProvider() + backend = provider.get_backend("ibmq_qasm_simulator") + qi = QuantumInstance(backend=backend) + + vqd = VQD(ansatz, optimizer, k=3, quantum_instance=qi) + result = vqd.compute_eigenvalues(operator=hamiltonian) + +**[Updated] Using Primitives:** + +.. code-block:: python + + from qiskit_ibm_runtime import Sampler, Estimator, QiskitRuntimeService + 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() + hamiltonian = SparsePauliOp.from_list([("XXZ", 1), ("XYI", 1)] + + # example executing in cloud simulator + service = QiskitRuntimeService(channel="ibm_quantum") + backend = service.backend("ibmq_qasm_simulator") + with Session(service=service, 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) + -``VQD``-> (:class:`~qiskit.algorithms.eigensolvers.VQD`\) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -(!) NEW IMPORT +.. raw:: html +
+
+ +For complete code examples, see the following updated tutorials: + +- `VQD `_ + +NumPyEigensolver +~~~~~~~~~~~~~~~~~ +Similarly to its minimum eigensolver counterpart, because this is a classical solver, the workflow has not changed +between the old and new implementation. +The import has however changed from :class:`qiskit.algorithms.eigen_solvers.NumPyEigensolver` +to :class:`qiskit.algorithms.eigensolvers.MinimumEigensolver` for consistency. + +.. raw:: html + +
+ NumPyEigensolver Example +
+ +**[Legacy]:** + +.. code-block:: python + + from qiskit.algorithms import NumpyEigensolver + from qiskit.algorithms.optimizers import SLSQP + from qiskit.opflow import PauliSumOp + + opt = SLSQP(maxiter=1000) + solver = NumpyEigensolver(optimizer=opt, k=2) + + hamiltonian = PauliSumOp.from_list([("XX", 1), ("XY", 1)] + result = solver.compute_eigenvalues(hamiltonian) + +**[Updated]:** + +.. code-block:: python + + from qiskit.algorithms.eigensolvers import NumpyEigensolver + from qiskit.algorithms.optimizers import SLSQP + from qiskit.quantum_info import SparsePauliOp + + opt = SLSQP(maxiter=1000) + solver = NumpyEigensolver(optimizer=opt, k=2) + + hamiltonian = SparsePauliOp.from_list([("XX", 1), ("XY", 1)] + result = solver.compute_eigenvalues(hamiltonian) + +.. raw:: html + +
+
-``NumPyEigensolver`` -> (:class:`~qiskit.algorithms.eigensolvers.NumPyEigensolver`\) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -(!) NEW IMPORT Time Evolvers ------------- +*Back to* `TL;DR`_ + +Instead of a :class:`~qiskit.utils.QuantumInstance`, :mod:`qiskit.algorithms.time_evolvers` are now initialized +using an instance of the :class:`~qiskit.primitives.Sampler` or :class:`~qiskit.primitives.Estimator` primitive, +depending on the algorithm. + +On top of the migration, the module has been substantially expanded to include **Variational Quantum Time Evolution** +(:class:`~qiskit.algorithms.time_evolvers.VarQTE`\) solvers. + +TrotterQRTE +~~~~~~~~~~~~ +.. attention:: + + For the :class:`qiskit.algorithms.time_evolvers.TrotterQRTE` class, depending on the import path, + you will access either the primitive-based or the quantum-instance-based + implementation. You have to be extra-careful, because the class name does not change. + + * Old import path (Quantum Instance): ``from qiskit.algorithms import TrotterQRTE`` + * New import path (Primitives): ``from qiskit.algorithms.time_evolvers import TrotterQRTE`` + +.. raw:: html + +
+ TrotterQRTE Example +
+ +**[Legacy] Using Quantum Instance:** + +.. code-block:: python + + 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 -``TrotterQRTE``-> (:class:`~qiskit.algorithms.time_evolvers.TrotterQRTE`\) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -(!) NEW IMPORT + 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 + +**[Updated] Using Primitives:** + +.. code-block:: python + + # note new import!!! + from qiskit.algorithms.time_evolvers import EvolutionProblem, TrotterQRTE + 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 = EvolutionProblem(operator, 1, initial_state) + + # Aer simulator using custom instruction + estimator = AerEstimator(run_options={"approximation": True, "shots": None}) + + # LieTrotter with 1 rep + trotter_qrte = TrotterQRTE(expectation=expectation, quantum_instance=quantum_instance) + evolved_state = trotter_qrte.evolve(evolution_problem).evolved_state + +.. raw:: html + +
+
-(this is the only evolvers algo that was shipped before the primitives change) -See the rest of the time evolvers here. Amplitude Amplifiers --------------------- -Inplace Algos: The QI or Sampler is a common theme in these. But maybe we show at least one algo in each category +*Back to* `TL;DR`_ + +Instead of a :class:`~qiskit.utils.QuantumInstance`, :mod:`qiskit.algorithms.amplitude_amplifiers` are now initialized +using any instance of the :mod:`~qiskit.primitives.Sampler` primitive. + +.. note:: + The full :mod:`qiskit.algorithms.amplitude_amplifiers` module has been refactored in place. No need to + change import paths. + +.. raw:: html + +
+ Grover Example +
-Grover -~~~~~~ -Grover + QI -> Grover + Sampler +**[Legacy] Using Quantum Instance:** + +.. code-block:: python + + from qiskit.algorithms import Grover + from qiskit.utils import QuantumInstance + + qi = QuantumInstance(backend=backend) + grover = Grover(quantum_instance=qi) + + +**[Updated] Using Primitives:** + +.. code-block:: python + + from qiskit.algorithms import Grover + from qiskit.primitives import Sampler + + grover = Grover(sampler=Sampler()) + +.. raw:: html + +
+
+ +For complete code examples, see the following updated tutorials: + +- `Amplitude Amplification and Grover `_ +- `Grover Examples `_ Amplitude Estimators -------------------- -Inplace Algos: The QI or Sampler is a common theme in these. But maybe we show at least one algo in each category +*Back to* `TL;DR`_ + +Instead of a :class:`~qiskit.utils.QuantumInstance`, :mod:`qiskit.algorithms.amplitude_estimators` are now initialized +using any instance of the :mod:`qiskit.primitives.Sampler` primitive. -(x)AE -~~~~~ -(x)AE + QI -> (x)AE + Sampler +.. note:: + The full :mod:`qiskit.algorithms.amplitude_estimators` module has been refactored in place. No need to + change import paths. -Since AE variants are similar in the old to new way maybe we only need to show one exmaple and state this fact +.. raw:: html + +
+ IAE Example +
+ + +**[Legacy] Using Quantum Instance:** + +.. code-block:: python + + 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:** + +.. code-block:: python + + 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(), + ) + +.. raw:: html + +
+
+ +For complete code examples, see the following updated tutorials: + +- `Amplitude Estimation `_ Phase Estimators ---------------- -Inplace Algos: The QI or Sampler is a common theme in these. But maybe we show at least one algo in each category +*Back to* `TL;DR`_ + +Instead of a :class:`~qiskit.utils.QuantumInstance`, :mod:`qiskit.algorithms.phase_estimators` are now initialized +using any instance of the :mod:`qiskit.primitives.Sampler` primitive. + +.. note:: + The full :mod:`qiskit.algorithms.phase_estimators` module has been refactored in place. No need to + change import paths. + +.. raw:: html + +
+ IQPE Example +
+ +**[Legacy] Using Quantum Instance:** + +.. code-block:: python + + 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:** + +.. code-block:: python + + 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(), + ) + +.. raw:: html -PhaseEstimation -~~~~~~~~~~~~~~~~ -PhaseEstimation + QI -> PhaseEstimation + Sampler +
+
-Similar to AE +For complete code examples, see the following updated tutorials: +- `Iterative Phase Estimation `_ diff --git a/docs/migration_guides/index.rst b/docs/migration_guides/index.rst index 161bd6d5bd0a..606f399a7ca7 100644 --- a/docs/migration_guides/index.rst +++ b/docs/migration_guides/index.rst @@ -5,5 +5,4 @@ Qiskit Migration Guides .. toctree:: :maxdepth: 1 - opflow_migration algorithms_migration diff --git a/docs/migration_guides/opflow_migration.rst b/docs/migration_guides/opflow_migration.rst deleted file mode 100644 index 5e21c53d0037..000000000000 --- a/docs/migration_guides/opflow_migration.rst +++ /dev/null @@ -1,724 +0,0 @@ -======================= -Opflow Migration Guide -======================= - -*Jump to* `TL;DR`_. - -Background ----------- - -The :mod:`~qiskit.opflow` module was originally introduced as a layer between circuits and algorithms, a series of building blocks -for quantum algorithms research and development. The core design of opflow was based on the assumption that the -point of access to backends (real devices or simulators) was a ``backend.run()`` -type of method: a method that takes in a circuit and returns its measurement results. -Under this assumption, all the tasks related to operator handling and building expectation value -computations were left to the user to manage. Opflow helped bridge that gap, it allowed to wrap circuits and -observables into operator classes that could be algebraically manipulated, so that the final result's expectation -values could be easily computed following different methods. - -This basic opflow functionality is covered by its core submodules: the ``operators`` submodule -(including :mod:`~qiskit.opflow.operator_globals`, :mod:`~qiskit.opflow.list_ops`, :mod:`~qiskit.opflow.primitive_ops`, and :mod:`~qiskit.opflow.state_fns`), -the :mod:`~qiskit.opflow.converters` submodule, and the :mod:`~qiskit.opflow.expectations` submodule. -Following this reference framework of ``operators``, :mod:`~qiskit.opflow.converters` and :mod:`~qiskit.opflow.expectations`, opflow includes more -algorithm-specific functionality, which can be found in the :mod:`~qiskit.opflow.evolutions` submodule (specific for hamiltonian -simulation algorithms), as well as the :mod:`~qiskit.opflow.gradients` submodule (applied in multiple machine learning and optimization -use-cases). Some classes from the core modules mentioned above are also algorithm or application-specific, -for example the :obj:`~CVarMeasurement` or the :obj:`~Z2Symmetries`. - -.. With the introduction of the primitives we have a new mechanism that allows.... efficient... error mitigation... - -The recent introduction of the :mod:`~qiskit.primitives` provided a new interface for interacting with backends. Now, instead of -preparing a circuit to execute with a ``backend.run()`` type of method, the algorithms can leverage the :class:`~Sampler` and -:class:`~Estimator` primitives, send parametrized circuits and observables, and directly receive quasi-probability distributions or -expectation values (respectively). This workflow simplifies considerably the pre-processing and post-processing steps -that previously relied on opflow. For example, the :class:`~Estimator` primitive returns expectation values from a series of -circuit-observable pairs, superseding most of the functionality of the :mod:`~qiskit.opflow.expectations` submodule. Without the need for -building opflow expectations, most of the components in ``operators`` also became redundant, as they commonly wrapped -elements from :mod:`~qiskit.quantum_info`. - -Higher-level opflow sub-modules, such as the :mod:`~qiskit.opflow.gradients` sub-module, were refactored to take full advantage -of the primitives interface. They can now be accessed as part of the :mod:`~qiskit.algorithms` module, -together with other primitive-based subroutines. Similarly, the :mod:`~qiskit.opflow.evolutions` sub-module got refactored, and now -can be easily integrated into a primitives-based workflow (as seen in the new :mod:`~qiskit.algorithms.time_evolvers` algorithms). - -All of these reasons have encouraged us to move away from opflow, and find new paths of developing algorithms based on -the :mod:`~qiskit.primitives` interface and the :mod:`~qiskit.quantum_info` module, which is a powerful tool for representing -and manipulating quantum operators. - -This guide traverses the opflow submodules and provides either a direct alternative -(i.e. using :mod:`~qiskit.quantum_info`), or an explanation of how to replace their functionality in algorithms. - -TL;DR ------ -The new :mod:`~qiskit.primitives` have superseded most of the :mod:`~qiskit.opflow` functionality. Thus, the latter is being deprecated. - -Index ------ -This guide covers the migration from these opflow sub-modules: - -**Operators** - -- `Operator Base Class`_ -- `Operator Globals`_ -- `Primitive and List Ops`_ -- `State Functions`_ - -**Converters** - -- `Converters`_ -- `Evolutions`_ -- `Expectations`_ - -**Gradients** - -- `Gradients`_ - - -Operator Base Class -------------------- - -The :class:`~opflow.OperatorBase` abstract class can generally be replaced with :class:`~quantum_info.BaseOperator`, keeping in -mind that :class:`~quantum_info.BaseOperator` is more generic than its opflow counterpart. In particular, you should consider that: - -1. :class:`~opflow.OperatorBase` implements a broader algebra mixin. Some operator overloads are not available in -:class:`~quantum_info.BaseOperator`. - -2. :class:`~opflow.OperatorBase` also implements methods such as ``.to_matrix()`` or ``.to_spmatrix()``, which are only found -in some of the :class:`~quantum_info.BaseOperator` subclasses. - -.. list-table:: Migration of ``qiskit.opflow.operator_base`` - :header-rows: 1 - - * - opflow - - alternative - - notes - * - :class:`~opflow.OperatorBase` - - :class:`~quantum_info.BaseOperator` - - For more information, check the :class:`~quantum_info.BaseOperator` source code. - -Operator Globals ----------------- -Opflow provided shortcuts to define common single qubit states, operators, and common non-parametrized gates in the -:mod:`~qiskit.opflow.operator_globals` module. These were mainly used for didactic purposes and can easily be replaced by their corresponding -:mod:`~qiskit.quantum_info` class: :class:`~qiskit.quantum_info.Pauli`, :class:`~qiskit.quantum_info.Clifford` or :class:`~qiskit.quantum_info.Statevector`. - -1-Qubit Paulis -~~~~~~~~~~~~~~ - -.. list-table:: Migration of ``qiskit.opflow.operator_globals`` (1/3) - :header-rows: 1 - - * - opflow - - alternative - - notes - * - :class:`~qiskit.opflow.X`, :class:`~qiskit.opflow.Y`, :class:`~qiskit.opflow.Z`, :class:`~qiskit.opflow.I` - - :class:`~qiskit.quantum_info.Pauli` - - For direct compatibility with classes in :mod:`~qiskit.algorithms`, wrap in :class:`~qiskit.quantum_info.SparsePauliOp`. - * - - - .. code-block:: python - - from qiskit.opflow import X - operator = X ^ X - - - - - .. code-block:: python - - from qiskit.quantum_info import Pauli - X = Pauli('X') - op = X ^ X - - - - - .. code-block:: python - - from qiskit.quantum_info import Pauli, SparsePauliOp - op = Pauli('X') ^ Pauli('X') - - # equivalent to: - op = SparsePauliOp('XX') - -Common non-parametrized gates (Clifford) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.. list-table:: Migration of ``qiskit.opflow.operator_globals`` (2/3) - :header-rows: 1 - - * - opflow - - alternative - - notes - - * - :class:`~qiskit.opflow.CX`, :class:`~qiskit.opflow.S`, :class:`~qiskit.opflow.H`, :class:`~qiskit.opflow.T`, :class:`~qiskit.opflow.CZ`, :class:`~qiskit.opflow.Swap` - - Append corresponding gate to :class:`~qiskit.QuantumCircuit` + :class:`~qiskit.quantum_info.Clifford` + ``.to_operator()`` - - - - * - - - .. code-block:: python - - from qiskit.opflow import H - op = H ^ H - - - - - .. code-block:: python - - from qiskit import QuantumCircuit - from qiskit.quantum_info import Clifford - qc = QuantumCircuit(2) - qc.h(0) - qc.h(1) - op = Clifford(qc).to_operator() - - # or... - qc = QuantumCircuit(1) - qc.h(0) - H = Clifford(qc).to_operator() - op = H ^ H - - - - -1-Qubit States -~~~~~~~~~~~~~~ -.. list-table:: Migration of ``qiskit.opflow.operator_globals`` (3/3) - :header-rows: 1 - - * - opflow - - alternative - - notes - - * - :class:`~qiskit.opflow.Zero`, :class:`~qiskit.opflow.One`, :class:`~qiskit.opflow.Plus`, :class:`~qiskit.opflow.Minus` - - :class:`~qiskit.quantum_info.StabilizerState` or :class:`~qiskit.quantum_info.Statevector` or :class:`~qiskit.QuantumCircuit`, depending on the use case - - In principle, :class:`~qiskit.quantum_info.StabilizerState` is the most efficient replacement for :class:`~qiskit.opflow` states, but the functionality is not identical. See API ref. for more info. - - * - - - .. code-block:: python - - from qiskit.opflow import Zero, One, Plus, Minus - - state1 = Zero ^ One - state2 = Plus ^ Minus - - - - - .. code-block:: python - - from qiskit import QuantumCircuit - from qiskit.quantum_info import StabilizerState - - qc_zero = QuantumCircuit(1) - qc_one = copy(qc_zero) - qc_one.x(0) - state1 = StabilizerState(qc_zero) ^ StabilizerState(qc_one) - - qc_plus = copy(qc_zero) - qc_plus.h(0) - qc_minus = copy(qc_one) - qc_minus.h(0) - state2 = StabilizerState(qc_plus) ^ StabilizerState(qc_minus) - - - - - -Primitive and List Ops ----------------------- -Most of the workflows that previously relied in components from :mod:`~qiskit.opflow.primitive_ops` and :mod:`~qiskit.opflow.list_ops` can now -leverage elements from :mod:`~qiskit.quantum_info.operators` instead. Some of these classes do not require a 1-1 replacement because -they were created to interface with other opflow components. - -PrimitiveOps -~~~~~~~~~~~~~~ - -.. list-table:: Migration of ``qiskit.opflow.primitive_ops`` - :header-rows: 1 - - * - opflow - - alternative - - notes - - * - :class:`~qiskit.opflow.PrimitiveOp` - - No replacement needed - - Can directly use :class:`~qiskit.quantum_info.Operator`` - * - :class:`~qiskit.opflow.CircuitOp` - - No replacement needed - - Can directly use :class:`~qiskit.QuantumCircuit` - * - :class:`~qiskit.opflow.MatrixOp` - - :class:`~qiskit.quantum_info.Operator`` - - - * - :class:`~qiskit.opflow.PauliOp` - - :class:`~qiskit.quantum_info.Pauli` - - For direct compatibility with classes in :mod:`~qiskit.algorithms`, wrap in :class:`~qiskit.quantum_info.SparsePauliOp` - * - :class:`~qiskit.opflow.PauliSumOp` - - :class:`~qiskit.quantum_info.SparsePauliOp` - - See example below - * - :class:`~qiskit.opflow.TaperedPauliSumOp` - - This class was used to combine a :class:`~PauliSumOp` with its identified symmetries in one object. It has been deprecated without replacement - - See ``Z2Symmetries`` example for updated workflow - * - :class:`~qiskit.opflow.Z2Symmetries` - - :class:`~qiskit.quantum_info.Z2Symmetries` - - See example below - - -PrimitiveOps Examples -~~~~~~~~~~~~~~~~~~~~~ -.. list-table:: - :header-rows: 1 - - * - opflow - - alternative - - notes - - * - ``PauliSumOp`` **Example:** - - .. code-block:: python - - from qiskit.opflow import PuliSumOp - from qiskit.quantum_info import SparsePauliOp, Pauli - - qubit_op = PauliSumOp(SparsePauliOp(Pauli("XYZY"), coeffs=[2]), coeff=-3j) - - - - - .. code-block:: python - - from qiskit.quantum_info import SparsePauliOp, Pauli - - qubit_op = SparsePauliOp(Pauli("XYZY")), coeff=-6j) - - - - * - ``Z2Symmetries`` **and** ``TaperedPauliSumOp`` **Example:** - - .. code-block:: python - - from qiskit.opflow import PuliSumOp, Z2Symmetries, TaperedPauliSumOp - - qubit_op = PauliSumOp.from_list( - [ - ("II", -1.0537076071291125), - ("IZ", 0.393983679438514), - ("ZI", -0.39398367943851387), - ("ZZ", -0.01123658523318205), - ("XX", 0.1812888082114961), - ] - ) - z2_symmetries = Z2Symmetries.find_Z2_symmetries(qubit_op) - tapered_op = z2_symmetries.taper(qubit_op) - # can be represented as: - tapered_op = TaperedPauliSumOp(primitive, z2_symmetries) - - - - .. code-block:: python - - from qiskit.quantum_info import SparsePauliOp, Z2Symmetries - - qubit_op = SparsePauliOp.from_list( - [ - ("II", -1.0537076071291125), - ("IZ", 0.393983679438514), - ("ZI", -0.39398367943851387), - ("ZZ", -0.01123658523318205), - ("XX", 0.1812888082114961), - ] - ) - z2_symmetries = Z2Symmetries.find_z2_symmetries(qubit_op) - tapered_op = z2_symmetries.taper(qubit_op) - - - - -ListOps -~~~~~~~ -.. list-table:: Migration of ``qiskit.opflow.list_ops`` - :header-rows: 1 - - * - opflow - - alternative - - notes - - * - :class:`~qiskit.opflow.ListOp` - - No replacement needed. This class was used internally within opflow. - - - - * - :class:`~qiskit.opflow.ComposedOp` - - No replacement needed. This class was used internally within opflow. - - - - * - :class:`~qiskit.opflow.SummedOp` - - No replacement needed. This class was used internally within opflow. - - - - * - :class:`~qiskit.opflow.TensoredOp` - - No replacement needed. This class was used internally within opflow. - - - -State Functions ---------------- - -This module can be generally replaced by :class:`~qiskit.quantum_info.QuantumState`, with some differences to keep in mind: - -1. The primitives-based workflow does not rely on constructing state functions as opflow did -2. The equivalence is, once again, not 1-1. -3. Algorithm-specific functionality has been migrated to the respective algorithm's module - -.. list-table:: Migration of ``qiskit.opflow.state_fns`` - :header-rows: 1 - - * - opflow - - alternative - - notes - - * - :class:`~qiskit.opflow.StateFn` - - No replacement needed. This class was used internally within opflow. - - - - * - :class:`~qiskit.opflow.CircuitStateFn` - - No replacement needed. This class was used internally within opflow. - - - - * - :class:`~qiskit.opflow.DictStateFn` - - No replacement needed. This class was used internally within opflow. - - - - * - :class:`~qiskit.opflow.VectorStateFn` - - This class was used internally within opflow, but there exists a :mod:`~qiskit.quantum_info` replacement. There's the :class:`~qiskit.quantum_info.Statevector` class and the :class:`~qiskit.quantum_info.StabilizerState` (Clifford based vector). - - - - * - :class:`~qiskit.opflow.SparseVectorStateFn` - - No replacement needed. This class was used internally within opflow. - - See :class:`~qiskit.opflow.VectorStateFn` - - * - :class:`~qiskit.opflow.OperatorStateFn` - - No replacement needed. This class was used internally within opflow. - - - * - :class:`~qiskit.opflow.CVaRMeasurement` - - Used in :class:`~qiskit.opflow.CVaRExpectation`. Functionality now covered by :class:`~SamplingEstimator`. See example in expectations. - - - -StateFn Examples -~~~~~~~~~~~~~~~~~ - -.. list-table:: - :header-rows: 1 - - * - opflow - - alternative - - notes - - * - ``StateFn`` **Example:** - - .. code-block:: python - - from qiskit.opflow import PuliSumOp - from qiskit.quantum_info import SparsePauliOp, Pauli - - qubit_op = PauliSumOp(SparsePauliOp(Pauli("XYZY"), coeffs=[2]), coeff=-3j) - - - - - .. code-block:: python - - from qiskit.quantum_info import SparsePauliOp, Pauli - - qubit_op = SparsePauliOp(Pauli("XYZY")), coeff=-6j) - - - - - -Converters ----------- - -The role of this sub-module was to convert the operators into other opflow operator classes (:class:`~qiskit.opflow.TwoQubitReduction`, :class:`~qiskit.opflow.PauliBasisChange`...). -In the case of the :class:`~qiskit.opflow.CircuitSampler`, it traversed an operator and outputted approximations of its state functions using a quantum backend. -Notably, this functionality has been replaced by the :mod:`~qiskit.primitives`. - -Circuit Sampler -~~~~~~~~~~~~~~~ - -.. list-table:: Migration of ``qiskit.opflow.CircuitSampler`` - :header-rows: 1 - - * - opflow - - alternative - - notes - - * - :class:`~qiskit.opflow.CircuitSampler` - - :class:`~primitives.Estimator` - - - - * - - - .. code-block:: python - - from qiskit import QuantumCircuit - from qiskit.opflow import X, Z, StateFn, CircuitStateFn, CircuitSampler - from qiskit.providers.aer import AerSimulator - - qc = QuantumCircuit(1) - qc.h(0) - state = CircuitStateFn(qc) - hamiltonian = X + Z - - expr = StateFn(hamiltonian, is_measurement=True).compose(state) - backend = AerSimulator() - sampler = CircuitSampler(backend) - expectation = sampler.convert(expr) - expectation_value = expectation.eval().real - - - - - .. code-block:: python - - from qiskit import QuantumCircuit - from qiskit.primitives import Estimator - from qiskit.quantum_info import SparsePauliOp - - state = QuantumCircuit(1) - state.h(0) - hamiltonian = SparsePauliOp.from_list([('X', 1), ('Z',1)]) - - estimator = Estimator() - expectation_value = estimator.run(state, hamiltonian).result().values - - - - -Two Qubit Reduction -~~~~~~~~~~~~~~~~~~~~ -.. list-table:: Migration of ``qiskit.opflow.TwoQubitReduction`` - :header-rows: 1 - - * - opflow - - alternative - - notes - - * - :class:`~qiskit.opflow.TwoQubitReduction` - - This class used to implement a chemistry-specific reduction. It has been directly integrated in to the parity mapper class in ``qiskit-nature`` and has no replacement in ``qiskit``. - - - -Other Converters -~~~~~~~~~~~~~~~~~ - -.. list-table:: Migration of ``qiskit.opflow.converters`` - :header-rows: 1 - - * - opflow - - alternative - - notes - - * - :class:`~qiskit.opflow.AbelianGrouper` - - No replacement needed. This class was used internally within opflow. - - - * - :class:`~qiskit.opflow.DictToCircuitSum` - - No replacement needed. This class was used internally within opflow. - - - * - :class:`~qiskit.opflow.PauliBasisChange` - - No replacement needed. This class was used internally within opflow. - - - -Evolutions ----------- - -The :mod:`~qiskit.opflow.evolutions` sub-module was created to provide building blocks for hamiltonian simulation algorithms, -including various methods for trotterization. The original opflow workflow for hamiltonian simulation did not allow for -delayed synthesis of the gates or efficient transpilation of the circuits, so this functionality was migrated to the -:mod:`~qiskit.synthesis.evolution` module. - -The :class:`~qiskit.opflow.PauliTrotterEvolution` class computes evolutions for exponentiated sums of Paulis by changing them each to the -Z basis, rotating with an RZ, changing back, and trotterizing following the desired scheme. Within its ``.convert`` method, -the class follows a recursive strategy that involves creating :class:`~qiskit.opflow.EvolvedOp` placeholders for the operators, -constructing :class:`~PauliEvolutionGate`\s out of the operator primitives and supplying one of the desired synthesis methods to -perform the trotterization (either via a ``string``\, which is then inputted into a :class:`~qiskit.opflow.TrotterizationFactory`, -or by supplying a method instance of :class:`~qiskit.opflow.Trotter`, :class:`~qiskit.opflow.Suzuki` or :class:`~qiskit.opflow.QDrift`). - -The different trotterization methods that extend :class:`~qiskit.opflow.TrotterizationBase` were migrated to :mod:`~qiskit.synthesis`, -and now extend the :class:`~qiskit.synthesis.evolution.ProductFormula` base class. They no longer contain a ``.convert()`` method for -standalone use, but now are designed to be plugged into the :class:`~qiskit.synthesis.PauliEvolutionGate` and called via ``.synthesize()``. -In this context, the job of the :class:`~qiskit.opflow.PauliTrotterEvolution` class can now be handled directly by the algorithms -(for example, :class:`~qiskit.algorithms.time_evolvers.TrotterQRTE`\), as shown in the following example: - -.. list-table:: Migration of ``qiskit.opflow.evolutions (1/2)`` - :header-rows: 1 - - * - opflow - - alternative - - * - - - .. code-block:: python - - from qiskit.opflow import Trotter, PauliTrotterEvolution, PauliSumOp - - hamiltonian = PauliSumOp.from_list([('X', 1), ('Z',1)]) - evolution = PauliTrotterEvolution(trotter_mode=Trotter(), reps=1) - evol_result = evolution.convert(hamiltonian.exp_i()) - evolved_state = evol_result.to_circuit() - - - - .. code-block:: python - - from qiskit.quantum_info import SparsePauliOp - from qiskit.synthesis import SuzukiTrotter - from qiskit.circuit.library import PauliEvolutionGate - from qiskit import QuantumCircuit - - hamiltonian = SparsePauliOp.from_list([('X', 1), ('Z',1)]) - evol_gate = PauliEvolutionGate(hamiltonian, 1, synthesis=SuzukiTrotter()) - evolved_state = QuantumCircuit(1) - evolved_state.append(evol_gate, [0]) - -In a similar manner, the :class:`~qiskit.opflow.MatrixEvolution` class performs evolution by classical matrix exponentiation, -constructing a circuit with :class:`~UnitaryGate`\s or :class:`~HamiltonianGate`\s containing the exponentiation of the operator. -This class is no longer necessary, as the :class:`~HamiltonianGate`\s can be directly handled by the algorithms. - -.. list-table:: Migration of ``qiskit.opflow.evolutions (2/2)`` - :header-rows: 1 - - * - opflow - - alternative - - * - - - .. code-block:: python - - from qiskit.opflow import MatrixEvolution, MatrixOp - - hamiltonian = MatrixOp([[0, 1], [1, 0]]) - evolution = MatrixEvolution() - evol_result = evolution.convert(hamiltonian.exp_i()) - evolved_state = evol_result.to_circuit() - - - - .. code-block:: python - - from qiskit.quantum_info import SparsePauliOp - from qiskit.extensions import HamiltonianGate - from qiskit import QuantumCircuit - - evol_gate = HamiltonianGate([[0, 1], [1, 0]], 1) - evolved_state = QuantumCircuit(1) - evolved_state.append(evol_gate, [0]) - -To summarize: - -.. list-table:: Migration of ``qiskit.opflow.evolutions.trotterizations`` - :header-rows: 1 - - * - opflow - - alternative - - notes - - * - :class:`~qiskit.opflow.TrotterizationFactory` - - This class is no longer necessary. - - - * - :class:`~qiskit.opflow.Trotter` - - :class:`~synthesis.SuzukiTrotter` or :class:`~synthesis.LieTrotter` - - - * - :class:`~qiskit.opflow.Suzuki` - - `:class:`~synthesis.SuzukiTrotter` - - - * - :class:`~qiskit.opflow.QDrift` - - :class:`~synthesis.QDrift` - - - -.. list-table:: Migration of ``qiskit.opflow.evolutions.evolutions`` - :header-rows: 1 - - * - opflow - - alternative - - notes - - * - :class:`~qiskit.opflow.EvolutionFactory` - - This class is no longer necessary. - - - * - :class:`~qiskit.opflow.EvolvedOp` - - :class:`~synthesis.SuzukiTrotter` - - This class is no longer necessary - * - :class:`~qiskit.opflow.MatrixEvolution` - - :class:`~HamiltonianGate` - - - * - :class:`~qiskit.opflow.PauliTrotterEvolution` - - :class:`~PauliEvolutionGate` - - - -Expectations ------------- -Expectations are converters which enable the computation of the expectation value of an observable with respect to some state function. -This functionality can now be found in the estimator primitive. - -Algorithm-Agnostic Expectations -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. list-table:: Migration of ``qiskit.opflow.expectations`` - :header-rows: 1 - - * - opflow - - alternative - - notes - * - :class:`~qiskit.opflow.ExpectationFactory` - - No replacement needed. - - - * - :class:`~qiskit.opflow.AerPauliExpectation` - - Use :class:`~Estimator` primitive from ``qiskit_aer`` instead. - - - * - :class:`~qiskit.opflow.MatrixExpectation` - - Use :class:`~Estimator` primitive from ``qiskit`` instead (uses Statevector). - - - * - :class:`~qiskit.opflow.PauliExpectation` - - Use any :class:`~Estimator` primitive. - - - -TODO: ADD EXAMPLE! - -CVarExpectation -~~~~~~~~~~~~~~~ - -.. list-table:: Migration of ``qiskit.opflow.expectations.CVaRExpectation`` - :header-rows: 1 - - * - opflow - - alternative - - notes - - * - :class:`~qiskit.opflow.expectations.CVaRExpectation` - - Functionality absorbed into corresponding VQE algorithm: :class:`~qiskit.algorithms.minimum_eigensolvers.SamplingVQE` - - - * - - - .. code-block:: python - - from qiskit.opflow import CVaRExpectation, PauliSumOp - - from qiskit.algorithms import VQE - from qiskit.algorithms.optimizers import SLSQP - from qiskit.circuit.library import TwoLocal - from qiskit_aer import AerSimulator - backend = AerSimulator() - ansatz = TwoLocal(2, 'ry', 'cz') - op = PauliSumOp.from_list([('ZZ',1), ('IZ',1), ('II',1)]) - alpha=0.2 - cvar_expectation = CVaRExpectation(alpha=alpha) - opt = SLSQP(maxiter=1000) - vqe = VQE(ansatz, expectation=cvar_expectation, optimizer=opt, quantum_instance=backend) - result = vqe.compute_minimum_eigenvalue(op) - - - - - .. code-block:: python - - from qiskit.quantum_info import SparsePauliOp - - from qiskit.algorithms.minimum_eigensolvers import SamplingVQE - from qiskit.algorithms.optimizers import SLSQP - from qiskit.circuit.library import TwoLocal - from qiskit.primitives import Sampler - ansatz = TwoLocal(2, 'ry', 'cz') - op = SparsePauliOp.from_list([('ZZ',1), ('IZ',1), ('II',1)]) - opt = SLSQP(maxiter=1000) - alpha=0.2 - vqe = SamplingVQE(Sampler(), ansatz, optm, aggregation=alpha) - result = vqe.compute_minimum_eigenvalue(op) - - - -**Gradients** -------------- -Replaced by new gradients module (link) (link to new tutorial). - From 1fc46e9e228bf7f32ef5ccf45440a8130289ea6b Mon Sep 17 00:00:00 2001 From: ElePT Date: Thu, 23 Feb 2023 16:41:46 +0100 Subject: [PATCH 07/18] Fix sampling VQE example --- docs/migration_guides/algorithms_migration.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/migration_guides/algorithms_migration.rst b/docs/migration_guides/algorithms_migration.rst index ae4c8356c3c2..e71a9042acfa 100644 --- a/docs/migration_guides/algorithms_migration.rst +++ b/docs/migration_guides/algorithms_migration.rst @@ -251,7 +251,7 @@ The legacy :class:`qiskit.algorithms.minimum_eigen_solvers.VQE` class has now be # shot-based simulation backend = AerSimulator() qi = QuantumInstance(backend=backend, shots=2048) - expectation = CVaRExpectation() + expectation = CVaRExpectation(alpha=0.2) vqe = VQE(ansatz, optimizer=opt, expectation=expectation, quantum_instance=qi) # diagonal hamiltonian @@ -275,11 +275,11 @@ The legacy :class:`qiskit.algorithms.minimum_eigen_solvers.VQE` class has now be # shot-based simulation sampler = Sampler(options={"shots": 2048}) - vqe = SamplingVQE(sampler, ansatz, opt) + vqe = SamplingVQE(sampler, ansatz, opt, aggregation=0.2) # another option aer_sampler = AerSampler(run_options={"shots": 2048}) - vqe = VQE(aer_sampler, ansatz, opt) + vqe = SamplingVQE(aer_sampler, ansatz, opt, aggregation=0.2) # diagonal hamiltonian hamiltonian = SparsePauliOp.from_list([("XX", 1), ("XY", 1)] From f29d729c7ba794e50d0ee328a7b77c135d90bab7 Mon Sep 17 00:00:00 2001 From: ElePT <57907331+ElePT@users.noreply.github.com> Date: Mon, 13 Mar 2023 13:34:35 +0100 Subject: [PATCH 08/18] Apply suggestions from Steve's code review Co-authored-by: Steve Wood <40241007+woodsp-ibm@users.noreply.github.com> --- docs/migration_guides/algorithms_migration.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/migration_guides/algorithms_migration.rst b/docs/migration_guides/algorithms_migration.rst index e71a9042acfa..d6aed9e24c10 100644 --- a/docs/migration_guides/algorithms_migration.rst +++ b/docs/migration_guides/algorithms_migration.rst @@ -6,7 +6,7 @@ TL;DR ----- The :mod:`qiskit.algorithms` module has been fully refactored to use the :mod:`~qiskit.primitives` instead of the -:class:`~qiskit.utils.QuantumInstance` for circuit execution. +:class:`~qiskit.utils.QuantumInstance`, which is now deprecated, for circuit execution. There have been **3 types of refactoring**: @@ -168,7 +168,7 @@ The legacy :class:`qiskit.algorithms.minimum_eigen_solvers.VQE` class has now be .. note:: The new :class:`~qiskit.algorithms.minimum_eigensolvers.VQEResult` class does not include the state anymore, as - this output was only useful in the case of diagonal operators. However, if it is available as part of the new + this output was only useful in the case of diagonal operators. However, it is available as part of the new :class:`~qiskit.algorithms.minimum_eigensolvers.SamplingVQE` :class:`~qiskit.algorithms.minimum_eigensolvers.SamplingVQEResult`. .. raw:: html From c780a4fa591075b623e6b5638a197998868c643b Mon Sep 17 00:00:00 2001 From: ElePT Date: Mon, 13 Mar 2023 16:55:23 +0100 Subject: [PATCH 09/18] Apply review, update code examples up to Eigensolvers --- .../migration_guides/algorithms_migration.rst | 796 +++++++++--------- 1 file changed, 413 insertions(+), 383 deletions(-) diff --git a/docs/migration_guides/algorithms_migration.rst b/docs/migration_guides/algorithms_migration.rst index e71a9042acfa..4978af7dd3e2 100644 --- a/docs/migration_guides/algorithms_migration.rst +++ b/docs/migration_guides/algorithms_migration.rst @@ -12,15 +12,16 @@ There have been **3 types of refactoring**: 1. Algorithms refactored in a new location to support :mod:`~qiskit.primitives`. These algorithms have the same class names as the :class:`~qiskit.utils.QuantumInstance`\-based ones but are in a new namespace. - **Careful with import paths!!** The old algorithms are still importable from :mod:`qiskit.algorithms` - until the legacy path is deprecated. + **Careful with import paths!!** The legacy algorithms are still importable directly from + :mod:`qiskit.algorithms`, until these are removed, this convenience import is not available for the + refactored algorithms. To import the refactored algorithms you must always **speficy the full import path**. - `Minimum Eigensolvers`_ - `Eigensolvers`_ - `Time Evolvers`_ 2. Algorithms refactored in-place (same namespace) to support both :class:`~qiskit.utils.QuantumInstance` and - :mod:`~qiskit.primitives`. In the future, the use of :class:`~qiskit.utils.QuantumInstance` will be deprecated, + :mod:`~qiskit.primitives`. In the future, the use of :class:`~qiskit.utils.QuantumInstance` will be removed, but the namespace will not change. - `Amplitude Amplifiers`_ @@ -40,9 +41,11 @@ Background *Back to* `TL;DR`_ The :mod:`qiskit.algorithms` module was originally built on top of the :mod:`qiskit.opflow` library and the -:class:`~qiskit.utils.QuantumInstance` utility. The introduction of the :mod:`~qiskit.primitives` superseded -these two tools, which are now soon to be deprecated, and allowed to refactor the :mod:`qiskit.algorithms` module -to make all algorithms **primitive-based**. +:class:`~qiskit.utils.QuantumInstance` utility. The development of the :mod:`~qiskit.primitives` +introduced a higher level execution paradigm, with the ``Estimator`` for computation of +expectation values for observables, and ``Sampler`` for executing circuits and returning probability +distributions. These tools allowed to refactor the :mod:`qiskit.algorithms` module, and deprecate both +:mod:`qiskit.opflow` and :class:`~qiskit.utils.QuantumInstance`. .. attention:: @@ -59,23 +62,46 @@ For further background and detailed migration steps, see the: * `Quantum Instance migration guide `_ -Choosing the Right Primitive for your Algorithm ------------------------------------------------ +How to Choose a Primitive Configuration for your Algorithm +------------------------------------------------------------ + *Back to* `TL;DR`_ -There are 3 different common configurations for algorithms that determine **which primitive import** you should -be selecting: +The classes in :mod:`qiskit.algorithms` state the base class primitive type (``Sampler``/``Estimator``) +they require for their initialization. Once the primitive type is known, you can choose between +four different primitive implementations, depending on how you want to configure your execution: + +a. Using **local** statevector simulators for quick prototyping: **Reference Primitives** in :mod:`qiskit.primitives` +b. Using **local** Aer simulators for finer algorithm tuning: **Aer Primitives** in :mod:`qiskit_aer.primitives` +c. Accessing backends using the **Qiskit Runtime Service**: **Runtime Primitives** in :mod:`qiskit_ibm_runtime` +d. Accessing backends using a **non-Runtime-enabled provider**: **Backend Primitives** in :mod:`qiskit.primitives` + +For more detailed information and examples, particularly on the use of the **Backend Primitives**, please refer to +the `Quantum Instance migration guide `_. -1. Running an algorithm with a statevector simulator - (ie. using :mod:`qiskit.opflow`\'s legacy :class:`.MatrixExpectation`): +In this guide, we will cover 3 different common configurations for algorithms that determine +**which primitive import** you should be selecting: - - Reference Primitives (see `QAOA`_ example): +1. Running an algorithm with a statevector simulator (ie. using :mod:`qiskit.opflow`\'s legacy + :class:`.MatrixExpectation`), when you want the ideal outcome without shot noise: + + - Reference Primitives with default configuration (see `QAOA`_ example): .. code-block:: python from qiskit.primitives import Sampler, Estimator -2. Running an algorithm using a "qasm" simulator/device (i.e. using :mod:`qiskit.opflow`\'s legacy :class:`.PauliExpectation`): + - Aer Primitives **with statevector simulator**: + + .. code-block:: python + + from qiskit_aer.primitives import Sampler, Estimator + + sampler = Sampler(backend_options={"method": "statevector"}. + estimator = Estimator(backend_options={"method": "statevector"}) + +2. Running an algorithm using a "qasm" simulator/device with shot noise + (i.e. using :mod:`qiskit.opflow`\'s legacy :class:`.PauliExpectation`): - Reference Primitives **with shots** (see `VQE`_ examples): @@ -93,20 +119,20 @@ be selecting: estimator = Estimator() job = estimator.run(circuits, observables, shots=100) - - Runtime Primitives (see `VQD`_ example): + - Runtime Primitives with default configuration (see `VQD`_ example): .. code-block:: python from qiskit_ibm_runtime import Sampler, Estimator - - Aer Primitives (see `VQE`_ examples): + - Aer Primitives with default configuration (see `VQE`_ examples): .. code-block:: python from qiskit_aer.primitives import Sampler, Estimator -3. Running an algorithm on an Aer simulator using custom instruction (ie. using :mod:`qiskit.opflow`\'s legacy +3. Running an algorithm on an Aer simulator using a custom instruction (ie. using :mod:`qiskit.opflow`\'s legacy :class:`.AerPauliExpectation`): - Aer Primitives with ``shots=None``, ``approximation=True`` (see `TrotterQRTE`_ example): @@ -118,24 +144,6 @@ be selecting: sampler = Sampler(run_options={"approximation": True, "shots": None}) estimator = Estimator(run_options={"approximation": True, "shots": None}) - -.. note:: - - In general, in order to know which primitive to use instead of :class:`~qiskit.utils.QuantumInstance`, - you should ask yourself two questions: - - 1. What is the minimal unit of information used by your algorithm? - a. **Expectation value** - you will need an ``Estimator`` - b. **Probability distribution** (from sampling the device) - you will need a ``Sampler`` - - 2. How do you want to execute your circuits? - a. Using **local** statevector simulators for quick prototyping: **Reference Primitives** - b. Using **local** noisy simulations for finer algorithm tuning: **Aer Primitives** - c. Accessing **runtime-enabled backends** (or cloud simulators): **Runtime Primitives** - d. Accessing **non runtime-enabled backends** : **Backend Primitives** - - For more information and examples, see the `Quantum Instance migration guide `_. - Minimum Eigensolvers -------------------- *Back to* `TL;DR`_ @@ -165,130 +173,173 @@ The legacy :class:`qiskit.algorithms.minimum_eigen_solvers.VQE` class has now be be realized using the legacy :class:`~qiskit.algorithms.minimum_eigen_solvers.VQE` with :class:`~qiskit.opflow.expectations.CVaRExpectation`. +.. note:: + + In addition to taking in an :mod:`~qiskit.primitives.Estimator` instance instead of a :class:`~qiskit.utils.QuantumInstance`, + the new :class:`~qiskit.algorithms.minimum_eigensolvers.VQE` signature has undergone the following changes: + + 1. The ``expectation`` and ``include_custom`` parameters have been removed, as this functionality is now + defined at the ``Estimator`` level. + 2. The ``gradient`` parameter now takes in an instance of a primitive-based gradient class from + :mod:`qiskit.algorithms.gradients` instead of the legacy :mod:`qiskit.opflow.gradients.Gradient` class. + 3. The ``max_evals_grouped`` parameter has been removed, as it can be set directly on the optimizer class. + 4. The ``estimator``, ``ansatz`` and ``optimizer`` are the only parameters that can be defined positionally + (and in this order), all others have become keyword-only arguments. + .. note:: The new :class:`~qiskit.algorithms.minimum_eigensolvers.VQEResult` class does not include the state anymore, as this output was only useful in the case of diagonal operators. However, if it is available as part of the new - :class:`~qiskit.algorithms.minimum_eigensolvers.SamplingVQE` :class:`~qiskit.algorithms.minimum_eigensolvers.SamplingVQEResult`. + :class:`~qiskit.algorithms.minimum_eigensolvers.SamplingVQE`'s :class:`~qiskit.algorithms.minimum_eigensolvers.SamplingVQEResult`. -.. raw:: html -
- VQE Example -
-**[Legacy] Using Quantum Instance:** +.. dropdown:: VQE Example + :animate: fade-in-slide-down -.. code-block:: python + **[Legacy] Using Quantum Instance:** - from qiskit.algorithms import VQE - from qiskit.algorithms.optimizers import SLSQP - from qiskit.circuit.library import TwoLocal - from qiskit.opflow import PauliSumOp - from qiskit_aer import AerSimulator + .. testsetup:: - ansatz = TwoLocal(2, 'ry', 'cz') - opt = SLSQP(maxiter=1000) + from qiskit.utils import algorithm_globals + algorithm_globals.random_seed = 42 - # shot-based simulation - backend = AerSimulator() - qi = QuantumInstance(backend=backend, shots=2048) - vqe = VQE(ansatz, optimizer=opt, quantum_instance=qi) + .. testcode:: - hamiltonian = PauliSumOp.from_list([("XX", 1), ("XY", 1)] - result = vqe.compute_minimum_eigenvalue(hamiltonian) + 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 -**[Updated] Using Primitives:** + ansatz = TwoLocal(2, 'ry', 'cz') + opt = SPSA(maxiter=50) -.. code-block:: python + # shot-based simulation + backend = AerSimulator() + qi = QuantumInstance(backend=backend, shots=2048, seed_simulator=42) + vqe = VQE(ansatz, optimizer=opt, quantum_instance=qi) - # new import!!! - from qiskit.algorithms.minimum_eigensolvers import VQE - from qiskit.algorithms.optimizers import SLSQP - 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 + hamiltonian = PauliSumOp.from_list([("XX", 1), ("XY", 1)]) + result = vqe.compute_minimum_eigenvalue(hamiltonian) - ansatz = TwoLocal(2, 'ry', 'cz') - opt = SLSQP(maxiter=1000) + print(result.eigenvalue) - # shot-based simulation - estimator = Estimator(options={"shots": 2048}) - vqe = VQE(estimator, ansatz, opt) + .. testoutput:: - # another option - aer_estimator = AerEstimator(run_options={"shots": 2048}) - vqe = VQE(aer_estimator, ansatz, opt) + (-0.9775390625+0j) - hamiltonian = SparsePauliOp.from_list([("XX", 1), ("XY", 1)] - result = vqe.compute_minimum_eigenvalue(hamiltonian) + **[Updated] Using Primitives:** -.. raw:: html + .. testsetup:: -
-
+ from qiskit.utils import algorithm_globals + algorithm_globals.random_seed = 42 -.. raw:: html + .. testcode:: -
- SamplingVQE Example -
+ 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 -**[Legacy] Using Quantum Instance:** + ansatz = TwoLocal(2, 'ry', 'cz') + opt = SPSA(maxiter=50) -.. code-block:: python + # shot-based simulation + estimator = Estimator(options={"shots": 2048}) + vqe = VQE(estimator, ansatz, opt) - 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_aer import AerSimulator + # another option + aer_estimator = AerEstimator(run_options={"shots": 2048, "seed": 42}) + vqe = VQE(aer_estimator, ansatz, opt) - ansatz = TwoLocal(2, 'ry', 'cz') - opt = SLSQP(maxiter=1000) + hamiltonian = SparsePauliOp.from_list([("XX", 1), ("XY", 1)]) + result = vqe.compute_minimum_eigenvalue(hamiltonian) - # 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) + print(result.eigenvalue) - # diagonal hamiltonian - hamiltonian = PauliSumOp.from_list([("ZZ",1), ("IZ", -0.5), ("II", 0.12)]) - result = vqe.compute_minimum_eigenvalue(hamiltonian) + .. testoutput:: -**[Updated] Using Primitives:** + -0.986328125 -.. code-block:: python +.. dropdown:: VQE applying CVaR (SamplingVQE) Example + :animate: fade-in-slide-down - # new import!!! - from qiskit.algorithms.minimum_eigensolvers import SamplingVQE - from qiskit.algorithms.optimizers import SLSQP - 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 + **[Legacy] Using Quantum Instance:** - ansatz = TwoLocal(2, 'ry', 'cz') - opt = SLSQP(maxiter=1000) + .. testsetup:: - # shot-based simulation - sampler = Sampler(options={"shots": 2048}) - vqe = SamplingVQE(sampler, ansatz, opt, aggregation=0.2) + from qiskit.utils import algorithm_globals + algorithm_globals.random_seed = 42 - # another option - aer_sampler = AerSampler(run_options={"shots": 2048}) - vqe = SamplingVQE(aer_sampler, ansatz, opt, aggregation=0.2) + .. testcode:: - # diagonal hamiltonian - hamiltonian = SparsePauliOp.from_list([("XX", 1), ("XY", 1)] - result = vqe.compute_minimum_eigenvalue(hamiltonian) + 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 -.. raw:: html + 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) + + .. testoutput:: + + -1.38 + + **[Updated] Using Primitives:** + + .. testsetup:: + + from qiskit.utils import algorithm_globals + algorithm_globals.random_seed = 42 + + .. testcode:: + + 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) + + .. testoutput:: + + -1.38 For complete code examples, see the following updated tutorials: @@ -311,54 +362,75 @@ For this reason, **the new QAOA only supports diagonal operators**. If your application requires the final probability distribution, you can instantiate a ``Sampler`` and run it with the optimal circuit after :class:`~qiskit.algorithms.minimum_eigensolvers.VQE`. -.. raw:: html +.. dropdown:: QAOA Example + :animate: fade-in-slide-down + + **[Legacy] Using Quantum Instance:** + + .. testsetup:: + + from qiskit.utils import algorithm_globals + algorithm_globals.random_seed = 42 + + .. testcode:: -
- QAOA Example -
+ 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 -**[Legacy] Using Quantum Instance:** + # exact statevector simulation + backend = AerSimulator() + qi = QuantumInstance(backend=backend, + backend_options={"method": "statevector"}) -.. code-block:: python + optimizer = COBYLA() + qaoa = QAOA(optimizer=optimizer, reps=2, quantum_instance=qi) - 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 + # diagonal operator + qubit_op = PauliSumOp.from_list([("ZIII", 1),("IZII", 1), ("IIIZ", 1), ("IIZI", 1)]) + result = qaoa.compute_minimum_eigenvalue(qubit_op) - # exact statevector simulation - backend = AerSimulator(method="statevector") - qi = QuantumInstance(backend=backend) - optimizer = COBYLA() - qaoa = QAOA(optimizer= optimizer, reps= 2, quantum_instance= qi) + print(result.eigenvalue.real) - # diagonal operator - qubit_op = PauliSumOp.from_list([("IIIX", 1), ("IIXI", 1), ("IXII", 1), ("XIII", 1)]) - result = qaoa.compute_minimum_eigenvalue(qubit_op) + .. testoutput:: -**[Updated] Using Primitives:** + -4.0 -.. code-block:: python + **[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 + .. testsetup:: - # exact statevector simulation - sampler = Sampler() - optimizer = COBYLA() - qaoa = QAOA(sampler, optimizer, reps=2) + from qiskit.utils import algorithm_globals + algorithm_globals.random_seed = 42 - # diagonal operator - qubit_op = SparsePauliOp.from_list([("IIIX", 1), ("IIXI", 1), ("IXII", 1), ("XIII", 1)]) - result = qaoa.compute_minimum_eigenvalue(qubit_op) + .. testcode:: -.. raw:: html + 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"}) + + 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) + + .. testoutput:: + + -4.0 For complete code examples, see the following updated tutorials: @@ -370,44 +442,54 @@ Because this is a classical solver, the workflow has not changed between the old The import has however changed from :class:`qiskit.algorithms.minimum_eigen_solvers.NumPyMinimumEigensolver` to :class:`qiskit.algorithms.minimum_eigensolvers.NumPyMinimumEigensolver` for consistency. -.. raw:: html +.. dropdown:: NumPyMinimumEigensolver Example + :animate: fade-in-slide-down + + **[Legacy] Using Quantum Instance:** + + .. testsetup:: + + from qiskit.utils import algorithm_globals + algorithm_globals.random_seed = 42 + + .. testcode:: -
- NumPyMinimumEigensolver Example -
+ from qiskit.algorithms import NumPyMinimumEigensolver + from qiskit.opflow import PauliSumOp -**[Legacy]:** + solver = NumPyMinimumEigensolver() -.. code-block:: python + hamiltonian = PauliSumOp.from_list([("XX", 1), ("XY", 1)]) + result = solver.compute_minimum_eigenvalue(hamiltonian) - from qiskit.algorithms import NumpyMinimumEigensolver - from qiskit.algorithms.optimizers import SLSQP - from qiskit.opflow import PauliSumOp + print(result.eigenvalue) - opt = SLSQP(maxiter=1000) - solver = NumpyMinimumEigensolver(optimizer=opt) + .. testoutput:: - hamiltonian = PauliSumOp.from_list([("XX", 1), ("XY", 1)] - result = solver.compute_minimum_eigenvalue(hamiltonian) + -1.4142135623730958 -**[Updated]:** + **[Updated] Using Primitives:** -.. code-block:: python + .. testsetup:: - from qiskit.algorithms.minimum_eigensolvers import NumpyMinimumEigensolver - from qiskit.algorithms.optimizers import SLSQP - from qiskit.quantum_info import SparsePauliOp + from qiskit.utils import algorithm_globals + algorithm_globals.random_seed = 42 - opt = SLSQP(maxiter=1000) - solver = NumpyMinimumEigensolver(optimizer=opt) + .. testcode:: - hamiltonian = SparsePauliOp.from_list([("XX", 1), ("XY", 1)] - result = solver.compute_minimum_eigenvalue(hamiltonian) + from qiskit.algorithms.minimum_eigensolvers import NumPyMinimumEigensolver + from qiskit.quantum_info import SparsePauliOp -.. raw:: html + solver = NumPyMinimumEigensolver() -
-
+ hamiltonian = SparsePauliOp.from_list([("XX", 1), ("XY", 1)]) + result = solver.compute_minimum_eigenvalue(hamiltonian) + + print(result.eigenvalue) + + .. testoutput:: + + -1.4142135623730951 For complete code examples, see the following updated tutorials: @@ -443,65 +525,56 @@ from :mod:`qiskit.algorithms.state_fidelities`. the state anymore. If your application requires the final probability distribution, you can instantiate a ``Sampler`` and run it with the optimal circuit after :class:`~qiskit.algorithms.eigensolvers.VQD`. -.. raw:: html - -
- VQD Example -
- -**[Legacy] Using Quantum Instance:** +.. dropdown:: VQD Example + :animate: fade-in-slide-down -.. code-block:: python + **[Legacy] Using Quantum Instance:** - from qiskit_ibm_provider import IBMProvider - 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 + .. code-block:: python - ansatz = TwoLocal(3, rotation_blocks=["ry", "rz"], entanglement_blocks="cz", reps=1) - optimizer = SLSQP() - hamiltonian = PauliSumOp.from_list([("XXZ", 1), ("XYI", 1)] + from qiskit_ibm_provider import IBMProvider + 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 - # example executing in cloud simulator - provider = IBMProvider() - backend = provider.get_backend("ibmq_qasm_simulator") - qi = QuantumInstance(backend=backend) + ansatz = TwoLocal(3, rotation_blocks=["ry", "rz"], entanglement_blocks="cz", reps=1) + optimizer = SLSQP() + hamiltonian = PauliSumOp.from_list([("XXZ", 1), ("XYI", 1)] - vqd = VQD(ansatz, optimizer, k=3, quantum_instance=qi) - result = vqd.compute_eigenvalues(operator=hamiltonian) + # example executing in cloud simulator + provider = IBMProvider() + backend = provider.get_backend("ibmq_qasm_simulator") + qi = QuantumInstance(backend=backend) -**[Updated] Using Primitives:** - -.. code-block:: python - - from qiskit_ibm_runtime import Sampler, Estimator, QiskitRuntimeService - 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 + vqd = VQD(ansatz, optimizer, k=3, quantum_instance=qi) + result = vqd.compute_eigenvalues(operator=hamiltonian) - ansatz = TwoLocal(3, rotation_blocks=["ry", "rz"], entanglement_blocks="cz", reps=1) - optimizer = SLSQP() - hamiltonian = SparsePauliOp.from_list([("XXZ", 1), ("XYI", 1)] + **[Updated] Using Primitives:** - # example executing in cloud simulator - service = QiskitRuntimeService(channel="ibm_quantum") - backend = service.backend("ibmq_qasm_simulator") - with Session(service=service, 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) + .. code-block:: python + from qiskit_ibm_runtime import Sampler, Estimator, QiskitRuntimeService + 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 -.. raw:: html + ansatz = TwoLocal(3, rotation_blocks=["ry", "rz"], entanglement_blocks="cz", reps=1) + optimizer = SLSQP() + hamiltonian = SparsePauliOp.from_list([("XXZ", 1), ("XYI", 1)] -
-
+ # example executing in cloud simulator + service = QiskitRuntimeService(channel="ibm_quantum") + backend = service.backend("ibmq_qasm_simulator") + with Session(service=service, 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) For complete code examples, see the following updated tutorials: @@ -514,45 +587,36 @@ between the old and new implementation. The import has however changed from :class:`qiskit.algorithms.eigen_solvers.NumPyEigensolver` to :class:`qiskit.algorithms.eigensolvers.MinimumEigensolver` for consistency. -.. raw:: html - -
- NumPyEigensolver Example -
- -**[Legacy]:** - -.. code-block:: python +.. dropdown:: NumPyEigensolver Example + :animate: fade-in-slide-down - from qiskit.algorithms import NumpyEigensolver - from qiskit.algorithms.optimizers import SLSQP - from qiskit.opflow import PauliSumOp + **[Legacy]:** - opt = SLSQP(maxiter=1000) - solver = NumpyEigensolver(optimizer=opt, k=2) + .. code-block:: python - hamiltonian = PauliSumOp.from_list([("XX", 1), ("XY", 1)] - result = solver.compute_eigenvalues(hamiltonian) + from qiskit.algorithms import NumpyEigensolver + from qiskit.algorithms.optimizers import SLSQP + from qiskit.opflow import PauliSumOp -**[Updated]:** + opt = SLSQP(maxiter=1000) + solver = NumpyEigensolver(optimizer=opt, k=2) -.. code-block:: python + hamiltonian = PauliSumOp.from_list([("XX", 1), ("XY", 1)] + result = solver.compute_eigenvalues(hamiltonian) - from qiskit.algorithms.eigensolvers import NumpyEigensolver - from qiskit.algorithms.optimizers import SLSQP - from qiskit.quantum_info import SparsePauliOp + **[Updated]:** - opt = SLSQP(maxiter=1000) - solver = NumpyEigensolver(optimizer=opt, k=2) + .. code-block:: python - hamiltonian = SparsePauliOp.from_list([("XX", 1), ("XY", 1)] - result = solver.compute_eigenvalues(hamiltonian) + from qiskit.algorithms.eigensolvers import NumpyEigensolver + from qiskit.algorithms.optimizers import SLSQP + from qiskit.quantum_info import SparsePauliOp -.. raw:: html - -
-
+ opt = SLSQP(maxiter=1000) + solver = NumpyEigensolver(optimizer=opt, k=2) + hamiltonian = SparsePauliOp.from_list([("XX", 1), ("XY", 1)] + result = solver.compute_eigenvalues(hamiltonian) Time Evolvers ------------- @@ -576,63 +640,54 @@ TrotterQRTE * Old import path (Quantum Instance): ``from qiskit.algorithms import TrotterQRTE`` * New import path (Primitives): ``from qiskit.algorithms.time_evolvers import TrotterQRTE`` -.. raw:: html - -
- TrotterQRTE Example -
- -**[Legacy] Using Quantum Instance:** - -.. code-block:: python - - 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 +.. dropdown:: TrotterQRTE Example + :animate: fade-in-slide-down - operator = PauliSumOp.from_list([("X", 1),("Z", 1)]) - initial_state = QuantumCircuit(1) # zero - time = 1 - evolution_problem = EvolutionProblem(operator, 1, initial_state) + **[Legacy] Using Quantum Instance:** - # Aer simulator using custom instruction - backend = AerSimulator() - quantum_instance = QuantumInstance(backend=backend) - expectation = AerPauliExpectation() + .. code-block:: python - # LieTrotter with 1 rep - trotter_qrte = TrotterQRTE(expectation=expectation, quantum_instance=quantum_instance) - evolved_state = trotter_qrte.evolve(evolution_problem).evolved_state + 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 -**[Updated] Using Primitives:** + operator = PauliSumOp.from_list([("X", 1),("Z", 1)]) + initial_state = QuantumCircuit(1) # zero + time = 1 + evolution_problem = EvolutionProblem(operator, 1, initial_state) -.. code-block:: python + # Aer simulator using custom instruction + backend = AerSimulator() + quantum_instance = QuantumInstance(backend=backend) + expectation = AerPauliExpectation() - # note new import!!! - from qiskit.algorithms.time_evolvers import EvolutionProblem, TrotterQRTE - from qiskit.circuit import QuantumCircuit - from qiskit.quantum_info import SparsePauliOp - from qiskit_aer.primitives import Estimator as AerEstimator + # LieTrotter with 1 rep + trotter_qrte = TrotterQRTE(expectation=expectation, quantum_instance=quantum_instance) + evolved_state = trotter_qrte.evolve(evolution_problem).evolved_state - operator = SparsePauliOp.from_list([("X", 1),("Z", 1)]) - initial_state = QuantumCircuit(1) # zero - time = 1 - evolution_problem = EvolutionProblem(operator, 1, initial_state) + **[Updated] Using Primitives:** - # Aer simulator using custom instruction - estimator = AerEstimator(run_options={"approximation": True, "shots": None}) + .. code-block:: python - # LieTrotter with 1 rep - trotter_qrte = TrotterQRTE(expectation=expectation, quantum_instance=quantum_instance) - evolved_state = trotter_qrte.evolve(evolution_problem).evolved_state + # note new import!!! + from qiskit.algorithms.time_evolvers import EvolutionProblem, TrotterQRTE + from qiskit.circuit import QuantumCircuit + from qiskit.quantum_info import SparsePauliOp + from qiskit_aer.primitives import Estimator as AerEstimator -.. raw:: html + operator = SparsePauliOp.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 + estimator = AerEstimator(run_options={"approximation": True, "shots": None}) + # LieTrotter with 1 rep + trotter_qrte = TrotterQRTE(expectation=expectation, quantum_instance=quantum_instance) + evolved_state = trotter_qrte.evolve(evolution_problem).evolved_state Amplitude Amplifiers --------------------- @@ -645,36 +700,28 @@ using any instance of the :mod:`~qiskit.primitives.Sampler` primitive. The full :mod:`qiskit.algorithms.amplitude_amplifiers` module has been refactored in place. No need to change import paths. -.. raw:: html +.. dropdown:: Grover Example + :animate: fade-in-slide-down -
- Grover Example -
+ **[Legacy] Using Quantum Instance:** -**[Legacy] Using Quantum Instance:** + .. code-block:: python -.. code-block:: python + from qiskit.algorithms import Grover + from qiskit.utils import QuantumInstance - from qiskit.algorithms import Grover - from qiskit.utils import QuantumInstance + qi = QuantumInstance(backend=backend) + grover = Grover(quantum_instance=qi) - qi = QuantumInstance(backend=backend) - grover = Grover(quantum_instance=qi) + **[Updated] Using Primitives:** -**[Updated] Using Primitives:** + .. code-block:: python -.. code-block:: python + from qiskit.algorithms import Grover + from qiskit.primitives import Sampler - from qiskit.algorithms import Grover - from qiskit.primitives import Sampler - - grover = Grover(sampler=Sampler()) - -.. raw:: html - -
-
+ grover = Grover(sampler=Sampler()) For complete code examples, see the following updated tutorials: @@ -692,44 +739,35 @@ using any instance of the :mod:`qiskit.primitives.Sampler` primitive. The full :mod:`qiskit.algorithms.amplitude_estimators` module has been refactored in place. No need to change import paths. -.. raw:: html - -
- IAE Example -
- +.. dropdown:: IAE Example + :animate: fade-in-slide-down -**[Legacy] Using Quantum Instance:** + **[Legacy] Using Quantum Instance:** -.. code-block:: python + .. code-block:: python - from qiskit.algorithms import IterativeAmplitudeEstimation - from qiskit.utils import 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, - ) + 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:** + **[Updated] Using Primitives:** -.. code-block:: python + .. code-block:: python - from qiskit.algorithms import IterativeAmplitudeEstimation - from qiskit.primitives import Sampler + 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(), - ) - -.. raw:: html - -
-
+ iae = IterativeAmplitudeEstimation( + epsilon_target=0.01, # target accuracy + alpha=0.05, # width of the confidence interval + sampler=Sampler(), + ) For complete code examples, see the following updated tutorials: @@ -746,43 +784,35 @@ using any instance of the :mod:`qiskit.primitives.Sampler` primitive. The full :mod:`qiskit.algorithms.phase_estimators` module has been refactored in place. No need to change import paths. -.. raw:: html - -
- IQPE Example -
- -**[Legacy] Using Quantum Instance:** - -.. code-block:: python +.. dropdown:: IQPE Example + :animate: fade-in-slide-down - from qiskit.algorithms import IterativeAmplitudeEstimation - from qiskit.utils import QuantumInstance + **[Legacy] Using Quantum Instance:** - qi = QuantumInstance(backend=backend) - iae = IterativeAmplitudeEstimation( - epsilon_target=0.01, # target accuracy - alpha=0.05, # width of the confidence interval - quantum_instance=qi, - ) + .. code-block:: python -**[Updated] Using Primitives:** + from qiskit.algorithms import IterativeAmplitudeEstimation + from qiskit.utils import QuantumInstance -.. code-block:: python + qi = QuantumInstance(backend=backend) + iae = IterativeAmplitudeEstimation( + epsilon_target=0.01, # target accuracy + alpha=0.05, # width of the confidence interval + quantum_instance=qi, + ) - from qiskit.algorithms import IterativeAmplitudeEstimation - from qiskit.primitives import Sampler + **[Updated] Using Primitives:** - iae = IterativeAmplitudeEstimation( - epsilon_target=0.01, # target accuracy - alpha=0.05, # width of the confidence interval - sampler=Sampler(), - ) + .. code-block:: python -.. raw:: html + 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 complete code examples, see the following updated tutorials: From 96b0b037bce1cd7513476f22733c4326a9deae99 Mon Sep 17 00:00:00 2001 From: ElePT Date: Mon, 13 Mar 2023 16:56:24 +0100 Subject: [PATCH 10/18] Add doctest to sphinx config --- docs/conf.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/conf.py b/docs/conf.py index 1d74bc42a14a..40bca5016b2a 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -16,6 +16,7 @@ # -- General configuration --------------------------------------------------- import datetime +import doctest project = "Qiskit" copyright = f"2017-{datetime.date.today().year}, Qiskit Development Team" # pylint: disable=redefined-builtin @@ -38,7 +39,9 @@ "reno.sphinxext", "sphinx_design", "matplotlib.sphinxext.plot_directive", + "sphinx.ext.doctest" ] + templates_path = ["_templates"] # Number figures, tables and code-blocks if they have a caption. @@ -63,6 +66,7 @@ intersphinx_mapping = { "retworkx": ("https://qiskit.org/documentation/retworkx/", None), + "qiskit_ibm_runtime": ("https://qiskit.org/documentation/partners/qiskit_ibm_runtime/", None) } # -- Options for HTML output ------------------------------------------------- @@ -88,3 +92,7 @@ autosummary_generate = True autosummary_generate_overwrite = False autoclass_content = "both" + +# -- Options for doctest ------------------------------------- + +doctest_default_flags = doctest.ELLIPSIS | doctest.NORMALIZE_WHITESPACE | doctest.IGNORE_EXCEPTION_DETAIL | doctest.DONT_ACCEPT_TRUE_FOR_1 From c1444e51a864dda319aa0fda38dd216c4ee0a57e Mon Sep 17 00:00:00 2001 From: ElePT Date: Tue, 14 Mar 2023 16:23:08 +0100 Subject: [PATCH 11/18] Add tests up to eigensolvers --- .../migration_guides/algorithms_migration.rst | 110 ++++++++++++++---- 1 file changed, 90 insertions(+), 20 deletions(-) diff --git a/docs/migration_guides/algorithms_migration.rst b/docs/migration_guides/algorithms_migration.rst index 30ddc8580a28..d276d8b3b2b0 100644 --- a/docs/migration_guides/algorithms_migration.rst +++ b/docs/migration_guides/algorithms_migration.rst @@ -12,9 +12,13 @@ There have been **3 types of refactoring**: 1. Algorithms refactored in a new location to support :mod:`~qiskit.primitives`. These algorithms have the same class names as the :class:`~qiskit.utils.QuantumInstance`\-based ones but are in a new namespace. - **Careful with import paths!!** The legacy algorithms are still importable directly from - :mod:`qiskit.algorithms`, until these are removed, this convenience import is not available for the - refactored algorithms. To import the refactored algorithms you must always **speficy the full import path**. + + .. attention:: + + **Careful with import paths!!** The legacy algorithms are still importable directly from + :mod:`qiskit.algorithms`. Until the legacy imports are removed, this convenience import is not available + for the refactored algorithms. Thus, to import the refactored algorithms you must always + **speficy the full import path** (i.e ``from qiskit.algorithms.eigensolvers import VQD``) - `Minimum Eigensolvers`_ - `Eigensolvers`_ @@ -71,10 +75,11 @@ The classes in :mod:`qiskit.algorithms` state the base class primitive type (``S they require for their initialization. Once the primitive type is known, you can choose between four different primitive implementations, depending on how you want to configure your execution: -a. Using **local** statevector simulators for quick prototyping: **Reference Primitives** in :mod:`qiskit.primitives` -b. Using **local** Aer simulators for finer algorithm tuning: **Aer Primitives** in :mod:`qiskit_aer.primitives` -c. Accessing backends using the **Qiskit Runtime Service**: **Runtime Primitives** in :mod:`qiskit_ibm_runtime` -d. Accessing backends using a **non-Runtime-enabled provider**: **Backend Primitives** in :mod:`qiskit.primitives` + a. Using **local** statevector simulators for quick prototyping: **Reference Primitives** in :mod:`qiskit.primitives` + b. Using **local** Aer simulators for finer algorithm tuning: **Aer Primitives** in :mod:`qiskit_aer.primitives` + c. Accessing backends using the **Qiskit Runtime Service**: **Runtime Primitives** in :mod:`qiskit_ibm_runtime` + d. Accessing backends using a **non-Runtime-enabled provider**: **Backend Primitives** in :mod:`qiskit.primitives` + For more detailed information and examples, particularly on the use of the **Backend Primitives**, please refer to the `Quantum Instance migration guide `_. @@ -100,7 +105,7 @@ In this guide, we will cover 3 different common configurations for algorithms th sampler = Sampler(backend_options={"method": "statevector"}. estimator = Estimator(backend_options={"method": "statevector"}) -2. Running an algorithm using a "qasm" simulator/device with shot noise +2. Running an algorithm using a simulator/device with shot noise (i.e. using :mod:`qiskit.opflow`\'s legacy :class:`.PauliExpectation`): - Reference Primitives **with shots** (see `VQE`_ examples): @@ -119,17 +124,17 @@ In this guide, we will cover 3 different common configurations for algorithms th estimator = Estimator() job = estimator.run(circuits, observables, shots=100) - - Runtime Primitives with default configuration (see `VQD`_ example): + - Aer Primitives with default configuration (see `VQE`_ examples): .. code-block:: python - from qiskit_ibm_runtime import Sampler, Estimator + from qiskit_aer.primitives import Sampler, Estimator - - Aer Primitives with default configuration (see `VQE`_ examples): + - Runtime Primitives with default configuration (see `VQD`_ example): .. code-block:: python - from qiskit_aer.primitives import Sampler, Estimator + from qiskit_ibm_runtime import Sampler, Estimator 3. Running an algorithm on an Aer simulator using a custom instruction (ie. using :mod:`qiskit.opflow`\'s legacy @@ -144,13 +149,14 @@ In this guide, we will cover 3 different common configurations for algorithms th sampler = Sampler(run_options={"approximation": True, "shots": None}) estimator = Estimator(run_options={"approximation": True, "shots": None}) + Minimum Eigensolvers -------------------- *Back to* `TL;DR`_ Instead of a :class:`~qiskit.utils.QuantumInstance`, :mod:`qiskit.algorithms.minimum_eigensolvers` are now initialized using an instance of the :mod:`~qiskit.primitives.Sampler` or :mod:`~qiskit.primitives.Estimator` primitive, depending -on the algorithm. +on the algorithm. The legacy classes can still be found in :mod:`qiskit.algorithms.minimum_eigen_solvers`. .. attention:: @@ -354,6 +360,19 @@ The legacy :class:`qiskit.algorithms.minimum_eigen_solvers.QAOA` class used to e extends :class:`qiskit.algorithms.minimum_eigensolvers.SamplingVQE`. For this reason, **the new QAOA only supports diagonal operators**. +.. note:: + + In addition to taking in an :mod:`~qiskit.primitives.Sampler` instance instead of a :class:`~qiskit.utils.QuantumInstance`, + the new :class:`~qiskit.algorithms.minimum_eigensolvers.QAOA` signature has undergone the following changes: + + 1. The ``expectation`` and ``include_custom`` parameters have been removed. In return, the ``aggregation`` + parameter has been added (it used to be defined through a custom ``expectation``). + 2. The ``gradient`` parameter now takes in an instance of a primitive-based gradient class from + :mod:`qiskit.algorithms.gradients` instead of the legacy :mod:`qiskit.opflow.gradients.Gradient` class. + 3. The ``max_evals_grouped`` parameter has been removed, as it can be set directly on the optimizer class. + 4. The ``sampler`` and ``optimizer`` are the only parameters that can be defined positionally + (and in this order), all others have become keyword-only arguments. + .. note:: If you want to run QAOA on a non-diagonal operator, you can use the :class:`.QAOAAnsatz` with @@ -488,7 +507,7 @@ to :class:`qiskit.algorithms.minimum_eigensolvers.NumPyMinimumEigensolver` for c .. testoutput:: - -1.4142135623730951 + -1.414213562373095 For complete code examples, see the following updated tutorials: @@ -500,7 +519,8 @@ Eigensolvers Instead of a :class:`~qiskit.utils.QuantumInstance`, :mod:`qiskit.algorithms.eigensolvers` are now initialized using an instance of the :class:`~qiskit.primitives.Sampler` or :class:`~qiskit.primitives.Estimator` primitive, or -**a primitive-based subroutine**, depending on the algorithm. +**a primitive-based subroutine**, depending on the algorithm. The legacy classes can still be found +in :mod:`qiskit.algorithms.eigen_solvers`. .. attention:: @@ -515,21 +535,41 @@ VQD ~~~~ The new :class:`qiskit.algorithms.eigensolvers.VQD` class is initialized with an :class:`~qiskit.primitives.Estimator` -primitive, as well as a :class:`~qiskit.primitives.Sampler`\-based fidelity class +primitive, as well as a :class:`~qiskit.primitives.Sampler`-based fidelity class from :mod:`qiskit.algorithms.state_fidelities`. +.. note:: + + In addition to taking in an :mod:`~qiskit.primitives.Estimator` instance instead of a :class:`~qiskit.utils.QuantumInstance`, + the new :class:`~qiskit.algorithms.eigensolvers.VQD` signature has undergone the following changes: + + 1. The ``expectation`` and ``include_custom`` parameters have been removed, as this functionality is now + defined at the ``Estimator`` level. + 2. The custom ``fidelity`` parameter has been added, and the custom ``gradient`` parameter has + been removed, as current classes in :mod:`qiskit.algorithms.gradients` cannot deal with state fidelity + gradients. + 3. The ``max_evals_grouped`` parameter has been removed, as it can be set directly on the optimizer class. + 4. The ``estimator``, ``fidelity``, ``ansatz`` and ``optimizer`` are the only parameters that can be defined positionally + (and in this order), all others have become keyword-only arguments. + .. note:: Similarly to VQE, the new :class:`~qiskit.algorithms.eigensolvers.VQDResult` class does not include the state anymore. If your application requires the final probability distribution, you can instantiate a ``Sampler`` and run it with the optimal circuit after :class:`~qiskit.algorithms.eigensolvers.VQD`. + .. dropdown:: VQD Example :animate: fade-in-slide-down **[Legacy] Using Quantum Instance:** - .. code-block:: python + .. testsetup:: + + from qiskit.utils import algorithm_globals + algorithm_globals.random_seed = 42 + + .. testcode:: from qiskit_ibm_provider import IBMProvider from qiskit.algorithms import VQD @@ -550,9 +590,21 @@ from :mod:`qiskit.algorithms.state_fidelities`. vqd = VQD(ansatz, optimizer, k=3, quantum_instance=qi) result = vqd.compute_eigenvalues(operator=hamiltonian) + print(result.eigenvalues) + + .. testoutput:: + :options: +SKIP + + 5.5 + **[Updated] Using Primitives:** - .. code-block:: python + .. testsetup:: + + from qiskit.utils import algorithm_globals + algorithm_globals.random_seed = 42 + + .. testcode:: from qiskit_ibm_runtime import Sampler, Estimator, QiskitRuntimeService from qiskit.algorithms.eigensolvers import VQD @@ -568,6 +620,7 @@ from :mod:`qiskit.algorithms.state_fidelities`. # example executing in cloud simulator service = QiskitRuntimeService(channel="ibm_quantum") backend = service.backend("ibmq_qasm_simulator") + with Session(service=service, backend=backend) as session: estimator = Estimator() sampler = Sampler() @@ -575,6 +628,13 @@ from :mod:`qiskit.algorithms.state_fidelities`. vqd = VQD(estimator, fidelity, ansatz, optimizer, k=3) result = vqd.compute_eigenvalues(operator=hamiltonian) + print(result.eigenvalues) + + .. testoutput:: + :options: +SKIP + + 5.5 + For complete code examples, see the following updated tutorials: - `VQD `_ @@ -622,8 +682,8 @@ Time Evolvers *Back to* `TL;DR`_ Instead of a :class:`~qiskit.utils.QuantumInstance`, :mod:`qiskit.algorithms.time_evolvers` are now initialized -using an instance of the :class:`~qiskit.primitives.Sampler` or :class:`~qiskit.primitives.Estimator` primitive, -depending on the algorithm. +using an instance of the :class:`~qiskit.primitives.Estimator` primitive. The legacy classes can still be found +in :mod:`qiskit.algorithms.evolvers`. On top of the migration, the module has been substantially expanded to include **Variational Quantum Time Evolution** (:class:`~qiskit.algorithms.time_evolvers.VarQTE`\) solvers. @@ -639,6 +699,16 @@ TrotterQRTE * Old import path (Quantum Instance): ``from qiskit.algorithms import TrotterQRTE`` * New import path (Primitives): ``from qiskit.algorithms.time_evolvers import TrotterQRTE`` +.. note:: + + In addition to taking in an :mod:`~qiskit.primitives.Estimator` instance instead of a :class:`~qiskit.utils.QuantumInstance`, + the new :class:`~qiskit.algorithms.eigensolvers.VQD` signature has undergone the following changes: + + 1. The ``expectation`` parameter has been removed, as this functionality is now + defined at the ``Estimator`` level. + 2. The ``num_timesteps`` parameters has been added, to allow to define the number of steps the full evolution + time is divided into. + .. dropdown:: TrotterQRTE Example :animate: fade-in-slide-down From 569ba191c4bceb6c89a569073069bd6c986fbfa2 Mon Sep 17 00:00:00 2001 From: ElePT Date: Tue, 14 Mar 2023 17:40:47 +0100 Subject: [PATCH 12/18] Review code examples from Eigensolvers --- .../migration_guides/algorithms_migration.rst | 125 +++++++++++------- 1 file changed, 80 insertions(+), 45 deletions(-) diff --git a/docs/migration_guides/algorithms_migration.rst b/docs/migration_guides/algorithms_migration.rst index d276d8b3b2b0..0ef2315342c0 100644 --- a/docs/migration_guides/algorithms_migration.rst +++ b/docs/migration_guides/algorithms_migration.rst @@ -96,7 +96,7 @@ In this guide, we will cover 3 different common configurations for algorithms th from qiskit.primitives import Sampler, Estimator - - Aer Primitives **with statevector simulator**: + - Aer Primitives **with statevector simulator** (see `QAOA`_ example): .. code-block:: python @@ -400,7 +400,8 @@ For this reason, **the new QAOA only supports diagonal operators**. # exact statevector simulation backend = AerSimulator() - qi = QuantumInstance(backend=backend, + qi = QuantumInstance(backend=backend, shots=None, + seed_simulator = 42, seed_transpiler = 42, backend_options={"method": "statevector"}) optimizer = COBYLA() @@ -435,7 +436,8 @@ For this reason, **the new QAOA only supports diagonal operators**. sampler = Sampler() # another option - sampler = AerSampler(backend_options={"method": "statevector"}) + sampler = AerSampler(backend_options={"method": "statevector"}, + run_options={"shots": None, "seed": 42}) optimizer = COBYLA() qaoa = QAOA(sampler, optimizer, reps=2) @@ -448,7 +450,7 @@ For this reason, **the new QAOA only supports diagonal operators**. .. testoutput:: - -4.0 + -3.999999832366272 For complete code examples, see the following updated tutorials: @@ -571,7 +573,7 @@ from :mod:`qiskit.algorithms.state_fidelities`. .. testcode:: - from qiskit_ibm_provider import IBMProvider + from qiskit import IBMQ from qiskit.algorithms import VQD from qiskit.algorithms.optimizers import SLSQP from qiskit.circuit.library import TwoLocal @@ -579,15 +581,15 @@ from :mod:`qiskit.algorithms.state_fidelities`. from qiskit.utils import QuantumInstance ansatz = TwoLocal(3, rotation_blocks=["ry", "rz"], entanglement_blocks="cz", reps=1) - optimizer = SLSQP() - hamiltonian = PauliSumOp.from_list([("XXZ", 1), ("XYI", 1)] + optimizer = SLSQP(maxiter=10) + hamiltonian = PauliSumOp.from_list([("XXZ", 1), ("XYI", 1)]) # example executing in cloud simulator - provider = IBMProvider() + provider = IBMQ.load_account() backend = provider.get_backend("ibmq_qasm_simulator") qi = QuantumInstance(backend=backend) - vqd = VQD(ansatz, optimizer, k=3, quantum_instance=qi) + vqd = VQD(ansatz, k=3, optimizer=optimizer, quantum_instance=qi) result = vqd.compute_eigenvalues(operator=hamiltonian) print(result.eigenvalues) @@ -595,7 +597,7 @@ from :mod:`qiskit.algorithms.state_fidelities`. .. testoutput:: :options: +SKIP - 5.5 + [ 0.01765114+0.0e+00j -0.58507654+0.0e+00j -0.15003642-2.8e-17j] **[Updated] Using Primitives:** @@ -606,7 +608,7 @@ from :mod:`qiskit.algorithms.state_fidelities`. .. testcode:: - from qiskit_ibm_runtime import Sampler, Estimator, QiskitRuntimeService + 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 @@ -614,8 +616,8 @@ from :mod:`qiskit.algorithms.state_fidelities`. from qiskit.quantum_info import SparsePauliOp ansatz = TwoLocal(3, rotation_blocks=["ry", "rz"], entanglement_blocks="cz", reps=1) - optimizer = SLSQP() - hamiltonian = SparsePauliOp.from_list([("XXZ", 1), ("XYI", 1)] + optimizer = SLSQP(maxiter=10) + hamiltonian = SparsePauliOp.from_list([("XXZ", 1), ("XYI", 1)]) # example executing in cloud simulator service = QiskitRuntimeService(channel="ibm_quantum") @@ -633,7 +635,7 @@ from :mod:`qiskit.algorithms.state_fidelities`. .. testoutput:: :options: +SKIP - 5.5 + [ 0.01765114+0.0e+00j -0.58507654+0.0e+00j -0.15003642-2.8e-17j] For complete code examples, see the following updated tutorials: @@ -651,32 +653,50 @@ to :class:`qiskit.algorithms.eigensolvers.MinimumEigensolver` for consistency. **[Legacy]:** - .. code-block:: python + .. testsetup:: - from qiskit.algorithms import NumpyEigensolver - from qiskit.algorithms.optimizers import SLSQP + from qiskit.utils import algorithm_globals + algorithm_globals.random_seed = 42 + + .. testcode:: + + from qiskit.algorithms import NumPyEigensolver from qiskit.opflow import PauliSumOp - opt = SLSQP(maxiter=1000) - solver = NumpyEigensolver(optimizer=opt, k=2) + solver = NumPyEigensolver(k=2) - hamiltonian = PauliSumOp.from_list([("XX", 1), ("XY", 1)] + hamiltonian = PauliSumOp.from_list([("XX", 1), ("XY", 1)]) result = solver.compute_eigenvalues(hamiltonian) + print(result.eigenvalues) + + .. testoutput:: + + [-1.41421356 -1.41421356] + **[Updated]:** - .. code-block:: python + .. testsetup:: - from qiskit.algorithms.eigensolvers import NumpyEigensolver - from qiskit.algorithms.optimizers import SLSQP + from qiskit.utils import algorithm_globals + algorithm_globals.random_seed = 42 + + .. testcode:: + + from qiskit.algorithms.eigensolvers import NumPyEigensolver from qiskit.quantum_info import SparsePauliOp - opt = SLSQP(maxiter=1000) - solver = NumpyEigensolver(optimizer=opt, k=2) + solver = NumPyEigensolver(k=2) - hamiltonian = SparsePauliOp.from_list([("XX", 1), ("XY", 1)] + hamiltonian = SparsePauliOp.from_list([("XX", 1), ("XY", 1)]) result = solver.compute_eigenvalues(hamiltonian) + print(result.eigenvalues) + + .. testoutput:: + + [-1.41421356 -1.41421356] + Time Evolvers ------------- *Back to* `TL;DR`_ @@ -714,7 +734,7 @@ TrotterQRTE **[Legacy] Using Quantum Instance:** - .. code-block:: python + .. testcode:: from qiskit.algorithms import EvolutionProblem, TrotterQRTE from qiskit.circuit import QuantumCircuit @@ -736,12 +756,21 @@ TrotterQRTE trotter_qrte = TrotterQRTE(expectation=expectation, quantum_instance=quantum_instance) evolved_state = trotter_qrte.evolve(evolution_problem).evolved_state + print(evolved_state) + + .. testoutput:: + + CircuitStateFn( + ┌─────────────────────┐ + q: ┤ exp(-it (X + Z))(1) ├ + └─────────────────────┘ + ) + **[Updated] Using Primitives:** - .. code-block:: python + .. testcode:: - # note new import!!! - from qiskit.algorithms.time_evolvers import EvolutionProblem, TrotterQRTE + 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 @@ -749,15 +778,23 @@ TrotterQRTE operator = SparsePauliOp.from_list([("X", 1),("Z", 1)]) initial_state = QuantumCircuit(1) # zero time = 1 - evolution_problem = EvolutionProblem(operator, 1, initial_state) + evolution_problem = TimeEvolutionProblem(operator, 1, initial_state) # Aer simulator using custom instruction estimator = AerEstimator(run_options={"approximation": True, "shots": None}) # LieTrotter with 1 rep - trotter_qrte = TrotterQRTE(expectation=expectation, quantum_instance=quantum_instance) + trotter_qrte = TrotterQRTE(estimator=estimator) evolved_state = trotter_qrte.evolve(evolution_problem).evolved_state + print(evolved_state.decompose()) + + .. testoutput:: + + ┌───────────┐┌───────────┐ + q: ┤ exp(it X) ├┤ exp(it Z) ├ + └───────────┘└───────────┘ + Amplitude Amplifiers --------------------- *Back to* `TL;DR`_ @@ -822,7 +859,7 @@ using any instance of the :mod:`qiskit.primitives.Sampler` primitive. iae = IterativeAmplitudeEstimation( epsilon_target=0.01, # target accuracy alpha=0.05, # width of the confidence interval - quantum_instance=qi, + quantum_instance=qi ) **[Updated] Using Primitives:** @@ -835,7 +872,7 @@ using any instance of the :mod:`qiskit.primitives.Sampler` primitive. iae = IterativeAmplitudeEstimation( epsilon_target=0.01, # target accuracy alpha=0.05, # width of the confidence interval - sampler=Sampler(), + sampler=Sampler() ) For complete code examples, see the following updated tutorials: @@ -853,34 +890,32 @@ using any instance of the :mod:`qiskit.primitives.Sampler` primitive. The full :mod:`qiskit.algorithms.phase_estimators` module has been refactored in place. No need to change import paths. -.. dropdown:: IQPE Example +.. dropdown:: IPE Example :animate: fade-in-slide-down **[Legacy] Using Quantum Instance:** .. code-block:: python - from qiskit.algorithms import IterativeAmplitudeEstimation + from qiskit.algorithms import IterativePhaseEstimation 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, + ipe = IterativePhaseEstimation( + num_iterations=num_iter, + quantum_instance=qi ) **[Updated] Using Primitives:** .. code-block:: python - from qiskit.algorithms import IterativeAmplitudeEstimation + from qiskit.algorithms import IterativePhaseEstimation from qiskit.primitives import Sampler - iae = IterativeAmplitudeEstimation( - epsilon_target=0.01, # target accuracy - alpha=0.05, # width of the confidence interval - sampler=Sampler(), + ipe = IterativePhaseEstimation( + num_iterations=num_iter, + sampler=Sampler() ) For complete code examples, see the following updated tutorials: From 68a25925a6d9eb00e84a0a57337039ef76c102e1 Mon Sep 17 00:00:00 2001 From: ElePT Date: Tue, 14 Mar 2023 17:44:11 +0100 Subject: [PATCH 13/18] Update heading style --- .../migration_guides/algorithms_migration.rst | 36 ++++++++++--------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/docs/migration_guides/algorithms_migration.rst b/docs/migration_guides/algorithms_migration.rst index 0ef2315342c0..c6caa255fce1 100644 --- a/docs/migration_guides/algorithms_migration.rst +++ b/docs/migration_guides/algorithms_migration.rst @@ -1,9 +1,9 @@ -========================== +########################## Algorithms Migration Guide -========================== +########################## TL;DR ------ +===== The :mod:`qiskit.algorithms` module has been fully refactored to use the :mod:`~qiskit.primitives` instead of the :class:`~qiskit.utils.QuantumInstance`, which is now deprecated, for circuit execution. @@ -41,7 +41,8 @@ There have been **3 types of refactoring**: - `Factorizers (Shor) `_ Background ----------- +========== + *Back to* `TL;DR`_ The :mod:`qiskit.algorithms` module was originally built on top of the :mod:`qiskit.opflow` library and the @@ -67,7 +68,7 @@ For further background and detailed migration steps, see the: How to Choose a Primitive Configuration for your Algorithm ------------------------------------------------------------- +========================================================== *Back to* `TL;DR`_ @@ -151,7 +152,7 @@ In this guide, we will cover 3 different common configurations for algorithms th Minimum Eigensolvers --------------------- +==================== *Back to* `TL;DR`_ Instead of a :class:`~qiskit.utils.QuantumInstance`, :mod:`qiskit.algorithms.minimum_eigensolvers` are now initialized @@ -168,7 +169,7 @@ on the algorithm. The legacy classes can still be found in :mod:`qiskit.algorith * New import path (Primitives): ``from qiskit.algorithms.minimum_eigensolvers import VQE, SamplingVQE, QAOA, NumPyMinimumEigensolver`` VQE -~~~ +--- The legacy :class:`qiskit.algorithms.minimum_eigen_solvers.VQE` class has now been split according to the use-case: @@ -353,7 +354,7 @@ For complete code examples, see the following updated tutorials: - `VQE with Aer Primitives `_ QAOA -~~~~ +---- The legacy :class:`qiskit.algorithms.minimum_eigen_solvers.QAOA` class used to extend :class:`qiskit.algorithms.minimum_eigen_solvers.VQE`, but now, :class:`qiskit.algorithms.minimum_eigensolvers.QAOA` @@ -457,7 +458,8 @@ For complete code examples, see the following updated tutorials: - `QAOA `_ NumPyMinimumEigensolver -~~~~~~~~~~~~~~~~~~~~~~~~~ +----------------------- + Because this is a classical solver, the workflow has not changed between the old and new implementation. The import has however changed from :class:`qiskit.algorithms.minimum_eigen_solvers.NumPyMinimumEigensolver` to :class:`qiskit.algorithms.minimum_eigensolvers.NumPyMinimumEigensolver` for consistency. @@ -516,7 +518,7 @@ For complete code examples, see the following updated tutorials: - `VQE, Callback, Gradients, Initial Point `_ Eigensolvers ------------- +============ *Back to* `TL;DR`_ Instead of a :class:`~qiskit.utils.QuantumInstance`, :mod:`qiskit.algorithms.eigensolvers` are now initialized @@ -534,7 +536,7 @@ in :mod:`qiskit.algorithms.eigen_solvers`. * New import path (Primitives): ``from qiskit.algorithms.eigensolvers import VQD, NumPyEigensolver`` VQD -~~~~ +--- The new :class:`qiskit.algorithms.eigensolvers.VQD` class is initialized with an :class:`~qiskit.primitives.Estimator` primitive, as well as a :class:`~qiskit.primitives.Sampler`-based fidelity class @@ -642,7 +644,7 @@ For complete code examples, see the following updated tutorials: - `VQD `_ NumPyEigensolver -~~~~~~~~~~~~~~~~~ +---------------- Similarly to its minimum eigensolver counterpart, because this is a classical solver, the workflow has not changed between the old and new implementation. The import has however changed from :class:`qiskit.algorithms.eigen_solvers.NumPyEigensolver` @@ -698,7 +700,7 @@ to :class:`qiskit.algorithms.eigensolvers.MinimumEigensolver` for consistency. [-1.41421356 -1.41421356] Time Evolvers -------------- +============= *Back to* `TL;DR`_ Instead of a :class:`~qiskit.utils.QuantumInstance`, :mod:`qiskit.algorithms.time_evolvers` are now initialized @@ -709,7 +711,7 @@ On top of the migration, the module has been substantially expanded to include * (:class:`~qiskit.algorithms.time_evolvers.VarQTE`\) solvers. TrotterQRTE -~~~~~~~~~~~~ +----------- .. attention:: For the :class:`qiskit.algorithms.time_evolvers.TrotterQRTE` class, depending on the import path, @@ -796,7 +798,7 @@ TrotterQRTE └───────────┘└───────────┘ Amplitude Amplifiers ---------------------- +==================== *Back to* `TL;DR`_ Instead of a :class:`~qiskit.utils.QuantumInstance`, :mod:`qiskit.algorithms.amplitude_amplifiers` are now initialized @@ -835,7 +837,7 @@ For complete code examples, see the following updated tutorials: - `Grover Examples `_ Amplitude Estimators --------------------- +==================== *Back to* `TL;DR`_ Instead of a :class:`~qiskit.utils.QuantumInstance`, :mod:`qiskit.algorithms.amplitude_estimators` are now initialized @@ -880,7 +882,7 @@ For complete code examples, see the following updated tutorials: - `Amplitude Estimation `_ Phase Estimators ----------------- +================ *Back to* `TL;DR`_ Instead of a :class:`~qiskit.utils.QuantumInstance`, :mod:`qiskit.algorithms.phase_estimators` are now initialized From 701561b381830bcc839a923f9eb207850d90f53e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20Pe=C3=B1a=20Tapia?= <57907331+ElePT@users.noreply.github.com> Date: Thu, 30 Mar 2023 14:59:50 +0200 Subject: [PATCH 14/18] Apply suggestions from Declan's code review Co-authored-by: Declan Millar --- docs/migration_guides/algorithms_migration.rst | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/migration_guides/algorithms_migration.rst b/docs/migration_guides/algorithms_migration.rst index c6caa255fce1..90b8ab09240c 100644 --- a/docs/migration_guides/algorithms_migration.rst +++ b/docs/migration_guides/algorithms_migration.rst @@ -18,7 +18,7 @@ There have been **3 types of refactoring**: **Careful with import paths!!** The legacy algorithms are still importable directly from :mod:`qiskit.algorithms`. Until the legacy imports are removed, this convenience import is not available for the refactored algorithms. Thus, to import the refactored algorithms you must always - **speficy the full import path** (i.e ``from qiskit.algorithms.eigensolvers import VQD``) + **specify the full import path** (i.e., ``from qiskit.algorithms.eigensolvers import VQD``) - `Minimum Eigensolvers`_ - `Eigensolvers`_ @@ -47,7 +47,7 @@ Background The :mod:`qiskit.algorithms` module was originally built on top of the :mod:`qiskit.opflow` library and the :class:`~qiskit.utils.QuantumInstance` utility. The development of the :mod:`~qiskit.primitives` -introduced a higher level execution paradigm, with the ``Estimator`` for computation of +introduced a higher-level execution paradigm, with the ``Estimator`` for computation of expectation values for observables, and ``Sampler`` for executing circuits and returning probability distributions. These tools allowed to refactor the :mod:`qiskit.algorithms` module, and deprecate both :mod:`qiskit.opflow` and :class:`~qiskit.utils.QuantumInstance`. @@ -88,7 +88,7 @@ the `Quantum Instance migration guide `_. In this guide, we will cover 3 different common configurations for algorithms that determine **which primitive import** you should be selecting: -1. Running an algorithm with a statevector simulator (ie. using :mod:`qiskit.opflow`\'s legacy +1. Running an algorithm with a statevector simulator (i.e., using :mod:`qiskit.opflow`\'s legacy :class:`.MatrixExpectation`), when you want the ideal outcome without shot noise: - Reference Primitives with default configuration (see `QAOA`_ example): @@ -103,11 +103,11 @@ In this guide, we will cover 3 different common configurations for algorithms th from qiskit_aer.primitives import Sampler, Estimator - sampler = Sampler(backend_options={"method": "statevector"}. + sampler = Sampler(backend_options={"method": "statevector"}) estimator = Estimator(backend_options={"method": "statevector"}) 2. Running an algorithm using a simulator/device with shot noise - (i.e. using :mod:`qiskit.opflow`\'s legacy :class:`.PauliExpectation`): + (i.e., using :mod:`qiskit.opflow`\'s legacy :class:`.PauliExpectation`): - Reference Primitives **with shots** (see `VQE`_ examples): @@ -138,7 +138,7 @@ In this guide, we will cover 3 different common configurations for algorithms th from qiskit_ibm_runtime import Sampler, Estimator -3. Running an algorithm on an Aer simulator using a custom instruction (ie. using :mod:`qiskit.opflow`\'s legacy +3. Running an algorithm on an Aer simulator using a custom instruction (i.e., using :mod:`qiskit.opflow`\'s legacy :class:`.AerPauliExpectation`): - Aer Primitives with ``shots=None``, ``approximation=True`` (see `TrotterQRTE`_ example): From cd1c1438f6e7c3ebe53d699bcf1dcc874f41df27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20Pe=C3=B1a=20Tapia?= <57907331+ElePT@users.noreply.github.com> Date: Fri, 31 Mar 2023 12:22:52 +0200 Subject: [PATCH 15/18] Apply suggestions from Steve's code review Co-authored-by: Steve Wood <40241007+woodsp-ibm@users.noreply.github.com> --- docs/migration_guides/algorithms_migration.rst | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/docs/migration_guides/algorithms_migration.rst b/docs/migration_guides/algorithms_migration.rst index 90b8ab09240c..b9a4f6c8ab6f 100644 --- a/docs/migration_guides/algorithms_migration.rst +++ b/docs/migration_guides/algorithms_migration.rst @@ -5,20 +5,19 @@ Algorithms Migration Guide TL;DR ===== -The :mod:`qiskit.algorithms` module has been fully refactored to use the :mod:`~qiskit.primitives` instead of the -:class:`~qiskit.utils.QuantumInstance`, which is now deprecated, for circuit execution. +The :mod:`qiskit.algorithms` module has been fully refactored to use the :mod:`~qiskit.primitives`, for circuit execution, instead of the :class:`~qiskit.utils.QuantumInstance`, which is now deprecated. There have been **3 types of refactoring**: 1. Algorithms refactored in a new location to support :mod:`~qiskit.primitives`. These algorithms have the same - class names as the :class:`~qiskit.utils.QuantumInstance`\-based ones but are in a new namespace. + class names as the :class:`~qiskit.utils.QuantumInstance`\-based ones but are in a new sub-package. .. attention:: **Careful with import paths!!** The legacy algorithms are still importable directly from :mod:`qiskit.algorithms`. Until the legacy imports are removed, this convenience import is not available for the refactored algorithms. Thus, to import the refactored algorithms you must always - **specify the full import path** (i.e., ``from qiskit.algorithms.eigensolvers import VQD``) + **specify the full import path** (e.g., ``from qiskit.algorithms.eigensolvers import VQD``) - `Minimum Eigensolvers`_ - `Eigensolvers`_ @@ -33,7 +32,7 @@ There have been **3 types of refactoring**: - `Phase Estimators`_ -3. Algorithms deprecated entirely in :mod:`qiskit.algorithms`. These are algorithms that do not currently serve +3. Algorithms that were deprecated and are now removed entirely from :mod:`qiskit.algorithms`. These are algorithms that do not currently serve as building blocks for applications. Their main value is educational, and as such, will be kept as tutorials in the qiskit textbook. You can consult the tutorials in the following links: From 0d562c1ec7cbdb1ca1a9712a871cf7974d888cc4 Mon Sep 17 00:00:00 2001 From: ElePT Date: Fri, 31 Mar 2023 17:45:37 +0200 Subject: [PATCH 16/18] Apply review --- .../migration_guides/algorithms_migration.rst | 48 +++++++++++++------ 1 file changed, 34 insertions(+), 14 deletions(-) diff --git a/docs/migration_guides/algorithms_migration.rst b/docs/migration_guides/algorithms_migration.rst index c6caa255fce1..113400f260ee 100644 --- a/docs/migration_guides/algorithms_migration.rst +++ b/docs/migration_guides/algorithms_migration.rst @@ -25,8 +25,7 @@ There have been **3 types of refactoring**: - `Time Evolvers`_ 2. Algorithms refactored in-place (same namespace) to support both :class:`~qiskit.utils.QuantumInstance` and - :mod:`~qiskit.primitives`. In the future, the use of :class:`~qiskit.utils.QuantumInstance` will be removed, - but the namespace will not change. + :mod:`~qiskit.primitives`. In the future, the use of :class:`~qiskit.utils.QuantumInstance` will be removed. - `Amplitude Amplifiers`_ - `Amplitude Estimators`_ @@ -37,8 +36,12 @@ There have been **3 types of refactoring**: as building blocks for applications. Their main value is educational, and as such, will be kept as tutorials in the qiskit textbook. You can consult the tutorials in the following links: - - `Linear Solvers (HHL) `_ - - `Factorizers (Shor) `_ + - `Linear Solvers (HHL) `_ , + - `Factorizers (Shor) `_ + + +The remainder of this migration guide will focus on the algorithms with migration alternatives within +:mod:`qiskit.algorithms`, that is, those under refactoring types 1 and 2. Background ========== @@ -155,6 +158,8 @@ Minimum Eigensolvers ==================== *Back to* `TL;DR`_ +The minimum eigensolver algorithms belong to the first type of refactoring listed above +(Algorithms refactored in a new location to support :mod:`~qiskit.primitives`). Instead of a :class:`~qiskit.utils.QuantumInstance`, :mod:`qiskit.algorithms.minimum_eigensolvers` are now initialized using an instance of the :mod:`~qiskit.primitives.Sampler` or :mod:`~qiskit.primitives.Estimator` primitive, depending on the algorithm. The legacy classes can still be found in :mod:`qiskit.algorithms.minimum_eigen_solvers`. @@ -165,8 +170,8 @@ on the algorithm. The legacy classes can still be found in :mod:`qiskit.algorith you will access either the primitive-based or the quantum-instance-based implementation. You have to be extra-careful, because the class name does not change. - * Old import path (Quantum Instance): ``from qiskit.algorithms import VQE, QAOA, NumPyMinimumEigensolver`` - * New import path (Primitives): ``from qiskit.algorithms.minimum_eigensolvers import VQE, SamplingVQE, QAOA, NumPyMinimumEigensolver`` + * Old import (Quantum Instance based): ``from qiskit.algorithms import VQE, QAOA, NumPyMinimumEigensolver`` + * New import (Primitives based): ``from qiskit.algorithms.minimum_eigensolvers import VQE, SamplingVQE, QAOA, NumPyMinimumEigensolver`` VQE --- @@ -462,7 +467,8 @@ NumPyMinimumEigensolver Because this is a classical solver, the workflow has not changed between the old and new implementation. The import has however changed from :class:`qiskit.algorithms.minimum_eigen_solvers.NumPyMinimumEigensolver` -to :class:`qiskit.algorithms.minimum_eigensolvers.NumPyMinimumEigensolver` for consistency. +to :class:`qiskit.algorithms.minimum_eigensolvers.NumPyMinimumEigensolver` to conform to the new interfaces +and result classes. .. dropdown:: NumPyMinimumEigensolver Example :animate: fade-in-slide-down @@ -521,7 +527,9 @@ Eigensolvers ============ *Back to* `TL;DR`_ -Instead of a :class:`~qiskit.utils.QuantumInstance`, :mod:`qiskit.algorithms.eigensolvers` are now initialized +The eigensolver algorithms also belong to the first type of refactoring +(Algorithms refactored in a new location to support :mod:`~qiskit.primitives`). Instead of a +:class:`~qiskit.utils.QuantumInstance`, :mod:`qiskit.algorithms.eigensolvers` are now initialized using an instance of the :class:`~qiskit.primitives.Sampler` or :class:`~qiskit.primitives.Estimator` primitive, or **a primitive-based subroutine**, depending on the algorithm. The legacy classes can still be found in :mod:`qiskit.algorithms.eigen_solvers`. @@ -560,7 +568,8 @@ from :mod:`qiskit.algorithms.state_fidelities`. Similarly to VQE, the new :class:`~qiskit.algorithms.eigensolvers.VQDResult` class does not include the state anymore. If your application requires the final probability distribution, you can instantiate - a ``Sampler`` and run it with the optimal circuit after :class:`~qiskit.algorithms.eigensolvers.VQD`. + a ``Sampler`` and run it with the optimal circuit for the desired excited state + after running :class:`~qiskit.algorithms.eigensolvers.VQD`. .. dropdown:: VQD Example @@ -639,7 +648,11 @@ from :mod:`qiskit.algorithms.state_fidelities`. [ 0.01765114+0.0e+00j -0.58507654+0.0e+00j -0.15003642-2.8e-17j] -For complete code examples, see the following updated tutorials: +.. raw:: html +
+ + +For complete code examples, see the following updated tutorial: - `VQD `_ @@ -648,7 +661,7 @@ NumPyEigensolver Similarly to its minimum eigensolver counterpart, because this is a classical solver, the workflow has not changed between the old and new implementation. The import has however changed from :class:`qiskit.algorithms.eigen_solvers.NumPyEigensolver` -to :class:`qiskit.algorithms.eigensolvers.MinimumEigensolver` for consistency. +to :class:`qiskit.algorithms.eigensolvers.MinimumEigensolver` to conform to the new interfaces and result classes. .. dropdown:: NumPyEigensolver Example :animate: fade-in-slide-down @@ -703,6 +716,8 @@ Time Evolvers ============= *Back to* `TL;DR`_ +The time evolvers are the last group of algorithms to undergo the first type of refactoring +(Algorithms refactored in a new location to support :mod:`~qiskit.primitives`). Instead of a :class:`~qiskit.utils.QuantumInstance`, :mod:`qiskit.algorithms.time_evolvers` are now initialized using an instance of the :class:`~qiskit.primitives.Estimator` primitive. The legacy classes can still be found in :mod:`qiskit.algorithms.evolvers`. @@ -801,8 +816,9 @@ Amplitude Amplifiers ==================== *Back to* `TL;DR`_ +The amplitude amplifier algorithms belong to the second type of refactoring (Algorithms refactored in-place). Instead of a :class:`~qiskit.utils.QuantumInstance`, :mod:`qiskit.algorithms.amplitude_amplifiers` are now initialized -using any instance of the :mod:`~qiskit.primitives.Sampler` primitive. +using an instance of any "Sampler" primitive e.g. :mod:`~qiskit.primitives.Sampler`. .. note:: The full :mod:`qiskit.algorithms.amplitude_amplifiers` module has been refactored in place. No need to @@ -840,8 +856,10 @@ Amplitude Estimators ==================== *Back to* `TL;DR`_ +Similarly to the amplitude amplifiers, the amplitude estimators also belong to the second type of refactoring +(Algorithms refactored in-place). Instead of a :class:`~qiskit.utils.QuantumInstance`, :mod:`qiskit.algorithms.amplitude_estimators` are now initialized -using any instance of the :mod:`qiskit.primitives.Sampler` primitive. +using an instance of any "Sampler" primitive e.g. :mod:`~qiskit.primitives.Sampler`. .. note:: The full :mod:`qiskit.algorithms.amplitude_estimators` module has been refactored in place. No need to @@ -885,8 +903,10 @@ Phase Estimators ================ *Back to* `TL;DR`_ +Finally, the phase estimators are the last group of algorithms to undergo the first type of refactoring +(Algorithms refactored in-place). Instead of a :class:`~qiskit.utils.QuantumInstance`, :mod:`qiskit.algorithms.phase_estimators` are now initialized -using any instance of the :mod:`qiskit.primitives.Sampler` primitive. +using an instance of any "Sampler" primitive e.g. :mod:`~qiskit.primitives.Sampler`. .. note:: The full :mod:`qiskit.algorithms.phase_estimators` module has been refactored in place. No need to From b5a29426466755db45712c2ed40ee99a79dfa115 Mon Sep 17 00:00:00 2001 From: ElePT Date: Mon, 3 Apr 2023 17:29:57 +0200 Subject: [PATCH 17/18] Final changes, fix docs --- docs/migration_guides/algorithms_migration.rst | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/migration_guides/algorithms_migration.rst b/docs/migration_guides/algorithms_migration.rst index b3323d0eb2ba..e2173cf7a75e 100644 --- a/docs/migration_guides/algorithms_migration.rst +++ b/docs/migration_guides/algorithms_migration.rst @@ -545,9 +545,10 @@ in :mod:`qiskit.algorithms.eigen_solvers`. VQD --- -The new :class:`qiskit.algorithms.eigensolvers.VQD` class is initialized with an :class:`~qiskit.primitives.Estimator` -primitive, as well as a :class:`~qiskit.primitives.Sampler`-based fidelity class -from :mod:`qiskit.algorithms.state_fidelities`. +The new :class:`qiskit.algorithms.eigensolvers.VQD` class is initialized with an instance of the +:class:`~qiskit.primitives.Estimator` primitive instead of a :class:`~qiskit.utils.QuantumInstance`. +In addition to this, it takes an instance of a state fidelity class from mod:`qiskit.algorithms.state_fidelities`, +such as the :class:`~qiskit.primitives.Sampler`-based :class:`~qiskit.algorithms.state_fidelities.ComputeUncompute`. .. note:: @@ -648,8 +649,8 @@ from :mod:`qiskit.algorithms.state_fidelities`. [ 0.01765114+0.0e+00j -0.58507654+0.0e+00j -0.15003642-2.8e-17j] .. raw:: html -
+
For complete code examples, see the following updated tutorial: From 14fee7e0f687855609a3a26bfd9dfb58974306ec Mon Sep 17 00:00:00 2001 From: ElePT Date: Wed, 5 Apr 2023 09:35:51 +0200 Subject: [PATCH 18/18] Apply Julien's comments --- docs/migration_guides/algorithms_migration.rst | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/migration_guides/algorithms_migration.rst b/docs/migration_guides/algorithms_migration.rst index e2173cf7a75e..033c662cc547 100644 --- a/docs/migration_guides/algorithms_migration.rst +++ b/docs/migration_guides/algorithms_migration.rst @@ -58,8 +58,8 @@ distributions. These tools allowed to refactor the :mod:`qiskit.algorithms` modu The transition away from :mod:`qiskit.opflow` affects the classes that algorithms take as part of the problem setup. As a rule of thumb, most :mod:`qiskit.opflow` dependencies have a direct :mod:`qiskit.quantum_info` - replacement. One common example is the class :mod:`qiskit.opflow.PauliSumOp`, used to define hamiltonian - operators (for example, to plug into VQE), that can be replaced by :mod:`qiskit.quantum_info.SparsePauliOp`. + replacement. One common example is the class :mod:`qiskit.opflow.PauliSumOp`, used to define Hamiltonians + (for example, to plug into VQE), that can be replaced by :mod:`qiskit.quantum_info.SparsePauliOp`. For information on how to migrate other :mod:`~qiskit.opflow` objects, you can refer to the `Opflow migration guide `_. @@ -69,7 +69,7 @@ For further background and detailed migration steps, see the: * `Quantum Instance migration guide `_ -How to Choose a Primitive Configuration for your Algorithm +How to choose a primitive configuration for your algorithm ========================================================== *Back to* `TL;DR`_ @@ -177,9 +177,9 @@ VQE The legacy :class:`qiskit.algorithms.minimum_eigen_solvers.VQE` class has now been split according to the use-case: -- For general-purpose hamiltonians, you can use the Estimator-based :class:`qiskit.algorithms.minimum_eigensolvers.VQE` +- For general-purpose Hamiltonians, you can use the Estimator-based :class:`qiskit.algorithms.minimum_eigensolvers.VQE` class. -- If you have a diagonal hamiltonian, and would like the algorithm to return a sampling of the state, you can use +- If you have a diagonal Hamiltonian, and would like the algorithm to return a sampling of the state, you can use the new Sampler-based :class:`qiskit.algorithms.minimum_eigensolvers.SamplingVQE` algorithm. This could formerly be realized using the legacy :class:`~qiskit.algorithms.minimum_eigen_solvers.VQE` with :class:`~qiskit.opflow.expectations.CVaRExpectation`. @@ -304,7 +304,7 @@ The legacy :class:`qiskit.algorithms.minimum_eigen_solvers.VQE` class has now be expectation = CVaRExpectation(alpha=0.2) vqe = VQE(ansatz, optimizer=opt, expectation=expectation, quantum_instance=qi) - # diagonal hamiltonian + # diagonal Hamiltonian hamiltonian = PauliSumOp.from_list([("ZZ",1), ("IZ", -0.5), ("II", 0.12)]) result = vqe.compute_minimum_eigenvalue(hamiltonian) @@ -341,7 +341,7 @@ The legacy :class:`qiskit.algorithms.minimum_eigen_solvers.VQE` class has now be aer_sampler = AerSampler(run_options={"shots": 2048, "seed": 42}) vqe = SamplingVQE(aer_sampler, ansatz, opt, aggregation=0.2) - # diagonal hamiltonian + # diagonal Hamiltonian hamiltonian = SparsePauliOp.from_list([("ZZ",1), ("IZ", -0.5), ("II", 0.12)]) result = vqe.compute_minimum_eigenvalue(hamiltonian)