///|
/// Returns a user-facing description of a problem variant.
pub fn ProblemVariant::description(self : ProblemVariant) -> String {
match self {
ProblemVariant::CVRP => "capacity-constrained vehicle routing"
ProblemVariant::VRPTW => "vehicle routing with time windows"
}
}
///|
pub fn DistanceMetric::description(self : DistanceMetric) -> String {
match self {
DistanceMetric::Euclidean => "continuous Euclidean distance"
DistanceMetric::RoundedEuclidean => "rounded Euclidean distance (EUC_2D)"
DistanceMetric::Manhattan => "rectilinear Manhattan distance"
DistanceMetric::Chebyshev => "maximum-coordinate Chebyshev distance"
}
}
///|
pub fn VrpInstance::validate(self : VrpInstance) -> ValidationReport {
validate_instance(self)
}
///|
pub fn Solution::validate(
self : Solution,
instance : VrpInstance,
) -> ValidationReport {
validate_solution(instance, self)
}
///|
pub fn Solution::is_complete(self : Solution, instance : VrpInstance) -> Bool {
self.unassigned.is_empty() &&
self.assigned_customers().length() == instance.customer_count() &&
solution_duplicate_count(self) == 0
}
///|
pub fn Solution::is_valid_for(
self : Solution,
instance : VrpInstance,
policy : FeasibilityPolicy,
) -> Bool {
assess_solution(instance, self, policy).is_feasible()
}
///|
pub fn RoutePlan::distance(self : RoutePlan) -> Double {
self.metrics.distance
}
///|
pub fn RoutePlan::load(self : RoutePlan) -> Double {
self.metrics.load
}
///|
pub fn RoutePlan::duration(self : RoutePlan) -> Double {
self.metrics.duration
}
///|
pub fn RoutePlan::is_feasible(self : RoutePlan) -> Bool {
self.metrics.feasible
}
///|
pub fn RoutePlan::customer_indices(self : RoutePlan) -> Array[Int] {
self.stops.copy()
}
///|
pub fn VrpInstance::has_time_windows(self : VrpInstance) -> Bool {
if self.variant is ProblemVariant::VRPTW {
true
} else {
self.customers.any(fn(customer) {
customer.ready_time != 0.0 || customer.due_time != 1_000_000_000.0
})
}
}
///|
pub fn VrpInstance::is_capacity_feasible(self : VrpInstance) -> Bool {
self.total_demand() <= self.fleet_capacity()
}
///|
pub fn VrpInstance::customer_labels(self : VrpInstance) -> Array[Int] {
self.customers.map(fn(customer) { customer.external_id })
}
///|
pub fn SolveConfig::iteration_budget(self : SolveConfig) -> Int {
self.iterations.max(0)
}
///|
pub fn SolveConfig::destroy_range(self : SolveConfig) -> (Int, Int) {
(self.min_destroy.max(0), self.max_destroy.max(self.min_destroy))
}
///|
pub fn SolveConfig::penalties(
self : SolveConfig,
) -> (Double, Double, Double, Double) {
(
self.capacity_penalty,
self.time_penalty,
self.duration_penalty,
self.unassigned_penalty,
)
}
///|
pub fn SolveResult::best(self : SolveResult) -> Solution {
self.best_solution.copy()
}
///|
pub fn SolveResult::seed(self : SolveResult) -> Solution {
self.seed_solution.copy()
}
///|
pub fn SolveResult::problem(self : SolveResult) -> VrpInstance {
self.instance
}
///|
pub fn SolveResult::is_improved(self : SolveResult) -> Bool {
self.stats.best_objective < self.stats.seed_objective
}
///|
pub fn SolveResult::improvement(self : SolveResult) -> Double {
self.stats.seed_objective - self.stats.best_objective
}
///|
pub fn SolveResult::completion_ratio(self : SolveResult) -> Double {
self.best_solution.completion_ratio(self.instance)
}
///|
pub fn Customer::window_width(self : Customer) -> Double {
self.due_time - self.ready_time
}
///|
pub fn VehicleProfile::capacity_per_route(self : VehicleProfile) -> Double {
self.capacity
}
///|
pub fn RouteMetrics::total_violation(self : RouteMetrics) -> Double {
self.capacity_violation + self.time_violation + self.duration_violation
}
///|
pub fn ConstraintSummary::unserved_ratio(
self : ConstraintSummary,
instance : VrpInstance,
) -> Double {
if instance.customer_count() == 0 {
0.0
} else {
Double::from_int(self.unassigned_count) /
Double::from_int(instance.customer_count())
}
}
///|
pub fn CostBreakdown::penalty_total(self : CostBreakdown) -> Double {
self.capacity_penalty +
self.time_penalty +
self.duration_penalty +
self.unassigned_penalty
}