///|
/// Neighborhoods implemented by the local improvement layer.
pub(all) enum NeighborhoodKind {
TwoOpt
Relocate
Swap
Reverse
} derive(Debug, Eq, ToJson)
///|
pub struct ImprovementReport {
solution : @core.Solution
rounds : Int
moves : Int
start_objective : Double
final_objective : Double
} derive(Debug, ToJson)
///|
pub fn ImprovementReport::new(
solution : @core.Solution,
rounds : Int,
moves : Int,
start_objective : Double,
final_objective : Double,
) -> ImprovementReport {
{ solution, rounds, moves, start_objective, final_objective }
}
///|
pub fn ImprovementReport::solution(self : ImprovementReport) -> @core.Solution {
self.solution.copy()
}
///|
pub fn ImprovementReport::rounds(self : ImprovementReport) -> Int {
self.rounds
}
///|
pub fn ImprovementReport::moves(self : ImprovementReport) -> Int {
self.moves
}
///|
pub fn ImprovementReport::start_objective(self : ImprovementReport) -> Double {
self.start_objective
}
///|
pub fn ImprovementReport::final_objective(self : ImprovementReport) -> Double {
self.final_objective
}
///|
fn accept_improvement(
current : @core.Solution,
candidate : @core.Solution,
) -> @core.Solution {
if candidate.is_feasible() &&
candidate.objective_value() < current.objective_value() {
candidate
} else {
current
}
}
///|
/// Searches all single-customer relocations and keeps the best strict improvement.
pub fn best_relocate(
instance : @core.VrpInstance,
solution : @core.Solution,
config : @core.SolveConfig,
) -> @core.Solution {
let mut best = solution
for from_route in 0.. ()
Some(route) =>
for from_position in 0.. ()
Some(target) =>
for to_position in 0..<=target.stop_count() {
match
@core.relocate_customer(
instance, solution, from_route, from_position, to_route, to_position,
config,
) {
None => ()
Some(candidate) =>
best = accept_improvement(best, candidate)
}
}
}
}
}
}
}
best
}
///|
/// Searches all swaps between route positions and keeps the best improvement.
pub fn best_swap(
instance : @core.VrpInstance,
solution : @core.Solution,
config : @core.SolveConfig,
) -> @core.Solution {
let mut best = solution
for left_route in 0.. ()
Some(left_plan) =>
for left_position in 0.. ()
Some(right_plan) => {
let start_position = if right_route == left_route {
left_position + 1
} else {
0
}
for right_position in start_position.. ()
Some(candidate) =>
best = accept_improvement(best, candidate)
}
}
}
}
}
}
}
}
best
}
///|
/// Searches a complete route reversal neighborhood.
pub fn best_reverse(
instance : @core.VrpInstance,
solution : @core.Solution,
config : @core.SolveConfig,
) -> @core.Solution {
let mut best = solution
for route_index in 0.. ()
Some(candidate) => best = accept_improvement(best, candidate)
}
}
best
}
///|
/// Runs a bounded variable-neighborhood descent over the core move operators.
pub fn variable_neighborhood_descent(
instance : @core.VrpInstance,
solution : @core.Solution,
config : @core.SolveConfig,
max_rounds : Int,
) -> @core.Solution {
let mut current = solution
let rounds = max_rounds.max(0)
for _ in 0..= before {
break
}
}
current
}
///|
/// Same search as `variable_neighborhood_descent`, with useful run statistics.
pub fn variable_neighborhood_report(
instance : @core.VrpInstance,
solution : @core.Solution,
config : @core.SolveConfig,
max_rounds : Int,
) -> ImprovementReport {
let start = solution.objective_value()
let improved = variable_neighborhood_descent(
instance, solution, config, max_rounds,
)
let finish = improved.objective_value()
let moves = if finish < start { 1 } else { 0 }
ImprovementReport::new(improved, max_rounds.max(0), moves, start, finish)
}