Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions ext/crates/algebra/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ bivec = { path = "../bivec" }
fp = { path = "../fp", default-features = false }
maybe-rayon = { path = "../maybe-rayon" }
once = { path = "../once" }
sseq = { path = "../sseq", default-features = false }

anyhow = "1.0.98"
auto_impl = "1.3.0"
Expand All @@ -36,8 +37,8 @@ rstest = "0.25.0"
[features]
default = ["odd-primes"]
cache-multiplication = []
concurrent = ["fp/concurrent", "maybe-rayon/concurrent"]
odd-primes = ["fp/odd-primes"]
concurrent = ["fp/concurrent", "maybe-rayon/concurrent", "sseq/concurrent"]
odd-primes = ["fp/odd-primes", "sseq/odd-primes"]

[[bench]]
name = "milnor"
Expand Down
22 changes: 14 additions & 8 deletions ext/crates/algebra/src/algebra/adem_algebra.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use fp::{
use itertools::Itertools;
use once::OnceVec;
use rustc_hash::FxHashMap as HashMap;
use sseq::coordinates::MultiDegree;

#[cfg(doc)]
use crate::algebra::SteenrodAlgebra;
Expand Down Expand Up @@ -222,7 +223,8 @@ impl Algebra for AdemAlgebra {
.collect()
}

fn compute_basis(&self, max_degree: i32) {
fn compute_basis(&self, max_degree: impl Into<MultiDegree<1>>) {
let max_degree = i32::from(max_degree.into());
if self.generic {
self.generate_basis_generic(max_degree);
self.generate_basis_element_to_index_map(max_degree);
Expand All @@ -238,7 +240,8 @@ impl Algebra for AdemAlgebra {
}
}

fn dimension(&self, degree: i32) -> usize {
fn dimension(&self, degree: impl Into<MultiDegree<1>>) -> usize {
let degree = i32::from(degree.into());
if degree < 0 {
0
} else {
Expand All @@ -250,24 +253,27 @@ impl Algebra for AdemAlgebra {
&self,
result: FpSliceMut,
coeff: u32,
r_degree: i32,
r_degree: impl Into<MultiDegree<1>>,
r_index: usize,
s_degree: i32,
s_degree: impl Into<MultiDegree<1>>,
s_index: usize,
) {
self.multiply_inner(
result,
coeff,
r_degree,
i32::from(r_degree.into()),
r_index,
s_degree,
i32::from(s_degree.into()),
s_index,
i32::MAX,
);
}

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, degree: impl Into<MultiDegree<1>>, idx: usize) -> String {
format!(
"{}",
self.basis_element_from_index(i32::from(degree.into()), idx)
)
}

fn basis_element_from_string(&self, mut elt: &str) -> Option<(i32, usize)> {
Expand Down
53 changes: 37 additions & 16 deletions ext/crates/algebra/src/algebra/algebra_trait.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use fp::{
vector::{FpSlice, FpSliceMut},
};
use itertools::Itertools;
use sseq::coordinates::MultiDegree;

/// A graded algebra over $\mathbb{F}_p$.
///
Expand All @@ -20,8 +21,16 @@ use itertools::Itertools;
/// this function before performing other operations at that degree.
///
/// Algebras may have a distinguished set of generators; see [`GeneratedAlgebra`].
#[enum_dispatch]
pub trait Algebra: std::fmt::Display + Send + Sync + 'static {
///
/// # Grading
/// The trait is generic over the number of gradings `N` (defaulting to `1`, the singly-graded
/// case). Degrees are [`MultiDegree<N>`]; every degree *input* is taken as `impl Into<MultiDegree<N>>`
/// so callers in the singly-graded world keep passing bare `i32`s (via `From<i32> for
/// MultiDegree<1>`). `MultiDegree<2>` is a bidegree, used by `Ext` (see `ext::ext_algebra`).
///
/// `Algebra` cannot use `#[enum_dispatch]` because it is now generic; `SteenrodAlgebra` dispatches
/// it by hand (see `dispatch_steenrod!`).
pub trait Algebra<const N: usize = 1>: std::fmt::Display + Send + Sync + 'static {
/// A name for the algebra to use in serialization operations. This defaults to "" for algebras
/// that don't care about this problem.
fn prefix(&self) -> &str {
Expand Down Expand Up @@ -49,10 +58,10 @@ pub trait Algebra: std::fmt::Display + Send + Sync + 'static {
///
/// This function must be idempotent and cheap to call again with the
/// same argument.
fn compute_basis(&self, degree: i32);
fn compute_basis(&self, degree: impl Into<MultiDegree<N>>);

/// Returns the dimension of the algebra in degree `degree`.
fn dimension(&self, degree: i32) -> usize;
fn dimension(&self, degree: impl Into<MultiDegree<N>>) -> usize;

/// Computes the product `r * s` of two basis elements, and adds the
/// result to `result`.
Expand All @@ -62,9 +71,9 @@ pub trait Algebra: std::fmt::Display + Send + Sync + 'static {
&self,
result: FpSliceMut,
coeff: u32,
r_degree: i32,
r_degree: impl Into<MultiDegree<N>>,
r_idx: usize,
s_degree: i32,
s_degree: impl Into<MultiDegree<N>>,
s_idx: usize,
);

Expand All @@ -76,12 +85,14 @@ pub trait Algebra: std::fmt::Display + Send + Sync + 'static {
&self,
mut result: FpSliceMut,
coeff: u32,
r_degree: i32,
r_degree: impl Into<MultiDegree<N>>,
r_idx: usize,
s_degree: i32,
s_degree: impl Into<MultiDegree<N>>,
s: FpSlice,
) {
let p = self.prime();
let r_degree = r_degree.into();
let s_degree = s_degree.into();
for (i, v) in s.iter_nonzero() {
self.multiply_basis_elements(
result.copy(),
Expand All @@ -102,12 +113,14 @@ pub trait Algebra: std::fmt::Display + Send + Sync + 'static {
&self,
mut result: FpSliceMut,
coeff: u32,
r_degree: i32,
r_degree: impl Into<MultiDegree<N>>,
r: FpSlice,
s_degree: i32,
s_degree: impl Into<MultiDegree<N>>,
s_idx: usize,
) {
let p = self.prime();
let r_degree = r_degree.into();
let s_degree = s_degree.into();
for (i, v) in r.iter_nonzero() {
self.multiply_basis_elements(
result.copy(),
Expand All @@ -128,12 +141,14 @@ pub trait Algebra: std::fmt::Display + Send + Sync + 'static {
&self,
mut result: FpSliceMut,
coeff: u32,
r_degree: i32,
r_degree: impl Into<MultiDegree<N>>,
r: FpSlice,
s_degree: i32,
s_degree: impl Into<MultiDegree<N>>,
s: FpSlice,
) {
let p = self.prime();
let r_degree = r_degree.into();
let s_degree = s_degree.into();
for (i, v) in s.iter_nonzero() {
self.multiply_element_by_basis_element(
result.copy(),
Expand All @@ -158,7 +173,7 @@ pub trait Algebra: std::fmt::Display + Send + Sync + 'static {
}

/// 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, degree: impl Into<MultiDegree<N>>, idx: usize) -> String;

/// Non-panicking variant of [`Self::basis_element_to_string`]. Returns `None`
/// when `degree` is negative or `idx` is out of range for that degree, instead
Expand All @@ -167,8 +182,13 @@ pub trait Algebra: std::fmt::Display + Send + Sync + 'static {
/// These are the usual failure conditions. An implementation of
/// [`Self::basis_element_to_string`] that can fail for other reasons should override both
/// that method and this one, keeping them consistent.
fn try_basis_element_to_string(&self, degree: i32, idx: usize) -> Option<String> {
if degree < 0 {
fn try_basis_element_to_string(
&self,
degree: impl Into<MultiDegree<N>>,
idx: usize,
) -> Option<String> {
let degree = degree.into();
if degree.t() < 0 {
return None;
}
self.compute_basis(degree);
Expand All @@ -187,7 +207,8 @@ pub trait Algebra: std::fmt::Display + Send + Sync + 'static {
fn basis_element_from_string(&self, elt: &str) -> Option<(i32, usize)>;

/// Converts a general element into a string for display.
fn element_to_string(&self, degree: i32, element: FpSlice) -> String {
fn element_to_string(&self, degree: impl Into<MultiDegree<N>>, element: FpSlice) -> String {
let degree = degree.into();
let result = element
.iter_nonzero()
.map(|(idx, value)| {
Expand Down
19 changes: 10 additions & 9 deletions ext/crates/algebra/src/algebra/field.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use fp::{
prime::ValidPrime,
vector::{FpSlice, FpSliceMut},
};
use sseq::coordinates::MultiDegree;

use crate::algebra::{Algebra, Bialgebra};

Expand Down Expand Up @@ -34,19 +35,19 @@ impl Algebra for Field {
self.prime
}

fn compute_basis(&self, _degree: i32) {}
fn compute_basis(&self, _degree: impl Into<MultiDegree<1>>) {}

fn dimension(&self, degree: i32) -> usize {
usize::from(degree == 0)
fn dimension(&self, degree: impl Into<MultiDegree<1>>) -> usize {
usize::from(i32::from(degree.into()) == 0)
}

fn multiply_basis_elements(
&self,
mut result: FpSliceMut,
coeff: u32,
_r_degree: i32,
_r_degree: impl Into<MultiDegree<1>>,
_r_idx: usize,
_s_degree: i32,
_s_degree: impl Into<MultiDegree<1>>,
_s_idx: usize,
) {
result.add_basis_element(0, coeff)
Expand All @@ -56,13 +57,13 @@ impl Algebra for Field {
vec![]
}

fn basis_element_to_string(&self, degree: i32, _idx: usize) -> String {
assert!(degree == 0);
fn basis_element_to_string(&self, degree: impl Into<MultiDegree<1>>, _idx: usize) -> String {
assert!(i32::from(degree.into()) == 0);
"1".to_string()
}

fn element_to_string(&self, degree: i32, element: FpSlice) -> String {
assert!(degree == 0);
fn element_to_string(&self, degree: impl Into<MultiDegree<1>>, element: FpSlice) -> String {
assert!(i32::from(degree.into()) == 0);
format!("{}", element.entry(0))
}

Expand Down
40 changes: 26 additions & 14 deletions ext/crates/algebra/src/algebra/milnor_algebra.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use itertools::Itertools;
use once::OnceVec;
use rustc_hash::FxHashMap as HashMap;
use serde::{Deserialize, Serialize};
use sseq::coordinates::MultiDegree;

use crate::algebra::{Algebra, Bialgebra, GeneratedAlgebra, UnstableAlgebra, combinatorics};

Expand Down Expand Up @@ -416,7 +417,8 @@ impl Algebra for MilnorAlgebra {
.collect()
}

fn compute_basis(&self, max_degree: i32) {
fn compute_basis(&self, max_degree: impl Into<MultiDegree<1>>) {
let max_degree = i32::from(max_degree.into());
self.compute_ppart(max_degree);

if self.generic() {
Expand Down Expand Up @@ -470,7 +472,8 @@ impl Algebra for MilnorAlgebra {
}
}

fn dimension(&self, degree: i32) -> usize {
fn dimension(&self, degree: impl Into<MultiDegree<1>>) -> usize {
let degree = i32::from(degree.into());
if degree < 0 {
return 0;
}
Expand All @@ -482,16 +485,16 @@ impl Algebra for MilnorAlgebra {
&self,
result: FpSliceMut,
coef: u32,
r_degree: i32,
r_degree: impl Into<MultiDegree<1>>,
r_idx: usize,
s_degree: i32,
s_degree: impl Into<MultiDegree<1>>,
s_idx: usize,
) {
self.multiply(
result,
coef,
self.basis_element_from_index(r_degree, r_idx),
self.basis_element_from_index(s_degree, s_idx),
self.basis_element_from_index(i32::from(r_degree.into()), r_idx),
self.basis_element_from_index(i32::from(s_degree.into()), s_idx),
);
}

Expand All @@ -500,11 +503,13 @@ impl Algebra for MilnorAlgebra {
&self,
mut result: FpSliceMut,
coef: u32,
r_degree: i32,
r_degree: impl Into<MultiDegree<1>>,
r_idx: usize,
s_degree: i32,
s_degree: impl Into<MultiDegree<1>>,
s_idx: usize,
) {
let r_degree = i32::from(r_degree.into());
let s_degree = i32::from(s_degree.into());
result.add(
self.multiplication_table[r_degree as usize][s_degree as usize][r_idx][s_idx]
.as_slice(),
Expand All @@ -516,12 +521,14 @@ impl Algebra for MilnorAlgebra {
&self,
mut result: FpSliceMut,
coeff: u32,
r_degree: i32,
r_degree: impl Into<MultiDegree<1>>,
r_idx: usize,
s_degree: i32,
s_degree: impl Into<MultiDegree<1>>,
s: FpSlice,
) {
let p = self.prime();
let r_degree = i32::from(r_degree.into());
let s_degree = i32::from(s_degree.into());
let r = self.basis_element_from_index(r_degree, r_idx);
PPartAllocation::with_local(|mut allocation| {
for (i, v) in s.iter_nonzero() {
Expand All @@ -542,11 +549,13 @@ impl Algebra for MilnorAlgebra {
&self,
mut res: FpSliceMut,
coef: u32,
r_deg: i32,
r_deg: impl Into<MultiDegree<1>>,
r: FpSlice,
s_deg: i32,
s_deg: impl Into<MultiDegree<1>>,
s: FpSlice,
) {
let r_deg = i32::from(r_deg.into());
let s_deg = i32::from(s_deg.into());
PPartAllocation::with_local(|mut allocation| {
for (i, c) in r.iter_nonzero() {
allocation = self.multiply_basis_by_element_with_allocation(
Expand All @@ -562,8 +571,11 @@ impl Algebra for MilnorAlgebra {
})
}

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, degree: impl Into<MultiDegree<1>>, idx: usize) -> String {
format!(
"{}",
self.basis_element_from_index(i32::from(degree.into()), idx)
)
}

fn basis_element_from_string(&self, elt: &str) -> Option<(i32, usize)> {
Expand Down
Loading