Skip to content

Commit 31fb62a

Browse files
Implement IsOrderIdeal (#609)
* Add VerticesReachableFrom and IsOrderIdeal * Unbind variables in oper.tst * Add IsOrderIdeal * Fix linting (maybe) * Fix syntax errors * Change variable name * Fix variable * Fix test file * Add IsOrderIdeal test * Update tests to test IsOrderIdeal * Add Documentation * Fix spacing in documentation * Add spaces to test file * Fix formatting * Fix documentation examples * Add IsOrderIdeal documentation * Switch to bfs approach * Fix syntax * Fix other syntax error * Fix syntax again * Fix even more syntax errors * Fix syntax again again * Add visited_as_ints to local variables * Fix incorrect OutNeighbours usage * Avoid infinite loop * Fix tests * Fix formatting * Add non-partial-order digraph test * Add Error messages * Fix missing N * Fix misuse of DigraphNrVertices * Add error check for VerticesReachableFrom * Fix BFS logic * Address formatting comments in oper.xml * Fix example ordering in oper.xml * Make linter pass (hopefully) * Rename variable * Appease linter * Appease linter * Appease linter v3 * Add failure case for VerticesReachableFrom * Switch to a more efficient list implementation * Update doc/oper.xml Co-authored-by: James Mitchell <james-d-mitchell@users.noreply.github.com> * Update doc/oper.xml Co-authored-by: James Mitchell <james-d-mitchell@users.noreply.github.com> * Update doc/oper.xml Co-authored-by: James Mitchell <james-d-mitchell@users.noreply.github.com> * Update gap/oper.gi Co-authored-by: James Mitchell <james-d-mitchell@users.noreply.github.com> * Update doc/oper.xml Co-authored-by: James Mitchell <james-d-mitchell@users.noreply.github.com> * Update doc/oper.xml Co-authored-by: James Mitchell <james-d-mitchell@users.noreply.github.com> * Update gap/oper.gi Co-authored-by: James Mitchell <james-d-mitchell@users.noreply.github.com> * Simplify function * Move lines after argument checking * Move N back * Fix test * fix error * appease linter * Appease linter * appease linter * Combine man sections for VerticesReachableFrom * Switch to using Append() * Apply suggestions from code review --------- Co-authored-by: James Mitchell <james-d-mitchell@users.noreply.github.com> Co-authored-by: James D. Mitchell <jdm3@st-andrews.ac.uk>
1 parent 397225b commit 31fb62a

4 files changed

Lines changed: 169 additions & 51 deletions

File tree

doc/oper.xml

Lines changed: 47 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1353,23 +1353,29 @@ false
13531353
<#GAPDoc Label="VerticesReachableFrom">
13541354
<ManSection>
13551355
<Oper Name="VerticesReachableFrom" Arg="digraph, root"/>
1356+
<Oper Name="VerticesReachableFrom" Arg="digraph, list"/>
13561357
<Returns>A list.</Returns>
13571358
<Description>
1358-
This operation returns a list of the vertices <A>v</A>, for which there exists
1359-
a non-trivial directed walk from vertex <A>root</A> to vertex <A>v</A> in the digraph
1359+
This operation returns a list of the vertices <C>v</C>, for which there
1360+
exists a non-trivial directed walk from the vertex <A>root</A>, or any of
1361+
the list of vertices <A>list</A>, to vertex <C>v</C> in the digraph
13601362
<A>digraph</A>. See Section <Ref Subsect="Definitions" Style="Number" />
13611363
for the definition of a non-trivial directed walk.
13621364
<P/>
13631365

1364-
The method for <C>VerticesReachableFrom</C> has worst case complexity of <M>O(m +
1365-
n)</M> where <M>m</M> is the number of edges and <M>n</M> the number of
1366-
vertices in <A>digraph</A>.
1366+
The method for <C>VerticesReachableFrom</C> has worst case complexity of
1367+
<M>O(m + n)</M> where <M>m</M> is the number of edges and <M>n</M> the
1368+
number of vertices in <A>digraph</A>.
1369+
<P/>
1370+
1371+
This function returns an error if <A>root</A>, or any vertex in <A>list</A>,
1372+
is not a vertices of <A>digraph</A>.
13671373

13681374
<Example><![CDATA[
13691375
gap> D := CompleteDigraph(5);
13701376
<immutable complete digraph with 5 vertices>
13711377
gap> VerticesReachableFrom(D, 1);
1372-
[ 2, 1, 3, 4, 5 ]
1378+
[ 1, 2, 3, 4, 5 ]
13731379
gap> VerticesReachableFrom(D, 3);
13741380
[ 1, 2, 3, 4, 5 ]
13751381
gap> D := EmptyDigraph(5);
@@ -1378,12 +1384,16 @@ gap> VerticesReachableFrom(D, 1);
13781384
[ ]
13791385
gap> VerticesReachableFrom(D, 3);
13801386
[ ]
1387+
gap> VerticesReachableFrom(D, [1, 2, 3, 4]);
1388+
[ ]
1389+
gap> VerticesReachableFrom(D, [3, 4, 5]);
1390+
[ ]
13811391
gap> D := CycleDigraph(4);
13821392
<immutable cycle digraph with 4 vertices>
13831393
gap> VerticesReachableFrom(D, 1);
1384-
[ 2, 3, 4, 1 ]
1394+
[ 1, 2, 3, 4 ]
13851395
gap> VerticesReachableFrom(D, 3);
1386-
[ 4, 1, 2, 3 ]
1396+
[ 1, 2, 3, 4 ]
13871397
gap> D := ChainDigraph(5);
13881398
<immutable chain digraph with 5 vertices>
13891399
gap> VerticesReachableFrom(D, 1);
@@ -1392,12 +1402,13 @@ gap> VerticesReachableFrom(D, 3);
13921402
[ 4, 5 ]
13931403
gap> VerticesReachableFrom(D, 5);
13941404
[ ]
1405+
gap> VerticesReachableFrom(D, [3, 4]);
1406+
[ 4, 5 ]
13951407
]]></Example>
13961408
</Description>
13971409
</ManSection>
13981410
<#/GAPDoc>
13991411

1400-
14011412
<#GAPDoc Label="DigraphPath">
14021413
<ManSection>
14031414
<Oper Name="DigraphPath" Arg="digraph, u, v"/>
@@ -2257,3 +2268,30 @@ true
22572268
</Description>
22582269
</ManSection>
22592270
<#/GAPDoc>
2271+
2272+
<#GAPDoc Label="IsOrderIdeal">
2273+
<ManSection>
2274+
<Oper Name="IsOrderIdeal" Arg="D, subset" Label="for a digraph and list"/>
2275+
<Returns><K>true</K> or <K>false</K>.</Returns>
2276+
<Description>
2277+
This function returns <K>true</K> if the specified subset is "downwards" closed, i.e. contains every vertex less than the given vertices in the order defined by <A>D</A>.
2278+
The function can only be used on digraphs satisfying <Ref Prop="IsPartialOrderDigraph"/> and will throw an error if passed a digraph that is not a partial order digraph.
2279+
2280+
<Example><![CDATA[
2281+
gap> D1 := Digraph([[1, 3], [2, 3], [3]]);
2282+
<immutable digraph with 3 vertices, 5 edges>
2283+
<immutable digraph with 3 vertices, 5 edges>
2284+
gap> IsOrderIdeal(D, [1, 2, 3]);
2285+
true
2286+
gap> D2 := DigraphDisjointUnion(D, D);
2287+
<immutable digraph with 6 vertices, 10 edges>
2288+
gap> IsOrderIdeal(D2, [1, 2, 3]);
2289+
true
2290+
gap> IsOrderIdeal(D2, [4, 5, 6]);
2291+
true
2292+
gap> IsOrderIdeal(D2, [1, 5, 6]);
2293+
false
2294+
]]></Example>
2295+
</Description>
2296+
</ManSection>
2297+
<#/GAPDoc>

gap/oper.gd

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,8 @@ DeclareOperation("DigraphShortestDistance", [IsDigraph, IsList]);
137137
DeclareOperation("DigraphShortestPath", [IsDigraph, IsPosInt, IsPosInt]);
138138
DeclareOperation("DigraphShortestPathSpanningTree", [IsDigraph, IsPosInt]);
139139
DeclareOperation("VerticesReachableFrom", [IsDigraph, IsPosInt]);
140+
DeclareOperation("VerticesReachableFrom", [IsDigraph, IsList]);
141+
DeclareOperation("IsOrderIdeal", [IsDigraph, IsList]);
140142
DeclareOperation("Dominators", [IsDigraph, IsPosInt]);
141143
DeclareOperation("DominatorTree", [IsDigraph, IsPosInt]);
142144

gap/oper.gi

Lines changed: 62 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -2005,42 +2005,75 @@ end);
20052005
InstallMethod(VerticesReachableFrom, "for a digraph and a vertex",
20062006
[IsDigraph, IsPosInt],
20072007
function(D, root)
2008-
local N, index, current, succ, visited, prev, n, i, parent,
2009-
have_visited_root;
2008+
local N;
20102009
N := DigraphNrVertices(D);
2010+
20112011
if 0 = root or root > N then
20122012
ErrorNoReturn("the 2nd argument (root) is not a vertex of the 1st ",
20132013
"argument (a digraph)");
20142014
fi;
2015-
index := ListWithIdenticalEntries(N, 0);
2016-
have_visited_root := false;
2017-
index[root] := 1;
2018-
current := root;
2019-
succ := OutNeighbours(D);
2020-
visited := [];
2021-
parent := [];
2022-
parent[root] := fail;
2023-
repeat
2024-
prev := current;
2025-
for i in [index[current] .. Length(succ[current])] do
2026-
n := succ[current][i];
2027-
if n = root and not have_visited_root then
2028-
Add(visited, root);
2029-
have_visited_root := true;
2030-
elif index[n] = 0 then
2031-
Add(visited, n);
2032-
parent[n] := current;
2033-
index[current] := i + 1;
2034-
current := n;
2035-
index[current] := 1;
2036-
break;
2015+
2016+
return VerticesReachableFrom(D, [root]);
2017+
end);
2018+
2019+
InstallMethod(VerticesReachableFrom, "for a digraph and a list of vertices",
2020+
[IsDigraph, IsList],
2021+
function(D, roots)
2022+
local N, index, visited, queue_tail, queue,
2023+
root, element, neighbour, graph_out_neighbors, node_neighbours;
2024+
2025+
N := DigraphNrVertices(D);
2026+
2027+
for root in roots do
2028+
if not IsPosInt(N) or 0 = root or root > N then
2029+
ErrorNoReturn("an element of the 2nd argument ",
2030+
"(roots) is not a vertex of the 1st ",
2031+
"argument (a digraph)");
2032+
fi;
2033+
od;
2034+
2035+
visited := BlistList([1 .. N], []);
2036+
2037+
graph_out_neighbors := OutNeighbors(D);
2038+
queue := EmptyPlist(N);
2039+
Append(queue, roots);
2040+
2041+
queue_tail := Length(roots);
2042+
2043+
index := 1;
2044+
while IsBound(queue[index]) do
2045+
element := queue[index];
2046+
node_neighbours := graph_out_neighbors[element];
2047+
for neighbour in node_neighbours do
2048+
if not visited[neighbour] then;
2049+
visited[neighbour] := true;
2050+
queue_tail := queue_tail + 1;
2051+
queue[queue_tail] := neighbour;
20372052
fi;
20382053
od;
2039-
if prev = current then
2040-
current := parent[current];
2041-
fi;
2042-
until current = fail;
2043-
return visited;
2054+
index := index + 1;
2055+
od;
2056+
2057+
return ListBlist([1 .. N], visited);
2058+
end);
2059+
2060+
InstallMethod(IsOrderIdeal, "for a digraph and a list of vertices",
2061+
[IsDigraph, IsList],
2062+
# Check if digraph represents a partial order
2063+
function(D, roots)
2064+
local reachable_vertices, vertex_in_subset, N;
2065+
if not IsPartialOrderDigraph(D) then
2066+
ErrorNoReturn(
2067+
"the 1st argument (a digraph) must be a partial order digraph");
2068+
fi;
2069+
2070+
N := DigraphNrVertices(D);
2071+
vertex_in_subset := BlistList([1 .. N], roots);
2072+
reachable_vertices := VerticesReachableFrom(D, roots);
2073+
2074+
return Length(reachable_vertices) = Length(roots)
2075+
and ForAll(reachable_vertices, x -> vertex_in_subset[x]);
2076+
20442077
end);
20452078

