最小費用流問題
Published:
By nobCategory: Posts
Tags: 数理最適化 Python-MIP Python
燃料費高騰のため輸送費を削減したい
工場で製造した製品を需要地に近い倉庫に車両で運搬する。燃料費高騰のため輸送費を削減したい。
- 工場の出荷可能量
supply = [39, 40, 41]
- 倉庫の需要量
demand = [38, 20, 22, 36]
- 輸送費の単価(1商品あたり)
cost = [
[19, 12, 13, 18],
[14, 12, 18, 12],
[15, 16, 15, 10],
]
モデルの作成
from mip import Model, OptimizationStatus, minimize, xsum
model = Model(solver_name="cbc")
model.verbose = 0
product = model.add_var_tensor((len(supply), len(demand)), "product")
model.objective = minimize(xsum((cost * product).flat))
for f in range(len(supply)):
model += xsum(product[f]) <= supply[f]
for w in range(len(demand)):
model += xsum(product[:, w]) == demand[w]
model.optimize()
if model.status == OptimizationStatus.OPTIMAL:
print(model.objective_value)
print(product.astype(float, subok=False))
else:
print(model.status)
1419.0
[[ 0. 17. 22. 0.]
[37. 3. 0. 0.]
[ 1. 0. 0. 36.]]
グラフの表示
import matplotlib.pyplot as plt
import networkx as nx
import numpy as np
def draw_graph(supply, demand, solution):
fig, ax = plt.subplots(1, 1)
g = nx.DiGraph()
pos = {}
for i, s in enumerate(supply):
node_name = "F" + str(i + 1)
g.add_node(node_name, amount=s)
pos[node_name] = (0, i + 0.5)
for i, d in enumerate(demand):
node_name = "W" + str(i + 1)
g.add_node(node_name, amount=d)
pos[node_name] = (1, i)
for f, w in zip(*np.where(solution > 0)):
g.add_edge("F" + str(f + 1), "W" + str(w + 1), product=solution[f, w])
node_size = list(
map(
lambda a: 50 * a,
nx.get_node_attributes(g, "amount").values(),
)
)
nx.draw_networkx_nodes(
g,
pos,
node_size=node_size,
node_color="white",
edgecolors="black",
linewidths=3,
ax=ax,
)
edge_width = list(
map(
lambda p: 0.5 * p,
nx.get_edge_attributes(g, "product").values(),
)
)
nx.draw_networkx_edges(
g,
pos,
width=edge_width,
node_size=1000,
edge_color="black",
arrowsize=40,
ax=ax,
)
nx.draw_networkx_labels(g, pos, font_size=15)
edge_label = nx.get_edge_attributes(g, "product")
nx.draw_networkx_edge_labels(
g, pos, edge_label, label_pos=0.2, font_size=12,
ax=ax,
)
plt.axis("off")
plt.tight_layout()
plt.show()
draw_graph(supply, demand, product.astype(float, subok=False))
積載率が低い!
車両の容積(capacity)を確認したところ
capacity = 5
だった。
F3 -> W1 は1個、F3 -> W4 は36個運搬するので、必要便数は
-
F3 -> W1: math.ceil(1 / 5) = 1
-
F3 -> W4: math.ceil(36 / 5) = 8
出荷先倉庫によって車両の積載率が大きく異なる。積載率が80%以上になるようにしたい。
1便あたりの単価は積載量にはあまり影響を受けないと考えてモデルを再作成する。
- 工場の出荷可能量
supply = [39, 40, 45]
- 倉庫の需要量
demand = [38, 20, 22, 36]
- 輸送費の単価(1便あたり)
cost_per_transport = np.ceil(np.array(cost) * capacity * 0.9)
cost_per_transport
array([[86., 54., 59., 81.],
[63., 54., 81., 54.],
[68., 72., 68., 45.]])
モデルの再作成
model = Model(solver_name="cbc")
model.verbose = 0
product = model.add_var_tensor((len(supply), len(demand)), "product")
transport = model.add_var_tensor(product.shape, "transport", var_type="I")
model.objective = minimize(xsum((cost_per_transport * transport).flat))
for f in range(len(supply)):
for w in range(len(demand)):
model += transport[f, w] >= product[f, w] / capacity
model += product[f, w] >= 0.8 * transport[f, w] * capacity
for f in range(len(supply)):
model += xsum(product[f]) <= supply[f]
for w in range(len(demand)):
model += xsum(product[:, w]) == demand[w]
model.optimize()
if model.status == OptimizationStatus.OPTIMAL:
print(model.objective_value)
print(product.astype(float, subok=False))
print(transport.astype(float, subok=False))
else:
print(model.status)
1380.0
[[ 0. 15. 22. 0.]
[34. 5. 0. 0.]
[ 4. 0. 0. 36.]]
[[0. 3. 5. 0.]
[7. 1. 0. 0.]
[1. 0. 0. 8.]]
積載率の計算
p = product.astype(float, subok=False)
t = transport.astype(float, subok=False)
t = np.where(t > 0, t, 1)
p / t / capacity
array([[0. , 1. , 0.88 , 0. ],
[0.97142857, 1. , 0. , 0. ],
[0.8 , 0. , 0. , 0.9 ]])
グラフの表示
draw_graph(supply, demand, product.astype(float, subok=False))
データの出典
- ビープラウド, PyQチーム, 斎藤努 著. Pythonで学ぶ数理最適化による問題解決入門, 翔泳社, 2024.4. 978-4-7981-7269-9.