Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement IsOrderIdeal #609

Merged
merged 60 commits into from
Mar 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
e28a2e6
Add VerticesReachableFrom and IsOrderIdeal
DanielPointon Jan 30, 2024
591b69b
Unbind variables in oper.tst
DanielPointon Jan 30, 2024
7bb968a
Add IsOrderIdeal
DanielPointon Jan 30, 2024
37feffc
Fix linting (maybe)
DanielPointon Jan 31, 2024
01dc63c
Fix syntax errors
DanielPointon Jan 31, 2024
10d93af
Change variable name
DanielPointon Jan 31, 2024
c998c75
Fix variable
DanielPointon Jan 31, 2024
c9b35d4
Fix test file
DanielPointon Jan 31, 2024
7f0bb1f
Add IsOrderIdeal test
DanielPointon Jan 31, 2024
40e0aa8
Update tests to test IsOrderIdeal
DanielPointon Jan 31, 2024
89a960b
Add Documentation
DanielPointon Jan 31, 2024
f7e3730
Fix spacing in documentation
DanielPointon Jan 31, 2024
45e9adc
Add spaces to test file
DanielPointon Jan 31, 2024
bf0d9b4
Fix formatting
DanielPointon Jan 31, 2024
31f4c5d
Fix documentation examples
DanielPointon Jan 31, 2024
c8b59e3
Add IsOrderIdeal documentation
DanielPointon Jan 31, 2024
2b8cb5c
Switch to bfs approach
DanielPointon Feb 6, 2024
65d3719
Fix syntax
DanielPointon Feb 6, 2024
9b0e994
Fix other syntax error
DanielPointon Feb 6, 2024
e2fe1f5
Fix syntax again
DanielPointon Feb 6, 2024
017d6e3
Fix even more syntax errors
DanielPointon Feb 6, 2024
8ca9d55
Fix syntax again again
DanielPointon Feb 6, 2024
5fb112f
Add visited_as_ints to local variables
DanielPointon Feb 6, 2024
4166869
Fix incorrect OutNeighbours usage
DanielPointon Feb 6, 2024
f5ba240
Avoid infinite loop
DanielPointon Feb 6, 2024
69dbf7b
Fix tests
DanielPointon Feb 6, 2024
00c7d2e
Fix formatting
DanielPointon Feb 6, 2024
c59722f
Add non-partial-order digraph test
DanielPointon Feb 6, 2024
da0575d
Add Error messages
DanielPointon Feb 6, 2024
c0e9c5b
Fix missing N
DanielPointon Feb 6, 2024
d29cb12
Fix misuse of DigraphNrVertices
DanielPointon Feb 6, 2024
40d8281
Add error check for VerticesReachableFrom
DanielPointon Feb 6, 2024
607a7e7
Fix BFS logic
DanielPointon Feb 6, 2024
d196f97
Address formatting comments in oper.xml
DanielPointon Feb 6, 2024
8bae38d
Fix example ordering in oper.xml
DanielPointon Feb 6, 2024
40a2252
Make linter pass (hopefully)
DanielPointon Feb 7, 2024
a1dfdb1
Rename variable
DanielPointon Feb 7, 2024
646377f
Appease linter
DanielPointon Feb 7, 2024
feb0adb
Appease linter
DanielPointon Feb 7, 2024
a06d032
Appease linter v3
DanielPointon Feb 7, 2024
4626066
Add failure case for VerticesReachableFrom
DanielPointon Feb 7, 2024
3c78227
Switch to a more efficient list implementation
DanielPointon Feb 7, 2024
3cc67e9
Update doc/oper.xml
DanielPointon Feb 13, 2024
28577af
Update doc/oper.xml
DanielPointon Feb 13, 2024
6c715e6
Update doc/oper.xml
DanielPointon Feb 13, 2024
afc524f
Update gap/oper.gi
DanielPointon Feb 13, 2024
e1a7187
Update doc/oper.xml
DanielPointon Feb 13, 2024
753c907
Update doc/oper.xml
DanielPointon Feb 13, 2024
e5d512b
Update gap/oper.gi
DanielPointon Feb 13, 2024
a0851c1
Simplify function
DanielPointon Feb 13, 2024
4bb08a8
Move lines after argument checking
DanielPointon Feb 13, 2024
e4cffe9
Move N back
DanielPointon Feb 13, 2024
a4faf94
Fix test
DanielPointon Feb 13, 2024
a1fd666
fix error
DanielPointon Feb 13, 2024
6ce6e99
appease linter
DanielPointon Feb 13, 2024
9a5b9f3
Appease linter
DanielPointon Feb 13, 2024
3d002da
appease linter
DanielPointon Feb 13, 2024
6cae53f
Combine man sections for VerticesReachableFrom
james-d-mitchell Mar 7, 2024
4929908
Switch to using Append()
DanielPointon Mar 11, 2024
e17a6e7
Apply suggestions from code review
james-d-mitchell Mar 11, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 47 additions & 9 deletions doc/oper.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1353,23 +1353,29 @@ false
<#GAPDoc Label="VerticesReachableFrom">
<ManSection>
<Oper Name="VerticesReachableFrom" Arg="digraph, root"/>
<Oper Name="VerticesReachableFrom" Arg="digraph, list"/>
<Returns>A list.</Returns>
<Description>
This operation returns a list of the vertices <A>v</A>, for which there exists
a non-trivial directed walk from vertex <A>root</A> to vertex <A>v</A> in the digraph
This operation returns a list of the vertices <C>v</C>, for which there
exists a non-trivial directed walk from the vertex <A>root</A>, or any of
the list of vertices <A>list</A>, to vertex <C>v</C> in the digraph
<A>digraph</A>. See Section <Ref Subsect="Definitions" Style="Number" />
for the definition of a non-trivial directed walk.
<P/>

