mod buchberger;
mod efficient_buchberger;
mod monomial_order;
mod reduction;
mod s_polynomial;
pub use buchberger::buchberger_algorithm;
pub use efficient_buchberger::efficient_buchberger_algorithm;
pub use monomial_order::{MonomialOrder, MonomialOrdering};
pub use reduction::{poly_reduce, poly_reduce_completely};
pub use s_polynomial::s_polynomial;
pub use crate::core::polynomial::sparse_polynomial::{
expression_to_sparse_polynomial, sparse_polynomial_to_expression, Monomial, SparsePolynomial,
};
use crate::core::{Expression, Symbol};
use std::collections::HashSet;
#[derive(Debug, Clone)]
pub struct GroebnerBasis {
pub basis: Vec<Expression>,
pub variables: Vec<Symbol>,
pub ordering: MonomialOrder,
pub is_reduced: bool,
}
impl GroebnerBasis {
pub fn new(
polynomials: Vec<Expression>,
variables: Vec<Symbol>,
ordering: MonomialOrder,
) -> Self {
Self {
basis: polynomials,
variables,
ordering,
is_reduced: false,
}
}
pub fn compute(&mut self) {
self.basis = efficient_buchberger_algorithm(&self.basis, &self.variables, &self.ordering)
.expect("Efficient Buchberger algorithm should converge for valid polynomial ideals");
self.is_reduced = false;
}
pub fn compute_with_result(&mut self) -> crate::error::MathResult<()> {
match efficient_buchberger_algorithm(&self.basis, &self.variables, &self.ordering) {
Ok(basis) => {
self.basis = basis;
self.is_reduced = false;
Ok(())
}
Err(e) => Err(e),
}
}
pub fn reduce(&mut self) {
if self.is_reduced {
return;
}
let mut reduced = Vec::new();
for poly in &self.basis {
if !poly.is_zero() {
let mut p = poly.clone();
let reduced_refs: Vec<&Expression> = reduced.iter().collect();
p = poly_reduce_completely(&p, &reduced_refs, &self.variables, &self.ordering);
if !p.is_zero() {
reduced.push(p);
}
}
}
self.basis = reduced;
self.is_reduced = true;
}
pub fn contains(&self, poly: &Expression) -> bool {
let basis_refs: Vec<&Expression> = self.basis.iter().collect();
let reduced = poly_reduce_completely(poly, &basis_refs, &self.variables, &self.ordering);
reduced.is_zero()
}
pub fn get_variables(&self) -> Vec<Symbol> {
let mut vars = HashSet::new();
for poly in &self.basis {
for var in find_variables(poly) {
vars.insert(var);
}
}
vars.into_iter().collect()
}
}
fn find_variables(expr: &Expression) -> Vec<Symbol> {
fn collect_symbols(expr: &Expression, symbols: &mut HashSet<Symbol>) {
match expr {
Expression::Symbol(s) => {
symbols.insert(s.clone());
}
Expression::Add(terms) | Expression::Mul(terms) => {
for term in terms.iter() {
collect_symbols(term, symbols);
}
}
Expression::Pow(base, exp) => {
collect_symbols(base, symbols);
collect_symbols(exp, symbols);
}
Expression::Function { args, .. } => {
for arg in args.iter() {
collect_symbols(arg, symbols);
}
}
_ => {}
}
}
let mut symbols = HashSet::new();
collect_symbols(expr, &mut symbols);
symbols.into_iter().collect()
}
#[cfg(test)]
mod tests {
use super::*;
use crate::symbol;
#[test]
fn test_groebner_basis_creation() {
let x = symbol!(x);
let y = symbol!(y);
let f1 = Expression::add(vec![
Expression::pow(Expression::symbol(x.clone()), Expression::integer(2)),
Expression::integer(-1),
]);
let gb = GroebnerBasis::new(vec![f1], vec![x, y], MonomialOrder::Lex);
assert_eq!(gb.basis.len(), 1);
assert_eq!(gb.variables.len(), 2);
}
#[test]
fn test_groebner_basis_simple() {
let x = symbol!(x);
let y = symbol!(y);
let f1 = Expression::symbol(x.clone()) - Expression::symbol(y.clone());
let f2 = Expression::pow(Expression::symbol(y.clone()), Expression::integer(2))
- Expression::integer(1);
let mut gb =
GroebnerBasis::new(vec![f1, f2], vec![x.clone(), y.clone()], MonomialOrder::Lex);
gb.compute();
assert!(!gb.basis.is_empty());
assert!(gb.basis.len() >= 2);
}
#[test]
#[ignore = "FIXME: Gröbner basis ideal membership test needs convergence tuning"]
fn test_ideal_membership() {
let x = symbol!(x);
let y = symbol!(y);
let f1 = Expression::symbol(x.clone()) - Expression::symbol(y.clone());
let f2 = Expression::pow(Expression::symbol(y.clone()), Expression::integer(2))
- Expression::integer(1);
let mut gb =
GroebnerBasis::new(vec![f1, f2], vec![x.clone(), y.clone()], MonomialOrder::Lex);
gb.compute();
let test = Expression::pow(Expression::symbol(x.clone()), Expression::integer(2))
- Expression::integer(1);
assert!(gb.contains(&test));
}
}