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

Fix the aggregation argument of SamplingVQE #9221

Merged
merged 4 commits into from
Dec 5, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ def aggregate(measurements):
accumulated_percent = 0 # once alpha is reached, stop
cvar = 0
for probability, value in sorted_measurements:
cvar += value * max(probability, alpha - accumulated_percent)
cvar += value * min(probability, alpha - accumulated_percent)
accumulated_percent += probability
if accumulated_percent >= alpha:
break
Expand Down
14 changes: 9 additions & 5 deletions qiskit/algorithms/minimum_eigensolvers/sampling_vqe.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,10 +98,12 @@ def my_minimizer(fun, x0, jac=None, bounds=None) -> OptimizerResult:
optimizer (Optimizer | Minimizer): A classical optimizer to find the minimum energy. This
can either be a Qiskit :class:`.Optimizer` or a callable implementing the
:class:`.Minimizer` protocol.
aggregation (float | Callable[[list[float]], float] | None): A float or callable to specify
how the objective function evaluated on the basis states should be aggregated. If a
float, this specifies the :math:`\alpha \in [0,1]` parameter for a CVaR expectation
value [1].
aggregation (float | Callable[[list[tuple[float, complex]], float] | None):
A float or callable to specify how the objective function evaluated on the basis states
should be aggregated. If a float, this specifies the :math:`\alpha \in [0,1]` parameter
for a CVaR expectation value [1]. If a callable, it takes a list of basis state
measurements specified as ``[(probability, objective_value)]`` and return an objective
value as float. If None, all an ordinary expectation value is calculated.
callback (Callable[[int, np.ndarray, float, dict[str, Any]], None] | None): A callback that
can access the intermediate data at each optimization step. These data are: the
evaluation count, the optimizer parameters for the ansatz, the evaluated value, and the
Expand Down Expand Up @@ -292,7 +294,9 @@ def store_best_measurement(best):
):
best_measurement["best"] = best_i

estimator = _DiagonalEstimator(sampler=self.sampler, callback=store_best_measurement)
estimator = _DiagonalEstimator(
sampler=self.sampler, callback=store_best_measurement, aggregation=self.aggregation
)

def evaluate_energy(parameters):
nonlocal eval_count
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
fixes:
- |
Fixed a bug in :class:`.SamplingVQE` where the ``aggregation`` did not have an effect.
Now the aggregation function and CVaR expectation value can correctly be specified.
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,29 @@ def store_intermediate_result(eval_count, parameters, mean, metadata):
for params in history["parameters"]:
self.assertTrue(all(isinstance(param, float) for param in params))

def test_aggregation(self):
"""Test the aggregation works."""

# test a custom aggregration that just uses the best measurement
def best_measurement(measurements):
res = min(measurements, key=lambda meas: meas[1])[1]
return res

# test CVaR with alpha of 0.4 (i.e. 40% of the best measurements)
alpha = 0.4

ansatz = RealAmplitudes(1, reps=0)
ansatz.h(0)

for aggregation in [alpha, best_measurement]:
with self.subTest(aggregation=aggregation):
vqe = SamplingVQE(Sampler(), ansatz, _mock_optimizer, aggregation=best_measurement)
result = vqe.compute_minimum_eigenvalue(Pauli("Z"))

# evaluation at x0=0 samples -1 and 1 with 50% probability, and our aggregation
# takes the smallest value
self.assertAlmostEqual(result.optimal_value, -1)


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