diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml
new file mode 100644
index 000000000..73551256f
--- /dev/null
+++ b/.github/workflows/stale.yml
@@ -0,0 +1,28 @@
+# This workflow warns and then closes issues and PRs that have had no activity for a specified amount of time.
+#
+# You can adjust the behavior by modifying this file.
+# For more information, see:
+# https://github.com/actions/stale
+name: Mark stale issues and pull requests
+
+on:
+ schedule:
+ - cron: '42 0 * * *'
+
+jobs:
+ stale:
+
+ runs-on: ubuntu-latest
+ permissions:
+ issues: write
+ pull-requests: write
+
+ steps:
+ - uses: actions/stale@v3
+ with:
+ repo-token: ${{ secrets.GITHUB_TOKEN }}
+ stale-issue-message: 'Stale issue message'
+ stale-pr-message: 'Stale pull request message'
+ stale-issue-label: 'no-issue-activity'
+ stale-pr-label: 'no-pr-activity'
+ days-before-close: -1
diff --git a/doc/attr.xml b/doc/attr.xml
index 09b27850e..a432c5757 100644
--- a/doc/attr.xml
+++ b/doc/attr.xml
@@ -2429,3 +2429,40 @@ gap> Length(M);
<#/GAPDoc>
+
+<#GAPDoc Label="NonUpperSemimodularPair">
+
+
+
+ A pair of vertices or fail.
+
+ NonUpperSemimodularPair returns a pair of vertices in the digraph
+ D that witnesses the fact that D does not represent an upper
+ semimodular lattice, if such a pair exists.
+
+ If the digraph D does not satisfy , then an error is given. Otherwise if the
+ digraph D does satisfy , then either a non-upper semimodular pair of
+ vertices is returned, or fail is returned if no such pair exists
+ (meaning that D is an upper semimodular lattice.
+
+ NonLowerSemimodularPair behaves in the analogous way to
+ NonUpperSemimodularPair with respect to lower semimodularity.
+
+ See and for the definition of upper
+ semimodularity of a lattice.
+
+ D := DigraphFromDigraph6String(
+> "&M~~sc`lYUZO__KIBboC_@h?U_?_GL?A_?c");
+
+gap> NonLowerSemimodularPair(D);
+[ 10, 9 ]
+gap> NonUpperSemimodularPair(D);
+fail
+]]>
+
+
+<#/GAPDoc>
diff --git a/doc/digraph.xml b/doc/digraph.xml
index ea43bb730..9f1e0159f 100644
--- a/doc/digraph.xml
+++ b/doc/digraph.xml
@@ -421,33 +421,52 @@ gap> D := DigraphByInNeighbours(IsMutableDigraph,
<#GAPDoc Label="AsDigraph">
-
+
A digraph, or fail.
- If f is a transformation or permutation,
- and n is a non-negative integer
- such that the restriction of f to [1..n] defines
- a function of [1..n], then AsDigraph
- returns the functional digraph with n vertices defined by
- f. See .
-
-
- Specifically, the digraph returned by AsDigraph has n edges:
- for each vertex v in [1..n], there is a unique edge
- with source v; this edge has range v^f.
-
- If the optional second argument n is not supplied, then
- the degree of the transformation f,
- or the largest moved point of the permutation f,
- as applicable, is used by default. If the restriction of
- f to [1..n] does not define a function of
- [1..n], then AsDigraph(f, n)
- returns fail.
+ If f is a binary relation represented as one of the following in
+ &GAP;:
+
+
+ a transformation
+
+ -
+ satisfying
;
+
+
+ a permutation
+
+ -
+ satisfying
;
+
+
+ a partial perm
+
+ -
+ satisfying
;
+
+
+ a binary relation
+
+ -
+ satisfying
;
+
+
+ and n is a non-negative integer, then AsDigraph attempts
+ to construct a digraph with n vertices whose edges are determined
+ by f.
+
+ The digraph returned by AsDigraph has for each vertex
+ v in [1 .. n], an edge with source v and range
+ v ^ f. If v ^ f is greater than n for any
+ v, then fail is returned.
- See also .
+ If the optional second argument n is not supplied, then the degree
+ of the transformation f, the largest moved point of the permutation
+ f, the maximum of the degree and the codegree of the partial perm
+ f, or as applicable, is used by default.
&STANDARD_FILT_TEXT;
diff --git a/doc/prop.xml b/doc/prop.xml
index e8b720471..ee75b409b 100644
--- a/doc/prop.xml
+++ b/doc/prop.xml
@@ -1459,3 +1459,39 @@ true
<#/GAPDoc>
+
+<#GAPDoc Label="IsUpperSemimodularDigraph">
+
+
+
+ true or false.
+
+ IsUpperSemimodularDigraph returns true if the digraph
+ D represents an upper semimodular lattice and false if it
+ does not. Similarly, IsLowerSemimodularDigraph returns true
+ if D represents a lower semimodular lattice and false if
+ it does not.
+
+ In a lattice we say that a vertex a covers a vertex b
+ if a is greater than b, and there are no further vertices
+ between a and b. A lattice is upper semimodular if
+ whenever the meet of a and b is covered by a, the join
+ of a and b covers b. Lower semimodularity is
+ defined analogously.
+
+ See also , ,
+ and .
+
+ &MUTABLE_RECOMPUTED_PROP;
+
+ D := DigraphFromDigraph6String(
+> "&M~~sc`lYUZO__KIBboC_@h?U_?_GL?A_?c");
+
+gap> IsUpperSemimodularDigraph(D);
+true
+gap> IsLowerSemimodularDigraph(D);
+false]]>
+
+
+<#/GAPDoc>
diff --git a/doc/z-chap4.xml b/doc/z-chap4.xml
index 00224832d..1524d671c 100644
--- a/doc/z-chap4.xml
+++ b/doc/z-chap4.xml
@@ -35,10 +35,14 @@
<#Include Label="InDegreeOfVertex">
<#Include Label="InNeighboursOfVertex">
<#Include Label="DigraphLoops">
- <#Include Label="PartialOrderDigraphMeetOfVertices">
<#Include Label="DegreeMatrix">
<#Include Label="LaplacianMatrix">
+
+ Orders
+ <#Include Label="PartialOrderDigraphMeetOfVertices">
+ <#Include Label="NonUpperSemimodularPair">
+
Reachability and connectivity
<#Include Label="DigraphDiameter">
diff --git a/doc/z-chap5.xml b/doc/z-chap5.xml
index 906c65d76..f571974c2 100644
--- a/doc/z-chap5.xml
+++ b/doc/z-chap5.xml
@@ -22,9 +22,13 @@
<#Include Label="IsSymmetricDigraph">
<#Include Label="IsTournament">
<#Include Label="IsTransitiveDigraph">
+
+
+ Orders
<#Include Label="IsPreorderDigraph">
<#Include Label="IsPartialOrderDigraph">
<#Include Label="IsMeetSemilatticeDigraph">
+ <#Include Label="IsUpperSemimodularDigraph">
Regularity
diff --git a/gap/attr.gd b/gap/attr.gd
index 5336d5b75..b67221290 100644
--- a/gap/attr.gd
+++ b/gap/attr.gd
@@ -121,3 +121,9 @@ DeclareAttribute("DigraphMaximumMatching", IsDigraph);
DeclareAttribute("Bridges", IsDigraph);
DeclareAttributeThatReturnsDigraph("StrongOrientation", IsDigraph);
+
+DeclareAttribute("NonUpperSemimodularPair", IsDigraph);
+DeclareAttribute("NonLowerSemimodularPair", IsDigraph);
+
+DeclareProperty("IsUpperSemimodularDigraph", IsDigraph);
+DeclareProperty("IsLowerSemimodularDigraph", IsDigraph);
diff --git a/gap/attr.gi b/gap/attr.gi
index 7cc679762..ca51e0aac 100644
--- a/gap/attr.gi
+++ b/gap/attr.gi
@@ -2657,3 +2657,73 @@ function(D)
M := List(DigraphLoops(D), x -> [x, x]);
return Union(M, DIGRAPHS_MateToMatching(D, mateD));
end);
+
+# The following function is a transliteration from python to GAP of
+# the function find_nonsemimodular_pair
+# in sage/src/sage/combinat/posets/hasse_diagram.py
+
+BindGlobal("DIGRAPHS_NonSemimodularPair",
+function(nbs)
+ local n, covers, covers_len, a, covers_a, b, e, a_i, b_i;
+ n := Length(nbs);
+
+ for e in [1 .. n] do
+ covers := nbs[e];
+ covers_len := Length(covers);
+ if covers_len < 2 then
+ continue;
+ fi;
+ for a_i in [1 .. covers_len] do
+ a := covers[a_i];
+ covers_a := nbs[a];
+ for b_i in [1 .. a_i] do
+ b := covers[b_i];
+ if not ForAny(nbs[b], j -> j in covers_a) then
+ return [a, b];
+ fi;
+ od;
+ od;
+ od;
+
+ return fail;
+end);
+
+InstallMethod(NonUpperSemimodularPair, "for a digraph",
+[IsDigraphByOutNeighboursRep],
+function(D)
+ if not IsLatticeDigraph(D) then
+ ErrorNoReturn("the argument (a digraph) is not a lattice");
+ fi;
+ D := DigraphReflexiveTransitiveReduction(DigraphMutableCopyIfMutable(D));
+ return DIGRAPHS_NonSemimodularPair(OutNeighbours(D));
+end);
+
+InstallMethod(NonLowerSemimodularPair, "for a digraph",
+[IsDigraphByOutNeighboursRep],
+function(D)
+ if not IsLatticeDigraph(D) then
+ ErrorNoReturn("the argument (a digraph) is not a lattice");
+ fi;
+ D := DigraphReflexiveTransitiveReduction(DigraphMutableCopyIfMutable(D));
+ return DIGRAPHS_NonSemimodularPair(InNeighbours(D));
+end);
+
+InstallMethod(IsUpperSemimodularDigraph, "for a digraph",
+[IsDigraphByOutNeighboursRep],
+function(D)
+ if not IsLatticeDigraph(D) then
+ return false;
+ fi;
+ D := DigraphReflexiveTransitiveReduction(DigraphMutableCopyIfMutable(D));
+ return DIGRAPHS_NonSemimodularPair(OutNeighbours(D)) = fail;
+end);
+
+InstallMethod(IsLowerSemimodularDigraph, "for a digraph",
+[IsDigraphByOutNeighboursRep],
+function(D)
+ if not IsLatticeDigraph(D) then
+ return false;
+ fi;
+ D := DigraphReflexiveTransitiveReduction(DigraphMutableCopyIfMutable(D));
+ return DIGRAPHS_NonSemimodularPair(InNeighbours(D)) = fail;
+end);
diff --git a/gap/digraph.gd b/gap/digraph.gd
index d93389b46..0f722b5a3 100644
--- a/gap/digraph.gd
+++ b/gap/digraph.gd
@@ -106,18 +106,24 @@ DeclareSynonym("DigraphByInNeighbors", DigraphByInNeighbours);
DeclareConstructor("AsDigraphCons", [IsDigraph, IsBinaryRelation]);
DeclareConstructor("AsDigraphCons", [IsDigraph, IsTransformation]);
DeclareConstructor("AsDigraphCons", [IsDigraph, IsTransformation, IsInt]);
+DeclareConstructor("AsDigraphCons", [IsDigraph, IsPartialPerm]);
+DeclareConstructor("AsDigraphCons", [IsDigraph, IsPartialPerm, IsInt]);
DeclareOperation("AsDigraph", [IsFunction, IsBinaryRelation]);
DeclareOperation("AsDigraph", [IsFunction, IsTransformation]);
DeclareOperation("AsDigraph", [IsFunction, IsTransformation, IsInt]);
DeclareOperation("AsDigraph", [IsFunction, IsPerm]);
DeclareOperation("AsDigraph", [IsFunction, IsPerm, IsInt]);
+DeclareOperation("AsDigraph", [IsFunction, IsPartialPerm]);
+DeclareOperation("AsDigraph", [IsFunction, IsPartialPerm, IsInt]);
DeclareOperation("AsDigraph", [IsBinaryRelation]);
DeclareOperation("AsDigraph", [IsTransformation]);
DeclareOperation("AsDigraph", [IsTransformation, IsInt]);
DeclareOperation("AsDigraph", [IsPerm]);
DeclareOperation("AsDigraph", [IsPerm, IsInt]);
+DeclareOperation("AsDigraph", [IsPartialPerm]);
+DeclareOperation("AsDigraph", [IsPartialPerm, IsInt]);
DeclareOperation("AsBinaryRelation", [IsDigraph]);
DeclareOperation("AsSemigroup", [IsFunction, IsDigraph]);
diff --git a/gap/digraph.gi b/gap/digraph.gi
index e400ff2e9..c0ef7a829 100644
--- a/gap/digraph.gi
+++ b/gap/digraph.gi
@@ -1062,6 +1062,58 @@ InstallMethod(AsDigraph, "for a function and a perm",
InstallMethod(AsDigraph, "for a perm", [IsPerm],
p -> AsDigraph(AsTransformation(p)));
+InstallMethod(AsDigraphCons,
+"for IsMutableDigraph, a partial perm, and an integer",
+[IsMutableDigraph, IsPartialPerm, IsInt],
+function(filt, f, n)
+ local list, x, i;
+ if n < 0 then
+ ErrorNoReturn("the 2nd argument should be a non-negative integer,");
+ fi;
+
+ list := EmptyPlist(n);
+ for i in [1 .. n] do
+ x := i ^ f;
+ if x > n then
+ return fail;
+ elif x <> 0 then
+ list[i] := [x];
+ else
+ list[i] := [];
+ fi;
+ od;
+ return DigraphNC(IsMutableDigraph, list);
+end);
+
+InstallMethod(AsDigraphCons,
+"for IsImmutableDigraph, a partial perm, and an integer",
+[IsImmutableDigraph, IsPartialPerm, IsInt],
+function(filt, f, n)
+ local D;
+ D := AsDigraph(IsMutableDigraph, f, n);
+ if D <> fail then
+ D := MakeImmutable(D);
+ SetIsMultiDigraph(D, false);
+ fi;
+ return D;
+end);
+
+InstallMethod(AsDigraph, "for a function, a partial perm, and an integer",
+[IsFunction, IsPartialPerm, IsInt], AsDigraphCons);
+
+InstallMethod(AsDigraph, "for a partial perm and an integer",
+[IsPartialPerm, IsInt],
+{t, n} -> AsDigraphCons(IsImmutableDigraph, t, n));
+
+InstallMethod(AsDigraph, "for a function and a partial perm",
+[IsFunction, IsPartialPerm],
+{func, t} -> AsDigraphCons(func, t, Maximum(DegreeOfPartialPerm(t),
+ CodegreeOfPartialPerm(t))));
+
+InstallMethod(AsDigraph, "for a partial perm", [IsPartialPerm],
+t -> AsDigraphCons(IsImmutableDigraph, t, Maximum(DegreeOfPartialPerm(t),
+ CodegreeOfPartialPerm(t))));
+
InstallMethod(AsBinaryRelation, "for a digraph", [IsDigraphByOutNeighboursRep],
function(D)
local rel;
diff --git a/tst/standard/attr.tst b/tst/standard/attr.tst
index 9f2d76de9..5c0558caf 100644
--- a/tst/standard/attr.tst
+++ b/tst/standard/attr.tst
@@ -2792,6 +2792,36 @@ gap> D := DigraphRemoveEdge(D, 1, 3);
gap> D := DigraphRemoveEdge(D, 1, 3);
+# Semimodular lattices
+gap> D := DigraphFromDigraph6String("&C[o?");
+
+gap> IsUpperSemimodularDigraph(D);
+false
+gap> IsLowerSemimodularDigraph(D);
+false
+gap> NonUpperSemimodularPair(D);
+Error, the argument (a digraph) is not a lattice
+gap> NonLowerSemimodularPair(D);
+Error, the argument (a digraph) is not a lattice
+gap> D := DigraphFromDigraph6String("&K~~]mKaC_EgLb?_?~?g?m?a?b");
+
+gap> IsUpperSemimodularDigraph(D);
+true
+gap> NonUpperSemimodularPair(D);
+fail
+gap> IsLowerSemimodularDigraph(D);
+true
+gap> NonLowerSemimodularPair(D);
+fail
+gap> D := DigraphFromDigraph6String("&M~~sc`lYUZO__KIBboC_@h?U_?_GL?A_?c");
+
+gap> IsUpperSemimodularDigraph(D);
+true
+gap> IsLowerSemimodularDigraph(D);
+false
+gap> NonLowerSemimodularPair(D);
+[ 10, 9 ]
+
# DIGRAPHS_UnbindVariables
gap> Unbind(adj);
gap> Unbind(adj1);
diff --git a/tst/standard/digraph.tst b/tst/standard/digraph.tst
index 3ad78e781..d8cbffcb6 100644
--- a/tst/standard/digraph.tst
+++ b/tst/standard/digraph.tst
@@ -479,7 +479,8 @@ fail
gap> f := ();;
gap> D := AsDigraph(f);
-gap> D = EmptyDigraph(0);;
+gap> D = EmptyDigraph(0);
+true
gap> AsDigraph(f, 10);
gap> g := (1, 3, 7)(2, 6, 5, 8);;
@@ -511,6 +512,41 @@ gap> D := AsDigraph(IsMutableDigraph, h, 6);
gap> OutNeighbours(D);
[ [ 1 ], [ 5 ], [ 2 ], [ 4 ], [ 3 ], [ 6 ] ]
+# AsDigraph for a partial perm
+gap> f := PartialPerm([]);;
+gap> D := AsDigraph(f);
+
+gap> D = EmptyDigraph(0);
+true
+gap> AsDigraph(f, 10);
+
+gap> x := AsPartialPerm((1, 3, 7)(2, 6, 5, 8));
+(1,3,7)(2,6,5,8)(4)
+gap> D := AsDigraph(x);
+
+gap> AsDigraph(x, -1);
+Error, the 2nd argument should be a non-negative integer,
+gap> AsDigraph(x, 0);
+
+gap> D := AsDigraph(g, 10);
+
+gap> AsDigraph(g, 7);
+fail
+gap> x := AsPartialPerm((2, 5, 3), 5);
+(1)(2,5,3)(4)
+gap> D := AsDigraph(IsMutableDigraph, x);
+
+gap> AsDigraph(IsImmutableDigraph, x, 5);
+
+gap> D = AsDigraph(IsImmutableDigraph, x, 5);
+true
+gap> D := AsDigraph(IsMutableDigraph, x, 6);
+
+gap> OutNeighbours(D);
+[ [ 1 ], [ 5 ], [ 2 ], [ 4 ], [ 3 ], [ ] ]
+gap> AsDigraph(AsPartialPerm((2, 5, 3)), 2);
+fail
+
# RandomDigraph
gap> IsImmutableDigraph(RandomDigraph(100, 0.2));
true