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 26 commits
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 16 additions & 5 deletions qiskit/primitives/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,14 @@
Estimator
BackendEstimator

EstimatorV2
===========

.. autosummary::
:toctree: ../stubs/

StatevectorEstimator

Sampler
=======

Expand All @@ -50,13 +58,16 @@

EstimatorResult
SamplerResult
PrimitiveResult
PubResult
"""

from .base import BaseEstimator
from .base import BaseSampler
from .backend_estimator import BackendEstimator
from .estimator import Estimator
from .base.estimator_result import EstimatorResult
from .backend_sampler import BackendSampler
from .sampler import Sampler
from .base import BaseEstimator, BaseSampler
from .base.estimator_result import EstimatorResult
from .base.sampler_result import SamplerResult
from .containers import BindingsArray, EstimatorPub, ObservablesArray, PrimitiveResult, PubResult
from .estimator import Estimator
from .sampler import Sampler
from .statevector_estimator import Estimator as StatevectorEstimator
2 changes: 1 addition & 1 deletion qiskit/primitives/base/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
Abstract base classes for primitives module.
"""

from .base_estimator import BaseEstimator
from .base_estimator import BaseEstimator, BaseEstimatorV2
from .base_sampler import BaseSampler
from .estimator_result import EstimatorResult
from .sampler_result import SamplerResult
135 changes: 127 additions & 8 deletions qiskit/primitives/base/base_estimator.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# This code is part of Qiskit.
#
# (C) Copyright IBM 2022.
# (C) Copyright IBM 2022, 2023.
#
# This code is licensed under the Apache License, Version 2.0. You may
# obtain a copy of this license in the LICENSE.txt file in the root directory
Expand All @@ -14,9 +14,98 @@

.. estimator-desc:

=====================
Overview of Estimator
=====================
========================
Overview of EstimatorV2
========================

:class:`~BaseEstimatorV2` is a primitive that estimates expectation values for provided quantum
circuit and observable combinations.

Following construction, and estimator is used by calling its :meth:`~.BaseEstimatorV2.run` method
ikkoham marked this conversation as resolved.
Show resolved Hide resolved
with a list of pubs (Primitive Unified Blocs). Each pub contains three values that, together,
define a computation unit of work for the estimator to complete:

* a single :class:`~qiskit.circuit.QuantumCircuit`, possibly parametrized, whose final state we
define as :math:`\psi(\theta)`,

* one or more observables (specified as any :class:`~.ObservablesArrayLike`, including
:class:`~.Pauli`, :class:`~.SparsePauliOp`, ``str``) that specify which expectation values to
estimate, denoted :math:`H_j`, and

* a collection parameter value sets to bind the circuit against, :math:`\theta_k`.

Running an estimator returns a :class:`~qiskit.providers.JobV1` object, where calling
the method :meth:`qiskit.providers.JobV1.result` results in expectation value estimates and metadata
for each pub:

.. math::

\langle\psi(\theta_k)|H_j|\psi(\theta_k)\rangle

The observables and parameter values portion of a pub can be array-valued with arbitrary dimensions,
where standard broadcasting rules are applied, so that, in turn, the estimated result for each pub
is in general array-valued as well. For more information, please check
`here <https://github.com/Qiskit/RFCs/blob/master/0015-estimator-interface.md#arrays-and
-broadcasting->`_.

Here is an example of how the estimator is used.

.. code-block:: python

from qiskit.primitives.statevector_estimator import Estimator
from qiskit.circuit.library import RealAmplitudes
from qiskit.quantum_info import SparsePauliOp

psi1 = RealAmplitudes(num_qubits=2, reps=2)
psi2 = RealAmplitudes(num_qubits=2, reps=3)

H1 = SparsePauliOp.from_list([("II", 1), ("IZ", 2), ("XI", 3)])
H2 = SparsePauliOp.from_list([("IZ", 1)])
H3 = SparsePauliOp.from_list([("ZI", 1), ("ZZ", 1)])

