Pyomo中基于起始变量关联调度变量的线性约束语法实现求助
Pyomo线性规划优化问题求解
背景与问题
之前采用@Airsquid的方案已实现问题的正确求解,但在能源供应量远小于请求数的场景下,求解时长过长。已添加早停设置:
solver.options = { 'sec': 600, 'threads':6, 'ratio': 5} #early stopping settings
但希望通过重构代码进一步优化。
根据@Airsquid在参考资料[2]中的建议:可通过start变量确定请求的运行状态,无需额外辅助变量,仅需跟踪每个请求的功率分配变量。本人作为线性规划及Pyomo新手,在语法实现上遇到困难。
尝试的方法及问题
方法1
尝试定义当m.start[t,r] == 1时,m.dispatch[t,r]在对应时长内等于请求功率,编写的约束代码如下:
@m.Constraint(m.windows_flat) def request_length(m, t, r): request_length_periods = int(m.duration_periods[r]) request_periods = set(list(range(t,t+request_length_periods+1))) return (m.dispatch[t,r] == m.request_power_size[r] for t in request_periods if m.start[t,r] == 1)
运行后报错:
ERROR: Rule failed when generating expression for Constraint request_length with index (910192, 88): ValueError: Constraint 'request_length[910192,88]' does not have a proper value. Found '<generator object generalOptimisation.<locals>.request_length.<locals>.<genexpr> at 0x168022b20>' Expecting a tuple or relational expression. Examples: sum(model.costs) == model.income (0, model.price[item], 50) ERROR: Constructing component 'request_length' from data=None failed: ValueError: Constraint 'request_length[910192,88]' does not have a proper value. Found '<generator object
已知request_periods的定义方式不正确,但不知如何在未知m.start[t,r] == 1时机的前提下正确定义该集合。
方法2
尝试用m.start[t,r] *(m.dispatch[t,r]...m.dispatch[t+request_length_periods,r]) == 1,但不清楚Pyomo语法,且该方式会导致问题非线性。
解决方案
线性约束实现(符合@Airsquid建议)
要通过start变量线性约束dispatch的取值,需拆解为两个核心约束:
- 请求启动互斥约束:确保每个请求最多被启动一次
@m.Constraint(m.R) def single_start_per_request(m, r): duration = int(m.duration_periods[r]) # 筛选请求r所有合法的启动时段(启动后能完整运行整个时长) valid_starts = [t for t in m.T if t + duration <= m.T[-1] + 1] return sum(m.start[t, r] for t in valid_starts) <= 1
- 启动后的功率锁定约束:当请求在
t时段启动时,后续duration个时段的dispatch必须等于请求功率。采用线性的大M法实现:
@m.Constraint(m.T, m.R) def dispatch_lock_after_start(m, t_prime, r): duration = int(m.duration_periods[r]) # 找到所有能覆盖当前时段t'的合法启动时段 valid_starts = [t for t in m.T if t <= t_prime < t + duration] # 设置足够大的M值(建议取请求功率最大值的2倍) M = max(m.request_power_size[r] for r in m.R) * 2 if not valid_starts: # 若当前时段不在任何请求的运行窗口内,dispatch必须为0(可根据业务需求调整) return m.dispatch[t_prime, r] == 0 else: # 线性约束:当任意一个valid_start的start变量为1时,dispatch必须等于请求功率 return ( m.dispatch[t_prime, r] >= m.request_power_size[r] - M * (1 - sum(m.start[t, r] for t in valid_starts)), m.dispatch[t_prime, r] <= m.request_power_size[r] + M * (1 - sum(m.start[t, r] for t in valid_starts)) )
早停参数优化建议
sec:当前设置600秒(10分钟),大规模问题可先测试缩短到300秒,观察解的质量;若质量下降明显再逐步延长。threads:设置为CPU核心数的70%-80%(如8核CPU设为6是合理的),避免线程竞争拖慢效率。ratio:当前设置5(找到比最优解差5%以内的解就停止),对精度要求不高可提高到10以缩短时间;要求高精度则降低到2-3,但会增加求解时长。
内容的提问来源于stack exchange,提问作者user23390596
相关产品推荐
相关产品推荐

