///|
fn build_distance_matrix(
depot : Depot,
customers : Array[Customer],
metric : DistanceMetric,
) -> Array[Array[Double]] {
let size = customers.length() + 1
let depot_index = customers.length()
let matrix = Array::makei(size, fn(_) { Array::make(size, 0.0) })
for i in 0.. VrpInstance {
make_instance_with_metric(
name,
variant,
depot,
vehicle,
customers,
DistanceMetric::Euclidean,
)
}
///|
/// Builds an instance with an explicit distance convention.
pub fn make_instance_with_metric(
name : String,
variant : ProblemVariant,
depot : Depot,
vehicle : VehicleProfile,
customers : Array[Customer],
metric : DistanceMetric,
) -> VrpInstance {
{
name,
variant,
metric,
depot,
vehicle,
customers,
distance_matrix: build_distance_matrix(depot, customers, metric),
}
}
///|
pub fn Depot::new(
x : Double,
y : Double,
ready_time? : Double = 0.0,
due_time? : Double = 1_000_000_000.0,
) -> Depot {
{ x, y, ready_time, due_time }
}
///|
pub fn Customer::new(
external_id : Int,
x : Double,
y : Double,
demand : Double,
ready_time? : Double = 0.0,
due_time? : Double = 1_000_000_000.0,
service_time? : Double = 0.0,
) -> Customer {
{ external_id, x, y, demand, ready_time, due_time, service_time }
}
///|
pub fn VehicleProfile::new(
capacity : Double,
fleet_size? : Int = 32,
max_route_duration? : Double = 1_000_000_000.0,
) -> VehicleProfile {
{ capacity, max_route_duration, fleet_size }
}
///|
pub fn SolveConfig::default() -> SolveConfig {
{
iterations: 600,
local_search_rounds: 0,
segment_length: 25,
min_destroy: 2,
max_destroy: 6,
initial_temperature: 12.0,
cooling_rate: 0.992,
capacity_penalty: 1200.0,
time_penalty: 1600.0,
duration_penalty: 600.0,
unassigned_penalty: 20_000.0,
}
}
///|
pub fn SolveConfig::tune(
self : SolveConfig,
iterations? : Int = self.iterations,
local_search_rounds? : Int = self.local_search_rounds,
segment_length? : Int = self.segment_length,
min_destroy? : Int = self.min_destroy,
max_destroy? : Int = self.max_destroy,
initial_temperature? : Double = self.initial_temperature,
cooling_rate? : Double = self.cooling_rate,
capacity_penalty? : Double = self.capacity_penalty,
time_penalty? : Double = self.time_penalty,
duration_penalty? : Double = self.duration_penalty,
unassigned_penalty? : Double = self.unassigned_penalty,
) -> SolveConfig {
{
iterations,
local_search_rounds,
segment_length,
min_destroy,
max_destroy,
initial_temperature,
cooling_rate,
capacity_penalty,
time_penalty,
duration_penalty,
unassigned_penalty,
}
}
///|
pub fn SolveStats::new(
iterations : Int,
accepted_moves : Int,
improved_moves : Int,
best_iteration : Int,
seed_objective : Double,
best_objective : Double,
local_improvements? : Int = 0,
evaluated_candidates? : Int = 0,
) -> SolveStats {
{
iterations,
accepted_moves,
improved_moves,
local_improvements,
evaluated_candidates,
best_iteration,
seed_objective,
best_objective,
}
}
///|
pub fn SolveResult::new(
instance : VrpInstance,
seed_solution : Solution,
best_solution : Solution,
stats : SolveStats,
) -> SolveResult {
{ instance, seed_solution, best_solution, stats }
}