Skip to content

Commit b4b403e

Browse files
Fix an off by one issue affecting number of iterations in multiple optimizers (#443)
1 parent 9aebc1d commit b4b403e

12 files changed

Lines changed: 67 additions & 22 deletions

File tree

HISTORY.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
### ensmallen ?.??.?: "???"
22
###### ????-??-??
3+
* Fix an off-by-one bug where the actual number of executed iterations was one
4+
fewer than the specified `maxIterations`
5+
([#443](https://github.com/mlpack/ensmallen/pull/443)).
36

47
### ensmallen 3.10.0: "Unexpected Rain"
58
###### 2025-09-25

include/ensmallen_bits/cd/cd_impl.hpp

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -66,9 +66,12 @@ CD<DescentPolicyType>::Optimize(
6666
// Controls early termination of the optimization process.
6767
bool terminate = false;
6868

69+
const size_t actualMaxIterations = (maxIterations == 0) ?
70+
std::numeric_limits<size_t>::max() : maxIterations;
71+
6972
// Start iterating.
7073
Callback::BeginOptimization(*this, function, iterate, callbacks...);
71-
for (size_t i = 1; i != maxIterations && !terminate; ++i)
74+
for (size_t i = 0; i < actualMaxIterations && !terminate; ++i)
7275
{
7376
// Get the coordinate to descend on.
7477
size_t featureIdx = descentPolicy.template DescentFeature<
@@ -120,9 +123,12 @@ CD<DescentPolicyType>::Optimize(
120123
}
121124
}
122125

123-
Info << "CD: maximum iterations (" << maxIterations << ") reached; "
124-
<< "terminating optimization." << std::endl;
125-
126+
if (!terminate)
127+
{
128+
Info << "CD: maximum iterations (" << maxIterations << ") reached; "
129+
<< "terminating optimization." << std::endl;
130+
}
131+
126132
// Calculate and return final objective. No need to pay attention to the
127133
// result of the callback.
128134
const ElemType objective = function.Evaluate(iterate);

include/ensmallen_bits/cmaes/active_cmaes_impl.hpp

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,9 @@ typename MatType::elem_type ActiveCMAES<SelectionPolicyType,
152152
// The current visitation order (sorted by population objectives).
153153
UVecType idx = linspace<UVecType>(0, lambda - 1, lambda);
154154

155+
const size_t actualMaxIterations = (maxIterations == 0) ?
156+
std::numeric_limits<size_t>::max() : maxIterations;
157+
155158
// Now iterate!
156159
Callback::BeginOptimization(*this, function, transformedIterate,
157160
callbacks...);
@@ -163,11 +166,11 @@ typename MatType::elem_type ActiveCMAES<SelectionPolicyType,
163166
size_t patience = 10 + (30 * iterate.n_elem / lambda) + 1;
164167
size_t steps = 0;
165168

166-
for (size_t i = 1; (i != maxIterations) && !terminate; ++i)
169+
for (size_t i = 0; i < actualMaxIterations && !terminate; ++i)
167170
{
168171
// To keep track of where we are.
169-
idx0 = (i - 1) % 2;
170-
idx1 = i % 2;
172+
idx0 = i % 2;
173+
idx1 = (i + 1) % 2;
171174

172175
// Perform Cholesky decomposition. If the matrix is not positive definite,
173176
// add a small value and try again.

include/ensmallen_bits/cmaes/cmaes_impl.hpp

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,9 @@ typename MatType::elem_type CMAES<SelectionPolicyType,
156156
// The current visitation order (sorted by population objectives).
157157
UVecType idx = linspace<UVecType>(0, lambda - 1, lambda);
158158

159+
const size_t actualMaxIterations = (maxIterations == 0) ?
160+
std::numeric_limits<size_t>::max() : maxIterations;
161+
159162
// Now iterate!
160163
Callback::BeginOptimization(*this, function, transformedIterate,
161164
callbacks...);
@@ -165,11 +168,11 @@ typename MatType::elem_type CMAES<SelectionPolicyType,
165168
size_t patience = 10 + (30 * iterate.n_elem / lambda) + 1;
166169
size_t steps = 0;
167170

168-
for (size_t i = 1; (i != maxIterations) && !terminate; ++i)
171+
for (size_t i = 0; i < actualMaxIterations && !terminate; ++i)
169172
{
170173
// To keep track of where we are.
171-
const size_t idx0 = (i - 1) % 2;
172-
const size_t idx1 = i % 2;
174+
const size_t idx0 = i % 2;
175+
const size_t idx1 = (i + 1) % 2;
173176

174177
// Perform Cholesky decomposition. If the matrix is not positive definite,
175178
// add a small value and try again.

include/ensmallen_bits/fasta/fasta_impl.hpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,8 +157,11 @@ FASTA<BackwardStepType>::Optimize(FunctionType& function,
157157
ElemType currentStepSize = (ElemType) maxStepSize;
158158
ElemType lastStepSize = (ElemType) maxStepSize;
159159

160+
const size_t actualMaxIterations = (maxIterations == 0) ?
161+
std::numeric_limits<size_t>::max() : maxIterations;
162+
160163
Callback::BeginOptimization(*this, f, x, callbacks...);
161-
for (size_t i = 1; i != maxIterations && !terminate; ++i)
164+
for (size_t i = 0; i < actualMaxIterations && !terminate; ++i)
162165
{
163166
// During this optimization, we want to optimize h(x) = f(x) + g(x).
164167
// f(x) is `f`, but g(x) is specified by `BackwardStepType`.

include/ensmallen_bits/fbs/fbs_impl.hpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,11 @@ FBS<BackwardStepType>::Optimize(FunctionType& function,
7777
// Controls early termination of the optimization process.
7878
bool terminate = false;
7979

80+
const size_t actualMaxIterations = (maxIterations == 0) ?
81+
std::numeric_limits<size_t>::max() : maxIterations;
82+
8083
Callback::BeginOptimization(*this, f, iterate, callbacks...);
81-
for (size_t i = 1; i != maxIterations && !terminate; ++i)
84+
for (size_t i = 0; i < actualMaxIterations && !terminate; ++i)
8285
{
8386
// During this optimization, we want to optimize h(x) = f(x) + g(x).
8487
// f(x) is `f`, but g(x) is specified by `BackwardStepType`.

include/ensmallen_bits/fista/fista_impl.hpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,8 +123,11 @@ FISTA<BackwardStepType>::Optimize(FunctionType& function,
123123
ElemType currentStepSize = (ElemType) maxStepSize;
124124
ElemType lastStepSize = (ElemType) maxStepSize;
125125

126+
const size_t actualMaxIterations = (maxIterations == 0) ?
127+
std::numeric_limits<size_t>::max() : maxIterations;
128+
126129
Callback::BeginOptimization(*this, f, x, callbacks...);
127-
for (size_t i = 1; i != maxIterations && !terminate; ++i)
130+
for (size_t i = 0; i < actualMaxIterations && !terminate; ++i)
128131
{
129132
// During this optimization, we want to optimize h(x) = f(x) + g(x).
130133
// f(x) is `f`, but g(x) is specified by `BackwardStepType`.

include/ensmallen_bits/fw/frank_wolfe_impl.hpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,11 @@ FrankWolfe<LinearConstrSolverType, UpdateRuleType>::Optimize(
7575
// Controls early termination of the optimization process.
7676
bool terminate = false;
7777

78+
const size_t actualMaxIterations = (maxIterations == 0) ?
79+
std::numeric_limits<size_t>::max() : maxIterations;
80+
7881
Callback::BeginOptimization(*this, f, iterate, callbacks...);
79-
for (size_t i = 1; i != maxIterations && !terminate; ++i)
82+
for (size_t i = 0; i < actualMaxIterations && !terminate; ++i)
8083
{
8184
currentObjective = f.EvaluateWithGradient(iterate, gradient);
8285

include/ensmallen_bits/gradient_descent/gradient_descent_impl.hpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,9 +65,12 @@ GradientDescent::Optimize(FunctionType& function,
6565
// Controls early termination of the optimization process.
6666
bool terminate = false;
6767

68+
const size_t actualMaxIterations = (maxIterations == 0) ?
69+
std::numeric_limits<size_t>::max() : maxIterations;
70+
6871
// Now iterate!
6972
Callback::BeginOptimization(*this, f, iterate, callbacks...);
70-
for (size_t i = 1; i != maxIterations && !terminate; ++i)
73+
for (size_t i = 0; i < actualMaxIterations && !terminate; ++i)
7174
{
7275
overallObjective = f.EvaluateWithGradient(iterate, gradient);
7376

include/ensmallen_bits/iqn/iqn_impl.hpp

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -113,8 +113,11 @@ IQN::Optimize(SeparableFunctionType& functionIn,
113113
BaseGradType gradient(iterate.n_rows, iterate.n_cols);
114114
BaseMatType u = t[0];
115115

116+
const size_t actualMaxIterations = (maxIterations == 0) ?
117+
std::numeric_limits<size_t>::max() : maxIterations;
118+
116119
Callback::BeginOptimization(*this, function, iterate, callbacks...);
117-
for (size_t i = 1; i != maxIterations && !terminate; ++i)
120+
for (size_t i = 0; i < actualMaxIterations && !terminate; ++i)
118121
{
119122
for (size_t j = 0, f = 0; f < numFunctions; j++)
120123
{
@@ -206,8 +209,11 @@ IQN::Optimize(SeparableFunctionType& functionIn,
206209
}
207210
}
208211

209-
Info << "IQN: maximum iterations (" << maxIterations << ") reached; "
210-
<< "terminating optimization." << std::endl;
212+
if (!terminate)
213+
{
214+
Info << "IQN: maximum iterations (" << maxIterations << ") reached; "
215+
<< "terminating optimization." << std::endl;
216+
}
211217

212218
Callback::EndOptimization(*this, function, iterate, callbacks...);
213219
return overallObjective;

0 commit comments

Comments
 (0)