lift_hom/
lift_hom.rs

1//! Given an element in $\Ext(M, N)$, this computes the induced map $\Ext(N, k) \to \Ext(M, k)$
2//! given by composition.
3//!
4//! It begins by asking for the two modules $M$, $N$ and the $\Ext$ class. Afterwards, you may
5//! either supply save files for the two modules, or a range to compute the map for.
6//!
7//! Afterwards, the user is prompted for the Ext class. If $R_s$ is the $s$th term of the minimal
8//! resolution of $M$, the Ext class is given as an element of $\Hom_A(R_s, \Sigma^t N) =
9//! \Hom(\Ext^{s, *}(M, k)^\vee, \Sigma^t N)$.
10//!
11//! In other words, for every basis element in $\Ext^{s, *}(M, k)$, one has to specify its image in
12//! $\Sigma^t N$. In the special case where $s = 0$, this is specifying the map between the
13//! underlying modules on module generators under the Steenrod action.
14//!
15//! Our notation is as follows:
16//!
17//!  - `f` is the map in $\Hom_A(R_s, \Sigma^t N)$.
18//!  - `F` is the induced map on Ext.
19//!
20//! Each prompt will be of the form `f(x_(s, n, i)) = ` and the user has to input the value of the
21//! homomorphism on this basis element. For example, the following session computes the map induced
22//! by the projection of spectra $C2 \to S^1$
23//!
24//! ```text
25//!  $ cargo run --example lift_hom
26//! Target module (default: S_2): C2
27//! Source module (default: Cnu): S_2
28//! s of Ext class (default: 0): 0
29//! n of Ext class (default: 0): -1
30//! Target save file (optional):
31//! Max target s (default: 10): 10
32//! Max target n (default: 10): 20
33//!
34//! Input module homomorphism to lift:
35//! f(x_(0, 0, 0)): [1]
36//! ```
37//!
38//! It is important to keep track of varaince when using this example; Both $\Ext(-, k)$ and
39//! $H^*(-)$ are contravariant functors. The words "source" and "target" refer to the map between
40//! Steenrod modules.
41
42use std::{path::PathBuf, sync::Arc};
43
44use algebra::module::Module;
45use anyhow::{Context, anyhow};
46use ext::{
47    chain_complex::{AugmentedChainComplex, ChainComplex, FreeChainComplex},
48    resolution_homomorphism::ResolutionHomomorphism,
49    utils,
50};
51use fp::matrix::Matrix;
52use sseq::coordinates::{Bidegree, BidegreeGenerator};
53
54fn main() -> anyhow::Result<()> {
55    ext::utils::init_logging()?;
56
57    let source = Arc::new(utils::query_module_only("Source module", None, true)?);
58    let b = Bidegree::n_s(
59        query::with_default("Max source n", "30", str::parse),
60        query::with_default("Max source s", "7", str::parse),
61    );
62
63    let source_name = source.name();
64    let target = query::with_default("Target module", source_name, |s| {
65        if s == source_name {
66            Ok(Arc::clone(&source))
67        } else if cfg!(feature = "nassau") {
68            Err(anyhow!("Can only resolve S_2 with nassau"))
69        } else {
70            let config: utils::Config = s.try_into()?;
71            let save_dir = query::optional("Target save directory", |x| {
72                Result::<PathBuf, std::convert::Infallible>::Ok(PathBuf::from(x))
73            });
74
75            let mut target = utils::construct(config, save_dir)
76                .context("Failed to load module from save file")
77                .unwrap();
78
79            target.set_name(s.to_owned());
80
81            #[cfg(feature = "nassau")]
82            unreachable!();
83
84            #[cfg(not(feature = "nassau"))]
85            Ok(Arc::new(target))
86        }
87    });
88
89    assert_eq!(source.prime(), target.prime());
90    let p = source.prime();
91
92    let name: String = query::raw("Name of product", str::parse);
93
94    let shift = Bidegree::n_s(
95        query::with_default("n of product", "0", str::parse),
96        query::with_default("s of product", "0", str::parse),
97    );
98
99    source.compute_through_stem(b);
100    target.compute_through_stem(b - shift);
101
102    let target_module = target.target().module(0);
103    let hom = ResolutionHomomorphism::new(name.clone(), source, target, shift);
104
105    eprintln!("\nInput Ext class to lift:");
106    for output_t in 0..=target_module
107        .max_degree()
108        .expect("lift_hom requires target to be bounded")
109    {
110        let output = Bidegree::s_t(0, output_t);
111        let input = output + shift;
112        let mut matrix = Matrix::new(
113            p,
114            hom.source.number_of_gens_in_bidegree(input),
115            target_module.dimension(output.t()),
116        );
117
118        if matrix.rows() == 0 || matrix.columns() == 0 {
119            hom.extend_step(input, None);
120        } else {
121            for (idx, mut row) in matrix.iter_mut().enumerate() {
122                let g = BidegreeGenerator::new(input, idx);
123                let v: Vec<u32> = query::vector(&format!("f(x_{g}"), row.as_slice().len());
124                for (i, &x) in v.iter().enumerate() {
125                    row.set_entry(i, x);
126                }
127            }
128            hom.extend_step(input, Some(&matrix));
129        }
130    }
131
132    hom.extend_all();
133
134    for b2 in hom.target.iter_stem() {
135        let shifted_b2 = b2 + shift;
136        if shifted_b2.s() >= hom.source.next_homological_degree()
137            || shifted_b2.t() > hom.source.module(shifted_b2.s()).max_computed_degree()
138        {
139            continue;
140        }
141        let matrix = hom.get_map(shifted_b2.s()).hom_k(b2.t());
142        for (i, r) in matrix.iter().enumerate() {
143            let g = BidegreeGenerator::new(b2, i);
144            println!("{name} x_{g} = {r:?}");
145        }
146    }
147    Ok(())
148}