From 52a74d7296c979fc835148c8f3fe447f6865f10d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Thu, 28 Aug 2025 15:58:51 +0200 Subject: [PATCH 1/3] implement the method "is_left_modular" for lattices --- src/sage/combinat/posets/lattices.py | 75 ++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/src/sage/combinat/posets/lattices.py b/src/sage/combinat/posets/lattices.py index be42489e25a..58b05caeda3 100644 --- a/src/sage/combinat/posets/lattices.py +++ b/src/sage/combinat/posets/lattices.py @@ -62,6 +62,7 @@ :meth:`~FiniteLatticePoset.is_planar` | Return ``True`` if the lattice has an upward planar drawing. :meth:`~FiniteLatticePoset.is_dismantlable` | Return ``True`` if the lattice is dismantlable. :meth:`~FiniteLatticePoset.is_interval_dismantlable` | Return ``True`` if the lattice is interval dismantlable. + :meth:`~FiniteLatticePoset.is_left_modular` | Return ``True`` if the lattice is left_modular. :meth:`~FiniteLatticePoset.is_sublattice_dismantlable` | Return ``True`` if the lattice is sublattice dismantlable. :meth:`~FiniteLatticePoset.is_stone` | Return ``True`` if the lattice is a Stone lattice. :meth:`~FiniteLatticePoset.is_trim` | Return ``True`` if the lattice is a trim lattice. @@ -1557,9 +1558,24 @@ def is_trim(self, certificate=False) -> bool | tuple: sage: LatticePoset({1:[]}).is_trim(True) (True, [1]) + Testing a trim lattice :: + + sage: L = LatticePoset(([1,2,3,4,5,6], + ....: [[1,2],[1,3],[3,4],[4,5],[2,5],[2,6],[6,5],[2,4]])) + sage: L.is_trim(True) + (True, [1, 2, 6, 5]) + + Testing a lattice which is not trim :: + + sage: L = LatticePoset(([1,2,3,4,5,6], + ....: [[1,2],[1,3],[3,4],[4,5],[2,5],[2,6],[6,5]])) + sage: L.is_trim(True) + (False, None) + .. SEEALSO:: - Weaker properties: :meth:`is_extremal` + - Weaker properties: :meth:`is_left_modular` - Stronger properties: :meth:`is_distributive` REFERENCES: @@ -1577,6 +1593,65 @@ def is_trim(self, certificate=False) -> bool | tuple: return (True, chain) if certificate else True return (False, None) if certificate else False + def is_left_modular(self, H=None, certificate=False) -> bool | list: + r""" + Return whether ``self`` is a left-modular lattice. + + INPUT: + + - ``H`` -- subset of elements; full ``self`` if no ``H`` is given + + - ``certificate`` -- boolean (default: ``False``); whether to return + a list of failures + + OUTPUT: + + if ``certificate == True``, this outputs a list of tuples + `(y, x, z)` which fail left-modularity. + + if ``certificate == False``, this outputs ``False`` + if any `x \in H` fails to be left-modular and ``True`` otherwise. + + ALGORITHM: + + Given a lattice `L` and a subset of elements `H`, + an element `x \in H` is left-modular + if for every `y,z \in L, y \leq z` + the equality `(y \vee x) \wedge z = y \vee (x \wedge z)`. + + .. SEEALSO:: + + - Stronger properties: :meth:`is_trim` + + EXAMPLES: + + A lattice that is not left-modular:: + + sage: L = LatticePoset(([1,2,3,4,5], + ....: [[1,2],[1,3],[3,4],[4,5],[2,5]])) + sage: L.is_left_modular() + False + + A left-modular lattice:: + + sage: L = LatticePoset(([1,2,3,4,5,6], + ....: [[1,2],[1,3],[3,4],[4,5],[2,5],[2,6],[6,5],[2,4]])) + sage: L.is_left_modular() + True + """ + if H is None: + H = self + out = [] + for x in H: + for z in self: + mxz = self.meet(x, z) + for y in self.principal_lower_set(z): + if self.join(y, mxz) != self.meet(self.join(y, x), z): + if not certificate: + return False + out.append((y, x, z)) + return out if certificate else True + def is_complemented(self, certificate=False) -> bool | tuple: r""" Return ``True`` if the lattice is complemented, and From bcedb88826b7e9371e044db5963c3302061ee407 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sat, 30 Aug 2025 08:09:08 +0200 Subject: [PATCH 2/3] using only cover relations --- src/sage/combinat/posets/lattices.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/combinat/posets/lattices.py b/src/sage/combinat/posets/lattices.py index 58b05caeda3..87d06ecf8f0 100644 --- a/src/sage/combinat/posets/lattices.py +++ b/src/sage/combinat/posets/lattices.py @@ -1645,7 +1645,7 @@ def is_left_modular(self, H=None, certificate=False) -> bool | list: for x in H: for z in self: mxz = self.meet(x, z) - for y in self.principal_lower_set(z): + for y in self.lower_covers_iterator(z): if self.join(y, mxz) != self.meet(self.join(y, x), z): if not certificate: return False From fd7eab716e82f84c28b24a1822c7960613b14b08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sat, 30 Aug 2025 13:28:37 +0200 Subject: [PATCH 3/3] suggested improvements --- src/sage/combinat/posets/lattices.py | 33 ++++++++++++++++++---------- 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/src/sage/combinat/posets/lattices.py b/src/sage/combinat/posets/lattices.py index 87d06ecf8f0..0ebfca858e4 100644 --- a/src/sage/combinat/posets/lattices.py +++ b/src/sage/combinat/posets/lattices.py @@ -1593,7 +1593,7 @@ def is_trim(self, certificate=False) -> bool | tuple: return (True, chain) if certificate else True return (False, None) if certificate else False - def is_left_modular(self, H=None, certificate=False) -> bool | list: + def is_left_modular(self, H=None, certificate=False) -> bool | tuple: r""" Return whether ``self`` is a left-modular lattice. @@ -1602,27 +1602,30 @@ def is_left_modular(self, H=None, certificate=False) -> bool | list: - ``H`` -- subset of elements; full ``self`` if no ``H`` is given - ``certificate`` -- boolean (default: ``False``); whether to return - a list of failures + a failure OUTPUT: - if ``certificate == True``, this outputs a list of tuples - `(y, x, z)` which fail left-modularity. + if ``certificate == True``, this returns either ``(True, None)`` + or ``(False, (y, x, z))`` where the tuple `(y, x, z)` + fails left-modularity. - if ``certificate == False``, this outputs ``False`` - if any `x \in H` fails to be left-modular and ``True`` otherwise. + if ``certificate == False``, this returns ``False`` if any + `x \in H` fails to be left-modular and ``True`` otherwise. ALGORITHM: Given a lattice `L` and a subset of elements `H`, an element `x \in H` is left-modular if for every `y,z \in L, y \leq z` - the equality `(y \vee x) \wedge z = y \vee (x \wedge z)`. + we have `(y \vee x) \wedge z = y \vee (x \wedge z)`. .. SEEALSO:: - Stronger properties: :meth:`is_trim` + - :meth:`is_left_modular_element` + EXAMPLES: A lattice that is not left-modular:: @@ -1638,19 +1641,23 @@ def is_left_modular(self, H=None, certificate=False) -> bool | list: ....: [[1,2],[1,3],[3,4],[4,5],[2,5],[2,6],[6,5],[2,4]])) sage: L.is_left_modular() True + + TESTS:: + + sage: L = LatticePoset(([1,2,3,4,5], + ....: [[1,2],[1,3],[3,4],[4,5],[2,5]])) + sage: L.is_left_modular(certificate=True) + (False, (3, 2, 4)) """ if H is None: H = self - out = [] for x in H: for z in self: mxz = self.meet(x, z) for y in self.lower_covers_iterator(z): if self.join(y, mxz) != self.meet(self.join(y, x), z): - if not certificate: - return False - out.append((y, x, z)) - return out if certificate else True + return False if not certificate else (False, (y, x, z)) + return (True, None) if certificate else True def is_complemented(self, certificate=False) -> bool | tuple: r""" @@ -2791,6 +2798,8 @@ def is_left_modular_element(self, x) -> bool: .. SEEALSO:: - Stronger properties: :meth:`is_modular_element` + + - :meth:`is_left_modular` """ return all(self.meet(self.join(y, x), z) == self.join(y, self.meet(x, z))