use std::sync::Arc;
use ndarray::{Array1, Array2, Array3};
use crate::basis::{PeriodicHarmonicEvaluator, SaeBasisEvaluator};
use crate::encode::{certified_encode_candidates, AtlasConfig, EncodeAtlas};
use crate::manifold::{SaeAtomBasisKind, SaeManifoldAtom};
fn folded_atom() -> (SaeManifoldAtom, Array2<f64>, usize) {
let m = 5usize; let decoder = ndarray::array![
[0.00_f64, 0.00],
[1.00, 0.00],
[0.00, 0.00],
[0.00, 1.00],
[0.00, 0.00],
];
let atom = SaeManifoldAtom::new_with_provided_function_gram(
"lemniscate",
SaeAtomBasisKind::Periodic,
1,
Array2::<f64>::eye(m),
Array3::<f64>::zeros((m, m, 1)),
decoder.clone(),
Array2::<f64>::eye(m),
)
.expect("lemniscate periodic atom builds")
.with_basis_evaluator(Arc::new(
PeriodicHarmonicEvaluator::new(m).expect("evaluator"),
));
(atom, decoder, m)
}
fn recon_at(decoder: &Array2<f64>, evaluator: &PeriodicHarmonicEvaluator, t: f64) -> Array1<f64> {
let coords = Array2::from_shape_vec((1, 1), vec![t]).expect("coordinate shape");
let (phi, _) = evaluator.evaluate(coords.view()).expect("basis evaluates");
phi.row(0).dot(decoder)
}
fn folded_atlas(atom: &SaeManifoldAtom, charts: usize) -> EncodeAtlas {
let centers =
Array2::from_shape_fn((charts, 1), |(c, _)| c as f64 / charts as f64);
let radii = vec![0.5 / charts as f64; charts];
let atlas = EncodeAtlas::build_atom_atlas_from_centers(
0,
atom,
centers.view(),
&radii,
1.0,
4.0,
&AtlasConfig::default(),
)
.expect("folded atlas builds");
EncodeAtlas {
atoms: vec![atlas],
config: AtlasConfig::default(),
}
}
#[test]
fn candidates_cover_every_certifiable_chart_in_distance_order() {
let (atom, _decoder, _m) = folded_atom();
let atlas = folded_atlas(&atom, 64);
let x = Array1::from(vec![0.4_f64, -0.7]);
let candidates = certified_encode_candidates(&atlas.atoms[0], x.view(), 1.0);
let certifiable = atlas.atoms[0]
.charts
.iter()
.filter(|c| c.certified_radius > 0.0)
.count();
assert_eq!(
candidates.len(),
certifiable,
"the certified encode must see every certifiable chart, not a fixed prefix"
);
assert!(
candidates.len() > 4,
"precondition: this fixture has more certifiable charts than the deleted constant \
admitted, or the completeness claim is untestable here (got {})",
candidates.len()
);
for pair in candidates.windows(2) {
assert!(
pair[0].1 <= pair[1].1,
"candidates must be distance-ordered for the tail-termination proof to hold: \
{} then {}",
pair[0].1,
pair[1].1
);
}
for (idx, _dist, slack) in &candidates {
assert!(
*slack >= 0.0,
"chart {idx} produced a negative residual slack, which would licence a FALSE skip"
);
}
}
#[test]
fn per_chart_slack_dominates_the_reachable_reconstruction_movement() {
let (atom, decoder, m) = folded_atom();
let evaluator = PeriodicHarmonicEvaluator::new(m).expect("evaluator");
let atlas = folded_atlas(&atom, 64);
let x = Array1::from(vec![0.1_f64, 0.2]);
let amplitude = 1.0_f64;
let candidates = certified_encode_candidates(&atlas.atoms[0], x.view(), amplitude);
assert!(
!candidates.is_empty(),
"no certifiable charts: this test would otherwise pass by having nothing to check"
);
for (idx, _dist, slack) in candidates {
let chart = &atlas.atoms[0].charts[idx];
let center = chart.region.center[0];
let radius = chart.region.radius;
let center_recon = recon_at(&decoder, &evaluator, center);
let samples = 64usize;
for s in 0..=samples {
let t = center - radius + 2.0 * radius * (s as f64 / samples as f64);
let moved = &recon_at(&decoder, &evaluator, t) - ¢er_recon;
let movement = amplitude * moved.dot(&moved).sqrt();
assert!(
movement <= slack * (1.0 + 1.0e-9) + 1.0e-12,
"chart {idx}: reconstruction moves {movement:.9e} inside the ball but the \
prune's slack is only {slack:.9e} — the skip rule would be UNSOUND"
);
}
}
}