Retract onto an embedded manifold#

The default geometry is ambient Euclidean space. A session can retract each trial point onto an embedded Riemannian manifold through Solver::set_manifold / rgmin_solver_set_manifold.

The contract is manoptcpp's AbstractManifold on a rank-1 f64 vector: project, retract, transport. Citations:

Tokens#

Token

Packing

Retraction

Euclidean

length n

x + v

RigidQuotient

3N Cartesians, N >= 2

horizontal lift x + v

MwRigid

same; masses on the session

same; Eckart inner product

Sphere

unit vector, length n

(x+v)/norm(x+v)

So3

row-major R, length 9

QR with positive diagonal

Stiefel

St(n,1): same as the sphere

same as the sphere

Se3

row-major R then t, length 12

SO(3) on the rotation, Euclidean on t

An isolated molecule or cluster lives on RigidQuotient (R^{3N}/SE(3)): Sella Cartesian fix_translation plus fix_rotation (Hermes, Sarsfield, Zádor, JCTC 2022, https://doi.org/10.1021/acs.jctc.2c00395). MwRigid is the same quotient with the Page–McIver mass-weighted metric used by Sella IRC and gproptimIRCDriver (https://doi.org/10.1063/1.454172, https://doi.org/10.1063/1.434152). Call set_masses with N atomic masses; unit mass makes MwRigid identical to RigidQuotient.

Sphere, So3, Stiefel, and Se3 are matrix-manifold embeddings. So3 rejects any length other than 9. Se3 rejects any length other than 12. They do not pack or prefix-interpret a 3N cluster.

Euclidean is the default. Existing eOn / rgpot / eindir paths do not change until a host calls the setter.

Rust#

use ndarray::array;
use rgmin::{Control, ManifoldKind, Method, Solver};

let n = (3.0_f64).sqrt();
let mut x = array![1.0 / n, 1.0 / n, 1.0 / n];
let mut solver = Solver::new(Method::Steepest, Control::default(), 3);
solver.set_manifold(ManifoldKind::Sphere);
let _ = solver.step(&obj, &mut x).unwrap();

First-order steps retract the taken increment. Quasi-Newton directions are projected into the tangent, then retracted by the accept rule.

C#

rgmin_solver_set_manifold(s, RGMIN_MANIFOLD_RIGID_QUOTIENT);
rgmin_solver_set_manifold(s, RGMIN_MANIFOLD_MW_RIGID);
rgmin_solver_set_masses(s, masses, n_atoms);
rgmin_solver_set_manifold(s, RGMIN_MANIFOLD_SPHERE);
rgmin_solver_set_manifold(s, RGMIN_MANIFOLD_SO3);
rgmin_solver_set_manifold(s, RGMIN_MANIFOLD_STIEFEL);
rgmin_solver_set_manifold(s, RGMIN_MANIFOLD_SE3);
rgmin_solver_set_manifold(s, RGMIN_MANIFOLD_EUCLIDEAN);

Changing the manifold drops method memory (forget).

Packing notes#

  • RigidQuotient is 3N Cartesians. Isolated: kernel of three translations and three infinitesimal rotations (R^{3N}/SE(3)). Periodic: Sella proj_rot = false, so only translations (R^{3N}/T(3)) via rgmin_solver_set_periodic. TRICs are a different chart, not a different packing here.

  • MwRigid is the same 3N packing. Masses are per atom (length N). The IRC path itself is steepest descent in that metric, not a separate token.

  • So3 is a 9-vector, row-major. The tangent projection returns the embedded vector R Omega, not the skew factor alone.

  • Se3 is twelve numbers: the same 9-vector, then a translation. It is one rigid body, not N atoms.

  • Stiefel is St(n,1). A frame with p > 1 is not a length token: n p does not name p.

  • set_project_rigid is the same horizontal projection as RigidQuotient and stays available on Euclidean.