1370 lines
56 KiB
Plaintext
1370 lines
56 KiB
Plaintext
---
|
|
title: qiskit.opflow migration guide
|
|
description: How to update your code to stop using the deprecated `qiskit.opflow` module.
|
|
---
|
|
|
|
{/* cspell:ignore XYZY */}
|
|
|
|
# Opflow migration guide
|
|
|
|
<Admonition type="caution">
|
|
**Deprecation Notice**
|
|
|
|
This guide precedes the introduction of the V2 primitives interface. Following the introduction of the V2 primitives, some providers have deprecated V1 primitive
|
|
implementations in favor of the V2 alternatives. If you are interested in following this guide, we recommend combining it with the
|
|
[Migrate to V2 primitives](./v2-primitives) guide to bring your code to the most updated state.
|
|
</Admonition>
|
|
|
|
The new [`qiskit.primitives`](/api/qiskit/primitives), in combination with the [`qiskit.quantum_info`](/api/qiskit/quantum_info) module, have superseded
|
|
functionality of [`qiskit.opflow`](/api/qiskit/0.46/opflow), which is being deprecated.
|
|
|
|
This migration guide contains instructions and code examples to migrate Qiskit code that uses
|
|
the [`qiskit.opflow`](/api/qiskit/0.46/opflow) module to the [`qiskit.primitives`](/api/qiskit/primitives) and [`qiskit.quantum_info`](/api/qiskit/quantum_info) modules.
|
|
|
|
<Admonition type="note">
|
|
The [`qiskit.opflow`](/api/qiskit/0.46/opflow) module was tightly coupled to the [`qiskit.utils.QuantumInstance`](/api/qiskit/0.46/qiskit.utils.QuantumInstance) class, which
|
|
is also being deprecated. For information about migrating the [`qiskit.utils.QuantumInstance`](/api/qiskit/0.46/qiskit.utils.QuantumInstance), see
|
|
the [Quantum instance migration guide.](./qiskit-quantum-instance)
|
|
</Admonition>
|
|
|
|
<span id="attention_primitives"></span>
|
|
<Admonition type="note">
|
|
Most references to the [`qiskit.primitives.Sampler`](/api/qiskit/qiskit.primitives.Sampler) or [`qiskit.primitives.Estimator`](/api/qiskit/qiskit.primitives.Estimator) in this guide
|
|
can be replaced with instances of any primitive implementation. For example, Aer primitives ([`qiskit_aer.primitives.Sampler`](https://qiskit.org/ecosystem/aer/stubs/qiskit_aer.primitives.Sampler.html)/[`qiskit_aer.primitives.Estimator`](https://qiskit.org/ecosystem/aer/stubs/qiskit_aer.primitives.Estimator.html)) or the Qiskit Runtime primitives ([`qiskit_ibm_runtime.Sampler`](/api/qiskit-ibm-runtime/0.26/qiskit_ibm_runtime.Sampler)/[`qiskit_ibm_runtime.Estimator`](/api/qiskit-ibm-runtime/0.26/qiskit_ibm_runtime.Estimator)).
|
|
Specific QPUs (quantum processing units) can be wrapped with ([`qiskit.primitives.BackendSampler`](/api/qiskit/qiskit.primitives.BackendSampler), [`qiskit.primitives.BackendEstimator`](/api/qiskit/qiskit.primitives.BackendEstimator)) to also present primitive-compatible interfaces.
|
|
|
|
Certain classes, such as the
|
|
[`qiskit.opflow.expectations.AerPauliExpectation`](/api/qiskit/0.46/qiskit.opflow.expectations.AerPauliExpectation), can only be replaced by a specific primitive instance
|
|
(in this case, [`qiskit_aer.primitives.Estimator`](https://qiskit.org/ecosystem/aer/stubs/qiskit_aer.primitives.Estimator.html)), or require a specific option configuration.
|
|
If this is the case, it will be explicitly indicated in the corresponding section.
|
|
</Admonition>
|
|
|
|
## Background
|
|
|
|
The [`qiskit.opflow`](/api/qiskit/0.46/opflow) module was originally introduced as a layer between circuits and algorithms, a series of building blocks
|
|
for quantum algorithm research and development.
|
|
|
|
The release of the [`qiskit.primitives`](/api/qiskit/primitives) introduced a new paradigm for interacting with quantum computers. Instead of
|
|
preparing a circuit to execute with a `backend.run()` type of method, algorithms can leverage the [`qiskit.primitives.Sampler`](/api/qiskit/qiskit.primitives.Sampler) and
|
|
[`qiskit.primitives.Estimator`](/api/qiskit/qiskit.primitives.Estimator) primitives, send parametrized circuits and observables, and directly receive quasi-probability distributions or
|
|
expectation values (respectively). This workflow simplifies the pre-processing and post-processing steps
|
|
that previously relied on this module; allowing us to move away from [`qiskit.opflow`](/api/qiskit/0.46/opflow)
|
|
and find new paths for developing algorithms based on the [`qiskit.primitives`](/api/qiskit/primitives) interface and
|
|
the [`qiskit.quantum_info`](/api/qiskit/quantum_info) module.
|
|
|
|
This guide describes the opflow submodules and provides either a direct alternative
|
|
(for example, using [`qiskit.quantum_info`](/api/qiskit/quantum_info)), or an explanation of how to replace their functionality in algorithms.
|
|
|
|
The functional equivalency can be roughly summarized as follows:
|
|
|
|
| Opflow Module | Alternative |
|
|
| --- | --- |
|
|
| Operators ([`qiskit.opflow.OperatorBase`](/api/qiskit/0.46/qiskit.opflow.OperatorBase), [`Operator Globals`](#operator-globals), [`qiskit.opflow.primitive_ops`](/api/qiskit/0.46/qiskit.opflow.primitive_ops), [`qiskit.opflow.list_ops`](/api/qiskit/0.46/qiskit.opflow.list_ops)) | `qiskit.quantum_info` [`Operators`](/api/qiskit/quantum_info#operators) |
|
|
| [`qiskit.opflow.state_fns`](/api/qiskit/0.46/qiskit.opflow.state_fns) | `qiskit.quantum_info` [`States`](/api/qiskit/quantum_info#states) |
|
|
| [`qiskit.opflow.converters`](/api/qiskit/0.46/qiskit.opflow.converters) | [`qiskit.primitives`](/api/qiskit/primitives) |
|
|
| [`qiskit.opflow.evolutions`](/api/qiskit/0.46/qiskit.opflow.evolutions) | `qiskit.synthesis` [`Evolution`](/api/qiskit/synthesis#evolution-synthesis) |
|
|
| [`qiskit.opflow.expectations`](/api/qiskit/0.46/qiskit.opflow.expectations) | [`qiskit.primitives.Estimator`](/api/qiskit/qiskit.primitives.Estimator) |
|
|
| [`qiskit.opflow.gradients`](/api/qiskit/0.46/qiskit.opflow.gradients) | [`qiskit.algorithms.gradients`](/api/qiskit/0.46/qiskit.algorithms.gradients) |
|
|
|
|
## Operator base class
|
|
|
|
The [`qiskit.opflow.OperatorBase`](/api/qiskit/0.46/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.
|
|
|
|
| Opflow | Alternative |
|
|
| --- | --- |
|
|
| [`qiskit.opflow.OperatorBase`](/api/qiskit/0.46/qiskit.opflow.OperatorBase) | `qiskit.quantum_info.BaseOperator` |
|
|
|
|
<Admonition type="note">
|
|
Despite the similar class names, [`qiskit.opflow.OperatorBase`](/api/qiskit/0.46/qiskit.opflow.OperatorBase) and
|
|
`qiskit.quantum_info.BaseOperator` are not completely equivalent, and the transition
|
|
should be handled with care. Namely:
|
|
|
|
* [`qiskit.opflow.OperatorBase`](/api/qiskit/0.46/qiskit.opflow.OperatorBase) implements a broader algebra mixin. Some operator overloads that were
|
|
commonly used in [`qiskit.opflow`](/api/qiskit/0.46/opflow) (for example `~` for `.adjoint()`) are not defined for
|
|
`qiskit.quantum_info.BaseOperator`. You might want to check the specific
|
|
[`qiskit.quantum_info`](/api/qiskit/quantum_info) subclass instead.
|
|
|
|
* [`qiskit.opflow.OperatorBase`](/api/qiskit/0.46/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.
|
|
|
|
See the [`qiskit.opflow.OperatorBase`](/api/qiskit/0.46/qiskit.opflow.OperatorBase) and [`qiskit.quantum_info.BaseOperator`](/api/qiskit/quantum_info#quantum-information) API references
|
|
for more information.
|
|
</Admonition>
|
|
|
|
## Operator globals
|
|
|
|
Opflow provided shortcuts to define common single qubit states, operators, and non-parametrized gates in the
|
|
[`operator_globals`](/api/qiskit/0.46/opflow#operator-globals) module.
|
|
|
|
These were mainly used for didactic purposes or quick prototyping and can easily be replaced by their corresponding
|
|
[`qiskit.quantum_info`](/api/qiskit/quantum_info) class: [`qiskit.quantum_info.Pauli`](/api/qiskit/qiskit.quantum_info.Pauli), [`qiskit.quantum_info.Clifford`](/api/qiskit/qiskit.quantum_info.Clifford) or
|
|
[`qiskit.quantum_info.Statevector`](/api/qiskit/qiskit.quantum_info.Statevector).
|
|
|
|
### Single-qubit Paulis
|
|
|
|
The single-qubit Paulis were commonly used for algorithm testing, 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 [`qiskit.opflow.primitive_ops.PauliSumOp`](/api/qiskit/0.46/qiskit.opflow.primitive_ops.PauliSumOp), and can be replaced by
|
|
directly creating a corresponding [`qiskit.quantum_info.SparsePauliOp`](/api/qiskit/qiskit.quantum_info.SparsePauliOp), as shown in the following examples.
|
|
|
|
| Opflow | Alternative |
|
|
| --- | --- |
|
|
| `qiskit.opflow.X`, `qiskit.opflow.Y`, `qiskit.opflow.Z`, `qiskit.opflow.I` | [`qiskit.quantum_info.Pauli`](/api/qiskit/qiskit.quantum_info.Pauli) <Admonition type="note">For direct compatibility with classes in [`qiskit.algorithms`](/api/qiskit/0.46/algorithms), wrap in [`qiskit.quantum_info.SparsePauliOp`](/api/qiskit/qiskit.quantum_info.SparsePauliOp).</Admonition> |
|
|
|
|
#### Example 1: Define the XX operator
|
|
|
|
Opflow:
|
|
|
|
```python
|
|
from qiskit.opflow import X
|
|
|
|
operator = X ^ X
|
|
print(repr(operator))
|
|
```
|
|
|
|
```python
|
|
PauliOp(Pauli('XX'), coeff=1.0)
|
|
```
|
|
|
|
Alternative:
|
|
|
|
```python
|
|
from qiskit.quantum_info import Pauli, SparsePauliOp
|
|
|
|
operator = Pauli('XX')
|
|
|
|
# equivalent to:
|
|
X = Pauli('X')
|
|
operator = X ^ X
|
|
print("As Pauli Op: ", repr(operator))
|
|
|
|
# another alternative is:
|
|
operator = SparsePauliOp('XX')
|
|
print("As Sparse Pauli Op: ", repr(operator))
|
|
```
|
|
|
|
```text
|
|
As Pauli Op: Pauli('XX')
|
|
As Sparse Pauli Op: SparsePauliOp(['XX'],
|
|
coeffs=[1.+0.j])
|
|
```
|
|
|
|
#### Example 2: Define a more complex operator
|
|
|
|
Opflow:
|
|
|
|
```python
|
|
from qiskit.opflow import I, X, Z, PauliSumOp
|
|
|
|
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))
|
|
```
|
|
|
|
```python
|
|
PauliSumOp(SparsePauliOp(['IZI', 'IXX'],
|
|
coeffs=[0.39+0.j, 0.5 +0.j]), coeff=1.0)
|
|
```
|
|
|
|
Alternative:
|
|
|
|
```python
|
|
from qiskit.quantum_info import SparsePauliOp
|
|
|
|
operator = SparsePauliOp(["IZI", "IXX"], coeffs = [0.39, 0.5])
|
|
|
|
# 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))
|
|
```
|
|
|
|
```python
|
|
SparsePauliOp(['IZI', 'IXX'],
|
|
coeffs=[0.39+0.j, 0.5 +0.j])
|
|
```
|
|
|
|
### Common non-parametrized gates (Clifford)
|
|
|
|
| Opflow | Alternative |
|
|
| --- | --- |
|
|
| `qiskit.opflow.CX`, `qiskit.opflow.S`, `qiskit.opflow.H`, `qiskit.opflow.T`, `qiskit.opflow.CZ`, `qiskit.opflow.Swap` | Append corresponding gate to [`qiskit.circuit.QuantumCircuit`](/api/qiskit/qiskit.circuit.QuantumCircuit). If necessary, a [`qiskit.quantum_info.Operator`](/api/qiskit/qiskit.quantum_info.Operator) can be directly constructed from quantum circuits. Another alternative is to wrap the circuit in [`qiskit.quantum_info.Clifford`](/api/qiskit/qiskit.quantum_info.Clifford) and call `Clifford.to_operator()` <Admonition type="note">Constructing [`qiskit.quantum_info`](/api/qiskit/quantum_info) operators from circuits is not efficient, as it is a dense operation and scales exponentially with the size of the circuit.</Admonition> |
|
|
|
|
#### Example 1: Define the HH operator
|
|
|
|
Opflow:
|
|
|
|
```python
|
|
from qiskit.opflow import H
|
|
|
|
operator = H ^ H
|
|
print(operator)
|
|
```
|
|
|
|
```text
|
|
┌───┐
|
|
q_0: ┤ H ├
|
|
├───┤
|
|
q_1: ┤ H ├
|
|
└───┘
|
|
```
|
|
|
|
Alternative:
|
|
|
|
```python
|
|
from qiskit import QuantumCircuit
|
|
from qiskit.quantum_info import Clifford, Operator
|
|
|
|
qc = QuantumCircuit(2)
|
|
qc.h(0)
|
|
qc.h(1)
|
|
print(qc)
|
|
```
|
|
|
|
```text
|
|
┌───┐
|
|
q_0: ┤ H ├
|
|
├───┤
|
|
q_1: ┤ H ├
|
|
└───┘
|
|
```
|
|
|
|
To turn this circuit into an operator, you can do the following:
|
|
|
|
```python
|
|
operator = Clifford(qc).to_operator()
|
|
|
|
# or, directly
|
|
operator = Operator(qc)
|
|
|
|
print(operator)
|
|
```
|
|
|
|
```python
|
|
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
|
|
|
|
| Opflow | Alternative |
|
|
| --- | --- |
|
|
| `qiskit.opflow.Zero`, `qiskit.opflow.One`, `qiskit.opflow.Plus`, `qiskit.opflow.Minus` | [`qiskit.quantum_info.Statevector`](/api/qiskit/qiskit.quantum_info.Statevector) or [`qiskit.circuit.QuantumCircuit`](/api/qiskit/qiskit.circuit.QuantumCircuit), depending on the use case. <Admonition type="note">To efficiently simulate stabilizer states, [`qiskit.quantum_info`](/api/qiskit/quantum_info) includes a [`qiskit.quantum_info.StabilizerState`](/api/qiskit/qiskit.quantum_info.StabilizerState) class. See the [`qiskit.quantum_info.StabilizerState`](/api/qiskit/qiskit.quantum_info.StabilizerState) API reference for more information.</Admonition> |
|
|
|
|
#### Example 1: Stabilizer states
|
|
|
|
Opflow:
|
|
|
|
```python
|
|
from qiskit.opflow import Zero, One, Plus, Minus
|
|
|
|
# Zero, One, Plus, Minus are all stabilizer states
|
|
state1 = Zero ^ One
|
|
state2 = Plus ^ Minus
|
|
|
|
print("State 1: ", state1)
|
|
print("State 2: ", state2)
|
|
```
|
|
|
|
```text
|
|
State 1: DictStateFn({'01': 1})
|
|
State 2: CircuitStateFn(
|
|
┌───┐┌───┐
|
|
q_0: ┤ X ├┤ H ├
|
|
├───┤└───┘
|
|
q_1: ┤ H ├─────
|
|
└───┘
|
|
)
|
|
```
|
|
|
|
Alternative:
|
|
|
|
```python
|
|
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)
|
|
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)
|
|
print("State 2: ", state2)
|
|
```
|
|
|
|
```text
|
|
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
|
|
|
|
Most of the workflows that previously relied on components from [`qiskit.opflow.primitive_ops`](/api/qiskit/0.46/qiskit.opflow.primitive_ops) and
|
|
[`qiskit.opflow.list_ops`](/api/qiskit/0.46/qiskit.opflow.list_ops) can now leverage elements from [`qiskit.quantum_info`](/api/qiskit/quantum_info)
|
|
operators instead.
|
|
Some of these classes do not require a one-to-one replacement because they were created to interface with other
|
|
opflow components.
|
|
|
|
### Primitive Ops
|
|
|
|
[`qiskit.opflow.primitive_ops.PrimitiveOp`](/api/qiskit/0.46/qiskit.opflow.primitive_ops.PrimitiveOp) is the [`qiskit.opflow.primitive_ops`](/api/qiskit/0.46/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.
|
|
|
|
<Admonition type="tip">
|
|
Interpreting [`qiskit.opflow.primitive_ops.PrimitiveOp`](/api/qiskit/0.46/qiskit.opflow.primitive_ops.PrimitiveOp) as a factory class:
|
|
|
|
| Class passed to [`qiskit.opflow.primitive_ops.PrimitiveOp`](/api/qiskit/0.46/qiskit.opflow.primitive_ops.PrimitiveOp) | Subclass returned |
|
|
| --- | --- |
|
|
| [`qiskit.quantum_info.Pauli`](/api/qiskit/qiskit.quantum_info.Pauli) | [`qiskit.opflow.primitive_ops.PauliOp`](/api/qiskit/0.46/qiskit.opflow.primitive_ops.PauliOp) |
|
|
| [`qiskit.circuit.Instruction`](/api/qiskit/qiskit.circuit.Instruction), [`qiskit.circuit.QuantumCircuit`](/api/qiskit/qiskit.circuit.QuantumCircuit) | [`qiskit.opflow.primitive_ops.CircuitOp`](/api/qiskit/0.46/qiskit.opflow.primitive_ops.CircuitOp) |
|
|
| `list`, `np.ndarray`, `scipy.sparse.spmatrix`, [`qiskit.quantum_info.Operator`](/api/qiskit/qiskit.quantum_info.Operator) | [`qiskit.opflow.primitive_ops.MatrixOp`](/api/qiskit/0.46/qiskit.opflow.primitive_ops.MatrixOp) |
|
|
</Admonition>
|
|
|
|
When migrating opflow code, it is important to look for alternatives to replace the specific subclasses that
|
|
are used within the original code:
|
|
|
|
| Opflow | Alternative |
|
|
| --- | --- |
|
|
| [`qiskit.opflow.primitive_ops.PrimitiveOp`](/api/qiskit/0.46/qiskit.opflow.primitive_ops.PrimitiveOp) | As mentioned previously, this class is used to generate an instance of one of the classes below, so there is no direct replacement. |
|
|
| [`qiskit.opflow.primitive_ops.CircuitOp`](/api/qiskit/0.46/qiskit.opflow.primitive_ops.CircuitOp) | [`qiskit.circuit.QuantumCircuit`](/api/qiskit/qiskit.circuit.QuantumCircuit) |
|
|
| [`qiskit.opflow.primitive_ops.MatrixOp`](/api/qiskit/0.46/qiskit.opflow.primitive_ops.MatrixOp) | [`qiskit.quantum_info.Operator`](/api/qiskit/qiskit.quantum_info.Operator) |
|
|
| [`qiskit.opflow.primitive_ops.PauliOp`](/api/qiskit/0.46/qiskit.opflow.primitive_ops.PauliOp) | [`qiskit.quantum_info.Pauli`](/api/qiskit/qiskit.quantum_info.Pauli). For direct compatibility with classes in [`qiskit.algorithms`](/api/qiskit/0.46/algorithms), wrap in [`qiskit.quantum_info.SparsePauliOp`](/api/qiskit/qiskit.quantum_info.SparsePauliOp). |
|
|
| [`qiskit.opflow.primitive_ops.PauliSumOp`](/api/qiskit/0.46/qiskit.opflow.primitive_ops.PauliSumOp) | [`qiskit.quantum_info.SparsePauliOp`](/api/qiskit/qiskit.quantum_info.SparsePauliOp). See example [below](#example-pauli-sum-op). |
|
|
| [`qiskit.opflow.primitive_ops.TaperedPauliSumOp`](/api/qiskit/0.46/qiskit.opflow.primitive_ops.TaperedPauliSumOp) | This class was used to combine a [`qiskit.opflow.primitive_ops.PauliSumOp`](/api/qiskit/0.46/qiskit.opflow.primitive_ops.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.analysis.Z2Symmetries`](/api/qiskit/qiskit.quantum_info.Z2Symmetries) example for updated workflow. |
|
|
| [`qiskit.opflow.primitive_ops.Z2Symmetries`](/api/qiskit/0.46/qiskit.opflow.primitive_ops.Z2Symmetries) | [`qiskit.quantum_info.analysis.Z2Symmetries`](/api/qiskit/qiskit.quantum_info.Z2Symmetries). See example [below](#example-z2-sym). |
|
|
|
|
<span id="example-pauli-sum-op"></span>
|
|
|
|
#### Example 1: `PauliSumOp`
|
|
|
|
Opflow:
|
|
|
|
```python
|
|
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))
|
|
```
|
|
|
|
```python
|
|
PauliSumOp(SparsePauliOp(['XYZY'],
|
|
coeffs=[2.+0.j]), coeff=(-0-3j))
|
|
```
|
|
|
|
Alternative:
|
|
|
|
```python
|
|
from qiskit.quantum_info import SparsePauliOp, Pauli
|
|
|
|
qubit_op = SparsePauliOp(Pauli("XYZY"), coeffs=[-6j])
|
|
print(repr(qubit_op))
|
|
```
|
|
|
|
```python
|
|
SparsePauliOp(['XYZY'],
|
|
coeffs=[0.-6.j])
|
|
```
|
|
|
|
<span id="example-z2-sym"></span>
|
|
|
|
#### Example 2: `Z2Symmetries` and `TaperedPauliSumOp`
|
|
|
|
Opflow:
|
|
|
|
```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)
|
|
print(z2_symmetries)
|
|
|
|
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)
|
|
print("Tapered PauliSumOp: ", tapered_op)
|
|
```
|
|
|
|
```text
|
|
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 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
|
|
```
|
|
|
|
Alternative:
|
|
|
|
```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)
|
|
print(z2_symmetries)
|
|
|
|
tapered_op = z2_symmetries.taper(qubit_op)
|
|
print("Tapered Op from Z2 symmetries: ", tapered_op)
|
|
```
|
|
|
|
```text
|
|
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 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
|
|
|
|
The [`qiskit.opflow.list_ops`](/api/qiskit/0.46/qiskit.opflow.list_ops) module contained classes for manipulating lists of [`qiskit.opflow.primitive_ops`](/api/qiskit/0.46/qiskit.opflow.primitive_ops)
|
|
or [`qiskit.opflow.state_fns`](/api/qiskit/0.46/qiskit.opflow.state_fns). The [`qiskit.quantum_info`](/api/qiskit/quantum_info) alternatives for this functionality are
|
|
[`qiskit.quantum_info.PauliList`](/api/qiskit/qiskit.quantum_info.PauliList) and [`qiskit.quantum_info.SparsePauliOp`](/api/qiskit/qiskit.quantum_info.SparsePauliOp) (for sums of [`qiskit.quantum_info.Pauli`](/api/qiskit/qiskit.quantum_info.Pauli)s).
|
|
|
|
| Opflow | Alternative |
|
|
| --- | --- |
|
|
| [`qiskit.opflow.list_ops.ListOp`](/api/qiskit/0.46/qiskit.opflow.list_ops.ListOp) | No direct replacement. This is the base class for operator lists. In general, these could be replaced with a Python `list`. For [`qiskit.quantum_info.Pauli`](/api/qiskit/qiskit.quantum_info.Pauli) operators, there are a few alternatives, depending on the use case. One alternative is [`qiskit.quantum_info.PauliList`](/api/qiskit/qiskit.quantum_info.PauliList). |
|
|
| [`qiskit.opflow.list_ops.ComposedOp`](/api/qiskit/0.46/qiskit.opflow.list_ops.ComposedOp) | No direct replacement. Current workflows do not require composing states and operators within one object (no lazy evaluation). |
|
|
| [`qiskit.opflow.list_ops.SummedOp`](/api/qiskit/0.46/qiskit.opflow.list_ops.SummedOp) | No direct replacement. For [`qiskit.quantum_info.Pauli`](/api/qiskit/qiskit.quantum_info.Pauli) operators, use [`qiskit.quantum_info.SparsePauliOp`](/api/qiskit/qiskit.quantum_info.SparsePauliOp). |
|
|
| [`qiskit.opflow.list_ops.TensoredOp`](/api/qiskit/0.46/qiskit.opflow.list_ops.TensoredOp) | No direct replacement. For [`qiskit.quantum_info.Pauli`](/api/qiskit/qiskit.quantum_info.Pauli) operators, use [`qiskit.quantum_info.SparsePauliOp`](/api/qiskit/qiskit.quantum_info.SparsePauliOp). |
|
|
|
|
## State functions
|
|
|
|
The [`qiskit.opflow.state_fns`](/api/qiskit/0.46/qiskit.opflow.state_fns) module can generally be replaced by subclasses of the [`qiskit.quantum_info`](/api/qiskit/quantum_info)
|
|
`qiskit.quantum_info.states.quantum_state.QuantumState`.
|
|
|
|
Similar to [`qiskit.opflow.primitive_ops.PrimitiveOp`](/api/qiskit/0.46/qiskit.opflow.primitive_ops.PrimitiveOp), [`qiskit.opflow.state_fns.StateFn`](/api/qiskit/0.46/qiskit.opflow.state_fns.StateFn)
|
|
acts as a factory to create the corresponding subclass depending on the computational primitive used to initialize it.
|
|
|
|
<Admonition type="tip">
|
|
Interpreting [`qiskit.opflow.state_fns.StateFn`](/api/qiskit/0.46/qiskit.opflow.state_fns.StateFn) as a factory class:
|
|
|
|
| Class passed to [`qiskit.opflow.state_fns.StateFn`](/api/qiskit/0.46/qiskit.opflow.state_fns.StateFn) | Subclass returned |
|
|
| --- | --- |
|
|
| `str`, `dict`, [`qiskit.result.Result`](/api/qiskit/qiskit.result.Result) | [`qiskit.opflow.state_fns.DictStateFn`](/api/qiskit/0.46/qiskit.opflow.state_fns.DictStateFn) |
|
|
| `list`, `np.ndarray`, [`qiskit.quantum_info.Statevector`](/api/qiskit/qiskit.quantum_info.Statevector) | [`qiskit.opflow.state_fns.VectorStateFn`](/api/qiskit/0.46/qiskit.opflow.state_fns.VectorStateFn) |
|
|
| [`qiskit.circuit.QuantumCircuit`](/api/qiskit/qiskit.circuit.QuantumCircuit), [`qiskit.circuit.Instruction`](/api/qiskit/qiskit.circuit.Instruction) | [`qiskit.opflow.state_fns.CircuitStateFn`](/api/qiskit/0.46/qiskit.opflow.state_fns.CircuitStateFn) |
|
|
| [`qiskit.opflow.OperatorBase`](/api/qiskit/0.46/qiskit.opflow.OperatorBase) | [`qiskit.opflow.state_fns.OperatorStateFn`](/api/qiskit/0.46/qiskit.opflow.state_fns.OperatorStateFn) |
|
|
</Admonition>
|
|
|
|
Examine references to [`qiskit.opflow.state_fns.StateFn`](/api/qiskit/0.46/qiskit.opflow.state_fns.StateFn) in opflow code to identify the subclass that is being used, then find an alternative.
|
|
|
|
| Opflow | Alternative |
|
|
| --- | --- |
|
|
| [`qiskit.opflow.state_fns.StateFn`](/api/qiskit/0.46/qiskit.opflow.state_fns.StateFn) | In most cases, [`qiskit.quantum_info.Statevector`](/api/qiskit/qiskit.quantum_info.Statevector). However, remember that [`qiskit.opflow.state_fns.StateFn`](/api/qiskit/0.46/qiskit.opflow.state_fns.StateFn) is a factory class. |
|
|
| [`qiskit.opflow.state_fns.CircuitStateFn`](/api/qiskit/0.46/qiskit.opflow.state_fns.CircuitStateFn) | [`qiskit.quantum_info.Statevector`](/api/qiskit/qiskit.quantum_info.Statevector) |
|
|
| [`qiskit.opflow.state_fns.DictStateFn`](/api/qiskit/0.46/qiskit.opflow.state_fns.DictStateFn) | This class was used to store efficient representations of sparse measurement results. The [`qiskit.primitives.Sampler`](/api/qiskit/qiskit.primitives.Sampler) now returns the measurements as an instance of [`qiskit.result.QuasiDistribution`](/api/qiskit/qiskit.result.QuasiDistribution). See the example in [`Converters`](#converters). |
|
|
| [`qiskit.opflow.state_fns.VectorStateFn`](/api/qiskit/0.46/qiskit.opflow.state_fns.VectorStateFn) | This class can be replaced with [`qiskit.quantum_info.Statevector`](/api/qiskit/qiskit.quantum_info.Statevector) or [`qiskit.quantum_info.StabilizerState`](/api/qiskit/qiskit.quantum_info.StabilizerState), for Clifford-based vectors.|
|
|
| [`qiskit.opflow.state_fns.SparseVectorStateFn`](/api/qiskit/0.46/qiskit.opflow.state_fns.SparseVectorStateFn) | No direct replacement. This class was used for sparse statevector representations. |
|
|
| [`qiskit.opflow.state_fns.OperatorStateFn`](/api/qiskit/0.46/qiskit.opflow.state_fns.OperatorStateFn) | No direct replacement. This class was used to represent measurements against operators. |
|
|
| [`qiskit.opflow.state_fns.CVaRMeasurement`](/api/qiskit/0.46/qiskit.opflow.state_fns.CVaRMeasurement) | Used in [`qiskit.opflow.expectations.CVaRExpectation`](/api/qiskit/0.46/qiskit.opflow.expectations.CVaRExpectation). This function is now covered by [`qiskit.algorithms.minimum_eigensolvers.SamplingVQE`](/api/qiskit/0.46/qiskit.algorithms.minimum_eigensolvers.SamplingVQE). See the example in [`Expectations`](#expectations). |
|
|
|
|
### Example 1: Apply an operator to a state
|
|
|
|
Opflow:
|
|
|
|
```python
|
|
|
|
from qiskit.opflow import StateFn, X, Y
|
|
from qiskit import QuantumCircuit
|
|
|
|
qc = QuantumCircuit(2)
|
|
qc.x(0)
|
|
qc.z(1)
|
|
op = X ^ Y
|
|
state = StateFn(qc)
|
|
|
|
comp = ~op @ state
|
|
eval = comp.eval()
|
|
|
|
print(state)
|
|
print(comp)
|
|
print(repr(eval))
|
|
```
|
|
|
|
```text
|
|
CircuitStateFn(
|
|
┌───┐
|
|
q_0: ┤ X ├
|
|
├───┤
|
|
q_1: ┤ Z ├
|
|
└───┘
|
|
)
|
|
CircuitStateFn(
|
|
┌───┐┌────────────┐
|
|
q_0: ┤ X ├┤0 ├
|
|
├───┤│ Pauli(XY) │
|
|
q_1: ┤ Z ├┤1 ├
|
|
└───┘└────────────┘
|
|
)
|
|
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)), coeff=1.0, is_measurement=False)
|
|
```
|
|
|
|
Alternative:
|
|
|
|
```python
|
|
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)
|
|
|
|
eval = state.evolve(op)
|
|
|
|
print(state)
|
|
print(eval)
|
|
```
|
|
|
|
```python
|
|
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))
|
|
```
|
|
|
|
See more examples in [Expectations](#expectations) and [Converters](#converters).
|
|
|
|
## Converters
|
|
|
|
The role of the [`qiskit.opflow.converters`](/api/qiskit/0.46/qiskit.opflow.converters) submodule was to convert the operators into other opflow operator classes:
|
|
([`qiskit.opflow.converters.TwoQubitReduction`](/api/qiskit/0.46/qiskit.opflow.converters.TwoQubitReduction), [`qiskit.opflow.converters.PauliBasisChange`](/api/qiskit/0.46/qiskit.opflow.converters.PauliBasisChange), and so on).
|
|
The [`qiskit.opflow.converters.CircuitSampler`](/api/qiskit/0.46/qiskit.opflow.converters.CircuitSampler) traversed an operator and outputted
|
|
approximations of its state functions using a quantum computer.
|
|
This functionality has been replaced by the [`qiskit.primitives`](/api/qiskit/primitives).
|
|
|
|
| Opflow | Alternative |
|
|
| --- | --- |
|
|
| [`qiskit.opflow.converters.CircuitSampler`](/api/qiskit/0.46/qiskit.opflow.converters.CircuitSampler) | [`qiskit.primitives.Sampler`](/api/qiskit/qiskit.primitives.Sampler) or [`qiskit.primitives.Estimator`](/api/qiskit/qiskit.primitives.Estimator) if used with [`qiskit.opflow.expectations`](/api/qiskit/0.46/qiskit.opflow.expectations). See examples [below]( #example-convert-state). |
|
|
| [`qiskit.opflow.converters.AbelianGrouper`](/api/qiskit/0.46/qiskit.opflow.converters.AbelianGrouper) | This class allowed a sum a of Pauli operators to be grouped. Similar functionality can be achieved through the [`qiskit.quantum_info.SparsePauliOp.group_commuting`](/api/qiskit/qiskit.quantum_info.SparsePauliOp#group_commuting) method of [`qiskit.quantum_info.SparsePauliOp`](/api/qiskit/qiskit.quantum_info.SparsePauliOp). However, this is not a one-to-one replacement, as you can see in the example [below](#example-commuting). |
|
|
| [`qiskit.opflow.converters.DictToCircuitSum`](/api/qiskit/0.46/qiskit.opflow.converters.DictToCircuitSum) | No direct replacement. This class was used to convert from [`qiskit.opflow.state_fns.DictStateFn`](/api/qiskit/0.46/qiskit.opflow.state_fns.DictStateFn) or [`qiskit.opflow.state_fns.VectorStateFn`](/api/qiskit/0.46/qiskit.opflow.state_fns.VectorStateFn) to an equivalent [`qiskit.opflow.state_fns.CircuitStateFn`](/api/qiskit/0.46/qiskit.opflow.state_fns.CircuitStateFn). |
|
|
| [`qiskit.opflow.converters.PauliBasisChange`](/api/qiskit/0.46/qiskit.opflow.converters.PauliBasisChange) | No direct replacement. This class was used for changing Paulis into other bases. |
|
|
| [`qiskit.opflow.converters.TwoQubitReduction`](/api/qiskit/0.46/qiskit.opflow.converters.TwoQubitReduction) | No direct replacement. This class implements a chemistry-specific reduction for the `ParityMapper` class in [Qiskit Nature](https://qiskit.org/ecosystem/nature/). The general symmetry logic this mapper depends on has been refactored to other classes in [`qiskit.quantum_info`](/api/qiskit/quantum_info), so this specific [`qiskit.opflow`](/api/qiskit/0.46/opflow) implementation is no longer necessary. |
|
|
|
|
<span id="example-convert-state"></span>
|
|
|
|
### Example 1: `CircuitSampler` for sampling parametrized circuits
|
|
|
|
Opflow:
|
|
|
|
```python
|
|
from qiskit.circuit import QuantumCircuit, Parameter
|
|
from qiskit.opflow import ListOp, StateFn, CircuitSampler
|
|
from qiskit_aer import AerSimulator
|
|
|
|
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(AerSimulator())
|
|
sampled = sampler.convert(listop, params=bindings).eval()
|
|
|
|
for s in sampled:
|
|
print(s)
|
|
```
|
|
|
|
```text
|
|
SparseVectorStateFn( (0, 0) 1.0)
|
|
SparseVectorStateFn( (0, 0) 1.0)
|
|
SparseVectorStateFn( (0, 0) 1.0)
|
|
```
|
|
|
|
Alternative:
|
|
|
|
```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)
|
|
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]]
|
|
|
|
sampler = Sampler()
|
|
sampled = sampler.run(circuits, param_values).result().quasi_dists
|
|
|
|
print(sampled)
|
|
```
|
|
|
|
```python
|
|
[{0: 1.0}, {0: 1.0}, {0: 1.0}]
|
|
```
|
|
|
|
### Example 2: `CircuitSampler` for computing expectation values
|
|
|
|
Opflow:
|
|
|
|
```python
|
|
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
|
|
|
|
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)
|
|
```
|
|
|
|
```python
|
|
1.0000000000000002
|
|
```
|
|
|
|
Alternative:
|
|
|
|
```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.real
|
|
|
|
print(expectation_value)
|
|
```
|
|
|
|
```python
|
|
[1.]
|
|
```
|
|
|
|
<span id="example-commuting"></span>
|
|
|
|
### Example 3: `AbelianGrouper` for grouping operators
|
|
|
|
Opflow:
|
|
|
|
```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)
|
|
|
|
print(repr(grouped_sum))
|
|
```
|
|
|
|
```python
|
|
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:
|
|
|
|
```python
|
|
from qiskit.quantum_info import SparsePauliOp
|
|
|
|
op = SparsePauliOp.from_list([("XX", 2), ("YY", 1), ("IZ",2j), ("ZZ",1j)])
|
|
|
|
grouped = op.group_commuting()
|
|
grouped_sum = op.group_commuting(qubit_wise=True)
|
|
|
|
print(repr(grouped))
|
|
print(repr(grouped_sum))
|
|
```
|
|
|
|
```text
|
|
[SparsePauliOp(['IZ', 'ZZ'],
|
|
coeffs=[0.+2.j, 0.+1.j]), SparsePauliOp(['XX', 'YY'],
|
|
coeffs=[2.+0.j, 1.+0.j])]
|
|
[SparsePauliOp(['XX'],
|
|
coeffs=[2.+0.j]), SparsePauliOp(['YY'],
|
|
coeffs=[1.+0.j]), SparsePauliOp(['IZ', 'ZZ'],
|
|
coeffs=[0.+2.j, 0.+1.j])]
|
|
```
|
|
|
|
## Evolutions
|
|
|
|
The [`qiskit.opflow.evolutions`](/api/qiskit/0.46/qiskit.opflow.evolutions) submodule was created to provide building blocks for Hamiltonian simulation algorithms,
|
|
including 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` [Evolution Synthesis](/api/qiskit/synthesis#evolution-synthesis) module.
|
|
|
|
<Admonition type="note">
|
|
The [`qiskit.opflow.evolutions.PauliTrotterEvolution`](/api/qiskit/0.46/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
|
|
[`qiskit.opflow.evolutions.EvolvedOp`](/api/qiskit/0.46/qiskit.opflow.evolutions.EvolvedOp) placeholders for the operators,
|
|
constructing [`qiskit.circuit.library.PauliEvolutionGate`s](/api/qiskit/qiskit.circuit.library.PauliEvolutionGate) out of the operator primitives, and supplying one of
|
|
the desired synthesis methods to perform the Trotterization. The methods can be specified by using a
|
|
`string`, which is then inputted into a [`qiskit.opflow.evolutions.TrotterizationFactory`](/api/qiskit/0.46/qiskit.opflow.evolutions.TrotterizationFactory),
|
|
or by supplying a method instance of [`qiskit.opflow.evolutions.Trotter`](/api/qiskit/0.46/qiskit.opflow.evolutions.Trotter),
|
|
[`qiskit.opflow.evolutions.Suzuki`](/api/qiskit/0.46/qiskit.opflow.evolutions.Suzuki) or [`qiskit.opflow.evolutions.QDrift`](/api/qiskit/0.46/qiskit.opflow.evolutions.QDrift).
|
|
|
|
The Trotterization methods that extend [`qiskit.opflow.evolutions.TrotterizationBase`](/api/qiskit/0.46/qiskit.opflow.evolutions.TrotterizationBase) were migrated to
|
|
[`qiskit.synthesis`](/api/qiskit/synthesis)
|
|
and now extend the [`qiskit.synthesis.ProductFormula`](/api/qiskit/qiskit.synthesis.ProductFormula) base class. They no longer contain a `.convert()` method for
|
|
standalone use, but are designed to be plugged into the [`qiskit.circuit.library.PauliEvolutionGate`](/api/qiskit/qiskit.circuit.library.PauliEvolutionGate) and called by using `.synthesize()`.
|
|
In this context, the job of the [`qiskit.opflow.evolutions.PauliTrotterEvolution`](/api/qiskit/0.46/qiskit.opflow.evolutions.PauliTrotterEvolution) class can now be handled directly by the algorithms, for example, [`qiskit.algorithms.time_evolvers.trotterization.TrotterQRTE`](/api/qiskit/0.46/qiskit.algorithms.time_evolvers.trotterization.TrotterQRTE).
|
|
|
|
Similarly, the [`qiskit.opflow.evolutions.MatrixEvolution`](/api/qiskit/0.46/qiskit.opflow.evolutions.MatrixEvolution) class performs evolution by classical matrix exponentiation,
|
|
constructing a circuit with [`qiskit.extensions.UnitaryGate`s](/api/qiskit/0.44/qiskit.extensions.UnitaryGate) or [`qiskit.extensions.HamiltonianGate`s](/api/qiskit/0.44/qiskit.extensions.HamiltonianGate) that contain the exponentiation of the operator.
|
|
This class is no longer necessary, as the [`qiskit.extensions.HamiltonianGate`s](/api/qiskit/0.44/qiskit.extensions.HamiltonianGate) can be directly handled by the algorithms.
|
|
</Admonition>
|
|
|
|
### Trotterizations
|
|
|
|
| Opflow | Alternative |
|
|
| --- | --- |
|
|
| [`qiskit.opflow.evolutions.TrotterizationFactory`](/api/qiskit/0.46/qiskit.opflow.evolutions.TrotterizationFactory) | No direct replacement. This class was used to create instances of one of the classes listed below. |
|
|
| [`qiskit.opflow.evolutions.Trotter`](/api/qiskit/0.46/qiskit.opflow.evolutions.Trotter) | [`qiskit.synthesis.SuzukiTrotter`](/api/qiskit/qiskit.synthesis.SuzukiTrotter) or [`qiskit.synthesis.LieTrotter`](/api/qiskit/qiskit.synthesis.LieTrotter) |
|
|
| [`qiskit.opflow.evolutions.Suzuki`](/api/qiskit/0.46/qiskit.opflow.evolutions.Suzuki) | [`qiskit.synthesis.SuzukiTrotter`](/api/qiskit/qiskit.synthesis.SuzukiTrotter) |
|
|
| [`qiskit.opflow.evolutions.QDrift`](/api/qiskit/0.46/qiskit.opflow.evolutions.QDrift) | [`qiskit.synthesis.QDrift`](/api/qiskit/qiskit.synthesis.QDrift) |
|
|
|
|
### Other evolution classes
|
|
|
|
| Opflow | Alternative |
|
|
| --- | --- |
|
|
| [`qiskit.opflow.evolutions.EvolutionFactory`](/api/qiskit/0.46/qiskit.opflow.evolutions.EvolutionFactory) | No direct replacement. This class was used to create instances of one of the classes listed below. |
|
|
| [`qiskit.opflow.evolutions.EvolvedOp`](/api/qiskit/0.46/qiskit.opflow.evolutions.EvolvedOp) | No direct replacement. The workflow no longer requires a specific operator for evolutions. |
|
|
| [`qiskit.opflow.evolutions.MatrixEvolution`](/api/qiskit/0.46/qiskit.opflow.evolutions.MatrixEvolution) | [`qiskit.extensions.HamiltonianGate`](/api/qiskit/0.44/qiskit.extensions.HamiltonianGate) |
|
|
| [`qiskit.opflow.evolutions.PauliTrotterEvolution`](/api/qiskit/0.46/qiskit.opflow.evolutions.PauliTrotterEvolution) | [`qiskit.circuit.library.PauliEvolutionGate`](/api/qiskit/qiskit.circuit.library.PauliEvolutionGate) |
|
|
|
|
#### Example 1: Trotter evolution
|
|
|
|
Opflow:
|
|
|
|
```python
|
|
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()
|
|
|
|
print(evolved_state)
|
|
```
|
|
|
|
```text
|
|
┌─────────────────────┐
|
|
q: ┤ exp(-it (X + Z))(1) ├
|
|
└─────────────────────┘
|
|
```
|
|
|
|
Alternative:
|
|
|
|
```python
|
|
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])
|
|
|
|
print(evolved_state)
|
|
```
|
|
|
|
```text
|
|
┌─────────────────────┐
|
|
q: ┤ exp(-it (X + Z))(1) ├
|
|
└─────────────────────┘
|
|
```
|
|
|
|
#### Example 2: Evolution with time-dependent Hamiltonian
|
|
|
|
Opflow:
|
|
|
|
```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()
|
|
|
|
print(evolved_state)
|
|
```
|
|
|
|
```text
|
|
┌─────────────────────────┐
|
|
q: ┤ exp(-it (X + Y))(1.0*t) ├
|
|
└─────────────────────────┘
|
|
```
|
|
|
|
Alternative:
|
|
|
|
```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)
|
|
```
|
|
|
|
```text
|
|
┌─────────────────────┐
|
|
q: ┤ exp(-it (X + Y))(t) ├
|
|
└─────────────────────┘
|
|
|
|
```
|
|
|
|
#### Example 3: Matrix evolution
|
|
|
|
Opflow:
|
|
|
|
```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()
|
|
|
|
print(evolved_state.decompose().decompose())
|
|
```
|
|
|
|
```X
|
|
┌────────────────┐
|
|
q: ┤ U3(2,-π/2,π/2) ├
|
|
└────────────────┘
|
|
```
|
|
|
|
Alternative:
|
|
|
|
```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())
|
|
```
|
|
|
|
```text
|
|
┌────────────────┐
|
|
q: ┤ U3(2,-π/2,π/2) ├
|
|
└────────────────┘
|
|
```
|
|
|
|
## Expectations
|
|
|
|
Expectations are converters that enable an observable's expectation value to be computed with respect to some state function.
|
|
This function can now be found in the [`qiskit.primitives.Estimator`](/api/qiskit/qiskit.primitives.Estimator) primitive. Remember that there
|
|
are different Estimator implementations, as noted [previously.](#attention_primitives)
|
|
|
|
### Algorithm-Agnostic Expectations
|
|
|
|
| Opflow | Alternative |
|
|
| --- | --- |
|
|
| [`qiskit.opflow.expectations.ExpectationFactory`](/api/qiskit/0.46/qiskit.opflow.expectations.ExpectationFactory) | No direct replacement. This class was used to create instances of one of the classes listed below. |
|
|
| [`qiskit.opflow.expectations.AerPauliExpectation`](/api/qiskit/0.46/qiskit.opflow.expectations.AerPauliExpectation) | Use [`qiskit_aer.primitives.Estimator`](https://qiskit.org/ecosystem/aer/stubs/qiskit_aer.primitives.Estimator.html). See example below. |
|
|
| [`qiskit.opflow.expectations.MatrixExpectation`](/api/qiskit/0.46/qiskit.opflow.expectations.MatrixExpectation) | Use [`qiskit.primitives.Estimator`](/api/qiskit/qiskit.primitives.Estimator) primitive. If no shots are set, it performs an exact Statevector calculation. See example below. |
|
|
| [`qiskit.opflow.expectations.PauliExpectation`](/api/qiskit/0.46/qiskit.opflow.expectations.PauliExpectation) | Use any Estimator primitive. For [`qiskit.primitives.Estimator`](/api/qiskit/qiskit.primitives.Estimator), set `shots!=None` for a shotbased simulation. For [`qiskit_aer.primitives.Estimator`](https://qiskit.org/ecosystem/aer/stubs/qiskit_aer.primitives.Estimator.html), this is the default. |
|
|
|
|
#### Example 1: Aer Pauli expectation
|
|
|
|
Opflow:
|
|
|
|
```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)
|
|
|
|
sampler = CircuitSampler(q_instance, attach_results=True)
|
|
expectation = AerPauliExpectation()
|
|
|
|
state = Minus
|
|
operator = 1j * X
|
|
|
|
converted_meas = expectation.convert(StateFn(operator, is_measurement=True) @ state)
|
|
expectation_value = sampler.convert(converted_meas).eval()
|
|
|
|
print(expectation_value)
|
|
```
|
|
|
|
```python
|
|
-1j
|
|
```
|
|
|
|
Alternative:
|
|
|
|
```python
|
|
from qiskit.quantum_info import SparsePauliOp
|
|
from qiskit import QuantumCircuit
|
|
from qiskit_aer.primitives import Estimator
|
|
|
|
estimator = Estimator()
|
|
|
|
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
|
|
|
|
print(expectation_value)
|
|
```
|
|
|
|
```python
|
|
[0.-1.j]
|
|
```
|
|
|
|
|
|
#### Example 2: Matrix expectation
|
|
|
|
Opflow:
|
|
|
|
```python
|
|
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()
|
|
|
|
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()
|
|
|
|
print(values_plus)
|
|
```
|
|
|
|
```python
|
|
[(1+0j), (0.7071067811865476+0j)]
|
|
```
|
|
|
|
Alternative:
|
|
|
|
```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
|
|
|
|
print(values_plus)
|
|
```
|
|
|
|
```text
|
|
[1. 0.70710678]
|
|
```
|
|
|
|
### CVaRExpectation
|
|
|
|
| Opflow | Alternative |
|
|
| --- | --- |
|
|
| [`qiskit.opflow.expectations.CVaRExpectation`](/api/qiskit/0.46/qiskit.opflow.expectations.CVaRExpectation) | Functionality migrated into new VQE algorithm: [`qiskit.algorithms.minimum_eigensolvers.SamplingVQE`](/api/qiskit/0.46/qiskit.algorithms.minimum_eigensolvers.SamplingVQE) |
|
|
|
|
#### Example 1: VQE with CVaR
|
|
|
|
Opflow:
|
|
|
|
```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(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)
|
|
```
|
|
|
|
```python
|
|
(-1+0j)
|
|
```
|
|
|
|
Alternative:
|
|
|
|
```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, opt, aggregation=alpha)
|
|
result = vqe.compute_minimum_eigenvalue(op)
|
|
|
|
print(result.eigenvalue)
|
|
```
|
|
|
|
```python
|
|
-1.0
|
|
```
|
|
|
|
## Gradients
|
|
|
|
The opflow [`qiskit.opflow.gradients`](/api/qiskit/0.46/qiskit.opflow.gradients) framework has been replaced by the [`qiskit.algorithms.gradients`](/api/qiskit/0.46/qiskit.algorithms.gradients)
|
|
module. The new gradients are **primitive-based subroutines** commonly used by algorithms and applications, which
|
|
can also be run standalone. For this reason, they now reside under [`qiskit.algorithms`](/api/qiskit/0.46/algorithms).
|
|
|
|
The former gradient framework contained base classes, converters, and derivatives. The "derivatives"
|
|
followed a factory design pattern, where different methods could be provided by using string identifiers
|
|
to each of these classes. The new gradient framework contains two main families of subroutines:
|
|
**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:
|
|
|
|
**Previous workflow**
|
|
|
|
```python
|
|
from qiskit.opflow import Gradient
|
|
|
|
grad = Gradient(method="param_shift")
|
|
|
|
# task based on expectation value computations + gradients
|
|
```
|
|
|
|
**New workflow**
|
|
|
|
We now explicitly import the desired class, depending on the target primitive (Sampler or Estimator) and target method:
|
|
|
|
```python
|
|
from qiskit.algorithms.gradients import ParamShiftEstimatorGradient
|
|
from qiskit.primitives import Estimator
|
|
|
|
grad = ParamShiftEstimatorGradient(Estimator())
|
|
|
|
# task based on expectation value computations + gradients
|
|
```
|
|
|
|
This works similarly for the QFI class:
|
|
|
|
**Previous workflow**
|
|
|
|
```python
|
|
from qiskit.opflow import QFI
|
|
|
|
qfi = QFI(method="lin_comb_full")
|
|
|
|
# task based on expectation value computations + QFI
|
|
```
|
|
|
|
**New workflow**
|
|
|
|
There is a generic QFI implementation that can be initialized with different QGT (Quantum Gradient Tensor)
|
|
implementations:
|
|
|
|
```python
|
|
from qiskit.algorithms.gradients import LinCombQGT, QFI
|
|
from qiskit.primitives import Estimator
|
|
|
|
qgt = LinCombQGT(Estimator())
|
|
qfi = QFI(qgt)
|
|
|
|
# task based on expectation value computations + QFI
|
|
```
|
|
|
|
<Admonition type="note">
|
|
Here is a quick guide for migrating the most common gradient settings. All new gradient
|
|
imports follow the format:
|
|
|
|
```python
|
|
from qiskit.algorithms.gradients import MethodPrimitiveGradient, QFI
|
|
```
|
|
|
|
Gradients:
|
|
|
|
| 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)` |
|
|
|
|
QFI/QGT:
|
|
|
|
| Opflow | Alternative |
|
|
| --- | --- |
|
|
| `QFI(method="lin_comb_full")` | `qgt=LinCombQGT(Estimator())` |
|
|
</Admonition>
|
|
|
|
Other auxiliary classes in the legacy gradient framework have been deprecated. Here is the complete migration
|
|
list:
|
|
|
|
| Opflow | Alternative |
|
|
| --- | --- |
|
|
| [`qiskit.opflow.gradients.DerivativeBase`](/api/qiskit/0.46/qiskit.opflow.gradients.DerivativeBase) | No replacement. This was the base class for the gradient, hessian, and QFI base classes. |
|
|
| [`qiskit.opflow.gradients.GradientBase`](/api/qiskit/0.46/qiskit.opflow.gradients.GradientBase) and [`qiskit.opflow.gradients.Gradient`](/api/qiskit/0.46/qiskit.opflow.gradients.Gradient) | [`qiskit.algorithms.gradients.BaseSamplerGradient`](/api/qiskit/0.46/qiskit.algorithms.gradients.BaseSamplerGradient) or [`qiskit.algorithms.gradients.BaseEstimatorGradient`](/api/qiskit/0.46/qiskit.algorithms.gradients.BaseEstimatorGradient), and specific subclasses per method, as explained above. |
|
|
| [`qiskit.opflow.gradients.HessianBase`](/api/qiskit/0.46/qiskit.opflow.gradients.HessianBase) and [`qiskit.opflow.gradients.Hessian`](/api/qiskit/0.46/qiskit.opflow.gradients.Hessian) | No replacement. The new gradient framework does not work with hessians as independent objects. |
|
|
| [`qiskit.opflow.gradients.QFIBase`](/api/qiskit/0.46/qiskit.opflow.gradients.QFIBase) and [`qiskit.opflow.gradients.QFI`](/api/qiskit/0.46/qiskit.opflow.gradients.QFI) | The new [`qiskit.algorithms.gradients.QFI`](/api/qiskit/0.46/qiskit.algorithms.gradients.QFI) class extends QGT, so the corresponding base class is [`qiskit.algorithms.gradients.BaseQGT`](/api/qiskit/0.46/qiskit.algorithms.gradients.BaseQGT) |
|
|
| [`qiskit.opflow.gradients.CircuitGradient`](/api/qiskit/0.46/qiskit.opflow.gradients.CircuitGradient) | No replacement. This class was used to convert between circuit and gradient [`qiskit.opflow.primitive_ops.PrimitiveOp`](/api/qiskit/0.46/qiskit.opflow.primitive_ops.PrimitiveOp) and this functionality is no longer necessary. |
|
|
| [`qiskit.opflow.gradients.CircuitQFI`](/api/qiskit/0.46/qiskit.opflow.gradients.CircuitQFI) | No replacement. This class was used to convert between circuit and QFI [`qiskit.opflow.primitive_ops.PrimitiveOp`](/api/qiskit/0.46/qiskit.opflow.primitive_ops.PrimitiveOp) and this functionality is no longer necessary. |
|
|
| [`qiskit.opflow.gradients.NaturalGradient`](/api/qiskit/0.46/qiskit.opflow.gradients.NaturalGradient) | No replacement. The same functionality can be achieved with the QFI module. |
|
|
|
|
### Example 1: Finite differences batched gradient
|
|
|
|
Opflow:
|
|
|
|
```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
|
|
|
|
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)
|
|
|
|
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)
|
|
|
|
# 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())
|
|
|
|
print(gradients)
|
|
```
|
|
|
|
```python
|
|
[[(0.35355339059327356+0j), (-1.182555756156289e-16+0j), (-1.6675e-16+0j)], [(0.10355339059327384+0j), (0.8535533905932734+0j), (1.103553390593273+0j)]]
|
|
```
|
|
|
|
Alternative:
|
|
|
|
```python
|
|
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)])
|
|
|
|
a = Parameter("a")
|
|
b = Parameter("b")
|
|
c = Parameter("c")
|
|
|
|
qc = QuantumCircuit(1)
|
|
qc.h(0)
|
|
qc.u(a, b, c, 0)
|
|
qc.h(0)
|
|
|
|
estimator = Estimator()
|
|
gradient = ParamShiftEstimatorGradient(estimator)
|
|
|
|
# The new workflow follows an interface close to that of 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] * 2, [ham] * 2, param_list).result().gradients
|
|
|
|
print(gradients)
|
|
```
|
|
|
|
```python
|
|
[array([ 3.53553391e-01, 0.00000000e+00, -1.80411242e-16]), array([0.10355339, 0.85355339, 1.10355339])]
|
|
```
|
|
|
|
### Example 2: QFI
|
|
|
|
Opflow:
|
|
|
|
```python
|
|
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)
|
|
|
|
# Convert the circuit to a QFI object.
|
|
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()
|
|
|
|
print(qfi)
|
|
```
|
|
|
|
```python
|
|
[[ 1.00000000e+00+0.j -3.63575685e-16+0.j]
|
|
[-3.63575685e-16+0.j 5.00000000e-01+0.j]]
|
|
```
|
|
|
|
Alternative:
|
|
|
|
```python
|
|
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)
|
|
|
|
# Initialize QFI.
|
|
estimator = Estimator()
|
|
qgt = LinCombQGT(estimator)
|
|
qfi = QFI(qgt)
|
|
|
|
# Evaluate
|
|
values_list = [[np.pi / 4, 0.1]]
|
|
qfi = qfi.run(qc, values_list).result().qfis
|
|
|
|
print(qfi)
|
|
```
|
|
|
|
```python
|
|
[array([[ 1.00000000e+00, -1.50274614e-16],
|
|
[-1.50274614e-16, 5.00000000e-01]])]
|
|
```
|