Skip to content

Commit

Permalink
Build clusters in job index
Browse files Browse the repository at this point in the history
  • Loading branch information
reinterpretcat committed Oct 1, 2024
1 parent e4c60b7 commit 036df17
Show file tree
Hide file tree
Showing 10 changed files with 47 additions and 25 deletions.
2 changes: 1 addition & 1 deletion vrp-core/src/models/domain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@ impl ProblemBuilder {
let logger = self.logger.unwrap_or_else(|| Arc::new(|msg| println!("{}", msg)));

// setup jobs
let jobs = Arc::new(Jobs::new(fleet.as_ref(), self.jobs, transport.as_ref(), &logger));
let jobs = Arc::new(Jobs::new(fleet.as_ref(), self.jobs, transport.as_ref(), &logger)?);

Ok(Problem { fleet, jobs, locks: vec![], goal, activity, transport, extras })
}
Expand Down
35 changes: 26 additions & 9 deletions vrp-core/src/models/problem/jobs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@
#[path = "../../../tests/unit/models/problem/jobs_test.rs"]
mod jobs_test;

use crate::construction::clustering::dbscan::create_job_clusters;
use crate::models::common::*;
use crate::models::problem::{Costs, Fleet, TransportCost};
use crate::utils::{short_type_name, Either};
use rosomaxa::prelude::{Float, InfoLogger};
use rosomaxa::prelude::{Float, GenericResult, InfoLogger};
use rosomaxa::utils::{compare_floats_f32, compare_floats_f32_refs, parallel_collect, Timer};
use std::cmp::Ordering::Less;
use std::collections::HashMap;
use std::collections::{HashMap, HashSet};
use std::fmt::{Debug, Formatter};
use std::hash::{Hash, Hasher};
use std::sync::{Arc, Weak};
Expand Down Expand Up @@ -233,14 +234,22 @@ const MAX_NEIGHBOURS: usize = 256;
pub struct Jobs {
jobs: Vec<Job>,
index: HashMap<usize, JobIndex>,
_clusters: Vec<HashSet<Job>>,
}

impl Jobs {
/// Creates a new instance of [`Jobs`].
pub fn new(fleet: &Fleet, jobs: Vec<Job>, transport: &(dyn TransportCost), logger: &InfoLogger) -> Jobs {
pub fn new(
fleet: &Fleet,
jobs: Vec<Job>,
transport: &(dyn TransportCost),
logger: &InfoLogger,
) -> GenericResult<Jobs> {
let index = create_index(fleet, jobs.clone(), transport, logger);
let _clusters =
create_job_clusters(&jobs, fleet, Some(3), None, |profile, job| neighbors(&index, profile, job))?;

Jobs { jobs, index }
Ok(Jobs { jobs, index, _clusters })
}

/// Returns all jobs in the original order as a slice.
Expand All @@ -251,11 +260,7 @@ 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)
.and_then(|index| index.get(job))
.into_iter()
.flat_map(|(info, _)| info.iter().map(|(job, cost)| (job, *cost as Float)))
neighbors(&self.index, profile, job)
}

/// Returns job rank as relative cost from any vehicle's start position.
Expand Down Expand Up @@ -295,6 +300,18 @@ impl Hash for Job {
}
}

fn neighbors<'a>(
index: &'a HashMap<usize, JobIndex>,
profile: &Profile,
job: &Job,
) -> impl Iterator<Item = (&'a Job, Cost)> {
index
.get(&profile.index)
.and_then(|index| index.get(job))
.into_iter()
.flat_map(|(info, _)| info.iter().map(|(job, cost)| (job, *cost as Float)))
}

