-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmincostflow.pyx
135 lines (122 loc) · 5.35 KB
/
mincostflow.pyx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
from ortools.graph import pywrapgraph
import numpy as np
cimport numpy as cnp
cnp.import_array()
def mincostflow(cnp.ndarray supply, cnp.ndarray demand, cnp.ndarray cost, cnp.ndarray scaled_cost):
"""
mincostflow
Parameters
----------
supply : cnp.ndarray
An array representing the supply at each node.
demand : cnp.ndarray
An array representing the demand at each node.
cost : cnp.ndarray
A 2D array representing the cost of transporting goods between nodes.
scaled_cost : cnp.ndarray
A 2D array representing the scaled cost of transporting goods between nodes.
'Scaled' means that the range of values is made smaller to avoid numerical issues.
Large costs that represent infeasible connections have been made much smaller.
All other costs have been multiplied by 1000. And will be multiplied again by 1000.
Returns
-------
None
"""
cdef int n = supply.size
cdef int m = demand.size
cdef int tot = max(supply.sum(), demand.sum())
cdef cnp.ndarray pens = cost < 1e17
cdef int pen_sum = pens.sum()
cdef int[:,:] true_penalty = (pens * 1).astype(np.intc)
scaled_cost = scaled_cost * 1000
return _mincostflow(supply.astype(np.intc), demand.astype(np.intc), cost, scaled_cost.astype(int), n, m, tot, true_penalty, pen_sum)
def maxflow_mincost(cnp.ndarray supply, cnp.ndarray demand, cnp.ndarray cost, cnp.ndarray scaled_cost):
"""
maxflow_mincost
Parameters
----------
supply : cnp.ndarray
An array representing the supply at each node.
demand : cnp.ndarray
An array representing the demand at each node.
cost : cnp.ndarray
A 2D array representing the cost of transporting goods between nodes.
scaled_cost : cnp.ndarray
A 2D array representing the scaled cost of transporting goods between nodes.
'Scaled' means that the range of values is made smaller to avoid numerical issues.
Large costs that represent infeasible connections have been made much smaller.
All other costs have been multiplied by 1000. And will be multiplied again by 1000.
Returns
-------
None
"""
cdef int n = supply.size
cdef int m = demand.size
cdef int tot = max(supply.sum(), demand.sum())
cdef cnp.ndarray pens = cost < 1e17
cdef int pen_sum = pens.sum()
cdef int[:,:] true_penalty = (pens * 1).astype(np.intc)
scaled_cost = scaled_cost * 1000
return _maxflow(supply.astype(np.intc), demand.astype(np.intc), cost, scaled_cost.astype(int), n, m, tot, true_penalty, pen_sum)
cdef cnp.ndarray _mincostflow(int[:] supply, int[:] demand, double[:, :] cost, long[:,:] scaled_cost, int n, int m, int tot, int[:,:] true_penalty, int pen_sum):
# Instantiate a SimpleMinCostFlow solver.
min_cost_flow = pywrapgraph.SimpleMinCostFlow(reserve_num_nodes=n+m, reserve_num_arcs=pen_sum)
# Add each arc.
cdef int i
cdef int j
cdef int[:,:] arcs = np.ones((n, m), dtype=np.intc) * -1
for i in range(n):
for j in range(m):
if true_penalty[i, j] > 0:
arcs[i, j] = min_cost_flow.AddArcWithCapacityAndUnitCost(
i, n+j, tot, scaled_cost[i, j])
# Add node supplies.
for i in range(n):
min_cost_flow.SetNodeSupply(i, supply[i])
for j in range(m):
min_cost_flow.SetNodeSupply(j+n, -demand[j])
# Find the minimum cost flow
cdef int result = min_cost_flow.Solve()
# Retrieve the optimal flows
cdef cnp.ndarray[int, ndim=2] plan = np.zeros((n, m), dtype=np.intc)
cdef int[:,:] plan_view = plan
if result == min_cost_flow.OPTIMAL:
for i in range(n):
for j in range(m):
if arcs[i, j] >= 0:
plan_view[i, j] = min_cost_flow.Flow(arcs[i, j])
return plan
else:
print(f'Problems!!! result = {result}')
raise RuntimeError('There was an issue with the mincostflow solver.')
cdef cnp.ndarray _maxflow(int[:] supply, int[:] demand, double[:, :] cost, long[:,:] scaled_cost, int n, int m, int tot, int[:,:] true_penalty, int pen_sum):
# Instantiate a SimpleMinCostFlow solver.
min_cost_flow = pywrapgraph.SimpleMinCostFlow(reserve_num_nodes=n+m, reserve_num_arcs=pen_sum)
# Add each arc.
cdef int i
cdef int j
cdef int[:,:] arcs = np.ones((n, m), dtype=np.intc) * -1
for i in range(n):
for j in range(m):
if true_penalty[i, j] > 0:
arcs[i, j] = min_cost_flow.AddArcWithCapacityAndUnitCost(
i, n+j, tot, scaled_cost[i, j])
# Add node supplies.
for i in range(n):
min_cost_flow.SetNodeSupply(i, supply[i])
for j in range(m):
min_cost_flow.SetNodeSupply(j+n, -demand[j])
# Find the minimum cost flow
cdef int result = min_cost_flow.SolveMaxFlowWithMinCost()
# Retrieve the optimal flows
cdef cnp.ndarray[int, ndim=2] plan = np.zeros((n, m), dtype=np.intc)
cdef int[:,:] plan_view = plan
if result == min_cost_flow.OPTIMAL:
for i in range(n):
for j in range(m):
if arcs[i, j] >= 0:
plan_view[i, j] = min_cost_flow.Flow(arcs[i, j])
return plan
else:
print(f'Problems!!! result = {result}')
raise RuntimeError('There was an issue with the mincostflow solver.')