Add the Newton decrement as a termination criterion of the Newton solver.

After an accepted line-search step, the solver has already rebuilt the gradient
and Hessian and solved for the next search direction, so the Newton decrement
0.5*g'*H^-1*g -- the quadratic model's predicted cost improvement of the next
iteration -- costs one dot product. Terminating when it falls below tolerance
avoids running one more iteration only to observe a correspondingly small
actual improvement.

This is a C port of Alain's proposal in MJWarp:
https://github.com/google-deepmind/mujoco_warp/pull/1520

PiperOrigin-RevId: 947768034
Change-Id: I94e5c71a4e2b4a7775611edd1dad254bba2633b4
This commit is contained in:
Yuval Tassa
2026-07-14 10:29:30 -07:00
committed by Copybara-Service
parent 2444defc63
commit 1e66efd114
6 changed files with 115 additions and 3 deletions
+6 -1
View File
@@ -2393,12 +2393,17 @@ static void mj_solPrimal(const mjModel* m, mjData* d, int island, int maxiter, i
saveStats(m, d, island, iter, improvement, gradient, ctx.LSslope,
ctx.nactive, nchange, ctx.LSiter, ctx.nupdate);
// Newton decrement: 0.5*grad'*H^-1*grad, the model's predicted improvement of the
// next step; clamp to 0 so that tolerance == 0 keeps early termination disabled
mjtNum decrement = flg_Newton ? mju_max(0, 0.5*scale*mju_dot(ctx.grad, ctx.Mgrad, nv)) : 0;
// increment iteration count
iter++;
// termination
if ((improvement > 0 && improvement < m->opt.tolerance) ||
gradient < m->opt.tolerance) {
gradient < m->opt.tolerance ||
(flg_Newton && decrement < m->opt.tolerance)) {
break;
}