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();