use crate::rotor::Rotor;
pub trait Fiber: Clone + Send + Sync + 'static {
type Element: Clone + Send + Sync + Default;
const FIBER_DIM: usize;
fn zero() -> Self::Element;
fn transport_by(rotation: &[f64], d: usize, element: &Self::Element) -> Self::Element;
fn transport_by_rotor(rotor: &Rotor, element: &Self::Element) -> Self::Element {
match rotor {
Rotor::R2(r) => Self::transport_by(&r.to_matrix(), 2, element),
Rotor::R3(r) => Self::transport_by(&r.to_matrix(), 3, element),
}
}
}
pub trait FiberOps: Fiber {
fn accumulate_diff(target: &mut Self::Element, a: &Self::Element, b: &Self::Element, scale: f64);
fn scale_element(target: &mut Self::Element, scale: f64);
}
pub trait Section<F: Fiber> {
fn n_vertices(&self) -> usize;
fn at(&self, v: usize) -> &F::Element;
fn at_mut(&mut self, v: usize) -> &mut F::Element;
}
#[cfg(feature = "alloc")]
use alloc::vec::Vec;
#[cfg(feature = "alloc")]
#[derive(Clone, Debug)]
pub struct VecSection<F: Fiber> {
data: Vec<F::Element>,
_marker: core::marker::PhantomData<F>,
}
#[cfg(feature = "alloc")]
impl<F: Fiber> VecSection<F> {
pub fn zeros(n: usize) -> Self {
Self {
data: (0..n).map(|_| F::zero()).collect(),
_marker: core::marker::PhantomData,
}
}
pub fn from_vec(data: Vec<F::Element>) -> Self {
Self { data, _marker: core::marker::PhantomData }
}
pub fn len(&self) -> usize {
self.data.len()
}
pub fn is_empty(&self) -> bool {
self.data.is_empty()
}
}
#[cfg(feature = "alloc")]
impl<F: Fiber> Section<F> for VecSection<F> {
fn n_vertices(&self) -> usize { self.data.len() }
fn at(&self, v: usize) -> &F::Element { &self.data[v] }
fn at_mut(&mut self, v: usize) -> &mut F::Element { &mut self.data[v] }
}
#[derive(Clone, Debug)]
pub struct U1Spin2;
impl Fiber for U1Spin2 {
type Element = [f64; 2];
const FIBER_DIM: usize = 2;
fn zero() -> [f64; 2] { [0.0, 0.0] }
fn transport_by(rotation: &[f64], d: usize, element: &[f64; 2]) -> [f64; 2] {
debug_assert_eq!(d, 2, "U1Spin2 requires d=2");
debug_assert!(rotation.len() >= 4);
let cos_t = rotation[0];
let sin_t = rotation[2]; let cos_2t = cos_t * cos_t - sin_t * sin_t;
let sin_2t = 2.0 * sin_t * cos_t;
let q1 = element[0];
let q2 = element[1];
[cos_2t * q1 - sin_2t * q2, sin_2t * q1 + cos_2t * q2]
}
fn transport_by_rotor(rotor: &Rotor, element: &[f64; 2]) -> [f64; 2] {
let r = match rotor {
Rotor::R2(r) => r,
Rotor::R3(_) => {
debug_assert!(false, "U1Spin2 requires a 2D rotor");
return *element;
}
};
let cos_t = r.c * r.c - r.s * r.s;
let sin_t = 2.0 * r.s * r.c;
let cos_2t = cos_t * cos_t - sin_t * sin_t;
let sin_2t = 2.0 * sin_t * cos_t;
let (q1, q2) = (element[0], element[1]);
[cos_2t * q1 - sin_2t * q2, sin_2t * q1 + cos_2t * q2]
}
}
impl FiberOps for U1Spin2 {
fn accumulate_diff(target: &mut [f64; 2], a: &[f64; 2], b: &[f64; 2], scale: f64) {
target[0] += scale * (a[0] - b[0]);
target[1] += scale * (a[1] - b[1]);
}
fn scale_element(target: &mut [f64; 2], scale: f64) {
target[0] *= scale;
target[1] *= scale;
}
}
#[derive(Clone, Debug)]
pub struct TangentFiber<const D: usize>;
impl<const D: usize> Fiber for TangentFiber<D>
where
[f64; D]: Default,
{
type Element = [f64; D];
const FIBER_DIM: usize = D;
fn zero() -> [f64; D] { [0.0; D] }
fn transport_by(rotation: &[f64], d: usize, element: &[f64; D]) -> [f64; D] {
debug_assert_eq!(d, D);
debug_assert!(rotation.len() >= D * D);
let mut result = [0.0; D];
for i in 0..D {
for j in 0..D {
result[i] += rotation[i * D + j] * element[j];
}
}
result
}
}
impl<const D: usize> FiberOps for TangentFiber<D>
where
[f64; D]: Default,
{
fn accumulate_diff(target: &mut [f64; D], a: &[f64; D], b: &[f64; D], scale: f64) {
for (t, (ai, bi)) in target.iter_mut().zip(a.iter().zip(b.iter())) {
*t += scale * (ai - bi);
}
}
fn scale_element(target: &mut [f64; D], scale: f64) {
for t in target.iter_mut() {
*t *= scale;
}
}
}
#[derive(Clone, Debug)]
pub struct NematicFiber3D;
impl Fiber for NematicFiber3D {
type Element = [f64; 5];
const FIBER_DIM: usize = 5;
fn zero() -> [f64; 5] { [0.0; 5] }
fn transport_by(rotation: &[f64], d: usize, element: &[f64; 5]) -> [f64; 5] {
debug_assert_eq!(d, 3, "NematicFiber3D requires d=3");
debug_assert!(rotation.len() >= 9);
let (q11, q12, q13, q22, q23) = (element[0], element[1], element[2], element[3], element[4]);
let q33 = -q11 - q22;
let q = [[q11, q12, q13], [q12, q22, q23], [q13, q23, q33]];
let r = |i: usize, j: usize| rotation[i * 3 + j];
let mut qp = [[0.0_f64; 3]; 3];
for (i, qp_row) in qp.iter_mut().enumerate() {
for (j, qp_ij) in qp_row.iter_mut().enumerate() {
let mut sum = 0.0;
for (a, q_row) in q.iter().enumerate() {
for (b, &q_ab) in q_row.iter().enumerate() {
sum += r(i, a) * q_ab * r(j, b);
}
}
*qp_ij = sum;
}
}
[qp[0][0], qp[0][1], qp[0][2], qp[1][1], qp[1][2]]
}
}
impl FiberOps for NematicFiber3D {
fn accumulate_diff(target: &mut [f64; 5], a: &[f64; 5], b: &[f64; 5], scale: f64) {
for (t, (ai, bi)) in target.iter_mut().zip(a.iter().zip(b.iter())) {
*t += scale * (ai - bi);
}
}
fn scale_element(target: &mut [f64; 5], scale: f64) {
for t in target.iter_mut() {
*t *= scale;
}
}
}
#[cfg(test)]
mod rotor_transport_tests {
use super::*;
use crate::rotor::{Rotor, Rotor2, Rotor3};
fn r3(angle: f64) -> Rotor3 {
let u = {
let a = [0.3_f64, -0.7, 0.5];
let n = (a[0]*a[0] + a[1]*a[1] + a[2]*a[2]).sqrt();
[a[0]/n, a[1]/n, a[2]/n]
};
let (c, s) = (angle.cos(), angle.sin());
let t = 1.0 - c;
let (ux, uy, uz) = (u[0], u[1], u[2]);
let m = [
c + ux*ux*t, ux*uy*t - uz*s, ux*uz*t + uy*s,
uy*ux*t + uz*s, c + uy*uy*t, uy*uz*t - ux*s,
uz*ux*t - uy*s, uz*uy*t + ux*s, c + uz*uz*t,
];
Rotor3::from_matrix(&m)
}
#[test]
fn u1spin2_rotor_matches_matrix() {
let rot = Rotor2::from_angle(0.83);
let elem = [0.4, -0.9];
let via_rotor = U1Spin2::transport_by_rotor(&Rotor::R2(rot), &elem);
let via_matrix = U1Spin2::transport_by(&rot.to_matrix(), 2, &elem);
assert!((via_rotor[0]-via_matrix[0]).abs() < 1e-12);
assert!((via_rotor[1]-via_matrix[1]).abs() < 1e-12);
}
#[test]
fn tangent3_rotor_matches_matrix() {
let rot = r3(1.1);
let elem = [1.0, -2.0, 0.5];
let via_rotor = TangentFiber::<3>::transport_by_rotor(&Rotor::R3(rot), &elem);
let via_matrix = TangentFiber::<3>::transport_by(&rot.to_matrix(), 3, &elem);
for k in 0..3 { assert!((via_rotor[k]-via_matrix[k]).abs() < 1e-12); }
}
#[test]
fn nematic3d_rotor_matches_matrix() {
let rot = r3(-0.6);
let elem = [0.2, -0.1, 0.05, 0.3, -0.15];
let via_rotor = NematicFiber3D::transport_by_rotor(&Rotor::R3(rot), &elem);
let via_matrix = NematicFiber3D::transport_by(&rot.to_matrix(), 3, &elem);
for k in 0..5 { assert!((via_rotor[k]-via_matrix[k]).abs() < 1e-12); }
}
}