Skip to content

mma, ccsa: halt with NLOPT_ROUNDOFF_LIMITED when the step is zero - #650

Merged
stevengj merged 3 commits into
stevengj:masterfrom
inayatku:ccsa-zero-step
Oct 17, 2026
Merged

stevengj merged 3 commits into
stevengj:masterfrom
inayatku:ccsa-zero-step

Conversation

@inayatku

@inayatku inayatku commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #635.

In the inner iterations of MMA and CCSA, when the approximation is not conservative, rho and rhoc[i] are increased by

rho = MIN(10*rho, 1.1 * (rho + (fcur-dd.gval) / dd.wval));

dd.wval is a sum of 0.5 * dx^2 / (sigma^2 - dx^2) terms. It is 0 when every step is 0 or so small that its square underflows. In the example of #635 (x'x on [-1,1]^2 from (0.5, 0.5) with ftol_rel), the minimum is 0, so the relative test on f does not stop the run before that happens. MMA runs down to x = 5.1e-163 and divides by dd.wval = 0; with floating-point exceptions enabled, as in the report, the division traps.

When dd.wval is 0, no increase of rho can change the step. As suggested in the review, this PR stops the run there with NLOPT_ROUNDOFF_LIMITED, in mma.c and ccsa_quadratic.c. The check is placed right after if (inner_done) break;, where rho or rhoc must increase. A run whose step is 0 because it starts at a stationary point has fcur == dd.gval, so it is done there and still ends as before.

The new test test/t_ccsa_zero_step.cxx runs the example of #635 with LD_MMA and LD_CCSAQ. It checks the FE_DIVBYZERO flag with fetestexcept, accepts NLOPT_ROUNDOFF_LIMITED, and checks that the minimum is found. On master the LD_MMA case fails on the flag. With this change, LD_MMA stops with NLOPT_ROUNDOFF_LIMITED at the same point, f = 0; LD_CCSAQ does not reach the check in this example and still returns NLOPT_FTOL_REACHED.

Checks, with MSVC 2022 on Windows:

  • ctest: 79 of 79 tests pass.
  • testopt with MMA and CCSAQ on all 22 objectives, seeds 0 to 2, and three stopping rules (-e 1000, -e 0 -f 1e-8, -e 0 -x 1e-10): the output (minimum, evaluation count, minimizer) is identical on master and on this branch for all 396 runs.

dd.wval is 0 when every step of the inner iteration is 0 or so small that
its square underflows. This happens, e.g., when minimizing x'x with
ftol_rel: since the minimum is 0, the run only stops once f underflows.
The rho and rhoc updates then divided (fcur - gval) by 0, which raised a
floating-point division-by-zero exception (stevengj#635). The quotient was +inf,
so MIN() picked the 10x increase anyway; use that value directly when
dd.wval is 0. The iterates are unchanged.

Add a test that runs LD_MMA and LD_CCSAQ on this problem and checks the
division-by-zero flag.
@stevengj

Copy link
Copy Markdown
Owner

Seems like we should simply halt the optimization with ROUNDOFF_LIMITED if the step sizes are all zero and/or their squares underflow?

…ivision

As suggested in review: when rho or rhoc must increase but dd.wval is 0
(every step is 0 or its square underflows), no increase can change the
step, so stop with NLOPT_ROUNDOFF_LIMITED. The rho updates are back to
their original form.

The halt is placed after "if (inner_done) break;", so a run whose step
is 0 because it starts at a stationary point (fcur == gval) still ends
as before.
@inayatku inayatku changed the title mma, ccsa: do not divide by zero when updating rho mma, ccsa: halt with NLOPT_ROUNDOFF_LIMITED when the step is zero Oct 17, 2026
@inayatku

Copy link
Copy Markdown
Contributor Author

Done in db2deba: when rho or rhoc must increase and dd.wval is 0, the run stops with NLOPT_ROUNDOFF_LIMITED, and the rho updates are back to their original form. I put the check after if (inner_done) break; rather than at every zero step: a run that starts at a stationary point also has all steps 0, but there fcur == dd.gval, so it is done and still ends as before. On the example of #635, MMA now stops at the same point with NLOPT_ROUNDOFF_LIMITED; the 396 testopt runs are unchanged. I updated the description.

@stevengj
stevengj merged commit ede8b9d into stevengj:master Oct 17, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

MMA causes division by zero when using relative tolerance (ftol_rel)

2 participants