python-constraint Allequal

时间:2012-08-29 11:49:29

标签: python constraints

这个问题是关于python包约束(参见http://labix.org/python-constraint),特别是内置的“ AllEqualConstraint ”。在一个问题 涉及4个变量,我想强制执行前两个和 第二个是平等的,即:

p = Problem()
p.addVariables([1,2,3,4], [0,1])
p.addConstraint(AllEqualConstraint(), [1,2])
p.addConstraint(AllEqualConstraint(), [3,4])

我只得到两个解决方案:

for sol in p.getSolutions():
  print sol

> {1: 1, 2: 1, 3: 1, 4: 1}
> {1: 0, 2: 0, 3: 0, 4: 0}

我希望看到四个,即:

> {1: 1, 2: 1, 3: 1, 4: 1}
> {1: 1, 2: 1, 3: 0, 4: 0}
> {1: 0, 2: 0, 3: 1, 4: 1}
> {1: 0, 2: 0, 3: 0, 4: 0}

我的问题是:任何人都可以确认这是包打算计算的内容以及 背后的推理是?

Ps:我已经联系了这个软件包的作者,但还没有得到答复。我知道这个包是众所周知的,之前在StackOverflow上有过关于它的问题。

回答LVC : 约束 总是将约束应用于所有变量

p = Problem()
p.addVariables([1,2,3], [0,1])
p.addConstraint(AllEqualConstraint(), [1,2])

给出

> {1: 1, 2: 1, 3: 1}
> {1: 1, 2: 1, 3: 0}
> {1: 0, 2: 0, 3: 1}
> {1: 0, 2: 0, 3: 0}

正如所料。如果AllEqualConstraint不尊重变量,那将非常有限。

2 个答案:

答案 0 :(得分:2)

模块的source code包含了AllEqualConstraint的内容:

def __call__(self, variables, domains, assignments, forwardcheck=False,
             _unassigned=Unassigned):
    singlevalue = _unassigned
    for value in assignments.values():
        if singlevalue is _unassigned:
            singlevalue = value
        elif value != singlevalue:
            return False

在每个Solver中调用此代码的代码执行此操作:

for constraint, variables in vconstraints[variable]:
    if not constraint(variables, domains, assignments,
                      pushdomains):
        # Value is not good.
        break

assignments包含所有变量的所有分配,而不仅仅是受约束影响的变量。这意味着AllEqualConstraint并不关心放在addConstraint中的变量 - 它总是测试潜在解决方案中的所有变量是否相等,这就是为什么你错过了两个解决方案的原因事实并非如此。

唯一一次AllEqualConstraint查看variables参数的时间是forwardcheck调用它的时候。在这种情况下,它会这样做:

if forwardcheck and singlevalue is not _unassigned:
    for variable in variables:
        if variable not in assignments:
            domain = domains[variable]
            if singlevalue not in domain:
                return False
            for value in domain[:]:
                if value != singlevalue:
                    domain.hideValue(value)

但是,所提供的求解器似乎都没有使用除默认值之外的任何forwardcheck值来调用约束 - 在AllEqualConstraint的情况下,FalseFunctionConstraint

所以,你必须指定你自己的手动约束 - 但这不是太难,因为它们只能是采用适当数量的变量的函数(它包含在Constraint.__call__中,所以你不需要关心传递给p.addConstraint(operator.eq, [1, 2]) p.addConstraint(operator.eq, [3, 4]) 的所有其他内容。

因此,您想要的约束是一个函数,它接受两个变量,并返回它们是否相等。 operator.eq非常适合这个法案:

def all_equal(a, b, c):
    return a == b == c

应该为您提供您正在寻找的结果。

因此将其缩放为两个以上的变量,您可以编写自己的函数:

def all_equal(*vars):
    if not vars:
       return True
    return all(var == var[0] for var in vars)

或者,更一般地说:

{{1}}

答案 1 :(得分:1)

[评论太长了。]

您所做的编辑并未表明AllEqualConstraint尊重变量限制,只是它并不总是设法完全忽略它们。 @lvc注意到的代码段做了他所说的。如果您对其进行检测,则可以看到(使用abcd作为变量名称,以便明确)

from constraint import *
p = Problem()
p.addVariables(list("abcd"), [0, 1])
p.addConstraint(AllEqualConstraint(), list("ab"))
p.addConstraint(AllEqualConstraint(), list("cd"))
print p.getSolutions()

最终产生

variables: ['c', 'd']
domains: {'a': [0, 1], 'c': [0, 1], 'b': [1], 'd': [1, 0]}
assignments: {'a': 1, 'c': 0, 'b': 1}
forwardcheck: [[1, 0]]
_unassigned: Unassigned
setting singlevalue to 1 from variable a
returning False because assigned variable c with value 0 != 1

事实上,正如@lvc所说,它强加了a应该等于c的约束,即使它不应该。我认为相关循环应该更像是

    for key, value in assignments.items():
        if key not in variables: continue

产生

[{'a': 1, 'c': 1, 'b': 1, 'd': 1}, {'a': 1, 'c': 0, 'b': 1, 'd': 0},
 {'a': 0, 'c': 1, 'b': 0, 'd': 1}, {'a': 0, 'c': 0, 'b': 0, 'd': 0}]

虽然我不知道代码是如何工作的,所以很难确定。这对我来说就像一个错误。这似乎不太可能,但它看起来不像AllEqualConstraint在代码库中的任何地方使用(在所有示例中都没有),除了它自己的docstring,其中示例测试每个变量参与的情况约束,因此错误不会出现。