Skip to content

Commit

Permalink
Fix issue with vicinity clustering and jobs in relation #141
Browse files Browse the repository at this point in the history
  • Loading branch information
reinterpretcat committed Dec 15, 2023
1 parent bbcb1ed commit 9f32b84
Show file tree
Hide file tree
Showing 5 changed files with 79 additions and 13 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ All notable changes to this project will be documented in this file.
* unexpected total_order behavior in dynamic heuristic (#128)
* improve validation rule for break with time offset (#129)
* fix issue with skills (#133)
* do not cluster jobs if they are defined in relations (#141)


## [v1.22.1]- 2023-08-26
Expand Down
10 changes: 8 additions & 2 deletions vrp-core/src/models/problem/jobs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -243,12 +243,18 @@ impl Jobs {
/// Returns range of jobs "near" to given one. Near is defined by costs with relation
/// transport profile and departure time.
pub fn neighbors(&self, profile: &Profile, job: &Job, _: Timestamp) -> impl Iterator<Item = (&Job, Cost)> {
self.index.get(&profile.index).unwrap().get(job).unwrap().0.iter().map(|(job, cost)| (job, *cost as f64))
let index = self.index.get(&profile.index).expect("no profile index");
let (neighbours_info, _) = index.get(job).expect("no job in profile index");

neighbours_info.iter().map(|(job, cost)| (job, *cost as f64))
}

/// Returns job rank as relative cost from any vehicle's start position.
pub fn rank(&self, profile: &Profile, job: &Job) -> Cost {
self.index.get(&profile.index).unwrap().get(job).unwrap().1 as f64
let index = self.index.get(&profile.index).expect("no profile index");
let &(_, cost) = index.get(job).expect("no job in profile index");

cost as f64
}

/// Returns amount of jobs.
Expand Down
32 changes: 21 additions & 11 deletions vrp-pragmatic/src/format/problem/clustering_reader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ pub(super) fn create_cluster_config(api_problem: &ApiProblem) -> Result<Option<C
}
VicinityServingPolicy::Fixed { value, parking } => ServingPolicy::Fixed { value, parking },
},
filtering: get_filter_policy(filtering.as_ref()),
filtering: get_filter_policy(api_problem, filtering.as_ref()),
building: get_builder_policy(),
})),
}
Expand Down Expand Up @@ -76,16 +76,26 @@ fn get_builder_policy() -> BuilderPolicy {
}
}

fn get_filter_policy(filtering: Option<&VicinityFilteringPolicy>) -> FilterPolicy {
if let Some(filtering) = filtering {
let excluded_job_ids = filtering.exclude_job_ids.iter().cloned().collect::<HashSet<_>>();
FilterPolicy {
job_filter: Arc::new(move |job| {
job.dimens().get_job_id().map_or(true, |job_id| !excluded_job_ids.contains(job_id))
}),
actor_filter: Arc::new(|_| true),
}
fn get_filter_policy(api_problem: &ApiProblem, filtering: Option<&VicinityFilteringPolicy>) -> FilterPolicy {
let relation_ids = api_problem
.plan
.relations
.iter()
.flat_map(|relations| relations.iter())
.flat_map(|relation| relation.jobs.iter())
.cloned()
.collect::<HashSet<_>>();

let excluded_job_ids = if let Some(filtering) = filtering {
filtering.exclude_job_ids.iter().cloned().chain(relation_ids.into_iter()).collect::<HashSet<_>>()

Check failure on line 90 in vrp-pragmatic/src/format/problem/clustering_reader.rs

View workflow job for this annotation

GitHub Actions / test-build

explicit call to `.into_iter()` in function argument accepting `IntoIterator`
} else {
FilterPolicy { job_filter: Arc::new(|_| true), actor_filter: Arc::new(|_| true) }
relation_ids
};

FilterPolicy {
job_filter: Arc::new(move |job| {
job.dimens().get_job_id().map_or(true, |job_id| !excluded_job_ids.contains(job_id))
}),
actor_filter: Arc::new(|_| true),
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
use super::*;

parameterized_test! {can_handle_job_in_relation_with_vicinity_cluster, type_field, {
can_handle_job_in_relation_with_vicinity_cluster_impl(type_field);
}}

can_handle_job_in_relation_with_vicinity_cluster! {
case_01_strict: RelationType::Any,
case_02_sequence: RelationType::Sequence,
case_03_strict: RelationType::Strict,
}

fn can_handle_job_in_relation_with_vicinity_cluster_impl(type_field: RelationType) {
let problem = Problem {
plan: Plan {
jobs: vec![create_delivery_job("job1", (1., 0.)), create_delivery_job("job2", (1., 0.))],
clustering: Some(Clustering::Vicinity {
profile: VehicleProfile { matrix: "car".to_string(), scale: None },
threshold: VicinityThresholdPolicy {
duration: 10.,
distance: 10.,
min_shared_time: None,
smallest_time_window: None,
max_jobs_per_cluster: None,
},
visiting: VicinityVisitPolicy::Continue,
serving: VicinityServingPolicy::Original { parking: 300. },
filtering: None,
}),
relations: Some(vec![Relation {
type_field,
jobs: vec!["departure".to_string(), "job1".to_string()],
vehicle_id: "my_vehicle_1".to_string(),
shift_index: None,
}]),
..create_empty_plan()
},
fleet: Fleet { vehicles: vec![create_default_vehicle("my_vehicle")], ..create_default_fleet() },
..create_empty_problem()
};
let matrix = create_matrix_from_problem(&problem);

let solution = solve_with_metaheuristic(problem, Some(vec![matrix]));

assert!(solution.unassigned.is_none());
assert_eq!(solution.tours[0].stops.len(), 3);
assert_eq!(solution.tours[0].stops[1].activities().len(), 2);
}
1 change: 1 addition & 0 deletions vrp-pragmatic/tests/features/clustering/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,5 +131,6 @@ fn create_test_problem(jobs_data: &[(f64, &str)], capacity: i32, clustering: Clu

mod basic_vicinity_test;
mod capacity_vicinity_test;
mod combination_vicinity_test;
mod profile_vicinity_test;
mod specific_vicinity_test;

0 comments on commit 9f32b84

Please sign in to comment.