secondary_product/
secondary_product.rs

1//! Computes products in $\Mod_{C\lambda^2}$.
2//!
3//! # Usage
4//! The program asks for a module $M$ and an element $x \in \Ext^{\*, \*}(M, k)$. It then computes
5//! the secondary product of the standard lift of $x$ with all (standard lifts of) elements in
6//! $\Ext^{\*, \*}(M, k)$ that survive $d_2$.
7//!
8//! These products are computed for all elements whose product with $x$ lies in the specified
9//! bidegree of $M$, and $k$ is resolved as far as necessary to support this computation.
10//!
11//! Running this program requires computing the secondary resolution of both $M$ and $k$, i.e. the
12//! calculations performed by [`secondary`](../secondary/index.html). The user is encouraged to make
13//! use of a save file to reuse these calculations for different products. (When $M$ is not equal to
14//! $k$, the user will be prompted for the save directory of $k$)
15//!
16//! # Output
17//! This prints the corresponding products in $\Mod_{C\lambda^2}$. In particular, $x$ multiplies on
18//! the left, and the sign twist of $(-1)^{s't}$ is inserted.
19//!
20//! # Notes
21//! The program verifies that $x$ is indeed permanent.
22
23use std::sync::Arc;
24
25use algebra::module::Module;
26use ext::{
27    chain_complex::{ChainComplex, FreeChainComplex},
28    resolution_homomorphism::ResolutionHomomorphism,
29    secondary::*,
30    utils::query_module,
31};
32use fp::{matrix::Matrix, prime::Prime, vector::FpVector};
33use itertools::Itertools;
34use sseq::coordinates::{Bidegree, BidegreeElement, BidegreeGenerator};
35
36fn main() -> anyhow::Result<()> {
37    ext::utils::init_logging()?;
38
39    let resolution = Arc::new(query_module(Some(algebra::AlgebraType::Milnor), true)?);
40
41    let (is_unit, unit) = ext::utils::get_unit(Arc::clone(&resolution))?;
42
43    let p = resolution.prime();
44
45    let name: String = query::raw("Name of product", str::parse);
46
47    let shift = Bidegree::n_s(
48        query::raw(&format!("n of Ext class {name}"), str::parse),
49        query::raw(&format!("s of Ext class {name}"), str::parse),
50    );
51
52    let hom = ResolutionHomomorphism::new(name, Arc::clone(&resolution), Arc::clone(&unit), shift);
53
54    let mut matrix = Matrix::new(p, hom.source.number_of_gens_in_bidegree(shift), 1);
55
56    if matrix.rows() == 0 || matrix.columns() == 0 {
57        panic!("No classes in this bidegree");
58    }
59    let v: Vec<u32> = query::vector("Input ext class", matrix.rows());
60    for (i, &x) in v.iter().enumerate() {
61        matrix.row_mut(i).set_entry(0, x);
62    }
63
64    if !is_unit {
65        let res_max = Bidegree::n_s(
66            resolution.module(0).max_computed_degree(),
67            resolution.next_homological_degree() - 1,
68        );
69        unit.compute_through_stem(res_max - shift);
70    }
71
72    hom.extend_step(shift, Some(&matrix));
73    hom.extend_all();
74
75    let res_lift = SecondaryResolution::new(Arc::clone(&resolution));
76    res_lift.extend_all();
77
78    // Check that class survives to E3.
79    {
80        let m = res_lift.homotopy(shift.s() + 2).homotopies.hom_k(shift.t());
81        assert_eq!(m.len(), v.len());
82        let mut sum = vec![0; m[0].len()];
83        for (x, d2) in v.iter().zip_eq(&m) {
84            sum.iter_mut().zip_eq(d2).for_each(|(a, b)| *a += x * b);
85        }
86        assert!(
87            sum.iter().all(|x| x.is_multiple_of(p.as_u32())),
88            "Class supports a non-zero d2"
89        );
90    }
91    let res_lift = Arc::new(res_lift);
92
93    let unit_lift = if is_unit {
94        Arc::clone(&res_lift)
95    } else {
96        let lift = SecondaryResolution::new(Arc::clone(&unit));
97        lift.extend_all();
98        Arc::new(lift)
99    };
100
101    let hom = Arc::new(hom);
102    let hom_lift = SecondaryResolutionHomomorphism::new(
103        Arc::clone(&res_lift),
104        Arc::clone(&unit_lift),
105        Arc::clone(&hom),
106    );
107
108    if let Some(s) = ext::utils::secondary_job() {
109        hom_lift.compute_partial(s);
110        return Ok(());
111    }
112
113    hom_lift.extend_all();
114
115    // Compute E3 page
116    let res_sseq = Arc::new(res_lift.e3_page());
117    let unit_sseq = if is_unit {
118        Arc::clone(&res_sseq)
119    } else {
120        Arc::new(unit_lift.e3_page())
121    };
122
123    fn get_page_data(sseq: &sseq::Sseq<2, sseq::Adams>, b: Bidegree) -> &fp::matrix::Subquotient {
124        let d = sseq.page_data(b);
125        &d[std::cmp::min(3, d.len() - 1)]
126    }
127
128    let name = hom_lift.name();
129    // Iterate through the multiplicand
130    for b in unit.iter_nonzero_stem() {
131        // The potential target has to be hit, and we need to have computed (the data need for) the
132        // d2 that hits the potential target.
133        if !resolution.has_computed_bidegree(b + shift + LAMBDA_BIDEGREE) {
134            continue;
135        }
136        if !resolution.has_computed_bidegree(b + shift - Bidegree::s_t(1, 0)) {
137            continue;
138        }
139
140        let page_data = get_page_data(unit_sseq.as_ref(), b);
141
142        let target_num_gens = resolution.number_of_gens_in_bidegree(b + shift);
143        let lambda_num_gens = resolution.number_of_gens_in_bidegree(b + shift + LAMBDA_BIDEGREE);
144
145        if target_num_gens == 0 && lambda_num_gens == 0 {
146            continue;
147        }
148
149        // First print the products with non-surviving classes
150        if target_num_gens > 0 {
151            let hom_k = hom.get_map((b + shift).s()).hom_k(b.t());
152            for i in page_data.complement_pivots() {
153                let g = BidegreeGenerator::new(b, i);
154                println!("{name} λ x_{g} = λ {:?}", &hom_k[i]);
155            }
156        }
157
158        // Now print the secondary products
159        if page_data.subspace_dimension() == 0 {
160            continue;
161        }
162
163        let mut outputs = vec![
164            FpVector::new(p, target_num_gens + lambda_num_gens);
165            page_data.subspace_dimension()
166        ];
167
168        hom_lift.hom_k(
169            Some(&res_sseq),
170            b,
171            page_data.subspace_gens(),
172            outputs.iter_mut().map(FpVector::as_slice_mut),
173        );
174        for (g, output) in page_data.subspace_gens().zip_eq(outputs) {
175            println!(
176                "{name} [{basis_string}] = {} + λ {}",
177                output.slice(0, target_num_gens),
178                output.slice(target_num_gens, target_num_gens + lambda_num_gens),
179                basis_string = BidegreeElement::new(b, g.to_owned()).to_basis_string(),
180            );
181        }
182    }
183    Ok(())
184}