pub mod buchberger;
#[cfg(feature = "groebner-cuda")]
pub mod cuda;
pub mod f5;
pub mod fglm;
pub mod ideal;
pub mod monomial_order;
pub mod reduce;
pub mod f4 {
pub use super::buchberger::compute_buchberger_basis as compute_groebner_basis;
}
pub use buchberger::compute_buchberger_basis;
#[cfg(feature = "groebner-cuda")]
pub use cuda::{compute_groebner_basis_gpu, GpuGroebnerError, MacaulayMatrix};
pub use f5::compute_groebner_basis_f5;
pub use fglm::{fglm, grevlex_staircase, is_zero_dimensional};
pub use ideal::GbPoly;
pub use monomial_order::MonomialOrder;
pub use reduce::reduce;
#[derive(Clone, Debug)]
pub struct GroebnerBasis {
generators: Vec<GbPoly>,
order: MonomialOrder,
}
impl GroebnerBasis {
pub fn compute(gens: Vec<GbPoly>, order: MonomialOrder) -> Self {
let generators = compute_buchberger_basis(gens, order);
GroebnerBasis { generators, order }
}
pub fn compute_lex(gens: Vec<GbPoly>) -> Self {
let n_vars = gens.first().map(|g| g.n_vars).unwrap_or(0);
if n_vars <= 1 {
return Self::compute(gens, MonomialOrder::Lex);
}
let grb = compute_buchberger_basis(gens.clone(), MonomialOrder::GRevLex);
if is_zero_dimensional(&grb, n_vars) {
if let Some(generators) = fglm(&grb, n_vars) {
return GroebnerBasis {
generators,
order: MonomialOrder::Lex,
};
}
}
Self::compute(gens, MonomialOrder::Lex)
}
pub fn compute_f5(gens: Vec<GbPoly>, order: MonomialOrder) -> Self {
let generators = compute_groebner_basis_f5(gens, order);
GroebnerBasis { generators, order }
}
pub fn generators(&self) -> &[GbPoly] {
&self.generators
}
pub fn reduce(&self, p: &GbPoly) -> GbPoly {
reduce(p, &self.generators, self.order)
}
pub fn contains(&self, p: &GbPoly) -> bool {
self.reduce(p).is_zero()
}
pub fn len(&self) -> usize {
self.generators.len()
}
pub fn is_empty(&self) -> bool {
self.generators.is_empty()
}
pub fn eliminate(&self, vars: &[usize]) -> GroebnerBasis {
let generators: Vec<GbPoly> = self
.generators
.iter()
.filter(|g| {
!g.terms
.keys()
.any(|e| vars.iter().any(|&i| e.get(i).copied().unwrap_or(0) > 0))
})
.cloned()
.collect();
GroebnerBasis {
generators,
order: self.order,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn eliminate_drops_generators() {
let xm_y = GbPoly {
terms: [
(vec![1u32, 0], rug::Rational::from(1)),
(vec![0, 1], rug::Rational::from(-1)),
]
.into_iter()
.collect(),
n_vars: 2,
};
let two_y2_m1 = GbPoly {
terms: [
(vec![0, 2], rug::Rational::from(2)),
(vec![0, 0], rug::Rational::from(-1)),
]
.into_iter()
.collect(),
n_vars: 2,
};
let gb = GroebnerBasis::compute(vec![xm_y, two_y2_m1], MonomialOrder::Lex);
let elim = gb.eliminate(&[0]);
assert_eq!(elim.generators().len(), 1);
for term in elim.generators()[0].terms.keys() {
assert_eq!(term[0], 0, "eliminated variable x must not appear");
}
}
#[test]
fn compute_lex_circle_parabola() {
let f = GbPoly {
terms: [
(vec![2u32, 0], rug::Rational::from(1)),
(vec![0, 2], rug::Rational::from(1)),
(vec![0, 0], rug::Rational::from(-1)),
]
.into_iter()
.collect(),
n_vars: 2,
};
let g = GbPoly {
terms: [
(vec![0u32, 1], rug::Rational::from(1)),
(vec![2, 0], rug::Rational::from(-1)),
]
.into_iter()
.collect(),
n_vars: 2,
};
let gb_lex = GroebnerBasis::compute_lex(vec![f.clone(), g.clone()]);
let gb_direct = GroebnerBasis::compute(vec![f, g], MonomialOrder::Lex);
for p in gb_direct.generators() {
assert!(gb_lex.contains(p), "FGLM basis missing generator");
}
for p in gb_lex.generators() {
assert!(gb_direct.contains(p), "direct basis missing FGLM generator");
}
}
}