Skip to content

Commit dbeb83b

Browse files
committed
Add python api
1 parent 6e8282c commit dbeb83b

File tree

3 files changed

+155
-1
lines changed

3 files changed

+155
-1
lines changed

python/cuopt/cuopt/routing/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,5 @@
99
update_routes_and_vehicles,
1010
)
1111
from cuopt.routing.utils_wrapper import DatasetDistribution
12-
from cuopt.routing.vehicle_routing import DataModel, Solve, SolverSettings
12+
from cuopt.routing.vehicle_routing import BatchSolve, DataModel, Solve, SolverSettings
1313
from cuopt.routing.vehicle_routing_wrapper import ErrorStatus, Objective

python/cuopt/cuopt/routing/vehicle_routing.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1540,3 +1540,44 @@ def Solve(data_model, solver_settings=None):
15401540
solver_settings.get_config_file_name(),
15411541
)
15421542
return solution
1543+
1544+
1545+
@catch_cuopt_exception
1546+
def BatchSolve(data_model_list, solver_settings=None):
1547+
"""
1548+
Solves multiple routing problems in batch mode using parallel execution.
1549+
1550+
Parameters
1551+
----------
1552+
data_model_list: list of DataModel
1553+
List of data model objects representing routing problems to solve.
1554+
solver_settings: SolverSettings
1555+
Settings to configure solver configurations.
1556+
By default, it uses default solver settings to solve.
1557+
1558+
Returns
1559+
-------
1560+
tuple
1561+
A tuple containing:
1562+
- list of Assignment: Solutions for each routing problem
1563+
- float: Total solve time in seconds
1564+
1565+
Examples
1566+
--------
1567+
>>> from cuopt import routing
1568+
>>> import cudf
1569+
>>> # Create multiple data models
1570+
>>> data_models = []
1571+
>>> for i in range(5):
1572+
... cost_matrix = cudf.DataFrame([[0, 1, 2], [1, 0, 3], [2, 3, 0]])
1573+
... dm = routing.DataModel(3, 1)
1574+
... dm.add_cost_matrix(cost_matrix)
1575+
... data_models.append(dm)
1576+
>>> settings = routing.SolverSettings()
1577+
>>> settings.set_time_limit(1.0)
1578+
>>> solutions, solve_time = routing.BatchSolve(data_models, settings)
1579+
"""
1580+
if solver_settings is None:
1581+
solver_settings = SolverSettings()
1582+
1583+
return vehicle_routing_wrapper.BatchSolve(data_model_list, solver_settings)
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
import cudf
5+
import numpy as np
6+
7+
from cuopt import routing
8+
9+
10+
def create_tsp_cost_matrix(n_locations):
11+
"""Creates a simple symmetric cost matrix for TSP."""
12+
cost_matrix = np.zeros((n_locations, n_locations), dtype=np.float32)
13+
for i in range(n_locations):
14+
for j in range(n_locations):
15+
cost_matrix[i, j] = abs(i - j)
16+
return cudf.DataFrame(cost_matrix)
17+
18+
19+
def test_batch_solve_varying_sizes():
20+
"""Test batch solving TSPs of varying sizes."""
21+
tsp_sizes = [5, 8, 10, 6, 7, 9]
22+
23+
# Create data models for each TSP
24+
data_models = []
25+
for n_locations in tsp_sizes:
26+
cost_matrix = create_tsp_cost_matrix(n_locations)
27+
dm = routing.DataModel(n_locations, 1)
28+
dm.add_cost_matrix(cost_matrix)
29+
data_models.append(dm)
30+
31+
# Configure solver settings
32+
settings = routing.SolverSettings()
33+
settings.set_time_limit(5.0)
34+
35+
# Call batch solve
36+
solutions, solve_time = routing.BatchSolve(data_models, settings)
37+
38+
# Verify results
39+
assert len(solutions) == len(tsp_sizes)
40+
for i, solution in enumerate(solutions):
41+
assert solution.get_status() == routing.SolutionStatus.SUCCESS, (
42+
f"TSP {i} (size {tsp_sizes[i]}) failed"
43+
)
44+
assert solution.get_vehicle_count() == 1, (
45+
f"TSP {i} (size {tsp_sizes[i]}) used multiple vehicles"
46+
)
47+
48+
# Verify solve time is reasonable
49+
assert solve_time > 0.0, "Solve time should be positive"
50+
51+
52+
def test_batch_solve_same_size():
53+
"""Test batch solving multiple TSPs of the same size."""
54+
n_problems = 10
55+
n_locations = 6
56+
57+
# Create data models
58+
data_models = []
59+
for _ in range(n_problems):
60+
cost_matrix = create_tsp_cost_matrix(n_locations)
61+
dm = routing.DataModel(n_locations, 1)
62+
dm.add_cost_matrix(cost_matrix)
63+
data_models.append(dm)
64+
65+
# Configure solver settings
66+
settings = routing.SolverSettings()
67+
settings.set_time_limit(2.0)
68+
69+
# Call batch solve
70+
solutions, solve_time = routing.BatchSolve(data_models, settings)
71+
72+
# Verify all solutions succeeded
73+
assert len(solutions) == n_problems
74+
for i, solution in enumerate(solutions):
75+
assert solution.get_status() == routing.SolutionStatus.SUCCESS, (
76+
f"TSP {i} failed"
77+
)
78+
79+
80+
def test_batch_solve_single_problem():
81+
"""Test batch solve with a single problem."""
82+
n_locations = 5
83+
84+
cost_matrix = create_tsp_cost_matrix(n_locations)
85+
dm = routing.DataModel(n_locations, 1)
86+
dm.add_cost_matrix(cost_matrix)
87+
88+
settings = routing.SolverSettings()
89+
settings.set_time_limit(2.0)
90+
91+
solutions, solve_time = routing.BatchSolve([dm], settings)
92+
93+
assert len(solutions) == 1
94+
assert solutions[0].get_status() == routing.SolutionStatus.SUCCESS
95+
96+
97+
def test_batch_solve_default_settings():
98+
"""Test batch solve with default solver settings."""
99+
tsp_sizes = [5, 6, 7]
100+
101+
data_models = []
102+
for n_locations in tsp_sizes:
103+
cost_matrix = create_tsp_cost_matrix(n_locations)
104+
dm = routing.DataModel(n_locations, 1)
105+
dm.add_cost_matrix(cost_matrix)
106+
data_models.append(dm)
107+
108+
# Call batch solve without explicit settings
109+
solutions, solve_time = routing.BatchSolve(data_models)
110+
111+
assert len(solutions) == len(tsp_sizes)
112+
for solution in solutions:
113+
assert solution.get_status() == routing.SolutionStatus.SUCCESS

0 commit comments

Comments
 (0)