use super::FixedVector;
use super::FixedMatrix;
use super::compute_matrix::{ComputeMatrix, compute_lu_decompose};
use super::kernels;
use super::kernels::Orient;
use super::Scalar;
use crate::fixed_point::fixed_num::{Compute, Fixed, FixedNum};
use crate::fixed_point::width::{Storage, WideInt};
use crate::fixed_point::core_types::errors::OverflowDetected;
pub trait FiberBundle<T: Scalar> {
fn project(&self, total_point: &FixedVector<T>) -> FixedVector<T>;
fn base_dim(&self) -> usize;
fn fiber_dim(&self) -> usize;
fn total_dim(&self) -> usize {
self.base_dim() + self.fiber_dim()
}
fn lift(&self, base: &FixedVector<T>, fiber: &FixedVector<T>) -> FixedVector<T>;
fn local_trivialization(&self, total_point: &FixedVector<T>) -> (FixedVector<T>, FixedVector<T>);
}
pub trait BundleConnection<T: Scalar>: FiberBundle<T> {
fn horizontal_lift(
&self,
total_point: &FixedVector<T>,
base_tangent: &FixedVector<T>,
) -> Result<FixedVector<T>, OverflowDetected>;
fn vertical_component(
&self,
total_point: &FixedVector<T>,
total_tangent: &FixedVector<T>,
) -> FixedVector<T>;
fn horizontal_component(
&self,
total_point: &FixedVector<T>,
total_tangent: &FixedVector<T>,
) -> FixedVector<T> {
let vert = self.vertical_component(total_point, total_tangent);
T::sub_vectors(total_tangent, &vert)
}
fn parallel_transport_along(
&self,
base_path: &[FixedVector<T>],
initial_fiber: &FixedVector<T>,
) -> Result<FixedVector<T>, OverflowDetected>;
}
pub struct TrivialBundle {
pub base_dimension: usize,
pub fiber_dimension: usize,
}
impl<S: Storage, const F: u32> FiberBundle<Fixed<S, F>> for TrivialBundle
where
Fixed<S, F>: FixedNum<Storage = S>,
{
fn project(&self, total_point: &FixedVector<Fixed<S, F>>) -> FixedVector<Fixed<S, F>> {
let mut base = FixedVector::<Fixed<S, F>>::new(self.base_dimension);
for i in 0..self.base_dimension {
base[i] = total_point[i];
}
base
}
fn base_dim(&self) -> usize { self.base_dimension }
fn fiber_dim(&self) -> usize { self.fiber_dimension }
fn lift(&self, base: &FixedVector<Fixed<S, F>>, fiber: &FixedVector<Fixed<S, F>>) -> FixedVector<Fixed<S, F>> {
let mut total = FixedVector::<Fixed<S, F>>::new(self.base_dimension + self.fiber_dimension);
for i in 0..self.base_dimension {
total[i] = base[i];
}
for i in 0..self.fiber_dimension {
total[self.base_dimension + i] = fiber[i];
}
total
}
fn local_trivialization(&self, total_point: &FixedVector<Fixed<S, F>>) -> (FixedVector<Fixed<S, F>>, FixedVector<Fixed<S, F>>) {
let mut base = FixedVector::<Fixed<S, F>>::new(self.base_dimension);
let mut fiber = FixedVector::<Fixed<S, F>>::new(self.fiber_dimension);
for i in 0..self.base_dimension {
base[i] = total_point[i];
}
for i in 0..self.fiber_dimension {
fiber[i] = total_point[self.base_dimension + i];
}
(base, fiber)
}
}
impl<S: Storage, const F: u32> BundleConnection<Fixed<S, F>> for TrivialBundle
where
Fixed<S, F>: FixedNum<Storage = S>,
{
fn horizontal_lift(
&self,
_total_point: &FixedVector<Fixed<S, F>>,
base_tangent: &FixedVector<Fixed<S, F>>,
) -> Result<FixedVector<Fixed<S, F>>, OverflowDetected> {
let mut total_tangent = FixedVector::<Fixed<S, F>>::new(self.total_dim());
for i in 0..self.base_dimension {
total_tangent[i] = base_tangent[i];
}
Ok(total_tangent)
}
fn vertical_component(
&self,
_total_point: &FixedVector<Fixed<S, F>>,
total_tangent: &FixedVector<Fixed<S, F>>,
) -> FixedVector<Fixed<S, F>> {
let mut vert = FixedVector::<Fixed<S, F>>::new(self.total_dim());
for i in 0..self.fiber_dimension {
vert[self.base_dimension + i] = total_tangent[self.base_dimension + i];
}
vert
}
fn parallel_transport_along(
&self,
_base_path: &[FixedVector<Fixed<S, F>>],
initial_fiber: &FixedVector<Fixed<S, F>>,
) -> Result<FixedVector<Fixed<S, F>>, OverflowDetected> {
Ok(initial_fiber.clone())
}
}
pub struct VectorBundle<T> {
pub base_dim_val: usize,
pub fiber_dim_val: usize,
pub connection_coeffs: Option<Vec<T>>,
}
impl<S: Storage, const F: u32> VectorBundle<Fixed<S, F>>
where
Fixed<S, F>: FixedNum<Storage = S>,
{
pub fn flat(base_dim: usize, fiber_dim: usize) -> Self {
Self {
base_dim_val: base_dim,
fiber_dim_val: fiber_dim,
connection_coeffs: None,
}
}
pub fn with_connection(base_dim: usize, fiber_dim: usize, coeffs: Vec<Fixed<S, F>>) -> Self {
assert_eq!(coeffs.len(), fiber_dim * fiber_dim * base_dim,
"Connection coefficients must have size fiber_dim² × base_dim");
Self {
base_dim_val: base_dim,
fiber_dim_val: fiber_dim,
connection_coeffs: Some(coeffs),
}
}
fn get_coeff(&self, a: usize, b: usize, i: usize) -> Fixed<S, F> {
match &self.connection_coeffs {
None => Fixed::ZERO,
Some(coeffs) => {
let k = self.fiber_dim_val;
let n = self.base_dim_val;
coeffs[a * k * n + b * n + i]
}
}
}
fn coeff_row(&self, a: usize, b: usize) -> Vec<S> {
(0..self.base_dim_val).map(|i| self.get_coeff(a, b, i).raw()).collect()
}
}
impl<S: Storage, const F: u32> FiberBundle<Fixed<S, F>> for VectorBundle<Fixed<S, F>>
where
Fixed<S, F>: FixedNum<Storage = S>,
{
fn project(&self, total_point: &FixedVector<Fixed<S, F>>) -> FixedVector<Fixed<S, F>> {
let mut base = FixedVector::<Fixed<S, F>>::new(self.base_dim_val);
for i in 0..self.base_dim_val {
base[i] = total_point[i];
}
base
}
fn base_dim(&self) -> usize { self.base_dim_val }
fn fiber_dim(&self) -> usize { self.fiber_dim_val }
fn lift(&self, base: &FixedVector<Fixed<S, F>>, fiber: &FixedVector<Fixed<S, F>>) -> FixedVector<Fixed<S, F>> {
let mut total = FixedVector::<Fixed<S, F>>::new(self.base_dim_val + self.fiber_dim_val);
for i in 0..self.base_dim_val {
total[i] = base[i];
}
for i in 0..self.fiber_dim_val {
total[self.base_dim_val + i] = fiber[i];
}
total
}
fn local_trivialization(&self, total_point: &FixedVector<Fixed<S, F>>) -> (FixedVector<Fixed<S, F>>, FixedVector<Fixed<S, F>>) {
let mut base = FixedVector::<Fixed<S, F>>::new(self.base_dim_val);
let mut fiber = FixedVector::<Fixed<S, F>>::new(self.fiber_dim_val);
for i in 0..self.base_dim_val {
base[i] = total_point[i];
}
for i in 0..self.fiber_dim_val {
fiber[i] = total_point[self.base_dim_val + i];
}
(base, fiber)
}
}
impl<S: Storage, const F: u32> BundleConnection<Fixed<S, F>> for VectorBundle<Fixed<S, F>>
where
Fixed<S, F>: FixedNum<Storage = S>,
{
fn horizontal_lift(
&self,
total_point: &FixedVector<Fixed<S, F>>,
base_tangent: &FixedVector<Fixed<S, F>>,
) -> Result<FixedVector<Fixed<S, F>>, OverflowDetected> {
let k = self.fiber_dim_val;
let n = self.base_dim_val;
let fiber: Vec<Fixed<S, F>> = (0..k)
.map(|a| total_point[n + a])
.collect();
let mut total_tangent = FixedVector::<Fixed<S, F>>::new(n + k);
for i in 0..n {
total_tangent[i] = base_tangent[i];
}
let v_raw: Vec<S> = (0..n).map(|i| base_tangent[i].raw()).collect();
for a in 0..k {
let mut sum = <Orient<Fixed<S, F>> as WideInt>::zero();
for b in 0..k {
let a_dot_v = kernels::exact_dot::<Fixed<S, F>>(&self.coeff_row(a, b), &v_raw)?;
sum = sum.add_exact(S::widen_product(a_dot_v, S::widen(fiber[b].raw())))?;
}
total_tangent[n + a] = Fixed::from_raw(kernels::narrow_triple_nearest::<Fixed<S, F>>(-sum)?);
}
Ok(total_tangent)
}
fn vertical_component(
&self,
_total_point: &FixedVector<Fixed<S, F>>,
total_tangent: &FixedVector<Fixed<S, F>>,
) -> FixedVector<Fixed<S, F>> {
let n = self.base_dim_val;
let k = self.fiber_dim_val;
let mut vert = FixedVector::<Fixed<S, F>>::new(n + k);
for a in 0..k {
vert[n + a] = total_tangent[n + a];
}
vert
}
fn parallel_transport_along(
&self,
base_path: &[FixedVector<Fixed<S, F>>],
initial_fiber: &FixedVector<Fixed<S, F>>,
) -> Result<FixedVector<Fixed<S, F>>, OverflowDetected> {
if base_path.len() < 2 {
return Ok(initial_fiber.clone());
}
let k = self.fiber_dim_val;
let n = self.base_dim_val;
let up = |x: Fixed<S, F>| Fixed::<S, F>::upscale_to_compute(x.raw());
let mut fiber: Vec<Compute<Fixed<S, F>>> = (0..k).map(|a| up(initial_fiber[a])).collect();
for step in 0..base_path.len() - 1 {
let dx: Vec<Compute<Fixed<S, F>>> = (0..n)
.map(|i| Fixed::<S, F>::compute_subtract(up(base_path[step + 1][i]), up(base_path[step][i])))
.collect();
let mut new_fiber = Vec::with_capacity(k);
for a in 0..k {
let mut coeff_dx = Vec::with_capacity(k);
for b in 0..k {
let mut acc = Fixed::<S, F>::make_compute_int(0);
for i in 0..n {
acc = Fixed::<S, F>::compute_checked_add(acc, Fixed::<S, F>::compute_multiply(up(self.get_coeff(a, b, i)), dx[i]))?;
}
coeff_dx.push(acc);
}
new_fiber.push(kernels::exact_sub_dot_compute::<Fixed<S, F>>(fiber[a], &coeff_dx, &fiber)?);
}
fiber = new_fiber;
}
let mut out = FixedVector::<Fixed<S, F>>::new(k);
for a in 0..k { out[a] = Fixed::from_raw(Fixed::<S, F>::downscale_to_storage(fiber[a])?); }
Ok(out)
}
}
pub struct PrincipalBundle<T> {
pub base_dim_val: usize,
pub group_dim: usize, pub matrix_dim: usize, pub num_charts: usize,
pub transitions: Vec<FixedMatrix<T>>,
}
impl<S: Storage, const F: u32> PrincipalBundle<Fixed<S, F>>
where
Fixed<S, F>: FixedNum<Storage = S>,
{
pub fn trivial(base_dim: usize, group_dim: usize, matrix_dim: usize, num_charts: usize) -> Self {
let id = FixedMatrix::<Fixed<S, F>>::identity(matrix_dim);
let transitions = vec![id; num_charts * num_charts];
Self {
base_dim_val: base_dim,
group_dim,
matrix_dim,
num_charts,
transitions,
}
}
pub fn transition(&self, alpha: usize, beta: usize) -> &FixedMatrix<Fixed<S, F>> {
&self.transitions[alpha * self.num_charts + beta]
}
pub fn set_transition(
&mut self,
alpha: usize,
beta: usize,
g: FixedMatrix<Fixed<S, F>>,
) -> Result<(), OverflowDetected> {
let inv_c = compute_lu_decompose(&ComputeMatrix::<Fixed<S, F>>::from_fixed_matrix(&g))?.inverse()?;
let mut g_inv = FixedMatrix::<Fixed<S, F>>::new(g.rows(), g.cols());
for i in 0..g.rows() {
for j in 0..g.cols() {
g_inv.set(i, j, Fixed::from_raw(Fixed::<S, F>::downscale_to_storage(inv_c.get(i, j))?));
}
}
self.transitions[alpha * self.num_charts + beta] = g;
self.transitions[beta * self.num_charts + alpha] = g_inv;
Ok(())
}
pub fn verify_cocycle(&self, tol: Fixed<S, F>) -> (bool, Fixed<S, F>) {
let mut max_err = Fixed::ZERO;
let mut ok = true;
for alpha in 0..self.num_charts {
for beta in 0..self.num_charts {
for gamma in 0..self.num_charts {
let g_ab = self.transition(alpha, beta);
let g_bg = self.transition(beta, gamma);
let g_ag = self.transition(alpha, gamma);
let product = g_ab * g_bg;
for i in 0..self.matrix_dim {
for j in 0..self.matrix_dim {
let err = (product.get(i, j) - g_ag.get(i, j)).abs();
if err > max_err { max_err = err; }
if err > tol { ok = false; }
}
}
}
}
}
(ok, max_err)
}
}
impl<S: Storage, const F: u32> FiberBundle<Fixed<S, F>> for PrincipalBundle<Fixed<S, F>>
where
Fixed<S, F>: FixedNum<Storage = S>,
{
fn project(&self, total_point: &FixedVector<Fixed<S, F>>) -> FixedVector<Fixed<S, F>> {
let mut base = FixedVector::<Fixed<S, F>>::new(self.base_dim_val);
for i in 0..self.base_dim_val {
base[i] = total_point[i];
}
base
}
fn base_dim(&self) -> usize { self.base_dim_val }
fn fiber_dim(&self) -> usize { self.group_dim }
fn lift(&self, base: &FixedVector<Fixed<S, F>>, fiber: &FixedVector<Fixed<S, F>>) -> FixedVector<Fixed<S, F>> {
let mut total = FixedVector::<Fixed<S, F>>::new(self.base_dim_val + self.group_dim);
for i in 0..self.base_dim_val {
total[i] = base[i];
}
for i in 0..self.group_dim {
total[self.base_dim_val + i] = fiber[i];
}
total
}
fn local_trivialization(&self, total_point: &FixedVector<Fixed<S, F>>) -> (FixedVector<Fixed<S, F>>, FixedVector<Fixed<S, F>>) {
let mut base = FixedVector::<Fixed<S, F>>::new(self.base_dim_val);
let mut fiber = FixedVector::<Fixed<S, F>>::new(self.group_dim);
for i in 0..self.base_dim_val {
base[i] = total_point[i];
}
for i in 0..self.group_dim {
fiber[i] = total_point[self.base_dim_val + i];
}
(base, fiber)
}
}
pub fn apply_representation<S: Storage, const F: u32>(
group_element: &FixedMatrix<Fixed<S, F>>,
fiber_element: &FixedVector<Fixed<S, F>>,
) -> FixedVector<Fixed<S, F>>
where
Fixed<S, F>: FixedNum<Storage = S>,
{
group_element.mul_vector(fiber_element)
}
pub fn change_chart<S: Storage, const F: u32>(
bundle: &PrincipalBundle<Fixed<S, F>>,
alpha: usize,
beta: usize,
fiber_alpha: &FixedVector<Fixed<S, F>>,
) -> FixedVector<Fixed<S, F>>
where
Fixed<S, F>: FixedNum<Storage = S>,
{
let g = bundle.transition(alpha, beta);
g.mul_vector(fiber_alpha)
}
pub fn vector_bundle_curvature<S: Storage, const F: u32>(
bundle: &VectorBundle<Fixed<S, F>>,
_base_point: &FixedVector<Fixed<S, F>>,
) -> Result<super::tensor::Tensor<Fixed<S, F>>, OverflowDetected>
where
Fixed<S, F>: FixedNum<Storage = S>,
{
let k = bundle.fiber_dim_val;
let n = bundle.base_dim_val;
let _h = super::curvature::differentiation_step::<S, F>();
if bundle.connection_coeffs.is_none() {
return Ok(super::tensor::Tensor::<Fixed<S, F>>::new(&[k, k, n, n]));
}
let mut curv = super::tensor::Tensor::<Fixed<S, F>>::new(&[k, k, n, n]);
for a in 0..k {
for b in 0..k {
for i in 0..n {
for j in 0..n {
let d_i_a_bj = Fixed::ZERO; let d_j_a_bi = Fixed::ZERO;
let a_ci: Vec<S> = (0..k).map(|c| bundle.get_coeff(a, c, i).raw()).collect();
let c_bj: Vec<S> = (0..k).map(|c| bundle.get_coeff(c, b, j).raw()).collect();
let a_cj: Vec<S> = (0..k).map(|c| bundle.get_coeff(a, c, j).raw()).collect();
let c_bi: Vec<S> = (0..k).map(|c| bundle.get_coeff(c, b, i).raw()).collect();
let quad = Fixed::<S, F>::compute_subtract(kernels::exact_dot::<Fixed<S, F>>(&a_ci, &c_bj)?, kernels::exact_dot::<Fixed<S, F>>(&a_cj, &c_bi)?);
let quad = Fixed::from_raw(Fixed::<S, F>::downscale_to_storage(quad)?);
curv.set(&[a, b, i, j], d_i_a_bj - d_j_a_bi + quad);
}
}
}
}
Ok(curv)
}