Changelog#

Unreleased#

0.5.2 (2026-10-25)#

SCG reduces its dot products and norms in plain left-to-right order again, so its trajectory reproduces the reference C++ implementation to the parity tolerance; the fused SIMD kernels of 0.5.0 had moved the iterates by roundoff that grew along the trajectory.

0.5.1 (2026-10-25)#

A negative maxmove on rgmin_control_t leaves every step cap off, the default per-atom cap included, so a one-shot minimizer or a session over hyperparameters or weights opts out without a session call. Zero keeps the defaults and a positive value is the Euclidean cap.

0.5.0 (2026-10-25)#

Steihaug-Toint Newton-CG is Method::NewtonCg (newton-cg), and Method::NewtonPcg (newton-pcg) adds a rank-16 Nyström preconditioner rebuilt at the current point. Solver::step and step_hess run it with a forward-difference product; Solver::step_hvp and minimize_method_hvp take an analytic H v. The session counts gradient_calls and hvp_calls separately. The C ABI adds RGMIN_NEWTON_CG, RGMIN_NEWTON_PCG, rgmin_method_from_name, rgmin_minimize_hvp, rgmin_solver_step_hvp and the two counters; Method::from_name parses every method token. abi_minor 36.

Solver::set_geometry_step (rgmin_solver_set_geometry_step) selects the capped Cartesian L-BFGS geometry step: eOn’s L-BFGS with a finite-difference inverse curvature on a severely clipped start and 0.1 I otherwise, s·y / y·y after that, a reset when curvature is negative, the step exceeds the per-atom cap or opposes the force, the probe clipped to the objective bounds, and an energy test with at most ten halvings before a short steepest step. An installed exponential preconditioner is that step’s initial inverse Hessian and the metric of its fallbacks. The step is an explicit opt-in because every Cartesian session already carries the default per-atom cap.

Newton and dogleg leave a first-order saddle: under the force tolerance, an analytic Hessian eigenvalue below the stationarity floor steps the geometry along that mode, scaled to the per-atom cap, at most four times per solve, and the dogleg radius restarts from the initial step. Dogleg grows its trust radius past a per-atom cap: Δmaxis the Euclidean maxmove or eight times the current radius, and the boundary test uses the dogleg length before the cap.

cargo cinstall --features capi installs the hand-written rgmin.h, the C++ wrappers and dlpack/dlpack.h with the rgmin pkg-config file. The header carries RGMIN_H_MAJOR, RGMIN_H_MINOR and RGMIN_H_PATCH, checked by build.rs against the crate version; tests/cinstall/smoke.sh compiles a C and a C++ program against an installed prefix.

A Cartesian session and the one-shot minimizers start with a per-atom cap of 0.2. set_atom_maxmove replaces it; a non-positive value clears it. A positive Euclidean maxmove, with no per-atom call, leaves that default off. The clip is the largest per-atom displacement. ASE maxstep and eOn max_move are both 0.2. On the half-scaled seed-1 LJ38 packing, clearing the cap moves the first L-BFGS atom 256.0348909921298; the default reaches tolerance in 199 L-BFGS steps at -169.49430900932856 (pair 3.895343497108207) and 540 BFGS steps at -166.8029675390938 (pair 4.185205749883185).

SR1 stores the direct Hessian and steps with dogleg, or the Cauchy point when that model is indefinite, inside a Nocedal-Wright trust region. SR2 is Powell’s symmetric Broyden update in the same region. The one-shot SR path passes the per-atom cap into sr_trust_limits and clips the trial with scale_step_atom. A positive Euclidean maxmove leaves that default off. DIIS searches along the GDIIS image sum c_i (x_i - α g_i) when that direction is downhill. Adam scales the Kingma-Ba direction to unit max-atom length.

Quick-min options (QuickMinOptions, Solver::set_quickmin_options, C rgmin_solver_set_quickmin_variant with RGMIN_QUICKMIN_RGMIN, RGMIN_QUICKMIN_EON, RGMIN_QUICKMIN_PER_ATOM, ABI minor 33). eon() is eOn’s step: fixed Δt and no clamp rescale. per_atom() projects each atom on its own force: 645 force calls on LJ38 against 1344 for the default, 32.0 against 19.6 on Pt7. curvature_cap bounds Δt by c / sqrt(|y| / |s|).

