vrp_core/models/
domain.rs1use crate::construction::heuristics::UnassignmentInfo;
2use crate::models::common::{Cost, Location};
3use crate::models::problem::*;
4use crate::models::solution::{Registry, Route};
5use crate::models::*;
6use rosomaxa::evolution::TelemetryMetrics;
7use rosomaxa::prelude::*;
8use std::fmt::{Debug, Formatter};
9use std::sync::Arc;
10
11pub struct Problem {
13 pub fleet: Arc<Fleet>,
15
16 pub jobs: Arc<Jobs>,
18
19 pub locks: Vec<Arc<Lock>>,
21
22 pub goal: Arc<GoalContext>,
24
25 pub activity: Arc<dyn ActivityCost>,
27
28 pub transport: Arc<dyn TransportCost>,
30
31 pub extras: Arc<Extras>,
33}
34
35impl Debug for Problem {
36 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
37 f.debug_struct(short_type_name::<Self>())
38 .field("fleet", &self.fleet)
39 .field("jobs", &self.jobs.size())
40 .field("locks", &self.locks.len())
41 .field("goal", self.goal.as_ref())
42 .finish_non_exhaustive()
43 }
44}
45
46pub struct Solution {
48 pub cost: Cost,
51
52 pub registry: Registry,
54
55 pub routes: Vec<Route>,
57
58 pub unassigned: Vec<(Job, UnassignmentInfo)>,
60
61 pub telemetry: Option<TelemetryMetrics>,
63}
64
65pub enum LockOrder {
67 Any,
69 Sequence,
71 Strict,
73}
74
75#[derive(Clone)]
77pub enum LockPosition {
78 Any,
80 Departure,
82 Arrival,
84 Fixed,
86}
87
88pub struct LockDetail {
90 pub order: LockOrder,
92 pub position: LockPosition,
94 pub jobs: Vec<Job>,
96}
97
98pub struct Lock {
100 pub condition_fn: Arc<dyn Fn(&Actor) -> bool + Sync + Send>,
102 pub details: Vec<LockDetail>,
104 pub is_lazy: bool,
107}
108
109impl LockDetail {
110 pub fn new(order: LockOrder, position: LockPosition, jobs: Vec<Job>) -> Self {
112 Self { order, position, jobs }
113 }
114}
115
116impl Lock {
117 pub fn new(condition: Arc<dyn Fn(&Actor) -> bool + Sync + Send>, details: Vec<LockDetail>, is_lazy: bool) -> Self {
119 Self { condition_fn: condition, details, is_lazy }
120 }
121}
122
123pub type FleetGroupKeyFn = dyn Fn(&Actor) -> usize + Send + Sync;
125
126#[derive(Default)]
128pub struct ProblemBuilder {
129 jobs: Vec<Job>,
130 vehicles: Vec<Vehicle>,
131 #[allow(clippy::type_complexity)]
132 group_key_fn: Option<Box<dyn Fn(&[Arc<Actor>]) -> Box<FleetGroupKeyFn>>>,
133 goal: Option<Arc<GoalContext>>,
134 activity: Option<Arc<dyn ActivityCost>>,
135 transport: Option<Arc<dyn TransportCost>>,
136 extras: Option<Arc<Extras>>,
137 logger: Option<InfoLogger>,
138}
139
140impl ProblemBuilder {
141 pub fn add_job(mut self, job: Job) -> Self {
143 self.jobs.push(job);
144 self
145 }
146
147 pub fn add_jobs(mut self, jobs: impl Iterator<Item = Job>) -> Self {
149 self.jobs.extend(jobs);
150 self
151 }
152
153 pub fn add_vehicle(mut self, vehicle: Vehicle) -> Self {
156 self.vehicles.push(vehicle);
157 self
158 }
159
160 pub fn add_vehicles(mut self, vehicles: impl Iterator<Item = Vehicle>) -> Self {
163 self.vehicles.extend(vehicles);
164 self
165 }
166
167 pub fn with_vehicle_similarity(
171 mut self,
172 group_key_fn: impl Fn(&[Arc<Actor>]) -> Box<FleetGroupKeyFn> + 'static,
173 ) -> Self {
174 self.group_key_fn = Some(Box::new(group_key_fn));
175 self
176 }
177
178 pub fn with_goal(mut self, goal: GoalContext) -> Self {
181 self.goal = Some(Arc::new(goal));
182 self
183 }
184
185 pub fn with_transport_cost(mut self, transport: Arc<dyn TransportCost>) -> Self {
189 self.transport = Some(transport);
190 self
191 }
192
193 pub fn with_activity_cost(mut self, activity: Arc<dyn ActivityCost>) -> Self {
196 self.activity = Some(activity);
197 self
198 }
199
200 pub fn with_extras(mut self, extras: Extras) -> Self {
204 self.extras = Some(Arc::new(extras));
205 self
206 }
207
208 pub fn with_logger(mut self, logger: InfoLogger) -> Self {
210 self.logger = Some(logger);
211 self
212 }
213
214 pub fn build(mut self) -> GenericResult<Problem> {
217 if self.jobs.is_empty() {
218 return Err("empty list of jobs: specify at least one job".into());
219 }
220
221 if self.vehicles.is_empty() {
222 return Err("empty list of vehicles: specify at least one vehicle".into());
223 }
224
225 let transport = self.transport.take().ok_or_else(|| {
227 GenericError::from("no information about routing data: use 'with_transport_cost' method to specify it")
228 })?;
229 let activity = self.activity.take().unwrap_or_else(|| Arc::new(SimpleActivityCost::default()));
230 let goal = self
231 .goal
232 .take()
233 .ok_or_else(|| GenericError::from("unknown goal of optimization: use 'with_goal' method to set it"))?;
234 let extras = self.extras.take().unwrap_or_else(|| Arc::new(Extras::default()));
235
236 let driver = Arc::new(Driver::empty());
239 let vehicles = self.vehicles.into_iter().map(Arc::new).collect();
240 let group_key = self.group_key_fn.take().unwrap_or_else(|| Box::new(|_| Box::new(|a| a.vehicle.profile.index)));
241 let fleet = Arc::new(Fleet::new(vec![driver], vehicles, group_key));
242
243 let logger = self.logger.unwrap_or_else(|| Arc::new(|msg| println!("{}", msg)));
244
245 let jobs = Arc::new(Jobs::new(fleet.as_ref(), self.jobs, transport.as_ref(), &logger)?);
247
248 Ok(Problem { fleet, jobs, locks: vec![], goal, activity, transport, extras })
249 }
250}
251
252impl Solution {
253 pub fn get_locations(&self) -> impl Iterator<Item = impl Iterator<Item = Location> + '_> + '_ {
255 self.routes.iter().map(|route| route.tour.all_activities().map(|activity| activity.place.location))
256 }
257}