diff --git a/doc/weights.xml b/doc/weights.xml
index e866705ae..79e4a4917 100644
--- a/doc/weights.xml
+++ b/doc/weights.xml
@@ -219,3 +219,55 @@ fail]]>
<#/GAPDoc>
+
+<#GAPDoc Label="DigraphMaximumFlow">
+
+
+ A list of lists of integers.
+
+ If digraph is an edge-weighted digraph with vertices start and
+ destination, this returns a record representing the maximum flow from
+ start to destination in the digraph.
+
+ A flow is a function from the weighted edges of digraph to the
+ positive real numbers, such that:
+
+
+ Each edge's flow is no more than its weight;
+
+
+ For each vertex other than start and destination, the sum
+ of flows for all incoming edges is equal to the sum of flows for all
+ outgoing edges;
+
+
+ The sum of flows of edges leaving start is equal to the sum of
+ flows of edges entering destination (this sum is denoted
+ M).
+
+
+ A maximum flow is a flow that maximises the value of M.
+
+ The flow is represented as a list of lists where each entry is a number
+ representing the flow on the edge in the corresponding position in
+ OutNeighbours(digraph).
+ Note that the value M of the flow can be found with
+ Sum(DigraphMaximumFlow(digraph, start,
+ destination)[start]).
+
+ This attribute is computed by an implementation of the push–relabel maximum
+ flow algorithm, which has time complexity O(v^2 e) where v is
+ the number of vertices of the digraph, and e is the number of
+ edges.
+
+ See .
+ g := EdgeWeightedDigraph([[2, 2], [3], []], [[3, 2], [1], []]);
+
+gap> flow := DigraphMaximumFlow(g, 1, 3);
+[ [ 1, 0 ], [ 1 ], [ ] ]
+gap> Sum(flow[1]);
+1]]>
+
+
+<#/GAPDoc>
diff --git a/doc/z-chap5.xml b/doc/z-chap5.xml
index c12da5479..14283ce39 100644
--- a/doc/z-chap5.xml
+++ b/doc/z-chap5.xml
@@ -31,6 +31,7 @@
<#Include Label="EdgeWeightedDigraphMinimumSpanningTree">
<#Include Label="EdgeWeightedDigraphShortestPaths">
<#Include Label="EdgeWeightedDigraphShortestPath">
+ <#Include Label="DigraphMaximumFlow">
Orders
diff --git a/gap/weights.gd b/gap/weights.gd
index e2e4d530d..c6136b504 100644
--- a/gap/weights.gd
+++ b/gap/weights.gd
@@ -34,3 +34,7 @@ DeclareGlobalFunction("DIGRAPHS_Edge_Weighted_Johnson");
DeclareGlobalFunction("DIGRAPHS_Edge_Weighted_FloydWarshall");
DeclareGlobalFunction("DIGRAPHS_Edge_Weighted_Bellman_Ford");
DeclareGlobalFunction("DIGRAPHS_Edge_Weighted_Dijkstra");
+
+# 5. Maximum Flow
+DeclareOperation("DigraphMaximumFlow",
+ [IsDigraph and HasEdgeWeights, IsPosInt, IsPosInt]);
diff --git a/gap/weights.gi b/gap/weights.gi
index 3cb37429e..2d62620ed 100644
--- a/gap/weights.gi
+++ b/gap/weights.gi
@@ -645,3 +645,138 @@ function(digraph, source)
return rec(distances := distances, parents := parents, edges := edges);
end);
+
+#############################################################################
+# 5. Maximum Flow
+#############################################################################
+
+InstallMethod(DigraphMaximumFlow, "for an edge weighted digraph",
+[IsDigraph and HasEdgeWeights, IsPosInt, IsPosInt],
+function(digraph, start, destination)
+ local push, relabel, discharge, weights, vertices, nrVertices, outs, capacity,
+ flowMatrix, seen, height, excess, queue, u, outNeighbours, i, v, w,
+ flows, delta;
+
+ # Push flow from u to v
+ push := function(u, v)
+ local delta;
+
+ delta := Minimum(excess[u], capacity[u][v] - flowMatrix[u][v]);
+
+ flowMatrix[u][v] := flowMatrix[u][v] + delta;
+ flowMatrix[v][u] := flowMatrix[v][u] - delta;
+ excess[u] := excess[u] - delta;
+ excess[v] := excess[v] + delta;
+
+ if excess[v] = delta then
+ PlistDequePushBack(queue, v);
+ fi;
+ end;
+
+ # Decide height of u, which must be above any vertex it can flow to
+ relabel := function(u)
+ local d, v;
+ d := infinity;
+ for v in vertices do
+ if capacity[u][v] - flowMatrix[u][v] > 0 then
+ d := Minimum(d, height[v]);
+ fi;
+ od;
+ if d < infinity then
+ height[u] := d + 1;
+ fi;
+ end;
+
+ # Push all excess flow from u to other vertices
+ discharge := function(u)
+ local v;
+ while excess[u] > 0 do
+ if seen[u] <= nrVertices then
+ v := seen[u];
+ if capacity[u][v] - flowMatrix[u][v] > 0
+ and height[u] > height[v] then
+ push(u, v);
+ else
+ seen[u] := seen[u] + 1;
+ fi;
+ else
+ relabel(u);
+ seen[u] := 1;
+ fi;
+ od;
+ end;
+
+ # Extract important data
+ weights := EdgeWeights(digraph);
+ vertices := DigraphVertices(digraph);
+ nrVertices := Length(vertices);
+ outs := OutNeighbors(digraph);
+
+ # Check input
+ if not start in vertices then
+ ErrorNoReturn(" must be a vertex of ,");
+ elif not destination in vertices then
+ ErrorNoReturn(" must be a vertex of ,");
+ fi;
+
+ # Setup data structures
+ capacity := EmptyPlist(nrVertices);
+ flowMatrix := EmptyPlist(nrVertices);
+ seen := EmptyPlist(nrVertices);
+ height := EmptyPlist(nrVertices);
+ excess := EmptyPlist(nrVertices);
+ queue := PlistDeque();
+ for u in vertices do
+ capacity[u] := ListWithIdenticalEntries(nrVertices, 0);
+ flowMatrix[u] := ListWithIdenticalEntries(nrVertices, 0);
+ seen[u] := 1; # highest vertex to which we've pushed flow from u
+ height[u] := 0; # proximity to start (further is lower)
+ excess[u] := 0; # flow coming into u that isn't yet leaving u
+ if u <> start and u <> destination then
+ PlistDequePushBack(queue, u);
+ fi;
+ od;
+
+ # Compute total capacity from each u to v
+ for u in vertices do
+ outNeighbours := outs[u];
+ for i in [1 .. Size(outNeighbours)] do
+ v := outNeighbours[i]; # the out neighbour
+ w := weights[u][i]; # the weight to the out neighbour
+ capacity[u][v] := capacity[u][v] + w;
+ od;
+ od;
+
+ # start vertex is "top", with unlimited flow coming out
+ height[start] := nrVertices;
+ excess[start] := infinity;
+
+ # Push flow from start to the other vertices
+ for v in vertices do
+ if v <> start then
+ push(start, v);
+ fi;
+ od;
+
+ # Discharge from vertices until there are none left to do
+ while not IsEmpty(queue) do
+ u := PlistDequePopFront(queue);
+ if u <> start and u <> destination then
+ discharge(u);
+ fi;
+ od;
+
+ # Convert to output format: a list of lists with same shape as weights
+ flows := List(weights, l -> EmptyPlist(Length(l)));
+ for u in vertices do
+ for i in [1 .. Length(outs[u])] do
+ v := outs[u][i];
+ delta := Minimum(flowMatrix[u][v], weights[u][i]);
+ delta := Maximum(0, delta);
+ flowMatrix[u][v] := flowMatrix[u][v] - delta;
+ flows[u][i] := delta;
+ od;
+ od;
+
+ return flows;
+end);
diff --git a/tst/standard/weights.tst b/tst/standard/weights.tst
index ed97c4f9e..9d54142f5 100644
--- a/tst/standard/weights.tst
+++ b/tst/standard/weights.tst
@@ -1,4 +1,3 @@
-
#############################################################################
##
#W standard/weights.tst
@@ -15,10 +14,14 @@ gap> LoadPackage("digraphs", false);;
#
gap> DIGRAPHS_StartTest();
-gap> d := EdgeWeightedDigraph([[2], []], [[5], []]);
-
+
+#############################################################################
+# 1. Edge Weights
+#############################################################################
# create edge weighted digraph
+gap> d := EdgeWeightedDigraph([[2], []], [[5], []]);
+
gap> d := EdgeWeightedDigraph(Digraph([[2], []]), [[5], []]);
gap> EdgeWeightedDigraphTotalWeight(d);
@@ -59,6 +62,10 @@ Error, the sizes of the out neighbours and weights for vertex 1 must be equal,
gap> d := EdgeWeightedDigraph([[2], []], [[5, 10], []]);
Error, the sizes of the out neighbours and weights for vertex 1 must be equal,
+#############################################################################
+# 2. Copies of edge weights
+#############################################################################
+
# changing edge weights mutable copy
gap> d := EdgeWeightedDigraph([[2], [1]], [[5], [10]]);
@@ -87,6 +94,10 @@ gap> d := EdgeWeightedDigraph([[2], [1]], [[-5], [10]]);
gap> IsNegativeEdgeWeightedDigraph(d);
true
+#############################################################################
+# 3. Minimum Spanning Trees
+#############################################################################
+
# not connnected digraph
gap> d := EdgeWeightedDigraph([[1], [2]], [[5], [10]]);
@@ -133,6 +144,10 @@ gap> d := EdgeWeightedDigraph([[2, 2, 2], [1]], [[10, 5, 15], [7]]);
gap> EdgeWeightedDigraphMinimumSpanningTree(d);
+#############################################################################
+# 4. Shortest Path
+#############################################################################
+
# Shortest paths: one node
gap> d := EdgeWeightedDigraph([[]], [[]]);
@@ -275,6 +290,84 @@ Error, the 2nd argument must be a vertex of the 1st argument EdgeWeightedDigraphShortestPath(d, 1, 3);
[ [ 1, 2, 3 ], [ 1, 1 ] ]
+#############################################################################
+# 5. Maximum Flow
+#############################################################################
+
+# Maximum flow: empty digraphs
+gap> d := EdgeWeightedDigraph([], []);
+
+gap> DigraphMaximumFlow(d, 1, 1);
+Error, must be a vertex of ,
+
+# Maximum flow: single vertex (also empty digraphs)
+gap> d := EdgeWeightedDigraph([[]], [[]]);
+
+gap> DigraphMaximumFlow(d, 1, 1);
+[ [ ] ]
+
+# Maximum flow: source = dest
+gap> d := EdgeWeightedDigraph([[2], []], [[5], []]);
+
+gap> DigraphMaximumFlow(d, 1, 1);
+[ [ 0 ], [ ] ]
+
+# Maximum flow: has loop
+gap> d := EdgeWeightedDigraph([[1, 2], []], [[5, 10], []]);
+
+gap> DigraphMaximumFlow(d, 1, 2);
+[ [ 0, 10 ], [ ] ]
+
+# Maximum flow: invalid source
+gap> d := EdgeWeightedDigraph([[1, 2], []], [[5, 10], []]);
+
+gap> DigraphMaximumFlow(d, 5, 2);
+Error, must be a vertex of ,
+
+# Maximum flow: invalid sink
+gap> d := EdgeWeightedDigraph([[1, 2], []], [[5, 10], []]);
+
+gap> DigraphMaximumFlow(d, 1, 5);
+Error, must be a vertex of ,
+
+# Maximum flow: sink not reachable
+gap> d := EdgeWeightedDigraph([[1], []], [[5], []]);
+
+gap> DigraphMaximumFlow(d, 1, 2);
+[ [ 0 ], [ ] ]
+
+# Maximum flow: source has in neighbours
+gap> d := EdgeWeightedDigraph([[2], [3], []], [[5], [10], []]);
+
+gap> DigraphMaximumFlow(d, 2, 3);
+[ [ 0 ], [ 10 ], [ ] ]
+
+# Maximum flow: sink has out neighbours
+gap> d := EdgeWeightedDigraph([[2], [3], [2]], [[5], [10], [7]]);
+
+gap> DigraphMaximumFlow(d, 2, 3);
+[ [ 0 ], [ 10 ], [ 0 ] ]
+
+# Maximum flow: cycle
+gap> d := EdgeWeightedDigraph([[2], [3], [1]], [[5], [10], [7]]);
+
+gap> DigraphMaximumFlow(d, 1, 3);
+[ [ 5 ], [ 5 ], [ 0 ] ]
+
+# Maximum flow: example from Wikipedia
+gap> gr := EdgeWeightedDigraph([[3, 4], [], [2, 4], [2]],
+> [[10, 5], [], [5, 15], [10]]);;
+gap> DigraphMaximumFlow(gr, 1, 2);
+[ [ 10, 5 ], [ ], [ 5, 5 ], [ 10 ] ]
+gap> DigraphMaximumFlow(gr, 3, 2);
+[ [ 0, 0 ], [ ], [ 5, 10 ], [ 10 ] ]
+
+# Maximum flow: example from Wikipedia article on Push-label maximum flow
+gap> gr := EdgeWeightedDigraph([[2], [3, 6], [4], [1, 6], [1, 3], []],
+> [[12], [3, 7], [10], [5, 10], [15, 4], []]);;
+gap> DigraphMaximumFlow(gr, 5, 6);
+[ [ 10 ], [ 3, 7 ], [ 7 ], [ 0, 7 ], [ 10, 4 ], [ ] ]
+
# DIGRAPHS_UnbindVariables
gap> Unbind(d);
gap> Unbind(tree);