python-constraint:根据函数的输出设置约束

时间:2011-02-15 16:29:43

标签: python constraints shortest-path

我一直在制作一个系统,该系统可以获取有关驾驶员,潜在乘客及其位置的数据,并尝试优化可以通过驾驶员获得升力的乘客数量。我正在使用python-constraint模块,决策变量就这样表示:

p = [(passenger, driver) for driver in drivers for passenger in passengers]
driver_set = [zip(passengers, [e1]*len(drivers)) for e1 in drivers]
passenger_set = [zip([e1]*len(passengers), drivers) for e1 in passengers]
self.problem.addVariables(p, [0,1])

因此,当我打印p的值以及driver_set和passenger_set时,我得到以下输出(给定我提供的测试数据):

[(0, 0), (1, 0), (2, 0), (0, 1), (1, 1), (2, 1)] # p
[[(0, 0), (0, 1)], [(1, 0), (1, 1)], [(2, 0), (2, 1)]] # passenger_set
[[(0, 0), (1, 0)], [(0, 1), (1, 1)]] # driver_set

因此,有3名乘客和2名司机:变量(2,0)意味着乘客2在车0中,依此类推。我添加了以下限制条件,以确保没有乘客乘坐多辆车,并且司机不能拥有比座位更多的人员:

for passenger in passenger_set:
        self.problem.addConstraint(MaxSumConstraint(1), passenger)
for driver in driver_set:
        realdriver = self.getDriverByOpId(driver[0][1])
        self.problem.addConstraint(MaxSumConstraint(realdriver.numSeats), driver)

这很有效 - 所有产生的解决方案都满足了这些限制。但是,我现在想添加约束条件,说任何解决方案都不应该让驱动程序超过一定距离。我有一个功能,它接受一个驱动程序(与driver_set中的实体格式相同)并计算驾驶员接载所有乘客的最短距离。我试图添加这样的约束:

for driver in driver_set:
        self.problem.addConstraint(MaxSumConstraint(MAX_DISTANCE), [self.getRouteDistance(self.getShortestRoute(driver))])

这给出了以下错误:

KeyError: 1.8725031790578293

我不确定如何为python-constraint定义此约束:每个驱动程序只有一个最短距离值。我应该使用lambda函数吗?

修改

我尝试实现这个的lambda版本,但我似乎没有lambda语法。我到处寻找,但似乎无法找到这个有什么问题。基本上我替换了最后一段代码(添加约束来限制getRouteDistance(驱动程序)的值),而是把它放在:

for driver in driver_set:
    self.problem.addConstraint(lambda d: self.getRouteDistance(d) <= float(MAX_DISTANCE), driver)

但后来我收到了这个错误(注意它没有从我编辑的行调用,它来自于之后的problem.getSolutions()):

File "allocation.py", line 130, in buildProblem
for solution in self.problem.getSolutions():
File "/Users/wadben/Documents/Dev/Python/sp-allocation/constraint.py", line 236, in getSolutions
return self._solver.getSolutions(domains, constraints, vconstraints)
File "/Users/wadben/Documents/Dev/Python/sp-allocation/constraint.py", line 529, in getSolutions
return list(self.getSolutionIter(domains, constraints, vconstraints))
File "/Users/wadben/Documents/Dev/Python/sp-allocation/constraint.py", line 506, in getSolutionIter
pushdomains):
File "/Users/wadben/Documents/Dev/Python/sp-allocation/constraint.py", line 939, in __call__
self.forwardCheck(variables, domains, assignments)))
File "/Users/wadben/Documents/Dev/Python/sp-allocation/constraint.py", line 891, in forwardCheck
if not self(variables, domains, assignments):
File "/Users/wadben/Documents/Dev/Python/sp-allocation/constraint.py", line 940, in __call__
return self._func(*parms)
TypeError: <lambda>() takes exactly 1 argument (3 given)

有没有其他人试图做这样的事情?我不明白为什么约束库不允许这样做。

1 个答案:

答案 0 :(得分:3)

Python中的lambda表单提供了一种创建匿名(无名)函数的方法。以下两个定义是等效的:

name = lambda arguments: expression

def name(arguments):
    return expression

由于lambda表达式的主体本身就是一个表达式,因此正文可能不包含任何语句(如print)。

在为问题添加函数约束时,必须确保函数接受与变量一样多的参数。当应用约束时,每个参数都会传递一个值(根据您的惯例,1对于驾驶员和乘客一起乘坐,否则为0)当前绑定到相应的变量。

由于与给定驾驶员相关的变量数量(等于乘客数量)可能会发生变化,因此约束中的函数接受任意数量的参数是明智的。这可以使用位置参数在Python中完成。因此,对于给定的一组驱动程序变量(在此处使用名称driver_variables),约束采用以下形式:

problem.addConstraint(FunctionConstraint(lambda *values: ...), driver_variables)

参数值绑定到当前绑定到driver_variables列表中相应变量的值列表。应该编写lambda体,以便执行以下操作:

  1. 创建一个列表,将值列表中的每个值(0或1)与driver_variables列表中的相应变量相关联;
  2. 从此列表中选择值为1的变量(对应乘坐驾驶员的乘客) - 此列表构成驾驶员所采用的路线;
  3. 找到路线距离(在此示例中使用get_route_distance函数)并与最大值(maximum_distance)进行比较。
  4. 可以使用zip作为(1)(值的顺序保证与变量的顺序相同),列表理解为(2)以及简单的函数调用和比较(3)。这产生了一个采用以下lambda形式的函数:

    lambda *values: get_route_distance([variable for variable, value in zip(driver_variables, values) if value == 1]) <= maximum_distance
    

    使用def。

    显式编写此函数可能有助于代码的可读性

    另外,在代码中有一个错误,用于定义上面的driver_set。 driver_set的正确值应为:

    [[(0, 0), (1, 0), (2, 0)], [(0, 1), (1, 1), (2, 1)]]
    

    在上面的示例中,由于len(驱动程序)为2,因此zip(乘客,[e1] * len(驱动程序))仅截断为两个项目。解决这个问题的一种方法是使用表达式zip(乘客,[e1] * len(乘客))作为driver_set(并对passenger_set进行类似的更改)。然而,有更多的Pythonic方式。

    可以使用以下语句生成正确的乘客和驾驶员集合(本示例中为passengers_variables和drivers_variables):

    passengers_variables = [[(passenger, driver) for driver in drivers] for passenger in passengers]
    drivers_variables = [[(passenger, driver) for passenger in passengers] for driver in drivers]