Pyomo混合整数规划:如何让二进制变量首次达阈值时置1?
问题描述
需要定义一个二进制变量$inv_year[i,t]$,用于标记项目$i$的首次投资年份——也就是投资变量$I[i,t]$首次大于0的时间点。要求每个项目$i$的$inv_year$仅在首次投资时取值为1,其余时间均为0。
当前使用Big M方法实现时,$inv_year[i,t]$会在$I[i,t]$每次大于0时都置1,导致一个项目多次触发该变量,不符合需求。现有Pyomo代码如下:
#First constraint, Big-M method for binary variable inv_year def bigM_constraint1(model, i, t): lhs_1 = model.inv_year[i,t] rhs_1 = model.I[i,t] return lhs_1<=rhs_1 #add the rule to the model as a constraint model.BigM_Constraint1 = Constraint(model.project_type, model.time, rule=bigM_constraint1) #Second constraint, Big-M method for binary variable inv_year def bigM_constraint2(model, i, t): lhs_2 = model.I[i,t] rhs_2 = model.M[i] * model.inv_year[i,t] return lhs_2<=rhs_2 #add the rule to the model as a constraint model.BigM_Constraint2 = Constraint(model.project_type, model.time, rule=bigM_constraint2)
解决方案
可以通过补充约束条件调整Big M方法,实现仅标记首次投资年份的需求,无需更换方法。需要新增两类核心约束:
1. 限制每个项目仅触发一次$inv_year$
确保每个项目$i$的所有时间点$inv_year[i,t]$之和不超过1,避免多次标记:
def single_investment_constraint(model, i): return sum(model.inv_year[i,t] for t in model.time) <= 1 model.Single_Investment_Constraint = Constraint(model.project_type, rule=single_investment_constraint)
2. 强制首次投资后不再触发$inv_year$
确保一旦某个时间点$t$的$inv_year[i,t]$为1,所有后续时间点$t' > t$的$inv_year[i,t']$必须为0。通过累加前置时间点的$inv_year$实现:
def no_subsequent_inv_constraint(model, i, t): if t == model.time.first(): return Constraint.Skip # 累加截至t-1的inv_year,若已标记过投资,后续不能再标记 return sum(model.inv_year[i,t_prev] for t_prev in model.time if t_prev < t) >= model.inv_year[i,t] model.No_Subsequent_Inv_Constraint = Constraint(model.project_type, model.time, rule=no_subsequent_inv_constraint)
3. 可选:强制投资必须标记首次年份
如果需要避免“有投资发生但$inv_year$全为0”的情况,可添加以下约束:
def inv_implies_mark_constraint(model, i): # M_total[i]为项目i所有时间点I的最大可能总和,需提前定义 return sum(model.I[i,t] for t in model.time) <= model.M_total[i] * sum(model.inv_year[i,t] for t in model.time) model.Inv_Implies_Mark_Constraint = Constraint(model.project_type, rule=inv_implies_mark_constraint)
原有的两个Big M约束已保证:$inv_year[i,t]=1$时$I[i,t]>0$,且$I[i,t]>0$时$inv_year[i,t]$可取值1。结合新增约束后,就能严格实现“仅在首次投资时标记$inv_year$”的需求。
内容的提问来源于stack exchange,提问作者Julia Schepp
相关产品推荐
相关产品推荐

