pub mod closure_family;
pub mod curvature_estimand;
pub mod integrator;
pub mod latent_seed;
pub mod manifold;
pub mod manifolds;
pub mod optimizer;
pub mod response_geometry;
pub mod sinkhorn_barycenter;
pub use manifolds::{circle, constant_curvature, euclidean, grassmann, lie_so, poincare, product, simplex, spd, sphere, stiefel, torus};
pub use closure_family::{ClosureFamily, ClosureProfileCi};
pub use curvature_estimand::{
CurvatureVerdict,
DesignCoordKappaJet,
FlatnessTest,
KappaEstimateSupport,
KappaProfileCi,
flatness_lr_test,
profile_ci_walk,
wald_half_width,
};
pub use integrator::GeodesicIntegrator;
pub use latent_seed::laplacian_eigenmap_coords;
pub use manifold::{GeometryError, GeometryResult, ManifoldSpec, RiemannianManifold};
pub use manifolds::{
CircleManifold,
ConstantCurvature,
EuclideanManifold,
GrassmannManifold,
ProductManifold,
SpdManifold,
SphereManifold,
StiefelManifold,
TorusManifold,
constant_curvature_dirichlet_penalty,
constant_curvature_dirichlet_penalty_kappa_derivative,
distance_kappa_jet,
};
pub use optimizer::{
RiemannianLBFGS,
RiemannianObjective,
RiemannianTrustRegion,
TrustRegionTermination,
};
pub use response_geometry::{
ResponseCurvatureFit,
ResponseGeometryError,
ResponseManifold,
fit_response_curvature,
response_curvature_criterion,
response_exp_map,
response_frechet_mean,
response_log_map,
};
use ndarray::{Array1, ArrayView1};
pub(crate) fn normalize_weights(
n: usize,
weights: Option<ArrayView1<'_, f64>>,
) -> Result<Array1<f64>, String> {
match weights {
None => Ok(Array1::from_elem(n, 1.0 / n as f64)),
Some(w) => {
if w.len() != n {
return Err("weights length must match the number of rows".to_string());
}
let mut total = 0.0_f64;
for value in w.iter() {
if !value.is_finite() || *value < 0.0 {
return Err(
"weights must be finite, non-negative, and have positive total".to_string(),
);
}
total += *value;
}
if total <= 0.0 {
return Err(
"weights must be finite, non-negative, and have positive total".to_string(),
);
}
Ok(w.mapv(|v| v / total))
}
}
}