1

我对线性编程很陌生,并且正在研究一个可以最大限度地降低工厂生产成本的示例。此示例中包括打开和关闭工厂的功能。

我的问题是如何添加额外的约束条件,如果工厂关闭,则需要关闭 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
4

2 回答 2

1

To model the first set of constraints, you will need a switch_off variable, defined similarly to the switch_on. The reason why this is necessary is because when the switch_on variable there is no way not know if there is no switching or a switch off (we can of course check the factory_status, but there is not a single variable that signifies that change).

Then, you can do this:

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

for all factories and for all months in{1, ... , TotalMonths - 2}.

Then, for months in {TotalMonths-1, TotalMonths}, set switch_on[month, factory] = 0, for all factories (or even do not define these variables at all). These are boundary conditions.

Switching on constraints are similar:

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

No additional boundary conditions are necessary.

These constraints are correct, and they have the advantage that it is only two of them (+ boundary conditions). However, they are not tight, in the sense that the linear programming (LP) relaxation of the model may be far off from the optimal solution. A version that can obtain an LP relaxation with higher objective is this one:

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}

This is probably better for your example. For very large models, the first version may be preferred, but it is really problem- (and maybe instance-) specific. The best option in my opinion is to put the second version of the constraints in a Lazy Pool, and their management is delegated to the solver, which is usually more efficient. Not all solvers and interfaces allow this, but it does make a difference in difficult models.

I hope this helps!

于 2017-05-31T15:21:46.930 回答
1

我认为“停工 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

哪里x是 的简称factory_status

于 2017-05-31T19:53:58.370 回答