Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
199 changes: 199 additions & 0 deletions cpp/tests/routing/unit_tests/breaks.cu
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@
#include <routing/utilities/test_utilities.hpp>

#include <cuopt/routing/solve.hpp>
#include <utilities/common_utils.hpp>
#include <utilities/copy_helpers.hpp>

#include <gtest/gtest.h>
#include <string>
#include <vector>

namespace cuopt {
Expand All @@ -36,6 +38,7 @@ TEST(vehicle_breaks, default_case)
auto v_break_locations = cuopt::device_copy(break_locations, stream);
cuopt::routing::data_model_view_t<int, float> data_model(&handle, 3, 2);
data_model.add_cost_matrix(v_cost_matrix.data());
data_model.add_transit_time_matrix(v_cost_matrix.data());
data_model.add_break_dimension(
v_break_earliest.data(), v_break_latest.data(), v_break_service.data());
data_model.set_break_locations(v_break_locations.data(), v_break_locations.size());
Expand Down Expand Up @@ -63,6 +66,7 @@ TEST(vehicle_breaks, non_default_case)

cuopt::routing::data_model_view_t<int, float> data_model(&handle, 3, 2);
data_model.add_cost_matrix(v_cost_matrix.data());
data_model.add_transit_time_matrix(v_cost_matrix.data());
data_model.add_break_dimension(
v_break_earliest.data(), v_break_latest.data(), v_break_service.data());
data_model.set_break_locations(v_break_locations.data(), v_break_locations.size());
Expand Down Expand Up @@ -232,6 +236,201 @@ TEST(vehicle_breaks, vehicle_time_windows)
}
}