20462079
InstallMethod(DominatorTree, "for a digraph and a vertex",

tst/standard/oper.tst

Lines changed: 58 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1942,7 +1942,7 @@ gap> DigraphShortestPath(gr, 12, 5014);
19421942
gap> D := CompleteDigraph(5);
19431943
<immutable complete digraph with 5 vertices>
19441944
gap> VerticesReachableFrom(D, 1);
1945-
[ 2, 1, 3, 4, 5 ]
1945+
[ 1, 2, 3, 4, 5 ]
19461946
gap> VerticesReachableFrom(D, 3);
19471947
[ 1, 2, 3, 4, 5 ]
19481948
gap> D := EmptyDigraph(5);
@@ -1951,12 +1951,14 @@ gap> VerticesReachableFrom(D, 1);
19511951
[ ]
19521952
gap> VerticesReachableFrom(D, 3);
19531953
[ ]
1954+
gap> VerticesReachableFrom(D, 6);
1955+
Error, the 2nd argument (root) is not a vertex of the 1st argument (a digraph)
19541956
gap> D := CycleDigraph(4);
19551957
<immutable cycle digraph with 4 vertices>
19561958
gap> VerticesReachableFrom(D, 1);
1957-
[ 2, 3, 4, 1 ]
1959+
[ 1, 2, 3, 4 ]
19581960
gap> VerticesReachableFrom(D, 3);
1959-
[ 4, 1, 2, 3 ]
1961+
[ 1, 2, 3, 4 ]
19601962
gap> D := ChainDigraph(5);
19611963
<immutable chain digraph with 5 vertices>
19621964
gap> VerticesReachableFrom(D, 1);
@@ -1968,41 +1970,41 @@ gap> VerticesReachableFrom(D, 5);
19681970
gap> D := Digraph([[2, 3, 5], [1, 6], [4, 6, 7], [7, 8], [4], [], [8, 6], []]);
19691971
<immutable digraph with 8 vertices, 13 edges>
19701972
gap> VerticesReachableFrom(D, 1);
1971-
[ 2, 1, 6, 3, 4, 7, 8, 5 ]
1973+
[ 1, 2, 3, 4, 5, 6, 7, 8 ]
19721974
gap> VerticesReachableFrom(D, 2);
1973-
[ 1, 2, 3, 4, 7, 8, 6, 5 ]
1975+
[ 1, 2, 3, 4, 5, 6, 7, 8 ]
19741976
gap> VerticesReachableFrom(D, 3);
1975-
[ 4, 7, 8, 6 ]
1977+
[ 4, 6, 7, 8 ]
19761978
gap> VerticesReachableFrom(D, 4);
1977-
[ 7, 8, 6 ]
1979+
[ 6, 7, 8 ]
19781980
gap> VerticesReachableFrom(D, 5);
1979-
[ 4, 7, 8, 6 ]
1981+
[ 4, 6, 7, 8 ]
19801982
gap> VerticesReachableFrom(D, 6);
19811983
[ ]
19821984
gap> VerticesReachableFrom(D, 7);
1983-
[ 8, 6 ]
1985+
[ 6, 8 ]
19841986
gap> VerticesReachableFrom(D, 8);
19851987
[ ]
19861988
gap> D := Digraph([[1, 2, 3], [4], [1, 5], [], [2]]);
19871989
<immutable digraph with 5 vertices, 7 edges>
19881990
gap> VerticesReachableFrom(D, 1);
1989-
[ 1, 2, 4, 3, 5 ]
1991+
[ 1, 2, 3, 4, 5 ]
19901992
gap> VerticesReachableFrom(D, 2);
19911993
[ 4 ]
19921994
gap> VerticesReachableFrom(D, 3);
1993-
[ 1, 2, 4, 3, 5 ]
1995+
[ 1, 2, 3, 4, 5 ]
19941996
gap> VerticesReachableFrom(D, 4);
19951997
[ ]
19961998
gap> VerticesReachableFrom(D, 5);
19971999
[ 2, 4 ]
19982000
gap> D := Digraph(IsMutableDigraph, [[1, 2, 3], [4], [1, 5], [], [2]]);
19992001
<mutable digraph with 5 vertices, 7 edges>
20002002
gap> VerticesReachableFrom(D, 1);
2001-
[ 1, 2, 4, 3, 5 ]
2003+
[ 1, 2, 3, 4, 5 ]
20022004
gap> VerticesReachableFrom(D, 2);
20032005
[ 4 ]
20042006
gap> VerticesReachableFrom(D, 3);
2005-
[ 1, 2, 4, 3, 5 ]
2007+
[ 1, 2, 3, 4, 5 ]
20062008
gap> VerticesReachableFrom(D, 4);
20072009
[ ]
20082010
gap> VerticesReachableFrom(D, 5);
@@ -2796,12 +2798,54 @@ gap> D := Digraph([
27962798
gap> path := DigraphPath(D, 5, 5);;
27972799
gap> IsDigraphPath(D, path);
27982800
true
2801+
gap> D1 := CompleteDigraph(5);
2802+
<immutable complete digraph with 5 vertices>
2803+
gap> D2 := CompleteDigraph(10);
2804+
<immutable complete digraph with 10 vertices>
2805+
gap> VerticesReachableFrom(D1, [1]);
2806+
[ 1, 2, 3, 4, 5 ]
2807+
gap> VerticesReachableFrom(D1, [1, 2]);
2808+
[ 1, 2, 3, 4, 5 ]
2809+
gap> VerticesReachableFrom(D2, [1]);
2810+
[ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]
2811+
gap> VerticesReachableFrom(D2, [1, 11]);
2812+
Error, an element of the 2nd argument (roots) is not a vertex of the 1st argum\
2813+
ent (a digraph)
2814+
gap> D3 := CompleteDigraph(7);
2815+
<immutable complete digraph with 7 vertices>
2816+
gap> D3_edges := [1 .. 7];
2817+
[ 1 .. 7 ]
2818+
gap> for i in D3_edges do
2819+
> D3 := DigraphRemoveEdge(D3, [1, i]);
2820+
> D3 := DigraphRemoveEdge(D3, [i, 1]);
2821+
> od;
2822+
gap> VerticesReachableFrom(D3, [1]);
2823+
[ ]
2824+
gap> TestPartialOrderDigraph := Digraph([[1, 3], [2, 3], [3]]);
2825+
<immutable digraph with 3 vertices, 5 edges>
2826+
gap> IsOrderIdeal(TestPartialOrderDigraph, [1, 2, 3]);
2827+
true
2828+
gap> TestPartialOrderDigraph2 := Digraph([[1, 3], [2, 3], [3]]);
2829+
<immutable digraph with 3 vertices, 5 edges>
2830+
gap> TestUnion := DigraphDisjointUnion(TestPartialOrderDigraph, TestPartialOrderDigraph2);
2831+
<immutable digraph with 6 vertices, 10 edges>
2832+
gap> IsOrderIdeal(TestUnion, [1, 2, 3]);
2833+
true
2834+
gap> IsOrderIdeal(TestUnion, [4, 5, 6]);
2835+
true
2836+
gap> IsOrderIdeal(TestUnion, [1, 5, 6]);
2837+
false
2838+
gap> D := CycleDigraph(5);;
2839+
gap> IsOrderIdeal(D, [1]);
2840+
Error, the 1st argument (a digraph) must be a partial order digraph
27992841

28002842
# DIGRAPHS_UnbindVariables
28012843
gap> Unbind(C);
28022844
gap> Unbind(D);
28032845
gap> Unbind(D1);
28042846
gap> Unbind(D2);
2847+
gap> Unbind(D3);
2848+
gap> Unbind(D3_edges);
28052849
gap> Unbind(DD);
28062850
gap> Unbind(G);
28072851
gap> Unbind(G1);
@@ -2854,6 +2898,7 @@ gap> Unbind(tclosure);
28542898
gap> Unbind(u1);
28552899
gap> Unbind(u2);
28562900
gap> Unbind(x);
2901+
gap> Unbind(TestPartialOrderDigraph);
28572902

28582903
#
28592904
gap> DIGRAPHS_StopTest();

0 commit comments

Comments
 (0)