|
29 | 29 |
|
30 | 30 |
|
31 | 31 | class StatevectorEstimator(BaseEstimatorV2): |
32 | | - r""" |
33 | | - Simple implementation of :class:`BaseEstimatorV2` with full state vector simulation. |
34 | | -
|
35 | | - This class is implemented via :class:`~.Statevector` which turns provided circuits into |
36 | | - pure state vectors. These states are subsequently acted on by :class:`~.SparsePauliOp`, |
37 | | - which implies that, at present, this implementation is only compatible with Pauli-based |
38 | | - observables. |
39 | | -
|
40 | | - Each tuple of ``(circuit, observables, <optional> parameter values, <optional> precision)``, |
41 | | - called an estimator primitive unified bloc (PUB), produces its own array-based result. The |
42 | | - :meth:`~.StatevectorEstimator.run` method can be given a sequence of pubs to run in one call. |
43 | | -
|
44 | | - The default precision value used by this estimator, ``default_precision=0.0``, results in |
45 | | - exact expecation value estimates for unitary circuits, up to numerical precision. If a |
46 | | - positive value is provided, then it is used to add artificial noise to the exact estimates |
47 | | - by drawing a random variates whose mean values are equal to the exact values, and whose |
48 | | - standard deviations are equal to ``precision`` clipped to the largest value consistent with |
49 | | - the expectation value's space of support. The method of doing so is described below. |
50 | | -
|
51 | | - When an observable is an n-qubit Pauli, its expectation value must live in the interval |
52 | | - :math:`[-1, 1]`, so that it is natural for this estimator implementation to always report |
53 | | - estimates in :math:`[-1,1]` even when the precision is set to a positive value. More |
54 | | - generally, for a multi-term observable :math:`A=\sum_k b_k P_k` expanded in the Pauli basis |
55 | | - and a quantum state :math:`\rho`, using Hölder's inequality and the triangle inequality, the |
56 | | - expectation value is bounded by |
| 32 | + r"""Simple implementation of :class:`BaseEstimatorV2` with full state vector simulation. |
| 33 | +
|
| 34 | + This class is implemented via :class:`~.Statevector` which turns provided circuits into |
| 35 | + pure state vectors. These states are subsequently acted on by :class:`~.SparsePauliOp`, |
| 36 | + which implies that, at present, this implementation is only compatible with Pauli-based |
| 37 | + observables. |
| 38 | +
|
| 39 | + Each tuple of ``(circuit, observables, <optional> parameter values, <optional> precision)``, |
| 40 | + called an estimator primitive unified bloc (PUB), produces its own array-based result. The |
| 41 | + :meth:`~.StatevectorEstimator.run` method can be given a sequence of pubs to run in one call. |
| 42 | +
|
| 43 | + The default precision value used by this estimator, ``default_precision=0.0``, results in |
| 44 | + exact expecation value estimates for unitary circuits, up to numerical precision. If a |
| 45 | + positive value is provided, then it is used to add artificial noise to the exact estimates |
| 46 | + by drawing a random variates whose mean values are equal to the exact values, and whose |
| 47 | + standard deviations are equal to ``precision`` clipped to the largest value consistent with |
| 48 | + the expectation value's space of support. The method of doing so is described below. |
| 49 | +
|
| 50 | + When an observable is an n-qubit Pauli, its expectation value must live in the interval |
| 51 | + :math:`[-1, 1]`, so that it is natural for this estimator implementation to always report |
| 52 | + estimates in :math:`[-1,1]` even when the precision is set to a positive value. More |
| 53 | + generally, for a multi-term observable :math:`A=\sum_k b_k P_k` expanded in the Pauli basis |
| 54 | + and a quantum state :math:`\rho`, using Hölder's inequality and the triangle inequality, the |
| 55 | + expectation value is bounded by |
| 56 | + |
| 57 | + .. math:: |
| 58 | +
|
| 59 | + |\mathrm{Tr}(\rho A)| |
| 60 | + \leq \left\lVert \rho \right\rVert_1 \left\lVert B \right\rVert_\infty |
| 61 | + \leq \sum_k |b_k|. |
| 62 | +
|
| 63 | + While this inequality is loose, in the sense that for a particular observable :math:`A` one |
| 64 | + can generally not find a state :math:`\rho` for which the inequality is saturated, it is |
| 65 | + still useful, because, in practical experiments, expectation values are often estimated |
| 66 | + term-by-term such that the right-hand-side naturally represents a typical bound seen |
| 67 | + experimentally. |
| 68 | +
|
| 69 | + This class chooses to use the Beta distribution to bound returned estimates to the interval |
| 70 | + :math:`[-b, b]` when non-zero precision is present, where :math:`b=\sum_k |b_k|`. Since the |
| 71 | + Beta distribution has support on the interval :math:`[0, 1]`, it is used in conjunction with |
| 72 | + an affine transformation between :math:`[-b, b]` and :math:`[0, 1]`. In particular, given an |
| 73 | + exact expecation value :math:`a`, setting |
| 74 | +
|
| 75 | + .. math:: |
| 76 | +
|
| 77 | + \mu = \frac{a + b}{2b} \\ |
| 78 | + \sigma^2 = \frac{\mathrm{min}(\mu(1-\mu), \mathrm{precision}^2)}{4b^2} \\ |
| 79 | + \alpha = \frac{\mu^2(1-\mu)}{\sigma^2} - \mu \\ |
| 80 | + \beta = \frac{\mu(1-\mu)^2}{\sigma^2} - (1-\mu) |
| 81 | +
|
| 82 | + and drawing :math:`\hat{a} \sim \mathrm{Beta}(\alpha, \beta)` leads to |
| 83 | + :math:`\mathbb{E}[\hat{a}]=a`, :math:`\mathbb{V}[\hat{a}]=\mathrm{precision}^2`, and |
| 84 | + :math:`-b\leq \hat{a} \leq b`. The minimum with $\mu(1-\mu)$ is taken in $\sigma^2$ because |
| 85 | + no random variable with support on a unit-length interval can have a variance greater |
| 86 | + than $\mu(1-\mu)$, where $\mu$ is its mean value; failing to do this would lead to negative |
| 87 | + values of $\alpha$ or $\beta$. |
| 88 | +
|
| 89 | + .. note:: |
| 90 | + Assuming the precision is set to zero, the result of this class is exact if the circuit |
| 91 | + contains only unitary operations. On the other hand, the result could be stochastic if |
| 92 | + the circuit contains a non-unitary operation such as a reset for some subsystems. |
| 93 | + The stochastic result can be made reproducible by setting ``seed``, e.g., |
| 94 | + ``StatevectorEstimator(seed=123)``. |
| 95 | +
|
| 96 | + .. plot:: |
| 97 | + :alt: Output from the previous code. |
| 98 | + :include-source: |
| 99 | +
|
| 100 | + from qiskit.circuit import Parameter, QuantumCircuit |
| 101 | + from qiskit.primitives import StatevectorEstimator |
| 102 | + from qiskit.quantum_info import Pauli, SparsePauliOp |
| 103 | +
|
| 104 | + import matplotlib.pyplot as plt |
| 105 | + import numpy as np |
| 106 | +
|
| 107 | + # Define a circuit with two parameters. |
| 108 | + circuit = QuantumCircuit(2) |
| 109 | + circuit.h(0) |
| 110 | + circuit.cx(0, 1) |
| 111 | + circuit.ry(Parameter("a"), 0) |
| 112 | + circuit.rz(Parameter("b"), 0) |
| 113 | + circuit.cx(0, 1) |
| 114 | + circuit.h(0) |
| 115 | +
|
| 116 | + # Define a sweep over parameter values, where the second axis is over |
| 117 | + # the two parameters in the circuit. |
| 118 | + params = np.vstack([ |
| 119 | + np.linspace(-np.pi, np.pi, 100), |
| 120 | + np.linspace(-4 * np.pi, 4 * np.pi, 100) |
| 121 | + ]).T |
| 122 | +
|
| 123 | + # Define three observables. Many formats are supported here including |
| 124 | + # classes such as qiskit.quantum_info.SparsePauliOp. The inner length-1 |
| 125 | + # lists cause this array of observables to have shape (3, 1), rather |
| 126 | + # than shape (3,) if they were omitted. |
| 127 | + observables = [ |
| 128 | + [SparsePauliOp(["XX", "IY"], [0.5, 0.5])], |
| 129 | + [Pauli("XX")], |
| 130 | + [Pauli("IY")] |
| 131 | + ] |
| 132 | +
|
| 133 | + # Instantiate a new statevector simulation based estimator object. |
| 134 | + estimator = StatevectorEstimator() |
| 135 | +
|
| 136 | + # Estimate the expectation value for all 300 combinations of |
| 137 | + # observables and parameter values, where the pub result will have |
| 138 | + # shape (3, 100). This shape is due to our array of parameter |
| 139 | + # bindings having shape (100,), combined with our array of observables |
| 140 | + # having shape (3, 1) |
| 141 | + pub = (circuit, observables, params) |
| 142 | + job = estimator.run([pub]) |
| 143 | +
|
| 144 | + # Extract the result for the 0th pub (this example only has one pub). |
| 145 | + result = job.result()[0] |
| 146 | +
|
| 147 | + # Error-bar information is also available, but the error is 0 |
| 148 | + # for this StatevectorEstimator. |
| 149 | + result.data.stds |
| 150 | +
|
| 151 | + # Pull out the array-based expectation value estimate data from the |
| 152 | + # result and plot a trace for each observable. |
| 153 | + for idx, pauli in enumerate(observables): |
| 154 | + plt.plot(result.data.evs[idx], label=pauli) |
| 155 | + plt.legend() |
57 | 156 | |
58 | | - .. math:: |
59 | | -
|
60 | | - |\mathrm{Tr}(\rho A)| |
61 | | - \leq \left\lVert \rho \right\rVert_1 \left\lVert B \right\rVert_\infty |
62 | | - \leq \sum_k |b_k|. |
63 | | -
|
64 | | - While this inequality is loose, in the sense that for a particular observable :math:`A` one |
65 | | - can generally not find a state :math:`\rho` for which the inequality is saturated, it is |
66 | | - still useful, because, in practical experiments, expectation values are often estimated |
67 | | - term-by-term such that the right-hand-side naturally represents a typical bound seen |
68 | | - experimentally. |
69 | | -
|
70 | | - This class chooses to use the Beta distribution to bound returned estimates to the interval |
71 | | - :math:`[-b, b]` when non-zero precision is present, where :math:`b=\sum_k |b_k|`. Since the |
72 | | - Beta distribution has support on the interval :math:`[0, 1]`, it is used in conjunction with |
73 | | - an affine transformation between :math:`[-b, b]` and :math:`[0, 1]`. In particular, given an |
74 | | - exact expecation value :math:`a`, setting |
75 | | -
|
76 | | - .. math:: |
77 | | -
|
78 | | - \mu = \frac{a + b}{2b} \\ |
79 | | - \sigma^2 = \frac{\mathrm{min}(\mu(1-\mu), \mathrm{precision}^2)}{4b^2} \\ |
80 | | - \alpha = \frac{\mu^2(1-\mu)}{\sigma^2} - \mu \\ |
81 | | - \beta = \frac{\mu(1-\mu)^2}{\sigma^2} - (1-\mu) |
82 | | -
|
83 | | - and drawing :math:`\hat{a} \sim \mathrm{Beta}(\alpha, \beta)` leads to |
84 | | - :math:`\mathbb{E}[\hat{a}]=a`, :math:`\mathbb{V}[\hat{a}]=\mathrm{precision}^2`, and |
85 | | - :math:`-b\leq \hat{a} \leq b`. The minimum with $\mu(1-\mu)$ is taken in $\sigma^2$ because |
86 | | - no random variable with support on a unit-length interval can have a variance greater |
87 | | - than $\mu(1-\mu)$, where $\mu$ is its mean value; failing to do this would lead to negative |
88 | | - values of $\alpha$ or $\beta$. |
89 | | -
|
90 | | - .. note:: |
91 | | - Assuming the precision is set to zero, the result of this class is exact if the circuit |
92 | | - contains only unitary operations. On the other hand, the result could be stochastic if |
93 | | - the circuit contains a non-unitary operation such as a reset for some subsystems. |
94 | | - The stochastic result can be made reproducible by setting ``seed``, e.g., |
95 | | - ``StatevectorEstimator(seed=123)``. |
96 | | -
|
97 | | - .. plot:: |
98 | | - :alt: Output from the previous code. |
99 | | - :include-source: |
100 | | -
|
101 | | - from qiskit.circuit import Parameter, QuantumCircuit |
102 | | - from qiskit.primitives import StatevectorEstimator |
103 | | - from qiskit.quantum_info import Pauli, SparsePauliOp |
104 | | -
|
105 | | - import matplotlib.pyplot as plt |
106 | | - import numpy as np |
107 | | -
|
108 | | - # Define a circuit with two parameters. |
109 | | - circuit = QuantumCircuit(2) |
110 | | - circuit.h(0) |
111 | | - circuit.cx(0, 1) |
112 | | - circuit.ry(Parameter("a"), 0) |
113 | | - circuit.rz(Parameter("b"), 0) |
114 | | - circuit.cx(0, 1) |
115 | | - circuit.h(0) |
116 | | -
|
117 | | - # Define a sweep over parameter values, where the second axis is over |
118 | | - # the two parameters in the circuit. |
119 | | - params = np.vstack([ |
120 | | - np.linspace(-np.pi, np.pi, 100), |
121 | | - np.linspace(-4 * np.pi, 4 * np.pi, 100) |
122 | | - ]).T |
123 | | -
|
124 | | - # Define three observables. Many formats are supported here including |
125 | | - # classes such as qiskit.quantum_info.SparsePauliOp. The inner length-1 |
126 | | - # lists cause this array of observables to have shape (3, 1), rather |
127 | | - # than shape (3,) if they were omitted. |
128 | | - observables = [ |
129 | | - [SparsePauliOp(["XX", "IY"], [0.5, 0.5])], |
130 | | - [Pauli("XX")], |
131 | | - [Pauli("IY")] |
132 | | - ] |
133 | | -
|
134 | | - # Instantiate a new statevector simulation based estimator object. |
135 | | - estimator = StatevectorEstimator() |
136 | | -
|
137 | | - # Estimate the expectation value for all 300 combinations of |
138 | | - # observables and parameter values, where the pub result will have |
139 | | - # shape (3, 100). This shape is due to our array of parameter |
140 | | - # bindings having shape (100,), combined with our array of observables |
141 | | - # having shape (3, 1) |
142 | | - pub = (circuit, observables, params) |
143 | | - job = estimator.run([pub]) |
144 | | -
|
145 | | - # Extract the result for the 0th pub (this example only has one pub). |
146 | | - result = job.result()[0] |
147 | | -
|
148 | | - # Error-bar information is also available, but the error is 0 |
149 | | - # for this StatevectorEstimator. |
150 | | - result.data.stds |
151 | | -
|
152 | | - # Pull out the array-based expectation value estimate data from the |
153 | | - # result and plot a trace for each observable. |
154 | | - for idx, pauli in enumerate(observables): |
155 | | - plt.plot(result.data.evs[idx], label=pauli) |
156 | | - plt.legend() |
157 | 157 | """ |
158 | 158 |
|
159 | 159 | def __init__( |
|
0 commit comments