diff --git a/doc/oper.xml b/doc/oper.xml
index 1bd1fc966..d60a391aa 100644
--- a/doc/oper.xml
+++ b/doc/oper.xml
@@ -1353,23 +1353,29 @@ false
<#GAPDoc Label="VerticesReachableFrom">
+
A list.
- This operation returns a list of the vertices v, for which there exists
- a non-trivial directed walk from vertex root to vertex v in the digraph
+ This operation returns a list of the vertices v, for which there
+ exists a non-trivial directed walk from the vertex root, or any of
+ the list of vertices list, to vertex v in the digraph
digraph. See Section
for the definition of a non-trivial directed walk.
- The method for VerticesReachableFrom has worst case complexity of O(m +
- n) where m is the number of edges and n the number of
- vertices in digraph.
+ The method for VerticesReachableFrom has worst case complexity of
+ O(m + n) where m is the number of edges and n the
+ number of vertices in digraph.
+
+
+ This function returns an error if root, or any vertex in list,
+ is not a vertices of digraph.
D := CompleteDigraph(5);
gap> VerticesReachableFrom(D, 1);
-[ 2, 1, 3, 4, 5 ]
+[ 1, 2, 3, 4, 5 ]
gap> VerticesReachableFrom(D, 3);
[ 1, 2, 3, 4, 5 ]
gap> D := EmptyDigraph(5);
@@ -1378,12 +1384,16 @@ gap> VerticesReachableFrom(D, 1);
[ ]
gap> VerticesReachableFrom(D, 3);
[ ]
+gap> VerticesReachableFrom(D, [1, 2, 3, 4]);
+[ ]
+gap> VerticesReachableFrom(D, [3, 4, 5]);
+[ ]
gap> D := CycleDigraph(4);
gap> VerticesReachableFrom(D, 1);
-[ 2, 3, 4, 1 ]
+[ 1, 2, 3, 4 ]
gap> VerticesReachableFrom(D, 3);
-[ 4, 1, 2, 3 ]
+[ 1, 2, 3, 4 ]
gap> D := ChainDigraph(5);
gap> VerticesReachableFrom(D, 1);
@@ -1392,12 +1402,13 @@ gap> VerticesReachableFrom(D, 3);
[ 4, 5 ]
gap> VerticesReachableFrom(D, 5);
[ ]
+gap> VerticesReachableFrom(D, [3, 4]);
+[ 4, 5 ]
]]>
<#/GAPDoc>
-
<#GAPDoc Label="DigraphPath">
@@ -2257,3 +2268,30 @@ true
<#/GAPDoc>
+
+<#GAPDoc Label="IsOrderIdeal">
+
+
+ true or false.
+
+ This function returns true if the specified subset is "downwards" closed, i.e. contains every vertex less than the given vertices in the order defined by D.
+ The function can only be used on digraphs satisfying and will throw an error if passed a digraph that is not a partial order digraph.
+
+ D1 := Digraph([[1, 3], [2, 3], [3]]);
+
+
+gap> IsOrderIdeal(D, [1, 2, 3]);
+true
+gap> D2 := DigraphDisjointUnion(D, D);
+
+gap> IsOrderIdeal(D2, [1, 2, 3]);
+true
+gap> IsOrderIdeal(D2, [4, 5, 6]);
+true
+gap> IsOrderIdeal(D2, [1, 5, 6]);
+false
+]]>
+
+
+<#/GAPDoc>
diff --git a/gap/oper.gd b/gap/oper.gd
index 5c1ea6046..5594ea8c8 100644
--- a/gap/oper.gd
+++ b/gap/oper.gd
@@ -137,6 +137,8 @@ DeclareOperation("DigraphShortestDistance", [IsDigraph, IsList]);
DeclareOperation("DigraphShortestPath", [IsDigraph, IsPosInt, IsPosInt]);
DeclareOperation("DigraphShortestPathSpanningTree", [IsDigraph, IsPosInt]);
DeclareOperation("VerticesReachableFrom", [IsDigraph, IsPosInt]);
+DeclareOperation("VerticesReachableFrom", [IsDigraph, IsList]);
+DeclareOperation("IsOrderIdeal", [IsDigraph, IsList]);
DeclareOperation("Dominators", [IsDigraph, IsPosInt]);
DeclareOperation("DominatorTree", [IsDigraph, IsPosInt]);
diff --git a/gap/oper.gi b/gap/oper.gi
index 5186a7d3f..4cb26c81b 100644
--- a/gap/oper.gi
+++ b/gap/oper.gi
@@ -2005,42 +2005,75 @@ end);
InstallMethod(VerticesReachableFrom, "for a digraph and a vertex",
[IsDigraph, IsPosInt],
function(D, root)
- local N, index, current, succ, visited, prev, n, i, parent,
- have_visited_root;
+ local N;
N := DigraphNrVertices(D);
+
if 0 = root or root > N then
ErrorNoReturn("the 2nd argument (root) is not a vertex of the 1st ",
"argument (a digraph)");
fi;
- index := ListWithIdenticalEntries(N, 0);
- have_visited_root := false;
- index[root] := 1;
- current := root;
- succ := OutNeighbours(D);
- visited := [];
- parent := [];
- parent[root] := fail;
- repeat
- prev := current;
- for i in [index[current] .. Length(succ[current])] do
- n := succ[current][i];
- if n = root and not have_visited_root then
- Add(visited, root);
- have_visited_root := true;
- elif index[n] = 0 then
- Add(visited, n);
- parent[n] := current;
- index[current] := i + 1;
- current := n;
- index[current] := 1;
- break;
+
+ return VerticesReachableFrom(D, [root]);
+end);
+
+InstallMethod(VerticesReachableFrom, "for a digraph and a list of vertices",
+[IsDigraph, IsList],
+function(D, roots)
+ local N, index, visited, queue_tail, queue,
+ root, element, neighbour, graph_out_neighbors, node_neighbours;
+
+ N := DigraphNrVertices(D);
+
+ for root in roots do
+ if not IsPosInt(N) or 0 = root or root > N then
+ ErrorNoReturn("an element of the 2nd argument ",
+ "(roots) is not a vertex of the 1st ",
+ "argument (a digraph)");
+ fi;
+ od;
+
+ visited := BlistList([1 .. N], []);
+
+ graph_out_neighbors := OutNeighbors(D);
+ queue := EmptyPlist(N);
+ Append(queue, roots);
+
+ queue_tail := Length(roots);
+
+ index := 1;
+ while IsBound(queue[index]) do
+ element := queue[index];
+ node_neighbours := graph_out_neighbors[element];
+ for neighbour in node_neighbours do
+ if not visited[neighbour] then;
+ visited[neighbour] := true;
+ queue_tail := queue_tail + 1;
+ queue[queue_tail] := neighbour;
fi;
od;
- if prev = current then
- current := parent[current];
- fi;
- until current = fail;
- return visited;
+ index := index + 1;
+ od;
+
+ return ListBlist([1 .. N], visited);
+end);
+
+InstallMethod(IsOrderIdeal, "for a digraph and a list of vertices",
+[IsDigraph, IsList],
+# Check if digraph represents a partial order
+function(D, roots)
+ local reachable_vertices, vertex_in_subset, N;
+ if not IsPartialOrderDigraph(D) then
+ ErrorNoReturn(
+ "the 1st argument (a digraph) must be a partial order digraph");
+ fi;
+
+ N := DigraphNrVertices(D);
+ vertex_in_subset := BlistList([1 .. N], roots);
+ reachable_vertices := VerticesReachableFrom(D, roots);
+
+ return Length(reachable_vertices) = Length(roots)
+ and ForAll(reachable_vertices, x -> vertex_in_subset[x]);
+
end);
InstallMethod(DominatorTree, "for a digraph and a vertex",
diff --git a/tst/standard/oper.tst b/tst/standard/oper.tst
index 80086203b..476cb9f66 100644
--- a/tst/standard/oper.tst
+++ b/tst/standard/oper.tst
@@ -1942,7 +1942,7 @@ gap> DigraphShortestPath(gr, 12, 5014);
gap> D := CompleteDigraph(5);
gap> VerticesReachableFrom(D, 1);
-[ 2, 1, 3, 4, 5 ]
+[ 1, 2, 3, 4, 5 ]
gap> VerticesReachableFrom(D, 3);
[ 1, 2, 3, 4, 5 ]
gap> D := EmptyDigraph(5);
@@ -1951,12 +1951,14 @@ gap> VerticesReachableFrom(D, 1);
[ ]
gap> VerticesReachableFrom(D, 3);
[ ]
+gap> VerticesReachableFrom(D, 6);
+Error, the 2nd argument (root) is not a vertex of the 1st argument (a digraph)
gap> D := CycleDigraph(4);
gap> VerticesReachableFrom(D, 1);
-[ 2, 3, 4, 1 ]
+[ 1, 2, 3, 4 ]
gap> VerticesReachableFrom(D, 3);
-[ 4, 1, 2, 3 ]
+[ 1, 2, 3, 4 ]
gap> D := ChainDigraph(5);
gap> VerticesReachableFrom(D, 1);
@@ -1968,29 +1970,29 @@ gap> VerticesReachableFrom(D, 5);
gap> D := Digraph([[2, 3, 5], [1, 6], [4, 6, 7], [7, 8], [4], [], [8, 6], []]);
gap> VerticesReachableFrom(D, 1);
-[ 2, 1, 6, 3, 4, 7, 8, 5 ]
+[ 1, 2, 3, 4, 5, 6, 7, 8 ]
gap> VerticesReachableFrom(D, 2);
-[ 1, 2, 3, 4, 7, 8, 6, 5 ]
+[ 1, 2, 3, 4, 5, 6, 7, 8 ]
gap> VerticesReachableFrom(D, 3);
-[ 4, 7, 8, 6 ]
+[ 4, 6, 7, 8 ]
gap> VerticesReachableFrom(D, 4);
-[ 7, 8, 6 ]
+[ 6, 7, 8 ]
gap> VerticesReachableFrom(D, 5);
-[ 4, 7, 8, 6 ]
+[ 4, 6, 7, 8 ]
gap> VerticesReachableFrom(D, 6);
[ ]
gap> VerticesReachableFrom(D, 7);
-[ 8, 6 ]
+[ 6, 8 ]
gap> VerticesReachableFrom(D, 8);
[ ]
gap> D := Digraph([[1, 2, 3], [4], [1, 5], [], [2]]);
gap> VerticesReachableFrom(D, 1);
-[ 1, 2, 4, 3, 5 ]
+[ 1, 2, 3, 4, 5 ]
gap> VerticesReachableFrom(D, 2);
[ 4 ]
gap> VerticesReachableFrom(D, 3);
-[ 1, 2, 4, 3, 5 ]
+[ 1, 2, 3, 4, 5 ]
gap> VerticesReachableFrom(D, 4);
[ ]
gap> VerticesReachableFrom(D, 5);
@@ -1998,11 +2000,11 @@ gap> VerticesReachableFrom(D, 5);
gap> D := Digraph(IsMutableDigraph, [[1, 2, 3], [4], [1, 5], [], [2]]);
gap> VerticesReachableFrom(D, 1);
-[ 1, 2, 4, 3, 5 ]
+[ 1, 2, 3, 4, 5 ]
gap> VerticesReachableFrom(D, 2);
[ 4 ]
gap> VerticesReachableFrom(D, 3);
-[ 1, 2, 4, 3, 5 ]
+[ 1, 2, 3, 4, 5 ]
gap> VerticesReachableFrom(D, 4);
[ ]
gap> VerticesReachableFrom(D, 5);
@@ -2796,12 +2798,54 @@ gap> D := Digraph([
gap> path := DigraphPath(D, 5, 5);;
gap> IsDigraphPath(D, path);
true
+gap> D1 := CompleteDigraph(5);
+
+gap> D2 := CompleteDigraph(10);
+
+gap> VerticesReachableFrom(D1, [1]);
+[ 1, 2, 3, 4, 5 ]
+gap> VerticesReachableFrom(D1, [1, 2]);
+[ 1, 2, 3, 4, 5 ]
+gap> VerticesReachableFrom(D2, [1]);
+[ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]
+gap> VerticesReachableFrom(D2, [1, 11]);
+Error, an element of the 2nd argument (roots) is not a vertex of the 1st argum\
+ent (a digraph)
+gap> D3 := CompleteDigraph(7);
+
+gap> D3_edges := [1 .. 7];
+[ 1 .. 7 ]
+gap> for i in D3_edges do
+> D3 := DigraphRemoveEdge(D3, [1, i]);
+> D3 := DigraphRemoveEdge(D3, [i, 1]);
+> od;
+gap> VerticesReachableFrom(D3, [1]);
+[ ]
+gap> TestPartialOrderDigraph := Digraph([[1, 3], [2, 3], [3]]);
+
+gap> IsOrderIdeal(TestPartialOrderDigraph, [1, 2, 3]);
+true
+gap> TestPartialOrderDigraph2 := Digraph([[1, 3], [2, 3], [3]]);
+
+gap> TestUnion := DigraphDisjointUnion(TestPartialOrderDigraph, TestPartialOrderDigraph2);
+
+gap> IsOrderIdeal(TestUnion, [1, 2, 3]);
+true
+gap> IsOrderIdeal(TestUnion, [4, 5, 6]);
+true
+gap> IsOrderIdeal(TestUnion, [1, 5, 6]);
+false
+gap> D := CycleDigraph(5);;
+gap> IsOrderIdeal(D, [1]);
+Error, the 1st argument (a digraph) must be a partial order digraph
# DIGRAPHS_UnbindVariables
gap> Unbind(C);
gap> Unbind(D);
gap> Unbind(D1);
gap> Unbind(D2);
+gap> Unbind(D3);
+gap> Unbind(D3_edges);
gap> Unbind(DD);
gap> Unbind(G);
gap> Unbind(G1);
@@ -2854,6 +2898,7 @@ gap> Unbind(tclosure);
gap> Unbind(u1);
gap> Unbind(u2);
gap> Unbind(x);
+gap> Unbind(TestPartialOrderDigraph);
#
gap> DIGRAPHS_StopTest();