Skip to content

Commit 8002a3e

Browse files
ikkohamt-imamichiihinckschriseclectic
authored
Add reference implementation of EstimatorV2 (#11227)
* Add EstimatorV2 * Update qiskit/primitives/base/base_estimator.py * Use PositiveInt for shots * improve type hint * Update qiskit/primitives/containers/data_bin.py Co-authored-by: Takashi Imamichi <[email protected]> * remove _run and make run abstractmethod * Apply suggestions from code review Co-authored-by: Ian Hincks <[email protected]> Co-authored-by: Christopher J. Wood <[email protected]> * fix from review comments * move test/python/primitives/containers * Apply suggestions from code review Co-authored-by: Takashi Imamichi <[email protected]> * update docs * rename task to pubs * Pubs -> Pub * make pydantic optional * Apply suggestions from code review Co-authored-by: Ian Hincks <[email protected]> * Apply suggestions from code review Co-authored-by: Ian Hincks <[email protected]> * fix lint * type hint * fix type hint for python 3.8 * improve BindingsArray * fix docs warning * Remove `slots=True` in dataclass usage. This argument was introduced in Python 3.10, so we cannot use it yet. We don't expect that this has any performance impacts, it is just being used here to make it more difficult to mutate. * update from review comments * Update qiskit/primitives/containers/estimator_pub.py Co-authored-by: Christopher J. Wood <[email protected]> * Update qiskit/primitives/base/base_estimator.py Co-authored-by: Ian Hincks <[email protected]> * Apply suggestions from code review Co-authored-by: Takashi Imamichi <[email protected]> * lint * Add BaseEstimatorV2 class Taken from 11227, but modified to - remove options from BaseEstimatorV2 - add precision attribute to BaseEstimatorV2 and EstimatorPub - remove BasePrimitiveV2 and BasePub Co-Authored-By: Ikko Hamamura <[email protected]> Co-Authored-By: Ian Hincks <[email protected]> * Fix removal of BasePub * Remove precision from EstimatorPub * Add BaseEstimator._make_data_bin() static method * Apply suggestions from code review Co-authored-by: Ian Hincks <[email protected]> * Apply suggestions from code review Co-authored-by: Ian Hincks <[email protected]> * linting * Move precision to EstimatorPub and Estimator.run * Update EstimatorV2 run return type, fix some typos * Fix some minor problems * add tests * update * Apply suggestions from code review Co-authored-by: Ian Hincks <[email protected]> * Update qiskit/primitives/statevector_estimator.py Co-authored-by: Ian Hincks <[email protected]> * revert BasePrimitiveV1 * update * rm base_pub.py * options do not have precision * remove Options * refactoring * Update qiskit/primitives/statevector_estimator.py * add seed * Update qiskit/primitives/statevector_estimator.py Co-authored-by: Takashi Imamichi <[email protected]> * clean * validate non hermitian * Update qiskit/primitives/statevector_estimator.py Co-authored-by: Ian Hincks <[email protected]> * black --------- Co-authored-by: Takashi Imamichi <[email protected]> Co-authored-by: Ian Hincks <[email protected]> Co-authored-by: Christopher J. Wood <[email protected]> Co-authored-by: Ian Hincks <[email protected]> Co-authored-by: Ian Hincks <[email protected]>
1 parent 9c58064 commit 8002a3e

File tree

4 files changed

+393
-2
lines changed

4 files changed

+393
-2
lines changed

qiskit/primitives/__init__.py

+10-1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,14 @@
3333
BackendEstimator
3434
EstimatorPub
3535
36+
EstimatorV2
37+
===========
38+
39+
.. autosummary::
40+
:toctree: ../stubs/
41+
42+
StatevectorEstimator
43+
3644
Sampler
3745
=======
3846
@@ -65,12 +73,13 @@
6573
from .base.sampler_result import SamplerResult
6674
from .containers import (
6775
BindingsArray,
76+
EstimatorPub,
6877
ObservablesArray,
6978
PrimitiveResult,
7079
PubResult,
7180
SamplerPub,
72-
EstimatorPub,
7381
)
7482
from .estimator import Estimator
7583
from .sampler import Sampler
84+
from .statevector_estimator import StatevectorEstimator
7685
from .statevector_sampler import StatevectorSampler

qiskit/primitives/containers/estimator_pub.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@
1717

1818
from __future__ import annotations
1919

20-
from typing import Tuple, Union
2120
from numbers import Real
21+
from typing import Tuple, Union
2222

2323
import numpy as np
2424

+100
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
# This code is part of Qiskit.
2+
#
3+
# (C) Copyright IBM 2023, 2024.
4+
#
5+
# This code is licensed under the Apache License, Version 2.0. You may
6+
# obtain a copy of this license in the LICENSE.txt file in the root directory
7+
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
8+
#
9+
# Any modifications or derivative works of this code must retain this
10+
# copyright notice, and modified files need to carry a notice indicating
11+
# that they have been altered from the originals.
12+
"""
13+
Estimator class
14+
"""
15+
16+
from __future__ import annotations
17+
18+
from collections.abc import Iterable
19+
20+
import numpy as np
21+
22+
from qiskit.quantum_info import SparsePauliOp, Statevector
23+
24+
from .base import BaseEstimatorV2
25+
from .containers import EstimatorPub, EstimatorPubLike, PrimitiveResult, PubResult
26+
from .primitive_job import PrimitiveJob
27+
from .utils import bound_circuit_to_instruction
28+
29+
30+
class StatevectorEstimator(BaseEstimatorV2):
31+
"""
32+
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+
40+
def __init__(
41+
self, *, default_precision: float = 0.0, seed: np.random.Generator | int | None = None
42+
):
43+
"""
44+
Args:
45+
default_precision: The default precision for the estimator if not specified during run.
46+
seed: The seed or Generator object for random number generation.
47+
If None, a random seeded default RNG will be used.
48+
"""
49+
self._default_precision = default_precision
50+
self._seed = seed
51+
52+
@property
53+
def default_precision(self) -> int:
54+
"""Return the default shots"""
55+
return self._default_precision
56+
57+
@property
58+
def seed(self) -> np.random.Generator | int | None:
59+
"""Return the seed or Generator object for random number generation."""
60+
return self._seed
61+
62+
def run(
63+
self, pubs: Iterable[EstimatorPubLike], *, precision: float | None = None
64+
) -> PrimitiveJob[PrimitiveResult[PubResult]]:
65+
if precision is None:
66+
precision = self._default_precision
67+
coerced_pubs = [EstimatorPub.coerce(pub, precision) for pub in pubs]
68+
69+
job = PrimitiveJob(self._run, coerced_pubs)
70+
job._submit()
71+
return job
72+
73+
def _run(self, pubs: list[EstimatorPub]) -> PrimitiveResult[PubResult]:
74+
return PrimitiveResult([self._run_pub(pub) for pub in pubs])
75+
76+
def _run_pub(self, pub: EstimatorPub) -> PubResult:
77+
rng = np.random.default_rng(self._seed)
78+
circuit = pub.circuit
79+
observables = pub.observables
80+
parameter_values = pub.parameter_values
81+
precision = pub.precision
82+
bound_circuits = parameter_values.bind_all(circuit)
83+
bc_circuits, bc_obs = np.broadcast_arrays(bound_circuits, observables)
84+
evs = np.zeros_like(bc_circuits, dtype=np.float64)
85+
stds = np.zeros_like(bc_circuits, dtype=np.float64)
86+
for index in np.ndindex(*bc_circuits.shape):
87+
bound_circuit = bc_circuits[index]
88+
observable = bc_obs[index]
89+
final_state = Statevector(bound_circuit_to_instruction(bound_circuit))
90+
paulis, coeffs = zip(*observable.items())
91+
obs = SparsePauliOp(paulis, coeffs) # TODO: support non Pauli operators
92+
expectation_value = np.real_if_close(final_state.expectation_value(obs))
93+
if precision != 0:
94+
if not np.isreal(expectation_value):
95+
raise ValueError("Given operator is not Hermitian and noise cannot be added.")
96+
expectation_value = rng.normal(expectation_value, precision)
97+
evs[index] = expectation_value
98+
data_bin_cls = self._make_data_bin(pub)
99+
data_bin = data_bin_cls(evs=evs, stds=stds)
100+
return PubResult(data_bin, metadata={"precision": precision})

0 commit comments

Comments
 (0)