Skip to content
Closed
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 .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ python/**/*.cpp
# datasets
datasets/**
!datasets/ref
!datasets/ref/fsmvrptwsc_small.txt
!datasets/get_test_data.sh
!datasets/distance_engine
!datasets/sat/get_test_data.sh
Expand Down
6 changes: 6 additions & 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 All @@ -88,8 +89,13 @@ class cpu_routing_problem_t {
std::vector<uint8_t> drop_return_trips; // 0/1 (avoid vector<bool>)
std::vector<uint8_t> skip_first_trips; // 0/1
std::vector<float> vehicle_max_costs;
std::vector<float> vehicle_max_distances;
std::vector<float> vehicle_max_times;
std::vector<float> vehicle_fixed_costs;
std::vector<float> distance_tier_thresholds;
std::vector<float> distance_tier_fixed_costs;
std::vector<float> distance_tier_costs_per_unit;
std::vector<int32_t> distance_tier_offsets;

std::vector<int32_t> order_locations;
std::vector<int32_t> order_tw_earliest;
Expand Down
97 changes: 85 additions & 12 deletions cpp/include/cuopt/routing/data_model_view.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,18 +55,28 @@ class data_model_view_t {
i_t const fleet_size,
i_t num_orders = -1);

/**
* @brief Set a distance matrix used by distance constraints and tiered pricing.
*
*
* @param[in] matrix Device memory pointer to a floating point square matrix of
* size
* num_locations_. cuOpt does not own or copy this data.
* @param[in] vehicle_type Identifier
* of the vehicle.
*/
void add_distance_matrix(f_t const* matrix, uint8_t vehicle_type = 0);

/**
* @brief Set a cost matrix for all locations (depot included) at
* once. A cost matrix is defined a square matrix containing the
* costs, taken pairwise, between all locations. Entries are non-negative
* real numbers. Diagonal elements
* should be 0. Users should pre-compute costs between each pair of
* locations with their own technique before calling this function. Entries in
* this matrix could represent time, miles, meters or any metric that can be
* stored as a real number and satisfy the property above.
* The user can call add_cost_matrix multiple times. Setting the
* vehicle type will enable heterogenous fleet. It can model traveling
* costs for different vehicles (bicyces, bikes, trucks).
* once. A cost matrix
* is defined a square matrix containing the costs, taken pairwise, between all locations. Entries
* are non-negative real numbers. Diagonal elements should be 0. Users should pre-compute costs
* between each pair of locations with their own technique before calling this function. Entries
* in this matrix could represent time, miles, meters or any metric that can be stored as a real
* number and satisfy the property above. The user can call add_cost_matrix multiple times.
* Setting the vehicle type will enable heterogenous fleet. It can model traveling costs for
* different vehicles (bicyces, bikes, trucks).
*
*
* @throws cuopt::logic_error when an error occurs.
Expand Down Expand Up @@ -408,9 +418,17 @@ class data_model_view_t {
*/
void set_min_vehicles(i_t min_vehicles);

/**
* @brief Limits the distance matrix values accumulated along each route.
* @param[in]
* vehicle_max_distances Upper bound for each vehicle's route distance.
*/
void set_vehicle_max_distances(f_t const* vehicle_max_distances);

/**
* @brief Limits the primary matrix cost cumulated along a route.
* @param[in] vehicle_max_costs Upper bound for route cost.
* @param[in]
* vehicle_max_costs Upper bound for route cost.
*/
void set_vehicle_max_costs(f_t const* vehicle_max_costs);

