Skip to content

Commit

Permalink
Gradients with the primitives (Qiskit#8528)
Browse files Browse the repository at this point in the history
* added the gradients with the primitives

Co-authored-by: Ikko Hamamura <ikkoham@users.noreply.github.com>
Co-authored-by: Takashi Imamichi <31178928+t-imamichi@users.noreply.github.com>

* add run_options and supported gate

Co-authored-by: Ikko Hamamura <ikkoham@users.noreply.github.com>
Co-authored-by: Takashi Imamichi <31178928+t-imamichi@users.noreply.github.com>

* added unittests

Co-authored-by: Ikko Hamamura <ikkoham@users.noreply.github.com>
Co-authored-by: Takashi Imamichi <31178928+t-imamichi@users.noreply.github.com>

* lint

Co-authored-by: Ikko Hamamura <ikkoham@users.noreply.github.com>
Co-authored-by: Takashi Imamichi <31178928+t-imamichi@users.noreply.github.com>

* fix based on the comments

Co-authored-by: Ikko Hamamura <ikkoham@users.noreply.github.com>
Co-authored-by: Takashi Imamichi <31178928+t-imamichi@users.noreply.github.com>

* fix

Co-authored-by: Ikko Hamamura <ikkoham@users.noreply.github.com>
Co-authored-by: Takashi Imamichi <31178928+t-imamichi@users.noreply.github.com>

* add spsa gradient

Co-authored-by: Ikko Hamamura <ikkoham@users.noreply.github.com>
Co-authored-by: Takashi Imamichi <31178928+t-imamichi@users.noreply.github.com>

* simplify + async

Co-authored-by: Ikko Hamamura <ikkoham@users.noreply.github.com>
Co-authored-by: Takashi Imamichi <31178928+t-imamichi@users.noreply.github.com>

* added gradient variance

Co-authored-by: Ikko Hamamura <ikkoham@users.noreply.github.com>
Co-authored-by: Takashi Imamichi <31178928+t-imamichi@users.noreply.github.com>

* lint

Co-authored-by: Ikko Hamamura <ikkoham@users.noreply.github.com>
Co-authored-by: Takashi Imamichi <31178928+t-imamichi@users.noreply.github.com>

* added the run_options field

Co-authored-by: Ikko Hamamura <ikkoham@users.noreply.github.com>
Co-authored-by: Takashi Imamichi <31178928+t-imamichi@users.noreply.github.com>

* fix lint

Co-authored-by: Ikko Hamamura <ikkoham@users.noreply.github.com>
Co-authored-by: Takashi Imamichi <31178928+t-imamichi@users.noreply.github.com>

* fix based on comments

Co-authored-by: Ikko Hamamura <ikkoham@users.noreply.github.com>
Co-authored-by: Takashi Imamichi <31178928+t-imamichi@users.noreply.github.com>

* wip fix2

Co-authored-by: Ikko Hamamura <ikkoham@users.noreply.github.com>
Co-authored-by: Takashi Imamichi <31178928+t-imamichi@users.noreply.github.com>

* fix

Co-authored-by: Ikko Hamamura <ikkoham@users.noreply.github.com>
Co-authored-by: Takashi Imamichi <31178928+t-imamichi@users.noreply.github.com>

* lint

Co-authored-by: Ikko Hamamura <ikkoham@users.noreply.github.com>
Co-authored-by: Takashi Imamichi <31178928+t-imamichi@users.noreply.github.com>

* fix epsilon and doc

* lint

Co-authored-by: Ikko Hamamura <ikkoham@users.noreply.github.com>
Co-authored-by: Takashi Imamichi <31178928+t-imamichi@users.noreply.github.com>

* fix

* Update qiskit/algorithms/gradients/base_sampler_gradient.py

Co-authored-by: Takashi Imamichi <31178928+t-imamichi@users.noreply.github.com>

* Update qiskit/algorithms/gradients/base_sampler_gradient.py

Co-authored-by: Takashi Imamichi <31178928+t-imamichi@users.noreply.github.com>

* Update qiskit/algorithms/gradients/base_estimator_gradient.py

Co-authored-by: Takashi Imamichi <31178928+t-imamichi@users.noreply.github.com>

* Update qiskit/algorithms/gradients/base_sampler_gradient.py

Co-authored-by: Takashi Imamichi <31178928+t-imamichi@users.noreply.github.com>

* Update qiskit/algorithms/gradients/base_estimator_gradient.py

Co-authored-by: Takashi Imamichi <31178928+t-imamichi@users.noreply.github.com>

* Update qiskit/algorithms/gradients/base_estimator_gradient.py

Co-authored-by: Takashi Imamichi <31178928+t-imamichi@users.noreply.github.com>

* change epsilon error

Co-authored-by: Ikko Hamamura <ikkoham@users.noreply.github.com>
Co-authored-by: Takashi Imamichi <31178928+t-imamichi@users.noreply.github.com>

* Update qiskit/algorithms/gradients/estimator_gradient_result.py

Co-authored-by: Takashi Imamichi <31178928+t-imamichi@users.noreply.github.com>

* Update qiskit/algorithms/gradients/sampler_gradient_result.py

Co-authored-by: Takashi Imamichi <31178928+t-imamichi@users.noreply.github.com>

* add gradient test

* added batch size in spsa gradients

Co-authored-by: Ikko Hamamura <ikkoham@users.noreply.github.com>
Co-authored-by: Takashi Imamichi <31178928+t-imamichi@users.noreply.github.com>

* fix

Co-authored-by: Ikko Hamamura <ikkoham@users.noreply.github.com>
Co-authored-by: Takashi Imamichi <31178928+t-imamichi@users.noreply.github.com>

* lint

* Update qiskit/algorithms/gradients/lin_comb_estimator_gradient.py

Co-authored-by: Julien Gacon <gaconju@gmail.com>

* add operator tests

Co-authored-by: Ikko Hamamura <ikkoham@users.noreply.github.com>
Co-authored-by: Takashi Imamichi <31178928+t-imamichi@users.noreply.github.com>

* consistent name

* rewrite spsa

Co-authored-by: Ikko Hamamura <ikkoham@users.noreply.github.com>
Co-authored-by: Takashi Imamichi <31178928+t-imamichi@users.noreply.github.com>

* use algorithm job

Co-authored-by: Ikko Hamamura <ikkoham@users.noreply.github.com>
Co-authored-by: Takashi Imamichi <31178928+t-imamichi@users.noreply.github.com>
Co-authored-by: Julien Gacon <gaconju@gmail.com>
Co-authored-by: Takashi Imamichi <imamichi@jp.ibm.com>
  • Loading branch information
5 people authored and ewinston committed Sep 8, 2022
1 parent 879f7ba commit a8e553c
Show file tree
Hide file tree
Showing 18 changed files with 2,702 additions and 0 deletions.
11 changes: 11 additions & 0 deletions qiskit/algorithms/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,17 @@
ShorResult
Gradients
----------
Algorithms to calculate the gradient of a quantum circuit.
.. autosummary::
:toctree: ../stubs/
gradients
Linear Solvers
--------------
Expand Down
87 changes: 87 additions & 0 deletions qiskit/algorithms/gradients/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# This code is part of Qiskit.
#
# (C) Copyright IBM 2022.
#
# 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.

"""
==============================================
Gradients (:mod:`qiskit.algorithms.gradients`)
==============================================
.. currentmodule:: qiskit.algorithms.gradients
Base Classes
============
.. autosummary::
:toctree: ../stubs/
BaseSamplerGradient
BaseEstimatorGradient
Estimator Gradients
===================
.. autosummary::
:toctree: ../stubs/
FiniteDiffEstimatorGradient
LinCombEstimatorGradient
ParamShiftEstimatorGradient
SPSAEstimatorGradient
Sampler Gradients
=================
.. autosummary::
:toctree: ../stubs/
FiniteDiffSamplerGradient
LinCombSamplerGradient
ParamShiftSamplerGradient
SPSASamplerGradient
Results
=======
.. autosummary::
:toctree: ../stubs/
EstimatorGradientResult
SamplerGradientResult
"""

from .base_estimator_gradient import BaseEstimatorGradient
from .base_sampler_gradient import BaseSamplerGradient
from .estimator_gradient_result import EstimatorGradientResult
from .finite_diff_estimator_gradient import FiniteDiffEstimatorGradient
from .finite_diff_sampler_gradient import FiniteDiffSamplerGradient
from .lin_comb_estimator_gradient import LinCombEstimatorGradient
from .lin_comb_sampler_gradient import LinCombSamplerGradient
from .param_shift_estimator_gradient import ParamShiftEstimatorGradient
from .param_shift_sampler_gradient import ParamShiftSamplerGradient
from .sampler_gradient_result import SamplerGradientResult
from .spsa_estimator_gradient import SPSAEstimatorGradient
from .spsa_sampler_gradient import SPSASamplerGradient

__all__ = [
"BaseEstimatorGradient",
"BaseSamplerGradient",
"EstimatorGradientResult",
"FiniteDiffEstimatorGradient",
"FiniteDiffSamplerGradient",
"LinCombEstimatorGradient",
"LinCombSamplerGradient",
"ParamShiftEstimatorGradient",
"ParamShiftSamplerGradient",
"SamplerGradientResult",
"SPSAEstimatorGradient",
"SPSASamplerGradient",
]
165 changes: 165 additions & 0 deletions qiskit/algorithms/gradients/base_estimator_gradient.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
# This code is part of Qiskit.
#
# (C) Copyright IBM 2022.
#
# 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.

"""
Abstract base class of gradient for ``Estimator``.
"""

from __future__ import annotations

from abc import ABC, abstractmethod
from collections.abc import Sequence
from copy import copy

from qiskit.circuit import Parameter, QuantumCircuit
from qiskit.opflow import PauliSumOp
from qiskit.primitives import BaseEstimator
from qiskit.algorithms import AlgorithmJob
from qiskit.quantum_info.operators.base_operator import BaseOperator

from .estimator_gradient_result import EstimatorGradientResult


class BaseEstimatorGradient(ABC):
"""Base class for an ``EstimatorGradient`` to compute the gradients of the expectation value."""

def __init__(
self,
estimator: BaseEstimator,
**run_options,
):
"""
Args:
estimator: The estimator used to compute the gradients.
run_options: Backend runtime options used for circuit execution. The order of priority is:
run_options in ``run`` method > gradient's default run_options > primitive's default
setting. Higher priority setting overrides lower priority setting.
"""
self._estimator: BaseEstimator = estimator
self._default_run_options = run_options

def run(
self,
circuits: Sequence[QuantumCircuit],
observables: Sequence[BaseOperator | PauliSumOp],
parameter_values: Sequence[Sequence[float]],
parameters: Sequence[Sequence[Parameter] | None] | None = None,
**run_options,
) -> AlgorithmJob:
"""Run the job of the estimator gradient on the given circuits.
Args:
circuits: The list of quantum circuits to compute the gradients.
observables: The list of observables.
parameter_values: The list of parameter values to be bound to the circuit.
parameters: The sequence of parameters to calculate only the gradients of
the specified parameters. Each sequence of parameters corresponds to a circuit in
``circuits``. Defaults to None, which means that the gradients of all parameters in
each circuit are calculated.
run_options: Backend runtime options used for circuit execution. The order of priority is:
run_options in ``run`` method > gradient's default run_options > primitive's default
setting. Higher priority setting overrides lower priority setting.
Returns:
The job object of the gradients of the expectation values. The i-th result corresponds to
``circuits[i]`` evaluated with parameters bound as ``parameter_values[i]``. The j-th
element of the i-th result corresponds to the gradient of the i-th circuit with respect
to the j-th parameter.
Raises:
ValueError: Invalid arguments are given.
"""
# if ``parameters`` is none, all parameters in each circuit are differentiated.
if parameters is None:
parameters = [None for _ in range(len(circuits))]
# Validate the arguments.
self._validate_arguments(circuits, observables, parameter_values, parameters)
# The priority of run option is as follows:
# run_options in ``run`` method > gradient's default run_options > primitive's default setting.
run_opts = copy(self._default_run_options)
run_opts.update(run_options)

job = AlgorithmJob(
self._run, circuits, observables, parameter_values, parameters, **run_opts
)
job.submit()
return job

@abstractmethod
def _run(
self,
circuits: Sequence[QuantumCircuit],
observables: Sequence[BaseOperator | PauliSumOp],
parameter_values: Sequence[Sequence[float]],
parameters: Sequence[Sequence[Parameter] | None],
**run_options,
) -> EstimatorGradientResult:
"""Compute the estimator gradients on the given circuits."""
raise NotImplementedError()

def _validate_arguments(
self,
circuits: Sequence[QuantumCircuit],
observables: Sequence[BaseOperator | PauliSumOp],
parameter_values: Sequence[Sequence[float]],
parameters: Sequence[Sequence[Parameter] | None] | None = None,
) -> None:
"""Validate the arguments of the ``run`` method.
Args:
circuits: The list of quantum circuits to compute the gradients.
observables: The list of observables.
parameter_values: The list of parameter values to be bound to the circuit.
parameters: The Sequence of Sequence of Parameters to calculate only the gradients of
the specified parameters. Each Sequence of Parameters corresponds to a circuit in
``circuits``. Defaults to None, which means that the gradients of all parameters in
each circuit are calculated.
Raises:
ValueError: Invalid arguments are given.
"""
# Validation
if len(circuits) != len(parameter_values):
raise ValueError(
f"The number of circuits ({len(circuits)}) does not match "
f"the number of parameter value sets ({len(parameter_values)})."
)

if len(circuits) != len(observables):
raise ValueError(
f"The number of circuits ({len(circuits)}) does not match "
f"the number of observables ({len(observables)})."
)

if parameters is not None:
if len(circuits) != len(parameters):
raise ValueError(
f"The number of circuits ({len(circuits)}) does not match "
f"the number of the specified parameter sets ({len(parameters)})."
)

for i, (circuit, parameter_value) in enumerate(zip(circuits, parameter_values)):
if not circuit.num_parameters:
raise ValueError(f"The {i}-th circuit is not parameterised.")
if len(parameter_value) != circuit.num_parameters:
raise ValueError(
f"The number of values ({len(parameter_value)}) does not match "
f"the number of parameters ({circuit.num_parameters}) for the {i}-th circuit."
)

for i, (circuit, observable) in enumerate(zip(circuits, observables)):
if circuit.num_qubits != observable.num_qubits:
raise ValueError(
f"The number of qubits of the {i}-th circuit ({circuit.num_qubits}) does "
f"not match the number of qubits of the {i}-th observable "
f"({observable.num_qubits})."
)
Loading

0 comments on commit a8e553c

Please sign in to comment.