Skip to content

feat(optimizer) :: OCINEB restore, LBFGS options, and the xtsci engine - #403

Open
HaoZeke wants to merge 35 commits into
TheochemUI:mainfrom
HaoZeke:fix/ocineb-restore-unhelpful-mmf
Open

feat(optimizer) :: OCINEB restore, LBFGS options, and the xtsci engine#403
HaoZeke wants to merge 35 commits into
TheochemUI:mainfrom
HaoZeke:fix/ocineb-restore-unhelpful-mmf

Conversation

@HaoZeke

@HaoZeke HaoZeke commented Aug 21, 2026

Copy link
Copy Markdown
Collaborator

OCINEB left a walked climbing image in place when the dimer Householder cut rejected the mode.
Pair 26 of OptBench LJ38: alignment 0.700 < 0.707, band force jumped to 8.47, and the NEB never recovered.
Restore the CI whenever MMF does not improve the band force.

Default stop stays Euclidean ||F||_2 (convergence_metric=norm).
OPTIM MYLBFGS RMS (||F||_2 / sqrt(3 N)) uses convergence_metric=rms.
Do not mix those clocks.

Newton and RFO sit outside L-BFGS.
They live in xtsci-optimize.
opt_method=xtsci selects the engine.
[Xtsci] method picks the solver: lbfgs, bfgs, sr1, sr2, newton, rfo, steepest, adam, pso, plus the NLCG tokens (polak_ribiere, fletcher_reeves, hestenes_stiefel, dai_yuan, conjugate_descent, hager_zhang, liu_storey, fr_pr).
Unknown tokens throw.

Native L-BFGS keeps literature options.
Defaults stay lbfgs_curvature=reset, lbfgs_secant=standard, lbfgs_precon=none, lbfgs_accept=energy.

OptBench

Chill et al., J. Chem. Theory Comput. 10, 2014 (doi:10.1021/ct5008718).
Force-call means.
Default metric is ||F||_2.

Minimization, 100 structures, lbfgs_memory=100, no angle/distance reset (Chill INI).

system method mean median n 2014 eOn OPTIM MYLBFGS
LJ38, ||F||_2 < 0.01 shifted Newton on the Mones pair Hessian (lbfgs_step=newton, lbfgs_precon=pair) 104 94 100/100 181 176
Morse_Pt FCC 256, ||F||_2 < 0.001 two-loop L-BFGS, Mones pair as H0 (lbfgs_precon=pair) 34 27 100/100 52 46

convergence_metric=rms uses a different stop (LJ38 mean 167, Morse mean 19).
That row is not the default clock.

LJ38 double-ended, 5 images, 50 pairs, published OCINEB INI (ci_mmf_after_rel=0, absolute ci_mmf_after=0.1):

method mean median n
eOn OCINEB, CI restore 611 360 47/50
OPTIM NEWNEB 5 500 0.001 then BFGSTS 1362 388 50/50

Pairs 13/32/38 still fail OCINEB (termination_reason=2 at 5007 calls).
They also fail CI-NEB.
Chill's published OPTIM DNEB+EF mean of 131 uses their 2-image protocol, not this 5-image rematch.

What

  • NEBOcinebController: restore the climbing image when MMF does not help, including an alignment reject.
  • L-BFGS: Powell damping, Li-Fukushima cautious skip, Zhang-Xu secant, Packwood Exp/C1, Lindh, Mones pair / pair_abs / pair_full, Grippo nonmonotone accept, Al-Baali extra-updates, optional rigid-body projection.
    lbfgs_step=newton|rfo stays as the in-process pair-Hessian path.
  • opt_method=xtsci + [Xtsci] method through Cap'n Proto SSoT, YAML, JSON, pydantic, and SSOT defaults.
    Needs -Dwith_xtsci=true.
  • Docs: optimizer.md, minimization.md.

Test plan

  • python3 -m pytest tests/test_params_ssot.py tests/test_config_metadata.py packages/eon-schema/tests (42 passed)
  • OptBench rematch: LJ38 104/100, Morse 34/100, OCINEB 47/50 vs OPTIM 5-image 50/50
  • GitHub Actions on this head
  • eonclient --features on a -Dwith_xtsci=true build lists the engine

