You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在OR-Tools中为Interval变量编写替代约束及相关疑问

OR-Tools中Interval变量的替代约束与激活状态判断

一、实现替代约束(替代不存在的AddAlternative方法)

OR-Tools的CP-SAT模块没有AddAlternative方法,要实现“一个区间变量由多个替代区间选其一”的逻辑,需要结合**可选区间变量(OptionalIntervalVar)**和布尔变量构建约束,核心逻辑如下:

  1. 将主区间或替代区间设为可选区间,用布尔变量控制激活状态;
  2. 确保同一时间仅一个区间处于激活状态;
  3. 绑定激活区间的start/end/duration与主区间的对应变量(如需同步时间属性)。

修正后的示例代码

from ortools.sat.python import cp_model

model = cp_model.CpModel()

# 定义时间范围常量
time_min = 0
time_max = 100

# 主区间:设为可选,用布尔变量控制激活
main_start = model.NewIntVar(time_min, time_max, 'main_start')
main_duration = model.NewIntVar(1, time_max, 'main_duration')
main_end = model.NewIntVar(time_min, time_max, 'main_end')
main_present = model.NewBoolVar('main_present')
main_interval = model.NewOptionalIntervalVar(main_start, main_duration, main_end, main_present, 'main_interval')

# 替代区间1:可选区间
alt1_start = model.NewIntVar(time_min, time_max, 'alt1_start')
alt1_duration = model.NewIntVar(5, 20, 'alt1_duration')
alt1_end = model.NewIntVar(time_min, time_max, 'alt1_end')
alt1_present = model.NewBoolVar('alt1_present')
alt1_interval = model.NewOptionalIntervalVar(alt1_start, alt1_duration, alt1_end, alt1_present, 'alt1_interval')

# 替代区间2:可选区间
alt2_start = model.NewIntVar(time_min, time_max, 'alt2_start')
alt2_duration = model.NewIntVar(10, 30, 'alt2_duration')
alt2_end = model.NewIntVar(time_min, time_max, 'alt2_end')
alt2_present = model.NewBoolVar('alt2_present')
alt2_interval = model.NewOptionalIntervalVar(alt2_start, alt2_duration, alt2_end, alt2_present, 'alt2_interval')

# 约束1:仅激活主区间或其中一个替代区间
model.AddBoolOr([main_present, alt1_present, alt2_present])
model.AddAtMostOne([main_present, alt1_present, alt2_present])

# 约束2:激活替代区间1时,同步主区间的时间属性
model.Add(main_start == alt1_start).OnlyEnforceIf(alt1_present)
model.Add(main_duration == alt1_duration).OnlyEnforceIf(alt1_present)
model.Add(main_end == alt1_end).OnlyEnforceIf(alt1_present)

# 约束3:激活替代区间2时,同步主区间的时间属性
model.Add(main_start == alt2_start).OnlyEnforceIf(alt2_present)
model.Add(main_duration == alt2_duration).OnlyEnforceIf(alt2_present)
model.Add(main_end == alt2_end).OnlyEnforceIf(alt2_present)

# 求解
solver = cp_model.CpSolver()
status = solver.Solve(model)

if status == cp_model.OPTIMAL or status == cp_model.FEASIBLE:
    print(f"主区间激活状态: {solver.Value(main_present)}")
    print(f"替代区间1激活状态: {solver.Value(alt1_present)}")
    print(f"替代区间2激活状态: {solver.Value(alt2_present)}")

适配你的实际代码

你的代码中y是强制激活的可选区间,若要让y作为x的替代区间,需修改y的激活控制为布尔变量,再添加约束:

# 修改y的定义:将is_present改为布尔变量
y = [
    model.NewOptionalIntervalVar(
        start=model.NewIntVar(row.dia_inicio, row.dia_fin, f"start_{idx}"),
        size=row.tiempo_total,
        end=model.NewIntVar(row.dia_inicio, row.dia_fin, f"end_{idx}"),
        is_present=model.NewBoolVar(f"y_present_{idx}"),  # 改为布尔变量控制激活
        name="pte_intv_{}".format(idx),
    )
    for idx, row in pozo_time_equipment.iterrows()
]

# 以x[0]为例,绑定对应的y区间作为替代
# 1. 将原x[0]转为可选区间(允许被替代)
x0_start = x[0].StartExpr()
x0_duration = x[0].DurationExpr()
x0_end = x[0].EndExpr()
x0_present = model.NewBoolVar("x0_present")
x0_optional = model.NewOptionalIntervalVar(x0_start, x0_duration, x0_end, x0_present, "x0_optional")

# 2. 选取对应的y区间(比如y[0]和y[1])作为替代
y0_present = y[0].IsPresentExpr()
y1_present = y[1].IsPresentExpr()

# 约束:x0、y0、y1只能激活一个
model.AddBoolOr([x0_present, y0_present, y1_present])
model.AddAtMostOne([x0_present, y0_present, y1_present])

# 约束:激活y0时同步x0的时间属性
model.Add(x0_start == y[0].StartExpr()).OnlyEnforceIf(y0_present)
model.Add(x0_duration == y[0].DurationExpr()).OnlyEnforceIf(y0_present)
model.Add(x0_end == y[0].EndExpr()).OnlyEnforceIf(y0_present)

# 约束:激活y1时同步x0的时间属性
model.Add(x0_start == y[1].StartExpr()).OnlyEnforceIf(y1_present)
model.Add(x0_duration == y[1].DurationExpr()).OnlyEnforceIf(y1_present)
model.Add(x0_end == y[1].EndExpr()).OnlyEnforceIf(y1_present)

二、判断Interval变量的激活状态

  • 普通IntervalVar(NewIntervalVar创建):这类变量始终处于激活状态,不存在未激活情况,无需判断。
  • 可选IntervalVar(NewOptionalIntervalVar创建):通过其绑定的is_present布尔变量判断,求解后调用solver.Value(is_present_var),返回1表示激活,0表示未激活。

示例代码

# 获取y[0]对应的激活状态布尔变量
y0_present = y[0].IsPresentExpr()
solver = cp_model.CpSolver()
status = solver.Solve(model)

if status == cp_model.OPTIMAL or status == cp_model.FEASIBLE:
    if solver.Value(y0_present) == 1:
        print(f"区间{y[0].Name()}已激活")
        print(f"开始时间: {solver.Value(y[0].StartExpr())}")
        print(f"结束时间: {solver.Value(y[0].EndExpr())}")
    else:
        print(f"区间{y[0].Name()}未激活")

内容的提问来源于stack exchange,提问作者Sebastian Jose

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 04:03:19