theta1 = [0, 1, 1, 2, 3, 5]
theta2 = [0, 1, 1, 2, 3, 5, 8, 13]
theta3 = [1, 2, 3, 4, 5, 6]

estimator = Estimator()

# calculate [ <psi1(theta1)|H1|psi1(theta1)> ]
job = estimator.run([(psi1, hamiltonian1, [theta1])])
job_result = job.result() # It will block until the job finishes.
print(f"The primitive-job finished with result {job_result}"))

# calculate [ [<psi1(theta1)|H1|psi1(theta1)>,
# <psi1(theta3)|H3|psi1(theta3)>],
# [<psi2(theta2)|H2|psi2(theta2)>] ]
job2 = estimator.run(
[(psi1, [hamiltonian1, hamiltonian3], [theta1, theta3]), (psi2, hamiltonian2, theta2)]
)
job_result = job2.result()
print(f"The primitive-job finished with result {job_result}")

==============================
Migration guide from V1 to V2
Copy link
Member

Choose a reason for hiding this comment

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

Should this be a user guide or tutorial doc, not hidden in API docs?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

OK, I removed this from this PR.

==============================


The original three arguments are now a single argument pubs.
To accommodate this change, the zip function can be used for easy migration.
For example, suppose the code originally is:

.. code-block:: python

estimator.run([psi1], [hamiltonian1], [theta1]) # for EstimatorV1

Just add zip function:

.. code-block:: python

estimator.run(zip([psi1], [hamiltonian1], [theta1])) # for EstimatorV2


========================
Overview of EstimatorV1
========================

Estimator class estimates expectation values of quantum circuits and observables.

Expand Down Expand Up @@ -82,24 +171,27 @@

import warnings
from abc import abstractmethod
from collections.abc import Sequence
from collections.abc import Iterable, Sequence
from copy import copy
from typing import Generic, TypeVar

from qiskit.utils.deprecation import deprecate_func
from qiskit.circuit import QuantumCircuit
from qiskit.circuit.parametertable import ParameterView
from qiskit.providers import JobV1 as Job
from qiskit.quantum_info.operators import SparsePauliOp
from qiskit.quantum_info.operators.base_operator import BaseOperator
from qiskit.utils.deprecation import deprecate_func
from qiskit.utils.optionals import HAS_PYDANTIC

from .base_primitive import BasePrimitive
from ..containers.estimator_pub import EstimatorPubLike
from ..containers.options import BasePrimitiveOptionsLike
from . import validation
from .base_primitive import BasePrimitive, BasePrimitiveV2

T = TypeVar("T", bound=Job)


class BaseEstimator(BasePrimitive, Generic[T]):
class BaseEstimatorV1(BasePrimitive, Generic[T]):
"""Estimator base class.

Base class for Estimator that estimates expectation values of quantum circuits and observables.
Expand Down Expand Up @@ -254,3 +346,30 @@ def parameters(self) -> tuple[ParameterView, ...]:
Parameters, where ``parameters[i][j]`` is the j-th parameter of the i-th circuit.
"""
return tuple(self._parameters)


BaseEstimator = BaseEstimatorV1


@HAS_PYDANTIC.require_in_instance
Copy link
Member

Choose a reason for hiding this comment

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

This isn't really optional as it forces pydantic to be installed to use BaseEstimatorV2, if you tried to use BaseEstimatorV2 without pydantic installed you'd get an error.

class BaseEstimatorV2(BasePrimitiveV2):
"""Estimator base class version 2.

An estimator estimates expectation values for provided quantum circuit and observable combinations.
"""

def __init__(self, options: BasePrimitiveOptionsLike | None):
super().__init__(options=options)

@abstractmethod
def run(self, pubs: Iterable[EstimatorPubLike]) -> Job:
"""Estimate expectation values for each provided pub (Primitive Unified Bloc).

Args:
pubs: a iterable of pubslike object. Typically, list of tuple
``(QuantumCircuit, observables, parameter_values)``

Returns:
A job object that contains results.
"""
pass
23 changes: 22 additions & 1 deletion qiskit/primitives/base/base_primitive.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,14 @@
from collections.abc import Sequence