HaoZeke added 14 commits August 21, 2026 08:42
An alignment reject left the dimer walk in place, so the band
force jumped from 1e-3 to O(1) and NEB never recovered.
MYLBFGS converges on RMS = ||F||_2/sqrt(3N) and shrinks a step
when the energy rises. The extra finite-difference H0 call is
not in Nocedal 7.20. Read 2014 lbfgs_* keys from [Optimizer].
norm is Euclidean ||F||_2 of the free-atom force vector. OPTIM
MYLBFGS RMS is ||F||_2/sqrt(3N) and is convergence_metric=rms.
lbfgs_curvature=reset|skip|damped (default reset). damped is
Powell 1978 / Nocedal-Wright 18.3. skip is Li-Fukushima cautious.
lbfgs_project_rigid removes the six null modes on isolated clusters.
Add optional L-BFGS pieces from the optimization literature that do
not change the default ASE two-loop path:

- lbfgs_secant=zhangxu: Zhang-Deng-Chen / Zhang-Xu modified secant
  (JOTA 1999/2001) folds the two function values into y
- lbfgs_curvature=cautious: Li-Fukushima 2001 pair test
  s·y >= ε ||s||^2 ||g||^α
- lbfgs_precon=exp|c1: Packwood-Kermode-Csanyi pair Laplacian
  (J. Chem. Phys. 2016) as the two-loop H0 metric
- lbfgs_h0=ss_sy|adaptive: Barzilai-Borwein-2 or min(BB1, BB2)
- lbfgs_accept=nonmonotone: Grippo window of the last five values
- lbfgs_extra_updates: Al-Baali reuse of the newest pair

Defaults stay reset / standard / none / energy / sy_yy.
Zhang-Xu inflates force calls on Lennard-Jones and Morse pair
potentials. Packwood Exp is the condensed-phase metric; ASE falls
back below about 100 atoms, and Exp on LJ38 costs calls.
lbfgs_precon=lindh is the Lindh 1995 stretch model
k = mu exp(alpha (r_nn^2 - r^2)).
lbfgs_precon=pair is the Mones 2018 positive-definite pair
Hessian of the running potential (analytic LJ or Morse_Pt).
Packwood exp/c1 stay available. Default remains none.
Mones pair drops V''<0. The LJ well sits past the inflection, so
those neighbours vanish. pair_abs keeps |V''| and |V'/r|.
lbfgs_step=newton|rfo uses the analytic pair Hessian as the search
direction, not as L-BFGS H0. rfo is Banerjee 1985 / Baker 1986.
pair_full keeps the signed V'' and V'/r blocks.
opt_method=xtsci with xtsci_method=newton|rfo. The pair Hessian stays
in eOn; the direction is xts_minimize_hess. Needs -Dwith_xtsci=true.
opt_method=xtsci and xtsci_method now exist on the L0 schema, catalog,
YAML, JSON, pydantic, and SSOT defaults, not only on the INI reader.
opt_method=xtsci selects the xtsci-optimize backend. [Xtsci] method
picks the solver: L-BFGS, BFGS, SR1/SR2, Newton, RFO, steepest, Adam,
PSO, and the NLCG conjugacies. Unknown tokens are rejected.
@codecov-commenter

codecov-commenter commented Aug 21, 2026

Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

❌ Patch coverage is 65.12262% with 128 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
client/LBFGS.cpp 58.08% 83 Missing ⚠️
client/ParametersINI.cpp 67.56% 24 Missing ⚠️
client/HelperFunctions.cpp 18.18% 9 Missing ⚠️
client/NEBOcinebController.cpp 11.11% 8 Missing ⚠️
client/Optimizer.cpp 0.00% 3 Missing ⚠️
client/ParametersSSOT.cpp 95.45% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

Published OCINEB restores the climbing image only on positive
curvature. Extra restore after an alignment reject stays behind
ci_mmf_restore_unhelpful until the Baker nebmmf_repro set is
strictly better with it on.
@HaoZeke

HaoZeke commented Aug 21, 2026

Copy link
Copy Markdown
Collaborator Author

Baker nebmmf_repro is the defaulting gate for extra OCINEB restore.
The published set is 24/24 GOOD (PET-MAD-S, published mmf INI).
Extra restore after an alignment reject was not in that protocol.
Without a rematch that is strictly better on every Baker system, it cannot be the default.

Published MMF vs CI-NEB force calls (data/baker_bench.csv):