Method is #[non_exhaustive]: a match outside rgmin needs a wildcard arm. This break ships with the next minor release, after which a new method is additive.

0.4.0#

Python package rgmin: a free-threaded nanobind module rgmin._core (Method, SolverConfig, Solver, Report, minimize) and rgmin.ase.Optimizer with one rgmin iteration per step. ASE is an extra. The wheel is built with scikit-build-core, Corrosion, and nanobind_add_module(_core FREE_THREADED ...).

rggate (bench/gate/, pixi task gate) compares minimisers on censored paired starts. The reference for a start is explicit, otherwise the baseline on that same start.

Contiguous vector kernels dispatch at runtime. dot and axpy accumulate with f64::mul_add, and the portable clone is the bit reference. The simd feature is on by default and compiles that loop with multiversion 0.9 (x86_64+avx512f+fma on rustc >= 1.89, x86_64+avx2+fma, aarch64+sve, aarch64+neon). RGMIN_SIMD selects a clone. The par feature folds contiguous chunks of 4096 and adds those totals from the first index upward. On this host the axpy wall ratio at n = 30000 had a Holm upper bound below 1, and no auto size was slower beyond its interval, so the clones are default. The pooled atomistic wall interval contained 1. Páll and Hess, Comput. Phys. Commun. 184, 2641 (2013), https://doi.org/10.1016/j.cpc.2013.06.003. Dröge et al., Softw. Pract. Exp. 53, 176 (2023), https://doi.org/10.1002/spe.3075.

Nonlinear CG, with or without a metric, opens its line search with Nocedal-Wright equation 3.60 instead of control.istep on every step. The descent fallback runs in both cases. Conjugacy::PolakRibierePlus is a new variant of the exhaustive Conjugacy enum (max(β_PR, 0)). A replaced metric restarts the recurrence at steepest descent.

Geometry preconditioners install through Solver::set_precon and C rgmin_solver_set_precon (ABI minor 32): Exp, Lindh, the bonded metric, or select_precon.

ECnoise (ecnoise, sample_noise, sample_gradient_noise, sample_gradient_noise_k). Moré and Wild, SIAM J. Sci. Comput. 33, 1292 (2011), https://doi.org/10.1137/100786125. The table uses m + 1 samples at spacing h (default m = 8). h is the gap between samples, matching ECnoise.m, not the half-width of the stencil. NoiseEstimate::declared skips sampling. Gradient noise is the table on one counter-based projection of g. For any covariance Σ, E[eps_g²] = tr(Σ) / n because the direction is uniform on the sphere, so the noise in ||g||_2 is sqrt(n) times eps_g. |f''| and |T| follow Moré and Wild, ACM Trans. Math. Softw. 38, 19 (2012), Algorithm 5.1, https://doi.org/10.1145/2168773.2168777, at their own spacing. The forward step and the gradient probe are clamped to [max(1e-16, 4 ulp(max |x_i|)), 1]. NoiseFloor stops at c * sqrt(n_free) * eps_g (default c = 3) with Status::NoiseFloor after the gtol test. NoiseFloor also carries eps_f. A line search that finds no trial whose decrease clears c * eps_f is the same stop and keeps the pre-step point. sample_value_gradient_noise reads both from one stencil. Status::StepTolerance is the SCG step and objective stop.

Report gains status and Control gains noise_floor. Struct literals break. This ships with 0.4.0. The C ABI (abi_minor 31, layout_revision 3) adds noise_eps, noise_c, and rgmin_stop_t stop on the report.

Pulay residual subspace (Method::Diis, C RGMIN_DIIS, ABI minor 30). Coefficients on the stored gradients sum to one and minimize the combined residual. memory 0 keeps 6 pairs. The step never line searches on the oracle value: one pair takes a steepest kick of length istep, and two or more pairs move to the combination. A kinetic preconditioner is not part of the step.

