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