Skip to content

Commit

Permalink
Tweak job index and other refactorings
Browse files Browse the repository at this point in the history
  • Loading branch information
reinterpretcat committed Apr 24, 2024
1 parent e1d35f4 commit 749477e
Show file tree
Hide file tree
Showing 3 changed files with 23 additions and 69 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ All notable changes to this project will be documented in this file.
* minor refactorings
* increase limits for ruin methods
* tweak rosomaxa algorithm
* tweak job index


## [v1.23.0]- 2023-12-22
Expand Down
57 changes: 19 additions & 38 deletions vrp-core/src/models/problem/jobs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ mod jobs_test;

use crate::models::common::*;
use crate::models::problem::{Costs, Fleet, TransportCost};
use crate::utils::short_type_name;
use crate::utils::{short_type_name, Either};
use hashbrown::HashMap;
use rosomaxa::utils::compare_floats_f32;
use std::cmp::Ordering::Less;
Expand Down Expand Up @@ -219,9 +219,9 @@ const DEFAULT_COST: LowPrecisionCost = 0.;
const UNREACHABLE_COST: LowPrecisionCost = f32::MAX;

/// Maximum amount of job's neighbours stored in index. We restrict this side to lower impact on
/// memory footprint. It is unlikely that more than 1000 neighbours needed to be processed in reality,
/// memory footprint. It is unlikely that more than 100 neighbours needed to be processed in reality,
/// but we keep it 5x times more.
const MAX_NEIGHBOURS: usize = 5000;
const MAX_NEIGHBOURS: usize = 512;

/// Stores all jobs taking into account their neighborhood.
pub struct Jobs {
Expand Down Expand Up @@ -289,10 +289,10 @@ impl Hash for Job {
}

/// Returns job locations.
pub fn get_job_locations<'a>(job: &'a Job) -> Box<dyn Iterator<Item = Option<Location>> + 'a> {
pub fn get_job_locations<'a>(job: &'a Job) -> impl Iterator<Item = Option<Location>> + 'a {

Check failure on line 292 in vrp-core/src/models/problem/jobs.rs

View workflow job for this annotation

GitHub Actions / test-build

the following explicit lifetimes could be elided: 'a
match job {
Job::Single(single) => Box::new(single.places.iter().map(|p| p.location)),
Job::Multi(multi) => Box::new(multi.jobs.iter().flat_map(|j| j.places.iter().map(|p| p.location))),
Job::Single(single) => Either::Left(single.places.iter().map(|p| p.location)),
Job::Multi(multi) => Either::Right(multi.jobs.iter().flat_map(|j| j.places.iter().map(|p| p.location))),
}
}