Quick-min (Method::QuickMin, C RGMIN_QUICKMIN, ABI minor 29). The reference step projects the velocity onto the force when v · F is non-negative and sets the velocity to zero otherwise, then adds Δt F and moves by Δt times the updated velocity. The session caps the displacement and rescales the velocity to the step taken, grows and shrinks Δt with FIRE’s factors (a zero power does not shrink it), and splits an atomic block from a cell block through Solver::set_quickmin_cell.

Rigid-motion removal runs through one Eckart workspace per session (rigid module): the basis is ranked by column-pivoted QR, frozen coordinates keep the combinations that do not move them, a periodic axis drops the rotations, and the host can mark broken translations and rotations. Solver::set_rigid_mode selects Off, Auto, Translation or TranslationRotation; set_project_rigid(true) is Auto; set_pbc_axes, set_frozen, set_broken_axes, set_rigid_mass_metric and set_rigid_mass_weighted declare the molecule. The session projects the step, the L-BFGS pairs and each L-BFGS trial direction, the FIRE force and the FIRE velocity (the velocity in the momentum metric), and the dogleg direction; Lanczos can project inside the product (lowest_mode_with). A Cartesian force is not run through the mass metric. align_rigid aligns two geometries by the Horn / Coutsias quaternion; a periodic axis skips the rotation. MwRigid stores q = sqrt(m) x, the chart metric Euclidean in q, and one atom is a quotient with three translations. The C ABI exports the rigid workspace, rgmin_rigid_project, rgmin_rigid_project_hessian, rgmin_rigid_align and the solver setters; rgmin.h declares them, xts.h aliases them, and the C++ session wraps the setters. abi_minor 35.

Tests cover frozen ranks, a slab, and quaternion alignment. The crate root exports RigidWorkspace and align_rigid.

Tests check that the projector is idempotent and symmetric in the active metric, and that a rigid motion of the geometry moves the projected vector.

align_rigid is Horn / Coutsias. Any periodic axis leaves the rotation at identity and shifts the mobile cloud onto the reference centroid.

project_out_rot_trans borrows one shared workspace. It no longer builds a private Gram-Schmidt basis.

RigidWorkspace projects a vector and replaces H with P H P. The caller owns the buffers. The workspace is reused after the first call at a given size.

Frozen coordinates are zeroed before the Eckart rank. One pin keeps three rotations, two pins keep one axis, and three non-collinear pins give rank 0.

The Eckart basis is ranked with column-pivoted QR. The tolerance is relative to the leading R diagonal. The six-by-six frozen overlap is diagonalized.

Eckart helpers live in rgmin::rigid. RigidMode names Off, Auto, Translation, and TranslationRotation. Auto is the integer set_project_rigid(1) will mean. Eckart vectors are weighted in the active metric.

cargo cinstall installs the hand-written include/rgmin.h and dlpack/dlpack.h. The header compiles as C and as C++. Version macros are RGMIN_H_MAJOR, RGMIN_H_MINOR, and RGMIN_H_PATCH. HighsInt stays out of the public header.

The exponential preconditioner of Packwood et al., J. Chem. Phys. 144, 164109 (2016) (10.1063/1.4947024) is Solver::set_exp. The caller supplies the neighbour list. L-BFGS uses H0 = P^{-1}. FIRE and nonlinear CG use P as the metric. A dense pair or Lindh matrix passed into the two-loop still wins. C: rgmin_exp_t, rgmin_solver_set_exp, rgmin_solver_clear_exp, rgmin_solver_exp_mu. The C++ session wraps those three as set_exp, clear_exp, and exp_mu. abi_minor 28.

Newton and dogleg leave a first-order saddle. When the force is already under gtol, the report reads the analytic Hessian. The floor is max(abs(gtol), 1e-6, 1e-8 times the Frobenius norm of H). A lowest eigenvalue below minus that floor steps along the mode, scaled to the per-atom cap, at most four times. The dogleg radius restarts from the initial step. A converged report costs one extra Hessian.