system MMF CI-NEB ratio
01_hcn 177 202 1.14
02_hcch 300 546 1.82
03_h2co 374 882 2.36
04_ch3o 228 330 1.45
05_cyclopropyl 115 202 1.76
06_bicyclobutane 358 1066 2.98
08_formyloxyethyl 217 242 1.12
09_parentdielsalder 353 722 2.05
10_tetrazine 198 594 3.00
11_trans_butadiene 179 242 1.35
12_ethane_h2_abstraction 224 450 2.01
13_hf_abstraction 154 450 2.92
14_vinyl_alcohol 233 394 1.69
15_hocl 135 298 2.21
16_h2po4_anion 887 1226 1.38
17_claisen 1975 2026 1.03
18_silylene_insertion 356 482 1.35
19_hnccs 1883 3954 2.10
20_hconh3_cation 477 482 1.01
21_acrolein_rot 166 394 2.37
22_hconhoh 201 322 1.60
23_hcn_h2 378 778 2.06
24_h2cnh 137 490 3.58
25_hcnh2 996 2202 2.21
total 10701 18976 1.77

4a371fede puts extra restore behind ci_mmf_restore_unhelpful (default false).
Published OCINEB still restores on positive curvature (status=-2).

Rematch (same published mmf INI, restore off vs on, 24 Baker systems) is the measurement that would lift that default.
Not run yet.
LJ38 pair 26 is not a Baker system and does not license changing the Baker default.

@HaoZeke

HaoZeke commented Aug 21, 2026

Copy link
Copy Markdown
Collaborator Author

ci_mmf_restore_unhelpful has to stay optional, default false.

The Frontiers OCI-NEB article (Goswami, Gunde, Jónsson, Front. Chem. 14, 1807063, 2026, doi:10.3389/fchem.2026.1807063) restores the climbing image only on positive curvature.
Algorithm 1: curvature > 0 restores the saved CI; alignment failure (α < α_tol) does not restore, it keeps the most-negative-curvature point and applies the linear penalty.
That is ci_mmf_restore_unhelpful = false.

The extra restore after an alignment reject is an opt-in deviation from that protocol.
The Baker nebmmf_repro numbers in the previous comment were measured with that paper setting.
They do not license turning the extra restore on by default.

Goswami, Gunde, Jonsson, Front. Chem. 14, 1807063 (2026)
doi:10.3389/fchem.2026.1807063 Algorithm 1 restores the climbing
image only on positive curvature. ci_mmf_restore_unhelpful=false
is that setting.
@github-actions

github-actions Bot commented Aug 21, 2026

Copy link
Copy Markdown

Benchmark Results

Tip

4 benchmark(s) improved

Count
🟢 Improved 4
⚪ Unchanged 4

Improvements

Benchmark Before After Ratio
🟢 bench_eonclient.TimeMinimizationLJCluster.time_minimization_lbfgs 25.5±0ms 13.8±0ms 0.54x
🟢 bench_eonclient.TimeNEBMorsePt.time_neb 208±0ms 167±0ms 0.8x
🟢 bench_eonclient.TimePointMorsePt.time_point_evaluation 11.7±0ms 7.27±0ms 0.62x
🟢 bench_eonclient.TimeSaddleSearchMorseDimer.time_saddle_search_dimer 58.5±0ms 45.9±0ms 0.78x
4 unchanged benchmark(s)
Benchmark Before After Ratio
bench_eonclient.TimeMinimizationLJCluster.peakmem_minimization_lbfgs 39M 39.2M ~1.01x
bench_eonclient.TimeNEBMorsePt.peakmem_neb 38.9M 39.3M ~1.01x
bench_eonclient.TimePointMorsePt.peakmem_point_evaluation 39M 39M ~1x
bench_eonclient.TimeSaddleSearchMorseDimer.peakmem_saddle_search_dimer 39.1M 39.2M ~1x
Details
  • Base: 99f90f25
  • Head: ff5996d5
  • Runner: ubuntu-22.04
Raw asv-spyglass output
All benchmarks:

