use crate::algebra::mv::Mv;
use crate::algebra::ops;
use crate::algebra::signature::Signature;
use crate::governance::composition::Embedding;
use crate::governance::construction::Construction;
use crate::governance::governance::Governance;
use crate::scalar::Scalar;
#[derive(Clone, Debug)]
pub struct Morphism {
pub source_sig: Signature,
pub target_sig: Signature,
pub generator_images: Vec<Mv>,
}
#[derive(Clone, Debug)]
pub enum MorphismError {
WrongImageCount {
expected: usize,
got: usize,
},
QuadraticFormViolation {
gen: u8,
expected: i8,
got: Scalar,
},
AnticommutativityViolation {
gen_i: u8,
gen_j: u8,
},
}
impl Morphism {
pub fn new(
source: Signature,
target: Signature,
images: Vec<Mv>,
) -> Result<Self, MorphismError> {
if images.len() != source.n() as usize {
return Err(MorphismError::WrongImageCount {
expected: source.n() as usize,
got: images.len(),
});
}
for (i, img) in images.iter().enumerate() {
let sq = ops::geometric(img, img, &target);
let expected = source.generator_square(i as u8);
let sq_scalar = sq.coefficient(0);
let expected_scalar = Scalar::from(expected as i64);
if sq_scalar != expected_scalar || sq.len() > 1 {
return Err(MorphismError::QuadraticFormViolation {
gen: i as u8,
expected,
got: sq_scalar,
});
}
}
for i in 0..images.len() {
for j in (i + 1)..images.len() {
let ab = ops::geometric(&images[i], &images[j], &target);
let ba = ops::geometric(&images[j], &images[i], &target);
let anticommutator = ab + ba;
if !anticommutator.is_zero() {
return Err(MorphismError::AnticommutativityViolation {
gen_i: i as u8,
gen_j: j as u8,
});
}
}
}
Ok(Morphism {
source_sig: source,
target_sig: target,
generator_images: images,
})
}
pub fn new_unchecked(source: Signature, target: Signature, images: Vec<Mv>) -> Self {
Morphism {
source_sig: source,
target_sig: target,
generator_images: images,
}
}
pub fn apply_mv(&self, mv: &Mv) -> Mv {
let mut result = Mv::new();
for (mask, coeff) in mv.blades() {
let mut blade_image = Mv::scalar(coeff.clone());
for k in 0..self.source_sig.n() {
if mask & (1u64 << k) != 0 {
blade_image = ops::geometric(
&blade_image,
&self.generator_images[k as usize],
&self.target_sig,
);
}
}
result += blade_image;
}
result
}
pub fn apply_derived_gens(&self, dgs: &[Mv]) -> Vec<Mv> {
dgs.iter().map(|dg| self.apply_mv(dg)).collect()
}
pub fn apply_governance(&self, gov: &Governance) -> Governance {
let new_derived_gens = self.apply_derived_gens(&gov.derived_gens);
Governance {
sig: self.target_sig,
derived_gens: new_derived_gens,
geom_classes: gov.geom_classes.clone(),
constructions: vec![], probe: gov.probe.clone(),
transform_rules: vec![],
}
}
}
impl std::fmt::Display for MorphismError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
MorphismError::WrongImageCount { expected, got } => {
write!(f, "expected {} generator images, got {}", expected, got)
}
MorphismError::QuadraticFormViolation { gen, expected, got } => write!(
f,
"g{}^2 should be {} but image^2 = {:?}",
gen, expected, got
),
MorphismError::AnticommutativityViolation { gen_i, gen_j } => {
write!(f, "images of g{} and g{} do not anti-commute", gen_i, gen_j)
}
}
}
}
impl Embedding {
pub fn to_morphism(&self) -> Morphism {
let images: Vec<Mv> = self
.generator_map
.iter()
.map(|&target_gen| Mv::generator(target_gen))
.collect();
Morphism::new_unchecked(self.source_sig, self.target_sig, images)
}
}
pub fn dual_morphism(sig: &Signature) -> Morphism {
let images: Vec<Mv> = (0..sig.n())
.map(|k| {
let gen = Mv::generator(k);
ops::dual(&gen, sig)
})
.collect();
Morphism::new_unchecked(*sig, *sig, images)
}
pub fn restrict_governance(gov: &Governance, class_indices: &[usize]) -> Governance {
let mut geom_classes = Vec::new();
let mut constructions = Vec::new();
let mut old_to_new: std::collections::HashMap<usize, usize> = std::collections::HashMap::new();
for (new_idx, &old_idx) in class_indices.iter().enumerate() {
if old_idx < gov.geom_classes.len() {
geom_classes.push(gov.geom_classes[old_idx].clone());
old_to_new.insert(old_idx, new_idx);
}
}
for constr in &gov.constructions {
if let Some(&new_class_idx) = old_to_new.get(&constr.class_index) {
constructions.push(Construction {
class_index: new_class_idx,
arity: constr.arity,
body: constr.body.clone(),
});
}
}
Governance {
sig: gov.sig,
derived_gens: gov.derived_gens.clone(),
geom_classes,
constructions,
probe: None,
transform_rules: vec![],
}
}
pub fn compose_morphisms(first: &Morphism, second: &Morphism) -> Morphism {
let composed_images: Vec<Mv> = first
.generator_images
.iter()
.map(|img| second.apply_mv(img))
.collect();
Morphism::new_unchecked(first.source_sig, second.target_sig, composed_images)
}
pub fn identity_morphism(sig: &Signature) -> Morphism {
let images: Vec<Mv> = (0..sig.n()).map(Mv::generator).collect();
Morphism::new_unchecked(*sig, *sig, images)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::algebra::blade_new::grade;
use crate::governance::govern;
use crate::governance::GeomClass;
use crate::scalar::Rat;
#[test]
fn identity_morphism_preserves_mv() {
let sig = Signature::new(0, 0, 3).unwrap();
let m = identity_morphism(&sig);
let v = Mv::from_rat_terms(&[
(0b001, Rat::from(3)),
(0b010, Rat::from(4)),
(0b100, Rat::from(5)),
]);
assert_eq!(m.apply_mv(&v), v);
}
#[test]
fn identity_morphism_validates() {
let sig = Signature::new(0, 0, 3).unwrap();
let m = identity_morphism(&sig);
let m2 = Morphism::new(sig, sig, m.generator_images.clone());
assert!(m2.is_ok());
}
#[test]
fn embedding_to_morphism() {
let source = Signature::new(0, 0, 3).unwrap();
let target = Signature::new(1, 0, 4).unwrap();
let emb = Embedding::new(source, target, vec![2, 3, 4]).unwrap();
let morph = emb.to_morphism();
let v = Mv::from_rat_terms(&[
(0b001, Rat::from(3)),
(0b010, Rat::from(4)),
(0b100, Rat::from(5)),
]);
let embedded = morph.apply_mv(&v);
assert_eq!(embedded.coefficient(0b00100), Scalar::from(3i64)); assert_eq!(embedded.coefficient(0b01000), Scalar::from(4i64)); assert_eq!(embedded.coefficient(0b10000), Scalar::from(5i64)); }
#[test]
fn embedding_morphism_matches_embed_mv() {
let source = Signature::new(0, 0, 3).unwrap();
let target = Signature::new(1, 0, 4).unwrap();
let emb = Embedding::new(source, target, vec![2, 3, 4]).unwrap();
let morph = emb.to_morphism();
let v = Mv::from_rat_terms(&[(0b001, Rat::from(7)), (0b010, Rat::from(-3))]);
assert_eq!(emb.embed_mv(&v), morph.apply_mv(&v));
}
#[test]
fn wrong_image_count_rejected() {
let sig = Signature::new(0, 0, 3).unwrap();
let result = Morphism::new(sig, sig, vec![Mv::generator(0)]); assert!(result.is_err());
}
#[test]
fn quadratic_form_violation_detected() {
let sig = Signature::new(0, 0, 2).unwrap(); let images = vec![
Mv::generator(0),
Mv::from_rat_terms(&[(0b01, Rat::from(1)), (0b10, Rat::from(1))]),
];
let result = Morphism::new(sig, sig, images);
assert!(result.is_err());
}
#[test]
fn dual_morphism_maps_vector_to_bivector() {
let sig = Signature::new(0, 0, 3).unwrap();
let dm = dual_morphism(&sig);
let e1 = Mv::generator(0);
let dual_e1 = dm.apply_mv(&e1);
for (mask, coeff) in dual_e1.blades() {
if !coeff.is_zero() {
assert_eq!(grade(mask), 2, "dual of grade-1 should be grade-2");
}
}
}
#[test]
fn dual_morphism_double_application() {
let sig = Signature::new(0, 0, 3).unwrap();
let v = Mv::from_rat_terms(&[(0b001, Rat::from(1))]);
let dv = ops::dual(&v, &sig);
let ddv = ops::dual(&dv, &sig);
assert_eq!(ddv.coefficient(0b001), Scalar::from(-1i64));
}
#[test]
fn compose_identity_is_identity() {
let sig = Signature::new(0, 0, 3).unwrap();
let id = identity_morphism(&sig);
let composed = compose_morphisms(&id, &id);
let v = Mv::from_rat_terms(&[(0b001, Rat::from(3)), (0b010, Rat::from(4))]);
assert_eq!(composed.apply_mv(&v), v);
}
#[test]
fn compose_embedding_with_dual() {
let vga3 = Signature::new(0, 0, 3).unwrap();
let cga3 = Signature::new(1, 0, 4).unwrap();
let emb = Embedding::new(vga3, cga3, vec![2, 3, 4]).unwrap();
let emb_morph = emb.to_morphism();
let dual_morph = dual_morphism(&cga3);
let composed = compose_morphisms(&emb_morph, &dual_morph);
let v = Mv::from_rat_terms(&[(0b001, Rat::from(1))]);
let result = composed.apply_mv(&v);
for (mask, coeff) in result.blades() {
if !coeff.is_zero() {
assert_eq!(
grade(mask),
4,
"embed then dual of grade-1 should be grade-4"
);
}
}
}
#[test]
fn restrict_governance_preserves_class() {
let gov = Governance {
sig: Signature::new(0, 0, 3).unwrap(),
derived_gens: vec![],
geom_classes: vec![
GeomClass::grades_only(&[1]), GeomClass::grades_only(&[2]), ],
constructions: vec![
Construction {
class_index: 0,
arity: 3,
body: crate::governance::Expr::Add(
crate::governance::Expr::add(
crate::governance::Expr::mul(
crate::governance::Expr::param(0),
crate::governance::Expr::gen(0),
),
crate::governance::Expr::mul(
crate::governance::Expr::param(1),
crate::governance::Expr::gen(1),
),
),
crate::governance::Expr::mul(
crate::governance::Expr::param(2),
crate::governance::Expr::gen(2),
),
),
},
Construction {
class_index: 1,
arity: 3,
body: crate::governance::Expr::Add(
crate::governance::Expr::add(
crate::governance::Expr::mul(
crate::governance::Expr::mul(
crate::governance::Expr::param(0),
crate::governance::Expr::gen(0),
),
crate::governance::Expr::gen(1),
),
crate::governance::Expr::mul(
crate::governance::Expr::mul(
crate::governance::Expr::param(1),
crate::governance::Expr::gen(0),
),
crate::governance::Expr::gen(2),
),
),
crate::governance::Expr::mul(
crate::governance::Expr::mul(
crate::governance::Expr::param(2),
crate::governance::Expr::gen(1),
),
crate::governance::Expr::gen(2),
),
),
},
],
probe: None,
transform_rules: vec![],
};
let restricted = restrict_governance(&gov, &[0]);
assert_eq!(restricted.geom_classes.len(), 1);
assert_eq!(restricted.constructions.len(), 1);
assert!(restricted.geom_classes[0].grade_permitted(1));
let mv = restricted
.construct(
0,
&[Scalar::from(3i64), Scalar::from(4i64), Scalar::from(5i64)],
)
.unwrap();
let geoit = govern(&mv, &restricted, 0).unwrap();
assert_eq!(
geoit.read_all().unwrap(),
vec![Scalar::from(3i64), Scalar::from(4i64), Scalar::from(5i64)]
);
}
#[test]
fn restrict_to_multiple_classes() {
let gov = Governance {
sig: Signature::new(0, 0, 3).unwrap(),
derived_gens: vec![],
geom_classes: vec![
GeomClass::grades_only(&[0]),
GeomClass::grades_only(&[1]),
GeomClass::grades_only(&[2]),
],
constructions: vec![],
probe: None,
transform_rules: vec![],
};
let restricted = restrict_governance(&gov, &[0, 2]);
assert_eq!(restricted.geom_classes.len(), 2);
assert!(restricted.geom_classes[0].grade_permitted(0)); assert!(restricted.geom_classes[1].grade_permitted(2)); }
#[test]
fn apply_governance_transforms_derived_gens() {
let sig = Signature::new(0, 0, 3).unwrap();
let dm = dual_morphism(&sig);
let gov = Governance {
sig,
derived_gens: vec![Mv::from_rat_terms(&[(0b001, Rat::from(1))])], geom_classes: vec![GeomClass::grades_only(&[1])],
constructions: vec![],
probe: None,
transform_rules: vec![],
};
let new_gov = dm.apply_governance(&gov);
assert_eq!(new_gov.sig, sig);
assert_eq!(new_gov.derived_gens.len(), 1);
let dual_dg = &new_gov.derived_gens[0];
for (mask, coeff) in dual_dg.blades() {
if !coeff.is_zero() {
assert_eq!(grade(mask), 2);
}
}
}
#[test]
fn anticommutativity_valid_embedding_passes() {
let source = Signature::new(0, 0, 3).unwrap();
let target = Signature::new(1, 0, 4).unwrap();
let images: Vec<Mv> = vec![
Mv::generator(2), Mv::generator(3), Mv::generator(4), ];
assert!(Morphism::new(source, target, images).is_ok());
}
#[test]
fn anticommutativity_commuting_images_rejected() {
let source = Signature::new(0, 0, 2).unwrap();
let target = Signature::new(0, 0, 3).unwrap();
let images: Vec<Mv> = vec![
Mv::generator(0), Mv::generator(0),
];
let result = Morphism::new(source, target, images);
assert!(matches!(
result,
Err(MorphismError::AnticommutativityViolation { .. })
));
}
}