Skip to content

Commit 64c1d9e

Browse files
proeselermergify[bot]
authored andcommitted
ADAM bug when calculating the gradient in batches #178 (#183)
Co-authored-by: Peter Röseler <peter.roeseler@gmail.com> Co-authored-by: Steve Wood <40241007+woodsp-ibm@users.noreply.github.com> Co-authored-by: Elena Peña Tapia <57907331+ElePT@users.noreply.github.com> (cherry picked from commit 6724b47)
1 parent de6a85e commit 64c1d9e

File tree

3 files changed

+99
-2
lines changed

3 files changed

+99
-2
lines changed

qiskit_algorithms/optimizers/adam_amsgrad.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# This code is part of a Qiskit project.
22
#
3-
# (C) Copyright IBM 2019, 2023.
3+
# (C) Copyright IBM 2019, 2024.
44
#
55
# This code is licensed under the Apache License, Version 2.0. You may
66
# obtain a copy of this license in the LICENSE.txt file in the root directory
@@ -209,7 +209,9 @@ def minimize(
209209
The result of the optimization, containing e.g. the result as attribute ``x``.
210210
"""
211211
if jac is None:
212-
jac = Optimizer.wrap_function(Optimizer.gradient_num_diff, (fun, self._eps))
212+
jac = Optimizer.wrap_function(
213+
Optimizer.gradient_num_diff, (fun, self._eps, self._max_evals_grouped)
214+
)
213215

214216
derivative = jac(x0)
215217
self._t = 0
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
fixes:
3+
- |
4+
Fixed the AQGD optimizer grouping objective function calls by default so that a single point is now passed to the
5+
objective function. For algorithms that can handle more than one gradient evaluations in their objective function,
6+
such as a VQE in the algorithms here, the number of grouped evaluations can be controlled via the max_grouped_evals
7+
parameter. Grouped evaluations allows a list of points to be handed over so that they can potentially be assessed
8+
more efficiently in a single job.
9+

test/optimizers/test_adam.py

+86
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
# This code is part of a Qiskit project.
2+
#
3+
# (C) Copyright IBM 2024.
4+
#
5+
# This code is licensed under the Apache License, Version 2.0. You may
6+
# obtain a copy of this license in the LICENSE.txt file in the root directory
7+
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
8+
#
9+
# Any modifications or derivative works of this code must retain this
10+
# copyright notice, and modified files need to carry a notice indicating
11+
# that they have been altered from the originals.
12+
13+
"""Tests for the ADAM optimizer."""
14+
15+
from test import QiskitAlgorithmsTestCase
16+
17+
from ddt import ddt, data
18+
import numpy as np
19+
20+
from qiskit_algorithms.optimizers import ADAM, Optimizer
21+
from qiskit_algorithms.utils import algorithm_globals
22+
23+
24+
@ddt
25+
class TestADAM(QiskitAlgorithmsTestCase):
26+
"""Tests for the ADAM optimizer."""
27+
28+
def setUp(self):
29+
super().setUp()
30+
algorithm_globals.random_seed = 52
31+
# Feature vector
32+
self.x = np.array([1, 2, 3, 4])
33+
# Target value
34+
self.y = 5
35+
36+
def objective(self, w):
37+
"""
38+
Objective function to minimize mean squared error.
39+
40+
Parameters:
41+
w : numpy array
42+
The weights (including bias) for the linear model.
43+
44+
Returns:
45+
float
46+
The mean squared error.
47+
"""
48+
# Extract weights and bias from the parameter vector
49+
new_shape = (5, int(len(w) / 5))
50+
w = np.reshape(w, new_shape)
51+
52+
weights = w[:-1, :]
53+
bias = w[-1, :]
54+
# Calculate the predicted values
55+
y_pred = np.dot(self.x, weights) + bias
56+
# Calculate the mean squared error
57+
mse = np.mean((self.y - y_pred) ** 2)
58+
return mse
59+
60+
def run_optimizer(self, optimizer: Optimizer, weights: np.ndarray, max_nfev: int):
61+
"""Test the optimizer.
62+
63+
Args:
64+
optimizer: The optimizer instance to test.
65+
weights: The weights to optimize.
66+
max_nfev: The maximal allowed number of function evaluations.
67+
"""
68+
69+
# Minimize
70+
res = optimizer.minimize(self.objective, np.array(weights), None)
71+
error = res.fun
72+
nfev = res.nfev
73+
74+
self.assertAlmostEqual(error, 0, places=3)
75+
self.assertLessEqual(nfev, max_nfev)
76+
77+
@data(1, 5)
78+
def test_adam_max_evals(self, max_evals_grouped):
79+
"""adam test"""
80+
# Initialize weights (including bias)
81+
w = np.zeros(len(self.x) + 1)
82+
# Initialize optimizer
83+
optimizer = ADAM(maxiter=10000, tol=1e-06)
84+
# Test one evaluation at a time
85+
optimizer.set_max_evals_grouped(max_evals_grouped)
86+
self.run_optimizer(optimizer, w, max_nfev=10000)

0 commit comments

Comments
 (0)