Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/305.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/mercurial/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
带有Python纸浆性能问题求解器的MILP模型速度非常慢_Python_Linear Programming_Pulp_Mixed Integer Programming - Fatal编程技术网

带有Python纸浆性能问题求解器的MILP模型速度非常慢

带有Python纸浆性能问题求解器的MILP模型速度非常慢,python,linear-programming,pulp,mixed-integer-programming,Python,Linear Programming,Pulp,Mixed Integer Programming,我最近开始学习Python中的线性规划,我用纸浆创建了我的第一个优化程序 我正在处理一个生产过程的调度问题。目标是通过为一天中的每一小时创建理想的生产计划,并为一年中的所有天创建此计划,从而最大限度地降低每天的生产成本 我遇到的问题是,算法的执行需要很长时间(几个小时),而且经常会卡住。而且,我觉得随着时间的推移,它会变得越来越慢 我希望得到关于如何提高代码性能的建议 我解决问题的方法: def set_supplyoptions(): cols = ['option', 'min_capaci

我最近开始学习Python中的线性规划,我用纸浆创建了我的第一个优化程序

我正在处理一个生产过程的调度问题。目标是通过为一天中的每一小时创建理想的生产计划,并为一年中的所有天创建此计划,从而最大限度地降低每天的生产成本

我遇到的问题是,算法的执行需要很长时间(几个小时),而且经常会卡住。而且,我觉得随着时间的推移,它会变得越来越慢

我希望得到关于如何提高代码性能的建议

我解决问题的方法:

def set_supplyoptions():
cols = ['option', 'min_capacity', 'max_capacity']
options_list = [{'option':'o', 'min_capacity': 0, 'max_capacity':146},
                {'option':'l30', 'min_capacity': 0, 'max_capacity':30},
                {'option':'l50', 'min_capacity': 31, 'max_capacity':50},
                {'option':'l90', 'min_capacity': 51, 'max_capacity':90},
                {'option':'l150', 'min_capacity': 91, 'max_capacity':150},
                {'option':'l230', 'min_capacity': 151, 'max_capacity':230},
                {'option':'a15', 'min_capacity': 0, 'max_capacity':15},
                {'option':'a30', 'min_capacity': 0, 'max_capacity':30},
                {'option':'a45', 'min_capacity': 0, 'max_capacity':45},
                {'option':'a60_below53', 'min_capacity': 0, 'max_capacity':52},
                {'option':'a_flow_below53', 'min_capacity': 0, 'max_capacity':52},
                {'option':'a_flow_above53', 'min_capacity': 0, 'max_capacity':8},
                {'option':'l_total', 'min_capacity': 0, 'max_capacity':230},
                {'option':'a_total', 'min_capacity': 0, 'max_capacity':60}]

options = pd.DataFrame(options_list, columns=cols)  
options = options.set_index('option')
  • 定义我的最小化问题,为一整天创建一个最优的时间表
  • 循环我的代码以在一年中的所有365天内运行此优化
  • 在每个循环结束时,我获取当天的优化生产计划并将其附加到一个数据框中,因此最后我有一个数据框,其中包含一年中所有日期的生产计划
我处理的是3种生产资产(“a”、“l”和“o”),每种资产都有几种生产模式。我将每个资产模式组合定义为一个“选项”,总共有14个选项。每个选项可以每小时变化一次,并且有一个整数值(生产量)和一个二进制值(开/关),导致大约14 x 2 x 24=672个变量。该问题包含大约1250个约束

我的代码有200多行,所以我有点犹豫是否要在这里分享所有内容,但我将在下面分享最重要的部分

定义供应选项:

def set_supplyoptions():
cols = ['option', 'min_capacity', 'max_capacity']
options_list = [{'option':'o', 'min_capacity': 0, 'max_capacity':146},
                {'option':'l30', 'min_capacity': 0, 'max_capacity':30},
                {'option':'l50', 'min_capacity': 31, 'max_capacity':50},
                {'option':'l90', 'min_capacity': 51, 'max_capacity':90},
                {'option':'l150', 'min_capacity': 91, 'max_capacity':150},
                {'option':'l230', 'min_capacity': 151, 'max_capacity':230},
                {'option':'a15', 'min_capacity': 0, 'max_capacity':15},
                {'option':'a30', 'min_capacity': 0, 'max_capacity':30},
                {'option':'a45', 'min_capacity': 0, 'max_capacity':45},
                {'option':'a60_below53', 'min_capacity': 0, 'max_capacity':52},
                {'option':'a_flow_below53', 'min_capacity': 0, 'max_capacity':52},
                {'option':'a_flow_above53', 'min_capacity': 0, 'max_capacity':8},
                {'option':'l_total', 'min_capacity': 0, 'max_capacity':230},
                {'option':'a_total', 'min_capacity': 0, 'max_capacity':60}]

options = pd.DataFrame(options_list, columns=cols)  
options = options.set_index('option')
变量:

# production per supply option per hour
production = pulp.LpVariable.dicts("production",
                                     ((hour, option) for hour in hours for option in options.index),
                                     lowBound=0,
                                     cat='Integer')

# status of supply options per hour (in use or not in use)
options_status = pulp.LpVariable.dicts("options_status",
                                     ((hour, option) for hour in hours for option in options.index),
                                     cat='Binary')

# use status of A tranches on day (used or not used)
a_status_15 = pulp.LpVariable('a_stat_15', cat='Binary')
a_status_30 = pulp.LpVariable('a_stat_30', cat='Binary')
a_status_45 = pulp.LpVariable('a_stat_45', cat='Binary')
a_status_60 = pulp.LpVariable('a_stat_60', cat='Binary')
opt_model = pulp.LpProblem("Optimizer", pulp.LpMinimize)

opt_model += pulp.lpSum(
    #O variable
    [0.2*demand.loc[hour, 'price']*production[hour, 'o'] + 
    #O fixed
    3*demand.loc[hour, 'price'] +
    #L30
    ( 12 )*options_status[hour, 'l30']*demand.loc[hour, 'price'] + 
    #L50
    ( 2*options_status[hour, 'l50']+0.13*production[hour, 'l50'] )*demand.loc[hour, 'price'] +
    #L90
    ( 15*options_status[hour, 'l90']+0.13*production[hour, 'l90'] )*demand.loc[hour, 'price'] +
    #L150
    ( 8*options_status[hour, 'l150']+0.2*production[hour, 'l150'] )*demand.loc[hour, 'price'] +
    #L230
    ( -3*options_status[hour, 'l230']+0.3*production[hour, 'l230'] )*demand.loc[hour, 'price'] +
    #L fixed
    ( 7*(1-options_status[hour, 'ltotal'])*demand.loc[hour, 'price'] ) +
    #A base unit price
    (10*production[hour, 'a_flow_below53']+8.88*production[hour, 'a_flow_above53'])*(c/20) for hour in hours]
    #A daily fee
    + (a_status_15 * 1000 + a_status_30 * 2000 + a_status_45 * 3 + a_status_60 * 4000)*(c/20))
# Sum of production == Demand
for hour in hours:
    opt_model += production[(hour, 'o')] + production[(hour, 'l_total')] + production[(hour, 'a_total')] == int(demand.loc[hour, 'demand'])

# Production <=  Max capacity AND => Min capacity
for hour in hours:
    for option in options.index:
        opt_model += production[(hour, option)] >= options_status[(hour, option)]
        opt_model += production[(hour, option)] >= options.loc[option, 'min_capacity'] * options_status[(hour, option)]
        opt_model += production[(hour, option)] <= options.loc[option, 'max_capacity'] * options_status[(hour, option)]

# Constraints L
for hour in hours:
    opt_model += production[(hour, 'l30')] + production[(hour, 'l0')] + production[(hour, 'l90')] + production[(hour, 'l150')] + production[(hour, 'l230')] == production[(hour, 'l_total')]
    opt_model += options_status[(hour, 'l30')] + options_status[(hour, 'l50')] + options_status[(hour, 'l90')] + options_status[(hour, 'l150')] + options_status[(hour, 'l230')] <= 1

# Constraints A
M = 999
for hour in hours:
    # total flow = sum of tranches
    opt_model += production[(hour, 'a_flow_below53')] == production[(hour, 'a_15')] + production[(hour, 'ap_30')] + production[(hour, 'a_45')] + production[(hour, 'ap_60_below53')] 
    opt_model += production[(hour, 'a_total')] == production[(hour, 'a_flow_below53')] + production[(hour, 'a_flow_above53')]

    # only 1 tranche can be active at the time
    opt_model += production[(hour, 'a_15')] <= M * a_status_15
    opt_model += production[(hour, 'a_30')] <= M * a_status_30 
    opt_model += production[(hour, 'a_45')] <= M * a_status_45
    opt_model += production[(hour, 'a_60_below53')] + production[(hour, 'a_flow_above53')] <= M * a_status_60

    # a_60_above53 can only be active if a_60_below53 == 52
    opt_model += production[(hour, 'a_flow_above53')] <= M * options_status[(hour, 'a_flow_above53')]
    opt_model += (52 - production[(hour, 'a_flow_below53')] ) <= M * (1-options_status[(hour, 'a_flow_above53')])

# only 1 'mode' can be used per day (corresponding to daily fee)
opt_model += a_status_15 + a_status_30 + a_status_45 + a_status_60 <= 1
目标函数:

# production per supply option per hour
production = pulp.LpVariable.dicts("production",
                                     ((hour, option) for hour in hours for option in options.index),
                                     lowBound=0,
                                     cat='Integer')

# status of supply options per hour (in use or not in use)
options_status = pulp.LpVariable.dicts("options_status",
                                     ((hour, option) for hour in hours for option in options.index),
                                     cat='Binary')

# use status of A tranches on day (used or not used)
a_status_15 = pulp.LpVariable('a_stat_15', cat='Binary')
a_status_30 = pulp.LpVariable('a_stat_30', cat='Binary')
a_status_45 = pulp.LpVariable('a_stat_45', cat='Binary')
a_status_60 = pulp.LpVariable('a_stat_60', cat='Binary')
opt_model = pulp.LpProblem("Optimizer", pulp.LpMinimize)

opt_model += pulp.lpSum(
    #O variable
    [0.2*demand.loc[hour, 'price']*production[hour, 'o'] + 
    #O fixed
    3*demand.loc[hour, 'price'] +
    #L30
    ( 12 )*options_status[hour, 'l30']*demand.loc[hour, 'price'] + 
    #L50
    ( 2*options_status[hour, 'l50']+0.13*production[hour, 'l50'] )*demand.loc[hour, 'price'] +
    #L90
    ( 15*options_status[hour, 'l90']+0.13*production[hour, 'l90'] )*demand.loc[hour, 'price'] +
    #L150
    ( 8*options_status[hour, 'l150']+0.2*production[hour, 'l150'] )*demand.loc[hour, 'price'] +
    #L230
    ( -3*options_status[hour, 'l230']+0.3*production[hour, 'l230'] )*demand.loc[hour, 'price'] +
    #L fixed
    ( 7*(1-options_status[hour, 'ltotal'])*demand.loc[hour, 'price'] ) +
    #A base unit price
    (10*production[hour, 'a_flow_below53']+8.88*production[hour, 'a_flow_above53'])*(c/20) for hour in hours]
    #A daily fee
    + (a_status_15 * 1000 + a_status_30 * 2000 + a_status_45 * 3 + a_status_60 * 4000)*(c/20))
# Sum of production == Demand
for hour in hours:
    opt_model += production[(hour, 'o')] + production[(hour, 'l_total')] + production[(hour, 'a_total')] == int(demand.loc[hour, 'demand'])

# Production <=  Max capacity AND => Min capacity
for hour in hours:
    for option in options.index:
        opt_model += production[(hour, option)] >= options_status[(hour, option)]
        opt_model += production[(hour, option)] >= options.loc[option, 'min_capacity'] * options_status[(hour, option)]
        opt_model += production[(hour, option)] <= options.loc[option, 'max_capacity'] * options_status[(hour, option)]

# Constraints L
for hour in hours:
    opt_model += production[(hour, 'l30')] + production[(hour, 'l0')] + production[(hour, 'l90')] + production[(hour, 'l150')] + production[(hour, 'l230')] == production[(hour, 'l_total')]
    opt_model += options_status[(hour, 'l30')] + options_status[(hour, 'l50')] + options_status[(hour, 'l90')] + options_status[(hour, 'l150')] + options_status[(hour, 'l230')] <= 1

# Constraints A
M = 999
for hour in hours:
    # total flow = sum of tranches
    opt_model += production[(hour, 'a_flow_below53')] == production[(hour, 'a_15')] + production[(hour, 'ap_30')] + production[(hour, 'a_45')] + production[(hour, 'ap_60_below53')] 
    opt_model += production[(hour, 'a_total')] == production[(hour, 'a_flow_below53')] + production[(hour, 'a_flow_above53')]

    # only 1 tranche can be active at the time
    opt_model += production[(hour, 'a_15')] <= M * a_status_15
    opt_model += production[(hour, 'a_30')] <= M * a_status_30 
    opt_model += production[(hour, 'a_45')] <= M * a_status_45
    opt_model += production[(hour, 'a_60_below53')] + production[(hour, 'a_flow_above53')] <= M * a_status_60

    # a_60_above53 can only be active if a_60_below53 == 52
    opt_model += production[(hour, 'a_flow_above53')] <= M * options_status[(hour, 'a_flow_above53')]
    opt_model += (52 - production[(hour, 'a_flow_below53')] ) <= M * (1-options_status[(hour, 'a_flow_above53')])

# only 1 'mode' can be used per day (corresponding to daily fee)
opt_model += a_status_15 + a_status_30 + a_status_45 + a_status_60 <= 1
约束条件:

# production per supply option per hour
production = pulp.LpVariable.dicts("production",
                                     ((hour, option) for hour in hours for option in options.index),
                                     lowBound=0,
                                     cat='Integer')

# status of supply options per hour (in use or not in use)
options_status = pulp.LpVariable.dicts("options_status",
                                     ((hour, option) for hour in hours for option in options.index),
                                     cat='Binary')

# use status of A tranches on day (used or not used)
a_status_15 = pulp.LpVariable('a_stat_15', cat='Binary')
a_status_30 = pulp.LpVariable('a_stat_30', cat='Binary')
a_status_45 = pulp.LpVariable('a_stat_45', cat='Binary')
a_status_60 = pulp.LpVariable('a_stat_60', cat='Binary')
opt_model = pulp.LpProblem("Optimizer", pulp.LpMinimize)

opt_model += pulp.lpSum(
    #O variable
    [0.2*demand.loc[hour, 'price']*production[hour, 'o'] + 
    #O fixed
    3*demand.loc[hour, 'price'] +
    #L30
    ( 12 )*options_status[hour, 'l30']*demand.loc[hour, 'price'] + 
    #L50
    ( 2*options_status[hour, 'l50']+0.13*production[hour, 'l50'] )*demand.loc[hour, 'price'] +
    #L90
    ( 15*options_status[hour, 'l90']+0.13*production[hour, 'l90'] )*demand.loc[hour, 'price'] +
    #L150
    ( 8*options_status[hour, 'l150']+0.2*production[hour, 'l150'] )*demand.loc[hour, 'price'] +
    #L230
    ( -3*options_status[hour, 'l230']+0.3*production[hour, 'l230'] )*demand.loc[hour, 'price'] +
    #L fixed
    ( 7*(1-options_status[hour, 'ltotal'])*demand.loc[hour, 'price'] ) +
    #A base unit price
    (10*production[hour, 'a_flow_below53']+8.88*production[hour, 'a_flow_above53'])*(c/20) for hour in hours]
    #A daily fee
    + (a_status_15 * 1000 + a_status_30 * 2000 + a_status_45 * 3 + a_status_60 * 4000)*(c/20))
# Sum of production == Demand
for hour in hours:
    opt_model += production[(hour, 'o')] + production[(hour, 'l_total')] + production[(hour, 'a_total')] == int(demand.loc[hour, 'demand'])

# Production <=  Max capacity AND => Min capacity
for hour in hours:
    for option in options.index:
        opt_model += production[(hour, option)] >= options_status[(hour, option)]
        opt_model += production[(hour, option)] >= options.loc[option, 'min_capacity'] * options_status[(hour, option)]
        opt_model += production[(hour, option)] <= options.loc[option, 'max_capacity'] * options_status[(hour, option)]

# Constraints L
for hour in hours:
    opt_model += production[(hour, 'l30')] + production[(hour, 'l0')] + production[(hour, 'l90')] + production[(hour, 'l150')] + production[(hour, 'l230')] == production[(hour, 'l_total')]
    opt_model += options_status[(hour, 'l30')] + options_status[(hour, 'l50')] + options_status[(hour, 'l90')] + options_status[(hour, 'l150')] + options_status[(hour, 'l230')] <= 1

# Constraints A
M = 999
for hour in hours:
    # total flow = sum of tranches
    opt_model += production[(hour, 'a_flow_below53')] == production[(hour, 'a_15')] + production[(hour, 'ap_30')] + production[(hour, 'a_45')] + production[(hour, 'ap_60_below53')] 
    opt_model += production[(hour, 'a_total')] == production[(hour, 'a_flow_below53')] + production[(hour, 'a_flow_above53')]

    # only 1 tranche can be active at the time
    opt_model += production[(hour, 'a_15')] <= M * a_status_15
    opt_model += production[(hour, 'a_30')] <= M * a_status_30 
    opt_model += production[(hour, 'a_45')] <= M * a_status_45
    opt_model += production[(hour, 'a_60_below53')] + production[(hour, 'a_flow_above53')] <= M * a_status_60

    # a_60_above53 can only be active if a_60_below53 == 52
    opt_model += production[(hour, 'a_flow_above53')] <= M * options_status[(hour, 'a_flow_above53')]
    opt_model += (52 - production[(hour, 'a_flow_below53')] ) <= M * (1-options_status[(hour, 'a_flow_above53')])

# only 1 'mode' can be used per day (corresponding to daily fee)
opt_model += a_status_15 + a_status_30 + a_status_45 + a_status_60 <= 1
#生产总额==需求
以小时为单位的小时:
opt_model+=生产[(小时,'o')]+生产[(小时,'l_总计')]+生产[(小时,'a_总计')]==int(demand.loc[小时,'demand']))
#最小生产能力
以小时为单位的小时:
对于options.index中的选项:
opt_模型+=生产[(小时,选项)]>=选项_状态[(小时,选项)]
opt_model+=生产[(小时,选项)]>=options.loc[选项,“最小容量”]*选项_状态[(小时,选项)]

