Skip to content

Commit

Permalink
Remove enum_dispatch dependency
Browse files Browse the repository at this point in the history
enum_dispatch is somewhat broken, and we need to write manual dispatch
code for pyo3 anyway (and have written similar dispatch code for many
other things).

rust-lang/rust#83527 would make it more
convenient to make our manual dispatch code generic.
  • Loading branch information
dalcde committed Apr 12, 2021
1 parent 4ae987b commit ff7d720
Show file tree
Hide file tree
Showing 4 changed files with 109 additions and 87 deletions.
3 changes: 0 additions & 3 deletions ext/crates/algebra/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,9 @@ crate-type = ["cdylib", "rlib"]
[dependencies]
serde_json = { version = "1.0.0", optional = true }
serde = { version = "1.0.0", features = ["derive"], optional = true }
# Cannot upgrade due to https://gitlab.com/antonok/enum_dispatch/-/issues/34
enum_dispatch = "=0.3.2"
itertools = "0.9.0"
lazy_static = "1.2.0"
nom = { version = "5.0.0", default-features = false, features = ["alloc"] }
syn = "=1.0.57"
rustc-hash = "1.1.0"


Expand Down
122 changes: 52 additions & 70 deletions ext/crates/algebra/src/algebra/algebra_trait.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
use fp::prime::ValidPrime;
use fp::vector::{Slice, SliceMut};

use enum_dispatch::enum_dispatch;

/// 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
Expand All @@ -17,7 +15,6 @@ use enum_dispatch::enum_dispatch;
/// The algebra should also come with a specified choice of algebra generators, which are
/// necessarily basis elements. It gives us a simpler way of describing finite modules by only
/// specifying the action of the generators.
#[enum_dispatch]
pub trait Algebra: std::fmt::Display + Send + Sync + 'static {
/// Returns the prime the algebra is over.
fn prime(&self) -> ValidPrime;
Expand Down Expand Up @@ -155,21 +152,19 @@ pub trait Algebra: std::fmt::Display + Send + Sync + 'static {
}

#[cfg(feature = "json")]
#[enum_dispatch]
pub trait JsonAlgebra: Algebra {
fn prefix(&self) -> &str;

/// 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) -> error::Result<(i32, usize)>;
fn json_to_basis(&self, json: serde_json::Value) -> error::Result<(i32, usize)>;

fn json_from_basis(&self, _degree: i32, _idx: usize) -> serde_json::Value;
fn json_from_basis(&self, degree: i32, idx: usize) -> serde_json::Value;
}