The docs export installs Org 9.7 or newer from GNU ELPA before ox-rst. Pull requests run the Org export and the Sphinx build. The Pages deploy stays on a push to main or workflow_dispatch. A pull request has its own concurrency group, so it does not cancel a deploy. pages: write and id-token: write belong to the deploy job; the workflow token stays read-only.

CI runs on pushes and pull requests to develop as well as main, and installs numpy and scipy for the HiGHS reference tests.

Capped Cartesian L-BFGS reserves the directional-curvature probe for a nominal step at least 64 times larger than the cap. Ordinary starts use H0 = 0.1 I and five-value nonmonotone acceptance. An installed exponential preconditioner replaces that scalar seed, including those steepest fallbacks. Uncapped L-BFGS keeps the two-loop step and the Wolfe search. A HiGHS feasible set and a non-Euclidean manifold stay on that path.

FIRE 2.0 follows Guénolé et al.’s semi-implicit update order, half-step-back uphill correction with a fresh corrected-point force, initial time-step delay, 2,000-step non-positive-power limit, and cap-consistent velocity. An installed exponential preconditioner uses P as the metric: power is F · v, and the kick and the mix use P^{-1} F.

Steihaug-Toint Newton-CG is a session method: tokens newton-cg and newton-pcg (RGMIN_NEWTON_CG = 21, RGMIN_NEWTON_PCG = 22). Hessian access is a product, analytic from rgmin_solver_step_hvp / rgmin_minimize_hvp and a one-gradient forward difference from rgmin_solver_step. maxmove caps the Euclidean trust radius; a per-atom cap clips the trial and the boundary test sees the pre-clip length. rgmin_solver_gradient_calls and rgmin_solver_hvp_calls count the two. rgmin_method_from_name parses the token. abi_minor 27. Steihaug 1983 (10.1137/0720042); Nocedal and Wright, chapter 7.

rgmin_solver_set_box: per-coordinate HiGHS box on x + p. A NULL side is unbounded. Same status as set_highs (0, or 1 without the highs feature). rgmin_solver_set_trust is the Linfradius; rgmin_solver_add_equality / rgmin_solver_clear_equalities carry a · p = rhs. HighsStep.lo / hi are per-coordinate vectors (length 1 is uniform). abi_minor 22.

Centered matrices (centeredmatrix / RGMIN_MANIFOLD_CENTERED_MATRIX = 22): manopt centeredmatrixfactory. Packed row-major m n. Projection is the centering operator (X 1 = 0 by default, or 1^T X = 0), retraction is X + U then center, transport is the identity. Not the sphere and not a 3N cluster. Tokens 7-10 stay reserved. abi_minor 21.

Positive orthant (positive / RGMIN_MANIFOLD_POSITIVE = 21): manopt positivefactory. Packed length n. Projection is the identity, retraction is x odot exp(v oslash x), transport is the identity. Not the sphere and not a 3N cluster. Tokens 7-10 stay reserved. abi_minor 20.

Complex unit sphere (spherecomplex / RGMIN_MANIFOLD_SPHERE_COMPLEX = 20): manopt spherecomplexfactory. Interleaved (re, im), length 2 n. Projection is v - Re(x^* v) x, retraction is (x+v)/||x+v||, transport is projection at arrival. Not the real sphere and not (S^1)^n. Tokens 7-10 stay reserved. abi_minor 19.

Symmetric doubly-stochastic matrices (multinomialsymmetric / RGMIN_MANIFOLD_MULTINOMIAL_SYM = 19): manopt multinomialsymmetricfactory. Packed row-major n^2, n >= 2. Fisher projection, Sinkhorn+symm retraction, transport at arrival. A 3N cluster is rejected. Tokens 7-10 stay reserved. abi_minor 18.

Doubly-stochastic matrices (multinomialdoublystochastic / RGMIN_MANIFOLD_MULTINOMIAL_DS = 18): manopt multinomialdoublystochasticfactory. Packed row-major n^2, n >= 2. Fisher projection, Sinkhorn retraction, transport at arrival. A 3N cluster is rejected. Tokens 7-10 stay reserved. abi_minor 17.

