vrp_core/models/problem/
builders.rs1use crate::construction::features::{JobDemandDimension, VehicleCapacityDimension};
4use crate::models::common::{Cost, Demand, Dimensions, Duration, LoadOps, Location, TimeSpan, TimeWindow, Timestamp};
5use crate::models::problem::{
6 Costs, Job, JobIdDimension, JobPermutation, Multi, Place, Single, Vehicle, VehicleDetail, VehicleIdDimension,
7 VehiclePlace,
8};
9use rosomaxa::prelude::{GenericError, GenericResult};
10use std::sync::Arc;
11
12#[derive(Debug)]
14pub struct SingleBuilder(Single);
15
16impl Default for SingleBuilder {
17 fn default() -> Self {
18 Self(Single { places: vec![], dimens: Default::default() })
19 }
20}
21
22impl SingleBuilder {
23 pub fn add_place(mut self, place: Place) -> Self {
26 self.0.places.push(place);
27 self
28 }
29
30 pub fn add_places(mut self, places: impl Iterator<Item = Place>) -> Self {
32 self.0.places.extend(places);
33 self
34 }
35
36 pub fn id(mut self, id: &str) -> Self {
38 self.0.dimens.set_job_id(id.to_string());
39 self
40 }
41
42 pub fn demand<T: LoadOps>(mut self, demand: Demand<T>) -> Self {
44 self.0.dimens.set_job_demand(demand);
45 self
46 }
47
48 pub fn dimension(mut self, func: impl FnOnce(&mut Dimensions)) -> Self {
50 func(&mut self.0.dimens);
51 self
52 }
53
54 pub fn location(mut self, location: Location) -> GenericResult<Self> {
58 self.ensure_single_place()?.location = Some(location);
59 Ok(self)
60 }
61
62 pub fn duration(mut self, duration: Duration) -> GenericResult<Self> {
65 self.ensure_single_place()?.duration = duration;
66 Ok(self)
67 }
68
69 pub fn times(mut self, times: Vec<TimeWindow>) -> GenericResult<Self> {
72 self.ensure_single_place()?.times = times.into_iter().map(TimeSpan::Window).collect();
73 Ok(self)
74 }
75
76 pub fn build(self) -> GenericResult<Single> {
78 Ok(self.0)
79 }
80
81 pub fn build_as_job(self) -> GenericResult<Job> {
83 Ok(Job::Single(Arc::new(self.0)))
84 }
85
86 fn ensure_single_place(&mut self) -> GenericResult<&mut Place> {
87 if self.0.places.len() > 1 {
88 return Err("cannot use the simple api with multiple places, use `SingleBuilder::add_place` and `JobPlaceBuilder` instead".into());
89 }
90
91 if self.0.places.is_empty() {
92 self.0.places.push(empty_place());
93 }
94
95 self.0.places.first_mut().ok_or_else(|| GenericError::from("no places"))
96 }
97}
98
99pub struct JobPlaceBuilder(Place);
101
102impl Default for JobPlaceBuilder {
103 fn default() -> Self {
104 Self(empty_place())
105 }
106}
107
108impl JobPlaceBuilder {
109 pub fn location(mut self, loc: Option<Location>) -> Self {
111 self.0.location = loc;
112 self
113 }
114
115 pub fn duration(mut self, duration: Duration) -> Self {
117 self.0.duration = duration;
118 self
119 }
120
121 pub fn times(mut self, times: Vec<TimeWindow>) -> Self {
123 self.0.times = times.into_iter().map(TimeSpan::Window).collect();
124 self
125 }
126
127 pub fn build(self) -> GenericResult<Place> {
129 Ok(self.0)
130 }
131}
132
133#[derive(Default)]
135pub struct MultiBuilder {
136 jobs: Vec<Arc<Single>>,
137 dimens: Dimensions,
138 permutator: Option<Box<dyn JobPermutation>>,
139}
140
141impl MultiBuilder {
142 pub fn id(mut self, id: &str) -> Self {
144 self.dimens.set_job_id(id.to_string());
145 self
146 }
147
148 pub fn add_job(mut self, single: Single) -> Self {
150 self.jobs.push(Arc::new(single));
151 self
152 }
153
154 pub fn dimension(mut self, func: impl FnOnce(&mut Dimensions)) -> Self {
156 func(&mut self.dimens);
157 self
158 }
159
160 pub fn permutation(mut self, permutation: impl JobPermutation + 'static) -> Self {
163 self.permutator = Some(Box::new(permutation));
164 self
165 }
166
167 pub fn build(self) -> GenericResult<Arc<Multi>> {
169 if self.jobs.len() < 2 {
170 return Err("the number of sub-jobs must be 2 or more".into());
171 }
172
173 Ok(if let Some(permutator) = self.permutator {
174 Multi::new_shared_with_permutator(self.jobs, self.dimens, permutator)
175 } else {
176 Multi::new_shared(self.jobs, self.dimens)
177 })
178 }
179
180 pub fn build_as_job(self) -> GenericResult<Job> {
182 Ok(Job::Multi(self.build()?))
183 }
184}
185
186fn empty_place() -> Place {
187 Place { location: None, duration: 0.0, times: vec![TimeSpan::Window(TimeWindow::max())] }
189}
190
191pub struct VehicleBuilder(Vehicle);
193
194impl Default for VehicleBuilder {
195 fn default() -> Self {
196 Self(Vehicle {
197 profile: Default::default(),
198 costs: Costs {
199 fixed: 0.0,
200 per_distance: 1.,
201 per_driving_time: 0.0,
202 per_waiting_time: 0.0,
203 per_service_time: 0.0,
204 },
205 dimens: Default::default(),
206 details: vec![],
207 })
208 }
209}
210
211impl VehicleBuilder {
212 pub fn id(mut self, id: &str) -> Self {
214 self.0.dimens.set_vehicle_id(id.to_string());
215 self
216 }
217
218 pub fn add_detail(mut self, detail: VehicleDetail) -> Self {
221 self.0.details.push(detail);
222 self
223 }
224
225 pub fn set_profile_idx(mut self, idx: usize) -> Self {
227 self.0.profile.index = idx;
228 self
229 }
230
231 pub fn set_distance_cost(mut self, cost: Cost) -> Self {
233 self.0.costs.per_distance = cost;
234 self
235 }
236
237 pub fn set_duration_cost(mut self, cost: Cost) -> Self {
239 self.0.costs.per_driving_time = cost;
240 self.0.costs.per_service_time = cost;
241 self.0.costs.per_waiting_time = cost;
242 self
243 }
244
245 pub fn capacity<T: LoadOps>(mut self, value: T) -> Self {
247 self.0.dimens.set_vehicle_capacity(value);
248 self
249 }
250
251 pub fn dimension(mut self, func: impl FnOnce(&mut Dimensions)) -> Self {
253 func(&mut self.0.dimens);
254 self
255 }
256
257 pub fn build(self) -> GenericResult<Vehicle> {
259 if self.0.details.is_empty() {
260 Err("at least one vehicle detail needs to be added, use `VehicleDetailBuilder` and `add_detail` function"
261 .into())
262 } else {
263 Ok(self.0)
264 }
265 }
266}
267
268pub struct VehicleDetailBuilder(VehicleDetail);
270
271impl Default for VehicleDetailBuilder {
272 fn default() -> Self {
273 Self(VehicleDetail { start: None, end: None })
274 }
275}
276
277impl VehicleDetailBuilder {
278 pub fn set_start_location(mut self, location: Location) -> Self {
280 self.ensure_start().location = location;
281 self
282 }
283
284 pub fn set_start_time(mut self, earliest: Timestamp) -> Self {
286 self.ensure_start().time.earliest = Some(earliest);
287 self.ensure_start().time.latest = Some(earliest);
289 self
290 }
291
292 pub fn set_start_time_latest(mut self, latest: Timestamp) -> Self {
294 self.ensure_start().time.latest = Some(latest);
295 self
296 }
297
298 pub fn set_end_location(mut self, location: Location) -> Self {
300 self.ensure_end().location = location;
301 self
302 }
303
304 pub fn set_end_time(mut self, latest: Timestamp) -> Self {
306 self.ensure_end().time.latest = Some(latest);
307 self
308 }
309
310 fn ensure_start(&mut self) -> &mut VehiclePlace {
311 if self.0.start.is_none() {
312 self.0.start = Some(VehiclePlace { location: 0, time: Default::default() });
313 }
314 self.0.start.as_mut().unwrap()
315 }
316
317 fn ensure_end(&mut self) -> &mut VehiclePlace {
318 if self.0.end.is_none() {
319 self.0.end = Some(VehiclePlace { location: 0, time: Default::default() });
320 }
321 self.0.end.as_mut().unwrap()
322 }
323
324 pub fn build(self) -> GenericResult<VehicleDetail> {
326 if self.0.start.is_none() {
327 Err("start place must be defined for vehicle detail".into())
328 } else {
329 Ok(self.0)
330 }
331 }
332}