/// An algebra with a specified list of generators and generating relations. This data can be used
/// to specify modules by specifying the actions of the generators.
#[enum_dispatch]
pub trait GeneratedAlgebra: Algebra {
/// Given a degree `degree`, the function returns a list of algebra generators in that degree.
/// This return value is the list of indices of the basis elements that are generators. The
Expand Down Expand Up @@ -220,75 +215,62 @@ pub trait GeneratedAlgebra: Algebra {

#[macro_export]
macro_rules! dispatch_algebra {
($dispatch_macro : ident) => {
fn prime(&self) -> fp::prime::ValidPrime {
$dispatch_macro!(prime, self,)
}

fn compute_basis(&self, degree: i32) {
$dispatch_macro!(compute_basis, self, degree)
}

fn dimension(&self, degree: i32, excess: i32) -> usize {
$dispatch_macro!(dimension, self, degree, excess)
}

fn multiply_basis_elements(
&self,
result: &mut FpVector,
coeff: u32,
r_deg: i32,
r_idx: usize,
s_deg: i32,
s_idx: usize,
excess: i32,
) {
$dispatch_macro!(
multiply_basis_elements,
self,
result,
coeff,
r_deg,
r_idx,
s_deg,
s_idx,
excess
)
}
($struct:ty, $dispatch_macro: ident) => {
impl Algebra for $struct {
$dispatch_macro! {
fn prime(&self) -> ValidPrime;
fn compute_basis(&self, degree: i32);
fn dimension(&self, degree: i32, excess: i32) -> usize;
fn multiply_basis_elements(
&self,
result: SliceMut,
coeff: u32,
r_degree: i32,
r_idx: usize,
s_degree: i32,
s_idx: usize,
excess: i32,
);

fn json_to_basis(&self, json: serde_json::Value) -> error::Result<(i32, usize)> {
$dispatch_macro!(json_to_basis, self, json)
}
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 json_from_basis(&self, degree: i32, idx: usize) -> serde_json::Value {
$dispatch_macro!(json_from_basis, self, degree, idx)
}
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 basis_element_to_string(&self, degree: i32, idx: usize) -> String {
$dispatch_macro!(basis_element_to_string, self, degree, idx)
}
fn multiply_element_by_element(
&self,
result: SliceMut,
coeff: u32,
r_degree: i32,
r: Slice,
s_degree: i32,
s: Slice,
excess: i32,
);

fn generators(&self, degree: i32) -> Vec<usize> {
$dispatch_macro!(generators, self, degree)
}
fn default_filtration_one_products(&self) -> Vec<(String, i32, usize)>;

fn string_to_generator<'a, 'b>(
&'a self,
input: &'b str,
) -> nom::IResult<&'b str, (i32, usize)> {
$dispatch_macro!(string_to_generator, self, input)
}

fn decompose_basis_element(
&self,
degree: i32,
idx: usize,
) -> Vec<(u32, (i32, usize), (i32, usize))> {
$dispatch_macro!(decompose_basis_element, self, degree, idx)
}
fn basis_element_to_string(&self, degree: i32, idx: usize) -> String;

fn generating_relations(&self, degree: i32) -> Vec<Vec<(u32, (i32, usize), (i32, usize))>> {
$dispatch_macro!(generating_relations, self, degree)
fn element_to_string(&self, degree: i32, element: Slice) -> String;
}
}
};
}
67 changes: 56 additions & 11 deletions ext/crates/algebra/src/algebra/steenrod_algebra.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@ use crate::algebra::JsonAlgebra;
use crate::algebra::{
AdemAlgebra, AdemAlgebraT, Algebra, Bialgebra, GeneratedAlgebra, MilnorAlgebra, MilnorAlgebraT,
};
use crate::dispatch_algebra;
use fp::prime::ValidPrime;
use fp::vector::{Slice, SliceMut};

use enum_dispatch::enum_dispatch;
#[cfg(feature = "json")]
use {serde::Deserialize, serde_json::Value};

// This is here so that the Python bindings can use modules defined for SteenrodAlgebraT with their own algebra enum.
// This is here so that the Python bindings can use modules defined aor SteenrodAlgebraT with their own algebra enum.
// In order for things to work SteenrodAlgebraT cannot implement Algebra.
// Otherwise, the algebra enum for our bindings will see an implementation clash.
pub trait SteenrodAlgebraT: Send + Sync + 'static + Algebra {
Expand All @@ -22,18 +22,21 @@ pub enum SteenrodAlgebraBorrow<'a> {
BorrowMilnor(&'a MilnorAlgebra),
}

#[cfg(feature = "json")]
#[enum_dispatch(Algebra, GeneratedAlgebra, JsonAlgebra)]
pub enum SteenrodAlgebra {
AdemAlgebra,
MilnorAlgebra,
AdemAlgebra(AdemAlgebra),
MilnorAlgebra(MilnorAlgebra),
}

#[cfg(not(feature = "json"))]
#[enum_dispatch(Algebra, GeneratedAlgebra)]
pub enum SteenrodAlgebra {
AdemAlgebra,
MilnorAlgebra,
impl From<AdemAlgebra> for SteenrodAlgebra {
fn from(adem: AdemAlgebra) -> SteenrodAlgebra {
SteenrodAlgebra::AdemAlgebra(adem)
}
}

impl From<MilnorAlgebra> for SteenrodAlgebra {
fn from(milnor: MilnorAlgebra) -> SteenrodAlgebra {
SteenrodAlgebra::MilnorAlgebra(milnor)
}
}

impl std::fmt::Display for SteenrodAlgebra {
Expand Down Expand Up @@ -197,3 +200,45 @@ impl std::error::Error for InvalidAlgebraError {
"Invalid algebra supplied"
}
}

macro_rules! dispatch_steenrod {
() => {};
($vis:vis fn $method:ident$(<$($lt:lifetime),+>)?(&$($lt2:lifetime)?self$(, $arg:ident: $ty:ty )*$(,)?) $(-> $ret:ty)?; $($tail:tt)*) => {
$vis fn $method$(<$($lt),+>)?(&$($lt2)?self, $($arg: $ty),* ) $(-> $ret)* {
match self {
SteenrodAlgebra::AdemAlgebra(a) => a.$method($($arg),*),
SteenrodAlgebra::MilnorAlgebra(a) => a.$method($($arg),*),
}
}
dispatch_steenrod!{$($tail)*}
};
}

dispatch_algebra!(SteenrodAlgebra, dispatch_steenrod);

#[cfg(feature = "json")]
impl JsonAlgebra for SteenrodAlgebra {
dispatch_steenrod! {
fn prefix(&self) -> &str;
fn json_to_basis(&self, json: serde_json::Value) -> error::Result<(i32, usize)>;
fn json_from_basis(&self, degree: i32, idx: usize) -> serde_json::Value;
}
}

/// An algebra with a specified list of generators and generating relations. This data can be used
/// to specify modules by specifying the actions of the generators.
impl GeneratedAlgebra for SteenrodAlgebra {
dispatch_steenrod! {
fn generators(&self, degree: i32) -> Vec<usize>;
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 decompose_basis_element(
&self,
degree: i32,
idx: usize,
) -> Vec<(u32, (i32, usize), (i32, usize))>;

fn generating_relations(&self, degree: i32) -> Vec<Vec<(u32, (i32, usize), (i32, usize))>>;
}
}
4 changes: 1 addition & 3 deletions python_ext/pyo3/python_algebra/src/algebra/algebra_rust.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,7 @@ macro_rules! because_enum_dispatch_doesnt_work_for_me {
};
}

impl algebra::Algebra for AlgebraRust {
algebra::dispatch_algebra!{because_enum_dispatch_doesnt_work_for_me}
}
algebra::dispatch_algebra!{AlgebraRust, because_enum_dispatch_doesnt_work_for_me}


// #[derive(Debug)]
Expand Down

0 comments on commit ff7d720

Please sign in to comment.