diff --git a/src/doc/en/reference/matroids/index.rst b/src/doc/en/reference/matroids/index.rst index b52c5b3420a..284caabe8d0 100644 --- a/src/doc/en/reference/matroids/index.rst +++ b/src/doc/en/reference/matroids/index.rst @@ -34,6 +34,15 @@ Concrete implementations sage/matroids/rank_matroid sage/matroids/graphic_matroid +Chow rings of matroids +---------------------- + +.. toctree:: + :maxdepth: 1 + + sage/matroids/chow_ring_ideal + sage/matroids/chow_ring + Abstract matroid classes ------------------------ diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index 3d548b48511..9aef0974409 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -261,6 +261,9 @@ REFERENCES: finite Drinfeld modules.* manuscripta mathematica 93, 1 (01 Aug 1997), 369–379. https://doi.org/10.1007/BF02677478 +.. [ANR2023] Robert Angarone, Anastasia Nathanson, and Victor Reiner. *Chow rings of + matroids as permutation representations*, 2023. :arxiv:`2309.14312`. + .. [AP1986] \S. Arnborg, A. Proskurowski, *Characterization and Recognition of Partial 3-Trees*, SIAM Journal of Alg. and Discrete Methods, @@ -4928,6 +4931,9 @@ REFERENCES: .. [MM2015] \J. Matherne and \G. Muller, *Computing upper cluster algebras*, Int. Math. Res. Not. IMRN, 2015, 3121-3149. +.. [MM2022] Matthew Mastroeni and Jason McCullough. *Chow rings of matroids are + Koszul*. Mathematische Annalen, 387(3-4):1819-1851, November 2022. + .. [MMRS2022] Ruslan G. Marzo, Rafael A. Melo, Celso C. Ribeiro and Marcio C. Santos: *New formulations and branch-and-cut procedures for the longest induced path problem*. Computers & Operations diff --git a/src/sage/matroids/chow_ring.py b/src/sage/matroids/chow_ring.py new file mode 100644 index 00000000000..173c8db7f84 --- /dev/null +++ b/src/sage/matroids/chow_ring.py @@ -0,0 +1,339 @@ +r""" +Chow rings of matroids + +AUTHORS: + +- Shriya M +""" + +from sage.matroids.chow_ring_ideal import ChowRingIdeal_nonaug, AugmentedChowRingIdeal_fy, AugmentedChowRingIdeal_atom_free +from sage.rings.quotient_ring import QuotientRing_generic +from sage.categories.graded_algebras_with_basis import GradedAlgebrasWithBasis +from sage.categories.commutative_rings import CommutativeRings + +class ChowRing(QuotientRing_generic): + r""" + The Chow ring of a matroid. + + The *Chow ring of the matroid* `M` is defined as the quotient ring + + .. MATH:: + + A^*(M)_R := R[x_{F_1}, \ldots, x_{F_k}] / (I_M + J_M), + + where `(I_M + J_M)` is the :class:`Chow ring ideal + ` of matroid `M`. + + The *augmented Chow ring of matroid* `M` has two different presentations + as quotient rings: + + The *Feitchner-Yuzvinsky presentation* is the quotient ring + + .. MATH:: + + A(M)_R := R[y_{e_1}, \ldots, y_{e_n}, x_{F_1}, \ldots, x_{F_k}] / I_{FY}(M), + + where `I_{FY}(M)` is the :class:`Feitchner-Yuzvinsky augmented Chow ring + ideal ` + of matroid `M`. + + The *atom-free presentation* is the quotient ring + + .. MATH:: + + A(M)_R := R[x_{F_1}, \ldots, x_{F_k}] / I_{af}(M), + + where `I_{af}(M)` is the :class:`atom-free augmented Chow ring ideal + ` + of matroid `M`. + + .. SEEALSO:: + + :mod:`sage.matroids.chow_ring_ideal` + + INPUT: + + - ``M`` -- matroid + - ``R`` -- commutative ring + - ``augmented`` -- boolean; when ``True``, this is the augmented + Chow ring and if ``False``, this is the non-augmented Chow ring + - ``presentation`` -- string (default: ``None``); one of the following + (ignored if ``augmented=False``) + + * ``"fy"`` - the Feitchner-Yuzvinsky presentation + * ``"atom-free"`` - the atom-free presentation + + REFERENCES: + + - [FY2004]_ + - [AHK2015]_ + + EXAMPLES:: + + sage: M1 = matroids.catalog.P8pp() + sage: ch = M1.chow_ring(QQ, False) + sage: ch + Chow ring of P8'': Matroid of rank 4 on 8 elements with 8 nonspanning circuits + over Rational Field + """ + def __init__(self, R, M, augmented, presentation=None): + r""" + Initialize ``self``. + + EXAMPLES:: + + sage: ch = matroids.Wheel(3).chow_ring(QQ, False) + sage: TestSuite(ch).run() + """ + self._matroid = M + self._augmented = augmented + self._presentation = presentation + if augmented is True: + if presentation == 'fy': + self._ideal = AugmentedChowRingIdeal_fy(M, R) + elif presentation == 'atom-free': + self._ideal = AugmentedChowRingIdeal_atom_free(M, R) + else: + self._ideal = ChowRingIdeal_nonaug(M, R) + C = CommutativeRings().Quotients() & GradedAlgebrasWithBasis(R).FiniteDimensional() + QuotientRing_generic.__init__(self, R=self._ideal.ring(), + I=self._ideal, + names=self._ideal.ring().variable_names(), + category=C) + + def _repr_(self): + r""" + EXAMPLES:: + + sage: M1 = matroids.catalog.Fano() + sage: ch = M1.chow_ring(QQ, False) + sage: ch + Chow ring of Fano: Binary matroid of rank 3 on 7 elements, type (3, 0) + over Rational Field + """ + output = "Chow ring of {}".format(self._matroid) + if self._augmented is True: + output = "Augmented " + output + if self._presentation == 'fy': + output += " in Feitchner-Yuzvinsky presentation" + elif self._presentation == 'atom-free': + output += " in atom-free presentation" + return output + " over " + repr(self.base_ring()) + + def _latex_(self): + r""" + Return the LaTeX output of the polynomial ring and Chow ring ideal. + + EXAMPLES:: + + sage: M1 = matroids.Uniform(2,5) + sage: ch = M1.chow_ring(QQ, False) + sage: ch._latex_() + 'A(\\begin{array}{l}\n\\text{\\texttt{U(2,{ }5):{ }Matroid{ }of{ }rank{ }2{ }on{ }5{ }elements{ }with{ }circuit{-}closures}}\\\\\n\\text{\\texttt{{\\char`\\{}2:{ }{\\char`\\{}{\\char`\\{}0,{ }1,{ }2,{ }3,{ }4{\\char`\\}}{\\char`\\}}{\\char`\\}}}}\n\\end{array})_{\\Bold{Q}}' + """ + from sage.misc.latex import latex + base = "A({})_{{{}}}" + if self._augmented: + base += "^*" + return base.format(latex(self._matroid), latex(self.base_ring())) + + def matroid(self): + r""" + Return the matroid of ``self``. + + EXAMPLES:: + + sage: ch = matroids.Uniform(3,6).chow_ring(QQ, True, 'fy') + sage: ch.matroid() + U(3, 6): Matroid of rank 3 on 6 elements with circuit-closures + {3: {{0, 1, 2, 3, 4, 5}}} + """ + return self._matroid + + def _coerce_map_from_base_ring(self): + r""" + Disable the coercion from the base ring from the category. + + TESTS:: + + sage: ch = matroids.Wheel(3).chow_ring(QQ, False) + sage: ch._coerce_map_from_base_ring() is None + True + """ + return None # don't need anything special + + def basis(self): + r""" + Return the monomial basis of the given Chow ring. + + EXAMPLES:: + + sage: ch = matroids.Uniform(3, 6).chow_ring(QQ, True, 'fy') + sage: ch.basis() + Family (1, B1, B1*B012345, B0, B0*B012345, B01, B01^2, B2, + B2*B012345, B02, B02^2, B12, B12^2, B3, B3*B012345, B03, B03^2, + B13, B13^2, B23, B23^2, B4, B4*B012345, B04, B04^2, B14, B14^2, + B24, B24^2, B34, B34^2, B5, B5*B012345, B05, B05^2, B15, B15^2, + B25, B25^2, B35, B35^2, B45, B45^2, B012345, B012345^2, B012345^3) + sage: set(ch.defining_ideal().normal_basis()) == set(ch.basis()) + True + sage: ch = matroids.catalog.Fano().chow_ring(QQ, False) + sage: ch.basis() + Family (1, Abcd, Aace, Aabf, Adef, Aadg, Abeg, Acfg, Aabcdefg, + Aabcdefg^2) + sage: set(ch.defining_ideal().normal_basis()) == set(ch.basis()) + True + sage: ch = matroids.Wheel(3).chow_ring(QQ, True, 'atom-free') + sage: ch.basis() + Family (1, A0, A0*A012345, A2, A2*A012345, A3, A3*A012345, A23, + A23^2, A1, A1*A012345, A013, A013^2, A4, A4*A012345, A04, A04^2, + A124, A124^2, A5, A5*A012345, A025, A025^2, A15, A15^2, A345, + A345^2, A012345, A012345^2, A012345^3) + sage: set(ch.defining_ideal().normal_basis()) == set(ch.basis()) + True + """ + from sage.sets.family import Family + monomial_basis = self._ideal.normal_basis() + return Family([self.element_class(self, mon, reduce=False) for mon in monomial_basis]) + + class Element(QuotientRing_generic.Element): + def to_vector(self, order=None): + r""" + Return ``self`` as a (dense) free module vector. + + EXAMPLES:: + + sage: ch = matroids.Uniform(3, 6).chow_ring(QQ, False) + sage: v = ch.an_element(); v + -A01 - A02 - A03 - A04 - A05 - A012345 + sage: v.to_vector() + (0, -1, -1, 0, -1, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, 0, -1, 0) + """ + P = self.parent() + B = P.basis() + FM = P._dense_free_module() + f = self.lift() + return FM([f.monomial_coefficient(b.lift()) for b in B]) + + _vector_ = to_vector + + def monomial_coefficients(self, copy=None): + r""" + Return the monomial coefficients of ``self``. + + EXAMPLES:: + + sage: ch = matroids.catalog.NonFano().chow_ring(QQ, True, 'atom-free') + sage: v = ch.an_element(); v + Aa + sage: v.monomial_coefficients() + {0: 0, 1: 1, 2: 0, 3: 0, 4: 0, 5: 0, 6: 0, 7: 0, 8: 0, 9: 0, + 10: 0, 11: 0, 12: 0, 13: 0, 14: 0, 15: 0, 16: 0, 17: 0, + 18: 0, 19: 0, 20: 0, 21: 0, 22: 0, 23: 0, 24: 0, 25: 0, + 26: 0, 27: 0, 28: 0, 29: 0, 30: 0, 31: 0, 32: 0, 33: 0, + 34: 0, 35: 0} + """ + B = self.parent().basis() + f = self.lift() + return {i: f.monomial_coefficient(b.lift()) for i, b in enumerate(B)} + + def degree(self): + r""" + Return the degree of ``self``. + + EXAMPLES:: + + sage: ch = matroids.Uniform(3, 6).chow_ring(QQ, False) + sage: for b in ch.basis(): + ....: print(b, b.degree()) + 1 0 + A01 1 + A02 1 + A12 1 + A03 1 + A13 1 + A23 1 + A04 1 + A14 1 + A24 1 + A34 1 + A05 1 + A15 1 + A25 1 + A35 1 + A45 1 + A012345 1 + A012345^2 2 + sage: v = sum(ch.basis()) + sage: v.degree() + 2 + """ + return self.lift().degree() + + def homogeneous_degree(self): + r""" + Return the (homogeneous) degree of ``self`` if homogeneous + otherwise raise an error. + + EXAMPLES:: + + sage: ch = matroids.catalog.Fano().chow_ring(QQ, True, 'fy') + sage: for b in ch.basis(): + ....: print(b, b.homogeneous_degree()) + 1 0 + Ba 1 + Ba*Babcdefg 2 + Bb 1 + Bb*Babcdefg 2 + Bc 1 + Bc*Babcdefg 2 + Bd 1 + Bd*Babcdefg 2 + Bbcd 1 + Bbcd^2 2 + Be 1 + Be*Babcdefg 2 + Bace 1 + Bace^2 2 + Bf 1 + Bf*Babcdefg 2 + Babf 1 + Babf^2 2 + Bdef 1 + Bdef^2 2 + Bg 1 + Bg*Babcdefg 2 + Badg 1 + Badg^2 2 + Bbeg 1 + Bbeg^2 2 + Bcfg 1 + Bcfg^2 2 + Babcdefg 1 + Babcdefg^2 2 + Babcdefg^3 3 + sage: v = sum(ch.basis()); v + Babcdefg^3 + Babf^2 + Bace^2 + Badg^2 + Bbcd^2 + Bbeg^2 + + Bcfg^2 + Bdef^2 + Ba*Babcdefg + Bb*Babcdefg + Bc*Babcdefg + + Bd*Babcdefg + Be*Babcdefg + Bf*Babcdefg + Bg*Babcdefg + + Babcdefg^2 + Ba + Bb + Bc + Bd + Be + Bf + Bg + Babf + Bace + + Badg + Bbcd + Bbeg + Bcfg + Bdef + Babcdefg + 1 + sage: v.homogeneous_degree() + Traceback (most recent call last): + ... + ValueError: element is not homogeneous + + TESTS:: + + sage: ch = matroids.Wheel(3).chow_ring(QQ, True, 'atom-free') + sage: ch.zero().homogeneous_degree() + Traceback (most recent call last): + ... + ValueError: the zero element does not have a well-defined degree + """ + if not self: + raise ValueError("the zero element does not have a well-defined degree") + f = self.lift() + if not f.is_homogeneous(): + raise ValueError("element is not homogeneous") + return f.degree() \ No newline at end of file diff --git a/src/sage/matroids/chow_ring_ideal.py b/src/sage/matroids/chow_ring_ideal.py new file mode 100644 index 00000000000..d0ac04a23ee --- /dev/null +++ b/src/sage/matroids/chow_ring_ideal.py @@ -0,0 +1,800 @@ +r""" +Chow ring ideals of matroids + +AUTHORS: + +- Shriya M +""" + +from sage.rings.polynomial.multi_polynomial_ideal import MPolynomialIdeal +from sage.matroids.utilities import cmp_elements_key +from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing +from sage.rings.polynomial.multi_polynomial_sequence import PolynomialSequence +from sage.combinat.posets.posets import Poset +from itertools import product + +class ChowRingIdeal(MPolynomialIdeal): + def matroid(self): + r""" + Return the matroid of the given Chow ring ideal. + + EXAMPLES:: + + sage: ch = matroids.Uniform(3,6).chow_ring(QQ, False) + sage: ch.defining_ideal().matroid() + U(3, 6): Matroid of rank 3 on 6 elements with circuit-closures + {3: {{0, 1, 2, 3, 4, 5}}} + """ + return self._matroid + + def _lattice_flats(self): + r""" + Return the ranks and chains of lattice of flats of the matroid. + + EXAMPLES:: + + sage: ch = matroids.catalog.NonFano().chow_ring(QQ, True, 'atom-free') + sage: ch.defining_ideal()._lattice_flats() + ({frozenset({'a'}): 1, frozenset({'b'}): 1, frozenset({'c'}): 1, + frozenset({'d'}): 1, frozenset({'e'}): 1, frozenset({'f'}): 1, + frozenset({'g'}): 1, frozenset({'d', 'e'}): 2, + frozenset({'d', 'f'}): 2, frozenset({'e', 'f'}): 2, + frozenset({'a', 'b', 'f'}): 2, frozenset({'a', 'c', 'e'}): 2, + frozenset({'a', 'd', 'g'}): 2, frozenset({'b', 'c', 'd'}): 2, + frozenset({'b', 'e', 'g'}): 2, frozenset({'c', 'f', 'g'}): 2, + frozenset({'a', 'b', 'c', 'd', 'e', 'f', 'g'}): 3}, + Set of chains of Finite poset containing 17 elements) + """ + F = self._matroid.lattice_of_flats() + H = F.hasse_diagram() + H.delete_vertex(self._matroid.flats(0)[0]) # remove the empty flat + lattice_flats = Poset(H) + flats = list(lattice_flats) + flats.sort(key=lambda X: (len(X), sorted(X))) + ranks = {F: self._matroid.rank(F) for F in flats} + chains = lattice_flats.chains() #Only chains + return (ranks, chains) + +class ChowRingIdeal_nonaug(ChowRingIdeal): + r""" + The Chow ring ideal of a matroid `M`. + + The *Chow ring ideal* for a matroid `M` is defined as the ideal + `(I_M + J_M)` of the polynomial ring + + .. MATH:: + + R[x_{F_1}, \ldots, x_{F_k}], + + where + + - `F_1, \ldots, F_k` are the non-empty flats of `M`, + - `I_M` is the Stanley-Reisner ideal, i.e., it is generated + by products `x_{F_1}, \ldots, x_{F_t}` for subsets `\{F_1, \ldots, F_t\}` + of flats that are not chains, and + - `J_M` is the ideal generated by all linear forms + + .. MATH:: + + \sum_{a \in F} x_F + + for all atoms `a` in the lattice of flats. + + INPUT: + + - ``M`` -- matroid + - ``R`` -- commutative ring + + REFERENCES: + + - [ANR2023]_ + + EXAMPLES: + + Chow ring ideal of uniform matroid of rank 3 on 6 elements:: + + sage: ch = matroids.Uniform(3,6).chow_ring(QQ, False) + sage: ch.defining_ideal() + Chow ring ideal of U(3, 6): Matroid of rank 3 on 6 elements with + circuit-closures {3: {{0, 1, 2, 3, 4, 5}}} - non augmented + sage: ch = matroids.catalog.Fano().chow_ring(QQ, False) + sage: ch.defining_ideal() + Chow ring ideal of Fano: Binary matroid of rank 3 on 7 elements, + type (3, 0) - non augmented + """ + def __init__(self, M, R): + r""" + Initialize ``self``. + + EXAMPLES:: + + sage: I = matroids.catalog.Fano().chow_ring(QQ, False).defining_ideal() + sage: TestSuite(I).run(skip="_test_category") + """ + self._matroid = M + flats = [X for i in range(1, self._matroid.rank() + 1) + for X in self._matroid.flats(i)] + names = ['A{}'.format(''.join(str(x) for x in sorted(F, key=cmp_elements_key))) for F in flats] + try: + poly_ring = PolynomialRing(R, names) #self.ring + except ValueError: # variables are not proper names + poly_ring = PolynomialRing(R, 'A', len(flats)) + gens = poly_ring.gens() + self._flats_generator = dict(zip(flats, gens)) + MPolynomialIdeal.__init__(self, poly_ring, self._gens_constructor(poly_ring)) + + def _gens_constructor(self, poly_ring): + r""" + Return the generators of ``self``. + + EXAMPLES:: + + sage: ch = matroids.catalog.NonFano().chow_ring(QQ, False) + sage: sorted(ch.defining_ideal()._gens_constructor(ch.defining_ideal().ring())) + [Ag + Aadg + Abeg + Acfg + Aabcdefg, + Af + Aabf + Acfg + Adf + Aef + Aabcdefg, + Ae + Aace + Abeg + Ade + Aef + Aabcdefg, + Ad + Aadg + Abcd + Ade + Adf + Aabcdefg, + Ac + Aace + Abcd + Acfg + Aabcdefg, + Ab + Aabf + Abcd + Abeg + Aabcdefg, + Aa + Aabf + Aace + Aadg + Aabcdefg, + Adf*Aef, Ade*Aef, Acfg*Aef, Abeg*Aef, Abcd*Aef, Aadg*Aef, + Aace*Aef, Aabf*Aef, Ag*Aef, Ad*Aef, Ac*Aef, Ab*Aef, Aa*Aef, + Ade*Adf, Acfg*Adf, Abeg*Adf, Abcd*Adf, Aadg*Adf, Aace*Adf, + Aabf*Adf, Ag*Adf, Ae*Adf, Ac*Adf, Ab*Adf, Aa*Adf, Acfg*Ade, + Abeg*Ade, Abcd*Ade, Aadg*Ade, Aace*Ade, Aabf*Ade, Ag*Ade, Af*Ade, + Ac*Ade, Ab*Ade, Aa*Ade, Abeg*Acfg, Abcd*Acfg, Aadg*Acfg, + Aace*Acfg, Aabf*Acfg, Ae*Acfg, Ad*Acfg, Ab*Acfg, Aa*Acfg, + Abcd*Abeg, Aadg*Abeg, Aace*Abeg, Aabf*Abeg, Af*Abeg, Ad*Abeg, + Ac*Abeg, Aa*Abeg, Aadg*Abcd, Aace*Abcd, Aabf*Abcd, Ag*Abcd, + Af*Abcd, Ae*Abcd, Aa*Abcd, Aace*Aadg, Aabf*Aadg, Af*Aadg, Ae*Aadg, + Ac*Aadg, Ab*Aadg, Aabf*Aace, Ag*Aace, Af*Aace, Ad*Aace, Ab*Aace, + Ag*Aabf, Ae*Aabf, Ad*Aabf, Ac*Aabf, Af*Ag, Ae*Ag, Ad*Ag, Ac*Ag, + Ab*Ag, Aa*Ag, Ae*Af, Ad*Af, Ac*Af, Ab*Af, Aa*Af, Ad*Ae, Ac*Ae, + Ab*Ae, Aa*Ae, Ac*Ad, Ab*Ad, Aa*Ad, Ab*Ac, Aa*Ac, Aa*Ab] + """ + flats = list(self._flats_generator) + lattice_flats = Poset((flats, lambda x, y: x <= y)) + I = [] + subsets = lattice_flats.antichains().elements_of_depth_iterator(2) + for subset in subsets: + term = poly_ring.one() + for el in subset: + term *= self._flats_generator[el] + I.append(term) # Stanley-Reisner Ideal + atoms = self._matroid.lattice_of_flats().atoms() + atoms_gen = {a: poly_ring.zero() for a in atoms} + for F in flats: + for a in atoms: + if a.issubset(F): + atoms_gen[a] += self._flats_generator[F] + J = list(atoms_gen.values()) # Linear generators + return I + J + + def _repr_(self): + r""" + Return a string representation of ``self``. + + EXAMPLES:: + + sage: ch = matroids.catalog.Fano().chow_ring(QQ, False) + sage: ch.defining_ideal() + Chow ring ideal of Fano: Binary matroid of rank 3 on 7 elements, + type (3, 0) - non augmented + """ + return "Chow ring ideal of {} - non augmented".format(self._matroid) + + def _latex_(self): + r""" + Return a LaTeX representation of ``self``. + + EXAMPLES:: + + sage: M1 = Matroid(groundset='abcd', bases=['ab','ad', 'bc']) + sage: ch = M1.chow_ring(QQ, False) + sage: ch.defining_ideal()._latex_() + '(I_{\\text{\\texttt{Matroid{ }of{ }rank{ }2{ }on{ }4{ }elements{ }with{ }3{ }bases}}} + J_{\\text{\\texttt{Matroid{ }of{ }rank{ }2{ }on{ }4{ }elements{ }with{ }3{ }bases}}}' + """ + from sage.misc.latex import latex + return '(I_{{{M}}} + J_{{{M}}}'.format(M=latex(self._matroid)) + + def groebner_basis(self, algorithm='', *args, **kwargs): + r""" + Return a Groebner basis of ``self``. + + EXAMPLES:: + + sage: ch = Matroid(groundset='abc', bases=['ab', 'ac']).chow_ring(QQ, False) + sage: ch.defining_ideal().groebner_basis() + [Aa*Abc, Aa + Aabc, Abc + Aabc, Aa*Aabc, Abc*Aabc, Aabc^2] + sage: ch.defining_ideal().groebner_basis().is_groebner() + True + sage: ch.defining_ideal().hilbert_series() == ch.defining_ideal().gens().ideal().hilbert_series() + True + + Another example would be the Groebner basis of the Chow ring ideal of + the matroid of the length 3 cycle graph:: + + sage: ch = Matroid(graphs.CycleGraph(3)).chow_ring(QQ, False) + sage: ch.defining_ideal().groebner_basis() + [A0*A1, A0*A2, A1*A2, A0 + A3, A1 + A3, A2 + A3, A0*A3, A1*A3, A2*A3, A3^2] + sage: ch.defining_ideal().groebner_basis().is_groebner() + True + sage: ch.defining_ideal().hilbert_series() == ch.defining_ideal().gens().ideal().hilbert_series() + True + """ + if algorithm == '': + algorithm = 'constructed' + if algorithm != 'constructed': + return super().groebner_basis(algorithm=algorithm, *args, **kwargs) + flats = sorted(list(self._flats_generator), key=len) + ranks = {F: self._matroid.rank(F) for F in flats} + gb = [] + R = self.ring() + flats_gen = self._flats_generator + lattice_flats = Poset((flats, lambda x, y: x <= y)) + antichains = lattice_flats.antichains().elements_of_depth_iterator(2) + for subset in antichains: # Taking antichains of size 2 + term = R.one() + for x in subset: + term *= flats_gen[x] + gb.append(term) + for F in flats: # Reduced groebner basis by computing the sum first and then the product + term = R.zero() + for G in lattice_flats.order_filter([F]): + term += flats_gen[G] + for G in lattice_flats.order_ideal([F]): + if G != F: + gb.append(flats_gen[G]*(term) ** (ranks[F] - ranks[G])) + + gb.append(term ** ranks[F]) + + return PolynomialSequence(R, [gb]) + + def normal_basis(self, algorithm='', *args, **kwargs): + r""" + Return the monomial basis of the quotient ring of this ideal. + + EXAMPLES:: + + sage: ch = matroids.Z(3).chow_ring(QQ, False) + sage: I = ch.defining_ideal() + sage: I.normal_basis() + [1, Ax2x3y1, Ax1x3y2, Ax1x2y3, Ay1y2y3, Atx1y1, Atx2y2, Atx3y3, Atx1x2x3y1y2y3, Atx1x2x3y1y2y3^2] + sage: set(I.gens().ideal().normal_basis()) == set(I.normal_basis()) + True + sage: ch = matroids.AG(2,3).chow_ring(QQ, False) + sage: I = ch.defining_ideal() + sage: I.normal_basis() + [1, A012, A236, A046, A156, A345, A247, A057, A137, A258, A678, + A038, A148, A012345678, A012345678^2] + sage: set(I.gens().ideal().normal_basis()) == set(I.normal_basis()) + True + """ + if algorithm == '': + algorithm = 'constructed' + if algorithm != 'constructed': + return super().normal_basis(algorithm=algorithm, *args, **kwargs) + R = self.ring() + flats_gen = self._flats_generator + monomial_basis = [] + ranks, chains = self._lattice_flats() + for subset in chains: + max_powers = [] + k = len(subset) + if (k == 0): + monomial_basis.append(R.one()) + elif not ((k == 1) & (ranks[subset[0]] == 1)): + for i in range(k): + if i == 0: + max_powers.append(ranks[subset[i]]) + else: + max_powers.append(ranks[subset[i]] - ranks[subset[i-1]]) + for combination in product(*(range(1, p) for p in max_powers)): + expression = R.one() + for val, c in zip(subset, combination): + expression *= flats_gen[val] ** c + monomial_basis.append(expression) + return PolynomialSequence(R, [monomial_basis]) + + +class AugmentedChowRingIdeal_fy(ChowRingIdeal): + r""" + The augmented Chow ring ideal of matroid `M` over ring `R` in + the Feitchner-Yuzvinsky presentation. + + The augmented Chow ring ideal for a matroid `M` is defined as the ideal + `(I_M + J_M)` of the following polynomial ring + + .. MATH:: + + R[y_{e_1}, \ldots, y_{e_n}, x_{F_1}, \ldots, x_{F_k}], + + where + + - `F_1, \ldots, F_k` are the proper flats of `M`, + - `e_1, \ldots, e_n` are `n` elements of groundset of `M`, + - `J_M` is the ideal generated by all quadratic monomials `x_{F} x_{F'}`, + where `F` and `F'` are incomparable elements in the lattice of + flats and `y_{i} x_F` for all flats `F` and `i \in E \setminus F` and + - `I_M` is the ideal generated by all linear forms + + .. MATH:: + + y_i - \sum_{i \notin F} x_F + + for all `i \in E`. + + The augmented Chow ring ideal in the Feitchner-Yuzvinsky presentation + for a simple matroid `M` is defined as the ideal `I_{FY}(M)` of the + following polynomial ring + + .. MATH:: + + R[y_{e_1}, \ldots, y_{e_n}, y_{F_1 \cup e}, \ldots, y_{F_k \cup e}], + + where `F_1, \ldots, F_k` are the flats of `M`, `e_1, \ldots, e_n` are + `n` elements of groundset of `M`, and `I_{FY}(M)` is the ideal generated by + + - all quadratic monomials `y_{F \cup e} y_{F' \cup e}`, for incomparable + elements `F` and `F'` in the lattice of flats, + + - `y_{i} y_{F \cup e}` for all flats `F` and all `i \in E \setminus F` + + - for all `i \in E` + + .. MATH:: + + y_i + \sum_{i \in F} y_{F \cup e} + + - and + + .. MATH:: + + \sum_{F} y_{F \cup e}. + + Setting `x_F = y_{F \cup e}` and using the last linear + form to eliminate `x_E` recovers the usual presentation of + augmented Chow ring of `M`. + + REFERENCES: + + - [MM2022]_ + + INPUT: + + - ``M`` -- matroid + - ``R`` -- commutative ring + + EXAMPLES: + + Augmented Chow ring ideal of Wheel matroid of rank 3:: + + sage: ch = matroids.Wheel(3).chow_ring(QQ, True, 'fy') + sage: ch.defining_ideal() + Augmented Chow ring ideal of Wheel(3): Regular matroid of rank 3 on 6 + elements with 16 bases of Feitchner-Yuzvinsky presentation + """ + def __init__(self, M, R): + r""" + Initialize ``self``. + + EXAMPLES:: + + sage: I = matroids.Wheel(3).chow_ring(QQ, True, 'fy').defining_ideal() + sage: TestSuite(I).run(skip="_test_category") + """ + self._matroid = M + self._flats = [X for i in range(self._matroid.rank() + 1) + for X in self._matroid.flats(i)] + E = list(self._matroid.groundset()) + self._flats_generator = dict() + try: + names_groundset = ['A{}'.format(''.join(str(x))) for x in E] + names_flats = ['B{}'.format(''.join(str(x) for x in sorted(F, key=cmp_elements_key))) for F in self._flats] + poly_ring = PolynomialRing(R, names_groundset + names_flats) #self.ring() + except ValueError: #variables are not proper names + poly_ring = PolynomialRing(R, 'A', len(E) + len(self._flats)) + for i, x in enumerate(E): + self._flats_generator[x] = poly_ring.gens()[i] + for i, F in enumerate(self._flats): + self._flats_generator[F] = poly_ring.gens()[len(E) + i] + self._flats_containing = {x: [] for x in E} + for F in self._flats: + for x in F: + self._flats_containing[x].append(F) + MPolynomialIdeal.__init__(self, poly_ring, self._gens_constructor(poly_ring)) + + def _gens_constructor(self, poly_ring): + r""" + Return the generators of ``self``. + + EXAMPLES:: + + sage: ch = matroids.Wheel(3).chow_ring(QQ, True, 'fy') + sage: sorted(ch.defining_ideal()._gens_constructor(ch.defining_ideal().ring())) + [B + B0 + B1 + B2 + B3 + B4 + B5 + B013 + B025 + B04 + B124 + B15 + B23 + B345 + B012345, + B + B0 + B1 + B2 + B3 + B4 + B5 + B013 + B025 + B04 + B124 + B15 + B23 + B345 + B012345, + B + B0 + B1 + B2 + B3 + B4 + B5 + B013 + B025 + B04 + B124 + B15 + B23 + B345 + B012345, + B + B0 + B1 + B2 + B3 + B4 + B5 + B013 + B025 + B04 + B124 + B15 + B23 + B345 + B012345, + B + B0 + B1 + B2 + B3 + B4 + B5 + B013 + B025 + B04 + B124 + B15 + B23 + B345 + B012345, + B + B0 + B1 + B2 + B3 + B4 + B5 + B013 + B025 + B04 + B124 + B15 + B23 + B345 + B012345, + A5 + B5 + B025 + B15 + B345 + B012345, + A4 + B4 + B04 + B124 + B345 + B012345, + A3 + B3 + B013 + B23 + B345 + B012345, + A2 + B2 + B025 + B124 + B23 + B012345, + A1 + B1 + B013 + B124 + B15 + B012345, + A0 + B0 + B013 + B025 + B04 + B012345, + B23*B345, B15*B345, B124*B345, B04*B345, B025*B345, B013*B345, + B2*B345, B1*B345, B0*B345, A2*B345, A1*B345, A0*B345, B15*B23, + B124*B23, B04*B23, B025*B23, B013*B23, B5*B23, B4*B23, B1*B23, + B0*B23, A5*B23, A4*B23, A1*B23, A0*B23, B124*B15, B04*B15, + B025*B15, B013*B15, B4*B15, B3*B15, B2*B15, B0*B15, A4*B15, + A3*B15, A2*B15, A0*B15, B04*B124, B025*B124, B013*B124, B5*B124, + B3*B124, B0*B124, A5*B124, A3*B124, A0*B124, B025*B04, B013*B04, + B5*B04, B3*B04, B2*B04, B1*B04, A5*B04, A3*B04, A2*B04, A1*B04, + B013*B025, B4*B025, B3*B025, B1*B025, A4*B025, A3*B025, A1*B025, + B5*B013, B4*B013, B2*B013, A5*B013, A4*B013, A2*B013, B4*B5, + B3*B5, B2*B5, B1*B5, B0*B5, A4*B5, A3*B5, A2*B5, A1*B5, A0*B5, + B3*B4, B2*B4, B1*B4, B0*B4, A5*B4, A3*B4, A2*B4, A1*B4, A0*B4, + B2*B3, B1*B3, B0*B3, A5*B3, A4*B3, A2*B3, A1*B3, A0*B3, B1*B2, + B0*B2, A5*B2, A4*B2, A3*B2, A1*B2, A0*B2, B0*B1, A5*B1, A4*B1, + A3*B1, A2*B1, A0*B1, A5*B0, A4*B0, A3*B0, A2*B0, A1*B0, A5*B, + A4*B, A3*B, A2*B, A1*B, A0*B] + """ + E = list(self._matroid.groundset()) + Q = [] + L = [] + lattice_flats = Poset((self._flats, lambda x, y: x <= y)) + antichains = lattice_flats.antichains().elements_of_depth_iterator(2) + for F, G in antichains: + Q.append(self._flats_generator[F] * self._flats_generator[G]) # Quadratic generators + + for x in E: + term = poly_ring.zero() + term1 = poly_ring.zero() + for F in self._flats: + term1 += self._flats_generator[F] + if F not in self._flats_containing[x]: + Q.append(self._flats_generator[x] * self._flats_generator[F]) + else: + term += self._flats_generator[F] + L.append(self._flats_generator[x] + term) # Linear generators + L.append(term1) + return Q + L + + def _repr_(self): + r""" + EXAMPLES:: + + sage: ch = matroids.Wheel(3).chow_ring(QQ, True, 'fy') + sage: ch.defining_ideal() + Augmented Chow ring ideal of Wheel(3): Regular matroid of rank 3 on + 6 elements with 16 bases of Feitchner-Yuzvinsky presentation + """ + return "Augmented Chow ring ideal of {} of Feitchner-Yuzvinsky presentation".format(self._matroid) + + def _latex_(self): + r""" + Return a LaTeX representation of ``self``. + + EXAMPLES:: + + sage: M1 = Matroid(graphs.CycleGraph(3)) + sage: ch = M1.chow_ring(QQ, True, 'fy') + sage: ch.defining_ideal()._latex_() + 'I_{FY}(\\text{\\texttt{Graphic{ }matroid{ }of{ }rank{ }2{ }on{ }3{ }elements}})' + """ + from sage.misc.latex import latex + return 'I_{{FY}}({})'.format((latex(self._matroid))) + + def groebner_basis(self, algorithm='', *args, **kwargs): + r""" + Return the Groebner basis of ``self``. + + EXAMPLES:: + + sage: ch = matroids.catalog.NonFano().chow_ring(QQ, True, 'fy') + sage: ch.defining_ideal().groebner_basis(algorithm='') + Polynomial Sequence with 178 Polynomials in 25 Variables + sage: ch.defining_ideal().groebner_basis(algorithm='').is_groebner() + True + sage: ch.defining_ideal().hilbert_series() == ch.defining_ideal().gens().ideal().hilbert_series() + True + """ + if algorithm == '': + algorithm = 'constructed' + if algorithm != 'constructed': + return super().groebner_basis(algorithm=algorithm, *args, **kwargs) + gb = [] # reduced groebner basis with two eliminated cases + E = list(self._matroid.groundset()) + poly_ring = self.ring() + reln = lambda x,y: x <= y + lattice_flats = Poset((self._flats, reln)) + antichains = lattice_flats.antichains().elements_of_depth_iterator(2) + for F, G in antichains: + gb.append(self._flats_generator[F] * self._flats_generator[G]) # non-nested flats + for i in E: + term = poly_ring.zero() + for H in self._flats_containing[i]: + term += self._flats_generator[H] + if term != poly_ring.zero(): + gb.append(self._flats_generator[i] + term) # 5.7 (MM2022) + + for F in self._flats: + term1 = poly_ring.zero() + for H in lattice_flats.order_filter([F]): + term1 += self._flats_generator[H] + if term1 != poly_ring.zero(): + gb.append(term1**(self._matroid.rank(F) + 1)) #5.6 (MM2022) + order_ideal_modified = lattice_flats.order_ideal([F]) + order_ideal_modified.remove(F) + for G in order_ideal_modified: # nested flats + gb.append(self._flats_generator[G]*term1**(self._matroid.rank(F) - self._matroid.rank(G))) + + return PolynomialSequence(poly_ring, [gb]) + + def normal_basis(self, algorithm='', *args, **kwargs): + r""" + Return the monomial basis of the quotient ring of this ideal. + + EXAMPLES:: + + sage: ch = matroids.Uniform(2,5).chow_ring(QQ, True, 'fy') + sage: I = ch.defining_ideal() + sage: I.normal_basis() + [1, B0, B1, B2, B3, B4, B01234, B01234^2] + sage: set(I.gens().ideal().normal_basis()) == set(I.normal_basis()) + True + sage: ch = matroids.catalog.Fano().chow_ring(QQ, True, 'fy') + sage: I = ch.defining_ideal() + sage: I.normal_basis() + Polynomial Sequence with 32 Polynomials in 15 Variables + sage: set(I.gens().ideal().normal_basis()) == set(I.normal_basis()) + True + """ + if algorithm == '': + algorithm = 'constructed' + if algorithm != 'constructed': + return super().normal_basis(algorithm=algorithm, *args, **kwargs) + R = self.ring() + flats_gen = self._flats_generator + monomial_basis = [] + ranks, chains = self._lattice_flats() + for subset in chains: + if not subset: + monomial_basis.append(R.one()) + else: + k = len(subset) + max_powers = [] + max_powers.append(ranks[subset[0]]) + for i in range(1, k): + max_powers.append(ranks[subset[i]] - ranks[subset[i-1]]) + ranges = [range(1, p) for p in max_powers] + ranges[0] = range(1, max_powers[0] + 1) + for combination in product(*(r for r in ranges)): + # generating combinations for all powers up to max_powers + expression = R.one() + for val, c in zip(subset, combination): + expression *= flats_gen[val] ** c + monomial_basis.append(expression) + return PolynomialSequence(R, [monomial_basis]) + + +class AugmentedChowRingIdeal_atom_free(ChowRingIdeal): + r""" + The augmented Chow ring ideal for a matroid `M` over ring `R` in the + atom-free presentation. + + The augmented Chow ring ideal in the atom-free presentation for a matroid + `M` is defined as the ideal `I_{af}(M)` of the polynomial ring: + + .. MATH:: + + R[x_{F_1}, \ldots, x_{F_k}], + + where `F_1, \ldots, F_k` are the non-empty flats of `M` and `I_{af}(M)` is + the ideal generated by + + - all quadratic monomials `x_{F} x_{F'}` for all incomparable elements + `F` and `F'` in the lattice of flats, + + - for all flats `F` and `i \in E \setminus F` + + .. MATH:: + + x_F \sum_{i \in F'} x_{F'} + + - and for all `i \in E` + + .. MATH:: + + \sum_{i \in F'} (x_{F'})^2. + + REFERENCES: + + - [MM2022]_ + + INPUT: + + - ``M`` -- matroid + - ``R`` -- commutative ring + + EXAMPLES: + + Augmented Chow ring ideal of Wheel matroid of rank 3:: + + sage: ch = matroids.Wheel(3).chow_ring(QQ, True, 'atom-free') + sage: ch.defining_ideal() + Augmented Chow ring ideal of Wheel(3): Regular matroid of rank 3 on 6 + elements with 16 bases in the atom-free presentation + """ + def __init__(self, M, R): + r""" + Initialize ``self``. + + EXAMPLES:: + + sage: I = matroids.Wheel(3).chow_ring(QQ, True, 'atom-free').defining_ideal() + sage: TestSuite(I).run(skip="_test_category") + """ + self._matroid = M + self._flats = [X for i in range(1, self._matroid.rank() + 1) + for X in self._matroid.flats(i)] + names = ['A{}'.format(''.join(str(x) for x in sorted(F, key=cmp_elements_key))) for F in self._flats] + try: + poly_ring = PolynomialRing(R, names) #self.ring + except ValueError: # variables are not proper names + poly_ring = PolynomialRing(R, 'A', len(self._flats)) + gens = poly_ring.gens() + self._flats_generator = dict(zip(self._flats, gens)) + MPolynomialIdeal.__init__(self, poly_ring, self._gens_constructor(poly_ring)) + + def _gens_constructor(self, poly_ring): + r""" + Return the generators of ``self``. + + EXAMPLES:: + + sage: M1 = Matroid(graphs.CycleGraph(3)) + sage: ch = M1.chow_ring(QQ, True, 'atom-free') + sage: sorted(ch.defining_ideal()._gens_constructor(ch.defining_ideal().ring())) + [A2^2 + 2*A2*A3 + A3^2, A1*A2, A1*A2 + A2*A3, A1*A2 + A1*A3, A0*A2, + A0*A2 + A2*A3, A0*A2 + A0*A3, A1^2 + 2*A1*A3 + A3^2, A0*A1, + A0*A1 + A1*A3, A0*A1 + A0*A3, A0^2 + 2*A0*A3 + A3^2] + """ + E = list(self._matroid.groundset()) + Q = [] # Quadratic generators + flats_containing = {x: [] for x in E} + for F in self._flats: + for x in F: + flats_containing[x].append(F) + reln = lambda x,y: x <= y + lattice_flats = Poset((self._flats, reln)) + antichains = lattice_flats.antichains().elements_of_depth_iterator(2) + for F, G in antichains: + Q.append(self._flats_generator[F] * self._flats_generator[G]) + for F in self._flats: + for x in E: # generators for every set of flats containing element + term = poly_ring.zero() + for H in flats_containing[x]: + term += self._flats_generator[H] + if term**2 not in Q: + Q.append(term**2) + + if F not in flats_containing[x]: # generators for every set of flats not containing element + Q.append(self._flats_generator[F]*term) + return Q + + def _repr_(self): + r""" + EXAMPLES:: + + sage: ch = matroids.Wheel(3).chow_ring(QQ, True, 'atom-free') + sage: ch.defining_ideal() + Augmented Chow ring ideal of Wheel(3): Regular matroid of rank 3 on + 6 elements with 16 bases in the atom-free presentation + """ + return "Augmented Chow ring ideal of {} in the atom-free presentation".format(self._matroid) + + def _latex_(self): + r""" + Return the LaTeX output of the ring and generators of ``self``. + + EXAMPLES:: + + sage: M1 = Matroid(graphs.CycleGraph(3)) + sage: ch = M1.chow_ring(QQ, True, 'atom-free') + sage: ch.defining_ideal()._latex_() + 'I_{af}(\\text{\\texttt{Graphic{ }matroid{ }of{ }rank{ }2{ }on{ }3{ }elements}})' + """ + from sage.misc.latex import latex + return 'I_{{af}}({})'.format(latex(self._matroid)) + + def groebner_basis(self, algorithm='', *args, **kwargs): + """ + Return the Groebner basis of ``self``. + + EXAMPLES:: + + sage: M1 = matroids.Uniform(3,6) + sage: ch = M1.chow_ring(QQ, True, 'atom-free') + sage: ch.defining_ideal().groebner_basis(algorithm='') + Polynomial Sequence with 253 Polynomials in 22 Variables + sage: ch.defining_ideal().groebner_basis(algorithm='').is_groebner() + True + sage: ch.defining_ideal().hilbert_series() == ch.defining_ideal().gens().ideal().hilbert_series() + True + """ + if algorithm == '': + algorithm = 'constructed' + if algorithm != 'constructed': + return super().groebner_basis(algorithm=algorithm, *args, **kwargs) + gb = [] + poly_ring = self.ring() + lattice_flats = Poset((self._flats, lambda x, y: x <= y)) + antichains = lattice_flats.antichains().elements_of_depth_iterator(2) + for F, G in antichains: + gb.append(self._flats_generator[F]*self._flats_generator[G]) + for F in self._flats: + term = poly_ring.zero() + for H in lattice_flats.order_filter([F]): + term += self._flats_generator[H] + if term != poly_ring.zero(): + order_ideal_modified = lattice_flats.order_ideal([F]) + order_ideal_modified.remove(F) + for G in order_ideal_modified: + gb.append(self._flats_generator[G] * (term ** (self._matroid.rank(F) - self._matroid.rank(G)))) + gb.append(term ** (self._matroid.rank(F) + 1)) + + return PolynomialSequence(poly_ring, [gb]) + + def normal_basis(self, algorithm='', *args, **kwargs): + r""" + Return the monomial basis of the quotient ring of this ideal. + + EXAMPLES:: + + sage: ch = Matroid(graphs.CycleGraph(3)).chow_ring(QQ, True, 'atom-free') + sage: I = ch.defining_ideal() + sage: I.normal_basis() + [1, A0, A1, A2, A3, A3^2] + sage: set(I.gens().ideal().normal_basis()) == set(I.normal_basis()) + True + sage: ch = matroids.Wheel(3).chow_ring(QQ, True, 'atom-free') + sage: I = ch.defining_ideal() + sage: I.normal_basis() + Polynomial Sequence with 30 Polynomials in 14 Variables + sage: set(I.gens().ideal().normal_basis()) == set(I.normal_basis()) + True + """ + if algorithm == '': + algorithm = 'constructed' + if algorithm != 'constructed': + return super().normal_basis(algorithm=algorithm, *args, **kwargs) + R = self.ring() + flats_gen = self._flats_generator + monomial_basis = [] + ranks, chains = self._lattice_flats() + for subset in chains: + max_powers = [] + k = len(subset) + if not subset: + monomial_basis.append(R.one()) + else: + for i in range(k): + if i == 0: + max_powers.append(ranks[subset[i]]) + else: + max_powers.append(ranks[subset[i]] - ranks[subset[i-1]]) + ranges = [range(1, p) for p in max_powers] + ranges[0] = range(1, max_powers[0] + 1) + first_rank = ranks[subset[k-1]] + 1 + for combination in product(*(r for r in ranges)): + # Generating combinations for all powers from 1 to max_powers + if sum(combination) <= first_rank: + expression = R.one() + for val, c in zip(subset, combination): + expression *= flats_gen[val] ** c + monomial_basis.append(expression) + return PolynomialSequence(R, [monomial_basis]) \ No newline at end of file diff --git a/src/sage/matroids/matroid.pyx b/src/sage/matroids/matroid.pyx index 9e1f1571db1..e06f9b423cf 100644 --- a/src/sage/matroids/matroid.pyx +++ b/src/sage/matroids/matroid.pyx @@ -8034,109 +8034,82 @@ cdef class Matroid(SageObject): return [F for F in FF if fsol[F]] - def chow_ring(self, R=None): + def chow_ring(self, R, augmented=False, presentation=None): r""" - Return the Chow ring of ``self`` over ``R``. + Return the (augmented) Chow ring of ``self`` over ``R``. - Let `M` be a matroid and `R` be a commutative ring. - The *Chow ring* of `M` is the quotient ring - - .. MATH:: - - A^*(M)_R := R[x_{F_1}, \ldots, x_{F_k}] / (Q_M + L_M), - - where - - - `F_1, \ldots, F_k` are the non-empty proper flats of `M`, - - `Q_M` is the ideal generated by all `x_{F_i} x_{F_j}` where - `F_i` and `F_j` are incomparable elements in the lattice of - flats, and - - `L_M` is the ideal generated by all linear forms - - .. MATH:: - - \sum_{i_1 \in F} x_F - \sum_{i_2 \in F} x_F + .. SEEALSO:: - for all `i_1 \neq i_2 \in E`. + - :mod:`sage.matroids.chow_ring_ideal` + - :mod:`sage.matroids.chow_ring` INPUT: - - ``R`` -- (default: `\ZZ`) the base ring + - ``M`` -- matroid + - ``R`` -- commutative ring + - ``augmented`` -- boolean (default: ``False``); when ``True``, this + is the augmented Chow ring and if ``False``, this is the + non-augmented Chow ring + - ``presentation`` -- string; if ``augmented=True``, then this + must be one of the following (ignored if ``augmented=False``): + + * ``"fy"`` - the Feitchner-Yuzvinsky presentation + * ``"atom-free"`` - the atom-free presentation EXAMPLES:: sage: M = matroids.Wheel(2) - sage: A = M.chow_ring(); A - Chow ring of Wheel(2): Regular matroid of rank 2 on 4 elements - with 5 bases over Integer Ring - sage: A.gens() # needs sage.libs.singular - (A23, A23, A23) - sage: A23 = A.gen(0) # needs sage.libs.singular - sage: A23*A23 # needs sage.libs.singular + sage: A = M.chow_ring(R=ZZ, augmented=False); A + Chow ring of Wheel(2): Regular matroid of rank 2 on 4 elements with + 5 bases over Integer Ring + sage: A.defining_ideal()._gens_constructor(A.defining_ideal().ring()) + [A0*A1, A0*A23, A1*A23, A0 + A0123, A1 + A0123, A23 + A0123] + sage: A23 = A.gen(0) + sage: A23*A23 0 We construct a more interesting example using the Fano matroid:: sage: M = matroids.catalog.Fano() sage: A = M.chow_ring(QQ); A - Chow ring of Fano: Binary matroid of rank 3 on 7 elements, - type (3, 0) over Rational Field + Chow ring of Fano: Binary matroid of rank 3 on 7 elements, type (3, 0) + over Rational Field Next we get the non-trivial generators and do some computations:: sage: # needs sage.libs.singular sage.rings.finite_rings - sage: G = A.gens()[6:] - sage: Ag, Aabf, Aace, Aadg, Abcd, Abeg, Acfg, Adef = G - sage: Ag * Ag - 2*Adef^2 - sage: Ag * Abeg - -Adef^2 + sage: G = A.gens()[7:]; G + (Aabf, Aace, Aadg, Abcd, Abeg, Acfg, Adef, Aabcdefg) + sage: Aabf, Aace, Aadg, Abcd, Abeg, Acfg, Adef, Aabcdefg = G + sage: Aabf*Aabf + -Aabcdefg^2 + sage: Aabf*Acfg + 0 sage: matrix([[x * y for x in G] for y in G]) - [2*Adef^2 0 0 -Adef^2 0 -Adef^2 -Adef^2 0] - [ 0 Adef^2 0 0 0 0 0 0] - [ 0 0 Adef^2 0 0 0 0 0] - [ -Adef^2 0 0 Adef^2 0 0 0 0] - [ 0 0 0 0 Adef^2 0 0 0] - [ -Adef^2 0 0 0 0 Adef^2 0 0] - [ -Adef^2 0 0 0 0 0 Adef^2 0] - [ 0 0 0 0 0 0 0 Adef^2] - - REFERENCES: - - - [FY2004]_ - - [AHK2015]_ - """ - # Setup - if R is None: - R = ZZ - # We only want proper flats - flats = [X for i in range(1, self.rank()) - for X in self.flats(i)] - E = list(self.groundset()) - flats_containing = {x: [] for x in E} - for i,F in enumerate(flats): - for x in F: - flats_containing[x].append(i) - - # Create the ambient polynomial ring - from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing - try: - names = ['A{}'.format(''.join(str(x) for x in sorted(F, key=cmp_elements_key))) for F in flats] - P = PolynomialRing(R, names) - except ValueError: # variables are not proper names - P = PolynomialRing(R, 'A', len(flats)) - names = P.variable_names() - gens = P.gens() - # Create the ideal of quadratic relations - Q = [gens[i] * gens[i+j+1] for i,F in enumerate(flats) - for j,G in enumerate(flats[i+1:]) if not (F < G or G < F)] - # Create the ideal of linear relations - L = [sum(gens[i] for i in flats_containing[x]) - - sum(gens[i] for i in flats_containing[y]) - for j,x in enumerate(E) for y in E[j+1:]] - ret = P.quotient(Q + L, names=names) - ret.rename("Chow ring of {} over {}".format(self, R)) - return ret + [-Aabcdefg^2 0 0 0 0 0 0 0] + [ 0 -Aabcdefg^2 0 0 0 0 0 0] + [ 0 0 -Aabcdefg^2 0 0 0 0 0] + [ 0 0 0 -Aabcdefg^2 0 0 0 0] + [ 0 0 0 0 -Aabcdefg^2 0 0 0] + [ 0 0 0 0 0 -Aabcdefg^2 0 0] + [ 0 0 0 0 0 0 -Aabcdefg^2 0] + [ 0 0 0 0 0 0 0 Aabcdefg^2] + + The augmented Chow ring can also be constructed with the + Feitchner-Yuzvinsky and atom-free presentation:: + + sage: M = matroids.Wheel(3) + sage: ch = M.chow_ring(QQ, augmented=True, presentation='fy'); ch + Augmented Chow ring of Wheel(3): Regular matroid of rank 3 on + 6 elements with 16 bases in Feitchner-Yuzvinsky presentation over + Rational Field + sage: M = matroids.Uniform(3, 6) + sage: ch = M.chow_ring(QQ, augmented=True, presentation='atom-free'); ch + Augmented Chow ring of U(3, 6): Matroid of rank 3 on 6 elements with circuit-closures + {3: {{0, 1, 2, 3, 4, 5}}} in atom-free presentation over Rational Field + """ + from sage.matroids.chow_ring import ChowRing + return ChowRing(M=self, R=R, augmented=augmented, presentation=presentation) cpdef plot(self, B=None, lineorders=None, pos_method=None,pos_dict=None,save_pos=False): """