车辆路径数据构建和带路径点矩阵的结果#

import json

累积问题数据#

json_data = {}

设置路径点图


需要提供偏移量、边和权重;权重不能为负值。

考虑到两种类型的车辆,厢式货车和卡车,厢式货车在较少的路径中会更快且成本更低,而卡车则会表现出旅行成本权重的变化。

类似地,也可以设置 travel_time_graph_data

# Van
graph_data_1 = {
    "offsets": [0,       3,    5,           9,    11,   13,   15,   17, 18, 19, 20, 21],
    "edges":   [1, 2, 9, 0, 7, 0, 3, 4, 10, 2, 4, 2, 5, 6, 9, 5, 8, 1,  6,  0,  5],
    "weights": [1, 1, 2, 1, 2, 1, 1, 1,  3, 2, 3, 2, 1, 2, 1, 3, 4, 2,  3,  1,  1]
}

# Truck
graph_data_2 = graph_data_1.copy()
graph_data_2["weights"] = [2, 2, 3, 2, 3, 2, 2, 2, 4, 4, 4, 3, 2, 3, 2, 4, 5, 3,  4,  2,  1]

json_data["cost_waypoint_graph_data"] = {
    'waypoint_graph': {
        1: graph_data_1, # Van
        2: graph_data_2  # Truck
    }
}

# For simplicity, we are using the same graph data for travel time here.
# In a real use case this would likely be different
json_data["travel_time_waypoint_graph_data"] = {
    'waypoint_graph': {
        1: graph_data_1, # Van
        2: graph_data_2  # Truck
    }
}

设置车队数据


提供车辆起点和终点位置以及车辆特征和容量。

fleet_data = {
    "vehicle_locations": [[0, 0], [1, 1], [0, 1], [1, 0], [0, 0]],
    "vehicle_ids": ["Van-A", "Truck-A", "Van-B", "Truck-B", "Van-C"],
    "vehicle_types": [1, 2, 1, 2, 1],
    "capacities": [[10, 12, 15, 8, 10]],
    "vehicle_time_windows": [
        [0, 80],
        [1, 40],
        [3, 30],
        [5, 80],
        [20, 100]
    ],
    # Vehicle can take breaks in this time window as per berak duration provided
    "vehicle_break_time_windows":[
        [
            [20, 25],
            [20, 25],
            [20, 25],
            [20, 25],
            [20, 25]
        ]
    ],
    "vehicle_break_durations": [[1, 1, 1, 1, 1]],
    # Vehicle Id 0 can only serve Order 0 and 4
    "vehicle_order_match": [
        {
            "vehicle_id": 0,
            "order_ids": [0, 4]
        }
    ],  # 0th vehicle can only serve 0th and 4th order only
    # Don't count trip from depot to first task location
    "skip_first_trips": [False, True, True, False, False],
    # Don't count trip from last task location to depot
    "drop_return_trips": [False, True, True, False, False],
    # Maximum cost a vehicle can incur while delivering
    "vehicle_max_costs": [100, 100, 100, 100, 100],
    # Maximum time a vehicle can be working
    "vehicle_max_times": [120, 120, 120, 120, 120],
    # Minimum 2 vehicles are required to be in solution
    "min_vehicles": 2,
}

json_data["fleet_data"] = fleet_data

设置任务数据


提供有关任务位置、需求和时间窗口的详细信息;还有其他选项。

task_data = {
    "task_locations": [1, 3, 4, 6, 8],
    "demand": [[3, 4, 4, 3, 2]],
    "task_time_windows": [
        [3, 20],
        [5, 30],
        [1, 20],
        [4, 40],
        [0, 30],
    ],
    "service_times": [3, 1, 8, 4, 0],
    # Order Id 0 and 4 can be served only by vehicle with id 0
    "order_vehicle_match": [
        {
            "order_id": 0,
            "vehicle_ids": [0]
        },
        {
            "order_id": 4,
            "vehicle_ids": [0]
        }
    ]
}

json_data["task_data"] = task_data

设置求解器配置


较大的问题可能需要更多时间。

solver_config = {
    "time_limit": 1
}

json_data["solver_config"] = solver_config

解决问题#

对于托管服务,可以触发 cuOpt 端点,如 托管服务的瘦客户端示例 中所示。

对于自托管,可以触发 cuOpt 端点,如 自托管的瘦客户端示例 中所示。

使用此数据并调用 cuOpt 端点,这将返回路线。

如果响应状态为 0,

则 cuOpt 找到了一个可行解,在字典 “solver_response” 下。

如果状态为 1,

则表示它通过超出约束找到了不可行解。警告将列出哪些约束导致了不可行解,可以进一步分析。任何其他表明 cuOpt 失败的状态可能是由于无效数据或某些未处理的问题。这将位于字典 “solver_infeasible_response” 下。

以下示例使用本地托管服务器,

from cuopt_sh_client import CuOptServiceSelfHostClient
import json

# If cuOpt is not running on localhost:5000, edit ip and port parameters
cuopt_service_client = CuOptServiceSelfHostClient(
    ip="localhost",
    port=5000
)

optimized_routes = cuopt_service_client.get_optimized_routes(json_data)
print(json.dumps(optimized_routes, indent=4))
    {
    "response": {
        "solver_response": {
            "status": 0,
            "num_vehicles": 2,
            "solution_cost": 25.0,
            "objective_values": {
                "cost": 25.0
            },
            "vehicle_data": {
                "Van-A": {
                    "task_id": ["Depot", "0", "Break", "4", "Depot"],
                    "arrival_stamp": [6.0, 7.0, 20.0, 21.0, 29.0],
                    "route": [0, 1, 0, 2, 4, 5, 6, 8, 8, 6, 5, 9, 0],
                    "type": ["Depot", "Delivery", "w", "w", "w", "w", "w", "Break", "Delivery", "w", "w", "w", "Depot"]
                },
                "Van-B": {
                    "task_id": ["1", "2", "Break", "3"],
                    "arrival_stamp": [8.0, 12.0, 20.0, 24.0],
                    "route": [3, 4, 4, 5, 6],
                    "type": ["Delivery", "Delivery", "Break", "w", "Delivery"]
                }
            },
            "dropped_tasks": {
                "task_id": [],
                "task_index": []
            },
            "msg": ""
        },
        "perf_times": null
    },
    "reqId": "483d43b5-2730-413b-898d-a803da251530"
}

警告 将作为列表列在响应中,例如弃用或任何导致不可行结果的约束。