Constant singleton (constant / RGMIN_MANIFOLD_CONSTANT = 17): manopt constantfactory. Packed length n. Projection and transport are the zero tangent, retraction is the fixed point. A 3N cluster is rejected. Tokens 7-10 stay reserved. abi_minor 16.

Complex Euclidean (euclidean_complex / RGMIN_MANIFOLD_EUCLIDEAN_COMPLEX = 16): manopt euclideancomplexfactory. Interleaved (re, im), length 2 n. Projection is the identity, retraction is x + v, transport is the identity. A 3N cluster is rejected. Tokens 7-10 stay reserved. abi_minor 15.

Skew-symmetric matrices (skewsymmetric / RGMIN_MANIFOLD_SKEWSYMMETRIC = 15): manopt skewsymmetricfactory. Packed row-major n^2, n >= 2. Projection is multiskew (0.5 (A - A^T)), retraction is X + U, transport is the identity. A 3N cluster is rejected.

Symmetric matrices (symmetric / RGMIN_MANIFOLD_SYMMETRIC = 14): manopt symmetricfactory. Packed row-major n^2. Projection is symmetrization, retraction is X + U, transport is the identity. A 3N cluster is rejected. Stamp layout 4.

xts.h declares the exported rgmin_minimize / rgmin_solver_* symbols; xts_minimize and xts_solver_* are macro aliases.

L-BFGS session Accept::None takes the clipped two-loop direction. Wolfe line search stays on Energy / Nonmonotone.

SCG C waist: closed xts_conjugacy_t on xts_scg_params_t (FR=0 .. FrPr=7, dest leaf order). params==NULL is Netlab Polak-Ribiere. Unknown codes are RGMIN_INVALID_PARAMETER. Stamp 1.13.3. Hybrid and Restart stay Rust-only.

Typed lowest-mode waist: closed EigensolverKind (Lanczos=0 through EigenExa=13, schema/eigen.capnp) and EigenParams (nev, krylov, max_iter, tol). No Text. C rgmin_lowest_eigenpair plus rgmin_eigen_params_t. Unlinked backends return RGMIN_UNAVAILABLE.

Molecular manifolds: rigid_quotient is Sella Cartesian R^{3N}/SE(3) (fix_translation + fix_rotation); mw_rigid is the Page–McIver / Sella IRC Eckart metric on the same quotient (rgmin_solver_set_masses). SO(3) is length 9 and SE(3) is length 12; a 3N cluster is rejected rather than packed or prefix-interpreted. Periodic bulk sets rgmin_solver_set_periodic: Sella proj_rot = false, quotient R^{3N}/T(3). Isolated stays R^{3N}/SE(3). abi_minor 10.

Embedded manopt tokens: Euclidean (default), sphere, SO(3), Stiefel St(n,1), SE(3). Hourglass mark and the Diataxis book under docs/orgmode/.

0.2.0#

rgmin_solver_set_highs: opt-in HiGHS feasible-set step. With a host Hessian this is the convex Newton QP min 1/2 p^T P p + g^T p and per-coordinate boxes. Without the highs feature the setter returns

  1. eindir / rgpot / eOn share the same session.

0.1.0#

Rust rewrite of the C++ xtensor tree (0.0.1 on the previous main). Solvers live in Rust. C and C++ reach them through rgmin_solver_t and rgmin_minimize over dlpk tensors.

  • Session API: create / step / step_hess / step_fg / fused evalgrad / forget / free

  • Methods: L-BFGS, BFGS, SR1, SR2, shifted Newton, RFO, eight NLCG conjugacies, steepest, Adam, PSO, FIRE, FIRE 2.0, Barzilai-Borwein, Powell dogleg

  • Host Hessian policy: qn_step two-loop / Newton / RFO; pair H0 = P^{-1}

  • Accept: none / energy / nonmonotone

  • Per-atom maxmove and optional rigid-body projection

  • Optional highs feature: two-loop direction, HiGHS only for a feasible set (box, trust, equalities)

The previous C++ main is unrelated history. This tag is the first Rust release.