vrp_pragmatic/format/solution/
initial_reader.rs1#[cfg(test)]
2#[path = "../../../tests/unit/format/solution/initial_reader_test.rs"]
3mod initial_reader_test;
4
5use crate::format::solution::activity_matcher::{try_match_point_job, JobInfo};
6use crate::format::solution::Activity as FormatActivity;
7use crate::format::solution::Stop as FormatStop;
8use crate::format::solution::Tour as FormatTour;
9use crate::format::solution::{deserialize_solution, map_reason_code};
10use crate::format::{get_indices, CoordIndex, JobIndex, ShiftIndexDimension, VehicleTypeDimension};
11use crate::parse_time;
12use std::collections::{HashMap, HashSet};
13use std::io::{BufReader, Read};
14use std::sync::Arc;
15use vrp_core::construction::heuristics::UnassignmentInfo;
16use vrp_core::models::common::*;
17use vrp_core::models::problem::{Actor, Job, JobIdDimension, VehicleIdDimension};
18use vrp_core::models::solution::Tour as CoreTour;
19use vrp_core::models::solution::{Activity, Registry, Route};
20use vrp_core::prelude::*;
21
22type ActorKey = (String, String, usize);
23
24pub fn read_init_solution<R: Read>(
27 solution: BufReader<R>,
28 problem: Arc<Problem>,
29 random: Arc<dyn Random>,
30) -> Result<Solution, GenericError> {
31 let solution = deserialize_solution(solution).map_err(|err| format!("cannot deserialize solution: {err}"))?;
32
33 let mut registry = Registry::new(&problem.fleet, random);
34 let mut added_jobs = HashSet::default();
35
36 let actor_index = registry.all().map(|actor| (get_actor_key(actor.as_ref()), actor)).collect::<HashMap<_, _>>();
37 let (job_index, coord_index) = get_indices(&problem.extras)?;
38
39 let routes =
40 solution.tours.iter().try_fold::<_, _, Result<_, GenericError>>(Vec::<_>::default(), |mut routes, tour| {
41 let actor_key = (tour.vehicle_id.clone(), tour.type_id.clone(), tour.shift_index);
42 let actor =
43 actor_index.get(&actor_key).ok_or_else(|| format!("cannot find vehicle for {actor_key:?}"))?.clone();
44 registry.use_actor(&actor);
45
46 let mut core_route = create_core_route(actor, tour)?;
47
48 tour.stops.iter().try_for_each(|stop| {
49 stop.activities().iter().try_for_each::<_, Result<_, GenericError>>(|activity| {
50 try_insert_activity(
51 &mut core_route,
52 tour,
53 stop,
54 activity,
55 job_index.as_ref(),
56 coord_index.as_ref(),
57 &mut added_jobs,
58 )
59 })
60 })?;
61
62 routes.push(core_route);
63
64 Ok(routes)
65 })?;
66
67 let mut unassigned = solution
68 .unassigned
69 .unwrap_or_default()
70 .iter()
71 .try_fold::<Vec<_>, _, Result<_, GenericError>>(Default::default(), |mut acc, unassigned_job| {
72 let job = job_index
73 .get(&unassigned_job.job_id)
74 .cloned()
75 .ok_or_else(|| format!("cannot get job id for: {unassigned_job:?}"))?;
76 let code = unassigned_job
78 .reasons
79 .first()
80 .map(|reason| UnassignmentInfo::Simple(map_reason_code(&reason.code)))
81 .ok_or_else(|| format!("cannot get reason for: {unassigned_job:?}"))?;
82
83 added_jobs.insert(job.clone());
84 acc.push((job, code));
85
86 Ok(acc)
87 })?;
88
89 unassigned.extend(
90 problem
91 .jobs
92 .all()
93 .iter()
94 .filter(|job| !added_jobs.contains(job))
95 .map(|job| (job.clone(), UnassignmentInfo::Unknown)),
96 );
97
98 Ok(Solution { cost: Cost::default(), registry, routes, unassigned, telemetry: None })
99}
100
101fn try_insert_activity(
102 route: &mut Route,
103 tour: &FormatTour,
104 stop: &FormatStop,
105 activity: &FormatActivity,
106 job_index: &JobIndex,
107 coord_index: &CoordIndex,
108 added_jobs: &mut HashSet<Job>,
109) -> Result<(), GenericError> {
110 if activity.commute.is_some() {
111 return Err("commute property in initial solution is not supported".into());
112 }
113
114 let stop = match stop {
115 FormatStop::Transit(_) => return Err("transit property in initial solution is not yet supported".into()),
116 FormatStop::Point(stop) => stop,
117 };
118
119 if let Some(JobInfo(job, single, place, time)) = try_match_point_job(tour, stop, activity, job_index, coord_index)?
120 {
121 let is_inserted = added_jobs.insert(job.clone());
122 if !is_inserted && matches!(job, Job::Single(_)) {
123 return Err(format!(
124 "potential double assignment for single job '{:?}', matched job id: '{:?}'; try to use a different tag as a discriminator",
125 activity.job_id,
126 job.dimens().get_job_id()
127 )
128 .into());
129 }
130
131 route.tour.insert_last(Activity {
132 place,
133 schedule: Schedule { arrival: time.start, departure: time.end },
134 job: Some(single),
135 commute: None,
136 });
137 } else if activity.activity_type != "departure" && activity.activity_type != "arrival" {
138 return Err(
139 format!("cannot match activity with job id '{}' in tour: '{}'", activity.job_id, tour.vehicle_id).into()
140 );
141 }
142
143 Ok(())
144}
145
146fn get_actor_key(actor: &Actor) -> ActorKey {
147 let dimens = &actor.vehicle.dimens;
148
149 let vehicle_id = dimens.get_vehicle_id().cloned().expect("cannot get vehicle id!");
150 let type_id = dimens.get_vehicle_type().cloned().expect("cannot get type id!");
151 let shift_index = dimens.get_shift_index().copied().expect("cannot get shift index!");
152
153 (vehicle_id, type_id, shift_index)
154}
155
156fn create_core_route(actor: Arc<Actor>, format_tour: &FormatTour) -> Result<Route, GenericError> {
157 let mut core_tour = CoreTour::new(&actor);
158
159 let set_activity_time = |format_stop: &FormatStop,
161 format_activity: &FormatActivity,
162 core_activity: &mut Activity|
163 -> Result<(), GenericError> {
164 let time = &format_stop.schedule();
165 let (arrival, departure) = format_activity
166 .time
167 .as_ref()
168 .map_or((&time.arrival, &time.departure), |interval| (&interval.start, &interval.end));
169
170 core_activity.schedule.arrival = parse_time(arrival);
171 core_activity.schedule.departure = parse_time(departure);
172
173 Ok(())
174 };
175
176 let start_stop = format_tour.stops.first().ok_or_else(|| "empty tour in init solution".to_string())?;
177 let start_activity = start_stop.activities().first().ok_or_else(|| "start stop has no activities".to_string())?;
178 let core_start = core_tour.all_activities_mut().next().expect("cannot get start activity from core tour");
179
180 set_activity_time(start_stop, start_activity, core_start)?;
181
182 if core_tour.end().is_some() {
183 let end_stop = format_tour.stops.last().unwrap();
184 let end_activity = end_stop.activities().first().ok_or_else(|| "end stop has no activities".to_string())?;
185 let core_end = core_tour.all_activities_mut().last().unwrap();
186
187 set_activity_time(end_stop, end_activity, core_end)?;
188 }
189
190 Ok(Route { actor, tour: core_tour })
191}