use ndarray::{Array1, Array2};
use crate::manifold::RiemannianManifold;
use crate::manifolds::constant_curvature::{
ConstantCurvature, distance_kappa_jet, exp_map_kappa_jet, log_map_kappa_jet,
};
use crate::manifolds::poincare;
struct Rng(u64);
impl Rng {
fn new(seed: u64) -> Self {
Self(seed)
}
fn next_u64(&mut self) -> u64 {
let mut x = self.0;
x ^= x << 13;
x ^= x >> 7;
x ^= x << 17;
self.0 = x;
x
}
fn uniform(&mut self) -> f64 {
(self.next_u64() >> 11) as f64 / (1u64 << 53) as f64
}
fn normal(&mut self) -> f64 {
let u1 = self.uniform().max(1.0e-300);
let u2 = self.uniform();
(-2.0 * u1.ln()).sqrt() * (std::f64::consts::TAU * u2).cos()
}
fn gaussian_vec(&mut self, n: usize) -> Array1<f64> {
Array1::from_shape_fn(n, |_| self.normal())
}
}
fn chart_point(rng: &mut Rng, dim: usize) -> Array1<f64> {
let mut v = rng.gaussian_vec(dim);
let magnitude = v.dot(&v).sqrt().max(1.0e-30);
v *= 0.45 * rng.uniform() / magnitude;
v
}
const CURVATURES: [f64; 11] = [
2.0, 1.0, 0.35, 0.01, 1.0e-6, 0.0, -1.0e-6, -0.01, -0.35, -1.0, -2.0,
];
const DIMS: [usize; 4] = [1, 2, 3, 5];
const TRIALS: usize = 200;
const SEED: u64 = 0x9E37_79B9_7F4A_7C15;
fn seed_for(dim: usize, kappa: f64) -> u64 {
SEED ^ ((dim as u64) << 32) ^ kappa.to_bits()
}
fn sup_diff(a: &Array1<f64>, b: &Array1<f64>) -> f64 {
a.iter()
.zip(b.iter())
.map(|(x, y)| (x - y).abs())
.fold(0.0, f64::max)
}
fn joint_scale(a: &Array1<f64>, b: &Array1<f64>) -> f64 {
a.iter()
.chain(b.iter())
.fold(1.0_f64, |acc, v| acc.max(v.abs()))
}
#[test]
fn sectional_curvature_is_kappa_everywhere() {
let mut verified = 0usize;
for dim in DIMS {
if dim < 2 {
continue;
}
for kappa in CURVATURES {
let manifold = ConstantCurvature::new(dim, kappa);
let mut rng = Rng::new(seed_for(dim, kappa));
for _ in 0..TRIALS {
let x = chart_point(&mut rng, dim);
let u = manifold
.project_tangent(x.view(), rng.gaussian_vec(dim).view())
.expect("tangent u");
let v = manifold
.project_tangent(x.view(), rng.gaussian_vec(dim).view())
.expect("tangent v");
let Ok(k) = manifold.sectional_curvature(x.view(), (u.view(), v.view())) else {
continue;
};
assert!(
(k - kappa).abs() <= 1.0e-9 * kappa.abs().max(1.0),
"dim {dim} kappa {kappa}: sectional curvature is {k}, not kappa"
);
verified += 1;
}
}
}
assert!(verified > 0, "no sectional curvature was evaluated");
}
#[test]
fn distance_is_invariant_under_mobius_translation() {
let mut verified = 0usize;
for dim in DIMS {
for kappa in CURVATURES {
let manifold = ConstantCurvature::new(dim, kappa);
let mut rng = Rng::new(seed_for(dim, kappa));
for _ in 0..TRIALS {
let x = chart_point(&mut rng, dim);
let y = chart_point(&mut rng, dim);
let g = chart_point(&mut rng, dim);
let base = manifold.distance(x.view(), y.view()).expect("d(x,y)");
let (Ok(gx), Ok(gy)) = (
manifold.mobius_add(g.view(), x.view()),
manifold.mobius_add(g.view(), y.view()),
) else {
continue;
};
let Ok(moved) = manifold.distance(gx.view(), gy.view()) else {
continue;
};
assert!(
(moved - base).abs() <= 1.0e-9 * base.max(1.0),
"dim {dim} kappa {kappa}: translation changed the distance \
({base} -> {moved})"
);
verified += 1;
}
}
}
assert!(verified > 0, "no translated distance was evaluated");
}
#[test]
fn the_kappa_family_is_one_manifold_rescaled() {
let mut verified = 0usize;
for dim in DIMS {
for kappa in CURVATURES {
if kappa.abs() <= 1.0e-9 {
continue;
}
let manifold = ConstantCurvature::new(dim, kappa);
let unit = ConstantCurvature::new(dim, kappa.signum());
let scale = kappa.abs().sqrt();
let mut rng = Rng::new(seed_for(dim, kappa));
for _ in 0..TRIALS {
let x = chart_point(&mut rng, dim);
let y = chart_point(&mut rng, dim);
let here = manifold.distance(x.view(), y.view()).expect("d_kappa");
let Ok(there) = unit.distance((&x * scale).view(), (&y * scale).view()) else {
continue;
};
assert!(
(here - there / scale).abs() <= 1.0e-9 * here.max(1.0),
"dim {dim} kappa {kappa}: d_kappa = {here} but the rescaled unit \
form gives {}",
there / scale
);
verified += 1;
}
}
}
assert!(verified > 0, "no rescaling was evaluated");
}
#[test]
fn the_flat_and_hyperbolic_members_match_their_classical_forms() {
let mut flat_checked = 0usize;
let mut hyperbolic_checked = 0usize;
for dim in DIMS {
let flat = ConstantCurvature::new(dim, 0.0);
let hyperbolic = ConstantCurvature::new(dim, -1.0);
let mut rng = Rng::new(seed_for(dim, 0.0));
for _ in 0..TRIALS {
let x = chart_point(&mut rng, dim);
let y = chart_point(&mut rng, dim);
let got = flat.distance(x.view(), y.view()).expect("flat distance");
let want = 2.0 * (&x - &y).dot(&(&x - &y)).sqrt();
assert!(
(got - want).abs() <= 1.0e-12 * want.max(1.0),
"dim {dim}: flat distance {got} != 2||x-y|| = {want}"
);
flat_checked += 1;
let got = hyperbolic
.distance(x.view(), y.view())
.expect("hyperbolic distance");
let want =
poincare::poincare_distance(x.view(), y.view(), -1.0).expect("poincare distance");
assert!(
(got - want).abs() <= 1.0e-12 * want.max(1.0),
"dim {dim}: kappa=-1 distance {got} disagrees with poincare.rs {want}"
);
hyperbolic_checked += 1;
}
}
assert!(flat_checked > 0 && hyperbolic_checked > 0);
}
#[test]
fn distance_is_a_metric_and_agrees_with_the_logarithm() {
let mut verified = 0usize;
for dim in DIMS {
for kappa in CURVATURES {
let manifold = ConstantCurvature::new(dim, kappa);
let mut rng = Rng::new(seed_for(dim, kappa));
for _ in 0..TRIALS {
let x = chart_point(&mut rng, dim);
let y = chart_point(&mut rng, dim);
let z = chart_point(&mut rng, dim);
let dxy = manifold.distance(x.view(), y.view()).expect("d(x,y)");
let dyx = manifold.distance(y.view(), x.view()).expect("d(y,x)");
let dyz = manifold.distance(y.view(), z.view()).expect("d(y,z)");
let dxz = manifold.distance(x.view(), z.view()).expect("d(x,z)");
assert!(
(dxy - dyx).abs() <= 1.0e-12 * dxy.max(1.0),
"dim {dim} kappa {kappa}: distance is not symmetric ({dxy} vs {dyx})"
);
assert!(
dxz <= dxy + dyz + 1.0e-12 * dxz.max(1.0),
"dim {dim} kappa {kappa}: triangle inequality violated \
({dxz} > {dxy} + {dyz})"
);
let logarithm = manifold.log_map(x.view(), y.view()).expect("log");
let lambda = manifold
.conformal_factor(x.view())
.expect("conformal factor");
let metric_norm = lambda * logarithm.dot(&logarithm).sqrt();
assert!(
(metric_norm - dxy).abs() <= 1.0e-9 * dxy.max(1.0),
"dim {dim} kappa {kappa}: ||log|| = {metric_norm} != d = {dxy}"
);
verified += 1;
}
}
}
assert!(verified > 0, "no metric axiom was evaluated");
}
#[test]
fn radial_volume_jacobian_matches_its_closed_form() {
let mut verified = 0usize;
for dim in DIMS {
for kappa in CURVATURES {
let manifold = ConstantCurvature::new(dim, kappa);
let mut rng = Rng::new(seed_for(dim, kappa));
for _ in 0..TRIALS {
let r = 2.0 * rng.uniform();
let got = manifold.jacobian_radial(r);
let want = if dim <= 1 {
1.0
} else {
let sn_over_r = if kappa.abs() <= 1.0e-12 || r == 0.0 {
1.0
} else if kappa > 0.0 {
let arc = kappa.sqrt() * r;
(arc.sin() / arc).max(0.0)
} else {
let arc = (-kappa).sqrt() * r;
arc.sinh() / arc
};
sn_over_r.powi((dim - 1) as i32)
};
assert!(
(got - want).abs() <= 1.0e-9 * want.abs().max(1.0e-12),
"dim {dim} kappa {kappa} r {r}: J = {got}, closed form {want}"
);
verified += 1;
}
}
}
assert!(verified > 0, "no volume Jacobian was evaluated");
}
#[test]
fn batched_distance_is_bit_identical_to_the_scalar_path() {
let mut verified = 0usize;
for dim in DIMS {
for kappa in CURVATURES {
let manifold = ConstantCurvature::new(dim, kappa);
let mut rng = Rng::new(seed_for(dim, kappa));
for rows in [1usize, 3, 4, 5, 8, 11] {
let base = chart_point(&mut rng, dim);
let mut targets = Array2::<f64>::zeros((rows, dim));
for r in 0..rows {
targets.row_mut(r).assign(&chart_point(&mut rng, dim));
}
let mut batched = vec![0.0_f64; rows];
manifold
.distance_batch(base.view(), targets.view(), &mut batched)
.expect("distance_batch");
for r in 0..rows {
let scalar = manifold
.distance(base.view(), targets.row(r))
.expect("scalar distance");
assert_eq!(
batched[r].to_bits(),
scalar.to_bits(),
"dim {dim} kappa {kappa} rows {rows} row {r}: \
batched {} != scalar {scalar}",
batched[r]
);
verified += 1;
}
}
}
}
assert!(verified > 0, "no batched distance was evaluated");
}
fn jet_tolerance(analytic: f64, difference: f64, floor: f64) -> f64 {
1.0e-6 * analytic.abs().max(difference.abs()) + floor
}
const FIRST_FLOOR: f64 = 5.0e-8;
const SECOND_FLOOR: f64 = 1.0e-7;
fn assert_vector_close(
analytic: &Array1<f64>,
difference: &Array1<f64>,
floor: f64,
context: &str,
) {
for (i, (a, d)) in analytic.iter().zip(difference.iter()).enumerate() {
assert!(
(a - d).abs() <= jet_tolerance(*a, *d, floor),
"{context}: component {i} analytic {a} vs finite difference {d}"
);
}
}
#[test]
fn kappa_jets_match_central_differences_of_the_value_path() {
const H: f64 = 1.0e-3;
let mut verified = 0usize;
for dim in [1usize, 2, 3] {
for kappa in [1.0_f64, 0.3, 0.0, -0.3, -1.0] {
let manifold = ConstantCurvature::new(dim, kappa);
let up = ConstantCurvature::new(dim, kappa + H);
let down = ConstantCurvature::new(dim, kappa - H);
let mut rng = Rng::new(seed_for(dim, kappa));
for _ in 0..TRIALS {
let x = chart_point(&mut rng, dim);
let y = chart_point(&mut rng, dim);
let (value, first, second) =
distance_kappa_jet(&manifold, x.view(), y.view()).expect("distance jet");
let here = manifold.distance(x.view(), y.view()).expect("d");
let above = up.distance(x.view(), y.view()).expect("d+");
let below = down.distance(x.view(), y.view()).expect("d-");
assert!(
(value - here).abs() <= 1.0e-12 * here.max(1.0),
"dim {dim} kappa {kappa}: jet value {value} != distance {here}"
);
let fd_first = (above - below) / (2.0 * H);
let fd_second = (above - 2.0 * here + below) / (H * H);
assert!(
(first - fd_first).abs() <= jet_tolerance(first, fd_first, FIRST_FLOOR),
"dim {dim} kappa {kappa}: d/dkappa distance {first} vs FD {fd_first}"
);
assert!(
(second - fd_second).abs() <= jet_tolerance(second, fd_second, SECOND_FLOOR),
"dim {dim} kappa {kappa}: d2/dkappa2 distance {second} vs FD {fd_second}"
);
let (value, first, second) =
log_map_kappa_jet(&manifold, x.view(), y.view()).expect("log jet");
let here = manifold.log_map(x.view(), y.view()).expect("log");
let above = up.log_map(x.view(), y.view()).expect("log+");
let below = down.log_map(x.view(), y.view()).expect("log-");
assert!(
sup_diff(&value, &here) <= 1.0e-12 * joint_scale(&value, &here),
"dim {dim} kappa {kappa}: log jet value disagrees with log_map"
);
let fd_first = (&above - &below) / (2.0 * H);
let fd_second = (&above - &(&here * 2.0) + &below) / (H * H);
assert_vector_close(
&first,
&fd_first,
FIRST_FLOOR,
&format!("dim {dim} kappa {kappa}: d/dkappa log"),
);
assert_vector_close(
&second,
&fd_second,
SECOND_FLOOR,
&format!("dim {dim} kappa {kappa}: d2/dkappa2 log"),
);
let tangent = manifold
.project_tangent(x.view(), (rng.gaussian_vec(dim) * 0.2).view())
.expect("tangent");
let (value, first, second) =
exp_map_kappa_jet(&manifold, x.view(), tangent.view()).expect("exp jet");
let here = manifold.exp_map(x.view(), tangent.view()).expect("exp");
let above = up.exp_map(x.view(), tangent.view()).expect("exp+");
let below = down.exp_map(x.view(), tangent.view()).expect("exp-");
assert!(
sup_diff(&value, &here) <= 1.0e-12 * joint_scale(&value, &here),
"dim {dim} kappa {kappa}: exp jet value disagrees with exp_map"
);
let fd_first = (&above - &below) / (2.0 * H);
let fd_second = (&above - &(&here * 2.0) + &below) / (H * H);
assert_vector_close(
&first,
&fd_first,
FIRST_FLOOR,
&format!("dim {dim} kappa {kappa}: d/dkappa exp"),
);
assert_vector_close(
&second,
&fd_second,
SECOND_FLOOR,
&format!("dim {dim} kappa {kappa}: d2/dkappa2 exp"),
);
verified += 1;
}
}
}
assert!(verified > 0, "no kappa jet was differenced");
}
#[test]
fn spherical_branch_folds_at_kappa_r2_one_so_the_kappa_window_is_symmetric_2687() {
const R: f64 = 0.6;
let r2 = R * R;
let x = ndarray::array![R, 0.0];
let y = ndarray::array![-R, 0.0];
let cap = 0.5 / r2;
let manifold = ConstantCurvature::new(2, cap);
let d_cap = manifold
.distance(x.view(), y.view())
.expect("the shipped κ cap must be strictly inside the chart");
assert!(
(d_cap - 2.089_007_403_281_048).abs() <= 1.0e-12,
"κ = 0.5/R²: d = {d_cap}, predicted 2.089007403281048"
);
let frac_cap = d_cap * cap.sqrt() / std::f64::consts::PI;
assert!(
(frac_cap - 0.783_653_104_061_214_8).abs() <= 1.0e-12,
"κ = 0.5/R²: antipodal fraction = {frac_cap}, predicted 0.7836531040612148"
);
let fold = 1.0 / r2;
let refused = ConstantCurvature::new(2, fold).distance(x.view(), y.view());
assert!(
refused.is_err(),
"κ = 1/R² = {fold} is the κ>0 antipodal fold and must be refused; got {refused:?}"
);
let exact = ConstantCurvature::new(2, 4.0);
let exact_refused = exact.distance(
ndarray::array![0.5, 0.0].view(),
ndarray::array![-0.5, 0.0].view(),
);
assert!(
exact_refused.is_err(),
"κ‖x‖‖y‖ = 1 exactly must be refused; got {exact_refused:?}"
);
let kappa_wide = 9.5 / r2;
let kappa_twin = (1.0 / 9.5) / r2;
let d_wide = ConstantCurvature::new(2, kappa_wide)
.distance(x.view(), y.view())
.expect("past the fold the chart still evaluates — that is the problem");
let d_twin = ConstantCurvature::new(2, kappa_twin)
.distance(x.view(), y.view())
.expect("the twin is interior");
let frac_wide = d_wide * kappa_wide.sqrt() / std::f64::consts::PI;
let frac_twin = d_twin * kappa_twin.sqrt() / std::f64::consts::PI;
assert!(
(frac_wide - 0.399_450_751_329_086_6).abs() <= 1.0e-12,
"κ = 9.5/R²: antipodal fraction = {frac_wide}, predicted 0.3994507513290866"
);
assert!(
(frac_wide - frac_twin).abs() <= 1.0e-14,
"κ = 9.5/R² and κ = (1/9.5)/R² must be indistinguishable in the pair's \
scale-free geometry: {frac_wide} vs {frac_twin}"
);
assert!(
kappa_twin < cap && cap < fold && fold < kappa_wide,
"the ordering this test is about: twin {kappa_twin} < cap {cap} < fold \
{fold} < proposed {kappa_wide}"
);
let mut rng = Rng::new(0x2687);
let mut verified = 0usize;
for _ in 0..64 {
let p = chart_point(&mut rng, 2);
let q = chart_point(&mut rng, 2);
let ab2 = p.dot(&p) * q.dot(&q);
for s in [0.25_f64, 0.75, 3.0] {
let k_lo = s / ab2.sqrt();
let k_hi = 1.0 / (k_lo * ab2);
let (Ok(d_lo), Ok(d_hi)) = (
ConstantCurvature::new(2, k_lo).distance(p.view(), q.view()),
ConstantCurvature::new(2, k_hi).distance(p.view(), q.view()),
) else {
continue;
};
let f_lo = d_lo * k_lo.sqrt() / std::f64::consts::PI;
let f_hi = d_hi * k_hi.sqrt() / std::f64::consts::PI;
assert!(
(f_lo - f_hi).abs() <= 1.0e-12,
"κ ↦ 1/(κ‖x‖²‖y‖²) must fix the scale-free separation: \
κ={k_lo} gives {f_lo}, κ={k_hi} gives {f_hi}"
);
verified += 1;
}
}
assert!(verified > 0, "no reflected pair was evaluated");
}