From 3b44965953a6f6aa15146b8eddccaa1b04a64f71 Mon Sep 17 00:00:00 2001 From: Miguel Young de la Sota Date: Sun, 24 Oct 2021 14:00:55 -0400 Subject: [PATCH 1/2] Add `*Elem` types to `algebra::algebra` --- .../algebra/src/algebra/algebra_trait.rs | 188 ++++++++++++++++++ ext/crates/algebra/src/algebra/mod.rs | 2 +- 2 files changed, 189 insertions(+), 1 deletion(-) diff --git a/ext/crates/algebra/src/algebra/algebra_trait.rs b/ext/crates/algebra/src/algebra/algebra_trait.rs index 0ba1785867..05688472b0 100644 --- a/ext/crates/algebra/src/algebra/algebra_trait.rs +++ b/ext/crates/algebra/src/algebra/algebra_trait.rs @@ -1,6 +1,194 @@ use fp::prime::ValidPrime; use fp::vector::{Slice, SliceMut}; +use std::marker::PhantomData; +use std::fmt; +use std::cmp; +use std::any; + +/// A basis element in an [`Algebra`] $A$. +pub struct BasisElem { + degree: i32, + index: usize, + _alg: PhantomData<*const A>, +} + +impl BasisElem { + /// Creates a new basis element for $A$ with the given data. + pub fn new(degree: i32, index: usize) -> Self { + Self {degree, index, _alg: PhantomData} + } + + /// Returns the degree of $A$ this is a basis element of. + pub fn degree(&self) -> i32 { + self.degree + } + + /// Returns the index of this basis element within the ordered basis of + /// $A_i$. + pub fn index(&self) -> usize { + self.index + } +} + +// Manual impls, since we want to avoid a bound involving `A`... +impl Clone for BasisElem { + fn clone(&self) -> Self { + Self::new(self.degree, self.index) + } +} + +impl Copy for BasisElem {} + +impl PartialEq for BasisElem { + fn eq(&self, that: &Self) -> bool { + cmp::Ord::cmp(self, that).is_eq() + } +} + +impl Eq for BasisElem {} + +impl PartialOrd for BasisElem { + fn partial_cmp(&self, that: &Self) -> Option { + Some(self.cmp(that)) + } +} + +impl Ord for BasisElem { + fn cmp(&self, that: &Self) -> cmp::Ordering { + (self.degree, self.index).cmp(&(that.degree, that.index)) + } +} + +impl fmt::Debug for BasisElem { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "BasisElem<{}>({}, {})", any::type_name::(), self.degree, self.index) + } +} + +/// A general element of fixed degree in an [`Algebra`] $A$. +/// +/// The coefficients of the element are given by the `fp` vector type `V`, where +/// the `i`th element of the vector is the coefficient for the `i`th basis +/// element. +pub struct VectorElem { + degree: i32, + coeffs: V, + _alg: PhantomData<*const A>, +} + +impl VectorElem { + /// Creates a new basis element for `A` with the given data. + pub fn new(degree: i32, coeffs: V) -> Self { + Self {degree, coeffs, _alg: PhantomData} + } + + /// Returns the degree of $A$ this element sit in. + pub fn degree(&self) -> i32 { + self.degree + } + + /// Returns the coefficients that give this element in terms of the + /// distinguished basis of $A_i$. + pub fn coeffs(&self) -> &V { + &self.coeffs + } + + /// Returns the mutable coefficients that give this element in terms of the + /// distinguished basis of $A_i$. + pub fn coeffs_mut(&mut self) -> &mut V { + &mut self.coeffs + } +} + +// Manual impls, since we want to avoid a bound involving `A`... +impl Clone for VectorElem { + fn clone(&self) -> Self { + Self::new(self.degree, self.coeffs.clone()) + } +} + +impl Copy for VectorElem {} + +impl, W> PartialEq> for VectorElem { + fn eq(&self, that: &VectorElem) -> bool { + self.degree == that.degree && self.coeffs == that.coeffs + } +} + +impl Eq for VectorElem {} + +impl cmp::PartialOrd for VectorElem { + fn partial_cmp(&self, that: &Self) -> Option { + (self.degree, &self.coeffs).partial_cmp(&(that.degree, &that.coeffs)) + } +} + +impl Ord for VectorElem { + fn cmp(&self, that: &Self) -> cmp::Ordering { + (self.degree, &self.coeffs).cmp(&(that.degree, &that.coeffs)) + } +} + +impl fmt::Debug for VectorElem { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "VectorElem<{}>({}, {:?})", any::type_name::(), self.degree, self.coeffs) + } +} + +/// A general element of fixed degree in an [`Algebra`] $A$. +/// +/// This enum combines `BasisElem` and `VectorElem` into one. +pub enum Elem { + Basis(BasisElem), + Vector(VectorElem), +} + +// Manual impls, since we want to avoid a bound involving `A`... +impl Clone for Elem { + fn clone(&self) -> Self { + match self { + Self::Basis(b) => Self::Basis(*b), + Self::Vector(v) => Self::Vector(v.clone()), + } + } +} + +impl Copy for Elem {} + +impl, W> PartialEq> for Elem { + fn eq(&self, that: &Elem) -> bool { + match (self, that) { + (Self::Basis(b1), Elem::Basis(b2)) => b1 == b2, + (Self::Vector(v1), Elem::Vector(v2)) => v1 == v2, + _ => false, + } + } +} + +impl Eq for Elem {} + +impl fmt::Debug for Elem { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Self::Basis(b) => b.fmt(f), + Self::Vector(v) => v.fmt(f), + } + } +} + +impl From> for Elem { + fn from(b: BasisElem) -> Self { + Self::Basis(b) + } +} + +impl From> for Elem { + fn from(v: VectorElem) -> Self { + Self::Vector(v) + } +} + /// A graded algebra over F_p, finite dimensional in each degree, equipped with a choice of ordered /// basis in each dimension. Basis elements of the algebra are referred to by their degree and /// index, and general elements are referred to by the degree and an `FpVector` listing the diff --git a/ext/crates/algebra/src/algebra/mod.rs b/ext/crates/algebra/src/algebra/mod.rs index d3f5a44ffe..12a04c4b11 100644 --- a/ext/crates/algebra/src/algebra/mod.rs +++ b/ext/crates/algebra/src/algebra/mod.rs @@ -11,7 +11,7 @@ mod steenrod_algebra; pub use adem_algebra::{AdemAlgebra, AdemAlgebraT}; #[cfg(feature = "json")] pub use algebra_trait::JsonAlgebra; -pub use algebra_trait::{Algebra, GeneratedAlgebra}; +pub use algebra_trait::{BasisElem, Algebra, GeneratedAlgebra}; pub use bialgebra_trait::Bialgebra; pub use field::Field; pub use milnor_algebra::{MilnorAlgebra, MilnorAlgebraT}; From fbde6d86326518b813df15e8adc2c287f8ddf125 Mon Sep 17 00:00:00 2001 From: Miguel Young de la Sota Date: Sun, 24 Oct 2021 16:04:11 -0400 Subject: [PATCH 2/2] WIP Begin refactoring `algebra::algebra` to use algebra::*Elem --- .../algebra/src/algebra/adem_algebra.rs | 237 ++++++++---------- .../algebra/src/algebra/algebra_trait.rs | 228 +++++++---------- .../algebra/src/algebra/bialgebra_trait.rs | 5 +- ext/crates/algebra/src/algebra/field.rs | 30 ++- .../algebra/src/algebra/milnor_algebra.rs | 92 ++++--- ext/crates/algebra/src/algebra/mod.rs | 8 +- .../algebra/src/algebra/steenrod_algebra.rs | 13 +- ext/crates/algebra/src/lib.rs | 8 +- 8 files changed, 262 insertions(+), 359 deletions(-) diff --git a/ext/crates/algebra/src/algebra/adem_algebra.rs b/ext/crates/algebra/src/algebra/adem_algebra.rs index a7540830db..fb6ad06e98 100644 --- a/ext/crates/algebra/src/algebra/adem_algebra.rs +++ b/ext/crates/algebra/src/algebra/adem_algebra.rs @@ -12,7 +12,7 @@ use rustc_hash::FxHashMap as HashMap; use std::sync::Mutex; use crate::algebra::combinatorics::{self, MAX_XI_TAU}; -use crate::algebra::{Algebra, Bialgebra, GeneratedAlgebra}; +use crate::algebra::{BasisElem, Algebra, Bialgebra, GeneratedAlgebra}; use fp::prime::{BinomialIterator, BitflagIterator, ValidPrime}; use fp::vector::{FpVector, SliceMut}; @@ -155,7 +155,7 @@ impl Algebra for AdemAlgebra { self.p } - fn default_filtration_one_products(&self) -> Vec<(String, i32, usize)> { + fn default_filtration_one_products(&self) -> Vec<(String, BasisElem)> { let mut products = Vec::with_capacity(4); let max_degree; if self.generic { @@ -201,7 +201,7 @@ impl Algebra for AdemAlgebra { self.compute_basis(max_degree); products .into_iter() - .map(|(name, b)| (name, b.degree, self.basis_element_to_index(&b))) + .map(|(name, b)| (name, BasisElem::new(b.degree, self.basis_element_to_index(&b)))) .collect() } @@ -245,27 +245,21 @@ impl Algebra for AdemAlgebra { fn multiply_basis_elements( &self, result: SliceMut, - coeff: u32, - r_degree: i32, - r_index: usize, - s_degree: i32, - s_index: usize, + c: u32, + r: BasisElem, + s: BasisElem, excess: i32, ) { - self.multiply( + self.multiply2( result, - coeff, - r_degree, - r_index, - s_degree, - s_index, + c, r, s, excess, self.unstable, ); } - fn basis_element_to_string(&self, degree: i32, idx: usize) -> String { - format!("{}", self.basis_element_from_index(degree, idx)) + fn basis_element_to_string(&self, x: BasisElem) -> String { + format!("{}", self.basis_element_from_index(x)) } } @@ -330,7 +324,7 @@ impl JsonAlgebra for AdemAlgebra { } impl GeneratedAlgebra for AdemAlgebra { - fn string_to_generator<'a, 'b>(&'a self, input: &'b str) -> IResult<&'b str, (i32, usize)> { + fn string_to_generator<'a, 'b>(&'a self, input: &'b str) -> IResult<&'b str, BasisElem> { let first = map( alt(( delimited(char('P'), digit1, space1), @@ -342,12 +336,12 @@ impl GeneratedAlgebra for AdemAlgebra { }, ); - let second = map(pair(char('b'), space1), |_| (1, 0)); + let second = map(pair(char('b'), space1), |_| BasisElem::new(1, 0)); alt((first, second))(input) } - fn generator_to_string(&self, degree: i32, _idx: usize) -> String { + fn generator_to_string(&self, x: BasisElem) -> String { if self.generic { if degree == 1 { "b".to_string() @@ -359,7 +353,7 @@ impl GeneratedAlgebra for AdemAlgebra { } } - fn generators(&self, degree: i32) -> Vec { + fn generators(&self, degree: i32) -> Vec> { let p = *self.prime(); if degree == 0 { return vec![]; @@ -387,7 +381,7 @@ impl GeneratedAlgebra for AdemAlgebra { ps: vec![degree as u32 / (2 * p - 2)], p_or_sq: *self.prime() != 2, }); - return vec![idx]; + vec![BasisElem::new(degree, idx)]; } else { // I guess we're assuming here that not generic ==> p == 2. There's probably tons of places we assume that though. if degree.count_ones() != 1 { @@ -400,7 +394,7 @@ impl GeneratedAlgebra for AdemAlgebra { ps: vec![degree as u32], p_or_sq: *self.prime() != 2, }); - return vec![idx]; + vec![BasisElem::new(degree, idx)]; } } @@ -408,7 +402,7 @@ impl GeneratedAlgebra for AdemAlgebra { &self, degree: i32, idx: usize, - ) -> Vec<(u32, (i32, usize), (i32, usize))> { + ) -> Vec<(u32, BasisElem, BasisElem)> { if self.generic { self.decompose_basis_element_generic(degree, idx) } else { @@ -418,10 +412,10 @@ impl GeneratedAlgebra for AdemAlgebra { /// We return Adem relations $b^2 = 0$, $P^i P^j = \cdots$ for $i < pj$, and $P^i b P^j = \cdots$ for $i < pj + 1$. It suffices to check these because /// they generate all relations. - fn generating_relations(&self, degree: i32) -> Vec> { + fn generating_relations(&self, degree: i32) -> Vec, BasisElem)>> { if self.generic && degree == 2 { // beta^2 = 0 is an edge case - return vec![vec![(1, (1, 0), (1, 0))]]; + return vec![vec![(1, BasisElem::new(1, 0), BasisElem::new(1, 0))]]; } let p = self.prime(); @@ -456,7 +450,7 @@ impl GeneratedAlgebra for AdemAlgebra { bocksteins: e1 as u32 | ((e2 as u32) << 1), p_or_sq: *self.prime() != 2, }); - relation.push((c as u32, (degree, idx), (0, 0))); + relation.push((c as u32, BasisElem::new(degree, idx), BasisElem::new(0, 0))); } } result.push(relation); @@ -678,17 +672,17 @@ impl AdemAlgebra { } } - pub fn basis_element_from_index(&self, degree: i32, idx: usize) -> &AdemBasisElement { - &self.basis_table[degree as usize][idx] + pub fn basis_element_from_index(&self, b: BasisElem) -> &AdemBasisElement { + &self.basis_table[b.degree() as usize][b.index()] } - pub fn try_basis_element_to_index(&self, elt: &AdemBasisElement) -> Option { + pub fn try_basis_element_to_index(&self, elt: &AdemBasisElement) -> Option> { self.basis_element_to_index_map[elt.degree as usize] .get(elt) - .copied() + .map(|&i| BasisElem::new(elt.degree, i)) } - pub fn basis_element_to_index(&self, elt: &AdemBasisElement) -> usize { + pub fn basis_element_to_index(&self, elt: &AdemBasisElement) -> BasisElem { self.try_basis_element_to_index(elt) .unwrap_or_else(|| panic!("Didn't find element: {:?}", elt)) } @@ -698,7 +692,7 @@ impl AdemAlgebra { elt: &mut AdemBasisElement, idx: u32, q: u32, - ) -> usize { + ) -> BasisElem { let degree = elt.degree; let bocksteins = elt.bocksteins; for i in 0..idx as usize { @@ -714,7 +708,7 @@ impl AdemAlgebra { } elt.degree = degree; elt.bocksteins = bocksteins; - result + BasisElem::new(elt.degree, result) } fn generate_multiplication_table_2(&self, mut next_degree: i32, max_degree: i32) { @@ -750,7 +744,7 @@ impl AdemAlgebra { ) -> FpVector { let output_dimension = self.dimension(n, i32::max_value()); let mut result = FpVector::new(self.prime(), output_dimension); - let cur_basis_elt = self.basis_element_from_index(n - x, idx); + let cur_basis_elt = self.basis_element_from_index(BasisElem::new(n - x, idx)); let x = x as u32; let mut working_elt = cur_basis_elt.clone(); @@ -760,15 +754,15 @@ impl AdemAlgebra { if cur_basis_elt.ps.is_empty() || x >= 2 * cur_basis_elt.ps[0] { working_elt.ps.insert(0, x); working_elt.degree = n; - let out_idx = self.basis_element_to_index(&working_elt); - result.add_basis_element(out_idx, 1); + let out = self.basis_element_to_index(&working_elt); + result.add_basis_element(out.index(), 1); return result; } // We now want to decompose Sq^x Sq^y = \sum_j *coef* Sq^{x + y - j} Sq^j. let y = working_elt.ps[0]; - let tail_idx = self.tail_of_basis_element_to_index(&mut working_elt, 1, 1); + let tail = self.tail_of_basis_element_to_index(&mut working_elt, 1, 1); for j in 0..=x / 2 { if combinatorics::adem_relation_coefficient(ValidPrime::new(2), x, y, j, 0, 0) == 0 { @@ -778,15 +772,15 @@ impl AdemAlgebra { working_elt.ps[0] = x + y; working_elt.degree += x as i32; // In this case the result is guaranteed to be admissible so we can immediately add it to result - let out_idx = self.basis_element_to_index(&working_elt); - result.add_basis_element(out_idx, 1); + let out = self.basis_element_to_index(&working_elt); + result.add_basis_element(out.index(), 1); continue; } // Now we need to reduce Sqj * (rest of Sqs) // The answer to this is in the table we're currently making. // total degree -> first sq -> idx of rest of squares let rest_reduced = - &self.multiplication_table[(n as u32 - (x + y) + j) as usize][j as usize][tail_idx]; + &self.multiplication_table[(n as u32 - (x + y) + j) as usize][j as usize][tail.index()]; for (i, _coeff) in rest_reduced.iter_nonzero() { // Reduce Sq^{x+y-j} * whatever square using the table in the same degree, larger index // Since we're doing the first squares in decreasing order and x + y - j > x, @@ -860,8 +854,7 @@ impl AdemAlgebra { if elt.bocksteins & 1 == 0 { elt.bocksteins |= 1; elt.degree += 1; - let index = self.basis_element_to_index(&elt); - result.add_basis_element(index, 1); + result.add_basis_element(, 1); } return result; } @@ -875,8 +868,7 @@ impl AdemAlgebra { if elt.bocksteins & 1 == 0 { elt.bocksteins |= 1; elt.degree += 1; - let index = self.basis_element_to_index(&elt); - result.add_basis_element(index, coef); + result.add_basis_element(self.basis_element_to_index(&elt).index(), coef); } } return result; @@ -894,15 +886,14 @@ impl AdemAlgebra { working_elt.bocksteins <<= 1; working_elt.degree = n; - let out_idx = self.basis_element_to_index(&working_elt); - result.add_basis_element(out_idx, 1); + result.add_basis_element(self.basis_element_to_index(&working_elt).index(), 1); return result; } // In other cases, use the Adem relations. let j: u32 = working_elt.ps[0]; - let tail_idx = self.tail_of_basis_element_to_index(&mut working_elt, 1, q as u32); + let tail = self.tail_of_basis_element_to_index(&mut working_elt, 1, q as u32); if b == 0 { // We use P^i P^j = \sum ... P^{i + j - k} P^k @@ -915,13 +906,12 @@ impl AdemAlgebra { // We will never need working_elt in the future. We can leave it messed up working_elt.ps[0] = i + j; working_elt.degree = n; - let new_index = self.basis_element_to_index(&working_elt); - result.add_basis_element(new_index, c); + result.add_basis_element( self.basis_element_to_index(&working_elt).index(), c); continue; } let rest_reduced = &self.multiplication_table - [(n - q * (i + j - k) as i32) as usize][2 * k as usize][tail_idx]; + [(n - q * (i + j - k) as i32) as usize][2 * k as usize][tail.index()]; for (id, coeff) in rest_reduced.iter().enumerate() { let source = &table[2 * (i + j - k) as usize][id]; result.add(source, (c * coeff) % *self.prime()); @@ -933,15 +923,13 @@ impl AdemAlgebra { let c = combinatorics::adem_relation_coefficient(self.prime(), i, j, 0, 1, 0); working_elt.ps[0] = i + j; working_elt.degree = n; - let index = self.basis_element_to_index(&working_elt); - result.add_basis_element(index, c); + result.add_basis_element(self.basis_element_to_index(&working_elt).index(), c); // P^{i + j - k} \beta P^k. Check if there is \beta following P^k if working_elt.bocksteins & 2 == 0 { let c = combinatorics::adem_relation_coefficient(self.prime(), i, j, 0, 0, 1); working_elt.bocksteins ^= 3; // flip the first two bits, so that it now ends with 10 - let index = self.basis_element_to_index(&working_elt); - result.add_basis_element(index, c); + result.add_basis_element(self.basis_element_to_index(&working_elt).index(), c); } for k in 1..=i / (*self.prime()) { @@ -989,57 +977,51 @@ impl AdemAlgebra { &self, mut result: SliceMut, coeff: u32, - r_degree: i32, - r_index: usize, - s_degree: i32, - s_index: usize, + r: BasisElem, + s: BasisElem, excess: i32, ) { - self.multiply( + self.multiply2( result.copy(), coeff, - r_degree, - r_index, - s_degree, - s_index, + r, s excess, true, ); + // Zeroing the rest of the result is a little unexpected, but I don't think it causes trouble? // Can't avoid this unexpected behavior without sacrificing some speed. result .slice_mut( - self.dimension_unstable(r_degree + s_degree, excess), - self.dimension_unstable(r_degree + s_degree, i32::max_value()), + self.dimension_unstable(r.degree() + s.degree(), excess), + self.dimension_unstable(r.degree() + s.degree(), i32::max_value()), ) .set_to_zero(); } - pub fn multiply( + pub fn multiply2( &self, mut result: SliceMut, coeff: u32, - r_degree: i32, - r_index: usize, - s_degree: i32, - s_index: usize, + r: BasisElem, + s: BasisElem, excess: i32, unstable: bool, ) { if coeff == 0 { return; } - assert!(r_index < self.dimension(r_degree, i32::max_value())); - assert!(s_index < self.dimension(s_degree, excess)); + assert!(r.index() < self.dimension(r.degree(), i32::max_value())); + assert!(s.index() < self.dimension(s.degree(), excess)); - if s_degree == 0 { + if s.degree() == 0 { // If s is of length 0 then max_idx "r->P_length" is off the edge of the list and it segfaults. // Avoid this by returning early in this case. - result.add_basis_element(r_index, coeff); + result.add_basis_element(r.index(), coeff); return; } - let r = self.basis_element_from_index(r_degree, r_index); - let s = self.basis_element_from_index(s_degree, s_index); + let r = self.basis_element_from_index(r.degree(), r.index()); + let s = self.basis_element_from_index(r.degree(), s.index()); let mut monomial = AdemBasisElement { degree: r.degree + s.degree, excess: 0, @@ -1158,7 +1140,7 @@ impl AdemAlgebra { { if idx < 0 || stop_early { // Admissible so write monomial to result. - let idx = self.basis_element_to_index(monomial); + let idx = self.basis_element_to_index(monomial).index(); // If excess is too large, quit. It's faster to check this by comparing idx to dimension // than to use fromIndex because fromIndex dereferences a hash map. if unstable && idx >= self.dimension(monomial.degree, excess) { @@ -1171,10 +1153,10 @@ impl AdemAlgebra { idx -= 1; } let idx = idx as usize; - let adm_idx = self.tail_of_basis_element_to_index(monomial, idx as u32 + 1, 1); + let adm = self.tail_of_basis_element_to_index(monomial, idx as u32 + 1, 1); let x = monomial.ps[idx] as i32; let tail_degree = monomial.degree - leading_degree + x; - let reduced_tail = &self.multiplication_table[tail_degree as usize][x as usize][adm_idx]; + let reduced_tail = &self.multiplication_table[tail_degree as usize][x as usize][adm.index()]; let mut new_monomial = AdemBasisElement { degree: monomial.degree, @@ -1185,7 +1167,7 @@ impl AdemAlgebra { }; for (it_idx, _value) in reduced_tail.iter_nonzero() { - let cur_tail_basis_elt = self.basis_element_from_index(tail_degree, it_idx); + let cur_tail_basis_elt = self.basis_element_from_index(BasisElem::new(tail_degree, it_idx); new_monomial.ps.truncate(idx); new_monomial.ps.extend_from_slice(&cur_tail_basis_elt.ps); self.make_mono_admissible_2( @@ -1226,7 +1208,7 @@ impl AdemAlgebra { { if idx < 0 || stop_early { // Admissible so write monomial to result. - let idx = self.basis_element_to_index(monomial); + let idx = self.basis_element_to_index(monomial).index(); if unstable && idx >= self.dimension(monomial.degree, excess) { return; } @@ -1238,12 +1220,12 @@ impl AdemAlgebra { idx -= 1; } let idx = idx as usize; - let adm_idx = self.tail_of_basis_element_to_index(monomial, idx as u32 + 1, q); + let adm = self.tail_of_basis_element_to_index(monomial, idx as u32 + 1, q); // Notice how much we avoid bockstein twiddling here. It's all hidden in multiplication_table =) let x = monomial.ps[idx]; let bx = (x << 1) + b1; let tail_degree = monomial.degree - leading_degree + (q * x + b1) as i32; - let reduced_tail = &self.multiplication_table[tail_degree as usize][bx as usize][adm_idx]; + let reduced_tail = &self.multiplication_table[tail_degree as usize][bx as usize][adm.index()]; let mut new_monomial = AdemBasisElement { degree: monomial.degree, excess: -1, @@ -1253,7 +1235,7 @@ impl AdemAlgebra { }; for (it_idx, it_value) in reduced_tail.iter_nonzero() { - let cur_tail_basis_elt = self.basis_element_from_index(tail_degree, it_idx); + let cur_tail_basis_elt = self.basis_element_from_index(BasisElem::new(tail_degree, it_idx)); new_monomial.ps.truncate(idx); new_monomial.ps.extend_from_slice(&cur_tail_basis_elt.ps); new_monomial.bocksteins = monomial.bocksteins & ((1 << idx) - 1); @@ -1274,10 +1256,11 @@ impl AdemAlgebra { fn decompose_basis_element_2( &self, - degree: i32, - idx: usize, + b: BasisElem ) -> Vec<(u32, (i32, usize), (i32, usize))> { - let b = self.basis_element_from_index(degree, idx); + let degree = b.degree(); + let idx = b.index(); + let b = self.basis_element_from_index(b); if b.ps.len() > 1 { let degree_first = b.ps[0] as i32; let degree_rest = b.degree - b.ps[0] as i32; @@ -1288,14 +1271,14 @@ impl AdemAlgebra { bocksteins: 0, ps: vec![b.ps[0]], p_or_sq: *self.prime() != 2, - }); + }).index(); let idx_rest = self.basis_element_to_index(&AdemBasisElement { degree: degree_rest, excess: 0, bocksteins: 0, ps: ps_rest, p_or_sq: *self.prime() != 2, - }); + }).index(); return vec![(1, (degree_first, idx_first), (degree_rest, idx_rest))]; } let sq = b.ps[0]; @@ -1304,14 +1287,14 @@ impl AdemAlgebra { let second_sq = sq ^ first_sq; let first_degree = first_sq as i32; let second_degree = second_sq as i32; - let first_idx = self.basis_element_to_index(&AdemBasisElement { + let first = self.basis_element_to_index(&AdemBasisElement { degree: first_degree, excess: 0, bocksteins: 0, ps: vec![first_sq], p_or_sq: *self.prime() != 2, }); - let second_idx = self.basis_element_to_index(&AdemBasisElement { + let second = self.basis_element_to_index(&AdemBasisElement { degree: second_degree, excess: 0, bocksteins: 0, @@ -1322,15 +1305,11 @@ impl AdemAlgebra { FpVector::new(ValidPrime::new(2), self.dimension(degree, i32::max_value())); self.multiply_basis_elements( out_vec.as_slice_mut(), - 1, - first_degree, - first_idx, - second_degree, - second_idx, + 1, first, second, i32::max_value(), ); out_vec.set_entry(idx, 0); - let mut result = vec![(1, (first_degree, first_idx), (second_degree, second_idx))]; + let mut result = vec![(1, first, second)]; for (i, _v) in out_vec.iter_nonzero() { result.extend(self.decompose_basis_element_2(degree, i)); } @@ -1339,11 +1318,10 @@ impl AdemAlgebra { fn decompose_basis_element_generic( &self, - degree: i32, - idx: usize, - ) -> Vec<(u32, (i32, usize), (i32, usize))> { + b: BasisElem, + ) -> Vec<(u32, BasisElem, BasisElem,)> { let p = self.prime(); - let b = self.basis_element_from_index(degree, idx); + let b = self.basis_element_from_index(b); let leading_bockstein_idx = 1; // << (b.ps.len()); if b.bocksteins & leading_bockstein_idx != 0 { let mut b_new = b.clone(); @@ -1374,9 +1352,7 @@ impl AdemAlgebra { ps: ps_rest, p_or_sq: *self.prime() != 2, }; - let first_idx = self.basis_element_to_index(&first); - let rest_idx = self.basis_element_to_index(&rest); - return vec![(1, (first_degree, first_idx), (rest_degree, rest_idx))]; + return vec![(1, self.basis_element_to_index(&first), self.basis_element_to_index(&rest))]; } let sq = b.ps[0]; @@ -1393,14 +1369,14 @@ impl AdemAlgebra { let second_sq = sq - first_sq; let first_degree = (first_sq * 2 * (*p - 1)) as i32; let second_degree = (second_sq * 2 * (*p - 1)) as i32; - let first_idx = self.basis_element_to_index(&AdemBasisElement { + let first = self.basis_element_to_index(&AdemBasisElement { degree: first_degree, excess: 0, bocksteins: 0, ps: vec![first_sq], p_or_sq: *self.prime() != 2, }); - let second_idx = self.basis_element_to_index(&AdemBasisElement { + let second = self.basis_element_to_index(&AdemBasisElement { degree: second_degree, excess: 0, bocksteins: 0, @@ -1411,10 +1387,7 @@ impl AdemAlgebra { self.multiply_basis_elements( out_vec.as_slice_mut(), 1, - first_degree, - first_idx, - second_degree, - second_idx, + first, second, i32::max_value(), ); let mut result = Vec::new(); @@ -1423,8 +1396,8 @@ impl AdemAlgebra { let c_inv = fp::prime::inverse(p, *p - c); result.push(( ((*p - 1) * c_inv) % *p, - (first_degree, first_idx), - (second_degree, second_idx), + + first, second, )); out_vec.set_entry(idx, 0); for (i, v) in out_vec.iter_nonzero() { @@ -1434,24 +1407,23 @@ impl AdemAlgebra { result } - pub fn beps_pn(&self, e: u32, x: u32) -> (i32, usize) { + pub fn beps_pn(&self, e: u32, x: u32) -> BasisElem { if x == 0 && e == 1 { - return (1, 0); + return BasisElem::new(1, 0); } else if x == 0 { - return (0, 0); + return BasisElem::new(0, 0); } let p = *self.prime(); let q = if self.generic { 2 * p - 2 } else { 1 }; let degree = (x * q + e) as i32; - let index = self.basis_element_to_index(&AdemBasisElement { + self.basis_element_to_index(&AdemBasisElement { degree, excess: 0, bocksteins: e, ps: vec![x], p_or_sq: *self.prime() != 2, - }); - (degree, index) + }) } } @@ -1508,8 +1480,8 @@ impl AdemAlgebra { // } impl Bialgebra for AdemAlgebra { - fn decompose(&self, op_deg: i32, op_idx: usize) -> Vec<(i32, usize)> { - let elt = &self.basis_table[op_deg as usize][op_idx]; + fn decompose(&self, x: BasisElem) -> Vec> { + let elt = &self.basis_table[x.degree() as usize][x.index()]; if self.generic { let mut result: Vec<(i32, usize)> = Vec::with_capacity(elt.ps.len() * 2 + 1); let mut bockstein = elt.bocksteins; @@ -1521,7 +1493,7 @@ impl Bialgebra for AdemAlgebra { result.push(self.beps_pn(0, *item)); } if bockstein & 1 == 1 { - result.push((1, 0)); + result.push(BasisElem::new(1, 0)); } result.reverse(); result @@ -1529,32 +1501,31 @@ impl Bialgebra for AdemAlgebra { elt.ps .iter() .rev() - .map(|i| (*i as i32, 0)) + .map(|i| BasisElem::new(*i as i32, 0)) .collect::>() } } - fn coproduct(&self, op_deg: i32, op_idx: usize) -> Vec<(i32, usize, i32, usize)> { + fn coproduct(&self, BasisElem) -> Vec<(BasisElem, BasisElem)> { if self.generic { - if op_deg == 1 { - vec![(1, 0, 0, 0), (0, 0, 1, 0)] + if b.degree() == 1 { + vec![(BasisElem::new(1, 0), BasisElem::new(0, 0)), (BasisElem::new(0, 0), BasisElem::new(1, 0))] } else { let q = *self.prime() * 2 - 2; - let op_deg = op_deg as u32; - assert_eq!(op_deg % q, 0); + let op_deg = b.degree() as u32; + assert_eq!(b.degree() % q, 0); (0..=op_deg / q) .map(|j| { - let first = self.beps_pn(0, j); - let last = self.beps_pn(0, op_deg / q - j); - (first.0, first.1, last.0, last.1) + (self.beps_pn(0, j); + self.beps_pn(0, b.degree() / q - j)) }) .collect::>() } } else { - assert_eq!(op_idx, 0); - (0..=op_deg) - .map(|j| (j, 0, op_deg - j, 0)) + assert_eq!(b.index(), 0); + (0..=b.degree()) + .map(|j| (j, 0, b.degree() - j, 0)) .collect::>() } } diff --git a/ext/crates/algebra/src/algebra/algebra_trait.rs b/ext/crates/algebra/src/algebra/algebra_trait.rs index 05688472b0..5ebf41eedb 100644 --- a/ext/crates/algebra/src/algebra/algebra_trait.rs +++ b/ext/crates/algebra/src/algebra/algebra_trait.rs @@ -216,126 +216,100 @@ pub trait Algebra: std::fmt::Display + Send + Sync { /// Gets the dimension of the algebra in degree `degree`. fn dimension(&self, degree: i32, excess: i32) -> usize; - /// Computes the product `r * s` of the two basis elements, and *adds* the result to `result`. + /// Computes the product `c * r * s` of the two basis elements, and *adds* the result to `result`. /// /// result is not required to be aligned. fn multiply_basis_elements( &self, result: SliceMut, - coeff: u32, - r_degree: i32, - r_idx: usize, - s_degree: i32, - s_idx: usize, + c: u32, + r: BasisElem, + s: BasisElem, excess: i32, ); - /// result and s are not required to be aligned. - fn multiply_basis_element_by_element( - &self, - mut result: SliceMut, - coeff: u32, - r_degree: i32, - r_idx: usize, - s_degree: i32, - s: Slice, - excess: i32, - ) { - let p = self.prime(); - for (i, v) in s.iter_nonzero() { - self.multiply_basis_elements( - result.copy(), - (coeff * v) % *p, - r_degree, - r_idx, - s_degree, - i, - excess, - ); - } - } - - /// result and r are not required to be aligned. - fn multiply_element_by_basis_element( - &self, - mut result: SliceMut, - coeff: u32, - r_degree: i32, - r: Slice, - s_degree: i32, - s_idx: usize, - excess: i32, - ) { - let p = self.prime(); - for (i, v) in r.iter_nonzero() { - self.multiply_basis_elements( - result.copy(), - (coeff * v) % *p, - r_degree, - i, - s_degree, - s_idx, - excess, - ); - } - } - - /// result, r and s are not required to be aligned. - fn multiply_element_by_element( - &self, - mut result: SliceMut, - coeff: u32, - r_degree: i32, - r: Slice, - s_degree: i32, - s: Slice, - excess: i32, - ) { + /// Computes the product `c * r * s` of two general elements, and *adds* the result to `result`. + /// + /// `result` is not required to be aligned. + // Ideally, `Slice` below should be a type parameter eventually. + fn multiply(&self, mut result: SliceMut, c: u32, r: impl Into>, s: impl Into>, excess: i32) { let p = self.prime(); - for (i, v) in s.iter_nonzero() { - self.multiply_element_by_basis_element( - result.copy(), - (coeff * v) % *p, - r_degree, - r, - s_degree, - i, - excess, - ); + match (r.into(), s.into()) { + (Elem::Basis(r), Elem::Basis(s)) => self.multiply_basis_elements(result, c, r, s, excess), + (Elem::Vector(r), Elem::Basis(s)) => { + for (i, v) in r.iter_nonzero() { + self.multiply_basis_elements( + result.copy(), + (c * v) % *p, + BasisElem::new(r.degree(), i), + s, + excess, + ); + } + } + (Elem::Basis(r), Elem::Vector(s)) => { + for (i, v) in s.iter_nonzero() { + self.multiply_basis_elements( + result.copy(), + (c * v) % *p, + r + BasisElem::new(s.degree(), i), + excess, + ); + } + } + (Elem::Vector(r), Elem::Vector(s)) => { + for (i, v) in r.iter_nonzero() { + for (j, w) in s.iter_nonzero() { + self.multiply_basis_elements( + result.copy(), + (c * v * w) % *p, + BasisElem::new(r.degree(), i), + BasisElem::new(s.degree(), j), + excess, + ); + } + } + } } } /// A filtration one element in Ext(k, k) is the same as an indecomposable element of the /// algebra. This function returns a default list of such elements in the format `(name, /// degree, index)` for whom we want to compute products with in the resolutions. - fn default_filtration_one_products(&self) -> Vec<(String, i32, usize)> { + fn default_filtration_one_products(&self) -> Vec<(String, BasisElem)> { Vec::new() } /// Converts a basis element into a string for display. - fn basis_element_to_string(&self, degree: i32, idx: usize) -> String; + fn basis_element_to_string(&self, b: BasisElem) -> String; /// Converts an element into a string for display. - fn element_to_string(&self, degree: i32, element: Slice) -> String { - let mut result = String::new(); - let mut zero = true; - for (idx, value) in element.iter_nonzero() { - zero = false; - if value != 1 { - result.push_str(&format!("{} * ", value)); + fn element_to_string(&self, x: impl Into>) -> String { + match x.into() { + Elem::Basis(b) => self.basis_element_to_string(b), + Elem::Vector(v) => { + let mut result = String::new(); + let mut zero = true; + for (idx, value) in v.iter_nonzero() { + zero = false; + if value != 1 { + result.push_str(&format!("{} * ", value)); + } + let b = self.basis_element_to_string(degree, idx); + result.push_str(&format!("{} + ", b)); + } + if zero { + result.push('0'); + } else { + // Remove trailing " + " + result.pop(); + result.pop(); + result.pop(); + } + result } - let b = self.basis_element_to_string(degree, idx); - result.push_str(&format!("{} + ", b)); - } - if zero { - result.push('0'); - } else { - // Remove trailing " + " - result.pop(); - result.pop(); - result.pop(); } - result } } @@ -346,9 +320,9 @@ pub trait JsonAlgebra: Algebra { /// Converts a JSON object into a basis element. The way basis elements are represented by JSON /// objects is to be specified by the algebra itself, and will be used by module /// specifications. - fn json_to_basis(&self, json: &serde_json::Value) -> anyhow::Result<(i32, usize)>; + fn json_to_basis(&self, json: &serde_json::Value) -> anyhow::Result>; - fn json_from_basis(&self, degree: i32, idx: usize) -> serde_json::Value; + fn json_from_basis(&self, BasisElem) -> serde_json::Value; } /// An algebra with a specified list of generators and generating relations. This data can be used @@ -360,7 +334,7 @@ pub trait GeneratedAlgebra: Algebra { /// /// This method need not be fast, because they will only be performed when constructing the module, /// and will often only involve low dimensional elements. - fn generators(&self, degree: i32) -> Vec; + fn generators(&self, degree: i32) -> Vec>; /// This returns the name of a generator. Note that the index is the index of the generator /// in the list of all basis elements. It is undefined behaviour to call this function with a @@ -370,7 +344,7 @@ pub trait GeneratedAlgebra: Algebra { /// generators might have alternative, more concise names that are preferred. /// /// This function MUST be inverse to `string_to_generator`. - fn generator_to_string(&self, degree: i32, idx: usize) -> String { + fn generator_to_string(&self, BasisElem) -> String { self.basis_element_to_string(degree, idx) } @@ -379,7 +353,7 @@ pub trait GeneratedAlgebra: Algebra { /// /// This function MUST be inverse to `string_to_generator` (and not `basis_element_to_string`). fn string_to_generator<'a, 'b>(&'a self, input: &'b str) - -> nom::IResult<&'b str, (i32, usize)>; + -> nom::IResult<&'b str, BasisElem>; /// Given a non-generator basis element of the algebra, decompose it in terms of algebra /// generators. Recall each basis element is given by a pair $(d, i))$, where $d$ is the degree of @@ -393,12 +367,11 @@ pub trait GeneratedAlgebra: Algebra { /// and will often only involve low dimensional elements. fn decompose_basis_element( &self, - degree: i32, - idx: usize, - ) -> Vec<(u32, (i32, usize), (i32, usize))>; + b: BasisElem, + ) -> Vec<(u32, BasisElem, BasisElem)>; /// Get any relations that the algebra wants checked to ensure the consistency of module. - fn generating_relations(&self, degree: i32) -> Vec>; + fn generating_relations(&self, degree: i32) -> Vec, BasisElem)>>; } #[macro_export] @@ -412,46 +385,13 @@ macro_rules! dispatch_algebra { fn multiply_basis_elements( &self, result: SliceMut, - coeff: u32, - r_degree: i32, - r_idx: usize, - s_degree: i32, - s_idx: usize, - excess: i32, - ); - - fn multiply_basis_element_by_element( - &self, - result: SliceMut, - coeff: u32, - r_degree: i32, - r_idx: usize, - s_degree: i32, - s: Slice, - excess: i32, - ); - - fn multiply_element_by_basis_element( - &self, - result: SliceMut, - coeff: u32, - r_degree: i32, - r: Slice, - s_degree: i32, - s_idx: usize, - excess: i32, - ); - - fn multiply_element_by_element( - &self, - result: SliceMut, - coeff: u32, - r_degree: i32, - r: Slice, - s_degree: i32, - s: Slice, + c: u32, + r: BasisElem, + r: BasisElem, excess: i32, ); + + fn multiply(&self, mut result: SliceMut, c: u32, r: impl Into>, s: impl Into>, excess: i32); fn default_filtration_one_products(&self) -> Vec<(String, i32, usize)>; diff --git a/ext/crates/algebra/src/algebra/bialgebra_trait.rs b/ext/crates/algebra/src/algebra/bialgebra_trait.rs index 27b97ddc14..b4ab32bc95 100644 --- a/ext/crates/algebra/src/algebra/bialgebra_trait.rs +++ b/ext/crates/algebra/src/algebra/bialgebra_trait.rs @@ -1,4 +1,5 @@ use crate::algebra::Algebra; +use crate::algebra::BasisElem; pub trait Bialgebra: Algebra { /// This function decomposes an element of the algebra as a product of elements, each of whose @@ -9,9 +10,9 @@ pub trait Bialgebra: Algebra { /// This structure is motivated by the fact that in the admissible basis for the Adem algebra, /// an element naturally decomposes into a product of Steenrod squares, each of which has an /// easy coproduct formula. - fn decompose(&self, op_deg: i32, op_idx: usize) -> Vec<(i32, usize)>; + fn decompose(&self, BasisElem) -> Vec>; /// Expresses Delta(x) as sum_j (A_{ij} (x) B_{ij}). Here x must be one of the elements /// returned by `decompose`. - fn coproduct(&self, op_deg: i32, op_idx: usize) -> Vec<(i32, usize, i32, usize)>; + fn coproduct(&self, BasisElem) -> Vec<(BasisElem, BasisElem)>; } diff --git a/ext/crates/algebra/src/algebra/field.rs b/ext/crates/algebra/src/algebra/field.rs index c2924c7c68..88c2c6ba94 100644 --- a/ext/crates/algebra/src/algebra/field.rs +++ b/ext/crates/algebra/src/algebra/field.rs @@ -1,4 +1,4 @@ -use crate::algebra::{Algebra, Bialgebra}; +use crate::algebra::{Algebra, Bialgebra, BasisElem, Elem}; use fp::prime::ValidPrime; use fp::vector::{Slice, SliceMut}; @@ -39,36 +39,34 @@ impl Algebra for Field { &self, mut result: SliceMut, coeff: u32, - _r_degree: i32, - _r_idx: usize, - _s_degree: i32, - _s_idx: usize, - _excess: i32, + _r: BasisElem, + _s: BasisElem, ) { result.add_basis_element(0, coeff) } - fn default_filtration_one_products(&self) -> Vec<(String, i32, usize)> { + fn default_filtration_one_products(&self) -> Vec<(String, BasisElem)> { vec![] } /// Converts a basis element into a string for display. - fn basis_element_to_string(&self, degree: i32, _idx: usize) -> String { - assert!(degree == 0); + fn basis_element_to_string(&self, b: BasisElem) -> String { + assert!(b.degree() == 0); "1".to_string() } - fn element_to_string(&self, degree: i32, element: Slice) -> String { - assert!(degree == 0); - format!("{}", element.entry(0)) + fn element_to_string(&self, x: impl Into>) -> String { + let x = x.into() + assert!(x.degree() == 0); + format!("{}", x.coeffs().entry(0)) } } impl Bialgebra for Field { - fn coproduct(&self, _op_deg: i32, _op_idx: usize) -> Vec<(i32, usize, i32, usize)> { - vec![(1, 0, 1, 0)] + fn coproduct(&self, _x: BasisElem) -> Vec<(BasisElem, BasisElem)> { + vec![(BasisElem::new(1, 0), BasisElem::new(1, 0))] } - fn decompose(&self, _op_deg: i32, _op_idx: usize) -> Vec<(i32, usize)> { - vec![(1, 0)] + fn decompose(&self, _x: BasisElem) -> Vec> { + vec![(BasisElem::new(1, 0))] } } diff --git a/ext/crates/algebra/src/algebra/milnor_algebra.rs b/ext/crates/algebra/src/algebra/milnor_algebra.rs index 6350e3afad..2eae9f285e 100644 --- a/ext/crates/algebra/src/algebra/milnor_algebra.rs +++ b/ext/crates/algebra/src/algebra/milnor_algebra.rs @@ -3,7 +3,7 @@ use rustc_hash::FxHashMap as HashMap; use std::sync::Mutex; use crate::algebra::combinatorics; -use crate::algebra::{Algebra, Bialgebra, GeneratedAlgebra}; +use crate::algebra::{Algebra, BasisElem, Elem, VectorElem,Bialgebra, GeneratedAlgebra}; use fp::prime::{integer_power, Binomial, BitflagIterator, ValidPrime}; use fp::vector::{FpVector, Slice, SliceMut}; use once::OnceVec; @@ -190,17 +190,17 @@ impl MilnorAlgebra { } } - pub fn basis_element_from_index(&self, degree: i32, idx: usize) -> &MilnorBasisElement { - &self.basis_table[degree as usize][idx] + pub fn basis_element_from_index(&self, b: BasisElem) -> &MilnorBasisElement { + &self.basis_table[b.degree() as usize][b.index()] } - pub fn try_basis_element_to_index(&self, elt: &MilnorBasisElement) -> Option { + pub fn try_basis_element_to_index(&self, elt: &MilnorBasisElement) -> Option> { self.basis_element_to_index_map[elt.degree as usize] .get(elt) - .copied() + .map(|&i| BasisElem::new(elt.degree, i)) } - pub fn basis_element_to_index(&self, elt: &MilnorBasisElement) -> usize { + pub fn basis_element_to_index(&self, elt: &MilnorBasisElement) -> BasisElem { self.try_basis_element_to_index(elt) .unwrap_or_else(|| panic!("Didn't find element: {:?}", elt)) } @@ -211,7 +211,7 @@ impl Algebra for MilnorAlgebra { self.p } - fn default_filtration_one_products(&self) -> Vec<(String, i32, usize)> { + fn default_filtration_one_products(&self) -> Vec<(String, BasisElem)> { let mut products = Vec::with_capacity(4); let max_degree; if self.generic() { @@ -262,7 +262,7 @@ impl Algebra for MilnorAlgebra { products .into_iter() - .map(|(name, b)| (name, b.degree, self.basis_element_to_index(&b))) + .map(|(name, b)| (name, self.basis_element_to_index(&b))) .collect() } @@ -339,17 +339,15 @@ impl Algebra for MilnorAlgebra { &self, result: SliceMut, coef: u32, - r_degree: i32, - r_idx: usize, - s_degree: i32, - s_idx: usize, + r: BasisElem, + s: BasisElem, _excess: i32, ) { self.multiply( result, coef, - &self.basis_table[r_degree as usize][r_idx], - &self.basis_table[s_degree as usize][s_idx], + &self.basis_table[r.degree() as usize][r.index()], + &self.basis_table[s.degree() as usize][s.index()], ); } @@ -358,21 +356,20 @@ impl Algebra for MilnorAlgebra { &self, result: SliceMut, coef: u32, - r_degree: i32, - r_idx: usize, - s_degree: i32, - s_idx: usize, + r: BasisElem, + s: BasisElem, _excess: i32, ) { result.shift_add( - &self.multiplication_table[r_degree as usize][s_degree as usize][r_idx][s_idx] + &self.multiplication_table[r.degree() as usize][s.degree() as usize][r.index()][s.index()] .as_slice(), coef, ); } - fn basis_element_to_string(&self, degree: i32, idx: usize) -> String { - format!("{}", self.basis_table[degree as usize][idx]) + fn basis_element_to_string(&self, + x: BasisElem,) -> String { + format!("{}", self.basis_table[x.degree() as usize][x.index()]) } } @@ -382,7 +379,7 @@ impl JsonAlgebra for MilnorAlgebra { "milnor" } - fn json_to_basis(&self, json: &Value) -> anyhow::Result<(i32, usize)> { + fn json_to_basis(&self, json: &Value) -> anyhow::Result> { let xi_degrees = combinatorics::xi_degrees(self.prime()); let tau_degrees = combinatorics::tau_degrees(self.prime()); @@ -414,11 +411,11 @@ impl JsonAlgebra for MilnorAlgebra { p_part, degree, }; - Ok((degree, self.basis_element_to_index(&m))) + Ok(self.basis_element_to_index(&m)) } - fn json_from_basis(&self, degree: i32, index: usize) -> Value { - let b = self.basis_element_from_index(degree, index); + fn json_from_basis(&self, b: BasisElem) -> Value { + let b = self.basis_element_from_index(b); if self.generic() { let mut q_part = b.q_part; let mut q_list = Vec::with_capacity(q_part.count_ones() as usize); @@ -436,7 +433,7 @@ impl JsonAlgebra for MilnorAlgebra { impl GeneratedAlgebra for MilnorAlgebra { // Same implementation as AdemAlgebra - fn string_to_generator<'a, 'b>(&'a self, input: &'b str) -> IResult<&'b str, (i32, usize)> { + fn string_to_generator<'a, 'b>(&'a self, input: &'b str) -> IResult<&'b str, BasisElem> { let first = map( alt(( delimited(char('P'), digit1, space1), @@ -448,24 +445,24 @@ impl GeneratedAlgebra for MilnorAlgebra { }, ); - let second = map(pair(char('b'), space1), |_| (1, 0)); + let second = map(pair(char('b'), space1), |_| BasisElem::new(1, 0)); alt((first, second))(input) } - fn generator_to_string(&self, degree: i32, _idx: usize) -> String { + fn generator_to_string(&self, b: BasisElem) -> String { if self.generic() { - if degree == 1 { + if b.degree() == 1 { "b".to_string() } else { - format!("P{}", degree as u32 / (2 * (*self.prime()) - 2)) + format!("P{}", b.degree() as u32 / (2 * (*self.prime()) - 2)) } } else { - format!("Sq{}", degree) + format!("Sq{}", b.degree()) } } - fn generators(&self, degree: i32) -> Vec { + fn generators(&self, degree: i32) -> Vec> { if degree == 0 { return vec![]; } @@ -498,14 +495,13 @@ impl GeneratedAlgebra for MilnorAlgebra { q_part: 0, p_part: vec![(degree as u32 / q) as PPartEntry], }); - return vec![idx]; + return vec![BasisElem::new(degree, idx)]; } fn decompose_basis_element( &self, - degree: i32, - idx: usize, - ) -> Vec<(u32, (i32, usize), (i32, usize))> { + b: BasisElem, + ) -> Vec<(u32, BasisElem, BasisElem)> { let basis = &self.basis_table[degree as usize][idx]; // If qpart = 0, return self if basis.q_part == 0 { @@ -515,10 +511,10 @@ impl GeneratedAlgebra for MilnorAlgebra { } } - fn generating_relations(&self, degree: i32) -> Vec> { + fn generating_relations(&self, degree: i32) -> Vec, BasisElem)>> { if self.generic() && degree == 2 { // beta^2 = 0 is an edge case - return vec![vec![(1, (1, 0), (1, 0))]]; + return vec![vec![(1, BasisElem::new(1, 0), BasisElem::new(1, 0))]]; } let p = self.prime(); let inadmissible_pairs = combinatorics::inadmissible_pairs(p, self.generic(), degree); @@ -527,12 +523,10 @@ impl GeneratedAlgebra for MilnorAlgebra { let mut relation = Vec::new(); // Adem relation. Sometimes these don't exist because of profiles. Then just ignore it. (|| { - let (first_degree, first_index) = self.try_beps_pn(0, x as PPartEntry)?; - let (second_degree, second_index) = self.try_beps_pn(b, y as PPartEntry)?; relation.push(( *p - 1, - (first_degree, first_index), - (second_degree, second_index), + self.try_beps_pn(0, x as PPartEntry)?, + self.try_beps_pn(b, y as PPartEntry)?, )); for e1 in 0..=b { let e2 = b - e1; @@ -549,7 +543,7 @@ impl GeneratedAlgebra for MilnorAlgebra { relation.push(( c, self.try_beps_pn(e1, (x + y) as PPartEntry)?, - (e2 as i32, 0), + BasisElem::new(e2 as i32, 0), )); continue; } @@ -721,7 +715,7 @@ impl MilnorAlgebra { // Multiplication logic impl MilnorAlgebra { - fn try_beps_pn(&self, e: u32, x: PPartEntry) -> Option<(i32, usize)> { + fn try_beps_pn(&self, e: u32, x: PPartEntry) -> Option> { let q = self.q() as u32; let degree = (q * x as u32 + e) as i32; self.try_basis_element_to_index(&MilnorBasisElement { @@ -729,10 +723,9 @@ impl MilnorAlgebra { q_part: e, p_part: vec![x as PPartEntry], }) - .map(|index| (degree, index)) } - fn beps_pn(&self, e: u32, x: PPartEntry) -> (i32, usize) { + fn beps_pn(&self, e: u32, x: PPartEntry) -> BasisElem { self.try_beps_pn(e, x).unwrap() } @@ -856,16 +849,15 @@ impl MilnorAlgebra { &self, mut res: SliceMut, coef: u32, - r_deg: i32, - r: Slice, + r: VectorElem, m2: &MilnorBasisElement, mut allocation: PPartAllocation, ) -> PPartAllocation { - for (i, c) in r.iter_nonzero() { + for (i, c) in r.coeffs().iter_nonzero() { allocation = self.multiply_with_allocation( res.copy(), coef * c, - self.basis_element_from_index(r_deg, i), + self.basis_element_from_index(BasisElem::new(r.degree(), i)), m2, allocation, ); diff --git a/ext/crates/algebra/src/algebra/mod.rs b/ext/crates/algebra/src/algebra/mod.rs index 12a04c4b11..87ad9f2970 100644 --- a/ext/crates/algebra/src/algebra/mod.rs +++ b/ext/crates/algebra/src/algebra/mod.rs @@ -5,17 +5,17 @@ mod algebra_trait; mod bialgebra_trait; pub mod field; pub mod milnor_algebra; -mod polynomial_algebra; +//mod polynomial_algebra; mod steenrod_algebra; pub use adem_algebra::{AdemAlgebra, AdemAlgebraT}; #[cfg(feature = "json")] pub use algebra_trait::JsonAlgebra; -pub use algebra_trait::{BasisElem, Algebra, GeneratedAlgebra}; +pub use algebra_trait::{BasisElem, VectorElem, Elem, Algebra, GeneratedAlgebra}; pub use bialgebra_trait::Bialgebra; pub use field::Field; pub use milnor_algebra::{MilnorAlgebra, MilnorAlgebraT}; -pub use polynomial_algebra::{ +/*pub use polynomial_algebra::{ PolynomialAlgebra, PolynomialAlgebraMonomial, PolynomialAlgebraTableEntry, -}; +};*/ pub use steenrod_algebra::{AlgebraType, SteenrodAlgebra, SteenrodAlgebraBorrow, SteenrodAlgebraT}; diff --git a/ext/crates/algebra/src/algebra/steenrod_algebra.rs b/ext/crates/algebra/src/algebra/steenrod_algebra.rs index 9e6478ccd5..8a5b28f2f0 100644 --- a/ext/crates/algebra/src/algebra/steenrod_algebra.rs +++ b/ext/crates/algebra/src/algebra/steenrod_algebra.rs @@ -1,6 +1,7 @@ #[cfg(feature = "json")] use crate::algebra::JsonAlgebra; use crate::algebra::{ + BasisElem, VectorElem, Elem, AdemAlgebra, AdemAlgebraT, Algebra, Bialgebra, GeneratedAlgebra, MilnorAlgebra, MilnorAlgebraT, }; use crate::dispatch_algebra; @@ -241,8 +242,8 @@ dispatch_algebra!(SteenrodAlgebra, dispatch_steenrod); impl JsonAlgebra for SteenrodAlgebra { dispatch_steenrod! { fn prefix(&self) -> &str; - fn json_to_basis(&self, json: &serde_json::Value) -> anyhow::Result<(i32, usize)>; - fn json_from_basis(&self, degree: i32, idx: usize) -> serde_json::Value; + fn json_to_basis(&self, json: &serde_json::Value) -> anyhow::Result>; + fn json_from_basis(&self, b: BasisElem) -> serde_json::Value; } } @@ -251,15 +252,15 @@ impl JsonAlgebra for SteenrodAlgebra { impl GeneratedAlgebra for SteenrodAlgebra { dispatch_steenrod! { fn generators(&self, degree: i32) -> Vec; - fn generator_to_string(&self, degree: i32, idx: usize) -> String; - fn string_to_generator<'a, 'b>(&'a self, input: &'b str) -> nom::IResult<&'b str, (i32, usize)>; + fn generator_to_string(&self, b: BasisElem) -> String; + fn string_to_generator<'a, 'b>(&'a self, input: &'b str) -> nom::IResult<&'b str, BasisElem>; fn decompose_basis_element( &self, degree: i32, idx: usize, - ) -> Vec<(u32, (i32, usize), (i32, usize))>; + ) -> Vec<(u32, BasisElem, BasisElem)>; - fn generating_relations(&self, degree: i32) -> Vec>; + fn generating_relations(&self, degree: i32) -> Vec, BasisElem)>>; } } diff --git a/ext/crates/algebra/src/lib.rs b/ext/crates/algebra/src/lib.rs index dfafef2d78..28aa47763e 100644 --- a/ext/crates/algebra/src/lib.rs +++ b/ext/crates/algebra/src/lib.rs @@ -1,8 +1,8 @@ mod algebra; -pub mod change_of_basis; -pub mod module; -pub mod steenrod_evaluator; -pub mod steenrod_parser; +//pub mod change_of_basis; +//pub mod module; +//pub mod steenrod_evaluator; +//pub mod steenrod_parser; //pub mod dense_bigraded_algebra; pub use crate::algebra::*;