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 3 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
TaskResult
"""

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, EstimatorTask, ObservablesArray, PrimitiveResult, TaskResult
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
152 changes: 143 additions & 9 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,99 @@

.. estimator-desc:

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

EstimatorV2 class estimates expectation values of quantum circuits and observables.
ikkoham marked this conversation as resolved.
Show resolved Hide resolved

An estimator is initialized with an empty parameter set. The estimator is used to
create a :class:`~qiskit.providers.JobV1`, via the
:meth:`~.BaseEstimatorV2.run()` method. This method is called
with the list of task.
Task is composed of tuple of following parameters ``[(circuit, observables, parameter_values)]``.

* quantum circuit (:math:`\psi(\theta)`): (parameterized) quantum circuits
:class:`~qiskit.circuit.QuantumCircuit`.

* observables (:math:`H_j`): a list of :class:`~.ObservablesArrayLike` classes
(including :class:`~.Pauli`, :class:`~.SparsePauliOp`, str).

* parameter values (:math:`\theta_k`): list of sets of values
to be bound to the parameters of the quantum circuits
(list of list of float or list of dict).

The method returns a :class:`~qiskit.providers.JobV1` object, calling
ikkoham marked this conversation as resolved.
Show resolved Hide resolved
:meth:`qiskit.providers.JobV1.result()` yields the
ikkoham marked this conversation as resolved.
Show resolved Hide resolved
a list of expectation values plus optional metadata like confidence intervals for
the estimation.
ikkoham marked this conversation as resolved.
Show resolved Hide resolved
ikkoham marked this conversation as resolved.
Show resolved Hide resolved

.. math::

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

The broadcast rule applies for observables and parameters. For more information, please check
ikkoham marked this conversation as resolved.
Show resolved Hide resolved
`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 task.
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 @@ -80,26 +170,29 @@

from __future__ import annotations

import typing
ikkoham marked this conversation as resolved.
Show resolved Hide resolved
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 typing import Generic, Optional, 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 .base_primitive import BasePrimitive
from ..containers.estimator_task import EstimatorTask, EstimatorTaskLike
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 +347,44 @@ 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


class BaseEstimatorV2(BasePrimitiveV2, Generic[T]):
"""Estimator base class version 2.

Estimator estimates expectation values of quantum circuits and observables.
ikkoham marked this conversation as resolved.
Show resolved Hide resolved
"""

def __init__(self, options: Optional[BasePrimitiveOptionsLike]):
super().__init__(options=options)

def run(self, tasks: EstimatorTaskLike | Iterable[EstimatorTaskLike]) -> T:
ikkoham marked this conversation as resolved.
Show resolved Hide resolved
ikkoham marked this conversation as resolved.
Show resolved Hide resolved
"""Run the tasks of the estimation of expectation value(s).

Args:
tasks: a tasklike object. Typically, list of tuple
``(QuantumCircuit, observables, parameter_values)``

Returns:
The job object of Estimator's Result.
ikkoham marked this conversation as resolved.
Show resolved Hide resolved
"""
if isinstance(tasks, EstimatorTask):
ikkoham marked this conversation as resolved.
Show resolved Hide resolved
tasks = [tasks]
elif isinstance(tasks, tuple) and isinstance(tasks[0], QuantumCircuit):
tasks = [EstimatorTask.coerce(tasks)]
elif isinstance(tasks, Iterable):
tasks = [EstimatorTask.coerce(task) for task in tasks]
else:
raise TypeError(f"Unsupported type {type(tasks)} is given.")

for task in tasks:
task.validate()

return self._run(tasks)

@abstractmethod
def _run(self, tasks: list[EstimatorTask]) -> T:
ikkoham marked this conversation as resolved.
Show resolved Hide resolved
pass
39 changes: 38 additions & 1 deletion qiskit/primitives/base/base_primitive.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,17 @@

from abc import ABC
from collections.abc import Sequence
from typing import Optional

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 +74,38 @@ 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: Optional[BasePrimitiveOptionsLike] = None):
ikkoham marked this conversation as resolved.
Show resolved Hide resolved
self._options: type(self)._options_class
self._set_options(options)

@property
def options(self) -> BasePrimitiveOptions:
t-imamichi marked this conversation as resolved.
Show resolved Hide resolved
"""Options for BaseEstimator"""
ikkoham marked this conversation as resolved.
Show resolved Hide resolved
return self._options

@options.setter
def options(self, options: BasePrimitiveOptionsLike):
self._set_options(options)

def _set_options(self, options):
ikkoham marked this conversation as resolved.
Show resolved Hide resolved
if options is None:
self._options = self._options_class()
elif isinstance(options, dict):
self._options = self._options_class(**options)
elif isinstance(options, self._options_class):
self._options = options
else:
raise TypeError(
f"Invalid 'options' type. It can only be a dictionary of {self._options_class}"
)
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_databin
from .estimator_task import EstimatorTask
from .observables_array import ObservablesArray
from .options import BasePrimitiveOptions, BasePrimitiveOptionsLike
from .primitive_result import PrimitiveResult
from .task_result import TaskResult
34 changes: 34 additions & 0 deletions qiskit/primitives/containers/base_task.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# 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 Task class
"""

from __future__ import annotations

from qiskit import QuantumCircuit

from .dataclasses import frozen_dataclass


@frozen_dataclass
class BaseTask:
"""Base class for Task"""

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

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