The method for <C>VerticesReachableFrom</C> has worst case complexity of <M>O(m +
n)</M> where <M>m</M> is the number of edges and <M>n</M> the number of
vertices in <A>digraph</A>.
The method for <C>VerticesReachableFrom</C> has worst case complexity of
<M>O(m + n)</M> where <M>m</M> is the number of edges and <M>n</M> the
number of vertices in <A>digraph</A>.
<P/>

This function returns an error if <A>root</A>, or any vertex in <A>list</A>,
is not a vertices of <A>digraph</A>.

<Example><![CDATA[
gap> D := CompleteDigraph(5);
<immutable complete digraph with 5 vertices>
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);
Expand All @@ -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);
<immutable cycle digraph with 4 vertices>
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);
<immutable chain digraph with 5 vertices>
gap> VerticesReachableFrom(D, 1);
Expand All @@ -1392,12 +1402,13 @@ gap> VerticesReachableFrom(D, 3);
[ 4, 5 ]
gap> VerticesReachableFrom(D, 5);
[ ]
gap> VerticesReachableFrom(D, [3, 4]);
[ 4, 5 ]
]]></Example>
</Description>
</ManSection>
<#/GAPDoc>


<#GAPDoc Label="DigraphPath">
<ManSection>
<Oper Name="DigraphPath" Arg="digraph, u, v"/>
Expand Down Expand Up @@ -2257,3 +2268,30 @@ true
</Description>
</ManSection>
<#/GAPDoc>

<#GAPDoc Label="IsOrderIdeal">
<ManSection>
<Oper Name="IsOrderIdeal" Arg="D, subset" Label="for a digraph and list"/>
<Returns><K>true</K> or <K>false</K>.</Returns>
<Description>
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>.
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.

<Example><![CDATA[
DanielPointon marked this conversation as resolved.
Show resolved Hide resolved
gap> D1 := Digraph([[1, 3], [2, 3], [3]]);
<immutable digraph with 3 vertices, 5 edges>
<immutable digraph with 3 vertices, 5 edges>
gap> IsOrderIdeal(D, [1, 2, 3]);
true
gap> D2 := DigraphDisjointUnion(D, D);
<immutable digraph with 6 vertices, 10 edges>
gap> IsOrderIdeal(D2, [1, 2, 3]);
true
gap> IsOrderIdeal(D2, [4, 5, 6]);
true
gap> IsOrderIdeal(D2, [1, 5, 6]);
false
]]></Example>
</Description>
</ManSection>
<#/GAPDoc>
2 changes: 2 additions & 0 deletions gap/oper.gd
Original file line number Diff line number Diff line change
Expand Up @@ -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]);

Expand Down
91 changes: 62 additions & 29 deletions gap/oper.gi
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
71 changes: 58 additions & 13 deletions tst/standard/oper.tst
Original file line number Diff line number Diff line change
Expand Up @@ -1942,7 +1942,7 @@ gap> DigraphShortestPath(gr, 12, 5014);
gap> D := CompleteDigraph(5);
<immutable complete digraph with 5 vertices>
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);
Expand All @@ -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);
<immutable cycle digraph with 4 vertices>
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);
<immutable chain digraph with 5 vertices>
gap> VerticesReachableFrom(D, 1);
Expand All @@ -1968,41 +1970,41 @@ gap> VerticesReachableFrom(D, 5);
gap> D := Digraph([[2, 3, 5], [1, 6], [4, 6, 7], [7, 8], [4], [], [8, 6], []]);
<immutable digraph with 8 vertices, 13 edges>
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]]);
<immutable digraph with 5 vertices, 7 edges>
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);
[ 2, 4 ]
gap> D := Digraph(IsMutableDigraph, [[1, 2, 3], [4], [1, 5], [], [2]]);
<mutable digraph with 5 vertices, 7 edges>
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);
Expand Down Expand Up @@ -2796,12 +2798,54 @@ gap> D := Digraph([
gap> path := DigraphPath(D, 5, 5);;
gap> IsDigraphPath(D, path);
true
gap> D1 := CompleteDigraph(5);
<immutable complete digraph with 5 vertices>
gap> D2 := CompleteDigraph(10);
<immutable complete digraph with 10 vertices>
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);
<immutable complete digraph with 7 vertices>
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]]);
<immutable digraph with 3 vertices, 5 edges>
gap> IsOrderIdeal(TestPartialOrderDigraph, [1, 2, 3]);
true
gap> TestPartialOrderDigraph2 := Digraph([[1, 3], [2, 3], [3]]);
<immutable digraph with 3 vertices, 5 edges>
gap> TestUnion := DigraphDisjointUnion(TestPartialOrderDigraph, TestPartialOrderDigraph2);
<immutable digraph with 6 vertices, 10 edges>
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);
Expand Down Expand Up @@ -2854,6 +2898,7 @@ gap> Unbind(tclosure);
gap> Unbind(u1);
gap> Unbind(u2);
gap> Unbind(x);
gap> Unbind(TestPartialOrderDigraph);

#
gap> DIGRAPHS_StopTest();
Expand Down
Loading