Python 用延迟约束回调实现TSP

Python 用延迟约束回调实现TSP,python,cplex,docplex,docplexcloud,Python,Cplex,Docplex,Docplexcloud,我正在尝试使用惰性约束回调来实现TSP。根据给出的答案和,我尝试使用链接中的代码,并能够添加回拨功能。现在,我正在努力解决添加惰性约束问题 这是我当前的代码:它是一个9节点的TSP import docplex.mp.model as cpx from cplex.callbacks import LazyConstraintCallback from docplex.mp.callbacks.cb_mixin import * class DOLazyCallback(Constraint

我正在尝试使用惰性约束回调来实现TSP。根据给出的答案和,我尝试使用链接中的代码,并能够添加回拨功能。现在,我正在努力解决
添加惰性约束问题

这是我当前的代码:它是一个9节点的TSP

import docplex.mp.model as cpx
from cplex.callbacks import LazyConstraintCallback
from docplex.mp.callbacks.cb_mixin import *


class DOLazyCallback(ConstraintCallbackMixin, LazyConstraintCallback):
    def __init__(self, env):
        LazyConstraintCallback.__init__(self, env)
        ConstraintCallbackMixin.__init__(self)
        self.nb_lazy_cts = 0

    def add_lazy_constraints(self, cts):
        self.register_constraints(cts)

    @print_called('--> lazy constraint callback called: #{0}')
    def __call__(self):
        # fetch variable values into a solution
        sol = self.make_solution()
        # for each lazy constraint, check whether it is verified,
        unsats = self.get_cpx_unsatisfied_cts(self.cts, sol, tolerance=1e-6)
        for ct, cpx_lhs, sense, cpx_rhs in unsats:
            self.add(cpx_lhs, sense, cpx_rhs)
            self.nb_lazy_cts += 1
            print('  -- new lazy constraint[{0}]: {1!s}'.format(self.nb_lazy_cts, ct))


DST = [[0, 0.238, 0.608, 0.5442, 0.6097, 1.2337, 0.5574, 0.8691, 1.3394],
       [0.238, 0, 0.37, 0.6694, 0.6039, 0.9957, 0.6826, 0.8633, 1.23],
       [0.608, 0.37, 0, 1.0394, 0.9739, 0.6257, 1.0526, 1.2333, 0.860],
       [0.5442, 0.6694, 1.0394, 0, 0.0655, 0.903, 0.0132, 0.3249, 0.7952],
       [0.6097, 0.6039, 0.9739, 0.0655, 0, 0.8375, 0.0787, 0.2594, 0.7297],
       [1.2337, 0.9957, 0.6257, 0.903, 0.8375, 0, 0.9162, 0.7046, 0.2343],
       [0.5574, 0.6826, 1.0526, 0.0132, 0.0787, 0.9162, 0, 0.3381, 0.8084],
       [0.8691, 0.8633, 1.2333, 0.3249, 0.2594, 0.7046, 0.3381, 0, 0.4703],
       [1.3394, 1.23, 0.860, 0.7952, 0.7297, 0.2343, 0.8084, 0.4703, 0]]

n = 9

set_n = range(9)
opt_model = cpx.Model(name="MIP Model")

x = {(i, j): opt_model.binary_var(name="x_{0}_{1}".format(i, j)) for i in set_n for j in set_n if not i == j}

objective = opt_model.sum(DST[i][j] * x[i, j] for i in set_n for j in set_n if not i == j)

# one incoming edge one outgoing edge
for i in set_n:
    xp = opt_model.sum(x[j, i] for j in set_n if not i == j) - opt_model.sum(x[i, k] for k in set_n if not i == k)
    opt_model.add_constraint(xp == 0)

for j in set_n:
    opt_model.add_constraint(opt_model.sum(x[i, j] for i in set_n if not i == j) == 1)

lazyct_cb = opt_model.register_callback(DOLazyCallback)

lazyct_cb.add_lazy_constraints( ?? WHAT TO ADD HERE ?? )


opt_model.lazy_callback = lazyct_cb

url = "URLL"
api = "APII"

#opt_model.parameters.mip.tolerances.mipgap = 0
opt_model.minimize(objective)

print(opt_model.print_information())

solv = opt_model.solve(url=url, key=api)
print(solv.solve_status)
print(solv.solve_details)

我认为您不想事先调用
add\u lazy\u约束。如果您这样做了,那么您可以直接将惰性约束添加到模型中


相反,您希望在回调中包含一些分离约束的代码。根据
sol
中的值,您可以找到一个违反的subtour消除约束并将其添加。

好的,因此在
def\uu call\uuuu(self):
中,我需要以某种方式获取二进制变量
x
的值,并检查是否有子路由,如果有,然后添加一个消除它的约束。我是否正确?在枚举(self.get_values())中为I,v使用以下代码
:如果v>0:print(self.index_to_var(I),v)
inside
def\u调用(self):
我得到了
x_0_1.0 x_1.0 x_2_0 1.0 x_3_4 1.0 x_4_6 1.0 x_5_7 1.0 x_6_3 1.0 x_7 1.0 x_8_5 1.0
我可以清楚地看到周期
5-7-8-5
0-1-2-0
3-4-6-3,现在我如何消除这些周期。我是否需要一些算法来找到循环,获取它们的节点并添加约束。下一步在
def\uu call\uuuuu(self)中:
我可以添加
self.add\u lazy\u constraints()
,但是在它里面写什么,即如何访问变量
x
正确,一旦你有了解向量,你就需要从中导出一个违反的约束。您可以查看CPLEX附带的
opl/examples/opl/models/TravelingSalesmanProblem
,其中包含一个用于在opl中分离子任务的示例。一旦您找到了一个subtour,您就可以为这个subtour构造一个subtour消除约束,将它作为一个单例列表输入到
get\u cpx\u unsatisfied\u cts
中,然后按照您已经引用的示例进行操作。我执行了这一行
unsats=self.get\u cpx\u unsatisfied\u cts(self.cts,sol,tolerance=1e-6)
unsats
是empy数组,因为我没有任何不满意的、有意义的惰性约束,但假设我知道节点
5-7-8-5
之间有一个子巡更。我不知道如何在
get\u cpx\u unsatisfied\u cts
中添加此约束,我的约束类似于
x[5,7]+x[7,8]+x[8,5],我在这里创建了一个带有惰性约束的TSP示例: