#[cfg(feature = "alloc")]
use alloc::{vec, vec::Vec};
use crate::fiber::{Fiber, FiberOps, Section, VecSection};
use crate::rotor::{Rotor, Rotor2, Rotor3};
pub trait DiscreteConnection<const D: usize> {
fn n_edges(&self) -> usize;
fn edge_vertices(&self, e: usize) -> [usize; 2];
fn frame_transport(&self, e: usize) -> &[f64];
fn transport_forward<F: Fiber>(&self, e: usize, element: &F::Element) -> F::Element {
F::transport_by(self.frame_transport(e), D, element)
}
fn transport_reverse<F: Fiber>(&self, e: usize, element: &F::Element) -> F::Element {
let r = self.frame_transport(e);
let mut r_t = [0.0_f64; 9]; for i in 0..D {
for j in 0..D {
r_t[i * D + j] = r[j * D + i];
}
}
F::transport_by(&r_t[..D * D], D, element)
}
}
#[derive(Clone, Debug)]
pub struct EdgeTransport2D {
pub edges: Vec<[usize; 2]>,
pub transports: Vec<[f64; 4]>,
}
impl DiscreteConnection<2> for EdgeTransport2D {
fn n_edges(&self) -> usize { self.edges.len() }
fn edge_vertices(&self, e: usize) -> [usize; 2] { self.edges[e] }
fn frame_transport(&self, e: usize) -> &[f64] { &self.transports[e] }
}
#[derive(Clone, Debug)]
pub struct EdgeTransport3D {
pub edges: Vec<[usize; 2]>,
pub transports: Vec<[f64; 9]>,
}
impl DiscreteConnection<3> for EdgeTransport3D {
fn n_edges(&self) -> usize { self.edges.len() }
fn edge_vertices(&self, e: usize) -> [usize; 2] { self.edges[e] }
fn frame_transport(&self, e: usize) -> &[f64] { &self.transports[e] }
}
pub trait RotorConnection<const D: usize> {
fn n_edges(&self) -> usize;
fn edge_vertices(&self, e: usize) -> [usize; 2];
fn rotor(&self, e: usize) -> Rotor;
fn transport_forward_rotor<F: Fiber>(&self, e: usize, element: &F::Element) -> F::Element {
F::transport_by_rotor(&self.rotor(e), element)
}
fn transport_reverse_rotor<F: Fiber>(&self, e: usize, element: &F::Element) -> F::Element {
F::transport_by_rotor(&self.rotor(e).reverse(), element)
}
}
#[derive(Clone, Debug)]
pub struct EdgeTransportRotor2D {
pub edges: Vec<[usize; 2]>,
pub rotors: Vec<Rotor2>,
}
impl RotorConnection<2> for EdgeTransportRotor2D {
fn n_edges(&self) -> usize { self.edges.len() }
fn edge_vertices(&self, e: usize) -> [usize; 2] { self.edges[e] }
fn rotor(&self, e: usize) -> Rotor { Rotor::R2(self.rotors[e]) }
}
#[derive(Clone, Debug)]
pub struct EdgeTransportRotor3D {
pub edges: Vec<[usize; 2]>,
pub rotors: Vec<Rotor3>,
}
impl RotorConnection<3> for EdgeTransportRotor3D {
fn n_edges(&self) -> usize { self.edges.len() }
fn edge_vertices(&self, e: usize) -> [usize; 2] { self.edges[e] }
fn rotor(&self, e: usize) -> Rotor { Rotor::R3(self.rotors[e]) }
}
pub struct CovLaplacian {
cot_weights: Vec<f64>,
dual_areas: Vec<f64>,
edges: Vec<[usize; 2]>,
vertex_edges: Vec<Vec<(usize, bool)>>,
}
impl CovLaplacian {
pub fn new(
n_vertices: usize,
edges: &[[usize; 2]],
cot_weights: &[f64],
dual_areas: &[f64],
) -> Self {
let mut vertex_edges: Vec<Vec<(usize, bool)>> = vec![vec![]; n_vertices];
for (e, &[v0, v1]) in edges.iter().enumerate() {
vertex_edges[v0].push((e, true));
vertex_edges[v1].push((e, false));
}
Self {
cot_weights: cot_weights.to_vec(),
dual_areas: dual_areas.to_vec(),
edges: edges.to_vec(),
vertex_edges,
}
}
pub fn apply<F, const D: usize, C>(
&self,
section: &impl Section<F>,
conn: &C,
) -> VecSection<F>
where
F: FiberOps,
C: DiscreteConnection<D>,
{
let nv = section.n_vertices();
let mut result = VecSection::<F>::zeros(nv);
for v in 0..nv {
let s_v = section.at(v);
for &(e, is_v0) in &self.vertex_edges[v] {
let neighbor = if is_v0 { self.edges[e][1] } else { self.edges[e][0] };
let s_neighbor = section.at(neighbor);
let w = self.cot_weights[e];
let transported = if is_v0 {
conn.transport_reverse::<F>(e, s_neighbor)
} else {
conn.transport_forward::<F>(e, s_neighbor)
};
F::accumulate_diff(result.at_mut(v), s_v, &transported, w);
}
if self.dual_areas[v] > 1e-30 {
F::scale_element(result.at_mut(v), 1.0 / self.dual_areas[v]);
}
}
result
}
pub fn apply_rotor<F, const D: usize, C>(
&self,
section: &impl Section<F>,
conn: &C,
) -> VecSection<F>
where
F: FiberOps,
C: RotorConnection<D>,
{
let nv = section.n_vertices();
let mut result = VecSection::<F>::zeros(nv);
for v in 0..nv {
let s_v = section.at(v);
for &(e, is_v0) in &self.vertex_edges[v] {
let neighbor = if is_v0 { self.edges[e][1] } else { self.edges[e][0] };
let s_neighbor = section.at(neighbor);
let w = self.cot_weights[e];
let transported = if is_v0 {
conn.transport_reverse_rotor::<F>(e, s_neighbor)
} else {
conn.transport_forward_rotor::<F>(e, s_neighbor)
};
F::accumulate_diff(result.at_mut(v), s_v, &transported, w);
}
if self.dual_areas[v] > 1e-30 {
F::scale_element(result.at_mut(v), 1.0 / self.dual_areas[v]);
}
}
result
}
}
#[cfg(test)]
mod rotor_conn_tests {
use super::*;
use crate::fiber::TangentFiber;
use crate::rotor::{Rotor2, Rotor3};
#[test]
fn rotor3_connection_forward_matches_matrix_connection() {
let edges = vec![[0usize, 1usize]];
let r = Rotor3::from_matrix(&[
0.0, -1.0, 0.0,
1.0, 0.0, 0.0,
0.0, 0.0, 1.0,
]);
let rconn = EdgeTransportRotor3D { edges: edges.clone(), rotors: vec![r] };
let mconn = EdgeTransport3D { edges, transports: vec![r.to_matrix()] };
let v = [1.0, 2.0, 3.0];
let fwd_rotor = rconn.transport_forward_rotor::<TangentFiber<3>>(0, &v);
let fwd_mat = mconn.transport_forward::<TangentFiber<3>>(0, &v);
for k in 0..3 { assert!((fwd_rotor[k]-fwd_mat[k]).abs() < 1e-12); }
let rev_rotor = rconn.transport_reverse_rotor::<TangentFiber<3>>(0, &v);
let rev_mat = mconn.transport_reverse::<TangentFiber<3>>(0, &v);
for k in 0..3 { assert!((rev_rotor[k]-rev_mat[k]).abs() < 1e-12); }
}
#[test]
fn rotor2_connection_builds() {
let conn = EdgeTransportRotor2D {
edges: vec![[0, 1], [1, 2]],
rotors: vec![Rotor2::from_angle(0.3), Rotor2::from_angle(-0.5)],
};
assert_eq!(conn.n_edges(), 2);
assert_eq!(conn.edge_vertices(1), [1, 2]);
}
#[test]
fn apply_rotor_matches_matrix_apply() {
use crate::fiber::{TangentFiber, VecSection, Section};
let edges = [[0usize, 1usize], [1, 2], [2, 0]];
let cot = [0.5, 0.5, 0.5];
let areas = [1.0, 1.0, 1.0];
let lap = CovLaplacian::new(3, &edges, &cot, &areas);
let rots = [Rotor3::from_matrix(&[
1.0,0.0,0.0, 0.0,1.0,0.0, 0.0,0.0,1.0]),
Rotor3::from_matrix(&[0.0,-1.0,0.0, 1.0,0.0,0.0, 0.0,0.0,1.0]),
Rotor3::from_matrix(&[1.0,0.0,0.0, 0.0,0.0,-1.0, 0.0,1.0,0.0])];
let rconn = EdgeTransportRotor3D { edges: edges.to_vec(), rotors: rots.to_vec() };
let mconn = EdgeTransport3D {
edges: edges.to_vec(),
transports: rots.iter().map(|r| r.to_matrix()).collect(),
};
let mut sec = VecSection::<TangentFiber<3>>::zeros(3);
*sec.at_mut(0) = [1.0, 0.0, 0.0];
*sec.at_mut(1) = [0.0, 1.0, 0.0];
*sec.at_mut(2) = [0.0, 0.0, 1.0];
let via_rotor = lap.apply_rotor::<TangentFiber<3>, 3, _>(&sec, &rconn);
let via_matrix = lap.apply::<TangentFiber<3>, 3, _>(&sec, &mconn);
for v in 0..3 {
for k in 0..3 {
assert!((via_rotor.at(v)[k] - via_matrix.at(v)[k]).abs() < 1e-12);
}
}
}
}