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
1 change: 1 addition & 0 deletions cpp/include/cuopt/routing/cpu_routing_problem.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ class cpu_routing_problem_t {

std::vector<cpu_cost_matrix_t> cost_matrices;
std::vector<cpu_cost_matrix_t> transit_time_matrices;
std::vector<cpu_cost_matrix_t> distance_matrices;

std::vector<int32_t> vehicle_start_locations;
std::vector<int32_t> vehicle_return_locations;
Expand Down
17 changes: 17 additions & 0 deletions cpp/include/cuopt/routing/data_model_view.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,16 @@ class data_model_view_t {
*/
void add_cost_matrix(f_t const* matrix, uint8_t vehicle_type = 0);

/**
* @brief Register an optional physical distance matrix, separate from cost and time.
*
* @param[in] matrix Non-owning device pointer to a square row-major matrix.
* @param[in]
* vehicle_type Vehicle-type identifier.
* @throws cuopt::logic_error if matrix is null.
*/
void add_distance_matrix(f_t const* matrix, uint8_t vehicle_type = 0);

/**
* @brief Set a transit time matrix for all locations (depot included) at
* once. The time matrix is used to check constraints satisfiability rather
Expand Down Expand Up @@ -436,6 +446,9 @@ class data_model_view_t {
*/
f_t const* get_cost_matrix(uint8_t vehicle_type = 0) const noexcept;

/** @brief Return the distance matrix for a vehicle type, or nullptr if not set. */
f_t const* get_distance_matrix(uint8_t vehicle_type = 0) const noexcept;

/**
* @brief Get transit time matrix
* @return Matrix pointer
Expand All @@ -448,6 +461,9 @@ class data_model_view_t {
*/
std::unordered_map<uint8_t, f_t const*> get_cost_matrices() const noexcept;

/** @brief Return all registered physical distance matrices. */
std::unordered_map<uint8_t, f_t const*> get_distance_matrices() const noexcept;

/**
* @brief Get all transit time matrices as a map
* @return map of vehicle type to transit time matrix
Expand Down Expand Up @@ -661,6 +677,7 @@ class data_model_view_t {
i_t n_requests_{};
raft::device_span<uint8_t const> vehicle_types_;
std::unordered_map<uint8_t, f_t const*> cost_matrices_{};
std::unordered_map<uint8_t, f_t const*> distance_matrices_{};
std::unordered_map<uint8_t, f_t const*> transit_time_matrices_{};
i_t const* order_locations_{nullptr};
i_t const* break_locations_{nullptr};
Expand Down
33 changes: 33 additions & 0 deletions cpp/src/routing/cpu_routing_problem.cu
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
#include <cuda/stream>
#include <rmm/device_uvector.hpp>

#include <algorithm>
#include <cmath>
#include <stdexcept>
#include <vector>

Expand All @@ -24,6 +26,7 @@ namespace routing {
struct cpu_routing_problem_t::device_data_t {
std::vector<std::unique_ptr<rmm::device_uvector<float>>> cost_matrices;
std::vector<std::unique_ptr<rmm::device_uvector<float>>> transit_time_matrices;
std::vector<std::unique_ptr<rmm::device_uvector<float>>> distance_matrices;

std::unique_ptr<rmm::device_uvector<int32_t>> vehicle_start_locations;
std::unique_ptr<rmm::device_uvector<int32_t>> vehicle_return_locations;
Expand Down Expand Up @@ -117,15 +120,25 @@ cpu_routing_problem_t::to_device(raft::handle_t* handle) const

int32_t orders = (num_orders < 0) ? num_locations : num_orders;
data_model_view_t<int, float> view(handle, num_locations, fleet_size, orders);
const auto matrix_size = static_cast<size_t>(num_locations) * num_locations;

for (auto const& cm : cost_matrices) {
if (cm.matrix.size() != matrix_size) {
throw std::invalid_argument(
"cpu_routing_problem_t::to_device: cost matrix size must equal num_locations squared");
}
auto d = copy_vector(cm.matrix, stream);
if (!d) { throw std::invalid_argument("cpu_routing_problem_t::to_device: empty cost matrix"); }
view.add_cost_matrix(d->data(), cm.vehicle_type);
data->cost_matrices.push_back(std::move(d));
}

for (auto const& tm : transit_time_matrices) {
if (tm.matrix.size() != matrix_size) {
throw std::invalid_argument(
"cpu_routing_problem_t::to_device: transit time matrix size must equal num_locations "
"squared");
}
auto d = copy_vector(tm.matrix, stream);
if (!d) {
throw std::invalid_argument("cpu_routing_problem_t::to_device: empty transit time matrix");
Expand All @@ -134,6 +147,26 @@ cpu_routing_problem_t::to_device(raft::handle_t* handle) const
data->transit_time_matrices.push_back(std::move(d));
}

for (auto const& dm : distance_matrices) {
if (dm.matrix.size() != matrix_size) {
throw std::invalid_argument(
"cpu_routing_problem_t::to_device: distance matrix size must equal num_locations squared");
}
if (std::any_of(dm.matrix.begin(), dm.matrix.end(), [](float value) {
return std::isnan(value) || value < 0.f;
})) {
throw std::invalid_argument(
"cpu_routing_problem_t::to_device: distance matrix values must be non-negative and not "
"NaN");
}
auto d = copy_vector(dm.matrix, stream);
if (!d) {
throw std::invalid_argument("cpu_routing_problem_t::to_device: empty distance matrix");
}
view.add_distance_matrix(d->data(), dm.vehicle_type);
data->distance_matrices.push_back(std::move(d));
}

if (!vehicle_start_locations.empty() && !vehicle_return_locations.empty()) {
data->vehicle_start_locations = copy_vector(vehicle_start_locations, stream);
data->vehicle_return_locations = copy_vector(vehicle_return_locations, stream);
Expand Down
21 changes: 21 additions & 0 deletions cpp/src/routing/data_model_view.cu
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,13 @@ data_model_view_t<i_t, f_t>::data_model_view_t(raft::handle_t* handle_ptr,
"Number of nodes should be lower than 65535");
}

template <typename i_t, typename f_t>
void data_model_view_t<i_t, f_t>::add_distance_matrix(f_t const* matrix, uint8_t vehicle_type)
{
cuopt_expects(matrix != nullptr, error_type_t::ValidationError, "Matrix input cannot be null");
distance_matrices_[vehicle_type] = matrix;
}

template <typename i_t, typename f_t>
void data_model_view_t<i_t, f_t>::add_cost_matrix(f_t const* matrix, uint8_t vehicle_type)
{
Expand Down Expand Up @@ -805,6 +812,20 @@ raft::handle_t const* data_model_view_t<i_t, f_t>::get_handle_ptr() const noexce
return handle_ptr_;
}

template <typename i_t, typename f_t>
f_t const* data_model_view_t<i_t, f_t>::get_distance_matrix(uint8_t vehicle_type) const noexcept
{
auto const matrix = distance_matrices_.find(vehicle_type);
return matrix == distance_matrices_.end() ? nullptr : matrix->second;
}

template <typename i_t, typename f_t>
std::unordered_map<uint8_t, f_t const*> data_model_view_t<i_t, f_t>::get_distance_matrices()
const noexcept
{
return distance_matrices_;
}

template class CUOPT_EXPORT data_model_view_t<int, float>;
} // namespace routing
} // namespace cuopt
24 changes: 24 additions & 0 deletions cpp/src/routing/fleet_info.cu
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ void populate_matrices(data_model_view_t<i_t, f_t> const& data_model, d_mdarray_

// Check for consistency of cost matrices
const auto& cost_matrices = data_model.get_cost_matrices();
const auto& distance_matrices = data_model.get_distance_matrices();
const auto& transit_time_matrices = data_model.get_transit_time_matrices();

if (cost_matrices.empty()) { EXE_CUOPT_FAIL("Cost matrix (or matrices) must be specified!"); }
Expand All @@ -50,6 +51,29 @@ void populate_matrices(data_model_view_t<i_t, f_t> const& data_model, d_mdarray_
}
}

if (!distance_matrices.empty()) {
for (auto const& [vtype, distance_matrix] : distance_matrices) {
cuopt_expects(cost_matrices.count(vtype) > 0,
error_type_t::ValidationError,
"Distance matrix vehicle types must have corresponding cost matrices");
const size_t matrix_size = static_cast<size_t>(nlocations) * nlocations;
const bool valid =
thrust::all_of(handle_ptr_->get_thrust_policy(),
distance_matrix,
distance_matrix + matrix_size,
[] __device__(f_t value) { return value == value && value >= f_t{0}; });
cuopt_expects(valid,
error_type_t::ValidationError,
"Distance matrix values must be non-negative and not NaN");
}
auto const types = get_unique_vehicle_types(data_model.get_vehicle_types(), stream_view_);
for (auto const& [vtype, mapped_type] : types) {
cuopt_expects(distance_matrices.count(vtype) > 0,
error_type_t::ValidationError,
"All vehicle distance matrices should be set");
}
}

auto n_matrix_types = detail::get_cost_matrix_type_dim<i_t, f_t>(data_model);
matrices_ =
detail::create_device_mdarray<f_t>(nlocations, n_vehicle_types, n_matrix_types, stream_view_);
Expand Down
13 changes: 11 additions & 2 deletions cpp/src/routing/fleet_info.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,10 @@ class fleet_info_t {

auto constexpr get_num_vehicles() const { return v_earliest_time_.size(); }

constexpr bool has_time_matrix() const { return matrices_.extent[1] > 1; }
constexpr bool has_time_matrix() const
{
return matrices_.time_matrix_index != matrices_.cost_matrix_index;
}

constexpr bool is_homogenous() const { return is_homogenous_; }

Expand Down Expand Up @@ -93,6 +96,9 @@ class fleet_info_t {
h.buckets = host_copy(v_buckets_, stream);
h.matrices = detail::create_host_mdarray<f_t>(
matrices_.extent[2], matrices_.extent[0], matrices_.extent[1]);
h.matrices.cost_matrix_index = matrices_.cost_matrix_index;
h.matrices.distance_matrix_index = matrices_.distance_matrix_index;
h.matrices.time_matrix_index = matrices_.time_matrix_index;
raft::copy(h.matrices.buffer.data(), matrices_.buffer.data(), matrices_.buffer.size(), stream);
return h;
}
Expand Down Expand Up @@ -178,7 +184,10 @@ class fleet_info_t {

constexpr i_t is_homogenous_fleet() const { return is_homogenous; }

constexpr bool has_time_matrix() const { return matrices.extent[1] > 1; }
constexpr bool has_time_matrix() const
{
return matrices.time_matrix_index != matrices.cost_matrix_index;
}
i_t num_vehicles = 0;
mdarray_view_t<f_t> matrices{};
const i_t* break_offset{nullptr};
Expand Down
6 changes: 3 additions & 3 deletions cpp/src/routing/solver.cu
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* clang-format off */
/*
* SPDX-FileCopyrightText: Copyright (c) 2021-2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
* SPDX-FileCopyrightText: Copyright (c) 2021-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
/* clang-format on */
Expand Down Expand Up @@ -43,8 +43,8 @@ solver_t<i_t, f_t>::solver_t(data_model_view_t<i_t, f_t> const& data_model,
solver_settings_t<i_t, f_t> const& settings)
: handle_ptr_(data_model.get_handle_ptr()), settings_(settings)
{
auto n_matrix_types = detail::get_cost_matrix_type_dim<i_t, f_t>(data_model);
if (n_matrix_types == 1 && !data_model.get_vehicle_max_times().empty()) {
if (!detail::has_transit_time_matrix<i_t, f_t>(data_model) &&
!data_model.get_vehicle_max_times().empty()) {
cuopt_expects(false,
error_type_t::ValidationError,
"Time matrix should be set in order to use vehicle max time constraints");
Expand Down
Loading