use std::collections::HashMap;
use super::functions::*;
#[derive(Debug, Clone)]
pub struct FreeModuleElement {
pub components: Vec<Polynomial>,
}
pub struct BuchbergerAlgorithm {
pub nvars: usize,
pub order: MonomialOrder,
}
impl BuchbergerAlgorithm {
pub fn new(nvars: usize, order: MonomialOrder) -> Self {
Self { nvars, order }
}
pub fn buchberger(&self, ideal: Vec<Polynomial>) -> GroebnerBasis {
let mut basis: Vec<Polynomial> = ideal;
let mut pairs: Vec<(usize, usize)> = Vec::new();
for i in 0..basis.len() {
for j in (i + 1)..basis.len() {
pairs.push((i, j));
}
}
while let Some((i, j)) = pairs.pop() {
if i >= basis.len() || j >= basis.len() {
continue;
}
let sp = s_polynomial(&basis[i], &basis[j]);
let rem = reduce(&sp, &basis);
if !rem.is_zero() {
let new_idx = basis.len();
basis.push(rem);
for k in 0..new_idx {
pairs.push((k, new_idx));
}
}
}
GroebnerBasis::new(basis, self.nvars, self.order.clone())
}
}
#[allow(dead_code)]
pub struct TropicalGroebnerComputer {
pub weight: Vec<f64>,
pub generators: Vec<Polynomial>,
pub nvars: usize,
}
impl TropicalGroebnerComputer {
pub fn new(weight: Vec<f64>, generators: Vec<Polynomial>, nvars: usize) -> Self {
TropicalGroebnerComputer {
weight,
generators,
nvars,
}
}
pub fn weighted_degree(&self, m: &Monomial) -> f64 {
m.exponents
.iter()
.enumerate()
.map(|(i, &e)| {
let wi = self.weight.get(i).copied().unwrap_or(0.0);
wi * e as f64
})
.sum()
}
pub fn initial_form(&self, f: &Polynomial) -> Polynomial {
if f.is_zero() {
return f.clone();
}
let max_wdeg = f
.terms
.iter()
.map(|t| self.weighted_degree(&t.monomial))
.fold(f64::NEG_INFINITY, f64::max);
let terms: Vec<_> = f
.terms
.iter()
.filter(|t| (self.weighted_degree(&t.monomial) - max_wdeg).abs() < 1e-10)
.cloned()
.collect();
Polynomial {
nvars: f.nvars,
terms,
order: f.order.clone(),
}
}
pub fn is_in_tropical_variety(&self) -> bool {
self.generators.iter().all(|f| {
let init = self.initial_form(f);
init.terms.len() != 1
})
}
pub fn tropical_monomial_count(&self) -> usize {
self.generators
.iter()
.filter(|f| {
let init = self.initial_form(f);
init.terms.len() == 1
})
.count()
}
}
#[derive(Debug, Clone)]
pub struct Polynomial {
pub nvars: usize,
pub terms: Vec<Term>,
pub order: MonomialOrder,
}
impl Polynomial {
pub fn zero(nvars: usize, order: MonomialOrder) -> Self {
Self {
nvars,
terms: vec![],
order,
}
}
pub fn constant(nvars: usize, order: MonomialOrder, c: i64) -> Self {
if c == 0 {
return Self::zero(nvars, order);
}
let mon = Monomial::new(vec![0; nvars]);
Self {
nvars,
terms: vec![Term::new(mon, c)],
order,
}
}
pub fn is_zero(&self) -> bool {
self.terms.is_empty()
}
pub fn leading_term(&self) -> Option<&Term> {
self.terms.first()
}
pub fn leading_monomial(&self) -> Option<&Monomial> {
self.terms.first().map(|t| &t.monomial)
}
pub fn leading_coeff(&self) -> Option<(i64, i64)> {
self.terms.first().map(|t| (t.coeff_num, t.coeff_den))
}
pub fn add(&self, other: &Polynomial) -> Polynomial {
let mut result_terms: Vec<Term> = Vec::new();
let mut i = 0usize;
let mut j = 0usize;
while i < self.terms.len() && j < other.terms.len() {
let ord = self.terms[i]
.monomial
.cmp_with_order(&other.terms[j].monomial, &self.order);
match ord {
std::cmp::Ordering::Greater => {
result_terms.push(self.terms[i].clone());
i += 1;
}
std::cmp::Ordering::Less => {
result_terms.push(other.terms[j].clone());
j += 1;
}
std::cmp::Ordering::Equal => {
let num = self.terms[i].coeff_num * other.terms[j].coeff_den
+ other.terms[j].coeff_num * self.terms[i].coeff_den;
let den = self.terms[i].coeff_den * other.terms[j].coeff_den;
if num != 0 {
result_terms.push(Term::rational(self.terms[i].monomial.clone(), num, den));
}
i += 1;
j += 1;
}
}
}
while i < self.terms.len() {
result_terms.push(self.terms[i].clone());
i += 1;
}
while j < other.terms.len() {
result_terms.push(other.terms[j].clone());
j += 1;
}
Polynomial {
nvars: self.nvars,
terms: result_terms,
order: self.order.clone(),
}
}
pub fn neg(&self) -> Polynomial {
Polynomial {
nvars: self.nvars,
terms: self
.terms
.iter()
.map(|t| Term {
monomial: t.monomial.clone(),
coeff_num: -t.coeff_num,
coeff_den: t.coeff_den,
})
.collect(),
order: self.order.clone(),
}
}
pub fn sub(&self, other: &Polynomial) -> Polynomial {
self.add(&other.neg())
}
pub fn mul(&self, other: &Polynomial) -> Polynomial {
let mut acc = Polynomial::zero(self.nvars, self.order.clone());
for t1 in &self.terms {
let mut partial_terms: Vec<Term> = Vec::new();
for t2 in &other.terms {
let mon = t1.monomial.mul(&t2.monomial);
let num = t1.coeff_num * t2.coeff_num;
let den = t1.coeff_den * t2.coeff_den;
if num != 0 {
partial_terms.push(Term::rational(mon, num, den));
}
}
partial_terms.sort_by(|a, b| b.monomial.cmp_with_order(&a.monomial, &self.order));
let partial = Polynomial {
nvars: self.nvars,
terms: partial_terms,
order: self.order.clone(),
};
acc = acc.add(&partial);
}
acc
}
pub fn make_monic(&self) -> Polynomial {
if let Some((lc_num, lc_den)) = self.leading_coeff() {
if lc_num == 0 {
return self.clone();
}
let terms = self
.terms
.iter()
.map(|t| {
Term::rational(
t.monomial.clone(),
t.coeff_num * lc_den,
t.coeff_den * lc_num,
)
})
.collect();
Polynomial {
nvars: self.nvars,
terms,
order: self.order.clone(),
}
} else {
self.clone()
}
}
pub fn scale(&self, num: i64, den: i64) -> Polynomial {
Polynomial {
nvars: self.nvars,
terms: self
.terms
.iter()
.filter_map(|t| {
let new_num = t.coeff_num * num;
let new_den = t.coeff_den * den;
if new_num == 0 {
None
} else {
Some(Term::rational(t.monomial.clone(), new_num, new_den))
}
})
.collect(),
order: self.order.clone(),
}
}
pub fn mul_monomial(&self, alpha: &Monomial) -> Polynomial {
Polynomial {
nvars: self.nvars,
terms: self
.terms
.iter()
.map(|t| Term {
monomial: t.monomial.mul(alpha),
coeff_num: t.coeff_num,
coeff_den: t.coeff_den,
})
.collect(),
order: self.order.clone(),
}
}
}
#[derive(Debug, Clone)]
pub struct HilbertFunction {
pub values: Vec<usize>,
}
impl HilbertFunction {
pub fn compute(rgb: &ReducedGroebnerBasis, max_degree: usize) -> Self {
let lt_set: Vec<Monomial> = rgb
.generators
.iter()
.filter_map(|g| g.leading_monomial().cloned())
.collect();
let values = (0..=max_degree)
.map(|d| {
let all_monomials = monomials_of_degree(rgb.nvars, d);
let standard_count = all_monomials
.iter()
.filter(|m| !lt_set.iter().any(|lt| lt.divides(m)))
.count();
standard_count
})
.collect();
Self { values }
}
pub fn at(&self, t: usize) -> usize {
self.values.get(t).copied().unwrap_or(0)
}
pub fn euler_characteristic(&self) -> i64 {
self.values
.iter()
.enumerate()
.map(|(t, &h)| if t % 2 == 0 { h as i64 } else { -(h as i64) })
.sum()
}
}
#[derive(Debug, Clone)]
pub struct RegularSequence {
pub elements: Vec<String>,
pub depth: usize,
}
impl RegularSequence {
pub fn new(elements: Vec<String>) -> Self {
let depth = elements.len();
Self { elements, depth }
}
}
#[allow(dead_code)]
pub struct GebauerMollerPruner {
pub basis: Vec<Polynomial>,
}
impl GebauerMollerPruner {
pub fn new(basis: Vec<Polynomial>) -> Self {
GebauerMollerPruner { basis }
}
pub fn product_criterion(&self, i: usize, j: usize) -> bool {
let lm_i = match self.basis.get(i).and_then(|f| f.leading_monomial()) {
Some(m) => m.clone(),
None => return false,
};
let lm_j = match self.basis.get(j).and_then(|f| f.leading_monomial()) {
Some(m) => m.clone(),
None => return false,
};
lm_i.exponents
.iter()
.zip(lm_j.exponents.iter())
.all(|(&a, &b)| a == 0 || b == 0)
}
pub fn chain_criterion(&self, i: usize, j: usize) -> bool {
let lm_i = match self.basis.get(i).and_then(|f| f.leading_monomial()) {
Some(m) => m.clone(),
None => return false,
};
let lm_j = match self.basis.get(j).and_then(|f| f.leading_monomial()) {
Some(m) => m.clone(),
None => return false,
};
let lcm_ij = lm_i.lcm(&lm_j);
self.basis.iter().enumerate().any(|(k, h)| {
k != i
&& k != j
&& h.leading_monomial()
.is_some_and(|lm_h| lm_h.divides(&lcm_ij))
})
}
pub fn filter_pairs(&self, pairs: Vec<(usize, usize)>) -> Vec<(usize, usize)> {
pairs
.into_iter()
.filter(|&(i, j)| !self.product_criterion(i, j) && !self.chain_criterion(i, j))
.collect()
}
pub fn count_necessary_pairs(&self) -> usize {
let n = self.basis.len();
let all_pairs: Vec<(usize, usize)> = (0..n)
.flat_map(|i| (i + 1..n).map(move |j| (i, j)))
.collect();
self.filter_pairs(all_pairs).len()
}
}
#[derive(Debug, Clone)]
pub struct BuchbergerCriterion {
pub basis: Vec<Polynomial>,
}
impl BuchbergerCriterion {
pub fn new(basis: Vec<Polynomial>) -> Self {
Self { basis }
}
pub fn check(&self) -> bool {
let gs = &self.basis;
for i in 0..gs.len() {
for j in (i + 1)..gs.len() {
let sp = s_polynomial(&gs[i], &gs[j]);
let rem = reduce(&sp, gs);
if !rem.is_zero() {
return false;
}
}
}
true
}
}
#[derive(Debug, Clone)]
pub struct ResolutionStep {
pub source_rank: usize,
pub target_rank: usize,
pub matrix: Vec<FreeModuleElement>,
}
#[derive(Debug, Clone)]
pub struct HilbertPolynomial {
pub coefficients: Vec<f64>,
}
impl HilbertPolynomial {
pub fn eval(&self, t: i64) -> f64 {
self.coefficients
.iter()
.enumerate()
.map(|(k, &c)| c * (t as f64).powi(k as i32))
.sum()
}
pub fn dimension(&self) -> usize {
self.coefficients
.iter()
.rposition(|&c| c.abs() > 1e-10)
.unwrap_or(0)
}
pub fn degree(&self) -> f64 {
let d = self.dimension();
self.coefficients.get(d).copied().unwrap_or(0.0)
}
}
pub struct CoxRing {
pub variety: String,
pub grading: Vec<Vec<i64>>,
}
impl CoxRing {
pub fn new(variety: String, grading: Vec<Vec<i64>>) -> Self {
Self { variety, grading }
}
pub fn generators(&self) -> Vec<String> {
self.grading
.iter()
.enumerate()
.map(|(i, deg)| {
let deg_str: Vec<String> = deg.iter().map(|d| d.to_string()).collect();
format!("x_{i} (degree [{}])", deg_str.join(", "))
})
.collect()
}
pub fn is_finitely_generated(&self) -> bool {
!self.variety.is_empty()
}
}
pub struct SystemSolver {
pub basis: ReducedGroebnerBasis,
}
impl SystemSolver {
pub fn new(basis: ReducedGroebnerBasis) -> Self {
Self { basis }
}
pub fn num_solutions(&self) -> usize {
let hf = HilbertFunction::compute(&self.basis, 20);
let vals = &hf.values;
for i in (1..vals.len()).rev() {
if vals[i] != vals[i - 1] {
return vals[i];
}
}
vals.last().copied().unwrap_or(0)
}
}
#[derive(Debug, Clone)]
pub struct ReducedGroebnerBasis {
pub generators: Vec<Polynomial>,
pub nvars: usize,
pub order: MonomialOrder,
}
impl ReducedGroebnerBasis {
pub fn from_groebner(gb: GroebnerBasis) -> Self {
let mut gens = gb.generators;
gens = gens.into_iter().map(|g| g.make_monic()).collect();
let mut i = 0;
while i < gens.len() {
let lm_i = match gens[i].leading_monomial() {
Some(m) => m.clone(),
None => {
gens.remove(i);
continue;
}
};
let redundant = gens.iter().enumerate().any(|(j, gj)| {
j != i
&& gj
.leading_monomial()
.is_some_and(|lm_j| lm_j.divides(&lm_i))
});
if redundant {
gens.remove(i);
} else {
i += 1;
}
}
let n = gens.len();
for i in 0..n {
let others: Vec<Polynomial> = gens
.iter()
.enumerate()
.filter(|(j, _)| *j != i)
.map(|(_, g)| g.clone())
.collect();
let gi = gens[i].clone();
gens[i] = reduce(&gi, &others);
gens[i] = gens[i].make_monic();
}
gens.retain(|g| !g.is_zero());
let order = gb.order;
let nvars = gb.nvars;
Self {
generators: gens,
nvars,
order,
}
}
}
pub struct ImplicitizationProblem {
pub parametric: Vec<Polynomial>,
pub num_params: usize,
pub num_coords: usize,
}
impl ImplicitizationProblem {
pub fn new(parametric: Vec<Polynomial>, num_params: usize) -> Self {
let num_coords = parametric.len();
Self {
parametric,
num_params,
num_coords,
}
}
pub fn solve(&self, nvars_total: usize) -> Vec<Polynomial> {
let ops = IdealOperations::new(nvars_total, MonomialOrder::GradedRevLex);
ops.elimination_ideal(self.parametric.clone(), self.num_params)
}
}
#[allow(dead_code)]
pub struct RadicalMembershipTester {
pub ideal: Vec<Polynomial>,
pub f: Polynomial,
pub nvars: usize,
pub order: MonomialOrder,
}
impl RadicalMembershipTester {
pub fn new(ideal: Vec<Polynomial>, f: Polynomial, nvars: usize, order: MonomialOrder) -> Self {
RadicalMembershipTester {
ideal,
f,
nvars,
order,
}
}
pub fn rabinowitsch_system_size(&self) -> usize {
self.ideal.len() + 1
}
pub fn is_radical_member(&self) -> bool {
if self.ideal.len() == 1 {
let g = &self.ideal[0];
let rem = reduce(&self.f, std::slice::from_ref(g));
return rem.is_zero();
}
let rem = reduce(&self.f, &self.ideal);
rem.is_zero()
}
pub fn exponent_estimate(&self) -> usize {
if self.is_radical_member() {
1
} else {
let f2 = self.f.mul(&self.f);
let rem2 = reduce(&f2, &self.ideal);
if rem2.is_zero() {
2
} else {
0
}
}
}
}
#[derive(Debug, Clone)]
pub struct SyzygyModule {
pub generators: Vec<Syzygy>,
pub rank: usize,
}
impl SyzygyModule {
pub fn compute(polys: &[Polynomial]) -> Self {
let n = polys.len();
if n == 0 {
return Self {
generators: vec![],
rank: 0,
};
}
let nvars = polys[0].nvars;
let order = polys[0].order.clone();
let mut syz_gens: Vec<Syzygy> = Vec::new();
for i in 0..n {
for j in (i + 1)..n {
let lm_i = match polys[i].leading_monomial() {
Some(m) => m.clone(),
None => continue,
};
let lm_j = match polys[j].leading_monomial() {
Some(m) => m.clone(),
None => continue,
};
let lcm = lm_i.lcm(&lm_j);
let gamma_i = lcm.div(&lm_i).unwrap_or_default();
let gamma_j = lcm.div(&lm_j).unwrap_or_default();
let (lc_i_num, lc_i_den) = polys[i].leading_coeff().unwrap_or((1, 1));
let (lc_j_num, lc_j_den) = polys[j].leading_coeff().unwrap_or((1, 1));
let mut components: Vec<Polynomial> = (0..n)
.map(|_| Polynomial::zero(nvars, order.clone()))
.collect();
components[i] = Polynomial {
nvars,
terms: vec![Term::rational(gamma_i, lc_j_den, lc_i_num * lc_j_num)],
order: order.clone(),
};
let neg_j_term = Term::rational(gamma_j, -lc_i_den, lc_i_num * lc_j_num);
components[j] = Polynomial {
nvars,
terms: if neg_j_term.is_zero() {
vec![]
} else {
vec![neg_j_term]
},
order: order.clone(),
};
syz_gens.push(Syzygy { components });
}
}
Self {
generators: syz_gens,
rank: n,
}
}
pub fn num_generators(&self) -> usize {
self.generators.len()
}
}
#[allow(dead_code)]
pub struct FaugereF4Simulator {
pub basis: Vec<Polynomial>,
pub nvars: usize,
pub order: MonomialOrder,
pub degree_bound: u32,
}
impl FaugereF4Simulator {
pub fn new(basis: Vec<Polynomial>, nvars: usize, order: MonomialOrder) -> Self {
FaugereF4Simulator {
basis,
nvars,
order,
degree_bound: 1,
}
}
pub fn select_pairs(&self) -> Vec<(usize, usize)> {
let gs = &self.basis;
let mut selected = Vec::new();
for i in 0..gs.len() {
for j in (i + 1)..gs.len() {
if let (Some(lm_i), Some(lm_j)) =
(gs[i].leading_monomial(), gs[j].leading_monomial())
{
let lcm = lm_i.lcm(lm_j);
if lcm.degree() <= self.degree_bound {
selected.push((i, j));
}
}
}
}
selected
}
pub fn step(&mut self) -> usize {
let pairs = self.select_pairs();
let mut new_polys = Vec::new();
for (i, j) in pairs {
if i < self.basis.len() && j < self.basis.len() {
let sp = s_polynomial(&self.basis[i], &self.basis[j]);
let rem = reduce(&sp, &self.basis);
if !rem.is_zero() {
new_polys.push(rem);
}
}
}
let count = new_polys.len();
self.basis.extend(new_polys);
self.degree_bound += 1;
count
}
pub fn run_to_convergence(&mut self, max_steps: usize) -> usize {
for step_idx in 0..max_steps {
let added = self.step();
if added == 0 {
return step_idx + 1;
}
}
max_steps
}
pub fn to_groebner_basis(&self) -> GroebnerBasis {
GroebnerBasis::new(self.basis.clone(), self.nvars, self.order.clone())
}
}
#[derive(Debug, Clone)]
pub struct Syzygy {
pub components: Vec<Polynomial>,
}
#[derive(Debug, Clone, Default)]
pub struct BettiNumbers {
pub table: HashMap<(usize, usize), usize>,
}
impl BettiNumbers {
pub fn new() -> Self {
Self::default()
}
pub fn set(&mut self, i: usize, j: usize, val: usize) {
self.table.insert((i, j), val);
}
pub fn get(&self, i: usize, j: usize) -> usize {
self.table.get(&(i, j)).copied().unwrap_or(0)
}
pub fn regularity(&self) -> Option<usize> {
self.table
.iter()
.filter(|(_, &v)| v > 0)
.map(|(&(i, j), _)| j.saturating_sub(i))
.max()
}
}
#[derive(Debug, Clone)]
pub struct ToricIdeal {
pub matrix: Vec<Vec<i64>>,
pub nvars: usize,
}
impl ToricIdeal {
pub fn new(matrix: Vec<Vec<i64>>) -> Self {
let nvars = matrix.len();
Self { matrix, nvars }
}
pub fn generators(&self, order: MonomialOrder) -> Vec<Polynomial> {
let _order = order;
vec![]
}
}
pub struct IdealOperations {
pub nvars: usize,
pub order: MonomialOrder,
}
impl IdealOperations {
pub fn new(nvars: usize, order: MonomialOrder) -> Self {
Self { nvars, order }
}
pub fn elimination_ideal(&self, generators: Vec<Polynomial>, k: usize) -> Vec<Polynomial> {
let elim_order = MonomialOrder::Elimination(k);
let algo = BuchbergerAlgorithm::new(self.nvars, elim_order.clone());
let gb = algo.buchberger(generators);
gb.generators
.into_iter()
.filter(|g| {
g.terms
.iter()
.all(|t| t.monomial.exponents.iter().take(k).all(|&e| e == 0))
})
.collect()
}
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct Monomial {
pub exponents: Vec<u32>,
}
impl Monomial {
pub fn new(exponents: Vec<u32>) -> Self {
Self { exponents }
}
pub fn degree(&self) -> u32 {
self.exponents.iter().sum()
}
pub fn nvars(&self) -> usize {
self.exponents.len()
}
pub fn lcm(&self, other: &Monomial) -> Monomial {
let n = self.exponents.len().max(other.exponents.len());
let exponents = (0..n)
.map(|i| {
let a = self.exponents.get(i).copied().unwrap_or(0);
let b = other.exponents.get(i).copied().unwrap_or(0);
a.max(b)
})
.collect();
Monomial { exponents }
}
pub fn divides(&self, other: &Monomial) -> bool {
self.exponents.iter().enumerate().all(|(i, &a)| {
let b = other.exponents.get(i).copied().unwrap_or(0);
a <= b
})
}
pub fn mul(&self, other: &Monomial) -> Monomial {
let n = self.exponents.len().max(other.exponents.len());
let exponents = (0..n)
.map(|i| {
let a = self.exponents.get(i).copied().unwrap_or(0);
let b = other.exponents.get(i).copied().unwrap_or(0);
a + b
})
.collect();
Monomial { exponents }
}
pub fn div(&self, other: &Monomial) -> Option<Monomial> {
if !other.divides(self) {
return None;
}
let exponents = self
.exponents
.iter()
.enumerate()
.map(|(i, &a)| {
let b = other.exponents.get(i).copied().unwrap_or(0);
a - b
})
.collect();
Some(Monomial { exponents })
}
pub fn cmp_lex(&self, other: &Monomial) -> std::cmp::Ordering {
let n = self.exponents.len().max(other.exponents.len());
for i in 0..n {
let a = self.exponents.get(i).copied().unwrap_or(0);
let b = other.exponents.get(i).copied().unwrap_or(0);
match a.cmp(&b) {
std::cmp::Ordering::Equal => {}
ord => return ord,
}
}
std::cmp::Ordering::Equal
}
pub fn cmp_grlex(&self, other: &Monomial) -> std::cmp::Ordering {
match self.degree().cmp(&other.degree()) {
std::cmp::Ordering::Equal => self.cmp_lex(other),
ord => ord,
}
}
pub fn cmp_grevlex(&self, other: &Monomial) -> std::cmp::Ordering {
match self.degree().cmp(&other.degree()) {
std::cmp::Ordering::Equal => {
let n = self.exponents.len().max(other.exponents.len());
for i in (0..n).rev() {
let a = self.exponents.get(i).copied().unwrap_or(0);
let b = other.exponents.get(i).copied().unwrap_or(0);
match b.cmp(&a) {
std::cmp::Ordering::Equal => {}
ord => return ord,
}
}
std::cmp::Ordering::Equal
}
ord => ord,
}
}
pub fn cmp_with_order(&self, other: &Monomial, order: &MonomialOrder) -> std::cmp::Ordering {
match order {
MonomialOrder::Lex => self.cmp_lex(other),
MonomialOrder::GradedLex => self.cmp_grlex(other),
MonomialOrder::GradedRevLex => self.cmp_grevlex(other),
MonomialOrder::Elimination(k) => {
let k = *k;
let n = self.exponents.len().max(other.exponents.len());
for i in 0..k.min(n) {
let a = self.exponents.get(i).copied().unwrap_or(0);
let b = other.exponents.get(i).copied().unwrap_or(0);
match a.cmp(&b) {
std::cmp::Ordering::Equal => {}
ord => return ord,
}
}
let self_tail = Monomial {
exponents: self.exponents.get(k..).unwrap_or(&[]).to_vec(),
};
let other_tail = Monomial {
exponents: other.exponents.get(k..).unwrap_or(&[]).to_vec(),
};
self_tail.cmp_grlex(&other_tail)
}
MonomialOrder::Weight(w) => {
let wa: i64 = self
.exponents
.iter()
.enumerate()
.map(|(i, &e)| w.get(i).copied().unwrap_or(1) * e as i64)
.sum();
let wb: i64 = other
.exponents
.iter()
.enumerate()
.map(|(i, &e)| w.get(i).copied().unwrap_or(1) * e as i64)
.sum();
match wa.cmp(&wb) {
std::cmp::Ordering::Equal => self.cmp_lex(other),
ord => ord,
}
}
}
}
}
#[derive(Debug, Clone)]
pub struct GroebnerBasis {
pub generators: Vec<Polynomial>,
pub nvars: usize,
pub order: MonomialOrder,
}
impl GroebnerBasis {
pub fn new(generators: Vec<Polynomial>, nvars: usize, order: MonomialOrder) -> Self {
Self {
generators,
nvars,
order,
}
}
pub fn is_groebner_basis(&self) -> bool {
let gs = &self.generators;
for i in 0..gs.len() {
for j in (i + 1)..gs.len() {
let sp = s_polynomial(&gs[i], &gs[j]);
let rem = reduce(&sp, gs);
if !rem.is_zero() {
return false;
}
}
}
true
}
pub fn reduce_to_normal_form(&self, f: &Polynomial) -> Polynomial {
reduce(f, &self.generators)
}
}
pub struct PolynomialSystem {
pub polys: Vec<String>,
pub vars: Vec<String>,
}
impl PolynomialSystem {
pub fn new(polys: Vec<String>, vars: Vec<String>) -> Self {
Self { polys, vars }
}
pub fn solve_over_algebraic_closure(&self) -> Vec<String> {
vec![
format!(
"Compute reduced Gröbner basis of ({}) in lex order on variables ({}).",
self.polys.join(", "),
self.vars.join(" > "),
),
"Apply Shape Lemma: last variable satisfies a univariate polynomial.".to_string(),
"Back-substitute to find all solutions in the algebraic closure.".to_string(),
]
}
pub fn count_solutions(&self) -> usize {
if self.polys.is_empty() {
0
} else {
self.polys.len() * 2
}
}
}
pub struct ElimAlgebra {
pub variables_to_elim: Vec<String>,
pub all_variables: Vec<String>,
}
impl ElimAlgebra {
pub fn new(variables_to_elim: Vec<String>, all_variables: Vec<String>) -> Self {
Self {
variables_to_elim,
all_variables,
}
}
pub fn eliminate(&self, basis: &GroebnerBasis) -> Vec<String> {
let elim_set: std::collections::HashSet<&str> =
self.variables_to_elim.iter().map(|s| s.as_str()).collect();
basis
.generators
.iter()
.filter_map(|g| {
let g_str = g.to_string();
if elim_set.iter().any(|v| g_str.contains(*v)) {
None
} else {
Some(g_str)
}
})
.collect()
}
pub fn projection_theorem(&self, basis: &GroebnerBasis) -> String {
let elim = self.eliminate(basis);
format!(
"Projection theorem: V(I) projects into V(I_{k}) ⊆ k^{m} \
where I_{k} is generated by {n} polynomials.",
k = self.variables_to_elim.len(),
m = self.all_variables.len() - self.variables_to_elim.len(),
n = elim.len(),
)
}
}
#[derive(Debug, Clone)]
pub struct NullstellensatzCertificate {
pub polynomial: String,
pub ideal_generators: Vec<String>,
pub exponent: usize,
pub cofactors: Vec<String>,
}
impl NullstellensatzCertificate {
pub fn new(
polynomial: String,
ideal_generators: Vec<String>,
exponent: usize,
cofactors: Vec<String>,
) -> Self {
Self {
polynomial,
ideal_generators,
exponent,
cofactors,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum MonomialOrder {
Lex,
GradedLex,
GradedRevLex,
Elimination(usize),
Weight(Vec<i64>),
}
#[derive(Debug, Clone, PartialEq)]
pub struct Term {
pub monomial: Monomial,
pub coeff_num: i64,
pub coeff_den: i64,
}
impl Term {
pub fn new(monomial: Monomial, coeff: i64) -> Self {
Self {
monomial,
coeff_num: coeff,
coeff_den: 1,
}
}
pub fn rational(monomial: Monomial, num: i64, den: i64) -> Self {
assert_ne!(den, 0, "denominator must be nonzero");
let g = gcd(num.unsigned_abs(), den.unsigned_abs()) as i64;
let sign = if den < 0 { -1 } else { 1 };
Self {
monomial,
coeff_num: sign * num / g,
coeff_den: sign * den / g,
}
}
pub fn is_zero(&self) -> bool {
self.coeff_num == 0
}
pub fn scale(&self, num: i64, den: i64) -> Term {
Term::rational(
self.monomial.clone(),
self.coeff_num * num,
self.coeff_den * den,
)
}
}
pub struct IdealMembership {
pub basis: GroebnerBasis,
}
impl IdealMembership {
pub fn new(basis: GroebnerBasis) -> Self {
Self { basis }
}
pub fn contains(&self, f: &Polynomial) -> bool {
self.basis.reduce_to_normal_form(f).is_zero()
}
}
pub struct SyzygiesComputation {
pub module: String,
pub rank: usize,
}
impl SyzygiesComputation {
pub fn new(module: String, rank: usize) -> Self {
Self { module, rank }
}
pub fn compute_syzygies(&self) -> Vec<String> {
(0..self.rank)
.flat_map(|i| {
(i + 1..self.rank)
.map(move |j| {
format!(
"Syz(f_{i}, f_{j}): e_{i} lcm(lm(f_{i}),lm(f_{j}))/lm(f_{i}) - e_{j} lcm(lm(f_{i}),lm(f_{j}))/lm(f_{j})"
)
})
})
.collect()
}
pub fn free_resolution(&self) -> Vec<String> {
let mut steps = Vec::new();
let mut current_rank = self.rank;
let mut depth = 0usize;
while current_rank > 0 && depth < self.rank + 1 {
let next_rank = if current_rank > 1 {
current_rank - 1
} else {
0
};
steps.push(format!(
"F_{depth}: k[vars]^{current_rank} --d_{depth}--> F_{prev}",
prev = if depth == 0 {
"M".to_string()
} else {
format!("{}", depth - 1)
},
));
current_rank = next_rank;
depth += 1;
}
steps
}
}
#[derive(Debug, Clone)]
pub struct MinimalFreeResolution {
pub steps: Vec<ResolutionStep>,
pub projective_dim: usize,
}
impl MinimalFreeResolution {
pub fn betti_numbers(&self) -> Vec<usize> {
self.steps.iter().map(|s| s.source_rank).collect()
}
}