在 OR-tools 车辆路径问题中仍然访问可选节点

Optional node still visited in OR-tools vehicle routing problem

在由 Google OR-tools 库解决的一个简单车辆路径问题中,两个节点 (2, 3) 被标记为可选,访问惩罚设置为 0。从仓库到垃圾填埋场的距离 2 的最短路径是 0 -> 1 -> 4,但是,求解器最终得到距离 4 的路径 0 -> 2 -> 3 -> 1 -> 4.

问题出在哪里?为什么求解器坚持通过可选节点的较长路径而不跳过它们?

#include "ortools/constraint_solver/routing.h"

using namespace operations_research;

struct DataModel {
    static constexpr int I = 2;
    const std::vector<std::vector<int>> dist {
        { 0, 1, 1, I, I},
        { I, 0, I, 1, 1},
        { I, I, 0, 1, 1},
        { I, 1, 1, 0, I},
        { I, I, I, 1, 0},
    };
    const RoutingIndexManager::NodeIndex depot{0};
    const RoutingIndexManager::NodeIndex landfill{4};
};

void printSolution(const RoutingIndexManager& manager,
                   const RoutingModel& routing,
                   const Assignment& solution)
{
    if (routing.status() != RoutingModel::Status::ROUTING_SUCCESS)
        return;

    int index = routing.Start(0);
    std::ostringstream route;
    while (routing.IsEnd(index) == false) {
        route << manager.IndexToNode(index).value() << " -> ";
        index = solution.Value(routing.NextVar(index));
    }

    LOG(INFO) << route.str() << manager.IndexToNode(index).value();
    LOG(INFO) << "Problem solved in " << routing.solver()->wall_time() << "ms";
}

int main(int /*argc*/, char** /*argv*/)
{
    DataModel data;
    RoutingIndexManager manager(data.dist.size(), 1, {data.depot}, {data.landfill});
    RoutingModel routing(manager);

    const int callback = routing.RegisterTransitCallback(
                [&data, &manager](int from_index, int to_index) -> int {
        auto from_node = manager.IndexToNode(from_index).value();
        auto to_node = manager.IndexToNode(to_index).value();

        return data.dist[from_node][to_node];
    });

    routing.SetArcCostEvaluatorOfAllVehicles(callback);

    // make nodes 2, 3 optional
    routing.AddDisjunction({manager.NodeToIndex(RoutingIndexManager::NodeIndex(2))}, 0, 1);
    routing.AddDisjunction({manager.NodeToIndex(RoutingIndexManager::NodeIndex(3))}, 0, 1);

    const Assignment* solution = routing.Solve();
    printSolution(manager, routing, *solution);

    return 0;
}

有趣的是,对于 I = 1,找到了正确的解决方案 0 -> 1 -> 4。然而,这样的 dist 矩阵是微不足道的。

已在 or-tools-discuss 邮件列表中对此进行了回答。

您遇到了默认参数设置的极端情况。感谢您转发此信息,我们将努力进行适当的修复。 要解决此问题,您可以按如下方式修改默认参数: 选项 1 - 激活 make_chain_inactive - 更快的选项

    RoutingSearchParameters search_parameters = DefaultRoutingSearchParameters();
    search_parameters.mutable_local_search_operators()->set_use_make_chain_inactive(OptionalBoolean::BOOL_TRUE);
    const Assignment* solution = routing.SolveWithParameters(search_parameters);

选项 2 - 激活 inactive_lns - 较慢的选项但稍微更通用

    RoutingSearchParameters search_parameters = DefaultRoutingSearchParameters();
    search_parameters.mutable_local_search_operators()->set_use_inactive_lns(OptionalBoolean::BOOL_TRUE);
    const Assignment* solution = routing.SolveWithParameters(search_parameters);