From dc0fae866ac0f53808ba2556e528274469847ed2 Mon Sep 17 00:00:00 2001 From: ElePT Date: Wed, 8 Feb 2023 11:59:18 +0100 Subject: [PATCH 01/50] 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 27af89033ee..69c065eb9fe 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 00000000000..0ae8193ca5b --- /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 00000000000..efc0564b989 --- /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/50] 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 0ae8193ca5b..38cea61c2a7 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/50] 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 efc0564b989..5e21c53d003 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 4c1b2ceaf50653e41964554394c52a798bd91fd5 Mon Sep 17 00:00:00 2001 From: ElePT Date: Wed, 8 Feb 2023 18:06:38 +0100 Subject: [PATCH 04/50] Apply comments boxnote --- docs/migration_guides/opflow_migration.rst | 50 +++++++++++++--------- 1 file changed, 30 insertions(+), 20 deletions(-) diff --git a/docs/migration_guides/opflow_migration.rst b/docs/migration_guides/opflow_migration.rst index 5e21c53d003..a3c7b71dc74 100644 --- a/docs/migration_guides/opflow_migration.rst +++ b/docs/migration_guides/opflow_migration.rst @@ -98,12 +98,20 @@ in some of the :class:`~quantum_info.BaseOperator` subclasses. 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 +Opflow provided shortcuts to define common single qubit states, operators, and 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 ~~~~~~~~~~~~~~ +The 1-qubit paulis were commonly used for quick testing of algorithms, as they could be combined to create more complex operators +(for example, ``0.39 * (I ^ Z) + 0.5 * (X ^ X)``). +These operations implicitly created operators of type :class:`~qiskit.opflow.PauliSumOp`, and can be replaced by +directly creating a corresponding :class:`~qiskit.quantum_info.SparsePauliOp`, as shown in the example below. + .. list-table:: Migration of ``qiskit.opflow.operator_globals`` (1/3) :header-rows: 1 @@ -238,10 +246,10 @@ PrimitiveOps - notes * - :class:`~qiskit.opflow.PrimitiveOp` - - No replacement needed + - No direct replacement - Can directly use :class:`~qiskit.quantum_info.Operator`` * - :class:`~qiskit.opflow.CircuitOp` - - No replacement needed + - No direct replacement - Can directly use :class:`~qiskit.QuantumCircuit` * - :class:`~qiskit.opflow.MatrixOp` - :class:`~qiskit.quantum_info.Operator`` @@ -336,19 +344,19 @@ ListOps - notes * - :class:`~qiskit.opflow.ListOp` - - No replacement needed. This class was used internally within opflow. + - No direct replacement. This class was used internally within opflow. - * - :class:`~qiskit.opflow.ComposedOp` - - No replacement needed. This class was used internally within opflow. + - No direct replacement. This class was used internally within opflow. - * - :class:`~qiskit.opflow.SummedOp` - - No replacement needed. This class was used internally within opflow. + - No direct replacement. This class was used internally within opflow. - * - :class:`~qiskit.opflow.TensoredOp` - - No replacement needed. This class was used internally within opflow. + - No direct replacement. This class was used internally within opflow. - State Functions @@ -368,15 +376,15 @@ This module can be generally replaced by :class:`~qiskit.quantum_info.QuantumSta - notes * - :class:`~qiskit.opflow.StateFn` - - No replacement needed. This class was used internally within opflow. + - No direct replacement. This class was used internally within opflow. - * - :class:`~qiskit.opflow.CircuitStateFn` - - No replacement needed. This class was used internally within opflow. + - No direct replacement. This class was used internally within opflow. - * - :class:`~qiskit.opflow.DictStateFn` - - No replacement needed. This class was used internally within opflow. + - No direct replacement. This class was used internally within opflow. - * - :class:`~qiskit.opflow.VectorStateFn` @@ -384,11 +392,11 @@ This module can be generally replaced by :class:`~qiskit.quantum_info.QuantumSta - * - :class:`~qiskit.opflow.SparseVectorStateFn` - - No replacement needed. This class was used internally within opflow. + - No direct replacement. 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. + - No direct replacement. 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. @@ -491,8 +499,10 @@ Two Qubit Reduction - 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``. - - + - No direct replacement. + - This class implements a chemistry-specific reduction for the ``ParityMapper`` class in ``qiskit-nature``. + The general symmetry logic this mapper depends on has been refactored to other classes in :mod:`~qiskit.quantum_info`, + so this specific :mod:`~qiskit.opflow` implementation is no longer necessary. Other Converters ~~~~~~~~~~~~~~~~~ @@ -505,13 +515,13 @@ Other Converters - notes * - :class:`~qiskit.opflow.AbelianGrouper` - - No replacement needed. This class was used internally within opflow. - - + - No direct replacement. + - This class allowed a sum a of Pauli operators to be grouped. These type of groupings are now left to the primitives to handle (??). * - :class:`~qiskit.opflow.DictToCircuitSum` - - No replacement needed. This class was used internally within opflow. + - No direct replacement. This class was used internally within opflow. - * - :class:`~qiskit.opflow.PauliBasisChange` - - No replacement needed. This class was used internally within opflow. + - No direct replacement. This class was used internally within opflow. - Evolutions @@ -654,7 +664,7 @@ Algorithm-Agnostic Expectations - alternative - notes * - :class:`~qiskit.opflow.ExpectationFactory` - - No replacement needed. + - No direct replacement. - * - :class:`~qiskit.opflow.AerPauliExpectation` - Use :class:`~Estimator` primitive from ``qiskit_aer`` instead. From fd294a67b54466e15f53860ccd3dec32a9d46bb0 Mon Sep 17 00:00:00 2001 From: ElePT Date: Wed, 8 Feb 2023 18:13:37 +0100 Subject: [PATCH 05/50] Small edit --- docs/migration_guides/opflow_migration.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/migration_guides/opflow_migration.rst b/docs/migration_guides/opflow_migration.rst index a3c7b71dc74..eabcd792df0 100644 --- a/docs/migration_guides/opflow_migration.rst +++ b/docs/migration_guides/opflow_migration.rst @@ -101,7 +101,7 @@ Operator Globals Opflow provided shortcuts to define common single qubit states, operators, and 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 +These were mainly used for didactic purposes or quick prototyping, 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`. From 8c0a92b96ff0985dd58862fcaf317258d0265002 Mon Sep 17 00:00:00 2001 From: ElePT Date: Wed, 8 Feb 2023 18:19:59 +0100 Subject: [PATCH 06/50] Apply review --- docs/migration_guides/opflow_migration.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/migration_guides/opflow_migration.rst b/docs/migration_guides/opflow_migration.rst index eabcd792df0..03eda652c38 100644 --- a/docs/migration_guides/opflow_migration.rst +++ b/docs/migration_guides/opflow_migration.rst @@ -158,7 +158,7 @@ Common non-parametrized gates (Clifford) * - :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()`` - - + - This alternative is not too efficient, as ``.to_operator()`` is dense and scales exponentially with the size of the circuit. * - @@ -667,7 +667,7 @@ Algorithm-Agnostic Expectations - No direct replacement. - * - :class:`~qiskit.opflow.AerPauliExpectation` - - Use :class:`~Estimator` primitive from ``qiskit_aer`` instead. + - Use :class:`~Estimator` primitive from ``qiskit_aer`` with ``approximation=True`` - * - :class:`~qiskit.opflow.MatrixExpectation` - Use :class:`~Estimator` primitive from ``qiskit`` instead (uses Statevector). @@ -676,7 +676,7 @@ Algorithm-Agnostic Expectations - Use any :class:`~Estimator` primitive. - -TODO: ADD EXAMPLE! +TODO: ADD EXAMPLES! CVarExpectation ~~~~~~~~~~~~~~~ From 2be8b5af87f9e0198ec0386015f47d96b6cbe4c1 Mon Sep 17 00:00:00 2001 From: ElePT Date: Thu, 9 Feb 2023 12:24:34 +0100 Subject: [PATCH 07/50] Update up to primitive ops --- docs/migration_guides/opflow_migration.rst | 257 +++++++++++++-------- 1 file changed, 159 insertions(+), 98 deletions(-) diff --git a/docs/migration_guides/opflow_migration.rst b/docs/migration_guides/opflow_migration.rst index 03eda652c38..1bfee564165 100644 --- a/docs/migration_guides/opflow_migration.rst +++ b/docs/migration_guides/opflow_migration.rst @@ -8,26 +8,10 @@ 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 +for quantum algorithms research and development. + +The recent introduction of the :mod:`~qiskit.primitives` provided a new interface for interacting with backends that disrupted +the "opflow way" of doing things. 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 @@ -231,12 +215,42 @@ Common non-parametrized gates (Clifford) 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. +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 ~~~~~~~~~~~~~~ +When looking for an alternative to update your code, it is important to keep in mind that :class:`~qiskit.opflow.primitive_ops.PrimitiveOp` +acted as a factory to create the corresponding subclass class depending on the primitive used to initialize it: + +.. list-table:: :class:`~qiskit.opflow.primitive_ops.PrimitiveOp` + :header-rows: 1 + + * - primitive sent to :class:`~qiskit.opflow.primitive_ops.PrimitiveOp` + - :class:`~qiskit.opflow.primitive_ops.PrimitiveOp` subclass returned + + * - :class:`~qiskit.quantum_info.Pauli` + - :class:`~qiskit.opflow.primitive_ops.PauliOp` + + * - :class:`~qiskit.circuit.Instruction` + - :class:`~qiskit.opflow.primitive_ops.CircuitOp` + + * - :class:`~qiskit.circuit.QuantumCircuit` + - :class:`~qiskit.opflow.primitive_ops.CircuitOp` + + * - ``list`` + - :class:`~qiskit.opflow.primitive_ops.MatrixOp` + + * - ``np.ndarray`` + - :class:`~qiskit.opflow.primitive_ops.MatrixOp` + + * - ``scipy.sparse.spmatrix` + - :class:`~qiskit.opflow.primitive_ops.MatrixOp` + + * - :class:`~qiskit.quantum_info.Operator` + - :class:`~qiskit.opflow.primitive_ops.MatrixOp` .. list-table:: Migration of ``qiskit.opflow.primitive_ops`` :header-rows: 1 @@ -245,27 +259,36 @@ PrimitiveOps - alternative - notes - * - :class:`~qiskit.opflow.PrimitiveOp` - - No direct replacement - - Can directly use :class:`~qiskit.quantum_info.Operator`` - * - :class:`~qiskit.opflow.CircuitOp` + * - :class:`~qiskit.opflow.primitive_ops.PrimitiveOp` + - No direct replacement. In most use-cases (representing operators), + the best alternative is :class:`~qiskit.quantum_info.Operator`` + - This class acted base class and **factory** for creating one of the operator sub-classes listed below. + + * - :class:`~qiskit.opflow.primitive_ops.CircuitOp` - No direct replacement - Can directly use :class:`~qiskit.QuantumCircuit` - * - :class:`~qiskit.opflow.MatrixOp` + + * - :class:`~qiskit.opflow.primitive_ops.MatrixOp` - :class:`~qiskit.quantum_info.Operator`` - - * - :class:`~qiskit.opflow.PauliOp` + + * - :class:`~qiskit.opflow.primitive_ops.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` + - For direct compatibility with classes in :mod:`~qiskit.algorithms`, + wrap in :class:`~qiskit.quantum_info.SparsePauliOp` + + * - :class:`~qiskit.opflow.primitive_ops.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 + + * - :class:`~qiskit.opflow.primitive_ops.TaperedPauliSumOp` + - This class was used to combine a :class:`~PauliSumOp` with its identified symmetries in one object. + This functionality is not currently used in any workflow, and has been deprecated without replacement. - See ``Z2Symmetries`` example for updated workflow - * - :class:`~qiskit.opflow.Z2Symmetries` + + * - :class:`~qiskit.opflow.primitive_ops.Z2Symmetries` - :class:`~qiskit.quantum_info.Z2Symmetries` - - See example below + - See example below. PrimitiveOps Examples @@ -281,7 +304,7 @@ PrimitiveOps Examples .. code-block:: python - from qiskit.opflow import PuliSumOp + from qiskit.opflow import PauliSumOp from qiskit.quantum_info import SparsePauliOp, Pauli qubit_op = PauliSumOp(SparsePauliOp(Pauli("XYZY"), coeffs=[2]), coeff=-3j) @@ -336,6 +359,13 @@ PrimitiveOps Examples ListOps ~~~~~~~ + +The :mod:`~qiskit.opflow.list_ops` module contained classes for manipulating lists of :mod:`~qiskit.opflow.primitive_ops` +or :mod:`~qiskit.opflow.state_fns`. The :mod:`~qiskit.quantum_info` alternatives for this functionality are the +:mod:`~qiskit.quantum_info.PauliList`, :mod:`~qiskit.quantum_info.SparsePauliOp` (for sums of ``Pauli``\s), +:mod:`~qiskit.quantum_info.PauliTable` (symplectic representation of lists of Pauli operators) and +:mod:`~qiskit.quantum_info.StabilizerTable` (symplectic representation of lists of state functions). + .. list-table:: Migration of ``qiskit.opflow.list_ops`` :header-rows: 1 @@ -343,20 +373,49 @@ ListOps - alternative - notes - * - :class:`~qiskit.opflow.ListOp` - - No direct replacement. This class was used internally within opflow. - - + * - :class:`~qiskit.opflow.list_ops.ListOp` + - No direct replacement + - This is the base class for operator lists. For ``Pauli`` operators, an + alternative is :mod:`~qiskit.quantum_info.PauliList`. - * - :class:`~qiskit.opflow.ComposedOp` - - No direct replacement. This class was used internally within opflow. + * - :class:`~qiskit.opflow.list_ops.ComposedOp` + - No direct replacement - - * - :class:`~qiskit.opflow.SummedOp` - - No direct replacement. This class was used internally within opflow. + * - :class:`~qiskit.opflow.list_ops.SummedOp` + - No direct replacement + - For sums of ``Pauli`` operators, use :class:`~qiskit.quantum_info.SparsePauliOp`. + + * - :class:`~qiskit.opflow.list_ops.TensoredOp` + - No direct replacement + - For ``Pauli`` operators, use :class:`~qiskit.quantum_info.SparsePauliOp`. + +ListOps Examples +~~~~~~~~~~~~~~~~~~~~~ +.. list-table:: + :header-rows: 1 + + * - opflow + - alternative + - notes + + * - ``ListOp`` **Example:** + + .. code-block:: python + + from qiskit.opflow import PauliSumOp + from qiskit.quantum_info import SparsePauliOp, Pauli + + qubit_op = PauliSumOp(SparsePauliOp(Pauli("XYZY"), coeffs=[2]), coeff=-3j) + - - * - :class:`~qiskit.opflow.TensoredOp` - - No direct replacement. This class was used internally within opflow. + .. code-block:: python + + from qiskit.quantum_info import SparsePauliOp, Pauli + + qubit_op = SparsePauliOp(Pauli("XYZY")), coeff=-6j) + - State Functions @@ -375,31 +434,31 @@ This module can be generally replaced by :class:`~qiskit.quantum_info.QuantumSta - alternative - notes - * - :class:`~qiskit.opflow.StateFn` - - No direct replacement. This class was used internally within opflow. + * - :class:`~qiskit.opflow.state_fns.StateFn` + - No direct replacement - - * - :class:`~qiskit.opflow.CircuitStateFn` - - No direct replacement. This class was used internally within opflow. + * - :class:`~qiskit.opflow.state_fns.CircuitStateFn` + - No direct replacement - - * - :class:`~qiskit.opflow.DictStateFn` - - No direct replacement. This class was used internally within opflow. + * - :class:`~qiskit.opflow.state_fns.DictStateFn` + - No direct replacement - - * - :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.state_fns.VectorStateFn` + - There's the :class:`~qiskit.quantum_info.Statevector` class and the :class:`~qiskit.quantum_info.StabilizerState` (Clifford based vector). - - * - :class:`~qiskit.opflow.SparseVectorStateFn` - - No direct replacement. This class was used internally within opflow. - - See :class:`~qiskit.opflow.VectorStateFn` + * - :class:`~qiskit.opflow.state_fns.SparseVectorStateFn` + - No direct replacement + - See :class:`~qiskit.opflow.state_fns.VectorStateFn` - * - :class:`~qiskit.opflow.OperatorStateFn` - - No direct replacement. This class was used internally within opflow. + * - :class:`~qiskit.opflow.state_fns.OperatorStateFn` + - No direct replacement - - * - :class:`~qiskit.opflow.CVaRMeasurement` - - Used in :class:`~qiskit.opflow.CVaRExpectation`. Functionality now covered by :class:`~SamplingEstimator`. See example in expectations. + * - :class:`~qiskit.opflow.state_fns.CVaRMeasurement` + - Used in :class:`~qiskit.opflow.expectations.CVaRExpectation`. Functionality now covered by :class:`~SamplingEstimator`. See example in expectations. - StateFn Examples @@ -435,21 +494,23 @@ StateFn Examples 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. +The role of this sub-module was to convert the operators into other opflow operator classes +(:class:`~qiskit.opflow.converters.TwoQubitReduction`, :class:`~qiskit.opflow.converters.PauliBasisChange`...). +In the case of the :class:`~qiskit.opflow.converters.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`` +.. list-table:: Migration of ``qiskit.opflow.converters.CircuitSampler`` :header-rows: 1 * - opflow - alternative - notes - * - :class:`~qiskit.opflow.CircuitSampler` + * - :class:`~qiskit.opflow.converters.CircuitSampler` - :class:`~primitives.Estimator` - @@ -491,15 +552,15 @@ Circuit Sampler Two Qubit Reduction ~~~~~~~~~~~~~~~~~~~~ -.. list-table:: Migration of ``qiskit.opflow.TwoQubitReduction`` +.. list-table:: Migration of ``qiskit.opflow.converters.TwoQubitReduction`` :header-rows: 1 * - opflow - alternative - notes - * - :class:`~qiskit.opflow.TwoQubitReduction` - - No direct replacement. + * - :class:`~qiskit.opflow.converters.TwoQubitReduction` + - No direct replacement - This class implements a chemistry-specific reduction for the ``ParityMapper`` class in ``qiskit-nature``. The general symmetry logic this mapper depends on has been refactored to other classes in :mod:`~qiskit.quantum_info`, so this specific :mod:`~qiskit.opflow` implementation is no longer necessary. @@ -514,14 +575,14 @@ Other Converters - alternative - notes - * - :class:`~qiskit.opflow.AbelianGrouper` - - No direct replacement. + * - :class:`~qiskit.opflow.converters.AbelianGrouper` + - No direct replacement - This class allowed a sum a of Pauli operators to be grouped. These type of groupings are now left to the primitives to handle (??). - * - :class:`~qiskit.opflow.DictToCircuitSum` - - No direct replacement. This class was used internally within opflow. + * - :class:`~qiskit.opflow.converters.DictToCircuitSum` + - No direct replacement - - * - :class:`~qiskit.opflow.PauliBasisChange` - - No direct replacement. This class was used internally within opflow. + * - :class:`~qiskit.opflow.converters.PauliBasisChange` + - No direct replacement - Evolutions @@ -532,17 +593,17 @@ including various methods for trotterization. The original opflow workflow for h 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 +The :class:`~qiskit.opflow.evolutions.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, +the class follows a recursive strategy that involves creating :class:`~qiskit.opflow.evolutions.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`). +perform the trotterization (either via a ``string``\, which is then inputted into a :class:`~qiskit.opflow.evolutions.TrotterizationFactory`, +or by supplying a method instance of :class:`~qiskit.opflow.evolutions.Trotter`, :class:`~qiskit.opflow.evolutions.Suzuki` or :class:`~qiskit.opflow.evolutions.QDrift`). -The different trotterization methods that extend :class:`~qiskit.opflow.TrotterizationBase` were migrated to :mod:`~qiskit.synthesis`, +The different trotterization methods that extend :class:`~qiskit.opflow.evolutions.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 +In this context, the job of the :class:`~qiskit.opflow.evolutions.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)`` @@ -575,7 +636,7 @@ In this context, the job of the :class:`~qiskit.opflow.PauliTrotterEvolution` cl 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, +In a similar manner, the :class:`~qiskit.opflow.evolutions.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. @@ -616,16 +677,16 @@ To summarize: - alternative - notes - * - :class:`~qiskit.opflow.TrotterizationFactory` - - This class is no longer necessary. + * - :class:`~qiskit.opflow.evolutions.TrotterizationFactory` + - - - * - :class:`~qiskit.opflow.Trotter` + * - :class:`~qiskit.opflow.evolutions.Trotter` - :class:`~synthesis.SuzukiTrotter` or :class:`~synthesis.LieTrotter` - - * - :class:`~qiskit.opflow.Suzuki` - - `:class:`~synthesis.SuzukiTrotter` + * - :class:`~qiskit.opflow.evolutions.Suzuki` + - `:class:~synthesis.SuzukiTrotter` - - * - :class:`~qiskit.opflow.QDrift` + * - :class:`~qiskit.opflow.evolutions.QDrift` - :class:`~synthesis.QDrift` - @@ -636,16 +697,16 @@ To summarize: - alternative - notes - * - :class:`~qiskit.opflow.EvolutionFactory` - - This class is no longer necessary. + * - :class:`~qiskit.opflow.evolutions.EvolutionFactory` - - * - :class:`~qiskit.opflow.EvolvedOp` + - + * - :class:`~qiskit.opflow.evolutions.EvolvedOp` - :class:`~synthesis.SuzukiTrotter` - - This class is no longer necessary - * - :class:`~qiskit.opflow.MatrixEvolution` + - + * - :class:`~qiskit.opflow.evolutions.MatrixEvolution` - :class:`~HamiltonianGate` - - * - :class:`~qiskit.opflow.PauliTrotterEvolution` + * - :class:`~qiskit.opflow.evolutions.PauliTrotterEvolution` - :class:`~PauliEvolutionGate` - @@ -663,16 +724,16 @@ Algorithm-Agnostic Expectations * - opflow - alternative - notes - * - :class:`~qiskit.opflow.ExpectationFactory` - - No direct replacement. + * - :class:`~qiskit.opflow.expectations.ExpectationFactory` + - No direct replacement - - * - :class:`~qiskit.opflow.AerPauliExpectation` - - Use :class:`~Estimator` primitive from ``qiskit_aer`` with ``approximation=True`` + * - :class:`~qiskit.opflow.expectations.AerPauliExpectation` + - Use :class:`~Estimator` primitive from ``qiskit_aer`` with ``approximation=True`` and ``shots=None`` - - * - :class:`~qiskit.opflow.MatrixExpectation` + * - :class:`~qiskit.opflow.expectations.MatrixExpectation` - Use :class:`~Estimator` primitive from ``qiskit`` instead (uses Statevector). - - * - :class:`~qiskit.opflow.PauliExpectation` + * - :class:`~qiskit.opflow.expectations.PauliExpectation` - Use any :class:`~Estimator` primitive. - From b7c1fccd99b523a5c1e83d0f5d245dcb93c63f82 Mon Sep 17 00:00:00 2001 From: ElePT Date: Thu, 9 Feb 2023 13:44:31 +0100 Subject: [PATCH 08/50] Dump changes from sphinx repo --- docs/migration_guides/opflow_migration.rst | 303 +++++++++------------ 1 file changed, 130 insertions(+), 173 deletions(-) diff --git a/docs/migration_guides/opflow_migration.rst b/docs/migration_guides/opflow_migration.rst index 1bfee564165..b17650c57bd 100644 --- a/docs/migration_guides/opflow_migration.rst +++ b/docs/migration_guides/opflow_migration.rst @@ -70,15 +70,14 @@ mind that :class:`~quantum_info.BaseOperator` is more generic than its opflow co 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`` +.. list-table:: :header-rows: 1 * - opflow - alternative - - notes * - :class:`~opflow.OperatorBase` - - :class:`~quantum_info.BaseOperator` - - For more information, check the :class:`~quantum_info.BaseOperator` source code. + - :class:`~quantum_info.BaseOperator`. + For more information, check out the :class:`~quantum_info.BaseOperator` source code. Operator Globals ---------------- @@ -97,62 +96,49 @@ These operations implicitly created operators of type :class:`~qiskit.opflow.Pa directly creating a corresponding :class:`~qiskit.quantum_info.SparsePauliOp`, as shown in the example below. -.. list-table:: Migration of ``qiskit.opflow.operator_globals`` (1/3) +.. list-table:: :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`. + - :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 + from qiskit.quantum_info import Pauli, SparsePauliOp 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) +.. list-table:: :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()`` - - This alternative is not too efficient, as ``.to_operator()`` is dense and scales exponentially with the size of the circuit. + - Append corresponding gate to :class:`~qiskit.QuantumCircuit` + :class:`~qiskit.quantum_info.Clifford` + ``.to_operator()``. + This alternative is not too efficient, as ``.to_operator()`` is dense and scales exponentially with the size of the circuit. * - - .. code-block:: python from qiskit.opflow import H op = H ^ H - - .. code-block:: python from qiskit import QuantumCircuit @@ -168,20 +154,17 @@ Common non-parametrized gates (Clifford) H = Clifford(qc).to_operator() op = H ^ H - - - 1-Qubit States ~~~~~~~~~~~~~~ -.. list-table:: Migration of ``qiskit.opflow.operator_globals`` (3/3) +.. list-table:: :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. + - :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. * - @@ -209,99 +192,83 @@ Common non-parametrized gates (Clifford) 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. +:mod:`~qiskit.opflow.list_ops` can now leverage elements from ``quantum_info``\'s :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 +Primitive Ops ~~~~~~~~~~~~~~ -When looking for an alternative to update your code, it is important to keep in mind that :class:`~qiskit.opflow.primitive_ops.PrimitiveOp` -acted as a factory to create the corresponding subclass class depending on the primitive used to initialize it: +:class:`~qiskit.opflow.primitive_ops.PrimitiveOp` is the :mod:`~qiskit.opflow.primitive_ops` module's base class. +It also acts as a factory to instantiate a corresponding sub-class depending on the computational primitive used +to initialize it: -.. list-table:: :class:`~qiskit.opflow.primitive_ops.PrimitiveOp` +.. list-table:: :header-rows: 1 - * - primitive sent to :class:`~qiskit.opflow.primitive_ops.PrimitiveOp` - - :class:`~qiskit.opflow.primitive_ops.PrimitiveOp` subclass returned + * - class passed to constructor + - sub-class returned * - :class:`~qiskit.quantum_info.Pauli` - :class:`~qiskit.opflow.primitive_ops.PauliOp` - * - :class:`~qiskit.circuit.Instruction` + * - :class:`~qiskit.circuit.Instruction`, :class:`~qiskit.circuit.QuantumCircuit` - :class:`~qiskit.opflow.primitive_ops.CircuitOp` - * - :class:`~qiskit.circuit.QuantumCircuit` - - :class:`~qiskit.opflow.primitive_ops.CircuitOp` - - * - ``list`` - - :class:`~qiskit.opflow.primitive_ops.MatrixOp` - - * - ``np.ndarray`` + * - ``list``, ``np.ndarray``, ``scipy.sparse.spmatrix``, :class:`~qiskit.quantum_info.Operator` - :class:`~qiskit.opflow.primitive_ops.MatrixOp` - * - ``scipy.sparse.spmatrix` - - :class:`~qiskit.opflow.primitive_ops.MatrixOp` +Thus, when migrating opflow code, it is important to look for alternatives to replace the specific subclasses that +might have been used "under the hood" in the original code: - * - :class:`~qiskit.quantum_info.Operator` - - :class:`~qiskit.opflow.primitive_ops.MatrixOp` - -.. list-table:: Migration of ``qiskit.opflow.primitive_ops`` +.. list-table:: :header-rows: 1 * - opflow - alternative - - notes * - :class:`~qiskit.opflow.primitive_ops.PrimitiveOp` - - No direct replacement. In most use-cases (representing operators), - the best alternative is :class:`~qiskit.quantum_info.Operator`` - - This class acted base class and **factory** for creating one of the operator sub-classes listed below. + - No direct replacement. In most use-cases (representing generic operators), + the alternative is :class:`~qiskit.quantum_info.Operator`. * - :class:`~qiskit.opflow.primitive_ops.CircuitOp` - - No direct replacement - - Can directly use :class:`~qiskit.QuantumCircuit` + - No direct replacement. :class:`~qiskit.QuantumCircuit` could be used as an alternative in some workflows. * - :class:`~qiskit.opflow.primitive_ops.MatrixOp` - - :class:`~qiskit.quantum_info.Operator`` - - + - :class:`~qiskit.quantum_info.Operator` * - :class:`~qiskit.opflow.primitive_ops.PauliOp` - - :class:`~qiskit.quantum_info.Pauli` - - For direct compatibility with classes in :mod:`~qiskit.algorithms`, + - :class:`~qiskit.quantum_info.Pauli`. For direct compatibility with classes in :mod:`~qiskit.algorithms`, wrap in :class:`~qiskit.quantum_info.SparsePauliOp` * - :class:`~qiskit.opflow.primitive_ops.PauliSumOp` - - :class:`~qiskit.quantum_info.SparsePauliOp` - - See example below + - :class:`~qiskit.quantum_info.SparsePauliOp`. See example below. * - :class:`~qiskit.opflow.primitive_ops.TaperedPauliSumOp` - This class was used to combine a :class:`~PauliSumOp` with its identified symmetries in one object. This functionality is not currently used in any workflow, and has been deprecated without replacement. - - See ``Z2Symmetries`` example for updated workflow + See ``Z2Symmetries`` example for updated workflow. * - :class:`~qiskit.opflow.primitive_ops.Z2Symmetries` - - :class:`~qiskit.quantum_info.Z2Symmetries` - - See example below. + - :class:`~qiskit.quantum_info.Z2Symmetries`. See example below. + +Primitive Ops Examples +~~~~~~~~~~~~~~~~~~~~~~ + +``PauliSumOp`` Example +####################### -PrimitiveOps Examples -~~~~~~~~~~~~~~~~~~~~~ .. list-table:: :header-rows: 1 * - opflow - alternative - - notes - - * - ``PauliSumOp`` **Example:** + * - .. code-block:: python from qiskit.opflow import PauliSumOp @@ -310,16 +277,21 @@ PrimitiveOps Examples 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:** +``Z2Symmetries`` and ``TaperedPauliSumOp`` Example +################################################## +.. list-table:: + :header-rows: 1 + * - opflow + - alternative + + * - .. code-block:: python from qiskit.opflow import PuliSumOp, Z2Symmetries, TaperedPauliSumOp @@ -338,7 +310,6 @@ PrimitiveOps Examples # can be represented as: tapered_op = TaperedPauliSumOp(primitive, z2_symmetries) - - .. code-block:: python from qiskit.quantum_info import SparsePauliOp, Z2Symmetries @@ -354,125 +325,134 @@ PrimitiveOps Examples ) z2_symmetries = Z2Symmetries.find_z2_symmetries(qubit_op) tapered_op = z2_symmetries.taper(qubit_op) - - - ListOps ~~~~~~~ The :mod:`~qiskit.opflow.list_ops` module contained classes for manipulating lists of :mod:`~qiskit.opflow.primitive_ops` or :mod:`~qiskit.opflow.state_fns`. The :mod:`~qiskit.quantum_info` alternatives for this functionality are the -:mod:`~qiskit.quantum_info.PauliList`, :mod:`~qiskit.quantum_info.SparsePauliOp` (for sums of ``Pauli``\s), -:mod:`~qiskit.quantum_info.PauliTable` (symplectic representation of lists of Pauli operators) and -:mod:`~qiskit.quantum_info.StabilizerTable` (symplectic representation of lists of state functions). +:class:`~qiskit.quantum_info.PauliList`, :class:`~qiskit.quantum_info.SparsePauliOp` (for sums of ``Pauli``\s), +:class:`~qiskit.quantum_info.PauliTable` (symplectic representation of lists of Pauli operators) and +:class:`~qiskit.quantum_info.StabilizerTable` (symplectic representation of lists of state functions). -.. list-table:: Migration of ``qiskit.opflow.list_ops`` +.. list-table:: :header-rows: 1 * - opflow - alternative - - notes * - :class:`~qiskit.opflow.list_ops.ListOp` - - No direct replacement - - This is the base class for operator lists. For ``Pauli`` operators, an - alternative is :mod:`~qiskit.quantum_info.PauliList`. + - No direct replacement. This is the base class for operator lists. For ``Pauli`` operators, the + alternative is :class:`~qiskit.quantum_info.PauliList`. For lists of state representations, an + option is :class:`~qiskit.quantum_info.StabilizerTable` * - :class:`~qiskit.opflow.list_ops.ComposedOp` - - No direct replacement - - + - No direct replacement. Current workflows do not require composition of states and operators within + one object. * - :class:`~qiskit.opflow.list_ops.SummedOp` - - No direct replacement - - For sums of ``Pauli`` operators, use :class:`~qiskit.quantum_info.SparsePauliOp`. + - No direct replacement. For ``Pauli`` operators, use :class:`~qiskit.quantum_info.SparsePauliOp`. * - :class:`~qiskit.opflow.list_ops.TensoredOp` - - No direct replacement - - For ``Pauli`` operators, use :class:`~qiskit.quantum_info.SparsePauliOp`. + - No direct replacement. For ``Pauli`` operators, use :class:`~qiskit.quantum_info.SparsePauliOp`. + +ListOp Examples +~~~~~~~~~~~~~~~ -ListOps Examples -~~~~~~~~~~~~~~~~~~~~~ +``ListOp`` Example: +#################### .. list-table:: :header-rows: 1 * - opflow - alternative - - notes - - * - ``ListOp`` **Example:** + * - .. code-block:: python - from qiskit.opflow import PauliSumOp - from qiskit.quantum_info import SparsePauliOp, Pauli - - qubit_op = PauliSumOp(SparsePauliOp(Pauli("XYZY"), coeffs=[2]), coeff=-3j) + from qiskit.opflow import Zero, One, ListOp + op1 = # list op with operators + op2 = ~ListOp([One, Zero]) @ ListOp([One, Zero]) - - .. code-block:: python - from qiskit.quantum_info import SparsePauliOp, Pauli - - qubit_op = SparsePauliOp(Pauli("XYZY")), coeff=-6j) + from qiskit.quantum_info import StabilizerTable - - State Functions --------------- -This module can be generally replaced by :class:`~qiskit.quantum_info.QuantumState`, with some differences to keep in mind: +The :mod:`~qiskit.opflow.state_fns` 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 +2. Algorithm-specific functionality has been migrated to the respective algorithm's module + +Similarly to :class:`~qiskit.opflow.primitive_ops.PrimitiveOp`, :class:`~qiskit.opflow.state_fns.StateFn` +acts as a factory to create the corresponding sub-class depending on the computational primitive used to initialize it: + +.. list-table:: + :header-rows: 1 + + * - class passed to constructor + - sub-class returned + + * - ``str``, ``dict``, :class:`~qiskit.result.Result` + - :class:`~qiskit.opflow.state_fns.DictStateFn` + + * - ``list``, ``np.ndarray``, :class:`~qiskit.quantum_info.Statevector` + - :class:`~qiskit.opflow.state_fns.VectorStateFn` + + * - :class:`~qiskit.circuit.QuantumCircuit`, :class:`~qiskit.circuit.Instruction` + - :class:`~qiskit.opflow.state_fns.CircuitStateFn` -.. list-table:: Migration of ``qiskit.opflow.state_fns`` + * - :class:`~qiskit.opflow.OperatorBase` + - :class:`~qiskit.opflow.state_fns.OperatorStateFn` + +This means that references to :class:`~qiskit.opflow.state_fns.StateFn` in opflow code should be examined to +identify the sub-class that is being used, to then look for an alternative. + +.. list-table:: :header-rows: 1 * - opflow - alternative - - notes * - :class:`~qiskit.opflow.state_fns.StateFn` - No direct replacement - - * - :class:`~qiskit.opflow.state_fns.CircuitStateFn` - No direct replacement - - * - :class:`~qiskit.opflow.state_fns.DictStateFn` - No direct replacement - - * - :class:`~qiskit.opflow.state_fns.VectorStateFn` - There's the :class:`~qiskit.quantum_info.Statevector` class and the :class:`~qiskit.quantum_info.StabilizerState` (Clifford based vector). - - * - :class:`~qiskit.opflow.state_fns.SparseVectorStateFn` - - No direct replacement - - See :class:`~qiskit.opflow.state_fns.VectorStateFn` + - No direct replacement. See :class:`~qiskit.opflow.state_fns.VectorStateFn` * - :class:`~qiskit.opflow.state_fns.OperatorStateFn` - No direct replacement - - + * - :class:`~qiskit.opflow.state_fns.CVaRMeasurement` - Used in :class:`~qiskit.opflow.expectations.CVaRExpectation`. Functionality now covered by :class:`~SamplingEstimator`. See example in expectations. - - StateFn Examples ~~~~~~~~~~~~~~~~~ +``StateFn`` Example: +#################### + .. list-table:: :header-rows: 1 * - opflow - alternative - - notes - - * - ``StateFn`` **Example:** + * - .. code-block:: python from qiskit.opflow import PuliSumOp @@ -481,16 +461,12 @@ StateFn Examples 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 ---------- @@ -503,19 +479,16 @@ Notably, this functionality has been replaced by the :mod:`~qiskit.primitives`. Circuit Sampler ~~~~~~~~~~~~~~~ -.. list-table:: Migration of ``qiskit.opflow.converters.CircuitSampler`` +.. list-table:: :header-rows: 1 * - opflow - alternative - - notes * - :class:`~qiskit.opflow.converters.CircuitSampler` - :class:`~primitives.Estimator` - - * - - .. code-block:: python from qiskit import QuantumCircuit @@ -534,7 +507,6 @@ Circuit Sampler expectation_value = expectation.eval().real - - .. code-block:: python from qiskit import QuantumCircuit @@ -548,42 +520,35 @@ Circuit Sampler estimator = Estimator() expectation_value = estimator.run(state, hamiltonian).result().values - - Two Qubit Reduction ~~~~~~~~~~~~~~~~~~~~ -.. list-table:: Migration of ``qiskit.opflow.converters.TwoQubitReduction`` +.. list-table:: :header-rows: 1 * - opflow - alternative - - notes * - :class:`~qiskit.opflow.converters.TwoQubitReduction` - - No direct replacement - - This class implements a chemistry-specific reduction for the ``ParityMapper`` class in ``qiskit-nature``. + - No direct replacement. This class implements a chemistry-specific reduction for the ``ParityMapper`` class in ``qiskit-nature``. The general symmetry logic this mapper depends on has been refactored to other classes in :mod:`~qiskit.quantum_info`, so this specific :mod:`~qiskit.opflow` implementation is no longer necessary. Other Converters ~~~~~~~~~~~~~~~~~ -.. list-table:: Migration of ``qiskit.opflow.converters`` +.. list-table:: :header-rows: 1 * - opflow - alternative - - notes * - :class:`~qiskit.opflow.converters.AbelianGrouper` - - No direct replacement - - This class allowed a sum a of Pauli operators to be grouped. These type of groupings are now left to the primitives to handle (??). + - No direct replacement. This class allowed a sum a of Pauli operators to be grouped. These type of groupings are now left to the primitives to handle. * - :class:`~qiskit.opflow.converters.DictToCircuitSum` - No direct replacement - - * - :class:`~qiskit.opflow.converters.PauliBasisChange` - No direct replacement - - Evolutions ---------- @@ -606,14 +571,13 @@ standalone use, but now are designed to be plugged into the :class:`~qiskit.synt In this context, the job of the :class:`~qiskit.opflow.evolutions.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)`` +.. list-table:: :header-rows: 1 * - opflow - alternative * - - .. code-block:: python from qiskit.opflow import Trotter, PauliTrotterEvolution, PauliSumOp @@ -623,7 +587,6 @@ In this context, the job of the :class:`~qiskit.opflow.evolutions.PauliTrotterEv evol_result = evolution.convert(hamiltonian.exp_i()) evolved_state = evol_result.to_circuit() - - .. code-block:: python from qiskit.quantum_info import SparsePauliOp @@ -640,14 +603,13 @@ In a similar manner, the :class:`~qiskit.opflow.evolutions.MatrixEvolution` clas 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)`` +.. list-table:: :header-rows: 1 * - opflow - alternative * - - .. code-block:: python from qiskit.opflow import MatrixEvolution, MatrixOp @@ -657,7 +619,6 @@ This class is no longer necessary, as the :class:`~HamiltonianGate`\s can be dir evol_result = evolution.convert(hamiltonian.exp_i()) evolved_state = evol_result.to_circuit() - - .. code-block:: python from qiskit.quantum_info import SparsePauliOp @@ -670,45 +631,42 @@ This class is no longer necessary, as the :class:`~HamiltonianGate`\s can be dir To summarize: -.. list-table:: Migration of ``qiskit.opflow.evolutions.trotterizations`` +.. list-table:: :header-rows: 1 * - opflow - alternative - - notes * - :class:`~qiskit.opflow.evolutions.TrotterizationFactory` - - - + * - :class:`~qiskit.opflow.evolutions.Trotter` - :class:`~synthesis.SuzukiTrotter` or :class:`~synthesis.LieTrotter` - - + * - :class:`~qiskit.opflow.evolutions.Suzuki` - - `:class:~synthesis.SuzukiTrotter` - - + - :class:~synthesis.SuzukiTrotter` + * - :class:`~qiskit.opflow.evolutions.QDrift` - :class:`~synthesis.QDrift` - - + .. list-table:: Migration of ``qiskit.opflow.evolutions.evolutions`` :header-rows: 1 * - opflow - alternative - - notes * - :class:`~qiskit.opflow.evolutions.EvolutionFactory` - - - + * - :class:`~qiskit.opflow.evolutions.EvolvedOp` - :class:`~synthesis.SuzukiTrotter` - - + * - :class:`~qiskit.opflow.evolutions.MatrixEvolution` - :class:`~HamiltonianGate` - - + * - :class:`~qiskit.opflow.evolutions.PauliTrotterEvolution` - :class:`~PauliEvolutionGate` - - Expectations ------------ @@ -723,19 +681,19 @@ Algorithm-Agnostic Expectations * - opflow - alternative - - notes + * - :class:`~qiskit.opflow.expectations.ExpectationFactory` - No direct replacement - - + * - :class:`~qiskit.opflow.expectations.AerPauliExpectation` - Use :class:`~Estimator` primitive from ``qiskit_aer`` with ``approximation=True`` and ``shots=None`` - - + * - :class:`~qiskit.opflow.expectations.MatrixExpectation` - Use :class:`~Estimator` primitive from ``qiskit`` instead (uses Statevector). - - + * - :class:`~qiskit.opflow.expectations.PauliExpectation` - Use any :class:`~Estimator` primitive. - - + TODO: ADD EXAMPLES! @@ -747,11 +705,10 @@ CVarExpectation * - opflow - alternative - - notes * - :class:`~qiskit.opflow.expectations.CVaRExpectation` - Functionality absorbed into corresponding VQE algorithm: :class:`~qiskit.algorithms.minimum_eigensolvers.SamplingVQE` - - + * - .. code-block:: python @@ -787,9 +744,9 @@ CVarExpectation 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). +Gradients +--------- +Replaced by the new :mod:`~qiskit.algorithms.gradients` module. You can see further details in the +algorithms migration guide. From 1bcf43549ce107f08aea73c3cf7b3d3142f25dce Mon Sep 17 00:00:00 2001 From: ElePT Date: Mon, 13 Feb 2023 10:52:14 +0100 Subject: [PATCH 09/50] Add twisties --- docs/migration_guides/opflow_migration.rst | 779 +++++++++++++-------- 1 file changed, 476 insertions(+), 303 deletions(-) diff --git a/docs/migration_guides/opflow_migration.rst b/docs/migration_guides/opflow_migration.rst index b17650c57bd..c8120bc555c 100644 --- a/docs/migration_guides/opflow_migration.rst +++ b/docs/migration_guides/opflow_migration.rst @@ -73,8 +73,8 @@ in some of the :class:`~quantum_info.BaseOperator` subclasses. .. list-table:: :header-rows: 1 - * - opflow - - alternative + * - Opflow + - Alternative * - :class:`~opflow.OperatorBase` - :class:`~quantum_info.BaseOperator`. For more information, check out the :class:`~quantum_info.BaseOperator` source code. @@ -95,103 +95,196 @@ The 1-qubit paulis were commonly used for quick testing of algorithms, as they c These operations implicitly created operators of type :class:`~qiskit.opflow.PauliSumOp`, and can be replaced by directly creating a corresponding :class:`~qiskit.quantum_info.SparsePauliOp`, as shown in the example below. - .. list-table:: :header-rows: 1 - * - opflow - - alternative + * - Opflow + - Alternative * - :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 +Example 1: Defining the XX operator +################################### +.. raw:: html - - - .. code-block:: python +
+ Opflow + +.. code-block:: python + + from qiskit.opflow import X + + operator = X ^ X + +.. raw:: html + +
+ +.. raw:: html + +
+ Alternative + +.. code-block:: python + + from qiskit.quantum_info import Pauli, SparsePauliOp + + X = Pauli('X') + op = X ^ X + + # equivalent to: + op = SparsePauliOp('XX') + +.. raw:: html + +
+ +Example 2: Defining a more complex operator +########################################### +.. raw:: html + +
+ Opflow + +.. code-block:: python + + from qiskit.opflow import I, X, Z + + op = 0.39 * (I ^ Z ^ I) + 0.5 * (I ^ X ^ X) + +.. raw:: html + +
+ +.. raw:: html + +
+ Alternative + +.. code-block:: python + + from qiskit.quantum_info import SparsePauliOp + + op = SparsePauliOp.from_list([("IZI", 0.39), ("IXX", 0.5)]) - from qiskit.quantum_info import Pauli, SparsePauliOp - X = Pauli('X') - op = X ^ X + # or... + op = SparsePauliOp.from_sparse_list([("Z", [1], 0.39), ("XX", [0,1], 0.5)], num_qubits = 3) - # equivalent to: - op = SparsePauliOp('XX') Common non-parametrized gates (Clifford) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. list-table:: :header-rows: 1 - * - opflow - - alternative + * - Opflow + - Alternative * - :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()``. - This alternative is not too efficient, as ``.to_operator()`` is dense and scales exponentially with the size of the circuit. + - Append corresponding gate to :class:`~qiskit.QuantumCircuit` and wrap in :class:`~qiskit.quantum_info.Clifford`. + To operate in a similar way to Opflow, you can then call ``Clifford.to_operator()``, but please note that + this alternative is not efficient, as ``.to_operator()`` is dense and scales exponentially with the size of the circuit. - * - - .. code-block:: python - from qiskit.opflow import H - op = H ^ H +Example 1: Defining the HH operator +################################### +.. raw:: html - - - .. code-block:: python +
+ Opflow - 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 +.. code-block:: python + + from qiskit.opflow import H + + op = H ^ H + +.. raw:: html + +
+ +.. raw:: html + +
+ Alternative + +.. 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 + +.. raw:: html + +
1-Qubit States ~~~~~~~~~~~~~~ .. list-table:: :header-rows: 1 - * - opflow - - alternative + * - Opflow + - Alternative * - :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. - * - +Example 1: Operating with 1-qubit states +######################################## +.. raw:: html - .. code-block:: python +
+ Opflow - from qiskit.opflow import Zero, One, Plus, Minus +.. code-block:: python - state1 = Zero ^ One - state2 = Plus ^ Minus + from qiskit.opflow import Zero, One, Plus, Minus - - + state1 = Zero ^ One + state2 = Plus ^ Minus - .. code-block:: python +.. raw:: html - from qiskit import QuantumCircuit - from qiskit.quantum_info import StabilizerState +
+ +.. raw:: html + +
+ Alternative + +.. code-block:: python + + from qiskit import QuantumCircuit + from qiskit.quantum_info import StabilizerState, Statevector - qc_zero = QuantumCircuit(1) - qc_one = copy(qc_zero) - qc_one.x(0) - state1 = StabilizerState(qc_zero) ^ StabilizerState(qc_one) + qc_zero = QuantumCircuit(1) + qc_one = qc_zero.copy() + 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) + qc_plus = qc_zero.copy() + qc_plus.h(0) + qc_minus = qc_one.copy() + qc_minus.h(0) + state2 = StabilizerState(qc_plus) ^ StabilizerState(qc_minus) + + # or... + state2 = Statevector(qc_plus) ^ Statevector(qc_minus) + +.. raw:: html + +
Primitive and List Ops ---------------------- @@ -209,8 +302,8 @@ to initialize it: .. list-table:: :header-rows: 1 - * - class passed to constructor - - sub-class returned + * - Class passed to constructor + - Sub-class returned * - :class:`~qiskit.quantum_info.Pauli` - :class:`~qiskit.opflow.primitive_ops.PauliOp` @@ -227,8 +320,8 @@ might have been used "under the hood" in the original code: .. list-table:: :header-rows: 1 - * - opflow - - alternative + * - Opflow + - Alternative * - :class:`~qiskit.opflow.primitive_ops.PrimitiveOp` - No direct replacement. In most use-cases (representing generic operators), @@ -256,75 +349,94 @@ might have been used "under the hood" in the original code: - :class:`~qiskit.quantum_info.Z2Symmetries`. See example below. -Primitive Ops Examples -~~~~~~~~~~~~~~~~~~~~~~ +Example 1: ``PauliSumOp`` +############################## -``PauliSumOp`` Example -####################### +.. raw:: html -.. list-table:: - :header-rows: 1 +
+ Opflow - * - opflow - - alternative +.. code-block:: python - * - - .. code-block:: python + from qiskit.opflow import PauliSumOp + from qiskit.quantum_info import SparsePauliOp, Pauli - from qiskit.opflow import PauliSumOp - from qiskit.quantum_info import SparsePauliOp, Pauli + qubit_op = PauliSumOp(SparsePauliOp(Pauli("XYZY"), coeffs=[2]), coeff=-3j) - qubit_op = PauliSumOp(SparsePauliOp(Pauli("XYZY"), coeffs=[2]), coeff=-3j) +.. raw:: html - - - .. code-block:: python +
- from qiskit.quantum_info import SparsePauliOp, Pauli +.. raw:: html - qubit_op = SparsePauliOp(Pauli("XYZY")), coeff=-6j) +
+ Alternative -``Z2Symmetries`` and ``TaperedPauliSumOp`` Example -################################################## -.. list-table:: - :header-rows: 1 +.. code-block:: python - * - opflow - - alternative - - * - - .. 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) + from qiskit.quantum_info import SparsePauliOp, Pauli + + qubit_op = SparsePauliOp(Pauli("XYZY")), coeff=-6j) + +.. raw:: html + +
+ +Example 2: ``Z2Symmetries`` and ``TaperedPauliSumOp`` +##################################################### + +.. raw:: html + +
+ Opflow + +.. 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) + +.. raw:: html + +
+ +.. raw:: html + +
+ Alternative + +.. 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) + +.. raw:: html + +
ListOps ~~~~~~~ @@ -338,8 +450,8 @@ or :mod:`~qiskit.opflow.state_fns`. The :mod:`~qiskit.quantum_info` alternatives .. list-table:: :header-rows: 1 - * - opflow - - alternative + * - Opflow + - Alternative * - :class:`~qiskit.opflow.list_ops.ListOp` - No direct replacement. This is the base class for operator lists. For ``Pauli`` operators, the @@ -356,29 +468,37 @@ or :mod:`~qiskit.opflow.state_fns`. The :mod:`~qiskit.quantum_info` alternatives * - :class:`~qiskit.opflow.list_ops.TensoredOp` - No direct replacement. For ``Pauli`` operators, use :class:`~qiskit.quantum_info.SparsePauliOp`. -ListOp Examples -~~~~~~~~~~~~~~~ -``ListOp`` Example: -#################### -.. list-table:: - :header-rows: 1 +Example 1: ``ListOp`` +##################### +.. raw:: html - * - opflow - - alternative +
+ Opflow - * - - .. code-block:: python +.. code-block:: python - from qiskit.opflow import Zero, One, ListOp + from qiskit.opflow import Zero, One, ListOp - op1 = # list op with operators - op2 = ~ListOp([One, Zero]) @ ListOp([One, Zero]) - - - .. code-block:: python + op1 = # list op with operators + op2 = ~ListOp([One, Zero]) @ ListOp([One, Zero]) - from qiskit.quantum_info import StabilizerTable +.. raw:: html +
+ +.. raw:: html + +
+ Alternative + +.. code-block:: python + + from qiskit.quantum_info import StabilizerTable + +.. raw:: html + +
State Functions --------------- @@ -395,8 +515,8 @@ acts as a factory to create the corresponding sub-class depending on the computa .. list-table:: :header-rows: 1 - * - class passed to constructor - - sub-class returned + * - Class passed to constructor + - Sub-class returned * - ``str``, ``dict``, :class:`~qiskit.result.Result` - :class:`~qiskit.opflow.state_fns.DictStateFn` @@ -416,8 +536,8 @@ identify the sub-class that is being used, to then look for an alternative. .. list-table:: :header-rows: 1 - * - opflow - - alternative + * - Opflow + - Alternative * - :class:`~qiskit.opflow.state_fns.StateFn` - No direct replacement @@ -440,32 +560,35 @@ identify the sub-class that is being used, to then look for an alternative. * - :class:`~qiskit.opflow.state_fns.CVaRMeasurement` - Used in :class:`~qiskit.opflow.expectations.CVaRExpectation`. Functionality now covered by :class:`~SamplingEstimator`. See example in expectations. -StateFn Examples -~~~~~~~~~~~~~~~~~ -``StateFn`` Example: -#################### +Example 1: ``StateFn`` +~~~~~~~~~~~~~~~~~~~~~~ -.. list-table:: - :header-rows: 1 +.. raw:: html - * - opflow - - alternative +
+ Opflow - * - - .. code-block:: python +.. code-block:: python - from qiskit.opflow import PuliSumOp - from qiskit.quantum_info import SparsePauliOp, Pauli + from opflow import StateFn - qubit_op = PauliSumOp(SparsePauliOp(Pauli("XYZY"), coeffs=[2]), coeff=-3j) +.. raw:: html - - - .. code-block:: python +
+ +.. raw:: html + +
+ Alternative + +.. code-block:: python - from qiskit.quantum_info import SparsePauliOp, Pauli + from opflow import StateFn - qubit_op = SparsePauliOp(Pauli("XYZY")), coeff=-6j) +.. raw:: html + +
Converters ---------- @@ -476,79 +599,76 @@ In the case of the :class:`~qiskit.opflow.converters.CircuitSampler`, it travers approximations of its state functions using a quantum backend. Notably, this functionality has been replaced by the :mod:`~qiskit.primitives`. -Circuit Sampler -~~~~~~~~~~~~~~~ - .. list-table:: :header-rows: 1 - * - opflow - - alternative + * - Opflow + - Alternative * - :class:`~qiskit.opflow.converters.CircuitSampler` - :class:`~primitives.Estimator` + * - :class:`~qiskit.opflow.converters.AbelianGrouper` + - No direct replacement. This class allowed a sum a of Pauli operators to be grouped. These type of groupings are now left to the primitives to handle. + * - :class:`~qiskit.opflow.converters.DictToCircuitSum` + - No direct replacement + * - :class:`~qiskit.opflow.converters.PauliBasisChange` + - No direct replacement + * - :class:`~qiskit.opflow.converters.TwoQubitReduction` + - No direct replacement. This class implements a chemistry-specific reduction for the ``ParityMapper`` class in ``qiskit-nature``. + The general symmetry logic this mapper depends on has been refactored to other classes in :mod:`~qiskit.quantum_info`, + so this specific :mod:`~qiskit.opflow` implementation is no longer necessary. - * - - .. code-block:: python - from qiskit import QuantumCircuit - from qiskit.opflow import X, Z, StateFn, CircuitStateFn, CircuitSampler - from qiskit.providers.aer import AerSimulator +Example 1: ``CircuitSampler`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - qc = QuantumCircuit(1) - qc.h(0) - state = CircuitStateFn(qc) - hamiltonian = X + Z +.. raw:: html - expr = StateFn(hamiltonian, is_measurement=True).compose(state) - backend = AerSimulator() - sampler = CircuitSampler(backend) - expectation = sampler.convert(expr) - expectation_value = expectation.eval().real +
+ Opflow - - - .. code-block:: python +.. code-block:: python - from qiskit import QuantumCircuit - from qiskit.primitives import Estimator - from qiskit.quantum_info import SparsePauliOp + from qiskit import QuantumCircuit + from qiskit.opflow import X, Z, StateFn, CircuitStateFn, CircuitSampler + from qiskit.providers.aer import AerSimulator - state = QuantumCircuit(1) - state.h(0) - hamiltonian = SparsePauliOp.from_list([('X', 1), ('Z',1)]) + qc = QuantumCircuit(1) + qc.h(0) + state = CircuitStateFn(qc) + hamiltonian = X + Z - estimator = Estimator() - expectation_value = estimator.run(state, hamiltonian).result().values + expr = StateFn(hamiltonian, is_measurement=True).compose(state) + backend = AerSimulator() + sampler = CircuitSampler(backend) + expectation = sampler.convert(expr) + expectation_value = expectation.eval().real +.. raw:: html -Two Qubit Reduction -~~~~~~~~~~~~~~~~~~~~ -.. list-table:: - :header-rows: 1 +
- * - opflow - - alternative +.. raw:: html - * - :class:`~qiskit.opflow.converters.TwoQubitReduction` - - No direct replacement. This class implements a chemistry-specific reduction for the ``ParityMapper`` class in ``qiskit-nature``. - The general symmetry logic this mapper depends on has been refactored to other classes in :mod:`~qiskit.quantum_info`, - so this specific :mod:`~qiskit.opflow` implementation is no longer necessary. +
+ Alternative -Other Converters -~~~~~~~~~~~~~~~~~ +.. code-block:: python -.. list-table:: - :header-rows: 1 + from qiskit import QuantumCircuit + from qiskit.primitives import Estimator + from qiskit.quantum_info import SparsePauliOp - * - opflow - - alternative + state = QuantumCircuit(1) + state.h(0) + hamiltonian = SparsePauliOp.from_list([('X', 1), ('Z',1)]) - * - :class:`~qiskit.opflow.converters.AbelianGrouper` - - No direct replacement. This class allowed a sum a of Pauli operators to be grouped. These type of groupings are now left to the primitives to handle. - * - :class:`~qiskit.opflow.converters.DictToCircuitSum` - - No direct replacement - * - :class:`~qiskit.opflow.converters.PauliBasisChange` - - No direct replacement + estimator = Estimator() + expectation_value = estimator.run(state, hamiltonian).result().values + +.. raw:: html + +
Evolutions ---------- @@ -569,73 +689,22 @@ The different trotterization methods that extend :class:`~qiskit.opflow.evolutio 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.evolutions.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:: - :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]) +(for example, :class:`~qiskit.algorithms.time_evolvers.TrotterQRTE`\). In a similar manner, the :class:`~qiskit.opflow.evolutions.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:: - :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: +Trotterizations +~~~~~~~~~~~~~~~ + .. list-table:: :header-rows: 1 - * - opflow - - alternative + * - Opflow + - Alternative * - :class:`~qiskit.opflow.evolutions.TrotterizationFactory` - @@ -649,12 +718,14 @@ To summarize: * - :class:`~qiskit.opflow.evolutions.QDrift` - :class:`~synthesis.QDrift` +Other Evolution Classes +~~~~~~~~~~~~~~~~~~~~~~~~ .. list-table:: Migration of ``qiskit.opflow.evolutions.evolutions`` :header-rows: 1 - * - opflow - - alternative + * - Opflow + - Alternative * - :class:`~qiskit.opflow.evolutions.EvolutionFactory` - @@ -668,6 +739,90 @@ To summarize: * - :class:`~qiskit.opflow.evolutions.PauliTrotterEvolution` - :class:`~PauliEvolutionGate` + +Example 1: Trotter evolution +############################ + +.. raw:: html + +
+ Opflow + +.. 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() +.. raw:: html + +
+ +.. raw:: html + +
+ Alternative + +.. 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]) + +.. raw:: html + +
+ + +Example 2: Matrix evolution +############################ + +.. raw:: html + +
+ Opflow + +.. 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() + +.. raw:: html + +
+ +.. raw:: html + +
+ Alternative + +.. 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]) + +.. raw:: html + +
+ + Expectations ------------ Expectations are converters which enable the computation of the expectation value of an observable with respect to some state function. @@ -679,8 +834,8 @@ Algorithm-Agnostic Expectations .. list-table:: Migration of ``qiskit.opflow.expectations`` :header-rows: 1 - * - opflow - - alternative + * - Opflow + - Alternative * - :class:`~qiskit.opflow.expectations.ExpectationFactory` - No direct replacement @@ -697,53 +852,71 @@ Algorithm-Agnostic Expectations TODO: ADD EXAMPLES! -CVarExpectation +CVaRExpectation ~~~~~~~~~~~~~~~ .. list-table:: Migration of ``qiskit.opflow.expectations.CVaRExpectation`` :header-rows: 1 - * - opflow - - alternative + * - Opflow + - Alternative * - :class:`~qiskit.opflow.expectations.CVaRExpectation` - - Functionality absorbed into corresponding VQE algorithm: :class:`~qiskit.algorithms.minimum_eigensolvers.SamplingVQE` + - Functionality migrated into new VQE algorithm: :class:`~qiskit.algorithms.minimum_eigensolvers.SamplingVQE` - * - - .. code-block:: python +Example 1: VQE with CVaR +######################## - from qiskit.opflow import CVaRExpectation, PauliSumOp +.. raw:: html - 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) +
+ Opflow - - +.. code-block:: python - .. code-block:: python + from qiskit.opflow import CVaRExpectation, PauliSumOp - from qiskit.quantum_info import SparsePauliOp + 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) + +.. raw:: html + +
+ +.. raw:: html + +
+ Alternative + +.. 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) + +.. raw:: html - 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 --------- From 889f211b20525270761e2d9ee3f46efb52df9ca8 Mon Sep 17 00:00:00 2001 From: ElePT Date: Mon, 13 Feb 2023 15:00:13 +0100 Subject: [PATCH 10/50] Update information, add examples --- docs/migration_guides/opflow_migration.rst | 243 ++++++++++++++++----- 1 file changed, 194 insertions(+), 49 deletions(-) diff --git a/docs/migration_guides/opflow_migration.rst b/docs/migration_guides/opflow_migration.rst index c8120bc555c..64e9977dd81 100644 --- a/docs/migration_guides/opflow_migration.rst +++ b/docs/migration_guides/opflow_migration.rst @@ -85,7 +85,8 @@ Opflow provided shortcuts to define common single qubit states, operators, and n :mod:`~qiskit.opflow.operator_globals` module. These were mainly used for didactic purposes or quick prototyping, 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`. +:mod:`~qiskit.quantum_info` class: :class:`~qiskit.quantum_info.Pauli`, :class:`~qiskit.quantum_info.Clifford` or +:class:`~qiskit.quantum_info.Statevector`. 1-Qubit Paulis @@ -180,10 +181,13 @@ Common non-parametrized gates (Clifford) * - Opflow - Alternative - * - :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` and wrap in :class:`~qiskit.quantum_info.Clifford`. - To operate in a similar way to Opflow, you can then call ``Clifford.to_operator()``, but please note that - this alternative is not efficient, as ``.to_operator()`` is dense and scales exponentially with the size of the circuit. + * - :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`. ``quantum_info`` + :class:`~qiskit.quantum_info.Operator`\s can be also directly constructed from quantum circuits. + Another alternative is to wrap the circuit in :class:`~qiskit.quantum_info.Clifford` and call + ``Clifford.to_operator()``. Please note that constructing ``quantum_info`` operators from circuits is not + efficient, as it is a dense operation and scales exponentially with the size of the circuit. Example 1: Defining the HH operator @@ -211,7 +215,7 @@ Example 1: Defining the HH operator .. code-block:: python from qiskit import QuantumCircuit - from qiskit.quantum_info import Clifford + from qiskit.quantum_info import Clifford, Operator qc = QuantumCircuit(2) qc.h(0) @@ -224,6 +228,12 @@ Example 1: Defining the HH operator H = Clifford(qc).to_operator() op = H ^ H + # or, directly + qc = QuantumCircuit(2) + qc.h(0) + qc.h(1) + op = Operator(qc) + .. raw:: html
@@ -237,11 +247,12 @@ Example 1: Defining the HH operator - Alternative * - :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. + - :class:`~qiskit.quantum_info.Statevector` or simply :class:`~qiskit.QuantumCircuit`, depending on the use case. + For efficient simulation of stabilizer states, ``quantum_info`` includes a :class:`~qiskit.quantum_info.StabilizerState` + class. See API ref. for more info. -Example 1: Operating with 1-qubit states -######################################## +Example 1 +########## .. raw:: html
@@ -251,6 +262,7 @@ Example 1: Operating with 1-qubit states from qiskit.opflow import Zero, One, Plus, Minus + # Zero, One, Plus, Minus are all stabilizer states state1 = Zero ^ One state2 = Plus ^ Minus @@ -271,16 +283,15 @@ Example 1: Operating with 1-qubit states qc_zero = QuantumCircuit(1) qc_one = qc_zero.copy() qc_one.x(0) - state1 = StabilizerState(qc_zero) ^ StabilizerState(qc_one) + state1 = Statevector(qc_zero) ^ Statevector(qc_one) qc_plus = qc_zero.copy() qc_plus.h(0) qc_minus = qc_one.copy() qc_minus.h(0) + state2 = StabilizerState(qc_plus) ^ StabilizerState(qc_minus) - # or... - state2 = Statevector(qc_plus) ^ Statevector(qc_minus) .. raw:: html @@ -324,11 +335,11 @@ might have been used "under the hood" in the original code: - Alternative * - :class:`~qiskit.opflow.primitive_ops.PrimitiveOp` - - No direct replacement. In most use-cases (representing generic operators), + - No alternative provided. In most use-cases (representing generic operators), the alternative is :class:`~qiskit.quantum_info.Operator`. * - :class:`~qiskit.opflow.primitive_ops.CircuitOp` - - No direct replacement. :class:`~qiskit.QuantumCircuit` could be used as an alternative in some workflows. + - No alternative provided. :class:`~qiskit.QuantumCircuit` could be used as an alternative in some workflows. * - :class:`~qiskit.opflow.primitive_ops.MatrixOp` - :class:`~qiskit.quantum_info.Operator` @@ -443,9 +454,7 @@ ListOps The :mod:`~qiskit.opflow.list_ops` module contained classes for manipulating lists of :mod:`~qiskit.opflow.primitive_ops` or :mod:`~qiskit.opflow.state_fns`. The :mod:`~qiskit.quantum_info` alternatives for this functionality are the -:class:`~qiskit.quantum_info.PauliList`, :class:`~qiskit.quantum_info.SparsePauliOp` (for sums of ``Pauli``\s), -:class:`~qiskit.quantum_info.PauliTable` (symplectic representation of lists of Pauli operators) and -:class:`~qiskit.quantum_info.StabilizerTable` (symplectic representation of lists of state functions). +:class:`~qiskit.quantum_info.PauliList`, :class:`~qiskit.quantum_info.SparsePauliOp` (for sums of ``Pauli``\s). .. list-table:: :header-rows: 1 @@ -454,13 +463,13 @@ or :mod:`~qiskit.opflow.state_fns`. The :mod:`~qiskit.quantum_info` alternatives - Alternative * - :class:`~qiskit.opflow.list_ops.ListOp` - - No direct replacement. This is the base class for operator lists. For ``Pauli`` operators, the - alternative is :class:`~qiskit.quantum_info.PauliList`. For lists of state representations, an - option is :class:`~qiskit.quantum_info.StabilizerTable` + - No direct replacement. This is the base class for operator lists. In general, these could be replaced with + Python ``list``\s. For ``Pauli`` operators, there are a few alternatives, depending on the use-case. + One alternative is :class:`~qiskit.quantum_info.PauliList`. * - :class:`~qiskit.opflow.list_ops.ComposedOp` - No direct replacement. Current workflows do not require composition of states and operators within - one object. + one object (no lazy evaluation). * - :class:`~qiskit.opflow.list_ops.SummedOp` - No direct replacement. For ``Pauli`` operators, use :class:`~qiskit.quantum_info.SparsePauliOp`. @@ -469,8 +478,11 @@ or :mod:`~qiskit.opflow.state_fns`. The :mod:`~qiskit.quantum_info` alternatives - No direct replacement. For ``Pauli`` operators, use :class:`~qiskit.quantum_info.SparsePauliOp`. -Example 1: ``ListOp`` -##################### +Example 1: ``SummedOp`` +######################## + +See application in MatrixExpectation example. + .. raw:: html
@@ -478,10 +490,8 @@ Example 1: ``ListOp`` .. code-block:: python - from qiskit.opflow import Zero, One, ListOp + from qiskit.opflow import - op1 = # list op with operators - op2 = ~ListOp([One, Zero]) @ ListOp([One, Zero]) .. raw:: html @@ -494,7 +504,8 @@ Example 1: ``ListOp`` .. code-block:: python - from qiskit.quantum_info import StabilizerTable + from qiskit import QuantumCircuit + .. raw:: html @@ -540,29 +551,32 @@ identify the sub-class that is being used, to then look for an alternative. - Alternative * - :class:`~qiskit.opflow.state_fns.StateFn` - - No direct replacement + - No direct replacement. This class was used to create instances of the classes listed below. * - :class:`~qiskit.opflow.state_fns.CircuitStateFn` - - No direct replacement + - No direct replacement. :class:`~qiskit.circuit.QuantumCircuit` can be used directly instead. * - :class:`~qiskit.opflow.state_fns.DictStateFn` - - No direct replacement + - No direct replacement. ?? * - :class:`~qiskit.opflow.state_fns.VectorStateFn` - - There's the :class:`~qiskit.quantum_info.Statevector` class and the :class:`~qiskit.quantum_info.StabilizerState` (Clifford based vector). + - This class can be replaced with :class:`~qiskit.quantum_info.Statevector` or + :class:`~qiskit.quantum_info.StabilizerState` (for Clifford-based vectors). * - :class:`~qiskit.opflow.state_fns.SparseVectorStateFn` - - No direct replacement. See :class:`~qiskit.opflow.state_fns.VectorStateFn` + - :class:`~qiskit.quantum_info.Statevector` is not sparse, but for stabilizer states, + :class:`~qiskit.quantum_info.StabilizerState` can simulate them efficiently. * - :class:`~qiskit.opflow.state_fns.OperatorStateFn` - - No direct replacement + - No direct replacement. * - :class:`~qiskit.opflow.state_fns.CVaRMeasurement` - - Used in :class:`~qiskit.opflow.expectations.CVaRExpectation`. Functionality now covered by :class:`~SamplingEstimator`. See example in expectations. + - Used in :class:`~qiskit.opflow.expectations.CVaRExpectation`. + Functionality now covered by :class:`~SamplingEstimator`. See example in expectations. -Example 1: ``StateFn`` -~~~~~~~~~~~~~~~~~~~~~~ +Example 1: Applying an operator to a state +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. raw:: html @@ -571,7 +585,17 @@ Example 1: ``StateFn`` .. code-block:: python - from opflow import StateFn + from qiskit.opflow import StateFn, X, Y + + qc = QuantumCircuit(2) + op = X ^ Y + state = StateFn(qc) + + comp = ~state @ op + # returns a CircuitStateFn + + eval = comp.eval() + # returns a VectorStateFn (Statevector) .. raw:: html @@ -584,12 +608,24 @@ Example 1: ``StateFn`` .. code-block:: python - from opflow import StateFn + from qiskit import QuantumCircuit + from qiskit.quantum_info import SparsePauliOp, Statevector + + qc = QuantumCircuit(2) + op = SparsePauliOp("XY") + state = Statevector(qc) + + eval = state.evolve(operator) + # returns a Statevector .. raw:: html
+ +See more applied examples in expectations and converters. + + Converters ---------- @@ -707,13 +743,13 @@ Trotterizations - Alternative * - :class:`~qiskit.opflow.evolutions.TrotterizationFactory` - - + - No direct replacement. This class was used to create instances of one of the classes listed below. * - :class:`~qiskit.opflow.evolutions.Trotter` - :class:`~synthesis.SuzukiTrotter` or :class:`~synthesis.LieTrotter` * - :class:`~qiskit.opflow.evolutions.Suzuki` - - :class:~synthesis.SuzukiTrotter` + - :class:`~synthesis.SuzukiTrotter` * - :class:`~qiskit.opflow.evolutions.QDrift` - :class:`~synthesis.QDrift` @@ -728,10 +764,10 @@ Other Evolution Classes - Alternative * - :class:`~qiskit.opflow.evolutions.EvolutionFactory` - - + - No direct replacement. This class was used to create instances of one of the classes listed below. * - :class:`~qiskit.opflow.evolutions.EvolvedOp` - - :class:`~synthesis.SuzukiTrotter` + - No direct replacement. The workflow no longer requires a specific operator for evolutions. * - :class:`~qiskit.opflow.evolutions.MatrixEvolution` - :class:`~HamiltonianGate` @@ -838,24 +874,133 @@ Algorithm-Agnostic Expectations - Alternative * - :class:`~qiskit.opflow.expectations.ExpectationFactory` - - No direct replacement + - No direct replacement. This class was used to create instances of one of the classes listed below. * - :class:`~qiskit.opflow.expectations.AerPauliExpectation` - - Use :class:`~Estimator` primitive from ``qiskit_aer`` with ``approximation=True`` and ``shots=None`` + - Use :class:`~Estimator` primitive from ``qiskit_aer`` with ``approximation=True`` and ``shots=None`` as ``run_options``. + See example below. * - :class:`~qiskit.opflow.expectations.MatrixExpectation` - - Use :class:`~Estimator` primitive from ``qiskit`` instead (uses Statevector). + - Use :class:`~Estimator` primitive from ``qiskit`` (if no shots are set, it performs an exact Statevector calculation). See example below. * - :class:`~qiskit.opflow.expectations.PauliExpectation` - - Use any :class:`~Estimator` primitive. + - Use any :class:`~Estimator` primitive from ``qiskit``. + + +Example 1: Aer Pauli Expectation +################################# + +.. raw:: html + +
+ Opflow + +.. code-block:: python + from qiskit.opflow import Z, CX, H, I, Zero, StateFn, AerPauliExpectation, CircuitSampler + from qiskit.utils import QuantumInstance + from qiskit_aer import Aer -TODO: ADD EXAMPLES! + backend = Aer.get_backend("aer_simulator") + q_instance = QuantumInstance(backend) + + sampler = CircuitSampler(q_instance, attach_results=True) + expect = AerPauliExpectation() + + op = Z ^ Z + wvf = CX @ (H ^ I) @ Zero + + converted_meas = expect.convert(~StateFn(op) @ wvf) + expect_values = sampler.convert(converted_meas).eval() + +.. raw:: html + +
+ +.. raw:: html + +
+ Alternative + +.. code-block:: python + + from qiskit.quantum_info import SparsePauliOp + from qiskit import QuantumCircuit + from qiskit_aer.primitives import Estimator as AerEstimator + + estimator = AerEstimator(run_options={"approximation": True, "shots": None}) + + op = SparsePauliOp.from_list([("ZZ", 1)]) + wvf = QuantumCircuit(2) + wvf.h(1) + wvf.cx(0,1) + + expect_values = estimator.run(wvf,op).result().values + +.. raw:: html + +
+ +Example 2: Matrix Expectation +################################# + +.. raw:: html + +
+ Opflow + +.. code-block:: python + + from qiskit_aer import Aer + from qiskit.opflow import X, H, I, MatrixExpectation, ListOp, StateFn + from qiskit.utils import QuantumInstance + + backend = Aer.get_backend("statevector_simulator") + q_instance = QuantumInstance(backend) + sampler = CircuitSampler(q_instance, attach_results=True) + expect = MatrixExpectation() + + mixed_ops = ListOp([X.to_matrix_op(), H]) + converted_meas = expect.convert(~StateFn(mixed_ops)) + + plus_mean = converted_meas @ Plus + values_plus = sampler.convert(plus_mean).eval() + +.. raw:: html + +
+ +.. raw:: html + +
+ Alternative + +.. code-block:: python + + from qiskit.primitives import Estimator + from qiskit.quantum_info import SparsePauliOp + from qiskit.quantum_info import Clifford + + X = SparsePauliOp("X") + + qc = QuantumCircuit(1) + qc.h(0) + H = Clifford(qc).to_operator() + + plus = QuantumCircuit(1) + plus.h(0) + + estimator = Estimator() + values_plus = estimator.run([plus, plus], [X, H]).result().values + +.. raw:: html + +
CVaRExpectation ~~~~~~~~~~~~~~~ -.. list-table:: Migration of ``qiskit.opflow.expectations.CVaRExpectation`` +.. list-table:: :header-rows: 1 * - Opflow From 9c188e2ee0129dacd1a5c922ae69d1db8f25ebc4 Mon Sep 17 00:00:00 2001 From: ElePT Date: Mon, 13 Feb 2023 16:10:28 +0100 Subject: [PATCH 11/50] Formatting changes --- docs/migration_guides/opflow_migration.rst | 85 +++++++++++++++++----- 1 file changed, 65 insertions(+), 20 deletions(-) diff --git a/docs/migration_guides/opflow_migration.rst b/docs/migration_guides/opflow_migration.rst index 64e9977dd81..bf80a2eed10 100644 --- a/docs/migration_guides/opflow_migration.rst +++ b/docs/migration_guides/opflow_migration.rst @@ -11,7 +11,7 @@ The :mod:`~qiskit.opflow` module was originally introduced as a layer between ci for quantum algorithms research and development. The recent introduction of the :mod:`~qiskit.primitives` provided a new interface for interacting with backends that disrupted -the "opflow way" of doing things. Now, instead of +the "opflow way of doing things". 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 @@ -34,7 +34,9 @@ This guide traverses the opflow submodules and provides either a direct alternat TL;DR ----- -The new :mod:`~qiskit.primitives` have superseded most of the :mod:`~qiskit.opflow` functionality. Thus, the latter is being deprecated. +.. note:: + + The new :mod:`~qiskit.primitives` have superseded most of the :mod:`~qiskit.opflow` functionality. Thus, the latter is being deprecated. Index ----- @@ -60,15 +62,10 @@ This guide covers the migration from these opflow sub-modules: Operator Base Class ------------------- +Back to `Index`_ 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. +mind that :class:`~quantum_info.BaseOperator` is more generic than its opflow counterpart. .. list-table:: :header-rows: 1 @@ -76,11 +73,25 @@ in some of the :class:`~quantum_info.BaseOperator` subclasses. * - Opflow - Alternative * - :class:`~opflow.OperatorBase` - - :class:`~quantum_info.BaseOperator`. - For more information, check out the :class:`~quantum_info.BaseOperator` source code. + - :class:`~quantum_info.BaseOperator` + +.. tip:: + + Please note 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. + + See API reference for more information. + Operator Globals ---------------- +Back to `Index`_ + Opflow provided shortcuts to define common single qubit states, operators, and non-parametrized gates in the :mod:`~qiskit.opflow.operator_globals` module. @@ -91,6 +102,8 @@ These were mainly used for didactic purposes or quick prototyping, and can easil 1-Qubit Paulis ~~~~~~~~~~~~~~ +Back to `Index`_ + The 1-qubit paulis were commonly used for quick testing of algorithms, as they could be combined to create more complex operators (for example, ``0.39 * (I ^ Z) + 0.5 * (X ^ X)``). These operations implicitly created operators of type :class:`~qiskit.opflow.PauliSumOp`, and can be replaced by @@ -102,8 +115,11 @@ directly creating a corresponding :class:`~qiskit.quantum_info.SparsePauliOp`, a * - Opflow - Alternative * - :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`. + - :class:`~qiskit.quantum_info.Pauli` + + .. note:: + + For direct compatibility with classes in :mod:`~qiskit.algorithms`, wrap in :class:`~qiskit.quantum_info.SparsePauliOp`. Example 1: Defining the XX operator ################################### @@ -172,9 +188,14 @@ Example 2: Defining a more complex operator # or... op = SparsePauliOp.from_sparse_list([("Z", [1], 0.39), ("XX", [0,1], 0.5)], num_qubits = 3) +.. raw:: html + +
Common non-parametrized gates (Clifford) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Back to `Index`_ + .. list-table:: :header-rows: 1 @@ -186,8 +207,12 @@ Common non-parametrized gates (Clifford) - Append corresponding gate to :class:`~qiskit.QuantumCircuit`. ``quantum_info`` :class:`~qiskit.quantum_info.Operator`\s can be also directly constructed from quantum circuits. Another alternative is to wrap the circuit in :class:`~qiskit.quantum_info.Clifford` and call - ``Clifford.to_operator()``. Please note that constructing ``quantum_info`` operators from circuits is not - efficient, as it is a dense operation and scales exponentially with the size of the circuit. + ``Clifford.to_operator()``. + + .. note:: + + Constructing ``quantum_info`` operators from circuits is not efficient, as it is a dense operation and + scales exponentially with the size of the circuit, use with care. Example 1: Defining the HH operator @@ -240,6 +265,8 @@ Example 1: Defining the HH operator 1-Qubit States ~~~~~~~~~~~~~~ +Back to `Index`_ + .. list-table:: :header-rows: 1 @@ -248,8 +275,10 @@ Example 1: Defining the HH operator * - :class:`~qiskit.opflow.Zero`, :class:`~qiskit.opflow.One`, :class:`~qiskit.opflow.Plus`, :class:`~qiskit.opflow.Minus` - :class:`~qiskit.quantum_info.Statevector` or simply :class:`~qiskit.QuantumCircuit`, depending on the use case. - For efficient simulation of stabilizer states, ``quantum_info`` includes a :class:`~qiskit.quantum_info.StabilizerState` - class. See API ref. for more info. + + .. note:: + + For efficient simulation of stabilizer states, ``quantum_info`` includes a :class:`~qiskit.quantum_info.StabilizerState` class. See API ref. for more info. Example 1 ########## @@ -297,8 +326,11 @@ Example 1 + Primitive and List Ops ---------------------- +Back to `Index`_ + 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 ``quantum_info``\'s :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 @@ -306,6 +338,8 @@ opflow components. Primitive Ops ~~~~~~~~~~~~~~ +Back to `Index`_ + :class:`~qiskit.opflow.primitive_ops.PrimitiveOp` is the :mod:`~qiskit.opflow.primitive_ops` module's base class. It also acts as a factory to instantiate a corresponding sub-class depending on the computational primitive used to initialize it: @@ -451,6 +485,7 @@ Example 2: ``Z2Symmetries`` and ``TaperedPauliSumOp`` ListOps ~~~~~~~ +Back to `Index`_ The :mod:`~qiskit.opflow.list_ops` module contained classes for manipulating lists of :mod:`~qiskit.opflow.primitive_ops` or :mod:`~qiskit.opflow.state_fns`. The :mod:`~qiskit.quantum_info` alternatives for this functionality are the @@ -513,6 +548,7 @@ See application in MatrixExpectation example. State Functions --------------- +Back to `Index`_ The :mod:`~qiskit.opflow.state_fns` module can be generally replaced by :class:`~qiskit.quantum_info.QuantumState`, with some differences to keep in mind: @@ -628,6 +664,7 @@ See more applied examples in expectations and converters. Converters ---------- +Back to `Index`_ The role of this sub-module was to convert the operators into other opflow operator classes (:class:`~qiskit.opflow.converters.TwoQubitReduction`, :class:`~qiskit.opflow.converters.PauliBasisChange`...). @@ -708,6 +745,7 @@ Example 1: ``CircuitSampler`` Evolutions ---------- +Back to `Index`_ 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 @@ -735,6 +773,7 @@ To summarize: Trotterizations ~~~~~~~~~~~~~~~ +Back to `Index`_ .. list-table:: :header-rows: 1 @@ -756,8 +795,9 @@ Trotterizations Other Evolution Classes ~~~~~~~~~~~~~~~~~~~~~~~~ +Back to `Index`_ -.. list-table:: Migration of ``qiskit.opflow.evolutions.evolutions`` +.. list-table:: :header-rows: 1 * - Opflow @@ -858,16 +898,18 @@ Example 2: Matrix evolution - Expectations ------------ +Back to `Index`_ + 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 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Back to `Index`_ -.. list-table:: Migration of ``qiskit.opflow.expectations`` +.. list-table:: :header-rows: 1 * - Opflow @@ -999,6 +1041,7 @@ Example 2: Matrix Expectation CVaRExpectation ~~~~~~~~~~~~~~~ +Back to `Index`_ .. list-table:: :header-rows: 1 @@ -1065,6 +1108,8 @@ Example 1: VQE with CVaR Gradients --------- +Back to `Index`_ + Replaced by the new :mod:`~qiskit.algorithms.gradients` module. You can see further details in the algorithms migration guide. From ee0355735639a5b9c3d18e870e4e84bc875a7167 Mon Sep 17 00:00:00 2001 From: ElePT Date: Mon, 13 Feb 2023 16:32:30 +0100 Subject: [PATCH 12/50] Simplify intro --- docs/migration_guides/opflow_migration.rst | 53 ++-------------------- 1 file changed, 4 insertions(+), 49 deletions(-) diff --git a/docs/migration_guides/opflow_migration.rst b/docs/migration_guides/opflow_migration.rst index bf80a2eed10..56a81b5af91 100644 --- a/docs/migration_guides/opflow_migration.rst +++ b/docs/migration_guides/opflow_migration.rst @@ -10,24 +10,12 @@ 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 recent introduction of the :mod:`~qiskit.primitives` provided a new interface for interacting with backends that disrupted -the "opflow way of doing things". Now, instead of +The recent release of the :mod:`~qiskit.primitives` introduced 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. +that previously relied on opflow; encouraging 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. 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. @@ -513,39 +501,6 @@ or :mod:`~qiskit.opflow.state_fns`. The :mod:`~qiskit.quantum_info` alternatives - No direct replacement. For ``Pauli`` operators, use :class:`~qiskit.quantum_info.SparsePauliOp`. -Example 1: ``SummedOp`` -######################## - -See application in MatrixExpectation example. - -.. raw:: html - -
- Opflow - -.. code-block:: python - - from qiskit.opflow import - - -.. raw:: html - -
- -.. raw:: html - -
- Alternative - -.. code-block:: python - - from qiskit import QuantumCircuit - - -.. raw:: html - -
- State Functions --------------- Back to `Index`_ @@ -593,7 +548,7 @@ identify the sub-class that is being used, to then look for an alternative. - No direct replacement. :class:`~qiskit.circuit.QuantumCircuit` can be used directly instead. * - :class:`~qiskit.opflow.state_fns.DictStateFn` - - No direct replacement. ?? + - No direct replacement. * - :class:`~qiskit.opflow.state_fns.VectorStateFn` - This class can be replaced with :class:`~qiskit.quantum_info.Statevector` or From 2e2e97230608ee48c03ef5379b75cd891100cb66 Mon Sep 17 00:00:00 2001 From: ElePT Date: Mon, 13 Feb 2023 17:27:54 +0100 Subject: [PATCH 13/50] Add links --- docs/migration_guides/opflow_migration.rst | 174 ++++++++++++--------- 1 file changed, 98 insertions(+), 76 deletions(-) diff --git a/docs/migration_guides/opflow_migration.rst b/docs/migration_guides/opflow_migration.rst index 56a81b5af91..c0bda4e2462 100644 --- a/docs/migration_guides/opflow_migration.rst +++ b/docs/migration_guides/opflow_migration.rst @@ -15,7 +15,7 @@ preparing a circuit to execute with a ``backend.run()`` type of method, the algo :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; encouraging 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. +the :mod:`~qiskit.primitives` interface and the :mod:`~qiskit.quantum_info` module. 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. @@ -24,7 +24,8 @@ TL;DR ----- .. note:: - The new :mod:`~qiskit.primitives` have superseded most of the :mod:`~qiskit.opflow` functionality. Thus, the latter is being deprecated. + The new :mod:`~qiskit.primitives`, in combination with the :mod:`~qiskit.quantum_info` module, have superseded + functionality of :mod:`~qiskit.opflow`. Thus, the latter is being deprecated. Index ----- @@ -50,7 +51,7 @@ This guide covers the migration from these opflow sub-modules: Operator Base Class ------------------- -Back to `Index`_ +*Back to* `Index`_ 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. @@ -78,7 +79,7 @@ mind that :class:`~quantum_info.BaseOperator` is more generic than its opflow co Operator Globals ---------------- -Back to `Index`_ +*Back to* `Index`_ Opflow provided shortcuts to define common single qubit states, operators, and non-parametrized gates in the :mod:`~qiskit.opflow.operator_globals` module. @@ -90,12 +91,12 @@ These were mainly used for didactic purposes or quick prototyping, and can easil 1-Qubit Paulis ~~~~~~~~~~~~~~ -Back to `Index`_ +*Back to* `Index`_ The 1-qubit paulis were commonly used for quick testing of algorithms, as they could be combined to create more complex operators (for example, ``0.39 * (I ^ Z) + 0.5 * (X ^ X)``). These operations implicitly created operators of type :class:`~qiskit.opflow.PauliSumOp`, and can be replaced by -directly creating a corresponding :class:`~qiskit.quantum_info.SparsePauliOp`, as shown in the example below. +directly creating a corresponding :class:`~qiskit.quantum_info.SparsePauliOp`, as shown in the example :ref:`below <1_q_pauli>`. .. list-table:: :header-rows: 1 @@ -105,10 +106,13 @@ directly creating a corresponding :class:`~qiskit.quantum_info.SparsePauliOp`, a * - :class:`~qiskit.opflow.X`, :class:`~qiskit.opflow.Y`, :class:`~qiskit.opflow.Z`, :class:`~qiskit.opflow.I` - :class:`~qiskit.quantum_info.Pauli` - .. note:: + .. tip:: For direct compatibility with classes in :mod:`~qiskit.algorithms`, wrap in :class:`~qiskit.quantum_info.SparsePauliOp`. + +.. _1_q_pauli: + Example 1: Defining the XX operator ################################### .. raw:: html @@ -182,7 +186,7 @@ Example 2: Defining a more complex operator Common non-parametrized gates (Clifford) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Back to `Index`_ +*Back to* `Index`_ .. list-table:: :header-rows: 1 @@ -253,7 +257,7 @@ Example 1: Defining the HH operator 1-Qubit States ~~~~~~~~~~~~~~ -Back to `Index`_ +*Back to* `Index`_ .. list-table:: :header-rows: 1 @@ -317,7 +321,7 @@ Example 1 Primitive and List Ops ---------------------- -Back to `Index`_ +*Back to* `Index`_ 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 ``quantum_info``\'s :mod:`~qiskit.quantum_info.operators` instead. @@ -326,26 +330,30 @@ opflow components. Primitive Ops ~~~~~~~~~~~~~~ -Back to `Index`_ +*Back to* `Index`_ :class:`~qiskit.opflow.primitive_ops.PrimitiveOp` is the :mod:`~qiskit.opflow.primitive_ops` module's base class. It also acts as a factory to instantiate a corresponding sub-class depending on the computational primitive used -to initialize it: +to initialize it. -.. list-table:: - :header-rows: 1 +.. tip:: + + Interpreting :class:`~qiskit.opflow.primitive_ops.PrimitiveOp` as a factory class: - * - Class passed to constructor - - Sub-class returned + .. list-table:: + :header-rows: 1 - * - :class:`~qiskit.quantum_info.Pauli` - - :class:`~qiskit.opflow.primitive_ops.PauliOp` + * - Class passed to :class:`~qiskit.opflow.primitive_ops.PrimitiveOp` + - Sub-class returned - * - :class:`~qiskit.circuit.Instruction`, :class:`~qiskit.circuit.QuantumCircuit` - - :class:`~qiskit.opflow.primitive_ops.CircuitOp` + * - :class:`~qiskit.quantum_info.Pauli` + - :class:`~qiskit.opflow.primitive_ops.PauliOp` - * - ``list``, ``np.ndarray``, ``scipy.sparse.spmatrix``, :class:`~qiskit.quantum_info.Operator` - - :class:`~qiskit.opflow.primitive_ops.MatrixOp` + * - :class:`~qiskit.circuit.Instruction`, :class:`~qiskit.circuit.QuantumCircuit` + - :class:`~qiskit.opflow.primitive_ops.CircuitOp` + + * - ``list``, ``np.ndarray``, ``scipy.sparse.spmatrix``, :class:`~qiskit.quantum_info.Operator` + - :class:`~qiskit.opflow.primitive_ops.MatrixOp` Thus, when migrating opflow code, it is important to look for alternatives to replace the specific subclasses that might have been used "under the hood" in the original code: @@ -357,11 +365,10 @@ might have been used "under the hood" in the original code: - Alternative * - :class:`~qiskit.opflow.primitive_ops.PrimitiveOp` - - No alternative provided. In most use-cases (representing generic operators), - the alternative is :class:`~qiskit.quantum_info.Operator`. + - :class:`~qiskit.quantum_info.Operator` * - :class:`~qiskit.opflow.primitive_ops.CircuitOp` - - No alternative provided. :class:`~qiskit.QuantumCircuit` could be used as an alternative in some workflows. + - :class:`~qiskit.quantum_info.Operator` * - :class:`~qiskit.opflow.primitive_ops.MatrixOp` - :class:`~qiskit.quantum_info.Operator` @@ -371,16 +378,17 @@ might have been used "under the hood" in the original code: wrap in :class:`~qiskit.quantum_info.SparsePauliOp` * - :class:`~qiskit.opflow.primitive_ops.PauliSumOp` - - :class:`~qiskit.quantum_info.SparsePauliOp`. See example below. + - :class:`~qiskit.quantum_info.SparsePauliOp`. See example :ref:`below `.. * - :class:`~qiskit.opflow.primitive_ops.TaperedPauliSumOp` - This class was used to combine a :class:`~PauliSumOp` with its identified symmetries in one object. This functionality is not currently used in any workflow, and has been deprecated without replacement. - See ``Z2Symmetries`` example for updated workflow. + See ``Z2Symmetries`` :ref:`example ` for updated workflow. * - :class:`~qiskit.opflow.primitive_ops.Z2Symmetries` - - :class:`~qiskit.quantum_info.Z2Symmetries`. See example below. + - :class:`~qiskit.quantum_info.Z2Symmetries`. See example :ref:`below `. +.. _pauli_sum_op: Example 1: ``PauliSumOp`` ############################## @@ -416,6 +424,8 @@ Example 1: ``PauliSumOp`` +.. _z2_sym: + Example 2: ``Z2Symmetries`` and ``TaperedPauliSumOp`` ##################################################### @@ -473,7 +483,7 @@ Example 2: ``Z2Symmetries`` and ``TaperedPauliSumOp`` ListOps ~~~~~~~ -Back to `Index`_ +*Back to* `Index`_ The :mod:`~qiskit.opflow.list_ops` module contained classes for manipulating lists of :mod:`~qiskit.opflow.primitive_ops` or :mod:`~qiskit.opflow.state_fns`. The :mod:`~qiskit.quantum_info` alternatives for this functionality are the @@ -503,7 +513,7 @@ or :mod:`~qiskit.opflow.state_fns`. The :mod:`~qiskit.quantum_info` alternatives State Functions --------------- -Back to `Index`_ +*Back to* `Index`_ The :mod:`~qiskit.opflow.state_fns` module can be generally replaced by :class:`~qiskit.quantum_info.QuantumState`, with some differences to keep in mind: @@ -511,26 +521,31 @@ with some differences to keep in mind: 1. The primitives-based workflow does not rely on constructing state functions as opflow did 2. Algorithm-specific functionality has been migrated to the respective algorithm's module + Similarly to :class:`~qiskit.opflow.primitive_ops.PrimitiveOp`, :class:`~qiskit.opflow.state_fns.StateFn` -acts as a factory to create the corresponding sub-class depending on the computational primitive used to initialize it: +acts as a factory to create the corresponding sub-class depending on the computational primitive used to initialize it. -.. list-table:: - :header-rows: 1 +.. tip:: + + Interpreting :class:`~qiskit.opflow.state_fns.StateFn` as a factory class: - * - Class passed to constructor - - Sub-class returned + .. list-table:: + :header-rows: 1 - * - ``str``, ``dict``, :class:`~qiskit.result.Result` - - :class:`~qiskit.opflow.state_fns.DictStateFn` + * - Class passed to :class:`~qiskit.opflow.state_fns.StateFn` + - Sub-class returned - * - ``list``, ``np.ndarray``, :class:`~qiskit.quantum_info.Statevector` - - :class:`~qiskit.opflow.state_fns.VectorStateFn` + * - ``str``, ``dict``, :class:`~qiskit.result.Result` + - :class:`~qiskit.opflow.state_fns.DictStateFn` - * - :class:`~qiskit.circuit.QuantumCircuit`, :class:`~qiskit.circuit.Instruction` - - :class:`~qiskit.opflow.state_fns.CircuitStateFn` + * - ``list``, ``np.ndarray``, :class:`~qiskit.quantum_info.Statevector` + - :class:`~qiskit.opflow.state_fns.VectorStateFn` - * - :class:`~qiskit.opflow.OperatorBase` - - :class:`~qiskit.opflow.state_fns.OperatorStateFn` + * - :class:`~qiskit.circuit.QuantumCircuit`, :class:`~qiskit.circuit.Instruction` + - :class:`~qiskit.opflow.state_fns.CircuitStateFn` + + * - :class:`~qiskit.opflow.OperatorBase` + - :class:`~qiskit.opflow.state_fns.OperatorStateFn` This means that references to :class:`~qiskit.opflow.state_fns.StateFn` in opflow code should be examined to identify the sub-class that is being used, to then look for an alternative. @@ -542,28 +557,27 @@ identify the sub-class that is being used, to then look for an alternative. - Alternative * - :class:`~qiskit.opflow.state_fns.StateFn` - - No direct replacement. This class was used to create instances of the classes listed below. + - In most cases, :class:`~qiskit.quantum_info.Statevector`. Remember that this is a factory class. * - :class:`~qiskit.opflow.state_fns.CircuitStateFn` - - No direct replacement. :class:`~qiskit.circuit.QuantumCircuit` can be used directly instead. + - :class:`~qiskit.quantum_info.Statevector` * - :class:`~qiskit.opflow.state_fns.DictStateFn` - - No direct replacement. + - :class:`~qiskit.quantum_info.Statevector` * - :class:`~qiskit.opflow.state_fns.VectorStateFn` - This class can be replaced with :class:`~qiskit.quantum_info.Statevector` or :class:`~qiskit.quantum_info.StabilizerState` (for Clifford-based vectors). * - :class:`~qiskit.opflow.state_fns.SparseVectorStateFn` - - :class:`~qiskit.quantum_info.Statevector` is not sparse, but for stabilizer states, - :class:`~qiskit.quantum_info.StabilizerState` can simulate them efficiently. + - No direct replacement. * - :class:`~qiskit.opflow.state_fns.OperatorStateFn` - - No direct replacement. + - :class:`~qiskit.quantum_info.Statevector` * - :class:`~qiskit.opflow.state_fns.CVaRMeasurement` - Used in :class:`~qiskit.opflow.expectations.CVaRExpectation`. - Functionality now covered by :class:`~SamplingEstimator`. See example in expectations. + Functionality now covered by :class:`~SamplingEstimator`. See :ref:`example ` in expectations. Example 1: Applying an operator to a state @@ -614,12 +628,12 @@ Example 1: Applying an operator to a state -See more applied examples in expectations and converters. +See more applied examples in :ref:`expectations ` and :ref:`converters `. Converters ---------- -Back to `Index`_ +*Back to* `Index`_ The role of this sub-module was to convert the operators into other opflow operator classes (:class:`~qiskit.opflow.converters.TwoQubitReduction`, :class:`~qiskit.opflow.converters.PauliBasisChange`...). @@ -647,6 +661,8 @@ Notably, this functionality has been replaced by the :mod:`~qiskit.primitives`. so this specific :mod:`~qiskit.opflow` implementation is no longer necessary. +.. _convert_state: + Example 1: ``CircuitSampler`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -700,35 +716,35 @@ Example 1: ``CircuitSampler`` Evolutions ---------- -Back to `Index`_ +*Back to* `Index`_ 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.evolutions.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.evolutions.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.evolutions.TrotterizationFactory`, -or by supplying a method instance of :class:`~qiskit.opflow.evolutions.Trotter`, :class:`~qiskit.opflow.evolutions.Suzuki` or :class:`~qiskit.opflow.evolutions.QDrift`). +.. note:: -The different trotterization methods that extend :class:`~qiskit.opflow.evolutions.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.evolutions.PauliTrotterEvolution` class can now be handled directly by the algorithms -(for example, :class:`~qiskit.algorithms.time_evolvers.TrotterQRTE`\). + The :class:`~qiskit.opflow.evolutions.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.evolutions.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.evolutions.TrotterizationFactory`, + or by supplying a method instance of :class:`~qiskit.opflow.evolutions.Trotter`, :class:`~qiskit.opflow.evolutions.Suzuki` or :class:`~qiskit.opflow.evolutions.QDrift`). -In a similar manner, the :class:`~qiskit.opflow.evolutions.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. + The different trotterization methods that extend :class:`~qiskit.opflow.evolutions.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.evolutions.PauliTrotterEvolution` class can now be handled directly by the algorithms + (for example, :class:`~qiskit.algorithms.time_evolvers.TrotterQRTE`\). -To summarize: + In a similar manner, the :class:`~qiskit.opflow.evolutions.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. Trotterizations ~~~~~~~~~~~~~~~ -Back to `Index`_ +*Back to* `Index`_ .. list-table:: :header-rows: 1 @@ -750,7 +766,7 @@ Back to `Index`_ Other Evolution Classes ~~~~~~~~~~~~~~~~~~~~~~~~ -Back to `Index`_ +*Back to* `Index`_ .. list-table:: :header-rows: 1 @@ -855,14 +871,14 @@ Example 2: Matrix evolution Expectations ------------ -Back to `Index`_ +*Back to* `Index`_ 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 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Back to `Index`_ +*Back to* `Index`_ .. list-table:: :header-rows: 1 @@ -875,15 +891,18 @@ Back to `Index`_ * - :class:`~qiskit.opflow.expectations.AerPauliExpectation` - Use :class:`~Estimator` primitive from ``qiskit_aer`` with ``approximation=True`` and ``shots=None`` as ``run_options``. - See example below. + See example :ref:`below `. * - :class:`~qiskit.opflow.expectations.MatrixExpectation` - - Use :class:`~Estimator` primitive from ``qiskit`` (if no shots are set, it performs an exact Statevector calculation). See example below. + - Use :class:`~Estimator` primitive from ``qiskit`` (if no shots are set, it performs an exact Statevector calculation). + See example :ref:`below `. * - :class:`~qiskit.opflow.expectations.PauliExpectation` - Use any :class:`~Estimator` primitive from ``qiskit``. +.. _expect_state: + Example 1: Aer Pauli Expectation ################################# @@ -938,6 +957,8 @@ Example 1: Aer Pauli Expectation +.. _matrix_state: + Example 2: Matrix Expectation ################################# @@ -996,7 +1017,7 @@ Example 2: Matrix Expectation CVaRExpectation ~~~~~~~~~~~~~~~ -Back to `Index`_ +*Back to* `Index`_ .. list-table:: :header-rows: 1 @@ -1007,6 +1028,7 @@ Back to `Index`_ * - :class:`~qiskit.opflow.expectations.CVaRExpectation` - Functionality migrated into new VQE algorithm: :class:`~qiskit.algorithms.minimum_eigensolvers.SamplingVQE` +.. _cvar: Example 1: VQE with CVaR ######################## @@ -1063,7 +1085,7 @@ Example 1: VQE with CVaR Gradients --------- -Back to `Index`_ +*Back to* `Index`_ Replaced by the new :mod:`~qiskit.algorithms.gradients` module. You can see further details in the algorithms migration guide. From 36ff6c038587dbb77e3c4dc27b0741777dc170fd Mon Sep 17 00:00:00 2001 From: ElePT Date: Mon, 13 Feb 2023 18:23:45 +0100 Subject: [PATCH 14/50] Add sampler example --- docs/migration_guides/opflow_migration.rst | 68 +++++++++++++++++++++- 1 file changed, 66 insertions(+), 2 deletions(-) diff --git a/docs/migration_guides/opflow_migration.rst b/docs/migration_guides/opflow_migration.rst index c0bda4e2462..cae9df7466b 100644 --- a/docs/migration_guides/opflow_migration.rst +++ b/docs/migration_guides/opflow_migration.rst @@ -663,8 +663,72 @@ Notably, this functionality has been replaced by the :mod:`~qiskit.primitives`. .. _convert_state: -Example 1: ``CircuitSampler`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Example 1: ``CircuitSampler`` for sampling Parametrized Circuits +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +.. raw:: html + +
+ Opflow + +.. code-block:: python + + from qiskit_aer import Aer + from qiskit.circuit import QuantumCircuit, Parameter + from qiskit.opflow import ListOp, StateFn, CircuitSampler + + x, y = Parameter("x"), Parameter("y") + + circuit1 = QuantumCircuit(1) + circuit1.p(0.2, 0) + circuit2 = QuantumCircuit(1) + circuit2.p(x, 0) + circuit3 = QuantumCircuit(1) + circuit3.p(y, 0) + + bindings = {x: -0.4, y: 0.4} + listop = ListOp([StateFn(circuit) for circuit in [circuit1, circuit2, circuit3]]) + + sampler = CircuitSampler(Aer.get_backend("aer_simulator")) + sampled = sampler.convert(listop, params=bindings).eval() + # returns list of SparseVectorStateFn + +.. raw:: html + +
+ +.. raw:: html + +
+ Alternative + +.. code-block:: python + + from qiskit.circuit import QuantumCircuit, Parameter + from qiskit.primitives import Sampler + + x, y = Parameter("x"), Parameter("y") + + circuit1 = QuantumCircuit(1) + circuit1.p(0.2, 0) + circuit2 = QuantumCircuit(1) + circuit2.p(x, 0) + circuit3 = QuantumCircuit(1) + circuit3.p(y, 0) + + circuits = [circuit1, circuit2, circuit3] + param_values = [[-0.4, 0.4] for _ in circuits] + + sampler = Sampler() + sampled = sampler.run(circuits, param_values).result().quasi_dists + +.. raw:: html + +
+ +Example 2: ``CircuitSampler`` for computing Expectation Values +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. raw:: html From cc8ae4bb162e82cfe11976b1f2790686a9e72181 Mon Sep 17 00:00:00 2001 From: ElePT <57907331+ElePT@users.noreply.github.com> Date: Tue, 14 Feb 2023 10:11:30 +0100 Subject: [PATCH 15/50] Update docs/migration_guides/opflow_migration.rst Co-authored-by: Steve Wood <40241007+woodsp-ibm@users.noreply.github.com> --- docs/migration_guides/opflow_migration.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/migration_guides/opflow_migration.rst b/docs/migration_guides/opflow_migration.rst index cae9df7466b..09b8095756d 100644 --- a/docs/migration_guides/opflow_migration.rst +++ b/docs/migration_guides/opflow_migration.rst @@ -10,7 +10,7 @@ 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 recent release of the :mod:`~qiskit.primitives` introduced a new interface for interacting with backends. Now, instead of +The recent release of the :mod:`~qiskit.primitives` introduced a new paradigm 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 From b0d01a208861ebbdeea6b40c56abb6e827f7be55 Mon Sep 17 00:00:00 2001 From: ElePT <57907331+ElePT@users.noreply.github.com> Date: Tue, 14 Feb 2023 10:12:20 +0100 Subject: [PATCH 16/50] Update docs/migration_guides/opflow_migration.rst Co-authored-by: Julien Gacon --- docs/migration_guides/opflow_migration.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/migration_guides/opflow_migration.rst b/docs/migration_guides/opflow_migration.rst index 09b8095756d..cb4f9e8624f 100644 --- a/docs/migration_guides/opflow_migration.rst +++ b/docs/migration_guides/opflow_migration.rst @@ -142,6 +142,9 @@ Example 1: Defining the XX operator X = Pauli('X') op = X ^ X + # equivalent to: + op = Pauli('XX') + # equivalent to: op = SparsePauliOp('XX') From b47ede4f4c567500359b243261c5b728dbf3baaa Mon Sep 17 00:00:00 2001 From: ElePT <57907331+ElePT@users.noreply.github.com> Date: Tue, 14 Feb 2023 10:13:43 +0100 Subject: [PATCH 17/50] Update docs/migration_guides/opflow_migration.rst Co-authored-by: Julien Gacon --- docs/migration_guides/opflow_migration.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/migration_guides/opflow_migration.rst b/docs/migration_guides/opflow_migration.rst index cb4f9e8624f..bc18f76b495 100644 --- a/docs/migration_guides/opflow_migration.rst +++ b/docs/migration_guides/opflow_migration.rst @@ -580,7 +580,7 @@ identify the sub-class that is being used, to then look for an alternative. * - :class:`~qiskit.opflow.state_fns.CVaRMeasurement` - Used in :class:`~qiskit.opflow.expectations.CVaRExpectation`. - Functionality now covered by :class:`~SamplingEstimator`. See :ref:`example ` in expectations. + Functionality now covered by :class:`.SamplingVQE`. See :ref:`example ` in expectations. Example 1: Applying an operator to a state From d4bb2d93ed03fa4d14d1da2fed9c636bf402ee11 Mon Sep 17 00:00:00 2001 From: ElePT <57907331+ElePT@users.noreply.github.com> Date: Tue, 14 Feb 2023 10:14:07 +0100 Subject: [PATCH 18/50] Update docs/migration_guides/opflow_migration.rst Co-authored-by: Julien Gacon --- docs/migration_guides/opflow_migration.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/migration_guides/opflow_migration.rst b/docs/migration_guides/opflow_migration.rst index bc18f76b495..8a354ffc9c4 100644 --- a/docs/migration_guides/opflow_migration.rst +++ b/docs/migration_guides/opflow_migration.rst @@ -651,7 +651,7 @@ Notably, this functionality has been replaced by the :mod:`~qiskit.primitives`. - Alternative * - :class:`~qiskit.opflow.converters.CircuitSampler` - - :class:`~primitives.Estimator` + - :class:`~qiskit.primitives.Estimator` * - :class:`~qiskit.opflow.converters.AbelianGrouper` - No direct replacement. This class allowed a sum a of Pauli operators to be grouped. These type of groupings are now left to the primitives to handle. * - :class:`~qiskit.opflow.converters.DictToCircuitSum` From 0a6bdb40406e061bd06bb299f32b5b49a600eddc Mon Sep 17 00:00:00 2001 From: ElePT <57907331+ElePT@users.noreply.github.com> Date: Tue, 14 Feb 2023 10:15:08 +0100 Subject: [PATCH 19/50] Update docs/migration_guides/opflow_migration.rst Co-authored-by: Julien Gacon --- docs/migration_guides/opflow_migration.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/migration_guides/opflow_migration.rst b/docs/migration_guides/opflow_migration.rst index 8a354ffc9c4..8af505870f0 100644 --- a/docs/migration_guides/opflow_migration.rst +++ b/docs/migration_guides/opflow_migration.rst @@ -775,7 +775,7 @@ Example 2: ``CircuitSampler`` for computing Expectation Values hamiltonian = SparsePauliOp.from_list([('X', 1), ('Z',1)]) estimator = Estimator() - expectation_value = estimator.run(state, hamiltonian).result().values + expectation_value = estimator.run(state, hamiltonian).result().values.real .. raw:: html From 8e3e7b525d848b9b70c12e8ac51d277f9f61cadd Mon Sep 17 00:00:00 2001 From: ElePT <57907331+ElePT@users.noreply.github.com> Date: Tue, 14 Feb 2023 10:16:14 +0100 Subject: [PATCH 20/50] Apply suggestions from code review Co-authored-by: Julien Gacon --- docs/migration_guides/opflow_migration.rst | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/migration_guides/opflow_migration.rst b/docs/migration_guides/opflow_migration.rst index 8af505870f0..9d2cf154a5c 100644 --- a/docs/migration_guides/opflow_migration.rst +++ b/docs/migration_guides/opflow_migration.rst @@ -11,8 +11,8 @@ The :mod:`~qiskit.opflow` module was originally introduced as a layer between ci for quantum algorithms research and development. The recent release of the :mod:`~qiskit.primitives` introduced a new paradigm 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 +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; encouraging 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. @@ -721,7 +721,7 @@ Example 1: ``CircuitSampler`` for sampling Parametrized Circuits circuit3.p(y, 0) circuits = [circuit1, circuit2, circuit3] - param_values = [[-0.4, 0.4] for _ in circuits] + param_values = [None, [-0.4], [0.4]] sampler = Sampler() sampled = sampler.run(circuits, param_values).result().quasi_dists @@ -785,7 +785,7 @@ Evolutions ---------- *Back to* `Index`_ -The :mod:`~qiskit.opflow.evolutions` sub-module was created to provide building blocks for hamiltonian simulation algorithms, +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. @@ -806,8 +806,8 @@ delayed synthesis of the gates or efficient transpilation of the circuits, so th (for example, :class:`~qiskit.algorithms.time_evolvers.TrotterQRTE`\). In a similar manner, the :class:`~qiskit.opflow.evolutions.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. + 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. Trotterizations ~~~~~~~~~~~~~~~ From 04ca1650cc3c5569c5f277d862036e5ebb11a983 Mon Sep 17 00:00:00 2001 From: ElePT <57907331+ElePT@users.noreply.github.com> Date: Tue, 14 Feb 2023 11:44:52 +0100 Subject: [PATCH 21/50] Update CircuitOp Co-authored-by: Julien Gacon --- docs/migration_guides/opflow_migration.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/migration_guides/opflow_migration.rst b/docs/migration_guides/opflow_migration.rst index 9d2cf154a5c..a7d680ad205 100644 --- a/docs/migration_guides/opflow_migration.rst +++ b/docs/migration_guides/opflow_migration.rst @@ -371,7 +371,7 @@ might have been used "under the hood" in the original code: - :class:`~qiskit.quantum_info.Operator` * - :class:`~qiskit.opflow.primitive_ops.CircuitOp` - - :class:`~qiskit.quantum_info.Operator` + - :class:`~qiskit.QuantumCircuit` * - :class:`~qiskit.opflow.primitive_ops.MatrixOp` - :class:`~qiskit.quantum_info.Operator` From 51bbf54b06179316aa6390c8590e348e78b61162 Mon Sep 17 00:00:00 2001 From: ElePT Date: Tue, 14 Feb 2023 11:46:23 +0100 Subject: [PATCH 22/50] Applied suggestions locally --- docs/migration_guides/opflow_migration.rst | 202 +++++++++++++++------ 1 file changed, 147 insertions(+), 55 deletions(-) diff --git a/docs/migration_guides/opflow_migration.rst b/docs/migration_guides/opflow_migration.rst index 9d2cf154a5c..ba121eb7d9f 100644 --- a/docs/migration_guides/opflow_migration.rst +++ b/docs/migration_guides/opflow_migration.rst @@ -2,7 +2,12 @@ Opflow Migration Guide ======================= -*Jump to* `TL;DR`_. +TL;DR +----- +.. note:: + + The new :mod:`~qiskit.primitives`, in combination with the :mod:`~qiskit.quantum_info` module, have superseded + functionality of :mod:`~qiskit.opflow`. Thus, the latter is being deprecated. Background ---------- @@ -10,7 +15,7 @@ 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 recent release of the :mod:`~qiskit.primitives` introduced a new paradigm for interacting with backends. Now, instead of +The recent release of the :mod:`qiskit.primitives` introduced a new paradigm 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 @@ -20,16 +25,37 @@ the :mod:`~qiskit.primitives` interface and the :mod:`~qiskit.quantum_info` modu 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 ------ -.. note:: +The function equivalency can be roughly summarized as follows: - The new :mod:`~qiskit.primitives`, in combination with the :mod:`~qiskit.quantum_info` module, have superseded - functionality of :mod:`~qiskit.opflow`. Thus, the latter is being deprecated. -Index ------ -This guide covers the migration from these opflow sub-modules: +.. list-table:: + :header-rows: 1 + + * - Opflow Module + - Alternative + * - Operators (:class:`~qiskit.opflow.OperatorBase`, ``operator_globals``, :mod:`~qiskit.opflow.primitive_ops`, + :mod:`~qiskit.opflow.list_ops`\) + - :mod:`qiskit.quantum_info` *Operators* + + * - :mod:`qiskit.opflow.state_fns` + - :mod:`qiskit.quantum_info` *States* + + * - :mod:`qiskit.opflow.converters` + - :mod:`qiskit.primitives` or :mod:`qiskit_aer.primitives` or :mod:`qiskit_ibm_runtime` *Sampler / Estimator* + + * - :mod:`qiskit.opflow.evolutions` + - :mod:`qiskit.quantum_info` *Synthesis* + + * - :mod:`qiskit.opflow.expectations` + - :mod:`qiskit.primitives` or :mod:`qiskit_aer.primitives` or :mod:`qiskit_ibm_runtime` *Estimator* + + * - :mod:`qiskit.opflow.gradients` + - :mod:`qiskit.algorithms.gradients` + +Contents +-------- + +This document covers the migration from these opflow sub-modules: **Operators** @@ -51,35 +77,39 @@ This guide covers the migration from these opflow sub-modules: Operator Base Class ------------------- -*Back to* `Index`_ +*Back to* `Contents`_ -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. +The :class:`qiskit.opflow.OperatorBase` abstract class can be replaced with :class:`qiskit.quantum_info.BaseOperator`, +keeping in mind that :class:`qiskit.quantum_info.BaseOperator` is more generic than its opflow counterpart. .. list-table:: :header-rows: 1 * - Opflow - Alternative - * - :class:`~opflow.OperatorBase` - - :class:`~quantum_info.BaseOperator` + * - :class:`qiskit.opflow.OperatorBase` + - :class:`qiskit.quantum_info.BaseOperator` -.. tip:: +.. attention:: - Please note that: + Despite the similar class names, :class:`qiskit.opflow.OperatorBase` and + :class:`qiskit.quantum_info.BaseOperator` are not completely equivalent to each other, and the transition + should be handled with care. Namely: - 1. :class:`~opflow.OperatorBase` implements a broader algebra mixin. Some operator overloads are not available in - :class:`~quantum_info.BaseOperator`. + 1. :class:`qiskit.opflow.OperatorBase` implements a broader algebra mixin. Some operator overloads that were + commonly used :mod:`~qiskit.opflow` (for example ``~`` for ``.adjoint()``) are not defined for + :class:`qiskit.quantum_info.BaseOperator`. You might want to check the specific + :mod:`~qiskit.quantum_info` subclass instead. - 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. + 2. :class:`qiskit.opflow.OperatorBase` also implements methods such as ``.to_matrix()`` or ``.to_spmatrix()``, + which are only found in some of the :class:`qiskit.quantum_info.BaseOperator` subclasses. See API reference for more information. Operator Globals ---------------- -*Back to* `Index`_ +*Back to* `Contents`_ Opflow provided shortcuts to define common single qubit states, operators, and non-parametrized gates in the :mod:`~qiskit.opflow.operator_globals` module. @@ -91,7 +121,7 @@ These were mainly used for didactic purposes or quick prototyping, and can easil 1-Qubit Paulis ~~~~~~~~~~~~~~ -*Back to* `Index`_ +*Back to* `Contents`_ The 1-qubit paulis were commonly used for quick testing of algorithms, as they could be combined to create more complex operators (for example, ``0.39 * (I ^ Z) + 0.5 * (X ^ X)``). @@ -161,10 +191,14 @@ Example 2: Defining a more complex operator .. code-block:: python - from qiskit.opflow import I, X, Z + from qiskit.opflow import I, X, Z, PauliSumOp op = 0.39 * (I ^ Z ^ I) + 0.5 * (I ^ X ^ X) + # or ... + op = PauliSumOp.from_list([("IZI", 0.39), ("IXX", 0.5)]) + + .. raw:: html @@ -178,6 +212,9 @@ Example 2: Defining a more complex operator from qiskit.quantum_info import SparsePauliOp + op = SparsePauliOp(["IZI", "IXX"], coeffs = [0.39, 0.5]) + + # or... op = SparsePauliOp.from_list([("IZI", 0.39), ("IXX", 0.5)]) # or... @@ -189,7 +226,7 @@ Example 2: Defining a more complex operator Common non-parametrized gates (Clifford) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -*Back to* `Index`_ +*Back to* `Contents`_ .. list-table:: :header-rows: 1 @@ -260,7 +297,7 @@ Example 1: Defining the HH operator 1-Qubit States ~~~~~~~~~~~~~~ -*Back to* `Index`_ +*Back to* `Contents`_ .. list-table:: :header-rows: 1 @@ -324,7 +361,7 @@ Example 1 Primitive and List Ops ---------------------- -*Back to* `Index`_ +*Back to* `Contents`_ 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 ``quantum_info``\'s :mod:`~qiskit.quantum_info.operators` instead. @@ -333,7 +370,7 @@ opflow components. Primitive Ops ~~~~~~~~~~~~~~ -*Back to* `Index`_ +*Back to* `Contents`_ :class:`~qiskit.opflow.primitive_ops.PrimitiveOp` is the :mod:`~qiskit.opflow.primitive_ops` module's base class. It also acts as a factory to instantiate a corresponding sub-class depending on the computational primitive used @@ -368,7 +405,7 @@ might have been used "under the hood" in the original code: - Alternative * - :class:`~qiskit.opflow.primitive_ops.PrimitiveOp` - - :class:`~qiskit.quantum_info.Operator` + - As mentioned above, this class is used to generate an instance of one of the classes below, so no * - :class:`~qiskit.opflow.primitive_ops.CircuitOp` - :class:`~qiskit.quantum_info.Operator` @@ -486,7 +523,7 @@ Example 2: ``Z2Symmetries`` and ``TaperedPauliSumOp`` ListOps ~~~~~~~ -*Back to* `Index`_ +*Back to* `Contents`_ The :mod:`~qiskit.opflow.list_ops` module contained classes for manipulating lists of :mod:`~qiskit.opflow.primitive_ops` or :mod:`~qiskit.opflow.state_fns`. The :mod:`~qiskit.quantum_info` alternatives for this functionality are the @@ -516,7 +553,7 @@ or :mod:`~qiskit.opflow.state_fns`. The :mod:`~qiskit.quantum_info` alternatives State Functions --------------- -*Back to* `Index`_ +*Back to* `Contents`_ The :mod:`~qiskit.opflow.state_fns` module can be generally replaced by :class:`~qiskit.quantum_info.QuantumState`, with some differences to keep in mind: @@ -566,17 +603,19 @@ identify the sub-class that is being used, to then look for an alternative. - :class:`~qiskit.quantum_info.Statevector` * - :class:`~qiskit.opflow.state_fns.DictStateFn` - - :class:`~qiskit.quantum_info.Statevector` + - This class was used to store efficient representations of sparse measurement results. The + :class:`~qiskit.primitives.Sampler` now returns the measurements as an instance of + :class:`~qiskit.result.QuasiDist` (see example in :ref:`converters `). * - :class:`~qiskit.opflow.state_fns.VectorStateFn` - This class can be replaced with :class:`~qiskit.quantum_info.Statevector` or :class:`~qiskit.quantum_info.StabilizerState` (for Clifford-based vectors). * - :class:`~qiskit.opflow.state_fns.SparseVectorStateFn` - - No direct replacement. + - No direct replacement. This class was used for sparse statevector representations. * - :class:`~qiskit.opflow.state_fns.OperatorStateFn` - - :class:`~qiskit.quantum_info.Statevector` + - No direct replacement. This class was used to represent measurements against operators. * - :class:`~qiskit.opflow.state_fns.CVaRMeasurement` - Used in :class:`~qiskit.opflow.expectations.CVaRExpectation`. @@ -599,7 +638,7 @@ Example 1: Applying an operator to a state op = X ^ Y state = StateFn(qc) - comp = ~state @ op + comp = ~op @ state # returns a CircuitStateFn eval = comp.eval() @@ -636,7 +675,7 @@ See more applied examples in :ref:`expectations ` and :ref:`conve Converters ---------- -*Back to* `Index`_ +*Back to* `Contents`_ The role of this sub-module was to convert the operators into other opflow operator classes (:class:`~qiskit.opflow.converters.TwoQubitReduction`, :class:`~qiskit.opflow.converters.PauliBasisChange`...). @@ -651,7 +690,8 @@ Notably, this functionality has been replaced by the :mod:`~qiskit.primitives`. - Alternative * - :class:`~qiskit.opflow.converters.CircuitSampler` - - :class:`~qiskit.primitives.Estimator` + - :class:`~qiskit.primitives.Sampler` or :class:`~qiskit.primitives.Estimator` if used with + :class:`~qiskit.oflow.expectations`. See examples :ref:`below `. * - :class:`~qiskit.opflow.converters.AbelianGrouper` - No direct replacement. This class allowed a sum a of Pauli operators to be grouped. These type of groupings are now left to the primitives to handle. * - :class:`~qiskit.opflow.converters.DictToCircuitSum` @@ -715,16 +755,21 @@ Example 1: ``CircuitSampler`` for sampling Parametrized Circuits circuit1 = QuantumCircuit(1) circuit1.p(0.2, 0) + # Don't forget to add measurements!!!!! + circuit1.measure_all() circuit2 = QuantumCircuit(1) circuit2.p(x, 0) + circuit2.measure_all() circuit3 = QuantumCircuit(1) circuit3.p(y, 0) + circuit3.measure_all() circuits = [circuit1, circuit2, circuit3] param_values = [None, [-0.4], [0.4]] sampler = Sampler() sampled = sampler.run(circuits, param_values).result().quasi_dists + # returns qiskit.result.QuasiDist .. raw:: html @@ -783,7 +828,7 @@ Example 2: ``CircuitSampler`` for computing Expectation Values Evolutions ---------- -*Back to* `Index`_ +*Back to* `Contents`_ 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 @@ -811,7 +856,7 @@ delayed synthesis of the gates or efficient transpilation of the circuits, so th Trotterizations ~~~~~~~~~~~~~~~ -*Back to* `Index`_ +*Back to* `Contents`_ .. list-table:: :header-rows: 1 @@ -833,7 +878,7 @@ Trotterizations Other Evolution Classes ~~~~~~~~~~~~~~~~~~~~~~~~ -*Back to* `Index`_ +*Back to* `Contents`_ .. list-table:: :header-rows: 1 @@ -867,9 +912,10 @@ Example 1: Trotter evolution from qiskit.opflow import Trotter, PauliTrotterEvolution, PauliSumOp hamiltonian = PauliSumOp.from_list([('X', 1), ('Z',1)]) - evolution = PauliTrotterEvolution(trotter_mode=Trotter(), reps=1) + evolution = PauliTrotterEvolution(trotter_mode=Trotter(), reps=2) evol_result = evolution.convert(hamiltonian.exp_i()) evolved_state = evol_result.to_circuit() + .. raw:: html @@ -881,22 +927,67 @@ Example 1: Trotter evolution .. code-block:: python - from qiskit.quantum_info import SparsePauliOp - from qiskit.synthesis import SuzukiTrotter - from qiskit.circuit.library import PauliEvolutionGate - from qiskit import QuantumCircuit + 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]) + hamiltonian = SparsePauliOp.from_list([('X', 1), ('Z',1)]) + evol_gate = PauliEvolutionGate(hamiltonian, time=1, synthesis=SuzukiTrotter(reps=2)) + evolved_state = QuantumCircuit(1) + evolved_state.append(evol_gate, [0]) .. raw:: html +Example 2: Evolution with time-dependent Hamiltonian +##################################################### + +.. raw:: html + +
+ Opflow + +.. code-block:: python + + from qiskit.opflow import Trotter, PauliTrotterEvolution, PauliSumOp + from qiskit.circuit import Parameter + + time = Parameter('t') + hamiltonian = PauliSumOp.from_list([('X', 1), ('Y',1)]) + evolution = PauliTrotterEvolution(trotter_mode=Trotter(), reps=1) + evol_result = evolution.convert((time * hamiltonian).exp_i()) + evolved_state = evol_result.to_circuit() + +.. raw:: html + +
+ +.. raw:: html + +
+ Alternative + +.. code-block:: python + + from qiskit.quantum_info import SparsePauliOp + from qiskit.synthesis import LieTrotter + from qiskit.circuit.library import PauliEvolutionGate + from qiskit import QuantumCircuit + from qiskit.circuit import Parameter + + time = Parameter('t') + hamiltonian = SparsePauliOp.from_list([('X', 1), ('Y',1)]) + evol_gate = PauliEvolutionGate(hamiltonian, time=time, synthesis=LieTrotter()) + evolved_state = QuantumCircuit(1) + evolved_state.append(evol_gate, [0]) + +.. raw:: html + +
-Example 2: Matrix evolution +Example 3: Matrix evolution ############################ .. raw:: html @@ -938,14 +1029,14 @@ Example 2: Matrix evolution Expectations ------------ -*Back to* `Index`_ +*Back to* `Contents`_ 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 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -*Back to* `Index`_ +*Back to* `Contents`_ .. list-table:: :header-rows: 1 @@ -965,7 +1056,8 @@ Algorithm-Agnostic Expectations See example :ref:`below `. * - :class:`~qiskit.opflow.expectations.PauliExpectation` - - Use any :class:`~Estimator` primitive from ``qiskit``. + - Use any ``Estimator`` primitive (for :class:`qiskit.primitives.Estimator`, set ``shots != None`` for a shot-based + simulation, for :class:`qiskit_aer.primitives.Estimator`, this is the default). .. _expect_state: @@ -1084,7 +1176,7 @@ Example 2: Matrix Expectation CVaRExpectation ~~~~~~~~~~~~~~~ -*Back to* `Index`_ +*Back to* `Contents`_ .. list-table:: :header-rows: 1 @@ -1152,7 +1244,7 @@ Example 1: VQE with CVaR Gradients --------- -*Back to* `Index`_ +*Back to* `Contents`_ Replaced by the new :mod:`~qiskit.algorithms.gradients` module. You can see further details in the algorithms migration guide. From d8fb0295590e8a3d35138c1784bbf85c5f902565 Mon Sep 17 00:00:00 2001 From: ElePT Date: Tue, 14 Feb 2023 14:12:36 +0100 Subject: [PATCH 23/50] Change format, links --- docs/migration_guides/opflow_migration.rst | 355 ++++++++------------- 1 file changed, 137 insertions(+), 218 deletions(-) diff --git a/docs/migration_guides/opflow_migration.rst b/docs/migration_guides/opflow_migration.rst index bb8fde17c95..86dfc371117 100644 --- a/docs/migration_guides/opflow_migration.rst +++ b/docs/migration_guides/opflow_migration.rst @@ -27,7 +27,6 @@ This guide traverses the opflow submodules and provides either a direct alternat The function equivalency can be roughly summarized as follows: - .. list-table:: :header-rows: 1 @@ -41,17 +40,27 @@ The function equivalency can be roughly summarized as follows: - :mod:`qiskit.quantum_info` *States* * - :mod:`qiskit.opflow.converters` - - :mod:`qiskit.primitives` or :mod:`qiskit_aer.primitives` or :mod:`qiskit_ibm_runtime` *Sampler / Estimator* + - :mod:`qiskit.primitives` * - :mod:`qiskit.opflow.evolutions` - :mod:`qiskit.quantum_info` *Synthesis* * - :mod:`qiskit.opflow.expectations` - - :mod:`qiskit.primitives` or :mod:`qiskit_aer.primitives` or :mod:`qiskit_ibm_runtime` *Estimator* + - :class:`qiskit.primitives.Estimator` * - :mod:`qiskit.opflow.gradients` - :mod:`qiskit.algorithms.gradients` +.. attention:: + + Most references to the :class:`qiskit.primitives.Sampler` or :class:`qiskit.primitives.Estimator` in this guide + can be replaced with instances of the Aer primitives (:mod:`qiskit_aer.primitives`), Runtime primitives + (:mod:`qiskit_ibm_runtime`) or Terra backend primitives (:class:`qiskit.primitives.BackendSampler`, + :class:`qiskit.primitives.BackendEstimator`). Certain classes, such as the + :class:`~qiskit.opflow.expectations.AerPauliExpectation`, are only replaced by a specific primitive instance + (in this case, :class:`qiskit_aer.primitives.Estimator`), or require a specific option configuration. + This will be explicitly indicated in the corresponding section. + Contents -------- @@ -126,7 +135,7 @@ These were mainly used for didactic purposes or quick prototyping, and can easil The 1-qubit paulis were commonly used for quick testing of algorithms, as they could be combined to create more complex operators (for example, ``0.39 * (I ^ Z) + 0.5 * (X ^ X)``). These operations implicitly created operators of type :class:`~qiskit.opflow.PauliSumOp`, and can be replaced by -directly creating a corresponding :class:`~qiskit.quantum_info.SparsePauliOp`, as shown in the example :ref:`below <1_q_pauli>`. +directly creating a corresponding :class:`~qiskit.quantum_info.SparsePauliOp`, as shown in the examples below. .. list-table:: :header-rows: 1 @@ -141,14 +150,15 @@ directly creating a corresponding :class:`~qiskit.quantum_info.SparsePauliOp`, a For direct compatibility with classes in :mod:`~qiskit.algorithms`, wrap in :class:`~qiskit.quantum_info.SparsePauliOp`. -.. _1_q_pauli: +.. _1_q_pauli: -Example 1: Defining the XX operator -################################### .. raw:: html
- Opflow + Example 1: Defining the XX operator +
+ +**Opflow** .. code-block:: python @@ -156,14 +166,8 @@ Example 1: Defining the XX operator operator = X ^ X -.. raw:: html - -
-.. raw:: html - -
- Alternative +**Alternative** .. code-block:: python @@ -182,12 +186,13 @@ Example 1: Defining the XX operator
-Example 2: Defining a more complex operator -########################################### .. raw:: html
- Opflow + Example 2: Defining a more complex operator +
+ +**Opflow** .. code-block:: python @@ -199,14 +204,7 @@ Example 2: Defining a more complex operator op = PauliSumOp.from_list([("IZI", 0.39), ("IXX", 0.5)]) -.. raw:: html - -
- -.. raw:: html - -
- Alternative +**Alternative** .. code-block:: python @@ -247,12 +245,13 @@ Common non-parametrized gates (Clifford) scales exponentially with the size of the circuit, use with care. -Example 1: Defining the HH operator -################################### .. raw:: html
- Opflow + Example 1: Defining the HH operator +
+ +**Opflow** .. code-block:: python @@ -260,14 +259,7 @@ Example 1: Defining the HH operator op = H ^ H -.. raw:: html - -
- -.. raw:: html - -
- Alternative +**Alternative** .. code-block:: python @@ -312,12 +304,13 @@ Example 1: Defining the HH operator For efficient simulation of stabilizer states, ``quantum_info`` includes a :class:`~qiskit.quantum_info.StabilizerState` class. See API ref. for more info. -Example 1 -########## .. raw:: html
- Opflow + Example 1: Working with stabilizer states +
+ +**Opflow** .. code-block:: python @@ -327,14 +320,7 @@ Example 1 state1 = Zero ^ One state2 = Plus ^ Minus -.. raw:: html - -
- -.. raw:: html - -
- Alternative +**Alternative** .. code-block:: python @@ -405,7 +391,8 @@ might have been used "under the hood" in the original code: - Alternative * - :class:`~qiskit.opflow.primitive_ops.PrimitiveOp` - - As mentioned above, this class is used to generate an instance of one of the classes below, so no + - As mentioned above, this class is used to generate an instance of one of the classes below, so there is + no direct replacement. * - :class:`~qiskit.opflow.primitive_ops.CircuitOp` - :class:`~qiskit.QuantumCircuit` @@ -414,29 +401,29 @@ might have been used "under the hood" in the original code: - :class:`~qiskit.quantum_info.Operator` * - :class:`~qiskit.opflow.primitive_ops.PauliOp` - - :class:`~qiskit.quantum_info.Pauli`. For direct compatibility with classes in :mod:`~qiskit.algorithms`, + - :class:`~qiskit.quantum_info.Pauli`. For direct compatibility with classes in :mod:`qiskit.algorithms`, wrap in :class:`~qiskit.quantum_info.SparsePauliOp` * - :class:`~qiskit.opflow.primitive_ops.PauliSumOp` - - :class:`~qiskit.quantum_info.SparsePauliOp`. See example :ref:`below `.. + - :class:`~qiskit.quantum_info.SparsePauliOp`. See example below * - :class:`~qiskit.opflow.primitive_ops.TaperedPauliSumOp` - - This class was used to combine a :class:`~PauliSumOp` with its identified symmetries in one object. + - This class was used to combine a :class:`.PauliSumOp` with its identified symmetries in one object. This functionality is not currently used in any workflow, and has been deprecated without replacement. - See ``Z2Symmetries`` :ref:`example ` for updated workflow. + See ``Z2Symmetries`` example for updated workflow. * - :class:`~qiskit.opflow.primitive_ops.Z2Symmetries` - - :class:`~qiskit.quantum_info.Z2Symmetries`. See example :ref:`below `. + - :class:`~qiskit.quantum_info.Z2Symmetries`. See example below. .. _pauli_sum_op: -Example 1: ``PauliSumOp`` -############################## - .. raw:: html
- Opflow + Example 1: PauliSumOp +
+ +**Opflow** .. code-block:: python @@ -445,14 +432,7 @@ Example 1: ``PauliSumOp`` qubit_op = PauliSumOp(SparsePauliOp(Pauli("XYZY"), coeffs=[2]), coeff=-3j) -.. raw:: html - -
- -.. raw:: html - -
- Alternative +**Alternative** .. code-block:: python @@ -466,13 +446,13 @@ Example 1: ``PauliSumOp`` .. _z2_sym: -Example 2: ``Z2Symmetries`` and ``TaperedPauliSumOp`` -##################################################### - .. raw:: html
- Opflow + Example 2: Z2Symmetries and TaperedPauliSumOp +
+ +**Opflow** .. code-block:: python @@ -492,14 +472,7 @@ Example 2: ``Z2Symmetries`` and ``TaperedPauliSumOp`` # can be represented as: tapered_op = TaperedPauliSumOp(primitive, z2_symmetries) -.. raw:: html - -
- -.. raw:: html - -
- Alternative +**Alternative** .. code-block:: python @@ -555,8 +528,8 @@ State Functions --------------- *Back to* `Contents`_ -The :mod:`~qiskit.opflow.state_fns` module can be generally replaced by :class:`~qiskit.quantum_info.QuantumState`, -with some differences to keep in mind: +The :mod:`~qiskit.opflow.state_fns` module can be generally replaced by subclasses of :mod:`~qiskit.quantum_info`\'s +:class:`~qiskit.quantum_info.states.quantum_state.QuantumState`, with some differences to keep in mind: 1. The primitives-based workflow does not rely on constructing state functions as opflow did 2. Algorithm-specific functionality has been migrated to the respective algorithm's module @@ -605,7 +578,7 @@ identify the sub-class that is being used, to then look for an alternative. * - :class:`~qiskit.opflow.state_fns.DictStateFn` - This class was used to store efficient representations of sparse measurement results. The :class:`~qiskit.primitives.Sampler` now returns the measurements as an instance of - :class:`~qiskit.result.QuasiDist` (see example in :ref:`converters `). + :class:`~qiskit.result.QuasiDist` (see example in `Converters`_). * - :class:`~qiskit.opflow.state_fns.VectorStateFn` - This class can be replaced with :class:`~qiskit.quantum_info.Statevector` or @@ -619,16 +592,17 @@ identify the sub-class that is being used, to then look for an alternative. * - :class:`~qiskit.opflow.state_fns.CVaRMeasurement` - Used in :class:`~qiskit.opflow.expectations.CVaRExpectation`. - Functionality now covered by :class:`.SamplingVQE`. See :ref:`example ` in expectations. + Functionality now covered by :class:`.SamplingVQE`. See example in `Expectations`_. -Example 1: Applying an operator to a state -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. raw:: html
- Opflow + Example 1: Applying an operator to a state +
+ +**Opflow** .. code-block:: python @@ -644,14 +618,7 @@ Example 1: Applying an operator to a state eval = comp.eval() # returns a VectorStateFn (Statevector) -.. raw:: html - -
- -.. raw:: html - -
- Alternative +**Alternative** .. code-block:: python @@ -668,9 +635,9 @@ Example 1: Applying an operator to a state .. raw:: html
+
- -See more applied examples in :ref:`expectations ` and :ref:`converters `. +See more applied examples in `Expectations`_ and `Converters`_. Converters @@ -691,13 +658,14 @@ Notably, this functionality has been replaced by the :mod:`~qiskit.primitives`. * - :class:`~qiskit.opflow.converters.CircuitSampler` - :class:`~qiskit.primitives.Sampler` or :class:`~qiskit.primitives.Estimator` if used with - :class:`~qiskit.oflow.expectations`. See examples :ref:`below `. + :class:`~qiskit.oflow.expectations`. See examples below. * - :class:`~qiskit.opflow.converters.AbelianGrouper` - No direct replacement. This class allowed a sum a of Pauli operators to be grouped. These type of groupings are now left to the primitives to handle. * - :class:`~qiskit.opflow.converters.DictToCircuitSum` - - No direct replacement + - No direct replacement. This class was used to convert from ``DictStateFns`` or ``VectorStateFns`` + to equivalent ``CircuitStateFns``. * - :class:`~qiskit.opflow.converters.PauliBasisChange` - - No direct replacement + - No direct replacement. This class was used for changing Paulis into other bases. * - :class:`~qiskit.opflow.converters.TwoQubitReduction` - No direct replacement. This class implements a chemistry-specific reduction for the ``ParityMapper`` class in ``qiskit-nature``. The general symmetry logic this mapper depends on has been refactored to other classes in :mod:`~qiskit.quantum_info`, @@ -706,14 +674,13 @@ Notably, this functionality has been replaced by the :mod:`~qiskit.primitives`. .. _convert_state: -Example 1: ``CircuitSampler`` for sampling Parametrized Circuits -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - .. raw:: html
- Opflow + Example 1: CircuitSampler for sampling parametrized circuits +
+ +**Opflow** .. code-block:: python @@ -737,14 +704,7 @@ Example 1: ``CircuitSampler`` for sampling Parametrized Circuits sampled = sampler.convert(listop, params=bindings).eval() # returns list of SparseVectorStateFn -.. raw:: html - -
- -.. raw:: html - -
- Alternative +**Alternative** .. code-block:: python @@ -775,13 +735,14 @@ Example 1: ``CircuitSampler`` for sampling Parametrized Circuits
-Example 2: ``CircuitSampler`` for computing Expectation Values -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. raw:: html
- Opflow + Example 2: CircuitSampler for computing expectation values +
+ +**Opflow** .. code-block:: python @@ -800,14 +761,7 @@ Example 2: ``CircuitSampler`` for computing Expectation Values expectation = sampler.convert(expr) expectation_value = expectation.eval().real -.. raw:: html - -
- -.. raw:: html - -
- Alternative +**Alternative** .. code-block:: python @@ -830,27 +784,27 @@ Evolutions ---------- *Back to* `Contents`_ -The :mod:`~qiskit.opflow.evolutions` sub-module was created to provide building blocks for Hamiltonian simulation algorithms, +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. +:mod:`qiskit.synthesis.evolution` module. .. note:: - The :class:`~qiskit.opflow.evolutions.PauliTrotterEvolution` class computes evolutions for exponentiated sums of Paulis by changing them each to the + The :class:`qiskit.opflow.evolutions.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.evolutions.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.evolutions.TrotterizationFactory`, - or by supplying a method instance of :class:`~qiskit.opflow.evolutions.Trotter`, :class:`~qiskit.opflow.evolutions.Suzuki` or :class:`~qiskit.opflow.evolutions.QDrift`). - - The different trotterization methods that extend :class:`~qiskit.opflow.evolutions.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.evolutions.PauliTrotterEvolution` class can now be handled directly by the algorithms - (for example, :class:`~qiskit.algorithms.time_evolvers.TrotterQRTE`\). - - In a similar manner, the :class:`~qiskit.opflow.evolutions.MatrixEvolution` class performs evolution by classical matrix exponentiation, + the class follows a recursive strategy that involves creating :class:`qiskit.opflow.evolutions.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.evolutions.TrotterizationFactory`, + or by supplying a method instance of :class:`qiskit.opflow.evolutions.Trotter`, :class:`qiskit.opflow.evolutions.Suzuki` or :class:`qiskit.opflow.evolutions.QDrift`). + + The different trotterization methods that extend :class:`qiskit.opflow.evolutions.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.evolutions.PauliTrotterEvolution` class can now be handled directly by the algorithms + (for example, :class:`qiskit.algorithms.time_evolvers.TrotterQRTE`\). + + In a similar manner, the :class:`qiskit.opflow.evolutions.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. @@ -868,13 +822,13 @@ Trotterizations - No direct replacement. This class was used to create instances of one of the classes listed below. * - :class:`~qiskit.opflow.evolutions.Trotter` - - :class:`~synthesis.SuzukiTrotter` or :class:`~synthesis.LieTrotter` + - :class:`qiskit.synthesis.SuzukiTrotter` or :class:`qiskit.synthesis.LieTrotter` * - :class:`~qiskit.opflow.evolutions.Suzuki` - - :class:`~synthesis.SuzukiTrotter` + - :class:`qiskit.synthesis.SuzukiTrotter` * - :class:`~qiskit.opflow.evolutions.QDrift` - - :class:`~synthesis.QDrift` + - :class:`qiskit.synthesis.QDrift` Other Evolution Classes ~~~~~~~~~~~~~~~~~~~~~~~~ @@ -893,19 +847,20 @@ Other Evolution Classes - No direct replacement. The workflow no longer requires a specific operator for evolutions. * - :class:`~qiskit.opflow.evolutions.MatrixEvolution` - - :class:`~HamiltonianGate` + - :class:`.HamiltonianGate` * - :class:`~qiskit.opflow.evolutions.PauliTrotterEvolution` - - :class:`~PauliEvolutionGate` + - :class:`.PauliEvolutionGate` -Example 1: Trotter evolution -############################ .. raw:: html
- Opflow + Example 1: Trotter evolution +
+ +**Opflow** .. code-block:: python @@ -916,14 +871,7 @@ Example 1: Trotter evolution evol_result = evolution.convert(hamiltonian.exp_i()) evolved_state = evol_result.to_circuit() -.. raw:: html - -
- -.. raw:: html - -
- Alternative +**Alternative** .. code-block:: python @@ -941,13 +889,14 @@ Example 1: Trotter evolution
-Example 2: Evolution with time-dependent Hamiltonian -##################################################### .. raw:: html
- Opflow + Example 2: Evolution with time-dependent Hamiltonian +
+ +**Opflow** .. code-block:: python @@ -960,14 +909,7 @@ Example 2: Evolution with time-dependent Hamiltonian evol_result = evolution.convert((time * hamiltonian).exp_i()) evolved_state = evol_result.to_circuit() -.. raw:: html - -
- -.. raw:: html - -
- Alternative +**Alternative** .. code-block:: python @@ -987,13 +929,15 @@ Example 2: Evolution with time-dependent Hamiltonian
-Example 3: Matrix evolution -############################ + .. raw:: html
- Opflow + Example 3: Matrix evolution +
+ +**Opflow** .. code-block:: python @@ -1004,14 +948,7 @@ Example 3: Matrix evolution evol_result = evolution.convert(hamiltonian.exp_i()) evolved_state = evol_result.to_circuit() -.. raw:: html - -
- -.. raw:: html - -
- Alternative +**Alternative** .. code-block:: python @@ -1048,27 +985,28 @@ Algorithm-Agnostic Expectations - No direct replacement. This class was used to create instances of one of the classes listed below. * - :class:`~qiskit.opflow.expectations.AerPauliExpectation` - - Use :class:`~Estimator` primitive from ``qiskit_aer`` with ``approximation=True`` and ``shots=None`` as ``run_options``. - See example :ref:`below `. + - Use ``Estimator`` primitive from ``qiskit_aer`` with ``approximation=True`` and ``shots=None`` as ``run_options``. + See example below. * - :class:`~qiskit.opflow.expectations.MatrixExpectation` - - Use :class:`~Estimator` primitive from ``qiskit`` (if no shots are set, it performs an exact Statevector calculation). - See example :ref:`below `. + - Use ``Estimator`` primitive from ``qiskit`` (if no shots are set, it performs an exact Statevector calculation). + See example below. * - :class:`~qiskit.opflow.expectations.PauliExpectation` - - Use any ``Estimator`` primitive (for :class:`qiskit.primitives.Estimator`, set ``shots != None`` for a shot-based + - Use any ``Estimator`` primitive (for :class:`qiskit.primitives.Estimator`, set ``shots!=None`` for a shot-based simulation, for :class:`qiskit_aer.primitives.Estimator`, this is the default). .. _expect_state: -Example 1: Aer Pauli Expectation -################################# .. raw:: html
- Opflow + Example 1: Aer Pauli expectation +
+ +**Opflow** .. code-block:: python @@ -1088,14 +1026,7 @@ Example 1: Aer Pauli Expectation converted_meas = expect.convert(~StateFn(op) @ wvf) expect_values = sampler.convert(converted_meas).eval() -.. raw:: html - -
- -.. raw:: html - -
- Alternative +**Alternative** .. code-block:: python @@ -1118,13 +1049,14 @@ Example 1: Aer Pauli Expectation .. _matrix_state: -Example 2: Matrix Expectation -################################# .. raw:: html
- Opflow + Example 2: Matrix expectation +
+ +**Opflow** .. code-block:: python @@ -1143,14 +1075,7 @@ Example 2: Matrix Expectation plus_mean = converted_meas @ Plus values_plus = sampler.convert(plus_mean).eval() -.. raw:: html - -
- -.. raw:: html - -
- Alternative +**Alternative** .. code-block:: python @@ -1189,13 +1114,14 @@ CVaRExpectation .. _cvar: -Example 1: VQE with CVaR -######################## .. raw:: html
- Opflow + Example 1: VQE with CVaR +
+ +**Opflow** .. code-block:: python @@ -1208,20 +1134,13 @@ Example 1: VQE with CVaR backend = AerSimulator() ansatz = TwoLocal(2, 'ry', 'cz') op = PauliSumOp.from_list([('ZZ',1), ('IZ',1), ('II',1)]) - alpha=0.2 + 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) -.. raw:: html - -
- -.. raw:: html - -
- Alternative +**Alternative** .. code-block:: python @@ -1234,8 +1153,8 @@ Example 1: VQE with CVaR 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) + alpha = 0.2 + vqe = SamplingVQE(Sampler(), ansatz, opt, aggregation=alpha) result = vqe.compute_minimum_eigenvalue(op) .. raw:: html @@ -1246,6 +1165,6 @@ Gradients --------- *Back to* `Contents`_ -Replaced by the new :mod:`~qiskit.algorithms.gradients` module. You can see further details in the -algorithms migration guide. +Replaced by the new :mod:`qiskit.algorithms.gradients` module. You can see further details in the +`algorithms migration guide `_. From 568c0b3f86491c2bdf1e5fa63e05a634a9f9eee9 Mon Sep 17 00:00:00 2001 From: ElePT Date: Thu, 16 Feb 2023 14:27:09 +0100 Subject: [PATCH 24/50] Add grouping, algorithms link --- docs/migration_guides/opflow_migration.rst | 49 +++++++++++++++++++++- 1 file changed, 47 insertions(+), 2 deletions(-) diff --git a/docs/migration_guides/opflow_migration.rst b/docs/migration_guides/opflow_migration.rst index 86dfc371117..6a6ad7d27e0 100644 --- a/docs/migration_guides/opflow_migration.rst +++ b/docs/migration_guides/opflow_migration.rst @@ -660,7 +660,10 @@ Notably, this functionality has been replaced by the :mod:`~qiskit.primitives`. - :class:`~qiskit.primitives.Sampler` or :class:`~qiskit.primitives.Estimator` if used with :class:`~qiskit.oflow.expectations`. See examples below. * - :class:`~qiskit.opflow.converters.AbelianGrouper` - - No direct replacement. This class allowed a sum a of Pauli operators to be grouped. These type of groupings are now left to the primitives to handle. + - This class allowed a sum a of Pauli operators to be grouped, a similar functionality can be achieved + through the :meth:`~qiskit.quantum_info.SparsePauliOp.group_commuting` method of + :class:`qiskit.quantum_info.SparsePauliOp`, although this is not a 1-1 replacement, as you can see + in the example below. * - :class:`~qiskit.opflow.converters.DictToCircuitSum` - No direct replacement. This class was used to convert from ``DictStateFns`` or ``VectorStateFns`` to equivalent ``CircuitStateFns``. @@ -780,6 +783,48 @@ Notably, this functionality has been replaced by the :mod:`~qiskit.primitives`.
+.. raw:: html + +
+ Example 3: AbelianGrouper for grouping operators +
+ +**Opflow** + +.. code-block:: python + + from qiskit.opflow import PauliSumOp, AbelianGrouper + + op = PauliSumOp.from_list([("XX", 2), ("YY", 1), ("IZ",2j), ("ZZ",1j)]) + + grouped_sum = AbelianGrouper.group_subops(op) + # returns: SummedOp([PauliSumOp(SparsePauliOp(['XX'], coeffs=[2.+0.j]), coeff=1.0), + # PauliSumOp(SparsePauliOp(['YY'], coeffs=[1.+0.j]), coeff=1.0), + # PauliSumOp(SparsePauliOp(['IZ', 'ZZ'], coeffs=[0.+2.j, 0.+1.j]), + # coeff=1.0)], coeff=1.0, abelian=False) + + +**Alternative** + +.. code-block:: python + + from qiskit.quantum_info import SparsePauliOp + + op = SparsePauliOp.from_list([("XX", 2), ("YY", 1), ("IZ",2j), ("ZZ",1j)]) + + grouped = op.group_commuting() + # returns: [SparsePauliOp(["IZ", "ZZ"], coeffs=[0.+2.j, 0.+1j]), + # SparsePauliOp(["XX", "YY"], coeffs=[2.+0.j, 1.+0.j])] + + grouped = op.group_commuting(qubit_wise=True) + # returns: [SparsePauliOp(['XX'], coeffs=[2.+0.j]), + # SparsePauliOp(['YY'], coeffs=[1.+0.j]), + # SparsePauliOp(['IZ', 'ZZ'], coeffs=[0.+2.j, 0.+1.j])] + +.. raw:: html + +
+ Evolutions ---------- *Back to* `Contents`_ @@ -1166,5 +1211,5 @@ Gradients *Back to* `Contents`_ Replaced by the new :mod:`qiskit.algorithms.gradients` module. You can see further details in the -`algorithms migration guide `_. +`algorithms migration guide `_. From d82739c3d446313613ed4542a780acb4936b776f Mon Sep 17 00:00:00 2001 From: ElePT Date: Thu, 16 Feb 2023 14:57:37 +0100 Subject: [PATCH 25/50] Fix indentation --- docs/migration_guides/opflow_migration.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/migration_guides/opflow_migration.rst b/docs/migration_guides/opflow_migration.rst index 6a6ad7d27e0..8d8c92563ec 100644 --- a/docs/migration_guides/opflow_migration.rst +++ b/docs/migration_guides/opflow_migration.rst @@ -663,7 +663,7 @@ Notably, this functionality has been replaced by the :mod:`~qiskit.primitives`. - This class allowed a sum a of Pauli operators to be grouped, a similar functionality can be achieved through the :meth:`~qiskit.quantum_info.SparsePauliOp.group_commuting` method of :class:`qiskit.quantum_info.SparsePauliOp`, although this is not a 1-1 replacement, as you can see - in the example below. + in the example below. * - :class:`~qiskit.opflow.converters.DictToCircuitSum` - No direct replacement. This class was used to convert from ``DictStateFns`` or ``VectorStateFns`` to equivalent ``CircuitStateFns``. From ef5f412d42b5f47952fe82cb820cd3ecae50ed19 Mon Sep 17 00:00:00 2001 From: ElePT Date: Tue, 21 Feb 2023 10:37:52 +0100 Subject: [PATCH 26/50] Final review, add links --- docs/migration_guides/opflow_migration.rst | 123 ++++++++++++++------- 1 file changed, 80 insertions(+), 43 deletions(-) diff --git a/docs/migration_guides/opflow_migration.rst b/docs/migration_guides/opflow_migration.rst index 8d8c92563ec..823650a2528 100644 --- a/docs/migration_guides/opflow_migration.rst +++ b/docs/migration_guides/opflow_migration.rst @@ -4,10 +4,15 @@ Opflow Migration Guide TL;DR ----- +The new :mod:`~qiskit.primitives`, in combination with the :mod:`~qiskit.quantum_info` module, have superseded +functionality of :mod:`~qiskit.opflow`. Thus, the latter is being deprecated. + .. note:: - The new :mod:`~qiskit.primitives`, in combination with the :mod:`~qiskit.quantum_info` module, have superseded - functionality of :mod:`~qiskit.opflow`. Thus, the latter is being deprecated. + The use of :mod:`~qiskit.opflow` was tightly coupled to the :class:`~qiskit.utils.QuantumInstance` class, which + is also being deprecated. For more information on migrating the :class:`~qiskit.utils.QuantumInstance`, please + read the `quantum instance migration guide `_. + Background ---------- @@ -19,20 +24,25 @@ The recent release of the :mod:`qiskit.primitives` introduced a new paradigm for 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; encouraging 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. +that previously relied on this module; encouraging us to move away from :mod:`~qiskit.opflow` +and find new paths for developing algorithms based on the :mod:`~qiskit.primitives` interface and +the :mod:`~qiskit.quantum_info` module. 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. The function equivalency can be roughly summarized as follows: +.. |operator_globals| replace:: ``operator_globals`` +.. _operator_globals: https://qiskit.org/documentation/apidoc/opflow.html#operator-globals/ + .. list-table:: :header-rows: 1 * - Opflow Module - Alternative - * - Operators (:class:`~qiskit.opflow.OperatorBase`, ``operator_globals``, :mod:`~qiskit.opflow.primitive_ops`, + * - Operators (:class:`~qiskit.opflow.OperatorBase`, |operator_globals|_ , + :mod:`~qiskit.opflow.primitive_ops`, :mod:`~qiskit.opflow.list_ops`\) - :mod:`qiskit.quantum_info` *Operators* @@ -51,14 +61,23 @@ The function equivalency can be roughly summarized as follows: * - :mod:`qiskit.opflow.gradients` - :mod:`qiskit.algorithms.gradients` +.. |qiskit_aer.primitives| replace:: ``qiskit_aer.primitives`` +.. _qiskit_aer.primitives: https://qiskit.org/documentation/locale/de_DE/apidoc/aer_primitives.html + +.. |qiskit_aer.primitives.Estimator| replace:: ``qiskit_aer.primitives.Estimator`` +.. _qiskit_aer.primitives.Estimator: https://qiskit.org/documentation/locale/de_DE/stubs/qiskit_aer.primitives.Estimator.html + +.. |qiskit_ibm_runtime| replace:: ``qiskit_ibm_runtime`` +.. _qiskit_ibm_runtime: https://qiskit.org/documentation/partners/qiskit_ibm_runtime/primitives.html + .. attention:: Most references to the :class:`qiskit.primitives.Sampler` or :class:`qiskit.primitives.Estimator` in this guide - can be replaced with instances of the Aer primitives (:mod:`qiskit_aer.primitives`), Runtime primitives - (:mod:`qiskit_ibm_runtime`) or Terra backend primitives (:class:`qiskit.primitives.BackendSampler`, + can be replaced with instances of the Aer primitives (|qiskit_aer.primitives|_ ), Runtime primitives + (|qiskit_ibm_runtime|_ ) or Terra backend primitives (:class:`qiskit.primitives.BackendSampler`, :class:`qiskit.primitives.BackendEstimator`). Certain classes, such as the :class:`~qiskit.opflow.expectations.AerPauliExpectation`, are only replaced by a specific primitive instance - (in this case, :class:`qiskit_aer.primitives.Estimator`), or require a specific option configuration. + (in this case, |qiskit_aer.primitives.Estimator|_ ), or require a specific option configuration. This will be explicitly indicated in the corresponding section. Contents @@ -88,8 +107,11 @@ Operator Base Class ------------------- *Back to* `Contents`_ -The :class:`qiskit.opflow.OperatorBase` abstract class can be replaced with :class:`qiskit.quantum_info.BaseOperator`, -keeping in mind that :class:`qiskit.quantum_info.BaseOperator` is more generic than its opflow counterpart. +.. |qiskit.quantum_info.BaseOperator| replace:: ``qiskit.quantum_info.BaseOperator`` +.. _qiskit.quantum_info.BaseOperator: https://github.com/Qiskit/qiskit-terra/blob/main/qiskit/quantum_info/operators/base_operator.py + +The :class:`qiskit.opflow.OperatorBase` abstract class can be replaced with |qiskit.quantum_info.BaseOperator|_ , +keeping in mind that |qiskit.quantum_info.BaseOperator|_ is more generic than its opflow counterpart. .. list-table:: :header-rows: 1 @@ -97,21 +119,21 @@ keeping in mind that :class:`qiskit.quantum_info.BaseOperator` is more generic t * - Opflow - Alternative * - :class:`qiskit.opflow.OperatorBase` - - :class:`qiskit.quantum_info.BaseOperator` + - |qiskit.quantum_info.BaseOperator|_ .. attention:: Despite the similar class names, :class:`qiskit.opflow.OperatorBase` and - :class:`qiskit.quantum_info.BaseOperator` are not completely equivalent to each other, and the transition + |qiskit.quantum_info.BaseOperator|_ are not completely equivalent to each other, and the transition should be handled with care. Namely: 1. :class:`qiskit.opflow.OperatorBase` implements a broader algebra mixin. Some operator overloads that were commonly used :mod:`~qiskit.opflow` (for example ``~`` for ``.adjoint()``) are not defined for - :class:`qiskit.quantum_info.BaseOperator`. You might want to check the specific + |qiskit.quantum_info.BaseOperator|_. You might want to check the specific :mod:`~qiskit.quantum_info` subclass instead. 2. :class:`qiskit.opflow.OperatorBase` also implements methods such as ``.to_matrix()`` or ``.to_spmatrix()``, - which are only found in some of the :class:`qiskit.quantum_info.BaseOperator` subclasses. + which are only found in some of the |qiskit.quantum_info.BaseOperator|_ subclasses. See API reference for more information. @@ -121,7 +143,7 @@ Operator Globals *Back to* `Contents`_ Opflow provided shortcuts to define common single qubit states, operators, and non-parametrized gates in the -:mod:`~qiskit.opflow.operator_globals` module. +|operator_globals|_ module. These were mainly used for didactic purposes or quick prototyping, and can easily be replaced by their corresponding :mod:`~qiskit.quantum_info` class: :class:`~qiskit.quantum_info.Pauli`, :class:`~qiskit.quantum_info.Clifford` or @@ -134,9 +156,10 @@ These were mainly used for didactic purposes or quick prototyping, and can easil The 1-qubit paulis were commonly used for quick testing of algorithms, as they could be combined to create more complex operators (for example, ``0.39 * (I ^ Z) + 0.5 * (X ^ X)``). -These operations implicitly created operators of type :class:`~qiskit.opflow.PauliSumOp`, and can be replaced by +These operations implicitly created operators of type :class:`~qiskit.opflow.primitive_ops.PauliSumOp`, and can be replaced by directly creating a corresponding :class:`~qiskit.quantum_info.SparsePauliOp`, as shown in the examples below. + .. list-table:: :header-rows: 1 @@ -234,14 +257,14 @@ Common non-parametrized gates (Clifford) * - :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`. ``quantum_info`` + - Append corresponding gate to :class:`~qiskit.circuit.QuantumCircuit`. :mod:`~qiskit.quantum_info` :class:`~qiskit.quantum_info.Operator`\s can be also directly constructed from quantum circuits. Another alternative is to wrap the circuit in :class:`~qiskit.quantum_info.Clifford` and call ``Clifford.to_operator()``. .. note:: - Constructing ``quantum_info`` operators from circuits is not efficient, as it is a dense operation and + Constructing :mod:`~qiskit.quantum_info` operators from circuits is not efficient, as it is a dense operation and scales exponentially with the size of the circuit, use with care. @@ -298,11 +321,12 @@ Common non-parametrized gates (Clifford) - Alternative * - :class:`~qiskit.opflow.Zero`, :class:`~qiskit.opflow.One`, :class:`~qiskit.opflow.Plus`, :class:`~qiskit.opflow.Minus` - - :class:`~qiskit.quantum_info.Statevector` or simply :class:`~qiskit.QuantumCircuit`, depending on the use case. + - :class:`~qiskit.quantum_info.Statevector` or simply :class:`~qiskit.circuit.QuantumCircuit`, depending on the use case. .. note:: - For efficient simulation of stabilizer states, ``quantum_info`` includes a :class:`~qiskit.quantum_info.StabilizerState` class. See API ref. for more info. + For efficient simulation of stabilizer states, :mod:`~qiskit.quantum_info` includes a + :class:`~qiskit.quantum_info.StabilizerState` class. See API ref. for more info. .. raw:: html @@ -350,7 +374,8 @@ Primitive and List Ops *Back to* `Contents`_ 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 ``quantum_info``\'s :mod:`~qiskit.quantum_info.operators` instead. +:mod:`~qiskit.opflow.list_ops` can now leverage elements from :mod:`~qiskit.quantum_info`\'s +operators instead. Some of these classes do not require a 1-1 replacement because they were created to interface with other opflow components. @@ -384,6 +409,9 @@ to initialize it. Thus, when migrating opflow code, it is important to look for alternatives to replace the specific subclasses that might have been used "under the hood" in the original code: +.. |qiskit.quantum_info.Z2Symmetries| replace:: ``qiskit.quantum_info.Z2Symmetries`` +.. _qiskit.quantum_info.Z2Symmetries: https://github.com/Qiskit/qiskit-terra/blob/main/qiskit/quantum_info/analysis/z2_symmetries.py + .. list-table:: :header-rows: 1 @@ -395,7 +423,7 @@ might have been used "under the hood" in the original code: no direct replacement. * - :class:`~qiskit.opflow.primitive_ops.CircuitOp` - - :class:`~qiskit.QuantumCircuit` + - :class:`~qiskit.circuit.QuantumCircuit` * - :class:`~qiskit.opflow.primitive_ops.MatrixOp` - :class:`~qiskit.quantum_info.Operator` @@ -410,10 +438,10 @@ might have been used "under the hood" in the original code: * - :class:`~qiskit.opflow.primitive_ops.TaperedPauliSumOp` - This class was used to combine a :class:`.PauliSumOp` with its identified symmetries in one object. This functionality is not currently used in any workflow, and has been deprecated without replacement. - See ``Z2Symmetries`` example for updated workflow. + See |qiskit.quantum_info.Z2Symmetries|_ example for updated workflow. - * - :class:`~qiskit.opflow.primitive_ops.Z2Symmetries` - - :class:`~qiskit.quantum_info.Z2Symmetries`. See example below. + * - :class:`qiskit.opflow.primitive_ops.Z2Symmetries` + - |qiskit.quantum_info.Z2Symmetries|_ . See example below. .. _pauli_sum_op: @@ -500,7 +528,7 @@ ListOps The :mod:`~qiskit.opflow.list_ops` module contained classes for manipulating lists of :mod:`~qiskit.opflow.primitive_ops` or :mod:`~qiskit.opflow.state_fns`. The :mod:`~qiskit.quantum_info` alternatives for this functionality are the -:class:`~qiskit.quantum_info.PauliList`, :class:`~qiskit.quantum_info.SparsePauliOp` (for sums of ``Pauli``\s). +:class:`~qiskit.quantum_info.PauliList`, :class:`~qiskit.quantum_info.SparsePauliOp` (for sums of :class:`~qiskit.quantum_info.Pauli`\s). .. list-table:: :header-rows: 1 @@ -510,7 +538,7 @@ or :mod:`~qiskit.opflow.state_fns`. The :mod:`~qiskit.quantum_info` alternatives * - :class:`~qiskit.opflow.list_ops.ListOp` - No direct replacement. This is the base class for operator lists. In general, these could be replaced with - Python ``list``\s. For ``Pauli`` operators, there are a few alternatives, depending on the use-case. + Python ``list``\s. For :class:`~qiskit.quantum_info.Pauli` operators, there are a few alternatives, depending on the use-case. One alternative is :class:`~qiskit.quantum_info.PauliList`. * - :class:`~qiskit.opflow.list_ops.ComposedOp` @@ -518,18 +546,22 @@ or :mod:`~qiskit.opflow.state_fns`. The :mod:`~qiskit.quantum_info` alternatives one object (no lazy evaluation). * - :class:`~qiskit.opflow.list_ops.SummedOp` - - No direct replacement. For ``Pauli`` operators, use :class:`~qiskit.quantum_info.SparsePauliOp`. + - No direct replacement. For :class:`~qiskit.quantum_info.Pauli` operators, use :class:`~qiskit.quantum_info.SparsePauliOp`. * - :class:`~qiskit.opflow.list_ops.TensoredOp` - - No direct replacement. For ``Pauli`` operators, use :class:`~qiskit.quantum_info.SparsePauliOp`. + - No direct replacement. For :class:`~qiskit.quantum_info.Pauli` operators, use :class:`~qiskit.quantum_info.SparsePauliOp`. State Functions --------------- *Back to* `Contents`_ +.. |qiskit.quantum_info.QuantumState| replace:: ``qiskit.quantum_info.QuantumState`` +.. _qiskit.quantum_info.QuantumState: https://github.com/Qiskit/qiskit-terra/blob/main/qiskit/quantum_info/states/quantum_state.py + + The :mod:`~qiskit.opflow.state_fns` module can be generally replaced by subclasses of :mod:`~qiskit.quantum_info`\'s -:class:`~qiskit.quantum_info.states.quantum_state.QuantumState`, with some differences to keep in mind: +|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. Algorithm-specific functionality has been migrated to the respective algorithm's module @@ -578,7 +610,7 @@ identify the sub-class that is being used, to then look for an alternative. * - :class:`~qiskit.opflow.state_fns.DictStateFn` - This class was used to store efficient representations of sparse measurement results. The :class:`~qiskit.primitives.Sampler` now returns the measurements as an instance of - :class:`~qiskit.result.QuasiDist` (see example in `Converters`_). + :class:`~qiskit.result.QuasiDistribution` (see example in `Converters`_). * - :class:`~qiskit.opflow.state_fns.VectorStateFn` - This class can be replaced with :class:`~qiskit.quantum_info.Statevector` or @@ -650,6 +682,10 @@ In the case of the :class:`~qiskit.opflow.converters.CircuitSampler`, it travers approximations of its state functions using a quantum backend. Notably, this functionality has been replaced by the :mod:`~qiskit.primitives`. +.. |ParityMapper| replace:: ``ParityMapper`` +.. _ParityMapper: https://qiskit.org/documentation/nature/stubs/qiskit_nature.second_q.mappers.ParityMapper.html#qiskit_nature.second_q.mappers.ParityMapper + + .. list-table:: :header-rows: 1 @@ -665,12 +701,12 @@ Notably, this functionality has been replaced by the :mod:`~qiskit.primitives`. :class:`qiskit.quantum_info.SparsePauliOp`, although this is not a 1-1 replacement, as you can see in the example below. * - :class:`~qiskit.opflow.converters.DictToCircuitSum` - - No direct replacement. This class was used to convert from ``DictStateFns`` or ``VectorStateFns`` - to equivalent ``CircuitStateFns``. + - No direct replacement. This class was used to convert from :class:`~qiskit.opflow.state_fns.DictStateFn`\s or + :class:`~qiskit.opflow.state_fns.VectorStateFn`\s to equivalent :class:`~qiskit.opflow.state_fns.CircuitStateFn`\s. * - :class:`~qiskit.opflow.converters.PauliBasisChange` - No direct replacement. This class was used for changing Paulis into other bases. * - :class:`~qiskit.opflow.converters.TwoQubitReduction` - - No direct replacement. This class implements a chemistry-specific reduction for the ``ParityMapper`` class in ``qiskit-nature``. + - No direct replacement. This class implements a chemistry-specific reduction for the |ParityMapper|_ class in ``qiskit-nature``. The general symmetry logic this mapper depends on has been refactored to other classes in :mod:`~qiskit.quantum_info`, so this specific :mod:`~qiskit.opflow` implementation is no longer necessary. @@ -832,7 +868,7 @@ 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. +:mod:`qiskit.synthesis` evolution module. .. note:: @@ -843,11 +879,12 @@ delayed synthesis of the gates or efficient transpilation of the circuits, so th perform the trotterization (either via a ``string``\, which is then inputted into a :class:`qiskit.opflow.evolutions.TrotterizationFactory`, or by supplying a method instance of :class:`qiskit.opflow.evolutions.Trotter`, :class:`qiskit.opflow.evolutions.Suzuki` or :class:`qiskit.opflow.evolutions.QDrift`). - The different trotterization methods that extend :class:`qiskit.opflow.evolutions.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()``. + The different trotterization methods that extend :class:`qiskit.opflow.evolutions.TrotterizationBase` were migrated to + :mod:`qiskit.synthesis`, + and now extend the :class:`qiskit.synthesis.ProductFormula` base class. They no longer contain a ``.convert()`` method for + standalone use, but now are designed to be plugged into the :class:`.PauliEvolutionGate` and called via ``.synthesize()``. In this context, the job of the :class:`qiskit.opflow.evolutions.PauliTrotterEvolution` class can now be handled directly by the algorithms - (for example, :class:`qiskit.algorithms.time_evolvers.TrotterQRTE`\). + (for example, :class:`~qiskit.algorithms.time_evolvers.trotterization.TrotterQRTE`\). In a similar manner, the :class:`qiskit.opflow.evolutions.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. @@ -1030,16 +1067,16 @@ Algorithm-Agnostic Expectations - No direct replacement. This class was used to create instances of one of the classes listed below. * - :class:`~qiskit.opflow.expectations.AerPauliExpectation` - - Use ``Estimator`` primitive from ``qiskit_aer`` with ``approximation=True`` and ``shots=None`` as ``run_options``. + - Use ``Estimator`` primitive from |qiskit_aer.primitives|_ with ``approximation=True`` and ``shots=None`` as ``run_options``. See example below. * - :class:`~qiskit.opflow.expectations.MatrixExpectation` - - Use ``Estimator`` primitive from ``qiskit`` (if no shots are set, it performs an exact Statevector calculation). + - Use :class:`~qiskit.primitives.Estimator` primitive from :mod:`qiskit` (if no shots are set, it performs an exact Statevector calculation). See example below. * - :class:`~qiskit.opflow.expectations.PauliExpectation` - - Use any ``Estimator`` primitive (for :class:`qiskit.primitives.Estimator`, set ``shots!=None`` for a shot-based - simulation, for :class:`qiskit_aer.primitives.Estimator`, this is the default). + - Use any Estimator primitive (for :class:`qiskit.primitives.Estimator`, set ``shots!=None`` for a shot-based + simulation, for |qiskit_aer.primitives.Estimator|_ , this is the default). .. _expect_state: From 2368ab674ae6ed136542ca857e5ac91cb9f1ada5 Mon Sep 17 00:00:00 2001 From: ElePT Date: Thu, 23 Feb 2023 18:10:27 +0100 Subject: [PATCH 27/50] Add gradients --- docs/migration_guides/opflow_migration.rst | 259 ++++++++++++++++++++- 1 file changed, 257 insertions(+), 2 deletions(-) diff --git a/docs/migration_guides/opflow_migration.rst b/docs/migration_guides/opflow_migration.rst index 823650a2528..05239e61721 100644 --- a/docs/migration_guides/opflow_migration.rst +++ b/docs/migration_guides/opflow_migration.rst @@ -1247,6 +1247,261 @@ Gradients --------- *Back to* `Contents`_ -Replaced by the new :mod:`qiskit.algorithms.gradients` module. You can see further details in the -`algorithms migration guide `_. +The opflow :mod:`~qiskit.opflow.gradients` framework has been replaced by the new :mod:`qiskit.algorithms.gradients` +module. The new gradients are **primitive-based subroutines** commonly used by algorithms and applications, which +can also be executed in a standalone manner. For this reason, they now reside under :mod:`qiskit.algorithms`. +The former gradient framework contained base classes, converters and derivatives. The "derivatives" +followed a factory design pattern, where different methods could be provided via string identifiers +to each of these classes. The new gradient framework contains two main families of subroutines: +**Gradients** and **QGT/QFI**. + +This leads to a change in the workflow, where instead of doing: + +.. code-block:: python + + from qiskit.opflow import Gradient + + grad = Gradient(method="param_shift") + + # task based on expectation value computations + gradients + +We now import explicitly the desired class, depending on the target primitive (Sampler/Estimator) and target method: + +.. code-block:: python + + from qiskit.algorithms.gradients import ParamShiftEstimatorGradient + + grad = ParamShiftEstimatorGradient(estimator) + + # task based on expectation value computations + gradients + +This works similarly for the QFI class, where instead of doing: + +.. code-block:: python + + from qiskit.opflow import QFI + + qfi = QFI(method="param_shift") + + # task based on expectation value computations + QFI + +You now have a generic QFI implementation that can be initialized with any gradient instance: + +.. code-block:: python + + from qiskit.algorithms.gradients import ParamShiftEstimatorGradient, QFI + + grad = ParamShiftEstimatorGradient(estimator) + qfi = QFI(gradient=grad) + + # task based on expectation value computations + QFI + +.. note:: + + Here is a quick guide for migrating the most common gradient settings. Please note that all new gradient + imports follow the format: + + .. code-block:: python + + from qiskit.algorithms.gradients import MethodPrimitiveGradient + + .. raw:: html + +
+ Gradients +
+ + .. list-table:: + :header-rows: 1 + + * - Opflow + - Alternative + + * - ``Gradient(method="lin_comb")`` + - ``LinCombEstimatorGradient(estimator=estimator)`` or ``LinCombSamplerGradient(sampler=sampler)`` + * - ``Gradient(method="param_shift")`` + - ``ParamShiftEstimatorGradient(estimator=estimator)`` or ``ParamShiftSamplerGradient(sampler=sampler)`` + * - ``Gradient(method="fin_diff")`` + - ``FiniteDiffEstimatorGradient(estimator=estimator)`` or ``ParamShiftSamplerGradient(sampler=sampler)`` + + + .. raw:: html + +
+ + .. raw:: html + +
+ QFI/QGT +
+ + .. list-table:: + :header-rows: 1 + + * - Opflow + - Alternative + + * - ``QFI(method="lin_comb")`` + - ``QFI(gradient=gradient_instance_for_method)`` + + .. raw:: html + +
+ +Other auxiliary classes in the legacy gradient framework have now been deprecated. Here is the complete migration +list: + +.. list-table:: + :header-rows: 1 + + * - Opflow + - Alternative + + * - :class:`~qiskit.opflow.gradients.DerivativeBase` + - No replacement. This was the base class for the gradient, hessian and QFI base classes. + * - :class:`.GradientBase` and :class:`~qiskit.opflow.gradients.Gradient` + - :class:`.BaseSamplerGradient` or :class:`.BaseEstimatorGradient`, and specific subclasses per method, + as explained above. + * - :class:`.HessianBase` and :class:`~qiskit.opflow.gradients.Hessian` + - No replacement. The new gradient framework does not work with hessians as independent objects. + * - :class:`.QFIBase` and :class:`~qiskit.opflow.gradients.QFI` + - The new :class:`~qiskit.algorithms.gradients.QFI` class extends :class:`~qiskit.algorithms.gradients.QGT`, so the + corresponding base class is :class:`~qiskit.algorithms.gradients.BaseQGT` + * - :class:`~qiskit.opflow.gradients.CircuitGradient` + - No replacement. This class was used to convert between circuit and gradient + :class:`~qiskit.opflow.primitive_ops.PrimitiveOp`, and this functionality is no longer necessary. + * - :class:`~qiskit.opflow.gradients.CircuitQFI` + - No replacement. This class was used to convert between circuit and QFI + :class:`~qiskit.opflow.primitive_ops.PrimitiveOp`, and this functionality is no longer necessary. + * - :class:`~qiskit.opflow.gradients.NaturalGradient` + - No replacement. The same functionality can be achieved with the QFI module. + +.. raw:: html + +
+ Example 1: Finite Differences Batched Gradient +
+ +**Opflow** + +.. code-block:: python + + from qiskit.circuit.library import Parameter, QuantumCircuit + from qiskit.opflow import Gradient, X, Z, StateFn, CircuitStateFn + + ham = 0.5 * X - 1 * Z + + a = Parameter("a") + b = Parameter("b") + c = Parameter("c") + + qc = QuantumCircuit(1) + qc.h(0) + qc.u(a, b, c, 0) + qc.h(0) + qc.measure_all() + + op = ~StateFn(ham) @ CircuitStateFn(primitive=qc, coeff=1.0) + + # the gradient class acted similarly opflow converters, + # with a .convert() step and an .eval() step + state_grad = Gradient(grad_method=method).convert(operator=op, params=params) + + # the old workflow did not allow for batched evaluation of parameter values + values_dict = [{a: np.pi / 4, b: 0, c: 0}, {a: np.pi / 4, b: np.pi / 4, c: np.pi / 4}] + gradients = [] + for i, value_dict in enumerate(values_dict): + gradients.append(state_grad.assign_parameters(value_dict).eval()) + +**Alternative** + +.. code-block:: python + + from qiskit.circuit.library import Parameter, QuantumCircuit + from qiskit.primitives import Estimator + from qiskit.algorithms.gradients import ParamShiftEstimatorGradient + from qiskit.quantum_info import SparsePauliOp + + ham = SparsePauliOp.from_list([(X, 0.5), (Z, -1)]) + + a = Parameter("a") + b = Parameter("b") + c = Parameter("c") + + qc = QuantumCircuit(1) + qc.h(0) + qc.u(a, b, c, 0) + qc.h(0) + qc.measure_all() + + estimator = Estimator() + gradient = ParamShiftEstimatorGradient(estimator) + + # the new workflow follows an interface close to the primitives' + param_list = [[np.pi / 4, 0, 0], [np.pi / 4, np.pi / 4, np.pi / 4]] + # for batched evaluations, the number of circuits must match the + # number of parameter value sets + gradients = gradient.run([qc] * 3, param_list).result().gradients + +.. raw:: html + +
+ +.. raw:: html + +
+ Example 2: QFI +
+ +**Opflow** + +.. code-block:: python + + from qiskit.circuit.library import Parameter, QuantumCircuit + from qiskit.opflow import QFI, CircuitStateFn + + # create the circuit + a, b = Parameter("a"), Parameter("b") + qc = QuantumCircuit(1) + qc.h(0) + qc.rz(a, 0) + qc.rx(b, 0) + + # convert the circuit to a QFI object + op = CircuitStateFn(qc) + qfi = QFI(qfi_method="lin_comb_full").convert(operator=op) + + # test for different values + values_dict = [{a: np.pi / 4, b: 0.1}, {a: np.pi, b: 0.1}, {a: np.pi / 2, b: 0.1}] + qfis = [] + for i, value_dict in enumerate(values_dict): + qfis.append(qfi.assign_parameters(value_dict).eval()) + +**Alternative** + +.. code-block:: python + + from qiskit.circuit.library import Parameter, QuantumCircuit + from qiskit.primitives import Sampler + from qiskit.algorithms.gradients import LinCombSamplerGradient, QFI + + # create the circuit + a, b = Parameter("a"), Parameter("b") + qc = QuantumCircuit(1) + qc.h(0) + qc.rz(a, 0) + qc.rx(b, 0) + + sampler = Sampler() + gradient = ParamShiftSamplerGradient(sampler) + qfi = QFI(gradient) + + # test for different values + values_list = [[np.pi / 4,, 0.1], [np.pi, 0.1], [np.pi / 2, 0.1]] + qfis = qfi.run([qc] * 3, values_list).results().qfis + + +.. raw:: html + +
From f67cd8b182d1c4ff80be0c12b51835ae56d9eb25 Mon Sep 17 00:00:00 2001 From: ElePT <57907331+ElePT@users.noreply.github.com> Date: Mon, 27 Feb 2023 15:16:12 +0100 Subject: [PATCH 28/50] Apply heading suggestions Co-authored-by: Junye Huang --- docs/migration_guides/index.rst | 4 ++-- docs/migration_guides/opflow_migration.rst | 21 +++++++++++---------- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/docs/migration_guides/index.rst b/docs/migration_guides/index.rst index 38cea61c2a7..d484cce8347 100644 --- a/docs/migration_guides/index.rst +++ b/docs/migration_guides/index.rst @@ -1,6 +1,6 @@ -======================= +####################### Qiskit Migration Guides -======================= +####################### .. toctree:: :maxdepth: 1 diff --git a/docs/migration_guides/opflow_migration.rst b/docs/migration_guides/opflow_migration.rst index 05239e61721..7b1fd606101 100644 --- a/docs/migration_guides/opflow_migration.rst +++ b/docs/migration_guides/opflow_migration.rst @@ -1,9 +1,9 @@ -======================= +####################### Opflow Migration Guide -======================= +####################### TL;DR ------ +===== The new :mod:`~qiskit.primitives`, in combination with the :mod:`~qiskit.quantum_info` module, have superseded functionality of :mod:`~qiskit.opflow`. Thus, the latter is being deprecated. @@ -15,7 +15,7 @@ functionality of :mod:`~qiskit.opflow`. Thus, the latter is being deprecated. 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. @@ -81,7 +81,7 @@ The function equivalency can be roughly summarized as follows: This will be explicitly indicated in the corresponding section. Contents --------- +======== This document covers the migration from these opflow sub-modules: @@ -104,7 +104,7 @@ This document covers the migration from these opflow sub-modules: Operator Base Class -------------------- +=================== *Back to* `Contents`_ .. |qiskit.quantum_info.BaseOperator| replace:: ``qiskit.quantum_info.BaseOperator`` @@ -139,7 +139,7 @@ keeping in mind that |qiskit.quantum_info.BaseOperator|_ is more generic than it Operator Globals ----------------- +================ *Back to* `Contents`_ Opflow provided shortcuts to define common single qubit states, operators, and non-parametrized gates in the @@ -152,6 +152,7 @@ These were mainly used for didactic purposes or quick prototyping, and can easil 1-Qubit Paulis ~~~~~~~~~~~~~~ +-------------- *Back to* `Contents`_ The 1-qubit paulis were commonly used for quick testing of algorithms, as they could be combined to create more complex operators @@ -246,7 +247,7 @@ directly creating a corresponding :class:`~qiskit.quantum_info.SparsePauliOp`, a
Common non-parametrized gates (Clifford) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +---------------------------------------- *Back to* `Contents`_ .. list-table:: @@ -311,7 +312,7 @@ Common non-parametrized gates (Clifford)
1-Qubit States -~~~~~~~~~~~~~~ +-------------- *Back to* `Contents`_ .. list-table:: @@ -370,7 +371,7 @@ Common non-parametrized gates (Clifford) Primitive and List Ops ----------------------- +====================== *Back to* `Contents`_ Most of the workflows that previously relied in components from :mod:`~qiskit.opflow.primitive_ops` and From b2b718d0a49f4c821a7748be4775cc41f9d83178 Mon Sep 17 00:00:00 2001 From: ElePT Date: Mon, 27 Feb 2023 16:49:00 +0100 Subject: [PATCH 29/50] Add tags --- qiskit/quantum_info/__init__.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/qiskit/quantum_info/__init__.py b/qiskit/quantum_info/__init__.py index 45a8d46e476..237ce35830b 100644 --- a/qiskit/quantum_info/__init__.py +++ b/qiskit/quantum_info/__init__.py @@ -16,6 +16,8 @@ .. currentmodule:: qiskit.quantum_info +.. _quantum_info_operators: + Operators ========= @@ -33,6 +35,8 @@ StabilizerTable pauli_basis +.. _quantum_info_states: + States ====== @@ -108,6 +112,8 @@ hellinger_distance hellinger_fidelity +.. _quantum_info_synthesis: + Synthesis ========= From fefd4a67204717d3d6435043a1fc72a71a79cc30 Mon Sep 17 00:00:00 2001 From: ElePT Date: Mon, 27 Feb 2023 16:49:53 +0100 Subject: [PATCH 30/50] Apply suggestions code review --- docs/index.rst | 4 +- docs/migration_guides/opflow_migration.rst | 53 ++++++++++++++-------- 2 files changed, 35 insertions(+), 22 deletions(-) diff --git a/docs/index.rst b/docs/index.rst index 69c065eb9fe..5dd462fe2ca 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -7,9 +7,9 @@ Qiskit Terra documentation :hidden: API References - Release Notes Migration Guides - + Release Notes + .. Hiding - Indices and tables :ref:`genindex` :ref:`modindex` diff --git a/docs/migration_guides/opflow_migration.rst b/docs/migration_guides/opflow_migration.rst index 7b1fd606101..873d7483370 100644 --- a/docs/migration_guides/opflow_migration.rst +++ b/docs/migration_guides/opflow_migration.rst @@ -44,16 +44,16 @@ The function equivalency can be roughly summarized as follows: * - Operators (:class:`~qiskit.opflow.OperatorBase`, |operator_globals|_ , :mod:`~qiskit.opflow.primitive_ops`, :mod:`~qiskit.opflow.list_ops`\) - - :mod:`qiskit.quantum_info` *Operators* + - :mod:`qiskit.quantum_info` :ref:`quantum_info_operators` * - :mod:`qiskit.opflow.state_fns` - - :mod:`qiskit.quantum_info` *States* + - :mod:`qiskit.quantum_info` :ref:`quantum_info_states` * - :mod:`qiskit.opflow.converters` - :mod:`qiskit.primitives` * - :mod:`qiskit.opflow.evolutions` - - :mod:`qiskit.quantum_info` *Synthesis* + - :mod:`qiskit.quantum_info` :ref:`quantum_info_synthesis` * - :mod:`qiskit.opflow.expectations` - :class:`qiskit.primitives.Estimator` @@ -64,21 +64,34 @@ The function equivalency can be roughly summarized as follows: .. |qiskit_aer.primitives| replace:: ``qiskit_aer.primitives`` .. _qiskit_aer.primitives: https://qiskit.org/documentation/locale/de_DE/apidoc/aer_primitives.html +.. |qiskit_aer.primitives.Sampler| replace:: ``qiskit_aer.primitives.Sampler`` +.. _qiskit_aer.primitives.Sampler: https://qiskit.org/documentation/locale/de_DE/stubs/qiskit_aer.primitives.Sampler.html + .. |qiskit_aer.primitives.Estimator| replace:: ``qiskit_aer.primitives.Estimator`` .. _qiskit_aer.primitives.Estimator: https://qiskit.org/documentation/locale/de_DE/stubs/qiskit_aer.primitives.Estimator.html .. |qiskit_ibm_runtime| replace:: ``qiskit_ibm_runtime`` .. _qiskit_ibm_runtime: https://qiskit.org/documentation/partners/qiskit_ibm_runtime/primitives.html +.. |qiskit_ibm_runtime.Sampler| replace:: ``qiskit_ibm_runtime.Sampler`` +.. _qiskit_ibm_runtime.Sampler: https://qiskit.org/documentation/partners/qiskit_ibm_runtime/stubs/qiskit_ibm_runtime.Sampler.html + +.. |qiskit_ibm_runtime.Estimator| replace:: ``qiskit_ibm_runtime.Estimator`` +.. _qiskit_ibm_runtime.Estimator: https://qiskit.org/documentation/partners/qiskit_ibm_runtime/stubs/qiskit_ibm_runtime.Estimator.html + .. attention:: Most references to the :class:`qiskit.primitives.Sampler` or :class:`qiskit.primitives.Estimator` in this guide - can be replaced with instances of the Aer primitives (|qiskit_aer.primitives|_ ), Runtime primitives - (|qiskit_ibm_runtime|_ ) or Terra backend primitives (:class:`qiskit.primitives.BackendSampler`, - :class:`qiskit.primitives.BackendEstimator`). Certain classes, such as the - :class:`~qiskit.opflow.expectations.AerPauliExpectation`, are only replaced by a specific primitive instance + can be replaced with instances of the: + + - Aer primitives (|qiskit_aer.primitives.Sampler|_, |qiskit_aer.primitives.Estimator|_) + - Runtime primitives (|qiskit_ibm_runtime.Sampler|_, |qiskit_ibm_runtime.Estimator|_ ) + - Terra backend primitives (:class:`qiskit.primitives.BackendSampler`, :class:`qiskit.primitives.BackendEstimator`) + + Certain classes, such as the + :class:`~qiskit.opflow.expectations.AerPauliExpectation`, can only be replaced by a specific primitive instance (in this case, |qiskit_aer.primitives.Estimator|_ ), or require a specific option configuration. - This will be explicitly indicated in the corresponding section. + If this is the case, it will be explicitly indicated in the corresponding section. Contents ======== @@ -151,7 +164,6 @@ These were mainly used for didactic purposes or quick prototyping, and can easil 1-Qubit Paulis -~~~~~~~~~~~~~~ -------------- *Back to* `Contents`_ @@ -381,7 +393,7 @@ Some of these classes do not require a 1-1 replacement because they were created opflow components. Primitive Ops -~~~~~~~~~~~~~~ +------------- *Back to* `Contents`_ :class:`~qiskit.opflow.primitive_ops.PrimitiveOp` is the :mod:`~qiskit.opflow.primitive_ops` module's base class. @@ -524,7 +536,7 @@ might have been used "under the hood" in the original code:
ListOps -~~~~~~~ +-------- *Back to* `Contents`_ The :mod:`~qiskit.opflow.list_ops` module contained classes for manipulating lists of :mod:`~qiskit.opflow.primitive_ops` @@ -554,7 +566,7 @@ or :mod:`~qiskit.opflow.state_fns`. The :mod:`~qiskit.quantum_info` alternatives State Functions ---------------- +=============== *Back to* `Contents`_ .. |qiskit.quantum_info.QuantumState| replace:: ``qiskit.quantum_info.QuantumState`` @@ -674,7 +686,8 @@ See more applied examples in `Expectations`_ and `Converters`_. Converters ----------- +========== + *Back to* `Contents`_ The role of this sub-module was to convert the operators into other opflow operator classes @@ -863,7 +876,7 @@ Notably, this functionality has been replaced by the :mod:`~qiskit.primitives`.
Evolutions ----------- +========== *Back to* `Contents`_ The :mod:`qiskit.opflow.evolutions` sub-module was created to provide building blocks for Hamiltonian simulation algorithms, @@ -892,7 +905,7 @@ delayed synthesis of the gates or efficient transpilation of the circuits, so th This class is no longer necessary, as the :class:`.HamiltonianGate`\s can be directly handled by the algorithms. Trotterizations -~~~~~~~~~~~~~~~ +--------------- *Back to* `Contents`_ .. list-table:: @@ -914,7 +927,7 @@ Trotterizations - :class:`qiskit.synthesis.QDrift` Other Evolution Classes -~~~~~~~~~~~~~~~~~~~~~~~~ +----------------------- *Back to* `Contents`_ .. list-table:: @@ -1048,14 +1061,14 @@ Other Evolution Classes
Expectations ------------- +============ *Back to* `Contents`_ 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 -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +------------------------------- *Back to* `Contents`_ .. list-table:: @@ -1183,7 +1196,7 @@ Algorithm-Agnostic Expectations
CVaRExpectation -~~~~~~~~~~~~~~~ +--------------- *Back to* `Contents`_ .. list-table:: @@ -1245,7 +1258,7 @@ CVaRExpectation
Gradients ---------- +========= *Back to* `Contents`_ The opflow :mod:`~qiskit.opflow.gradients` framework has been replaced by the new :mod:`qiskit.algorithms.gradients` From a3c0c0767014e32e4d438dbf36fe8b6df1417224 Mon Sep 17 00:00:00 2001 From: ElePT Date: Tue, 28 Feb 2023 14:11:26 +0100 Subject: [PATCH 31/50] Correct code examples, add outputs --- docs/migration_guides/opflow_migration.rst | 590 ++++++++++++++++----- 1 file changed, 460 insertions(+), 130 deletions(-) diff --git a/docs/migration_guides/opflow_migration.rst b/docs/migration_guides/opflow_migration.rst index 873d7483370..a862021ad58 100644 --- a/docs/migration_guides/opflow_migration.rst +++ b/docs/migration_guides/opflow_migration.rst @@ -202,6 +202,10 @@ directly creating a corresponding :class:`~qiskit.quantum_info.SparsePauliOp`, a operator = X ^ X +.. code-block:: python + + >>> operator + PauliOp(Pauli('XX'), coeff=1.0) **Alternative** @@ -210,13 +214,22 @@ directly creating a corresponding :class:`~qiskit.quantum_info.SparsePauliOp`, a from qiskit.quantum_info import Pauli, SparsePauliOp X = Pauli('X') - op = X ^ X + operator = X ^ X + +.. code-block:: python - # equivalent to: - op = Pauli('XX') + >>> operator + Pauli('XX') - # equivalent to: - op = SparsePauliOp('XX') + >>> # equivalent to: + >>> operator = Pauli('XX') + >>> operator + Pauli('XX') + + >>> # equivalent to: + >>> operator = SparsePauliOp('XX') + >>> operator + SparsePauliOp(['XX'], coeffs=[1.+0.j]) .. raw:: html @@ -234,11 +247,18 @@ directly creating a corresponding :class:`~qiskit.quantum_info.SparsePauliOp`, a from qiskit.opflow import I, X, Z, PauliSumOp - op = 0.39 * (I ^ Z ^ I) + 0.5 * (I ^ X ^ X) + operator = 0.39 * (I ^ Z ^ I) + 0.5 * (I ^ X ^ X) + +.. code-block:: python - # or ... - op = PauliSumOp.from_list([("IZI", 0.39), ("IXX", 0.5)]) + >>> operator + PauliSumOp(SparsePauliOp(['IZI', 'IXX'], + coeffs=[0.39+0.j, 0.5 +0.j]), coeff=1.0) + >>> # or ... + >>> operator = PauliSumOp.from_list([("IZI", 0.39), ("IXX", 0.5)]) + PauliSumOp(SparsePauliOp(['IZI', 'IXX'], + coeffs=[0.39+0.j, 0.5 +0.j]), coeff=1.0) **Alternative** @@ -246,13 +266,23 @@ directly creating a corresponding :class:`~qiskit.quantum_info.SparsePauliOp`, a from qiskit.quantum_info import SparsePauliOp - op = SparsePauliOp(["IZI", "IXX"], coeffs = [0.39, 0.5]) + operator = SparsePauliOp(["IZI", "IXX"], coeffs = [0.39, 0.5]) + +.. code-block:: python + + >>> operator + SparsePauliOp(['IZI', 'IXX'], + coeffs=[0.39+0.j, 0.5 +0.j]) - # or... - op = SparsePauliOp.from_list([("IZI", 0.39), ("IXX", 0.5)]) + >>> # or... + >>> operator = SparsePauliOp.from_list([("IZI", 0.39), ("IXX", 0.5)]) + SparsePauliOp(['IZI', 'IXX'], + coeffs=[0.39+0.j, 0.5 +0.j]) - # or... - op = SparsePauliOp.from_sparse_list([("Z", [1], 0.39), ("XX", [0,1], 0.5)], num_qubits = 3) + >>> # or... + >>> operator = SparsePauliOp.from_sparse_list([("Z", [1], 0.39), ("XX", [0,1], 0.5)], num_qubits = 3) + SparsePauliOp(['IZI', 'IXX'], + coeffs=[0.39+0.j, 0.5 +0.j]) .. raw:: html @@ -293,7 +323,19 @@ Common non-parametrized gates (Clifford) from qiskit.opflow import H - op = H ^ H + operator = H ^ H + +.. code-block:: python + + >>> operator + CircuitOp(, coeff=1.0) + + >>> print(operator) + ┌───┐ + q_0: ┤ H ├ + ├───┤ + q_1: ┤ H ├ + └───┘ **Alternative** @@ -305,19 +347,36 @@ Common non-parametrized gates (Clifford) qc = QuantumCircuit(2) qc.h(0) qc.h(1) - op = Clifford(qc).to_operator() + operator = Clifford(qc).to_operator() - # or... - qc = QuantumCircuit(1) - qc.h(0) - H = Clifford(qc).to_operator() - op = H ^ H +.. code-block:: python + + >>> operator + Operator([[ 0.5+0.j, 0.5+0.j, 0.5+0.j, 0.5+0.j], + [ 0.5+0.j, -0.5+0.j, 0.5+0.j, -0.5+0.j], + [ 0.5+0.j, 0.5+0.j, -0.5+0.j, -0.5+0.j], + [ 0.5+0.j, -0.5+0.j, -0.5+0.j, 0.5+0.j]], + input_dims=(2, 2), output_dims=(2, 2)) + + >>> # or, directly + >>> operator = Operator(qc) + Operator([[ 0.5+0.j, 0.5+0.j, 0.5+0.j, 0.5+0.j], + [ 0.5+0.j, -0.5+0.j, 0.5+0.j, -0.5+0.j], + [ 0.5+0.j, 0.5+0.j, -0.5+0.j, -0.5+0.j], + [ 0.5+0.j, -0.5+0.j, -0.5+0.j, 0.5+0.j]], + input_dims=(2, 2), output_dims=(2, 2)) + + >>> # or... + >>> qc = QuantumCircuit(1) + >>> qc.h(0) + >>> H = Clifford(qc).to_operator() + >>> operator = H ^ H + Operator([[ 0.5+0.j, 0.5+0.j, 0.5+0.j, 0.5+0.j], + [ 0.5+0.j, -0.5+0.j, 0.5+0.j, -0.5+0.j], + [ 0.5+0.j, 0.5+0.j, -0.5+0.j, -0.5+0.j], + [ 0.5+0.j, -0.5+0.j, -0.5+0.j, 0.5-0.j]], + input_dims=(2, 2), output_dims=(2, 2)) - # or, directly - qc = QuantumCircuit(2) - qc.h(0) - qc.h(1) - op = Operator(qc) .. raw:: html @@ -357,6 +416,23 @@ Common non-parametrized gates (Clifford) state1 = Zero ^ One state2 = Plus ^ Minus +.. code-block:: python + + >>> state1 + DictStateFn({'01': 1}, coeff=1.0, is_measurement=False) + + >>> state2 + CircuitStateFn(, coeff=1.0, is_measurement=False) + + >>> print(state2) + CircuitStateFn( + ┌───┐┌───┐ + q_0: ┤ X ├┤ H ├ + ├───┤└───┘ + q_1: ┤ H ├───── + └───┘ + ) + **Alternative** .. code-block:: python @@ -373,9 +449,16 @@ Common non-parametrized gates (Clifford) qc_plus.h(0) qc_minus = qc_one.copy() qc_minus.h(0) - state2 = StabilizerState(qc_plus) ^ StabilizerState(qc_minus) +.. code-block:: python + + >>> state1 + Statevector([0.+0.j, 1.+0.j, 0.+0.j, 0.+0.j], + dims=(2, 2)) + + >>> state2 + StabilizerState(StabilizerTable: ['-IX', '+XI']) .. raw:: html @@ -420,7 +503,7 @@ to initialize it. - :class:`~qiskit.opflow.primitive_ops.MatrixOp` Thus, when migrating opflow code, it is important to look for alternatives to replace the specific subclasses that -might have been used "under the hood" in the original code: +are used "under the hood" in the original code: .. |qiskit.quantum_info.Z2Symmetries| replace:: ``qiskit.quantum_info.Z2Symmetries`` .. _qiskit.quantum_info.Z2Symmetries: https://github.com/Qiskit/qiskit-terra/blob/main/qiskit/quantum_info/analysis/z2_symmetries.py @@ -443,10 +526,10 @@ might have been used "under the hood" in the original code: * - :class:`~qiskit.opflow.primitive_ops.PauliOp` - :class:`~qiskit.quantum_info.Pauli`. For direct compatibility with classes in :mod:`qiskit.algorithms`, - wrap in :class:`~qiskit.quantum_info.SparsePauliOp` + wrap in :class:`~qiskit.quantum_info.SparsePauliOp`. * - :class:`~qiskit.opflow.primitive_ops.PauliSumOp` - - :class:`~qiskit.quantum_info.SparsePauliOp`. See example below + - :class:`~qiskit.quantum_info.SparsePauliOp`. See example below. * - :class:`~qiskit.opflow.primitive_ops.TaperedPauliSumOp` - This class was used to combine a :class:`.PauliSumOp` with its identified symmetries in one object. @@ -454,7 +537,7 @@ might have been used "under the hood" in the original code: See |qiskit.quantum_info.Z2Symmetries|_ example for updated workflow. * - :class:`qiskit.opflow.primitive_ops.Z2Symmetries` - - |qiskit.quantum_info.Z2Symmetries|_ . See example below. + - |qiskit.quantum_info.Z2Symmetries|_. See example below. .. _pauli_sum_op: @@ -473,13 +556,25 @@ might have been used "under the hood" in the original code: qubit_op = PauliSumOp(SparsePauliOp(Pauli("XYZY"), coeffs=[2]), coeff=-3j) +.. code-block:: python + + >>> qubit_op + PauliSumOp(SparsePauliOp(['XYZY'], + coeffs=[2.+0.j]), coeff=(-0-3j)) + **Alternative** .. code-block:: python from qiskit.quantum_info import SparsePauliOp, Pauli - qubit_op = SparsePauliOp(Pauli("XYZY")), coeff=-6j) + qubit_op = SparsePauliOp(Pauli("XYZY"), coeffs=[-6j]) + +.. code-block:: python + + >>> qubit_op + SparsePauliOp(['XYZY'], + coeffs=[0.-6.j]) .. raw:: html @@ -497,7 +592,7 @@ might have been used "under the hood" in the original code: .. code-block:: python - from qiskit.opflow import PuliSumOp, Z2Symmetries, TaperedPauliSumOp + from qiskit.opflow import PauliSumOp, Z2Symmetries, TaperedPauliSumOp qubit_op = PauliSumOp.from_list( [ @@ -513,11 +608,32 @@ might have been used "under the hood" in the original code: # can be represented as: tapered_op = TaperedPauliSumOp(primitive, z2_symmetries) +.. code-block:: python + + >>> print(z2_symmetries) + Z2 symmetries: + Symmetries: + ZZ + Single-Qubit Pauli X: + IX + Cliffords: + 0.7071067811865475 * ZZ + + 0.7071067811865475 * IX + Qubit index: + [0] + Tapering values: + - Possible values: [1], [-1] + >>> tapered_op + TaperedPauliSumOp(SparsePauliOp(['II', 'IZ', 'ZI', 'ZZ', 'XX'], + coeffs=[-1.05370761+0.j, 0.39398368+0.j, -0.39398368+0.j, -0.01123659+0.j, + 0.18128881+0.j]), coeff=1.0) + **Alternative** .. code-block:: python - from qiskit.quantum_info import SparsePauliOp, Z2Symmetries + from qiskit.quantum_info import SparsePauliOp + from qiskit.quantum_info.analysis.z2_symmetries import Z2Symmetries qubit_op = SparsePauliOp.from_list( [ @@ -531,6 +647,27 @@ might have been used "under the hood" in the original code: z2_symmetries = Z2Symmetries.find_z2_symmetries(qubit_op) tapered_op = z2_symmetries.taper(qubit_op) +.. code-block:: python + + >>> print(z2_symmetries) + Z2 symmetries: + Symmetries: + ZZ + Single-Qubit Pauli X: + IX + Cliffords: + SparsePauliOp(['ZZ', 'IX'], + coeffs=[0.70710678+0.j, 0.70710678+0.j]) + Qubit index: + [0] + Tapering values: + - Possible values: [1], [-1] + >>> tapered_op + [SparsePauliOp(['I', 'X'], + coeffs=[-1.06494419+0.j, 0.18128881+0.j]), + SparsePauliOp(['I', 'Z', 'X'], + coeffs=[-1.04247102+0.j, -0.78796736+0.j, -0.18128881+0.j])] + .. raw:: html
@@ -574,11 +711,7 @@ State Functions The :mod:`~qiskit.opflow.state_fns` module can be generally replaced by subclasses of :mod:`~qiskit.quantum_info`\'s -|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. Algorithm-specific functionality has been migrated to the respective algorithm's module - +|qiskit.quantum_info.QuantumState|_. Similarly to :class:`~qiskit.opflow.primitive_ops.PrimitiveOp`, :class:`~qiskit.opflow.state_fns.StateFn` acts as a factory to create the corresponding sub-class depending on the computational primitive used to initialize it. @@ -663,6 +796,30 @@ identify the sub-class that is being used, to then look for an alternative. eval = comp.eval() # returns a VectorStateFn (Statevector) +.. code-block:: python + + >>> print(state) + CircuitStateFn( + + q_0: + + q_1: + + ) + + >>> print(comp) + CircuitStateFn( + ┌────────────┐ + q_0: ┤0 ├ + │ Pauli(XY) │ + q_1: ┤1 ├ + └────────────┘ + ) + + >>> print(eval) + VectorStateFn(Statevector([0.0e+00+0.j, 0.0e+00+0.j, 0.0e+00+0.j, 6.1e-17+1.j], + dims=(2, 2))) + **Alternative** .. code-block:: python @@ -677,6 +834,16 @@ identify the sub-class that is being used, to then look for an alternative. eval = state.evolve(operator) # returns a Statevector +.. code-block:: python + + >>> print(state) + Statevector([1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j], + dims=(2, 2)) + + >>> print(eval) + Statevector([0.5+0.j, 0.5+0.j, 0.5+0.j, 0.5+0.j], + dims=(2, 2)) + .. raw:: html
@@ -757,6 +924,22 @@ Notably, this functionality has been replaced by the :mod:`~qiskit.primitives`. sampled = sampler.convert(listop, params=bindings).eval() # returns list of SparseVectorStateFn +.. code-block:: python + + >>> print(sampled) + [SparseVectorStateFn(<1x2 sparse matrix of type '' + with 1 stored elements in Compressed Sparse Row format>, coeff=1.0, is_measurement=False), + SparseVectorStateFn(<1x2 sparse matrix of type '' + with 1 stored elements in Compressed Sparse Row format>, coeff=1.0, is_measurement=False), + SparseVectorStateFn(<1x2 sparse matrix of type '' + with 1 stored elements in Compressed Sparse Row format>, coeff=1.0, is_measurement=False)] + + >>> for s in sampled: + >>> print(s) + SparseVectorStateFn( (0, 0) 1.0) + SparseVectorStateFn( (0, 0) 1.0) + SparseVectorStateFn( (0, 0) 1.0) + **Alternative** .. code-block:: python @@ -768,8 +951,7 @@ Notably, this functionality has been replaced by the :mod:`~qiskit.primitives`. circuit1 = QuantumCircuit(1) circuit1.p(0.2, 0) - # Don't forget to add measurements!!!!! - circuit1.measure_all() + circuit1.measure_all() # Don't forget measurements!!!!! circuit2 = QuantumCircuit(1) circuit2.p(x, 0) circuit2.measure_all() @@ -778,12 +960,17 @@ Notably, this functionality has been replaced by the :mod:`~qiskit.primitives`. circuit3.measure_all() circuits = [circuit1, circuit2, circuit3] - param_values = [None, [-0.4], [0.4]] + param_values = [[], [-0.4], [0.4]] sampler = Sampler() sampled = sampler.run(circuits, param_values).result().quasi_dists # returns qiskit.result.QuasiDist +.. code-block:: python + + >>> print(sampled) + [{0: 1.0}, {0: 1.0}, {0: 1.0}] + .. raw:: html @@ -814,6 +1001,11 @@ Notably, this functionality has been replaced by the :mod:`~qiskit.primitives`. expectation = sampler.convert(expr) expectation_value = expectation.eval().real +.. code-block:: python + + >>> expectation_value + 1.0000000000000002 + **Alternative** .. code-block:: python @@ -829,6 +1021,11 @@ Notably, this functionality has been replaced by the :mod:`~qiskit.primitives`. estimator = Estimator() expectation_value = estimator.run(state, hamiltonian).result().values.real +.. code-block:: python + + >>> expectation_value + array([1.]) + .. raw:: html @@ -848,11 +1045,14 @@ Notably, this functionality has been replaced by the :mod:`~qiskit.primitives`. op = PauliSumOp.from_list([("XX", 2), ("YY", 1), ("IZ",2j), ("ZZ",1j)]) grouped_sum = AbelianGrouper.group_subops(op) - # returns: SummedOp([PauliSumOp(SparsePauliOp(['XX'], coeffs=[2.+0.j]), coeff=1.0), - # PauliSumOp(SparsePauliOp(['YY'], coeffs=[1.+0.j]), coeff=1.0), - # PauliSumOp(SparsePauliOp(['IZ', 'ZZ'], coeffs=[0.+2.j, 0.+1.j]), - # coeff=1.0)], coeff=1.0, abelian=False) +.. code-block:: python + + >>> grouped_sum + SummedOp([PauliSumOp(SparsePauliOp(['XX'], coeffs=[2.+0.j]), coeff=1.0), + PauliSumOp(SparsePauliOp(['YY'], coeffs=[1.+0.j]), coeff=1.0), + PauliSumOp(SparsePauliOp(['IZ', 'ZZ'], coeffs=[0.+2.j, 0.+1.j]), + coeff=1.0)], coeff=1.0, abelian=False) **Alternative** @@ -863,13 +1063,18 @@ Notably, this functionality has been replaced by the :mod:`~qiskit.primitives`. op = SparsePauliOp.from_list([("XX", 2), ("YY", 1), ("IZ",2j), ("ZZ",1j)]) grouped = op.group_commuting() - # returns: [SparsePauliOp(["IZ", "ZZ"], coeffs=[0.+2.j, 0.+1j]), - # SparsePauliOp(["XX", "YY"], coeffs=[2.+0.j, 1.+0.j])] + grouped_sum = op.group_commuting(qubit_wise=True) + +.. code-block:: python - grouped = op.group_commuting(qubit_wise=True) - # returns: [SparsePauliOp(['XX'], coeffs=[2.+0.j]), - # SparsePauliOp(['YY'], coeffs=[1.+0.j]), - # SparsePauliOp(['IZ', 'ZZ'], coeffs=[0.+2.j, 0.+1.j])] + >>> grouped + [SparsePauliOp(["IZ", "ZZ"], coeffs=[0.+2.j, 0.+1j]), + SparsePauliOp(["XX", "YY"], coeffs=[2.+0.j, 1.+0.j])] + + >>> grouped_sum + [SparsePauliOp(['XX'], coeffs=[2.+0.j]), + SparsePauliOp(['YY'], coeffs=[1.+0.j]), + SparsePauliOp(['IZ', 'ZZ'], coeffs=[0.+2.j, 0.+1.j])] .. raw:: html @@ -886,17 +1091,20 @@ delayed synthesis of the gates or efficient transpilation of the circuits, so th .. note:: - The :class:`qiskit.opflow.evolutions.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.evolutions.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.evolutions.TrotterizationFactory`, - or by supplying a method instance of :class:`qiskit.opflow.evolutions.Trotter`, :class:`qiskit.opflow.evolutions.Suzuki` or :class:`qiskit.opflow.evolutions.QDrift`). + The :class:`qiskit.opflow.evolutions.PauliTrotterEvolution` class computes evolutions for exponentiated + sums of Paulis by converting to the Z basis, rotating with an RZ, changing back, and trotterizing. + When calling ``.convert()``, the class follows a recursive strategy that involves creating + :class:`~qiskit.opflow.evolutions.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. The methods can be specified via + ``string``, which is then inputted into a :class:`~qiskit.opflow.evolutions.TrotterizationFactory`, + or by supplying a method instance of :class:`qiskit.opflow.evolutions.Trotter`, + :class:`qiskit.opflow.evolutions.Suzuki` or :class:`qiskit.opflow.evolutions.QDrift`. The different trotterization methods that extend :class:`qiskit.opflow.evolutions.TrotterizationBase` were migrated to :mod:`qiskit.synthesis`, and now extend the :class:`qiskit.synthesis.ProductFormula` base class. They no longer contain a ``.convert()`` method for - standalone use, but now are designed to be plugged into the :class:`.PauliEvolutionGate` and called via ``.synthesize()``. + standalone use, but are now designed to be plugged into the :class:`.PauliEvolutionGate` and called via ``.synthesize()``. In this context, the job of the :class:`qiskit.opflow.evolutions.PauliTrotterEvolution` class can now be handled directly by the algorithms (for example, :class:`~qiskit.algorithms.time_evolvers.trotterization.TrotterQRTE`\). @@ -967,6 +1175,13 @@ Other Evolution Classes evol_result = evolution.convert(hamiltonian.exp_i()) evolved_state = evol_result.to_circuit() +.. code-block:: python + + >>> print(evolved_state) + ┌─────────────────────┐ + q: ┤ exp(-it (X + Z))(1) ├ + └─────────────────────┘ + **Alternative** .. code-block:: python @@ -981,6 +1196,13 @@ Other Evolution Classes evolved_state = QuantumCircuit(1) evolved_state.append(evol_gate, [0]) +.. code-block:: python + + >>> print(evolved_state) + ┌─────────────────────┐ + q: ┤ exp(-it (X + Z))(1) ├ + └─────────────────────┘ + .. raw:: html @@ -1005,6 +1227,13 @@ Other Evolution Classes evol_result = evolution.convert((time * hamiltonian).exp_i()) evolved_state = evol_result.to_circuit() +.. code-block:: python + + >>> print(evolved_state) + ┌─────────────────────────┐ + q: ┤ exp(-it (X + Y))(1.0*t) ├ + └─────────────────────────┘ + **Alternative** .. code-block:: python @@ -1021,12 +1250,18 @@ Other Evolution Classes evolved_state = QuantumCircuit(1) evolved_state.append(evol_gate, [0]) +.. code-block:: python + + >>> print(evolved_state) + ┌─────────────────────┐ + q: ┤ exp(-it (X + Y))(t) ├ + └─────────────────────┘ + .. raw:: html - .. raw:: html
@@ -1044,6 +1279,13 @@ Other Evolution Classes evol_result = evolution.convert(hamiltonian.exp_i()) evolved_state = evol_result.to_circuit() +.. code-block:: python + + >>> print(evolved_state.decompose().decompose()) + ┌────────────────┐ + q: ┤ U3(2,-π/2,π/2) ├ + └────────────────┘ + **Alternative** .. code-block:: python @@ -1056,6 +1298,13 @@ Other Evolution Classes evolved_state = QuantumCircuit(1) evolved_state.append(evol_gate, [0]) +.. code-block:: python + + >>> print(evolved_state.decompose().decompose()) + ┌────────────────┐ + q: ┤ U3(2,-π/2,π/2) ├ + └────────────────┘ + .. raw:: html
@@ -1065,7 +1314,7 @@ Expectations *Back to* `Contents`_ 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. +This functionality can now be found in the Estimator primitive. Algorithm-Agnostic Expectations ------------------------------- @@ -1106,7 +1355,7 @@ Algorithm-Agnostic Expectations .. code-block:: python - from qiskit.opflow import Z, CX, H, I, Zero, StateFn, AerPauliExpectation, CircuitSampler + from qiskit.opflow import X, Minus, StateFn, AerPauliExpectation, CircuitSampler from qiskit.utils import QuantumInstance from qiskit_aer import Aer @@ -1114,13 +1363,18 @@ Algorithm-Agnostic Expectations q_instance = QuantumInstance(backend) sampler = CircuitSampler(q_instance, attach_results=True) - expect = AerPauliExpectation() + expectation = AerPauliExpectation() + + state = Minus + operator = 1j * X + + converted_meas = expectation.convert(StateFn(operator, is_measurement=True) @ state) + expectation_value = sampler.convert(converted_meas).eval() - op = Z ^ Z - wvf = CX @ (H ^ I) @ Zero +.. code-block:: python - converted_meas = expect.convert(~StateFn(op) @ wvf) - expect_values = sampler.convert(converted_meas).eval() + >>> print(expectation_value) + -1j **Alternative** @@ -1132,12 +1386,17 @@ Algorithm-Agnostic Expectations estimator = AerEstimator(run_options={"approximation": True, "shots": None}) - op = SparsePauliOp.from_list([("ZZ", 1)]) - wvf = QuantumCircuit(2) - wvf.h(1) - wvf.cx(0,1) + op = SparsePauliOp.from_list([("X", 1j)]) + states_op = QuantumCircuit(1) + states_op.x(0) + states_op.h(0) + + expectation_value = estimator.run(states_op, op).result().values + +.. code-block:: python - expect_values = estimator.run(wvf,op).result().values + >>> print(expectation_value) + array([0.-1.j]) .. raw:: html @@ -1171,6 +1430,11 @@ Algorithm-Agnostic Expectations plus_mean = converted_meas @ Plus values_plus = sampler.convert(plus_mean).eval() +.. code-block:: python + + >>> values_plus + [(1+0j), (0.7071067811865476+0j)] + **Alternative** .. code-block:: python @@ -1191,6 +1455,11 @@ Algorithm-Agnostic Expectations estimator = Estimator() values_plus = estimator.run([plus, plus], [X, H]).result().values +.. code-block:: python + + >>> values_plus + array([1. , 0.70710678]) + .. raw:: html @@ -1236,6 +1505,32 @@ CVaRExpectation vqe = VQE(ansatz, expectation=cvar_expectation, optimizer=opt, quantum_instance=backend) result = vqe.compute_minimum_eigenvalue(op) +.. code-block:: python + + >>> print(result) + { 'aux_operator_eigenvalues': None, + 'cost_function_evals': 9, + 'eigenstate': { '00': 0.03125, + '01': 0.9190900731157964, + '10': 0.36174490321219455, + '11': 0.15309310892394862}, + 'eigenvalue': (-1+0j), + 'optimal_circuit': None, + 'optimal_parameters': { ParameterVectorElement(θ[4]): -3.0690128161958303, + ParameterVectorElement(θ[3]): 5.76518147200243, + ParameterVectorElement(θ[5]): 4.864530916850402, + ParameterVectorElement(θ[6]): 6.12792883765818, + ParameterVectorElement(θ[7]): -2.7501333383184803, + ParameterVectorElement(θ[2]): -3.4487045115663344, + ParameterVectorElement(θ[1]): -5.8788723016341295, + ParameterVectorElement(θ[0]): -3.65330163985105}, + 'optimal_point': array([-3.65330164, -5.8788723 , -3.44870451, 5.76518147, -3.06901282, + 4.86453092, 6.12792884, -2.75013334]), + 'optimal_value': -1.0, + 'optimizer_evals': None, + 'optimizer_result': None, + 'optimizer_time': 0.03872823715209961} + **Alternative** .. code-block:: python @@ -1253,6 +1548,14 @@ CVaRExpectation vqe = SamplingVQE(Sampler(), ansatz, opt, aggregation=alpha) result = vqe.compute_minimum_eigenvalue(op) +.. code-block:: python + + >>> print(result) + SamplingMinimumEigensolverResult: + Eigenvalue: -1.0 + Best measurement: + {'state': 1, 'bitstring': '01', 'value': (-1+0j), 'probability': 0.7505629720987731} + .. raw:: html @@ -1268,7 +1571,8 @@ can also be executed in a standalone manner. For this reason, they now reside un The former gradient framework contained base classes, converters and derivatives. The "derivatives" followed a factory design pattern, where different methods could be provided via string identifiers to each of these classes. The new gradient framework contains two main families of subroutines: -**Gradients** and **QGT/QFI**. +**Gradients** and **QGT/QFI**. The **Gradients** can either be Sampler or Estimator based, while the current +**QGT/QFI** implementations are Estimator-based. This leads to a change in the workflow, where instead of doing: @@ -1285,8 +1589,9 @@ We now import explicitly the desired class, depending on the target primitive (S .. code-block:: python from qiskit.algorithms.gradients import ParamShiftEstimatorGradient + from qiskit.primitives import Estimator - grad = ParamShiftEstimatorGradient(estimator) + grad = ParamShiftEstimatorGradient(Estimator()) # task based on expectation value computations + gradients @@ -1296,18 +1601,20 @@ This works similarly for the QFI class, where instead of doing: from qiskit.opflow import QFI - qfi = QFI(method="param_shift") + qfi = QFI(method="lin_comb_full") # task based on expectation value computations + QFI -You now have a generic QFI implementation that can be initialized with any gradient instance: +You now have a generic QFI implementation that can be initialized with different QGT (Quantum Gradient Tensor) +implementations: .. code-block:: python - from qiskit.algorithms.gradients import ParamShiftEstimatorGradient, QFI + from qiskit.algorithms.gradients import LinCombQGT, QFI + from qiskit.primitives import Estimator - grad = ParamShiftEstimatorGradient(estimator) - qfi = QFI(gradient=grad) + qgt = LinCombQGT(Estimator()) + qfi = QFI(qgt) # task based on expectation value computations + QFI @@ -1316,52 +1623,53 @@ You now have a generic QFI implementation that can be initialized with any gradi Here is a quick guide for migrating the most common gradient settings. Please note that all new gradient imports follow the format: - .. code-block:: python + .. code-block:: python - from qiskit.algorithms.gradients import MethodPrimitiveGradient + from qiskit.algorithms.gradients import MethodPrimitiveGradient, QFI - .. raw:: html + .. raw:: html -
- Gradients -
+
+ Gradients +
- .. list-table:: - :header-rows: 1 + .. list-table:: + :header-rows: 1 - * - Opflow - - Alternative + * - Opflow + - Alternative - * - ``Gradient(method="lin_comb")`` - - ``LinCombEstimatorGradient(estimator=estimator)`` or ``LinCombSamplerGradient(sampler=sampler)`` - * - ``Gradient(method="param_shift")`` - - ``ParamShiftEstimatorGradient(estimator=estimator)`` or ``ParamShiftSamplerGradient(sampler=sampler)`` - * - ``Gradient(method="fin_diff")`` - - ``FiniteDiffEstimatorGradient(estimator=estimator)`` or ``ParamShiftSamplerGradient(sampler=sampler)`` + * - ``Gradient(method="lin_comb")`` + - ``LinCombEstimatorGradient(estimator=estimator)`` or ``LinCombSamplerGradient(sampler=sampler)`` + * - ``Gradient(method="param_shift")`` + - ``ParamShiftEstimatorGradient(estimator=estimator)`` or ``ParamShiftSamplerGradient(sampler=sampler)`` + * - ``Gradient(method="fin_diff")`` + - ``FiniteDiffEstimatorGradient(estimator=estimator)`` or ``ParamShiftSamplerGradient(sampler=sampler)`` - .. raw:: html + .. raw:: html -
+
- .. raw:: html + .. raw:: html -
- QFI/QGT -
+
+ QFI/QGT +
- .. list-table:: - :header-rows: 1 + .. list-table:: + :header-rows: 1 - * - Opflow - - Alternative + * - Opflow + - Alternative - * - ``QFI(method="lin_comb")`` - - ``QFI(gradient=gradient_instance_for_method)`` + * - ``QFI(method="lin_comb_full")`` + - ``qgt=LinCombQGT(Estimator())`` + ``QFI(qgt=qgt)`` - .. raw:: html + .. raw:: html -
+
Other auxiliary classes in the legacy gradient framework have now been deprecated. Here is the complete migration list: @@ -1401,26 +1709,27 @@ list: .. code-block:: python - from qiskit.circuit.library import Parameter, QuantumCircuit + from qiskit.circuit import Parameter, QuantumCircuit from qiskit.opflow import Gradient, X, Z, StateFn, CircuitStateFn + import numpy as np ham = 0.5 * X - 1 * Z a = Parameter("a") b = Parameter("b") c = Parameter("c") + params = [a,b,c] qc = QuantumCircuit(1) qc.h(0) qc.u(a, b, c, 0) qc.h(0) - qc.measure_all() op = ~StateFn(ham) @ CircuitStateFn(primitive=qc, coeff=1.0) # the gradient class acted similarly opflow converters, # with a .convert() step and an .eval() step - state_grad = Gradient(grad_method=method).convert(operator=op, params=params) + state_grad = Gradient(grad_method="param_shift").convert(operator=op, params=params) # the old workflow did not allow for batched evaluation of parameter values values_dict = [{a: np.pi / 4, b: 0, c: 0}, {a: np.pi / 4, b: np.pi / 4, c: np.pi / 4}] @@ -1428,16 +1737,22 @@ list: for i, value_dict in enumerate(values_dict): gradients.append(state_grad.assign_parameters(value_dict).eval()) +.. code-block:: python + + >>> print(gradients) + [[(0.35355339059327356+0j), (-1.182555756156289e-16+0j), (-1.6675e-16+0j)], + [(0.10355339059327384+0j), (0.8535533905932734+0j), (1.103553390593273+0j)]] + **Alternative** .. code-block:: python - from qiskit.circuit.library import Parameter, QuantumCircuit + from qiskit.circuit import Parameter, QuantumCircuit from qiskit.primitives import Estimator from qiskit.algorithms.gradients import ParamShiftEstimatorGradient from qiskit.quantum_info import SparsePauliOp - ham = SparsePauliOp.from_list([(X, 0.5), (Z, -1)]) + ham = SparsePauliOp.from_list([("X", 0.5), ("Z", -1)]) a = Parameter("a") b = Parameter("b") @@ -1447,7 +1762,6 @@ list: qc.h(0) qc.u(a, b, c, 0) qc.h(0) - qc.measure_all() estimator = Estimator() gradient = ParamShiftEstimatorGradient(estimator) @@ -1456,7 +1770,13 @@ list: param_list = [[np.pi / 4, 0, 0], [np.pi / 4, np.pi / 4, np.pi / 4]] # for batched evaluations, the number of circuits must match the # number of parameter value sets - gradients = gradient.run([qc] * 3, param_list).result().gradients + gradients = gradient.run([qc] * 2, [ham] * 2, param_list).result().gradients + +.. code-block:: python + + >>> print(gradients) + [array([ 3.53553391e-01, 0.00000000e+00, -1.80411242e-16]), + array([0.10355339, 0.85355339, 1.10355339])] .. raw:: html @@ -1472,7 +1792,7 @@ list: .. code-block:: python - from qiskit.circuit.library import Parameter, QuantumCircuit + from qiskit.circuit import Parameter, QuantumCircuit from qiskit.opflow import QFI, CircuitStateFn # create the circuit @@ -1486,19 +1806,23 @@ list: op = CircuitStateFn(qc) qfi = QFI(qfi_method="lin_comb_full").convert(operator=op) - # test for different values - values_dict = [{a: np.pi / 4, b: 0.1}, {a: np.pi, b: 0.1}, {a: np.pi / 2, b: 0.1}] - qfis = [] - for i, value_dict in enumerate(values_dict): - qfis.append(qfi.assign_parameters(value_dict).eval()) + # bind parameters and evaluate + values_dict = {a: np.pi / 4, b: 0.1} + qfi = qfi.bind_parameters(values_dict).eval() + +.. code-block:: python + + >>> print(qfi) + [[ 1.00000000e+00+0.j -3.63575685e-16+0.j] + [-3.63575685e-16+0.j 5.00000000e-01+0.j]] **Alternative** .. code-block:: python - from qiskit.circuit.library import Parameter, QuantumCircuit - from qiskit.primitives import Sampler - from qiskit.algorithms.gradients import LinCombSamplerGradient, QFI + from qiskit.circuit import Parameter, QuantumCircuit + from qiskit.primitives import Estimator + from qiskit.algorithms.gradients import LinCombQGT, QFI # create the circuit a, b = Parameter("a"), Parameter("b") @@ -1507,14 +1831,20 @@ list: qc.rz(a, 0) qc.rx(b, 0) - sampler = Sampler() - gradient = ParamShiftSamplerGradient(sampler) - qfi = QFI(gradient) + # initialize QFI + estimator = Estimator() + qgt = LinCombQGT(estimator) + qfi = QFI(qgt) - # test for different values - values_list = [[np.pi / 4,, 0.1], [np.pi, 0.1], [np.pi / 2, 0.1]] - qfis = qfi.run([qc] * 3, values_list).results().qfis + # evaluate + values_list = [np.pi / 4, 0.1] + qfi = qfi.run(qc, values_list).result().qfis + +.. code-block:: python + >>> print(qfi) + [array([[ 1.00000000e+00, -1.50274614e-16], + [-1.50274614e-16, 5.00000000e-01]])] .. raw:: html From fc715320e5143017622353c827073d91fdf2a54d Mon Sep 17 00:00:00 2001 From: ElePT Date: Tue, 28 Feb 2023 15:56:38 +0100 Subject: [PATCH 32/50] Run code examples by doctest --- docs/migration_guides/opflow_migration.rst | 843 +++++++++------------ 1 file changed, 370 insertions(+), 473 deletions(-) diff --git a/docs/migration_guides/opflow_migration.rst b/docs/migration_guides/opflow_migration.rst index a862021ad58..f2b477f5012 100644 --- a/docs/migration_guides/opflow_migration.rst +++ b/docs/migration_guides/opflow_migration.rst @@ -198,9 +198,9 @@ directly creating a corresponding :class:`~qiskit.quantum_info.SparsePauliOp`, a .. code-block:: python - from qiskit.opflow import X + >>> from qiskit.opflow import X - operator = X ^ X + >>> operator = X ^ X .. code-block:: python @@ -211,25 +211,23 @@ directly creating a corresponding :class:`~qiskit.quantum_info.SparsePauliOp`, a .. code-block:: python - from qiskit.quantum_info import Pauli, SparsePauliOp - - X = Pauli('X') - operator = X ^ X - -.. code-block:: python + >>> from qiskit.quantum_info import Pauli, SparsePauliOp + >>> X = Pauli('X') + >>> operator = X ^ X >>> operator Pauli('XX') - >>> # equivalent to: + # equivalent to: >>> operator = Pauli('XX') >>> operator Pauli('XX') - >>> # equivalent to: + # equivalent to: >>> operator = SparsePauliOp('XX') >>> operator - SparsePauliOp(['XX'], coeffs=[1.+0.j]) + SparsePauliOp(['XX'], + coeffs=[1.+0.j]) .. raw:: html @@ -245,18 +243,16 @@ directly creating a corresponding :class:`~qiskit.quantum_info.SparsePauliOp`, a .. code-block:: python - from qiskit.opflow import I, X, Z, PauliSumOp - - operator = 0.39 * (I ^ Z ^ I) + 0.5 * (I ^ X ^ X) - -.. code-block:: python + >>> from qiskit.opflow import I, X, Z, PauliSumOp + >>> operator = 0.39 * (I ^ Z ^ I) + 0.5 * (I ^ X ^ X) >>> operator PauliSumOp(SparsePauliOp(['IZI', 'IXX'], coeffs=[0.39+0.j, 0.5 +0.j]), coeff=1.0) - >>> # or ... + # or ... >>> operator = PauliSumOp.from_list([("IZI", 0.39), ("IXX", 0.5)]) + >>> operator PauliSumOp(SparsePauliOp(['IZI', 'IXX'], coeffs=[0.39+0.j, 0.5 +0.j]), coeff=1.0) @@ -264,23 +260,22 @@ directly creating a corresponding :class:`~qiskit.quantum_info.SparsePauliOp`, a .. code-block:: python - from qiskit.quantum_info import SparsePauliOp - - operator = SparsePauliOp(["IZI", "IXX"], coeffs = [0.39, 0.5]) - -.. code-block:: python + >>> from qiskit.quantum_info import SparsePauliOp + >>> operator = SparsePauliOp(["IZI", "IXX"], coeffs = [0.39, 0.5]) >>> operator SparsePauliOp(['IZI', 'IXX'], coeffs=[0.39+0.j, 0.5 +0.j]) - >>> # or... + # or... >>> operator = SparsePauliOp.from_list([("IZI", 0.39), ("IXX", 0.5)]) + >>> operator SparsePauliOp(['IZI', 'IXX'], coeffs=[0.39+0.j, 0.5 +0.j]) - >>> # or... + # or... >>> operator = SparsePauliOp.from_sparse_list([("Z", [1], 0.39), ("XX", [0,1], 0.5)], num_qubits = 3) + >>> operator SparsePauliOp(['IZI', 'IXX'], coeffs=[0.39+0.j, 0.5 +0.j]) @@ -321,15 +316,9 @@ Common non-parametrized gates (Clifford) .. code-block:: python - from qiskit.opflow import H - - operator = H ^ H - -.. code-block:: python - - >>> operator - CircuitOp(, coeff=1.0) + >>> from qiskit.opflow import H + >>> operator = H ^ H >>> print(operator) ┌───┐ q_0: ┤ H ├ @@ -341,36 +330,35 @@ Common non-parametrized gates (Clifford) .. code-block:: python - from qiskit import QuantumCircuit - from qiskit.quantum_info import Clifford, Operator - - qc = QuantumCircuit(2) - qc.h(0) - qc.h(1) - operator = Clifford(qc).to_operator() - -.. code-block:: python + >>> from qiskit import QuantumCircuit + >>> from qiskit.quantum_info import Clifford, Operator + >>> qc = QuantumCircuit(2) + >>> _ = qc.h(0) + >>> _ = qc.h(1) + >>> operator = Clifford(qc).to_operator() >>> operator Operator([[ 0.5+0.j, 0.5+0.j, 0.5+0.j, 0.5+0.j], - [ 0.5+0.j, -0.5+0.j, 0.5+0.j, -0.5+0.j], - [ 0.5+0.j, 0.5+0.j, -0.5+0.j, -0.5+0.j], - [ 0.5+0.j, -0.5+0.j, -0.5+0.j, 0.5+0.j]], - input_dims=(2, 2), output_dims=(2, 2)) + [ 0.5+0.j, -0.5+0.j, 0.5+0.j, -0.5+0.j], + [ 0.5+0.j, 0.5+0.j, -0.5+0.j, -0.5+0.j], + [ 0.5+0.j, -0.5+0.j, -0.5+0.j, 0.5+0.j]], + input_dims=(2, 2), output_dims=(2, 2)) - >>> # or, directly + # or, directly >>> operator = Operator(qc) + >>> operator Operator([[ 0.5+0.j, 0.5+0.j, 0.5+0.j, 0.5+0.j], [ 0.5+0.j, -0.5+0.j, 0.5+0.j, -0.5+0.j], [ 0.5+0.j, 0.5+0.j, -0.5+0.j, -0.5+0.j], [ 0.5+0.j, -0.5+0.j, -0.5+0.j, 0.5+0.j]], input_dims=(2, 2), output_dims=(2, 2)) - >>> # or... + # or... >>> qc = QuantumCircuit(1) - >>> qc.h(0) + >>> _ = qc.h(0) >>> H = Clifford(qc).to_operator() >>> operator = H ^ H + >>> operator Operator([[ 0.5+0.j, 0.5+0.j, 0.5+0.j, 0.5+0.j], [ 0.5+0.j, -0.5+0.j, 0.5+0.j, -0.5+0.j], [ 0.5+0.j, 0.5+0.j, -0.5+0.j, -0.5+0.j], @@ -410,20 +398,15 @@ Common non-parametrized gates (Clifford) .. code-block:: python - from qiskit.opflow import Zero, One, Plus, Minus + >>> from qiskit.opflow import Zero, One, Plus, Minus # Zero, One, Plus, Minus are all stabilizer states - state1 = Zero ^ One - state2 = Plus ^ Minus - -.. code-block:: python + >>> state1 = Zero ^ One + >>> state2 = Plus ^ Minus >>> state1 DictStateFn({'01': 1}, coeff=1.0, is_measurement=False) - >>> state2 - CircuitStateFn(, coeff=1.0, is_measurement=False) - >>> print(state2) CircuitStateFn( ┌───┐┌───┐ @@ -437,21 +420,19 @@ Common non-parametrized gates (Clifford) .. code-block:: python - from qiskit import QuantumCircuit - from qiskit.quantum_info import StabilizerState, Statevector + >>> from qiskit import QuantumCircuit + >>> from qiskit.quantum_info import StabilizerState, Statevector - qc_zero = QuantumCircuit(1) - qc_one = qc_zero.copy() - qc_one.x(0) - state1 = Statevector(qc_zero) ^ Statevector(qc_one) + >>> qc_zero = QuantumCircuit(1) + >>> qc_one = qc_zero.copy() + >>> _ = qc_one.x(0) + >>> state1 = Statevector(qc_zero) ^ Statevector(qc_one) - qc_plus = qc_zero.copy() - qc_plus.h(0) - qc_minus = qc_one.copy() - qc_minus.h(0) - state2 = StabilizerState(qc_plus) ^ StabilizerState(qc_minus) - -.. code-block:: python + >>> qc_plus = qc_zero.copy() + >>> _ = qc_plus.h(0) + >>> qc_minus = qc_one.copy() + >>> _ = qc_minus.h(0) + >>> state2 = StabilizerState(qc_plus) ^ StabilizerState(qc_minus) >>> state1 Statevector([0.+0.j, 1.+0.j, 0.+0.j, 0.+0.j], @@ -551,13 +532,10 @@ are used "under the hood" in the original code: .. code-block:: python - from qiskit.opflow import PauliSumOp - from qiskit.quantum_info import SparsePauliOp, Pauli - - qubit_op = PauliSumOp(SparsePauliOp(Pauli("XYZY"), coeffs=[2]), coeff=-3j) - -.. code-block:: python + >>> from qiskit.opflow import PauliSumOp + >>> from qiskit.quantum_info import SparsePauliOp, Pauli + >>> qubit_op = PauliSumOp(SparsePauliOp(Pauli("XYZY"), coeffs=[2]), coeff=-3j) >>> qubit_op PauliSumOp(SparsePauliOp(['XYZY'], coeffs=[2.+0.j]), coeff=(-0-3j)) @@ -566,12 +544,9 @@ are used "under the hood" in the original code: .. code-block:: python - from qiskit.quantum_info import SparsePauliOp, Pauli - - qubit_op = SparsePauliOp(Pauli("XYZY"), coeffs=[-6j]) - -.. code-block:: python + >>> from qiskit.quantum_info import SparsePauliOp, Pauli + >>> qubit_op = SparsePauliOp(Pauli("XYZY"), coeffs=[-6j]) >>> qubit_op SparsePauliOp(['XYZY'], coeffs=[0.-6.j]) @@ -592,23 +567,22 @@ are used "under the hood" in the original code: .. code-block:: python - from qiskit.opflow import PauliSumOp, Z2Symmetries, TaperedPauliSumOp + >>> from qiskit.opflow import PauliSumOp, 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) + >>> 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) -.. code-block:: python + # can be represented as: + >>> tapered_op = TaperedPauliSumOp(qubit_op.primitive, z2_symmetries) >>> print(z2_symmetries) Z2 symmetries: @@ -623,31 +597,30 @@ are used "under the hood" in the original code: [0] Tapering values: - Possible values: [1], [-1] + >>> tapered_op TaperedPauliSumOp(SparsePauliOp(['II', 'IZ', 'ZI', 'ZZ', 'XX'], coeffs=[-1.05370761+0.j, 0.39398368+0.j, -0.39398368+0.j, -0.01123659+0.j, - 0.18128881+0.j]), coeff=1.0) + 0.18128881+0.j]), coeff=1.0) **Alternative** .. code-block:: python - from qiskit.quantum_info import SparsePauliOp - from qiskit.quantum_info.analysis.z2_symmetries import 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) + >>> from qiskit.quantum_info import SparsePauliOp + >>> from qiskit.quantum_info.analysis.z2_symmetries import Z2Symmetries -.. code-block:: python + >>> 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) >>> print(z2_symmetries) Z2 symmetries: @@ -662,11 +635,11 @@ are used "under the hood" in the original code: [0] Tapering values: - Possible values: [1], [-1] + >>> tapered_op [SparsePauliOp(['I', 'X'], - coeffs=[-1.06494419+0.j, 0.18128881+0.j]), - SparsePauliOp(['I', 'Z', 'X'], - coeffs=[-1.04247102+0.j, -0.78796736+0.j, -0.18128881+0.j])] + coeffs=[-1.06494419+0.j, 0.18128881+0.j]), SparsePauliOp(['I', 'Z', 'X'], + coeffs=[-1.04247102+0.j, -0.78796736+0.j, -0.18128881+0.j])] .. raw:: html @@ -784,65 +757,63 @@ identify the sub-class that is being used, to then look for an alternative. .. code-block:: python - from qiskit.opflow import StateFn, X, Y - - qc = QuantumCircuit(2) - op = X ^ Y - state = StateFn(qc) + >>> from qiskit.opflow import StateFn, X, Y - comp = ~op @ state - # returns a CircuitStateFn + >>> qc = QuantumCircuit(2) + >>> _ = qc.x(0) + >>> _ = qc.z(1) + >>> op = X ^ Y + >>> state = StateFn(qc) - eval = comp.eval() - # returns a VectorStateFn (Statevector) - -.. code-block:: python + >>> comp = ~op @ state + >>> eval = comp.eval() >>> print(state) CircuitStateFn( - - q_0: - - q_1: - + ┌───┐ + q_0: ┤ X ├ + ├───┤ + q_1: ┤ Z ├ + └───┘ ) + >>> print(comp) CircuitStateFn( - ┌────────────┐ - q_0: ┤0 ├ - │ Pauli(XY) │ - q_1: ┤1 ├ - └────────────┘ + ┌───┐┌────────────┐ + q_0: ┤ X ├┤0 ├ + ├───┤│ Pauli(XY) │ + q_1: ┤ Z ├┤1 ├ + └───┘└────────────┘ ) + >>> print(eval) - VectorStateFn(Statevector([0.0e+00+0.j, 0.0e+00+0.j, 0.0e+00+0.j, 6.1e-17+1.j], - dims=(2, 2))) + VectorStateFn(Statevector([ 0.0e+00+0.j, 0.0e+00+0.j, -6.1e-17-1.j, 0.0e+00+0.j], + dims=(2, 2))) **Alternative** .. code-block:: python - from qiskit import QuantumCircuit - from qiskit.quantum_info import SparsePauliOp, Statevector + >>> from qiskit import QuantumCircuit + >>> from qiskit.quantum_info import SparsePauliOp, Statevector - qc = QuantumCircuit(2) - op = SparsePauliOp("XY") - state = Statevector(qc) - - eval = state.evolve(operator) - # returns a Statevector - -.. code-block:: python + >>> qc = QuantumCircuit(2) + >>> _ = qc.x(0) + >>> _ = qc.z(1) + >>> op = SparsePauliOp("XY") + >>> state = Statevector(qc) >>> print(state) - Statevector([1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j], - dims=(2, 2)) + Statevector([0.+0.j, 1.+0.j, 0.+0.j, 0.+0.j], + dims=(2, 2)) + + >>> eval = state.evolve(op) >>> print(eval) - Statevector([0.5+0.j, 0.5+0.j, 0.5+0.j, 0.5+0.j], - dims=(2, 2)) + Statevector([0.+0.j, 0.+0.j, 0.-1.j, 0.+0.j], + dims=(2, 2)) .. raw:: html @@ -904,69 +875,55 @@ Notably, this functionality has been replaced by the :mod:`~qiskit.primitives`. .. code-block:: python - from qiskit_aer import Aer - from qiskit.circuit import QuantumCircuit, Parameter - from qiskit.opflow import ListOp, StateFn, CircuitSampler - - x, y = Parameter("x"), Parameter("y") - - circuit1 = QuantumCircuit(1) - circuit1.p(0.2, 0) - circuit2 = QuantumCircuit(1) - circuit2.p(x, 0) - circuit3 = QuantumCircuit(1) - circuit3.p(y, 0) + >>> from qiskit_aer import Aer + >>> from qiskit.circuit import QuantumCircuit, Parameter + >>> from qiskit.opflow import ListOp, StateFn, CircuitSampler - bindings = {x: -0.4, y: 0.4} - listop = ListOp([StateFn(circuit) for circuit in [circuit1, circuit2, circuit3]]) + >>> x, y = Parameter("x"), Parameter("y") - sampler = CircuitSampler(Aer.get_backend("aer_simulator")) - sampled = sampler.convert(listop, params=bindings).eval() - # returns list of SparseVectorStateFn + >>> circuit1 = QuantumCircuit(1) + >>> _ = circuit1.p(0.2, 0) + >>> circuit2 = QuantumCircuit(1) + >>> _ = circuit2.p(x, 0) + >>> circuit3 = QuantumCircuit(1) + >>> _ = circuit3.p(y, 0) -.. code-block:: python + >>> bindings = {x: -0.4, y: 0.4} + >>> listop = ListOp([StateFn(circuit) for circuit in [circuit1, circuit2, circuit3]]) - >>> print(sampled) - [SparseVectorStateFn(<1x2 sparse matrix of type '' - with 1 stored elements in Compressed Sparse Row format>, coeff=1.0, is_measurement=False), - SparseVectorStateFn(<1x2 sparse matrix of type '' - with 1 stored elements in Compressed Sparse Row format>, coeff=1.0, is_measurement=False), - SparseVectorStateFn(<1x2 sparse matrix of type '' - with 1 stored elements in Compressed Sparse Row format>, coeff=1.0, is_measurement=False)] + >>> sampler = CircuitSampler(Aer.get_backend("aer_simulator")) + >>> sampled = sampler.convert(listop, params=bindings).eval() >>> for s in sampled: - >>> print(s) - SparseVectorStateFn( (0, 0) 1.0) - SparseVectorStateFn( (0, 0) 1.0) - SparseVectorStateFn( (0, 0) 1.0) + ... print(s) + SparseVectorStateFn( (0, 0) 1.0) + SparseVectorStateFn( (0, 0) 1.0) + SparseVectorStateFn( (0, 0) 1.0) **Alternative** .. code-block:: python - from qiskit.circuit import QuantumCircuit, Parameter - from qiskit.primitives import Sampler + >>> from qiskit.circuit import QuantumCircuit, Parameter + >>> from qiskit.primitives import Sampler - x, y = Parameter("x"), Parameter("y") + >>> x, y = Parameter("x"), Parameter("y") - circuit1 = QuantumCircuit(1) - circuit1.p(0.2, 0) - circuit1.measure_all() # Don't forget measurements!!!!! - circuit2 = QuantumCircuit(1) - circuit2.p(x, 0) - circuit2.measure_all() - circuit3 = QuantumCircuit(1) - circuit3.p(y, 0) - circuit3.measure_all() + >>> circuit1 = QuantumCircuit(1) + >>> _ = circuit1.p(0.2, 0) + >>> circuit1.measure_all() # Don't forget measurements!!!!! + >>> circuit2 = QuantumCircuit(1) + >>> _ = circuit2.p(x, 0) + >>> circuit2.measure_all() + >>> circuit3 = QuantumCircuit(1) + >>> _ = circuit3.p(y, 0) + >>> circuit3.measure_all() - circuits = [circuit1, circuit2, circuit3] - param_values = [[], [-0.4], [0.4]] + >>> circuits = [circuit1, circuit2, circuit3] + >>> param_values = [[], [-0.4], [0.4]] - sampler = Sampler() - sampled = sampler.run(circuits, param_values).result().quasi_dists - # returns qiskit.result.QuasiDist - -.. code-block:: python + >>> sampler = Sampler() + >>> sampled = sampler.run(circuits, param_values).result().quasi_dists >>> print(sampled) [{0: 1.0}, {0: 1.0}, {0: 1.0}] @@ -986,22 +943,20 @@ Notably, this functionality has been replaced by the :mod:`~qiskit.primitives`. .. code-block:: python - from qiskit import QuantumCircuit - from qiskit.opflow import X, Z, StateFn, CircuitStateFn, CircuitSampler - from qiskit.providers.aer import AerSimulator + >>> 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 + >>> qc = QuantumCircuit(1) + >>> _ = qc.h(0) + >>> state = CircuitStateFn(qc) + >>> hamiltonian = X + Z -.. code-block:: python + >>> expr = StateFn(hamiltonian, is_measurement=True).compose(state) + >>> backend = AerSimulator(method="statevector") + >>> sampler = CircuitSampler(backend) + >>> expectation = sampler.convert(expr) + >>> expectation_value = expectation.eval().real >>> expectation_value 1.0000000000000002 @@ -1010,18 +965,16 @@ Notably, this functionality has been replaced by the :mod:`~qiskit.primitives`. .. 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)]) + >>> from qiskit import QuantumCircuit + >>> from qiskit.primitives import Estimator + >>> from qiskit.quantum_info import SparsePauliOp - estimator = Estimator() - expectation_value = estimator.run(state, hamiltonian).result().values.real + >>> state = QuantumCircuit(1) + >>> _ = state.h(0) + >>> hamiltonian = SparsePauliOp.from_list([('X', 1), ('Z',1)]) -.. code-block:: python + >>> estimator = Estimator() + >>> expectation_value = estimator.run(state, hamiltonian).result().values.real >>> expectation_value array([1.]) @@ -1040,41 +993,39 @@ Notably, this functionality has been replaced by the :mod:`~qiskit.primitives`. .. code-block:: python - from qiskit.opflow import PauliSumOp, AbelianGrouper + >>> from qiskit.opflow import PauliSumOp, AbelianGrouper - op = PauliSumOp.from_list([("XX", 2), ("YY", 1), ("IZ",2j), ("ZZ",1j)]) + >>> op = PauliSumOp.from_list([("XX", 2), ("YY", 1), ("IZ",2j), ("ZZ",1j)]) - grouped_sum = AbelianGrouper.group_subops(op) - -.. code-block:: python + >>> grouped_sum = AbelianGrouper.group_subops(op) >>> grouped_sum - SummedOp([PauliSumOp(SparsePauliOp(['XX'], coeffs=[2.+0.j]), coeff=1.0), - PauliSumOp(SparsePauliOp(['YY'], coeffs=[1.+0.j]), coeff=1.0), - PauliSumOp(SparsePauliOp(['IZ', 'ZZ'], coeffs=[0.+2.j, 0.+1.j]), - coeff=1.0)], coeff=1.0, abelian=False) + SummedOp([PauliSumOp(SparsePauliOp(['XX'], + coeffs=[2.+0.j]), coeff=1.0), PauliSumOp(SparsePauliOp(['YY'], + coeffs=[1.+0.j]), coeff=1.0), PauliSumOp(SparsePauliOp(['IZ', 'ZZ'], + coeffs=[0.+2.j, 0.+1.j]), coeff=1.0)], coeff=1.0, abelian=False) **Alternative** .. code-block:: python - from qiskit.quantum_info import SparsePauliOp + >>> from qiskit.quantum_info import SparsePauliOp - op = SparsePauliOp.from_list([("XX", 2), ("YY", 1), ("IZ",2j), ("ZZ",1j)]) + >>> op = SparsePauliOp.from_list([("XX", 2), ("YY", 1), ("IZ",2j), ("ZZ",1j)]) - grouped = op.group_commuting() - grouped_sum = op.group_commuting(qubit_wise=True) - -.. code-block:: python + >>> grouped = op.group_commuting() + >>> grouped_sum = op.group_commuting(qubit_wise=True) >>> grouped - [SparsePauliOp(["IZ", "ZZ"], coeffs=[0.+2.j, 0.+1j]), - SparsePauliOp(["XX", "YY"], coeffs=[2.+0.j, 1.+0.j])] + [SparsePauliOp(['IZ', 'ZZ'], + coeffs=[0.+2.j, 0.+1.j]), SparsePauliOp(['XX', 'YY'], + coeffs=[2.+0.j, 1.+0.j])] >>> grouped_sum - [SparsePauliOp(['XX'], coeffs=[2.+0.j]), - SparsePauliOp(['YY'], coeffs=[1.+0.j]), - SparsePauliOp(['IZ', 'ZZ'], coeffs=[0.+2.j, 0.+1.j])] + [SparsePauliOp(['XX'], + coeffs=[2.+0.j]), SparsePauliOp(['YY'], + coeffs=[1.+0.j]), SparsePauliOp(['IZ', 'ZZ'], + coeffs=[0.+2.j, 0.+1.j])] .. raw:: html @@ -1168,14 +1119,12 @@ Other Evolution Classes .. code-block:: python - from qiskit.opflow import Trotter, PauliTrotterEvolution, PauliSumOp + >>> from qiskit.opflow import Trotter, PauliTrotterEvolution, PauliSumOp - hamiltonian = PauliSumOp.from_list([('X', 1), ('Z',1)]) - evolution = PauliTrotterEvolution(trotter_mode=Trotter(), reps=2) - evol_result = evolution.convert(hamiltonian.exp_i()) - evolved_state = evol_result.to_circuit() - -.. code-block:: python + >>> hamiltonian = PauliSumOp.from_list([('X', 1), ('Z',1)]) + >>> evolution = PauliTrotterEvolution(trotter_mode=Trotter(), reps=2) + >>> evol_result = evolution.convert(hamiltonian.exp_i()) + >>> evolved_state = evol_result.to_circuit() >>> print(evolved_state) ┌─────────────────────┐ @@ -1186,17 +1135,15 @@ Other Evolution Classes .. code-block:: python - from qiskit.quantum_info import SparsePauliOp - from qiskit.synthesis import SuzukiTrotter - from qiskit.circuit.library import PauliEvolutionGate - from qiskit import QuantumCircuit + >>> 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, time=1, synthesis=SuzukiTrotter(reps=2)) - evolved_state = QuantumCircuit(1) - evolved_state.append(evol_gate, [0]) - -.. code-block:: python + >>> hamiltonian = SparsePauliOp.from_list([('X', 1), ('Z',1)]) + >>> evol_gate = PauliEvolutionGate(hamiltonian, time=1, synthesis=SuzukiTrotter(reps=2)) + >>> evolved_state = QuantumCircuit(1) + >>> _ = evolved_state.append(evol_gate, [0]) >>> print(evolved_state) ┌─────────────────────┐ @@ -1218,16 +1165,14 @@ Other Evolution Classes .. code-block:: python - from qiskit.opflow import Trotter, PauliTrotterEvolution, PauliSumOp - from qiskit.circuit import Parameter + >>> from qiskit.opflow import Trotter, PauliTrotterEvolution, PauliSumOp + >>> from qiskit.circuit import Parameter - time = Parameter('t') - hamiltonian = PauliSumOp.from_list([('X', 1), ('Y',1)]) - evolution = PauliTrotterEvolution(trotter_mode=Trotter(), reps=1) - evol_result = evolution.convert((time * hamiltonian).exp_i()) - evolved_state = evol_result.to_circuit() - -.. code-block:: python + >>> time = Parameter('t') + >>> hamiltonian = PauliSumOp.from_list([('X', 1), ('Y',1)]) + >>> evolution = PauliTrotterEvolution(trotter_mode=Trotter(), reps=1) + >>> evol_result = evolution.convert((time * hamiltonian).exp_i()) + >>> evolved_state = evol_result.to_circuit() >>> print(evolved_state) ┌─────────────────────────┐ @@ -1238,19 +1183,17 @@ Other Evolution Classes .. code-block:: python - from qiskit.quantum_info import SparsePauliOp - from qiskit.synthesis import LieTrotter - from qiskit.circuit.library import PauliEvolutionGate - from qiskit import QuantumCircuit - from qiskit.circuit import Parameter + >>> from qiskit.quantum_info import SparsePauliOp + >>> from qiskit.synthesis import LieTrotter + >>> from qiskit.circuit.library import PauliEvolutionGate + >>> from qiskit import QuantumCircuit + >>> from qiskit.circuit import Parameter - time = Parameter('t') - hamiltonian = SparsePauliOp.from_list([('X', 1), ('Y',1)]) - evol_gate = PauliEvolutionGate(hamiltonian, time=time, synthesis=LieTrotter()) - evolved_state = QuantumCircuit(1) - evolved_state.append(evol_gate, [0]) - -.. code-block:: python + >>> time = Parameter('t') + >>> hamiltonian = SparsePauliOp.from_list([('X', 1), ('Y',1)]) + >>> evol_gate = PauliEvolutionGate(hamiltonian, time=time, synthesis=LieTrotter()) + >>> evolved_state = QuantumCircuit(1) + >>> _ = evolved_state.append(evol_gate, [0]) >>> print(evolved_state) ┌─────────────────────┐ @@ -1272,14 +1215,12 @@ Other Evolution Classes .. code-block:: python - from qiskit.opflow import MatrixEvolution, MatrixOp + >>> 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 + >>> hamiltonian = MatrixOp([[0, 1], [1, 0]]) + >>> evolution = MatrixEvolution() + >>> evol_result = evolution.convert(hamiltonian.exp_i()) + >>> evolved_state = evol_result.to_circuit() >>> print(evolved_state.decompose().decompose()) ┌────────────────┐ @@ -1290,15 +1231,13 @@ Other Evolution Classes .. code-block:: python - from qiskit.quantum_info import SparsePauliOp - from qiskit.extensions import HamiltonianGate - from qiskit import QuantumCircuit + >>> 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]) - -.. code-block:: python + >>> evol_gate = HamiltonianGate([[0, 1], [1, 0]], 1) + >>> evolved_state = QuantumCircuit(1) + >>> _ = evolved_state.append(evol_gate, [0]) >>> print(evolved_state.decompose().decompose()) ┌────────────────┐ @@ -1355,23 +1294,21 @@ Algorithm-Agnostic Expectations .. code-block:: python - from qiskit.opflow import X, Minus, StateFn, AerPauliExpectation, CircuitSampler - from qiskit.utils import QuantumInstance - from qiskit_aer import Aer + >>> from qiskit.opflow import X, Minus, StateFn, AerPauliExpectation, CircuitSampler + >>> from qiskit.utils import QuantumInstance + >>> from qiskit_aer import Aer - backend = Aer.get_backend("aer_simulator") - q_instance = QuantumInstance(backend) + >>> backend = Aer.get_backend("aer_simulator") + >>> q_instance = QuantumInstance(backend) - sampler = CircuitSampler(q_instance, attach_results=True) - expectation = AerPauliExpectation() + >>> sampler = CircuitSampler(q_instance, attach_results=True) + >>> expectation = AerPauliExpectation() - state = Minus - operator = 1j * X + >>> state = Minus + >>> operator = 1j * X - converted_meas = expectation.convert(StateFn(operator, is_measurement=True) @ state) - expectation_value = sampler.convert(converted_meas).eval() - -.. code-block:: python + >>> converted_meas = expectation.convert(StateFn(operator, is_measurement=True) @ state) + >>> expectation_value = sampler.convert(converted_meas).eval() >>> print(expectation_value) -1j @@ -1380,23 +1317,21 @@ Algorithm-Agnostic Expectations .. code-block:: python - from qiskit.quantum_info import SparsePauliOp - from qiskit import QuantumCircuit - from qiskit_aer.primitives import Estimator as AerEstimator + >>> from qiskit.quantum_info import SparsePauliOp + >>> from qiskit import QuantumCircuit + >>> from qiskit_aer.primitives import Estimator as AerEstimator - estimator = AerEstimator(run_options={"approximation": True, "shots": None}) + >>> estimator = AerEstimator(run_options={"approximation": True, "shots": None}) - op = SparsePauliOp.from_list([("X", 1j)]) - states_op = QuantumCircuit(1) - states_op.x(0) - states_op.h(0) + >>> op = SparsePauliOp.from_list([("X", 1j)]) + >>> states_op = QuantumCircuit(1) + >>> _ = states_op.x(0) + >>> _ = states_op.h(0) - expectation_value = estimator.run(states_op, op).result().values - -.. code-block:: python + >>> expectation_value = estimator.run(states_op, op).result().values >>> print(expectation_value) - array([0.-1.j]) + [0.-1.j] .. raw:: html @@ -1415,22 +1350,20 @@ Algorithm-Agnostic Expectations .. code-block:: python - from qiskit_aer import Aer - from qiskit.opflow import X, H, I, MatrixExpectation, ListOp, StateFn - from qiskit.utils import QuantumInstance + >>> from qiskit_aer import Aer + >>> from qiskit.opflow import X, H, I, MatrixExpectation, ListOp, StateFn + >>> from qiskit.utils import QuantumInstance - backend = Aer.get_backend("statevector_simulator") - q_instance = QuantumInstance(backend) - sampler = CircuitSampler(q_instance, attach_results=True) - expect = MatrixExpectation() + >>> backend = Aer.get_backend("statevector_simulator") + >>> q_instance = QuantumInstance(backend) + >>> sampler = CircuitSampler(q_instance, attach_results=True) + >>> expect = MatrixExpectation() - mixed_ops = ListOp([X.to_matrix_op(), H]) - converted_meas = expect.convert(~StateFn(mixed_ops)) + >>> mixed_ops = ListOp([X.to_matrix_op(), H]) + >>> converted_meas = expect.convert(~StateFn(mixed_ops)) - plus_mean = converted_meas @ Plus - values_plus = sampler.convert(plus_mean).eval() - -.. code-block:: python + >>> plus_mean = converted_meas @ Plus + >>> values_plus = sampler.convert(plus_mean).eval() >>> values_plus [(1+0j), (0.7071067811865476+0j)] @@ -1439,26 +1372,24 @@ Algorithm-Agnostic Expectations .. code-block:: python - from qiskit.primitives import Estimator - from qiskit.quantum_info import SparsePauliOp - from qiskit.quantum_info import Clifford - - X = SparsePauliOp("X") + >>> from qiskit.primitives import Estimator + >>> from qiskit.quantum_info import SparsePauliOp + >>> from qiskit.quantum_info import Clifford - qc = QuantumCircuit(1) - qc.h(0) - H = Clifford(qc).to_operator() + >>> X = SparsePauliOp("X") - plus = QuantumCircuit(1) - plus.h(0) + >>> qc = QuantumCircuit(1) + >>> _ = qc.h(0) + >>> H = Clifford(qc).to_operator() - estimator = Estimator() - values_plus = estimator.run([plus, plus], [X, H]).result().values + >>> plus = QuantumCircuit(1) + >>> _ = plus.h(0) -.. code-block:: python + >>> estimator = Estimator() + >>> values_plus = estimator.run([plus, plus], [X, H]).result().values >>> values_plus - array([1. , 0.70710678]) + array([1. , 0.70710678]) .. raw:: html @@ -1490,71 +1421,46 @@ CVaRExpectation .. 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) + >>> from qiskit.opflow import CVaRExpectation, PauliSumOp -.. code-block:: python + >>> from qiskit.algorithms import VQE + >>> from qiskit.algorithms.optimizers import SLSQP + >>> from qiskit.circuit.library import TwoLocal + >>> from qiskit_aer import AerSimulator - >>> print(result) - { 'aux_operator_eigenvalues': None, - 'cost_function_evals': 9, - 'eigenstate': { '00': 0.03125, - '01': 0.9190900731157964, - '10': 0.36174490321219455, - '11': 0.15309310892394862}, - 'eigenvalue': (-1+0j), - 'optimal_circuit': None, - 'optimal_parameters': { ParameterVectorElement(θ[4]): -3.0690128161958303, - ParameterVectorElement(θ[3]): 5.76518147200243, - ParameterVectorElement(θ[5]): 4.864530916850402, - ParameterVectorElement(θ[6]): 6.12792883765818, - ParameterVectorElement(θ[7]): -2.7501333383184803, - ParameterVectorElement(θ[2]): -3.4487045115663344, - ParameterVectorElement(θ[1]): -5.8788723016341295, - ParameterVectorElement(θ[0]): -3.65330163985105}, - 'optimal_point': array([-3.65330164, -5.8788723 , -3.44870451, 5.76518147, -3.06901282, - 4.86453092, 6.12792884, -2.75013334]), - 'optimal_value': -1.0, - 'optimizer_evals': None, - 'optimizer_result': None, - 'optimizer_time': 0.03872823715209961} + >>> backend = AerSimulator(method="statevector") + >>> 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) + + >>> print(result.eigenvalue) + (-1+0j) **Alternative** .. code-block:: python - from qiskit.quantum_info import SparsePauliOp + >>> 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, opt, aggregation=alpha) - result = vqe.compute_minimum_eigenvalue(op) + >>> from qiskit.algorithms.minimum_eigensolvers import SamplingVQE + >>> from qiskit.algorithms.optimizers import SLSQP + >>> from qiskit.circuit.library import TwoLocal + >>> from qiskit.primitives import Sampler -.. code-block:: python + >>> 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, opt, aggregation=alpha) + >>> result = vqe.compute_minimum_eigenvalue(op) + + >>> print(result.eigenvalue) + -1.0 - >>> print(result) - SamplingMinimumEigensolverResult: - Eigenvalue: -1.0 - Best measurement: - {'state': 1, 'bitstring': '01', 'value': (-1+0j), 'probability': 0.7505629720987731} .. raw:: html @@ -1709,74 +1615,69 @@ list: .. code-block:: python - from qiskit.circuit import Parameter, QuantumCircuit - from qiskit.opflow import Gradient, X, Z, StateFn, CircuitStateFn - import numpy as np + >>> from qiskit.circuit import Parameter, QuantumCircuit + >>> from qiskit.opflow import Gradient, X, Z, StateFn, CircuitStateFn + >>> import numpy as np - ham = 0.5 * X - 1 * Z + >>> ham = 0.5 * X - 1 * Z - a = Parameter("a") - b = Parameter("b") - c = Parameter("c") - params = [a,b,c] + >>> a = Parameter("a") + >>> b = Parameter("b") + >>> c = Parameter("c") + >>> params = [a,b,c] - qc = QuantumCircuit(1) - qc.h(0) - qc.u(a, b, c, 0) - qc.h(0) + >>> qc = QuantumCircuit(1) + >>> _ = qc.h(0) + >>> _ = qc.u(a, b, c, 0) + >>> _ = qc.h(0) - op = ~StateFn(ham) @ CircuitStateFn(primitive=qc, coeff=1.0) + >>> op = ~StateFn(ham) @ CircuitStateFn(primitive=qc, coeff=1.0) # the gradient class acted similarly opflow converters, # with a .convert() step and an .eval() step - state_grad = Gradient(grad_method="param_shift").convert(operator=op, params=params) + >>> state_grad = Gradient(grad_method="param_shift").convert(operator=op, params=params) # the old workflow did not allow for batched evaluation of parameter values - values_dict = [{a: np.pi / 4, b: 0, c: 0}, {a: np.pi / 4, b: np.pi / 4, c: np.pi / 4}] - gradients = [] - for i, value_dict in enumerate(values_dict): - gradients.append(state_grad.assign_parameters(value_dict).eval()) - -.. code-block:: python + >>> values_dict = [{a: np.pi / 4, b: 0, c: 0}, {a: np.pi / 4, b: np.pi / 4, c: np.pi / 4}] + >>> gradients = [] + >>> for i, value_dict in enumerate(values_dict): + ... gradients.append(state_grad.assign_parameters(value_dict).eval()) >>> print(gradients) - [[(0.35355339059327356+0j), (-1.182555756156289e-16+0j), (-1.6675e-16+0j)], - [(0.10355339059327384+0j), (0.8535533905932734+0j), (1.103553390593273+0j)]] + [[(0.35355339059327356+0j), (-1.182555756156289e-16+0j), (-1.6675e-16+0j)], [(0.10355339059327384+0j), (0.8535533905932734+0j), (1.103553390593273+0j)]] **Alternative** .. code-block:: python - from qiskit.circuit import Parameter, QuantumCircuit - from qiskit.primitives import Estimator - from qiskit.algorithms.gradients import ParamShiftEstimatorGradient - from qiskit.quantum_info import SparsePauliOp + >>> from qiskit.circuit import Parameter, QuantumCircuit + >>> from qiskit.primitives import Estimator + >>> from qiskit.algorithms.gradients import ParamShiftEstimatorGradient + >>> from qiskit.quantum_info import SparsePauliOp - ham = SparsePauliOp.from_list([("X", 0.5), ("Z", -1)]) + >>> ham = SparsePauliOp.from_list([("X", 0.5), ("Z", -1)]) - a = Parameter("a") - b = Parameter("b") - c = Parameter("c") + >>> a = Parameter("a") + >>> b = Parameter("b") + >>> c = Parameter("c") - qc = QuantumCircuit(1) - qc.h(0) - qc.u(a, b, c, 0) - qc.h(0) + >>> qc = QuantumCircuit(1) + >>> _ = qc.h(0) + >>> _ = qc.u(a, b, c, 0) + >>> _ = qc.h(0) - estimator = Estimator() - gradient = ParamShiftEstimatorGradient(estimator) + >>> estimator = Estimator() + >>> gradient = ParamShiftEstimatorGradient(estimator) # the new workflow follows an interface close to the primitives' - param_list = [[np.pi / 4, 0, 0], [np.pi / 4, np.pi / 4, np.pi / 4]] + >>> param_list = [[np.pi / 4, 0, 0], [np.pi / 4, np.pi / 4, np.pi / 4]] + # for batched evaluations, the number of circuits must match the # number of parameter value sets - gradients = gradient.run([qc] * 2, [ham] * 2, param_list).result().gradients - -.. code-block:: python + >>> gradients = gradient.run([qc] * 2, [ham] * 2, param_list).result().gradients >>> print(gradients) - [array([ 3.53553391e-01, 0.00000000e+00, -1.80411242e-16]), - array([0.10355339, 0.85355339, 1.10355339])] + [array([ 3.53553391e-01, 0.00000000e+00, -1.80411242e-16]), array([0.10355339, 0.85355339, 1.10355339])] .. raw:: html @@ -1792,25 +1693,23 @@ list: .. code-block:: python - from qiskit.circuit import Parameter, QuantumCircuit - from qiskit.opflow import QFI, CircuitStateFn + >>> from qiskit.circuit import Parameter, QuantumCircuit + >>> from qiskit.opflow import QFI, CircuitStateFn # create the circuit - a, b = Parameter("a"), Parameter("b") - qc = QuantumCircuit(1) - qc.h(0) - qc.rz(a, 0) - qc.rx(b, 0) + >>> a, b = Parameter("a"), Parameter("b") + >>> qc = QuantumCircuit(1) + >>> _ = qc.h(0) + >>> _ = qc.rz(a, 0) + >>> _ = qc.rx(b, 0) # convert the circuit to a QFI object - op = CircuitStateFn(qc) - qfi = QFI(qfi_method="lin_comb_full").convert(operator=op) + >>> op = CircuitStateFn(qc) + >>> qfi = QFI(qfi_method="lin_comb_full").convert(operator=op) # bind parameters and evaluate - values_dict = {a: np.pi / 4, b: 0.1} - qfi = qfi.bind_parameters(values_dict).eval() - -.. code-block:: python + >>> values_dict = {a: np.pi / 4, b: 0.1} + >>> qfi = qfi.bind_parameters(values_dict).eval() >>> print(qfi) [[ 1.00000000e+00+0.j -3.63575685e-16+0.j] @@ -1820,27 +1719,25 @@ list: .. code-block:: python - from qiskit.circuit import Parameter, QuantumCircuit - from qiskit.primitives import Estimator - from qiskit.algorithms.gradients import LinCombQGT, QFI + >>> from qiskit.circuit import Parameter, QuantumCircuit + >>> from qiskit.primitives import Estimator + >>> from qiskit.algorithms.gradients import LinCombQGT, QFI # create the circuit - a, b = Parameter("a"), Parameter("b") - qc = QuantumCircuit(1) - qc.h(0) - qc.rz(a, 0) - qc.rx(b, 0) + >>> a, b = Parameter("a"), Parameter("b") + >>> qc = QuantumCircuit(1) + >>> _ = qc.h(0) + >>> _ = qc.rz(a, 0) + >>> _ = qc.rx(b, 0) # initialize QFI - estimator = Estimator() - qgt = LinCombQGT(estimator) - qfi = QFI(qgt) + >>> estimator = Estimator() + >>> qgt = LinCombQGT(estimator) + >>> qfi = QFI(qgt) # evaluate - values_list = [np.pi / 4, 0.1] - qfi = qfi.run(qc, values_list).result().qfis - -.. code-block:: python + >>> values_list = [[np.pi / 4, 0.1]] + >>> qfi = qfi.run(qc, values_list).result().qfis >>> print(qfi) [array([[ 1.00000000e+00, -1.50274614e-16], From d0eaacecb35d507f35226888dba4645ba2f0503e Mon Sep 17 00:00:00 2001 From: ElePT Date: Tue, 28 Feb 2023 16:04:55 +0100 Subject: [PATCH 33/50] Remove underscores --- docs/migration_guides/opflow_migration.rst | 74 +++++++++++----------- 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/docs/migration_guides/opflow_migration.rst b/docs/migration_guides/opflow_migration.rst index f2b477f5012..582744dcfba 100644 --- a/docs/migration_guides/opflow_migration.rst +++ b/docs/migration_guides/opflow_migration.rst @@ -334,8 +334,8 @@ Common non-parametrized gates (Clifford) >>> from qiskit.quantum_info import Clifford, Operator >>> qc = QuantumCircuit(2) - >>> _ = qc.h(0) - >>> _ = qc.h(1) + >>> qc.h(0) + >>> qc.h(1) >>> operator = Clifford(qc).to_operator() >>> operator Operator([[ 0.5+0.j, 0.5+0.j, 0.5+0.j, 0.5+0.j], @@ -355,7 +355,7 @@ Common non-parametrized gates (Clifford) # or... >>> qc = QuantumCircuit(1) - >>> _ = qc.h(0) + >>> qc.h(0) >>> H = Clifford(qc).to_operator() >>> operator = H ^ H >>> operator @@ -425,13 +425,13 @@ Common non-parametrized gates (Clifford) >>> qc_zero = QuantumCircuit(1) >>> qc_one = qc_zero.copy() - >>> _ = qc_one.x(0) + >>> qc_one.x(0) >>> state1 = Statevector(qc_zero) ^ Statevector(qc_one) >>> qc_plus = qc_zero.copy() - >>> _ = qc_plus.h(0) + >>> qc_plus.h(0) >>> qc_minus = qc_one.copy() - >>> _ = qc_minus.h(0) + >>> qc_minus.h(0) >>> state2 = StabilizerState(qc_plus) ^ StabilizerState(qc_minus) >>> state1 @@ -760,8 +760,8 @@ identify the sub-class that is being used, to then look for an alternative. >>> from qiskit.opflow import StateFn, X, Y >>> qc = QuantumCircuit(2) - >>> _ = qc.x(0) - >>> _ = qc.z(1) + >>> qc.x(0) + >>> qc.z(1) >>> op = X ^ Y >>> state = StateFn(qc) @@ -800,8 +800,8 @@ identify the sub-class that is being used, to then look for an alternative. >>> from qiskit.quantum_info import SparsePauliOp, Statevector >>> qc = QuantumCircuit(2) - >>> _ = qc.x(0) - >>> _ = qc.z(1) + >>> qc.x(0) + >>> qc.z(1) >>> op = SparsePauliOp("XY") >>> state = Statevector(qc) @@ -882,11 +882,11 @@ Notably, this functionality has been replaced by the :mod:`~qiskit.primitives`. >>> x, y = Parameter("x"), Parameter("y") >>> circuit1 = QuantumCircuit(1) - >>> _ = circuit1.p(0.2, 0) + >>> circuit1.p(0.2, 0) >>> circuit2 = QuantumCircuit(1) - >>> _ = circuit2.p(x, 0) + >>> circuit2.p(x, 0) >>> circuit3 = QuantumCircuit(1) - >>> _ = circuit3.p(y, 0) + >>> circuit3.p(y, 0) >>> bindings = {x: -0.4, y: 0.4} >>> listop = ListOp([StateFn(circuit) for circuit in [circuit1, circuit2, circuit3]]) @@ -910,13 +910,13 @@ Notably, this functionality has been replaced by the :mod:`~qiskit.primitives`. >>> x, y = Parameter("x"), Parameter("y") >>> circuit1 = QuantumCircuit(1) - >>> _ = circuit1.p(0.2, 0) + >>> circuit1.p(0.2, 0) >>> circuit1.measure_all() # Don't forget measurements!!!!! >>> circuit2 = QuantumCircuit(1) - >>> _ = circuit2.p(x, 0) + >>> circuit2.p(x, 0) >>> circuit2.measure_all() >>> circuit3 = QuantumCircuit(1) - >>> _ = circuit3.p(y, 0) + >>> circuit3.p(y, 0) >>> circuit3.measure_all() >>> circuits = [circuit1, circuit2, circuit3] @@ -948,7 +948,7 @@ Notably, this functionality has been replaced by the :mod:`~qiskit.primitives`. >>> from qiskit.providers.aer import AerSimulator >>> qc = QuantumCircuit(1) - >>> _ = qc.h(0) + >>> qc.h(0) >>> state = CircuitStateFn(qc) >>> hamiltonian = X + Z @@ -970,7 +970,7 @@ Notably, this functionality has been replaced by the :mod:`~qiskit.primitives`. >>> from qiskit.quantum_info import SparsePauliOp >>> state = QuantumCircuit(1) - >>> _ = state.h(0) + >>> state.h(0) >>> hamiltonian = SparsePauliOp.from_list([('X', 1), ('Z',1)]) >>> estimator = Estimator() @@ -1143,7 +1143,7 @@ Other Evolution Classes >>> hamiltonian = SparsePauliOp.from_list([('X', 1), ('Z',1)]) >>> evol_gate = PauliEvolutionGate(hamiltonian, time=1, synthesis=SuzukiTrotter(reps=2)) >>> evolved_state = QuantumCircuit(1) - >>> _ = evolved_state.append(evol_gate, [0]) + >>> evolved_state.append(evol_gate, [0]) >>> print(evolved_state) ┌─────────────────────┐ @@ -1193,7 +1193,7 @@ Other Evolution Classes >>> hamiltonian = SparsePauliOp.from_list([('X', 1), ('Y',1)]) >>> evol_gate = PauliEvolutionGate(hamiltonian, time=time, synthesis=LieTrotter()) >>> evolved_state = QuantumCircuit(1) - >>> _ = evolved_state.append(evol_gate, [0]) + >>> evolved_state.append(evol_gate, [0]) >>> print(evolved_state) ┌─────────────────────┐ @@ -1237,7 +1237,7 @@ Other Evolution Classes >>> evol_gate = HamiltonianGate([[0, 1], [1, 0]], 1) >>> evolved_state = QuantumCircuit(1) - >>> _ = evolved_state.append(evol_gate, [0]) + >>> evolved_state.append(evol_gate, [0]) >>> print(evolved_state.decompose().decompose()) ┌────────────────┐ @@ -1325,8 +1325,8 @@ Algorithm-Agnostic Expectations >>> op = SparsePauliOp.from_list([("X", 1j)]) >>> states_op = QuantumCircuit(1) - >>> _ = states_op.x(0) - >>> _ = states_op.h(0) + >>> states_op.x(0) + >>> states_op.h(0) >>> expectation_value = estimator.run(states_op, op).result().values @@ -1379,11 +1379,11 @@ Algorithm-Agnostic Expectations >>> X = SparsePauliOp("X") >>> qc = QuantumCircuit(1) - >>> _ = qc.h(0) + >>> qc.h(0) >>> H = Clifford(qc).to_operator() >>> plus = QuantumCircuit(1) - >>> _ = plus.h(0) + >>> plus.h(0) >>> estimator = Estimator() >>> values_plus = estimator.run([plus, plus], [X, H]).result().values @@ -1627,9 +1627,9 @@ list: >>> params = [a,b,c] >>> qc = QuantumCircuit(1) - >>> _ = qc.h(0) - >>> _ = qc.u(a, b, c, 0) - >>> _ = qc.h(0) + >>> qc.h(0) + >>> qc.u(a, b, c, 0) + >>> qc.h(0) >>> op = ~StateFn(ham) @ CircuitStateFn(primitive=qc, coeff=1.0) @@ -1662,9 +1662,9 @@ list: >>> c = Parameter("c") >>> qc = QuantumCircuit(1) - >>> _ = qc.h(0) - >>> _ = qc.u(a, b, c, 0) - >>> _ = qc.h(0) + >>> qc.h(0) + >>> qc.u(a, b, c, 0) + >>> qc.h(0) >>> estimator = Estimator() >>> gradient = ParamShiftEstimatorGradient(estimator) @@ -1699,9 +1699,9 @@ list: # create the circuit >>> a, b = Parameter("a"), Parameter("b") >>> qc = QuantumCircuit(1) - >>> _ = qc.h(0) - >>> _ = qc.rz(a, 0) - >>> _ = qc.rx(b, 0) + >>> qc.h(0) + >>> qc.rz(a, 0) + >>> qc.rx(b, 0) # convert the circuit to a QFI object >>> op = CircuitStateFn(qc) @@ -1726,9 +1726,9 @@ list: # create the circuit >>> a, b = Parameter("a"), Parameter("b") >>> qc = QuantumCircuit(1) - >>> _ = qc.h(0) - >>> _ = qc.rz(a, 0) - >>> _ = qc.rx(b, 0) + >>> qc.h(0) + >>> qc.rz(a, 0) + >>> qc.rx(b, 0) # initialize QFI >>> estimator = Estimator() From c29805163a8febc440ac63d72226e1d5cd7b725d Mon Sep 17 00:00:00 2001 From: ElePT <57907331+ElePT@users.noreply.github.com> Date: Fri, 3 Mar 2023 10:41:36 +0100 Subject: [PATCH 34/50] Apply suggestions second round of review Co-authored-by: Junye Huang --- docs/migration_guides/opflow_migration.rst | 40 +++++++++++----------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/docs/migration_guides/opflow_migration.rst b/docs/migration_guides/opflow_migration.rst index 582744dcfba..d0d36eea32a 100644 --- a/docs/migration_guides/opflow_migration.rst +++ b/docs/migration_guides/opflow_migration.rst @@ -44,7 +44,7 @@ The function equivalency can be roughly summarized as follows: * - Operators (:class:`~qiskit.opflow.OperatorBase`, |operator_globals|_ , :mod:`~qiskit.opflow.primitive_ops`, :mod:`~qiskit.opflow.list_ops`\) - - :mod:`qiskit.quantum_info` :ref:`quantum_info_operators` + - :ref:`qiskit.quantum_info Operators ` * - :mod:`qiskit.opflow.state_fns` - :mod:`qiskit.quantum_info` :ref:`quantum_info_states` @@ -295,8 +295,8 @@ Common non-parametrized gates (Clifford) * - :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.circuit.QuantumCircuit`. :mod:`~qiskit.quantum_info` - :class:`~qiskit.quantum_info.Operator`\s can be also directly constructed from quantum circuits. + - Append corresponding gate to :class:`~qiskit.circuit.QuantumCircuit`. + :class:`qiskit.quantum_info.Operator`\s can be also directly constructed from quantum circuits. Another alternative is to wrap the circuit in :class:`~qiskit.quantum_info.Clifford` and call ``Clifford.to_operator()``. @@ -386,7 +386,7 @@ Common non-parametrized gates (Clifford) .. note:: For efficient simulation of stabilizer states, :mod:`~qiskit.quantum_info` includes a - :class:`~qiskit.quantum_info.StabilizerState` class. See API ref. for more info. + :class:`~qiskit.quantum_info.StabilizerState` class. See API reference of :class:`~qiskit.quantum_info.StabilizerState` for more info. .. raw:: html @@ -651,7 +651,7 @@ ListOps The :mod:`~qiskit.opflow.list_ops` module contained classes for manipulating lists of :mod:`~qiskit.opflow.primitive_ops` or :mod:`~qiskit.opflow.state_fns`. The :mod:`~qiskit.quantum_info` alternatives for this functionality are the -:class:`~qiskit.quantum_info.PauliList`, :class:`~qiskit.quantum_info.SparsePauliOp` (for sums of :class:`~qiskit.quantum_info.Pauli`\s). +:class:`~qiskit.quantum_info.PauliList` and :class:`~qiskit.quantum_info.SparsePauliOp` (for sums of :class:`~qiskit.quantum_info.Pauli`\s). .. list-table:: :header-rows: 1 @@ -721,7 +721,7 @@ identify the sub-class that is being used, to then look for an alternative. - Alternative * - :class:`~qiskit.opflow.state_fns.StateFn` - - In most cases, :class:`~qiskit.quantum_info.Statevector`. Remember that this is a factory class. + - In most cases, :class:`~qiskit.quantum_info.Statevector`. However, please remember that :class:`~qiskit.opflow.state_fns.StateFn` is a factory class. * - :class:`~qiskit.opflow.state_fns.CircuitStateFn` - :class:`~qiskit.quantum_info.Statevector` @@ -828,7 +828,7 @@ Converters *Back to* `Contents`_ -The role of this sub-module was to convert the operators into other opflow operator classes +The role of the :class:`qiskit.opflow.converters` sub-module was to convert the operators into other opflow operator classes (:class:`~qiskit.opflow.converters.TwoQubitReduction`, :class:`~qiskit.opflow.converters.PauliBasisChange`...). In the case of the :class:`~qiskit.opflow.converters.CircuitSampler`, it traversed an operator and outputted approximations of its state functions using a quantum backend. @@ -875,9 +875,9 @@ Notably, this functionality has been replaced by the :mod:`~qiskit.primitives`. .. code-block:: python - >>> from qiskit_aer import Aer >>> from qiskit.circuit import QuantumCircuit, Parameter >>> from qiskit.opflow import ListOp, StateFn, CircuitSampler + >>> from qiskit_aer import AerSimulator >>> x, y = Parameter("x"), Parameter("y") @@ -891,7 +891,7 @@ Notably, this functionality has been replaced by the :mod:`~qiskit.primitives`. >>> bindings = {x: -0.4, y: 0.4} >>> listop = ListOp([StateFn(circuit) for circuit in [circuit1, circuit2, circuit3]]) - >>> sampler = CircuitSampler(Aer.get_backend("aer_simulator")) + >>> sampler = CircuitSampler(AerSimulator()) >>> sampled = sampler.convert(listop, params=bindings).eval() >>> for s in sampled: @@ -911,7 +911,7 @@ Notably, this functionality has been replaced by the :mod:`~qiskit.primitives`. >>> circuit1 = QuantumCircuit(1) >>> circuit1.p(0.2, 0) - >>> circuit1.measure_all() # Don't forget measurements!!!!! + >>> circuit1.measure_all() # Sampler primitive requires measurement readout >>> circuit2 = QuantumCircuit(1) >>> circuit2.p(x, 0) >>> circuit2.measure_all() @@ -945,7 +945,7 @@ Notably, this functionality has been replaced by the :mod:`~qiskit.primitives`. >>> from qiskit import QuantumCircuit >>> from qiskit.opflow import X, Z, StateFn, CircuitStateFn, CircuitSampler - >>> from qiskit.providers.aer import AerSimulator + >>> from qiskit_aer import AerSimulator >>> qc = QuantumCircuit(1) >>> qc.h(0) @@ -1135,10 +1135,10 @@ Other Evolution Classes .. code-block:: python + >>> from qiskit import QuantumCircuit >>> from qiskit.quantum_info import SparsePauliOp - >>> from qiskit.synthesis import SuzukiTrotter >>> from qiskit.circuit.library import PauliEvolutionGate - >>> from qiskit import QuantumCircuit + >>> from qiskit.synthesis import SuzukiTrotter >>> hamiltonian = SparsePauliOp.from_list([('X', 1), ('Z',1)]) >>> evol_gate = PauliEvolutionGate(hamiltonian, time=1, synthesis=SuzukiTrotter(reps=2)) @@ -1253,7 +1253,7 @@ Expectations *Back to* `Contents`_ 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. +This functionality can now be found in the :class:`~qiskit.primitives.Estimator` primitive. Algorithm-Agnostic Expectations ------------------------------- @@ -1273,7 +1273,7 @@ Algorithm-Agnostic Expectations See example below. * - :class:`~qiskit.opflow.expectations.MatrixExpectation` - - Use :class:`~qiskit.primitives.Estimator` primitive from :mod:`qiskit` (if no shots are set, it performs an exact Statevector calculation). + - Use :class:`qiskit.primitives.Estimator` primitive (if no shots are set, it performs an exact Statevector calculation). See example below. * - :class:`~qiskit.opflow.expectations.PauliExpectation` @@ -1296,9 +1296,9 @@ Algorithm-Agnostic Expectations >>> from qiskit.opflow import X, Minus, StateFn, AerPauliExpectation, CircuitSampler >>> from qiskit.utils import QuantumInstance - >>> from qiskit_aer import Aer + >>> from qiskit_aer import AerSimulator - >>> backend = Aer.get_backend("aer_simulator") + >>> backend = AerSimulator() >>> q_instance = QuantumInstance(backend) >>> sampler = CircuitSampler(q_instance, attach_results=True) @@ -1319,7 +1319,7 @@ Algorithm-Agnostic Expectations >>> from qiskit.quantum_info import SparsePauliOp >>> from qiskit import QuantumCircuit - >>> from qiskit_aer.primitives import Estimator as AerEstimator + >>> from qiskit_aer.primitives import Estimator >>> estimator = AerEstimator(run_options={"approximation": True, "shots": None}) @@ -1350,11 +1350,11 @@ Algorithm-Agnostic Expectations .. code-block:: python - >>> from qiskit_aer import Aer >>> from qiskit.opflow import X, H, I, MatrixExpectation, ListOp, StateFn >>> from qiskit.utils import QuantumInstance + >>> from qiskit_aer import AerSimulator - >>> backend = Aer.get_backend("statevector_simulator") + >>> backend = AerSimulator(method='statevector') >>> q_instance = QuantumInstance(backend) >>> sampler = CircuitSampler(q_instance, attach_results=True) >>> expect = MatrixExpectation() From 4d5c5a20060b7e9e0a4641e23ab67588c607b1df Mon Sep 17 00:00:00 2001 From: ElePT Date: Fri, 3 Mar 2023 11:08:44 +0100 Subject: [PATCH 35/50] Add globals link --- docs/migration_guides/opflow_migration.rst | 7 +++++-- qiskit/opflow/__init__.py | 1 + 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/docs/migration_guides/opflow_migration.rst b/docs/migration_guides/opflow_migration.rst index 582744dcfba..afe5b2b054a 100644 --- a/docs/migration_guides/opflow_migration.rst +++ b/docs/migration_guides/opflow_migration.rst @@ -7,6 +7,9 @@ TL;DR The new :mod:`~qiskit.primitives`, in combination with the :mod:`~qiskit.quantum_info` module, have superseded functionality of :mod:`~qiskit.opflow`. Thus, the latter is being deprecated. +In this migration guide, you will find instructions and code examples for how to migrate your code based on +the:mod:`~qiskit.opflow` module to the :mod:`~qiskit.primitives` and :mod:`~qiskit.quantum_info` modules. + .. note:: The use of :mod:`~qiskit.opflow` was tightly coupled to the :class:`~qiskit.utils.QuantumInstance` class, which @@ -34,14 +37,14 @@ This guide traverses the opflow submodules and provides either a direct alternat The function equivalency can be roughly summarized as follows: .. |operator_globals| replace:: ``operator_globals`` -.. _operator_globals: https://qiskit.org/documentation/apidoc/opflow.html#operator-globals/ +.. _operator_globals: https://qiskit.org/documentation/apidoc/opflow.html#operator-globals .. list-table:: :header-rows: 1 * - Opflow Module - Alternative - * - Operators (:class:`~qiskit.opflow.OperatorBase`, |operator_globals|_ , + * - Operators (:class:`~qiskit.opflow.OperatorBase`, :ref:`operator_globals`, :mod:`~qiskit.opflow.primitive_ops`, :mod:`~qiskit.opflow.list_ops`\) - :mod:`qiskit.quantum_info` :ref:`quantum_info_operators` diff --git a/qiskit/opflow/__init__.py b/qiskit/opflow/__init__.py index df980959be7..35fa212fcc6 100644 --- a/qiskit/opflow/__init__.py +++ b/qiskit/opflow/__init__.py @@ -86,6 +86,7 @@ OperatorBase +.. _operator_globals: Operator Globals ================ From 94d0dcc87c0fa750554d61f8150f1af6c1f34cd5 Mon Sep 17 00:00:00 2001 From: ElePT Date: Fri, 3 Mar 2023 14:44:20 +0100 Subject: [PATCH 36/50] Change links to refs --- docs/conf.py | 1 + docs/migration_guides/opflow_migration.rst | 94 ++++++++-------------- qiskit/quantum_info/__init__.py | 2 + qiskit/quantum_info/analysis/__init__.py | 1 + 4 files changed, 37 insertions(+), 61 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 1d74bc42a14..549390c8bb5 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -63,6 +63,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 ------------------------------------------------- diff --git a/docs/migration_guides/opflow_migration.rst b/docs/migration_guides/opflow_migration.rst index 4c38ad60647..d688d537ebd 100644 --- a/docs/migration_guides/opflow_migration.rst +++ b/docs/migration_guides/opflow_migration.rst @@ -16,6 +16,20 @@ the:mod:`~qiskit.opflow` module to the :mod:`~qiskit.primitives` and :mod:`~qisk is also being deprecated. For more information on migrating the :class:`~qiskit.utils.QuantumInstance`, please read the `quantum instance migration guide `_. +.. attention:: + + Most references to the :class:`qiskit.primitives.Sampler` or :class:`qiskit.primitives.Estimator` in this guide + can be replaced with instances of the: + + - Aer primitives (:class:`qiskit_aer.primitives.Sampler`, :class:`qiskit_aer.primitives.Estimator`) + - Runtime primitives (:class:`qiskit_ibm_runtime.Sampler`, :class:`qiskit_ibm_runtime.Estimator`) + - Terra backend primitives (:class:`qiskit.primitives.BackendSampler`, :class:`qiskit.primitives.BackendEstimator`) + + Certain classes, such as the + :class:`~qiskit.opflow.expectations.AerPauliExpectation`, can only be replaced by a specific primitive instance + (in this case, :class:`qiskit_aer.primitives.Estimator`), or require a specific option configuration. + If this is the case, it will be explicitly indicated in the corresponding section. + Background ========== @@ -36,27 +50,23 @@ This guide traverses the opflow submodules and provides either a direct alternat The function equivalency can be roughly summarized as follows: -.. |operator_globals| replace:: ``operator_globals`` -.. _operator_globals: https://qiskit.org/documentation/apidoc/opflow.html#operator-globals - .. list-table:: :header-rows: 1 * - Opflow Module - Alternative * - Operators (:class:`~qiskit.opflow.OperatorBase`, :ref:`operator_globals`, - :mod:`~qiskit.opflow.primitive_ops`, - :mod:`~qiskit.opflow.list_ops`\) + :mod:`~qiskit.opflow.primitive_ops`, :mod:`~qiskit.opflow.list_ops`) - :ref:`qiskit.quantum_info Operators ` * - :mod:`qiskit.opflow.state_fns` - - :mod:`qiskit.quantum_info` :ref:`quantum_info_states` + - :ref:`qiskit.quantum_info States ` * - :mod:`qiskit.opflow.converters` - :mod:`qiskit.primitives` * - :mod:`qiskit.opflow.evolutions` - - :mod:`qiskit.quantum_info` :ref:`quantum_info_synthesis` + - :ref:`qiskit.quantum_info Synthesis ` * - :mod:`qiskit.opflow.expectations` - :class:`qiskit.primitives.Estimator` @@ -64,38 +74,6 @@ The function equivalency can be roughly summarized as follows: * - :mod:`qiskit.opflow.gradients` - :mod:`qiskit.algorithms.gradients` -.. |qiskit_aer.primitives| replace:: ``qiskit_aer.primitives`` -.. _qiskit_aer.primitives: https://qiskit.org/documentation/locale/de_DE/apidoc/aer_primitives.html - -.. |qiskit_aer.primitives.Sampler| replace:: ``qiskit_aer.primitives.Sampler`` -.. _qiskit_aer.primitives.Sampler: https://qiskit.org/documentation/locale/de_DE/stubs/qiskit_aer.primitives.Sampler.html - -.. |qiskit_aer.primitives.Estimator| replace:: ``qiskit_aer.primitives.Estimator`` -.. _qiskit_aer.primitives.Estimator: https://qiskit.org/documentation/locale/de_DE/stubs/qiskit_aer.primitives.Estimator.html - -.. |qiskit_ibm_runtime| replace:: ``qiskit_ibm_runtime`` -.. _qiskit_ibm_runtime: https://qiskit.org/documentation/partners/qiskit_ibm_runtime/primitives.html - -.. |qiskit_ibm_runtime.Sampler| replace:: ``qiskit_ibm_runtime.Sampler`` -.. _qiskit_ibm_runtime.Sampler: https://qiskit.org/documentation/partners/qiskit_ibm_runtime/stubs/qiskit_ibm_runtime.Sampler.html - -.. |qiskit_ibm_runtime.Estimator| replace:: ``qiskit_ibm_runtime.Estimator`` -.. _qiskit_ibm_runtime.Estimator: https://qiskit.org/documentation/partners/qiskit_ibm_runtime/stubs/qiskit_ibm_runtime.Estimator.html - -.. attention:: - - Most references to the :class:`qiskit.primitives.Sampler` or :class:`qiskit.primitives.Estimator` in this guide - can be replaced with instances of the: - - - Aer primitives (|qiskit_aer.primitives.Sampler|_, |qiskit_aer.primitives.Estimator|_) - - Runtime primitives (|qiskit_ibm_runtime.Sampler|_, |qiskit_ibm_runtime.Estimator|_ ) - - Terra backend primitives (:class:`qiskit.primitives.BackendSampler`, :class:`qiskit.primitives.BackendEstimator`) - - Certain classes, such as the - :class:`~qiskit.opflow.expectations.AerPauliExpectation`, can only be replaced by a specific primitive instance - (in this case, |qiskit_aer.primitives.Estimator|_ ), or require a specific option configuration. - If this is the case, it will be explicitly indicated in the corresponding section. - Contents ======== @@ -123,11 +101,8 @@ Operator Base Class =================== *Back to* `Contents`_ -.. |qiskit.quantum_info.BaseOperator| replace:: ``qiskit.quantum_info.BaseOperator`` -.. _qiskit.quantum_info.BaseOperator: https://github.com/Qiskit/qiskit-terra/blob/main/qiskit/quantum_info/operators/base_operator.py - -The :class:`qiskit.opflow.OperatorBase` abstract class can be replaced with |qiskit.quantum_info.BaseOperator|_ , -keeping in mind that |qiskit.quantum_info.BaseOperator|_ is more generic than its opflow counterpart. +The :class:`qiskit.opflow.OperatorBase` abstract class can be replaced with :class:`qiskit.quantum_info.BaseOperator` , +keeping in mind that :class:`qiskit.quantum_info.BaseOperator` is more generic than its opflow counterpart. .. list-table:: :header-rows: 1 @@ -135,23 +110,24 @@ keeping in mind that |qiskit.quantum_info.BaseOperator|_ is more generic than it * - Opflow - Alternative * - :class:`qiskit.opflow.OperatorBase` - - |qiskit.quantum_info.BaseOperator|_ + - :class:`qiskit.quantum_info.BaseOperator` .. attention:: Despite the similar class names, :class:`qiskit.opflow.OperatorBase` and - |qiskit.quantum_info.BaseOperator|_ are not completely equivalent to each other, and the transition + :class:`qiskit.quantum_info.BaseOperator` are not completely equivalent to each other, and the transition should be handled with care. Namely: 1. :class:`qiskit.opflow.OperatorBase` implements a broader algebra mixin. Some operator overloads that were commonly used :mod:`~qiskit.opflow` (for example ``~`` for ``.adjoint()``) are not defined for - |qiskit.quantum_info.BaseOperator|_. You might want to check the specific + :class:`qiskit.quantum_info.BaseOperator`. You might want to check the specific :mod:`~qiskit.quantum_info` subclass instead. 2. :class:`qiskit.opflow.OperatorBase` also implements methods such as ``.to_matrix()`` or ``.to_spmatrix()``, - which are only found in some of the |qiskit.quantum_info.BaseOperator|_ subclasses. + which are only found in some of the :class:`qiskit.quantum_info.BaseOperator` subclasses. - See API reference for more information. + See :class:`~qiskit.opflow.OperatorBase` and :class:`~qiskit.quantum_info.BaseOperator` API references + for more information. Operator Globals @@ -159,7 +135,7 @@ Operator Globals *Back to* `Contents`_ Opflow provided shortcuts to define common single qubit states, operators, and non-parametrized gates in the -|operator_globals|_ module. +:ref:`operator_globals` module. These were mainly used for didactic purposes or quick prototyping, and can easily be replaced by their corresponding :mod:`~qiskit.quantum_info` class: :class:`~qiskit.quantum_info.Pauli`, :class:`~qiskit.quantum_info.Clifford` or @@ -298,7 +274,7 @@ Common non-parametrized gates (Clifford) * - :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.circuit.QuantumCircuit`. + - Append corresponding gate to :class:`~qiskit.circuit.QuantumCircuit`. :class:`qiskit.quantum_info.Operator`\s can be also directly constructed from quantum circuits. Another alternative is to wrap the circuit in :class:`~qiskit.quantum_info.Clifford` and call ``Clifford.to_operator()``. @@ -518,10 +494,10 @@ are used "under the hood" in the original code: * - :class:`~qiskit.opflow.primitive_ops.TaperedPauliSumOp` - This class was used to combine a :class:`.PauliSumOp` with its identified symmetries in one object. This functionality is not currently used in any workflow, and has been deprecated without replacement. - See |qiskit.quantum_info.Z2Symmetries|_ example for updated workflow. + See :class:`qiskit.quantum_info.analysis.Z2Symmetries` example for updated workflow. * - :class:`qiskit.opflow.primitive_ops.Z2Symmetries` - - |qiskit.quantum_info.Z2Symmetries|_. See example below. + - :class:`qiskit.quantum_info.analysis.Z2Symmetries`. See example below. .. _pauli_sum_op: @@ -611,7 +587,7 @@ are used "under the hood" in the original code: .. code-block:: python >>> from qiskit.quantum_info import SparsePauliOp - >>> from qiskit.quantum_info.analysis.z2_symmetries import Z2Symmetries + >>> from qiskit.quantum_info.analysis import Z2Symmetries >>> qubit_op = SparsePauliOp.from_list( ... [ @@ -682,12 +658,8 @@ State Functions =============== *Back to* `Contents`_ -.. |qiskit.quantum_info.QuantumState| replace:: ``qiskit.quantum_info.QuantumState`` -.. _qiskit.quantum_info.QuantumState: https://github.com/Qiskit/qiskit-terra/blob/main/qiskit/quantum_info/states/quantum_state.py - - The :mod:`~qiskit.opflow.state_fns` module can be generally replaced by subclasses of :mod:`~qiskit.quantum_info`\'s -|qiskit.quantum_info.QuantumState|_. +:class:`qiskit.quantum_info.QuantumState`. Similarly to :class:`~qiskit.opflow.primitive_ops.PrimitiveOp`, :class:`~qiskit.opflow.state_fns.StateFn` acts as a factory to create the corresponding sub-class depending on the computational primitive used to initialize it. @@ -1272,7 +1244,7 @@ Algorithm-Agnostic Expectations - No direct replacement. This class was used to create instances of one of the classes listed below. * - :class:`~qiskit.opflow.expectations.AerPauliExpectation` - - Use ``Estimator`` primitive from |qiskit_aer.primitives|_ with ``approximation=True`` and ``shots=None`` as ``run_options``. + - Use :class:`qiskit_aer.primitives.Estimator` with ``approximation=True`` and ``shots=None`` as ``run_options``. See example below. * - :class:`~qiskit.opflow.expectations.MatrixExpectation` @@ -1281,7 +1253,7 @@ Algorithm-Agnostic Expectations * - :class:`~qiskit.opflow.expectations.PauliExpectation` - Use any Estimator primitive (for :class:`qiskit.primitives.Estimator`, set ``shots!=None`` for a shot-based - simulation, for |qiskit_aer.primitives.Estimator|_ , this is the default). + simulation, for :class:`qiskit_aer.primitives.Estimator` , this is the default). .. _expect_state: diff --git a/qiskit/quantum_info/__init__.py b/qiskit/quantum_info/__init__.py index 237ce35830b..b3d0d81835d 100644 --- a/qiskit/quantum_info/__init__.py +++ b/qiskit/quantum_info/__init__.py @@ -111,6 +111,7 @@ hellinger_distance hellinger_fidelity + Z2Symmetries .. _quantum_info_synthesis: @@ -172,4 +173,5 @@ from .analysis import ( hellinger_distance, hellinger_fidelity, + Z2Symmetries ) diff --git a/qiskit/quantum_info/analysis/__init__.py b/qiskit/quantum_info/analysis/__init__.py index afc072a6edd..243dc5d3084 100644 --- a/qiskit/quantum_info/analysis/__init__.py +++ b/qiskit/quantum_info/analysis/__init__.py @@ -13,3 +13,4 @@ """Module for functions for post processing results.""" from .average import average_data from .distance import hellinger_fidelity, hellinger_distance +from .z2_symmetries import Z2Symmetries From 5d504f383d80dc9dcd0f4becb18629f861cca50c Mon Sep 17 00:00:00 2001 From: ElePT Date: Fri, 3 Mar 2023 15:10:45 +0100 Subject: [PATCH 37/50] Add dropdowns --- docs/migration_guides/opflow_migration.rst | 1470 +++++++++----------- 1 file changed, 666 insertions(+), 804 deletions(-) diff --git a/docs/migration_guides/opflow_migration.rst b/docs/migration_guides/opflow_migration.rst index d688d537ebd..31d91f50373 100644 --- a/docs/migration_guides/opflow_migration.rst +++ b/docs/migration_guides/opflow_migration.rst @@ -167,100 +167,88 @@ directly creating a corresponding :class:`~qiskit.quantum_info.SparsePauliOp`, a .. _1_q_pauli: -.. raw:: html -
- Example 1: Defining the XX operator -
+.. dropdown:: Example 1: Defining the XX operator + :animate: fade-in-slide-down -**Opflow** + **Opflow** -.. code-block:: python + .. code-block:: python - >>> from qiskit.opflow import X + >>> from qiskit.opflow import X - >>> operator = X ^ X + >>> operator = X ^ X -.. code-block:: python + .. code-block:: python - >>> operator - PauliOp(Pauli('XX'), coeff=1.0) + >>> operator + PauliOp(Pauli('XX'), coeff=1.0) -**Alternative** + **Alternative** -.. code-block:: python + .. code-block:: python - >>> from qiskit.quantum_info import Pauli, SparsePauliOp + >>> from qiskit.quantum_info import Pauli, SparsePauliOp - >>> X = Pauli('X') - >>> operator = X ^ X - >>> operator - Pauli('XX') + >>> X = Pauli('X') + >>> operator = X ^ X + >>> operator + Pauli('XX') - # equivalent to: - >>> operator = Pauli('XX') - >>> operator - Pauli('XX') + # equivalent to: + >>> operator = Pauli('XX') + >>> operator + Pauli('XX') - # equivalent to: - >>> operator = SparsePauliOp('XX') - >>> operator - SparsePauliOp(['XX'], - coeffs=[1.+0.j]) + # equivalent to: + >>> operator = SparsePauliOp('XX') + >>> operator + SparsePauliOp(['XX'], + coeffs=[1.+0.j]) -.. raw:: html +.. dropdown:: Example 2: Defining a more complex operator + :animate: fade-in-slide-down -
-.. raw:: html + **Opflow** -
- Example 2: Defining a more complex operator -
+ .. code-block:: python -**Opflow** + >>> from qiskit.opflow import I, X, Z, PauliSumOp -.. code-block:: python + >>> operator = 0.39 * (I ^ Z ^ I) + 0.5 * (I ^ X ^ X) + >>> operator + PauliSumOp(SparsePauliOp(['IZI', 'IXX'], + coeffs=[0.39+0.j, 0.5 +0.j]), coeff=1.0) - >>> from qiskit.opflow import I, X, Z, PauliSumOp + # or ... + >>> operator = PauliSumOp.from_list([("IZI", 0.39), ("IXX", 0.5)]) + >>> operator + PauliSumOp(SparsePauliOp(['IZI', 'IXX'], + coeffs=[0.39+0.j, 0.5 +0.j]), coeff=1.0) - >>> operator = 0.39 * (I ^ Z ^ I) + 0.5 * (I ^ X ^ X) - >>> operator - PauliSumOp(SparsePauliOp(['IZI', 'IXX'], - coeffs=[0.39+0.j, 0.5 +0.j]), coeff=1.0) + **Alternative** - # or ... - >>> operator = PauliSumOp.from_list([("IZI", 0.39), ("IXX", 0.5)]) - >>> operator - PauliSumOp(SparsePauliOp(['IZI', 'IXX'], - coeffs=[0.39+0.j, 0.5 +0.j]), coeff=1.0) - -**Alternative** - -.. code-block:: python + .. code-block:: python - >>> from qiskit.quantum_info import SparsePauliOp + >>> from qiskit.quantum_info import SparsePauliOp - >>> operator = SparsePauliOp(["IZI", "IXX"], coeffs = [0.39, 0.5]) - >>> operator - SparsePauliOp(['IZI', 'IXX'], - coeffs=[0.39+0.j, 0.5 +0.j]) + >>> operator = SparsePauliOp(["IZI", "IXX"], coeffs = [0.39, 0.5]) + >>> operator + SparsePauliOp(['IZI', 'IXX'], + coeffs=[0.39+0.j, 0.5 +0.j]) - # or... - >>> operator = SparsePauliOp.from_list([("IZI", 0.39), ("IXX", 0.5)]) - >>> operator - SparsePauliOp(['IZI', 'IXX'], - coeffs=[0.39+0.j, 0.5 +0.j]) + # or... + >>> operator = SparsePauliOp.from_list([("IZI", 0.39), ("IXX", 0.5)]) + >>> operator + SparsePauliOp(['IZI', 'IXX'], + coeffs=[0.39+0.j, 0.5 +0.j]) - # or... - >>> operator = SparsePauliOp.from_sparse_list([("Z", [1], 0.39), ("XX", [0,1], 0.5)], num_qubits = 3) - >>> operator - SparsePauliOp(['IZI', 'IXX'], - coeffs=[0.39+0.j, 0.5 +0.j]) - -.. raw:: html - -
+ # or... + >>> operator = SparsePauliOp.from_sparse_list([("Z", [1], 0.39), ("XX", [0,1], 0.5)], num_qubits = 3) + >>> operator + SparsePauliOp(['IZI', 'IXX'], + coeffs=[0.39+0.j, 0.5 +0.j]) Common non-parametrized gates (Clifford) ---------------------------------------- @@ -284,70 +272,61 @@ Common non-parametrized gates (Clifford) Constructing :mod:`~qiskit.quantum_info` operators from circuits is not efficient, as it is a dense operation and scales exponentially with the size of the circuit, use with care. - -.. raw:: html - -
- Example 1: Defining the HH operator -
- -**Opflow** - -.. code-block:: python - - >>> from qiskit.opflow import H - - >>> operator = H ^ H - >>> print(operator) - ┌───┐ - q_0: ┤ H ├ - ├───┤ - q_1: ┤ H ├ - └───┘ - -**Alternative** - -.. code-block:: python - - >>> from qiskit import QuantumCircuit - >>> from qiskit.quantum_info import Clifford, Operator - - >>> qc = QuantumCircuit(2) - >>> qc.h(0) - >>> qc.h(1) - >>> operator = Clifford(qc).to_operator() - >>> operator - Operator([[ 0.5+0.j, 0.5+0.j, 0.5+0.j, 0.5+0.j], - [ 0.5+0.j, -0.5+0.j, 0.5+0.j, -0.5+0.j], - [ 0.5+0.j, 0.5+0.j, -0.5+0.j, -0.5+0.j], - [ 0.5+0.j, -0.5+0.j, -0.5+0.j, 0.5+0.j]], - input_dims=(2, 2), output_dims=(2, 2)) - - # or, directly - >>> operator = Operator(qc) - >>> operator - Operator([[ 0.5+0.j, 0.5+0.j, 0.5+0.j, 0.5+0.j], - [ 0.5+0.j, -0.5+0.j, 0.5+0.j, -0.5+0.j], - [ 0.5+0.j, 0.5+0.j, -0.5+0.j, -0.5+0.j], - [ 0.5+0.j, -0.5+0.j, -0.5+0.j, 0.5+0.j]], - input_dims=(2, 2), output_dims=(2, 2)) - - # or... - >>> qc = QuantumCircuit(1) - >>> qc.h(0) - >>> H = Clifford(qc).to_operator() - >>> operator = H ^ H - >>> operator - Operator([[ 0.5+0.j, 0.5+0.j, 0.5+0.j, 0.5+0.j], - [ 0.5+0.j, -0.5+0.j, 0.5+0.j, -0.5+0.j], - [ 0.5+0.j, 0.5+0.j, -0.5+0.j, -0.5+0.j], - [ 0.5+0.j, -0.5+0.j, -0.5+0.j, 0.5-0.j]], - input_dims=(2, 2), output_dims=(2, 2)) - - -.. raw:: html - -
+.. dropdown:: Example 1: Defining the HH operator + :animate: fade-in-slide-down + + **Opflow** + + .. code-block:: python + + >>> from qiskit.opflow import H + + >>> operator = H ^ H + >>> print(operator) + ┌───┐ + q_0: ┤ H ├ + ├───┤ + q_1: ┤ H ├ + └───┘ + + **Alternative** + + .. code-block:: python + + >>> from qiskit import QuantumCircuit + >>> from qiskit.quantum_info import Clifford, Operator + + >>> qc = QuantumCircuit(2) + >>> qc.h(0) + >>> qc.h(1) + >>> operator = Clifford(qc).to_operator() + >>> operator + Operator([[ 0.5+0.j, 0.5+0.j, 0.5+0.j, 0.5+0.j], + [ 0.5+0.j, -0.5+0.j, 0.5+0.j, -0.5+0.j], + [ 0.5+0.j, 0.5+0.j, -0.5+0.j, -0.5+0.j], + [ 0.5+0.j, -0.5+0.j, -0.5+0.j, 0.5+0.j]], + input_dims=(2, 2), output_dims=(2, 2)) + + # or, directly + >>> operator = Operator(qc) + >>> operator + Operator([[ 0.5+0.j, 0.5+0.j, 0.5+0.j, 0.5+0.j], + [ 0.5+0.j, -0.5+0.j, 0.5+0.j, -0.5+0.j], + [ 0.5+0.j, 0.5+0.j, -0.5+0.j, -0.5+0.j], + [ 0.5+0.j, -0.5+0.j, -0.5+0.j, 0.5+0.j]], + input_dims=(2, 2), output_dims=(2, 2)) + + # or... + >>> qc = QuantumCircuit(1) + >>> qc.h(0) + >>> H = Clifford(qc).to_operator() + >>> operator = H ^ H + >>> operator + Operator([[ 0.5+0.j, 0.5+0.j, 0.5+0.j, 0.5+0.j], + [ 0.5+0.j, -0.5+0.j, 0.5+0.j, -0.5+0.j], + [ 0.5+0.j, 0.5+0.j, -0.5+0.j, -0.5+0.j], + [ 0.5+0.j, -0.5+0.j, -0.5+0.j, 0.5-0.j]], + input_dims=(2, 2), output_dims=(2, 2)) 1-Qubit States -------------- @@ -367,62 +346,55 @@ Common non-parametrized gates (Clifford) For efficient simulation of stabilizer states, :mod:`~qiskit.quantum_info` includes a :class:`~qiskit.quantum_info.StabilizerState` class. See API reference of :class:`~qiskit.quantum_info.StabilizerState` for more info. -.. raw:: html +.. dropdown:: Example 1: Working with stabilizer states + :animate: fade-in-slide-down -
- Example 1: Working with stabilizer states -
+ **Opflow** -**Opflow** + .. code-block:: python -.. code-block:: python - - >>> from qiskit.opflow import Zero, One, Plus, Minus - - # Zero, One, Plus, Minus are all stabilizer states - >>> state1 = Zero ^ One - >>> state2 = Plus ^ Minus + >>> from qiskit.opflow import Zero, One, Plus, Minus - >>> state1 - DictStateFn({'01': 1}, coeff=1.0, is_measurement=False) + # Zero, One, Plus, Minus are all stabilizer states + >>> state1 = Zero ^ One + >>> state2 = Plus ^ Minus - >>> print(state2) - CircuitStateFn( - ┌───┐┌───┐ - q_0: ┤ X ├┤ H ├ - ├───┤└───┘ - q_1: ┤ H ├───── - └───┘ - ) + >>> state1 + DictStateFn({'01': 1}, coeff=1.0, is_measurement=False) -**Alternative** + >>> print(state2) + CircuitStateFn( + ┌───┐┌───┐ + q_0: ┤ X ├┤ H ├ + ├───┤└───┘ + q_1: ┤ H ├───── + └───┘ + ) -.. code-block:: python - - >>> from qiskit import QuantumCircuit - >>> from qiskit.quantum_info import StabilizerState, Statevector + **Alternative** - >>> qc_zero = QuantumCircuit(1) - >>> qc_one = qc_zero.copy() - >>> qc_one.x(0) - >>> state1 = Statevector(qc_zero) ^ Statevector(qc_one) + .. code-block:: python - >>> qc_plus = qc_zero.copy() - >>> qc_plus.h(0) - >>> qc_minus = qc_one.copy() - >>> qc_minus.h(0) - >>> state2 = StabilizerState(qc_plus) ^ StabilizerState(qc_minus) + >>> from qiskit import QuantumCircuit + >>> from qiskit.quantum_info import StabilizerState, Statevector - >>> state1 - Statevector([0.+0.j, 1.+0.j, 0.+0.j, 0.+0.j], - dims=(2, 2)) + >>> qc_zero = QuantumCircuit(1) + >>> qc_one = qc_zero.copy() + >>> qc_one.x(0) + >>> state1 = Statevector(qc_zero) ^ Statevector(qc_one) - >>> state2 - StabilizerState(StabilizerTable: ['-IX', '+XI']) + >>> qc_plus = qc_zero.copy() + >>> qc_plus.h(0) + >>> qc_minus = qc_one.copy() + >>> qc_minus.h(0) + >>> state2 = StabilizerState(qc_plus) ^ StabilizerState(qc_minus) -.. raw:: html + >>> state1 + Statevector([0.+0.j, 1.+0.j, 0.+0.j, 0.+0.j], + dims=(2, 2)) -
+ >>> state2 + StabilizerState(StabilizerTable: ['-IX', '+XI']) Primitive and List Ops @@ -501,128 +473,117 @@ are used "under the hood" in the original code: .. _pauli_sum_op: -.. raw:: html +.. dropdown:: Example 1: ``PauliSumOp`` + :animate: fade-in-slide-down -
- Example 1: PauliSumOp -
-**Opflow** + **Opflow** -.. code-block:: python + .. code-block:: python - >>> from qiskit.opflow import PauliSumOp - >>> from qiskit.quantum_info import SparsePauliOp, Pauli + >>> from qiskit.opflow import PauliSumOp + >>> from qiskit.quantum_info import SparsePauliOp, Pauli - >>> qubit_op = PauliSumOp(SparsePauliOp(Pauli("XYZY"), coeffs=[2]), coeff=-3j) - >>> qubit_op - PauliSumOp(SparsePauliOp(['XYZY'], - coeffs=[2.+0.j]), coeff=(-0-3j)) + >>> qubit_op = PauliSumOp(SparsePauliOp(Pauli("XYZY"), coeffs=[2]), coeff=-3j) + >>> qubit_op + PauliSumOp(SparsePauliOp(['XYZY'], + coeffs=[2.+0.j]), coeff=(-0-3j)) -**Alternative** + **Alternative** -.. code-block:: python - - >>> from qiskit.quantum_info import SparsePauliOp, Pauli + .. code-block:: python - >>> qubit_op = SparsePauliOp(Pauli("XYZY"), coeffs=[-6j]) - >>> qubit_op - SparsePauliOp(['XYZY'], - coeffs=[0.-6.j]) + >>> from qiskit.quantum_info import SparsePauliOp, Pauli -.. raw:: html + >>> qubit_op = SparsePauliOp(Pauli("XYZY"), coeffs=[-6j]) + >>> qubit_op + SparsePauliOp(['XYZY'], + coeffs=[0.-6.j]) -
.. _z2_sym: -.. raw:: html +.. dropdown:: Example 2: ``Z2Symmetries`` and ``TaperedPauliSumOp`` + :animate: fade-in-slide-down + + **Opflow** + + .. code-block:: python + + >>> from qiskit.opflow import PauliSumOp, 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(qubit_op.primitive, z2_symmetries) + + >>> print(z2_symmetries) + Z2 symmetries: + Symmetries: + ZZ + Single-Qubit Pauli X: + IX + Cliffords: + 0.7071067811865475 * ZZ + + 0.7071067811865475 * IX + Qubit index: + [0] + Tapering values: + - Possible values: [1], [-1] + + >>> tapered_op + TaperedPauliSumOp(SparsePauliOp(['II', 'IZ', 'ZI', 'ZZ', 'XX'], + coeffs=[-1.05370761+0.j, 0.39398368+0.j, -0.39398368+0.j, -0.01123659+0.j, + 0.18128881+0.j]), coeff=1.0) + + **Alternative** + + .. code-block:: python + + >>> from qiskit.quantum_info import SparsePauliOp + >>> from qiskit.quantum_info.analysis import 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) + + >>> print(z2_symmetries) + Z2 symmetries: + Symmetries: + ZZ + Single-Qubit Pauli X: + IX + Cliffords: + SparsePauliOp(['ZZ', 'IX'], + coeffs=[0.70710678+0.j, 0.70710678+0.j]) + Qubit index: + [0] + Tapering values: + - Possible values: [1], [-1] + + >>> tapered_op + [SparsePauliOp(['I', 'X'], + coeffs=[-1.06494419+0.j, 0.18128881+0.j]), SparsePauliOp(['I', 'Z', 'X'], + coeffs=[-1.04247102+0.j, -0.78796736+0.j, -0.18128881+0.j])] -
- Example 2: Z2Symmetries and TaperedPauliSumOp -
- -**Opflow** - -.. code-block:: python - - >>> from qiskit.opflow import PauliSumOp, 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(qubit_op.primitive, z2_symmetries) - - >>> print(z2_symmetries) - Z2 symmetries: - Symmetries: - ZZ - Single-Qubit Pauli X: - IX - Cliffords: - 0.7071067811865475 * ZZ - + 0.7071067811865475 * IX - Qubit index: - [0] - Tapering values: - - Possible values: [1], [-1] - - >>> tapered_op - TaperedPauliSumOp(SparsePauliOp(['II', 'IZ', 'ZI', 'ZZ', 'XX'], - coeffs=[-1.05370761+0.j, 0.39398368+0.j, -0.39398368+0.j, -0.01123659+0.j, - 0.18128881+0.j]), coeff=1.0) - -**Alternative** - -.. code-block:: python - - >>> from qiskit.quantum_info import SparsePauliOp - >>> from qiskit.quantum_info.analysis import 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) - - >>> print(z2_symmetries) - Z2 symmetries: - Symmetries: - ZZ - Single-Qubit Pauli X: - IX - Cliffords: - SparsePauliOp(['ZZ', 'IX'], - coeffs=[0.70710678+0.j, 0.70710678+0.j]) - Qubit index: - [0] - Tapering values: - - Possible values: [1], [-1] - - >>> tapered_op - [SparsePauliOp(['I', 'X'], - coeffs=[-1.06494419+0.j, 0.18128881+0.j]), SparsePauliOp(['I', 'Z', 'X'], - coeffs=[-1.04247102+0.j, -0.78796736+0.j, -0.18128881+0.j])] - -.. raw:: html - -
ListOps -------- @@ -721,79 +682,70 @@ identify the sub-class that is being used, to then look for an alternative. Functionality now covered by :class:`.SamplingVQE`. See example in `Expectations`_. +.. dropdown:: Example 1: Applying an operator to a state + :animate: fade-in-slide-down -.. raw:: html - -
- Example 1: Applying an operator to a state -
+ **Opflow** -**Opflow** + .. code-block:: python -.. code-block:: python - - >>> from qiskit.opflow import StateFn, X, Y + >>> from qiskit.opflow import StateFn, X, Y - >>> qc = QuantumCircuit(2) - >>> qc.x(0) - >>> qc.z(1) - >>> op = X ^ Y - >>> state = StateFn(qc) + >>> qc = QuantumCircuit(2) + >>> qc.x(0) + >>> qc.z(1) + >>> op = X ^ Y + >>> state = StateFn(qc) - >>> comp = ~op @ state - >>> eval = comp.eval() + >>> comp = ~op @ state + >>> eval = comp.eval() - >>> print(state) - CircuitStateFn( - ┌───┐ - q_0: ┤ X ├ - ├───┤ - q_1: ┤ Z ├ - └───┘ - ) + >>> print(state) + CircuitStateFn( + ┌───┐ + q_0: ┤ X ├ + ├───┤ + q_1: ┤ Z ├ + └───┘ + ) - >>> print(comp) - CircuitStateFn( - ┌───┐┌────────────┐ - q_0: ┤ X ├┤0 ├ - ├───┤│ Pauli(XY) │ - q_1: ┤ Z ├┤1 ├ - └───┘└────────────┘ - ) + >>> print(comp) + CircuitStateFn( + ┌───┐┌────────────┐ + q_0: ┤ X ├┤0 ├ + ├───┤│ Pauli(XY) │ + q_1: ┤ Z ├┤1 ├ + └───┘└────────────┘ + ) - >>> print(eval) - VectorStateFn(Statevector([ 0.0e+00+0.j, 0.0e+00+0.j, -6.1e-17-1.j, 0.0e+00+0.j], - dims=(2, 2))) + >>> print(eval) + VectorStateFn(Statevector([ 0.0e+00+0.j, 0.0e+00+0.j, -6.1e-17-1.j, 0.0e+00+0.j], + dims=(2, 2))) -**Alternative** + **Alternative** -.. code-block:: python + .. code-block:: python - >>> from qiskit import QuantumCircuit - >>> from qiskit.quantum_info import SparsePauliOp, Statevector + >>> from qiskit import QuantumCircuit + >>> from qiskit.quantum_info import SparsePauliOp, Statevector - >>> qc = QuantumCircuit(2) - >>> qc.x(0) - >>> qc.z(1) - >>> op = SparsePauliOp("XY") - >>> state = Statevector(qc) + >>> qc = QuantumCircuit(2) + >>> qc.x(0) + >>> qc.z(1) + >>> op = SparsePauliOp("XY") + >>> state = Statevector(qc) - >>> print(state) - Statevector([0.+0.j, 1.+0.j, 0.+0.j, 0.+0.j], - dims=(2, 2)) + >>> print(state) + Statevector([0.+0.j, 1.+0.j, 0.+0.j, 0.+0.j], + dims=(2, 2)) - >>> eval = state.evolve(op) + >>> eval = state.evolve(op) - >>> print(eval) - Statevector([0.+0.j, 0.+0.j, 0.-1.j, 0.+0.j], - dims=(2, 2)) - -.. raw:: html - -
-
+ >>> print(eval) + Statevector([0.+0.j, 0.+0.j, 0.-1.j, 0.+0.j], + dims=(2, 2)) See more applied examples in `Expectations`_ and `Converters`_. @@ -840,171 +792,151 @@ Notably, this functionality has been replaced by the :mod:`~qiskit.primitives`. .. _convert_state: -.. raw:: html - -
- Example 1: CircuitSampler for sampling parametrized circuits -
- -**Opflow** - -.. code-block:: python - - >>> from qiskit.circuit import QuantumCircuit, Parameter - >>> from qiskit.opflow import ListOp, StateFn, CircuitSampler - >>> from qiskit_aer import AerSimulator +.. dropdown:: Example 1: ``CircuitSampler`` for sampling parametrized circuits + :animate: fade-in-slide-down - >>> x, y = Parameter("x"), Parameter("y") + **Opflow** - >>> circuit1 = QuantumCircuit(1) - >>> circuit1.p(0.2, 0) - >>> circuit2 = QuantumCircuit(1) - >>> circuit2.p(x, 0) - >>> circuit3 = QuantumCircuit(1) - >>> circuit3.p(y, 0) + .. code-block:: python - >>> bindings = {x: -0.4, y: 0.4} - >>> listop = ListOp([StateFn(circuit) for circuit in [circuit1, circuit2, circuit3]]) + >>> from qiskit.circuit import QuantumCircuit, Parameter + >>> from qiskit.opflow import ListOp, StateFn, CircuitSampler + >>> from qiskit_aer import AerSimulator - >>> sampler = CircuitSampler(AerSimulator()) - >>> sampled = sampler.convert(listop, params=bindings).eval() + >>> x, y = Parameter("x"), Parameter("y") - >>> for s in sampled: - ... print(s) - SparseVectorStateFn( (0, 0) 1.0) - SparseVectorStateFn( (0, 0) 1.0) - SparseVectorStateFn( (0, 0) 1.0) + >>> circuit1 = QuantumCircuit(1) + >>> circuit1.p(0.2, 0) + >>> circuit2 = QuantumCircuit(1) + >>> circuit2.p(x, 0) + >>> circuit3 = QuantumCircuit(1) + >>> circuit3.p(y, 0) -**Alternative** + >>> bindings = {x: -0.4, y: 0.4} + >>> listop = ListOp([StateFn(circuit) for circuit in [circuit1, circuit2, circuit3]]) -.. code-block:: python + >>> sampler = CircuitSampler(AerSimulator()) + >>> sampled = sampler.convert(listop, params=bindings).eval() - >>> from qiskit.circuit import QuantumCircuit, Parameter - >>> from qiskit.primitives import Sampler + >>> for s in sampled: + ... print(s) + SparseVectorStateFn( (0, 0) 1.0) + SparseVectorStateFn( (0, 0) 1.0) + SparseVectorStateFn( (0, 0) 1.0) - >>> x, y = Parameter("x"), Parameter("y") + **Alternative** - >>> circuit1 = QuantumCircuit(1) - >>> circuit1.p(0.2, 0) - >>> circuit1.measure_all() # Sampler primitive requires measurement readout - >>> circuit2 = QuantumCircuit(1) - >>> circuit2.p(x, 0) - >>> circuit2.measure_all() - >>> circuit3 = QuantumCircuit(1) - >>> circuit3.p(y, 0) - >>> circuit3.measure_all() + .. code-block:: python - >>> circuits = [circuit1, circuit2, circuit3] - >>> param_values = [[], [-0.4], [0.4]] + >>> from qiskit.circuit import QuantumCircuit, Parameter + >>> from qiskit.primitives import Sampler - >>> sampler = Sampler() - >>> sampled = sampler.run(circuits, param_values).result().quasi_dists + >>> x, y = Parameter("x"), Parameter("y") - >>> print(sampled) - [{0: 1.0}, {0: 1.0}, {0: 1.0}] + >>> circuit1 = QuantumCircuit(1) + >>> circuit1.p(0.2, 0) + >>> circuit1.measure_all() # Sampler primitive requires measurement readout + >>> circuit2 = QuantumCircuit(1) + >>> circuit2.p(x, 0) + >>> circuit2.measure_all() + >>> circuit3 = QuantumCircuit(1) + >>> circuit3.p(y, 0) + >>> circuit3.measure_all() -.. raw:: html + >>> circuits = [circuit1, circuit2, circuit3] + >>> param_values = [[], [-0.4], [0.4]] -
+ >>> sampler = Sampler() + >>> sampled = sampler.run(circuits, param_values).result().quasi_dists + >>> print(sampled) + [{0: 1.0}, {0: 1.0}, {0: 1.0}] -.. raw:: html -
- Example 2: CircuitSampler for computing expectation values -
+.. dropdown:: Example 2: ``CircuitSampler`` for computing expectation values + :animate: fade-in-slide-down -**Opflow** + **Opflow** -.. code-block:: python + .. code-block:: python - >>> from qiskit import QuantumCircuit - >>> from qiskit.opflow import X, Z, StateFn, CircuitStateFn, CircuitSampler - >>> from qiskit_aer import AerSimulator + >>> from qiskit import QuantumCircuit + >>> from qiskit.opflow import X, Z, StateFn, CircuitStateFn, CircuitSampler + >>> from qiskit_aer import AerSimulator - >>> qc = QuantumCircuit(1) - >>> qc.h(0) - >>> state = CircuitStateFn(qc) - >>> hamiltonian = X + Z + >>> qc = QuantumCircuit(1) + >>> qc.h(0) + >>> state = CircuitStateFn(qc) + >>> hamiltonian = X + Z - >>> expr = StateFn(hamiltonian, is_measurement=True).compose(state) - >>> backend = AerSimulator(method="statevector") - >>> sampler = CircuitSampler(backend) - >>> expectation = sampler.convert(expr) - >>> expectation_value = expectation.eval().real + >>> expr = StateFn(hamiltonian, is_measurement=True).compose(state) + >>> backend = AerSimulator(method="statevector") + >>> sampler = CircuitSampler(backend) + >>> expectation = sampler.convert(expr) + >>> expectation_value = expectation.eval().real - >>> expectation_value - 1.0000000000000002 + >>> expectation_value + 1.0000000000000002 -**Alternative** + **Alternative** -.. code-block:: python + .. code-block:: python - >>> from qiskit import QuantumCircuit - >>> from qiskit.primitives import Estimator - >>> from qiskit.quantum_info import SparsePauliOp + >>> 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)]) + >>> state = QuantumCircuit(1) + >>> state.h(0) + >>> hamiltonian = SparsePauliOp.from_list([('X', 1), ('Z',1)]) - >>> estimator = Estimator() - >>> expectation_value = estimator.run(state, hamiltonian).result().values.real + >>> estimator = Estimator() + >>> expectation_value = estimator.run(state, hamiltonian).result().values.real - >>> expectation_value - array([1.]) + >>> expectation_value + array([1.]) -.. raw:: html -
+.. dropdown:: Example 3: ``AbelianGrouper`` for grouping operators + :animate: fade-in-slide-down -.. raw:: html + **Opflow** -
- Example 3: AbelianGrouper for grouping operators -
+ .. code-block:: python -**Opflow** + >>> from qiskit.opflow import PauliSumOp, AbelianGrouper -.. code-block:: python + >>> op = PauliSumOp.from_list([("XX", 2), ("YY", 1), ("IZ",2j), ("ZZ",1j)]) - >>> from qiskit.opflow import PauliSumOp, AbelianGrouper + >>> grouped_sum = AbelianGrouper.group_subops(op) - >>> op = PauliSumOp.from_list([("XX", 2), ("YY", 1), ("IZ",2j), ("ZZ",1j)]) + >>> grouped_sum + SummedOp([PauliSumOp(SparsePauliOp(['XX'], + coeffs=[2.+0.j]), coeff=1.0), PauliSumOp(SparsePauliOp(['YY'], + coeffs=[1.+0.j]), coeff=1.0), PauliSumOp(SparsePauliOp(['IZ', 'ZZ'], + coeffs=[0.+2.j, 0.+1.j]), coeff=1.0)], coeff=1.0, abelian=False) - >>> grouped_sum = AbelianGrouper.group_subops(op) + **Alternative** - >>> grouped_sum - SummedOp([PauliSumOp(SparsePauliOp(['XX'], - coeffs=[2.+0.j]), coeff=1.0), PauliSumOp(SparsePauliOp(['YY'], - coeffs=[1.+0.j]), coeff=1.0), PauliSumOp(SparsePauliOp(['IZ', 'ZZ'], - coeffs=[0.+2.j, 0.+1.j]), coeff=1.0)], coeff=1.0, abelian=False) + .. code-block:: python -**Alternative** + >>> from qiskit.quantum_info import SparsePauliOp -.. code-block:: python + >>> op = SparsePauliOp.from_list([("XX", 2), ("YY", 1), ("IZ",2j), ("ZZ",1j)]) - >>> from qiskit.quantum_info import SparsePauliOp + >>> grouped = op.group_commuting() + >>> grouped_sum = op.group_commuting(qubit_wise=True) - >>> op = SparsePauliOp.from_list([("XX", 2), ("YY", 1), ("IZ",2j), ("ZZ",1j)]) + >>> grouped + [SparsePauliOp(['IZ', 'ZZ'], + coeffs=[0.+2.j, 0.+1.j]), SparsePauliOp(['XX', 'YY'], + coeffs=[2.+0.j, 1.+0.j])] - >>> grouped = op.group_commuting() - >>> grouped_sum = op.group_commuting(qubit_wise=True) - - >>> grouped - [SparsePauliOp(['IZ', 'ZZ'], - coeffs=[0.+2.j, 0.+1.j]), SparsePauliOp(['XX', 'YY'], - coeffs=[2.+0.j, 1.+0.j])] - - >>> grouped_sum - [SparsePauliOp(['XX'], - coeffs=[2.+0.j]), SparsePauliOp(['YY'], - coeffs=[1.+0.j]), SparsePauliOp(['IZ', 'ZZ'], - coeffs=[0.+2.j, 0.+1.j])] - -.. raw:: html - -
+ >>> grouped_sum + [SparsePauliOp(['XX'], + coeffs=[2.+0.j]), SparsePauliOp(['YY'], + coeffs=[1.+0.j]), SparsePauliOp(['IZ', 'ZZ'], + coeffs=[0.+2.j, 0.+1.j])] Evolutions ========== @@ -1083,145 +1015,124 @@ Other Evolution Classes - :class:`.PauliEvolutionGate` +.. dropdown:: Example 1: Trotter evolution + :animate: fade-in-slide-down -.. raw:: html + **Opflow** -
- Example 1: Trotter evolution -
+ .. code-block:: python -**Opflow** + >>> from qiskit.opflow import Trotter, PauliTrotterEvolution, PauliSumOp -.. code-block:: python + >>> hamiltonian = PauliSumOp.from_list([('X', 1), ('Z',1)]) + >>> evolution = PauliTrotterEvolution(trotter_mode=Trotter(), reps=2) + >>> evol_result = evolution.convert(hamiltonian.exp_i()) + >>> evolved_state = evol_result.to_circuit() - >>> from qiskit.opflow import Trotter, PauliTrotterEvolution, PauliSumOp + >>> print(evolved_state) + ┌─────────────────────┐ + q: ┤ exp(-it (X + Z))(1) ├ + └─────────────────────┘ - >>> hamiltonian = PauliSumOp.from_list([('X', 1), ('Z',1)]) - >>> evolution = PauliTrotterEvolution(trotter_mode=Trotter(), reps=2) - >>> evol_result = evolution.convert(hamiltonian.exp_i()) - >>> evolved_state = evol_result.to_circuit() + **Alternative** - >>> print(evolved_state) - ┌─────────────────────┐ - q: ┤ exp(-it (X + Z))(1) ├ - └─────────────────────┘ + .. code-block:: python -**Alternative** + >>> from qiskit import QuantumCircuit + >>> from qiskit.quantum_info import SparsePauliOp + >>> from qiskit.circuit.library import PauliEvolutionGate + >>> from qiskit.synthesis import SuzukiTrotter -.. code-block:: python + >>> hamiltonian = SparsePauliOp.from_list([('X', 1), ('Z',1)]) + >>> evol_gate = PauliEvolutionGate(hamiltonian, time=1, synthesis=SuzukiTrotter(reps=2)) + >>> evolved_state = QuantumCircuit(1) + >>> evolved_state.append(evol_gate, [0]) - >>> from qiskit import QuantumCircuit - >>> from qiskit.quantum_info import SparsePauliOp - >>> from qiskit.circuit.library import PauliEvolutionGate - >>> from qiskit.synthesis import SuzukiTrotter + >>> print(evolved_state) + ┌─────────────────────┐ + q: ┤ exp(-it (X + Z))(1) ├ + └─────────────────────┘ - >>> hamiltonian = SparsePauliOp.from_list([('X', 1), ('Z',1)]) - >>> evol_gate = PauliEvolutionGate(hamiltonian, time=1, synthesis=SuzukiTrotter(reps=2)) - >>> evolved_state = QuantumCircuit(1) - >>> evolved_state.append(evol_gate, [0]) +.. dropdown:: Example 2: Evolution with time-dependent Hamiltonian + :animate: fade-in-slide-down - >>> print(evolved_state) - ┌─────────────────────┐ - q: ┤ exp(-it (X + Z))(1) ├ - └─────────────────────┘ + **Opflow** -.. raw:: html + .. code-block:: python -
+ >>> from qiskit.opflow import Trotter, PauliTrotterEvolution, PauliSumOp + >>> from qiskit.circuit import Parameter + >>> time = Parameter('t') + >>> hamiltonian = PauliSumOp.from_list([('X', 1), ('Y',1)]) + >>> evolution = PauliTrotterEvolution(trotter_mode=Trotter(), reps=1) + >>> evol_result = evolution.convert((time * hamiltonian).exp_i()) + >>> evolved_state = evol_result.to_circuit() -.. raw:: html + >>> print(evolved_state) + ┌─────────────────────────┐ + q: ┤ exp(-it (X + Y))(1.0*t) ├ + └─────────────────────────┘ -
- Example 2: Evolution with time-dependent Hamiltonian -
+ **Alternative** -**Opflow** + .. code-block:: python -.. code-block:: python + >>> from qiskit.quantum_info import SparsePauliOp + >>> from qiskit.synthesis import LieTrotter + >>> from qiskit.circuit.library import PauliEvolutionGate + >>> from qiskit import QuantumCircuit + >>> from qiskit.circuit import Parameter - >>> from qiskit.opflow import Trotter, PauliTrotterEvolution, PauliSumOp - >>> from qiskit.circuit import Parameter + >>> time = Parameter('t') + >>> hamiltonian = SparsePauliOp.from_list([('X', 1), ('Y',1)]) + >>> evol_gate = PauliEvolutionGate(hamiltonian, time=time, synthesis=LieTrotter()) + >>> evolved_state = QuantumCircuit(1) + >>> evolved_state.append(evol_gate, [0]) - >>> time = Parameter('t') - >>> hamiltonian = PauliSumOp.from_list([('X', 1), ('Y',1)]) - >>> evolution = PauliTrotterEvolution(trotter_mode=Trotter(), reps=1) - >>> evol_result = evolution.convert((time * hamiltonian).exp_i()) - >>> evolved_state = evol_result.to_circuit() + >>> print(evolved_state) + ┌─────────────────────┐ + q: ┤ exp(-it (X + Y))(t) ├ + └─────────────────────┘ - >>> print(evolved_state) - ┌─────────────────────────┐ - q: ┤ exp(-it (X + Y))(1.0*t) ├ - └─────────────────────────┘ -**Alternative** +.. dropdown:: Example 3: Matrix evolution + :animate: fade-in-slide-down -.. code-block:: python - - >>> from qiskit.quantum_info import SparsePauliOp - >>> from qiskit.synthesis import LieTrotter - >>> from qiskit.circuit.library import PauliEvolutionGate - >>> from qiskit import QuantumCircuit - >>> from qiskit.circuit import Parameter - - >>> time = Parameter('t') - >>> hamiltonian = SparsePauliOp.from_list([('X', 1), ('Y',1)]) - >>> evol_gate = PauliEvolutionGate(hamiltonian, time=time, synthesis=LieTrotter()) - >>> evolved_state = QuantumCircuit(1) - >>> evolved_state.append(evol_gate, [0]) - >>> print(evolved_state) - ┌─────────────────────┐ - q: ┤ exp(-it (X + Y))(t) ├ - └─────────────────────┘ + **Opflow** -.. raw:: html + .. 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() -.. raw:: html + >>> print(evolved_state.decompose().decompose()) + ┌────────────────┐ + q: ┤ U3(2,-π/2,π/2) ├ + └────────────────┘ -
- Example 3: Matrix evolution -
+ **Alternative** -**Opflow** + .. code-block:: python -.. code-block:: python - - >>> from qiskit.opflow import MatrixEvolution, MatrixOp + >>> from qiskit.quantum_info import SparsePauliOp + >>> from qiskit.extensions import HamiltonianGate + >>> from qiskit import QuantumCircuit - >>> hamiltonian = MatrixOp([[0, 1], [1, 0]]) - >>> evolution = MatrixEvolution() - >>> evol_result = evolution.convert(hamiltonian.exp_i()) - >>> evolved_state = evol_result.to_circuit() + >>> evol_gate = HamiltonianGate([[0, 1], [1, 0]], 1) + >>> evolved_state = QuantumCircuit(1) + >>> evolved_state.append(evol_gate, [0]) - >>> print(evolved_state.decompose().decompose()) - ┌────────────────┐ - q: ┤ U3(2,-π/2,π/2) ├ - └────────────────┘ + >>> print(evolved_state.decompose().decompose()) + ┌────────────────┐ + q: ┤ U3(2,-π/2,π/2) ├ + └────────────────┘ -**Alternative** - -.. 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]) - - >>> print(evolved_state.decompose().decompose()) - ┌────────────────┐ - q: ┤ U3(2,-π/2,π/2) ├ - └────────────────┘ - -.. raw:: html - -
Expectations ============ @@ -1258,117 +1169,103 @@ Algorithm-Agnostic Expectations .. _expect_state: +.. dropdown:: Example 1: Aer Pauli expectation + :animate: fade-in-slide-down -.. raw:: html + **Opflow** -
- Example 1: Aer Pauli expectation -
+ .. code-block:: python -**Opflow** + >>> from qiskit.opflow import X, Minus, StateFn, AerPauliExpectation, CircuitSampler + >>> from qiskit.utils import QuantumInstance + >>> from qiskit_aer import AerSimulator -.. code-block:: python - - >>> from qiskit.opflow import X, Minus, StateFn, AerPauliExpectation, CircuitSampler - >>> from qiskit.utils import QuantumInstance - >>> from qiskit_aer import AerSimulator - - >>> backend = AerSimulator() - >>> q_instance = QuantumInstance(backend) + >>> backend = AerSimulator() + >>> q_instance = QuantumInstance(backend) - >>> sampler = CircuitSampler(q_instance, attach_results=True) - >>> expectation = AerPauliExpectation() + >>> sampler = CircuitSampler(q_instance, attach_results=True) + >>> expectation = AerPauliExpectation() - >>> state = Minus - >>> operator = 1j * X + >>> state = Minus + >>> operator = 1j * X - >>> converted_meas = expectation.convert(StateFn(operator, is_measurement=True) @ state) - >>> expectation_value = sampler.convert(converted_meas).eval() + >>> converted_meas = expectation.convert(StateFn(operator, is_measurement=True) @ state) + >>> expectation_value = sampler.convert(converted_meas).eval() - >>> print(expectation_value) - -1j + >>> print(expectation_value) + -1j -**Alternative** + **Alternative** -.. code-block:: python - - >>> from qiskit.quantum_info import SparsePauliOp - >>> from qiskit import QuantumCircuit - >>> from qiskit_aer.primitives import Estimator + .. code-block:: python - >>> estimator = AerEstimator(run_options={"approximation": True, "shots": None}) + >>> from qiskit.quantum_info import SparsePauliOp + >>> from qiskit import QuantumCircuit + >>> from qiskit_aer.primitives import Estimator - >>> op = SparsePauliOp.from_list([("X", 1j)]) - >>> states_op = QuantumCircuit(1) - >>> states_op.x(0) - >>> states_op.h(0) + >>> estimator = AerEstimator(run_options={"approximation": True, "shots": None}) - >>> expectation_value = estimator.run(states_op, op).result().values + >>> op = SparsePauliOp.from_list([("X", 1j)]) + >>> states_op = QuantumCircuit(1) + >>> states_op.x(0) + >>> states_op.h(0) - >>> print(expectation_value) - [0.-1.j] + >>> expectation_value = estimator.run(states_op, op).result().values -.. raw:: html + >>> print(expectation_value) + [0.-1.j] -
.. _matrix_state: +.. dropdown:: Example 2: Matrix expectation + :animate: fade-in-slide-down -.. raw:: html - -
- Example 2: Matrix expectation -
+ **Opflow** -**Opflow** + .. code-block:: python -.. code-block:: python - - >>> from qiskit.opflow import X, H, I, MatrixExpectation, ListOp, StateFn - >>> from qiskit.utils import QuantumInstance - >>> from qiskit_aer import AerSimulator + >>> from qiskit.opflow import X, H, I, MatrixExpectation, ListOp, StateFn + >>> from qiskit.utils import QuantumInstance + >>> from qiskit_aer import AerSimulator - >>> backend = AerSimulator(method='statevector') - >>> q_instance = QuantumInstance(backend) - >>> sampler = CircuitSampler(q_instance, attach_results=True) - >>> expect = MatrixExpectation() + >>> backend = AerSimulator(method='statevector') + >>> q_instance = QuantumInstance(backend) + >>> sampler = CircuitSampler(q_instance, attach_results=True) + >>> expect = MatrixExpectation() - >>> mixed_ops = ListOp([X.to_matrix_op(), H]) - >>> converted_meas = expect.convert(~StateFn(mixed_ops)) + >>> mixed_ops = ListOp([X.to_matrix_op(), H]) + >>> converted_meas = expect.convert(~StateFn(mixed_ops)) - >>> plus_mean = converted_meas @ Plus - >>> values_plus = sampler.convert(plus_mean).eval() + >>> plus_mean = converted_meas @ Plus + >>> values_plus = sampler.convert(plus_mean).eval() - >>> values_plus - [(1+0j), (0.7071067811865476+0j)] + >>> values_plus + [(1+0j), (0.7071067811865476+0j)] -**Alternative** - -.. code-block:: python + **Alternative** - >>> from qiskit.primitives import Estimator - >>> from qiskit.quantum_info import SparsePauliOp - >>> from qiskit.quantum_info import Clifford + .. code-block:: python - >>> X = SparsePauliOp("X") + >>> from qiskit.primitives import Estimator + >>> from qiskit.quantum_info import SparsePauliOp + >>> from qiskit.quantum_info import Clifford - >>> qc = QuantumCircuit(1) - >>> qc.h(0) - >>> H = Clifford(qc).to_operator() + >>> X = SparsePauliOp("X") - >>> plus = QuantumCircuit(1) - >>> plus.h(0) + >>> qc = QuantumCircuit(1) + >>> qc.h(0) + >>> H = Clifford(qc).to_operator() - >>> estimator = Estimator() - >>> values_plus = estimator.run([plus, plus], [X, H]).result().values + >>> plus = QuantumCircuit(1) + >>> plus.h(0) - >>> values_plus - array([1. , 0.70710678]) + >>> estimator = Estimator() + >>> values_plus = estimator.run([plus, plus], [X, H]).result().values -.. raw:: html + >>> values_plus + array([1. , 0.70710678]) -
CVaRExpectation --------------- @@ -1385,61 +1282,53 @@ CVaRExpectation .. _cvar: +.. dropdown:: Example 1: VQE with CVaR + :animate: fade-in-slide-down -.. raw:: html + **Opflow** -
- Example 1: VQE with CVaR -
+ .. code-block:: python -**Opflow** + >>> from qiskit.opflow import CVaRExpectation, PauliSumOp -.. 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 - >>> from qiskit.algorithms import VQE - >>> from qiskit.algorithms.optimizers import SLSQP - >>> from qiskit.circuit.library import TwoLocal - >>> from qiskit_aer import AerSimulator + >>> backend = AerSimulator(method="statevector") + >>> 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) - >>> backend = AerSimulator(method="statevector") - >>> 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) + >>> print(result.eigenvalue) + (-1+0j) - >>> print(result.eigenvalue) - (-1+0j) + **Alternative** -**Alternative** + .. code-block:: python -.. code-block:: python + >>> from qiskit.quantum_info import SparsePauliOp - >>> 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 - >>> 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, opt, aggregation=alpha) + >>> result = vqe.compute_minimum_eigenvalue(op) - >>> 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, opt, aggregation=alpha) - >>> result = vqe.compute_minimum_eigenvalue(op) + >>> print(result.eigenvalue) + -1.0 - >>> print(result.eigenvalue) - -1.0 - - -.. raw:: html - -
Gradients ========= @@ -1508,11 +1397,8 @@ implementations: from qiskit.algorithms.gradients import MethodPrimitiveGradient, QFI - .. raw:: html - -
- Gradients -
+ .. dropdown:: Gradients + :animate: fade-in-slide-down .. list-table:: :header-rows: 1 @@ -1527,16 +1413,8 @@ implementations: * - ``Gradient(method="fin_diff")`` - ``FiniteDiffEstimatorGradient(estimator=estimator)`` or ``ParamShiftSamplerGradient(sampler=sampler)`` - - .. raw:: html - -
- - .. raw:: html - -
- QFI/QGT -
+ .. dropdown:: QFI/QGT + :animate: fade-in-slide-down .. list-table:: :header-rows: 1 @@ -1548,9 +1426,6 @@ implementations: - ``qgt=LinCombQGT(Estimator())`` ``QFI(qgt=qgt)`` - .. raw:: html - -
Other auxiliary classes in the legacy gradient framework have now been deprecated. Here is the complete migration list: @@ -1580,144 +1455,131 @@ list: * - :class:`~qiskit.opflow.gradients.NaturalGradient` - No replacement. The same functionality can be achieved with the QFI module. -.. raw:: html - -
- Example 1: Finite Differences Batched Gradient -
- -**Opflow** - -.. code-block:: python - - >>> from qiskit.circuit import Parameter, QuantumCircuit - >>> from qiskit.opflow import Gradient, X, Z, StateFn, CircuitStateFn - >>> import numpy as np - - >>> ham = 0.5 * X - 1 * Z +.. dropdown:: Example 1: Finite Differences Batched Gradient + :animate: fade-in-slide-down - >>> a = Parameter("a") - >>> b = Parameter("b") - >>> c = Parameter("c") - >>> params = [a,b,c] + **Opflow** - >>> qc = QuantumCircuit(1) - >>> qc.h(0) - >>> qc.u(a, b, c, 0) - >>> qc.h(0) + .. code-block:: python - >>> op = ~StateFn(ham) @ CircuitStateFn(primitive=qc, coeff=1.0) + >>> from qiskit.circuit import Parameter, QuantumCircuit + >>> from qiskit.opflow import Gradient, X, Z, StateFn, CircuitStateFn + >>> import numpy as np - # the gradient class acted similarly opflow converters, - # with a .convert() step and an .eval() step - >>> state_grad = Gradient(grad_method="param_shift").convert(operator=op, params=params) + >>> ham = 0.5 * X - 1 * Z - # the old workflow did not allow for batched evaluation of parameter values - >>> values_dict = [{a: np.pi / 4, b: 0, c: 0}, {a: np.pi / 4, b: np.pi / 4, c: np.pi / 4}] - >>> gradients = [] - >>> for i, value_dict in enumerate(values_dict): - ... gradients.append(state_grad.assign_parameters(value_dict).eval()) + >>> a = Parameter("a") + >>> b = Parameter("b") + >>> c = Parameter("c") + >>> params = [a,b,c] - >>> print(gradients) - [[(0.35355339059327356+0j), (-1.182555756156289e-16+0j), (-1.6675e-16+0j)], [(0.10355339059327384+0j), (0.8535533905932734+0j), (1.103553390593273+0j)]] + >>> qc = QuantumCircuit(1) + >>> qc.h(0) + >>> qc.u(a, b, c, 0) + >>> qc.h(0) -**Alternative** + >>> op = ~StateFn(ham) @ CircuitStateFn(primitive=qc, coeff=1.0) -.. code-block:: python + # the gradient class acted similarly opflow converters, + # with a .convert() step and an .eval() step + >>> state_grad = Gradient(grad_method="param_shift").convert(operator=op, params=params) - >>> from qiskit.circuit import Parameter, QuantumCircuit - >>> from qiskit.primitives import Estimator - >>> from qiskit.algorithms.gradients import ParamShiftEstimatorGradient - >>> from qiskit.quantum_info import SparsePauliOp + # the old workflow did not allow for batched evaluation of parameter values + >>> values_dict = [{a: np.pi / 4, b: 0, c: 0}, {a: np.pi / 4, b: np.pi / 4, c: np.pi / 4}] + >>> gradients = [] + >>> for i, value_dict in enumerate(values_dict): + ... gradients.append(state_grad.assign_parameters(value_dict).eval()) - >>> ham = SparsePauliOp.from_list([("X", 0.5), ("Z", -1)]) + >>> print(gradients) + [[(0.35355339059327356+0j), (-1.182555756156289e-16+0j), (-1.6675e-16+0j)], [(0.10355339059327384+0j), (0.8535533905932734+0j), (1.103553390593273+0j)]] - >>> a = Parameter("a") - >>> b = Parameter("b") - >>> c = Parameter("c") + **Alternative** - >>> qc = QuantumCircuit(1) - >>> qc.h(0) - >>> qc.u(a, b, c, 0) - >>> qc.h(0) + .. code-block:: python - >>> estimator = Estimator() - >>> gradient = ParamShiftEstimatorGradient(estimator) + >>> from qiskit.circuit import Parameter, QuantumCircuit + >>> from qiskit.primitives import Estimator + >>> from qiskit.algorithms.gradients import ParamShiftEstimatorGradient + >>> from qiskit.quantum_info import SparsePauliOp - # the new workflow follows an interface close to the primitives' - >>> param_list = [[np.pi / 4, 0, 0], [np.pi / 4, np.pi / 4, np.pi / 4]] + >>> ham = SparsePauliOp.from_list([("X", 0.5), ("Z", -1)]) - # for batched evaluations, the number of circuits must match the - # number of parameter value sets - >>> gradients = gradient.run([qc] * 2, [ham] * 2, param_list).result().gradients + >>> a = Parameter("a") + >>> b = Parameter("b") + >>> c = Parameter("c") - >>> print(gradients) - [array([ 3.53553391e-01, 0.00000000e+00, -1.80411242e-16]), array([0.10355339, 0.85355339, 1.10355339])] + >>> qc = QuantumCircuit(1) + >>> qc.h(0) + >>> qc.u(a, b, c, 0) + >>> qc.h(0) -.. raw:: html + >>> estimator = Estimator() + >>> gradient = ParamShiftEstimatorGradient(estimator) -
+ # the new workflow follows an interface close to the primitives' + >>> param_list = [[np.pi / 4, 0, 0], [np.pi / 4, np.pi / 4, np.pi / 4]] -.. raw:: html + # for batched evaluations, the number of circuits must match the + # number of parameter value sets + >>> gradients = gradient.run([qc] * 2, [ham] * 2, param_list).result().gradients -
- Example 2: QFI -
+ >>> print(gradients) + [array([ 3.53553391e-01, 0.00000000e+00, -1.80411242e-16]), array([0.10355339, 0.85355339, 1.10355339])] -**Opflow** -.. code-block:: python +.. dropdown:: Example 2: QFI + :animate: fade-in-slide-down - >>> from qiskit.circuit import Parameter, QuantumCircuit - >>> from qiskit.opflow import QFI, CircuitStateFn + **Opflow** - # create the circuit - >>> a, b = Parameter("a"), Parameter("b") - >>> qc = QuantumCircuit(1) - >>> qc.h(0) - >>> qc.rz(a, 0) - >>> qc.rx(b, 0) + .. code-block:: python - # convert the circuit to a QFI object - >>> op = CircuitStateFn(qc) - >>> qfi = QFI(qfi_method="lin_comb_full").convert(operator=op) + >>> from qiskit.circuit import Parameter, QuantumCircuit + >>> from qiskit.opflow import QFI, CircuitStateFn - # bind parameters and evaluate - >>> values_dict = {a: np.pi / 4, b: 0.1} - >>> qfi = qfi.bind_parameters(values_dict).eval() + # create the circuit + >>> a, b = Parameter("a"), Parameter("b") + >>> qc = QuantumCircuit(1) + >>> qc.h(0) + >>> qc.rz(a, 0) + >>> qc.rx(b, 0) - >>> print(qfi) - [[ 1.00000000e+00+0.j -3.63575685e-16+0.j] - [-3.63575685e-16+0.j 5.00000000e-01+0.j]] + # convert the circuit to a QFI object + >>> op = CircuitStateFn(qc) + >>> qfi = QFI(qfi_method="lin_comb_full").convert(operator=op) -**Alternative** + # bind parameters and evaluate + >>> values_dict = {a: np.pi / 4, b: 0.1} + >>> qfi = qfi.bind_parameters(values_dict).eval() -.. code-block:: python + >>> print(qfi) + [[ 1.00000000e+00+0.j -3.63575685e-16+0.j] + [-3.63575685e-16+0.j 5.00000000e-01+0.j]] - >>> from qiskit.circuit import Parameter, QuantumCircuit - >>> from qiskit.primitives import Estimator - >>> from qiskit.algorithms.gradients import LinCombQGT, QFI + **Alternative** - # create the circuit - >>> a, b = Parameter("a"), Parameter("b") - >>> qc = QuantumCircuit(1) - >>> qc.h(0) - >>> qc.rz(a, 0) - >>> qc.rx(b, 0) + .. code-block:: python - # initialize QFI - >>> estimator = Estimator() - >>> qgt = LinCombQGT(estimator) - >>> qfi = QFI(qgt) + >>> from qiskit.circuit import Parameter, QuantumCircuit + >>> from qiskit.primitives import Estimator + >>> from qiskit.algorithms.gradients import LinCombQGT, QFI - # evaluate - >>> values_list = [[np.pi / 4, 0.1]] - >>> qfi = qfi.run(qc, values_list).result().qfis + # create the circuit + >>> a, b = Parameter("a"), Parameter("b") + >>> qc = QuantumCircuit(1) + >>> qc.h(0) + >>> qc.rz(a, 0) + >>> qc.rx(b, 0) - >>> print(qfi) - [array([[ 1.00000000e+00, -1.50274614e-16], - [-1.50274614e-16, 5.00000000e-01]])] + # initialize QFI + >>> estimator = Estimator() + >>> qgt = LinCombQGT(estimator) + >>> qfi = QFI(qgt) -.. raw:: html + # evaluate + >>> values_list = [[np.pi / 4, 0.1]] + >>> qfi = qfi.run(qc, values_list).result().qfis -
+ >>> print(qfi) + [array([[ 1.00000000e+00, -1.50274614e-16], + [-1.50274614e-16, 5.00000000e-01]])] From 15c35e2d7e63645261406d69b2110d7c08c2d365 Mon Sep 17 00:00:00 2001 From: ElePT Date: Thu, 9 Mar 2023 10:26:36 +0100 Subject: [PATCH 38/50] Change doctest syntax, apply reviews --- docs/migration_guides/opflow_migration.rst | 998 +++++++++++---------- 1 file changed, 546 insertions(+), 452 deletions(-) diff --git a/docs/migration_guides/opflow_migration.rst b/docs/migration_guides/opflow_migration.rst index 31d91f50373..985ed31b4d6 100644 --- a/docs/migration_guides/opflow_migration.rst +++ b/docs/migration_guides/opflow_migration.rst @@ -173,80 +173,79 @@ directly creating a corresponding :class:`~qiskit.quantum_info.SparsePauliOp`, a **Opflow** - .. code-block:: python + .. testcode:: - >>> from qiskit.opflow import X + from qiskit.opflow import X - >>> operator = X ^ X + operator = X ^ X + print(repr(operator)) - .. code-block:: python + .. testoutput:: - >>> operator PauliOp(Pauli('XX'), coeff=1.0) **Alternative** - .. code-block:: python + .. testcode:: - >>> from qiskit.quantum_info import Pauli, SparsePauliOp + from qiskit.quantum_info import Pauli, SparsePauliOp - >>> X = Pauli('X') - >>> operator = X ^ X - >>> operator - Pauli('XX') + operator = Pauli('XX') # equivalent to: - >>> operator = Pauli('XX') - >>> operator - Pauli('XX') + X = Pauli('X') + operator = X ^ X + print("As Pauli Op: ", repr(operator)) - # equivalent to: - >>> operator = SparsePauliOp('XX') - >>> operator - SparsePauliOp(['XX'], + # another alternative is: + operator = SparsePauliOp('XX') + print("As Sparse Pauli Op: ", repr(operator)) + + .. testoutput:: + + As Pauli Op: Pauli('XX') + As Sparse Pauli Op: SparsePauliOp(['XX'], coeffs=[1.+0.j]) .. dropdown:: Example 2: Defining a more complex operator :animate: fade-in-slide-down - **Opflow** - .. code-block:: python + .. testcode:: - >>> from qiskit.opflow import I, X, Z, PauliSumOp + from qiskit.opflow import I, X, Z, PauliSumOp - >>> operator = 0.39 * (I ^ Z ^ I) + 0.5 * (I ^ X ^ X) - >>> operator - PauliSumOp(SparsePauliOp(['IZI', 'IXX'], - coeffs=[0.39+0.j, 0.5 +0.j]), coeff=1.0) + operator = 0.39 * (I ^ Z ^ I) + 0.5 * (I ^ X ^ X) + + # equivalent to: + operator = PauliSumOp.from_list([("IZI", 0.39), ("IXX", 0.5)]) + + print(repr(operator)) + + .. testoutput:: - # or ... - >>> operator = PauliSumOp.from_list([("IZI", 0.39), ("IXX", 0.5)]) - >>> operator PauliSumOp(SparsePauliOp(['IZI', 'IXX'], coeffs=[0.39+0.j, 0.5 +0.j]), coeff=1.0) **Alternative** - .. code-block:: python + .. testcode:: - >>> from qiskit.quantum_info import SparsePauliOp + from qiskit.quantum_info import SparsePauliOp - >>> operator = SparsePauliOp(["IZI", "IXX"], coeffs = [0.39, 0.5]) - >>> operator - SparsePauliOp(['IZI', 'IXX'], - coeffs=[0.39+0.j, 0.5 +0.j]) + operator = SparsePauliOp(["IZI", "IXX"], coeffs = [0.39, 0.5]) - # or... - >>> operator = SparsePauliOp.from_list([("IZI", 0.39), ("IXX", 0.5)]) - >>> operator - SparsePauliOp(['IZI', 'IXX'], - coeffs=[0.39+0.j, 0.5 +0.j]) + # equivalent to: + operator = SparsePauliOp.from_list([("IZI", 0.39), ("IXX", 0.5)]) + + # equivalent to: + operator = SparsePauliOp.from_sparse_list([("Z", [1], 0.39), ("XX", [0,1], 0.5)], num_qubits = 3) + + print(repr(operator)) + + .. testoutput:: - # or... - >>> operator = SparsePauliOp.from_sparse_list([("Z", [1], 0.39), ("XX", [0,1], 0.5)], num_qubits = 3) - >>> operator SparsePauliOp(['IZI', 'IXX'], coeffs=[0.39+0.j, 0.5 +0.j]) @@ -262,8 +261,8 @@ Common non-parametrized gates (Clifford) * - :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.circuit.QuantumCircuit`. - :class:`qiskit.quantum_info.Operator`\s can be also directly constructed from quantum circuits. + - Append corresponding gate to :class:`~qiskit.circuit.QuantumCircuit`. If necessary, + :class:`qiskit.quantum_info.Operator`\s can be directly constructed from quantum circuits. Another alternative is to wrap the circuit in :class:`~qiskit.quantum_info.Clifford` and call ``Clifford.to_operator()``. @@ -277,12 +276,15 @@ Common non-parametrized gates (Clifford) **Opflow** - .. code-block:: python + .. testcode:: - >>> from qiskit.opflow import H + from qiskit.opflow import H + + operator = H ^ H + print(operator) + + .. testoutput:: - >>> operator = H ^ H - >>> print(operator) ┌───┐ q_0: ┤ H ├ ├───┤ @@ -291,42 +293,43 @@ Common non-parametrized gates (Clifford) **Alternative** - .. code-block:: python + .. testcode:: - >>> from qiskit import QuantumCircuit - >>> from qiskit.quantum_info import Clifford, Operator + from qiskit import QuantumCircuit + from qiskit.quantum_info import Clifford, Operator - >>> qc = QuantumCircuit(2) - >>> qc.h(0) - >>> qc.h(1) - >>> operator = Clifford(qc).to_operator() - >>> operator - Operator([[ 0.5+0.j, 0.5+0.j, 0.5+0.j, 0.5+0.j], - [ 0.5+0.j, -0.5+0.j, 0.5+0.j, -0.5+0.j], - [ 0.5+0.j, 0.5+0.j, -0.5+0.j, -0.5+0.j], - [ 0.5+0.j, -0.5+0.j, -0.5+0.j, 0.5+0.j]], - input_dims=(2, 2), output_dims=(2, 2)) + qc = QuantumCircuit(2) + qc.h(0) + qc.h(1) + print(qc) + + .. testoutput:: + + ┌───┐ + q_0: ┤ H ├ + ├───┤ + q_1: ┤ H ├ + └───┘ + + If we want to turn this circuit into an operator, we can do: + + .. testcode:: + + operator = Clifford(qc).to_operator() # or, directly - >>> operator = Operator(qc) - >>> operator + operator = Operator(qc) + + print(operator) + + .. testoutput:: + Operator([[ 0.5+0.j, 0.5+0.j, 0.5+0.j, 0.5+0.j], [ 0.5+0.j, -0.5+0.j, 0.5+0.j, -0.5+0.j], [ 0.5+0.j, 0.5+0.j, -0.5+0.j, -0.5+0.j], [ 0.5+0.j, -0.5+0.j, -0.5+0.j, 0.5+0.j]], input_dims=(2, 2), output_dims=(2, 2)) - # or... - >>> qc = QuantumCircuit(1) - >>> qc.h(0) - >>> H = Clifford(qc).to_operator() - >>> operator = H ^ H - >>> operator - Operator([[ 0.5+0.j, 0.5+0.j, 0.5+0.j, 0.5+0.j], - [ 0.5+0.j, -0.5+0.j, 0.5+0.j, -0.5+0.j], - [ 0.5+0.j, 0.5+0.j, -0.5+0.j, -0.5+0.j], - [ 0.5+0.j, -0.5+0.j, -0.5+0.j, 0.5-0.j]], - input_dims=(2, 2), output_dims=(2, 2)) 1-Qubit States -------------- @@ -351,19 +354,21 @@ Common non-parametrized gates (Clifford) **Opflow** - .. code-block:: python + .. testcode:: - >>> from qiskit.opflow import Zero, One, Plus, Minus + from qiskit.opflow import Zero, One, Plus, Minus # Zero, One, Plus, Minus are all stabilizer states - >>> state1 = Zero ^ One - >>> state2 = Plus ^ Minus + state1 = Zero ^ One + state2 = Plus ^ Minus - >>> state1 - DictStateFn({'01': 1}, coeff=1.0, is_measurement=False) + print("State 1: ", state1) + print("State 2: ", state2) - >>> print(state2) - CircuitStateFn( + .. testoutput:: + + State 1: DictStateFn({'01': 1}) + State 2: CircuitStateFn( ┌───┐┌───┐ q_0: ┤ X ├┤ H ├ ├───┤└───┘ @@ -373,29 +378,29 @@ Common non-parametrized gates (Clifford) **Alternative** - .. code-block:: python + .. testcode:: - >>> from qiskit import QuantumCircuit - >>> from qiskit.quantum_info import StabilizerState, Statevector + from qiskit import QuantumCircuit + from qiskit.quantum_info import StabilizerState, Statevector - >>> qc_zero = QuantumCircuit(1) - >>> qc_one = qc_zero.copy() - >>> qc_one.x(0) - >>> state1 = Statevector(qc_zero) ^ Statevector(qc_one) + qc_zero = QuantumCircuit(1) + qc_one = qc_zero.copy() + qc_one.x(0) + state1 = Statevector(qc_zero) ^ Statevector(qc_one) + print("State 1: ", state1) - >>> qc_plus = qc_zero.copy() - >>> qc_plus.h(0) - >>> qc_minus = qc_one.copy() - >>> qc_minus.h(0) - >>> state2 = StabilizerState(qc_plus) ^ StabilizerState(qc_minus) + qc_plus = qc_zero.copy() + qc_plus.h(0) + qc_minus = qc_one.copy() + qc_minus.h(0) + state2 = StabilizerState(qc_plus) ^ StabilizerState(qc_minus) + print("State 2: ", state2) - >>> state1 - Statevector([0.+0.j, 1.+0.j, 0.+0.j, 0.+0.j], - dims=(2, 2)) - - >>> state2 - StabilizerState(StabilizerTable: ['-IX', '+XI']) + .. testoutput:: + State 1: Statevector([0.+0.j, 1.+0.j, 0.+0.j, 0.+0.j], + dims=(2, 2)) + State 2: StabilizerState(StabilizerTable: ['-IX', '+XI']) Primitive and List Ops ====================== @@ -479,28 +484,33 @@ are used "under the hood" in the original code: **Opflow** - .. code-block:: python + .. testcode:: - >>> from qiskit.opflow import PauliSumOp - >>> from qiskit.quantum_info import SparsePauliOp, Pauli + from qiskit.opflow import PauliSumOp + from qiskit.quantum_info import SparsePauliOp, Pauli + + qubit_op = PauliSumOp(SparsePauliOp(Pauli("XYZY"), coeffs=[2]), coeff=-3j) + print(repr(qubit_op)) + + .. testoutput:: - >>> qubit_op = PauliSumOp(SparsePauliOp(Pauli("XYZY"), coeffs=[2]), coeff=-3j) - >>> qubit_op PauliSumOp(SparsePauliOp(['XYZY'], coeffs=[2.+0.j]), coeff=(-0-3j)) **Alternative** - .. code-block:: python + .. testcode:: + + from qiskit.quantum_info import SparsePauliOp, Pauli - >>> from qiskit.quantum_info import SparsePauliOp, Pauli + qubit_op = SparsePauliOp(Pauli("XYZY"), coeffs=[-6j]) + print(repr(qubit_op)) + + .. testoutput:: - >>> qubit_op = SparsePauliOp(Pauli("XYZY"), coeffs=[-6j]) - >>> qubit_op SparsePauliOp(['XYZY'], coeffs=[0.-6.j]) - .. _z2_sym: .. dropdown:: Example 2: ``Z2Symmetries`` and ``TaperedPauliSumOp`` @@ -508,26 +518,31 @@ are used "under the hood" in the original code: **Opflow** - .. code-block:: python + .. testcode:: - >>> from qiskit.opflow import PauliSumOp, Z2Symmetries, TaperedPauliSumOp + from qiskit.opflow import PauliSumOp, 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) + print(z2_symmetries) - >>> 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) + tapered_op = z2_symmetries.taper(qubit_op) + print("Tapered Op from Z2 symmetries: ", tapered_op) # can be represented as: - >>> tapered_op = TaperedPauliSumOp(qubit_op.primitive, z2_symmetries) + tapered_op = TaperedPauliSumOp(qubit_op.primitive, z2_symmetries) + print("Tapered PauliSumOp: ", tapered_op) + + .. testoutput:: - >>> print(z2_symmetries) Z2 symmetries: Symmetries: ZZ @@ -540,32 +555,44 @@ are used "under the hood" in the original code: [0] Tapering values: - Possible values: [1], [-1] + Tapered Op from Z2 symmetries: ListOp([ + -1.0649441923622942 * I + + 0.18128880821149604 * X, + -1.0424710218959303 * I + - 0.7879673588770277 * Z + - 0.18128880821149604 * X + ]) + Tapered PauliSumOp: -1.0537076071291125 * II + + 0.393983679438514 * IZ + - 0.39398367943851387 * ZI + - 0.01123658523318205 * ZZ + + 0.1812888082114961 * XX - >>> tapered_op - TaperedPauliSumOp(SparsePauliOp(['II', 'IZ', 'ZI', 'ZZ', 'XX'], - coeffs=[-1.05370761+0.j, 0.39398368+0.j, -0.39398368+0.j, -0.01123659+0.j, - 0.18128881+0.j]), coeff=1.0) **Alternative** - .. code-block:: python + .. testcode:: + + from qiskit.quantum_info import SparsePauliOp + from qiskit.quantum_info.analysis import 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) + print(z2_symmetries) - >>> from qiskit.quantum_info import SparsePauliOp - >>> from qiskit.quantum_info.analysis import Z2Symmetries + tapered_op = z2_symmetries.taper(qubit_op) + print("Tapered Op from Z2 symmetries: ", tapered_op) - >>> 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) + .. testoutput:: - >>> print(z2_symmetries) Z2 symmetries: Symmetries: ZZ @@ -578,13 +605,10 @@ are used "under the hood" in the original code: [0] Tapering values: - Possible values: [1], [-1] - - >>> tapered_op - [SparsePauliOp(['I', 'X'], + Tapered Op from Z2 symmetries: [SparsePauliOp(['I', 'X'], coeffs=[-1.06494419+0.j, 0.18128881+0.j]), SparsePauliOp(['I', 'Z', 'X'], coeffs=[-1.04247102+0.j, -0.78796736+0.j, -0.18128881+0.j])] - ListOps -------- *Back to* `Contents`_ @@ -687,20 +711,26 @@ identify the sub-class that is being used, to then look for an alternative. **Opflow** - .. code-block:: python + .. testcode:: + + from qiskit.opflow import StateFn, X, Y + from qiskit import QuantumCircuit - >>> from qiskit.opflow import StateFn, X, Y + qc = QuantumCircuit(2) + qc.x(0) + qc.z(1) + op = X ^ Y + state = StateFn(qc) - >>> qc = QuantumCircuit(2) - >>> qc.x(0) - >>> qc.z(1) - >>> op = X ^ Y - >>> state = StateFn(qc) + comp = ~op @ state + eval = comp.eval() - >>> comp = ~op @ state - >>> eval = comp.eval() + print(state) + print(comp) + print(repr(eval)) + + .. testoutput:: - >>> print(state) CircuitStateFn( ┌───┐ q_0: ┤ X ├ @@ -708,9 +738,6 @@ identify the sub-class that is being used, to then look for an alternative. q_1: ┤ Z ├ └───┘ ) - - - >>> print(comp) CircuitStateFn( ┌───┐┌────────────┐ q_0: ┤ X ├┤0 ├ @@ -718,32 +745,31 @@ identify the sub-class that is being used, to then look for an alternative. q_1: ┤ Z ├┤1 ├ └───┘└────────────┘ ) - - - >>> print(eval) VectorStateFn(Statevector([ 0.0e+00+0.j, 0.0e+00+0.j, -6.1e-17-1.j, 0.0e+00+0.j], - dims=(2, 2))) + dims=(2, 2)), coeff=1.0, is_measurement=False) **Alternative** - .. code-block:: python + .. testcode:: - >>> from qiskit import QuantumCircuit - >>> from qiskit.quantum_info import SparsePauliOp, Statevector + from qiskit import QuantumCircuit + from qiskit.quantum_info import SparsePauliOp, Statevector - >>> qc = QuantumCircuit(2) - >>> qc.x(0) - >>> qc.z(1) - >>> op = SparsePauliOp("XY") - >>> state = Statevector(qc) + qc = QuantumCircuit(2) + qc.x(0) + qc.z(1) + op = SparsePauliOp("XY") + state = Statevector(qc) - >>> print(state) - Statevector([0.+0.j, 1.+0.j, 0.+0.j, 0.+0.j], - dims=(2, 2)) + eval = state.evolve(op) + + print(state) + print(eval) - >>> eval = state.evolve(op) + .. testoutput:: - >>> print(eval) + Statevector([0.+0.j, 1.+0.j, 0.+0.j, 0.+0.j], + dims=(2, 2)) Statevector([0.+0.j, 0.+0.j, 0.-1.j, 0.+0.j], dims=(2, 2)) @@ -797,59 +823,65 @@ Notably, this functionality has been replaced by the :mod:`~qiskit.primitives`. **Opflow** - .. code-block:: python + .. testcode:: + + from qiskit.circuit import QuantumCircuit, Parameter + from qiskit.opflow import ListOp, StateFn, CircuitSampler + from qiskit_aer import AerSimulator - >>> from qiskit.circuit import QuantumCircuit, Parameter - >>> from qiskit.opflow import ListOp, StateFn, CircuitSampler - >>> from qiskit_aer import AerSimulator + x, y = Parameter("x"), Parameter("y") - >>> x, y = Parameter("x"), Parameter("y") + circuit1 = QuantumCircuit(1) + circuit1.p(0.2, 0) + circuit2 = QuantumCircuit(1) + circuit2.p(x, 0) + circuit3 = QuantumCircuit(1) + circuit3.p(y, 0) - >>> circuit1 = QuantumCircuit(1) - >>> circuit1.p(0.2, 0) - >>> circuit2 = QuantumCircuit(1) - >>> circuit2.p(x, 0) - >>> circuit3 = QuantumCircuit(1) - >>> circuit3.p(y, 0) + bindings = {x: -0.4, y: 0.4} + listop = ListOp([StateFn(circuit) for circuit in [circuit1, circuit2, circuit3]]) - >>> bindings = {x: -0.4, y: 0.4} - >>> listop = ListOp([StateFn(circuit) for circuit in [circuit1, circuit2, circuit3]]) + sampler = CircuitSampler(AerSimulator()) + sampled = sampler.convert(listop, params=bindings).eval() - >>> sampler = CircuitSampler(AerSimulator()) - >>> sampled = sampler.convert(listop, params=bindings).eval() + for s in sampled: + print(s) - >>> for s in sampled: - ... print(s) - SparseVectorStateFn( (0, 0) 1.0) - SparseVectorStateFn( (0, 0) 1.0) - SparseVectorStateFn( (0, 0) 1.0) + .. testoutput:: + + SparseVectorStateFn( (0, 0) 1.0) + SparseVectorStateFn( (0, 0) 1.0) + SparseVectorStateFn( (0, 0) 1.0) **Alternative** - .. code-block:: python + .. testcode:: + + from qiskit.circuit import QuantumCircuit, Parameter + from qiskit.primitives import Sampler - >>> from qiskit.circuit import QuantumCircuit, Parameter - >>> from qiskit.primitives import Sampler + x, y = Parameter("x"), Parameter("y") - >>> x, y = Parameter("x"), Parameter("y") + circuit1 = QuantumCircuit(1) + circuit1.p(0.2, 0) + circuit1.measure_all() # Sampler primitive requires measurement readout + circuit2 = QuantumCircuit(1) + circuit2.p(x, 0) + circuit2.measure_all() + circuit3 = QuantumCircuit(1) + circuit3.p(y, 0) + circuit3.measure_all() - >>> circuit1 = QuantumCircuit(1) - >>> circuit1.p(0.2, 0) - >>> circuit1.measure_all() # Sampler primitive requires measurement readout - >>> circuit2 = QuantumCircuit(1) - >>> circuit2.p(x, 0) - >>> circuit2.measure_all() - >>> circuit3 = QuantumCircuit(1) - >>> circuit3.p(y, 0) - >>> circuit3.measure_all() + circuits = [circuit1, circuit2, circuit3] + param_values = [[], [-0.4], [0.4]] - >>> circuits = [circuit1, circuit2, circuit3] - >>> param_values = [[], [-0.4], [0.4]] + sampler = Sampler() + sampled = sampler.run(circuits, param_values).result().quasi_dists - >>> sampler = Sampler() - >>> sampled = sampler.run(circuits, param_values).result().quasi_dists + print(sampled) + + .. testoutput:: - >>> print(sampled) [{0: 1.0}, {0: 1.0}, {0: 1.0}] @@ -858,43 +890,49 @@ Notably, this functionality has been replaced by the :mod:`~qiskit.primitives`. **Opflow** - .. code-block:: python + .. testcode:: + + from qiskit import QuantumCircuit + from qiskit.opflow import X, Z, StateFn, CircuitStateFn, CircuitSampler + from qiskit_aer import AerSimulator - >>> from qiskit import QuantumCircuit - >>> from qiskit.opflow import X, Z, StateFn, CircuitStateFn, CircuitSampler - >>> from qiskit_aer import AerSimulator + qc = QuantumCircuit(1) + qc.h(0) + state = CircuitStateFn(qc) + hamiltonian = X + Z - >>> qc = QuantumCircuit(1) - >>> qc.h(0) - >>> state = CircuitStateFn(qc) - >>> hamiltonian = X + Z + expr = StateFn(hamiltonian, is_measurement=True).compose(state) + backend = AerSimulator(method="statevector") + sampler = CircuitSampler(backend) + expectation = sampler.convert(expr) + expectation_value = expectation.eval().real - >>> expr = StateFn(hamiltonian, is_measurement=True).compose(state) - >>> backend = AerSimulator(method="statevector") - >>> sampler = CircuitSampler(backend) - >>> expectation = sampler.convert(expr) - >>> expectation_value = expectation.eval().real + print(expectation_value) + + .. testoutput:: - >>> expectation_value 1.0000000000000002 **Alternative** - .. code-block:: python + .. testcode:: + + from qiskit import QuantumCircuit + from qiskit.primitives import Estimator + from qiskit.quantum_info import SparsePauliOp - >>> 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)]) - >>> state = QuantumCircuit(1) - >>> state.h(0) - >>> hamiltonian = SparsePauliOp.from_list([('X', 1), ('Z',1)]) + estimator = Estimator() + expectation_value = estimator.run(state, hamiltonian).result().values.real - >>> estimator = Estimator() - >>> expectation_value = estimator.run(state, hamiltonian).result().values.real + print(expectation_value) - >>> expectation_value - array([1.]) + .. testoutput:: + + [1.] .. dropdown:: Example 3: ``AbelianGrouper`` for grouping operators @@ -902,15 +940,18 @@ Notably, this functionality has been replaced by the :mod:`~qiskit.primitives`. **Opflow** - .. code-block:: python + .. testcode:: + + from qiskit.opflow import PauliSumOp, AbelianGrouper - >>> from qiskit.opflow import PauliSumOp, AbelianGrouper + op = PauliSumOp.from_list([("XX", 2), ("YY", 1), ("IZ",2j), ("ZZ",1j)]) - >>> op = PauliSumOp.from_list([("XX", 2), ("YY", 1), ("IZ",2j), ("ZZ",1j)]) + grouped_sum = AbelianGrouper.group_subops(op) - >>> grouped_sum = AbelianGrouper.group_subops(op) + print(repr(grouped_sum)) + + .. testoutput:: - >>> grouped_sum SummedOp([PauliSumOp(SparsePauliOp(['XX'], coeffs=[2.+0.j]), coeff=1.0), PauliSumOp(SparsePauliOp(['YY'], coeffs=[1.+0.j]), coeff=1.0), PauliSumOp(SparsePauliOp(['IZ', 'ZZ'], @@ -918,21 +959,23 @@ Notably, this functionality has been replaced by the :mod:`~qiskit.primitives`. **Alternative** - .. code-block:: python + .. testcode:: + + from qiskit.quantum_info import SparsePauliOp - >>> from qiskit.quantum_info import SparsePauliOp + op = SparsePauliOp.from_list([("XX", 2), ("YY", 1), ("IZ",2j), ("ZZ",1j)]) - >>> op = SparsePauliOp.from_list([("XX", 2), ("YY", 1), ("IZ",2j), ("ZZ",1j)]) + grouped = op.group_commuting() + grouped_sum = op.group_commuting(qubit_wise=True) - >>> grouped = op.group_commuting() - >>> grouped_sum = op.group_commuting(qubit_wise=True) + print(repr(grouped)) + print(repr(grouped_sum)) + + .. testoutput:: - >>> grouped [SparsePauliOp(['IZ', 'ZZ'], coeffs=[0.+2.j, 0.+1.j]), SparsePauliOp(['XX', 'YY'], coeffs=[2.+0.j, 1.+0.j])] - - >>> grouped_sum [SparsePauliOp(['XX'], coeffs=[2.+0.j]), SparsePauliOp(['YY'], coeffs=[1.+0.j]), SparsePauliOp(['IZ', 'ZZ'], @@ -1020,35 +1063,41 @@ Other Evolution Classes **Opflow** - .. code-block:: python + .. testcode:: - >>> from qiskit.opflow import Trotter, PauliTrotterEvolution, PauliSumOp + from qiskit.opflow import Trotter, PauliTrotterEvolution, PauliSumOp - >>> hamiltonian = PauliSumOp.from_list([('X', 1), ('Z',1)]) - >>> evolution = PauliTrotterEvolution(trotter_mode=Trotter(), reps=2) - >>> evol_result = evolution.convert(hamiltonian.exp_i()) - >>> evolved_state = evol_result.to_circuit() + hamiltonian = PauliSumOp.from_list([('X', 1), ('Z',1)]) + evolution = PauliTrotterEvolution(trotter_mode=Trotter(), reps=2) + evol_result = evolution.convert(hamiltonian.exp_i()) + evolved_state = evol_result.to_circuit() + + print(evolved_state) + + .. testoutput:: - >>> print(evolved_state) ┌─────────────────────┐ q: ┤ exp(-it (X + Z))(1) ├ └─────────────────────┘ **Alternative** - .. code-block:: python + .. testcode:: + + from qiskit import QuantumCircuit + from qiskit.quantum_info import SparsePauliOp + from qiskit.circuit.library import PauliEvolutionGate + from qiskit.synthesis import SuzukiTrotter - >>> from qiskit import QuantumCircuit - >>> from qiskit.quantum_info import SparsePauliOp - >>> from qiskit.circuit.library import PauliEvolutionGate - >>> from qiskit.synthesis import SuzukiTrotter + hamiltonian = SparsePauliOp.from_list([('X', 1), ('Z',1)]) + evol_gate = PauliEvolutionGate(hamiltonian, time=1, synthesis=SuzukiTrotter(reps=2)) + evolved_state = QuantumCircuit(1) + evolved_state.append(evol_gate, [0]) - >>> hamiltonian = SparsePauliOp.from_list([('X', 1), ('Z',1)]) - >>> evol_gate = PauliEvolutionGate(hamiltonian, time=1, synthesis=SuzukiTrotter(reps=2)) - >>> evolved_state = QuantumCircuit(1) - >>> evolved_state.append(evol_gate, [0]) + print(evolved_state) + + .. testoutput:: - >>> print(evolved_state) ┌─────────────────────┐ q: ┤ exp(-it (X + Z))(1) ├ └─────────────────────┘ @@ -1058,39 +1107,45 @@ Other Evolution Classes **Opflow** - .. code-block:: python + .. testcode:: + + from qiskit.opflow import Trotter, PauliTrotterEvolution, PauliSumOp + from qiskit.circuit import Parameter - >>> from qiskit.opflow import Trotter, PauliTrotterEvolution, PauliSumOp - >>> from qiskit.circuit import Parameter + time = Parameter('t') + hamiltonian = PauliSumOp.from_list([('X', 1), ('Y',1)]) + evolution = PauliTrotterEvolution(trotter_mode=Trotter(), reps=1) + evol_result = evolution.convert((time * hamiltonian).exp_i()) + evolved_state = evol_result.to_circuit() - >>> time = Parameter('t') - >>> hamiltonian = PauliSumOp.from_list([('X', 1), ('Y',1)]) - >>> evolution = PauliTrotterEvolution(trotter_mode=Trotter(), reps=1) - >>> evol_result = evolution.convert((time * hamiltonian).exp_i()) - >>> evolved_state = evol_result.to_circuit() + print(evolved_state) + + .. testoutput:: - >>> print(evolved_state) ┌─────────────────────────┐ q: ┤ exp(-it (X + Y))(1.0*t) ├ └─────────────────────────┘ **Alternative** - .. code-block:: python + .. testcode:: + + from qiskit.quantum_info import SparsePauliOp + from qiskit.synthesis import LieTrotter + from qiskit.circuit.library import PauliEvolutionGate + from qiskit import QuantumCircuit + from qiskit.circuit import Parameter - >>> from qiskit.quantum_info import SparsePauliOp - >>> from qiskit.synthesis import LieTrotter - >>> from qiskit.circuit.library import PauliEvolutionGate - >>> from qiskit import QuantumCircuit - >>> from qiskit.circuit import Parameter + time = Parameter('t') + hamiltonian = SparsePauliOp.from_list([('X', 1), ('Y',1)]) + evol_gate = PauliEvolutionGate(hamiltonian, time=time, synthesis=LieTrotter()) + evolved_state = QuantumCircuit(1) + evolved_state.append(evol_gate, [0]) - >>> time = Parameter('t') - >>> hamiltonian = SparsePauliOp.from_list([('X', 1), ('Y',1)]) - >>> evol_gate = PauliEvolutionGate(hamiltonian, time=time, synthesis=LieTrotter()) - >>> evolved_state = QuantumCircuit(1) - >>> evolved_state.append(evol_gate, [0]) + print(evolved_state) + + .. testoutput:: - >>> print(evolved_state) ┌─────────────────────┐ q: ┤ exp(-it (X + Y))(t) ├ └─────────────────────┘ @@ -1102,33 +1157,39 @@ Other Evolution Classes **Opflow** - .. code-block:: python + .. testcode:: + + from qiskit.opflow import MatrixEvolution, MatrixOp - >>> 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() - >>> hamiltonian = MatrixOp([[0, 1], [1, 0]]) - >>> evolution = MatrixEvolution() - >>> evol_result = evolution.convert(hamiltonian.exp_i()) - >>> evolved_state = evol_result.to_circuit() + print(evolved_state.decompose().decompose()) + + .. testoutput:: - >>> print(evolved_state.decompose().decompose()) ┌────────────────┐ q: ┤ U3(2,-π/2,π/2) ├ └────────────────┘ **Alternative** - .. code-block:: python + .. testcode:: + + from qiskit.quantum_info import SparsePauliOp + from qiskit.extensions import HamiltonianGate + from qiskit import QuantumCircuit - >>> 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]) - >>> evol_gate = HamiltonianGate([[0, 1], [1, 0]], 1) - >>> evolved_state = QuantumCircuit(1) - >>> evolved_state.append(evol_gate, [0]) + print(evolved_state.decompose().decompose()) + + .. testoutput:: - >>> print(evolved_state.decompose().decompose()) ┌────────────────┐ q: ┤ U3(2,-π/2,π/2) ├ └────────────────┘ @@ -1174,45 +1235,51 @@ Algorithm-Agnostic Expectations **Opflow** - .. code-block:: python + .. testcode:: + + from qiskit.opflow import X, Minus, StateFn, AerPauliExpectation, CircuitSampler + from qiskit.utils import QuantumInstance + from qiskit_aer import AerSimulator - >>> from qiskit.opflow import X, Minus, StateFn, AerPauliExpectation, CircuitSampler - >>> from qiskit.utils import QuantumInstance - >>> from qiskit_aer import AerSimulator + backend = AerSimulator() + q_instance = QuantumInstance(backend) - >>> backend = AerSimulator() - >>> q_instance = QuantumInstance(backend) + sampler = CircuitSampler(q_instance, attach_results=True) + expectation = AerPauliExpectation() - >>> sampler = CircuitSampler(q_instance, attach_results=True) - >>> expectation = AerPauliExpectation() + state = Minus + operator = 1j * X - >>> state = Minus - >>> operator = 1j * X + converted_meas = expectation.convert(StateFn(operator, is_measurement=True) @ state) + expectation_value = sampler.convert(converted_meas).eval() - >>> converted_meas = expectation.convert(StateFn(operator, is_measurement=True) @ state) - >>> expectation_value = sampler.convert(converted_meas).eval() + print(expectation_value) + + .. testoutput:: - >>> print(expectation_value) -1j **Alternative** - .. code-block:: python + .. testcode:: + + from qiskit.quantum_info import SparsePauliOp + from qiskit import QuantumCircuit + from qiskit_aer.primitives import Estimator - >>> from qiskit.quantum_info import SparsePauliOp - >>> from qiskit import QuantumCircuit - >>> from qiskit_aer.primitives import Estimator + estimator = Estimator(run_options={"approximation": True, "shots": None}) - >>> estimator = AerEstimator(run_options={"approximation": True, "shots": None}) + op = SparsePauliOp.from_list([("X", 1j)]) + states_op = QuantumCircuit(1) + states_op.x(0) + states_op.h(0) - >>> op = SparsePauliOp.from_list([("X", 1j)]) - >>> states_op = QuantumCircuit(1) - >>> states_op.x(0) - >>> states_op.h(0) + expectation_value = estimator.run(states_op, op).result().values - >>> expectation_value = estimator.run(states_op, op).result().values + print(expectation_value) + + .. testoutput:: - >>> print(expectation_value) [0.-1.j] @@ -1223,48 +1290,54 @@ Algorithm-Agnostic Expectations **Opflow** - .. code-block:: python + .. testcode:: + + from qiskit.opflow import X, H, I, MatrixExpectation, ListOp, StateFn + from qiskit.utils import QuantumInstance + from qiskit_aer import AerSimulator + + backend = AerSimulator(method='statevector') + q_instance = QuantumInstance(backend) + sampler = CircuitSampler(q_instance, attach_results=True) + expect = MatrixExpectation() - >>> from qiskit.opflow import X, H, I, MatrixExpectation, ListOp, StateFn - >>> from qiskit.utils import QuantumInstance - >>> from qiskit_aer import AerSimulator + mixed_ops = ListOp([X.to_matrix_op(), H]) + converted_meas = expect.convert(~StateFn(mixed_ops)) - >>> backend = AerSimulator(method='statevector') - >>> q_instance = QuantumInstance(backend) - >>> sampler = CircuitSampler(q_instance, attach_results=True) - >>> expect = MatrixExpectation() + plus_mean = converted_meas @ Plus + values_plus = sampler.convert(plus_mean).eval() - >>> mixed_ops = ListOp([X.to_matrix_op(), H]) - >>> converted_meas = expect.convert(~StateFn(mixed_ops)) + print(values_plus) - >>> plus_mean = converted_meas @ Plus - >>> values_plus = sampler.convert(plus_mean).eval() + .. testoutput:: - >>> values_plus [(1+0j), (0.7071067811865476+0j)] **Alternative** - .. code-block:: python + .. testcode:: - >>> from qiskit.primitives import Estimator - >>> from qiskit.quantum_info import SparsePauliOp - >>> from qiskit.quantum_info import Clifford + from qiskit.primitives import Estimator + from qiskit.quantum_info import SparsePauliOp + from qiskit.quantum_info import Clifford - >>> X = SparsePauliOp("X") + X = SparsePauliOp("X") - >>> qc = QuantumCircuit(1) - >>> qc.h(0) - >>> H = Clifford(qc).to_operator() + qc = QuantumCircuit(1) + qc.h(0) + H = Clifford(qc).to_operator() - >>> plus = QuantumCircuit(1) - >>> plus.h(0) + plus = QuantumCircuit(1) + plus.h(0) - >>> estimator = Estimator() - >>> values_plus = estimator.run([plus, plus], [X, H]).result().values + estimator = Estimator() + values_plus = estimator.run([plus, plus], [X, H]).result().values - >>> values_plus - array([1. , 0.70710678]) + print(values_plus) + + .. testoutput:: + + [1. 0.70710678] CVaRExpectation @@ -1287,46 +1360,52 @@ CVaRExpectation **Opflow** - .. code-block:: python + .. testcode:: + + 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 - >>> from qiskit.opflow import CVaRExpectation, PauliSumOp + backend = AerSimulator(method="statevector") + 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) - >>> from qiskit.algorithms import VQE - >>> from qiskit.algorithms.optimizers import SLSQP - >>> from qiskit.circuit.library import TwoLocal - >>> from qiskit_aer import AerSimulator + print(result.eigenvalue) - >>> backend = AerSimulator(method="statevector") - >>> 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) + .. testoutput:: - >>> print(result.eigenvalue) (-1+0j) **Alternative** - .. code-block:: python + .. testcode:: - >>> from qiskit.quantum_info import SparsePauliOp + 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 + 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, opt, aggregation=alpha) - >>> result = vqe.compute_minimum_eigenvalue(op) + 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, opt, aggregation=alpha) + result = vqe.compute_minimum_eigenvalue(op) + + print(result.eigenvalue) + + .. testoutput:: - >>> print(result.eigenvalue) -1.0 @@ -1460,70 +1539,77 @@ list: **Opflow** - .. code-block:: python + .. testcode:: - >>> from qiskit.circuit import Parameter, QuantumCircuit - >>> from qiskit.opflow import Gradient, X, Z, StateFn, CircuitStateFn - >>> import numpy as np + from qiskit.circuit import Parameter, QuantumCircuit + from qiskit.opflow import Gradient, X, Z, StateFn, CircuitStateFn + import numpy as np - >>> ham = 0.5 * X - 1 * Z + ham = 0.5 * X - 1 * Z - >>> a = Parameter("a") - >>> b = Parameter("b") - >>> c = Parameter("c") - >>> params = [a,b,c] + a = Parameter("a") + b = Parameter("b") + c = Parameter("c") + params = [a,b,c] - >>> qc = QuantumCircuit(1) - >>> qc.h(0) - >>> qc.u(a, b, c, 0) - >>> qc.h(0) + qc = QuantumCircuit(1) + qc.h(0) + qc.u(a, b, c, 0) + qc.h(0) - >>> op = ~StateFn(ham) @ CircuitStateFn(primitive=qc, coeff=1.0) + op = ~StateFn(ham) @ CircuitStateFn(primitive=qc, coeff=1.0) # the gradient class acted similarly opflow converters, # with a .convert() step and an .eval() step - >>> state_grad = Gradient(grad_method="param_shift").convert(operator=op, params=params) + state_grad = Gradient(grad_method="param_shift").convert(operator=op, params=params) # the old workflow did not allow for batched evaluation of parameter values - >>> values_dict = [{a: np.pi / 4, b: 0, c: 0}, {a: np.pi / 4, b: np.pi / 4, c: np.pi / 4}] - >>> gradients = [] - >>> for i, value_dict in enumerate(values_dict): - ... gradients.append(state_grad.assign_parameters(value_dict).eval()) + values_dict = [{a: np.pi / 4, b: 0, c: 0}, {a: np.pi / 4, b: np.pi / 4, c: np.pi / 4}] + gradients = [] + for i, value_dict in enumerate(values_dict): + gradients.append(state_grad.assign_parameters(value_dict).eval()) + + print(gradients) + + .. testoutput:: - >>> print(gradients) [[(0.35355339059327356+0j), (-1.182555756156289e-16+0j), (-1.6675e-16+0j)], [(0.10355339059327384+0j), (0.8535533905932734+0j), (1.103553390593273+0j)]] **Alternative** - .. code-block:: python + .. testcode:: - >>> from qiskit.circuit import Parameter, QuantumCircuit - >>> from qiskit.primitives import Estimator - >>> from qiskit.algorithms.gradients import ParamShiftEstimatorGradient - >>> from qiskit.quantum_info import SparsePauliOp + from qiskit.circuit import Parameter, QuantumCircuit + from qiskit.primitives import Estimator + from qiskit.algorithms.gradients import ParamShiftEstimatorGradient + from qiskit.quantum_info import SparsePauliOp + import numpy as np - >>> ham = SparsePauliOp.from_list([("X", 0.5), ("Z", -1)]) + ham = SparsePauliOp.from_list([("X", 0.5), ("Z", -1)]) - >>> a = Parameter("a") - >>> b = Parameter("b") - >>> c = Parameter("c") + a = Parameter("a") + b = Parameter("b") + c = Parameter("c") - >>> qc = QuantumCircuit(1) - >>> qc.h(0) - >>> qc.u(a, b, c, 0) - >>> qc.h(0) + qc = QuantumCircuit(1) + qc.h(0) + qc.u(a, b, c, 0) + qc.h(0) - >>> estimator = Estimator() - >>> gradient = ParamShiftEstimatorGradient(estimator) + estimator = Estimator() + gradient = ParamShiftEstimatorGradient(estimator) # the new workflow follows an interface close to the primitives' - >>> param_list = [[np.pi / 4, 0, 0], [np.pi / 4, np.pi / 4, np.pi / 4]] + param_list = [[np.pi / 4, 0, 0], [np.pi / 4, np.pi / 4, np.pi / 4]] # for batched evaluations, the number of circuits must match the # number of parameter value sets - >>> gradients = gradient.run([qc] * 2, [ham] * 2, param_list).result().gradients + gradients = gradient.run([qc] * 2, [ham] * 2, param_list).result().gradients + + print(gradients) + + .. testoutput:: - >>> print(gradients) [array([ 3.53553391e-01, 0.00000000e+00, -1.80411242e-16]), array([0.10355339, 0.85355339, 1.10355339])] @@ -1532,54 +1618,62 @@ list: **Opflow** - .. code-block:: python + .. testcode:: - >>> from qiskit.circuit import Parameter, QuantumCircuit - >>> from qiskit.opflow import QFI, CircuitStateFn + from qiskit.circuit import Parameter, QuantumCircuit + from qiskit.opflow import QFI, CircuitStateFn + import numpy as np # create the circuit - >>> a, b = Parameter("a"), Parameter("b") - >>> qc = QuantumCircuit(1) - >>> qc.h(0) - >>> qc.rz(a, 0) - >>> qc.rx(b, 0) + a, b = Parameter("a"), Parameter("b") + qc = QuantumCircuit(1) + qc.h(0) + qc.rz(a, 0) + qc.rx(b, 0) # convert the circuit to a QFI object - >>> op = CircuitStateFn(qc) - >>> qfi = QFI(qfi_method="lin_comb_full").convert(operator=op) + op = CircuitStateFn(qc) + qfi = QFI(qfi_method="lin_comb_full").convert(operator=op) # bind parameters and evaluate - >>> values_dict = {a: np.pi / 4, b: 0.1} - >>> qfi = qfi.bind_parameters(values_dict).eval() + values_dict = {a: np.pi / 4, b: 0.1} + qfi = qfi.bind_parameters(values_dict).eval() + + print(qfi) + + .. testoutput:: - >>> print(qfi) [[ 1.00000000e+00+0.j -3.63575685e-16+0.j] [-3.63575685e-16+0.j 5.00000000e-01+0.j]] **Alternative** - .. code-block:: python + .. testcode:: - >>> from qiskit.circuit import Parameter, QuantumCircuit - >>> from qiskit.primitives import Estimator - >>> from qiskit.algorithms.gradients import LinCombQGT, QFI + from qiskit.circuit import Parameter, QuantumCircuit + from qiskit.primitives import Estimator + from qiskit.algorithms.gradients import LinCombQGT, QFI + import numpy as np # create the circuit - >>> a, b = Parameter("a"), Parameter("b") - >>> qc = QuantumCircuit(1) - >>> qc.h(0) - >>> qc.rz(a, 0) - >>> qc.rx(b, 0) + a, b = Parameter("a"), Parameter("b") + qc = QuantumCircuit(1) + qc.h(0) + qc.rz(a, 0) + qc.rx(b, 0) # initialize QFI - >>> estimator = Estimator() - >>> qgt = LinCombQGT(estimator) - >>> qfi = QFI(qgt) + estimator = Estimator() + qgt = LinCombQGT(estimator) + qfi = QFI(qgt) # evaluate - >>> values_list = [[np.pi / 4, 0.1]] - >>> qfi = qfi.run(qc, values_list).result().qfis + values_list = [[np.pi / 4, 0.1]] + qfi = qfi.run(qc, values_list).result().qfis + + print(qfi) + + .. testoutput:: - >>> print(qfi) [array([[ 1.00000000e+00, -1.50274614e-16], [-1.50274614e-16, 5.00000000e-01]])] From 3bf38963c537fc3f9d9fb2c062729b82f634e734 Mon Sep 17 00:00:00 2001 From: ElePT Date: Thu, 9 Mar 2023 14:06:26 +0100 Subject: [PATCH 39/50] Try CI config --- docs/conf.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/conf.py b/docs/conf.py index 549390c8bb5..4b0ec31f646 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -38,7 +38,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. @@ -89,3 +91,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 11a35a069725ae2bdc5e22f3b5c4ff45ce2e310c Mon Sep 17 00:00:00 2001 From: ElePT Date: Thu, 9 Mar 2023 14:06:55 +0100 Subject: [PATCH 40/50] Add internal links --- docs/migration_guides/opflow_migration.rst | 35 +++++++++++----------- qiskit/quantum_info/__init__.py | 2 -- qiskit/synthesis/__init__.py | 2 ++ 3 files changed, 20 insertions(+), 19 deletions(-) diff --git a/docs/migration_guides/opflow_migration.rst b/docs/migration_guides/opflow_migration.rst index 985ed31b4d6..3350c74175b 100644 --- a/docs/migration_guides/opflow_migration.rst +++ b/docs/migration_guides/opflow_migration.rst @@ -16,6 +16,8 @@ the:mod:`~qiskit.opflow` module to the :mod:`~qiskit.primitives` and :mod:`~qisk is also being deprecated. For more information on migrating the :class:`~qiskit.utils.QuantumInstance`, please read the `quantum instance migration guide `_. +.. _attention_primitives: + .. attention:: Most references to the :class:`qiskit.primitives.Sampler` or :class:`qiskit.primitives.Estimator` in this guide @@ -57,16 +59,16 @@ The function equivalency can be roughly summarized as follows: - Alternative * - Operators (:class:`~qiskit.opflow.OperatorBase`, :ref:`operator_globals`, :mod:`~qiskit.opflow.primitive_ops`, :mod:`~qiskit.opflow.list_ops`) - - :ref:`qiskit.quantum_info Operators ` + - ``qiskit.quantum_info`` :ref:`Operators ` * - :mod:`qiskit.opflow.state_fns` - - :ref:`qiskit.quantum_info States ` + - ``qiskit.quantum_info`` :ref:`States ` * - :mod:`qiskit.opflow.converters` - :mod:`qiskit.primitives` * - :mod:`qiskit.opflow.evolutions` - - :ref:`qiskit.quantum_info Synthesis ` + - ``qiskit.synthesis`` :ref:`Evolution ` * - :mod:`qiskit.opflow.expectations` - :class:`qiskit.primitives.Estimator` @@ -466,7 +468,7 @@ are used "under the hood" in the original code: wrap in :class:`~qiskit.quantum_info.SparsePauliOp`. * - :class:`~qiskit.opflow.primitive_ops.PauliSumOp` - - :class:`~qiskit.quantum_info.SparsePauliOp`. See example below. + - :class:`~qiskit.quantum_info.SparsePauliOp`. See example :ref:`below `. * - :class:`~qiskit.opflow.primitive_ops.TaperedPauliSumOp` - This class was used to combine a :class:`.PauliSumOp` with its identified symmetries in one object. @@ -474,9 +476,9 @@ are used "under the hood" in the original code: See :class:`qiskit.quantum_info.analysis.Z2Symmetries` example for updated workflow. * - :class:`qiskit.opflow.primitive_ops.Z2Symmetries` - - :class:`qiskit.quantum_info.analysis.Z2Symmetries`. See example below. + - :class:`qiskit.quantum_info.analysis.Z2Symmetries`. See example :ref:`below `. -.. _pauli_sum_op: +.. _example_pauli_sum_op: .. dropdown:: Example 1: ``PauliSumOp`` :animate: fade-in-slide-down @@ -511,7 +513,7 @@ are used "under the hood" in the original code: SparsePauliOp(['XYZY'], coeffs=[0.-6.j]) -.. _z2_sym: +.. _example_z2_sym: .. dropdown:: Example 2: ``Z2Symmetries`` and ``TaperedPauliSumOp`` :animate: fade-in-slide-down @@ -787,10 +789,6 @@ In the case of the :class:`~qiskit.opflow.converters.CircuitSampler`, it travers approximations of its state functions using a quantum backend. Notably, this functionality has been replaced by the :mod:`~qiskit.primitives`. -.. |ParityMapper| replace:: ``ParityMapper`` -.. _ParityMapper: https://qiskit.org/documentation/nature/stubs/qiskit_nature.second_q.mappers.ParityMapper.html#qiskit_nature.second_q.mappers.ParityMapper - - .. list-table:: :header-rows: 1 @@ -799,24 +797,25 @@ Notably, this functionality has been replaced by the :mod:`~qiskit.primitives`. * - :class:`~qiskit.opflow.converters.CircuitSampler` - :class:`~qiskit.primitives.Sampler` or :class:`~qiskit.primitives.Estimator` if used with - :class:`~qiskit.oflow.expectations`. See examples below. + :class:`~qiskit.oflow.expectations`. See examples :ref:`below `. * - :class:`~qiskit.opflow.converters.AbelianGrouper` - This class allowed a sum a of Pauli operators to be grouped, a similar functionality can be achieved through the :meth:`~qiskit.quantum_info.SparsePauliOp.group_commuting` method of :class:`qiskit.quantum_info.SparsePauliOp`, although this is not a 1-1 replacement, as you can see - in the example below. + in the example :ref:`below `. * - :class:`~qiskit.opflow.converters.DictToCircuitSum` - No direct replacement. This class was used to convert from :class:`~qiskit.opflow.state_fns.DictStateFn`\s or :class:`~qiskit.opflow.state_fns.VectorStateFn`\s to equivalent :class:`~qiskit.opflow.state_fns.CircuitStateFn`\s. * - :class:`~qiskit.opflow.converters.PauliBasisChange` - No direct replacement. This class was used for changing Paulis into other bases. * - :class:`~qiskit.opflow.converters.TwoQubitReduction` - - No direct replacement. This class implements a chemistry-specific reduction for the |ParityMapper|_ class in ``qiskit-nature``. + - No direct replacement. This class implements a chemistry-specific reduction for the :class:`.ParityMapper` + class in :mod:`qiskit_nature`. The general symmetry logic this mapper depends on has been refactored to other classes in :mod:`~qiskit.quantum_info`, so this specific :mod:`~qiskit.opflow` implementation is no longer necessary. -.. _convert_state: +.. _example_convert_state: .. dropdown:: Example 1: ``CircuitSampler`` for sampling parametrized circuits :animate: fade-in-slide-down @@ -934,6 +933,7 @@ Notably, this functionality has been replaced by the :mod:`~qiskit.primitives`. [1.] +.. _example_commuting: .. dropdown:: Example 3: ``AbelianGrouper`` for grouping operators :animate: fade-in-slide-down @@ -988,7 +988,7 @@ 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. +``qiskit.synthesis`` :ref:`Evolution ` module. .. note:: @@ -1200,7 +1200,8 @@ Expectations *Back to* `Contents`_ 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 :class:`~qiskit.primitives.Estimator` primitive. +This functionality can now be found in the :class:`~qiskit.primitives.Estimator` primitive. Please remember that there +are different ``Estimator`` implementations, as noted :ref:`here ` Algorithm-Agnostic Expectations ------------------------------- diff --git a/qiskit/quantum_info/__init__.py b/qiskit/quantum_info/__init__.py index b3d0d81835d..4e31cc12501 100644 --- a/qiskit/quantum_info/__init__.py +++ b/qiskit/quantum_info/__init__.py @@ -113,8 +113,6 @@ hellinger_fidelity Z2Symmetries -.. _quantum_info_synthesis: - Synthesis ========= diff --git a/qiskit/synthesis/__init__.py b/qiskit/synthesis/__init__.py index eb3adae2955..881a4b7a7d7 100644 --- a/qiskit/synthesis/__init__.py +++ b/qiskit/synthesis/__init__.py @@ -17,6 +17,8 @@ .. currentmodule:: qiskit.synthesis +.. _evolution_synthesis: + Evolution Synthesis =================== From 52d53f25fd53d6f4b06a7b939e1a8909e38785d4 Mon Sep 17 00:00:00 2001 From: ElePT Date: Thu, 9 Mar 2023 14:24:16 +0100 Subject: [PATCH 41/50] Fix lint, add doctest import --- docs/conf.py | 1 + docs/migration_guides/opflow_migration.rst | 2 +- qiskit/quantum_info/__init__.py | 6 +----- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 4b0ec31f646..40bca5016b2 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 diff --git a/docs/migration_guides/opflow_migration.rst b/docs/migration_guides/opflow_migration.rst index 3350c74175b..b12631e5a8e 100644 --- a/docs/migration_guides/opflow_migration.rst +++ b/docs/migration_guides/opflow_migration.rst @@ -8,7 +8,7 @@ The new :mod:`~qiskit.primitives`, in combination with the :mod:`~qiskit.quantum functionality of :mod:`~qiskit.opflow`. Thus, the latter is being deprecated. In this migration guide, you will find instructions and code examples for how to migrate your code based on -the:mod:`~qiskit.opflow` module to the :mod:`~qiskit.primitives` and :mod:`~qiskit.quantum_info` modules. +the :mod:`~qiskit.opflow` module to the :mod:`~qiskit.primitives` and :mod:`~qiskit.quantum_info` modules. .. note:: diff --git a/qiskit/quantum_info/__init__.py b/qiskit/quantum_info/__init__.py index 4e31cc12501..32193fbddb0 100644 --- a/qiskit/quantum_info/__init__.py +++ b/qiskit/quantum_info/__init__.py @@ -168,8 +168,4 @@ XXDecomposer, ) -from .analysis import ( - hellinger_distance, - hellinger_fidelity, - Z2Symmetries -) +from .analysis import hellinger_distance, hellinger_fidelity, Z2Symmetries From 379496095627d7dc825f43011cd3636a01692d21 Mon Sep 17 00:00:00 2001 From: ElePT Date: Fri, 10 Mar 2023 10:04:01 +0100 Subject: [PATCH 42/50] Fix cyclic import --- docs/migration_guides/opflow_migration.rst | 3 --- .../implements_two_step_tapering-f481a8cac3990cd5.yaml | 8 ++++---- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/docs/migration_guides/opflow_migration.rst b/docs/migration_guides/opflow_migration.rst index b12631e5a8e..ee193b7699a 100644 --- a/docs/migration_guides/opflow_migration.rst +++ b/docs/migration_guides/opflow_migration.rst @@ -444,9 +444,6 @@ to initialize it. Thus, when migrating opflow code, it is important to look for alternatives to replace the specific subclasses that are used "under the hood" in the original code: -.. |qiskit.quantum_info.Z2Symmetries| replace:: ``qiskit.quantum_info.Z2Symmetries`` -.. _qiskit.quantum_info.Z2Symmetries: https://github.com/Qiskit/qiskit-terra/blob/main/qiskit/quantum_info/analysis/z2_symmetries.py - .. list-table:: :header-rows: 1 diff --git a/releasenotes/notes/0.22/implements_two_step_tapering-f481a8cac3990cd5.yaml b/releasenotes/notes/0.22/implements_two_step_tapering-f481a8cac3990cd5.yaml index 822417a07df..cfae1fef169 100644 --- a/releasenotes/notes/0.22/implements_two_step_tapering-f481a8cac3990cd5.yaml +++ b/releasenotes/notes/0.22/implements_two_step_tapering-f481a8cac3990cd5.yaml @@ -1,10 +1,10 @@ --- features: - | - The :class:`~.Z2Symmetries` class has two new methods, - :meth:`~.Z2Symmetries.convert_clifford` and - :meth:`~.Z2Symmetries.taper_clifford`. These two methods are the two + The :class:`~qiskit.opflow.primitive_ops.Z2Symmetries` class has two new methods, + :meth:`qiskit.opflow.primitive_ops.Z2Symmetries.convert_clifford` and + :meth:`qiskit.opflow.primitive_ops.Z2Symmetries.taper_clifford`. These two methods are the two operations necessary for taperng an operator based on the Z2 symmetries in the object and were previously performed internally via the - :meth:`~.Z2Symmetries.taper` method. However, these methods are now + :meth:`qiskit.opflow.primitive_ops.Z2Symmetries.taper` method. However, these methods are now public methods of the class which can be called individually if needed. From 7f2f9c7f05e66bf7ef1f5386d9bf867cd9a4e7b8 Mon Sep 17 00:00:00 2001 From: ElePT Date: Fri, 10 Mar 2023 11:55:50 +0100 Subject: [PATCH 43/50] Fix cyclic import --- qiskit/quantum_info/analysis/z2_symmetries.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit/quantum_info/analysis/z2_symmetries.py b/qiskit/quantum_info/analysis/z2_symmetries.py index 49684274d0c..b61a397df68 100644 --- a/qiskit/quantum_info/analysis/z2_symmetries.py +++ b/qiskit/quantum_info/analysis/z2_symmetries.py @@ -22,7 +22,7 @@ import numpy as np from qiskit.exceptions import QiskitError -from qiskit.quantum_info import Pauli, SparsePauliOp +from ..operators import Pauli, SparsePauliOp class Z2Symmetries: From fd883c45300c7039b7bf7b012a7719fe21055dc2 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:53:54 +0200 Subject: [PATCH 44/50] Apply suggestions from Declan's code review Co-authored-by: Declan Millar --- docs/migration_guides/opflow_migration.rst | 26 +++++++++++----------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/docs/migration_guides/opflow_migration.rst b/docs/migration_guides/opflow_migration.rst index ee193b7699a..c6b899dae5c 100644 --- a/docs/migration_guides/opflow_migration.rst +++ b/docs/migration_guides/opflow_migration.rst @@ -48,9 +48,9 @@ and find new paths for developing algorithms based on the :mod:`~qiskit.primitiv the :mod:`~qiskit.quantum_info` module. 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. +(i.e., using :mod:`~qiskit.quantum_info`), or an explanation of how to replace their functionality in algorithms. -The function equivalency can be roughly summarized as follows: +The functional equivalency can be roughly summarized as follows: .. list-table:: :header-rows: 1 @@ -79,7 +79,7 @@ The function equivalency can be roughly summarized as follows: Contents ======== -This document covers the migration from these opflow sub-modules: +This document covers the migration from these opflow submodules: **Operators** @@ -408,7 +408,7 @@ Primitive and List Ops ====================== *Back to* `Contents`_ -Most of the workflows that previously relied in components from :mod:`~qiskit.opflow.primitive_ops` and +Most of the workflows that previously relied on components from :mod:`~qiskit.opflow.primitive_ops` and :mod:`~qiskit.opflow.list_ops` can now leverage elements from :mod:`~qiskit.quantum_info`\'s operators instead. Some of these classes do not require a 1-1 replacement because they were created to interface with other @@ -430,7 +430,7 @@ to initialize it. :header-rows: 1 * - Class passed to :class:`~qiskit.opflow.primitive_ops.PrimitiveOp` - - Sub-class returned + - Subclass returned * - :class:`~qiskit.quantum_info.Pauli` - :class:`~qiskit.opflow.primitive_ops.PauliOp` @@ -646,7 +646,7 @@ The :mod:`~qiskit.opflow.state_fns` module can be generally replaced by subclass :class:`qiskit.quantum_info.QuantumState`. Similarly to :class:`~qiskit.opflow.primitive_ops.PrimitiveOp`, :class:`~qiskit.opflow.state_fns.StateFn` -acts as a factory to create the corresponding sub-class depending on the computational primitive used to initialize it. +acts as a factory to create the corresponding subclass depending on the computational primitive used to initialize it. .. tip:: @@ -671,7 +671,7 @@ acts as a factory to create the corresponding sub-class depending on the computa - :class:`~qiskit.opflow.state_fns.OperatorStateFn` This means that references to :class:`~qiskit.opflow.state_fns.StateFn` in opflow code should be examined to -identify the sub-class that is being used, to then look for an alternative. +identify the subclass that is being used, to then look for an alternative. .. list-table:: :header-rows: 1 @@ -780,7 +780,7 @@ Converters *Back to* `Contents`_ -The role of the :class:`qiskit.opflow.converters` sub-module was to convert the operators into other opflow operator classes +The role of the :class:`qiskit.opflow.converters` submodule was to convert the operators into other opflow operator classes (:class:`~qiskit.opflow.converters.TwoQubitReduction`, :class:`~qiskit.opflow.converters.PauliBasisChange`...). In the case of the :class:`~qiskit.opflow.converters.CircuitSampler`, it traversed an operator and outputted approximations of its state functions using a quantum backend. @@ -982,24 +982,24 @@ Evolutions ========== *Back to* `Contents`_ -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 +The :mod:`qiskit.opflow.evolutions` submodule 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 ``qiskit.synthesis`` :ref:`Evolution ` module. .. note:: The :class:`qiskit.opflow.evolutions.PauliTrotterEvolution` class computes evolutions for exponentiated - sums of Paulis by converting to the Z basis, rotating with an RZ, changing back, and trotterizing. + sums of Paulis by converting to the Z basis, rotating with an RZ, changing back, and Trotterizing. When calling ``.convert()``, the class follows a recursive strategy that involves creating :class:`~qiskit.opflow.evolutions.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. The methods can be specified via + the desired synthesis methods to perform the Trotterization. The methods can be specified via ``string``, which is then inputted into a :class:`~qiskit.opflow.evolutions.TrotterizationFactory`, or by supplying a method instance of :class:`qiskit.opflow.evolutions.Trotter`, :class:`qiskit.opflow.evolutions.Suzuki` or :class:`qiskit.opflow.evolutions.QDrift`. - The different trotterization methods that extend :class:`qiskit.opflow.evolutions.TrotterizationBase` were migrated to + The different Trotterization methods that extend :class:`qiskit.opflow.evolutions.TrotterizationBase` were migrated to :mod:`qiskit.synthesis`, and now extend the :class:`qiskit.synthesis.ProductFormula` base class. They no longer contain a ``.convert()`` method for standalone use, but are now designed to be plugged into the :class:`.PauliEvolutionGate` and called via ``.synthesize()``. From 8c771baa4e8c348a3d4d63e70273e989b7cfd85d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20Pe=C3=B1a=20Tapia?= <57907331+ElePT@users.noreply.github.com> Date: Mon, 3 Apr 2023 17:12:58 +0200 Subject: [PATCH 45/50] Update qiskit/quantum_info/__init__.py --- qiskit/quantum_info/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit/quantum_info/__init__.py b/qiskit/quantum_info/__init__.py index 2987e3d4324..398b5d6eedb 100644 --- a/qiskit/quantum_info/__init__.py +++ b/qiskit/quantum_info/__init__.py @@ -182,4 +182,4 @@ decompose_clifford, two_qubit_cnot_decompose, ) -from .analysis import hellinger_distance, hellinger_fidelity, Z2Symmetries \ No newline at end of file +from .analysis import hellinger_distance, hellinger_fidelity, Z2Symmetries From bd336f510dfe1fa75d0db239759697d024fb1f0d Mon Sep 17 00:00:00 2001 From: ElePT Date: Wed, 5 Apr 2023 11:38:03 +0200 Subject: [PATCH 46/50] Fix import --- qiskit/quantum_info/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qiskit/quantum_info/__init__.py b/qiskit/quantum_info/__init__.py index 398b5d6eedb..bd40b78e2dd 100644 --- a/qiskit/quantum_info/__init__.py +++ b/qiskit/quantum_info/__init__.py @@ -130,7 +130,7 @@ XXDecomposer """ -from .analysis import hellinger_distance, hellinger_fidelity +from .analysis import hellinger_distance, hellinger_fidelity, Z2Symmetries from .operators import ( Clifford, Operator, @@ -182,4 +182,4 @@ decompose_clifford, two_qubit_cnot_decompose, ) -from .analysis import hellinger_distance, hellinger_fidelity, Z2Symmetries + From a5b8a73737e414efbd27e59335fc283ae7e34f55 Mon Sep 17 00:00:00 2001 From: ElePT Date: Wed, 5 Apr 2023 11:40:25 +0200 Subject: [PATCH 47/50] Copy docs config from #9716 --- docs/conf.py | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 5ab16811b81..0d847990fe1 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -16,7 +16,6 @@ # -- General configuration --------------------------------------------------- import datetime -import doctest project = "Qiskit" copyright = f"2017-{datetime.date.today().year}, Qiskit Development Team" # pylint: disable=redefined-builtin @@ -35,12 +34,11 @@ "sphinx.ext.viewcode", "sphinx.ext.extlinks", "sphinx.ext.intersphinx", + "sphinx.ext.doctest", "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. @@ -65,7 +63,9 @@ intersphinx_mapping = { "retworkx": ("https://qiskit.org/documentation/retworkx/", None), - "qiskit_ibm_runtime": ("https://qiskit.org/documentation/partners/qiskit_ibm_runtime/", None) + "qiskit-ibm-runtime": ("https://qiskit.org/documentation/partners/qiskit_ibm_runtime/", None), + "qiskit-aer": ("https://qiskit.org/documentation/aer/", None), + "numpy": ("https://numpy.org/doc/stable/", None) } # -- Options for HTML output ------------------------------------------------- @@ -113,6 +113,17 @@ autoclass_content = "both" -# -- Options for doctest ------------------------------------- +# -- Options for Doctest -------------------------------------------------------- + +import sphinx.ext.doctest + +# This option will make doctest ignore whitespace when testing code. +# It's specially important for circuit representation as it gives an +# error otherwise +doctest_default_flags = sphinx.ext.doctest.doctest.NORMALIZE_WHITESPACE -doctest_default_flags = doctest.ELLIPSIS | doctest.NORMALIZE_WHITESPACE | doctest.IGNORE_EXCEPTION_DETAIL | doctest.DONT_ACCEPT_TRUE_FOR_1 +# Leaving this string empty disables testing of doctest blocks from docstrings. +# Doctest blocks are structures like this one: +# >> code +# output +doctest_test_doctest_blocks = "" From 15b666af625253269392a1845ee9af3ef031a0ba Mon Sep 17 00:00:00 2001 From: ElePT Date: Wed, 5 Apr 2023 12:07:57 +0200 Subject: [PATCH 48/50] Make black --- qiskit/quantum_info/__init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/qiskit/quantum_info/__init__.py b/qiskit/quantum_info/__init__.py index bd40b78e2dd..c20d0a0b4fb 100644 --- a/qiskit/quantum_info/__init__.py +++ b/qiskit/quantum_info/__init__.py @@ -182,4 +182,3 @@ decompose_clifford, two_qubit_cnot_decompose, ) - From c5ec2f26f97b14eed9e68acfc4fa958c3d3886d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20Pe=C3=B1a=20Tapia?= <57907331+ElePT@users.noreply.github.com> Date: Wed, 5 Apr 2023 16:46:56 +0200 Subject: [PATCH 49/50] Remove duplicate --- docs/index.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/index.rst b/docs/index.rst index c73ed315c04..69c065eb9fe 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -7,7 +7,6 @@ Qiskit Terra documentation :hidden: API References - Migration Guides Release Notes Migration Guides From 1285e7a7e6683f47e54dc86a625a9016c1b72e99 Mon Sep 17 00:00:00 2001 From: ElePT Date: Wed, 5 Apr 2023 16:51:02 +0200 Subject: [PATCH 50/50] Reorder --- docs/index.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/index.rst b/docs/index.rst index 69c065eb9fe..5dd462fe2ca 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -7,9 +7,9 @@ Qiskit Terra documentation :hidden: API References - Release Notes Migration Guides - + Release Notes + .. Hiding - Indices and tables :ref:`genindex` :ref:`modindex`