///|
/// Numerical policy used by integrations that need explicit tolerances.
pub struct FeasibilityPolicy {
capacity_tolerance : Double
time_tolerance : Double
duration_tolerance : Double
max_unassigned : Int
} derive(Debug, ToJson)
///|
pub fn FeasibilityPolicy::default() -> FeasibilityPolicy {
{
capacity_tolerance: 0.000001,
time_tolerance: 0.000001,
duration_tolerance: 0.000001,
max_unassigned: 0,
}
}
///|
pub fn FeasibilityPolicy::tune(
self : FeasibilityPolicy,
capacity_tolerance? : Double = self.capacity_tolerance,
time_tolerance? : Double = self.time_tolerance,
duration_tolerance? : Double = self.duration_tolerance,
max_unassigned? : Int = self.max_unassigned,
) -> FeasibilityPolicy {
{
capacity_tolerance: capacity_tolerance.max(0.0),
time_tolerance: time_tolerance.max(0.0),
duration_tolerance: duration_tolerance.max(0.0),
max_unassigned: max_unassigned.max(0),
}
}
///|
pub fn FeasibilityPolicy::capacity_tolerance(
self : FeasibilityPolicy,
) -> Double {
self.capacity_tolerance
}
///|
pub fn FeasibilityPolicy::time_tolerance(self : FeasibilityPolicy) -> Double {
self.time_tolerance
}
///|
pub fn FeasibilityPolicy::duration_tolerance(
self : FeasibilityPolicy,
) -> Double {
self.duration_tolerance
}
///|
pub fn FeasibilityPolicy::max_unassigned(self : FeasibilityPolicy) -> Int {
self.max_unassigned
}
///|
pub struct FeasibilityAssessment {
feasible : Bool
capacity_violation : Double
time_violation : Double
duration_violation : Double
unassigned_count : Int
route_count : Int
validation : ValidationReport
} derive(Debug, ToJson)
///|
pub fn FeasibilityAssessment::is_feasible(self : FeasibilityAssessment) -> Bool {
self.feasible
}
///|
pub fn FeasibilityAssessment::capacity_violation(
self : FeasibilityAssessment,
) -> Double {
self.capacity_violation
}
///|
pub fn FeasibilityAssessment::time_violation(
self : FeasibilityAssessment,
) -> Double {
self.time_violation
}
///|
pub fn FeasibilityAssessment::duration_violation(
self : FeasibilityAssessment,
) -> Double {
self.duration_violation
}
///|
pub fn FeasibilityAssessment::unassigned_count(
self : FeasibilityAssessment,
) -> Int {
self.unassigned_count
}
///|
pub fn FeasibilityAssessment::route_count(self : FeasibilityAssessment) -> Int {
self.route_count
}
///|
pub fn FeasibilityAssessment::validation(
self : FeasibilityAssessment,
) -> ValidationReport {
self.validation
}
///|
/// Evaluates a solution against a caller-provided tolerance policy.
pub fn assess_solution(
instance : VrpInstance,
solution : Solution,
policy : FeasibilityPolicy,
) -> FeasibilityAssessment {
let validation = validate_solution(instance, solution)
let capacity = solution.total_capacity_violation()
let time = solution.total_time_violation()
let duration = solution.total_duration_violation()
let unassigned = solution.unassigned_customers().length()
let within_limits = capacity <= policy.capacity_tolerance &&
time <= policy.time_tolerance &&
duration <= policy.duration_tolerance &&
unassigned <= policy.max_unassigned &&
solution.route_count() <= instance.vehicle().fleet_size()
{
feasible: within_limits && validation.is_valid(),
capacity_violation: capacity,
time_violation: time,
duration_violation: duration,
unassigned_count: unassigned,
route_count: solution.route_count(),
validation,
}
}
///|
pub fn route_can_accept(
instance : VrpInstance,
route : RoutePlan,
customer_index : Int,
policy : FeasibilityPolicy,
) -> Bool {
match insertion_delta(instance, route, customer_index, route.stop_count()) {
None => false
Some(_) => {
let next = route.stops.copy()
next.push(customer_index)
let metrics = evaluate_route(instance, next)
metrics.capacity_violation <= policy.capacity_tolerance &&
metrics.time_violation <= policy.time_tolerance &&
metrics.duration_violation <= policy.duration_tolerance
}
}
}
///|
pub fn feasible_insertions(
instance : VrpInstance,
solution : Solution,
customer_index : Int,
config : SolveConfig,
policy : FeasibilityPolicy,
) -> Array[CandidateInsertion] {
best_insertions_for_customer(instance, solution, customer_index, config).filter(fn(
candidate,
) {
let result = candidate.result()
assess_solution(instance, result, policy).is_feasible() ||
(
result.total_capacity_violation() <= policy.capacity_tolerance &&
result.total_time_violation() <= policy.time_tolerance &&
result.total_duration_violation() <= policy.duration_tolerance
)
},
)
}