1use std::collections::HashMap;
6
7use super::functions::*;
8
9#[derive(Debug, Clone)]
11pub struct FreeModuleElement {
12 pub components: Vec<Polynomial>,
14}
15pub struct BuchbergerAlgorithm {
19 pub nvars: usize,
21 pub order: MonomialOrder,
23}
24impl BuchbergerAlgorithm {
25 pub fn new(nvars: usize, order: MonomialOrder) -> Self {
27 Self { nvars, order }
28 }
29 pub fn buchberger(&self, ideal: Vec<Polynomial>) -> GroebnerBasis {
31 let mut basis: Vec<Polynomial> = ideal;
32 let mut pairs: Vec<(usize, usize)> = Vec::new();
33 for i in 0..basis.len() {
34 for j in (i + 1)..basis.len() {
35 pairs.push((i, j));
36 }
37 }
38 while let Some((i, j)) = pairs.pop() {
39 if i >= basis.len() || j >= basis.len() {
40 continue;
41 }
42 let sp = s_polynomial(&basis[i], &basis[j]);
43 let rem = reduce(&sp, &basis);
44 if !rem.is_zero() {
45 let new_idx = basis.len();
46 basis.push(rem);
47 for k in 0..new_idx {
48 pairs.push((k, new_idx));
49 }
50 }
51 }
52 GroebnerBasis::new(basis, self.nvars, self.order.clone())
53 }
54}
55#[allow(dead_code)]
61pub struct TropicalGroebnerComputer {
62 pub weight: Vec<f64>,
64 pub generators: Vec<Polynomial>,
66 pub nvars: usize,
68}
69impl TropicalGroebnerComputer {
70 pub fn new(weight: Vec<f64>, generators: Vec<Polynomial>, nvars: usize) -> Self {
72 TropicalGroebnerComputer {
73 weight,
74 generators,
75 nvars,
76 }
77 }
78 pub fn weighted_degree(&self, m: &Monomial) -> f64 {
80 m.exponents
81 .iter()
82 .enumerate()
83 .map(|(i, &e)| {
84 let wi = self.weight.get(i).copied().unwrap_or(0.0);
85 wi * e as f64
86 })
87 .sum()
88 }
89 pub fn initial_form(&self, f: &Polynomial) -> Polynomial {
91 if f.is_zero() {
92 return f.clone();
93 }
94 let max_wdeg = f
95 .terms
96 .iter()
97 .map(|t| self.weighted_degree(&t.monomial))
98 .fold(f64::NEG_INFINITY, f64::max);
99 let terms: Vec<_> = f
100 .terms
101 .iter()
102 .filter(|t| (self.weighted_degree(&t.monomial) - max_wdeg).abs() < 1e-10)
103 .cloned()
104 .collect();
105 Polynomial {
106 nvars: f.nvars,
107 terms,
108 order: f.order.clone(),
109 }
110 }
111 pub fn is_in_tropical_variety(&self) -> bool {
114 self.generators.iter().all(|f| {
115 let init = self.initial_form(f);
116 init.terms.len() != 1
117 })
118 }
119 pub fn tropical_monomial_count(&self) -> usize {
121 self.generators
122 .iter()
123 .filter(|f| {
124 let init = self.initial_form(f);
125 init.terms.len() == 1
126 })
127 .count()
128 }
129}
130#[derive(Debug, Clone)]
135pub struct Polynomial {
136 pub nvars: usize,
138 pub terms: Vec<Term>,
140 pub order: MonomialOrder,
142}
143impl Polynomial {
144 pub fn zero(nvars: usize, order: MonomialOrder) -> Self {
146 Self {
147 nvars,
148 terms: vec![],
149 order,
150 }
151 }
152 pub fn constant(nvars: usize, order: MonomialOrder, c: i64) -> Self {
154 if c == 0 {
155 return Self::zero(nvars, order);
156 }
157 let mon = Monomial::new(vec![0; nvars]);
158 Self {
159 nvars,
160 terms: vec![Term::new(mon, c)],
161 order,
162 }
163 }
164 pub fn is_zero(&self) -> bool {
166 self.terms.is_empty()
167 }
168 pub fn leading_term(&self) -> Option<&Term> {
170 self.terms.first()
171 }
172 pub fn leading_monomial(&self) -> Option<&Monomial> {
174 self.terms.first().map(|t| &t.monomial)
175 }
176 pub fn leading_coeff(&self) -> Option<(i64, i64)> {
178 self.terms.first().map(|t| (t.coeff_num, t.coeff_den))
179 }
180 pub fn add(&self, other: &Polynomial) -> Polynomial {
182 let mut result_terms: Vec<Term> = Vec::new();
183 let mut i = 0usize;
184 let mut j = 0usize;
185 while i < self.terms.len() && j < other.terms.len() {
186 let ord = self.terms[i]
187 .monomial
188 .cmp_with_order(&other.terms[j].monomial, &self.order);
189 match ord {
190 std::cmp::Ordering::Greater => {
191 result_terms.push(self.terms[i].clone());
192 i += 1;
193 }
194 std::cmp::Ordering::Less => {
195 result_terms.push(other.terms[j].clone());
196 j += 1;
197 }
198 std::cmp::Ordering::Equal => {
199 let num = self.terms[i].coeff_num * other.terms[j].coeff_den
200 + other.terms[j].coeff_num * self.terms[i].coeff_den;
201 let den = self.terms[i].coeff_den * other.terms[j].coeff_den;
202 if num != 0 {
203 result_terms.push(Term::rational(self.terms[i].monomial.clone(), num, den));
204 }
205 i += 1;
206 j += 1;
207 }
208 }
209 }
210 while i < self.terms.len() {
211 result_terms.push(self.terms[i].clone());
212 i += 1;
213 }
214 while j < other.terms.len() {
215 result_terms.push(other.terms[j].clone());
216 j += 1;
217 }
218 Polynomial {
219 nvars: self.nvars,
220 terms: result_terms,
221 order: self.order.clone(),
222 }
223 }
224 pub fn neg(&self) -> Polynomial {
226 Polynomial {
227 nvars: self.nvars,
228 terms: self
229 .terms
230 .iter()
231 .map(|t| Term {
232 monomial: t.monomial.clone(),
233 coeff_num: -t.coeff_num,
234 coeff_den: t.coeff_den,
235 })
236 .collect(),
237 order: self.order.clone(),
238 }
239 }
240 pub fn sub(&self, other: &Polynomial) -> Polynomial {
242 self.add(&other.neg())
243 }
244 pub fn mul(&self, other: &Polynomial) -> Polynomial {
246 let mut acc = Polynomial::zero(self.nvars, self.order.clone());
247 for t1 in &self.terms {
248 let mut partial_terms: Vec<Term> = Vec::new();
249 for t2 in &other.terms {
250 let mon = t1.monomial.mul(&t2.monomial);
251 let num = t1.coeff_num * t2.coeff_num;
252 let den = t1.coeff_den * t2.coeff_den;
253 if num != 0 {
254 partial_terms.push(Term::rational(mon, num, den));
255 }
256 }
257 partial_terms.sort_by(|a, b| b.monomial.cmp_with_order(&a.monomial, &self.order));
258 let partial = Polynomial {
259 nvars: self.nvars,
260 terms: partial_terms,
261 order: self.order.clone(),
262 };
263 acc = acc.add(&partial);
264 }
265 acc
266 }
267 pub fn make_monic(&self) -> Polynomial {
269 if let Some((lc_num, lc_den)) = self.leading_coeff() {
270 if lc_num == 0 {
271 return self.clone();
272 }
273 let terms = self
274 .terms
275 .iter()
276 .map(|t| {
277 Term::rational(
278 t.monomial.clone(),
279 t.coeff_num * lc_den,
280 t.coeff_den * lc_num,
281 )
282 })
283 .collect();
284 Polynomial {
285 nvars: self.nvars,
286 terms,
287 order: self.order.clone(),
288 }
289 } else {
290 self.clone()
291 }
292 }
293 pub fn scale(&self, num: i64, den: i64) -> Polynomial {
295 Polynomial {
296 nvars: self.nvars,
297 terms: self
298 .terms
299 .iter()
300 .filter_map(|t| {
301 let new_num = t.coeff_num * num;
302 let new_den = t.coeff_den * den;
303 if new_num == 0 {
304 None
305 } else {
306 Some(Term::rational(t.monomial.clone(), new_num, new_den))
307 }
308 })
309 .collect(),
310 order: self.order.clone(),
311 }
312 }
313 pub fn mul_monomial(&self, alpha: &Monomial) -> Polynomial {
315 Polynomial {
316 nvars: self.nvars,
317 terms: self
318 .terms
319 .iter()
320 .map(|t| Term {
321 monomial: t.monomial.mul(alpha),
322 coeff_num: t.coeff_num,
323 coeff_den: t.coeff_den,
324 })
325 .collect(),
326 order: self.order.clone(),
327 }
328 }
329}
330#[derive(Debug, Clone)]
332pub struct HilbertFunction {
333 pub values: Vec<usize>,
335}
336impl HilbertFunction {
337 pub fn compute(rgb: &ReducedGroebnerBasis, max_degree: usize) -> Self {
341 let lt_set: Vec<Monomial> = rgb
342 .generators
343 .iter()
344 .filter_map(|g| g.leading_monomial().cloned())
345 .collect();
346 let values = (0..=max_degree)
347 .map(|d| {
348 let all_monomials = monomials_of_degree(rgb.nvars, d);
349 let standard_count = all_monomials
350 .iter()
351 .filter(|m| !lt_set.iter().any(|lt| lt.divides(m)))
352 .count();
353 standard_count
354 })
355 .collect();
356 Self { values }
357 }
358 pub fn at(&self, t: usize) -> usize {
360 self.values.get(t).copied().unwrap_or(0)
361 }
362 pub fn euler_characteristic(&self) -> i64 {
364 self.values
365 .iter()
366 .enumerate()
367 .map(|(t, &h)| if t % 2 == 0 { h as i64 } else { -(h as i64) })
368 .sum()
369 }
370}
371#[derive(Debug, Clone)]
375pub struct RegularSequence {
376 pub elements: Vec<String>,
378 pub depth: usize,
380}
381impl RegularSequence {
382 pub fn new(elements: Vec<String>) -> Self {
384 let depth = elements.len();
385 Self { elements, depth }
386 }
387}
388#[allow(dead_code)]
395pub struct GebauerMollerPruner {
396 pub basis: Vec<Polynomial>,
398}
399impl GebauerMollerPruner {
400 pub fn new(basis: Vec<Polynomial>) -> Self {
402 GebauerMollerPruner { basis }
403 }
404 pub fn product_criterion(&self, i: usize, j: usize) -> bool {
407 let lm_i = match self.basis.get(i).and_then(|f| f.leading_monomial()) {
408 Some(m) => m.clone(),
409 None => return false,
410 };
411 let lm_j = match self.basis.get(j).and_then(|f| f.leading_monomial()) {
412 Some(m) => m.clone(),
413 None => return false,
414 };
415 lm_i.exponents
416 .iter()
417 .zip(lm_j.exponents.iter())
418 .all(|(&a, &b)| a == 0 || b == 0)
419 }
420 pub fn chain_criterion(&self, i: usize, j: usize) -> bool {
423 let lm_i = match self.basis.get(i).and_then(|f| f.leading_monomial()) {
424 Some(m) => m.clone(),
425 None => return false,
426 };
427 let lm_j = match self.basis.get(j).and_then(|f| f.leading_monomial()) {
428 Some(m) => m.clone(),
429 None => return false,
430 };
431 let lcm_ij = lm_i.lcm(&lm_j);
432 self.basis.iter().enumerate().any(|(k, h)| {
433 k != i
434 && k != j
435 && h.leading_monomial()
436 .is_some_and(|lm_h| lm_h.divides(&lcm_ij))
437 })
438 }
439 pub fn filter_pairs(&self, pairs: Vec<(usize, usize)>) -> Vec<(usize, usize)> {
441 pairs
442 .into_iter()
443 .filter(|&(i, j)| !self.product_criterion(i, j) && !self.chain_criterion(i, j))
444 .collect()
445 }
446 pub fn count_necessary_pairs(&self) -> usize {
448 let n = self.basis.len();
449 let all_pairs: Vec<(usize, usize)> = (0..n)
450 .flat_map(|i| (i + 1..n).map(move |j| (i, j)))
451 .collect();
452 self.filter_pairs(all_pairs).len()
453 }
454}
455#[derive(Debug, Clone)]
458pub struct BuchbergerCriterion {
459 pub basis: Vec<Polynomial>,
461}
462impl BuchbergerCriterion {
463 pub fn new(basis: Vec<Polynomial>) -> Self {
465 Self { basis }
466 }
467 pub fn check(&self) -> bool {
469 let gs = &self.basis;
470 for i in 0..gs.len() {
471 for j in (i + 1)..gs.len() {
472 let sp = s_polynomial(&gs[i], &gs[j]);
473 let rem = reduce(&sp, gs);
474 if !rem.is_zero() {
475 return false;
476 }
477 }
478 }
479 true
480 }
481}
482#[derive(Debug, Clone)]
484pub struct ResolutionStep {
485 pub source_rank: usize,
487 pub target_rank: usize,
489 pub matrix: Vec<FreeModuleElement>,
491}
492#[derive(Debug, Clone)]
494pub struct HilbertPolynomial {
495 pub coefficients: Vec<f64>,
497}
498impl HilbertPolynomial {
499 pub fn eval(&self, t: i64) -> f64 {
501 self.coefficients
502 .iter()
503 .enumerate()
504 .map(|(k, &c)| c * (t as f64).powi(k as i32))
505 .sum()
506 }
507 pub fn dimension(&self) -> usize {
509 self.coefficients
510 .iter()
511 .rposition(|&c| c.abs() > 1e-10)
512 .unwrap_or(0)
513 }
514 pub fn degree(&self) -> f64 {
516 let d = self.dimension();
517 self.coefficients.get(d).copied().unwrap_or(0.0)
518 }
519}
520pub struct CoxRing {
522 pub variety: String,
524 pub grading: Vec<Vec<i64>>,
526}
527impl CoxRing {
528 pub fn new(variety: String, grading: Vec<Vec<i64>>) -> Self {
530 Self { variety, grading }
531 }
532 pub fn generators(&self) -> Vec<String> {
537 self.grading
538 .iter()
539 .enumerate()
540 .map(|(i, deg)| {
541 let deg_str: Vec<String> = deg.iter().map(|d| d.to_string()).collect();
542 format!("x_{i} (degree [{}])", deg_str.join(", "))
543 })
544 .collect()
545 }
546 pub fn is_finitely_generated(&self) -> bool {
551 !self.variety.is_empty()
552 }
553}
554pub struct SystemSolver {
558 pub basis: ReducedGroebnerBasis,
560}
561impl SystemSolver {
562 pub fn new(basis: ReducedGroebnerBasis) -> Self {
564 Self { basis }
565 }
566 pub fn num_solutions(&self) -> usize {
570 let hf = HilbertFunction::compute(&self.basis, 20);
571 let vals = &hf.values;
572 for i in (1..vals.len()).rev() {
573 if vals[i] != vals[i - 1] {
574 return vals[i];
575 }
576 }
577 vals.last().copied().unwrap_or(0)
578 }
579}
580#[derive(Debug, Clone)]
582pub struct ReducedGroebnerBasis {
583 pub generators: Vec<Polynomial>,
585 pub nvars: usize,
587 pub order: MonomialOrder,
589}
590impl ReducedGroebnerBasis {
591 pub fn from_groebner(gb: GroebnerBasis) -> Self {
593 let mut gens = gb.generators;
594 gens = gens.into_iter().map(|g| g.make_monic()).collect();
595 let mut i = 0;
596 while i < gens.len() {
597 let lm_i = match gens[i].leading_monomial() {
598 Some(m) => m.clone(),
599 None => {
600 gens.remove(i);
601 continue;
602 }
603 };
604 let redundant = gens.iter().enumerate().any(|(j, gj)| {
605 j != i
606 && gj
607 .leading_monomial()
608 .is_some_and(|lm_j| lm_j.divides(&lm_i))
609 });
610 if redundant {
611 gens.remove(i);
612 } else {
613 i += 1;
614 }
615 }
616 let n = gens.len();
617 for i in 0..n {
618 let others: Vec<Polynomial> = gens
619 .iter()
620 .enumerate()
621 .filter(|(j, _)| *j != i)
622 .map(|(_, g)| g.clone())
623 .collect();
624 let gi = gens[i].clone();
625 gens[i] = reduce(&gi, &others);
626 gens[i] = gens[i].make_monic();
627 }
628 gens.retain(|g| !g.is_zero());
629 let order = gb.order;
630 let nvars = gb.nvars;
631 Self {
632 generators: gens,
633 nvars,
634 order,
635 }
636 }
637}
638pub struct ImplicitizationProblem {
641 pub parametric: Vec<Polynomial>,
643 pub num_params: usize,
645 pub num_coords: usize,
647}
648impl ImplicitizationProblem {
649 pub fn new(parametric: Vec<Polynomial>, num_params: usize) -> Self {
651 let num_coords = parametric.len();
652 Self {
653 parametric,
654 num_params,
655 num_coords,
656 }
657 }
658 pub fn solve(&self, nvars_total: usize) -> Vec<Polynomial> {
660 let ops = IdealOperations::new(nvars_total, MonomialOrder::GradedRevLex);
661 ops.elimination_ideal(self.parametric.clone(), self.num_params)
662 }
663}
664#[allow(dead_code)]
669pub struct RadicalMembershipTester {
670 pub ideal: Vec<Polynomial>,
672 pub f: Polynomial,
674 pub nvars: usize,
676 pub order: MonomialOrder,
678}
679impl RadicalMembershipTester {
680 pub fn new(ideal: Vec<Polynomial>, f: Polynomial, nvars: usize, order: MonomialOrder) -> Self {
682 RadicalMembershipTester {
683 ideal,
684 f,
685 nvars,
686 order,
687 }
688 }
689 pub fn rabinowitsch_system_size(&self) -> usize {
692 self.ideal.len() + 1
693 }
694 pub fn is_radical_member(&self) -> bool {
697 if self.ideal.len() == 1 {
698 let g = &self.ideal[0];
699 let rem = reduce(&self.f, std::slice::from_ref(g));
700 return rem.is_zero();
701 }
702 let rem = reduce(&self.f, &self.ideal);
703 rem.is_zero()
704 }
705 pub fn exponent_estimate(&self) -> usize {
707 if self.is_radical_member() {
708 1
709 } else {
710 let f2 = self.f.mul(&self.f);
711 let rem2 = reduce(&f2, &self.ideal);
712 if rem2.is_zero() {
713 2
714 } else {
715 0
716 }
717 }
718 }
719}
720#[derive(Debug, Clone)]
724pub struct SyzygyModule {
725 pub generators: Vec<Syzygy>,
727 pub rank: usize,
729}
730impl SyzygyModule {
731 pub fn compute(polys: &[Polynomial]) -> Self {
733 let n = polys.len();
734 if n == 0 {
735 return Self {
736 generators: vec![],
737 rank: 0,
738 };
739 }
740 let nvars = polys[0].nvars;
741 let order = polys[0].order.clone();
742 let mut syz_gens: Vec<Syzygy> = Vec::new();
743 for i in 0..n {
744 for j in (i + 1)..n {
745 let lm_i = match polys[i].leading_monomial() {
746 Some(m) => m.clone(),
747 None => continue,
748 };
749 let lm_j = match polys[j].leading_monomial() {
750 Some(m) => m.clone(),
751 None => continue,
752 };
753 let lcm = lm_i.lcm(&lm_j);
754 let gamma_i = lcm.div(&lm_i).unwrap_or_default();
755 let gamma_j = lcm.div(&lm_j).unwrap_or_default();
756 let (lc_i_num, lc_i_den) = polys[i].leading_coeff().unwrap_or((1, 1));
757 let (lc_j_num, lc_j_den) = polys[j].leading_coeff().unwrap_or((1, 1));
758 let mut components: Vec<Polynomial> = (0..n)
759 .map(|_| Polynomial::zero(nvars, order.clone()))
760 .collect();
761 components[i] = Polynomial {
762 nvars,
763 terms: vec![Term::rational(gamma_i, lc_j_den, lc_i_num * lc_j_num)],
764 order: order.clone(),
765 };
766 let neg_j_term = Term::rational(gamma_j, -lc_i_den, lc_i_num * lc_j_num);
767 components[j] = Polynomial {
768 nvars,
769 terms: if neg_j_term.is_zero() {
770 vec![]
771 } else {
772 vec![neg_j_term]
773 },
774 order: order.clone(),
775 };
776 syz_gens.push(Syzygy { components });
777 }
778 }
779 Self {
780 generators: syz_gens,
781 rank: n,
782 }
783 }
784 pub fn num_generators(&self) -> usize {
786 self.generators.len()
787 }
788}
789#[allow(dead_code)]
795pub struct FaugereF4Simulator {
796 pub basis: Vec<Polynomial>,
798 pub nvars: usize,
800 pub order: MonomialOrder,
802 pub degree_bound: u32,
804}
805impl FaugereF4Simulator {
806 pub fn new(basis: Vec<Polynomial>, nvars: usize, order: MonomialOrder) -> Self {
808 FaugereF4Simulator {
809 basis,
810 nvars,
811 order,
812 degree_bound: 1,
813 }
814 }
815 pub fn select_pairs(&self) -> Vec<(usize, usize)> {
817 let gs = &self.basis;
818 let mut selected = Vec::new();
819 for i in 0..gs.len() {
820 for j in (i + 1)..gs.len() {
821 if let (Some(lm_i), Some(lm_j)) =
822 (gs[i].leading_monomial(), gs[j].leading_monomial())
823 {
824 let lcm = lm_i.lcm(lm_j);
825 if lcm.degree() <= self.degree_bound {
826 selected.push((i, j));
827 }
828 }
829 }
830 }
831 selected
832 }
833 pub fn step(&mut self) -> usize {
836 let pairs = self.select_pairs();
837 let mut new_polys = Vec::new();
838 for (i, j) in pairs {
839 if i < self.basis.len() && j < self.basis.len() {
840 let sp = s_polynomial(&self.basis[i], &self.basis[j]);
841 let rem = reduce(&sp, &self.basis);
842 if !rem.is_zero() {
843 new_polys.push(rem);
844 }
845 }
846 }
847 let count = new_polys.len();
848 self.basis.extend(new_polys);
849 self.degree_bound += 1;
850 count
851 }
852 pub fn run_to_convergence(&mut self, max_steps: usize) -> usize {
854 for step_idx in 0..max_steps {
855 let added = self.step();
856 if added == 0 {
857 return step_idx + 1;
858 }
859 }
860 max_steps
861 }
862 pub fn to_groebner_basis(&self) -> GroebnerBasis {
864 GroebnerBasis::new(self.basis.clone(), self.nvars, self.order.clone())
865 }
866}
867#[derive(Debug, Clone)]
870pub struct Syzygy {
871 pub components: Vec<Polynomial>,
873}
874#[derive(Debug, Clone, Default)]
876pub struct BettiNumbers {
877 pub table: HashMap<(usize, usize), usize>,
879}
880impl BettiNumbers {
881 pub fn new() -> Self {
883 Self::default()
884 }
885 pub fn set(&mut self, i: usize, j: usize, val: usize) {
887 self.table.insert((i, j), val);
888 }
889 pub fn get(&self, i: usize, j: usize) -> usize {
891 self.table.get(&(i, j)).copied().unwrap_or(0)
892 }
893 pub fn regularity(&self) -> Option<usize> {
895 self.table
896 .iter()
897 .filter(|(_, &v)| v > 0)
898 .map(|(&(i, j), _)| j.saturating_sub(i))
899 .max()
900 }
901}
902#[derive(Debug, Clone)]
905pub struct ToricIdeal {
906 pub matrix: Vec<Vec<i64>>,
908 pub nvars: usize,
910}
911impl ToricIdeal {
912 pub fn new(matrix: Vec<Vec<i64>>) -> Self {
914 let nvars = matrix.len();
915 Self { matrix, nvars }
916 }
917 pub fn generators(&self, order: MonomialOrder) -> Vec<Polynomial> {
921 let _order = order;
922 vec![]
923 }
924}
925pub struct IdealOperations {
927 pub nvars: usize,
929 pub order: MonomialOrder,
931}
932impl IdealOperations {
933 pub fn new(nvars: usize, order: MonomialOrder) -> Self {
935 Self { nvars, order }
936 }
937 pub fn elimination_ideal(&self, generators: Vec<Polynomial>, k: usize) -> Vec<Polynomial> {
940 let elim_order = MonomialOrder::Elimination(k);
941 let algo = BuchbergerAlgorithm::new(self.nvars, elim_order.clone());
942 let gb = algo.buchberger(generators);
943 gb.generators
944 .into_iter()
945 .filter(|g| {
946 g.terms
947 .iter()
948 .all(|t| t.monomial.exponents.iter().take(k).all(|&e| e == 0))
949 })
950 .collect()
951 }
952}
953#[derive(Debug, Clone, PartialEq, Eq, Default)]
955pub struct Monomial {
956 pub exponents: Vec<u32>,
958}
959impl Monomial {
960 pub fn new(exponents: Vec<u32>) -> Self {
962 Self { exponents }
963 }
964 pub fn degree(&self) -> u32 {
966 self.exponents.iter().sum()
967 }
968 pub fn nvars(&self) -> usize {
970 self.exponents.len()
971 }
972 pub fn lcm(&self, other: &Monomial) -> Monomial {
974 let n = self.exponents.len().max(other.exponents.len());
975 let exponents = (0..n)
976 .map(|i| {
977 let a = self.exponents.get(i).copied().unwrap_or(0);
978 let b = other.exponents.get(i).copied().unwrap_or(0);
979 a.max(b)
980 })
981 .collect();
982 Monomial { exponents }
983 }
984 pub fn divides(&self, other: &Monomial) -> bool {
986 self.exponents.iter().enumerate().all(|(i, &a)| {
987 let b = other.exponents.get(i).copied().unwrap_or(0);
988 a <= b
989 })
990 }
991 pub fn mul(&self, other: &Monomial) -> Monomial {
993 let n = self.exponents.len().max(other.exponents.len());
994 let exponents = (0..n)
995 .map(|i| {
996 let a = self.exponents.get(i).copied().unwrap_or(0);
997 let b = other.exponents.get(i).copied().unwrap_or(0);
998 a + b
999 })
1000 .collect();
1001 Monomial { exponents }
1002 }
1003 pub fn div(&self, other: &Monomial) -> Option<Monomial> {
1005 if !other.divides(self) {
1006 return None;
1007 }
1008 let exponents = self
1009 .exponents
1010 .iter()
1011 .enumerate()
1012 .map(|(i, &a)| {
1013 let b = other.exponents.get(i).copied().unwrap_or(0);
1014 a - b
1015 })
1016 .collect();
1017 Some(Monomial { exponents })
1018 }
1019 pub fn cmp_lex(&self, other: &Monomial) -> std::cmp::Ordering {
1021 let n = self.exponents.len().max(other.exponents.len());
1022 for i in 0..n {
1023 let a = self.exponents.get(i).copied().unwrap_or(0);
1024 let b = other.exponents.get(i).copied().unwrap_or(0);
1025 match a.cmp(&b) {
1026 std::cmp::Ordering::Equal => {}
1027 ord => return ord,
1028 }
1029 }
1030 std::cmp::Ordering::Equal
1031 }
1032 pub fn cmp_grlex(&self, other: &Monomial) -> std::cmp::Ordering {
1034 match self.degree().cmp(&other.degree()) {
1035 std::cmp::Ordering::Equal => self.cmp_lex(other),
1036 ord => ord,
1037 }
1038 }
1039 pub fn cmp_grevlex(&self, other: &Monomial) -> std::cmp::Ordering {
1041 match self.degree().cmp(&other.degree()) {
1042 std::cmp::Ordering::Equal => {
1043 let n = self.exponents.len().max(other.exponents.len());
1044 for i in (0..n).rev() {
1045 let a = self.exponents.get(i).copied().unwrap_or(0);
1046 let b = other.exponents.get(i).copied().unwrap_or(0);
1047 match b.cmp(&a) {
1048 std::cmp::Ordering::Equal => {}
1049 ord => return ord,
1050 }
1051 }
1052 std::cmp::Ordering::Equal
1053 }
1054 ord => ord,
1055 }
1056 }
1057 pub fn cmp_with_order(&self, other: &Monomial, order: &MonomialOrder) -> std::cmp::Ordering {
1059 match order {
1060 MonomialOrder::Lex => self.cmp_lex(other),
1061 MonomialOrder::GradedLex => self.cmp_grlex(other),
1062 MonomialOrder::GradedRevLex => self.cmp_grevlex(other),
1063 MonomialOrder::Elimination(k) => {
1064 let k = *k;
1065 let n = self.exponents.len().max(other.exponents.len());
1066 for i in 0..k.min(n) {
1067 let a = self.exponents.get(i).copied().unwrap_or(0);
1068 let b = other.exponents.get(i).copied().unwrap_or(0);
1069 match a.cmp(&b) {
1070 std::cmp::Ordering::Equal => {}
1071 ord => return ord,
1072 }
1073 }
1074 let self_tail = Monomial {
1075 exponents: self.exponents.get(k..).unwrap_or(&[]).to_vec(),
1076 };
1077 let other_tail = Monomial {
1078 exponents: other.exponents.get(k..).unwrap_or(&[]).to_vec(),
1079 };
1080 self_tail.cmp_grlex(&other_tail)
1081 }
1082 MonomialOrder::Weight(w) => {
1083 let wa: i64 = self
1084 .exponents
1085 .iter()
1086 .enumerate()
1087 .map(|(i, &e)| w.get(i).copied().unwrap_or(1) * e as i64)
1088 .sum();
1089 let wb: i64 = other
1090 .exponents
1091 .iter()
1092 .enumerate()
1093 .map(|(i, &e)| w.get(i).copied().unwrap_or(1) * e as i64)
1094 .sum();
1095 match wa.cmp(&wb) {
1096 std::cmp::Ordering::Equal => self.cmp_lex(other),
1097 ord => ord,
1098 }
1099 }
1100 }
1101 }
1102}
1103#[derive(Debug, Clone)]
1105pub struct GroebnerBasis {
1106 pub generators: Vec<Polynomial>,
1108 pub nvars: usize,
1110 pub order: MonomialOrder,
1112}
1113impl GroebnerBasis {
1114 pub fn new(generators: Vec<Polynomial>, nvars: usize, order: MonomialOrder) -> Self {
1116 Self {
1117 generators,
1118 nvars,
1119 order,
1120 }
1121 }
1122 pub fn is_groebner_basis(&self) -> bool {
1124 let gs = &self.generators;
1125 for i in 0..gs.len() {
1126 for j in (i + 1)..gs.len() {
1127 let sp = s_polynomial(&gs[i], &gs[j]);
1128 let rem = reduce(&sp, gs);
1129 if !rem.is_zero() {
1130 return false;
1131 }
1132 }
1133 }
1134 true
1135 }
1136 pub fn reduce_to_normal_form(&self, f: &Polynomial) -> Polynomial {
1138 reduce(f, &self.generators)
1139 }
1140}
1141pub struct PolynomialSystem {
1143 pub polys: Vec<String>,
1145 pub vars: Vec<String>,
1147}
1148impl PolynomialSystem {
1149 pub fn new(polys: Vec<String>, vars: Vec<String>) -> Self {
1151 Self { polys, vars }
1152 }
1153 pub fn solve_over_algebraic_closure(&self) -> Vec<String> {
1159 vec![
1160 format!(
1161 "Compute reduced Gröbner basis of ({}) in lex order on variables ({}).",
1162 self.polys.join(", "),
1163 self.vars.join(" > "),
1164 ),
1165 "Apply Shape Lemma: last variable satisfies a univariate polynomial.".to_string(),
1166 "Back-substitute to find all solutions in the algebraic closure.".to_string(),
1167 ]
1168 }
1169 pub fn count_solutions(&self) -> usize {
1174 if self.polys.is_empty() {
1175 0
1176 } else {
1177 self.polys.len() * 2
1178 }
1179 }
1180}
1181pub struct ElimAlgebra {
1183 pub variables_to_elim: Vec<String>,
1185 pub all_variables: Vec<String>,
1187}
1188impl ElimAlgebra {
1189 pub fn new(variables_to_elim: Vec<String>, all_variables: Vec<String>) -> Self {
1191 Self {
1192 variables_to_elim,
1193 all_variables,
1194 }
1195 }
1196 pub fn eliminate(&self, basis: &GroebnerBasis) -> Vec<String> {
1202 let elim_set: std::collections::HashSet<&str> =
1203 self.variables_to_elim.iter().map(|s| s.as_str()).collect();
1204 basis
1205 .generators
1206 .iter()
1207 .filter_map(|g| {
1208 let g_str = g.to_string();
1209 if elim_set.iter().any(|v| g_str.contains(*v)) {
1210 None
1211 } else {
1212 Some(g_str)
1213 }
1214 })
1215 .collect()
1216 }
1217 pub fn projection_theorem(&self, basis: &GroebnerBasis) -> String {
1220 let elim = self.eliminate(basis);
1221 format!(
1222 "Projection theorem: V(I) projects into V(I_{k}) ⊆ k^{m} \
1223 where I_{k} is generated by {n} polynomials.",
1224 k = self.variables_to_elim.len(),
1225 m = self.all_variables.len() - self.variables_to_elim.len(),
1226 n = elim.len(),
1227 )
1228 }
1229}
1230#[derive(Debug, Clone)]
1234pub struct NullstellensatzCertificate {
1235 pub polynomial: String,
1237 pub ideal_generators: Vec<String>,
1239 pub exponent: usize,
1241 pub cofactors: Vec<String>,
1243}
1244impl NullstellensatzCertificate {
1245 pub fn new(
1247 polynomial: String,
1248 ideal_generators: Vec<String>,
1249 exponent: usize,
1250 cofactors: Vec<String>,
1251 ) -> Self {
1252 Self {
1253 polynomial,
1254 ideal_generators,
1255 exponent,
1256 cofactors,
1257 }
1258 }
1259}
1260#[derive(Debug, Clone, PartialEq, Eq)]
1262pub enum MonomialOrder {
1263 Lex,
1265 GradedLex,
1267 GradedRevLex,
1269 Elimination(usize),
1271 Weight(Vec<i64>),
1273}
1274#[derive(Debug, Clone, PartialEq)]
1276pub struct Term {
1277 pub monomial: Monomial,
1279 pub coeff_num: i64,
1281 pub coeff_den: i64,
1283}
1284impl Term {
1285 pub fn new(monomial: Monomial, coeff: i64) -> Self {
1287 Self {
1288 monomial,
1289 coeff_num: coeff,
1290 coeff_den: 1,
1291 }
1292 }
1293 pub fn rational(monomial: Monomial, num: i64, den: i64) -> Self {
1295 assert_ne!(den, 0, "denominator must be nonzero");
1296 let g = gcd(num.unsigned_abs(), den.unsigned_abs()) as i64;
1297 let sign = if den < 0 { -1 } else { 1 };
1298 Self {
1299 monomial,
1300 coeff_num: sign * num / g,
1301 coeff_den: sign * den / g,
1302 }
1303 }
1304 pub fn is_zero(&self) -> bool {
1306 self.coeff_num == 0
1307 }
1308 pub fn scale(&self, num: i64, den: i64) -> Term {
1310 Term::rational(
1311 self.monomial.clone(),
1312 self.coeff_num * num,
1313 self.coeff_den * den,
1314 )
1315 }
1316}
1317pub struct IdealMembership {
1319 pub basis: GroebnerBasis,
1321}
1322impl IdealMembership {
1323 pub fn new(basis: GroebnerBasis) -> Self {
1325 Self { basis }
1326 }
1327 pub fn contains(&self, f: &Polynomial) -> bool {
1329 self.basis.reduce_to_normal_form(f).is_zero()
1330 }
1331}
1332pub struct SyzygiesComputation {
1334 pub module: String,
1336 pub rank: usize,
1338}
1339impl SyzygiesComputation {
1340 pub fn new(module: String, rank: usize) -> Self {
1342 Self { module, rank }
1343 }
1344 pub fn compute_syzygies(&self) -> Vec<String> {
1349 (0..self.rank)
1350 .flat_map(|i| {
1351 (i + 1..self.rank)
1352 .map(move |j| {
1353 format!(
1354 "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})"
1355 )
1356 })
1357 })
1358 .collect()
1359 }
1360 pub fn free_resolution(&self) -> Vec<String> {
1364 let mut steps = Vec::new();
1365 let mut current_rank = self.rank;
1366 let mut depth = 0usize;
1367 while current_rank > 0 && depth < self.rank + 1 {
1368 let next_rank = if current_rank > 1 {
1369 current_rank - 1
1370 } else {
1371 0
1372 };
1373 steps.push(format!(
1374 "F_{depth}: k[vars]^{current_rank} --d_{depth}--> F_{prev}",
1375 prev = if depth == 0 {
1376 "M".to_string()
1377 } else {
1378 format!("{}", depth - 1)
1379 },
1380 ));
1381 current_rank = next_rank;
1382 depth += 1;
1383 }
1384 steps
1385 }
1386}
1387#[derive(Debug, Clone)]
1389pub struct MinimalFreeResolution {
1390 pub steps: Vec<ResolutionStep>,
1392 pub projective_dim: usize,
1394}
1395impl MinimalFreeResolution {
1396 pub fn betti_numbers(&self) -> Vec<usize> {
1398 self.steps.iter().map(|s| s.source_rank).collect()
1399 }
1400}