车辆路径数据构建和结果(使用成本/时间矩阵)#

import json

累积问题数据#

json_data = {}

设置成本/行程时间矩阵


我们需要为成本/时间矩阵提供具有正浮点值的方阵。

成本矩阵表示从一个地点到另一个地点的出行成本。此成本可以在结果中累积,以找到最佳解决方案。默认情况下,此成本会被累积,可以通过将目标值设置为 0 来禁用它。

行程时间矩阵用于评估车辆和任务的时间窗口约束。因此,如果使用任何类型的时间相关约束,则时间矩阵是必需的。

当不同类型的车辆成本和时间不同时(例如,汽车和自行车),可以为不同类型的车辆提供多个成本和行程时间矩阵。

# Considering cost is half of travel time

# Car
car_cost_matrix = [
    [0, 5, 4, 3, 5],
    [5, 0, 6, 4, 3],
    [4, 8, 0, 4, 2],
    [1, 4, 3, 0, 4],
    [3, 3, 5, 6, 0],
]

car_time_matrix = [
    [ 0, 10,  8,  6, 10],
    [10,  0, 12,  8,  6],
    [ 8, 16,  0,  8,  4],
    [ 2,  8,  6,  0,  8],
    [ 6,  6, 10, 12,  0],
]

# 1 represents car type and 2 represents bike type

# Add cost matrix

json_data["cost_matrix_data"] = {
    "data" : {
        1:car_cost_matrix
    }
}

# Add time matrix

json_data["travel_time_matrix_data"] = {
    "data" : {
        1:car_time_matrix
    }
}

设置车队数据


提供车辆的起始和结束位置,以及车辆的功能和容量。

fleet_data = {
    "vehicle_locations": [[0, 0], [0, 0], [0, 0], [0, 0], [0, 0]],
    "vehicle_ids": ["Car-A", "Car-B", "Car-C", "Car-D", "Car-E"],
    "vehicle_types": [1, 1, 1, 1, 1],
    "capacities": [[75, 75, 75, 75, 75]],
    "vehicle_time_windows": [
        [0, 100],
        [0, 100],
        [0, 100],
        [0, 100],
        [0, 100]
    ],
    # Vehicle can take breaks in this time window as per break duration provided
    "vehicle_break_time_windows":[
        [
            [20, 25],
            [20, 25],
            [20, 25],
            [20, 25],
            [20, 25]
        ]
    ],
    "vehicle_break_durations": [[1, 1, 1, 1, 1]],
    # 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]
}

json_data["fleet_data"] = fleet_data

设置任务数据


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

task_data = {
    "task_locations": [1, 2, 3, 4],
    "demand": [[30, 40, 40, 30]],
    "task_time_windows": [
        [3, 20],
        [5, 30],
        [1, 20],
        [4, 40],
    ],
    "service_times": [3, 1, 8, 4],
    "prizes": [50, 50, 100, 50]
}

json_data["task_data"] = task_data

设置求解器配置


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

solver_config = {
    "objectives": {
        "cost": 1,
        "travel_time": 1,
        "prize": 2
    }
}

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": -407.0,
            "objective_values": {"cost": 25.0, "travel_time": 68.0, "prize": -250.0},
            "vehicle_data": {
                "Car-A": {
                    "task_id": ["Depot", "2", "Break", "3", "Depot"],
                    "arrival_stamp": [6.0, 12.0, 20.0, 29.0, 39.0],
                    "route": [0, 3, 3, 4, 0],
                    "type": ["Depot", "Delivery", "Break", "Delivery", "Depot"]
                },
                "Car-C": {
                    "task_id": [ "Depot", "0", "Break", "1", "Depot"],
                    "arrival_stamp": [0.0, 10.0, 25.0, 26.0, 35.0],
                    "route": [0, 1, 2, 2, 0],
                    "type": ["Depot", "Delivery", "Break", "Delivery", "Depot"]
                }
            },
            "dropped_tasks": {
                "task_id": [],
                "task_index": []
            },
            "msg": ""
        },
        "perf_times": null
    },
    "reqId": "4a60ebbc-f582-4062-8258-43e2bf87a9e0"
}

解决方案成本为 -407.0,这是由于目标值设置为奖励,该奖励从其他值中抵消。

如果车辆具有非统一休息时间,该如何处理?#

# Lets pop  uniform breaks added earlier
del fleet_data["vehicle_break_time_windows"]
del fleet_data["vehicle_break_durations"]

车辆 2 和 3 请求额外增加 2 次休息,让我们根据请求更新休息时间

vehicle_breaks = [
    {
     "vehicle_id": 0,
     "earliest": 20,
     "latest": 25,
     "duration": 1,
     "locations": [0, 1, 2, 3, 4]
    },
    {
     "vehicle_id": 1,
     "earliest": 20,
     "latest": 25,
     "duration": 1,
     "locations": [0, 1, 2, 3, 4]
    },
    {
     "vehicle_id": 2,
     "earliest": 12,
     "latest": 15,
     "duration": 1,
     "locations": [0, 1, 2, 3, 4]
    },
    {
     "vehicle_id": 2,
     "earliest": 20,
     "latest": 25,
     "duration": 1,
     "locations": [0, 1, 2, 3, 4]
    },
    {
     "vehicle_id": 3,
     "earliest": 10,
     "latest": 14,
     "duration": 1,
     "locations": [0, 1, 2, 3, 4]
    },
    {
     "vehicle_id": 3,
     "earliest": 20,
     "latest": 25,
     "duration": 1,
     "locations": [0, 1, 2, 3, 4]
    },
    {
     "vehicle_id": 4,
     "earliest": 20,
     "latest": 25,
     "duration": 1,
     "locations": [0, 1, 2, 3, 4]
    },
]

fleet_data["vehicle_breaks"] = vehicle_breaks

让我们测试非统一休息时间

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": -403.0,
            "objective_values": {
                "cost": 26.0,
                "travel_time": 71.0,
                "prize": -250.0
            },
            "vehicle_data": {
                "Car-A": {
                    "task_id": [
                        "Depot",
                        "2",
                        "Break",
                        "3",
                        "Depot"
                    ],
                    "arrival_stamp": [
                        6.0,
                        12.0,
                        20.0,
                        29.0,
                        39.0
                    ],
                    "type": [
                        "Depot",
                        "Delivery",
                        "Break",
                        "Delivery",
                        "Depot"
                    ],
                    "route": [
                        0,
                        3,
                        3,
                        4,
                        0
                    ]
                },
                "Car-C": {
                    "task_id": [
                        "Depot",
                        "0",
                        "Break",
                        "Break",
                        "1",
                        "Depot"
                    ],
                    "arrival_stamp": [
                        0.0,
                        10.0,
                        13.0,
                        22.0,
                        29.0,
                        38.0
                    ],
                    "type": [
                        "Depot",
                        "Delivery",
                        "Break",
                        "Break",
                        "Delivery",
                        "Depot"
                    ],
                    "route": [
                        0,
                        1,
                        1,
                        3,
                        2,
                        0
                    ]
                }
            },
            "dropped_tasks": {
                "task_id": [],
                "task_index": []
            }
        }
    },
    "reqId": "925a48cd-ef91-4180-9382-3f1188ef0de0"
}

正如您所看到的,对应于 Car-C 的汽车索引 2 正在获得 2 次休息。