/// Returns job locations.
pub fn get_job_locations(job: &Job) -> impl Iterator<Item = Option<Location>> + '_ {
match job {
Expand Down
2 changes: 1 addition & 1 deletion vrp-core/src/solver/processing/vicinity_clustering.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ impl HeuristicContextProcessing for VicinityClustering {

let problem = Arc::new(Problem {
fleet: problem.fleet.clone(),
jobs: Arc::new(Jobs::new(problem.fleet.as_ref(), jobs, problem.transport.as_ref(), &logger)),
jobs: Arc::new(Jobs::new(problem.fleet.as_ref(), jobs, problem.transport.as_ref(), &logger).unwrap()),
locks: problem.locks.clone(),
goal: problem.goal.clone(),
activity: problem.activity.clone(),
Expand Down
2 changes: 1 addition & 1 deletion vrp-core/tests/helpers/construction/heuristics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ impl TestInsertionContextBuilder {
fn create_empty_problem() -> Problem {
let transport = TestTransportCost::new_shared();
let fleet = Arc::new(test_fleet());
let jobs = Arc::new(Jobs::new(fleet.as_ref(), vec![], transport.as_ref(), &test_logger()));
let jobs = Arc::new(Jobs::new(fleet.as_ref(), vec![], transport.as_ref(), &test_logger()).unwrap());
Problem {
fleet,
jobs,
Expand Down
4 changes: 2 additions & 2 deletions vrp-core/tests/helpers/models/domain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ impl ProblemBuilder {
}

pub fn with_jobs(&mut self, jobs: Vec<Job>) -> &mut Self {
self.0.jobs = Arc::new(Jobs::new(&self.0.fleet, jobs, self.0.transport.as_ref(), &test_logger()));
self.0.jobs = Arc::new(Jobs::new(&self.0.fleet, jobs, self.0.transport.as_ref(), &test_logger()).unwrap());
self
}

Expand Down Expand Up @@ -141,7 +141,7 @@ pub fn get_customer_id(job: &Job) -> String {
fn create_empty_problem() -> Problem {
let transport = TestTransportCost::new_shared();
let fleet = test_fleet();
let jobs = Jobs::new(&fleet, vec![], transport.as_ref(), &test_logger());
let jobs = Jobs::new(&fleet, vec![], transport.as_ref(), &test_logger()).unwrap();

Problem {
fleet: Arc::new(fleet),
Expand Down
2 changes: 1 addition & 1 deletion vrp-core/tests/helpers/solver/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ pub fn generate_matrix_routes(
let matrix_data = MatrixData::new(0, None, durations, distances);
let transport = create_matrix_transport_cost(vec![matrix_data]).unwrap();
let activity = Arc::new(TestActivityCost::default());
let jobs = Jobs::new(&fleet, jobs, transport.as_ref(), &test_logger());
let jobs = Jobs::new(&fleet, jobs, transport.as_ref(), &test_logger()).unwrap();

let problem = Problem {
fleet,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ fn create_test_problem(

Problem {
fleet: fleet.clone(),
jobs: Arc::new(Jobs::new(&fleet, jobs, transport.as_ref(), &test_logger())),
jobs: Arc::new(Jobs::new(&fleet, jobs, transport.as_ref(), &test_logger()).unwrap()),
locks,
goal: Arc::new(goal),
activity,
Expand Down
15 changes: 10 additions & 5 deletions vrp-core/tests/unit/models/problem/jobs_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,10 @@ fn all_returns_all_jobs() {
let jobs = vec![TestSingleBuilder::default().build_as_job_ref(), TestSingleBuilder::default().build_as_job_ref()];

assert_eq!(
Jobs::new(&test_fleet(), jobs, create_only_distance_transport_cost().as_ref(), &test_logger()).all().len(),
Jobs::new(&test_fleet(), jobs, create_only_distance_transport_cost().as_ref(), &test_logger())
.unwrap()
.all()
.len(),
2
)
}
Expand Down Expand Up @@ -166,7 +169,8 @@ fn returns_proper_job_neighbours_impl(index: usize, expected: Vec<String>) {
TestSingleBuilder::default().id("s3").location(Some(3)).build_as_job_ref(),
TestSingleBuilder::default().id("s4").location(Some(4)).build_as_job_ref(),
];
let jobs = Jobs::new(&fleet, species.clone(), create_profile_aware_transport_cost().as_ref(), &test_logger());
let jobs =
Jobs::new(&fleet, species.clone(), create_profile_aware_transport_cost().as_ref(), &test_logger()).unwrap();

let result: Vec<String> =
jobs.neighbors(&p1, species.get(index).unwrap(), 0.0).map(|(j, _)| get_job_id(j).clone()).collect();
Expand Down Expand Up @@ -215,7 +219,8 @@ fn returns_proper_job_ranks_impl(index: usize, profile_index: usize, expected: D
TestSingleBuilder::default().id("s2").location(Some(21)).build_as_job_ref(),
TestSingleBuilder::default().id("s3").location(Some(31)).build_as_job_ref(),
];
let jobs = Jobs::new(&fleet, species.clone(), create_profile_aware_transport_cost().as_ref(), &test_logger());
let jobs =
Jobs::new(&fleet, species.clone(), create_profile_aware_transport_cost().as_ref(), &test_logger()).unwrap();

let result = jobs.rank(&profile, species.get(index).unwrap());

Expand All @@ -227,7 +232,7 @@ fn can_use_multi_job_bind_and_roots() {
let job = test_multi_job_with_locations(vec![vec![Some(0)], vec![Some(1)]]);
let jobs = vec![Job::Multi(job.clone())];

let jobs = Jobs::new(&test_fleet(), jobs, create_only_distance_transport_cost().as_ref(), &test_logger());
let jobs = Jobs::new(&test_fleet(), jobs, create_only_distance_transport_cost().as_ref(), &test_logger()).unwrap();
let job = Job::Multi(Multi::roots(job.jobs.first().unwrap()).unwrap());

assert_eq!(jobs.neighbors(&Profile::default(), &job, 0.0).count(), 0);
Expand All @@ -251,7 +256,7 @@ fn can_handle_negative_distances_durations_impl(transport_costs: Arc<dyn Transpo
TestSingleBuilder::default().id("s1").location(Some(1)).build_as_job_ref(),
];

let jobs = Jobs::new(&test_fleet(), species.clone(), transport_costs.as_ref(), &test_logger());
let jobs = Jobs::new(&test_fleet(), species.clone(), transport_costs.as_ref(), &test_logger()).unwrap();

for job in &species {
assert!(jobs
Expand Down
2 changes: 1 addition & 1 deletion vrp-pragmatic/src/format/problem/job_reader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ pub(super) fn read_jobs_with_extra_locks(
jobs.extend(conditional_jobs);
locks.extend(conditional_locks);

(Jobs::new(fleet, jobs, transport, logger), locks)
(Jobs::new(fleet, jobs, transport, logger).unwrap(), locks)
}

pub(super) fn read_locks(api_problem: &ApiProblem, job_index: &JobIndex) -> Vec<Arc<Lock>> {
Expand Down
6 changes: 3 additions & 3 deletions vrp-scientific/src/common/text_reader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@ use vrp_core::prelude::{GenericError, InfoLogger};
use vrp_core::utils::GenericResult;

pub(crate) trait TextReader {
fn read_problem(&mut self, is_rounded: bool) -> Result<Problem, GenericError> {
fn read_problem(&mut self, is_rounded: bool) -> GenericResult<Problem> {
let (jobs, fleet) = self.read_definitions()?;
let transport = self.create_transport(is_rounded)?;
let activity = Arc::new(SimpleActivityCost::default());
let jobs = Jobs::new(&fleet, jobs, transport.as_ref(), &self.get_logger());
let jobs = Jobs::new(&fleet, jobs, transport.as_ref(), &self.get_logger())?;
let extras = self.create_extras();
let goal = self.create_goal_context(activity.clone(), transport.clone()).expect("cannot create goal context");
let goal = self.create_goal_context(activity.clone(), transport.clone())?;

Ok(Problem {
fleet: Arc::new(fleet),
Expand Down

0 comments on commit 036df17

Please sign in to comment.