You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The L-BFGS methods now detect convergence instead of always burning the whole iteration
budget.trainLBFGS and trainInverseLBFGS stop once the solver's gradient or step norm
falls below the new LBFGSGradientTolerance / LBFGSStepTolerance (start at 1e-5, the trainingOptions("lbfgs") default). Both default to 0 = off, so existing scripts keep their
exact previous behaviour. This mattered because post-convergence L-BFGS iterations are the
most expensive in the toolbox — each pays a full loss + gradient plus up to LBFGSMaxNumLineSearchIterations extra objective evaluations — and there was no
workaround: chaining short trainLBFGS calls builds a fresh lbfgsState every time,
throwing away the curvature history that makes L-BFGS worth using. Measured on the toy
Poisson refinement (50 Adam + up to 200 L-BFGS, rng(0)): 200 iterations / 2.72 s at the
full budget versus 45 iterations / 0.56 s with a 1e-2 gradient tolerance — 78 % of the
budget saved, and the relative L2 error at the stop was no worse (0.109 % vs 0.183 %).
The L-BFGS solver is now tunable at all. New LBFGSHistorySize (curvature pairs kept;
10 by default, the PINN literature commonly uses 50–100 for the ill-conditioned PINN
Hessian), LBFGSLineSearchMethod, LBFGSMaxNumLineSearchIterations, LBFGSInitialInverseHessianFactor and LBFGSInitialStepSize. Previously the refinement
stage — the documented route to high accuracy — had no lever but MaxIterations.
Every training history carries StopReason, so a converged run, an exhausted budget, an
early stop, a user Stop click and a NaN rollback are finally distinguishable
programmatically: "max-iterations", "early-stop", "non-finite-loss", "monitor-stop", "gradient-tolerance", "step-tolerance", "line-search-failed".
tools/runAllExamples now runs all of them. It covered 9 of the 19 examples — only
those ending with a vysledky summary struct — so the release check silently skipped ten,
including four that chain L-BFGS. It now runs every example in the order of the guided tour,
threshold-checks the 9 that report numbers and smoke-checks the rest (they must run without
error), and refuses to start if any examples/*.m is neither listed nor explicitly excluded,
so the coverage cannot drift out of sight again. It also reports per example whether warning PINN:Optimizer:LBFGSLineSearchFailed fired — the one 1.2.0 behaviour change that can stale
an example's committed outputs.
The Adam histories identify the state that early stopping restores — BestLoss and BestIteration. BestLoss equals min(Loss) only when StopTolerance = 0; it is NaN
(with BestIteration = 0) when no improvement was recorded.
The L-BFGS histories carry the per-iteration solver normsGradientsNorm and StepNorm
— recorded unconditionally, because a previous run's trace is the only sound way to pick a
tolerance. Plot them to see whether a refinement is still progressing or has flattened.
tests/test_LBFGSConvergence.m (10 tests) and five more in test_OptimizerExtensions.m —
157 tests total, including a fifth dlaccelerate equivalence sentinel covering the
convergence-stopping path and the previously untested StopPatience + ResampleEvery
combination. The pre-existing 142 tests are unchanged.
Changed
A failed L-BFGS line search now stops the loop (LBFGSStopOnLineSearchFailure, default
true) with warning PINN:Optimizer:LBFGSLineSearchFailed. The full-batch objective is
deterministic, so after a failure the iterate does not move and every further iteration
re-fails identically while still paying its line-search evaluations. Measured with the
failure forced from iteration one: spinning through a 100-iteration budget took 9.82 s with
the loss frozen at its initial value, against 0.12 s for stopping — 99 % of the wall clock
was pure waste. Set the property false for the previous keep-spinning behaviour; the warning
fires once per run either way, and the message names the remedies.
This is not hypothetical: the shipped priklad_helmholtz trips it. Its L-BFGS polish phase
was budgeted 300 iterations and stops at 16, because the example sits on the
network-capacity plateau it exists to teach — the reported error is identical before and
after the phase, so 95 % of that budget had been buying nothing. The failed line search is
the mechanism behind a plateau the docs had only observed empirically until now.
Fixed
StopPatience combined with ResampleEvery compared losses across different objectives.
In-loop resampling reset the Adam moment estimates but not the best-loss/patience state, so
after a collocation swap the patience counter measured the objective jump rather than a
stall — it could stop the run spuriously, or restore a bestNet that was judged on the old
collocation set. Each resample now restarts the best/patience tracking, which makes early
stopping act per resample segment (documented on both properties). No test covered the
combination before; one does now.
The Trainer app reported a loss that did not belong to the network it returned. After an
early stop the app printed history.Loss(end) — the discarded last iterate — while showing
the restored best network. It now reports BestLoss in that case (verified end-to-end: the
re-evaluated loss of the returned network matches BestLoss exactly).
Documentation
Czech terminology: the advection equation is advekční rovnice. The convection example
and the guided tour called it "transportní (konvekční) rovnice", which is a calque —
in Czech literature "transportní rovnice" is the superset (it carries a diffusion term too),
and "konvekční" names a term inside it, not the equation. The glossary
(doc/Slovnik_terminologie.md) now records the ruling and its source. Prose only; no code,
so the embedded example outputs are unaffected.
The documented test-suite timings were corrected (they had drifted far from reality, which
wasted planning time), and RELEASING.md now warns that an open MATLAB project holds file
handles on the working tree — a branch switch then dies partway with a permission error.
Six rows of the README results table disagreed with the numbers the examples actually
print. The table is supposed to quote the committed %[output:...] blocks verbatim, but priklad_lbfgs (0.0026 % vs the printed 0.0041 %), priklad_burgers, priklad_inverzni, priklad_kyvadlo (the parameter error), priklad_duffing and priklad_konvekce had drifted
— the examples were re-run at some point without the table following. All six now match
their examples, and priklad_inverzni_lbfgs shows its before/after numbers instead of
"see the script". No measurements were re-taken: the values come from the committed outputs.
priklad_helmholtz re-run, and its numbers moved (35.9 % → 7.39 % → 0.75 %, from
34.7 % → 6.61 % → 0.62 %). The lesson and the ranking of the three remedies are unchanged;
the shift is the documented dlaccelerate FP-reordering spread, and it appears in all three
attempts including the two that never touch L-BFGS. Its committed output now also carries the
line-search warning, which makes the capacity plateau visible in the example itself. The
README table, both tuning guides (§3, §7, §9), the roadmap E2 entry, both guided tours and
the skill file were realigned to the new committed values.