use ndarray::{Array1, Array2, ArrayView1, ArrayView2};
use opt::constants::{ARMIJO_C1, BACKTRACK_CONTRACTION};
use opt::{AcceptedStep, BacktrackConfig, backtracking_line_search};
use crate::chart_coordinate_solve::PeriodicCurveExtrema;
use crate::manifold::{AffineCoordinateEvaluator, AmbientSphereHarmonicEvaluator, CylinderHarmonicEvaluator, DuchonCoordinateEvaluator, EuclideanPatchEvaluator, PeriodicHarmonicEvaluator, SaeBasisEvaluator, SaeManifoldAtom, TorusHarmonicEvaluator};
use gam_linalg::faer_ndarray::FaerEigh;
use faer::Side;
pub const KANTOROVICH_THRESHOLD: f64 = 0.5;
pub(crate) const NEWTON_REFINE_CONVERGED_EPS: f64 = 1.0e-12;
pub(crate) const CERTIFIED_GLOBAL_MIN_RECON_FLOOR: f64 = 1.0e-11;
#[derive(Debug, Clone)]
pub struct ChartRegion {
pub center: Array1<f64>,
pub radius: f64,
pub exclusion_r_min: Option<f64>,
pub radial_r_max: Option<f64>,
}
impl ChartRegion {
pub fn new(center: Array1<f64>, radius: f64) -> Self {
Self {
center,
radius,
exclusion_r_min: None,
radial_r_max: None,
}
}
pub fn with_radial_bounds(mut self, r_min: f64, r_max: f64) -> Self {
self.exclusion_r_min = Some(r_min);
self.radial_r_max = Some(r_max);
self
}
pub(crate) fn assert_valid(&self) {
assert!(
self.radius.is_finite()
&& self.radius >= 0.0
&& self.center.iter().all(|c| c.is_finite()),
"ChartRegion must have a finite center and a finite non-negative radius"
);
}
}
pub trait BasisHessianLipschitz {
fn value_sup(&self, chart: &ChartRegion) -> f64;
fn jacobian_sup(&self, chart: &ChartRegion) -> f64;
fn hessian_sup(&self, chart: &ChartRegion) -> f64;
fn third_sup(&self, chart: &ChartRegion) -> f64;
}
pub(crate) fn harmonic_jet_sup(num_basis: usize, order: u32) -> f64 {
let top_harmonic = num_basis.saturating_sub(1) / 2;
let omega = std::f64::consts::TAU * top_harmonic as f64;
omega.powi(order as i32)
}
impl BasisHessianLipschitz for PeriodicHarmonicEvaluator {
fn value_sup(&self, chart: &ChartRegion) -> f64 {
chart.assert_valid();
1.0
}
fn jacobian_sup(&self, chart: &ChartRegion) -> f64 {
chart.assert_valid();
harmonic_jet_sup(self.num_basis, 1)
}
fn hessian_sup(&self, chart: &ChartRegion) -> f64 {
chart.assert_valid();
harmonic_jet_sup(self.num_basis, 2)
}
fn third_sup(&self, chart: &ChartRegion) -> f64 {
chart.assert_valid();
harmonic_jet_sup(self.num_basis, 3)
}
}
impl BasisHessianLipschitz for TorusHarmonicEvaluator {
fn value_sup(&self, chart: &ChartRegion) -> f64 {
chart.assert_valid();
1.0
}
fn jacobian_sup(&self, chart: &ChartRegion) -> f64 {
chart.assert_valid();
torus_jet_sup(self.num_harmonics(), self.latent_dim(), 1)
}
fn hessian_sup(&self, chart: &ChartRegion) -> f64 {
chart.assert_valid();
torus_jet_sup(self.num_harmonics(), self.latent_dim(), 2)
}
fn third_sup(&self, chart: &ChartRegion) -> f64 {
chart.assert_valid();
torus_jet_sup(self.num_harmonics(), self.latent_dim(), 3)
}
}
pub(crate) fn torus_jet_sup(num_harmonics: usize, latent_dim: usize, order: u32) -> f64 {
let omega = std::f64::consts::TAU * num_harmonics as f64;
omega.powi(order as i32) * (latent_dim as f64).powi(order as i32)
}
impl BasisHessianLipschitz for AmbientSphereHarmonicEvaluator {
fn value_sup(&self, chart: &ChartRegion) -> f64 {
chart.assert_valid();
self.column_jet_bound()
}
fn jacobian_sup(&self, chart: &ChartRegion) -> f64 {
chart.assert_valid();
self.column_jet_bound() * self.degree() as f64
}
fn hessian_sup(&self, chart: &ChartRegion) -> f64 {
chart.assert_valid();
self.column_jet_bound() * (self.degree() as f64).powi(2)
}
fn third_sup(&self, chart: &ChartRegion) -> f64 {
chart.assert_valid();
self.column_jet_bound() * (self.degree() as f64).powi(3)
}
}
impl BasisHessianLipschitz for AffineCoordinateEvaluator {
fn value_sup(&self, chart: &ChartRegion) -> f64 {
let center_norm = chart.center.dot(&chart.center).sqrt();
1.0 + center_norm + chart.radius
}
fn jacobian_sup(&self, chart: &ChartRegion) -> f64 {
chart.assert_valid();
1.0
}
fn hessian_sup(&self, chart: &ChartRegion) -> f64 {
chart.assert_valid();
0.0
}
fn third_sup(&self, chart: &ChartRegion) -> f64 {
chart.assert_valid();
0.0
}
}
impl BasisHessianLipschitz for EuclideanPatchEvaluator {
fn value_sup(&self, chart: &ChartRegion) -> f64 {
let rho = patch_rho(chart);
let d = self.max_degree as i32;
rho.powi(d).max(1.0)
}
fn jacobian_sup(&self, chart: &ChartRegion) -> f64 {
patch_jet_sup(self.latent_dim, self.max_degree, chart, 1)
}
fn hessian_sup(&self, chart: &ChartRegion) -> f64 {
patch_jet_sup(self.latent_dim, self.max_degree, chart, 2)
}
fn third_sup(&self, chart: &ChartRegion) -> f64 {
patch_jet_sup(self.latent_dim, self.max_degree, chart, 3)
}
}
impl BasisHessianLipschitz for CylinderHarmonicEvaluator {
fn value_sup(&self, chart: &ChartRegion) -> f64 {
cylinder_jet_sup(self.circle_harmonics, self.line_degree, chart, 0)
}
fn jacobian_sup(&self, chart: &ChartRegion) -> f64 {
cylinder_jet_sup(self.circle_harmonics, self.line_degree, chart, 1)
}
fn hessian_sup(&self, chart: &ChartRegion) -> f64 {
cylinder_jet_sup(self.circle_harmonics, self.line_degree, chart, 2)
}
fn third_sup(&self, chart: &ChartRegion) -> f64 {
cylinder_jet_sup(self.circle_harmonics, self.line_degree, chart, 3)
}
}
pub(crate) fn cylinder_jet_sup(
circle_harmonics: usize,
line_degree: usize,
chart: &ChartRegion,
order: u32,
) -> f64 {
let omega = std::f64::consts::TAU * circle_harmonics as f64;
let big_d = line_degree as f64;
let rho = patch_rho(chart);
let mut best = 0.0_f64;
for k0 in 0..=order {
let k1 = order - k0;
let circle = if k0 == 0 { 1.0 } else { omega.powi(k0 as i32) };
let line = if k1 == 0 {
rho.powi(line_degree as i32).max(1.0)
} else {
let residual = line_degree.saturating_sub(k1 as usize) as i32;
big_d.powi(k1 as i32) * rho.powi(residual).max(1.0)
};
best = best.max(circle * line);
}
best
}
pub(crate) fn patch_rho(chart: &ChartRegion) -> f64 {
let center_inf = chart
.center
.iter()
.fold(0.0_f64, |acc, &v| acc.max(v.abs()));
center_inf + chart.radius
}
pub(crate) fn patch_jet_sup(
latent_dim: usize,
max_degree: usize,
chart: &ChartRegion,
order: u32,
) -> f64 {
let d = latent_dim as f64;
let big_d = max_degree as f64;
let rho = patch_rho(chart);
let residual_degree = max_degree.saturating_sub(order as usize) as i32;
d.powi(order as i32) * big_d.powi(order as i32) * rho.powi(residual_degree).max(1.0)
}
impl BasisHessianLipschitz for DuchonCoordinateEvaluator {
fn value_sup(&self, chart: &ChartRegion) -> f64 {
let r_max = chart.radial_r_max.unwrap_or(chart.radius);
let poly = duchon_poly_jet_sup(self.centers.ncols(), self.order_degree(), chart, 0);
(r_max.powi(3)).max(poly)
}
fn jacobian_sup(&self, chart: &ChartRegion) -> f64 {
let r_max = chart.radial_r_max.unwrap_or(chart.radius);
let kernel = 3.0 * r_max * r_max;
let poly = duchon_poly_jet_sup(self.centers.ncols(), self.order_degree(), chart, 1);
kernel.max(poly)
}
fn hessian_sup(&self, chart: &ChartRegion) -> f64 {
let r_max = chart.radial_r_max.unwrap_or(chart.radius);
let r_min = chart
.exclusion_r_min
.unwrap_or(chart.radius)
.max(f64::MIN_POSITIVE);
let kernel = 6.0 * r_max + 3.0 * r_max * r_max / r_min;
let poly = duchon_poly_jet_sup(self.centers.ncols(), self.order_degree(), chart, 2);
kernel.max(poly)
}
fn third_sup(&self, chart: &ChartRegion) -> f64 {
let r_max = chart.radial_r_max.unwrap_or(chart.radius);
let r_min = chart
.exclusion_r_min
.unwrap_or(chart.radius)
.max(f64::MIN_POSITIVE);
let kernel = 6.0 + 18.0 * r_max / r_min + 9.0 * r_max * r_max / (r_min * r_min);
let poly = duchon_poly_jet_sup(self.centers.ncols(), self.order_degree(), chart, 3);
kernel.max(poly)
}
}
trait DuchonOrderDegree {
fn order_degree(&self) -> usize;
}
impl DuchonOrderDegree for DuchonCoordinateEvaluator {
fn order_degree(&self) -> usize {
match self.order {
gam_terms::basis::DuchonNullspaceOrder::Zero => 0,
gam_terms::basis::DuchonNullspaceOrder::Linear => 1,
gam_terms::basis::DuchonNullspaceOrder::Degree(d) => d,
}
}
}
pub(crate) fn duchon_poly_jet_sup(
latent_dim: usize,
order_degree: usize,
chart: &ChartRegion,
order: u32,
) -> f64 {
if order_degree == 0 {
return if order == 0 { 1.0 } else { 0.0 };
}
patch_jet_sup(latent_dim, order_degree, chart, order)
}
pub(crate) fn decoder_row_norm_sum(decoder: ArrayView2<'_, f64>) -> f64 {
let mut acc = 0.0;
for row in decoder.rows() {
acc += row.dot(&row).sqrt();
}
acc
}
#[derive(Debug, Clone, Copy)]
pub(crate) struct ReconstructionJetSups {
pub(crate) value: f64,
pub(crate) jacobian: f64,
pub(crate) hessian: f64,
pub(crate) third: f64,
}
pub(crate) fn pair_trig_decoder_sup(
sin_row: ArrayView1<'_, f64>,
cos_row: ArrayView1<'_, f64>,
) -> f64 {
let aa = sin_row.dot(&sin_row);
let bb = cos_row.dot(&cos_row);
let ab = sin_row.dot(&cos_row);
let trace = aa + bb;
let disc = ((aa - bb) * (aa - bb) + 4.0 * ab * ab).sqrt();
(0.5 * (trace + disc)).sqrt()
}
pub(crate) fn periodic_reconstruction_jet_sups(
decoder: ArrayView2<'_, f64>,
) -> ReconstructionJetSups {
let mut value = 0.0;
let mut jacobian = 0.0;
let mut hessian = 0.0;
let mut third = 0.0;
if decoder.nrows() > 0 {
value += decoder.row(0).dot(&decoder.row(0)).sqrt();
}
let harmonics = decoder.nrows().saturating_sub(1) / 2;
for h in 1..=harmonics {
let sin_idx = 2 * h - 1;
let cos_idx = 2 * h;
let amp = pair_trig_decoder_sup(decoder.row(sin_idx), decoder.row(cos_idx));
let omega = std::f64::consts::TAU * h as f64;
value += amp;
jacobian += omega * amp;
hessian += omega.powi(2) * amp;
third += omega.powi(3) * amp;
}
for row in (1 + 2 * harmonics)..decoder.nrows() {
let amp = decoder.row(row).dot(&decoder.row(row)).sqrt();
value += amp;
let omega = std::f64::consts::TAU * harmonics.max(1) as f64;
jacobian += omega * amp;
hessian += omega.powi(2) * amp;
third += omega.powi(3) * amp;
}
ReconstructionJetSups {
value,
jacobian,
hessian,
third,
}
}
pub(crate) fn reconstruction_jet_sups(
atom: &SaeManifoldAtom,
sups: JetSups,
) -> ReconstructionJetSups {
let full_decoder = atom
.reduced_column_map
.is_some()
.then(|| atom.full_width_decoder());
let decoder = full_decoder
.as_ref()
.map_or_else(|| atom.decoder_coefficients().view(), |b| b.view());
if matches!(
atom.basis_kind(),
crate::manifold::SaeAtomBasisKind::Periodic
) {
periodic_reconstruction_jet_sups(decoder)
} else {
let decoder_norm_sum = decoder_row_norm_sum(decoder);
ReconstructionJetSups {
value: decoder_norm_sum * sups.value,
jacobian: decoder_norm_sum * sups.jacobian,
hessian: decoder_norm_sum * sups.hessian,
third: decoder_norm_sum * sups.third,
}
}
}
pub(crate) fn hessian_lipschitz_constant(
recon_sups: ReconstructionJetSups,
amplitude: f64,
target_norm: f64,
prior_lipschitz: f64,
) -> f64 {
let z = amplitude.abs();
let m_jac = z * recon_sups.jacobian;
let m_hess = z * recon_sups.hessian;
let m_third = z * recon_sups.third;
let recon_value = z * recon_sups.value;
let r_norm = target_norm + recon_value;
3.0 * m_jac * m_hess + r_norm * m_third + prior_lipschitz
}
#[derive(Debug, Clone)]
pub struct CertifiedChart {
pub region: ChartRegion,
pub lipschitz: f64,
pub beta_center: f64,
pub certified_radius: f64,
pub amortized_jacobian: Option<Array2<f64>>,
pub recon_center: Array1<f64>,
pub amortized_base: Option<Array1<f64>>,
pub jacobian_sup: f64,
}
#[derive(Debug, Clone)]
pub struct AtomEncodeAtlas {
pub atom_index: usize,
pub latent_dim: usize,
pub decoder_norm_sum: f64,
pub charts: Vec<CertifiedChart>,
pub(crate) periodic_fiber: Option<PeriodicCurveExtrema>,
}
#[derive(Debug, Clone)]
pub struct EncodeResult {
pub coords: Array2<f64>,
pub certified: Vec<bool>,
pub encode_uncertified_count: usize,
}
#[derive(Debug, Clone)]
pub struct JointEncodeResult {
pub coords: Vec<Array2<f64>>,
pub converged: Vec<bool>,
pub unconverged_count: usize,
}
impl JointEncodeResult {
pub(crate) fn new(coords: Vec<Array2<f64>>, converged: Vec<bool>) -> Self {
let unconverged_count = converged.iter().filter(|ok| !**ok).count();
Self {
coords,
converged,
unconverged_count,
}
}
}
#[derive(Debug, Clone, Default)]
pub struct FallbackTelemetry {
pub n_rows: usize,
pub n_atoms: usize,
pub amortized_certified: usize,
pub newton_rescued: usize,
pub multistart_fallback: usize,
}
impl FallbackTelemetry {
#[must_use]
pub fn total(&self) -> usize {
self.n_rows * self.n_atoms
}
pub fn accumulate(&mut self, other: &FallbackTelemetry) {
self.n_rows = other.n_rows;
self.n_atoms += other.n_atoms;
self.amortized_certified += other.amortized_certified;
self.newton_rescued += other.newton_rescued;
self.multistart_fallback += other.multistart_fallback;
}
}
#[derive(Debug, Clone, Copy)]
pub struct RowCertificate {
pub beta: f64,
pub eta: f64,
pub lipschitz: f64,
pub h: f64,
}
impl RowCertificate {
pub fn certified(&self) -> bool {
self.h.is_finite() && self.h <= KANTOROVICH_THRESHOLD
}
}
#[derive(Debug, Clone)]
struct CertifiedEncodeProbe {
coord: Array1<f64>,
final_cert: RowCertificate,
}
pub(crate) const SAE_CYLINDER_LINE_DEGREE: usize = 2;
pub(crate) fn family_jet_sups(
atom: &SaeManifoldAtom,
chart: &ChartRegion,
) -> Result<JetSups, String> {
use crate::manifold::SaeAtomBasisKind::*;
let m = atom.full_basis_size();
let d = atom.latent_dim();
let sups = match atom.basis_kind() {
Periodic => {
let ev = PeriodicHarmonicEvaluator::new(m)?;
JetSups::from_family(&ev, chart)
}
Torus => {
let axis_m = integer_root(m, d.max(1));
let num_harmonics = axis_m.saturating_sub(1) / 2;
let ev = TorusHarmonicEvaluator::new(d, num_harmonics.max(1))?;
JetSups::from_family(&ev, chart)
}
Sphere => {
let ev = AmbientSphereHarmonicEvaluator::new(
crate::manifold::SAE_AMBIENT_SPHERE_DEFAULT_DEGREE,
)?;
JetSups::from_family(&ev, chart)
}
ProjectivePlane | KleinBottle => {
return Err(
"EncodeAtlas: quotient spectral jet sup requires a plan-native bound; route this atom through exact analytic encode"
.to_string(),
);
}
Cylinder => {
let ml = SAE_CYLINDER_LINE_DEGREE + 1;
if d != 2 || ml == 0 || m % ml != 0 {
return Err(format!(
"EncodeAtlas: Cylinder atom requires latent_dim == 2 and width divisible by {ml}; got dim={d}, m={m}"
));
}
let axis_mc = m / ml;
let h = axis_mc.saturating_sub(1) / 2;
let ev = CylinderHarmonicEvaluator::new(h.max(1), SAE_CYLINDER_LINE_DEGREE)?;
JetSups::from_family(&ev, chart)
}
Mobius => {
return Err(
"EncodeAtlas: Mobius jet bounds require its persisted harmonic and width \
degrees; use the atom's exact analytic jets"
.to_string(),
);
}
Linear | EuclideanPatch | Poincare => {
let degree = euclidean_patch_degree(d, m);
let ev = EuclideanPatchEvaluator::new(d, degree)?;
JetSups::from_family(&ev, chart)
}
Duchon => {
let centers = duchon_centers_from_atom(atom);
let conservative_m = m.max(1);
let ev = DuchonCoordinateEvaluator::new(centers, conservative_m)?;
JetSups::from_family(&ev, chart)
}
Precomputed(name) => {
return Err(format!(
"EncodeAtlas: precomputed basis '{name}' has no closed-form jet sup; route to exact encode"
));
}
FiniteSet => {
return Err(
"EncodeAtlas: finite-set (indicator) basis has no closed-form jet sup; \
route to exact encode"
.to_string(),
);
}
};
Ok(sups)
}
pub(crate) fn euclidean_patch_degree(latent_dim: usize, m: usize) -> usize {
let mut degree = 0usize;
while patch_column_count(latent_dim, degree) < m && degree < m {
degree += 1;
}
degree
}
pub(crate) fn integer_root(n: usize, k: usize) -> usize {
if k == 0 {
return 1;
}
if k == 1 {
return n;
}
let mut a = 1usize;
loop {
let next = a + 1;
let mut pow: u128 = 1;
let mut overflow = false;
for _ in 0..k {
pow = pow.saturating_mul(next as u128);
if pow > n as u128 {
overflow = true;
break;
}
}
if overflow {
return a;
}
a = next;
}
}
pub(crate) fn patch_column_count(latent_dim: usize, degree: usize) -> usize {
let mut num = 1u128;
let mut den = 1u128;
for i in 1..=degree {
num *= (latent_dim + i) as u128;
den *= i as u128;
}
(num / den) as usize
}
pub(crate) fn duchon_centers_from_atom(atom: &SaeManifoldAtom) -> Array2<f64> {
Array2::<f64>::zeros((1, atom.latent_dim().max(1)))
}
#[derive(Debug, Clone, Copy)]
pub(crate) struct JetSups {
pub(crate) value: f64,
pub(crate) jacobian: f64,
pub(crate) hessian: f64,
pub(crate) third: f64,
}
impl JetSups {
pub(crate) fn from_family<B: BasisHessianLipschitz>(family: &B, chart: &ChartRegion) -> Self {
Self {
value: family.value_sup(chart),
jacobian: family.jacobian_sup(chart),
hessian: family.hessian_sup(chart),
third: family.third_sup(chart),
}
}
}
pub fn encode_grad_hess(
atom: &SaeManifoldAtom,
evaluator: &dyn SaeBasisEvaluator,
t: ArrayView1<'_, f64>,
x: ArrayView1<'_, f64>,
amplitude: f64,
) -> Result<Option<(Array1<f64>, Array2<f64>)>, String> {
encode_grad_hess_core(
atom,
evaluator,
t,
x,
amplitude,
&EncodeObjective::euclidean(),
)
}
#[derive(Clone, Copy)]
pub struct EncodeObjective<'a> {
pub metric_factor: Option<ArrayView2<'a, f64>>,
pub prior_alpha: Option<&'a [f64]>,
pub metric_norm_bound: f64,
}
impl<'a> EncodeObjective<'a> {
pub fn euclidean() -> Self {
Self {
metric_factor: None,
prior_alpha: None,
metric_norm_bound: 1.0,
}
}
fn prior_lipschitz(&self, atom: &SaeManifoldAtom) -> f64 {
let Some(alpha) = self.prior_alpha else {
return 0.0;
};
let mut l = 0.0;
for axis in 0..atom.latent_dim().min(alpha.len()) {
if let Some(period) = latent_axis_period(atom, axis) {
let kappa = std::f64::consts::TAU / period;
l += alpha[axis].abs() * kappa;
}
}
l
}
fn effective_lipschitz(&self, atom: &SaeManifoldAtom, data_lipschitz: f64) -> f64 {
self.metric_norm_bound * data_lipschitz + self.prior_lipschitz(atom)
}
}
fn apply_row_metric(u: ArrayView2<'_, f64>, v: ArrayView1<'_, f64>) -> Array1<f64> {
let utv = u.t().dot(&v); u.dot(&utv) }
const JOINT_ENCODE_MAX_ITER: usize = 64;
const JOINT_ENCODE_GRAD_TOL: f64 = 1.0e-10;
const JOINT_ENCODE_STEP_TOL: f64 = 1.0e-12;
const JOINT_ENCODE_DAMPING_FLOOR: f64 = 1.0e-10;
const JOINT_ENCODE_DAMPING_GROWTH: f64 = 10.0;
const JOINT_ENCODE_DAMPING_DECAY: f64 = 3.0;
const JOINT_ENCODE_DAMPING_MAX_ATTEMPTS: usize = 12;
const JOINT_ENCODE_ARMIJO_MAX_STEPS: usize = 24;
fn joint_data_value_grad_hess(
jac: ArrayView2<'_, f64>,
residual: ArrayView1<'_, f64>,
metric_factor: Option<ArrayView2<'_, f64>>,
) -> (f64, Array1<f64>, Array2<f64>) {
let q = jac.nrows();
let p = jac.ncols();
let weighted_residual = match metric_factor.as_ref() {
Some(u) => apply_row_metric(u.view(), residual),
None => residual.to_owned(),
};
let value = 0.5 * residual.dot(&weighted_residual);
let grad = jac.dot(&weighted_residual);
let weighted_jac = match metric_factor.as_ref() {
Some(u) => {
let mut out = Array2::<f64>::zeros((q, p));
for axis in 0..q {
out.row_mut(axis)
.assign(&apply_row_metric(u.view(), jac.row(axis)));
}
out
}
None => jac.to_owned(),
};
let hess = jac.dot(&weighted_jac.t());
(value, grad, hess)
}
fn joint_encode_value_grad_hess(
atoms: &[SaeManifoldAtom],
coords: &[Array1<f64>],
x: ArrayView1<'_, f64>,
amplitudes: ArrayView1<'_, f64>,
metric_factor: Option<ArrayView2<'_, f64>>,
) -> Result<(f64, Array1<f64>, Array2<f64>), String> {
let k_atoms = atoms.len();
if coords.len() != k_atoms || amplitudes.len() != k_atoms {
return Err(format!(
"joint encode: {} atoms require {} coordinate blocks and amplitudes; got {} and {}",
k_atoms,
k_atoms,
coords.len(),
amplitudes.len()
));
}
let p = x.len();
if let Some(u) = metric_factor.as_ref() {
if u.nrows() != p {
return Err(format!(
"joint encode: metric factor has {} rows but target has {p} outputs",
u.nrows()
));
}
}
let mut offsets = Vec::with_capacity(k_atoms + 1);
offsets.push(0usize);
for (atom_idx, atom) in atoms.iter().enumerate() {
if atom.output_dim() != p {
return Err(format!(
"joint encode: atom {atom_idx} output_dim {} != target width {p}",
atom.output_dim()
));
}
if coords[atom_idx].len() != atom.latent_dim() {
return Err(format!(
"joint encode: atom {atom_idx} coordinate length {} != latent_dim {}",
coords[atom_idx].len(),
atom.latent_dim()
));
}
offsets.push(offsets[atom_idx] + atom.latent_dim());
}
let q = *offsets.last().unwrap_or(&0);
let mut recon = Array1::<f64>::zeros(p);
let mut jac = Array2::<f64>::zeros((q, p));
for (atom_idx, atom) in atoms.iter().enumerate() {
let z = amplitudes[atom_idx];
if !z.is_finite() {
return Err(format!("joint encode: amplitude[{atom_idx}] is not finite"));
}
let Some(evaluator) = atom.basis_evaluator.as_ref() else {
return Err(format!(
"joint encode: atom {atom_idx} has no basis evaluator for its live coordinate block"
));
};
let d = atom.latent_dim();
let m = atom.basis_size();
let coord = coords[atom_idx]
.view()
.to_shape((1, d))
.map_err(|e| format!("joint encode: atom {atom_idx} coordinate reshape: {e}"))?
.to_owned();
let (phi, dphi) = evaluator.evaluate(coord.view())?;
if phi.dim() != (1, m) || dphi.dim() != (1, m, d) {
return Err(format!(
"joint encode: atom {atom_idx} evaluator returned phi {:?}, jet {:?}; expected (1,{m}) and (1,{m},{d})",
phi.dim(),
dphi.dim()
));
}
let start = offsets[atom_idx];
for basis_col in 0..m {
let phi_v = phi[[0, basis_col]];
for out in 0..p {
let b = atom.decoder_coefficients()[[basis_col, out]];
recon[out] += z * phi_v * b;
for axis in 0..d {
jac[[start + axis, out]] += z * dphi[[0, basis_col, axis]] * b;
}
}
}
}
let residual = &recon - &x;
let (mut value, mut grad, mut hess) =
joint_data_value_grad_hess(jac.view(), residual.view(), metric_factor.clone());
for (atom_idx, atom) in atoms.iter().enumerate() {
let Some(alpha) = atom.ard_precisions.as_deref() else {
continue;
};
let start = offsets[atom_idx];
for axis in 0..atom.latent_dim().min(alpha.len()) {
if alpha[axis] == 0.0 {
continue;
}
let prior = crate::manifold::ArdAxisPrior::eval(
alpha[axis],
coords[atom_idx][axis],
latent_axis_period(atom, axis),
);
value += prior.value;
grad[start + axis] += prior.grad;
hess[[start + axis, start + axis]] += prior.psd_majorizer_hess();
}
}
Ok((value, grad, hess))
}
fn joint_encode_damped_step(
hess: ArrayView2<'_, f64>,
grad: ArrayView1<'_, f64>,
damping: f64,
) -> Result<Option<Array1<f64>>, String> {
let q = grad.len();
if q == 0 {
return Ok(Some(Array1::zeros(0)));
}
let mut system = Array2::<f64>::zeros((q, q));
for i in 0..q {
for j in 0..q {
system[[i, j]] = 0.5 * (hess[[i, j]] + hess[[j, i]]);
}
system[[i, i]] += damping;
}
let (evals, evecs) = system
.eigh(Side::Lower)
.map_err(|e| format!("joint encode: damped eigensolve failed: {e:?}"))?;
if evals.iter().any(|&v| !(v.is_finite() && v > 0.0)) {
return Ok(None);
}
let mut step = Array1::<f64>::zeros(q);
for (col, &lambda) in evals.iter().enumerate() {
let v = evecs.column(col);
let coefficient = -v.dot(&grad) / lambda;
for row in 0..q {
step[row] += coefficient * v[row];
}
}
if step.iter().any(|v| !v.is_finite()) {
Ok(None)
} else {
Ok(Some(step))
}
}
fn joint_encode_add_step(
atoms: &[SaeManifoldAtom],
coords: &[Array1<f64>],
step: ArrayView1<'_, f64>,
scale: f64,
) -> Vec<Array1<f64>> {
let mut out = Vec::with_capacity(atoms.len());
let mut offset = 0usize;
for (atom_idx, atom) in atoms.iter().enumerate() {
let mut next = coords[atom_idx].clone();
for axis in 0..atom.latent_dim() {
next[axis] += scale * step[offset + axis];
if let Some(period) = latent_axis_period(atom, axis) {
next[axis] = next[axis].rem_euclid(period);
}
}
offset += atom.latent_dim();
out.push(next);
}
out
}
pub(crate) fn joint_encode_refine_row(
atoms: &[SaeManifoldAtom],
initial_coords: &[Array1<f64>],
x: ArrayView1<'_, f64>,
amplitudes: ArrayView1<'_, f64>,
metric_factor: Option<ArrayView2<'_, f64>>,
) -> Result<(Vec<Array1<f64>>, bool), String> {
let mut coords = initial_coords.to_vec();
let q: usize = atoms.iter().map(SaeManifoldAtom::latent_dim).sum();
if q == 0 {
return Ok((coords, true));
}
let target_scale = 1.0 + x.dot(&x).sqrt();
let mut damping = JOINT_ENCODE_DAMPING_FLOOR;
for _ in 0..JOINT_ENCODE_MAX_ITER {
let (value, grad, hess) =
joint_encode_value_grad_hess(atoms, &coords, x, amplitudes, metric_factor.clone())?;
let grad_norm = grad.dot(&grad).sqrt();
if grad_norm <= JOINT_ENCODE_GRAD_TOL * target_scale {
return Ok((coords, true));
}
let diag_scale = (0..q)
.map(|i| hess[[i, i]].abs())
.fold(0.0_f64, f64::max)
.max(1.0);
damping = damping.max(f64::EPSILON * diag_scale);
let mut accepted = None;
for _ in 0..JOINT_ENCODE_DAMPING_MAX_ATTEMPTS {
let Some(step) = joint_encode_damped_step(hess.view(), grad.view(), damping)? else {
damping *= JOINT_ENCODE_DAMPING_GROWTH;
continue;
};
let directional = grad.dot(&step);
if !(directional.is_finite() && directional < 0.0) {
damping *= JOINT_ENCODE_DAMPING_GROWTH;
continue;
}
let base_value = value;
let step_unit_norm = step.dot(&step).sqrt();
let line_search = backtracking_line_search::<Vec<Array1<f64>>, String>(
BacktrackConfig {
initial_step: 1.0,
contraction: BACKTRACK_CONTRACTION,
max_steps: JOINT_ENCODE_ARMIJO_MAX_STEPS,
},
|line_scale| {
let candidate = joint_encode_add_step(atoms, &coords, step.view(), line_scale);
let (candidate_value, _, _) = joint_encode_value_grad_hess(
atoms,
&candidate,
x,
amplitudes,
metric_factor.clone(),
)?;
Ok(Some((candidate_value, candidate)))
},
|line_scale, candidate_value| {
candidate_value <= base_value + ARMIJO_C1 * line_scale * directional
},
)?;
if let Some(AcceptedStep {
step: line_scale,
payload: candidate,
..
}) = line_search
{
accepted = Some((candidate, line_scale * step_unit_norm));
}
if accepted.is_some() {
damping = (damping / JOINT_ENCODE_DAMPING_DECAY).max(f64::EPSILON * diag_scale);
break;
}
damping *= JOINT_ENCODE_DAMPING_GROWTH;
}
let Some((next, step_norm)) = accepted else {
return Ok((coords, false));
};
coords = next;
if step_norm <= JOINT_ENCODE_STEP_TOL * target_scale {
let (_, final_grad, _) =
joint_encode_value_grad_hess(atoms, &coords, x, amplitudes, metric_factor.clone())?;
let converged =
final_grad.dot(&final_grad).sqrt() <= JOINT_ENCODE_GRAD_TOL * target_scale;
return Ok((coords, converged));
}
}
let (_, final_grad, _) =
joint_encode_value_grad_hess(atoms, &coords, x, amplitudes, metric_factor)?;
let converged = final_grad.dot(&final_grad).sqrt() <= JOINT_ENCODE_GRAD_TOL * target_scale;
Ok((coords, converged))
}
pub(crate) fn encode_grad_hess_core(
atom: &SaeManifoldAtom,
evaluator: &dyn SaeBasisEvaluator,
t: ArrayView1<'_, f64>,
x: ArrayView1<'_, f64>,
amplitude: f64,
objective: &EncodeObjective<'_>,
) -> Result<Option<(Array1<f64>, Array2<f64>)>, String> {
let d = atom.latent_dim();
let p = atom.output_dim();
let m = atom.basis_size();
let coords = t.to_shape((1, d)).map_err(|e| e.to_string())?.to_owned();
let (phi, jet) = evaluator.evaluate(coords.view())?;
if phi.dim() != (1, m) {
return Err(format!(
"encode_grad_hess: evaluator returned phi {:?}, expected (1, {m})",
phi.dim()
));
}
let decoder = atom.decoder_coefficients();
let mut recon = Array1::<f64>::zeros(p);
for basis_col in 0..m {
let phi_v = phi[[0, basis_col]];
if phi_v == 0.0 {
continue;
}
for out in 0..p {
recon[out] += amplitude * phi_v * decoder[[basis_col, out]];
}
}
let residual = &recon - &x;
let mut jm = Array2::<f64>::zeros((d, p));
for axis in 0..d {
for basis_col in 0..m {
let dphi = jet[[0, basis_col, axis]];
if dphi == 0.0 {
continue;
}
for out in 0..p {
jm[[axis, out]] += amplitude * dphi * decoder[[basis_col, out]];
}
}
}
let second = match evaluator.second_jet_dyn(coords.view()) {
Some(result) => result?,
None => return Ok(None),
};
let mr_owned;
let wr: &Array1<f64> = match objective.metric_factor {
Some(u) => {
mr_owned = apply_row_metric(u, residual.view());
&mr_owned
}
None => &residual,
};
let mjm: Option<Vec<Array1<f64>>> = objective
.metric_factor
.map(|u| (0..d).map(|a| apply_row_metric(u, jm.row(a))).collect());
let mut rd = vec![0.0_f64; m];
for (basis_col, rd_col) in rd.iter_mut().enumerate() {
let mut dot = 0.0;
for out in 0..p {
dot += wr[out] * decoder[[basis_col, out]];
}
*rd_col = dot;
}
let mut g = Array1::<f64>::zeros(d);
let mut h = Array2::<f64>::zeros((d, d));
for a in 0..d {
let ja = jm.row(a);
g[a] = ja.dot(wr);
for b in a..d {
let mut hab = match &mjm {
Some(v) => ja.dot(&v[b]),
None => ja.dot(&jm.row(b)),
};
let mut curv = 0.0;
for basis_col in 0..m {
let d2phi = second[[0, basis_col, a, b]];
if d2phi == 0.0 {
continue;
}
curv += amplitude * d2phi * rd[basis_col];
}
hab += curv;
h[[a, b]] = hab;
h[[b, a]] = hab;
}
}
if let Some(alpha) = objective.prior_alpha {
for axis in 0..d.min(alpha.len()) {
let alpha_axis = alpha[axis];
if alpha_axis == 0.0 {
continue;
}
let pr = crate::manifold::ArdAxisPrior::eval(
alpha_axis,
t[axis],
latent_axis_period(atom, axis),
);
g[axis] += pr.grad;
h[[axis, axis]] += pr.hess;
}
}
Ok(Some((g, h)))
}
pub(crate) fn beta_eta_newton(
h: ArrayView2<'_, f64>,
g: ArrayView1<'_, f64>,
) -> Result<Option<(f64, f64, Array1<f64>)>, String> {
let d = h.nrows();
if d == 1 {
let h00 = h[[0, 0]];
if !(h00.is_finite() && h00 > 0.0) {
return Ok(None);
}
let delta0 = -g[0] / h00;
let mut delta = Array1::<f64>::zeros(1);
delta[0] = delta0;
return Ok(Some((1.0 / h00, delta0.abs(), delta)));
}
if d == 2 {
let a = h[[0, 0]];
let b = h[[1, 0]];
let c = h[[1, 1]];
let tr = a + c;
let det = a * c - b * b;
let disc = ((a - c) * (a - c) + 4.0 * b * b).max(0.0).sqrt();
let lambda_min = 0.5 * (tr - disc);
let lambda_max = 0.5 * (tr + disc);
let max_abs = lambda_min.abs().max(lambda_max.abs());
if !(lambda_min.is_finite() && lambda_max.is_finite() && max_abs > 0.0) {
return Ok(None);
}
let floor = gam_solve::arrow_schur::SPECTRAL_DEFLATION_REL_FLOOR * max_abs;
if lambda_min > floor {
let inv_det = 1.0 / det;
let g0 = g[0];
let g1 = g[1];
let d0 = -(c * g0 - b * g1) * inv_det;
let d1 = -(a * g1 - b * g0) * inv_det;
if !(d0.is_finite() && d1.is_finite()) {
return Ok(None);
}
let mut delta = Array1::<f64>::zeros(2);
delta[0] = d0;
delta[1] = d1;
let eta = (d0 * d0 + d1 * d1).sqrt();
return Ok(Some((1.0 / lambda_min, eta, delta)));
}
return Ok(None);
}
beta_eta_newton_positive_definite(h, g)
}
fn beta_eta_newton_positive_definite(
h: ArrayView2<'_, f64>,
g: ArrayView1<'_, f64>,
) -> Result<Option<(f64, f64, Array1<f64>)>, String> {
let d = h.nrows();
let mut sym = Array2::<f64>::zeros((d, d));
for i in 0..d {
for j in 0..d {
let v = 0.5 * (h[[i, j]] + h[[j, i]]);
if !v.is_finite() {
return Ok(None);
}
sym[[i, j]] = v;
}
}
let (vals, vecs) = sym
.eigh(Side::Lower)
.map_err(|e| format!("beta_eta_newton: eigh failed: {e:?}"))?;
let max_abs = vals.iter().fold(
0.0_f64,
|acc, &v| if v.is_finite() { acc.max(v.abs()) } else { acc },
);
if !(max_abs.is_finite() && max_abs > 0.0) {
return Ok(None);
}
let floor = gam_solve::arrow_schur::SPECTRAL_DEFLATION_REL_FLOOR * max_abs;
if vals
.iter()
.any(|&lambda| !lambda.is_finite() || lambda <= floor)
{
return Ok(None);
}
let lambda_min = vals.iter().cloned().fold(f64::INFINITY, f64::min);
if !(lambda_min.is_finite() && lambda_min > 0.0) {
return Ok(None);
}
let beta = 1.0 / lambda_min;
let mut delta = Array1::<f64>::zeros(d);
for (col, &lam) in vals.iter().enumerate() {
let vi = vecs.column(col);
let coeff = vi.dot(&g) / lam;
for row in 0..d {
delta[row] -= coeff * vi[row];
}
}
if delta.iter().any(|v| !v.is_finite()) {
return Ok(None);
}
let eta = delta.dot(&delta).sqrt();
Ok(Some((beta, eta, delta)))
}
pub fn row_certificate(
atom: &SaeManifoldAtom,
evaluator: &dyn SaeBasisEvaluator,
t0: ArrayView1<'_, f64>,
x: ArrayView1<'_, f64>,
amplitude: f64,
lipschitz: f64,
) -> Result<(RowCertificate, Array1<f64>), String> {
row_certificate_core(
atom,
evaluator,
t0,
x,
amplitude,
lipschitz,
&EncodeObjective::euclidean(),
)
}
pub(crate) fn row_certificate_core(
atom: &SaeManifoldAtom,
evaluator: &dyn SaeBasisEvaluator,
t0: ArrayView1<'_, f64>,
x: ArrayView1<'_, f64>,
amplitude: f64,
lipschitz: f64,
objective: &EncodeObjective<'_>,
) -> Result<(RowCertificate, Array1<f64>), String> {
let uncertified = || {
(
RowCertificate {
beta: f64::INFINITY,
eta: f64::INFINITY,
lipschitz,
h: f64::INFINITY,
},
Array1::<f64>::zeros(atom.latent_dim()),
)
};
let Some((g, h)) = encode_grad_hess_core(atom, evaluator, t0, x, amplitude, objective)? else {
return Ok(uncertified());
};
match beta_eta_newton(h.view(), g.view())? {
Some((beta, eta, delta)) => {
let cert = RowCertificate {
beta,
eta,
lipschitz,
h: beta * eta * lipschitz,
};
Ok((cert, delta))
}
None => Ok(uncertified()),
}
}
fn uncertified_certificate(lipschitz: f64) -> RowCertificate {
RowCertificate {
beta: f64::INFINITY,
eta: f64::INFINITY,
lipschitz,
h: f64::INFINITY,
}
}
fn refine_certified_start(
atom: &SaeManifoldAtom,
evaluator: &dyn SaeBasisEvaluator,
mut t: Array1<f64>,
x: ArrayView1<'_, f64>,
amplitude: f64,
lipschitz: f64,
newton_steps: usize,
initial_cert: RowCertificate,
mut delta: Array1<f64>,
chart_center: ArrayView1<'_, f64>,
chart_radius: f64,
objective: &EncodeObjective<'_>,
) -> Result<Option<CertifiedEncodeProbe>, String> {
assert!(initial_cert.certified());
let mut final_cert = initial_cert;
for _ in 0..newton_steps {
if delta.dot(&delta).sqrt() <= NEWTON_REFINE_CONVERGED_EPS * (1.0 + t.dot(&t).sqrt()) {
break;
}
let next = &t + δ
if latent_coordinate_distance(atom, next.view(), chart_center) > chart_radius {
return Ok(None);
}
t = next;
let (cert, next_delta) = row_certificate_core(
atom,
evaluator,
t.view(),
x,
amplitude,
lipschitz,
objective,
)?;
if !cert.certified() {
return Ok(None);
}
final_cert = cert;
delta = next_delta;
}
Ok(Some(CertifiedEncodeProbe {
coord: t,
final_cert,
}))
}
const WARMUP_MIN_MULTIPLICATIVE_DECREASE: f64 = 1.0 / 64.0;
const WARMUP_QUADRATIC_KAPPA: f64 = 0.5;
fn warmup_progress_sufficient(h_new: f64, h_prev: f64) -> bool {
if !(h_new.is_finite() && h_prev.is_finite()) {
return false;
}
if h_new <= (1.0 - WARMUP_MIN_MULTIPLICATIVE_DECREASE) * h_prev {
return true;
}
h_prev < 1.0 && h_new <= WARMUP_QUADRATIC_KAPPA * h_prev * h_prev
}
fn warmup_should_reject(next_certified: bool, h_new: f64, h_prev: f64) -> bool {
!next_certified && !warmup_progress_sufficient(h_new, h_prev)
}
fn certify_with_basin_warmup(
atom: &SaeManifoldAtom,
evaluator: &dyn SaeBasisEvaluator,
t_start: Array1<f64>,
x: ArrayView1<'_, f64>,
amplitude: f64,
lipschitz: f64,
newton_steps: usize,
chart_center: ArrayView1<'_, f64>,
chart_radius: f64,
objective: &EncodeObjective<'_>,
) -> Result<Option<CertifiedEncodeProbe>, String> {
let in_chart = |t: &Array1<f64>| -> bool {
latent_coordinate_distance(atom, t.view(), chart_center) <= chart_radius
};
let mut t = t_start;
if !in_chart(&t) {
return Ok(None);
}
let (mut cert, mut delta) = row_certificate_core(
atom,
evaluator,
t.view(),
x,
amplitude,
lipschitz,
objective,
)?;
while !cert.certified() {
if !(cert.h.is_finite() && cert.beta.is_finite() && cert.eta.is_finite()) {
return Ok(None);
}
let prev_h = cert.h;
let next = &t + δ
if !in_chart(&next) {
return Ok(None);
}
t = next;
let (next_cert, next_delta) = row_certificate_core(
atom,
evaluator,
t.view(),
x,
amplitude,
lipschitz,
objective,
)?;
cert = next_cert;
delta = next_delta;
if warmup_should_reject(cert.certified(), cert.h, prev_h) {
return Ok(None);
}
}
refine_certified_start(
atom,
evaluator,
t,
x,
amplitude,
lipschitz,
newton_steps,
cert,
delta,
chart_center,
chart_radius,
objective,
)
}
fn kantorovich_root_radius(cert: RowCertificate) -> f64 {
if !cert.certified() || !(cert.eta.is_finite() && cert.eta >= 0.0) {
return f64::INFINITY;
}
if cert.eta == 0.0 {
return 0.0;
}
if !(cert.h.is_finite() && cert.h >= 0.0) {
return f64::INFINITY;
}
let h = cert.h.min(KANTOROVICH_THRESHOLD);
let discriminant = (1.0 - 2.0 * h).max(0.0).sqrt();
let radius = 2.0 * cert.eta / (1.0 + discriminant);
if radius.is_finite() {
radius
} else {
f64::INFINITY
}
}
fn distilled_probe_tolerance(
amortized: &CertifiedEncodeProbe,
cold: &CertifiedEncodeProbe,
amplitude: f64,
x: ArrayView1<'_, f64>,
) -> f64 {
let certified_radius =
kantorovich_root_radius(amortized.final_cert) + kantorovich_root_radius(cold.final_cert);
let coord_scale = amortized.coord.dot(&amortized.coord).sqrt()
+ cold.coord.dot(&cold.coord).sqrt()
+ x.dot(&x).sqrt()
+ amplitude.abs()
+ 1.0;
certified_radius + 1024.0 * f64::EPSILON * coord_scale
}
fn latent_coordinate_distance(
atom: &SaeManifoldAtom,
lhs: ArrayView1<'_, f64>,
rhs: ArrayView1<'_, f64>,
) -> f64 {
let mut acc = 0.0;
for axis in 0..lhs.len().min(rhs.len()) {
let mut diff = (lhs[axis] - rhs[axis]).abs();
if let Some(period) = latent_axis_period(atom, axis) {
let wrapped = diff.rem_euclid(period);
diff = wrapped.min(period - wrapped);
}
acc += diff * diff;
}
acc.sqrt()
}
fn latent_axis_period(atom: &SaeManifoldAtom, axis: usize) -> Option<f64> {
use crate::manifold::SaeAtomBasisKind::*;
match atom.basis_kind() {
Periodic | Torus => Some(1.0),
Cylinder if axis == 0 => Some(1.0),
Sphere if axis == 1 => Some(std::f64::consts::TAU),
_ => None,
}
}
#[derive(Debug, Clone, Copy)]
pub struct AtlasConfig {
pub grid_resolution: usize,
pub ridge: f64,
pub newton_steps: usize,
}
impl Default for AtlasConfig {
fn default() -> Self {
Self {
grid_resolution: 16,
ridge: 1.0e-9,
newton_steps: 2,
}
}
}
#[derive(Debug, Clone)]
pub struct EncodeAtlas {
pub atoms: Vec<AtomEncodeAtlas>,
pub config: AtlasConfig,
}
impl EncodeAtlas {
pub fn build(
atoms: &[SaeManifoldAtom],
amplitude_bound: &[f64],
target_norm_bound: f64,
config: AtlasConfig,
) -> Result<Self, String> {
if amplitude_bound.len() != atoms.len() {
return Err(format!(
"EncodeAtlas::build: amplitude_bound length {} != atom count {}",
amplitude_bound.len(),
atoms.len()
));
}
let mut atom_atlases = Vec::with_capacity(atoms.len());
for (k, atom) in atoms.iter().enumerate() {
let atlas =
Self::build_atom_atlas(k, atom, amplitude_bound[k], target_norm_bound, &config)?;
atom_atlases.push(atlas);
}
Ok(Self {
atoms: atom_atlases,
config,
})
}
pub(crate) fn build_atom_atlas(
atom_index: usize,
atom: &SaeManifoldAtom,
amplitude_bound: f64,
target_norm_bound: f64,
config: &AtlasConfig,
) -> Result<AtomEncodeAtlas, String> {
let centers = chart_center_grid(atom, config.grid_resolution);
let nominal_radius = chart_nominal_radius(atom, config.grid_resolution);
let radii = vec![nominal_radius; centers.nrows()];
Self::build_atom_atlas_from_centers(
atom_index,
atom,
centers.view(),
&radii,
amplitude_bound,
target_norm_bound,
config,
)
}
pub(crate) fn build_atom_atlas_from_centers(
atom_index: usize,
atom: &SaeManifoldAtom,
centers: ArrayView2<'_, f64>,
radii: &[f64],
amplitude_bound: f64,
target_norm_bound: f64,
config: &AtlasConfig,
) -> Result<AtomEncodeAtlas, String> {
let d = atom.latent_dim();
if centers.ncols() != d {
return Err(format!(
"build_atom_atlas_from_centers: centers have {} cols but atom latent_dim is {d}",
centers.ncols()
));
}
if radii.len() != centers.nrows() {
return Err(format!(
"build_atom_atlas_from_centers: {} radii != {} centers",
radii.len(),
centers.nrows()
));
}
let decoder_norm_sum = decoder_row_norm_sum(atom.full_width_decoder().view());
let mut charts = Vec::with_capacity(centers.nrows());
let duchon_uncertifiable =
matches!(atom.basis_kind(), crate::manifold::SaeAtomBasisKind::Duchon);
for c in 0..centers.nrows() {
let center = centers.row(c).to_owned();
let nominal_radius = radii[c];
let region = chart_region(atom, center.clone(), nominal_radius);
if duchon_uncertifiable {
charts.push(CertifiedChart {
region,
lipschitz: f64::INFINITY,
beta_center: f64::INFINITY,
certified_radius: 0.0,
amortized_jacobian: None,
recon_center: Array1::<f64>::zeros(atom.output_dim()),
amortized_base: None,
jacobian_sup: f64::INFINITY,
});
continue;
}
let sups = family_jet_sups(atom, ®ion)?;
let recon_sups = reconstruction_jet_sups(atom, sups);
let lipschitz =
hessian_lipschitz_constant(recon_sups, amplitude_bound, target_norm_bound, 0.0);
let beta_center = match center_beta(atom, ¢er, config.ridge) {
Some(b) => b,
None => {
charts.push(CertifiedChart {
region,
lipschitz,
beta_center: f64::INFINITY,
certified_radius: 0.0,
amortized_jacobian: None,
recon_center: Array1::<f64>::zeros(atom.output_dim()),
amortized_base: None,
jacobian_sup: recon_sups.jacobian,
});
continue;
}
};
let (amortized_jacobian, recon_center) =
match center_amortized_jacobian(atom, ¢er, config.ridge) {
Some((a1, m1)) => (Some(a1), m1),
None => (None, Array1::<f64>::zeros(atom.output_dim())),
};
let certified_radius = if lipschitz > 0.0 && beta_center.is_finite() {
(0.5 / (beta_center * lipschitz)).min(region.radius)
} else {
region.radius
};
let amortized_base = amortized_jacobian
.as_ref()
.map(|a1| ¢er - &a1.dot(&recon_center));
charts.push(CertifiedChart {
region,
lipschitz,
beta_center,
certified_radius,
amortized_jacobian,
recon_center,
amortized_base,
jacobian_sup: recon_sups.jacobian,
});
}
Ok(AtomEncodeAtlas {
atom_index,
latent_dim: d,
decoder_norm_sum,
charts,
periodic_fiber: build_periodic_fiber(atom),
})
}
fn refine_certified_encode_start(
&self,
atom: &SaeManifoldAtom,
evaluator: &dyn SaeBasisEvaluator,
chart: &CertifiedChart,
t: Array1<f64>,
x: ArrayView1<'_, f64>,
amplitude: f64,
objective: &EncodeObjective<'_>,
) -> Result<(Array1<f64>, RowCertificate), String> {
let lipschitz = objective.effective_lipschitz(atom, chart.lipschitz);
let Some(probe) = certify_with_basin_warmup(
atom,
evaluator,
t,
x,
amplitude,
lipschitz,
self.config.newton_steps,
chart.region.center.view(),
chart.region.radius,
objective,
)?
else {
return Ok((
Array1::<f64>::zeros(atom.latent_dim()),
uncertified_certificate(chart.lipschitz),
));
};
Ok((probe.coord, probe.final_cert))
}
pub fn certified_encode_row_with_objective(
&self,
atom: &SaeManifoldAtom,
atom_index: usize,
x: ArrayView1<'_, f64>,
amplitude: f64,
objective: &EncodeObjective<'_>,
) -> Result<(Array1<f64>, RowCertificate), String> {
let atom_atlas = self
.atoms
.get(atom_index)
.ok_or_else(|| format!("certified_encode_row: atom {atom_index} not in atlas"))?;
let d = atom.latent_dim();
if let Some(u) = objective.metric_factor {
if u.nrows() != atom.output_dim() {
return Err(format!(
"certified_encode_row_with_objective: metric factor has {} rows but atom output_dim is {}",
u.nrows(),
atom.output_dim()
));
}
}
let Some(evaluator) = atom.basis_evaluator.as_ref().cloned() else {
return Ok((
Array1::<f64>::zeros(d),
RowCertificate {
beta: f64::INFINITY,
eta: f64::INFINITY,
lipschitz: f64::INFINITY,
h: f64::INFINITY,
},
));
};
let candidates = certified_encode_candidates(atom_atlas, x, amplitude);
if candidates.is_empty() {
return Ok((
Array1::<f64>::zeros(d),
RowCertificate {
beta: f64::INFINITY,
eta: f64::INFINITY,
lipschitz: f64::INFINITY,
h: f64::INFINITY,
},
));
}
let mut best: Option<(Array1<f64>, RowCertificate, f64)> = None;
let mut nearest_fallback: Option<(Array1<f64>, RowCertificate)> = None;
let mut max_slack = 0.0_f64;
for (chart_idx, dist, slack) in candidates {
max_slack = max_slack.max(slack);
if let Some((_, _, best_err)) = best.as_ref() {
if dist - max_slack >= *best_err {
break;
}
if dist - slack >= *best_err {
continue;
}
}
let chart = &atom_atlas.charts[chart_idx];
let Some(t) = amortized_warm_start(chart, x, amplitude) else {
if nearest_fallback.is_none() {
nearest_fallback = Some((
Array1::<f64>::zeros(d),
uncertified_certificate(chart.lipschitz),
));
}
continue;
};
let (coord, cert) = self.refine_certified_encode_start(
atom,
evaluator.as_ref(),
chart,
t,
x,
amplitude,
objective,
)?;
if nearest_fallback.is_none() {
nearest_fallback = Some((coord.clone(), cert.clone()));
}
if cert.certified() {
let err = encode_reconstruction_error_core(
atom,
evaluator.as_ref(),
coord.view(),
x,
amplitude,
objective,
);
if best.as_ref().map(|(_, _, e)| err < *e).unwrap_or(true) {
best = Some((coord, cert, err));
}
if let Some((_, _, e)) = best.as_ref() {
if *e <= CERTIFIED_GLOBAL_MIN_RECON_FLOOR * (1.0 + x.dot(&x).sqrt()) {
break;
}
}
}
}
if let Some(fiber) = atom_atlas.periodic_fiber.as_ref() {
if amplitude.is_finite() && amplitude > 0.0 {
let projected = atom.decoder_coefficients().dot(&x);
let linear: Vec<f64> = projected
.iter()
.map(|value| value / amplitude)
.collect();
if let Ok(extremum) = fiber.minimize_squared_distance(&linear) {
if let Some(chart_idx) =
nearest_chart_to_periodic_coordinate(atom_atlas, extremum.coordinate)
{
let chart = &atom_atlas.charts[chart_idx];
let start = Array1::from_elem(1, extremum.coordinate);
let (coord, cert) = self.refine_certified_encode_start(
atom,
evaluator.as_ref(),
chart,
start,
x,
amplitude,
objective,
)?;
if cert.certified() {
let err = encode_reconstruction_error_core(
atom,
evaluator.as_ref(),
coord.view(),
x,
amplitude,
objective,
);
if best.as_ref().map(|(_, _, e)| err < *e).unwrap_or(true) {
best = Some((coord, cert, err));
}
}
}
}
}
}
match best {
Some((coord, cert, _)) => Ok((coord, cert)),
None => Ok(nearest_fallback.unwrap_or_else(|| {
(
Array1::<f64>::zeros(d),
RowCertificate {
beta: f64::INFINITY,
eta: f64::INFINITY,
lipschitz: f64::INFINITY,
h: f64::INFINITY,
},
)
})),
}
}
pub fn amortized_encode_row_with_objective(
&self,
atom: &SaeManifoldAtom,
atom_index: usize,
x: ArrayView1<'_, f64>,
amplitude: f64,
objective: &EncodeObjective<'_>,
) -> Result<(Array1<f64>, RowCertificate), String> {
let atom_atlas = self
.atoms
.get(atom_index)
.ok_or_else(|| format!("amortized_encode_row: atom {atom_index} not in atlas"))?;
let d = atom.latent_dim();
let uncertified = || {
(
Array1::<f64>::zeros(d),
RowCertificate {
beta: f64::INFINITY,
eta: f64::INFINITY,
lipschitz: f64::INFINITY,
h: f64::INFINITY,
},
)
};
let Some(evaluator) = atom.basis_evaluator.as_ref().cloned() else {
return Ok(uncertified());
};
let Some((chart_idx, _)) = nearest_chart(atom_atlas, x, amplitude) else {
return Ok(uncertified());
};
let chart = &atom_atlas.charts[chart_idx];
let Some(t_hat) = amortized_warm_start(chart, x, amplitude) else {
return Ok(uncertified());
};
let lipschitz = objective.effective_lipschitz(atom, chart.lipschitz);
let Some(amortized_probe) = certify_with_basin_warmup(
atom,
evaluator.as_ref(),
t_hat,
x,
amplitude,
lipschitz,
self.config.newton_steps,
chart.region.center.view(),
chart.region.radius,
objective,
)?
else {
return Ok((Array1::<f64>::zeros(d), uncertified_certificate(lipschitz)));
};
let cold_start = chart.region.center.clone();
let Some(cold_probe) = certify_with_basin_warmup(
atom,
evaluator.as_ref(),
cold_start,
x,
amplitude,
lipschitz,
self.config.newton_steps,
chart.region.center.view(),
chart.region.radius,
objective,
)?
else {
return Ok((amortized_probe.coord, uncertified_certificate(lipschitz)));
};
let gap =
latent_coordinate_distance(atom, amortized_probe.coord.view(), cold_probe.coord.view());
let tolerance = distilled_probe_tolerance(&amortized_probe, &cold_probe, amplitude, x);
if !(gap.is_finite() && gap <= tolerance) {
return Ok((amortized_probe.coord, uncertified_certificate(lipschitz)));
}
Ok((amortized_probe.coord, amortized_probe.final_cert))
}
}
pub(crate) fn center_beta(atom: &SaeManifoldAtom, center: &Array1<f64>, ridge: f64) -> Option<f64> {
let evaluator = atom.basis_evaluator.as_ref()?.clone();
let d = atom.latent_dim();
let p = atom.output_dim();
let m = atom.basis_size();
let coords = center.view().to_shape((1, d)).ok()?.to_owned();
let (_phi, jet) = evaluator.evaluate(coords.view()).ok()?;
let decoder = atom.decoder_coefficients();
let mut jm = Array2::<f64>::zeros((d, p));
for axis in 0..d {
for basis_col in 0..m {
let dphi = jet[[0, basis_col, axis]];
if dphi == 0.0 {
continue;
}
for out in 0..p {
jm[[axis, out]] += dphi * decoder[[basis_col, out]];
}
}
}
let mut h = Array2::<f64>::zeros((d, d));
for a in 0..d {
for b in 0..d {
h[[a, b]] = jm.row(a).dot(&jm.row(b));
}
h[[a, a]] += ridge;
}
let (vals, _vecs) = h.eigh(Side::Lower).ok()?;
let lambda_min = vals.iter().cloned().fold(f64::INFINITY, f64::min);
if lambda_min.is_finite() && lambda_min > 0.0 {
Some(1.0 / lambda_min)
} else {
None
}
}
pub(crate) fn amortized_warm_start(
chart: &CertifiedChart,
x: ArrayView1<'_, f64>,
amplitude: f64,
) -> Option<Array1<f64>> {
let a1 = chart.amortized_jacobian.as_ref()?;
if !(amplitude.is_finite() && amplitude.abs() > 0.0) {
return None;
}
let d = a1.nrows();
let mut t_hat = chart.region.center.clone();
for (out_idx, &m1_out) in chart.recon_center.iter().enumerate().take(a1.ncols()) {
let resid = x[out_idx] - amplitude * m1_out;
for axis in 0..d {
t_hat[axis] += a1[[axis, out_idx]] * resid / amplitude;
}
}
Some(t_hat)
}
pub(crate) fn center_amortized_jacobian(
atom: &SaeManifoldAtom,
center: &Array1<f64>,
ridge: f64,
) -> Option<(Array2<f64>, Array1<f64>)> {
let evaluator = atom.basis_evaluator.as_ref()?.clone();
let d = atom.latent_dim();
let p = atom.output_dim();
let m = atom.basis_size();
let coords = center.view().to_shape((1, d)).ok()?.to_owned();
let (phi, jet) = evaluator.evaluate(coords.view()).ok()?;
let decoder = atom.decoder_coefficients();
let mut recon = Array1::<f64>::zeros(p);
for basis_col in 0..m {
let phi_v = phi[[0, basis_col]];
if phi_v == 0.0 {
continue;
}
for out in 0..p {
recon[out] += phi_v * decoder[[basis_col, out]];
}
}
let mut jm = Array2::<f64>::zeros((d, p));
for axis in 0..d {
for basis_col in 0..m {
let dphi = jet[[0, basis_col, axis]];
if dphi == 0.0 {
continue;
}
for out in 0..p {
jm[[axis, out]] += dphi * decoder[[basis_col, out]];
}
}
}
let mut h = Array2::<f64>::zeros((d, d));
for a in 0..d {
for b in 0..d {
h[[a, b]] = jm.row(a).dot(&jm.row(b));
}
h[[a, a]] += ridge;
}
let (vals, vecs) = h.eigh(Side::Lower).ok()?;
let lambda_min = vals.iter().cloned().fold(f64::INFINITY, f64::min);
if !(lambda_min.is_finite() && lambda_min > 0.0) {
return None;
}
let mut a1 = Array2::<f64>::zeros((d, p));
for out in 0..p {
let jcol = jm.column(out);
for (i, &lam) in vals.iter().enumerate() {
if !(lam.is_finite() && lam > 0.0) {
return None;
}
let vi = vecs.column(i);
let coeff = vi.dot(&jcol) / lam;
for row in 0..d {
a1[[row, out]] += coeff * vi[row];
}
}
}
Some((a1, recon))
}
#[inline]
pub(crate) fn amplitude_scaled_center_dist(
recon: ArrayView1<'_, f64>,
x: ArrayView1<'_, f64>,
amplitude: f64,
) -> f64 {
let mut dist = 0.0;
for (r, xv) in recon.iter().zip(x.iter()) {
let diff = amplitude * r - xv;
dist += diff * diff;
}
dist
}
pub(crate) fn select_nearest_charts_topk(
n_charts: usize,
x: ArrayView1<'_, f64>,
amplitude: f64,
k: usize,
mut recon_into: impl FnMut(usize, &mut [f64]) -> bool,
) -> Vec<(usize, f64)> {
if n_charts == 0 || k == 0 {
return Vec::new();
}
let mut recon = vec![0.0_f64; x.len()];
let mut scored: Vec<(usize, f64)> = Vec::new();
for idx in 0..n_charts {
if !recon_into(idx, &mut recon) {
continue;
}
let dist = amplitude_scaled_center_dist(ArrayView1::from(recon.as_slice()), x, amplitude);
scored.push((idx, dist));
}
scored.sort_by(|a, b| {
a.1.partial_cmp(&b.1)
.unwrap_or(std::cmp::Ordering::Equal)
.then(a.0.cmp(&b.0))
});
scored.truncate(k);
scored
}
pub(crate) fn nearest_chart(
atom_atlas: &AtomEncodeAtlas,
x: ArrayView1<'_, f64>,
amplitude: f64,
) -> Option<(usize, f64)> {
select_nearest_charts_topk(atom_atlas.charts.len(), x, amplitude, 1, |idx, out| {
let chart = &atom_atlas.charts[idx];
if chart.certified_radius <= 0.0 {
return false;
}
for (o, r) in out.iter_mut().zip(chart.recon_center.iter()) {
*o = *r;
}
true
})
.into_iter()
.next()
}
fn build_periodic_fiber(atom: &SaeManifoldAtom) -> Option<PeriodicCurveExtrema> {
if atom.latent_dim() != 1 {
return None;
}
if !matches!(atom.basis_kind(), crate::manifold::SaeAtomBasisKind::Periodic) {
return None;
}
let decoder = atom.decoder_coefficients();
let gram = decoder.dot(&decoder.t());
PeriodicCurveExtrema::from_gram(gram.view()).ok()
}
fn nearest_chart_to_periodic_coordinate(
atom_atlas: &AtomEncodeAtlas,
coordinate: f64,
) -> Option<usize> {
let mut best: Option<(usize, f64)> = None;
for (index, chart) in atom_atlas.charts.iter().enumerate() {
if !(chart.certified_radius > 0.0) || chart.region.center.len() != 1 {
continue;
}
let raw = coordinate - chart.region.center[0];
let wrapped = (raw - raw.round()).abs();
if best.map(|(_, d)| wrapped < d).unwrap_or(true) {
best = Some((index, wrapped));
}
}
best.map(|(index, _)| index)
}
pub(crate) fn certified_encode_candidates(
atom_atlas: &AtomEncodeAtlas,
x: ArrayView1<'_, f64>,
amplitude: f64,
) -> Vec<(usize, f64, f64)> {
let scaled = if amplitude.is_finite() { amplitude.abs() } else { 0.0 };
select_nearest_charts_topk(
atom_atlas.charts.len(),
x,
amplitude,
atom_atlas.charts.len(),
|idx, out| {
let chart = &atom_atlas.charts[idx];
if chart.certified_radius <= 0.0 {
return false;
}
for (o, r) in out.iter_mut().zip(chart.recon_center.iter()) {
*o = *r;
}
true
},
)
.into_iter()
.map(|(idx, dist)| {
let chart = &atom_atlas.charts[idx];
let slack = scaled * chart.jacobian_sup * chart.region.radius;
(idx, dist, if slack.is_finite() { slack } else { f64::INFINITY })
})
.collect()
}
pub(crate) fn encode_reconstruction_error_core(
atom: &SaeManifoldAtom,
evaluator: &dyn SaeBasisEvaluator,
coord: ArrayView1<'_, f64>,
x: ArrayView1<'_, f64>,
amplitude: f64,
objective: &EncodeObjective<'_>,
) -> f64 {
let d = atom.latent_dim();
let p = atom.output_dim();
let m = atom.basis_size();
let coords = match coord.to_shape((1, d)) {
Ok(c) => c.to_owned(),
Err(_) => return f64::INFINITY,
};
let Ok((phi, _jet)) = evaluator.evaluate(coords.view()) else {
return f64::INFINITY;
};
let mut residual = Array1::<f64>::zeros(p);
for out in 0..p {
let mut recon = 0.0;
for basis_col in 0..m {
recon += phi[[0, basis_col]] * atom.decoder_coefficients()[[basis_col, out]];
}
residual[out] = x[out] - amplitude * recon;
}
let err2 = match objective.metric_factor {
Some(u) => {
let utr = u.t().dot(&residual);
utr.dot(&utr)
}
None => {
let mut e = 0.0;
for out in 0..p {
e += residual[out] * residual[out];
}
e
}
};
if err2.is_finite() {
err2.sqrt()
} else {
f64::INFINITY
}
}
pub(crate) const SHAPE_BAND_MAX_POINTS: usize = 512;
pub(crate) fn chart_center_grid(atom: &SaeManifoldAtom, resolution: usize) -> Array2<f64> {
use crate::manifold::SaeAtomBasisKind::*;
let d = atom.latent_dim();
match atom.basis_kind() {
Periodic | Torus | KleinBottle => regular_product_grid(d, resolution, 0.0, 1.0, false),
Cylinder if d == 2 => cylinder_chart_center_grid(resolution),
Cylinder => regular_product_grid(d, resolution, -0.5, 0.5, true),
Mobius if d == 2 => mobius_chart_center_grid(resolution),
Mobius => regular_product_grid(d, resolution, -1.0, 1.0, true),
Sphere | ProjectivePlane if d == 2 => sphere_latlon_grid(resolution),
Linear | Sphere | ProjectivePlane | Duchon | EuclideanPatch | Poincare | Precomputed(_)
| FiniteSet => {
regular_product_grid(d, resolution, -0.5, 0.5, true)
}
}
}
pub(crate) fn capped_per_axis(d: usize, resolution: usize) -> usize {
let mut per_axis = resolution.max(2);
while per_axis.saturating_pow(d as u32) > SHAPE_BAND_MAX_POINTS && per_axis > 2 {
per_axis -= 1;
}
per_axis
}
pub(crate) fn regular_product_grid(
d: usize,
resolution: usize,
lo: f64,
hi: f64,
include_endpoint: bool,
) -> Array2<f64> {
if d == 0 {
return Array2::<f64>::zeros((1, 0));
}
let per_axis = capped_per_axis(d, resolution);
let total = per_axis.saturating_pow(d as u32).max(1);
let denom = if include_endpoint {
(per_axis.max(2) - 1) as f64
} else {
per_axis as f64
};
let mut grid = Array2::<f64>::zeros((total, d));
let mut idx = vec![0usize; d];
for flat in 0..total {
for axis in 0..d {
let frac = idx[axis] as f64 / denom;
grid[[flat, axis]] = lo + (hi - lo) * frac;
}
for axis in (0..d).rev() {
idx[axis] += 1;
if idx[axis] < per_axis {
break;
}
idx[axis] = 0;
}
}
grid
}
pub(crate) fn sphere_latlon_grid(resolution: usize) -> Array2<f64> {
use std::f64::consts::PI;
let r_cap = SHAPE_BAND_MAX_POINTS.isqrt();
let r = resolution.max(2).min(r_cap);
let mut grid = Array2::<f64>::zeros((r * r, 2));
for i in 0..r {
let lat = -PI / 2.0 + PI * (i as f64 + 0.5) / r as f64;
for j in 0..r {
let lon = -PI + 2.0 * PI * (j as f64) / r as f64;
grid[[i * r + j, 0]] = lat;
grid[[i * r + j, 1]] = lon;
}
}
grid
}
pub(crate) fn cylinder_chart_center_grid(resolution: usize) -> Array2<f64> {
let mut per_axis = resolution.max(2);
while per_axis * per_axis > SHAPE_BAND_MAX_POINTS && per_axis > 2 {
per_axis -= 1;
}
let total = per_axis * per_axis;
let line_denom = (per_axis.max(2) - 1) as f64;
let mut grid = Array2::<f64>::zeros((total, 2));
for i in 0..per_axis {
let circle = i as f64 / per_axis as f64;
for j in 0..per_axis {
let line = -0.5 + (j as f64) / line_denom;
grid[[i * per_axis + j, 0]] = circle;
grid[[i * per_axis + j, 1]] = line;
}
}
grid
}
pub(crate) fn mobius_chart_center_grid(resolution: usize) -> Array2<f64> {
let mut per_axis = resolution.max(2);
while per_axis * per_axis > SHAPE_BAND_MAX_POINTS && per_axis > 2 {
per_axis -= 1;
}
let mut grid = Array2::<f64>::zeros((per_axis * per_axis, 2));
let width_denom = (per_axis - 1) as f64;
for i in 0..per_axis {
let angle = 2.0 * i as f64 / per_axis as f64;
for j in 0..per_axis {
let width = -1.0 + 2.0 * j as f64 / width_denom;
grid[[i * per_axis + j, 0]] = angle;
grid[[i * per_axis + j, 1]] = width;
}
}
grid
}
pub(crate) fn chart_nominal_radius(atom: &SaeManifoldAtom, resolution: usize) -> f64 {
use crate::manifold::SaeAtomBasisKind::*;
match atom.basis_kind() {
Periodic | Torus | KleinBottle => {
0.5 / (capped_per_axis(atom.latent_dim(), resolution) as f64)
}
Sphere | ProjectivePlane => {
let r_cap = SHAPE_BAND_MAX_POINTS.isqrt();
std::f64::consts::PI / (resolution.max(2).min(r_cap) as f64)
}
Cylinder => 0.5 / (capped_per_axis(atom.latent_dim(), resolution) as f64),
Mobius => 1.0 / (capped_per_axis(atom.latent_dim(), resolution) as f64),
Linear | Duchon | EuclideanPatch | Poincare | Precomputed(_) | FiniteSet => {
1.0 / (resolution.max(2) as f64)
}
}
}
pub(crate) fn chart_region(
atom: &SaeManifoldAtom,
center: Array1<f64>,
radius: f64,
) -> ChartRegion {
use crate::manifold::SaeAtomBasisKind::*;
let region = ChartRegion::new(center.clone(), radius);
match atom.basis_kind() {
Duchon => {
let center_norm = center.dot(¢er).sqrt();
let r_min = (center_norm - radius).max(f64::MIN_POSITIVE);
let r_max = center_norm + radius;
region.with_radial_bounds(r_min, r_max)
}
Periodic | Sphere | Torus | ProjectivePlane | KleinBottle | Cylinder | Mobius | Linear
| EuclideanPatch | Poincare | Precomputed(_) | FiniteSet => region,
}
}
#[cfg(test)]
mod encode_fix_tests {
use super::*;
use crate::manifold::SaeAtomBasisKind;
use ndarray::{Array1, Array2, Array3};
fn tiny_atom(kind: SaeAtomBasisKind, latent_dim: usize) -> SaeManifoldAtom {
let m = 2usize;
let phi = Array2::<f64>::eye(m);
let jet = Array3::<f64>::zeros((m, m, latent_dim));
let dec = Array2::<f64>::from_elem((m, 1), 0.5);
let smooth = Array2::<f64>::eye(m);
SaeManifoldAtom::new_with_provided_function_gram(
"tiny", kind, latent_dim, phi, jet, dec, smooth,
)
.expect("tiny atom builds")
}
#[test]
fn joint_normal_equations_use_the_shared_multi_atom_residual() {
let jac = ndarray::array![[1.0_f64, 0.0], [1.0, 1.0]];
let residual = ndarray::array![-2.0_f64, -1.0];
let (_value, grad, hess) = joint_data_value_grad_hess(jac.view(), residual.view(), None);
let step = joint_encode_damped_step(hess.view(), grad.view(), 1.0e-15)
.expect("joint system factors")
.expect("joint system is positive definite");
assert!(
(step[0] - 1.0).abs() < 1.0e-12,
"first coefficient={}",
step[0]
);
assert!(
(step[1] - 1.0).abs() < 1.0e-12,
"second coefficient={}",
step[1]
);
let recon0 = step[0] + step[1];
let recon1 = step[1];
assert!((recon0 - 2.0).abs() < 1.0e-12 && (recon1 - 1.0).abs() < 1.0e-12);
}
#[test]
fn wrap_aware_containment_accepts_seam_point() {
let atom = tiny_atom(SaeAtomBasisKind::Periodic, 1);
let t = Array1::from(vec![0.99f64]);
let center = Array1::from(vec![0.01f64]);
let radius = 0.05;
let raw = (t[0] - center[0]).abs();
assert!(
raw > radius,
"precondition: raw Euclidean distance {raw} must exceed the chart radius {radius}"
);
let d = latent_coordinate_distance(&atom, t.view(), center.view());
assert!(
(d - 0.02).abs() < 1e-12,
"wrap-aware distance across the seam must be 0.02, got {d}"
);
assert!(
d <= radius,
"wrap-aware seam point must now be in-chart (d={d} <= r={radius})"
);
}
#[test]
fn wrap_aware_containment_does_not_wrap_flat_axis() {
let atom = tiny_atom(SaeAtomBasisKind::EuclideanPatch, 1);
let t = Array1::from(vec![0.99f64]);
let center = Array1::from(vec![0.01f64]);
let d = latent_coordinate_distance(&atom, t.view(), center.view());
assert!(
(d - 0.98).abs() < 1e-12,
"a flat (non-periodic) axis must keep the full 0.98 distance, got {d}"
);
assert!(
d > 0.05,
"flat-axis point correctly stays out of a 0.05 ball"
);
}
#[test]
fn converging_h_sequence_is_never_flagged() {
let mut h = 0.9f64;
while h > KANTOROVICH_THRESHOLD {
let h_next = h * h;
assert!(
warmup_progress_sufficient(h_next, h),
"converging step {h} -> {h_next} must be accepted (no regression)"
);
h = h_next;
}
}
#[test]
fn plateau_h_sequence_terminates_bounded() {
let limit = 0.65f64; let ratio = 0.8f64; let mut h = 2.0f64; let start = h;
let mut accepted = 0usize;
let mut iters = 0usize;
loop {
iters += 1;
assert!(
iters < 10_000,
"warm-up must terminate in a bounded number of steps, not loop"
);
let h_next = limit + (h - limit) * ratio; if !warmup_progress_sufficient(h_next, h) {
break; }
accepted += 1;
h = h_next;
}
assert!(
accepted >= 1,
"expected the warm-up to accept at least one contracting step first"
);
assert!(
h > limit && h < start,
"flagged mid-descent (limit < h={h} < start={start}); old rule would not stop here"
);
assert!(iters < 200, "plateau flagged in {iters} steps (bounded)");
}
#[test]
fn non_finite_h_flags() {
assert!(!warmup_progress_sufficient(f64::NAN, 0.9));
assert!(!warmup_progress_sufficient(f64::INFINITY, 0.9));
assert!(!warmup_progress_sufficient(0.5, f64::NAN));
}
#[test]
fn chart_nominal_radius_sphere_covers_capped_grid_spacing() {
let atom = tiny_atom(SaeAtomBasisKind::Sphere, 2);
let r_cap = SHAPE_BAND_MAX_POINTS.isqrt(); let resolution = r_cap * 2; let lon_half_spacing = std::f64::consts::PI / r_cap as f64;
let r = chart_nominal_radius(&atom, resolution);
assert!(
r >= lon_half_spacing - 1e-12,
"sphere radius {r} must cover the capped lon half-spacing {lon_half_spacing} \
(no gaps for resolution>r_cap); raw π/{resolution}={} would gap",
std::f64::consts::PI / resolution as f64
);
}
fn chart_with_recon(recon: Vec<f64>) -> CertifiedChart {
CertifiedChart {
region: ChartRegion::new(Array1::zeros(1), 1.0),
lipschitz: 1.0,
beta_center: 1.0,
certified_radius: 1.0,
amortized_jacobian: None,
recon_center: Array1::from(recon),
amortized_base: None,
jacobian_sup: 0.0,
}
}
fn atlas_two_charts(m1: f64, m2: f64) -> AtomEncodeAtlas {
AtomEncodeAtlas {
periodic_fiber: None,
atom_index: 0,
latent_dim: 1,
decoder_norm_sum: 1.0,
charts: vec![chart_with_recon(vec![m1]), chart_with_recon(vec![m2])],
}
}
#[test]
fn f1_routing_scores_amplitude_scaled_reconstruction() {
let atlas = atlas_two_charts(1.0, 10.0);
let x = Array1::from(vec![1.0]);
let (idx, _) = nearest_chart(&atlas, x.view(), 0.1).expect("routes");
assert_eq!(
idx, 1,
"z=0.1: must route to the m=10 chart (z·m=1 exact), not m=1"
);
let ranked = crate::test_support::nearest_charts_topk(&atlas, x.view(), 0.1, 2);
assert_eq!(ranked[0], 1, "z=0.1: nearest chart is the m=10 chart");
let (idxn, _) = nearest_chart(&atlas, x.view(), -0.1).expect("routes");
assert_eq!(idxn, 0, "z=-0.1: sign-aware routing prefers the m=1 chart");
let (idx1, _) = nearest_chart(&atlas, x.view(), 1.0).expect("routes");
assert_eq!(idx1, 0, "z=1 recovers the amplitude-1 nearest (m=1) chart");
assert_eq!(
crate::test_support::nearest_charts_topk(&atlas, x.view(), 1.0, 1)[0],
0
);
}
#[derive(Debug)]
struct ConstantPhi {
m: usize,
d: usize,
}
impl SaeBasisEvaluator for ConstantPhi {
fn evaluate(
&self,
coords: ndarray::ArrayView2<'_, f64>,
) -> Result<(Array2<f64>, Array3<f64>), String> {
let n = coords.nrows();
Ok((
Array2::ones((n, self.m)),
Array3::zeros((n, self.m, self.d)),
))
}
fn second_jet_dyn(
&self,
coords: ndarray::ArrayView2<'_, f64>,
) -> Option<Result<ndarray::Array4<f64>, String>> {
let n = coords.nrows();
Some(Ok(ndarray::Array4::zeros((n, self.m, self.d, self.d))))
}
fn third_jet_dyn(
&self,
coords: ndarray::ArrayView2<'_, f64>,
) -> Option<Result<ndarray::Array5<f64>, String>> {
if coords.ncols() != self.d {
return Some(Err(format!(
"ConstantPhi::third_jet_dyn: expected d = {}, got {} coords",
self.d,
coords.ncols()
)));
}
None
}
}
#[test]
fn f2_certificate_uses_true_hessian_refuses_singular_field() {
let atom = tiny_atom(SaeAtomBasisKind::EuclideanPatch, 1);
let eval = ConstantPhi {
m: atom.basis_size(),
d: 1,
};
let t0 = Array1::from(vec![0.0]);
let x = Array1::from(vec![0.5]);
let (g, h) = encode_grad_hess(&atom, &eval, t0.view(), x.view(), 1.0)
.expect("encode_grad_hess runs")
.expect("second jet present ⇒ Some");
assert!(
h.iter().all(|&v| v == 0.0),
"the TRUE Hessian of a constant reconstruction is 0 — no ridge is added \
to the certified field; got {h:?}"
);
assert!(
g.iter().all(|&v| v == 0.0),
"gradient is 0 at a flat reconstruction"
);
let (cert, _) = row_certificate(&atom, &eval, t0.view(), x.view(), 1.0, 1.0)
.expect("row_certificate runs");
assert!(
!cert.certified(),
"a singular true Hessian must NOT be certified (the old ridged H falsely did)"
);
assert!(
!cert.beta.is_finite(),
"β must be ∞ (uncertifiable), never the ridge-faked 1/ridge; got {}",
cert.beta
);
}
#[test]
fn the_d1_periodic_fiber_enumerator_minimises_the_encode_objective_2518() {
let m = 5usize;
let decoder = ndarray::array![
[0.00_f64, 0.00],
[1.00, 0.00],
[0.00, 1.00],
[0.00, 0.90],
[0.90, 0.00],
];
let atom = SaeManifoldAtom::new_with_provided_function_gram(
"folded",
SaeAtomBasisKind::Periodic,
1,
Array2::<f64>::eye(m),
Array3::<f64>::zeros((m, m, 1)),
decoder.clone(),
Array2::<f64>::eye(m),
)
.expect("folded periodic atom builds");
let fiber = build_periodic_fiber(&atom)
.expect("a d = 1 periodic atom of odd harmonic width carries the enumerator");
let evaluator = PeriodicHarmonicEvaluator::new(m).expect("evaluator");
let scan = 40_000usize;
let mut folded_targets = 0usize;
for probe in 0..23usize {
let angle = std::f64::consts::TAU * probe as f64 / 23.0;
let radius = 0.35 + 0.75 * ((probe % 5) as f64 / 4.0);
let x = Array1::from(vec![radius * angle.cos(), radius * angle.sin()]);
let projected = decoder.dot(&x);
let linear: Vec<f64> = projected.iter().copied().collect();
let extremum = fiber
.minimize_squared_distance(&linear)
.expect("the fiber enumerates");
let recon_error = |t: f64| -> f64 {
let coords =
Array2::from_shape_vec((1, 1), vec![t]).expect("coordinate shape");
let (phi, _) = evaluator.evaluate(coords.view()).expect("basis evaluates");
let recon = phi.row(0).dot(&decoder);
(&recon - &x).dot(&(&recon - &x))
};
let mut best_scan = (f64::INFINITY, 0.0_f64);
let mut second_basin = f64::INFINITY;
for step in 0..scan {
let t = step as f64 / scan as f64;
let err = recon_error(t);
if err < best_scan.0 {
best_scan = (err, t);
}
}
for step in 0..scan {
let t = step as f64 / scan as f64;
let gap = (t - best_scan.1).abs();
if gap.min(1.0 - gap) < 0.2 {
continue;
}
second_basin = second_basin.min(recon_error(t));
}
if second_basin < best_scan.0 * 4.0 {
folded_targets += 1;
}
let enumerated = recon_error(extremum.coordinate);
assert!(
enumerated <= best_scan.0 + 1.0e-8 * (1.0 + best_scan.0),
"probe {probe}: the enumerated coordinate {:.9} reconstructs at {:.12e}, \
worse than the scan's best {:.12e} at t = {:.9} — the enumerator and the \
evaluator disagree about the harmonic ordering",
extremum.coordinate,
enumerated,
best_scan.0,
best_scan.1
);
}
assert!(
folded_targets >= 5,
"the fixture must actually fold: only {folded_targets} of 23 targets had a \
competing basin within 4x of the winner, so agreeing with the scan proves \
nothing about multi-basin routing"
);
assert!(
build_periodic_fiber(&tiny_atom(SaeAtomBasisKind::Linear, 1)).is_none(),
"a linear atom must not claim the period-one harmonic enumerator"
);
assert!(
build_periodic_fiber(&tiny_atom(SaeAtomBasisKind::Torus, 2)).is_none(),
"a d = 2 atom must not claim the one-dimensional enumerator"
);
}
#[test]
fn f3_duchon_atoms_are_uncertifiable() {
let atom = tiny_atom(SaeAtomBasisKind::Duchon, 1);
let centers = ndarray::array![[0.0_f64], [0.3], [0.7]];
let radii = vec![0.1_f64, 0.1, 0.1];
let atlas = EncodeAtlas::build_atom_atlas_from_centers(
0,
&atom,
centers.view(),
&radii,
1.0,
1.0,
&AtlasConfig::default(),
)
.expect("duchon atlas builds (uncertified)");
assert_eq!(atlas.charts.len(), 3, "one chart per center");
for (i, chart) in atlas.charts.iter().enumerate() {
assert_eq!(
chart.certified_radius, 0.0,
"duchon chart {i} must be uncertified (refused), got r={}",
chart.certified_radius
);
assert!(
chart.amortized_jacobian.is_none(),
"duchon chart {i} must carry no amortized predictor"
);
}
}
#[test]
fn f6_certified_step_not_rejected_by_progress_rule() {
assert!(
!warmup_progress_sufficient(0.499, 0.501),
"precondition: this tiny decrease fails the progress bar"
);
assert!(
!warmup_should_reject(true, 0.499, 0.501),
"a certified step must be accepted regardless of the progress floor"
);
assert!(
warmup_should_reject(false, 0.499, 0.501),
"an uncertified sub-floor step is a plateau and must flag to fallback"
);
assert!(!warmup_should_reject(true, 0.1, 0.9));
assert!(!warmup_should_reject(false, 0.4, 0.9));
}
#[test]
fn beta_eta_newton_refuses_rank1_null_2x2() {
let h = ndarray::array![[4.0_f64, 0.0], [0.0, 0.0]];
let g = Array1::from(vec![4.0_f64, 0.0]);
assert!(beta_eta_newton(h.view(), g.view()).expect("runs").is_none());
}
#[test]
fn beta_eta_newton_refuses_null_with_projected_gradient() {
let h = ndarray::array![[4.0_f64, 0.0], [0.0, 0.0]];
let g = Array1::from(vec![4.0_f64, 1.0e-3]);
assert!(beta_eta_newton(h.view(), g.view()).expect("runs").is_none());
}
#[test]
fn beta_eta_newton_refuses_genuine_negative_curvature_2x2() {
let h = ndarray::array![[4.0_f64, 0.0], [0.0, -2.0]];
let g = Array1::from(vec![1.0_f64, 1.0]);
let out = beta_eta_newton(h.view(), g.view()).expect("runs");
assert!(
out.is_none(),
"a negative-curvature (indefinite) start must NOT certify — it is at/past a \
basin boundary; deflating it to +1 would be a false certificate"
);
}
#[test]
fn beta_eta_newton_refuses_rank_deficient_3x3() {
let h = ndarray::array![[4.0_f64, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 0.0]];
let g = Array1::from(vec![4.0_f64, 1.0, 0.0]);
assert!(beta_eta_newton(h.view(), g.view()).expect("runs").is_none());
}
#[test]
fn beta_eta_newton_healthy_pd_unchanged() {
let h = ndarray::array![[4.0_f64, 1.0], [1.0, 3.0]];
let g = Array1::from(vec![2.0_f64, -1.0]);
let (beta, eta, delta) = beta_eta_newton(h.view(), g.view())
.expect("runs")
.expect("a PD block certifies");
let lambda_min = 0.5 * (7.0 - 5.0_f64.sqrt());
assert!((beta - 1.0 / lambda_min).abs() < 1e-9, "β={beta}");
let hd0 = h[[0, 0]] * delta[0] + h[[0, 1]] * delta[1];
let hd1 = h[[1, 0]] * delta[0] + h[[1, 1]] * delta[1];
assert!((hd0 + g[0]).abs() < 1e-9 && (hd1 + g[1]).abs() < 1e-9);
assert!((eta - delta.dot(&delta).sqrt()).abs() < 1e-12);
}
}