diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index 4fef159bfd0..228024b92ac 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -505,6 +505,9 @@ REFERENCES: .. [BeBo2009] Olivier Bernardi and Nicolas Bonichon, *Intervals in Catalan lattices and realizers of triangulations*, JCTA 116 (2009) +.. [BES2024] Spencer Backman, Christopher Eur, and Connor Simpson. *Simplicial generation + of Chow rings of matroids*, 2024. :arxiv:`1905.07114` + .. [Best2021] Alex J. Best: Tools and Techniques for Rational Points on Curves. PhD Thesis, Boston University, 2021. diff --git a/src/sage/matroids/chow_ring.py b/src/sage/matroids/chow_ring.py index 5fea543aeb3..ac6117bce15 100644 --- a/src/sage/matroids/chow_ring.py +++ b/src/sage/matroids/chow_ring.py @@ -15,25 +15,46 @@ # https://www.gnu.org/licenses/ # **************************************************************************** -from sage.matroids.chow_ring_ideal import ChowRingIdeal_nonaug, AugmentedChowRingIdeal_fy, AugmentedChowRingIdeal_atom_free +from sage.matroids.chow_ring_ideal import ChowRingIdeal_nonaug_fy, ChowRingIdeal_nonaug_af, ChowRingIdeal_nonaug_sp, AugmentedChowRingIdeal_fy, AugmentedChowRingIdeal_atom_free from sage.rings.quotient_ring import QuotientRing_generic from sage.categories.kahler_algebras import KahlerAlgebras from sage.categories.commutative_rings import CommutativeRings from sage.misc.cachefunc import cached_method +from sage.modules.with_basis.representation import Representation_abstract -class ChowRing(QuotientRing_generic): +class ChowRing(QuotientRing_generic, Representation_abstract): r""" The Chow ring of a matroid. - The *Chow ring of the matroid* `M` is defined as the quotient ring + The *Chow ring of the matroid* `M` has three different presentations. + + The *Feitchner-Yuzvinsky presentation* is 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 *atom-free presentation* is the quotient ring + + .. MATH:: + + A^*(M)_R := R[x_{F_1}, \ldots, x_{F_k}] / (I_M + J_M + K_M), + + where `(I_M + J_M + K_M)` is the :class:`Chow ring ideal + ` of matroid `M`. + + The *simplicial presentation* is 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`. + ` of matroid `M`. The *augmented Chow ring of matroid* `M` has two different presentations as quotient rings: @@ -74,10 +95,10 @@ class ChowRing(QuotientRing_generic): - ``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 + * ``"simplicial"`` - the simplicial presentation REFERENCES: @@ -87,10 +108,10 @@ class ChowRing(QuotientRing_generic): EXAMPLES:: sage: M1 = matroids.catalog.P8pp() - sage: ch = M1.chow_ring(QQ, False) + sage: ch = M1.chow_ring(QQ, False, 'fy') sage: ch - Chow ring of P8'': Matroid of rank 4 on 8 elements with 8 nonspanning circuits - over Rational Field + Chow ring of P8'': Matroid of rank 4 on 8 elements with 8 nonspanning + circuits in Feitchner-Yuzvinsky presentation over Rational Field """ def __init__(self, R, M, augmented, presentation=None): r""" @@ -98,42 +119,50 @@ def __init__(self, R, M, augmented, presentation=None): EXAMPLES:: - sage: ch = matroids.Wheel(3).chow_ring(QQ, False) + sage: ch = matroids.Wheel(3).chow_ring(QQ, False, 'fy') sage: TestSuite(ch).run() """ self._matroid = M self._augmented = augmented self._presentation = presentation - if augmented is True: + if augmented: 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) + if presentation == 'fy': + self._ideal = ChowRingIdeal_nonaug_fy(M, R) + if presentation == 'atom-free': + self._ideal = ChowRingIdeal_nonaug_af(M, R) + if presentation == 'simplicial': + self._ideal = ChowRingIdeal_nonaug_sp(M, R) C = CommutativeRings().Quotients() & KahlerAlgebras(R) QuotientRing_generic.__init__(self, R=self._ideal.ring(), I=self._ideal, names=self._ideal.ring().variable_names(), category=C) + Representation_abstract.__init__(self, semigroup=M.automorphism_group(), side="left") def _repr_(self): r""" EXAMPLES:: sage: M1 = matroids.catalog.Fano() - sage: ch = M1.chow_ring(QQ, False) + sage: ch = M1.chow_ring(QQ, False, 'fy') sage: ch - 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) in Feitchner-Yuzvinsky presentation over Rational Field """ output = "Chow ring of {}".format(self._matroid) - if self._augmented is True: + if self._augmented: output = "Augmented " + output - if self._presentation == 'fy': - output += " in Feitchner-Yuzvinsky presentation" - elif self._presentation == 'atom-free': - output += " in atom-free presentation" + if self._presentation == 'fy': + output += " in Feitchner-Yuzvinsky presentation" + elif self._presentation == 'atom-free': + output += " in atom-free presentation" + elif self._presentation == 'simplicial': + output += " in simplicial presentation" return output + " over " + repr(self.base_ring()) def _latex_(self): @@ -143,7 +172,7 @@ def _latex_(self): EXAMPLES:: sage: M1 = matroids.Uniform(2, 5) - sage: ch = M1.chow_ring(QQ, False) + sage: ch = M1.chow_ring(QQ, False, 'fy') 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}}' """ @@ -172,7 +201,7 @@ def _coerce_map_from_base_ring(self): TESTS:: - sage: ch = matroids.Wheel(3).chow_ring(QQ, False) + sage: ch = matroids.Wheel(3).chow_ring(QQ, False, 'atom-free') sage: ch._coerce_map_from_base_ring() is None True """ @@ -186,14 +215,14 @@ def basis(self): 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) + Family (1, B02, B02*A5, B01, B01*A5, B13, B13^2, B03, B03*A5, B14, + B14^2, B25, B25^2, B04, B04*A5, B15, B15^2, B34, B34^2, B012345, + B012345^2, B05, B05*A5, B23, B23^2, B35, B35^2, A0, A0^2, A2, A2^2, + B12, B12*A5, B24, B24^2, B45, B45^2, A1, A1^2, A3, A3^2, A4, A4^2, + A5, A5^2, A5^3) sage: set(ch.defining_ideal().normal_basis()) == set(ch.basis()) True - sage: ch = matroids.catalog.Fano().chow_ring(QQ, False) + sage: ch = matroids.catalog.Fano().chow_ring(QQ, False, 'fy') sage: ch.basis() Family (1, Abcd, Aace, Aabf, Adef, Aadg, Abeg, Acfg, Aabcdefg, Aabcdefg^2) @@ -219,7 +248,7 @@ def lefschetz_element(self): EXAMPLES:: - sage: ch = matroids.catalog.P8pp().chow_ring(QQ, False) + sage: ch = matroids.catalog.P8pp().chow_ring(QQ, False, 'fy') sage: ch.lefschetz_element() -2*Aab - 2*Aac - 2*Aad - 2*Aae - 2*Aaf - 2*Aag - 2*Aah - 2*Abc - 2*Abd - 2*Abe - 2*Abf - 2*Abg - 2*Abh - 2*Acd - 2*Ace - 2*Acf @@ -236,7 +265,7 @@ def lefschetz_element(self): It is then multiplied with the elements of FY-monomial bases of different degrees:: - sage: ch = matroids.Uniform(4, 5).chow_ring(QQ, False) + sage: ch = matroids.Uniform(4, 5).chow_ring(QQ, False, 'fy') sage: basis_deg = {} sage: for b in ch.basis(): ....: deg = b.homogeneous_degree() @@ -325,6 +354,28 @@ def poincare_pairing(self, el1, el2): new_el += hom_components1[i] * hom_components2[r - i] return new_el.degree() + def graded_character(self, G=None): + r""" + Return the graded character of ``self`` as a representation of the + automorphism group of the defining matroid. + + EXAMPLES:: + + sage: ch = matroids.Z(3).chow_ring(QQ, False, 'simplicial') + sage: gchi = ch.graded_character(); gchi + (q^2 + 8*q + 1, q^2 + 8*q + 1, q^2 + 8*q + 1, q^2 + 8*q + 1, + q^2 + 8*q + 1, q^2 + 8*q + 1) + """ + if G is None: + G = self._matroid.automorphism_group() + from sage.rings.rational_field import QQ + q = QQ['q'].gen() + B = self.basis() + from sage.modules.free_module_element import vector + return vector([sum(q**b.degree() * (g * b).lift().monomial_coefficient(b.lift()) for b in B) + for g in G.conjugacy_classes_representatives()], + immutable=True) + class Element(QuotientRing_generic.Element): def to_vector(self, order=None): r""" @@ -332,7 +383,7 @@ def to_vector(self, order=None): EXAMPLES:: - sage: ch = matroids.Uniform(3, 6).chow_ring(QQ, False) + sage: ch = matroids.Uniform(3, 6).chow_ring(QQ, False, 'fy') sage: v = ch.an_element(); v -A01 - A02 - A03 - A04 - A05 - A012345 sage: v.to_vector() @@ -372,7 +423,7 @@ def degree(self): EXAMPLES:: - sage: ch = matroids.Uniform(3, 6).chow_ring(QQ, False) + sage: ch = matroids.Uniform(3, 6).chow_ring(QQ, False, 'fy') sage: for b in ch.basis(): ....: print(b, b.degree()) 1 0 @@ -410,43 +461,42 @@ def homogeneous_degree(self): 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 + Babf*Ae 2 + Bace 1 + Bace*Ae 2 Badg 1 - Badg^2 2 + Badg*Ae 2 + Bbcd 1 + Bbcd*Ae 2 + Aa 1 + Aa^2 2 Bbeg 1 - Bbeg^2 2 + Bbeg*Ae 2 + Ac 1 + Ac^2 2 Bcfg 1 - Bcfg^2 2 + Bcfg*Ae 2 Babcdefg 1 Babcdefg^2 2 - Babcdefg^3 3 + Af 1 + Af^2 2 + Bdef 1 + Bdef*Ae 2 + Ad 1 + Ad^2 2 + Ag 1 + Ag^2 2 + Ab 1 + Ab^2 2 + Ae 1 + Ae^2 2 + Ae^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 + Ae^3 + Babcdefg^2 + Ac^2 + Ad^2 + Aa^2 + Ag^2 + Ab^2 + Af^2 + + Babf*Ae + Bace*Ae + Badg*Ae + Bbcd*Ae + Bbeg*Ae + Bcfg*Ae + + Bdef*Ae + Ae^2 + Babf + Bace + Badg + Bbcd + Bbeg + Bcfg + + Bdef + Babcdefg + Ac + Ad + Aa + Ag + Ab + Af + Ae + 1 sage: v.homogeneous_degree() Traceback (most recent call last): ... @@ -466,3 +516,31 @@ def homogeneous_degree(self): if not f.is_homogeneous(): raise ValueError("element is not homogeneous") return f.degree() + + def _acted_upon_(self, scalar, self_on_left=True): + r""" + Return the action of ``scalar`` on ``self``. + + EXAMPLES:: + + sage: ch = matroids.catalog.P8pp().chow_ring(QQ, False, 'atom-free') + sage: y = ch.an_element(); y + Aab + sage: semigroup = ch.semigroup() + sage: x = semigroup.an_element(); x + ('c','h','d','e','a','f','b','g') + sage: x * y # indirect doctest + Aab + """ + P = self.parent() + if scalar in P.base_ring(): + return super()._acted_upon_(scalar, self_on_left) + if scalar in P._matroid.automorphism_group(): + gens = P.ambient().gens() + return P.retract(self.lift().subs({g: gens[scalar(i+1)-1] for i, g in enumerate(gens)})) + if not self_on_left and scalar in P._matroid.automorphism_group(): + scalar = P._semigroup_algebra(scalar) + gens = P.ambient().gens() + return P.sum(c * P.retract(self.lift().subs({g: gens[sigma(i+1)-1] for i, g in enumerate(gens)})) + for sigma, c in scalar.monomial_coefficients(copy=False).items()) + return super()._acted_upon_(scalar, self_on_left) \ No newline at end of file diff --git a/src/sage/matroids/chow_ring_ideal.py b/src/sage/matroids/chow_ring_ideal.py index 00b5592ee42..fefdb247607 100644 --- a/src/sage/matroids/chow_ring_ideal.py +++ b/src/sage/matroids/chow_ring_ideal.py @@ -20,7 +20,7 @@ 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 +from itertools import product, combinations class ChowRingIdeal(MPolynomialIdeal): @@ -30,7 +30,7 @@ def matroid(self): EXAMPLES:: - sage: ch = matroids.Uniform(3, 6).chow_ring(QQ, False) + sage: ch = matroids.Uniform(3, 6).chow_ring(QQ, False, 'simplicial') 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}}} @@ -98,13 +98,38 @@ def flats_to_generator_dict(self): """ return dict(self._flats_generator) + @staticmethod + def _construct_ambient_poly_ring(R, names, flats_groundset_el): + r""" + Return the ambient polynomial ring that will be used to + construct a Chow ring ideal. + + EXAMPLES:: + + sage: ch = matroids.catalog.NonFano().chow_ring(QQ, False, 'atom-free') + sage: flats = [X for i in range(1, ch.matroid().rank() + 1) + ....: for X in ch.matroid().flats(i)] + sage: from sage.matroids.utilities import cmp_elements_key + sage: names = ['A{}'.format(''.join(str(x) for x in sorted(F, key=cmp_elements_key))) for F in flats] + sage: ch.defining_ideal()._construct_ambient_poly_ring(ch.defining_ideal().ring(), names, flats) + Multivariate Polynomial Ring in Aa, Ab, Ac, Ad, Ae, Af, Ag, Aabf, + Aace, Aadg, Abcd, Abeg, Acfg, Ade, Adf, Aef, Aabcdefg over + Multivariate Polynomial Ring in Aabf, Aace, Aadg, Abcd, Abeg, + Acfg, Ade, Adf, Aef, Aabcdefg over Rational Field + """ + try: + poly_ring = PolynomialRing(R, names, len(names)) # self.ring + except ValueError: # variables are not proper names + poly_ring = PolynomialRing(R, 'A', len(flats_groundset_el)) + return poly_ring + -class ChowRingIdeal_nonaug(ChowRingIdeal): +class ChowRingIdeal_nonaug_fy(ChowRingIdeal): r""" - The Chow ring ideal of a matroid `M`. + The Chow ring ideal of a matroid `M` in Feitchner-Yuzvinsky presentation. - The *Chow ring ideal* for a matroid `M` is defined as the ideal - `(I_M + J_M)` of the polynomial ring + The *Chow ring ideal* for a matroid `M` in Feitchner-Yuzvinsky presentation + is defined as the ideal `(I_M + J_M)` of the polynomial ring .. MATH:: @@ -137,14 +162,15 @@ class ChowRingIdeal_nonaug(ChowRingIdeal): Chow ring ideal of uniform matroid of rank 3 on 6 elements:: - sage: ch = matroids.Uniform(3, 6).chow_ring(QQ, False) + sage: ch = matroids.Uniform(3, 6).chow_ring(QQ, False, 'fy') 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) + circuit-closures {3: {{0, 1, 2, 3, 4, 5}}} - non augmented in + Feitchner-Yuzvinksy presentation + sage: ch = matroids.catalog.Fano().chow_ring(QQ, False, 'fy') sage: ch.defining_ideal() Chow ring ideal of Fano: Binary matroid of rank 3 on 7 elements, - type (3, 0) - non augmented + type (3, 0) - non augmented in Feitchner-Yuzvinksy presentation """ def __init__(self, M, R): r""" @@ -152,17 +178,14 @@ def __init__(self, M, R): EXAMPLES:: - sage: I = matroids.catalog.Fano().chow_ring(QQ, False).defining_ideal() + sage: I = matroids.catalog.Fano().chow_ring(QQ, False, 'fy').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)) + poly_ring = ChowRingIdeal._construct_ambient_poly_ring(R, names, flats) gens = poly_ring.gens() self._flats_generator = dict(zip(flats, gens)) MPolynomialIdeal.__init__(self, poly_ring, self._gens_constructor(poly_ring)) @@ -173,7 +196,7 @@ def _gens_constructor(self, poly_ring): EXAMPLES:: - sage: ch = matroids.catalog.NonFano().chow_ring(QQ, False) + sage: ch = matroids.catalog.NonFano().chow_ring(QQ, False, 'fy') sage: sorted(ch.defining_ideal()._gens_constructor(ch.defining_ideal().ring())) [Ag + Aadg + Abeg + Acfg + Aabcdefg, Af + Aabf + Acfg + Adf + Aef + Aabcdefg, @@ -198,7 +221,10 @@ def _gens_constructor(self, poly_ring): 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)) + 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) I = [] subsets = lattice_flats.antichains().elements_of_depth_iterator(2) for subset in subsets: @@ -221,12 +247,12 @@ def _repr_(self): EXAMPLES:: - sage: ch = matroids.catalog.Fano().chow_ring(QQ, False) + sage: ch = matroids.catalog.Fano().chow_ring(QQ, False, 'fy') sage: ch.defining_ideal() Chow ring ideal of Fano: Binary matroid of rank 3 on 7 elements, - type (3, 0) - non augmented + type (3, 0) - non augmented in Feitchner-Yuzvinksy presentation """ - return "Chow ring ideal of {} - non augmented".format(self._matroid) + return "Chow ring ideal of {} - non augmented in Feitchner-Yuzvinksy presentation".format(self._matroid) def _latex_(self): r""" @@ -235,7 +261,7 @@ def _latex_(self): EXAMPLES:: sage: M1 = Matroid(groundset='abcd', bases=['ab','ad', 'bc']) - sage: ch = M1.chow_ring(QQ, False) + sage: ch = M1.chow_ring(QQ, False, 'fy') 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}}}' """ @@ -248,7 +274,7 @@ def groebner_basis(self, algorithm='', *args, **kwargs): EXAMPLES:: - sage: ch = Matroid(groundset='abc', bases=['ab', 'ac']).chow_ring(QQ, False) + sage: ch = Matroid(groundset='abc', bases=['ab', 'ac']).chow_ring(QQ, False, 'fy') 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() @@ -259,7 +285,7 @@ def groebner_basis(self, algorithm='', *args, **kwargs): 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 = Matroid(graphs.CycleGraph(3)).chow_ring(QQ, False, 'fy') 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() @@ -276,7 +302,10 @@ def groebner_basis(self, algorithm='', *args, **kwargs): gb = [] R = self.ring() flats_gen = self._flats_generator - lattice_flats = Poset((flats, lambda x, y: x <= y)) + 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) antichains = lattice_flats.antichains().elements_of_depth_iterator(2) for subset in antichains: # Taking antichains of size 2 term = R.one() @@ -301,17 +330,17 @@ def normal_basis(self, algorithm='', *args, **kwargs): EXAMPLES:: - sage: ch = matroids.Z(3).chow_ring(QQ, False) + sage: ch = matroids.Z(3).chow_ring(QQ, False, 'fy') 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: ch = matroids.AG(2,3).chow_ring(QQ, False, 'fy') 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] + [1, A156, A236, A012, A046, A345, A057, A137, A247, A038, A148, + A258, A678, A012345678, A012345678^2] sage: set(I.gens().ideal().normal_basis()) == set(I.normal_basis()) True """ @@ -342,6 +371,475 @@ def normal_basis(self, algorithm='', *args, **kwargs): return PolynomialSequence(R, [monomial_basis]) +class ChowRingIdeal_nonaug_af(ChowRingIdeal): + r""" + The Chow ring ideal of a matroid `M` in atom-free presentation. + + The *Chow ring ideal* for a matroid `M` in atom-free presentation + is defined as the ideal `(I_M + J_M + K_M)` of the polynomial ring + + .. MATH:: + + R[x_{F_1}, \ldots, x_{F_k}], + + where + + - `F_1, \ldots, F_k` are flats of `M` of at least rank 2, + - `I_M` is the ideal generated by products `x_{F_i} x_{F_j}` for + incomparable flats `F_i` and `F_j`, + - `J_M` is the ideal generated by all linear forms + + .. MATH:: + + x_F \sum_{F' \superseteq F \vee i} x_{F'} + + for all flats `F` and `i` in `E - F`, where `E` is the groundset of + the matroid, and + - `K_M` is the ideal generated by all quadratic forms + + .. MATH:: + + \sum_{F \superseteq i \vee j} x_F^2 + \sum_{F' \superset F \superseteq i \vee j} 2 x_F x_{F'} + + for all elements `i, j` in `E`, such that `i \neq j`. + + INPUT: + + - ``M`` -- matroid + - ``R`` -- commutative ring + + REFERENCES: + + - [MM2022]_ + + EXAMPLES: + + Chow ring ideal of uniform matroid of rank 3 on 6 elements:: + + sage: ch = matroids.catalog.NonFano().chow_ring(QQ, False, 'atom-free') + sage: ch.defining_ideal() + Chow ring ideal of NonFano: Ternary matroid of rank 3 on 7 elements, + type 0- - non augmented in the atom-free presentation + """ + def __init__(self, M, R): + r""" + Initialize ``self``. + + EXAMPLES:: + + sage: I = matroids.catalog.Fano().chow_ring(QQ, False, 'atom-free').defining_ideal() + sage: TestSuite(I).run(skip="_test_category") + """ + self._matroid = M + flats = [X for i in range(2, 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] + poly_ring = ChowRingIdeal._construct_ambient_poly_ring(R, names, 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.Wheel(3).chow_ring(QQ, False, 'atom-free') + sage: sorted(ch.defining_ideal()._gens_constructor(ch.defining_ideal().ring())) + [A345*A012345, A23*A012345, A15*A012345, A124*A012345, A04*A012345, + A025*A012345, A013*A012345, A345^2 + 2*A345*A012345 + A012345^2, + A345^2 + 2*A345*A012345 + A012345^2, + A345^2 + 2*A345*A012345 + A012345^2, A23*A345, A15*A345, + A124*A345, A04*A345, A025*A345, A013*A345, + A23^2 + 2*A23*A012345 + A012345^2, A15*A23, A124*A23, A04*A23, + A025*A23, A013*A23, A15^2 + 2*A15*A012345 + A012345^2, A124*A15, + A04*A15, A025*A15, A013*A15, A124^2 + 2*A124*A012345 + A012345^2, + A124^2 + 2*A124*A012345 + A012345^2, + A124^2 + 2*A124*A012345 + A012345^2, A04*A124, A025*A124, A013*A124, + A04^2 + 2*A04*A012345 + A012345^2, A025*A04, A013*A04, + A025^2 + 2*A025*A012345 + A012345^2, + A025^2 + 2*A025*A012345 + A012345^2, + A025^2 + 2*A025*A012345 + A012345^2, A013*A025, + A013^2 + 2*A013*A012345 + A012345^2, + A013^2 + 2*A013*A012345 + A012345^2, + A013^2 + 2*A013*A012345 + A012345^2] + """ + E = list(self._matroid.groundset()) + flats = list(self._flats_generator) + F = self._matroid.lattice_of_flats() + H = F.hasse_diagram() + for i in range(2): + for j in self._matroid.flats(i): + H.delete_vertex(j) # remove all flats of rank < 2 + lattice_flats = Poset(H) + I = [] + flats_gen = self._flats_generator + subsets = lattice_flats.antichains().elements_of_depth_iterator(2) + for subset in subsets: + I.append(flats_gen[subset[0]] * flats_gen[subset[1]]) # Stanley-Reisner Ideal + J = [] + for F in flats: + for G in lattice_flats.upper_covers_iterator(F): + term = poly_ring.zero() + for H in lattice_flats.order_filter([G]): + term += flats_gen[G] + J.append(flats_gen[F] * term) + K = [] + for i, j in combinations(E, 2): + term1 = poly_ring.zero() + term2 = poly_ring.zero() + for F in flats: + H = frozenset({i}).union(frozenset({j})) + if F >= H: + term1 += flats_gen[F] ** 2 + for G in lattice_flats.order_filter([F]): + if G != F: + term2 += flats_gen[F]*flats_gen[G] + K.append(term1 + (2 * term2)) + return I + J + K + + def _repr_(self): + r""" + Return a string representation of ``self``. + + EXAMPLES:: + + sage: ch = matroids.catalog.Fano().chow_ring(QQ, False, 'atom-free') + sage: ch.defining_ideal() + Chow ring ideal of Fano: Binary matroid of rank 3 on 7 elements, + type (3, 0) - non augmented in the atom-free presentation + """ + return "Chow ring ideal of {} - non augmented in the atom-free presentation".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, 'atom-free') + 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}}} + K_{\\text{\\texttt{Matroid{ }of{ }rank{ }2{ }on{ }4{ }elements{ }with{ }3{ }bases}}}' + """ + from sage.misc.latex import latex + return '(I_{{{M}}} + J_{{{M}}} + K_{{{M}}}'.format(M=latex(self._matroid)) + + def groebner_basis(self, algorithm='', *args, **kwargs): + r""" + Return a Groebner basis of ``self``. + + EXAMPLES:: + + sage: ch = matroids.Uniform(3, 6).chow_ring(QQ, False, 'atom-free') + sage: ch.defining_ideal().groebner_basis() + Polynomial Sequence with 136 Polynomials in 16 Variables + 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 = list(self._flats_generator) + F = self._matroid.lattice_of_flats() + H = F.hasse_diagram() + for i in range(2): + for j in self._matroid.flats(i): + H.delete_vertex(j) # remove all flats of rank < 2 + lattice_flats = Poset(H) + gb = [] + poly_ring = self.ring() + flats_gen = self._flats_generator + subsets = lattice_flats.antichains().elements_of_depth_iterator(2) + for subset in subsets: + gb.append(flats_gen[subset[0]] * flats_gen[subset[1]]) + subsets = lattice_flats.chains().elements_of_depth_iterator(2) + ranks, chains = self._lattice_flats() + for subset in subsets: + term = poly_ring.zero() + for F in lattice_flats.order_filter([subset[1]]): + term += flats_gen[F] + gb.append(flats_gen[subset[0]] * (term ** (ranks[subset[1]] - ranks[subset[0]]))) + for F in flats: + term = poly_ring.zero() + for G in lattice_flats.order_filter([F]): + term += flats_gen[G] + gb.append(term ** ranks[F]) + 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(3, 5).chow_ring(QQ, False, 'atom-free') + sage: I = ch.defining_ideal() + sage: I.normal_basis() + [1, A01, A02, A12, A03, A13, A23, A04, A14, A24, A34, A01234, A01234^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 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] + first_rank = ranks[subset[k-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]) + + +class ChowRingIdeal_nonaug_sp(ChowRingIdeal): + r""" + The Chow ring ideal of a matroid `M` in simplicial presentation. + + The *Chow ring ideal* for a matroid `M` in simplicial presentation + 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 ideal generated by products `x_{F}` for atoms `F` in the + lattice of flats, and + - `J_M` is the ideal generated by all terms + + .. MATH:: + + (\sum_{F \subseteq G} \mu(F, G) x_{G}) (\sum_{F' \subseteq G'} \mu(F', G') x_{G'}) + + for all incomparable flats `F` and `F'` of the the matroid, where `\mu` + is the Moebius function of the lattice of flats. + + INPUT: + + - ``M`` -- matroid + - ``R`` -- commutative ring + + REFERENCES: + + - [BES2024]_ + + EXAMPLES: + + Chow ring ideal of uniform matroid of rank 3 on 6 elements:: + + sage: ch = matroids.catalog.NonFano().chow_ring(QQ, False, 'simplicial') + sage: ch.defining_ideal() + Chow ring ideal of NonFano: Ternary matroid of rank 3 on 7 elements, + type 0- - non augmented in simplicial presentation + """ + def __init__(self, M, R): + r""" + Initialize ``self``. + + EXAMPLES:: + + sage: I = matroids.catalog.P8pp().chow_ring(QQ, False, 'simplicial').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] + poly_ring = ChowRingIdeal._construct_ambient_poly_ring(R, names, 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.Uniform(2, 5).chow_ring(QQ, False, 'simplicial') + sage: sorted(ch.defining_ideal()._gens_constructor(ch.defining_ideal().ring())) + [A4, A3, A2, A1, A0, A3*A4 - A3*A01234 - A4*A01234 + A01234^2, + A2*A4 - A2*A01234 - A4*A01234 + A01234^2, + A1*A4 - A1*A01234 - A4*A01234 + A01234^2, + A0*A4 - A0*A01234 - A4*A01234 + A01234^2, + A2*A3 - A2*A01234 - A3*A01234 + A01234^2, + A1*A3 - A1*A01234 - A3*A01234 + A01234^2, + A0*A3 - A0*A01234 - A3*A01234 + A01234^2, + A1*A2 - A1*A01234 - A2*A01234 + A01234^2, + A0*A2 - A0*A01234 - A2*A01234 + A01234^2, + A0*A1 - A0*A01234 - A1*A01234 + A01234^2] + """ + flats = list(self._flats_generator) + F = self._matroid.lattice_of_flats() + H = F.hasse_diagram() + H.delete_vertex(self._matroid.flats(0)[0]) # remove empty flat + lattice_flats = Poset(H) + flats_gen = self._flats_generator + ranks, chains = self._lattice_flats() + atoms = [F for F in flats if ranks[F] == 1] + I = [flats_gen[a] for a in atoms] + J = [] + subsets = lattice_flats.antichains().elements_of_depth_iterator(2) + for subset in subsets: + F = subset[0] + G = subset[1] + term1 = poly_ring.zero() + term2 = poly_ring.zero() + for H in lattice_flats.order_filter([F]): + term1 += lattice_flats.moebius_function(F, H) * flats_gen[H] + for H in lattice_flats.order_filter([G]): + term2 += lattice_flats.moebius_function(G, H) * flats_gen[H] + J.append(term1 * term2) + return I + J + + def _repr_(self): + r""" + Return a string representation of ``self``. + + EXAMPLES:: + + sage: ch = matroids.catalog.Fano().chow_ring(QQ, False, 'simplicial') + sage: ch.defining_ideal() + Chow ring ideal of Fano: Binary matroid of rank 3 on 7 elements, + type (3, 0) - non augmented in simplicial presentation + """ + return "Chow ring ideal of {} - non augmented in simplicial presentation".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, 'simplicial') + 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 = matroids.Wheel(3).chow_ring(QQ, False, 'simplicial') + sage: ch.defining_ideal().groebner_basis() + Polynomial Sequence with 105 Polynomials in 14 Variables + 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 = list(self._flats_generator) + F = self._matroid.lattice_of_flats() + H = F.hasse_diagram() + H.delete_vertex(self._matroid.flats(0)[0]) # remove empty flat + lattice_flats = Poset(H) + gb = [] + poly_ring = self.ring() + flats_gen = self._flats_generator + ranks, chains = self._lattice_flats() + subsets = lattice_flats.antichains().elements_of_depth_iterator(2) + for subset in subsets: + F = subset[0] + G = subset[1] + term1 = poly_ring.zero() + term2 = poly_ring.zero() + for H in lattice_flats.order_filter([F]): + term1 += lattice_flats.moebius_function(F, H) * flats_gen[H] + for H in lattice_flats.order_filter([G]): + term2 += lattice_flats.moebius_function(G, H) * flats_gen[H] + gb.append(term1 * term2) + subsets = lattice_flats.chains().elements_of_depth_iterator(2) + for subset in subsets: + F = subset[0] + G = subset[1] + term = poly_ring.zero() + for H in lattice_flats.order_filter([F]): + term += lattice_flats.moebius_function(F, H) * flats_gen[H] + gb.append(term * (flats_gen[G] ** (ranks[G] - ranks[F]))) + + for F in flats: + gb.append(flats_gen[F] ** ranks[F]) + 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.Z(3).chow_ring(QQ, False, 'simplicial') + 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 + """ + if algorithm == '': + algorithm = 'constructed' + if algorithm != 'constructed': + return super().normal_basis(algorithm=algorithm, *args, **kwargs) + r = self._matroid.rank() - 1 + 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]) + for combination in product(*(ran for ran 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 + if sum(combination) <= r: + 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 @@ -433,12 +931,9 @@ def __init__(self, M, R): 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)) + 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 = ChowRingIdeal._construct_ambient_poly_ring(R, names_flats + names_groundset, E + self._flats) for i, x in enumerate(E): self._flats_generator[x] = poly_ring.gens()[i] for i, F in enumerate(self._flats): @@ -457,39 +952,40 @@ def _gens_constructor(self, poly_ring): 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] + [B5 + B013 + B025 + B04 + B124 + B15 + B23 + B345 + B012345 + A0 + A1 + A2 + A3 + A4 + A5, + B5 + B013 + B025 + B04 + B124 + B15 + B23 + B345 + B012345 + A0 + A1 + A2 + A3 + A4 + A5, + B5 + B013 + B025 + B04 + B124 + B15 + B23 + B345 + B012345 + A0 + A1 + A2 + A3 + A4 + A5, + B5 + B013 + B025 + B04 + B124 + B15 + B23 + B345 + B012345 + A0 + A1 + A2 + A3 + A4 + A5, + B5 + B013 + B025 + B04 + B124 + B15 + B23 + B345 + B012345 + A0 + A1 + A2 + A3 + A4 + A5, + B5 + B013 + B025 + B04 + B124 + B15 + B23 + B345 + B012345 + A0 + A1 + A2 + A3 + A4 + A5, + B4 + B23 + B012345 + A2 + A4 + A5, + B3 + B15 + A0 + A1 + A4 + A5, + B2 + B124 + B345 + A3 + A4 + A5, + B1 + B04 + B012345 + A1 + A3 + A5, + B0 + B025 + B345 + A1 + A2 + A5, + B + B013 + B345 + B012345 + A0 + A5, A3*A4, A2*A4, A1*A4, A0*A4, + B012345*A4, B345*A4, B04*A4, B025*A4, B013*A4, B1*A4, B0*A4, B*A4, + A2*A3, A1*A3, A0*A3, B012345*A3, B345*A3, B23*A3, B15*A3, B025*A3, + B013*A3, B4*A3, B3*A3, B0*A3, B*A3, A1*A2, A0*A2, B012345*A2, + B345*A2, B15*A2, B124*A2, B04*A2, B013*A2, B3*A2, B2*A2, B1*A2, + B*A2, A0*A1, B012345*A1, B345*A1, B23*A1, B124*A1, B013*A1, B4*A1, + B2*A1, B*A1, B012345*A0, B345*A0, B23*A0, B124*A0, B04*A0, + B025*A0, B4*A0, B2*A0, B1*A0, B0*A0, B345*B012345, B15*B012345, + B124*B012345, B025*B012345, B3*B012345, B2*B012345, + B0*B012345, B23*B345, B15*B345, B04*B345, B4*B345, B3*B345, + B1*B345, B15*B23, B124*B23, B04*B23, B025*B23, B013*B23, B3*B23, + B2*B23, B1*B23, B0*B23, B*B23, B124*B15, B04*B15, B025*B15, + B013*B15, B4*B15, B2*B15, B1*B15, B0*B15, B*B15, B04*B124, + B025*B124, B013*B124, B4*B124, B3*B124, B1*B124, B0*B124, B*B124, + B025*B04, B013*B04, B4*B04, B3*B04, B2*B04, B0*B04, B*B04, + B013*B025, B4*B025, B3*B025, B2*B025, B1*B025, B*B025, B4*B013, + B3*B013, B2*B013, B1*B013, B0*B013, B4*B5, B3*B5, B2*B5, B1*B5, + B0*B5, B*B5] """ E = list(self._matroid.groundset()) Q = [] L = [] - lattice_flats = Poset((self._flats, lambda x, y: x <= y)) + lattice_flats = self._matroid.lattice_of_flats() 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 @@ -553,8 +1049,7 @@ def groebner_basis(self, 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)) + lattice_flats = self._matroid.lattice_of_flats() 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 @@ -587,7 +1082,7 @@ def normal_basis(self, algorithm='', *args, **kwargs): 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] + [1, B01234, A0, A1, A2, A3, A4, A4^2] sage: set(I.gens().ideal().normal_basis()) == set(I.normal_basis()) True sage: ch = matroids.catalog.Fano().chow_ring(QQ, True, 'fy') @@ -686,10 +1181,7 @@ def __init__(self, M, R): 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)) + poly_ring = ChowRingIdeal._construct_ambient_poly_ring(R, names, self._flats) gens = poly_ring.gens() self._flats_generator = dict(zip(self._flats, gens)) MPolynomialIdeal.__init__(self, poly_ring, self._gens_constructor(poly_ring)) @@ -713,8 +1205,10 @@ def _gens_constructor(self, poly_ring): 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)) + F = self._matroid.lattice_of_flats() + H = F.hasse_diagram() + H.delete_vertex(self._matroid.flats(0)[0]) # remove empty flat + lattice_flats = Poset(H) antichains = lattice_flats.antichains().elements_of_depth_iterator(2) for F, G in antichains: Q.append(self._flats_generator[F] * self._flats_generator[G]) @@ -776,7 +1270,10 @@ def groebner_basis(self, algorithm='', *args, **kwargs): return super().groebner_basis(algorithm=algorithm, *args, **kwargs) gb = [] poly_ring = self.ring() - lattice_flats = Poset((self._flats, lambda x, y: x <= y)) + F = self._matroid.lattice_of_flats() + H = F.hasse_diagram() + H.delete_vertex(self._matroid.flats(0)[0]) # remove empty flat + lattice_flats = Poset(H) antichains = lattice_flats.antichains().elements_of_depth_iterator(2) for F, G in antichains: gb.append(self._flats_generator[F]*self._flats_generator[G]) diff --git a/src/sage/matroids/matroid.pyx b/src/sage/matroids/matroid.pyx index d4a71e8a38e..164849c0be3 100644 --- a/src/sage/matroids/matroid.pyx +++ b/src/sage/matroids/matroid.pyx @@ -8069,18 +8069,18 @@ cdef class Matroid(SageObject): - ``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``): + - ``presentation`` -- string; one of the following: * ``"fy"`` - the Feitchner-Yuzvinsky presentation * ``"atom-free"`` - the atom-free presentation + *``"simplicial"`` - the simplicial presentation EXAMPLES:: sage: M = matroids.Wheel(2) - sage: A = M.chow_ring(R=ZZ, augmented=False); A + sage: A = M.chow_ring(R=ZZ, augmented=False, presentation='fy'); A Chow ring of Wheel(2): Regular matroid of rank 2 on 4 elements with - 5 bases over Integer Ring + 5 bases in Feitchner-Yuzvinsky presentation 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) @@ -8090,9 +8090,9 @@ cdef class Matroid(SageObject): 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 + sage: A = M.chow_ring(QQ, False, 'fy'); A + Chow ring of Fano: Binary matroid of rank 3 on 7 elements, + type (3, 0) in Feitchner-Yuzvinsky presentation over Rational Field Next we get the non-trivial generators and do some computations::