Skip to content

Commit

Permalink
tsp reoptimiztion for transitioncycles
Browse files Browse the repository at this point in the history
  • Loading branch information
LeonSering committed Jun 22, 2024
1 parent 68ee191 commit 429da59
Show file tree
Hide file tree
Showing 13 changed files with 476 additions and 187 deletions.
19 changes: 10 additions & 9 deletions heuristic_framework/src/local_search/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,13 @@ use search_result::SearchResult::{Improvement, NoImprovement};
// * time that local search started (Option)
type FunctionBetweenSteps<S> = Box<
dyn Fn(
u32,
&EvaluatedSolution<S>,
Option<&EvaluatedSolution<S>>,
Arc<Objective<S>>,
Option<Instant>,
),
u32,
&EvaluatedSolution<S>,
Option<&EvaluatedSolution<S>>,
Arc<Objective<S>>,
Option<Instant>,
) + Send
+ Sync,
>;

/// A local search neighborhood that provides for each solution a iterator over all neighbors.
Expand All @@ -51,7 +52,7 @@ pub trait Neighborhood<S>: Send + Sync {
pub struct LocalSearchSolver<S> {
neighborhood: Arc<dyn Neighborhood<S>>,
objective: Arc<Objective<S>>,
local_improver: Option<Box<dyn LocalImprover<S>>>,
local_improver: Option<Box<dyn LocalImprover<S> + Send + Sync>>,
function_between_steps: Option<FunctionBetweenSteps<S>>,
}

Expand All @@ -73,7 +74,7 @@ impl<S> LocalSearchSolver<S> {
pub fn with_local_improver_and_function(
neighborhood: Arc<dyn Neighborhood<S>>,
objective: Arc<Objective<S>>,
local_improver: Option<Box<dyn LocalImprover<S>>>,
local_improver: Option<Box<dyn LocalImprover<S> + Send + Sync>>,
function_between_steps: Option<FunctionBetweenSteps<S>>,
) -> Self {
Self {
Expand All @@ -91,7 +92,7 @@ impl<S> LocalSearchSolver<S> {
let init_solution = self.objective.evaluate(initial_solution);

// default local improver is Minimizer
let minimizer: Box<dyn LocalImprover<S>> = Box::new(Minimizer::new(
let minimizer: Box<dyn LocalImprover<S> + Send + Sync> = Box::new(Minimizer::new(
self.neighborhood.clone(),
self.objective.clone(),
));
Expand Down
18 changes: 10 additions & 8 deletions internal/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use solver::min_cost_flow_solver::MinCostFlowSolver;
use solver::objective;

use model::json_serialisation::load_rolling_stock_problem_instance_from_json;
use solver::transition_cycle_tsp::TransitionCycleWithInfo;
use solver::transition_local_search::{build_transition_local_search_solver, TransitionWithInfo};

use std::sync::Arc;
Expand Down Expand Up @@ -65,27 +66,28 @@ pub fn run(input_data: serde_json::Value) -> serde_json::Value {
let start_time_transition_optimization = stdtime::Instant::now();
let mut optimized_transitions: HashMap<VehicleTypeIdx, Transition> = HashMap::new();
let schedule = solution.solution().get_schedule();
let transition_local_search_solver =
build_transition_local_search_solver(schedule, network.clone());
for vehicle_type in network.vehicle_types().iter() {
println!(
"\nOptimizing transitions for vehicle type {}",
network.vehicle_types().get(vehicle_type).unwrap()
);
let transition_local_search_solver =
build_transition_local_search_solver(schedule, network.clone());
let start_transition = TransitionWithInfo::new(
schedule.next_day_transition_of(vehicle_type).clone(),
"Initial transition".to_string(),
);
let improved_transition = transition_local_search_solver.solve(start_transition);
optimized_transitions.insert(
vehicle_type,
improved_transition.unwrap_solution().unwrap_transition(),
);
let improved_transition = transition_local_search_solver
.solve(start_transition)
.unwrap_solution()
.unwrap_transition();

optimized_transitions.insert(vehicle_type, improved_transition);
}
let schedule_with_optimized_transitions =
schedule.set_next_day_transitions(optimized_transitions);
println!(
"\nTransition optimized (elapsed time: {:0.2}sec)",
"Transition optimized (elapsed time: {:0.2}sec)",
start_time_transition_optimization.elapsed().as_secs_f32()
);
schedule_with_optimized_transitions.print_next_day_transitions();
Expand Down
2 changes: 1 addition & 1 deletion model/src/json_serialisation/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ pub fn load_rolling_stock_problem_instance_from_json(

fn create_locations(json_input: &JsonInput) -> (Locations, HashMap<IdType, LocationIdx>) {
let planning_days = determine_planning_days(json_input);
let mut stations: HashMap<LocationIdx, (String, Option<VehicleCount>)> = HashMap::new(); // PERF: use vec instead
let mut stations: HashMap<LocationIdx, (String, Option<VehicleCount>)> = HashMap::new(); // PpRF: use vec instead
let mut dead_head_trips: HashMap<LocationIdx, HashMap<LocationIdx, DeadHeadTrip>> =
HashMap::new();

Expand Down
2 changes: 2 additions & 0 deletions server/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ pub fn solve_instance(input_data: serde_json::Value) -> serde_json::Value {
objective.evaluate(start_schedule_with_info.clone())
};

// TODO add transition local search here

// reassign end depots to be consistent with transitions
let final_schedule = solution
.solution()
Expand Down
75 changes: 22 additions & 53 deletions solution/src/transition.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
pub mod modifications;
pub mod transition_cycle;

use std::collections::HashSet;

use im::HashMap;
Expand All @@ -12,6 +14,8 @@ pub type CycleIdx = usize;

use crate::tour::Tour;

use self::transition_cycle::TransitionCycle;

#[derive(Clone)]
pub struct Transition {
cycles: Vec<TransitionCycle>,
Expand Down Expand Up @@ -140,7 +144,7 @@ impl Transition {
let cycle_lookup = cycles
.iter()
.enumerate()
.flat_map(|(idx, cycle)| cycle.cycle.iter().map(move |&vehicle| (vehicle, idx)))
.flat_map(|(idx, cycle)| cycle.iter().map(move |vehicle| (vehicle, idx)))
.collect();

Transition {
Expand All @@ -155,9 +159,9 @@ impl Transition {
pub fn get_successor_of(&self, vehicle: VehicleIdx) -> VehicleIdx {
let cycle_idx = self.cycle_lookup.get(&vehicle).unwrap();
let cycle = self.cycles.get(*cycle_idx).unwrap();
let vehicle_position = cycle.cycle.iter().position(|&v| v == vehicle).unwrap();
let successor_position = (vehicle_position + 1) % cycle.cycle.len();
cycle.cycle[successor_position]
let vehicle_position = cycle.iter().position(|v| v == vehicle).unwrap();
let successor_position = (vehicle_position + 1) % cycle.len();
cycle.get_vec()[successor_position]
}

pub fn number_of_cycles(&self) -> usize {
Expand All @@ -168,6 +172,10 @@ impl Transition {
self.cycles.iter()
}

pub fn get_cycle(&self, cycle_idx: CycleIdx) -> &TransitionCycle {
self.cycles.get(cycle_idx).unwrap()
}

pub fn maintenance_violation(&self) -> MaintenanceCounter {
self.total_maintenance_violation
}
Expand All @@ -178,7 +186,7 @@ impl Transition {

pub fn print(&self) {
for transition_cycle in self.cycles.iter() {
if !transition_cycle.cycle.is_empty() {
if !transition_cycle.is_empty() {
println!("{}", transition_cycle);
}
}
Expand All @@ -199,7 +207,7 @@ impl Transition {
let cycles: Vec<VehicleIdx> = self
.cycles
.iter()
.flat_map(|transition_cycle| transition_cycle.cycle.iter().cloned())
.flat_map(|transition_cycle| transition_cycle.iter())
.collect();
assert_eq!(cycles.len(), tours.len());
let vehicles_from_tours: HashSet<VehicleIdx> = tours.keys().cloned().collect();
Expand All @@ -211,16 +219,15 @@ impl Transition {
let mut computed_total_maintenance_counter = 0;
for transition_cycle in self.cycles.iter() {
let mut computed_maintenance_counter: MaintenanceCounter = transition_cycle
.cycle
.iter()
.map(|&vehicle_id| tours.get(&vehicle_id).unwrap().maintenance_counter())
.map(|vehicle_id| tours.get(&vehicle_id).unwrap().maintenance_counter())
.sum();

let dead_head_distance_between_depots = match transition_cycle.cycle.len() {
let dead_head_distance_between_depots = match transition_cycle.len() {
0 => 0,
1 => {
let vehicle = transition_cycle.cycle.first().unwrap();
let tour = tours.get(vehicle).unwrap();
let vehicle = transition_cycle.first().unwrap();
let tour = tours.get(&vehicle).unwrap();
network
.dead_head_distance_between(
tour.end_depot().unwrap(),
Expand All @@ -230,7 +237,7 @@ impl Transition {
.unwrap_or(INF_DISTANCE) as MaintenanceCounter
}
_ => transition_cycle
.cycle
.get_vec()
.iter()
.circular_tuple_windows()
.map(|(vehicle_1, vehicle_2)| {
Expand All @@ -252,7 +259,7 @@ impl Transition {

assert_eq!(
computed_maintenance_counter,
transition_cycle.maintenance_counter,
transition_cycle.maintenance_counter(),
);
computed_total_maintenance_violation += computed_maintenance_counter.max(0);
computed_total_maintenance_counter += computed_maintenance_counter;
Expand All @@ -268,12 +275,12 @@ impl Transition {

// verify cycle lookup
for (vehicle, cycle_idx) in self.cycle_lookup.iter() {
assert!(self.cycles[*cycle_idx].cycle.contains(vehicle));
assert!(self.cycles[*cycle_idx].get_vec().contains(vehicle));
}

// verify empty cycles
for empty_cycle_idx in self.empty_cycles.iter() {
assert!(self.cycles[*empty_cycle_idx].cycle.is_empty());
assert!(self.cycles[*empty_cycle_idx].is_empty());
}
}

Expand Down Expand Up @@ -301,41 +308,3 @@ impl Transition {
*maintenance_counter += tour.maintenance_counter() + dist_between_end_depot_to_start_depot;
}
}

#[derive(Debug, Clone)]
pub struct TransitionCycle {
cycle: Vec<VehicleIdx>,
maintenance_counter: MaintenanceCounter,
}

impl TransitionCycle {
pub fn new(cycle: Vec<VehicleIdx>, maintenance_counter: MaintenanceCounter) -> TransitionCycle {
TransitionCycle {
cycle,
maintenance_counter,
}
}

pub fn iter(&self) -> impl Iterator<Item = VehicleIdx> + '_ {
self.cycle.iter().copied()
}

pub fn get_vec(&self) -> &Vec<VehicleIdx> {
&self.cycle
}
}

impl std::fmt::Display for TransitionCycle {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(
f,
"Cycle: ({}), counter: {}",
self.cycle
.iter()
.map(|&idx| format!("{}", idx.idx()))
.collect::<Vec<String>>()
.join(", "),
self.maintenance_counter
)
}
}
Loading

0 comments on commit 429da59

Please sign in to comment.