使用 Google OR 工具设置二元约束

Setting Binary Constraints with Google OR-tools

我一直在使用 OR 工具,特别是在查看它在调度方面的用途。我觉得我现在已经掌握了这个库,尽管 Google 的主要示例 ( https://github.com/google/or-tools/blob/master/examples/python/shift_scheduling_sat.py ) 有一个方面我无法理解。我遇到问题的功能是:add_soft_sequence_constraint() 和相关的:negated_bounded_span(相关代码在下面)。

这些是为了限制一个人可以连续工作的轮班次数,但我不知道这是如何实现的。

我的问题是:使用 .Not() 的结果究竟是什么?我无法找到关于它的任何文档或为其生成清晰的测试。为什么 negated_bounded_space()(一个依赖于 .Not() 的函数)是必需的?最后,在 add_soft_sequence_constraint 的两种情况下,它如何知道您处理一个不允许的长序列(即连续 6 个班次)和 2 个较短的序列(4 个班次,一个休息,然后 3 个)之间的区别shifts) 这可能是允许的,但加起来与长序列相同(或更多)?

如果能提供任何帮助,我将不胜感激,我希望能够使用和改编代码,但在正确理解代码之前这样做我感到不舒服。

def negated_bounded_span(works, start, length):
    sequence = []
    # Left border (start of works, or works[start - 1])
    if start > 0:
        sequence.append(works[start - 1])
    for i in range(length):
        sequence.append(works[start + i].Not())
    # Right border (end of works or works[start + length])
    if start + length < len(works):
        sequence.append(works[start + length])
    return sequence

def add_soft_sequence_constraint(model, works, hard_min, soft_min, min_cost,
                                 soft_max, hard_max, max_cost, prefix):

    # Forbid sequences that are too short.
    for length in range(1, hard_min):
        for start in range(len(works) - length - 1):
            model.AddBoolOr(negated_bounded_span(works, start, length))


    # Just forbid any sequence of true variables with length hard_max + 1
    for start in range(len(works) - hard_max - 1):
        model.AddBoolOr(
            [works[i].Not() for i in range(start, start + hard_max + 1)])

Not() 是布尔变量的否定。

参见 https://en.wikipedia.org/wiki/Boolean_satisfiability_problem.

主要思想是,如果你想禁止给定的模式:

v0 = 假,v1 = 真,v2 = 真,v3 = 假

即从位置1开始长度为2的序列,你加一个BoolOr指定v0为真,或v1为假,或v2为假,或v3为真。

如果这些条件中的任何一个为真,则此特定模式不存在。

这写成

BoolOr([v0, v1.Not(), v2.Not(), v3])

.

详细说明Laurent的回答:

如果您想在长度为 4 的列表中避免长度为 2 的序列:

  • [1,1,0,0] -> BoolOr[v0.Not(),v1.Not(),v2]
  • [0,1,1,0] -> BoolOr[v0, v1.Not(), v2.Not(), v3]
  • [0,0,1,1] -> BoolOr[v1, v2.Not(), v3.Not()]

我也开了一期Githubhttps://github.com/google/or-tools/issues/1399,作为行

for start in range(len(works) - length - 1):

可能有误

长度为 4 且 hard min 为 3 的列表的小示例:

from ortools.sat.python import cp_model


def negated_bounded_span(works, start, length):
    sequence = []
    # Left border (start of works, or works[start - 1])
    if start > 0:
        sequence.append(works[start - 1])
    for i in range(length):
        sequence.append(works[start + i].Not())
    # Right border (end of works or works[start + length])
    if start + length < len(works):
        sequence.append(works[start + length])
    return sequence


if __name__ == '__main__':
    model = cp_model.CpModel()
    works = [model.NewBoolVar(f'{i}') for i in range(4)]
    for length in range(1, 3):
        print(f'Length {length}')
        for start in range(len(works) - length + 1):
            print(negated_bounded_span(works, start, length))

Returns 类似于:

Length 1
[0.Not(), 1(0..1)]
[0(0..1), 1.Not(), 2(0..1)]
[1(0..1), 2.Not(), 3(0..1)]
[2(0..1), 3.Not()]
Length 2
[0.Not(), 1.Not(), 2(0..1)]
[0(0..1), 1.Not(), 2.Not(), 3(0..1)]
[1(0..1), 2.Not(), 3.Not()]