Quelle oper.gi
Sprache: unbekannt
|
|
#############################################################################
##
## oper.gi
## Copyright (C) 2014-19 James D. Mitchell
##
## Licensing information can be found in the README file of this package.
##
#############################################################################
##
#############################################################################
# This file is organised as follows:
#
# 1. Adding and removing vertices
# 2. Adding, removing, and reversing edges
# 3. Ways of combining digraphs
# 4. Actions
# 5. Substructures and quotients
# 6. In and out degrees, neighbours, and edges of vertices
# 7. Copies of out/in-neighbours
# 8. IsSomething
# 9. Connectivity
# 10. Operations for vertices
#############################################################################
#############################################################################
# 1. Adding and removing vertices
#############################################################################
InstallMethod(DigraphAddVertex,
"for a mutable digraph by out-neighbours and an object",
[IsMutableDigraph and IsDigraphByOutNeighboursRep, IsObject],
function(D, label)
Add(D!.OutNeighbours, []);
SetDigraphVertexLabel(D, DigraphNrVertices(D), label);
if IsBound(D!.edgelabels) then
Add(D!.edgelabels, []);
fi;
return D;
end);
InstallMethod(DigraphAddVertex, "for a immutable digraph and an object",
[IsImmutableDigraph, IsObject],
{D, label} -> MakeImmutable(DigraphAddVertex(DigraphMutableCopy(D), label)));
InstallMethod(DigraphAddVertex, "for a mutable digraph",
[IsMutableDigraph],
D -> DigraphAddVertex(D, DigraphNrVertices(D) + 1));
InstallMethod(DigraphAddVertex, "for an immutable digraph",
[IsImmutableDigraph],
D -> MakeImmutable(DigraphAddVertex(DigraphMutableCopy(D))));
InstallMethod(DigraphAddVertices, "for a mutable digraph and list",
[IsMutableDigraph, IsList],
function(D, labels)
local label;
for label in labels do
DigraphAddVertex(D, label);
od;
return D;
end);
InstallMethod(DigraphAddVertices, "for an immutable digraph and list",
[IsImmutableDigraph, IsList],
function(D, labels)
if IsEmpty(labels) then
return D;
fi;
return MakeImmutable(DigraphAddVertices(DigraphMutableCopy(D), labels));
end);
InstallMethod(DigraphAddVertices, "for a mutable digraph and an integer",
[IsMutableDigraph, IsInt],
function(D, m)
local N;
if m < 0 then
ErrorNoReturn("the 2nd argument <m> must be a non-negative integer,");
fi;
N := DigraphNrVertices(D);
return DigraphAddVertices(D, [N + 1 .. N + m]);
end);
InstallMethod(DigraphAddVertices, "for an immutable digraph and an integer",
[IsImmutableDigraph, IsInt],
function(D, m)
if m = 0 then
return D;
fi;
return MakeImmutable(DigraphAddVertices(DigraphMutableCopy(D), m));
end);
# Included for backwards compatibility, even though the 2nd arg is redundant.
# See https://github.com/digraphs/Digraphs/issues/264
# This is deliberately kept undocumented.
InstallMethod(DigraphAddVertices, "for a digraph, an integer, and a list",
[IsDigraph, IsInt, IsList],
function(D, m, labels)
if m <> Length(labels) then
ErrorNoReturn("the list <labels> (3rd argument) must have length <m> ",
"(2nd argument),");
fi;
return DigraphAddVertices(D, labels);
end);
InstallMethod(DigraphRemoveVertex,
"for a mutable digraph by out-neighbours and positive integer",
[IsMutableDigraph and IsDigraphByOutNeighboursRep, IsPosInt],
function(D, u)
local pos, w, v;
if u > DigraphNrVertices(D) then
return D;
fi;
RemoveDigraphVertexLabel(D, u);
if IsBound(D!.edgelabels) then
Remove(D!.edgelabels, u);
fi;
Remove(D!.OutNeighbours, u);
for v in DigraphVertices(D) do
pos := 1;
while pos <= Length(D!.OutNeighbours[v]) do
w := D!.OutNeighbours[v][pos];
if w = u then
Remove(D!.OutNeighbours[v], pos);
RemoveDigraphEdgeLabel(D, v, pos);
elif w > u then
D!.OutNeighbours[v][pos] := w - 1;
pos := pos + 1;
else
pos := pos + 1;
fi;
od;
od;
return D;
end);
InstallMethod(DigraphRemoveVertex,
"for an immutable digraph and positive integer",
[IsImmutableDigraph, IsPosInt],
function(D, u)
if u > DigraphNrVertices(D) then
return D;
fi;
return MakeImmutable(DigraphRemoveVertex(DigraphMutableCopy(D), u));
end);
InstallMethod(DigraphRemoveVertices, "for a mutable digraph and a list",
[IsMutableDigraph, IsList],
function(D, list)
local v;
if not IsDuplicateFreeList(list) or not ForAll(list, IsPosInt) then
ErrorNoReturn("the 2nd argument <list> must be a ",
"duplicate-free list of positive integers,");
elif not IsMutable(list) then
list := ShallowCopy(list);
fi;
# The next line is essential since otherwise removing the 1st node,
# the 2nd node becomes the 1st node, and so removing the 2nd node we
# actually remove the 3rd node instead.
Sort(list, {x, y} -> x > y);
for v in list do
DigraphRemoveVertex(D, v);
od;
return D;
end);
InstallMethod(DigraphRemoveVertices, "for an immutable digraph and a list",
[IsImmutableDigraph, IsList],
{D, list} -> MakeImmutable(DigraphRemoveVertices(DigraphMutableCopy(D), list)));
#############################################################################
# 2. Adding and removing edges
#############################################################################
InstallMethod(DigraphAddEdge,
"for a mutable digraph by out-neighbours, a two positive integers",
[IsMutableDigraph and IsDigraphByOutNeighboursRep, IsPosInt, IsPosInt],
function(D, src, ran)
if not src in DigraphVertices(D)then
ErrorNoReturn("the 2nd argument <src> must be a vertex of the ",
"digraph <D> that is the 1st argument,");
elif not ran in DigraphVertices(D) then
ErrorNoReturn("the 2nd argument <ran> must be a vertex of the ",
"digraph <D> that is the 1st argument,");
fi;
Add(D!.OutNeighbours[src], ran);
if HaveEdgeLabelsBeenAssigned(D) and not IsMultiDigraph(D) then
SetDigraphEdgeLabel(D, src, ran, 1);
fi;
return D;
end);
InstallMethod(DigraphAddEdge,
"for an immutable digraph and two positive integers",
[IsImmutableDigraph, IsPosInt, IsPosInt],
{D, src, ran}
-> MakeImmutable(DigraphAddEdge(DigraphMutableCopy(D), src, ran)));
InstallMethod(DigraphAddEdge, "for a mutable digraph and a list",
[IsMutableDigraph, IsList],
function(D, edge)
if Length(edge) <> 2 then
ErrorNoReturn("the 2nd argument <edge> must be a list of length 2,");
fi;
return DigraphAddEdge(D, edge[1], edge[2]);
end);
InstallMethod(DigraphAddEdge, "for a mutable digraph and a list",
[IsImmutableDigraph, IsList],
{D, edge} -> MakeImmutable(DigraphAddEdge(DigraphMutableCopy(D), edge)));
InstallMethod(DigraphAddEdges, "for a mutable digraph and a list",
[IsMutableDigraph, IsList],
function(D, edges)
local edge;
for edge in edges do
DigraphAddEdge(D, edge);
od;
return D;
end);
InstallMethod(DigraphAddEdges, "for an immutable digraph and a list",
[IsImmutableDigraph, IsList],
{D, edges} -> MakeImmutable(DigraphAddEdges(DigraphMutableCopy(D), edges)));
InstallMethod(DigraphRemoveEdge,
"for a mutable digraph by out-neighbours and two positive integers",
[IsMutableDigraph and IsDigraphByOutNeighboursRep, IsPosInt, IsPosInt],
function(D, src, ran)
local pos;
if IsMultiDigraph(D) then
ErrorNoReturn("the 1st argument <D> must be a digraph with no multiple ",
"edges,");
elif not (IsPosInt(src) and src in DigraphVertices(D)) then
ErrorNoReturn("the 2nd argument <src> must be a vertex of the ",
"digraph <D> that is the 1st argument,");
elif not (IsPosInt(ran) and ran in DigraphVertices(D)) then
ErrorNoReturn("the 3rd argument <ran> must be a vertex of the ",
"digraph <D> that is the 1st argument,");
fi;
pos := Position(D!.OutNeighbours[src], ran);
if pos <> fail then
Remove(D!.OutNeighbours[src], pos);
RemoveDigraphEdgeLabel(D, src, pos);
fi;
return D;
end);
InstallMethod(DigraphRemoveEdge,
"for a immutable digraph and two positive integers",
[IsImmutableDigraph, IsPosInt, IsPosInt],
{D, src, ran}
-> MakeImmutable(DigraphRemoveEdge(DigraphMutableCopy(D), src, ran)));
InstallMethod(DigraphRemoveEdge, "for a mutable digraph and a list",
[IsMutableDigraph, IsList],
function(D, edge)
if Length(edge) <> 2 then
ErrorNoReturn("the 2nd argument <edge> must be a list of length 2,");
fi;
return DigraphRemoveEdge(D, edge[1], edge[2]);
end);
InstallMethod(DigraphRemoveEdge,
"for a immutable digraph and a list",
[IsImmutableDigraph, IsList],
{D, edge} -> MakeImmutable(DigraphRemoveEdge(DigraphMutableCopy(D), edge)));
InstallMethod(DigraphRemoveEdges, "for a digraph and a list",
[IsMutableDigraph, IsList],
function(D, edges)
Perform(edges, edge -> DigraphRemoveEdge(D, edge));
return D;
end);
InstallMethod(DigraphRemoveEdges, "for an immutable digraph and a list",
[IsImmutableDigraph, IsList],
{D, edges} -> MakeImmutable(DigraphRemoveEdges(DigraphMutableCopy(D), edges)));
InstallMethod(DigraphReverseEdge,
"for a mutable digraph by out-neighbours and two positive integers",
[IsMutableDigraph and IsDigraphByOutNeighboursRep, IsPosInt, IsPosInt],
function(D, u, v)
local pos;
if IsMultiDigraph(D) then
ErrorNoReturn("the 1st argument <D> must be a digraph with no ",
"multiple edges,");
fi;
pos := Position(D!.OutNeighbours[u], v);
if pos = fail then
ErrorNoReturn("there is no edge from ", u, " to ", v,
" in the digraph <D> that is the 1st argument,");
elif u = v then
return D;
fi;
Remove(D!.OutNeighbours[u], pos);
Add(D!.OutNeighbours[v], u);
if Length(Positions(D!.OutNeighbours[v], u)) > 1 then
# output is a multidigraph
ClearDigraphEdgeLabels(D);
elif IsBound(D!.edgelabels)
and IsBound(D!.edgelabels[u])
and IsBound(D!.edgelabels[u][pos]) then
SetDigraphEdgeLabel(D, v, u, D!.edgelabels[u][pos]);
RemoveDigraphEdgeLabel(D, u, pos);
fi;
return D;
end);
InstallMethod(DigraphReverseEdge,
"for an immutable digraph, positive integer, and positive integer",
[IsImmutableDigraph, IsPosInt, IsPosInt],
{D, u, v} -> MakeImmutable(DigraphReverseEdge(DigraphMutableCopy(D), u, v)));
InstallMethod(DigraphReverseEdge, "for a mutable digraph and list",
[IsMutableDigraph, IsList],
function(D, e)
if Length(e) <> 2 then
ErrorNoReturn("the 2nd argument <e> must be a list of length 2,");
fi;
return DigraphReverseEdge(D, e[1], e[2]);
end);
InstallMethod(DigraphReverseEdge, "for an immutable digraph and a list",
[IsImmutableDigraph, IsList],
{D, e} -> MakeImmutable(DigraphReverseEdge(DigraphMutableCopy(D), e)));
InstallMethod(DigraphReverseEdges, "for a mutable digraph and a list",
[IsMutableDigraph, IsList],
function(D, E)
Perform(E, e -> DigraphReverseEdge(D, e));
return D;
end);
InstallMethod(DigraphReverseEdges, "for an immutable digraph and a list",
[IsImmutableDigraph, IsList],
{D, E} -> MakeImmutable(DigraphReverseEdges(DigraphMutableCopy(D), E)));
InstallMethod(DigraphClosure,
"for a mutable digraph by out-neighbours and a positive integer",
[IsMutableDigraph and IsDigraphByOutNeighboursRep, IsPosInt],
function(D, k)
local list, mat, deg, n, stop, i, j;
if not IsSymmetricDigraph(D) or DigraphHasLoops(D) or IsMultiDigraph(D) then
ErrorNoReturn("the 1st argument <D> must be a symmetric digraph with no ",
"loops, and no multiple edges,");
fi;
list := D!.OutNeighbours;
mat := BooleanAdjacencyMatrixMutableCopy(D);
deg := ShallowCopy(InDegrees(D));
n := DigraphNrVertices(D);
repeat
stop := true;
for i in [1 .. n] do
for j in [1 .. n] do
if j <> i and (not mat[i][j]) and deg[i] + deg[j] >= k then
Add(list[i], j);
Add(list[j], i);
mat[i][j] := true;
mat[j][i] := true;
deg[i] := deg[i] + 1;
deg[j] := deg[j] + 1;
stop := false;
fi;
od;
od;
until stop;
ClearDigraphEdgeLabels(D);
return D;
end);
InstallMethod(DigraphClosure,
"for an immutable digraph by out-neighbours and a positive integer",
[IsImmutableDigraph, IsPosInt],
{D, k} -> MakeImmutable(DigraphClosure(DigraphMutableCopy(D), k)));
DIGRAPHS_CheckContractEdgeDigraph := function(D, u, v)
if not IsDigraphEdge(D, u, v) then # Check if [u, v] is an edge in digraph D
ErrorNoReturn("expected an edge between the 2nd and 3rd arguments ",
"(vertices) ", u, " and ", v, " but found none");
elif IsMultiDigraph(D) then
ErrorNoReturn("The 1st argument (a digraph) must not satisfy ",
"IsMultiDigraph");
elif u = v then # edge should not be contracted if u = v
ErrorNoReturn("The 2nd argument <u> must not be equal to the 3rd ",
"argument <v>");
fi;
end;
InstallMethod(DigraphContractEdge,
"for a mutable digraph and two positive integers",
[IsMutableDigraph, IsPosInt, IsPosInt],
function(D, u, v)
local w, neighbours, transformations_to_edge, transformations_to_old_edges,
old_edge, new_edge, neighbour, t, vertices, edge_label;
DIGRAPHS_CheckContractEdgeDigraph(D, u, v);
# if (v, u) is an edge, disallow loops, so remove (v, u)
if IsDigraphEdge(D, v, u) then
DigraphRemoveEdge(D, v, u);
fi;
# remove the contracted edge
DigraphRemoveEdge(D, u, v);
# Find vertex neighbours of u and v to construct new incident edges of w
neighbours := [Immutable(InNeighboursOfVertex(D, u)),
Immutable(InNeighboursOfVertex(D, v)),
Immutable(OutNeighboursOfVertex(D, u)),
Immutable(OutNeighboursOfVertex(D, v))];
# immutable reference to old edge labels to add to any
# new transformed incident edges
DigraphAddVertex(D, [DigraphVertexLabel(D, u),
DigraphVertexLabel(D, v)]); # add vertex w
vertices := DigraphVertices(D);
w := Length(vertices); # w is the new vertex identifier
# Handle loops from edges u or w, with the same source / range
# No relevant edges are removed from D until vertices are removed
# So old edge labls are taken from D
if IsDigraphEdge(D, u, u) and IsDigraphEdge(D, v, v) then
DigraphAddEdge(D, w, w);
edge_label := [DigraphEdgeLabel(D, u, u), DigraphEdgeLabel(D, w, w)];
SetDigraphEdgeLabel(D, w, w, edge_label);
elif IsDigraphEdge(D, u, u) then
DigraphAddEdge(D, w, w);
edge_label := DigraphEdgeLabel(D, u, u);
SetDigraphEdgeLabel(D, w, w, edge_label);
elif IsDigraphEdge(D, v, v) then
DigraphAddEdge(D, w, w);
edge_label := DigraphEdgeLabel(D, v, v);
SetDigraphEdgeLabel(D, w, w, edge_label);
fi;
# translate edges based on neighbours
# transformation functions translating neighbours to their new edge
transformations_to_edge := [{x, y} -> [x, y],
{x, y} -> [x, y],
{x, y} -> [y, x],
{x, y} -> [y, x]];
# transformation functions translating neighbours
# to their original edge in D
transformations_to_old_edges := [e -> [e, u],
e -> [e, v],
e -> [u, e],
e -> [v, e]];
# Add translated new edges, and setup edge labels
for t in [1 .. Length(transformations_to_edge)] do
for neighbour in neighbours[t] do
new_edge := transformations_to_edge[t](neighbour, w);
old_edge := transformations_to_old_edges[t](neighbour);
edge_label := DigraphEdgeLabel(D, old_edge[1], old_edge[2]);
DigraphAddEdge(D, new_edge);
SetDigraphEdgeLabel(D, new_edge[1], new_edge[2], edge_label);
od;
od;
DigraphRemoveVertices(D, [u, v]); # remove the vertices of the
# contracted edge (and therefore,
# all its incident edges)
return D;
end);
InstallMethod(DigraphContractEdge,
"for an immutable digraph and two positive integers",
[IsImmutableDigraph, IsPosInt, IsPosInt],
function(D, u, v)
local w, neighbours, transformations_to_edge, transformations_to_old_edges,
existing_edges, edges_to_not_include, new_digraph, new_edge, old_edge,
neighbour, t, vertices, edge_label;
DIGRAPHS_CheckContractEdgeDigraph(D, u, v);
# Incident edges to [u, v] that will be transformed to be incident to w
edges_to_not_include := [];
existing_edges := []; # existing edges that should be re added
# contracted edge should not be added to the new digraph
new_digraph := NullDigraph(IsMutableDigraph, 0);
# if (v, u) is an edge, disallow loops, so remove (v, u)
if IsDigraphEdge(D, v, u) then
D := DigraphRemoveEdge(D, v, u);
fi;
D := DigraphRemoveEdge(D, u, v); # remove the edge to be contracted
DigraphAddVertices(new_digraph, DigraphVertexLabels(D));
# add vertex w with combined labels from u and v
DigraphAddVertex(new_digraph, [DigraphVertexLabel(D, u),
DigraphVertexLabel(D, v)]); # add vertex w
vertices := DigraphVertices(new_digraph);
w := Length(vertices); # w is the new vertex identifier
# Handle loops from edges u or w, with the same source / range
if IsDigraphEdge(D, u, u) and IsDigraphEdge(D, v, v) then
DigraphAddEdge(new_digraph, w, w);
SetDigraphEdgeLabel(new_digraph, w, w, [DigraphEdgeLabel(D, u, u),
DigraphEdgeLabel(D, v, v)]);
elif IsDigraphEdge(D, u, u) then
DigraphAddEdge(new_digraph, w, w);
SetDigraphEdgeLabel(new_digraph, w, w,
DigraphEdgeLabel(D, u, u));
elif IsDigraphEdge(D, v, v) then
DigraphAddEdge(new_digraph, w, w);
SetDigraphEdgeLabel(new_digraph, w, w, DigraphEdgeLabel(D, v, v));
fi;
# if there were loops in D at the vertices u or w, don't include these edges,
# but include a new loop at w
Append(edges_to_not_include, [[u, u], [v, v]]);
# Find vertex neighbours of u and v to construct new incident edges of w
neighbours := [InNeighboursOfVertex(D, u),
InNeighboursOfVertex(D, v),
OutNeighboursOfVertex(D, u),
OutNeighboursOfVertex(D, v)];
# translate edges based on neighbours
# transformation functions translating neighbours to their new edge
transformations_to_edge := [{x, y} -> [x, y], {x, y} -> [x, y],
{x, y} -> [y, x], {x, y} -> [y, x]];
# transformation functions translating neighbours to their original edge in D
transformations_to_old_edges := [e -> [e, u],
e -> [e, v],
e -> [u, e],
e -> [v, e]];
# remove edges that will be adjusted
for t in [1 .. Length(transformations_to_old_edges)] do
Append(edges_to_not_include, List(neighbours[t],
transformations_to_old_edges[t]));
od;
# Find edges that should be included, but remain the same
Sort(edges_to_not_include);
Append(existing_edges, ShallowCopy(DigraphEdges(D)));
Sort(existing_edges);
SubtractSet(existing_edges, edges_to_not_include);
# Add translated new edges, and setup edge labels
for t in [1 .. Length(transformations_to_edge)] do
for neighbour in neighbours[t] do
new_edge := transformations_to_edge[t](neighbour, w);
# old_edge is what the new transformed edge was previously
old_edge := transformations_to_old_edges[t](neighbour);
edge_label := DigraphEdgeLabel(D, old_edge[1], old_edge[2]);
new_digraph := DigraphAddEdge(new_digraph, new_edge);
SetDigraphEdgeLabel(new_digraph, new_edge[1], new_edge[2], edge_label);
od;
od;
# Add the existing edges that have not changed,
# and set their edge labels to that of the previous digraph
for new_edge in existing_edges do
new_digraph := DigraphAddEdge(new_digraph, new_edge);
SetDigraphEdgeLabel(new_digraph, new_edge[1], new_edge[2],
DigraphEdgeLabel(D, new_edge[1], new_edge[2]));
od;
# remove the vertices of the contracted edge
return MakeImmutable(DigraphRemoveVertices(new_digraph, [u, v]));
end);
InstallMethod(DigraphContractEdge,
"for a digraph and a dense list",
[IsDigraph, IsDenseList],
function(D, edge)
if Length(edge) <> 2 then
ErrorNoReturn("the 2nd argument <edge> must be a list of length 2");
fi;
return DigraphContractEdge(D, edge[1], edge[2]);
end);
#############################################################################
# 3. Ways of combining digraphs
#############################################################################
InstallGlobalFunction(DIGRAPHS_CombinationOperProcessArgs,
function(arg...)
local copy, i;
arg := ShallowCopy(arg[1]);
if IsMutableDigraph(arg[1]) then
for i in [2 .. Length(arg)] do
if IsIdenticalObj(arg[1], arg[i]) then
if not IsBound(copy) then
copy := OutNeighboursMutableCopy(arg[1]);
fi;
arg[i] := copy;
else
arg[i] := OutNeighbours(arg[i]);
fi;
od;
arg[1] := arg[1]!.OutNeighbours;
else
arg[1] := OutNeighboursMutableCopy(arg[1]);
for i in [2 .. Length(arg)] do
arg[i] := OutNeighbours(arg[i]);
od;
fi;
return arg;
end);
InstallGlobalFunction(DigraphDisjointUnion,
function(arg...)
local D, offset, n, i, j;
# Allow the possibility of supplying arguments in a list.
if Length(arg) = 1 and IsList(arg[1]) then
arg := arg[1];
fi;
if not IsList(arg) or IsEmpty(arg)
or not ForAll(arg, IsDigraphByOutNeighboursRep) then
ErrorNoReturn("the arguments must be digraphs by out-neighbours, or a ",
"single non-empty list of digraphs by out-neighbours,");
fi;
D := arg[1];
arg := DIGRAPHS_CombinationOperProcessArgs(arg);
offset := DigraphNrVertices(D);
for i in [2 .. Length(arg)] do
n := Length(arg[i]);
for j in [1 .. n] do
arg[1][offset + j] := ShallowCopy(arg[i][j]) + offset;
od;
offset := offset + n;
od;
if IsMutableDigraph(D) then
SetDigraphVertexLabels(D, DigraphVertices(D));
# This above stops D keeping its old vertex labels after being changed
ClearDigraphEdgeLabels(D);
return D;
fi;
return ConvertToImmutableDigraphNC(arg[1]);
end);
InstallGlobalFunction(DigraphJoin,
function(arg...)
local D, tot, offset, n, list, i, v;
# Allow the possibility of supplying arguments in a list.
if Length(arg) = 1 and IsList(arg[1]) then
arg := arg[1];
fi;
if not IsList(arg) or IsEmpty(arg)
or not ForAll(arg, IsDigraphByOutNeighboursRep) then
ErrorNoReturn("the arguments must be digraphs by out-neighbours, or a ",
"single list of digraphs by out-neighbours,");
fi;
D := arg[1];
tot := Sum(arg, DigraphNrVertices);
offset := DigraphNrVertices(D);
arg := DIGRAPHS_CombinationOperProcessArgs(arg);
for list in arg[1] do
Append(list, [offset + 1 .. tot]);
od;
for i in [2 .. Length(arg)] do
n := Length(arg[i]);
for v in [1 .. n] do
arg[1][v + offset] := Concatenation([1 .. offset],
arg[i][v] + offset,
[offset + n + 1 .. tot]);
od;
offset := offset + n;
od;
if IsMutableDigraph(D) then
ClearDigraphEdgeLabels(D);
return D;
fi;
return ConvertToImmutableDigraphNC(arg[1]);
end);
InstallGlobalFunction(DigraphEdgeUnion,
function(arg...)
local D, n, i, v;
# Allow the possibility of supplying arguments in a list.
if Length(arg) = 1 and IsList(arg[1]) then
arg := arg[1];
fi;
if not IsList(arg) or IsEmpty(arg)
or not ForAll(arg, IsDigraphByOutNeighboursRep) then
ErrorNoReturn("the arguments must be digraphs by out-neighbours, or a ",
"single list of digraphs by out-neighbours,");
fi;
D := arg[1];
n := Maximum(List(arg, DigraphNrVertices));
arg := DIGRAPHS_CombinationOperProcessArgs(arg);
if IsMutableDigraph(D) then
DigraphAddVertices(D, n - DigraphNrVertices(D));
else
Append(arg[1], List([1 .. n - Length(arg[1])], x -> []));
fi;
for i in [2 .. Length(arg)] do
for v in [1 .. Length(arg[i])] do
if not IsEmpty(arg[i][v]) then
Append(arg[1][v], arg[i][v]);
fi;
od;
od;
if IsMutableDigraph(D) then
ClearDigraphEdgeLabels(D);
ClearDigraphVertexLabels(D);
return D;
fi;
return ConvertToImmutableDigraphNC(arg[1]);
end);
InstallGlobalFunction(DigraphCartesianProduct,
function(arg...)
local D, n, i, j, proj, m, labs;
# Allow the possibility of supplying arguments in a list.
if Length(arg) = 1 and IsList(arg[1]) then
arg := arg[1];
fi;
if not IsList(arg) or IsEmpty(arg)
or not ForAll(arg, IsDigraphByOutNeighboursRep) then
ErrorNoReturn("the arguments must be digraphs by out-neighbours, or a ",
"single list of digraphs by out-neighbours,");
fi;
labs := List(Cartesian(Reversed(List(arg, DigraphVertexLabels))), Reversed);
D := arg[1];
arg := DIGRAPHS_CombinationOperProcessArgs(arg);
m := Product(List(arg, Length));
proj := [Transformation([1 .. m], x -> RemInt(x - 1, Length(arg[1])) + 1)];
for i in [2 .. Length(arg)] do
n := Length(arg[1]);
Add(proj, Transformation([1 .. m],
x -> RemInt(QuoInt(x - 1, n), Length(arg[i])) * n + 1));
for j in [2 .. Length(arg[i])] do
arg[1]{[1 + n * (j - 1) .. n * j]} := List([1 .. n],
x -> Concatenation(arg[1][x] + n * (j - 1),
x + n * (arg[i][j] - 1)));
od;
for j in [1 .. n] do
Append(arg[1][j], j + n * (arg[i][1] - 1));
od;
od;
if IsMutableDigraph(D) then
ClearDigraphEdgeLabels(D);
else
D := DigraphNC(arg[1]);
fi;
SetDigraphCartesianProductProjections(D, proj);
SetDigraphVertexLabels(D, labs);
return D;
end);
InstallGlobalFunction(DigraphDirectProduct,
function(arg...)
local D, n, i, j, proj, m, labs;
# Allow the possibility of supplying arguments in a list.
if Length(arg) = 1 and IsList(arg[1]) then
arg := arg[1];
fi;
if not IsList(arg) or IsEmpty(arg)
or not ForAll(arg, IsDigraphByOutNeighboursRep) then
ErrorNoReturn("the arguments must be digraphs by out-neighbours, or a ",
"single list of digraphs by out-neighbours,");
fi;
labs := List(Cartesian(Reversed(List(arg, DigraphVertexLabels))), Reversed);
D := arg[1];
arg := DIGRAPHS_CombinationOperProcessArgs(arg);
m := Product(List(arg, Length));
proj := [Transformation([1 .. m], x -> RemInt(x - 1, Length(arg[1])) + 1)];
for i in [2 .. Length(arg)] do
n := Length(arg[1]);
Add(proj, Transformation([1 .. m],
x -> RemInt(QuoInt(x - 1, n), Length(arg[i])) * n + 1));
for j in [2 .. Length(arg[i])] do
arg[1]{[1 + n * (j - 1) .. n * j]} := List([1 .. n],
x -> List(Cartesian(arg[1][x], n * (arg[i][j] - 1)), Sum));
od;
for j in [1 .. n] do
arg[1][j] := List(Cartesian(arg[1][j], n * (arg[i][1] - 1)), Sum);
od;
od;
if IsMutableDigraph(D) then
ClearDigraphEdgeLabels(D);
else
D := DigraphNC(arg[1]);
fi;
SetDigraphDirectProductProjections(D, proj);
SetDigraphVertexLabels(D, labs);
return D;
end);
InstallMethod(ModularProduct, "for a digraph and digraph",
[IsDigraph, IsDigraph],
function(D1, D2)
local edge_function;
edge_function := function(u, v, m, n, map) # gaplint: disable=W046
# neither m nor n is used, but can't replace them both with _
local w, x, connections;
connections := [];
for w in OutNeighbours(D1)[u] do
for x in OutNeighbours(D2)[v] do
if (u = w) = (v = x) then
Add(connections, map(w, x));
fi;
od;
od;
for w in OutNeighbours(DigraphDual(D1))[u] do
for x in OutNeighbours(DigraphDual(D2))[v] do
if (u = w) = (v = x) then
Add(connections, map(w, x));
fi;
od;
od;
return connections;
end;
return DIGRAPHS_GraphProduct(D1, D2, edge_function);
end);
InstallMethod(StrongProduct, "for a digraph and digraph",
[IsDigraph, IsDigraph],
function(D1, D2)
local edge_function;
edge_function := function(u, v, m, n, map) # gaplint: disable=W046
# neither m nor n is used, but can't replace them both with _
local w, x, connections;
connections := [];
for x in OutNeighbours(D2)[v] do
AddSet(connections, map(u, x));
for w in OutNeighbours(D1)[u] do
AddSet(connections, map(w, x));
od;
od;
for w in OutNeighbours(D1)[u] do
AddSet(connections, map(w, v));
od;
return connections;
end;
return DIGRAPHS_GraphProduct(D1, D2, edge_function);
end);
InstallMethod(ConormalProduct, "for a digraph and digraph",
[IsDigraph, IsDigraph],
function(D1, D2)
local edge_function;
edge_function := function(u, v, m, n, map)
local w, x, connections;
connections := [];
for w in OutNeighbours(D1)[u] do
for x in [1 .. n] do
AddSet(connections, map(w, x));
od;
od;
for x in OutNeighbours(D2)[v] do
for w in [1 .. m] do
AddSet(connections, map(w, x));
od;
od;
return connections;
end;
return DIGRAPHS_GraphProduct(D1, D2, edge_function);
end);
InstallMethod(HomomorphicProduct, "for a digraph and digraph",
[IsDigraph, IsDigraph],
function(D1, D2)
local edge_function;
edge_function := function(u, v, _, n, map)
local w, x, connections;
connections := [];
for x in [1 .. n] do
AddSet(connections, map(u, x));
od;
for w in OutNeighbours(D1)[u] do
for x in OutNeighbours(DigraphDual(D2))[v] do
AddSet(connections, map(w, x));
od;
od;
return connections;
end;
return DIGRAPHS_GraphProduct(D1, D2, edge_function);
end);
InstallMethod(LexicographicProduct, "for a digraph and digraph",
[IsDigraph, IsDigraph],
function(D1, D2)
local edge_function;
edge_function := function(u, v, _, n, map)
local w, x, connections;
connections := [];
for w in OutNeighbours(D1)[u] do
for x in [1 .. n] do
AddSet(connections, map(w, x));
od;
od;
for x in OutNeighbours(D2)[v] do
AddSet(connections, map(u, x));
od;
return connections;
end;
return DIGRAPHS_GraphProduct(D1, D2, edge_function);
end);
InstallMethod(DIGRAPHS_GraphProduct,
"for a digraph, a digraph, and a function",
[IsDigraph, IsDigraph, IsFunction],
function(D1, D2, edge_function)
local m, n, edges, u, v, map;
if IsMultiDigraph(D1) then
ErrorNoReturn(
"the 1st argument (a digraph) must not satisfy IsMultiDigraph");
elif IsMultiDigraph(D2) then
ErrorNoReturn(
"the 2nd argument (a digraph) must not satisfy IsMultiDigraph");
fi;
m := DigraphNrVertices(D1);
n := DigraphNrVertices(D2);
edges := EmptyPlist(m * n);
map := {a, b} -> (a - 1) * n + b;
for u in [1 .. m] do
for v in [1 .. n] do
edges[map(u, v)] := edge_function(u, v, m, n, map);
od;
od;
return Digraph(edges);
end);
###############################################################################
# 4. Actions
###############################################################################
InstallMethod(OnDigraphs, "for a digraph and a perm",
[IsDigraph, IsPerm],
function(D, p)
if ForAll(DigraphVertices(D), i -> i ^ p = i) then
return D;
elif ForAny(DigraphVertices(D), i -> i ^ p > DigraphNrVertices(D)) then
ErrorNoReturn("the 2nd argument <p> must be a permutation that permutes ",
"the vertices of the digraph <D> that is the 1st argument");
fi;
return OnDigraphsNC(D, p);
end);
InstallMethod(OnDigraphsNC,
"for a mutable digraph by out-neighbours and a perm",
[IsMutableDigraph and IsDigraphByOutNeighboursRep, IsPerm],
function(D, p)
local out;
if p = () then
return D;
fi;
out := D!.OutNeighbours;
out{DigraphVertices(D)} := Permuted(out, p);
Apply(out, x -> OnTuples(x, p));
ClearDigraphEdgeLabels(D);
return D;
end);
InstallMethod(OnDigraphsNC, "for a immutable digraph and a perm",
[IsImmutableDigraph, IsPerm],
function(D, p)
local out, permed;
if p = () then
return D;
fi;
out := D!.OutNeighbours;
permed := Permuted(out, p);
Apply(permed, x -> OnTuples(x, p));
return DigraphNC(IsImmutableDigraph, permed);
end);
InstallMethod(OnDigraphs,
"for a digraph and a transformation",
[IsDigraph, IsTransformation],
function(D, t)
if ForAll(DigraphVertices(D), i -> i ^ t = i) then
return D;
elif ForAny(DigraphVertices(D), i -> i ^ t > DigraphNrVertices(D)) then
ErrorNoReturn("the 2nd argument <t> must be a transformation that ",
"maps every vertex of the digraph <D> that is the 1st ",
"argument, to another vertex");
fi;
return OnDigraphsNC(D, t);
end);
InstallMethod(OnDigraphsNC,
"for a mutable digraph by out-neighbours and a transformation",
[IsMutableDigraph and IsDigraphByOutNeighboursRep, IsTransformation],
function(D, t)
local old, new, v;
if t = IdentityTransformation then
return D;
fi;
old := D!.OutNeighbours;
new := List(DigraphVertices(D), x -> []);
for v in DigraphVertices(D) do
Append(new[v ^ t], OnTuples(old[v], t));
od;
old{DigraphVertices(D)} := new;
ClearDigraphEdgeLabels(D);
return D;
end);
InstallMethod(OnDigraphsNC, "for a immutable digraph and a transformation",
[IsImmutableDigraph, IsTransformation],
function(D, t)
local old, new, v;
if t = IdentityTransformation then
return D;
fi;
old := D!.OutNeighbours;
new := List(DigraphVertices(D), x -> []);
for v in DigraphVertices(D) do
Append(new[v ^ t], OnTuples(old[v], t));
od;
return DigraphNC(IsImmutableDigraph, new);
end);
InstallMethod(OnTuplesDigraphs,
"for list of digraphs and a perm",
[IsDigraphCollection and IsHomogeneousList, IsPerm],
{L, p} -> List(L, D -> OnDigraphs(DigraphMutableCopyIfMutable(D), p)));
InstallMethod(OnSetsDigraphs,
"for a list of digraphs and a perm",
[IsDigraphCollection and IsHomogeneousList, IsPerm],
function(S, p)
if not IsSet(S) then
ErrorNoReturn("the first argument must be a set (a strictly sorted list)");
fi;
return Set(S, D -> OnDigraphs(DigraphMutableCopyIfMutable(D), p));
end);
# Not revising the following because multi-digraphs are being withdrawn in the
# near future.
InstallMethod(OnMultiDigraphs, "for a digraph, perm and perm",
[IsDigraph, IsPerm, IsPerm],
{D, perm1, perm2} -> OnMultiDigraphs(D, [perm1, perm2]));
InstallMethod(OnMultiDigraphs, "for a digraph and perm coll",
[IsDigraph, IsPermCollection],
function(D, perms)
if Length(perms) <> 2 then
ErrorNoReturn("the 2nd argument <perms> must be a pair of permutations,");
elif ForAny([1 .. DigraphNrEdges(D)],
i -> i ^ perms[2] > DigraphNrEdges(D)) then
ErrorNoReturn("the 2nd entry of the 2nd argument <perms> must ",
"permute the edges of the digraph <D> that is the 1st ",
"argument");
fi;
return OnDigraphs(D, perms[1]);
end);
# DomainForAction returns `true` instead of a proper domain here as a stopgap
# measure to ensure that the Orbit method of GAP uses a SparseHashTable instead
# of a DictionaryByPosition.
# As SparseIntKey for digraphs does not depend on the domain, and always
# returns DigraphHash, the actual returned value of DomainForAction does not
# matter as long as it returns something other than `fail`.
InstallMethod(DomainForAction, "for a digraph, list or collection and function",
[IsDigraph, IsListOrCollection, IsFunction],
ReturnTrue);
#############################################################################
# 5. Substructures and quotients
#############################################################################
InstallMethod(InducedSubdigraph,
"for a mutable digraph by out-neighbours and a homogeneous list",
[IsDigraphByOutNeighboursRep and IsMutableDigraph, IsHomogeneousList],
function(D, list)
local M, N, old, old_edl, new_edl, lookup, next, vv, w, old_labels, v, i;
M := Length(list);
N := DigraphNrVertices(D);
if M = 0 then
D!.OutNeighbours := [];
return D;
elif M = DigraphVertices(D) then
return D;
elif not IsDuplicateFree(list)
or ForAny(list, x -> not IsPosInt(x) or x > N) then
ErrorNoReturn("the 2nd argument <list> must be a duplicate-free ",
"subset of the vertices of the digraph <D> that is ",
"the 1st argument,");
fi;
old := D!.OutNeighbours;
old_edl := DigraphEdgeLabelsNC(D);
new_edl := List([1 .. M], x -> []);
lookup := ListWithIdenticalEntries(N, 0);
lookup{list} := [1 .. M];
for v in [1 .. M] do
next := [];
vv := list[v]; # old vertex corresponding to vertex v in new subdigraph
for i in [1 .. Length(old[vv])] do
w := old[vv][i];
if lookup[w] <> 0 then
Add(next, lookup[w]);
Add(new_edl[v], old_edl[vv][i]);
fi;
od;
old[vv] := next;
od;
old_labels := DigraphVertexLabels(D);
D!.OutNeighbours := old{list};
# Note that the following line means multidigraphs have wrong edge labels set.
SetDigraphEdgeLabelsNC(D, new_edl);
SetDigraphVertexLabels(D, old_labels{list});
return D;
end);
InstallMethod(InducedSubdigraph,
"for an immutable digraph and a homogeneous list",
[IsImmutableDigraph, IsHomogeneousList],
function(D, list)
if Length(list) = 0 then
return EmptyDigraph(0);
elif list = DigraphVertices(D) then
return D;
fi;
return MakeImmutable(InducedSubdigraph(DigraphMutableCopy(D), list));
end);
InstallMethod(QuotientDigraph,
"for a mutable digraph by out-neighbours and a homogeneous list",
[IsDigraphByOutNeighboursRep and IsMutableDigraph, IsHomogeneousList],
function(D, partition)
local N, M, check, lookup, new, new_vl, old, old_vl, x, i, u, v;
N := DigraphNrVertices(D);
M := Length(partition);
if N = 0 then
if IsEmpty(partition) then
return D;
fi;
ErrorNoReturn("the 2nd argument <partition> should be an empty list, which",
" is the only valid partition of the vertices of 1st",
" argument <D> because it has no vertices,");
elif M = 0 or not IsList(partition[1])
or IsEmpty(partition[1]) or not IsPosInt(partition[1][1]) then
ErrorNoReturn("the 2nd argument <partition> is not a valid ",
"partition of the vertices [1 .. ", N, "] of the 1st ",
"argument <D>,");
fi;
check := ListWithIdenticalEntries(DigraphNrVertices(D), false);
lookup := EmptyPlist(N);
for x in [1 .. Length(partition)] do
for i in partition[x] do
if i < 1 or i > N or check[i] then
ErrorNoReturn("the 2nd argument <partition> is not a valid ",
"partition of the vertices [1 .. ", N, "] of the 1st ",
"argument <D>,");
fi;
check[i] := true;
lookup[i] := x;
od;
od;
if not ForAll(check, IdFunc) then
ErrorNoReturn("the 2nd argument <partition> is not a valid ",
"partition of the vertices [1 .. ", N, "] of the 1st ",
"argument <D>,");
fi;
new := List([1 .. M], x -> []);
new_vl := List([1 .. M], x -> []);
old := D!.OutNeighbours;
old_vl := DigraphVertexLabels(D);
for u in DigraphVertices(D) do
Add(new_vl[lookup[u]], old_vl[u]);
for v in old[u] do
AddSet(new[lookup[u]], lookup[v]);
od;
od;
D!.OutNeighbours := new;
SetDigraphVertexLabels(D, new_vl);
ClearDigraphEdgeLabels(D);
return D;
end);
InstallMethod(QuotientDigraph,
"for an immutable digraph and a homogeneous list",
[IsImmutableDigraph, IsHomogeneousList],
{D, partition} ->
MakeImmutable(QuotientDigraph(DigraphMutableCopy(D), partition)));
#############################################################################
# 6. In and out degrees, neighbours, and edges of vertices
#############################################################################
InstallMethod(InNeighboursOfVertex, "for a digraph and a positive integer",
[IsDigraph, IsPosInt],
function(D, v)
if not v in DigraphVertices(D) then
ErrorNoReturn("the 2nd argument <v> is not a vertex of the ",
"1st argument <D>,");
fi;
return InNeighboursOfVertexNC(D, v);
end);
InstallMethod(InNeighboursOfVertexNC,
"for a digraph with in-neighbours and a positive integer",
[IsDigraph and HasInNeighbours, IsPosInt],
2, # to beat the next method for IsDigraphByOutNeighboursRep
{D, v} -> InNeighbours(D)[v]);
InstallMethod(InNeighboursOfVertexNC,
"for a digraph by out-neighbours and a positive integer",
[IsDigraphByOutNeighboursRep, IsPosInt],
function(D, v)
local inn, out, i, j;
inn := [];
out := OutNeighbours(D);
for i in [1 .. Length(out)] do
for j in [1 .. Length(out[i])] do
if out[i][j] = v then
Add(inn, i);
fi;
od;
od;
return inn;
end);
InstallMethod(OutNeighboursOfVertex,
"for a digraph by out-neighbours and a positive integer",
[IsDigraphByOutNeighboursRep, IsPosInt],
function(D, v)
if not v in DigraphVertices(D) then
ErrorNoReturn("the 2nd argument <v> is not a vertex of the ",
"1st argument <D>,");
fi;
return OutNeighboursOfVertexNC(D, v);
end);
InstallMethod(OutNeighboursOfVertexNC,
"for a digraph by out-neighbours and a positive integer",
[IsDigraphByOutNeighboursRep, IsPosInt],
{D, v} -> OutNeighbours(D)[v]);
InstallMethod(InDegreeOfVertex, "for a digraph and a positive integer",
[IsDigraph, IsPosInt],
function(D, v)
if not v in DigraphVertices(D) then
ErrorNoReturn("the 2nd argument <v> is not a vertex of the ",
"1st argument <D>,");
fi;
return InDegreeOfVertexNC(D, v);
end);
InstallMethod(InDegreeOfVertexNC,
"for a digraph with in-degrees and a positive integer",
[IsDigraph and HasInDegrees, IsPosInt], 4,
{D, v} -> InDegrees(D)[v]);
InstallMethod(InDegreeOfVertexNC,
"for a digraph with in-neighbours and a positive integer",
[IsDigraph and HasInNeighbours, IsPosInt],
2, # to beat the next method for IsDigraphByOutNeighboursRep
{D, v} -> Length(InNeighbours(D)[v]));
InstallMethod(InDegreeOfVertexNC, "for a digraph and a positive integer",
[IsDigraphByOutNeighboursRep, IsPosInt],
function(D, v)
local count, out, x, i;
count := 0;
out := OutNeighbours(D);
for x in out do
for i in x do
if i = v then
count := count + 1;
fi;
od;
od;
return count;
end);
InstallMethod(OutDegreeOfVertex, "for a digraph and a positive integer",
[IsDigraph, IsPosInt],
function(D, v)
if not v in DigraphVertices(D) then
ErrorNoReturn("the 2nd argument <v> is not a vertex of the ",
"1st argument <D>,");
fi;
return OutDegreeOfVertexNC(D, v);
end);
InstallMethod(OutDegreeOfVertexNC,
"for a digraph with out-degrees and a positive integer",
[IsDigraph and HasOutDegrees, IsPosInt],
2, # to beat the next method for IsDigraphByOutNeighboursRep
{D, v} -> OutDegrees(D)[v]);
InstallMethod(OutDegreeOfVertexNC,
"for a digraph by out-neighbours and a positive integer",
[IsDigraphByOutNeighboursRep, IsPosInt],
{D, v} -> Length(OutNeighbours(D)[v]));
InstallMethod(DigraphOutEdges,
"for a digraph by out-neighbours and a positive integer",
[IsDigraphByOutNeighboursRep, IsPosInt],
function(D, v)
if not v in DigraphVertices(D) then
ErrorNoReturn("the 2nd argument <v> is not a vertex of the ",
"1st argument <D>,");
fi;
return List(OutNeighboursOfVertex(D, v), x -> [v, x]);
end);
InstallMethod(DigraphInEdges, "for a digraph and a positive integer",
[IsDigraph, IsPosInt],
function(D, v)
if not v in DigraphVertices(D) then
ErrorNoReturn("the 2nd argument <v> is not a vertex of the ",
"1st argument <D>,");
fi;
return List(InNeighboursOfVertex(D, v), x -> [x, v]);
end);
#############################################################################
# 7. Copies of out/in-neighbours
#############################################################################
InstallMethod(OutNeighboursMutableCopy, "for a digraph by out-neighbours",
[IsDigraphByOutNeighboursRep],
D -> List(OutNeighbours(D), ShallowCopy));
InstallMethod(InNeighboursMutableCopy, "for a digraph", [IsDigraph],
D -> List(InNeighbours(D), ShallowCopy));
InstallMethod(AdjacencyMatrixMutableCopy, "for a digraph", [IsDigraph],
D -> List(AdjacencyMatrix(D), ShallowCopy));
InstallMethod(BooleanAdjacencyMatrixMutableCopy, "for a digraph", [IsDigraph],
D -> List(BooleanAdjacencyMatrix(D), ShallowCopy));
#############################################################################
# 8. IsSomething
#############################################################################
InstallMethod(IsDigraphEdge, "for a digraph and a list",
[IsDigraph, IsList],
function(D, edge)
if Length(edge) <> 2 or not IsPosInt(edge[1]) or not IsPosInt(edge[2]) then
return false;
fi;
return IsDigraphEdge(D, edge[1], edge[2]);
end);
InstallMethod(IsDigraphEdge, "for a digraph by out-neighbours, int, int",
[IsDigraphByOutNeighboursRep, IsInt, IsInt],
function(D, u, v)
local n;
n := DigraphNrVertices(D);
if u > n or v > n or u <= 0 or v <= 0 then
return false;
elif HasAdjacencyMatrix(D) then
return AdjacencyMatrix(D)[u][v] <> 0;
elif IsDigraphWithAdjacencyFunction(D) then
return DigraphAdjacencyFunction(D)(u, v);
fi;
return v in OutNeighboursOfVertex(D, u);
end);
InstallMethod(IsSubdigraph,
"for two digraphs by out-neighbours",
[IsDigraphByOutNeighboursRep, IsDigraphByOutNeighboursRep],
function(super, sub)
local n, x, y, i, j;
n := DigraphNrVertices(super);
if n <> DigraphNrVertices(sub)
or DigraphNrEdges(super) < DigraphNrEdges(sub) then
return false;
elif not IsMultiDigraph(sub) then
return ForAll(DigraphVertices(super), i ->
IsSubset(OutNeighboursOfVertex(super, i),
OutNeighboursOfVertex(sub, i)));
elif not IsMultiDigraph(super) then
return false;
fi;
x := [1 .. n];
y := [1 .. n];
for i in DigraphVertices(super) do
if OutDegreeOfVertex(super, i) < OutDegreeOfVertex(sub, i) then
return false;
fi;
x := x * 0;
y := y * 0;
for j in OutNeighboursOfVertex(super, i) do
x[j] := x[j] + 1;
od;
for j in OutNeighboursOfVertex(sub, i) do
y[j] := y[j] + 1;
od;
if not ForAll(DigraphVertices(super), k -> y[k] <= x[k]) then
return false;
fi;
od;
return true;
end);
InstallMethod(IsUndirectedSpanningForest, "for two digraphs",
[IsDigraph, IsDigraph],
function(super, sub)
local sym, comps1, comps2;
if not IsUndirectedForest(sub) then
return false;
fi;
sym := MaximalSymmetricSubdigraph(DigraphMutableCopyIfMutable(super));
if not IsSubdigraph(sym, sub) then
return false;
fi;
comps1 := DigraphConnectedComponents(sym).comps;
comps2 := DigraphConnectedComponents(sub).comps;
if Length(comps1) <> Length(comps2) then
return false;
fi;
return Set(comps1, SortedList) = Set(comps2, SortedList);
end);
InstallMethod(IsUndirectedSpanningTree, "for a digraph and a digraph",
[IsDigraph, IsDigraph],
function(super, sub)
super := DigraphMutableCopyIfMutable(super);
return IsConnectedDigraph(MaximalSymmetricSubdigraph(super))
and IsUndirectedSpanningForest(super, sub);
end);
# The following function performs almost all of the calculations necessary for
# IsMatching, IsPerfectMatching, and IsMaximalMatching, without duplicating work
BindGlobal("DIGRAPHS_Matching",
function(D, edges)
local seen, e;
Assert(1, IsDigraph(D));
Assert(1, IsHomogeneousList(edges));
if not IsDuplicateFreeList(edges)
or not ForAll(edges, e -> IsDigraphEdge(D, e)) then
return false;
fi;
seen := BlistList(DigraphVertices(D), []);
for e in edges do
if seen[e[1]] or seen[e[2]] then
return false;
fi;
seen[e[1]] := true;
seen[e[2]] := true;
od;
return seen;
end);
InstallMethod(IsMatching, "for a digraph and a list",
[IsDigraph, IsHomogeneousList],
{D, edges} -> DIGRAPHS_Matching(D, edges) <> false);
InstallMethod(IsPerfectMatching, "for a digraph and a list",
[IsDigraph, IsHomogeneousList],
function(D, edges)
local seen;
seen := DIGRAPHS_Matching(D, edges);
if seen = false then
return false;
fi;
return SizeBlist(seen) = DigraphNrVertices(D);
end);
InstallMethod(IsMaximumMatching, "for a digraph and a list",
[IsDigraph, IsHomogeneousList],
function(D, edges)
return IsMatching(D, edges)
and Length(edges) = Length(DigraphMaximumMatching(D));
end);
InstallMethod(IsMaximalMatching, "for a digraph and a list",
[IsDigraphByOutNeighboursRep, IsHomogeneousList],
function(D, edges)
local seen, nbs, i, j;
seen := DIGRAPHS_Matching(D, edges);
if seen = false then
return false;
fi;
nbs := OutNeighbours(D);
for i in DigraphVertices(D) do
if not seen[i] then
for j in nbs[i] do
if not seen[j] then
return false;
fi;
od;
fi;
od;
return true;
end);
#############################################################################
# 9. Connectivity
#############################################################################
InstallMethod(DigraphIsKing,
"for a digraph and two positive integers",
[IsDigraph, IsPosInt, IsPosInt],
function(D, v, k)
local layers;
if not v in DigraphVertices(D) then
ErrorNoReturn("the 2nd argument <v> is not a vertex of the ",
"1st argument <D>,");
elif not IsTournament(D) then
ErrorNoReturn("the 1st argument <D> must be a tournament,");
fi;
layers := DigraphLayers(D, v);
return ((Length(layers) <= k + 1) and (Union(layers) = DigraphVertices(D)));
end);
InstallMethod(DigraphFloydWarshall,
"for a digraph by out-neighbours, function, object, and object",
[IsDigraphByOutNeighboursRep, IsFunction, IsObject, IsObject],
function(D, func, nopath, edge)
local vertices, n, mat, out, i, j, k;
vertices := DigraphVertices(D);
n := DigraphNrVertices(D);
mat := EmptyPlist(n);
for i in vertices do
mat[i] := EmptyPlist(n);
for j in vertices do
mat[i][j] := nopath;
od;
od;
out := OutNeighbours(D);
for i in vertices do
for j in out[i] do
mat[i][j] := edge;
od;
od;
for k in vertices do
for i in vertices do
for j in vertices do
func(mat, i, j, k);
od;
od;
od;
return mat;
end);
InstallMethod(DigraphStronglyConnectedComponent,
"for a digraph and a positive integer",
[IsDigraph, IsPosInt],
function(D, v)
local scc;
if not v in DigraphVertices(D) then
ErrorNoReturn("the 2nd argument <v> is not a vertex of the ",
"1st argument <D>,");
fi;
# TODO check if strongly connected components are known and use them if they
# are and don't use them if they are not.
scc := DigraphStronglyConnectedComponents(D);
return scc.comps[scc.id[v]];
end);
InstallMethod(DigraphConnectedComponent, "for a digraph and a positive integer",
[IsDigraph, IsPosInt],
function(D, v)
local wcc;
if not v in DigraphVertices(D) then
ErrorNoReturn("the 2nd argument <v> is not a vertex of the ",
"1st argument <D>,");
fi;
wcc := DigraphConnectedComponents(D);
return wcc.comps[wcc.id[v]];
end);
InstallMethod(IsReachable, "for a digraph and two pos ints",
[IsDigraph, IsPosInt, IsPosInt],
function(D, u, v)
local verts, scc;
verts := DigraphVertices(D);
if not (u in verts and v in verts) then
ErrorNoReturn("the 2nd and 3rd arguments <u> and <v> must be ",
"vertices of the 1st argument <D>,");
elif IsDigraphEdge(D, u, v) then
return true;
elif HasDigraphStronglyConnectedComponents(D) then
# Glean information from SCC if we have it
scc := DigraphStronglyConnectedComponents(D);
return (u <> v and scc.id[u] = scc.id[v])
or (u = v and Length(scc.comps[scc.id[u]]) > 1);
fi;
return DigraphPath(D, u, v) <> fail;
end);
InstallMethod(DigraphPath, "for a digraph by out-neighbours and two pos ints",
[IsDigraphByOutNeighboursRep, IsPosInt, IsPosInt],
function(D, u, v)
local verts;
verts := DigraphVertices(D);
if not (u in verts and v in verts) then
ErrorNoReturn("the 2nd and 3rd arguments <u> and <v> must be ",
"vertices of the 1st argument <D>,");
elif IsDigraphEdge(D, u, v) then
return [[u, v], [Position(OutNeighboursOfVertex(D, u), v)]];
elif HasIsTransitiveDigraph(D) and IsTransitiveDigraph(D) then
# If it's a known transitive digraph, just check whether the edge exists
return fail;
# Glean information from WCC if we have it
elif HasDigraphConnectedComponents(D)
and DigraphConnectedComponents(D).id[u] <>
DigraphConnectedComponents(D).id[v] then
return fail;
fi;
return DIGRAPH_PATH(OutNeighbours(D), u, v);
end);
InstallMethod(IsDigraphPath, "for a digraph and list",
[IsDigraph, IsList],
function(D, digraph_path_output)
if Length(digraph_path_output) <> 2 then
DError(["the 2nd argument (a list) must have length 2, ",
"but found length {}"],
Length(digraph_path_output));
fi;
return IsDigraphPath(D, digraph_path_output[1], digraph_path_output[2]);
end);
InstallMethod(IsDigraphPath, "for a digraph, homog. list, and homog. list",
[IsDigraph, IsHomogeneousList, IsHomogeneousList],
function(D, nodes, edges)
local a, i;
if Length(nodes) <> Length(edges) + 1 then
DError(["the 2nd and 3rd arguments (lists) are incompatible, ",
"expected 3rd argument of length {}, got {}"],
Length(nodes) - 1,
Length(edges));
fi;
a := OutNeighbours(D);
for i in [1 .. Length(edges)] do
if nodes[i] > Length(a) or edges[i] > Length(a[nodes[i]])
or a[nodes[i]][edges[i]] <> nodes[i + 1] then
return false;
fi;
od;
return true;
end);
InstallMethod(DigraphShortestPath,
"for a digraph by out-neighbours and two pos ints",
[IsDigraphByOutNeighboursRep, IsPosInt, IsPosInt],
function(D, u, v)
local current, next, parent, distance, falselist, verts, nbs, path, edge,
n, a, b, i;
verts := DigraphVertices(D);
if not (u in verts and v in verts) then
ErrorNoReturn("the 2nd and 3rd arguments <u> and <v> must be ",
"vertices of the 1st argument <D>,");
fi;
if IsDigraphEdge(D, u, v) then
return [[u, v], [Position(OutNeighboursOfVertex(D, u), v)]];
elif HasIsTransitiveDigraph(D) and IsTransitiveDigraph(D) then
# If it's a known transitive digraph, just check whether the edge exists
return fail;
# Glean information from WCC if we have it
elif HasDigraphConnectedComponents(D)
and DigraphConnectedComponents(D).id[u] <>
DigraphConnectedComponents(D).id[v] then
return fail;
fi;
nbs := OutNeighbours(D);
distance := ListWithIdenticalEntries(Length(verts), -1);
# Setting up objects useful in the function.
parent := [];
current := [u];
edge := [];
next := BlistList([1 .. Length(verts)], []);
falselist := BlistList([1 .. Length(verts)], []);
n := 0;
while current <> [] do
n := n + 1;
for a in current do
for i in [1 .. Length(nbs[a])] do
b := nbs[a][i];
if distance[b] = -1 then
distance[b] := n;
next[b] := true;
parent[b] := a;
edge[b] := i;
fi;
if b = v then
path := [[], []];
# Finds the path
for i in [1 .. n] do
Add(path[1], b);
Add(path[2], edge[b]);
b := parent[b];
od;
Add(path[1], u); # Adds the starting vertex to the list of vertices
return [Reversed(path[1]), Reversed(path[2])];
fi;
od;
od;
current := ListBlist(verts, next);
IntersectBlist(next, falselist);
od;
return fail;
end);
BindGlobal("DIGRAPHS_DijkstraST",
function(digraph, source, target)
local dist, prev, queue, u, v, alt;
if not source in DigraphVertices(digraph) then
ErrorNoReturn("the 2nd argument <source> must be a vertex of the ",
"1st argument <digraph>");
elif target <> fail and not target in DigraphVertices(digraph) then
ErrorNoReturn("the 3rd argument <target> must be a vertex of the ",
"1st argument <digraph>");
fi;
dist := [];
prev := [];
queue := BinaryHeap({x, y} -> x[1] < y[1]);
for v in DigraphVertices(digraph) do
dist[v] := infinity;
prev[v] := -1;
od;
dist[source] := 0;
Push(queue, [0, source]);
while not IsEmpty(queue) do
u := Pop(queue);
u := u[2];
# TODO: this has a small performance impact for DigraphDijkstraS,
# but do we care?
if u = target then
return [dist, prev];
fi;
for v in OutNeighbours(digraph)[u] do
alt := dist[u] + DigraphEdgeLabel(digraph, u, v);
if alt < dist[v] then
dist[v] := alt;
prev[v] := u;
Push(queue, [dist[v], v]);
fi;
od;
od;
return [dist, prev];
end);
InstallMethod(DigraphDijkstra, "for a digraph, a vertex, and a vertex",
[IsDigraph, IsPosInt, IsPosInt], DIGRAPHS_DijkstraST);
InstallMethod(DigraphDijkstra, "for a digraph, and a vertex",
[IsDigraph, IsPosInt],
{digraph, source} -> DIGRAPHS_DijkstraST(digraph, source, fail));
InstallMethod(IteratorOfPaths,
"for a digraph by out-neighbours and two pos ints",
[IsDigraphByOutNeighboursRep, IsPosInt, IsPosInt],
function(D, u, v)
if not (u in DigraphVertices(D) and v in DigraphVertices(D)) then
ErrorNoReturn("the 2nd and 3rd arguments <u> and <v> must be ",
"vertices of the 1st argument <D>,");
fi;
return IteratorOfPathsNC(OutNeighbours(D), u, v);
end);
InstallMethod(IteratorOfPaths, "for a list and two pos ints",
[IsList, IsPosInt, IsPosInt],
function(out, u, v)
local n;
n := Length(out);
if not ForAll(out, x -> IsHomogeneousList(x)
and ForAll(x, y -> IsPosInt(y) and y <= n)) then
ErrorNoReturn("the 1st argument <out> must be a list of out-neighbours",
" of a digraph,");
elif not (u <= n and v <= n) then
ErrorNoReturn("the 2nd and 3rd arguments <u> and <v> must be vertices ",
"of the digraph defined by the 1st argument <out>,");
fi;
return IteratorOfPathsNC(out, u, v);
end);
InstallMethod(IteratorOfPathsNC, "for a list and two pos ints",
[IsList, IsPosInt, IsPosInt],
function(D, u, v)
local n, record;
n := Length(D);
# can assume that n > 0 since u and v are extant vertices of digraph
Assert(1, n > 0);
record := rec(adj := D,
start := u,
stop := v,
onpath := BlistList([1 .. n], []),
nbs := ListWithIdenticalEntries(n, 1));
record.NextIterator := function(iter)
local adj, path, ptr, nbs, level, stop, onpath, backtracked, j, k, current,
new, next, x;
adj := iter!.adj;
path := [iter!.start];
ptr := ListWithIdenticalEntries(n, 0);
nbs := iter!.nbs;
level := 1;
stop := iter!.stop;
onpath := iter!.onpath;
backtracked := false;
while true do
j := path[level];
k := nbs[j];
# Backtrack if vertex j is already in path, or it has no k^th neighbour
if (not ptr[j] = 0 or k > Length(adj[j])) then
if k > Length(adj[j]) then
nbs[j] := 1;
fi;
if k > Length(adj[j]) and onpath[j] then
ptr[j] := 0;
else
ptr[j] := 1;
fi;
level := level - 1;
backtracked := true;
if level = 0 then
# No more paths to be found
current := fail;
break;
fi;
# Backtrack and choose next available branch
Remove(path);
ptr[path[level]] := 0;
nbs[path[level]] := nbs[path[level]] + 1;
continue;
fi;
# Otherwise move into next available branch
# Check if new branch is a valid complete path
if adj[j][k] = stop then
current := [Concatenation(path, [adj[j][k]]), List(path, x -> nbs[x])];
nbs[j] := nbs[j] + 1;
# Everything in the path should keep its nbs
# but everything else should be back to 1
new := ListWithIdenticalEntries(n, 1);
for x in path do
onpath[x] := true;
new[x] := nbs[x];
od;
iter!.nbs := new;
iter!.onpath := onpath;
break;
fi;
ptr[j] := 2;
level := level + 1;
path[level] := adj[j][k];
# this is the troublesome line
if ptr[path[level]] = 0 and backtracked then
nbs[path[level]] := 1;
fi;
od;
if not IsBound(iter!.current) then
return current;
fi;
next := iter!.current;
iter!.current := current;
return next;
end;
record.current := record.NextIterator(record);
record.IsDoneIterator := function(iter)
if iter!.current = fail then
return true;
fi;
return false;
end;
record.ShallowCopy := function(iter)
return rec(adj := iter!.adj,
start := iter!.start,
stop := iter!.stop,
nbs := ShallowCopy(iter!.nbs),
onpath := ShallowCopy(iter!.onpath),
current := ShallowCopy(iter!.current));
end;
return IteratorByFunctions(record);
end);
InstallMethod(DigraphLongestDistanceFromVertex, "for a digraph and a pos int",
[IsDigraphByOutNeighboursRep, IsPosInt],
function(D, v)
local dist;
if not v in DigraphVertices(D) then
ErrorNoReturn("the 2nd argument <v> must be a vertex of the 1st ",
"argument <D>,");
fi;
dist := DIGRAPH_LONGEST_DIST_VERTEX(OutNeighbours(D), v);
if dist = -2 then
return infinity;
fi;
return dist;
end);
InstallMethod(DigraphRandomWalk,
"for a digraph, a pos int and a non-negative int",
[IsDigraph, IsPosInt, IsInt],
function(D, v, t)
local vertices, edge_indices, i, neighbours, index;
# Check input
if v > DigraphNrVertices(D) then
ErrorNoReturn("the 2nd argument <v> must be ",
"a vertex of the 1st argument <D>,");
elif t < 0 then
ErrorNoReturn("the 3rd argument <t> must be a non-negative int,");
fi;
# Prepare output lists
vertices := [v];
edge_indices := [];
# Iterate to desired length
for i in [1 .. t] do
neighbours := OutNeighboursOfVertex(D, v);
if IsEmpty(neighbours) then
break; # Sink: path ends here
fi;
# Follow a random edge
index := Random(1, Length(neighbours));
v := neighbours[index];
vertices[i + 1] := v;
edge_indices[i] := index;
od;
# Format matches that of DigraphPath
return [vertices, edge_indices];
end);
InstallMethod(DigraphLayers, "for a digraph, and a positive integer",
[IsDigraph, IsPosInt],
function(D, v)
local layers, gens, sch, trace, rep, word, orbs, layers_with_orbnums,
layers_of_v, i, x;
# TODO: make use of known distances matrix
if v > DigraphNrVertices(D) then
ErrorNoReturn("the 2nd argument <v> must be a vertex of the 1st ",
"argument <D>,");
fi;
layers := DIGRAPHS_Layers(D);
if IsBound(layers[v]) then
return layers[v];
elif HasDigraphGroup(D) then
gens := GeneratorsOfGroup(DigraphGroup(D));
sch := DigraphSchreierVector(D);
trace := DIGRAPHS_TraceSchreierVector(gens, sch, v);
rep := DigraphOrbitReps(D)[trace.representative];
word := DIGRAPHS_EvaluateWord(gens, trace.word);
if rep <> v then
layers[v] := List(DigraphLayers(D, rep),
x -> OnTuples(x, word));
return layers[v];
fi;
orbs := DIGRAPHS_Orbits(DigraphStabilizer(D, v),
DigraphVertices(D)).orbits;
else
rep := v;
orbs := List(DigraphVertices(D), x -> [x]);
fi;
# from now on rep = v
layers_with_orbnums := DIGRAPH_ConnectivityDataForVertex(D, v).layers;
layers_of_v := [[v]];
for i in [2 .. Length(layers_with_orbnums)] do
Add(layers_of_v, []);
for x in layers_with_orbnums[i] do
Append(layers_of_v[i], orbs[x]);
od;
od;
layers[v] := layers_of_v;
return layers[v];
end);
InstallMethod(DIGRAPHS_Layers, "for a digraph", [IsDigraph],
D -> EmptyPlist(0));
InstallMethod(DigraphDistanceSet,
"for a digraph, a vertex, and a non-negative integers",
[IsDigraph, IsPosInt, IsInt],
function(D, vertex, distance)
if vertex > DigraphNrVertices(D) then
ErrorNoReturn("the 2nd argument <vertex> must be a vertex of the ",
"digraph,");
elif distance < 0 then
ErrorNoReturn("the 3rd argument <distance> must be a non-negative ",
"integer,");
fi;
return DigraphDistanceSet(D, vertex, [distance]);
end);
InstallMethod(DigraphDistanceSet,
"for a digraph, a vertex, and a list of non-negative integers",
[IsDigraph, IsPosInt, IsList],
function(D, vertex, distances)
local layers;
if vertex > DigraphNrVertices(D) then
ErrorNoReturn("the 2nd argument <vertex> must be a vertex of ",
"the digraph,");
elif not ForAll(distances, x -> IsInt(x) and x >= 0) then
ErrorNoReturn("the 3rd argument <distances> must be a list of ",
"non-negative integers,");
fi;
--> --------------------
--> maximum size reached
--> --------------------
[ Dauer der Verarbeitung: 0.12 Sekunden
(vorverarbeitet)
]
|
2026-03-28
|