Skip to main content

vrp_pragmatic/format/solution/
model.rs

1use super::FeatureCollection;
2use crate::format::{CoordIndex, Location};
3use crate::{format_time, parse_time};
4use serde::{Deserialize, Serialize};
5use std::io::{BufReader, BufWriter, Error, Read, Write};
6use vrp_core::models::common::{Duration, Timestamp};
7use vrp_core::models::solution::Commute as DomainCommute;
8use vrp_core::models::solution::CommuteInfo as DomainCommuteInfo;
9use vrp_core::prelude::Float;
10
11/// Timing statistic.
12#[derive(Clone, Default, Deserialize, Serialize, PartialEq, Eq, Debug)]
13pub struct Timing {
14    /// Driving time.
15    pub driving: i64,
16    /// Serving time.
17    pub serving: i64,
18    /// Waiting time.
19    pub waiting: i64,
20    /// Break time.
21    #[serde(rename(serialize = "break", deserialize = "break"))]
22    pub break_time: i64,
23    /// Commuting time.
24    #[serde(default = "i64::default")]
25    pub commuting: i64,
26    /// Parking time.
27    #[serde(default = "i64::default")]
28    pub parking: i64,
29}
30
31/// Represents statistic.
32#[derive(Clone, Deserialize, Default, Serialize, PartialEq, Debug)]
33pub struct Statistic {
34    /// Total cost.
35    pub cost: Float,
36    /// Total distance.
37    pub distance: i64,
38    /// Total duration.
39    pub duration: i64,
40    /// Timing statistic.
41    pub times: Timing,
42}
43
44/// Represents a schedule.
45#[derive(Clone, Deserialize, Serialize, Eq, PartialEq, Debug)]
46pub struct Schedule {
47    /// Arrival time specified in RFC3339 format.
48    pub arrival: String,
49    /// Departure time specified in RFC3339 format.
50    pub departure: String,
51}
52
53/// Represents time interval.
54#[derive(Clone, Deserialize, Serialize, Eq, PartialEq, Debug)]
55pub struct Interval {
56    /// Start time specified in RFC3339 format.
57    pub start: String,
58    /// End time specified in RFC3339 format.
59    pub end: String,
60}
61
62/// Stores information about commuting to perform activity.
63#[derive(Clone, Deserialize, Serialize, PartialEq, Debug)]
64pub struct Commute {
65    /// Commuting to the activity place.
66    #[serde(skip_serializing_if = "Option::is_none")]
67    pub forward: Option<CommuteInfo>,
68    /// Commuting from the activity place.
69    #[serde(skip_serializing_if = "Option::is_none")]
70    pub backward: Option<CommuteInfo>,
71}
72
73/// Stores information about commuting information in one direction.
74#[derive(Clone, Deserialize, Serialize, PartialEq, Debug)]
75pub struct CommuteInfo {
76    /// Commute location.
77    pub location: Location,
78    /// Travelled distance.
79    pub distance: Float,
80    /// Travel time.
81    pub time: Interval,
82}
83
84/// An activity is unit of work performed at some place.
85#[derive(Clone, Deserialize, Serialize, PartialEq, Debug)]
86#[serde(rename_all = "camelCase")]
87pub struct Activity {
88    /// Job id.
89    pub job_id: String,
90    /// Activity type.
91    #[serde(rename(serialize = "type", deserialize = "type"))]
92    pub activity_type: String,
93    /// Location.
94    #[serde(skip_serializing_if = "Option::is_none")]
95    pub location: Option<Location>,
96    /// Activity time.
97    #[serde(skip_serializing_if = "Option::is_none")]
98    pub time: Option<Interval>,
99    /// Job tag.
100    #[serde(skip_serializing_if = "Option::is_none")]
101    pub job_tag: Option<String>,
102    /// Commute information.
103    #[serde(skip_serializing_if = "Option::is_none")]
104    pub commute: Option<Commute>,
105}
106
107/// A stop is a place where vehicle is supposed to do some work.
108#[derive(Clone, Deserialize, Serialize, PartialEq, Debug)]
109#[serde(untagged)]
110pub enum Stop {
111    /// A point stop is a stop where vehicle is supposed to be parked and do some work.
112    Point(PointStop),
113    /// A transit stop specifies some transit place to stay without concrete location.
114    Transit(TransitStop),
115}
116
117impl Stop {
118    /// Returns stop's schedule time.
119    pub fn schedule(&self) -> &Schedule {
120        match self {
121            Self::Transit(transit) => &transit.time,
122            Self::Point(point) => &point.time,
123        }
124    }
125
126    /// Returns stop's schedule time as mutable.
127    pub fn schedule_mut(&mut self) -> &mut Schedule {
128        match self {
129            Self::Transit(transit) => &mut transit.time,
130            Self::Point(point) => &mut point.time,
131        }
132    }
133
134    /// Returns stop's load.
135    pub fn load(&self) -> &Vec<i32> {
136        match self {
137            Self::Transit(transit) => &transit.load,
138            Self::Point(point) => &point.load,
139        }
140    }
141
142    /// Returns stop's load as mutable.
143    pub fn load_mut(&mut self) -> &mut Vec<i32> {
144        match self {
145            Self::Transit(transit) => &mut transit.load,
146            Self::Point(point) => &mut point.load,
147        }
148    }
149
150    /// Returns stop activities.
151    pub fn activities(&self) -> &Vec<Activity> {
152        match self {
153            Stop::Transit(transit) => &transit.activities,
154            Stop::Point(point) => &point.activities,
155        }
156    }
157
158    /// Returns stop activities as mutable vector.
159    pub fn activities_mut(&mut self) -> &mut Vec<Activity> {
160        match self {
161            Stop::Transit(transit) => &mut transit.activities,
162            Stop::Point(point) => &mut point.activities,
163        }
164    }
165
166    /// Returns stop's location if there is any.
167    pub fn location(&self) -> Option<&Location> {
168        match self {
169            Self::Transit(_) => None,
170            Self::Point(point) => Some(&point.location),
171        }
172    }
173
174    /// A helper method used to get stop point variant safely.
175    pub fn as_point(&self) -> Option<&PointStop> {
176        match self {
177            Self::Point(point) => Some(point),
178            _ => None,
179        }
180    }
181
182    /// A helper method used to unwrap stop point variant.
183    pub fn to_point(self) -> PointStop {
184        match self {
185            Self::Point(point) => point,
186            _ => panic!("non point type"),
187        }
188    }
189}
190
191/// A transit stop specifies some transit place to stay without concrete location.
192#[derive(Clone, Deserialize, Serialize, PartialEq, Debug)]
193pub struct TransitStop {
194    /// Stop schedule.
195    pub time: Schedule,
196    /// Vehicle load after departure from this stop.
197    pub load: Vec<i32>,
198    /// Activities performed at the stop.
199    pub activities: Vec<Activity>,
200}
201
202/// A point stop is a stop where vehicle is supposed to be parked and do some work.
203#[derive(Clone, Deserialize, Serialize, PartialEq, Debug)]
204pub struct PointStop {
205    /// Stop location. When omitted vehicle can stop anywhere.
206    pub location: Location,
207    /// Stop schedule.
208    pub time: Schedule,
209    /// Distance traveled since departure from start.
210    pub distance: i64,
211    /// Vehicle load after departure from this stop.
212    pub load: Vec<i32>,
213    /// Parking time.
214    #[serde(skip_serializing_if = "Option::is_none")]
215    pub parking: Option<Interval>,
216    /// Activities performed at the stop.
217    pub activities: Vec<Activity>,
218}
219
220/// A tour is list of stops with their activities performed by specific vehicle.
221#[derive(Clone, Deserialize, Serialize, PartialEq, Debug)]
222#[serde(rename_all = "camelCase")]
223pub struct Tour {
224    /// Vehicle id.
225    pub vehicle_id: String,
226    /// Vehicle type id.
227    pub type_id: String,
228    /// Shift index.
229    #[serde(default)]
230    pub shift_index: usize,
231    /// List of stops.
232    pub stops: Vec<Stop>,
233    /// Tour statistic.
234    pub statistic: Statistic,
235}
236
237/// Unassigned job reason.
238#[derive(Clone, Deserialize, Serialize, Eq, PartialEq, Debug)]
239pub struct UnassignedJobReason {
240    /// A reason code.
241    pub code: String,
242    /// Description.
243    pub description: String,
244    /// Optionally, more details.
245    #[serde(skip_serializing_if = "Option::is_none")]
246    pub details: Option<Vec<UnassignedJobDetail>>,
247}
248
249/// Unassigned job details.
250#[derive(Clone, Deserialize, Serialize, Eq, PartialEq, Debug)]
251#[serde(rename_all = "camelCase")]
252pub struct UnassignedJobDetail {
253    /// Vehicle id.
254    pub vehicle_id: String,
255    /// Vehicle shift index.
256    pub shift_index: usize,
257}
258
259/// Unassigned job.
260#[derive(Clone, Deserialize, Serialize, Eq, PartialEq, Debug)]
261#[serde(rename_all = "camelCase")]
262pub struct UnassignedJob {
263    /// Job id.
264    pub job_id: String,
265    /// Possible reasons.
266    pub reasons: Vec<UnassignedJobReason>,
267}
268
269/// Specifies a type of violation.
270#[derive(Clone, Deserialize, Serialize, Eq, PartialEq, Debug)]
271#[serde(rename_all = "camelCase")]
272#[serde(tag = "type")]
273pub enum Violation {
274    /// A break assignment violation.
275    #[serde(rename(deserialize = "break", serialize = "break"))]
276    Break {
277        /// An id of a vehicle break belong to.
278        vehicle_id: String,
279        /// Index of the shift.
280        shift_index: usize,
281    },
282}
283
284/// Encapsulates different measurements regarding algorithm evaluation.
285#[derive(Clone, Deserialize, Serialize, PartialEq, Debug)]
286pub struct Metrics {
287    /// Total algorithm duration.
288    pub duration: usize,
289    /// Total amount of generations.
290    pub generations: usize,
291    /// Speed: generations per second.
292    pub speed: Float,
293    /// Evolution progress.
294    pub evolution: Vec<Generation>,
295}
296
297/// Represents information about generation.
298#[derive(Clone, Deserialize, Serialize, PartialEq, Debug)]
299#[serde(rename_all = "camelCase")]
300pub struct Generation {
301    /// Generation sequence number.
302    pub number: usize,
303    /// Time since evolution started.
304    pub timestamp: Float,
305    /// Overall improvement ratio.
306    pub i_all_ratio: Float,
307    /// Improvement ratio last 1000 generations.
308    pub i_1000_ratio: Float,
309    /// True if this generation considered as improvement.
310    pub is_improvement: bool,
311    /// Population state.
312    pub population: Population,
313}
314
315/// Keeps essential information about particular individual in population.
316#[derive(Clone, Deserialize, Serialize, PartialEq, Debug)]
317#[serde(rename_all = "camelCase")]
318pub struct Individual {
319    /// Solution cost difference from best individual.
320    pub difference: Float,
321    /// Objectives fitness values.
322    pub fitness: Vec<Float>,
323}
324
325/// Holds population state.
326#[derive(Clone, Deserialize, Serialize, PartialEq, Debug)]
327#[serde(rename_all = "camelCase")]
328pub struct Population {
329    /// Population individuals.
330    pub individuals: Vec<Individual>,
331}
332
333/// Contains extra information.
334#[derive(Clone, Deserialize, Serialize, PartialEq, Debug)]
335pub struct Extras {
336    /// A telemetry metrics.
337    #[serde(skip_serializing_if = "Option::is_none")]
338    pub metrics: Option<Metrics>,
339
340    /// Represents solution as a collection of geo json features.
341    #[serde(skip_serializing_if = "Option::is_none")]
342    pub features: Option<FeatureCollection>,
343}
344
345/// A VRP solution.
346#[derive(Clone, Deserialize, Serialize, PartialEq, Debug)]
347#[serde(rename_all = "camelCase")]
348pub struct Solution {
349    /// Total statistic.
350    pub statistic: Statistic,
351
352    /// List of tours.
353    pub tours: Vec<Tour>,
354
355    /// List of unassigned jobs.
356    #[serde(skip_serializing_if = "Option::is_none")]
357    pub unassigned: Option<Vec<UnassignedJob>>,
358
359    /// List of constraint violations.
360    #[serde(skip_serializing_if = "Option::is_none")]
361    pub violations: Option<Vec<Violation>>,
362
363    /// An extra information.
364    #[serde(skip_serializing_if = "Option::is_none")]
365    pub extras: Option<Extras>,
366}
367
368/// Serializes solution into json format.
369pub fn serialize_solution<W: Write>(solution: &Solution, writer: &mut BufWriter<W>) -> Result<(), Error> {
370    serde_json::to_writer_pretty(writer, solution).map_err(Error::from)
371}
372
373/// Deserializes solution from json format.
374pub fn deserialize_solution<R: Read>(reader: BufReader<R>) -> Result<Solution, Error> {
375    serde_json::from_reader(reader).map_err(Error::from)
376}
377
378impl Interval {
379    /// Returns interval's duration.
380    pub fn duration(&self) -> Duration {
381        parse_time(&self.end) - parse_time(&self.start)
382    }
383}
384
385impl Commute {
386    /// Creates a new instance of `Commute`.
387    pub fn new(commute: &DomainCommute, start: Timestamp, end: Timestamp, coord_index: &CoordIndex) -> Commute {
388        let parse_info = |info: &DomainCommuteInfo, time: Timestamp| {
389            if info.is_zero_distance() {
390                None
391            } else {
392                Some(CommuteInfo {
393                    location: coord_index.get_by_idx(info.location).expect("commute info has no location"),
394                    distance: info.distance,
395                    time: Interval { start: format_time(time), end: format_time(time + info.duration) },
396                })
397            }
398        };
399
400        Commute { forward: parse_info(&commute.forward, start), backward: parse_info(&commute.backward, end) }
401    }
402
403    /// Converts given commute object to core model.
404    pub(crate) fn to_domain(&self, coord_index: &CoordIndex) -> DomainCommute {
405        let parse_info = |info: &Option<CommuteInfo>| {
406            info.as_ref().map_or(DomainCommuteInfo::default(), |info| {
407                let start = parse_time(&info.time.start);
408                let end = parse_time(&info.time.end);
409                DomainCommuteInfo {
410                    location: coord_index.get_by_loc(&info.location).expect("expect to have coordinate in commute"),
411                    distance: info.distance,
412                    duration: end - start,
413                }
414            })
415        };
416
417        DomainCommute { forward: parse_info(&self.forward), backward: parse_info(&self.backward) }
418    }
419}