from qiskit.circuit import QuantumCircuit
from qiskit.primitives.containers import BasePrimitiveOptions, BasePrimitiveOptionsLike
from qiskit.providers import Options
from qiskit.utils.deprecation import deprecate_func

from . import validation


class BasePrimitive(ABC):
class BasePrimitiveV1(ABC):
ikkoham marked this conversation as resolved.
Show resolved Hide resolved
"""Primitive abstract base class."""

def __init__(self, options: dict | None = None):
Expand Down Expand Up @@ -72,3 +73,23 @@ def _cross_validate_circuits_parameter_values(
return validation._cross_validate_circuits_parameter_values(
circuits, parameter_values=parameter_values
)


BasePrimitive = BasePrimitiveV1


class BasePrimitiveV2(ABC):
Copy link
Member

Choose a reason for hiding this comment

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

Wasn't base primitive v2 supposed to have an abstract run method? I don't really see the point of having a BasePrimitive class if not, might as well just have BaseEstimatorV2 and BaseSamplerV2

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I was aware that Generic base classes were opposed in the RFC. Can I implement it? @ihincks

TBH, I like Generic, so I implemented Generic features for V1.

Copy link
Contributor

Choose a reason for hiding this comment

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

#11254, #11257, and #11529 together suggest to remove the need for mandatory options by making base sampler and estimator maintain a shots attribute and a precision attribute, respectively. If options are removed from the base primitive, the base primitive doesn't serve a concrete purpose, and so can be removed. There is always the possibility to add it later should a need arise, whereas the reverse is trickier.

"""Primitive abstract base class version 2."""

version = 2
_options_class: type[BasePrimitiveOptions] = BasePrimitiveOptions

def __init__(self, options: BasePrimitiveOptionsLike | None = None):
self._options = self._options_class()
if options:
self._options.update(options)

@property
def options(self) -> BasePrimitiveOptions:
t-imamichi marked this conversation as resolved.
Show resolved Hide resolved
"""Options for the primitive"""
return self._options
23 changes: 23 additions & 0 deletions qiskit/primitives/containers/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# This code is part of Qiskit.
#
# (C) Copyright IBM 2023.
#
# This code is licensed under the Apache License, Version 2.0. You may
# obtain a copy of this license in the LICENSE.txt file in the root directory
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
#
# Any modifications or derivative works of this code must retain this
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.

"""
Data containers for primitives.
"""

from .bindings_array import BindingsArray
from .data_bin import make_data_bin
from .estimator_pub import EstimatorPub, EstimatorPubLike
from .observables_array import ObservablesArray
from .options import BasePrimitiveOptions, BasePrimitiveOptionsLike
from .primitive_result import PrimitiveResult
from .pub_result import PubResult
36 changes: 36 additions & 0 deletions qiskit/primitives/containers/base_pub.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# This code is part of Qiskit.
#
# (C) Copyright IBM 2023.
#
# This code is licensed under the Apache License, Version 2.0. You may
# obtain a copy of this license in the LICENSE.txt file in the root directory
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
#
# Any modifications or derivative works of this code must retain this
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.

"""
Base Pubs class
"""

from __future__ import annotations

from qiskit import QuantumCircuit
from qiskit.utils.optionals import HAS_PYDANTIC

from .dataclasses import frozen_dataclass


@HAS_PYDANTIC.require_in_instance
@frozen_dataclass
class BasePub:
"""Base class for PUB (Primitive Unified Bloc)"""

circuit: QuantumCircuit
"""Quantum circuit object for the pubs."""

def validate(self):
Copy link
Member

Choose a reason for hiding this comment

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

What is the point of using pydantic here if we have to manually validate anyway? Might as well make it a regular class

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Pydantic is written in Rust, so performance is good, but well, it's a small class, so there won't be that much overhead.
I don't think we need this validation if we use pydantic.
But there are many voices against pydantic, so I'll make it as a regular class.

"""Validate the data"""
if not isinstance(self.circuit, QuantumCircuit):
raise TypeError("circuit must be QuantumCircuit.")
Loading