Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add reference implementation of EstimatorV2 #11227

Merged
merged 69 commits into from
Jan 30, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
69 commits
Select commit Hold shift + click to select a range
0e428a9
Add EstimatorV2
ikkoham Nov 6, 2023
5564603
Merge branch 'main' into primitives/estimator-v2
ikkoham Nov 22, 2023
14c3ff3
Merge branch 'main' into primitives/estimator-v2
ikkoham Nov 27, 2023
4698d74
Update qiskit/primitives/base/base_estimator.py
ikkoham Nov 27, 2023
a074c82
Merge branch 'main' into primitives/estimator-v2
ikkoham Nov 28, 2023
a3d7643
Use PositiveInt for shots
ikkoham Nov 28, 2023
c18b209
improve type hint
ikkoham Nov 28, 2023
cc27a64
Update qiskit/primitives/containers/data_bin.py
ikkoham Nov 28, 2023
9d59318
remove _run and make run abstractmethod
ikkoham Nov 29, 2023
556aec2
Apply suggestions from code review
ikkoham Nov 29, 2023
2325224
fix from review comments
ikkoham Nov 29, 2023
200ef36
move test/python/primitives/containers
ikkoham Nov 29, 2023
a679f17
Merge branch 'main' into primitives/estimator-v2
ikkoham Nov 29, 2023
55122f4
Apply suggestions from code review
ikkoham Nov 29, 2023
9ffda4e
update docs
ikkoham Nov 29, 2023
8cfc8ff
rename task to pubs
ikkoham Nov 30, 2023
18e7395
Pubs -> Pub
ikkoham Nov 30, 2023
cd5b70f
make pydantic optional
ikkoham Dec 1, 2023
f075b4e
Apply suggestions from code review
ikkoham Dec 1, 2023
90447be
Apply suggestions from code review
ikkoham Dec 1, 2023
18053bd
fix lint
ikkoham Dec 1, 2023
998547e
type hint
ikkoham Dec 1, 2023
c3fd89a
fix type hint for python 3.8
ikkoham Dec 1, 2023
2bc2655
improve BindingsArray
ikkoham Dec 1, 2023
dfcc996
fix docs warning
ikkoham Dec 1, 2023
ce01d8f
Remove `slots=True` in dataclass usage.
ihincks Dec 1, 2023
199aeaa
update from review comments
ikkoham Dec 2, 2023
388d2bf
Update qiskit/primitives/containers/estimator_pub.py
ikkoham Dec 2, 2023
7777dea
Update qiskit/primitives/base/base_estimator.py
ikkoham Dec 2, 2023
5d320e9
Merge branch 'main' into primitives/estimator-v2
ikkoham Dec 4, 2023
5307922
Apply suggestions from code review
ikkoham Dec 19, 2023
bcdf842
lint
ikkoham Dec 19, 2023
b6d9376
Merge branch 'main' into primitives/estimator-v2
ikkoham Dec 21, 2023
48bddad
Merge branch 'main' into primitives/estimator-v2
ikkoham Jan 4, 2024
3066fb1
Merge branch 'main' into primitives/estimator-v2
ikkoham Jan 9, 2024
4bbb67f
Merge branch 'main' into primitives/estimator-v2
ikkoham Jan 11, 2024
321cd14
Add BaseEstimatorV2 class
chriseclectic Jan 9, 2024
db9e22a
Fix removal of BasePub
chriseclectic Jan 9, 2024
63ed918
Remove precision from EstimatorPub
chriseclectic Jan 9, 2024
3db0063
Add BaseEstimator._make_data_bin() static method
ihincks Jan 9, 2024
e0101f4
Apply suggestions from code review
chriseclectic Jan 9, 2024
9046da8
Apply suggestions from code review
chriseclectic Jan 9, 2024
75ca51c
linting
chriseclectic Jan 9, 2024
dea7b3b
Move precision to EstimatorPub and Estimator.run
chriseclectic Jan 10, 2024
a5d1e55
Update EstimatorV2 run return type, fix some typos
chriseclectic Jan 16, 2024
b2b74e4
Fix some minor problems
ihincks Jan 17, 2024
3d694ac
add tests
ihincks Jan 17, 2024
4d917c6
Merge branch 'main' into primitives/estimator-v2
ikkoham Jan 17, 2024
c12bb3c
Merge branch 'primitives/estimator-v2-redux' into primitives/estimato…
ikkoham Jan 17, 2024
8387f27
update
ikkoham Jan 17, 2024
5bb3833
Merge branch 'main' into primitives/estimator-v2
ikkoham Jan 18, 2024
1d37ddb
Apply suggestions from code review
ikkoham Jan 18, 2024
a095154
Update qiskit/primitives/statevector_estimator.py
ikkoham Jan 18, 2024
bfaf4c0
revert BasePrimitiveV1
ikkoham Jan 18, 2024
105551d
update
ikkoham Jan 18, 2024
47b2302
rm base_pub.py
ikkoham Jan 18, 2024
71a814a
options do not have precision
ikkoham Jan 18, 2024
1aa8e4a
remove Options
ikkoham Jan 19, 2024
d37ce1b
Merge branch 'main' into primitives/estimator-v2
ikkoham Jan 19, 2024
c2c97dd
refactoring
ikkoham Jan 19, 2024
8e26f18
Update qiskit/primitives/statevector_estimator.py
ihincks Jan 19, 2024
49a1275
Merge branch 'main' into primitives/estimator-v2
ikkoham Jan 24, 2024
f30debb
add seed
ikkoham Jan 24, 2024
10648a7
Update qiskit/primitives/statevector_estimator.py
ikkoham Jan 24, 2024
00a7acf
clean
ikkoham Jan 24, 2024
98af76c
validate non hermitian
ikkoham Jan 25, 2024
a128546
Merge branch 'main' into primitives/estimator-v2
ikkoham Jan 26, 2024
dbf4363
Update qiskit/primitives/statevector_estimator.py
ikkoham Jan 30, 2024
5d2ae1b
black
ikkoham Jan 30, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
46 changes: 42 additions & 4 deletions qiskit/primitives/statevector_estimator.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,35 @@ class StatevectorEstimator(BaseEstimatorV2):
Simple implementation of :class:`BaseEstimatorV2` with full state vector simulation.
chriseclectic marked this conversation as resolved.
Show resolved Hide resolved
ikkoham marked this conversation as resolved.
Show resolved Hide resolved
"""

def __init__(
self, *, default_precision: float = 0.0, seed: np.random.Generator | int | None = None
):
"""
Args:
default_precision: The default precision for the estimator if not specified during run.
seed: The seed or Generator object for random number generation.
If None, a random seeded default RNG will be used.
"""
self._default_precision = default_precision
self._seed = seed

@property
def default_precision(self) -> int:
"""Return the default shots"""
return self._default_precision

@property
def seed(self) -> np.random.Generator | int | None:
"""Return the seed or Generator object for random number generation."""
return self._seed

def run(
self, pubs: Iterable[EstimatorPubLike], *, precision: float | None = None
) -> PrimitiveJob[PrimitiveResult[PubResult]]:
if precision is not None and precision > 0:
raise ValueError("precision must be None or 0 for StatevectorEstimator.")
if precision is None:
precision = self._default_precision
coerced_pubs = [EstimatorPub.coerce(pub, precision) for pub in pubs]

job = PrimitiveJob(self._run, coerced_pubs)
job._submit()
return job
Expand All @@ -46,9 +69,11 @@ def _run(self, pubs: list[EstimatorPub]) -> PrimitiveResult[PubResult]:
return PrimitiveResult([self._run_pub(pub) for pub in pubs])

def _run_pub(self, pub: EstimatorPub) -> PubResult:
rng = _get_rng(self._seed)
ikkoham marked this conversation as resolved.
Show resolved Hide resolved
circuit = pub.circuit
observables = pub.observables
parameter_values = pub.parameter_values
precision = pub.precision
bound_circuits = parameter_values.bind_all(circuit)
bc_circuits, bc_obs = np.broadcast_arrays(bound_circuits, observables)
evs = np.zeros_like(bc_circuits, dtype=np.float64)
Expand All @@ -59,7 +84,20 @@ def _run_pub(self, pub: EstimatorPub) -> PubResult:
final_state = Statevector(bound_circuit_to_instruction(bound_circuit))
paulis, coeffs = zip(*observable.items())
obs = SparsePauliOp(paulis, coeffs) # TODO: support non Pauli operators
evs[index] = np.real_if_close(final_state.expectation_value(obs))
expectation_value = final_state.expectation_value(obs)
if precision != 0:
expectation_value = rng.normal(expectation_value, precision)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You need to make sure expectation_value is float to use normal. Otherwise, it raises an error as follows.

In [4]: rng.normal(1j, 0.1)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[4], line 1
----> 1 rng.normal(1j, 0.1)

File numpy/random/_generator.pyx:1220, in numpy.random._generator.Generator.normal()

File _common.pyx:585, in numpy.random._common.cont()

TypeError: float() argument must be a string or a real number, not 'complex'

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. You are right. Actually, I haven't validate this since V1.

evs[index] = np.real_if_close(expectation_value)
data_bin_cls = self._make_data_bin(pub)
data_bin = data_bin_cls(evs=evs, stds=stds)
return PubResult(data_bin, metadata={"precision": 0})
return PubResult(data_bin, metadata={"precision": precision})


def _get_rng(seed):
if seed is None:
rng = np.random.default_rng()
elif isinstance(seed, np.random.Generator):
rng = seed
else:
rng = np.random.default_rng(seed)
return rng
ikkoham marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
from qiskit.test import QiskitTestCase


class TestEstimatorV2(QiskitTestCase):
class TestStatevectorEstimator(QiskitTestCase):
"""Test Estimator"""

def setUp(self):
Expand Down Expand Up @@ -224,7 +224,6 @@ def test_run_errors(self):
op2 = SparsePauliOp.from_list([("II", 1)])

est = StatevectorEstimator()
# TODO: add validation
with self.assertRaises(ValueError):
est.run([(qc, op2)]).result()
with self.assertRaises(ValueError):
Expand All @@ -233,6 +232,8 @@ def test_run_errors(self):
est.run([(qc2, op2, [[1, 2]])]).result()
with self.assertRaises(ValueError):
est.run([(qc, [op, op2], [[1]])]).result()
with self.assertRaises(ValueError):
est.run([(qc, op)], precision=-1).result()

def test_run_numpy_params(self):
"""Test for numpy array as parameter values"""
Expand All @@ -255,6 +256,25 @@ def test_run_numpy_params(self):
self.assertEqual(len(result[0].data.evs), k)
np.testing.assert_allclose(result[0].data.evs, target[0].data.evs)

def test_precision_seed(self):
"""Test for precision and seed"""
estimator = StatevectorEstimator(default_precision=1.0, seed=1)
psi1 = self.psi[0]
hamiltonian1 = self.hamiltonian[0]
theta1 = self.theta[0]
job = estimator.run([(psi1, hamiltonian1, [theta1])])
result = job.result()
np.testing.assert_allclose(result[0].data.evs, [1.901141473854881])
# The result of the second run is the same
job = estimator.run([(psi1, hamiltonian1, [theta1]), (psi1, hamiltonian1, [theta1])])
result = job.result()
np.testing.assert_allclose(result[0].data.evs, [1.901141473854881])
np.testing.assert_allclose(result[1].data.evs, [1.901141473854881])
# precision=0 impliese the exact expectation value
job = estimator.run([(psi1, hamiltonian1, [theta1])], precision=0)
result = job.result()
np.testing.assert_allclose(result[0].data.evs, [1.5555572817900956])


if __name__ == "__main__":
unittest.main()