///|
/// Parameters for the optional simulated-annealing driver.
pub struct AnnealingConfig {
iterations : Int
initial_temperature : Double
cooling_rate : Double
destroy_size : Int
seed : Int
} derive(Debug, ToJson)
///|
pub fn AnnealingConfig::default() -> AnnealingConfig {
{
iterations: 240,
initial_temperature: 18.0,
cooling_rate: 0.985,
destroy_size: 4,
seed: 31,
}
}
///|
pub fn AnnealingConfig::tune(
self : AnnealingConfig,
iterations? : Int = self.iterations,
initial_temperature? : Double = self.initial_temperature,
cooling_rate? : Double = self.cooling_rate,
destroy_size? : Int = self.destroy_size,
seed? : Int = self.seed,
) -> AnnealingConfig {
{ iterations, initial_temperature, cooling_rate, destroy_size, seed }
}
///|
pub fn AnnealingConfig::iterations(self : AnnealingConfig) -> Int {
self.iterations
}
///|
pub fn AnnealingConfig::initial_temperature(self : AnnealingConfig) -> Double {
self.initial_temperature
}
///|
pub fn AnnealingConfig::cooling_rate(self : AnnealingConfig) -> Double {
self.cooling_rate
}
///|
pub fn AnnealingConfig::destroy_size(self : AnnealingConfig) -> Int {
self.destroy_size
}
///|
pub fn AnnealingConfig::seed(self : AnnealingConfig) -> Int {
self.seed
}
///|
pub struct AnnealingRun {
result : @core.SolveResult
iterations : Int
accepted_moves : Int
improved_moves : Int
final_temperature : Double
} derive(Debug)
///|
pub fn AnnealingRun::result(self : AnnealingRun) -> @core.SolveResult {
self.result
}
///|
pub fn AnnealingRun::iterations(self : AnnealingRun) -> Int {
self.iterations
}
///|
pub fn AnnealingRun::accepted_moves(self : AnnealingRun) -> Int {
self.accepted_moves
}
///|
pub fn AnnealingRun::improved_moves(self : AnnealingRun) -> Int {
self.improved_moves
}
///|
pub fn AnnealingRun::final_temperature(self : AnnealingRun) -> Double {
self.final_temperature
}
///|
/// Computes the Metropolis acceptance probability for a positive delta.
pub fn acceptance_probability(delta : Double, temperature : Double) -> Double {
if delta <= 0.0 {
1.0
} else {
@math.exp(-(delta / temperature.max(0.000001)))
}
}
///|
fn annealing_destroy_operator(iteration : Int) -> @core.DestroyOperator {
match iteration % 6 {
0 => @core.DestroyOperator::Random
1 => @core.DestroyOperator::Shaw
2 => @core.DestroyOperator::Worst
3 => @core.DestroyOperator::Route
4 => @core.DestroyOperator::Cluster
_ => @core.DestroyOperator::TimeCritical
}
}
///|
fn annealing_repair_operator(iteration : Int) -> @core.RepairOperator {
match iteration % 4 {
0 => @core.RepairOperator::Greedy
1 => @core.RepairOperator::Regret2
2 => @core.RepairOperator::DemandFirst
_ => @core.RepairOperator::TimeWindow
}
}
///|
/// Runs a deterministic simulated-annealing search from a Clarke-Wright seed.
pub fn anneal(
instance : @core.VrpInstance,
solve_config : @core.SolveConfig,
config : AnnealingConfig,
) -> AnnealingRun {
let seed_solution = @constructive.clarke_wright_seed(
instance,
config=solve_config,
)
let mut current = seed_solution
let mut best = seed_solution
let rng = @rng.Rng::new(config.seed)
let mut temperature = config.initial_temperature.max(0.000001)
let mut accepted_moves = 0
let mut improved_moves = 0
let mut best_iteration = 0
let iterations = config.iterations.max(0)
for iteration in 0.. String {
let summary = summarize_search(run.result)
[
search_summary_text(summary),
"Annealing accepted moves: \{run.accepted_moves}",
"Annealing improved moves: \{run.improved_moves}",
"Annealing final temperature: \{run.final_temperature}",
].join("\n")
}