Skip to content

Commit

Permalink
Rename run_options to options in fidelity and gradients and fidelity …
Browse files Browse the repository at this point in the history
…bug fix (Qiskit/qiskit#8755)

* Rename run_options to options

* Update qiskit/algorithms/state_fidelities/base_state_fidelity.py

* Update qiskit/algorithms/state_fidelities/base_state_fidelity.py

* Update qiskit/algorithms/state_fidelities/compute_uncompute.py

* Fix bug

* Fix measurements, empty list

* Add check empty list

* Update test/python/algorithms/state_fidelities/test_compute_uncompute.py

* Update test/python/algorithms/state_fidelities/test_compute_uncompute.py

* Fix black

* Add options properties

* fix unit tests

Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
  • Loading branch information
ElePT and mergify[bot] authored Sep 23, 2022
1 parent a081e75 commit e31b2ea
Show file tree
Hide file tree
Showing 18 changed files with 367 additions and 165 deletions.
71 changes: 48 additions & 23 deletions qiskit_algorithms/gradients/base_estimator_gradient.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,27 +36,28 @@ class BaseEstimatorGradient(ABC):
def __init__(
self,
estimator: BaseEstimator,
run_options: dict | None = None,
options: Options | None = None,
):
"""
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.
options: Primitive backend runtime options used for circuit execution.
The order of priority is: options in ``run`` method > gradient's
default options > primitive's default setting.
Higher priority setting overrides lower priority setting
"""
self._estimator: BaseEstimator = estimator
self._default_run_options = Options()
if run_options is not None:
self._default_run_options.update_options(**run_options)
self._default_options = Options()
if options is not None:
self._default_options.update_options(**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,
**options,
) -> AlgorithmJob:
"""Run the job of the estimator gradient on the given circuits.
Expand All @@ -68,9 +69,10 @@ def run(
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.
options: Primitive backend runtime options used for circuit execution.
The order of priority is: options in ``run`` method > gradient's
default 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
Expand All @@ -87,11 +89,11 @@ def run(
# 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_options(**run_options)
# options in ``run`` method > gradient's default options > primitive's default setting.
opts = copy(self._default_options)
opts.update_options(**options)
job = AlgorithmJob(
self._run, circuits, observables, parameter_values, parameters, **run_opts.__dict__
self._run, circuits, observables, parameter_values, parameters, **opts.__dict__
)
job.submit()
return job
Expand All @@ -103,7 +105,7 @@ def _run(
observables: Sequence[BaseOperator | PauliSumOp],
parameter_values: Sequence[Sequence[float]],
parameters: Sequence[Sequence[Parameter] | None],
**run_options,
**options,
) -> EstimatorGradientResult:
"""Compute the estimator gradients on the given circuits."""
raise NotImplementedError()
Expand Down Expand Up @@ -166,15 +168,38 @@ def _validate_arguments(
f"({observable.num_qubits})."
)

def _get_local_run_options(self, run_options: dict) -> Options:
"""Update the run options in the results.
@property
def options(self) -> Options:
"""Return the union of estimator options setting and gradient default options,
where, if the same field is set in both, the gradient's default options override
the primitive's default setting.
Returns:
The gradient default + estimator options.
"""
return self._get_local_options(self._default_options.__dict__)

def update_default_options(self, **options):
"""Update the gradient's default options setting.
Args:
**options: The fields to update the default options.
"""

self._default_options.update_options(**options)

def _get_local_options(self, options: Options) -> Options:
"""Return the union of the primitive's default setting,
the gradient default options, and the options in the ``run`` method.
The order of priority is: options in ``run`` method > gradient's
default options > primitive's default setting.
Args:
run_options: The run options to update.
options: The fields to update the options
Returns:
The updated run options.
The gradient default + estimator + run options.
"""
run_opts = copy(self._estimator.options)
run_opts.update_options(**run_options)
return run_opts
opts = copy(self._estimator.options)
opts.update_options(**options)
return opts
72 changes: 48 additions & 24 deletions qiskit_algorithms/gradients/base_sampler_gradient.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,25 +30,26 @@
class BaseSamplerGradient(ABC):
"""Base class for a ``SamplerGradient`` to compute the gradients of the sampling probability."""

def __init__(self, sampler: BaseSampler, run_options: dict | None = None):
def __init__(self, sampler: BaseSampler, options: Options | None = None):
"""
Args:
sampler: The sampler 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.
options: Primitive backend runtime options used for circuit execution.
The order of priority is: options in ``run`` method > gradient's
default options > primitive's default setting.
Higher priority setting overrides lower priority setting
"""
self._sampler: BaseSampler = sampler
self._default_run_options = Options()
if run_options is not None:
self._default_run_options.update_options(**run_options)
self._default_options = Options()
if options is not None:
self._default_options.update_options(**options)

def run(
self,
circuits: Sequence[QuantumCircuit],
parameter_values: Sequence[Sequence[float]],
parameters: Sequence[Sequence[Parameter] | None] | None = None,
**run_options,
**options,
) -> AlgorithmJob:
"""Run the job of the sampler gradient on the given circuits.
Expand All @@ -59,10 +60,10 @@ def run(
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.
options: Primitive backend runtime options used for circuit execution.
The order of priority is: options in ``run`` method > gradient's
default options > primitive's default setting.
Higher priority setting overrides lower priority setting
Returns:
The job object of the gradients of the sampling probability. The i-th result
corresponds to ``circuits[i]`` evaluated with parameters bound as ``parameter_values[i]``.
Expand All @@ -78,10 +79,10 @@ def run(
# Validate the arguments.
self._validate_arguments(circuits, parameter_values, parameters)
# The priority of run option is as follows:
# run_options in `run` method > gradient's default run_options > primitive's default run_options.
run_opts = copy(self._default_run_options)
run_opts.update_options(**run_options)
job = AlgorithmJob(self._run, circuits, parameter_values, parameters, **run_opts.__dict__)
# options in `run` method > gradient's default options > primitive's default options.
opts = copy(self._default_options)
opts.update_options(**options)
job = AlgorithmJob(self._run, circuits, parameter_values, parameters, **opts.__dict__)
job.submit()
return job

Expand All @@ -91,7 +92,7 @@ def _run(
circuits: Sequence[QuantumCircuit],
parameter_values: Sequence[Sequence[float]],
parameters: Sequence[Sequence[Parameter] | None],
**run_options,
**options,
) -> SamplerGradientResult:
"""Compute the sampler gradients on the given circuits."""
raise NotImplementedError()
Expand Down Expand Up @@ -138,15 +139,38 @@ def _validate_arguments(
f"the number of parameters ({circuit.num_parameters}) for the {i}-th circuit."
)

def _get_local_run_options(self, run_options: dict) -> dict:
"""Update the run options in the results.
@property
def options(self) -> Options:
"""Return the union of sampler options setting and gradient default options,
where, if the same field is set in both, the gradient's default options override
the primitive's default setting.
Returns:
The gradient default + sampler options.
"""
return self._get_local_options(self._default_options.__dict__)

def update_default_options(self, **options):
"""Update the gradient's default options setting.
Args:
**options: The fields to update the default options.
"""

self._default_options.update_options(**options)

def _get_local_options(self, options: Options) -> Options:
"""Return the union of the primitive's default setting,
the gradient default options, and the options in the ``run`` method.
The order of priority is: options in ``run`` method > gradient's
default options > primitive's default setting.
Args:
run_options: The run options to update.
options: The fields to update the options
Returns:
The updated run options.
The gradient default + sampler + run options.
"""
run_opts = copy(self._sampler.options)
run_opts.update_options(**run_options)
return run_opts
opts = copy(self._sampler.options)
opts.update_options(**options)
return opts
4 changes: 2 additions & 2 deletions qiskit_algorithms/gradients/estimator_gradient_result.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,5 @@ class EstimatorGradientResult:
"""The gradients of the expectation values."""
metadata: list[dict[str, Any]]
"""Additional information about the job."""
run_options: Options
"""run_options for the job."""
options: Options
"""Primitive runtime options for the execution of the job."""
19 changes: 10 additions & 9 deletions qiskit_algorithms/gradients/finite_diff_estimator_gradient.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,15 @@ class FiniteDiffEstimatorGradient(BaseEstimatorGradient):
Compute the gradients of the expectation values by finite difference method.
"""

def __init__(self, estimator: BaseEstimator, epsilon: float, **run_options):
def __init__(self, estimator: BaseEstimator, epsilon: float, **options):
"""
Args:
estimator: The estimator used to compute the gradients.
epsilon: The offset size for the finite difference 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.
options: Primitive backend runtime options used for circuit execution.
The order of priority is: options in ``run`` method > gradient's
default options > primitive's default setting.
Higher priority setting overrides lower priority setting
Raises:
ValueError: If ``epsilon`` is not positive.
Expand All @@ -49,15 +50,15 @@ def __init__(self, estimator: BaseEstimator, epsilon: float, **run_options):
raise ValueError(f"epsilon ({epsilon}) should be positive.")
self._epsilon = epsilon
self._base_parameter_values_dict = {}
super().__init__(estimator, **run_options)
super().__init__(estimator, **options)

def _run(
self,
circuits: Sequence[QuantumCircuit],
observables: Sequence[BaseOperator | PauliSumOp],
parameter_values: Sequence[Sequence[float]],
parameters: Sequence[Sequence[Parameter] | None],
**run_options,
**options,
) -> EstimatorGradientResult:
"""Compute the estimator gradients on the given circuits."""
jobs, metadata_ = [], []
Expand All @@ -76,7 +77,7 @@ def _run(
minus = parameter_values_ - self._epsilon * offset
n = 2 * len(indices)
job = self._estimator.run(
[circuit] * n, [observable] * n, plus.tolist() + minus.tolist(), **run_options
[circuit] * n, [observable] * n, plus.tolist() + minus.tolist(), **options
)
jobs.append(job)

Expand All @@ -91,5 +92,5 @@ def _run(
n = len(result.values) // 2 # is always a multiple of 2
gradient_ = (result.values[:n] - result.values[n:]) / (2 * self._epsilon)
gradients.append(gradient_)
run_opt = self._get_local_run_options(run_options)
return EstimatorGradientResult(gradients=gradients, metadata=metadata_, run_options=run_opt)
opt = self._get_local_options(options)
return EstimatorGradientResult(gradients=gradients, metadata=metadata_, options=opt)
19 changes: 10 additions & 9 deletions qiskit_algorithms/gradients/finite_diff_sampler_gradient.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,30 +33,31 @@ def __init__(
self,
sampler: BaseSampler,
epsilon: float,
**run_options,
**options,
):
"""
Args:
sampler: The sampler used to compute the gradients.
epsilon: The offset size for the finite difference 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.
options: Primitive backend runtime options used for circuit execution.
The order of priority is: options in ``run`` method > gradient's
default options > primitive's default setting.
Higher priority setting overrides lower priority setting
Raises:
ValueError: If ``epsilon`` is not positive.
"""
if epsilon <= 0:
raise ValueError(f"epsilon ({epsilon}) should be positive.")
self._epsilon = epsilon
super().__init__(sampler, **run_options)
super().__init__(sampler, **options)

def _run(
self,
circuits: Sequence[QuantumCircuit],
parameter_values: Sequence[Sequence[float]],
parameters: Sequence[Sequence[Parameter] | None],
**run_options,
**options,
) -> SamplerGradientResult:
"""Compute the sampler gradients on the given circuits."""
jobs, metadata_ = [], []
Expand All @@ -71,7 +72,7 @@ def _run(
plus = parameter_values_ + self._epsilon * offset
minus = parameter_values_ - self._epsilon * offset
n = 2 * len(indices)
job = self._sampler.run([circuit] * n, plus.tolist() + minus.tolist(), **run_options)
job = self._sampler.run([circuit] * n, plus.tolist() + minus.tolist(), **options)
jobs.append(job)

# combine the results
Expand All @@ -92,5 +93,5 @@ def _run(
gradient_.append(dict(enumerate(grad_dist)))
gradients.append(gradient_)

run_opt = self._get_local_run_options(run_options)
return SamplerGradientResult(gradients=gradients, metadata=metadata_, run_options=run_opt)
opt = self._get_local_options(options)
return SamplerGradientResult(gradients=gradients, metadata=metadata_, options=opt)
Loading

0 comments on commit e31b2ea

Please sign in to comment.