algebra/module/
hom_module.rs

1use std::sync::Arc;
2
3use bivec::BiVec;
4use fp::vector::FpSliceMut;
5use once::OnceBiVec;
6
7use crate::{
8    algebra::Field,
9    module::{FreeModule, Module, block_structure::BlockStructure},
10};
11
12/// Given a module N and a free module M, this is the module Hom(M, N) as a module over the ground
13/// field.
14///
15/// This requires N to be bounded, and is graded *opposite* to the usual grading so that Hom(M, N)
16/// is bounded below.
17pub struct HomModule<M: Module> {
18    algebra: Arc<Field>,
19    source: Arc<FreeModule<M::Algebra>>,
20    target: Arc<M>,
21    pub block_structures: OnceBiVec<BlockStructure>,
22}
23
24impl<M: Module> std::fmt::Display for HomModule<M> {
25    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
26        write!(f, "Hom({}, {})", self.source, self.target)
27    }
28}
29
30impl<M: Module> HomModule<M> {
31    pub fn new(source: Arc<FreeModule<M::Algebra>>, target: Arc<M>) -> Self {
32        let p = source.prime();
33        let algebra = Arc::new(Field::new(p));
34        let min_degree = source.min_degree()
35            - target
36                .max_degree()
37                .expect("HomModule requires target to be bounded");
38        Self {
39            algebra,
40            source,
41            target,
42            block_structures: OnceBiVec::new(min_degree), // fn_degree -> blocks
43        }
44    }
45
46    pub fn source(&self) -> Arc<FreeModule<M::Algebra>> {
47        Arc::clone(&self.source)
48    }
49
50    pub fn target(&self) -> Arc<M> {
51        Arc::clone(&self.target)
52    }
53}
54
55impl<M: Module> Module for HomModule<M> {
56    type Algebra = Field;
57
58    fn algebra(&self) -> Arc<Self::Algebra> {
59        Arc::clone(&self.algebra)
60    }
61
62    fn min_degree(&self) -> i32 {
63        self.block_structures.min_degree()
64    }
65
66    fn max_computed_degree(&self) -> i32 {
67        self.source.max_computed_degree() - self.target.max_degree().unwrap()
68    }
69
70    fn compute_basis(&self, degree: i32) {
71        self.source
72            .compute_basis(degree + self.target.max_degree().unwrap());
73        self.block_structures.extend(degree, |d| {
74            let mut block_sizes = BiVec::new(self.target.min_degree() + d);
75            block_sizes.extend_with(self.target.max_degree().unwrap() + d, |gen_deg| {
76                vec![
77                    self.target.dimension(gen_deg - d);
78                    if self.source.max_computed_degree() >= gen_deg {
79                        self.source.number_of_gens_in_degree(gen_deg)
80                    } else {
81                        0
82                    }
83                ]
84            });
85            BlockStructure::new(&block_sizes)
86        });
87    }
88
89    fn dimension(&self, degree: i32) -> usize {
90        self.block_structures[degree].total_dimension()
91    }
92
93    fn act_on_basis(
94        &self,
95        mut result: FpSliceMut,
96        coeff: u32,
97        op_degree: i32,
98        op_index: usize,
99        _mod_degree: i32,
100        mod_index: usize,
101    ) {
102        assert_eq!(op_degree, 0);
103        assert_eq!(op_index, 0);
104        result.add_basis_element(mod_index, coeff);
105    }
106
107    fn basis_element_to_string(&self, degree: i32, idx: usize) -> String {
108        let gen_basis_elt = self.block_structures[degree].index_to_generator_basis_elt(idx);
109        let gen_deg = gen_basis_elt.generator_degree;
110        let gen_idx = gen_basis_elt.generator_index;
111        let gen_mod_idx = self
112            .source
113            .operation_generator_to_index(0, 0, gen_deg, gen_idx);
114        let basis_deg = gen_deg - degree;
115        let basis_idx = gen_basis_elt.basis_index;
116        format!(
117            "{}*⊗{}",
118            self.source.basis_element_to_string(gen_deg, gen_mod_idx),
119            self.target.basis_element_to_string(basis_deg, basis_idx),
120        )
121    }
122}
123
124#[cfg(test)]
125mod tests {
126    use super::*;
127    use crate::{MilnorAlgebra, module::FDModule};
128
129    #[test]
130    fn test_hom_dim() {
131        const NUM_GENS: [usize; 3] = [1, 2, 1];
132        const TARGET_DIM: [usize; 3] = [1, 3, 4];
133
134        let algebra = Arc::new(MilnorAlgebra::new(fp::prime::TWO, false));
135        let f = Arc::new(FreeModule::new(Arc::clone(&algebra), "F0".to_string(), 0));
136        let m = Arc::new(
137            FDModule::from_json(Arc::clone(&algebra), &crate::tests::joker_json()).unwrap(),
138        );
139
140        for (deg, num_gens) in NUM_GENS.into_iter().enumerate() {
141            f.add_generators(deg as i32, num_gens, None);
142        }
143        f.compute_basis(NUM_GENS.len() as i32 - 1);
144
145        let hom = HomModule::new(f, m);
146        assert_eq!(hom.min_degree(), -4);
147        assert_eq!(hom.max_computed_degree(), -2);
148        hom.compute_basis(-2);
149
150        for (&target_dim, deg) in
151            std::iter::zip(&TARGET_DIM, hom.min_degree()..=hom.max_computed_degree())
152        {
153            assert_eq!(hom.dimension(deg), target_dim);
154        }
155    }
156}