Skip to main content

vrp_scientific/lilim/
reader.rs

1#[cfg(test)]
2#[path = "../../tests/unit/lilim/reader_test.rs"]
3mod reader_test;
4
5use crate::common::*;
6use std::collections::HashMap;
7use std::io::{BufReader, Read};
8use std::sync::Arc;
9use vrp_core::construction::features::JobDemandDimension;
10use vrp_core::models::common::*;
11use vrp_core::models::problem::*;
12use vrp_core::models::*;
13use vrp_core::models::{Extras, Problem};
14use vrp_core::prelude::GenericError;
15use vrp_core::utils::Float;
16
17/// A trait to read lilim problem.
18pub trait LilimProblem {
19    /// Reads lilim problem.
20    fn read_lilim(self, is_rounded: bool) -> Result<Problem, GenericError>;
21}
22
23impl<R: Read> LilimProblem for BufReader<R> {
24    fn read_lilim(self, is_rounded: bool) -> Result<Problem, GenericError> {
25        LilimReader { buffer: String::new(), reader: self, coord_index: CoordIndex::default() }.read_problem(is_rounded)
26    }
27}
28
29impl LilimProblem for String {
30    fn read_lilim(self, is_rounded: bool) -> Result<Problem, GenericError> {
31        BufReader::new(self.as_bytes()).read_lilim(is_rounded)
32    }
33}
34
35struct VehicleLine {
36    number: usize,
37    capacity: usize,
38    _ignored: usize,
39}
40
41struct JobLine {
42    id: usize,
43    location: (i32, i32),
44    demand: i32,
45    tw: TimeWindow,
46    service: usize,
47    relation: usize,
48}
49
50struct Relation {
51    pickup: usize,
52    delivery: usize,
53}
54
55struct LilimReader<R: Read> {
56    buffer: String,
57    reader: BufReader<R>,
58    coord_index: CoordIndex,
59}
60
61impl<R: Read> TextReader for LilimReader<R> {
62    fn create_goal_context(
63        &self,
64        activity: Arc<SimpleActivityCost>,
65        transport: Arc<dyn TransportCost>,
66    ) -> Result<GoalContext, GenericError> {
67        let is_time_constrained = true;
68        create_goal_context_prefer_min_tours(activity, transport, is_time_constrained)
69    }
70
71    fn read_definitions(&mut self) -> Result<(Vec<Job>, Fleet), GenericError> {
72        let fleet = self.read_fleet()?;
73        let jobs = self.read_jobs()?;
74
75        Ok((jobs, fleet))
76    }
77
78    fn create_transport(&self, is_rounded: bool) -> Result<Arc<dyn TransportCost>, GenericError> {
79        self.coord_index.create_transport(is_rounded, &self.get_logger())
80    }
81
82    fn create_extras(&self) -> Extras {
83        get_extras(self.coord_index.clone())
84    }
85}
86
87impl<R: Read> LilimReader<R> {
88    fn read_fleet(&mut self) -> Result<Fleet, GenericError> {
89        let vehicle = self.read_vehicle()?;
90        let depot = self.read_customer()?;
91
92        Ok(create_fleet_with_distance_costs(
93            vehicle.number,
94            vehicle.capacity,
95            self.coord_index.collect(depot.location),
96            depot.tw,
97        ))
98    }
99
100    fn read_jobs(&mut self) -> Result<Vec<Job>, GenericError> {
101        let mut customers: HashMap<usize, JobLine> = Default::default();
102        let mut relations: Vec<Relation> = Default::default();
103        loop {
104            match self.read_customer() {
105                Ok(customer) => {
106                    if customer.demand > 0 {
107                        relations.push(Relation { pickup: customer.id, delivery: customer.relation });
108                    }
109                    customers.insert(customer.id, customer);
110                }
111                Err(error) => {
112                    if self.buffer.is_empty() {
113                        break;
114                    } else {
115                        return Err(error);
116                    }
117                }
118            }
119        }
120
121        let mut jobs: Vec<Job> = Default::default();
122        relations.iter().zip(0..).for_each(|(relation, index)| {
123            let pickup = customers.get(&relation.pickup).unwrap();
124            let delivery = customers.get(&relation.delivery).unwrap();
125
126            jobs.push(Job::Multi(Multi::new_shared(
127                vec![self.create_single_job(pickup), self.create_single_job(delivery)],
128                create_dimens_with_id("", &index.to_string(), |id, dimens| {
129                    dimens.set_job_id(id.to_string());
130                }),
131            )));
132        });
133
134        Ok(jobs)
135    }
136
137    fn create_single_job(&mut self, customer: &JobLine) -> Arc<Single> {
138        let mut dimens = create_dimens_with_id("c", &customer.id.to_string(), |id, dimens| {
139            dimens.set_job_id(id.to_string());
140        });
141        dimens.set_job_demand(if customer.demand > 0 {
142            Demand::<SingleDimLoad> {
143                pickup: (SingleDimLoad::default(), SingleDimLoad::new(customer.demand)),
144                delivery: (SingleDimLoad::default(), SingleDimLoad::default()),
145            }
146        } else {
147            Demand::<SingleDimLoad> {
148                pickup: (SingleDimLoad::default(), SingleDimLoad::default()),
149                delivery: (SingleDimLoad::default(), SingleDimLoad::new(customer.demand)),
150            }
151        });
152
153        Arc::new(Single {
154            places: vec![Place {
155                location: Some(self.coord_index.collect(customer.location)),
156                duration: customer.service as Float,
157                times: vec![TimeSpan::Window(customer.tw.clone())],
158            }],
159            dimens: Default::default(),
160        })
161    }
162
163    fn read_vehicle(&mut self) -> Result<VehicleLine, GenericError> {
164        read_line(&mut self.reader, &mut self.buffer)?;
165        let (number, capacity, _ignored) = self
166            .buffer
167            .split_whitespace()
168            .map(|line| line.parse::<usize>().unwrap())
169            .try_collect_tuple()
170            .ok_or_else(|| "cannot parse vehicle number or/and capacity".to_string())?;
171
172        Ok(VehicleLine { number, capacity, _ignored })
173    }
174
175    fn read_customer(&mut self) -> Result<JobLine, GenericError> {
176        read_line(&mut self.reader, &mut self.buffer)?;
177        let (id, x, y, demand, start, end, service, _, relation) = self
178            .buffer
179            .split_whitespace()
180            .map(|line| line.parse::<i32>().unwrap())
181            .try_collect_tuple()
182            .ok_or_else(|| "cannot read customer line".to_string())?;
183        Ok(JobLine {
184            id: id as usize,
185            location: (x, y),
186            demand,
187            tw: TimeWindow::new(start as Float, end as Float),
188            service: service as usize,
189            relation: relation as usize,
190        })
191    }
192}