Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
54 changes: 53 additions & 1 deletion doc/weights.xml
Original file line number Diff line number Diff line change
Expand Up @@ -69,4 +69,56 @@ gap> EdgeWeights(g);
]]></Example>
</Description>
</ManSection>
<#/GAPDoc>
<#/GAPDoc>

<#GAPDoc Label="EdgeWeightedDigraphTotalWeight">
<ManSection>
<Attr Name="EdgeWeightedDigraphTotalWeight" Arg="digraph"/>
<Returns>An integer, float or rational.</Returns>
<Description>
If <A>digraph</A> is a digraph with edge weights, then this attribute
returns the sum of the weights of its edges.<P/>

&MUTABLE_RECOMPUTED_ATTR;

See <Ref Attr="EdgeWeights"/>.
<Example><![CDATA[
gap> D := EdgeWeightedDigraph([[2], [1], [1, 2]],
> [[12], [5], [6, 9]]);
<immutable digraph with 3 vertices, 4 edges>
gap> EdgeWeightedDigraphTotalWeight(D);
32]]></Example>
</Description>
</ManSection>
<#/GAPDoc>

<#GAPDoc Label="EdgeWeightedDigraphMinimumSpanningTree">
<ManSection>
<Attr Name="EdgeWeightedDigraphMinimumSpanningTree" Arg="digraph"/>
<Returns>A digraph.</Returns>
<Description>
If <A>digraph</A> is a connected digraph with edge weights, then this
attribute returns a digraph which is a minimum spanning tree of
<A>digraph</A>.<P/>

A <E>spanning tree</E> of a digraph is a subdigraph with the same
vertices but a subset of its edges that form an undirected tree. It is
<E>minimum</E> if it has the smallest possible total weight for a
spanning tree of that digraph.<P/>

&MUTABLE_RECOMPUTED_ATTR;

See <Ref Attr="EdgeWeights"/>,
<Ref Attr="EdgeWeightedDigraphTotalWeight"/> and
<Ref Prop="IsConnectedDigraph"/>.
<Example><![CDATA[
gap> D := EdgeWeightedDigraph([[2], [1], [1, 2]],
> [[12], [5], [6, 9]]);
<immutable digraph with 3 vertices, 4 edges>
gap> T := EdgeWeightedDigraphMinimumSpanningTree(D);
<immutable digraph with 3 vertices, 2 edges>
gap> EdgeWeights(T);
[ [ ], [ 5 ], [ 6 ] ]]]></Example>
</Description>
</ManSection>
<#/GAPDoc>
2 changes: 2 additions & 0 deletions doc/z-chap5.xml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
<Section><Heading>Edge Weights</Heading>
<#Include Label="EdgeWeights">
<#Include Label="EdgeWeightedDigraph">
<#Include Label="EdgeWeightedDigraphTotalWeight">
<#Include Label="EdgeWeightedDigraphMinimumSpanningTree">
</Section>

<Section><Heading>Orders</Heading>
Expand Down
7 changes: 6 additions & 1 deletion gap/weights.gd
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@
DeclareAttribute("EdgeWeights", IsDigraph);
DeclareGlobalFunction("EdgeWeightedDigraph");
DeclareProperty("IsNegativeEdgeWeightedDigraph", IsDigraph and HasEdgeWeights);
DeclareAttribute("EdgeWeightedDigraphTotalWeight", IsDigraph and HasEdgeWeights);

# 2. Edge Weight Copies
DeclareOperation("EdgeWeightsMutableCopy", [IsDigraph and HasEdgeWeights]);
DeclareOperation("EdgeWeightsMutableCopy", [IsDigraph and HasEdgeWeights]);

# 3. Minimum Spanning Trees
DeclareAttribute("EdgeWeightedDigraphMinimumSpanningTree",
IsDigraph and HasEdgeWeights);
85 changes: 83 additions & 2 deletions gap/weights.gi
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ function(digraph, weights)
return digraph;
end);

InstallMethod(IsNegativeEdgeWeightedDigraph, "for an edge weighted digraph",
InstallMethod(IsNegativeEdgeWeightedDigraph, "for a digraph with edge weights",
[IsDigraph and HasEdgeWeights],
function(digraph)
local weights, u, w;
Expand All @@ -84,10 +84,91 @@ function(digraph)
return false;
end);

InstallMethod(EdgeWeightedDigraphTotalWeight,
"for a digraph with edge weights",
[IsDigraph and HasEdgeWeights],
D -> Sum(EdgeWeights(D), Sum));

#############################################################################
# 2. Copies of edge weights
#############################################################################

InstallMethod(EdgeWeightsMutableCopy, "for a digraph with edge weights",
[IsDigraph and HasEdgeWeights],
D -> List(EdgeWeights(D), ShallowCopy));
D -> List(EdgeWeights(D), ShallowCopy));

#############################################################################
# 3. Minimum Spanning Trees
#############################################################################

