Skip to content

Commit 33e496b

Browse files
Fix issue in lilim reader
1 parent 6b50a83 commit 33e496b

5 files changed

Lines changed: 100 additions & 5 deletions

File tree

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,6 @@ __pycache__
2424
heuristic_state.json
2525

2626
# profiler
27-
callgrind*
27+
callgrind*
28+
29+
.claude

vrp-scientific/src/lilim/reader.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use vrp_core::models::{Extras, Problem};
1414
use vrp_core::prelude::GenericError;
1515
use vrp_core::utils::Float;
1616

17-
/// A trait to read lilim problem.
17+
/// A trait to read lilim problem. Original format is described here: https://www.sintef.no/projectweb/top/pdptw/documentation
1818
pub trait LilimProblem {
1919
/// Reads lilim problem.
2020
fn read_lilim(self, is_rounded: bool) -> Result<Problem, GenericError>;
@@ -146,7 +146,8 @@ impl<R: Read> LilimReader<R> {
146146
} else {
147147
Demand::<SingleDimLoad> {
148148
pickup: (SingleDimLoad::default(), SingleDimLoad::default()),
149-
delivery: (SingleDimLoad::default(), SingleDimLoad::new(customer.demand)),
149+
// we have a negative demand for deliveries in original problem formulation
150+
delivery: (SingleDimLoad::default(), SingleDimLoad::new(-customer.demand)),
150151
}
151152
});
152153

@@ -156,7 +157,7 @@ impl<R: Read> LilimReader<R> {
156157
duration: customer.service as Float,
157158
times: vec![TimeSpan::Window(customer.tw.clone())],
158159
}],
159-
dimens: Default::default(),
160+
dimens,
160161
})
161162
}
162163

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/// id x y demand early late service pickup delivery
2+
pub type LilimCustomer = (i32, i32, i32, i32, i32, i32, i32, i32, i32);
3+
4+
pub struct LilimBuilder {
5+
vehicle: (usize, usize),
6+
customers: Vec<LilimCustomer>,
7+
}
8+
9+
impl Default for LilimBuilder {
10+
fn default() -> Self {
11+
Self { vehicle: (0, 0), customers: vec![] }
12+
}
13+
}
14+
15+
impl LilimBuilder {
16+
pub fn new() -> Self {
17+
Self::default()
18+
}
19+
20+
pub fn set_vehicle(&mut self, vehicle: (usize, usize)) -> &mut Self {
21+
self.vehicle = vehicle;
22+
self
23+
}
24+
25+
pub fn add_customer(&mut self, customer: LilimCustomer) -> &mut Self {
26+
self.customers.push(customer);
27+
self
28+
}
29+
30+
pub fn build(&self) -> String {
31+
let mut data = String::new();
32+
33+
data.push_str(&format!("{} {} 0\n", self.vehicle.0, self.vehicle.1));
34+
35+
for c in &self.customers {
36+
data.push_str(&format!("{} {} {} {} {} {} {} {} {}\n", c.0, c.1, c.2, c.3, c.4, c.5, c.6, c.7, c.8));
37+
}
38+
39+
data
40+
}
41+
}

vrp-scientific/tests/helpers/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ pub mod macros;
66
mod analysis;
77
pub use self::analysis::*;
88

9+
mod lilim;
10+
pub use self::lilim::LilimBuilder;
11+
912
mod solomon;
1013
pub use self::solomon::SolomonBuilder;
1114

vrp-scientific/tests/unit/lilim/reader_test.rs

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
use crate::helpers::{create_lc101_problem, get_job_ids, get_vehicle_capacity};
1+
use crate::helpers::{LilimBuilder, create_lc101_problem, get_job_ids, get_vehicle_capacity};
2+
use crate::lilim::LilimProblem;
3+
use vrp_core::construction::features::JobDemandDimension;
4+
use vrp_core::models::common::{Demand, SingleDimLoad};
5+
use vrp_core::models::problem::Job;
26

37
#[test]
48
fn can_read_lilim_format_from_test_file() {
@@ -9,3 +13,47 @@ fn can_read_lilim_format_from_test_file() {
913
assert_eq!(problem.fleet.vehicles.len(), 25);
1014
assert_eq!(get_vehicle_capacity(&problem), 200);
1115
}
16+
17+
#[test]
18+
fn can_read_lilim_format_properly() {
19+
let problem = LilimBuilder::new()
20+
.set_vehicle((3, 15))
21+
.add_customer((0, 0, 0, 0, 0, 1000, 0, 0, 0)) // depot
22+
.add_customer((1, 1, 0, 15, 0, 1000, 5, 0, 2)) // pickup, delivers to 2
23+
.add_customer((2, 2, 0, -15, 0, 1000, 5, 1, 0)) // delivery, picked from 1
24+
.add_customer((3, 3, 0, 10, 0, 1000, 3, 0, 4)) // pickup, delivers to 4
25+
.add_customer((4, 4, 0, -10, 0, 1000, 3, 3, 0)) // delivery, picked from 3
26+
.build()
27+
.read_lilim(false)
28+
.unwrap();
29+
30+
assert_eq!(problem.fleet.vehicles.len(), 3);
31+
assert_eq!(get_vehicle_capacity(&problem), 15);
32+
assert_eq!(get_job_ids(&problem), vec!["0", "1"]);
33+
34+
// verify demands on sub-jobs of each multi job
35+
let jobs: Vec<_> = problem.jobs.all().into_iter().collect();
36+
37+
let (pickup_demand_0, delivery_demand_0) = get_multi_job_demands(&jobs[0]);
38+
assert_eq!(pickup_demand_0.pickup.1.value, 15);
39+
assert_eq!(pickup_demand_0.delivery.1.value, 0);
40+
assert_eq!(delivery_demand_0.delivery.1.value, 15);
41+
assert_eq!(delivery_demand_0.pickup.1.value, 0);
42+
43+
let (pickup_demand_1, delivery_demand_1) = get_multi_job_demands(&jobs[1]);
44+
assert_eq!(pickup_demand_1.pickup.1.value, 10);
45+
assert_eq!(pickup_demand_1.delivery.1.value, 0);
46+
assert_eq!(delivery_demand_1.delivery.1.value, 10);
47+
assert_eq!(delivery_demand_1.pickup.1.value, 0);
48+
}
49+
50+
fn get_multi_job_demands(job: &Job) -> (&Demand<SingleDimLoad>, &Demand<SingleDimLoad>) {
51+
match job {
52+
Job::Multi(multi) => {
53+
let pickup = multi.jobs[0].dimens.get_job_demand().unwrap();
54+
let delivery = multi.jobs[1].dimens.get_job_demand().unwrap();
55+
(pickup, delivery)
56+
}
57+
_ => panic!("expected multi job"),
58+
}
59+
}

0 commit comments

Comments
 (0)