use cartan_core::{Manifold, Real};
use rand::Rng;
use crate::error::StochasticError;
#[derive(Debug, Clone)]
pub struct OrthonormalFrame<M: Manifold> {
pub basis: Vec<M::Tangent>,
}
impl<M: Manifold> OrthonormalFrame<M> {
#[inline]
pub fn len(&self) -> usize {
self.basis.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.basis.is_empty()
}
pub fn from_orthonormal(basis: Vec<M::Tangent>) -> Self {
Self { basis }
}
pub fn from_candidates(
manifold: &M,
p: &M::Point,
candidates: Vec<M::Tangent>,
tol: Real,
) -> Result<Self, StochasticError> {
let n = manifold.dim();
if candidates.len() < n {
return Err(StochasticError::FrameDimMismatch {
expected: n,
got: candidates.len(),
});
}
let mut basis: Vec<M::Tangent> = Vec::with_capacity(n);
for v in candidates {
if basis.len() == n {
break;
}
let mut u = manifold.project_tangent(p, &v);
for e in &basis {
let c = manifold.inner(p, &u, e);
u = u - e.clone() * c;
}
let norm = manifold.norm(p, &u);
if norm < tol {
continue;
}
basis.push(u * (1.0 / norm));
}
if basis.len() < n {
return Err(StochasticError::GramSchmidtRankDeficient {
index: basis.len(),
norm: 0.0,
threshold: tol,
});
}
Ok(Self { basis })
}
pub fn reorthonormalize(
&mut self,
manifold: &M,
p: &M::Point,
tol: Real,
) -> Result<(), StochasticError> {
let taken = std::mem::take(&mut self.basis);
let new = Self::from_candidates(manifold, p, taken, tol)?;
self.basis = new.basis;
Ok(())
}
}
pub fn random_frame_at<M: Manifold, R: Rng>(
manifold: &M,
p: &M::Point,
rng: &mut R,
) -> Result<OrthonormalFrame<M>, StochasticError> {
let n = manifold.dim();
let candidates: Vec<M::Tangent> = (0..(2 * n))
.map(|_| manifold.random_tangent(p, rng))
.collect();
OrthonormalFrame::from_candidates(manifold, p, candidates, 1e-10)
}