Skip to content
This repository was archived by the owner on Oct 22, 2021. It is now read-only.

Commit 894d467

Browse files
authored
mincost_flow (#2)
* added mincost func * added default solver * added tests for mincost, export function * default sink and source, circulation tests * changed LP solver, added doc, conservation tests
1 parent 3b4f9bb commit 894d467

File tree

5 files changed

+198
-3
lines changed

5 files changed

+198
-3
lines changed

REQUIRE

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
11
julia 0.6
22
LightGraphs
33
SimpleTraits
4+
JuMP
5+
MathProgBase
6+
GLPK
7+
GLPKMathProgInterface

src/LightGraphsFlows.jl

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@ const lg = LightGraphs
55
using SimpleTraits: @traitfn, @traitimpl
66
import SimpleTraits
77

8+
import JuMP
9+
using MathProgBase.SolverInterface: AbstractMathProgSolver
10+
using GLPKMathProgInterface: GLPKSolverLP
11+
812
import Base: getindex, size, transpose, ctranspose
913

1014
include("maximum_flow.jl")
@@ -15,10 +19,10 @@ include("push_relabel.jl")
1519
include("multiroute_flow.jl")
1620
include("kishimoto.jl")
1721
include("ext_multiroute_flow.jl")
22+
include("mincost.jl")
1823

19-
# flow
2024
export
2125
maximum_flow, EdmondsKarpAlgorithm, DinicAlgorithm, BoykovKolmogorovAlgorithm, PushRelabelAlgorithm,
22-
multiroute_flow, KishimotoAlgorithm, ExtendedMultirouteFlowAlgorithm
26+
multiroute_flow, KishimotoAlgorithm, ExtendedMultirouteFlowAlgorithm, mincost_flow
2327

2428
end

src/mincost.jl

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
"""
2+
mincost_flow(graph, capacity, demand, cost[, source][, sink][, solver])
3+
4+
Find a flow satisfying the `demand` and `capacity` constraints for each edge
5+
while minimizing the `sum(cost.*flow)`.
6+
7+
- If `source` and `sink` are specified, they are allowed a net flow production,
8+
consumption respectively. All other nodes must respect the flow conservation
9+
property.
10+
11+
- The problem can be seen as a linear programming problem and uses a LP
12+
solver under the hood. The default solver is GLPKSolverLP.
13+
14+
Returns a flow matrix, flow[i,j] corresponds to the flow on the (i,j) arc.
15+
16+
### Usage Example:
17+
18+
```jldoctest
19+
julia> g = lg.DiGraph(6) # Create a flow-graph
20+
julia> lg.add_edge!(g, 5, 1)
21+
julia> lg.add_edge!(g, 5, 2)
22+
julia> lg.add_edge!(g, 3, 6)
23+
julia> lg.add_edge!(g, 4, 6)
24+
julia> lg.add_edge!(g, 1, 3)
25+
julia> lg.add_edge!(g, 1, 4)
26+
julia> lg.add_edge!(g, 2, 3)
27+
julia> lg.add_edge!(g, 2, 4)
28+
julia> w = zeros(6,6)
29+
julia> w[1,3] = 10.
30+
julia> w[1,4] = 5.
31+
julia> w[2,3] = 2.
32+
julia> w[2,4] = 2.
33+
julia> # v2 -> sink have demand of one
34+
julia> demand = spzeros(6,6)
35+
julia> demand[3,6] = 1
36+
julia> demand[4,6] = 1
37+
julia> capacity = ones(6,6)
38+
julia> flow = mincost_flow(g, capacity, demand, w, 5, 6)
39+
```
40+
"""
41+
function mincost_flow(g::lg.DiGraph,
42+
capacity::AbstractMatrix,
43+
demand::AbstractMatrix,
44+
cost::AbstractMatrix,
45+
source::Int = -1, # if source and/or sink omitted or not in nodes, circulation problem
46+
sink::Int = -1,
47+
solver::AbstractMathProgSolver = GLPKSolverLP())
48+
m = JuMP.Model(solver = solver)
49+
flow = JuMP.@variable(m, x[1:lg.nv(g),1:lg.nv(g)] >= 0.)
50+
JuMP.@constraint(m,
51+
[i=1:lg.nv(g),j=1:lg.nv(g)],
52+
flow[i,j] - demand[i,j] >= 0.
53+
)
54+
JuMP.@constraint(m,
55+
[i=1:lg.nv(g),j=1:lg.nv(g)],
56+
capacity[i,j] - flow[i,j] >= 0.
57+
)
58+
for n1 in 1:lg.nv(g)
59+
for n2 in 1:lg.nv(g)
60+
if !lg.has_edge(g,n1,n2)
61+
JuMP.@constraint(m, flow[n1,n2] <= 0.)
62+
end
63+
end
64+
end
65+
# flow conservation constraints
66+
for node in 1:lg.nv(g)
67+
if node != source && node != sink
68+
JuMP.@constraint(m,
69+
sum(flow[node,j] for j in 1:lg.nv(g)) -
70+
sum(flow[i,node] for i in 1:lg.nv(g)) == 0
71+
)
72+
end
73+
end
74+
# source is a net flow producer
75+
if source 1:lg.nv(g)
76+
JuMP.@constraint(m,
77+
sum(flow[source,j] for j in 1:lg.nv(g)) -
78+
sum(flow[i,source] for i in 1:lg.nv(g)) >= 0
79+
)
80+
end
81+
# source is a net flow consumer
82+
if sink 1:lg.nv(g)
83+
JuMP.@constraint(m,
84+
sum(flow[sink,j] for j in 1:lg.nv(g)) -
85+
sum(flow[i,sink] for i in 1:lg.nv(g)) <= 0
86+
)
87+
end
88+
JuMP.@objective(m, :Min, sum(flow[i,j]*cost[i,j] for i=1:lg.nv(g),j=1:lg.nv(g)))
89+
solution = JuMP.solve(m)
90+
return JuMP.getvalue(flow)
91+
end

test/mincost.jl

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
@testset "Minimum-cost flow" begin
2+
3+
# bipartite oriented + source & sink
4+
# source 5, sink 6, v1 {1, 2} v2 {3, 4}
5+
g = lg.DiGraph(6)
6+
lg.add_edge!(g, 5, 1)
7+
lg.add_edge!(g, 5, 2)
8+
lg.add_edge!(g, 3, 6)
9+
lg.add_edge!(g, 4, 6)
10+
lg.add_edge!(g, 1, 3)
11+
lg.add_edge!(g, 1, 4)
12+
lg.add_edge!(g, 2, 3)
13+
lg.add_edge!(g, 2, 4)
14+
w = zeros(6,6)
15+
w[1,3] = 10.
16+
w[1,4] = 5.
17+
w[2,3] = 2.
18+
w[2,4] = 2.
19+
# v2 -> sink have demand of one
20+
demand = spzeros(6,6)
21+
demand[3,6] = 1
22+
demand[4,6] = 1
23+
capacity = ones(6,6)
24+
25+
flow = mincost_flow(g, capacity, demand, w, 5, 6)
26+
@test flow[5,1] == 1
27+
@test flow[5,2] == 1
28+
@test flow[3,6] == 1
29+
@test flow[4,6] == 1
30+
@test flow[1,4] == 1
31+
@test flow[2,4] == 0
32+
@test flow[2,3] == 1
33+
@test flow[1,3] == 0
34+
@test sum(diag(flow)) == 0
35+
36+
# flow conservation property
37+
for n1 = 1:4
38+
@test sum(flow[n1,:]) sum(flow[:,n1])
39+
end
40+
41+
# no demand => null flow
42+
d2 = spzeros(6,6)
43+
flow = mincost_flow(g, capacity, d2, w, 5, 6)
44+
for idx in 1:6
45+
for jdx in 1:6
46+
@test flow[idx,jdx] 0.0
47+
end
48+
end
49+
50+
# graph without sink or source
51+
g = lg.DiGraph(6)
52+
# create circuit
53+
for s in 1:5
54+
lg.add_edge!(g, s, s+1)
55+
end
56+
lg.add_edge!(g, 6, 1)
57+
lg.add_edge!(g, 2, 5) # shortcut
58+
59+
capacity = ones(6,6)
60+
demand = spzeros(6,6)
61+
demand[1,2] = 1
62+
costs = ones(6,6)
63+
flow = mincost_flow(g, capacity, demand, costs, -1, -1)
64+
active_flows = [(1,2), (2,5), (5,6),(6,1)]
65+
for s in 1:6
66+
for t in 1:6
67+
if (s,t) in active_flows
68+
@test flow[s,t] 1.
69+
else
70+
@test flow[s,t] 0.
71+
end
72+
end
73+
end
74+
# flow conservation property
75+
for n1 = 1:6
76+
@test sum(flow[n1,:]) sum(flow[:,n1])
77+
end
78+
# higher short-circuit cost
79+
costs[2,5] = 10.
80+
flow = mincost_flow(g, capacity, demand, costs, -1, -1)
81+
active_flows = [(1,2),(2,3),(3,4),(4,5),(5,6),(6,1)]
82+
for s in 1:6
83+
for t in 1:6
84+
if (s,t) in active_flows
85+
@test flow[s,t] 1.
86+
else
87+
@test flow[s,t] 0.
88+
end
89+
end
90+
end
91+
# flow conservation property
92+
for n1 = 1:6
93+
@test sum(flow[n1,:]) sum(flow[:,n1])
94+
end
95+
end

test/runtests.jl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ for t in [
1515
"boykov_kolmogorov",
1616
"push_relabel",
1717
"maximum_flow",
18-
"multiroute_flow"]
18+
"multiroute_flow",
19+
"mincost"]
1920
tp = joinpath(testdir, "$(t).jl")
2021
include(tp)
2122
end

0 commit comments

Comments
 (0)