// Test uniform breaks (Solomon 100 nodes)
TEST(vehicle_breaks, uniform_breaks)
{
raft::handle_t handle;
auto stream = handle.get_stream();

std::string path = cuopt::test::get_rapids_dataset_root_dir() + "/solomon/In/r107.txt";
Route<int, float> route;
load_solomon(path, route, 101);

int nodes = route.n_locations;
int n_orders = nodes - 1;
int vehicle_num = 25;

std::vector<float> cost_matrix(nodes * nodes), time_matrix(nodes * nodes);
build_dense_matrix(cost_matrix.data(), route.x_h, route.y_h);
build_dense_matrix(time_matrix.data(), route.x_h, route.y_h);

std::vector<int> order_locations(n_orders), order_earliest(n_orders), order_latest(n_orders),
order_service(n_orders);
for (int i = 0; i < n_orders; ++i) {
order_locations[i] = i + 1;
order_earliest[i] = route.earliest_time_h[i + 1];
order_latest[i] = route.latest_time_h[i + 1];
order_service[i] = route.service_time_h[i + 1];
}
int num_breaks = 2;
std::vector<int> break_earliest(vehicle_num * num_breaks);
std::vector<int> break_latest(vehicle_num * num_breaks);
std::vector<int> break_duration(vehicle_num * num_breaks);
for (int v = 0; v < vehicle_num; ++v) {
break_earliest[v * num_breaks + 0] = 40;
break_latest[v * num_breaks + 0] = 50;
break_duration[v * num_breaks + 0] = 10;
break_earliest[v * num_breaks + 1] = 170;
break_latest[v * num_breaks + 1] = 180;
break_duration[v * num_breaks + 1] = 10;
}

std::vector<int> break_locations(nodes);
for (int i = 0; i < nodes; ++i) { break_locations[i] = i; }

cuopt::routing::data_model_view_t<int, float> data_model(&handle, nodes, vehicle_num, n_orders);

auto v_cost_matrix = cuopt::device_copy(cost_matrix, stream);
auto v_time_matrix = cuopt::device_copy(time_matrix, stream);
auto v_order_locations = cuopt::device_copy(order_locations, stream);
auto v_order_earliest = cuopt::device_copy(order_earliest, stream);
auto v_order_latest = cuopt::device_copy(order_latest, stream);
auto v_order_service = cuopt::device_copy(order_service, stream);
auto v_break_locations = cuopt::device_copy(break_locations, stream);

data_model.add_cost_matrix(v_cost_matrix.data());
data_model.add_transit_time_matrix(v_time_matrix.data());
data_model.set_order_locations(v_order_locations.data());
data_model.set_order_time_windows(v_order_earliest.data(), v_order_latest.data());
data_model.set_order_service_times(v_order_service.data());
data_model.set_break_locations(v_break_locations.data(), v_break_locations.size());

std::vector<int> dim0_earliest(vehicle_num), dim0_latest(vehicle_num), dim0_duration(vehicle_num);
std::vector<int> dim1_earliest(vehicle_num), dim1_latest(vehicle_num), dim1_duration(vehicle_num);
for (int v = 0; v < vehicle_num; ++v) {
dim0_earliest[v] = break_earliest[v * num_breaks + 0];
dim0_latest[v] = break_latest[v * num_breaks + 0];
dim0_duration[v] = break_duration[v * num_breaks + 0];
dim1_earliest[v] = break_earliest[v * num_breaks + 1];
dim1_latest[v] = break_latest[v * num_breaks + 1];
dim1_duration[v] = break_duration[v * num_breaks + 1];
}
auto v_break_earliest_0 = cuopt::device_copy(dim0_earliest, stream);
auto v_break_latest_0 = cuopt::device_copy(dim0_latest, stream);
auto v_break_duration_0 = cuopt::device_copy(dim0_duration, stream);
data_model.add_break_dimension(
v_break_earliest_0.data(), v_break_latest_0.data(), v_break_duration_0.data());

auto v_break_earliest_1 = cuopt::device_copy(dim1_earliest, stream);
auto v_break_latest_1 = cuopt::device_copy(dim1_latest, stream);
auto v_break_duration_1 = cuopt::device_copy(dim1_duration, stream);
data_model.add_break_dimension(
v_break_earliest_1.data(), v_break_latest_1.data(), v_break_duration_1.data());

cuopt::routing::solver_settings_t<int, float> settings;
settings.set_time_limit(30);

auto routing_solution = cuopt::routing::solve(data_model, settings);
handle.sync_stream();

ASSERT_EQ(routing_solution.get_status(), cuopt::routing::solution_status_t::SUCCESS);
host_assignment_t<int> h_routing_solution(routing_solution);
check_route(data_model, h_routing_solution);
}

