はじめに
ここで論じるのは,需要量が期によって変動するときの 各期の生産量(もしくは発注量)ならびに在庫量を決定するためのモデル(動的ロットサイズ決定モデル)である. 発注量を決める古典モデルである経済発注量モデルは,サプライ・チェイン基本分析システム SCBASの,安全在庫、ロットサイズ、目標在庫(基在庫レベル)の設定関数 inventory_analysisで計算できる. ここで考えるモデルは,経済発注量モデルにおける需要が一定という仮定を拡張し,期によって変動することを許したものである.
計画期間は有限であるとし,発注を行う際の段取り費用(もしくは生産費用)と 在庫費用のトレードオフをとることがモデルの主目的になる.
ロットサイズ決定は,タクティカルレベルの意思決定モデルであり, 与えられた資源(機械や人)の下で,活動をどの程度まとめて行うかを決定する.
一般に,生産や輸送は規模の経済性をもつ. これをモデル化する際には,生産や輸送のための諸活動を行うためには「段取り」とよばれる準備活動が必要になると考える. ロットサイズ決定とは,段取り活動を行う期を決定し, 生産・輸送を表す諸活動をまとめて行うときの「量」を決定するモデルである.
ロットサイズ決定問題は,古くから多くの研究が行われている問題であるが, 国内での(特に実務家の間での)認知度は今ひとつのようである. 適用可能な実務は,ERP(Enterprise Resource Planning)やAPS(Advanced Planning and Scheduling)などの処理的情報技術を導入しており,かつ 段取りの意思決定が比較的重要な分野である. そのような分野においては, ERPやAPSで単純なルールで自動化されていた部分に最適化を持ち込むことによって, より現実的かつ効率的な解を得ることができる. 特に,装置産業においては,ロットサイズ決定モデルは,生産計画の中核を担う.
現在の我が国で販売されている生産スケジューラは,すべてルールに基づいたヒューリスティクスであり,いわゆる処理的情報技術に過ぎない. ロットまとめの意思決定はタクティカルレベルであり,オペレーショナルレベルのスケジューリング最適化と同時に行うべきではない. 個々の意思決定レベルの最適化に基づき,情報のやりとりによって全体最適化を目指す必要があり,そのためには,広範囲の問題が解けるロットサイズ決定モデルが必須である.
prod_df = pd.read_csv(folder + "lotprod.csv",index_col=0)
prod_df.set_index("name", inplace=True)
production_df = pd.read_csv(folder + "production.csv",index_col=0)
bom_df = pd.read_csv(folder + "bomodel.csv", index_col =0)
resource_df = pd.read_csv(folder + "resource.csv", index_col=0)
plnt_demand_df = pd.read_csv(folder+"plnt-demand.csv")
demand = pd.pivot_table(plnt_demand_df, index= "prod", columns ="period", values="demand", aggfunc=sum)
prod_df
production_df
bom_df
demand
resource_df.head()
T = 24
yearly_ratio = [1.0 + np.sin(i)*0.5 for i in range(13)]
dem, prod, month = [], [], []
try:
prod_df.reset_index(inplace=True)
except:
pass
for row in prod_df.itertuples():
mu, sigma = row.average_demand, row.standard_deviation #monthly demand and std (original average is weekly)
for t in range(T):
dem.append( int(yearly_ratio[t//12]* random.gauss(mu,sigma)) )
prod.append(row.name)
month.append(t)
plnt_demand_df = pd.DataFrame({"prod":prod, "period":month, "demand": dem})
plnt_demand_df.to_csv(folder+"plnt-demand.csv")
plnt_demand_df.head()
#print("T=",T)
try:
prod_df.set_index("name",inplace=True)
except:
pass
#2段階モデルを仮定;各段階には1つの資源制約と仮定
num_stages = 2
maintenance_cycle = 100 #資源が使用不可になる周期;この数で割り切れると休止
#num_resources = [2,3]
#assert num_stages == len(num_resources) #段階数と資源数のリストの長さは同じであることの確認
prod_time_bound = (1,1)
setup_time_bound =(3600,7200)
prod_cost_bound = (100,300)
setup_cost_bound =(10000,20000)
units_bound =(1,1)
num_parents = 5 # 1つの子品目から生成される親品目の数;distribution型の生産工程を仮定
products = list(prod_df.index)
num_prod = len(products)
#原材料リストを生成
raw_material_name=""
raw_materials = []
for i,p in enumerate(prod_df.index):
raw_material_name += str(p)
if (i+1)%num_parents == 0 or i==len(prod_df)-1:
#print(i,p, raw_material_name)
raw_materials.append(raw_material_name)
raw_material_name =""
#親子関係を定義
parent = defaultdict(list)
child = {}
for r in raw_materials:
parent[r] = list(r)
for p in parent[r]:
child[p] = r
#生産工程の容量を計算
average_demand = plnt_demand_df.demand.sum()/T
print(average_demand)
cycle_time= 4
capacity = (average_demand*prod_time_bound[1]+setup_time_bound[1]*num_prod/cycle_time)
print("capacity=",capacity)
#資源データフレーム
#期ごとに容量が変化するモデル
#print(capacity)
name_, period_, capacity_ = [], [], []
for s in range(num_stages):
for t in range(T):
name_.append( f"Res{s}")
period_.append(t)
if (t+1) % maintenance_cycle == 0:
capacity_.append( 0 )
else:
if s ==0:
capacity_.append( capacity/2 )
else:
capacity_.append( capacity)
resource_df = pd.DataFrame(data={"name": name_, "period": period_, "capacity": capacity_})
#部品展開表のデータフレーム生成
bom_df = pd.DataFrame(data={"child": [child[p] for p in products],
"parent": products,
"units": [random.randint(units_bound[0], units_bound[1]) for i in range(num_prod)]
})
#生産情報データフレーム生成
items = products+raw_materials
num_item = len(items)
production_df = pd.DataFrame(data={"name": items,
"ProdTime": [random.randint(prod_time_bound[0], prod_time_bound[1]) for i in range(num_item)],
"SetupTime": [random.randint(setup_time_bound[0], setup_time_bound[1]) for i in range(num_item)],
"ProdCost": [random.randint(prod_cost_bound[0], prod_cost_bound[1]) for i in range(num_item)],
"SetupCost": [random.randint(setup_cost_bound[0], setup_cost_bound[1]) for i in range(num_item)]
})
production_df.set_index("name", inplace=True)
production_df.reset_index(inplace=True)
production_df.to_csv(folder+"production.csv")
bom_df.to_csv(folder+"bomodel.csv")
resource_df.to_csv(folder+"resource.csv")
#inv_cost: Optional[float] = Field(description="工場における在庫費用")
#safety_inventory: Optional[float] = Field(description="安全在庫量(最終期の目標在庫量)")
#initial_inventory: Optional[float] = Field(description="初期在庫量")
#target_inventory
prod_df.reset_index(inplace=True)
name = prod_df.name.to_list()
inv_cost = prod_df.inv_cost.to_list()
safety_inventory = prod_df.safety_inventory.to_list()
initial_inventory = prod_df.initial_inventory.to_list()
target_inventory = prod_df.target_inventory.to_list()
name.extend(raw_materials)
inv_cost.extend( [0.002 for i in range(len(raw_materials))])
safety_inventory.extend([sum(safety_inventory[:num_parents]),sum(safety_inventory[num_parents:])] )
initial_inventory.extend([sum(initial_inventory[:num_parents]),sum(initial_inventory[num_parents:])] )
target_inventory.extend([sum(target_inventory[:num_parents]),sum(target_inventory[num_parents:])] )
prod_df = pd.DataFrame({"name":name, "inv_cost":inv_cost,"safety_inventory":safety_inventory,"initial_inventory":initial_inventory,"target_inventory":target_inventory})
prod_df.to_csv(folder+"lotprod.csv")
ロットサイズ決定問題を解く関数 lotsizing
2段階モデルを仮定し、最初の段階(原材料生成工程)における資源名を Res0、次の段階(完成品生産工程)における資源名をRes1とする。
原材料の在庫は許さないものとする。
親品目は子品目1単位から生成される。
引数:
- prod_df : 品目データフレーム
- production_df : 生産情報データフレーム
- bom_df : 部品展開表データフレーム
- demand : (期別・品目別の)需要を入れた配列(行が品目,列が期)
- resource_df : 資源データフレーム
返値:
- model : ロットサイズ決定モデルのオブジェクト(最適解の情報も mode.__data に含む); 最適化の状態は model.Status
- T : 計画期間数
prod_df = pd.read_csv(folder + "lotprod.csv",index_col=0)
prod_df.set_index("name", inplace=True)
production_df = pd.read_csv(folder + "production.csv",index_col="name")
bom_df = pd.read_csv(folder + "bom.csv")
#需要量の計算
#demand = demand_df.iloc[:,3:].values
plnt_demand_df = pd.read_csv(folder+"plnt-demand.csv")
demand_ = pd.pivot_table(plnt_demand_df, index= "prod", columns ="period", values="demand", aggfunc=sum)
demand = demand_.values
_, T = demand.shape
resource_df = pd.read_csv(folder + "resource.csv")
model, T = lotsizing(prod_df, production_df, bom_df, demand = demand, resource_df=resource_df, max_cpu= 10, solver="SCIP")
最適化結果から図とデータフレームを生成する関数 show_result_for_lotsizing
引数:
- model : ロットサイズ決定モデル
- T : 計画期間
- production_df : 生産情報データフレーム
- bom_df : 部品展開表データフレーム
- resource_df : 資源データフレーム
返値:
- violated : 需要満足条件を逸脱した品目と期と逸脱量を保存したデータフレーム
- production : 生産量を保管したデータフレーム
- inventory : 在庫量を保管したデータフレーム
- fig_inv : 在庫量の推移を表した図オブジェクト
- fig_capacity : 容量制約を表した図オブジェクト
violated, production, inventory, fig_inv, fig_capacity = show_result_for_lotsizing(model, T, prod_df, production_df, bom_df, resource_df=resource_df)
plotly.offline.plot(fig_inv);
plotly.offline.plot(fig_capacity);
production.columns = [str(i) for i in list(production.columns)]
production
production.head()
wb = Workbook()
ws = wb.active
wb.remove(ws)
wb = generate_item_master(wb)
wb = generate_lotsize_master()
wb.save("optlot-master.xlsx")
wb = load_workbook("lotsize-ex1.xlsx")
wb = add_bom_resource_sheets(wb)
#wb.save("lotsize-ex1.xlsx")
資源データ入力Excelテンプレートの生成関数
基本データをマスタで入力した後, Webアプリで開始日と終了日と期間(pandasの頻度(freq)パラメータ)を入力し,生成した期別の入力シートをダウンロードしてもらい, 期別の容量(稼働時間上限)をExcelで入力し,アップロードする.これが, オペレーショナルに用いる資源データになる.
引数:
- wb: ロットサイズ決定のためのExcel Workbook
- start: 計画期間の開始日(日付時刻を表す文字列もしくは日付時刻型)
- finish: 計画期間の終了日(日付時刻を表す文字列もしくは日付時刻型)
- period: 期を構成する単位期間の数;既定値は $1$
- period_unit: 期の単位 (時,日,週,月から選択; 既定値は日; periodとあわせて期間の生成に用いる. たとえば,既定値だと1日が1期となる.
返値:
- wb: 資源の期別の容量(稼働時間上限)のシートを追加したWorkbook; シート名は「期別上限 開始日 終了日」となる.
wb = load_workbook("lotsize-sample-new.xlsx")
start = '2021-01-01' #開始日
finish = '2021-01-5'
period=1
period_unit="日"
wb = add_detailed_resource_sheet(wb, start, finish, period, period_unit)
wb.save("lotsize-sample-new.xlsx")
wb = load_workbook("optlot-master-at.xlsx")
start = '2021-01-5' #開始日
finish = '2021-12-17'
period=1
period_unit="日"
wb = add_detailed_resource_sheet(wb, start, finish, period, period_unit)
wb.save("optlot-master-at2.xlsx")
wb = generate_order_master()
wb.save("order.xlsx")
wb = load_workbook("optlot-master2-ex1.xlsx")
item_df, process_df, resource_df, bom_df, usage_df = read_dfs_from_excel_lot(wb)
print(item_df)
#print(process_df)
#print(resource_df)
注文Workbookから需要を生成する関数 generate_demand_from_order
引数
- wb: 注文情報を入れたExcel Workbook(シート名は注文)
- start: 開始日;日単位でない(たとえば1週単位の)場合には,開始日から1週間前からその開始日までが最初の期になる.
- finish: 終了日:日単位でない(たとえば1週単位の)場合には,生成したい週の最後の日が終了日以前である必要がある.
- period: 期を構成する単位期間の数;既定値は $1$
- period_unit: 期の単位 (時,日,週,月から選択; 既定値は日; periodとあわせて期間の生成に用いる. たとえば,既定値だと1日が1期となる.
返値
- demand: 品目 $p$ の期 $t$ の需要量 demand[t,p] を入れた辞書
- T: 計画期間数
wb = load_workbook("order-sample.xlsx")
#開始日を設定し,需要を生成
start = '2021-01-10' #開始日
finish = '2021-1-20' #終了日
period=1
period_unit="日"
demand, T = generate_demand_from_order(wb, start, finish, period, period_unit)
print("Demand=",demand)
print("T=",T)
期別資源使用量上限をもつWorkbookから情報を抽出する関数 get_resource_ub
引数
- wb: 注文情報を入れたExcel Workbook(期別資源使用量上限のシート名は「期別上限 {開始日} {終了日}」である必要がある.)
- start: 開始日;日単位でない(たとえば1週単位の)場合には,開始日から1週間前からその開始日までが最初の期になる.
- finish: 終了日:日単位でない(たとえば1週単位の)場合には,生成したい週の最後の日が終了日以前である必要がある.
- period: 期を構成する単位期間の数;既定値は $1$
- period_unit: 期の単位 (時,日,週,月から選択; 既定値は日; periodとあわせて期間の生成に用いる. たとえば,既定値だと1日が1期となる.
返値:
- M: 期別の資源量上限を表す辞書; 資源 $r$ の期 $t$ の上限が M[t,r] になる.
start = '2021-01-01' #開始日
finish = '2021-01-25'
period=1
period_unit="日"
wb = load_workbook("optlot-master2-ex1.xlsx")
M = get_resource_ub(wb, start, finish, period, period_unit)
print(M)
最適化結果Workbookの色情報を元に変数の固定情報を抽出する関数 extract_fix_info
出力のExcelシートに色を塗った箇所の生産(発注)量を固定する情報を抽出する.
引数
- wb: 最適化結果を入れた Excel Workbook
- start: 開始日;日単位でない(たとえば1週単位の)場合には,開始日から1週間前からその開始日までが最初の期になる.
- finish: 終了日:日単位でない(たとえば1週単位の)場合には,生成したい週の最後の日が終了日以前である必要がある.
- period: 期を構成する単位期間の数;既定値は $1$
- period_unit: 期の単位 (時,日,週,月から選択; 既定値は日; periodとあわせて期間の生成に用いる. たとえば,既定値だと1日が1期となる.
返値:
- fix_x: 変数 $x$ の固定情報;キーは「期番号,モード,アイテム」で値は固定したい数値を入れた辞書
wb = load_workbook("lotsize-out.xlsx")
start = '2021-01-01' #開始日
finish = '2021-01-25'
period=1
period_unit="日"
fix_x = extract_fix_info(wb, start, finish, period=1, period_unit="日")
fix_x
#from mypulp import GRB, quicksum, Model
#from pulp import PULP_CBC_CMD
from gurobipy import GRB, quicksum, Model
#開始日を設定し,需要を生成
start = '2021-01-8' #開始日
finish = '2021-02-26'
period=1
period_unit="週"
wb = load_workbook("optlot-master-at.xlsx")
item_df, process_df, resource_df, bom_df, usage_df = read_dfs_from_excel_lot(wb)
M = get_resource_ub(wb, start, finish, period, period_unit)
wb = load_workbook("optlot-order-at.xlsx")
demand, T = generate_demand_from_order(wb, start, finish, period, period_unit)
# print(demand,T)
model = multi_mode_lotsizing(item_df, resource_df, process_df, bom_df, usage_df, demand, M, T, fix_x=None)
model.optimize()
#PULPの場合
#solver = PULP_CBC_CMD(timeLimit=10, presolve=False)
#model.optimize(solver)
#SCIPの場合
#solver = SCIP(timeLimit=10)
#model.optimize(solver=solver)
x, I, y, slack, surplus, inv_slack, inv_surplus, cost, items, modes, item_modes, setup_time, prod_time, parent, resources = model.__data
print(model.Status)
# INF = 9999999999.
# fix_x=None
# h = {} #在庫費用
# IUB, ILB = {},{} #在庫量の下限と上限
# for row in item_df.itertuples():
# h[row[1]] = row[2]
# if pd.isnull(row[3]):
# ILB[row[1]] = 0.
# else:
# ILB[row[1]] = row[3]
# if pd.isnull(row[4]):
# IUB[row[1]] = INF # 在庫量上限がない場合
# else:
# IUB[row[1]] = row[4]
# #親子関係や資源必要量の辞書を作成
# parent = defaultdict(set) #子品目pを必要とする親品目とモードの組の集合
# phi = defaultdict(float) #親品目qをモードmで1単位生産するために必要な子品目pのunit数
# modes = defaultdict(set) #親品目qのモード集合
# resources = defaultdict(set) #親品目qをモードmで生産するときに必要な資源の集合
# setup_time = defaultdict(float)
# setup_cost = defaultdict(float)
# prod_time = defaultdict(float)
# prod_cost = defaultdict(float)
# items = item_df.iloc[:,0] #品目のリストを準備
# item_set = set(items)
# resource_set = set( resource_df.iloc[:,0])
# #resource内にない場合にエラーを起こす
# #品目にモードがない場合にもエラー
# for row in process_df.itertuples():
# if row[1] is not None:
# q = row[1]
# if row[2] is not None: #モード
# m = row[2]
# modes[q].add(m)
# #費用
# if row[3] is None or np.isnan(row[3]):
# setup_cost[q,m] = 0.
# else:
# setup_cost[q,m] = row[3]
# if row[4] is None or np.isnan(row[4]):
# prod_cost[q,m] = 0.
# else:
# prod_cost[q,m] = row[4]
# for q in items:
# if len(modes[q])==0:
# raise ValueError(f"品目{q}にモードがありません.")
# #BOM
# for row in bom_df.itertuples():
# if row[1] is not None:
# q = row[1]
# if row[2] is not None: #モード
# m = row[2]
# if row[3] is not None:
# p = row[3]
# phi[p,q,m] = row[4]
# parent[p].add( (q,m) )
# if q not in item_set:
# raise ValueError(f"品目{q}が品目シートにありません.")
# if p not in item_set:
# raise ValueError(f"品目{p}が品目シートにありません.")
# #usage
# for row in usage_df.itertuples():
# if row[1] is not None:
# q = row[1]
# if row[2] is not None: #モード
# m = row[2]
# #資源と時間
# if row[3] is not None:
# if r in resource_set and q in item_set:
# r = row[3]
# resources[q,m].add(r)
# if row[4] is None or np.isnan(row[4]):
# setup_time[q,m,r] = 0.
# else:
# setup_time[q,m,r] = row[4]
# if row[5] is None or np.isnan(row[5]):
# prod_time[q,m,r] = 0.
# else:
# prod_time[q,m,r] = row[5]
# # if r not in resource_set:
# # raise ValueError(f"資源{r}が資源シートにありません.")
# # if q not in item_set:
# # raise ValueError(f"品目{q}が品目シートにありません.")
# item_modes = defaultdict(set) #資源rを使用する品目とモードの組の集合(resourcesの逆写像)
# for key in resources:
# for r in resources[key]:
# item_modes[r].add(key)
# model = Model()
# x, I, y = {}, {}, {}
# slack, surplus = {}, {}
# inv_slack, inv_surplus = {}, {}
# Ts = range(0, T)
# for row in item_df.itertuples():
# p = row[1]
# for m in modes[p]:
# for t in Ts:
# x[t, m, p] = model.addVar(name=f"x({p},{m},{t})")
# I[t, p] = model.addVar(name=f"I({p},{t})")
# y[t, m, p] = model.addVar(name=f"y({p},{m},{t})", vtype="B")
# slack[t, p] = model.addVar(name=f"slack({p},{t})")
# surplus[t, p] = model.addVar(name=f"surplus({p},{t})")
# inv_slack[t, p] = model.addVar(name=f"inv_slack({p},{t})")
# inv_surplus[t, p] = model.addVar(name=f"inv_surplus({p},{t})")
# if pd.isnull(row[5]):
# I[-1, p] = 0.
# else:
# I[-1, p] = row[5] # 初期在庫
# if pd.isnull(row[6]):
# I[T-1,p] = 0.
# else:
# I[T-1,p] = row[6] # 最終期の在庫量
# #各費用項目を別途合計する
# cost ={}
# for i in range(5):
# cost[i] = model.addVar(vtype="C",name=f"cost[{i}]")
# model.update()
# #変数の固定
# if fix_x is not None:
# for (t,m,p) in fix_x:
# model.addConstr( x[t,m,p] == fix_x[t,m,p] )
# #在庫量の上下限の逸脱の計算
# for t in Ts:
# for p in items:
# model.addConstr( I[t, p] <= IUB[p] + inv_surplus[t,p], f"IUB({t},{p})" )
# model.addConstr( ILB[p] <= I[t, p]+ inv_slack[t,p], f"ILB({t},{p})" )
# for row in resource_df.itertuples():
# r = row[1]
# for t in Ts:
# # time capacity constraints
# model.addConstr(quicksum(prod_time[p,m,r]*x[t,m,p] + setup_time[p,m,r]*y[t,m,p] for (p,m) in item_modes[r]) <= M[t,r],
# f"TimeConstraint1({r},{t})")
# for t in Ts:
# for p in items:
# # flow conservation constraints(ソフト制約)
# model.addConstr(I[t-1, p] + quicksum(x[t, m, p] for m in modes[p]) + slack[t, p] - surplus[t, p] == I[t, p] +demand[t,p]+
# quicksum( phi[p,q,m]*x[t, m, q] for (q,m) in parent[p]), f"FlowCons({t},{p})" )
# for t in Ts:
# for p in items:
# # capacity connection constraints
# for m in modes[p]:
# for r in resources[p,m]:
# model.addConstr(prod_time[p,m,r]*x[t,m,p]
# <= (M[t,r]-setup_time[p,m,r])*y[t,m,p], f"ConstrUB({t},{m},{r},{p})")
# model.addConstr( quicksum( slack[t, p]+surplus[t, p] for t in Ts for p in items) == cost[0] )
# model.addConstr( quicksum( inv_slack[t, p]+inv_surplus[t, p] for t in Ts for p in items) == cost[1] )
# model.addConstr( quicksum( setup_cost[p,m]*y[t,m,p] for t in Ts for p in items for m in modes[p] for r in resources[p,m]) == cost[2])
# model.addConstr( quicksum( prod_cost[p,m]*x[t,m,p] for t in Ts for p in items for m in modes[p] for r in resources[p,m]) == cost[3])
# model.addConstr( quicksum( h[p]*I[t, p] for t in Ts for p in items) == cost[4] )
# model.setObjective(99999999.*cost[0] + 999999.*cost[1] + quicksum(cost[i] for i in range(2,5)) , GRB.MINIMIZE)
# model.__data = x, I, y, slack, surplus, inv_slack, inv_surplus, cost, items, modes, item_modes, setup_time, prod_time, parent, resources
# model.optimize()
# from pulp import PULP_CBC_CMD
# from mypulp import GRB, quicksum, Model
# order_wb = load_workbook("optlot-order-ex1.xlsx")
# start = '2021-01-1' #開始日
# finish = '2021-01-5'
# period=1
# period_unit="日"
# demand, T = generate_demand_from_order(order_wb, start, finish, period, period_unit)
# #資源量設定
# wb = load_workbook("optlot-master2-ex1.xlsx")
# item_df, process_df, resource_df, bom_df, usage_df = read_dfs_from_excel_lot(wb)
# M = get_resource_ub(wb, start, finish, period, period_unit)
# #変数固定情報
# result_wb = load_workbook("lotsize-out.xlsx")
# fix_x = extract_fix_info(result_wb, start, finish, period=1, period_unit="日")
# model = multi_mode_lotsizing(item_df, resource_df, process_df, bom_df, usage_df, demand, M, T, fix_x)
# model.Params.OutputFlag=False
# solver = PULP_CBC_CMD(timeLimit=100, presolve=True)
# model.optimize(solver)
# print(model.Status, model.ObjVal)
# # class Status:
# # OPTIMAL = 2
# # INFEASIBLE = 3
# # INF_OR_UNBD = 4
# # UNBOUNDED = 5
# # UNDEFINED = None
# x, I, y, slack, surplus, inv_slack, inv_surplus, cost, items, modes, item_modes, setup_time, prod_time, parent, resources = model.__data
# for v in y:
# if y[v].X is not None and y[v].X > 0:
# print(v,y[v].X, x[v].X)
# for i in cost:
# print(cost[i].X)
cost_df = make_cost_df(cost)
cost_df
最適化結果のExcelファイルを出力する関数 lot_output_excel
在庫・生産,需要量とシミュレーション用の関数を埋め込み
次の計画の初期在庫量は,このテンプレートの該当列になる. ローリングホライズンで行うために,以前のWorkbookを入れてそれに追加
引数
- model: ロットサイズ最適化のモデルオブジェクト
- start: 開始日;日単位でない(たとえば1週単位の)場合には,開始日から1週間前からその開始日までが最初の期になる.
- finish: 終了日:日単位でない(たとえば1週単位の)場合には,生成したい週の最後の日が終了日以前である必要がある.
- period: 期を構成する単位期間の数;既定値は $1$
- period_unit: 期の単位 (時,日,週,月から選択; 既定値は日; periodとあわせて期間の生成に用いる. たとえば,既定値だと1日が1期となる.
- demand: 需要を入れた辞書
- cost: 費用の変数を入れた辞書
返値:
- wb: 品目別の在庫量・生産量(モードごと)・需要量・品切れ量・超過量を入れたExcel Workbook
start = '2021-01-1' #開始日
finish = '2021-01-5'
period=1
period_unit="日"
cost_df = make_cost_df(cost)
wb = lot_output_excel(model, start, finish, period, period_unit, demand, cost_df)
wb.save("lotsize-out.xlsx")
多モードモデルの最適化結果の図を生成する関数 show_result_for_multimode_lotsizing
引数
- model: ロットサイズ最適化のモデルオブジェクト
- start: 開始日;日単位でない(たとえば1週単位の)場合には,開始日から1週間前からその開始日までが最初の期になる.
- finish: 終了日:日単位でない(たとえば1週単位の)場合には,生成したい週の最後の日が終了日以前である必要がある.
- period: 期を構成する単位期間の数;既定値は $1$
- period_unit: 期の単位 (時,日,週,月から選択; 既定値は日; periodとあわせて期間の生成に用いる. たとえば,既定値だと1日が1期となる.
- capacity: 資源量上限を入れた辞書
返値:
- fig_inv : 在庫量の推移を表した図オブジェクト
- fig_capacity : 容量制約を表した図オブジェクト
period=1
period_unit="週"
fig_inv, fig = show_result_for_multimode_lotsizing(model, start, finish, period, period_unit, M)
plotly.offline.plot(fig);
plotly.offline.plot(fig_inv);
fig.write_html("resource.html")
fig_inv.write_html("inv.html")
start = '2021-01-1' #開始日
finish = '2021-01-5' #終了日
period=1
period_unit="日"
fig_inv, fig = show_result_for_multimode_lotsizing(model, start, finish, period, period_unit, M)
plotly.offline.plot(fig);
plotly.offline.plot(fig_inv);