InstallMethod(EdgeWeightedDigraphMinimumSpanningTree,
"for a digraph with edge weights",
[IsDigraph and HasEdgeWeights],
function(digraph)
local weights, numberOfVertices, edgeList, u, outNeighbours, idx, v, w, mst,
mstWeights, partition, i, nrEdges, total, node, x, y, out;

# check graph is connected
if not IsConnectedDigraph(digraph) then
ErrorNoReturn("the argument <digraph> must be a connected digraph,");
fi;

weights := EdgeWeights(digraph);

# create a list of edges containing u-v
# w: the weight of the edge
# u: the start vertex
# v: the finishing vertex of that edge
numberOfVertices := DigraphNrVertices(digraph);
edgeList := [];
for u in DigraphVertices(digraph) do
outNeighbours := OutNeighboursOfVertex(digraph, u);
for idx in [1 .. Size(outNeighbours)] do
v := outNeighbours[idx]; # the out neighbour
w := weights[u][idx]; # the weight to the out neighbour
Add(edgeList, [w, u, v]);
od;
od;

# sort edge weights by their weight
StableSortBy(edgeList, x -> x[1]);

mst := EmptyPlist(numberOfVertices);
mstWeights := EmptyPlist(numberOfVertices);

partition := PartitionDS(IsPartitionDS, numberOfVertices);

for v in [1 .. numberOfVertices] do
Add(mst, []);
Add(mstWeights, []);
od;

i := 1;
nrEdges := 0;
total := 0;
while nrEdges < numberOfVertices - 1 do
node := edgeList[i];

w := node[1];
u := node[2];
v := node[3];

i := i + 1;

x := Representative(partition, u);
y := Representative(partition, v);

# if cycle doesn't exist
if x <> y then
Add(mst[u], v);
Add(mstWeights[u], w);
nrEdges := nrEdges + 1;
total := total + w;
Unite(partition, x, y);
fi;
od;

out := EdgeWeightedDigraph(mst, mstWeights);
SetEdgeWeightedDigraphTotalWeight(out, total);
return out;
end);
54 changes: 53 additions & 1 deletion tst/standard/weights.tst
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,11 @@ gap> DIGRAPHS_StartTest();
gap> d := EdgeWeightedDigraph([[2], []], [[5], []]);
<immutable digraph with 2 vertices, 1 edge>

# create with Digraph
# create edge weighted digraph
gap> d := EdgeWeightedDigraph(Digraph([[2], []]), [[5], []]);
<immutable digraph with 2 vertices, 1 edge>
gap> EdgeWeightedDigraphTotalWeight(d);
5

# weight not valid
gap> d := EdgeWeightedDigraph([[2], []], [["a"], []]);
Expand Down Expand Up @@ -85,6 +87,56 @@ gap> d := EdgeWeightedDigraph([[2], [1]], [[-5], [10]]);
gap> IsNegativeEdgeWeightedDigraph(d);
true

# not connnected digraph
gap> d := EdgeWeightedDigraph([[1], [2]], [[5], [10]]);
<immutable digraph with 2 vertices, 2 edges>
gap> EdgeWeightedDigraphMinimumSpanningTree(d);
Error, the argument <digraph> must be a connected digraph,

# digraph with one node
gap> d := EdgeWeightedDigraph([[]], [[]]);
<immutable empty digraph with 1 vertex>
gap> tree := EdgeWeightedDigraphMinimumSpanningTree(d);
<immutable empty digraph with 1 vertex>
gap> EdgeWeightedDigraphTotalWeight(tree);
0

# digraph with loop
gap> d := EdgeWeightedDigraph([[1]], [[5]]);
<immutable digraph with 1 vertex, 1 edge>
gap> EdgeWeightedDigraphMinimumSpanningTree(d);
<immutable empty digraph with 1 vertex>

# digraph with cycle
gap> d := EdgeWeightedDigraph([[2], [3], [1]], [[5], [10], [15]]);
<immutable digraph with 3 vertices, 3 edges>
gap> tree := EdgeWeightedDigraphMinimumSpanningTree(d);
<immutable digraph with 3 vertices, 2 edges>
gap> EdgeWeightedDigraphTotalWeight(tree);
15

# digraph with negative edge
gap> d := EdgeWeightedDigraph([[2], []], [[-5], []]);
<immutable digraph with 2 vertices, 1 edge>
gap> EdgeWeightedDigraphMinimumSpanningTree(d);
<immutable digraph with 2 vertices, 1 edge>

# digraph with negative cycle
gap> d := EdgeWeightedDigraph([[2], [3], [1]], [[-5], [-10], [-15]]);
<immutable digraph with 3 vertices, 3 edges>
gap> EdgeWeightedDigraphMinimumSpanningTree(d);
<immutable digraph with 3 vertices, 2 edges>

# digraph with parallel edges
gap> d := EdgeWeightedDigraph([[2, 2, 2], [1]], [[10, 5, 15], [7]]);
<immutable multidigraph with 2 vertices, 4 edges>
gap> EdgeWeightedDigraphMinimumSpanningTree(d);
<immutable digraph with 2 vertices, 1 edge>

# DIGRAPHS_UnbindVariables
gap> Unbind(d);
gap> Unbind(tree);

#
gap> DIGRAPHS_StopTest();
gap> STOP_TEST("Digraphs package: standard/weights.tst", 0);
6 changes: 6 additions & 0 deletions tst/testinstall.tst
Original file line number Diff line number Diff line change
Expand Up @@ -411,10 +411,16 @@ gap> String(D);
"DigraphFromDigraph6String(\"&CECG\")"
gap> String(CycleDigraph(4));
"CycleDigraph(4)"

# Edge-weighted digraphs
gap> d := EdgeWeightedDigraph([[2], [1]], [[5], [10]]);
<immutable digraph with 2 vertices, 2 edges>
gap> EdgeWeights(d);
[ [ 5 ], [ 10 ] ]
gap> EdgeWeightedDigraphTotalWeight(d);
15
gap> EdgeWeightedDigraphMinimumSpanningTree(d);
<immutable digraph with 2 vertices, 1 edge>

# Issue 617: bug in DigraphRemoveEdge, wasn't removing edge labels
gap> D := DigraphByEdges(IsMutableDigraph, [[1, 2], [2, 3], [3, 4], [4, 1], [1, 1]]);;
Expand Down