Expand All @@ -430,6 +448,32 @@ class data_model_view_t {
*/
void set_vehicle_max_times(f_t const* vehicle_max_times);

/**
* @brief Set distance-based tiered pricing for vehicles.
* Each vehicle can have multiple tiers with different cost structures based on total route
* distance. Tier costs are accumulated by distance band in ascending threshold order.
*
* @param[in] thresholds Device memory pointer to distance thresholds for all tiers (flattened
* array)
* @param[in] fixed_costs Device memory pointer to fixed costs for all tiers (flattened array)
* @param[in] costs_per_unit Device memory pointer to cost per unit for all tiers (flattened
* array)
* @param[in] tier_offsets Device memory pointer to offsets array (size = fleet_size + 1)
* tier_offsets[i] indicates where vehicle i's tiers start in the flattened arrays
* @param[in] total_tiers Total number of tiers across all vehicles
*/
void set_vehicle_distance_tiers(f_t const* thresholds,
f_t const* fixed_costs,
f_t const* costs_per_unit,
i_t const* tier_offsets,
i_t total_tiers);

/**
* @brief Get distance matrix
* @return Distance matrix pointer
*/
f_t const* get_distance_matrix(uint8_t vehicle_type = 0) const noexcept;

/**
* @brief Get cost matrix
* @return Matrix pointer
Expand All @@ -442,10 +486,17 @@ class data_model_view_t {
*/
f_t const* get_transit_time_matrix(uint8_t vehicle_type = 0) const noexcept;

/**
* @brief Get all distance matrices as a map
* @return map of vehicle type to distance
* matrix
*/
std::unordered_map<uint8_t, f_t const*> get_distance_matrices() const noexcept;

/**
* @brief Get all cost matrices as a map
* @return map of vehicle type to cost matrix
*/
*/
std::unordered_map<uint8_t, f_t const*> get_cost_matrices() const noexcept;

/**
Expand Down Expand Up @@ -623,6 +674,12 @@ class data_model_view_t {
*/
i_t get_min_vehicles() const noexcept;

/**
* @brief Return max distance allowed per vehicle
* @return max distance per route
*/
raft::device_span<f_t const> get_vehicle_max_distances() const noexcept;

/**
* @brief Return max cost allowed per vehicle
* @return max cost per route
Expand All @@ -641,6 +698,13 @@ class data_model_view_t {
*/
raft::device_span<f_t const> get_vehicle_fixed_costs() const noexcept;

/**
* @brief Get distance tiers configuration for all vehicles
* @return Tuple of (thresholds, fixed_costs, costs_per_unit, tier_offsets, total_tiers)
*/
std::tuple<f_t const*, f_t const*, f_t const*, i_t const*, i_t> get_vehicle_distance_tiers()
const noexcept;

/**
* @brief Get raft handle object containing GPU resource objects
* @return Handle object
Expand All @@ -661,6 +725,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 All @@ -687,10 +752,18 @@ class data_model_view_t {
std::unordered_map<i_t, std::pair<i_t const*, i_t>> precedence_{};
i_t min_num_vehicles_{0};

raft::device_span<f_t const> vehicle_max_distances_{};
raft::device_span<f_t const> vehicle_max_costs_{};
raft::device_span<f_t const> vehicle_max_times_{};
raft::device_span<f_t const> vehicle_fixed_costs_{};

// Distance tiers for tiered pricing
f_t const* distance_tier_thresholds_{nullptr};
f_t const* distance_tier_fixed_costs_{nullptr};
f_t const* distance_tier_costs_per_unit_{nullptr};
i_t const* distance_tier_offsets_{nullptr};
i_t total_distance_tiers_{0};

raft::device_span<i_t const> initial_vehicle_ids_{};
raft::device_span<i_t const> initial_routes_{};
raft::device_span<node_type_t const> initial_types_{};
Expand Down
10 changes: 10 additions & 0 deletions cpp/src/grpc/routing/cuopt_routing.proto
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,13 @@ message InitialSolution {
repeated int32 sol_offsets = 4;
}

message VehicleDistanceTiers {
repeated float thresholds = 1;
repeated float fixed_costs = 2;
repeated float costs_per_unit = 3;
repeated int32 offsets = 4;
}

// --- Main problem ---

message RoutingProblem {
Expand All @@ -86,6 +93,7 @@ message RoutingProblem {

repeated CostMatrix cost_matrices = 10;
repeated CostMatrix transit_time_matrices = 11;
repeated CostMatrix distance_matrices = 12;

// Vehicle arrays (size = fleet_size)
repeated int32 vehicle_start_locations = 20;
Expand All @@ -98,6 +106,8 @@ message RoutingProblem {
repeated float vehicle_max_costs = 27;
repeated float vehicle_max_times = 28;
repeated float vehicle_fixed_costs = 29;
repeated float vehicle_max_distances = 35;
VehicleDistanceTiers vehicle_distance_tiers = 36;

// Order arrays (size = num_orders)
repeated int32 order_locations = 30;
Expand Down
5 changes: 5 additions & 0 deletions cpp/src/grpc/routing/grpc_routing_mapper_utils.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
#include <google/protobuf/repeated_field.h>

#include <cstdint>
#include <limits>
#include <stdexcept>
#include <vector>

namespace cuopt {
Expand Down Expand Up @@ -40,6 +42,9 @@ inline void copy_u32_to_u8(const google::protobuf::RepeatedField<uint32_t>& src,
dst.clear();
dst.reserve(static_cast<size_t>(src.size()));
for (auto v : src) {
if (v > std::numeric_limits<uint8_t>::max()) {
throw std::invalid_argument("vehicle type must be within [0, 255]");
}
dst.push_back(static_cast<uint8_t>(v));
}
}
Expand Down
39 changes: 39 additions & 0 deletions cpp/src/grpc/routing/grpc_routing_problem_mapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
#include "grpc_routing_mapper_utils.hpp"

#include <cstdint>
#include <limits>
#include <stdexcept>
#include <utility>

namespace cuopt {
Expand All @@ -29,17 +31,32 @@ void map_proto_to_routing_problem(const cuopt::remote::RoutingProblem& pb,
p.num_orders = pb.num_orders();

for (auto const& cm : pb.cost_matrices()) {
if (cm.vehicle_type() > std::numeric_limits<uint8_t>::max()) {
throw std::invalid_argument("cost matrix vehicle_type must be within [0, 255]");
}
cuopt::routing::cpu_cost_matrix_t out;
out.vehicle_type = static_cast<uint8_t>(cm.vehicle_type());
copy_repeated_to_vector(cm.values(), out.matrix);
p.cost_matrices.push_back(std::move(out));
}
for (auto const& tm : pb.transit_time_matrices()) {
if (tm.vehicle_type() > std::numeric_limits<uint8_t>::max()) {
throw std::invalid_argument("transit time matrix vehicle_type must be within [0, 255]");
}
cuopt::routing::cpu_cost_matrix_t out;
out.vehicle_type = static_cast<uint8_t>(tm.vehicle_type());
copy_repeated_to_vector(tm.values(), out.matrix);
p.transit_time_matrices.push_back(std::move(out));
}
for (auto const& dm : pb.distance_matrices()) {
if (dm.vehicle_type() > std::numeric_limits<uint8_t>::max()) {
throw std::invalid_argument("distance matrix vehicle_type must be within [0, 255]");
}
cuopt::routing::cpu_cost_matrix_t out;
out.vehicle_type = static_cast<uint8_t>(dm.vehicle_type());
copy_repeated_to_vector(dm.values(), out.matrix);
p.distance_matrices.push_back(std::move(out));
}

copy_repeated_to_vector(pb.vehicle_start_locations(), p.vehicle_start_locations);
copy_repeated_to_vector(pb.vehicle_return_locations(), p.vehicle_return_locations);
Expand All @@ -49,8 +66,16 @@ void map_proto_to_routing_problem(const cuopt::remote::RoutingProblem& pb,
copy_bool_to_u8(pb.drop_return_trips(), p.drop_return_trips);
copy_bool_to_u8(pb.skip_first_trips(), p.skip_first_trips);
copy_repeated_to_vector(pb.vehicle_max_costs(), p.vehicle_max_costs);
copy_repeated_to_vector(pb.vehicle_max_distances(), p.vehicle_max_distances);
copy_repeated_to_vector(pb.vehicle_max_times(), p.vehicle_max_times);
copy_repeated_to_vector(pb.vehicle_fixed_costs(), p.vehicle_fixed_costs);
if (pb.has_vehicle_distance_tiers()) {
auto const& tiers = pb.vehicle_distance_tiers();
copy_repeated_to_vector(tiers.thresholds(), p.distance_tier_thresholds);
copy_repeated_to_vector(tiers.fixed_costs(), p.distance_tier_fixed_costs);
copy_repeated_to_vector(tiers.costs_per_unit(), p.distance_tier_costs_per_unit);
copy_repeated_to_vector(tiers.offsets(), p.distance_tier_offsets);
}

copy_repeated_to_vector(pb.order_locations(), p.order_locations);
copy_repeated_to_vector(pb.order_tw_earliest(), p.order_tw_earliest);
Expand Down Expand Up @@ -155,6 +180,11 @@ void map_routing_problem_to_proto(const cuopt::routing::cpu_routing_problem_t& p
out->set_vehicle_type(tm.vehicle_type);
copy_vector_to_repeated(tm.matrix, out->mutable_values());
}
for (auto const& dm : p.distance_matrices) {
auto* out = pb->add_distance_matrices();
out->set_vehicle_type(dm.vehicle_type);
copy_vector_to_repeated(dm.matrix, out->mutable_values());
}

copy_vector_to_repeated(p.vehicle_start_locations, pb->mutable_vehicle_start_locations());
copy_vector_to_repeated(p.vehicle_return_locations, pb->mutable_vehicle_return_locations());
Expand All @@ -170,8 +200,17 @@ void map_routing_problem_to_proto(const cuopt::routing::cpu_routing_problem_t& p
pb->add_skip_first_trips(v != 0);
}
copy_vector_to_repeated(p.vehicle_max_costs, pb->mutable_vehicle_max_costs());
copy_vector_to_repeated(p.vehicle_max_distances, pb->mutable_vehicle_max_distances());
copy_vector_to_repeated(p.vehicle_max_times, pb->mutable_vehicle_max_times());
copy_vector_to_repeated(p.vehicle_fixed_costs, pb->mutable_vehicle_fixed_costs());
if (!p.distance_tier_thresholds.empty() || !p.distance_tier_fixed_costs.empty() ||
!p.distance_tier_costs_per_unit.empty() || !p.distance_tier_offsets.empty()) {
auto* tiers = pb->mutable_vehicle_distance_tiers();
copy_vector_to_repeated(p.distance_tier_thresholds, tiers->mutable_thresholds());
copy_vector_to_repeated(p.distance_tier_fixed_costs, tiers->mutable_fixed_costs());
copy_vector_to_repeated(p.distance_tier_costs_per_unit, tiers->mutable_costs_per_unit());
copy_vector_to_repeated(p.distance_tier_offsets, tiers->mutable_offsets());
}

copy_vector_to_repeated(p.order_locations, pb->mutable_order_locations());
copy_vector_to_repeated(p.order_tw_earliest, pb->mutable_order_tw_earliest());
Expand Down
Loading