| Change   | Before   | After    |   Ratio | Benchmark (Parameter)                                                  |
|----------|----------|----------|---------|------------------------------------------------------------------------|
|          | 39M      | 39.2M    |    1.01 | bench_eonclient.TimeMinimizationLJCluster.peakmem_minimization_lbfgs   |
| -        | 25.5±0ms | 13.8±0ms |    0.54 | bench_eonclient.TimeMinimizationLJCluster.time_minimization_lbfgs      |
|          | 38.9M    | 39.3M    |    1.01 | bench_eonclient.TimeNEBMorsePt.peakmem_neb                             |
| -        | 208±0ms  | 167±0ms  |    0.8  | bench_eonclient.TimeNEBMorsePt.time_neb                                |
|          | 39M      | 39M      |    1    | bench_eonclient.TimePointMorsePt.peakmem_point_evaluation              |
| -        | 11.7±0ms | 7.27±0ms |    0.62 | bench_eonclient.TimePointMorsePt.time_point_evaluation                 |
|          | 39.1M    | 39.2M    |    1    | bench_eonclient.TimeSaddleSearchMorseDimer.peakmem_saddle_search_dimer |
| -        | 58.5±0ms | 45.9±0ms |    0.78 | bench_eonclient.TimeSaddleSearchMorseDimer.time_saddle_search_dimer    |

HaoZeke added 10 commits August 21, 2026 15:52
Energy and Grippo shrink are opt-in. The default two-loop step
matches ASE and the SVN process_search Morse budget. PartialPivLU
replaces LDLT so manylinux Eigen plus C++20 compiles.
After a downhill dimer step maxEnergyImage can hop. Band
convergenceForce is then a neighbor, so restore rewound the
saddle. Help and shouldReset use the image the dimer moved.
relaxMatter advances by step(). Mapping that to run(1) cold-starts
every first-order xtsci method, so L-BFGS, BFGS, SR1/2, and the NLCG
conjugacies all take the same Wolfe line search and the same force-call
count. The first step now consumes max_iterations in one solve.
relaxMatter calls Optimizer::step. Mapping that to a full
xts_minimize cold-started every first-order method. The adapter now
owns a session: create once, step once per outer iteration, free
in the destructor. Requires xtsci-optimize ABI minor 2.
The pair matrix stays in eOn. xtsci applies it as H0 or as a
Newton/RFO step. Requires xtsci-optimize ABI minor 3.
Expose the xtsci L-BFGS session policy on Optimizer.Xtsci in Cap'n
Proto, the generated catalog, INI, JSON, YAML, and Pydantic. Native
[LBFGS] lbfgs_step / lbfgs_precon stay as they are.
The session reads Optimizer.Xtsci first. [LBFGS] lbfgs_step and
lbfgs_precon remain a fallback so rematch INIs keep working. Native
LBFGS::hessianStep is unchanged.
none / energy / nonmonotone through Cap'n Proto, INI, JSON, YAML,
and Pydantic. Native [LBFGS] lbfgs_accept stays as the fallback.
Matter bills energy and forces together. The adapter uses
xts_solver_step_fg / step_hess_fg and skips setPositions when x
is unchanged. [Xtsci] accept drives xts_solver_set_accept so
rematch can use nonmonotone instead of a mandatory backtrack.
Drive xts_solver_set_atom_maxmove, set_project_rigid, extra_updates,
and cautious from the native [LBFGS] rematch knobs. Drop the Euclidean
3N cap that crushed isolated-cluster Newton steps.
The last accepted x is stored on the optimizer. Recreating the
callback context every step() was dirtying Matter and billing a
second force() at the same geometry.
The last fused callback already placed Matter at the accepted
geometry. A second setPositions dirtied the PES cache.
getPositions after a PBC wrap does not match the accepted tensor,
so the session never reused the last (f, g) and billed twice.
The first step was setPositions on the start point and paying a
second force() after the host convergence check.
[Xtsci] method maps onto xtsci-optimize abi_minor 6. Dogleg
defaults the host pair Hessian when precon is still none. FIRE
takes the optimizer time step as the inertial dt.
Host Cartesian P for [Xtsci] precon and native L-BFGS H0. lindh_full
is the Lindh 1995 stretch-bend-torsion model. swart screens that
model into strong and weak pairs. fischer and schlegel are the
standard QM estimated Hessians assembled as B^T k B. Native L-BFGS
now calls pairhess::build so the token list cannot drift.
Opt-in HiGHS feasible-set step. Needs xtsci-optimize 0.2.0 built
with --features highs. The setter fails cleanly without that
feature.
Tokens match xts_manifold_t: euclidean, sphere, so3, stiefel, se3.
The session setter is xts_solver_set_manifold (ABI minor 8).
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.

2 participants