Pyomo:根据条件定义 objective 规则

Pyomo: define objective Rule based on condition

在传输问题中,我尝试将以下规则插入到 objective 函数中:

如果BC的供应量<19,000吨,那么我们将有$125/MT的罚款

我添加了一个约束来检查条件,但想在 objective 函数中应用惩罚。

我可以在 Excel Solver 中做到这一点,但是值不匹配。我已经检查了两者,并调试了代码,但我无法找出问题所在。

约束条件如下:

def bc_rule(model):
    return sum(model.x[supplier, market] for supplier in model.suppliers \
                                       for market in model.markets \
                                       if 'BC' in supplier) >= 19000  
model.bc_rules = Constraint(rule=bc_rule, doc='Minimum production')

问题出在 objective 规则中:

def objective_rule(model):
    PENALTY_THRESHOLD = 19000
    PENALTY_COST = 125

    cost = sum(model.costs[supplier, market] * model.x[supplier, market] for supplier in model.suppliers for market in model.markets)

    # what is the problem here?
    bc = sum(model.x[supplier, market] for supplier in model.suppliers \
                                       for market in model.markets \
                                       if 'BC' in supplier)
    if bc < PENALTY_THRESHOLD:
        cost += (PENALTY_THRESHOLD - bc) * PENALTY_COST

    return cost

model.objective = Objective(rule=objective_rule, sense=minimize, doc='Define objective function')

我得到的值比 Excel 求解器中的值低得多。

您的情况 (if) 取决于模型中的一个变量。

通常,ifs 不应该用在数学模型中,这不仅适用于 Pyomo。即使在 Excel 中,如果公式中的语句在优化之前只是简单地转换为标量值,所以在说它是真正的最优值时我会非常小心。

好消息是 if 语句很容易转换为数学约束。

为此,您需要向模型添加一个二进制变量 (0/1)。如果bc <= PENALTY_TRESHOLD,它将取值1。我们称这个变量为y,定义为model.y = Var(domain=Binary)

您将添加 model.y * PENALTY_COST 作为 objective 函数的一项,以包含惩罚成本。

然后,对于约束,添加如下代码:

def y_big_M(model):
    bigM = 10000 # Should be a big number, big enough that it will be bigger than any number in your 
                 # model, but small enough that it will stay around the same order of magnitude. Avoid 
                 # utterly big number like 1e12 and + if you don't need to, since having numbers too 
                 # large causes problems. 
    PENALTY_TRESHOLD = 19000
    return PENALTY_TRESHOLD - sum(
        model.x[supplier, market]
        for supplier in model.suppliers
        for market in model.markets
        if 'BC' in supplier
    ) <= model.y * bigM
model.y_big_M = Constraint(rule=y_big_M)

前面的约束确保当计算 bc 的总和小于 PENALTY_TRESHOLD 时,y 将取大于 0 的值(即 1)。此差值的任何大于 0 的值都将强制模型将 1 放入变量 y 的值中,因为如果 y=1,约束的右侧将是 1 * bigM , 这是一个非常大的数字,大到 bc 总是小于 bigM.

请同时检查您的 Excel 模型,看看您的 if 语句在求解器计算期间是否真的有效。上次我检查时,Excel 求解器不会将 if 语句转换为 bigM 约束。我向您展示的建模技术绝对适用于所有编程方法,即使在 Excel.

中也是如此