はじめに
我々は通常の生活の中でも様々なリスクに直面している.そして,リスクを回避した行動をとり,リスクに備えて準備をし,万が一に備えて保険をかける.リスクを上手に管理することによって,良い人生を送ることができるからだ. サプライ・チェインも,我々の人生と同様に,様々なリスクに直面している.リスクを上手に管理することは, サプライ・チェインの効率化と同様に重要なのだ. リスクを無視して効率だけを目標としたサプライ・チェインは, 継続性・永続性の意味で弱く, 脆弱なサプライ・チェインであると考えられる.
近年のサプライ・チェインのグローバル化に伴い,リスクは増加しており, 世界で発生する様々なリスクが,サプライ・チェインに大きな影響を与えるようになってきている. さらに我が国は,地震や津波や台風などの自然災害の危険性が大きく,リスクを無視したサプライ・チェインはギャンブルに他ならない.
サプライ・チェインの効率性だけでなく,リスクを考慮して柔軟かつ頑強なサプライ・チェインを設計,管理, 運用するための理論体系が,サプライ・チェイン・リスク管理(supply chain risk management)である.
以下では,サプライ・チェイン・リスク管理のために有用な最適化モデルを構築する.
例題
簡単な例題を準備する.
小さな例題
MITの修論 (2011):Enhancing service providers reliability by mitigating supply chain risk : the case of telecommunication networks (https://dspace.mit.edu/handle/1721.1/90782)
Disruption Risk Mitigation in Supply Chains - The Risk Exposure Index Revisited (OR 2019) Gao et al. より
G = nx.DiGraph() #plant graph
BOM=nx.DiGraph() #BOM: bill of materials
G.add_edges_from( [(1,0),(2,0)] )
# 工場グラフ
# 1 => 0
# 2 => 0
UB={}
Capacity = {0: 300, 1: 500, 2: 200 }
Product = {0: ['P4','P5'], 1:['P1','P3'], 2: ['P2','P3']}
BOM.add_weighted_edges_from([ ('P1','P4', 1), ('P2','P5',2),('P3','P5',1) ])
#Construct product graph(生産グラフ)
ProdGraph = nx.tensor_product(G,BOM)
#print(ProdGraph.edges())
Temp = ProdGraph.copy()
for (i,p) in Temp:
if p not in Product[i]:
ProdGraph.remove_node( (i,p) )
print("ProdGraph Nodes=",ProdGraph.nodes())
print("ProdGraph Edges=",ProdGraph.edges())
Pipeline, Demand= {}, {}
h, b ={},{} #inventory cost, baclorder cost
prob = {1:0.005, 2:0.002, 0:0.001} #disruption probability
TTR0 ={1:1, 2:5, 0:2} #Time-To-Recovery
TTR = defaultdict(int)
for i in TTR0:
TTR[i] = TTR0[i]
for n in ProdGraph:
if n[0] > 0:
h[n] = 1.
b[n] = 1000.
else:
h[n] = 4.
b[n] = 1000.
Pipeline[n] =300.
UB[n] = 1000000.
if ProdGraph.out_degree(n)==0:
Demand[n]=100.
R={}
for (u,v) in ProdGraph.edges():
(i,p)=u
(j,q)=v
R[u,v]=BOM[p][q]['weight']
print("R=",R)
print("Demand=",Demand)
TotalDemand ={}
# 座標を得るためにSCMGraphにコピー
ProdGraph2 = SCMGraph()
ProdGraph2.add_nodes_from(ProdGraph.nodes())
ProdGraph2.add_edges_from(ProdGraph.edges())
pos3 = ProdGraph2.layout()
nx.draw(ProdGraph2, pos=pos3, with_labels=True, node_color="y", node_size=100)
import networkx as nx
G = nx.DiGraph() #plant graph
BOM=nx.DiGraph() #BOM: bill of materials
# 工場グラフ
layer1 = [1,2,3,4,5]
layer2 = [6,7,8,9]
layer3 = [10,11,12,13]
G.add_edges_from( [ (i,j) for i in layer1 for j in layer2] )
G.add_edges_from( [ (i,j) for i in layer2 for j in layer3] )
#len(G.edges())
bom_matrix = [[1,1,1,0],
[1,1,1,0],
[1,1,2,1],
[1,0,1,0],
[1,0,1,0],
[0,3,0,0],
[3,0,0,0],
[3,0,3,2]]
children = ["ABX", "ABN", "GHY","KIU","PFR","JKI1","JKI2","JKI3"]
parents = [ f"CFG{i}" for i in range(1,5)]
arcs = []
for i,c in enumerate(children):
for j,p in enumerate(parents):
if bom_matrix[i][j]>0:
arcs.append( (c,p,bom_matrix[i][j]) )
for p in parents:
arcs.append( (p,"F_"+p,1) )
BOM.add_weighted_edges_from( arcs )
#len(BOM.edges())
UB={}
Product = {1: ["ABX", "ABN"], 2:["GHY"], 3: ["KIU"], 4:["PFR"], 5:["JKI1","JKI2","JKI3"],
6:['CFG1'],7:['CFG2'], 8:['CFG3'],9:['CFG4'],
10:['F_CFG1','F_CFG4'],11:['F_CFG1','F_CFG2'], 12:['F_CFG2','F_CFG3'],13:['F_CFG3','F_CFG4']}
#Construct product graph(生産グラフ)
ProdGraph = nx.tensor_product(G,BOM)
#print(ProdGraph.edges())
Temp = ProdGraph.copy()
for (i,p) in Temp:
if p not in Product[i]:
ProdGraph.remove_node( (i,p) )
#print("ProdGraph Nodes=",ProdGraph.nodes())
#print("ProdGraph Edges=",ProdGraph.edges())
#ProdGraph.edges()
Pipeline, Demand= {}, {}
for n in ProdGraph:
Pipeline[n] = random.random()
UB[n] = 10 #random.randint(1,10)
Demand ={}
TotalDemand ={10:0.18, 11: 0.21, 12:0.2, 13:0.21 }
R={}
for (u,v) in ProdGraph.edges():
(i,p)=u
(j,q)=v
R[u,v]=BOM[p][q]['weight']
print("R=",R)
print("Total Demand=",TotalDemand)
#容量をalpha倍に増大させる
alpha = 1.2
Capacity0 = {1: 1.2, 2: 0.9, 3: 0.5, 4:0.5, 5:2.4, 6:0.2, 7:0.4, 8:0.3, 9:0.2 }
Capacity ={}
for i in Capacity0:
Capacity[i] = Capacity0[i]*alpha
hlist=[31,30,32,29,30,30,33,31,50,550,600,505]
ProdList=[]
h0, b0 = {}, {}
h, b = {}, {}
for p in Product:
ProdList.extend(Product[p])
for i, p in enumerate(ProdList):
try:
h0[p] = hlist[i] #inventory cost
b0[p] = h0[p]*100.
except:
break
for p in ['CFG1','CFG2','CFG3','CFG4']:
h0["F_"+p] = h0[p]
b0["F_"+p] = b0[p]
for (i,p) in ProdGraph:
h[i,p] = h0[p]
b[i,p] = b0[p]
prob = {1: 0.0667, 2: 0.1333, 3: 0.2, 4:0.1667, 5:0.0667, 6:0.0667, 7:0.1667, 8:0.0667, 9:0.0667 } #disruption probability
TTR0 = {1: 1, 2: 1.2, 3: 1.4, 4:1.6, 5:1.8, 6:2, 7:2.2, 8:2.4, 9:2.6 } #Time-To-Recovery
TTR = defaultdict(int)
for i in TTR0:
TTR[i] = TTR0[i]
# 座標を得るためにSCMGraphにコピー
ProdGraph2 = SCMGraph()
ProdGraph2.add_nodes_from(ProdGraph.nodes())
ProdGraph2.add_edges_from(ProdGraph.edges())
pos3 = ProdGraph2.layout()
nx.draw(ProdGraph2, pos=pos3, with_labels=True, node_color="y", node_size=100)
survival_time
sum_ =0
for i in I:
print(i, I[i].X)
sum_ += h[i]*I[i].X/100.
print("Total Invenory=", sum_)
#model.write("tmp.lp")
print("Backorder")
for j in B:
if B[j].X>0:
print(j,B[j].X)
ベンチマーク問題例からデータを生成する関数 data_generation_for_scrm
3つの有向グラフが必要となる。
- ベンチマークのグラフである部品展開表 BOM
- 工場グラフ: 各段階に n_plnts 個の工場があり、次段階に確率 prob で枝があるグラフ
- 生産グラフ: 各段階の工場にその段階の品目が n_flex 個割り振られるように設定して得られるグラフ;工場と製品の集合のテンソル積から、工場で生産できない製品を表す点を除くことによって得ることができる。
引数:
- BOM : ベンチマーク問題例から読み込まれた部品展開表 (BOM) を表すNetworkXの有向グラフ
- n_plnts : ベンチマークのBOMの各階層ごとの工場数(既定値は3)
- n_flex : 各工場で生産可能な製品の数;生産の柔軟性を表す。どの工場でも生産されない製品は最後の工場で生産する。(既定値は2)
- prob : 工場グラフにおける枝の発生確率;同じ順番の点間には枝をはり、それ以外の枝をこの確率で発生させる。(既定値は0.5)
- capacity_factor : 工場の生産容量を決めるパラメータ。総需要量にこの値を乗じた量が、各工場の容量になる。(既定値は1.0)
- production_factor : 生産量上限を決めるパラメータ。総需要量にこの値を乗じた量が、各地点の生産量上限になる。(既定値は1.0)
- pipeline_factor : パイプライン在庫量を決めるパラメータ。総需要量に生産時間の値とこの値を乗じた量が、各地点のパイプライン在庫量になる。(既定値は1.0)
- seed : 乱数の種 (既定値は1)
返値:
- Demand : 需要地点の需要を入れた辞書
- total_demand : 総需要量
- UB : 各点での生産量上限を入れた辞書
- Capacity : 工場の生産容量を入れた辞書
- Pipeline : 各点でのパイプライン在庫量を入れた辞書
- R : 親製品を生産するために必要な子製品の数を入れた辞書
- Product : 工場で生産可能な製品を入れた辞書
- G : 工場グラフ
- ProdGraph : 生産グラフ
- pos2 : 工場グラフの点の座標を入れた辞書
- pos3 : 生産グラフの点の座標を入れた辞書
Demand, total_demand, UB, Capacity, Pipeline, R, Product, G, ProdGraph2, pos2, pos3 = data_generation_for_scrm(BOM, n_plnts = 3, n_flex = 2, prob = 0.5, capacity_factor=1.0,
production_factor=1.0, pipeline_factor =0.5, seed = 1)
# fn = str(i)
# if len(fn) == 1:
# fn = "0"+fn
# BOM, pos = read_willems(file_name=fn)
# Demand, total_demand, UB, Capacity, Pipeline, R, Product, G, ProdGraph2, pos2, pos3 = data_generation_for_scrm(BOM, n_plnts = 3, n_flex = 2, prob = 0.5, capacity_factor=1.0,
# production_factor=1.0, pipeline_factor =1.0, seed = 1)
# make_df_for_scrm(G,Demand,UB,Capacity,Pipeline,BOM,fn)
fn = "01"
Demand, UB, Capacity, Pipeline, R, BOM, Product, G, ProdGraph2, pos, pos2, pos3, bom_df, plnt_df, plnt_prod_df, trans_df = prepare(fn, folder=folder)
survival_time = solve_scrm(Demand, UB, Capacity, Pipeline, R, Product, ProdGraph, BOM)
#plnt_prod_df["survival_time"] = survival_time
#plnt_prod_df.head()
fig = draw_scrm(ProdGraph, survival_time, Pipeline, UB, pos3)
plotly.offline.plot(fig);