2017-05-31 54 views
1

我是線性編程的新手,並且正在通過一個最小化工廠生產成本的例子。這個例子中包含了打開和關閉工廠的能力。如何在PuLP中添加約束條件以保持工廠開啓或關閉一段時間?

我的問題是如何添加額外的限制,如果工廠關閉,那麼它需要停留3個月,如果它切換回來,那麼它需要保持4個月?

代碼:

import pandas as pd 
import pulp 

factories = pd.DataFrame.from_csv('csv/factory_variables.csv', index_col=['Month', 'Factory']) 


demand = pd.DataFrame.from_csv('csv/monthly_demand.csv', index_col=['Month']) 


# Production 
production = pulp.LpVariable.dicts("production", 
            ((month, factory) for month, factory in factories.index), 
            lowBound=0, 
            cat='Integer') 

# Factory Status, On or Off 
factory_status = pulp.LpVariable.dicts("factory_status", 
            ((month, factory) for month, factory in factories.index), 
            cat='Binary') 

# Factory switch on or off 
switch_on = pulp.LpVariable.dicts("switch_on", 
            ((month, factory) for month, factory in factories.index), 
            cat='Binary') 

# Instantiate the model 
model = pulp.LpProblem("Cost minimising scheduling problem", pulp.LpMinimize) 

# Select index on factory A or B 
factory_A_index = [tpl for tpl in factories.index if tpl[1] == 'A'] 
factory_B_index = [tpl for tpl in factories.index if tpl[1] == 'B'] 

# Define objective function 
model += pulp.lpSum(
    [production[m, f] * factories.loc[(m, f), 'Variable_Costs'] for m, f in factories.index] 
    + [factory_status[m, f] * factories.loc[(m, f), 'Fixed_Costs'] for m, f in factories.index] 
    + [switch_on[m, f] * 20000 for m, f in factory_A_index] 
    + [switch_on[m, f] * 400000 for m, f in factory_B_index] 
) 

# Production in any month must be equal to demand 
months = demand.index 
for month in months: 
    model += production[(month, 'A')] + production[(month, 'B')] == demand.loc[month, 'Demand'] 

# Production in any month must be between minimum and maximum capacity, or zero. 
for month, factory in factories.index: 
    min_production = factories.loc[(month, factory), 'Min_Capacity'] 
    max_production = factories.loc[(month, factory), 'Max_Capacity'] 
    model += production[(month, factory)] >= min_production * factory_status[month, factory] 
    model += production[(month, factory)] <= max_production * factory_status[month, factory] 

# Factory B is off in May 
model += factory_status[5, 'B'] == 0 
model += production[5, 'B'] == 0 

#Constraints for switching factory on and off 
for month, factory in factories.index: 
    # In month 1, if the factory ison, we assume it turned on 
    if month == 1: 
     model += switch_on[month, factory] == factory_status[month, factory] 

    # In other months, if the factory is on in the current month AND off in the previous month, switch on = 1 
    else: 
     model += switch_on[month, factory] >= factory_status[month, factory] - factory_status[month-1, factory] 
     model += switch_on[month, factory] <= 1 - factory_status[month-1, factory] 
     model += switch_on[month, factory] <= factory_status[month, factory] 


model.solve() 
pulp.LpStatus[model.status] 

output = [] 
for month, factory in production: 
    var_output = { 
     'Month': month, 
     'Factory': factory, 
     'Production': production[(month, factory)].varValue, 
     'Factory Status': factory_status[(month, factory)].varValue, 
     'Switch On': switch_on[(month, factory)].varValue 
    } 
    output.append(var_output) 
output_df = pd.DataFrame.from_records(output).sort_values(['Month', 'Factory']) 
output_df.set_index(['Month', 'Factory'], inplace=True) 
output_df 

回答

1

爲了模擬第一組約束,你需要一個switch_off變量,定義類似於switch_on。這是必要的原因是因爲當switch_on變量沒有辦法不知道是否沒有切換或關閉(我們當然可以檢查factory_status,但沒有表示該變化的單個變量)。

然後,你可以這樣做:

model += factory_status[month, factory] + factory_status[month+1, factory] + \ 
factory_status[month+2, factory] <= 3 * (1 - switch_off[month, factory]) 

爲各工廠在{1, ... , TotalMonths - 2}所有月份。

然後,在{TotalMonths-1, TotalMonths}的月份中,爲所有工廠設置switch_on[month, factory] = 0(甚至根本不定義這些變量)。這些是邊界條件

接通約束是類似的:

factory_status[month, factory] + factory_status[month + 1, factory]+ \ 
factory_status[month + 2, factory] + factory_status[month + 3, factory] >= 
4 * switch_on[month, factory] 

不需要額外的邊界條件是必要的。

這些約束是正確的,它們的優點是它只有兩個(+邊界條件)。然而,它們並不緊密,因爲模型的線性規劃(LP)鬆弛可能遠離最優解。可以獲得更高目標的LP放鬆的版本是這一個:

factory_status[month + u, factory] <= 1 - switch_off[month, factory], for u in {0, 1, 2}, 

factory_status[month + u, factory] >= switch_on[month, factory], for u in {0, 1, 2, 3} 

這可能對您的示例更好。對於非常大的模型,第一個版本可能是首選,但它確實是問題(也許是實例)特定的。在我看來,最好的選擇是將第二個版本的約束放在懶惰池中,並將其管理委託給解算器,這通常更有效。並非所有求解器和接口都可以實現這一點,但它確實對困難模型有所幫助。

我希望這有助於!

+0

感謝您的幫助和快速回復我的問題Ioannis!我從來沒有考慮過使用'switch_off'變量。我的研究還沒有擴展到LP放鬆或Lazy Pool限制的話題。你能解釋他們還是推薦閱讀材料?我想我會首先嚐試第一個選項,因爲我知道那裏發生了什麼 - 我會隨着我的進度更新此帖。乾杯! – usermw

+0

在評論中解釋整數編程有點困難。簡而言之,在實踐中解決比線性編程要困難得多,並且有理論上的理由。 [This](http://inside.mines.edu/~anewman/MIP_practice120212.pdf)是開始閱讀更多細節的好地方。祝你好運,享受! – Ioannis

0

我假設「停留3個月」和「停留4個月」並不完全意味着這一點,而是「停留至少3個月」和「至少停留4個月」。

這裏是沒有額外的變量的替代製劑:

  • 禁止不圖案101:x[t,i]-x[t+1,i]+x[t+2,i] <= 1
  • 禁止不圖案1001:x[t,i]-x[t+1,i]-x[t+2,i]+x[t+3,i] <= 1
  • 禁止不圖案010:-x[t,i]+x[t+1,i]-x[t+2,i] <= 0
  • 禁止不圖案0110 -x[t,i]+x[t+1,i]+x[t+2,i]-x[t+3,i] <= 1
  • 禁止圖案01110 -x[t,i]+x[t+1,i]+x[t+2,i]+x[t+3,i]-x[t+4,i] <= 2

其中xfactory_status的較短的名稱。

+0

感謝您的回答Erwin!是的,你是對的,這正是我的意思。有意義且易於實施,所以我也會放棄這一點。我的問題的答案看起來相對容易,現在我已經看到了一些解決方案。隊友的歡呼聲 – usermw

相關問題