Expand Down Expand Up @@ -321,10 +321,12 @@ fn create_index(
.iter()
.filter(|j| **j != job)
.map(|j| (j.clone(), get_cost_between_jobs(profile, avg_costs, transport, &job, j)))
.take(MAX_NEIGHBOURS)
.collect();
sorted_job_costs.sort_by(|(_, a), (_, b)| a.partial_cmp(b).unwrap_or(Less));

sorted_job_costs.truncate(MAX_NEIGHBOURS);
sorted_job_costs.shrink_to_fit();

let fleet_costs = starts
.iter()
.cloned()
Expand Down Expand Up @@ -368,12 +370,10 @@ fn get_cost_between_job_and_location(
to: Location,
) -> LowPrecisionCost {
get_job_locations(job)
.map(|from| match from {
Some(from) => get_cost_between_locations(profile, costs, transport, from, to),
_ => DEFAULT_COST,
})
.flatten()
.map(|from| get_cost_between_locations(profile, costs, transport, from, to))
.min_by(|a, b| a.partial_cmp(b).unwrap_or(Less))
.unwrap_or(DEFAULT_COST)
.unwrap_or(UNREACHABLE_COST)
}

/// Returns minimal cost between jobs.
Expand All @@ -387,38 +387,19 @@ fn get_cost_between_jobs(
let outer: Vec<Option<Location>> = get_job_locations(lhs).collect();
let inner: Vec<Option<Location>> = get_job_locations(rhs).collect();

let (location_cost, duration) = outer
let routing_cost = outer
.iter()
.flat_map(|o| inner.iter().map(move |i| (*o, *i)))
.map(|pair| match pair {
(Some(from), Some(to)) => {
let total = get_cost_between_locations(profile, costs, transport, from, to);
let duration = transport.duration_approx(profile, from, to);
(total, duration)
}
_ => (DEFAULT_COST, 0.),
})
.min_by(|(a, _), (b, _)| compare_floats_f32(*a, *b))
.unwrap_or((DEFAULT_COST, 0.));

let time_cost = lhs
.places()
.flat_map(|place| place.times.iter())
.flat_map(|left| {
rhs.places().flat_map(|place| place.times.iter()).map(move |right| match (left, right) {
(TimeSpan::Window(left), TimeSpan::Window(right)) => {
// NOTE exclude traveling duration between jobs
(left.distance(right) - duration).max(0.)
}
_ => 0.,
})
(Some(from), Some(to)) => get_cost_between_locations(profile, costs, transport, from, to),
_ => DEFAULT_COST,
})
.map(|cost| cost as LowPrecisionCost)
.min_by(|a, b| compare_floats_f32(*a, *b))
.unwrap_or(0.)
* costs.per_waiting_time as LowPrecisionCost;
.unwrap_or(DEFAULT_COST);

// NOTE: ignore time window difference costs as it is hard to balance with routing costs

location_cost + time_cost
routing_cost
}

fn get_avg_profile_costs(fleet: &Fleet) -> HashMap<usize, Costs> {
Expand Down
34 changes: 3 additions & 31 deletions vrp-pragmatic/src/validation/objectives.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ mod objectives_test;
use super::*;
use crate::format::problem::Objective::*;
use crate::utils::combine_error_results;
use hashbrown::HashSet;

/// Checks that objective is not empty when specified.
fn check_e1600_empty_objective(objectives: &[&Objective]) -> Result<(), FormatError> {
Expand All @@ -21,38 +22,9 @@ fn check_e1600_empty_objective(objectives: &[&Objective]) -> Result<(), FormatEr

/// Checks that each objective type specified only once.
fn check_e1601_duplicate_objectives(objectives: &[&Objective]) -> Result<(), FormatError> {
let mut duplicates = objectives
.iter()
.fold(HashMap::new(), |mut acc, objective| {
match objective {
MinimizeCost => acc.entry("minimize-cost"),
MinimizeDistance => acc.entry("minimize-distance"),
MinimizeDuration => acc.entry("minimize-duration"),
MinimizeTours => acc.entry("minimize-tours"),
MaximizeTours => acc.entry("maximize-tours"),
MaximizeValue { .. } => acc.entry("maximize-value"),
MinimizeUnassigned { .. } => acc.entry("minimize-unassigned"),
MinimizeArrivalTime => acc.entry("minimize-arrival-time"),
BalanceMaxLoad { .. } => acc.entry("balance-max-load"),
BalanceActivities { .. } => acc.entry("balance-activities"),
BalanceDistance { .. } => acc.entry("balance-distance"),
BalanceDuration { .. } => acc.entry("balance-duration"),
CompactTour { .. } => acc.entry("compact-tour"),
TourOrder => acc.entry("tour-order"),
FastService { .. } => acc.entry("fast-service"),
}
.and_modify(|count| *count += 1)
.or_insert(1_usize);

acc
})
.iter()
.filter_map(|(name, count)| if *count > 1 { Some((*name).to_string()) } else { None })
.collect::<Vec<_>>();

duplicates.sort();
let unique = objectives.iter().cloned().map(std::mem::discriminant).collect::<HashSet<_>>();

if duplicates.is_empty() {
if unique.len() == objectives.len() {
Ok(())
} else {
Err(FormatError::new(
Expand Down

0 comments on commit 749477e

Please sign in to comment.