Skip to content

Commit

Permalink
Apply some refactorings
Browse files Browse the repository at this point in the history
  • Loading branch information
reinterpretcat committed Sep 10, 2024
1 parent f80fb9a commit 869d882
Show file tree
Hide file tree
Showing 13 changed files with 60 additions and 67 deletions.
72 changes: 34 additions & 38 deletions vrp-core/src/construction/heuristics/evaluators.rs
Original file line number Diff line number Diff line change
Expand Up @@ -271,42 +271,58 @@ fn analyze_insertion_in_route_leg(
single: &Single,
target: &mut Activity,
route_costs: InsertionCost,
init: SingleContext,
mut single_ctx: SingleContext,
) -> ControlFlow<SingleContext, SingleContext> {
let (items, index) = leg;
let (prev, next) = match items {
[prev] => (prev, None),
[prev, next] => (prev, Some(next)),
_ => return ControlFlow::Break(init),
_ => return ControlFlow::Break(single_ctx),
};
let start_time = route_ctx.route().tour.start().map_or(Timestamp::default(), |act| act.schedule.departure);
// analyze service details
single.places.iter().enumerate().try_fold(init, |acc, (idx, detail)| {
// analyze detail time windows
detail.times.iter().try_fold(acc, |acc, time| {
target.place = Place {
idx,
location: detail.location.unwrap_or(prev.place.location),
duration: detail.duration,
time: time.to_time_window(start_time),
};

// iterate over places and times to find the next best insertion point
for (place_idx, place) in single.places.iter().enumerate() {
target.place.idx = place_idx;
target.place.location = place.location.unwrap_or(prev.place.location);
target.place.duration = place.duration;

// iterate over time windows of the place
for time in place.times.iter() {
target.place.time = time.to_time_window(start_time);

let activity_ctx = ActivityContext { index, prev, target, next };
let move_ctx = MoveContext::activity(route_ctx, &activity_ctx);

if let Some(violation) = eval_ctx.goal.evaluate(&move_ctx) {
return SingleContext::fail(violation, acc);
let is_stopped = violation.stopped;
single_ctx.violation = Some(violation);
if is_stopped {
// should stop processing this leg and next ones
return ControlFlow::Break(single_ctx);
} else {
// can continue within the next place
continue;
}
}

let costs = eval_ctx.goal.estimate(&move_ctx) + &route_costs;
let other_costs = acc.cost.clone().unwrap_or_else(InsertionCost::max_value);
let other_costs = single_ctx.cost.clone().unwrap_or_else(InsertionCost::max_value);

match eval_ctx.result_selector.select_cost(&costs, &other_costs) {
Either::Left(_) => SingleContext::success(activity_ctx.index, costs, target.place.clone()),
Either::Right(_) => SingleContext::skip(acc),
// found better insertion
Either::Left(_) => {
single_ctx.violation = None;
single_ctx.index = index;
single_ctx.cost = Some(costs.clone());
single_ctx.place = Some(target.place.clone());
}
Either::Right(_) => continue,
}
})
})
}
}

ControlFlow::Continue(single_ctx)
}