// Test non-uniform breaks (Solomon 100 nodes)
TEST(vehicle_breaks, non_uniform_breaks)
{
raft::handle_t handle;
auto stream = handle.get_stream();

std::string path = cuopt::test::get_rapids_dataset_root_dir() + "/solomon/In/r107.txt";
Route<int, float> route;
load_solomon(path, route, 101);

int nodes = route.n_locations;
int n_orders = nodes - 1;
int vehicle_num = 30;

std::vector<float> cost_matrix(nodes * nodes), time_matrix(nodes * nodes);
build_dense_matrix(cost_matrix.data(), route.x_h, route.y_h);
build_dense_matrix(time_matrix.data(), route.x_h, route.y_h);

std::vector<int> order_locations(n_orders), order_earliest(n_orders), order_latest(n_orders),
order_service(n_orders);
for (int i = 0; i < n_orders; ++i) {
order_locations[i] = i + 1;
order_earliest[i] = route.earliest_time_h[i + 1];
order_latest[i] = route.latest_time_h[i + 1];
order_service[i] = route.service_time_h[i + 1];
}
int num_v_type_1 = vehicle_num / 2;
int num_v_type_2 = vehicle_num - num_v_type_1;
int num_breaks = 3;

// Type 1: [40,50]/5, [100,120]/20, [170,180]/10
// Type 2: [60,90]/20, [110,120]/10, [200,210]/5
std::vector<int> break_earliest(vehicle_num * num_breaks);
std::vector<int> break_latest(vehicle_num * num_breaks);
std::vector<int> break_duration(vehicle_num * num_breaks);
for (int v = 0; v < num_v_type_1; ++v) {
break_earliest[v * num_breaks + 0] = 40;
break_latest[v * num_breaks + 0] = 50;
break_duration[v * num_breaks + 0] = 5;
break_earliest[v * num_breaks + 1] = 100;
break_latest[v * num_breaks + 1] = 120;
break_duration[v * num_breaks + 1] = 20;
break_earliest[v * num_breaks + 2] = 170;
break_latest[v * num_breaks + 2] = 180;
break_duration[v * num_breaks + 2] = 10;
}
for (int v = num_v_type_1; v < vehicle_num; ++v) {
break_earliest[v * num_breaks + 0] = 60;
break_latest[v * num_breaks + 0] = 90;
break_duration[v * num_breaks + 0] = 20;
break_earliest[v * num_breaks + 1] = 110;
break_latest[v * num_breaks + 1] = 120;
break_duration[v * num_breaks + 1] = 10;
break_earliest[v * num_breaks + 2] = 200;
break_latest[v * num_breaks + 2] = 210;
break_duration[v * num_breaks + 2] = 5;
}

// Depot (0) excluded from break locations
std::vector<int> break_locations(nodes - 1);
for (int i = 0; i < nodes - 1; ++i) { break_locations[i] = i + 1; }

cuopt::routing::data_model_view_t<int, float> data_model(&handle, nodes, vehicle_num, n_orders);

auto v_cost_matrix = cuopt::device_copy(cost_matrix, stream);
auto v_time_matrix = cuopt::device_copy(time_matrix, stream);
auto v_order_locations = cuopt::device_copy(order_locations, stream);
auto v_order_earliest = cuopt::device_copy(order_earliest, stream);
auto v_order_latest = cuopt::device_copy(order_latest, stream);
auto v_order_service = cuopt::device_copy(order_service, stream);
auto v_break_locations = cuopt::device_copy(break_locations, stream);

data_model.add_cost_matrix(v_cost_matrix.data());
data_model.add_transit_time_matrix(v_time_matrix.data());
data_model.set_order_locations(v_order_locations.data());
data_model.set_order_time_windows(v_order_earliest.data(), v_order_latest.data());
data_model.set_order_service_times(v_order_service.data());
data_model.set_break_locations(v_break_locations.data(), v_break_locations.size());

for (int b = 0; b < num_breaks; ++b) {
std::vector<int> e(vehicle_num), l(vehicle_num), d(vehicle_num);
for (int v = 0; v < vehicle_num; ++v) {
e[v] = break_earliest[v * num_breaks + b];
l[v] = break_latest[v * num_breaks + b];
d[v] = break_duration[v * num_breaks + b];
}
auto v_e = cuopt::device_copy(e, stream);
auto v_l = cuopt::device_copy(l, stream);
auto v_d = cuopt::device_copy(d, stream);
data_model.add_break_dimension(v_e.data(), v_l.data(), v_d.data());
}

cuopt::routing::solver_settings_t<int, float> settings;
settings.set_time_limit(30);

auto routing_solution = cuopt::routing::solve(data_model, settings);
handle.sync_stream();

ASSERT_EQ(routing_solution.get_status(), cuopt::routing::solution_status_t::SUCCESS);
host_assignment_t<int> h_routing_solution(routing_solution);
check_route(data_model, h_routing_solution);
}

} // namespace test
} // namespace routing
} // namespace cuopt
86 changes: 86 additions & 0 deletions cpp/tests/routing/unit_tests/heterogenous_breaks.cu
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@
#include <routing/utilities/test_utilities.hpp>

#include <cuopt/routing/solve.hpp>
#include <utilities/common_utils.hpp>
#include <utilities/copy_helpers.hpp>

#include <gtest/gtest.h>
#include <random>
#include <string>
#include <vector>

