use super::FixedPoint;
use super::FixedVector;
use super::FixedMatrix;
use super::manifold::Manifold;
use super::matrix_functions::{matrix_exp, matrix_log, matrix_exp_compute, matrix_log_compute};
use super::compute_matrix::{ComputeMatrix, compute_lu_decompose};
use super::linalg::{upscale_to_compute, sincos_at_compute_tier, ComputeStorage, downscale_to_storage, exact_dot};
use super::interval::exact_product;
use super::wide_acc::{acc, divide_to_compute_nearest, narrow_triple_nearest, widen_product, widen_storage, Wide};
use crate::fixed_point::core_types::errors::OverflowDetected;
use crate::fixed_point::universal::fasc::stack_evaluator::BinaryStorage;
use crate::fixed_point::universal::fasc::stack_evaluator::compute::{
compute_add, compute_subtract, compute_multiply, compute_divide, compute_halve, compute_negate,
compute_is_zero, compute_is_negative, compute_mul_div_int, make_compute_int, sqrt_at_compute_tier,
};
#[inline]
fn up(x: FixedPoint) -> ComputeStorage { upscale_to_compute(x.raw()) }
#[inline]
fn down(c: ComputeStorage) -> Result<FixedPoint, OverflowDetected> {
Ok(FixedPoint::from_raw(downscale_to_storage(c)?))
}
fn down_vector(v: &[ComputeStorage]) -> Result<FixedVector, OverflowDetected> {
let mut out = FixedVector::new(v.len());
for (i, c) in v.iter().enumerate() { out[i] = down(*c)?; }
Ok(out)
}
fn down_matrix(m: &ComputeMatrix) -> Result<FixedMatrix, OverflowDetected> {
let (rows, cols) = (m.rows(), m.cols());
let mut out = FixedMatrix::new(rows, cols);
for r in 0..rows {
for c in 0..cols { out.set(r, c, down(m.get(r, c))?); }
}
Ok(out)
}
fn norm_to_storage(v: &[ComputeStorage]) -> Result<FixedPoint, OverflowDetected> {
let mut sum = make_compute_int(0);
for c in v { sum = compute_add(sum, compute_multiply(*c, *c)); }
down(sqrt_at_compute_tier(sum))
}
fn atan2_compute(y: ComputeStorage, x: ComputeStorage) -> ComputeStorage {
use crate::fixed_point::domains::binary_fixed::transcendental as t;
#[cfg(table_format = "q16_16")]
{ t::atan2_compute_tier_i64(y, x) }
#[cfg(table_format = "q32_32")]
{ t::atan2_binary_i128(y, x) }
#[cfg(table_format = "q64_64")]
{ t::atan2_compute_tier_i256(y, x) }
#[cfg(table_format = "q128_128")]
{ t::atan2_compute_tier_i512(y, x) }
#[cfg(table_format = "q256_256")]
{ t::atan2_compute_tier_i1024(y, x) }
}
fn series(t: ComputeStorage, coeffs: &[(i64, i64)]) -> Result<ComputeStorage, OverflowDetected> {
let mut power = make_compute_int(1);
let mut sum = make_compute_int(0);
for (k, (num, den)) in coeffs.iter().enumerate() {
if k > 0 { power = compute_multiply(power, t); }
sum = compute_add(sum, compute_mul_div_int(power, *num, *den)?);
}
Ok(sum)
}
fn triple_to_storage(terms: &[(ComputeStorage, BinaryStorage)], negate: bool) -> Result<FixedPoint, OverflowDetected> {
let mut sum = <acc::Orient as Wide>::zero();
for (c, s) in terms { sum = sum.add_exact(widen_product(*c, widen_storage(*s)))?; }
Ok(FixedPoint::from_raw(narrow_triple_nearest(if negate { -sum } else { sum })?))
}
fn cross_exact(a: &FixedVector, b: &FixedVector) -> [ComputeStorage; 3] {
let p = |i: usize, j: usize| exact_product(a[i].raw(), b[j].raw());
[
compute_subtract(p(1, 2), p(2, 1)),
compute_subtract(p(2, 0), p(0, 2)),
compute_subtract(p(0, 1), p(1, 0)),
]
}
fn commutator_exact(a: &FixedMatrix, b: &FixedMatrix) -> ComputeMatrix {
let n = a.rows();
ComputeMatrix::from_fn(n, n, |i, j| {
let (mut ab, mut ba) = (make_compute_int(0), make_compute_int(0));
for k in 0..n {
ab = compute_add(ab, exact_product(a.get(i, k).raw(), b.get(k, j).raw()));
ba = compute_add(ba, exact_product(b.get(i, k).raw(), a.get(k, j).raw()));
}
compute_subtract(ab, ba)
})
}
fn conjugate_exact(g: &FixedMatrix, x: &FixedMatrix, h: &FixedMatrix) -> Result<FixedMatrix, OverflowDetected> {
let n = g.rows();
let mut out = FixedMatrix::new(n, n);
for i in 0..n {
for j in 0..n {
let mut terms = Vec::with_capacity(n * n);
for k in 0..n {
for l in 0..n {
terms.push((exact_product(g.get(i, k).raw(), x.get(k, l).raw()), h.get(j, l).raw()));
}
}
out.set(i, j, triple_to_storage(&terms, false)?);
}
}
Ok(out)
}
fn inverse_compute(g: &ComputeMatrix) -> Result<ComputeMatrix, OverflowDetected> {
compute_lu_decompose(g)?.inverse()
}
fn conjugate_by_inverse(g: &FixedMatrix, xi_hat: &FixedMatrix) -> Result<ComputeMatrix, OverflowDetected> {
let g_c = ComputeMatrix::from_fixed_matrix(g);
let g_inv = inverse_compute(&g_c)?;
Ok(g_c.mat_mul(&ComputeMatrix::from_fixed_matrix(xi_hat)).mat_mul(&g_inv))
}
fn skew_half(a: &ComputeMatrix) -> ComputeMatrix {
a.sub(&a.transpose()).halve()
}
pub trait LieGroup: Manifold {
fn algebra_dim(&self) -> usize;
fn matrix_dim(&self) -> usize;
fn identity_element(&self) -> FixedMatrix;
fn compose(&self, g1: &FixedMatrix, g2: &FixedMatrix) -> FixedMatrix;
fn group_inverse(&self, g: &FixedMatrix) -> Result<FixedMatrix, OverflowDetected>;
fn lie_exp(&self, xi: &FixedVector) -> Result<FixedMatrix, OverflowDetected>;
fn lie_log(&self, g: &FixedMatrix) -> Result<FixedVector, OverflowDetected>;
fn hat(&self, xi: &FixedVector) -> FixedMatrix;
fn vee(&self, xi_hat: &FixedMatrix) -> FixedVector;
fn adjoint(&self, g: &FixedMatrix, xi: &FixedVector) -> Result<FixedVector, OverflowDetected>;
fn bracket(&self, xi: &FixedVector, eta: &FixedVector) -> FixedVector;
fn act(&self, g: &FixedMatrix, point: &FixedVector) -> FixedVector;
}
pub struct SO3;
#[cfg(any(table_format = "q16_16", table_format = "q32_32"))]
const RODRIGUES_THRESH: &str = "0.001";
#[cfg(table_format = "q64_64")]
const RODRIGUES_THRESH: &str = "0.00001";
#[cfg(table_format = "q128_128")]
const RODRIGUES_THRESH: &str = "0.0000000001";
#[cfg(table_format = "q256_256")]
const RODRIGUES_THRESH: &str = "0.00000000000000000001";
fn rodrigues_threshold() -> FixedPoint {
let t = FixedPoint::from_str(RODRIGUES_THRESH);
#[cfg(table_format = "q16_16")]
{ if t.is_zero() { FixedPoint::from_raw(1) } else { t } }
#[cfg(not(table_format = "q16_16"))]
{ t }
}
fn se3_series_threshold() -> ComputeStorage {
let mut t = make_compute_int(4);
for _ in 0..crate::fixed_point::frac_config::FRAC_BITS / 2 { t = compute_halve(t); }
t
}
fn angle_compute(omega: &[ComputeStorage]) -> (ComputeStorage, ComputeStorage) {
let mut theta_sq = make_compute_int(0);
for c in omega { theta_sq = compute_add(theta_sq, compute_multiply(*c, *c)); }
(theta_sq, sqrt_at_compute_tier(theta_sq))
}
fn hat_compute(w: &[ComputeStorage]) -> ComputeMatrix {
let z = make_compute_int(0);
let m = [z, compute_negate(w[2]), w[1], w[2], z, compute_negate(w[0]), compute_negate(w[1]), w[0], z];
ComputeMatrix::from_fn(3, 3, |r, c| m[3 * r + c])
}
fn rodrigues_exp_compute(omega: &[ComputeStorage]) -> Result<ComputeMatrix, OverflowDetected> {
let (theta_sq, theta) = angle_compute(omega);
let (sinc, half_cosc) = if theta < upscale_to_compute(rodrigues_threshold().raw()) {
(
series(theta_sq, &[(1, 1), (-1, 6), (1, 120), (-1, 5040)])?,
series(theta_sq, &[(1, 2), (-1, 24), (1, 720), (-1, 40320)])?,
)
} else {
let (sin_c, cos_c) = sincos_at_compute_tier(theta);
(
compute_divide(sin_c, theta)?,
compute_divide(compute_subtract(make_compute_int(1), cos_c), theta_sq)?,
)
};
let k = hat_compute(omega);
let k2 = k.mat_mul(&k);
Ok(ComputeMatrix::identity(3).add(&k.scalar_mul(sinc)).add(&k2.scalar_mul(half_cosc)))
}
fn rodrigues_log_compute(r: &ComputeMatrix) -> Result<([ComputeStorage; 3], ComputeStorage), OverflowDetected> {
let one = make_compute_int(1);
let zero = make_compute_int(0);
let d = [
compute_subtract(r.get(2, 1), r.get(1, 2)),
compute_subtract(r.get(0, 2), r.get(2, 0)),
compute_subtract(r.get(1, 0), r.get(0, 1)),
];
let tr_minus_one = compute_subtract(compute_add(compute_add(r.get(0, 0), r.get(1, 1)), r.get(2, 2)), one);
let (_, d_norm) = angle_compute(&d);
let d_zero = compute_is_zero(&d_norm);
if !compute_is_negative(&tr_minus_one) {
if d_zero {
return Ok(([zero, zero, zero], zero));
}
let theta = atan2_compute(d_norm, tr_minus_one);
let mut omega = [zero; 3];
for i in 0..3 {
omega[i] = divide_to_compute_nearest(widen_product(theta, d[i]), d_norm)?;
}
return Ok((omega, theta));
}
let theta = if d_zero { atan2_compute(zero, tr_minus_one) } else { atan2_compute(d_norm, tr_minus_one) };
let cos_theta = compute_halve(tr_minus_one);
let one_minus_cos = compute_subtract(one, cos_theta);
let mut i = 0;
for k in 1..3 {
if r.get(k, k) > r.get(i, i) { i = k; }
}
let ui_sq = compute_divide(compute_subtract(r.get(i, i), cos_theta), one_minus_cos)?;
if compute_is_negative(&ui_sq) || compute_is_zero(&ui_sq) {
return Err(OverflowDetected::DomainError);
}
let ui = sqrt_at_compute_tier(ui_sq);
let den = compute_multiply(compute_add(one_minus_cos, one_minus_cos), ui);
let mut u = [zero; 3];
for k in 0..3 {
u[k] = if k == i { ui } else { compute_divide(compute_add(r.get(i, k), r.get(k, i)), den)? };
}
let mut dot = zero;
for k in 0..3 { dot = compute_add(dot, compute_multiply(u[k], d[k])); }
let sign_flip = compute_is_negative(&dot);
let mut omega = [zero; 3];
for k in 0..3 {
let w = compute_multiply(theta, u[k]);
omega[k] = if sign_flip { compute_negate(w) } else { w };
}
Ok((omega, theta))
}
fn se3_exp_compute(xi: &[ComputeStorage]) -> Result<ComputeMatrix, OverflowDetected> {
let omega = &xi[0..3];
let r = rodrigues_exp_compute(omega)?;
let (theta_sq, theta) = angle_compute(omega);
let (c1, c2) = if theta < se3_series_threshold() {
(
series(theta_sq, &[(1, 2), (-1, 24), (1, 720), (-1, 40320)])?,
series(theta_sq, &[(1, 6), (-1, 120), (1, 5040), (-1, 362880)])?,
)
} else {
let (sin_c, cos_c) = sincos_at_compute_tier(theta);
(
compute_divide(compute_subtract(make_compute_int(1), cos_c), theta_sq)?,
compute_divide(compute_divide(compute_subtract(theta, sin_c), theta_sq)?, theta)?,
)
};
let k = hat_compute(omega);
let k2 = k.mat_mul(&k);
let v_mat = ComputeMatrix::identity(3).add(&k.scalar_mul(c1)).add(&k2.scalar_mul(c2));
let t = v_mat.mul_vector_compute(&xi[3..6]);
Ok(ComputeMatrix::from_fn(4, 4, |i, j| {
if i < 3 && j < 3 { r.get(i, j) }
else if i < 3 { t[i] }
else if j == 3 { make_compute_int(1) }
else { make_compute_int(0) }
}))
}
fn se3_log_compute(g: &ComputeMatrix) -> Result<[ComputeStorage; 6], OverflowDetected> {
let r = ComputeMatrix::from_fn(3, 3, |i, j| g.get(i, j));
let t = [g.get(0, 3), g.get(1, 3), g.get(2, 3)];
let (omega, theta) = rodrigues_log_compute(&r)?;
let theta_sq = compute_multiply(theta, theta);
let c2 = if theta < se3_series_threshold() {
series(theta_sq, &[(1, 12), (1, 720), (1, 30240), (1, 1209600)])?
} else {
let one = make_compute_int(1);
let (sin_c, cos_c) = sincos_at_compute_tier(theta);
let one_minus_cos = compute_subtract(one, cos_c);
let alpha = compute_divide(compute_multiply(theta, sin_c), compute_add(one_minus_cos, one_minus_cos))?;
compute_divide(compute_subtract(one, alpha), theta_sq)?
};
let k = hat_compute(&omega);
let k2 = k.mat_mul(&k);
let v_inv = ComputeMatrix::identity(3).sub(&k.halve()).add(&k2.scalar_mul(c2));
let v = v_inv.mul_vector_compute(&t);
Ok([omega[0], omega[1], omega[2], v[0], v[1], v[2]])
}
fn se3_inverse_compute(g: &ComputeMatrix) -> ComputeMatrix {
let mut out = ComputeMatrix::identity(4);
for i in 0..3 {
let mut acc = make_compute_int(0);
for k in 0..3 {
out.set(i, k, g.get(k, i));
acc = compute_add(acc, compute_multiply(g.get(k, i), g.get(k, 3)));
}
out.set(i, 3, compute_negate(acc));
}
out
}
fn up_all(v: &FixedVector) -> Vec<ComputeStorage> { (0..v.len()).map(|i| up(v[i])).collect() }
fn so3_log_map_compute(base: &FixedVector, target: &FixedVector) -> Result<[ComputeStorage; 3], OverflowDetected> {
let g_base = rodrigues_exp_compute(&up_all(base))?;
let g_target = rodrigues_exp_compute(&up_all(target))?;
Ok(rodrigues_log_compute(&g_base.transpose().mat_mul(&g_target))?.0)
}
fn se3_log_map_compute(base: &FixedVector, target: &FixedVector) -> Result<[ComputeStorage; 6], OverflowDetected> {
let g_base = se3_exp_compute(&up_all(base))?;
let g_target = se3_exp_compute(&up_all(target))?;
se3_log_compute(&se3_inverse_compute(&g_base).mat_mul(&g_target))
}
impl SO3 {
pub fn hat_so3(omega: &FixedVector) -> FixedMatrix {
let (wx, wy, wz) = (omega[0], omega[1], omega[2]);
let z = FixedPoint::ZERO;
FixedMatrix::from_slice(3, 3, &[z, -wz, wy, wz, z, -wx, -wy, wx, z])
}
pub fn vee_so3(m: &FixedMatrix) -> FixedVector {
FixedVector::from_slice(&[m.get(2, 1), m.get(0, 2), m.get(1, 0)])
}
pub fn rodrigues_exp(omega: &FixedVector) -> Result<FixedMatrix, OverflowDetected> {
assert_eq!(omega.len(), 3);
down_matrix(&rodrigues_exp_compute(&up_all(omega))?)
}
pub fn rodrigues_log(r: &FixedMatrix) -> Result<FixedVector, OverflowDetected> {
assert!(r.rows() == 3 && r.cols() == 3, "rodrigues_log: R must be 3x3");
down_vector(&rodrigues_log_compute(&ComputeMatrix::from_fixed_matrix(r))?.0)
}
}
impl Manifold for SO3 {
fn dimension(&self) -> usize { 3 }
fn inner_product(&self, _base: &FixedVector, u: &FixedVector, v: &FixedVector) -> FixedPoint {
u.dot_precise(v)
}
fn exp_map(&self, base: &FixedVector, tangent: &FixedVector) -> Result<FixedVector, OverflowDetected> {
let g_base = rodrigues_exp_compute(&up_all(base))?;
let g_tangent = rodrigues_exp_compute(&up_all(tangent))?;
down_vector(&rodrigues_log_compute(&g_base.mat_mul(&g_tangent))?.0)
}
fn log_map(&self, base: &FixedVector, target: &FixedVector) -> Result<FixedVector, OverflowDetected> {
down_vector(&so3_log_map_compute(base, target)?)
}
fn distance(&self, p: &FixedVector, q: &FixedVector) -> Result<FixedPoint, OverflowDetected> {
norm_to_storage(&so3_log_map_compute(p, q)?)
}
fn parallel_transport(&self, base: &FixedVector, target: &FixedVector, tangent: &FixedVector) -> Result<FixedVector, OverflowDetected> {
let diff = so3_log_map_compute(base, target)?;
let half = [compute_halve(diff[0]), compute_halve(diff[1]), compute_halve(diff[2])];
let r_half = rodrigues_exp_compute(&half)?;
down_vector(&r_half.mul_vector_compute(&up_all(tangent)))
}
}
impl LieGroup for SO3 {
fn algebra_dim(&self) -> usize { 3 }
fn matrix_dim(&self) -> usize { 3 }
fn identity_element(&self) -> FixedMatrix { FixedMatrix::identity(3) }
fn compose(&self, g1: &FixedMatrix, g2: &FixedMatrix) -> FixedMatrix { g1 * g2 }
fn group_inverse(&self, g: &FixedMatrix) -> Result<FixedMatrix, OverflowDetected> { Ok(g.transpose()) }
fn lie_exp(&self, xi: &FixedVector) -> Result<FixedMatrix, OverflowDetected> { SO3::rodrigues_exp(xi) }
fn lie_log(&self, g: &FixedMatrix) -> Result<FixedVector, OverflowDetected> { SO3::rodrigues_log(g) }
fn hat(&self, xi: &FixedVector) -> FixedMatrix { SO3::hat_so3(xi) }
fn vee(&self, xi_hat: &FixedMatrix) -> FixedVector { SO3::vee_so3(xi_hat) }
fn adjoint(&self, g: &FixedMatrix, xi: &FixedVector) -> Result<FixedVector, OverflowDetected> {
Ok(g.mul_vector(xi)) }
fn bracket(&self, xi: &FixedVector, eta: &FixedVector) -> FixedVector {
let c = cross_exact(xi, eta);
down_vector(&c).expect("SO3::bracket: result exceeds storage")
}
fn act(&self, g: &FixedMatrix, point: &FixedVector) -> FixedVector {
g.mul_vector(point)
}
}
pub struct SE3;
impl SE3 {
pub fn hat_se3(xi: &FixedVector) -> FixedMatrix {
assert_eq!(xi.len(), 6);
let z = FixedPoint::ZERO;
FixedMatrix::from_slice(4, 4, &[
z, -xi[2], xi[1], xi[3],
xi[2], z, -xi[0], xi[4],
-xi[1], xi[0], z, xi[5],
z, z, z, z,
])
}
pub fn vee_se3(m: &FixedMatrix) -> FixedVector {
FixedVector::from_slice(&[m.get(2, 1), m.get(0, 2), m.get(1, 0), m.get(0, 3), m.get(1, 3), m.get(2, 3)])
}
pub fn extract_rotation(g: &FixedMatrix) -> FixedMatrix {
g.submatrix(0, 0, 3, 3)
}
pub fn extract_translation(g: &FixedMatrix) -> FixedVector {
FixedVector::from_slice(&[g.get(0, 3), g.get(1, 3), g.get(2, 3)])
}
pub fn from_rt(r: &FixedMatrix, t: &FixedVector) -> FixedMatrix {
let mut m = FixedMatrix::new(4, 4);
m.set_submatrix(0, 0, r);
m.set(0, 3, t[0]); m.set(1, 3, t[1]); m.set(2, 3, t[2]);
m.set(3, 3, FixedPoint::one());
m
}
pub fn se3_exp(xi: &FixedVector) -> Result<FixedMatrix, OverflowDetected> {
assert_eq!(xi.len(), 6);
down_matrix(&se3_exp_compute(&up_all(xi))?)
}
pub fn se3_log(g: &FixedMatrix) -> Result<FixedVector, OverflowDetected> {
down_vector(&se3_log_compute(&ComputeMatrix::from_fixed_matrix(g))?)
}
}
impl Manifold for SE3 {
fn dimension(&self) -> usize { 6 }
fn inner_product(&self, _base: &FixedVector, u: &FixedVector, v: &FixedVector) -> FixedPoint {
u.dot_precise(v)
}
fn exp_map(&self, base: &FixedVector, tangent: &FixedVector) -> Result<FixedVector, OverflowDetected> {
let g_base = se3_exp_compute(&up_all(base))?;
let g_tangent = se3_exp_compute(&up_all(tangent))?;
down_vector(&se3_log_compute(&g_base.mat_mul(&g_tangent))?)
}
fn log_map(&self, base: &FixedVector, target: &FixedVector) -> Result<FixedVector, OverflowDetected> {
down_vector(&se3_log_map_compute(base, target)?)
}
fn distance(&self, p: &FixedVector, q: &FixedVector) -> Result<FixedPoint, OverflowDetected> {
norm_to_storage(&se3_log_map_compute(p, q)?)
}
fn parallel_transport(&self, _base: &FixedVector, _target: &FixedVector, tangent: &FixedVector) -> Result<FixedVector, OverflowDetected> {
Ok(tangent.clone())
}
}
impl LieGroup for SE3 {
fn algebra_dim(&self) -> usize { 6 }
fn matrix_dim(&self) -> usize { 4 }
fn identity_element(&self) -> FixedMatrix { FixedMatrix::identity(4) }
fn compose(&self, g1: &FixedMatrix, g2: &FixedMatrix) -> FixedMatrix { g1 * g2 }
fn group_inverse(&self, g: &FixedMatrix) -> Result<FixedMatrix, OverflowDetected> {
let r = SE3::extract_rotation(g);
let t = SE3::extract_translation(g);
let rt = r.transpose();
let t_raw: Vec<BinaryStorage> = (0..3).map(|i| t[i].raw()).collect();
let mut neg_rt_t = FixedVector::new(3);
for i in 0..3 {
let col: Vec<BinaryStorage> = (0..3).map(|k| r.get(k, i).raw()).collect();
neg_rt_t[i] = down(compute_negate(exact_dot(&col, &t_raw)?))?;
}
Ok(SE3::from_rt(&rt, &neg_rt_t))
}
fn lie_exp(&self, xi: &FixedVector) -> Result<FixedMatrix, OverflowDetected> { SE3::se3_exp(xi) }
fn lie_log(&self, g: &FixedMatrix) -> Result<FixedVector, OverflowDetected> { SE3::se3_log(g) }
fn hat(&self, xi: &FixedVector) -> FixedMatrix { SE3::hat_se3(xi) }
fn vee(&self, xi_hat: &FixedMatrix) -> FixedVector { SE3::vee_se3(xi_hat) }
fn adjoint(&self, g: &FixedMatrix, xi: &FixedVector) -> Result<FixedVector, OverflowDetected> {
let r = SE3::extract_rotation(g);
let t = SE3::extract_translation(g);
let omega: Vec<BinaryStorage> = (0..3).map(|i| xi[i].raw()).collect();
let v: Vec<BinaryStorage> = (0..3).map(|i| xi[3 + i].raw()).collect();
let mut r_omega = [make_compute_int(0); 3];
let mut r_v = [make_compute_int(0); 3];
for i in 0..3 {
let row: Vec<BinaryStorage> = (0..3).map(|k| r.get(i, k).raw()).collect();
r_omega[i] = exact_dot(&row, &omega)?;
r_v[i] = exact_dot(&row, &v)?;
}
let one = FixedPoint::one().raw();
let mut out = FixedVector::new(6);
for i in 0..3 {
out[i] = down(r_omega[i])?;
let (j, k) = ((i + 1) % 3, (i + 2) % 3);
out[3 + i] = triple_to_storage(&[
(r_v[i], one),
(r_omega[k], t[j].raw()),
(compute_negate(r_omega[j]), t[k].raw()),
], false)?;
}
Ok(out)
}
fn bracket(&self, xi: &FixedVector, eta: &FixedVector) -> FixedVector {
let w1 = FixedVector::from_slice(&[xi[0], xi[1], xi[2]]);
let v1 = FixedVector::from_slice(&[xi[3], xi[4], xi[5]]);
let w2 = FixedVector::from_slice(&[eta[0], eta[1], eta[2]]);
let v2 = FixedVector::from_slice(&[eta[3], eta[4], eta[5]]);
let w = cross_exact(&w1, &w2);
let (a, b) = (cross_exact(&w1, &v2), cross_exact(&w2, &v1));
let out = [w[0], w[1], w[2],
compute_subtract(a[0], b[0]), compute_subtract(a[1], b[1]), compute_subtract(a[2], b[2])];
down_vector(&out).expect("SE3::bracket: result exceeds storage")
}
fn act(&self, g: &FixedMatrix, point: &FixedVector) -> FixedVector {
let r = SE3::extract_rotation(g);
let t = SE3::extract_translation(g);
&r.mul_vector(point) + &t
}
}
pub struct SOn {
pub n: usize,
}
impl SOn {
pub fn hat_son(&self, xi: &FixedVector) -> FixedMatrix {
let n = self.n;
let mut m = FixedMatrix::new(n, n);
let mut k = 0;
for i in 0..n {
for j in (i + 1)..n {
m.set(i, j, xi[k]);
m.set(j, i, -xi[k]);
k += 1;
}
}
m
}
pub fn vee_son(&self, m: &FixedMatrix) -> FixedVector {
let n = self.n;
let dim = n * (n - 1) / 2;
let mut v = FixedVector::new(dim);
let mut k = 0;
for i in 0..n {
for j in (i + 1)..n {
v[k] = m.get(i, j);
k += 1;
}
}
v
}
fn vee_son_compute(&self, m: &ComputeMatrix) -> Result<FixedVector, OverflowDetected> {
let n = self.n;
let mut v = Vec::with_capacity(n * (n - 1) / 2);
for i in 0..n {
for j in (i + 1)..n { v.push(m.get(i, j)); }
}
down_vector(&v)
}
fn log_compute(&self, g: &ComputeMatrix) -> Result<Vec<ComputeStorage>, OverflowDetected> {
let skew = skew_half(&matrix_log_compute(g)?);
let n = self.n;
let mut v = Vec::with_capacity(n * (n - 1) / 2);
for i in 0..n {
for j in (i + 1)..n { v.push(skew.get(i, j)); }
}
Ok(v)
}
fn exp_compute(&self, xi: &FixedVector) -> Result<ComputeMatrix, OverflowDetected> {
matrix_exp_compute(&ComputeMatrix::from_fixed_matrix(&self.hat_son(xi)))
}
fn log_map_compute(&self, base: &FixedVector, target: &FixedVector) -> Result<Vec<ComputeStorage>, OverflowDetected> {
let g = self.exp_compute(base)?.transpose().mat_mul(&self.exp_compute(target)?);
self.log_compute(&g)
}
}
impl Manifold for SOn {
fn dimension(&self) -> usize { self.n * (self.n - 1) / 2 }
fn inner_product(&self, _base: &FixedVector, u: &FixedVector, v: &FixedVector) -> FixedPoint { u.dot_precise(v) }
fn exp_map(&self, base: &FixedVector, tangent: &FixedVector) -> Result<FixedVector, OverflowDetected> {
let g = self.exp_compute(base)?.mat_mul(&self.exp_compute(tangent)?);
down_vector(&self.log_compute(&g)?)
}
fn log_map(&self, base: &FixedVector, target: &FixedVector) -> Result<FixedVector, OverflowDetected> {
down_vector(&self.log_map_compute(base, target)?)
}
fn distance(&self, p: &FixedVector, q: &FixedVector) -> Result<FixedPoint, OverflowDetected> {
norm_to_storage(&self.log_map_compute(p, q)?)
}
fn parallel_transport(&self, _base: &FixedVector, _target: &FixedVector, tangent: &FixedVector) -> Result<FixedVector, OverflowDetected> {
Ok(tangent.clone())
}
}
impl LieGroup for SOn {
fn algebra_dim(&self) -> usize { self.n * (self.n - 1) / 2 }
fn matrix_dim(&self) -> usize { self.n }
fn identity_element(&self) -> FixedMatrix { FixedMatrix::identity(self.n) }
fn compose(&self, g1: &FixedMatrix, g2: &FixedMatrix) -> FixedMatrix { g1 * g2 }
fn group_inverse(&self, g: &FixedMatrix) -> Result<FixedMatrix, OverflowDetected> { Ok(g.transpose()) }
fn lie_exp(&self, xi: &FixedVector) -> Result<FixedMatrix, OverflowDetected> {
matrix_exp(&self.hat_son(xi))
}
fn lie_log(&self, g: &FixedMatrix) -> Result<FixedVector, OverflowDetected> {
down_vector(&self.log_compute(&ComputeMatrix::from_fixed_matrix(g))?)
}
fn hat(&self, xi: &FixedVector) -> FixedMatrix { self.hat_son(xi) }
fn vee(&self, xi_hat: &FixedMatrix) -> FixedVector { self.vee_son(xi_hat) }
fn adjoint(&self, g: &FixedMatrix, xi: &FixedVector) -> Result<FixedVector, OverflowDetected> {
Ok(self.vee_son(&conjugate_exact(g, &self.hat_son(xi), g)?))
}
fn bracket(&self, xi: &FixedVector, eta: &FixedVector) -> FixedVector {
self.vee_son_compute(&commutator_exact(&self.hat_son(xi), &self.hat_son(eta)))
.expect("SOn::bracket: result exceeds storage")
}
fn act(&self, g: &FixedMatrix, point: &FixedVector) -> FixedVector {
g.mul_vector(point)
}
}
pub struct GLn {
pub n: usize,
}
impl GLn {
pub fn hat_gln(&self, xi: &FixedVector) -> FixedMatrix {
let n = self.n;
FixedMatrix::from_fn(n, n, |i, j| xi[i * n + j])
}
pub fn vee_gln(&self, m: &FixedMatrix) -> FixedVector {
let n = self.n;
let mut v = FixedVector::new(n * n);
for i in 0..n {
for j in 0..n {
v[i * n + j] = m.get(i, j);
}
}
v
}
fn vee_gln_compute(&self, m: &ComputeMatrix) -> Vec<ComputeStorage> {
let n = self.n;
(0..n * n).map(|k| m.get(k / n, k % n)).collect()
}
fn exp_compute(&self, xi: &FixedVector) -> Result<ComputeMatrix, OverflowDetected> {
matrix_exp_compute(&ComputeMatrix::from_fixed_matrix(&self.hat_gln(xi)))
}
fn log_map_compute(&self, base: &FixedVector, target: &FixedVector) -> Result<Vec<ComputeStorage>, OverflowDetected> {
let g = inverse_compute(&self.exp_compute(base)?)?.mat_mul(&self.exp_compute(target)?);
Ok(self.vee_gln_compute(&matrix_log_compute(&g)?))
}
}
impl Manifold for GLn {
fn dimension(&self) -> usize { self.n * self.n }
fn inner_product(&self, _base: &FixedVector, u: &FixedVector, v: &FixedVector) -> FixedPoint {
u.dot_precise(v)
}
fn exp_map(&self, base: &FixedVector, tangent: &FixedVector) -> Result<FixedVector, OverflowDetected> {
let g = self.exp_compute(base)?.mat_mul(&self.exp_compute(tangent)?);
down_vector(&self.vee_gln_compute(&matrix_log_compute(&g)?))
}
fn log_map(&self, base: &FixedVector, target: &FixedVector) -> Result<FixedVector, OverflowDetected> {
down_vector(&self.log_map_compute(base, target)?)
}
fn distance(&self, p: &FixedVector, q: &FixedVector) -> Result<FixedPoint, OverflowDetected> {
norm_to_storage(&self.log_map_compute(p, q)?)
}
fn parallel_transport(&self, _base: &FixedVector, _target: &FixedVector, tangent: &FixedVector) -> Result<FixedVector, OverflowDetected> {
Ok(tangent.clone())
}
}
impl LieGroup for GLn {
fn algebra_dim(&self) -> usize { self.n * self.n }
fn matrix_dim(&self) -> usize { self.n }
fn identity_element(&self) -> FixedMatrix { FixedMatrix::identity(self.n) }
fn compose(&self, g1: &FixedMatrix, g2: &FixedMatrix) -> FixedMatrix { g1 * g2 }
fn group_inverse(&self, g: &FixedMatrix) -> Result<FixedMatrix, OverflowDetected> {
down_matrix(&inverse_compute(&ComputeMatrix::from_fixed_matrix(g))?)
}
fn lie_exp(&self, xi: &FixedVector) -> Result<FixedMatrix, OverflowDetected> { matrix_exp(&self.hat_gln(xi)) }
fn lie_log(&self, g: &FixedMatrix) -> Result<FixedVector, OverflowDetected> { Ok(self.vee_gln(&matrix_log(g)?)) }
fn hat(&self, xi: &FixedVector) -> FixedMatrix { self.hat_gln(xi) }
fn vee(&self, xi_hat: &FixedMatrix) -> FixedVector { self.vee_gln(xi_hat) }
fn adjoint(&self, g: &FixedMatrix, xi: &FixedVector) -> Result<FixedVector, OverflowDetected> {
down_vector(&self.vee_gln_compute(&conjugate_by_inverse(g, &self.hat_gln(xi))?))
}
fn bracket(&self, xi: &FixedVector, eta: &FixedVector) -> FixedVector {
let c = commutator_exact(&self.hat_gln(xi), &self.hat_gln(eta));
down_vector(&self.vee_gln_compute(&c)).expect("GLn::bracket: result exceeds storage")
}
fn act(&self, g: &FixedMatrix, point: &FixedVector) -> FixedVector { g.mul_vector(point) }
}
pub struct On {
pub n: usize,
}
impl Manifold for On {
fn dimension(&self) -> usize { self.n * (self.n - 1) / 2 }
fn inner_product(&self, _base: &FixedVector, u: &FixedVector, v: &FixedVector) -> FixedPoint { u.dot_precise(v) }
fn exp_map(&self, base: &FixedVector, tangent: &FixedVector) -> Result<FixedVector, OverflowDetected> { SOn { n: self.n }.exp_map(base, tangent) }
fn log_map(&self, base: &FixedVector, target: &FixedVector) -> Result<FixedVector, OverflowDetected> { SOn { n: self.n }.log_map(base, target) }
fn distance(&self, p: &FixedVector, q: &FixedVector) -> Result<FixedPoint, OverflowDetected> { Ok(self.log_map(p, q)?.length()) }
fn parallel_transport(&self, _base: &FixedVector, _target: &FixedVector, tangent: &FixedVector) -> Result<FixedVector, OverflowDetected> { Ok(tangent.clone()) }
}
impl LieGroup for On {
fn algebra_dim(&self) -> usize { self.n * (self.n - 1) / 2 }
fn matrix_dim(&self) -> usize { self.n }
fn identity_element(&self) -> FixedMatrix { FixedMatrix::identity(self.n) }
fn compose(&self, g1: &FixedMatrix, g2: &FixedMatrix) -> FixedMatrix { g1 * g2 }
fn group_inverse(&self, g: &FixedMatrix) -> Result<FixedMatrix, OverflowDetected> { Ok(g.transpose()) }
fn lie_exp(&self, xi: &FixedVector) -> Result<FixedMatrix, OverflowDetected> { SOn { n: self.n }.lie_exp(xi) }
fn lie_log(&self, g: &FixedMatrix) -> Result<FixedVector, OverflowDetected> { SOn { n: self.n }.lie_log(g) }
fn hat(&self, xi: &FixedVector) -> FixedMatrix { SOn { n: self.n }.hat_son(xi) }
fn vee(&self, xi_hat: &FixedMatrix) -> FixedVector { SOn { n: self.n }.vee_son(xi_hat) }
fn adjoint(&self, g: &FixedMatrix, xi: &FixedVector) -> Result<FixedVector, OverflowDetected> { SOn { n: self.n }.adjoint(g, xi) }
fn bracket(&self, xi: &FixedVector, eta: &FixedVector) -> FixedVector { SOn { n: self.n }.bracket(xi, eta) }
fn act(&self, g: &FixedMatrix, point: &FixedVector) -> FixedVector { g.mul_vector(point) }
}
pub struct SLn {
pub n: usize,
}
impl SLn {
pub fn hat_sln(&self, xi: &FixedVector) -> FixedMatrix {
let n = self.n;
let mut m = FixedMatrix::new(n, n);
let mut trace_sum = FixedPoint::ZERO;
for i in 0..n - 1 {
m.set(i, i, xi[i]);
trace_sum = trace_sum + xi[i];
}
m.set(n - 1, n - 1, -trace_sum);
let mut k = n - 1;
for i in 0..n {
for j in 0..n {
if i != j { m.set(i, j, xi[k]); k += 1; }
}
}
m
}
pub fn vee_sln(&self, m: &FixedMatrix) -> FixedVector {
let n = self.n;
let mut v = FixedVector::new(n * n - 1);
for i in 0..n - 1 { v[i] = m.get(i, i); }
let mut k = n - 1;
for i in 0..n {
for j in 0..n {
if i != j { v[k] = m.get(i, j); k += 1; }
}
}
v
}
pub fn project_traceless(m: &FixedMatrix) -> FixedMatrix {
down_matrix(&project_traceless_compute(&ComputeMatrix::from_fixed_matrix(m)))
.expect("SLn::project_traceless: result exceeds storage")
}
fn vee_sln_compute(&self, m: &ComputeMatrix) -> Vec<ComputeStorage> {
let n = self.n;
let mut v: Vec<ComputeStorage> = (0..n - 1).map(|i| m.get(i, i)).collect();
for i in 0..n {
for j in 0..n {
if i != j { v.push(m.get(i, j)); }
}
}
v
}
fn exp_compute(&self, xi: &FixedVector) -> Result<ComputeMatrix, OverflowDetected> {
matrix_exp_compute(&ComputeMatrix::from_fixed_matrix(&self.hat_sln(xi)))
}
fn log_compute(&self, g: &ComputeMatrix) -> Result<Vec<ComputeStorage>, OverflowDetected> {
Ok(self.vee_sln_compute(&project_traceless_compute(&matrix_log_compute(g)?)))
}
fn log_map_compute(&self, base: &FixedVector, target: &FixedVector) -> Result<Vec<ComputeStorage>, OverflowDetected> {
let g = inverse_compute(&self.exp_compute(base)?)?.mat_mul(&self.exp_compute(target)?);
self.log_compute(&g)
}
}
fn project_traceless_compute(m: &ComputeMatrix) -> ComputeMatrix {
let n = m.rows();
let mut trace = make_compute_int(0);
for i in 0..n { trace = compute_add(trace, m.get(i, i)); }
let trace_per_n = compute_mul_div_int(trace, 1, n as i64).expect("project_traceless: n > 0");
let mut result = m.copy();
for i in 0..n { result.set(i, i, compute_subtract(m.get(i, i), trace_per_n)); }
result
}
impl Manifold for SLn {
fn dimension(&self) -> usize { self.n * self.n - 1 }
fn inner_product(&self, _base: &FixedVector, u: &FixedVector, v: &FixedVector) -> FixedPoint { u.dot_precise(v) }
fn exp_map(&self, base: &FixedVector, tangent: &FixedVector) -> Result<FixedVector, OverflowDetected> {
let g = self.exp_compute(base)?.mat_mul(&self.exp_compute(tangent)?);
down_vector(&self.log_compute(&g)?)
}
fn log_map(&self, base: &FixedVector, target: &FixedVector) -> Result<FixedVector, OverflowDetected> {
down_vector(&self.log_map_compute(base, target)?)
}
fn distance(&self, p: &FixedVector, q: &FixedVector) -> Result<FixedPoint, OverflowDetected> {
norm_to_storage(&self.log_map_compute(p, q)?)
}
fn parallel_transport(&self, _base: &FixedVector, _target: &FixedVector, tangent: &FixedVector) -> Result<FixedVector, OverflowDetected> { Ok(tangent.clone()) }
}
impl LieGroup for SLn {
fn algebra_dim(&self) -> usize { self.n * self.n - 1 }
fn matrix_dim(&self) -> usize { self.n }
fn identity_element(&self) -> FixedMatrix { FixedMatrix::identity(self.n) }
fn compose(&self, g1: &FixedMatrix, g2: &FixedMatrix) -> FixedMatrix { g1 * g2 }
fn group_inverse(&self, g: &FixedMatrix) -> Result<FixedMatrix, OverflowDetected> {
down_matrix(&inverse_compute(&ComputeMatrix::from_fixed_matrix(g))?)
}
fn lie_exp(&self, xi: &FixedVector) -> Result<FixedMatrix, OverflowDetected> { matrix_exp(&self.hat_sln(xi)) }
fn lie_log(&self, g: &FixedMatrix) -> Result<FixedVector, OverflowDetected> {
down_vector(&self.log_compute(&ComputeMatrix::from_fixed_matrix(g))?)
}
fn hat(&self, xi: &FixedVector) -> FixedMatrix { self.hat_sln(xi) }
fn vee(&self, xi_hat: &FixedMatrix) -> FixedVector { self.vee_sln(xi_hat) }
fn adjoint(&self, g: &FixedMatrix, xi: &FixedVector) -> Result<FixedVector, OverflowDetected> {
let conj = conjugate_by_inverse(g, &self.hat_sln(xi))?;
down_vector(&self.vee_sln_compute(&project_traceless_compute(&conj)))
}
fn bracket(&self, xi: &FixedVector, eta: &FixedVector) -> FixedVector {
let c = commutator_exact(&self.hat_sln(xi), &self.hat_sln(eta));
down_vector(&self.vee_sln_compute(&c)).expect("SLn::bracket: result exceeds storage")
}
fn act(&self, g: &FixedMatrix, point: &FixedVector) -> FixedVector { g.mul_vector(point) }
}