fn get_insertion_index(route_ctx: &RouteContext, position: InsertionPosition) -> Option<usize> {
Expand Down Expand Up @@ -335,26 +351,6 @@ impl SingleContext {
fn new(cost: Option<InsertionCost>, index: usize) -> Self {
Self { violation: None, index, cost, place: None }
}

fn fail(violation: ConstraintViolation, other: SingleContext) -> ControlFlow<Self, Self> {
let stopped = violation.stopped;
let ctx = Self { violation: Some(violation), index: other.index, cost: other.cost, place: other.place };
if stopped {
ControlFlow::Break(ctx)
} else {
ControlFlow::Continue(ctx)
}
}

#[allow(clippy::unnecessary_wraps)]
fn success(index: usize, cost: InsertionCost, place: Place) -> ControlFlow<Self, Self> {
ControlFlow::Continue(Self { violation: None, index, cost: Some(cost), place: Some(place) })
}

#[allow(clippy::unnecessary_wraps)]
fn skip(other: SingleContext) -> ControlFlow<Self, Self> {
ControlFlow::Continue(other)
}
}

/// Stores information needed for multi job insertion.
Expand Down
3 changes: 1 addition & 2 deletions vrp-core/src/models/problem/fleet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ mod fleet_test;
use crate::models::common::*;
use crate::utils::short_type_name;
use rosomaxa::prelude::Float;
use std::cmp::Ordering::Less;
use std::collections::{HashMap, HashSet};
use std::fmt::{Debug, Formatter};
use std::hash::{Hash, Hasher};
Expand Down Expand Up @@ -162,7 +161,7 @@ impl Fleet {

let profiles: HashMap<usize, Profile> = vehicles.iter().map(|v| (v.profile.index, v.profile.clone())).collect();
let mut profiles = profiles.into_iter().collect::<Vec<_>>();
profiles.sort_by(|(a, _), (b, _)| a.partial_cmp(b).unwrap_or(Less));
profiles.sort_by(|(a, _), (b, _)| a.cmp(b));
let (_, profiles): (Vec<_>, Vec<_>) = profiles.into_iter().unzip();

let actors = vehicles
Expand Down
4 changes: 2 additions & 2 deletions vrp-core/src/models/problem/jobs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use crate::models::common::*;
use crate::models::problem::{Costs, Fleet, TransportCost};
use crate::utils::{short_type_name, Either};
use rosomaxa::prelude::Float;
use rosomaxa::utils::compare_floats_f32;
use rosomaxa::utils::{compare_floats_f32, compare_floats_f32_refs};
use std::cmp::Ordering::Less;
use std::collections::HashMap;
use std::fmt::{Debug, Formatter};
Expand Down Expand Up @@ -324,7 +324,7 @@ fn create_index(fleet: &Fleet, jobs: Vec<Job>, transport: &(dyn TransportCost))
.filter(|j| **j != job)
.map(|j| (j.clone(), get_cost_between_jobs(profile, avg_costs, transport, &job, j)))
.collect();
sorted_job_costs.sort_by(|(_, a), (_, b)| a.partial_cmp(b).unwrap_or(Less));
sorted_job_costs.sort_by(|(_, a), (_, b)| compare_floats_f32_refs(a, b));

sorted_job_costs.truncate(MAX_NEIGHBOURS);
sorted_job_costs.shrink_to_fit();
Expand Down
8 changes: 4 additions & 4 deletions vrp-core/src/solver/search/recreate/recreate_with_blinks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ use crate::models::Problem;
use crate::solver::search::recreate::Recreate;
use crate::solver::RefinementContext;
use rosomaxa::prelude::*;
use std::cmp::Ordering;
use std::sync::Arc;

struct ChunkJobSelector {
Expand Down Expand Up @@ -49,9 +48,10 @@ impl JobSelector for RankedJobSelector {
fn prepare(&self, insertion_ctx: &mut InsertionContext) {
let problem = &insertion_ctx.problem;

insertion_ctx.solution.required.sort_by(|a, b| {
Self::rank_job(problem, a).partial_cmp(&Self::rank_job(problem, b)).unwrap_or(Ordering::Less)
});
insertion_ctx
.solution
.required
.sort_by(|a, b| compare_floats(Self::rank_job(problem, a), Self::rank_job(problem, b)));

if self.asc_order {
insertion_ctx.solution.required.reverse();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ impl InsertionEvaluator for SkipBestInsertionEvaluator {

// TODO use result_selector?
results.sort_by(|a, b| match (a, b) {
(InsertionResult::Success(a), InsertionResult::Success(b)) => a.cost.partial_cmp(&b.cost).unwrap_or(Less),
(InsertionResult::Success(a), InsertionResult::Success(b)) => a.cost.cmp(&b.cost),
(InsertionResult::Success(_), InsertionResult::Failure(_)) => Less,
(InsertionResult::Failure(_), InsertionResult::Success(_)) => Greater,
(
Expand Down
3 changes: 1 addition & 2 deletions vrp-core/src/solver/search/ruin/worst_jobs_removal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ use crate::solver::search::{get_route_jobs, JobRemovalTracker, TabuList};
use crate::solver::RefinementContext;
use rosomaxa::utils::parallel_collect;
use std::cell::RefCell;
use std::cmp::Ordering::Less;
use std::collections::HashMap;
use std::iter::once;
use std::sync::Arc;
Expand Down Expand Up @@ -103,7 +102,7 @@ fn get_routes_cost_savings(insertion_ctx: &InsertionContext) -> Vec<(Profile, Ve
})
.drain()
.collect();
savings.sort_by(|(_, a), (_, b)| b.partial_cmp(a).unwrap_or(Less));
savings.sort_by(|(_, a), (_, b)| compare_floats_refs(b, a));

(route_ctx.route().actor.vehicle.profile.clone(), savings)
})
Expand Down
3 changes: 1 addition & 2 deletions vrp-core/tests/unit/models/solution/actor_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ use crate::helpers::models::problem::{test_driver, test_vehicle_detail, FleetBui
use crate::models::common::TimeInterval;
use crate::models::problem::{Actor, VehicleDetail, VehiclePlace};
use crate::models::solution::Registry;
use std::cmp::Ordering::Less;
use std::sync::Arc;

parameterized_test! {can_provide_available_actors_from_registry, (count, expected), {
Expand Down Expand Up @@ -50,7 +49,7 @@ fn can_provide_next_actors_from_registry() {
actors.sort_by(|a, b| {
let a = a.detail.start.as_ref().map(|s| s.location);
let b = b.detail.start.as_ref().map(|s| s.location);
a.partial_cmp(&b).unwrap_or(Less)
a.cmp(&b)
});
assert_eq!(actors.len(), 2);
assert_eq!(actors.first().unwrap().detail.start.as_ref().map(|s| s.location), Some(0));
Expand Down
3 changes: 1 addition & 2 deletions vrp-pragmatic/src/format/coord_index.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ mod coord_index_test;

use crate::format::problem::{Problem, VehicleBreak};
use crate::format::{CustomLocationType, Location};
use std::cmp::Ordering::Less;
use std::collections::{HashMap, HashSet};
use std::hash::{Hash, Hasher};

Expand Down Expand Up @@ -127,7 +126,7 @@ impl CoordIndex {
/// Gets unique locations.
pub fn unique(&self) -> Vec<Location> {
let mut sorted_pairs: Vec<_> = self.reverse_index.iter().collect();
sorted_pairs.sort_by(|(a, _), (b, _)| a.partial_cmp(b).unwrap_or(Less));
sorted_pairs.sort_by(|(a, _), (b, _)| a.cmp(b));
sorted_pairs.iter().map(|pair| pair.1.clone()).collect()
}

Expand Down
11 changes: 7 additions & 4 deletions vrp-pragmatic/src/format/problem/fleet_reader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,15 +60,18 @@ pub(super) fn create_transport_costs(

let mut durations: Vec<Duration> = Vec::with_capacity(capacity);
let mut distances: Vec<Distance> = Vec::with_capacity(capacity);
let err_fn = |i| move || GenericError::from(format!("invalid matrix index: {i}"));

for (i, error) in error_codes.iter().enumerate() {
if *error > 0 {
durations.push(-1.);
distances.push(-1.);
} else {
durations.push(*matrix.travel_times.get(i).unwrap() as Float);
distances.push(*matrix.distances.get(i).unwrap() as Float);
durations.push(*matrix.travel_times.get(i).ok_or_else(err_fn(i))? as Float);
distances.push(*matrix.distances.get(i).ok_or_else(err_fn(i))? as Float);
}
}

(durations, distances)
} else {
(
Expand All @@ -77,9 +80,9 @@ pub(super) fn create_transport_costs(
)
};

MatrixData::new(profile, timestamp.map(|t| parse_time(&t)), durations, distances)
Ok(MatrixData::new(profile, timestamp.map(|t| parse_time(&t)), durations, distances))
})
.collect::<Vec<_>>();
.collect::<Result<Vec<_>, GenericError>>()?;

let matrix_indices = matrix_data.iter().map(|data| data.index).collect::<HashSet<_>>().len();
if matrix_profiles.len() != matrix_indices {
Expand Down
4 changes: 2 additions & 2 deletions vrp-pragmatic/src/validation/common.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::parse_time_safe;
use std::cmp::Ordering::Less;
use std::collections::HashSet;
use vrp_core::models::common::TimeWindow;
use vrp_core::utils::compare_floats;

/// Checks time window rules.
pub fn check_raw_time_windows(tws: &[Vec<String>], skip_intersection_check: bool) -> bool {
Expand All @@ -18,7 +18,7 @@ pub fn check_time_windows(tws: &[Option<TimeWindow>], skip_intersection_check: b
if let [a] = tws.as_slice() {
a.start <= a.end
} else {
tws.sort_by(|a, b| a.start.partial_cmp(&b.start).unwrap_or(Less));
tws.sort_by(|a, b| compare_floats(a.start, b.start));
tws.windows(2).any(|pair| {
if let [a, b] = pair {
a.start <= a.end && b.start <= b.end && (skip_intersection_check || !a.intersects(b))
Expand Down
5 changes: 2 additions & 3 deletions vrp-pragmatic/tests/generator/common.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
use super::*;
use crate::format::Location;
use crate::{format_time, parse_time};
use std::cmp::Ordering::Less;
use std::ops::Range;
use std::time::Duration;
use vrp_core::models::common::TimeWindow;
use vrp_core::prelude::Float;
use vrp_core::prelude::{compare_floats, Float};

/// Creates `Duration` from hours amount.
pub fn from_hours(hours: i32) -> Duration {
Expand Down Expand Up @@ -60,7 +59,7 @@ prop_compose! {
})) -> Vec<Vec<String>> {

let mut time_windows = time_windows;
time_windows.sort_by(|a, b| a.start.partial_cmp(&b.start).unwrap_or(Less));
time_windows.sort_by(|a, b| compare_floats(a.start, b.start));

time_windows.iter().map(|tw| vec![format_time(tw.start), format_time(tw.end)]).collect()
}
Expand Down
4 changes: 2 additions & 2 deletions vrp-pragmatic/tests/helpers/solution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use crate::format::solution::*;
use crate::format::{CustomLocationType, Location};
use crate::format_time;
use crate::helpers::ToLocation;
use std::cmp::Ordering::{Equal, Less};
use std::cmp::Ordering::Equal;
use std::collections::HashMap;
use std::io::{BufReader, BufWriter};
use std::sync::Arc;
Expand Down Expand Up @@ -439,7 +439,7 @@ pub fn assert_vehicle_agnostic(result: Solution, expected: Solution) {
});

result.tours.sort_by(|a, b| {
let ordering = a.vehicle_id.partial_cmp(&b.vehicle_id).unwrap_or(Less);
let ordering = a.vehicle_id.cmp(&b.vehicle_id);

if ordering == Equal {
a.shift_index.cmp(&b.shift_index)
Expand Down
5 changes: 2 additions & 3 deletions vrp-pragmatic/tests/helpers/solver.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use crate::checker::CheckerContext;
use crate::format::problem::{Matrix, PragmaticProblem, Problem};
use crate::format::solution::{create_solution, Solution};
use std::cmp::Ordering::Less;
use std::sync::Arc;
use vrp_core::construction::heuristics::InsertionContext;
use vrp_core::models::Problem as CoreProblem;
Expand Down Expand Up @@ -114,10 +113,10 @@ fn get_core_solution<F: FnOnce(Arc<CoreProblem>) -> CoreSolution>(
fn sort_all_data(solution: Solution) -> Solution {
let mut solution = solution;

solution.tours.sort_by(|a, b| a.vehicle_id.partial_cmp(&b.vehicle_id).unwrap_or(Less));
solution.tours.sort_by(|a, b| a.vehicle_id.cmp(&b.vehicle_id));

if let Some(ref mut unassigned) = solution.unassigned {
unassigned.sort_by(|a, b| a.job_id.partial_cmp(&b.job_id).unwrap_or(Less));
unassigned.sort_by(|a, b| a.job_id.cmp(&b.job_id));
}

solution
Expand Down

0 comments on commit 869d882

Please sign in to comment.