namespace cuopt {
Expand Down Expand Up @@ -111,6 +113,90 @@ TEST(heterogenous_breaks, simple_non_uniform)
check_route(data_model, h_routing_solution);
}

// Test heterogenous breaks (Solomon 100 nodes):
// Half of vehicles have 2 breaks with custom locations; remaining half have 3 breaks with default (any) location.
TEST(heterogenous_breaks, test_heterogeneous_breaks)
{
raft::handle_t handle;
auto stream = handle.get_stream();

const std::string& rapidsDatasetRootDir = cuopt::test::get_rapids_dataset_root_dir();
std::string routing_file = rapidsDatasetRootDir + "/solomon/In/r107.txt";
Route<int, float> route;
load_solomon(routing_file, route, 101);

int nodes = route.n_locations;
int n_orders = nodes - 1;
int vehicle_num = 20;
int num_v_type_1 = vehicle_num / 2;

std::vector<float> cost_matrix(nodes * nodes), time_matrix(nodes * nodes);
build_dense_matrix(cost_matrix.data(), route.x_h, route.y_h);
build_dense_matrix(time_matrix.data(), route.x_h, route.y_h);

std::vector<int> order_locations(n_orders), order_earliest(n_orders), order_latest(n_orders),
order_service(n_orders);
for (int i = 0; i < n_orders; ++i) {
order_locations[i] = i + 1;
order_earliest[i] = route.earliest_time_h[i + 1];
order_latest[i] = route.latest_time_h[i + 1];
order_service[i] = route.service_time_h[i + 1];
}

// Type 1: 2 breaks [90,100]/15, [150,170]/15 at locations 5,10,15,...,85 (every 5th node, excluding depot)
std::vector<int> break_locations_1;
for (int i = 1; i <= 17; ++i) { break_locations_1.push_back(5 * i); }

cuopt::routing::data_model_view_t<int, float> data_model(&handle, nodes, vehicle_num, n_orders);

auto v_cost_matrix = cuopt::device_copy(cost_matrix, stream);
auto v_time_matrix = cuopt::device_copy(time_matrix, stream);
auto v_order_locations = cuopt::device_copy(order_locations, stream);
auto v_order_earliest = cuopt::device_copy(order_earliest, stream);
auto v_order_latest = cuopt::device_copy(order_latest, stream);
auto v_order_service = cuopt::device_copy(order_service, stream);

data_model.add_cost_matrix(v_cost_matrix.data());
data_model.add_transit_time_matrix(v_time_matrix.data());
data_model.set_order_locations(v_order_locations.data());
data_model.set_order_time_windows(v_order_earliest.data(), v_order_latest.data());
data_model.set_order_service_times(v_order_service.data());

auto v_break_locations_1 = cuopt::device_copy(break_locations_1, stream);

for (int i = 0; i < num_v_type_1; ++i) {
data_model.add_vehicle_break(i,
90,
100,
15,
v_break_locations_1.data(),
static_cast<int>(break_locations_1.size()));
data_model.add_vehicle_break(i,
150,
170,
15,
v_break_locations_1.data(),
static_cast<int>(break_locations_1.size()));
}

// Type 2: 3 breaks [40,50]/10, [110,120]/10, [160,170]/10 with default (any) location
for (int i = num_v_type_1; i < vehicle_num; ++i) {
data_model.add_vehicle_break(i, 40, 50, 10, nullptr, 0);
data_model.add_vehicle_break(i, 110, 120, 10, nullptr, 0);
data_model.add_vehicle_break(i, 160, 170, 10, nullptr, 0);
}

cuopt::routing::solver_settings_t<int, float> settings;
settings.set_time_limit(30);

auto routing_solution = cuopt::routing::solve(data_model, settings);
handle.sync_stream();

ASSERT_EQ(routing_solution.get_status(), cuopt::routing::solution_status_t::SUCCESS);
host_assignment_t<int> h_routing_solution(routing_solution);
check_route(data_model, h_routing_solution);
}

} // namespace test
} // namespace routing
} // namespace cuopt
Loading
Loading