1#[path = "./common/routing.rs"]
13mod common;
14use crate::common::define_routing_data;
15
16use std::sync::Arc;
17use vrp_core::prelude::*;
18
19fn define_problem(goal: GoalContext, transport: Arc<dyn TransportCost>) -> GenericResult<Problem> {
21 let single_jobs = (1..=4)
23 .map(|idx| {
24 SingleBuilder::default()
25 .id(format!("job{idx}").as_str())
26 .demand(Demand::delivery(1))
28 .location(idx)?
30 .build_as_job()
31 })
32 .collect::<Result<Vec<_>, _>>()?;
33
34 let vehicles = (1..=4)
36 .map(|idx| {
37 VehicleBuilder::default()
38 .id(format!("v{idx}").as_str())
39 .add_detail(
40 VehicleDetailBuilder::default()
41 .set_start_location(0)
43 .set_end_location(0)
45 .build()?,
46 )
47 .capacity(SingleDimLoad::new(2))
49 .build()
50 })
51 .collect::<Result<Vec<_>, _>>()?;
52
53 ProblemBuilder::default()
54 .add_jobs(single_jobs.into_iter())
55 .add_vehicles(vehicles.into_iter())
56 .with_goal(goal)
57 .with_transport_cost(transport)
58 .build()
59}
60
61fn define_goal(transport: Arc<dyn TransportCost>) -> GenericResult<GoalContext> {
63 let minimize_unassigned = MinimizeUnassignedBuilder::new("min-unassigned").build()?;
65 let capacity_feature = CapacityFeatureBuilder::<SingleDimLoad>::new("capacity").build()?;
66 let transport_feature = TransportFeatureBuilder::new("min-distance")
67 .set_transport_cost(transport)
68 .set_time_constrained(false)
70 .build_minimize_distance()?;
71
72 GoalContextBuilder::with_features(&[minimize_unassigned, transport_feature, capacity_feature])?.build()
76}
77
78fn main() -> GenericResult<()> {
79 let transport = Arc::new(define_routing_data()?);
81
82 let goal = define_goal(transport.clone())?;
84 let problem = Arc::new(define_problem(goal, transport)?);
85
86 let config = VrpConfigBuilder::new(problem.clone())
88 .prebuild()?
89 .with_max_time(Some(5))
90 .with_max_generations(Some(10))
91 .build()?;
92
93 let solution = Solver::new(problem, config).solve()?;
95
96 assert!(solution.unassigned.is_empty(), "has unassigned jobs, but all jobs must be assigned");
97 assert_eq!(solution.routes.len(), 2, "two tours are expected");
98 assert_eq!(solution.cost, 2135., "unexpected cost (total distance traveled)");
99
100 println!(
102 "\nIn solution, locations are visited in the following order:\n{:?}\n",
103 solution.get_locations().map(Iterator::collect::<Vec<_>>).collect::<Vec<_>>()
104 );
105
106 Ok(())
107}