1pub mod closure_family;
2pub mod curvature_estimand;
3pub mod integrator;
4pub mod latent_seed;
5pub mod manifold;
6pub mod manifolds;
7pub mod optimizer;
8pub mod response_geometry;
9pub mod sinkhorn_barycenter;
10
11pub use manifolds::{circle, constant_curvature, euclidean, grassmann, lie_so, poincare, product, simplex, spd, sphere, stiefel, torus};
15
16pub use closure_family::{ClosureFamily, ClosureProfileCi};
17pub use curvature_estimand::{
18 CurvatureVerdict,
19 DesignCoordKappaJet,
20 FlatnessTest,
21 KappaEstimateSupport,
22 KappaProfileCi,
23 flatness_lr_test,
24 profile_ci_walk,
25 wald_half_width,
26};
27pub use integrator::GeodesicIntegrator;
28pub use latent_seed::laplacian_eigenmap_coords;
29pub use manifold::{GeometryError, GeometryResult, ManifoldSpec, RiemannianManifold};
30pub use manifolds::{
31 CircleManifold,
32 ConstantCurvature,
33 EuclideanManifold,
34 GrassmannManifold,
35 ProductManifold,
36 SpdManifold,
37 SphereManifold,
38 StiefelManifold,
39 TorusManifold,
40 constant_curvature_dirichlet_penalty,
41 constant_curvature_dirichlet_penalty_kappa_derivative,
42 distance_kappa_jet,
43};
44pub use optimizer::{
45 RiemannianLBFGS,
46 RiemannianObjective,
47 RiemannianTrustRegion,
48 TrustRegionTermination,
49};
50pub use response_geometry::{
51 ResponseCurvatureFit,
52 ResponseGeometryError,
53 ResponseManifold,
54 fit_response_curvature,
55 response_curvature_criterion,
56 response_exp_map,
57 response_frechet_mean,
58 response_log_map,
59};
60
61use ndarray::{Array1, ArrayView1};
62
63pub(crate) fn normalize_weights(
69 n: usize,
70 weights: Option<ArrayView1<'_, f64>>,
71) -> Result<Array1<f64>, String> {
72 match weights {
73 None => Ok(Array1::from_elem(n, 1.0 / n as f64)),
74 Some(w) => {
75 if w.len() != n {
76 return Err("weights length must match the number of rows".to_string());
77 }
78 let mut total = 0.0_f64;
79 for value in w.iter() {
80 if !value.is_finite() || *value < 0.0 {
81 return Err(
82 "weights must be finite, non-negative, and have positive total".to_string(),
83 );
84 }
85 total += *value;
86 }
87 if total <= 0.0 {
88 return Err(
89 "weights must be finite, non-negative, and have positive total".to_string(),
90 );
91 }
92 Ok(w.mapv(|v| v / total))
93 }
94 }
95}