opt_模型+=生产[(小时,选项)]LP问题是NP难问题。Python对于您的问题来说可能太慢了。您可以考虑以下建议中的一个或多个(按您的附加工作顺序)

  • 减少约束的数量,它可以更快地生成解决方案。消除不可信的解决方案

  • 通过组合输入或约束来重新建模您的问题

  • 使用其他可能更快的软件或库。您可以考虑CPLEX或GLPK。


  • LP问题的求解速度非常快,除非你有大量的变量。正是整数和二进制变量减慢了速度

    最有可能是你的
    生产
    变量杀死了你。我建议将这些变量视为线性/连续变量(“线性松弛”),以快速获得解决方案(然后可以向下/向上取整以获得可行的解决方案)

    另一个选项可能是增加允许的最佳性差距,或设置超时限制。默认的最优性差距将非常小,这意味着MILP的分支和边界解算器将继续运行。求解者通常会相对较快地获得好的解,然后花费大量时间试图证明解的最优性。-您可以为解算器提供超时以获得近似解:


    prob.solve(palp.solvers.palp\u CBC\u CMD(maxSeconds=120))

    LP问题的求解速度非常快,除非变量数量非常多。正是整数和二进制变量减慢了速度。在我看来,是你的
    生产
    变量害死了你。我建议将这些变量视为线性/连续变量(“线性松弛”),以快速获得解决方案(然后可以向下/向上取整以获得可行的解决方案)。另一种选择可能是增加允许的最优性差距。默认值将非常低,这意味着MILP的分支和绑定解算器将继续运行。谢谢你,Kabdulla,谢谢你的帮助!你的建议真的解决了我的问题性能问题:)很高兴听到有帮助的评论。我将粘贴它作为答案,以便您可以接受:)LP问题在P中。