Struct vrp_core::models::ProblemBuilder
source · pub struct ProblemBuilder { /* private fields */ }Expand description
Provides way to build a VRP definition.
Implementations§
source§impl ProblemBuilder
impl ProblemBuilder
sourcepub fn add_jobs(self, jobs: impl Iterator<Item = Job>) -> Self
pub fn add_jobs(self, jobs: impl Iterator<Item = Job>) -> Self
Adds multiple jobs to the collection of the things to be done.
Examples found in repository?
84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114
fn define_problem(goal: GoalContext, transport: Arc<dyn TransportCost + Send + Sync>) -> GenericResult<Problem> {
// create 4 jobs where two are having top prio
let single_jobs = (1..=4)
.map(|idx| {
SingleBuilder::default()
.id(format!("job{idx}").as_str())
.demand(Demand::delivery(1))
.dimension(|dimens| {
// mark two jobs as top priority (2 and 4 locations)
dimens.set_job_priority(idx % 2 == 0);
})
.location(idx)?
.build_as_job()
})
.collect::<Result<Vec<_>, _>>()?;
// define a single vehicle with limited capacity which doesn't need to return back to the depot
let vehicle = VehicleBuilder::default()
.id("v1".to_string().as_str())
.add_detail(VehicleDetailBuilder::default().set_start_location(0).build()?)
// only two jobs can be served by the vehicle
.capacity(SingleDimLoad::new(2))
.build()?;
ProblemBuilder::default()
.add_jobs(single_jobs.into_iter())
.add_vehicles(once(vehicle))
.with_goal(goal)
.with_transport_cost(transport)
.build()
}More examples
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
fn define_problem(goal: GoalContext, transport: Arc<dyn TransportCost + Send + Sync>) -> GenericResult<Problem> {
// create 4 jobs with location indices from 1 to 4
let single_jobs = (1..=4)
.map(|idx| {
SingleBuilder::default()
.id(format!("job{idx}").as_str())
// each job is delivery job with demand=1
.demand(Demand::delivery(1))
// job has location, which is an index in routing matrix
.location(idx)?
.build_as_job()
})
.collect::<Result<Vec<_>, _>>()?;
// create 4 vehicles
let vehicles = (1..=4)
.map(|idx| {
VehicleBuilder::default()
.id(format!("v{idx}").as_str())
.add_detail(
VehicleDetailBuilder::default()
// vehicle starts at location with index 0 in routing matrix
.set_start_location(0)
// vehicle should return to location with index 0
.set_end_location(0)
.build()?,
)
// each vehicle has capacity=2, so it can serve at most 2 jobs
.capacity(SingleDimLoad::new(2))
.build()
})
.collect::<Result<Vec<_>, _>>()?;
ProblemBuilder::default()
.add_jobs(single_jobs.into_iter())
.add_vehicles(vehicles.into_iter())
.with_goal(goal)
.with_transport_cost(transport)
.build()
}52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
fn define_problem(goal: GoalContext, transport: Arc<dyn TransportCost + Send + Sync>) -> GenericResult<Problem> {
// create 4 jobs when second and forth have fridge requirement
let single_jobs = (1..=4)
.map(|idx| {
SingleBuilder::default()
.id(format!("job{idx}").as_str())
.demand(Demand::delivery(1))
.dimension(|dimens| {
// all jobs have fridge requirements, but only one vehicle will be allowed to serve them
dimens.set_job_hardware("fridge".to_string());
})
.location(idx)?
.build_as_job()
})
.collect::<Result<Vec<_>, _>>()?;
// create 2 vehicles
let vehicles = (1..=2)
.map(|idx| {
VehicleBuilder::default()
.id(format!("v{idx}").as_str())
.add_detail(
VehicleDetailBuilder::default()
// vehicle starts at location with index 0 in routing matrix
.set_start_location(0)
// vehicle should return to location with index 0
.set_end_location(0)
.build()?,
)
.dimension(|dimens| {
if idx % 2 == 0 {
// only one vehicle has a hardware requirement set to 'fridge'
dimens.set_vehicle_hardware(once("fridge".to_string()).collect());
}
})
// each vehicle has capacity=2, so it can serve at most 2 jobs
.capacity(SingleDimLoad::new(2))
.build()
})
.collect::<Result<Vec<_>, _>>()?;
ProblemBuilder::default()
.add_jobs(single_jobs.into_iter())
.add_vehicles(vehicles.into_iter())
.with_goal(goal)
.with_transport_cost(transport)
.build()
}22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
fn define_problem(goal: GoalContext, transport: Arc<dyn TransportCost + Send + Sync>) -> GenericResult<Problem> {
// build two PUDO (pick up/drop off) jobs with demand=1 and permissive time windows (just to show API usage)
let pudos = (1..=2)
.map(|idx| {
let location_idx = if idx == 1 { 1 } else { 3 };
MultiBuilder::default()
.id(format!("pudo{idx}").as_str())
.add_job(
SingleBuilder::default()
.demand(Demand::pudo_pickup(1))
.times(vec![TimeWindow::new(0., 1000.)])?
.duration(10.)?
.location(location_idx)?
.build()?,
)
.add_job(
SingleBuilder::default()
.demand(Demand::pudo_delivery(1))
.times(vec![TimeWindow::new(0., 1000.)])?
.duration(10.)?
.location(location_idx + 1)?
.build()?,
)
.build_as_job()
})
.collect::<Result<Vec<_>, _>>()?;
// define a single vehicle with limited capacity
let vehicle = VehicleBuilder::default()
.id("v1".to_string().as_str())
.add_detail(
VehicleDetailBuilder::default()
// vehicle starts at location with index 0 in routing matrix
.set_start_location(0)
.set_start_time(0.)
// vehicle should return to location with index 0
.set_end_location(0)
.set_end_time(10000.)
.build()?,
)
// the vehicle has capacity=1, so it is forced to do delivery after each pickup
.capacity(SingleDimLoad::new(1))
.build()?;
ProblemBuilder::default()
.add_jobs(pudos.into_iter())
.add_vehicles(once(vehicle))
.with_goal(goal)
.with_transport_cost(transport)
.build()
}sourcepub fn add_vehicle(self, vehicle: Vehicle) -> Self
pub fn add_vehicle(self, vehicle: Vehicle) -> Self
Add a vehicle to the fleet. At least one has to be provided.
sourcepub fn add_vehicles(self, vehicles: impl Iterator<Item = Vehicle>) -> Self
pub fn add_vehicles(self, vehicles: impl Iterator<Item = Vehicle>) -> Self
Add multiple vehicles to the fleet. At least one has to be provided.
Examples found in repository?
84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114
fn define_problem(goal: GoalContext, transport: Arc<dyn TransportCost + Send + Sync>) -> GenericResult<Problem> {
// create 4 jobs where two are having top prio
let single_jobs = (1..=4)
.map(|idx| {
SingleBuilder::default()
.id(format!("job{idx}").as_str())
.demand(Demand::delivery(1))
.dimension(|dimens| {
// mark two jobs as top priority (2 and 4 locations)
dimens.set_job_priority(idx % 2 == 0);
})
.location(idx)?
.build_as_job()
})
.collect::<Result<Vec<_>, _>>()?;
// define a single vehicle with limited capacity which doesn't need to return back to the depot
let vehicle = VehicleBuilder::default()
.id("v1".to_string().as_str())
.add_detail(VehicleDetailBuilder::default().set_start_location(0).build()?)
// only two jobs can be served by the vehicle
.capacity(SingleDimLoad::new(2))
.build()?;
ProblemBuilder::default()
.add_jobs(single_jobs.into_iter())
.add_vehicles(once(vehicle))
.with_goal(goal)
.with_transport_cost(transport)
.build()
}More examples
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
fn define_problem(goal: GoalContext, transport: Arc<dyn TransportCost + Send + Sync>) -> GenericResult<Problem> {
// create 4 jobs with location indices from 1 to 4
let single_jobs = (1..=4)
.map(|idx| {
SingleBuilder::default()
.id(format!("job{idx}").as_str())
// each job is delivery job with demand=1
.demand(Demand::delivery(1))
// job has location, which is an index in routing matrix
.location(idx)?
.build_as_job()
})
.collect::<Result<Vec<_>, _>>()?;
// create 4 vehicles
let vehicles = (1..=4)
.map(|idx| {
VehicleBuilder::default()
.id(format!("v{idx}").as_str())
.add_detail(
VehicleDetailBuilder::default()
// vehicle starts at location with index 0 in routing matrix
.set_start_location(0)
// vehicle should return to location with index 0
.set_end_location(0)
.build()?,
)
// each vehicle has capacity=2, so it can serve at most 2 jobs
.capacity(SingleDimLoad::new(2))
.build()
})
.collect::<Result<Vec<_>, _>>()?;
ProblemBuilder::default()
.add_jobs(single_jobs.into_iter())
.add_vehicles(vehicles.into_iter())
.with_goal(goal)
.with_transport_cost(transport)
.build()
}52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
fn define_problem(goal: GoalContext, transport: Arc<dyn TransportCost + Send + Sync>) -> GenericResult<Problem> {
// create 4 jobs when second and forth have fridge requirement
let single_jobs = (1..=4)
.map(|idx| {
SingleBuilder::default()
.id(format!("job{idx}").as_str())
.demand(Demand::delivery(1))
.dimension(|dimens| {
// all jobs have fridge requirements, but only one vehicle will be allowed to serve them
dimens.set_job_hardware("fridge".to_string());
})
.location(idx)?
.build_as_job()
})
.collect::<Result<Vec<_>, _>>()?;
// create 2 vehicles
let vehicles = (1..=2)
.map(|idx| {
VehicleBuilder::default()
.id(format!("v{idx}").as_str())
.add_detail(
VehicleDetailBuilder::default()
// vehicle starts at location with index 0 in routing matrix
.set_start_location(0)
// vehicle should return to location with index 0
.set_end_location(0)
.build()?,
)
.dimension(|dimens| {
if idx % 2 == 0 {
// only one vehicle has a hardware requirement set to 'fridge'
dimens.set_vehicle_hardware(once("fridge".to_string()).collect());
}
})
// each vehicle has capacity=2, so it can serve at most 2 jobs
.capacity(SingleDimLoad::new(2))
.build()
})
.collect::<Result<Vec<_>, _>>()?;
ProblemBuilder::default()
.add_jobs(single_jobs.into_iter())
.add_vehicles(vehicles.into_iter())
.with_goal(goal)
.with_transport_cost(transport)
.build()
}22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
fn define_problem(goal: GoalContext, transport: Arc<dyn TransportCost + Send + Sync>) -> GenericResult<Problem> {
// build two PUDO (pick up/drop off) jobs with demand=1 and permissive time windows (just to show API usage)
let pudos = (1..=2)
.map(|idx| {
let location_idx = if idx == 1 { 1 } else { 3 };
MultiBuilder::default()
.id(format!("pudo{idx}").as_str())
.add_job(
SingleBuilder::default()
.demand(Demand::pudo_pickup(1))
.times(vec![TimeWindow::new(0., 1000.)])?
.duration(10.)?
.location(location_idx)?
.build()?,
)
.add_job(
SingleBuilder::default()
.demand(Demand::pudo_delivery(1))
.times(vec![TimeWindow::new(0., 1000.)])?
.duration(10.)?
.location(location_idx + 1)?
.build()?,
)
.build_as_job()
})
.collect::<Result<Vec<_>, _>>()?;
// define a single vehicle with limited capacity
let vehicle = VehicleBuilder::default()
.id("v1".to_string().as_str())
.add_detail(
VehicleDetailBuilder::default()
// vehicle starts at location with index 0 in routing matrix
.set_start_location(0)
.set_start_time(0.)
// vehicle should return to location with index 0
.set_end_location(0)
.set_end_time(10000.)
.build()?,
)
// the vehicle has capacity=1, so it is forced to do delivery after each pickup
.capacity(SingleDimLoad::new(1))
.build()?;
ProblemBuilder::default()
.add_jobs(pudos.into_iter())
.add_vehicles(once(vehicle))
.with_goal(goal)
.with_transport_cost(transport)
.build()
}sourcepub fn with_vehicle_similarity(
self,
group_key_fn: impl Fn(&[Arc<Actor>]) -> Box<FleetGroupKeyFn> + 'static,
) -> Self
pub fn with_vehicle_similarity( self, group_key_fn: impl Fn(&[Arc<Actor>]) -> Box<FleetGroupKeyFn> + 'static, ) -> Self
Sets a vehicle similarity function which allows grouping of similar vehicles together.
That helps the solver to take more effective decisions job-vehicle assignment.
Optional: when omitted, only vehicles with the same profile.index are grouped together.
sourcepub fn with_goal(self, goal: GoalContext) -> Self
pub fn with_goal(self, goal: GoalContext) -> Self
Adds a goal of optimization. Use GoalContextBuilder to create the one. A required field.
Examples found in repository?
84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114
fn define_problem(goal: GoalContext, transport: Arc<dyn TransportCost + Send + Sync>) -> GenericResult<Problem> {
// create 4 jobs where two are having top prio
let single_jobs = (1..=4)
.map(|idx| {
SingleBuilder::default()
.id(format!("job{idx}").as_str())
.demand(Demand::delivery(1))
.dimension(|dimens| {
// mark two jobs as top priority (2 and 4 locations)
dimens.set_job_priority(idx % 2 == 0);
})
.location(idx)?
.build_as_job()
})
.collect::<Result<Vec<_>, _>>()?;
// define a single vehicle with limited capacity which doesn't need to return back to the depot
let vehicle = VehicleBuilder::default()
.id("v1".to_string().as_str())
.add_detail(VehicleDetailBuilder::default().set_start_location(0).build()?)
// only two jobs can be served by the vehicle
.capacity(SingleDimLoad::new(2))
.build()?;
ProblemBuilder::default()
.add_jobs(single_jobs.into_iter())
.add_vehicles(once(vehicle))
.with_goal(goal)
.with_transport_cost(transport)
.build()
}More examples
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
fn define_problem(goal: GoalContext, transport: Arc<dyn TransportCost + Send + Sync>) -> GenericResult<Problem> {
// create 4 jobs with location indices from 1 to 4
let single_jobs = (1..=4)
.map(|idx| {
SingleBuilder::default()
.id(format!("job{idx}").as_str())
// each job is delivery job with demand=1
.demand(Demand::delivery(1))
// job has location, which is an index in routing matrix
.location(idx)?
.build_as_job()
})
.collect::<Result<Vec<_>, _>>()?;
// create 4 vehicles
let vehicles = (1..=4)
.map(|idx| {
VehicleBuilder::default()
.id(format!("v{idx}").as_str())
.add_detail(
VehicleDetailBuilder::default()
// vehicle starts at location with index 0 in routing matrix
.set_start_location(0)
// vehicle should return to location with index 0
.set_end_location(0)
.build()?,
)
// each vehicle has capacity=2, so it can serve at most 2 jobs
.capacity(SingleDimLoad::new(2))
.build()
})
.collect::<Result<Vec<_>, _>>()?;
ProblemBuilder::default()
.add_jobs(single_jobs.into_iter())
.add_vehicles(vehicles.into_iter())
.with_goal(goal)
.with_transport_cost(transport)
.build()
}52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
fn define_problem(goal: GoalContext, transport: Arc<dyn TransportCost + Send + Sync>) -> GenericResult<Problem> {
// create 4 jobs when second and forth have fridge requirement
let single_jobs = (1..=4)
.map(|idx| {
SingleBuilder::default()
.id(format!("job{idx}").as_str())
.demand(Demand::delivery(1))
.dimension(|dimens| {
// all jobs have fridge requirements, but only one vehicle will be allowed to serve them
dimens.set_job_hardware("fridge".to_string());
})
.location(idx)?
.build_as_job()
})
.collect::<Result<Vec<_>, _>>()?;
// create 2 vehicles
let vehicles = (1..=2)
.map(|idx| {
VehicleBuilder::default()
.id(format!("v{idx}").as_str())
.add_detail(
VehicleDetailBuilder::default()
// vehicle starts at location with index 0 in routing matrix
.set_start_location(0)
// vehicle should return to location with index 0
.set_end_location(0)
.build()?,
)
.dimension(|dimens| {
if idx % 2 == 0 {
// only one vehicle has a hardware requirement set to 'fridge'
dimens.set_vehicle_hardware(once("fridge".to_string()).collect());
}
})
// each vehicle has capacity=2, so it can serve at most 2 jobs
.capacity(SingleDimLoad::new(2))
.build()
})
.collect::<Result<Vec<_>, _>>()?;
ProblemBuilder::default()
.add_jobs(single_jobs.into_iter())
.add_vehicles(vehicles.into_iter())
.with_goal(goal)
.with_transport_cost(transport)
.build()
}22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
fn define_problem(goal: GoalContext, transport: Arc<dyn TransportCost + Send + Sync>) -> GenericResult<Problem> {
// build two PUDO (pick up/drop off) jobs with demand=1 and permissive time windows (just to show API usage)
let pudos = (1..=2)
.map(|idx| {
let location_idx = if idx == 1 { 1 } else { 3 };
MultiBuilder::default()
.id(format!("pudo{idx}").as_str())
.add_job(
SingleBuilder::default()
.demand(Demand::pudo_pickup(1))
.times(vec![TimeWindow::new(0., 1000.)])?
.duration(10.)?
.location(location_idx)?
.build()?,
)
.add_job(
SingleBuilder::default()
.demand(Demand::pudo_delivery(1))
.times(vec![TimeWindow::new(0., 1000.)])?
.duration(10.)?
.location(location_idx + 1)?
.build()?,
)
.build_as_job()
})
.collect::<Result<Vec<_>, _>>()?;
// define a single vehicle with limited capacity
let vehicle = VehicleBuilder::default()
.id("v1".to_string().as_str())
.add_detail(
VehicleDetailBuilder::default()
// vehicle starts at location with index 0 in routing matrix
.set_start_location(0)
.set_start_time(0.)
// vehicle should return to location with index 0
.set_end_location(0)
.set_end_time(10000.)
.build()?,
)
// the vehicle has capacity=1, so it is forced to do delivery after each pickup
.capacity(SingleDimLoad::new(1))
.build()?;
ProblemBuilder::default()
.add_jobs(pudos.into_iter())
.add_vehicles(once(vehicle))
.with_goal(goal)
.with_transport_cost(transport)
.build()
}sourcepub fn with_transport_cost(
self,
transport: Arc<dyn TransportCost + Send + Sync>,
) -> Self
pub fn with_transport_cost( self, transport: Arc<dyn TransportCost + Send + Sync>, ) -> Self
Adds a transport distance/duration estimation logic. A typical implementation will normally wrap routing distance/duration matrices. A required field.
Examples found in repository?
84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114
fn define_problem(goal: GoalContext, transport: Arc<dyn TransportCost + Send + Sync>) -> GenericResult<Problem> {
// create 4 jobs where two are having top prio
let single_jobs = (1..=4)
.map(|idx| {
SingleBuilder::default()
.id(format!("job{idx}").as_str())
.demand(Demand::delivery(1))
.dimension(|dimens| {
// mark two jobs as top priority (2 and 4 locations)
dimens.set_job_priority(idx % 2 == 0);
})
.location(idx)?
.build_as_job()
})
.collect::<Result<Vec<_>, _>>()?;
// define a single vehicle with limited capacity which doesn't need to return back to the depot
let vehicle = VehicleBuilder::default()
.id("v1".to_string().as_str())
.add_detail(VehicleDetailBuilder::default().set_start_location(0).build()?)
// only two jobs can be served by the vehicle
.capacity(SingleDimLoad::new(2))
.build()?;
ProblemBuilder::default()
.add_jobs(single_jobs.into_iter())
.add_vehicles(once(vehicle))
.with_goal(goal)
.with_transport_cost(transport)
.build()
}More examples
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
fn define_problem(goal: GoalContext, transport: Arc<dyn TransportCost + Send + Sync>) -> GenericResult<Problem> {
// create 4 jobs with location indices from 1 to 4
let single_jobs = (1..=4)
.map(|idx| {
SingleBuilder::default()
.id(format!("job{idx}").as_str())
// each job is delivery job with demand=1
.demand(Demand::delivery(1))
// job has location, which is an index in routing matrix
.location(idx)?
.build_as_job()
})
.collect::<Result<Vec<_>, _>>()?;
// create 4 vehicles
let vehicles = (1..=4)
.map(|idx| {
VehicleBuilder::default()
.id(format!("v{idx}").as_str())
.add_detail(
VehicleDetailBuilder::default()
// vehicle starts at location with index 0 in routing matrix
.set_start_location(0)
// vehicle should return to location with index 0
.set_end_location(0)
.build()?,
)
// each vehicle has capacity=2, so it can serve at most 2 jobs
.capacity(SingleDimLoad::new(2))
.build()
})
.collect::<Result<Vec<_>, _>>()?;
ProblemBuilder::default()
.add_jobs(single_jobs.into_iter())
.add_vehicles(vehicles.into_iter())
.with_goal(goal)
.with_transport_cost(transport)
.build()
}52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
fn define_problem(goal: GoalContext, transport: Arc<dyn TransportCost + Send + Sync>) -> GenericResult<Problem> {
// create 4 jobs when second and forth have fridge requirement
let single_jobs = (1..=4)
.map(|idx| {
SingleBuilder::default()
.id(format!("job{idx}").as_str())
.demand(Demand::delivery(1))
.dimension(|dimens| {
// all jobs have fridge requirements, but only one vehicle will be allowed to serve them
dimens.set_job_hardware("fridge".to_string());
})
.location(idx)?
.build_as_job()
})
.collect::<Result<Vec<_>, _>>()?;
// create 2 vehicles
let vehicles = (1..=2)
.map(|idx| {
VehicleBuilder::default()
.id(format!("v{idx}").as_str())
.add_detail(
VehicleDetailBuilder::default()
// vehicle starts at location with index 0 in routing matrix
.set_start_location(0)
// vehicle should return to location with index 0
.set_end_location(0)
.build()?,
)
.dimension(|dimens| {
if idx % 2 == 0 {
// only one vehicle has a hardware requirement set to 'fridge'
dimens.set_vehicle_hardware(once("fridge".to_string()).collect());
}
})
// each vehicle has capacity=2, so it can serve at most 2 jobs
.capacity(SingleDimLoad::new(2))
.build()
})
.collect::<Result<Vec<_>, _>>()?;
ProblemBuilder::default()
.add_jobs(single_jobs.into_iter())
.add_vehicles(vehicles.into_iter())
.with_goal(goal)
.with_transport_cost(transport)
.build()
}22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
fn define_problem(goal: GoalContext, transport: Arc<dyn TransportCost + Send + Sync>) -> GenericResult<Problem> {
// build two PUDO (pick up/drop off) jobs with demand=1 and permissive time windows (just to show API usage)
let pudos = (1..=2)
.map(|idx| {
let location_idx = if idx == 1 { 1 } else { 3 };
MultiBuilder::default()
.id(format!("pudo{idx}").as_str())
.add_job(
SingleBuilder::default()
.demand(Demand::pudo_pickup(1))
.times(vec![TimeWindow::new(0., 1000.)])?
.duration(10.)?
.location(location_idx)?
.build()?,
)
.add_job(
SingleBuilder::default()
.demand(Demand::pudo_delivery(1))
.times(vec![TimeWindow::new(0., 1000.)])?
.duration(10.)?
.location(location_idx + 1)?
.build()?,
)
.build_as_job()
})
.collect::<Result<Vec<_>, _>>()?;
// define a single vehicle with limited capacity
let vehicle = VehicleBuilder::default()
.id("v1".to_string().as_str())
.add_detail(
VehicleDetailBuilder::default()
// vehicle starts at location with index 0 in routing matrix
.set_start_location(0)
.set_start_time(0.)
// vehicle should return to location with index 0
.set_end_location(0)
.set_end_time(10000.)
.build()?,
)
// the vehicle has capacity=1, so it is forced to do delivery after each pickup
.capacity(SingleDimLoad::new(1))
.build()?;
ProblemBuilder::default()
.add_jobs(pudos.into_iter())
.add_vehicles(once(vehicle))
.with_goal(goal)
.with_transport_cost(transport)
.build()
}sourcepub fn with_activity_cost(
self,
activity: Arc<dyn ActivityCost + Send + Sync>,
) -> Self
pub fn with_activity_cost( self, activity: Arc<dyn ActivityCost + Send + Sync>, ) -> Self
Adds an activity service time estimation logic. An optional field: SimpleActivityCost will be used by default.
sourcepub fn with_extras(self, extras: Extras) -> Self
pub fn with_extras(self, extras: Extras) -> Self
Adds an extras: an extension mechanism to pass arbitrary properties associated within the problem definition. An optional field.
sourcepub fn build(self) -> GenericResult<Problem>
pub fn build(self) -> GenericResult<Problem>
Builds a problem definition. Returns Err in case of an invalid configuration.
Examples found in repository?
84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114
fn define_problem(goal: GoalContext, transport: Arc<dyn TransportCost + Send + Sync>) -> GenericResult<Problem> {
// create 4 jobs where two are having top prio
let single_jobs = (1..=4)
.map(|idx| {
SingleBuilder::default()
.id(format!("job{idx}").as_str())
.demand(Demand::delivery(1))
.dimension(|dimens| {
// mark two jobs as top priority (2 and 4 locations)
dimens.set_job_priority(idx % 2 == 0);
})
.location(idx)?
.build_as_job()
})
.collect::<Result<Vec<_>, _>>()?;
// define a single vehicle with limited capacity which doesn't need to return back to the depot
let vehicle = VehicleBuilder::default()
.id("v1".to_string().as_str())
.add_detail(VehicleDetailBuilder::default().set_start_location(0).build()?)
// only two jobs can be served by the vehicle
.capacity(SingleDimLoad::new(2))
.build()?;
ProblemBuilder::default()
.add_jobs(single_jobs.into_iter())
.add_vehicles(once(vehicle))
.with_goal(goal)
.with_transport_cost(transport)
.build()
}More examples
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
fn define_problem(goal: GoalContext, transport: Arc<dyn TransportCost + Send + Sync>) -> GenericResult<Problem> {
// create 4 jobs with location indices from 1 to 4
let single_jobs = (1..=4)
.map(|idx| {
SingleBuilder::default()
.id(format!("job{idx}").as_str())
// each job is delivery job with demand=1
.demand(Demand::delivery(1))
// job has location, which is an index in routing matrix
.location(idx)?
.build_as_job()
})
.collect::<Result<Vec<_>, _>>()?;
// create 4 vehicles
let vehicles = (1..=4)
.map(|idx| {
VehicleBuilder::default()
.id(format!("v{idx}").as_str())
.add_detail(
VehicleDetailBuilder::default()
// vehicle starts at location with index 0 in routing matrix
.set_start_location(0)
// vehicle should return to location with index 0
.set_end_location(0)
.build()?,
)
// each vehicle has capacity=2, so it can serve at most 2 jobs
.capacity(SingleDimLoad::new(2))
.build()
})
.collect::<Result<Vec<_>, _>>()?;
ProblemBuilder::default()
.add_jobs(single_jobs.into_iter())
.add_vehicles(vehicles.into_iter())
.with_goal(goal)
.with_transport_cost(transport)
.build()
}52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
fn define_problem(goal: GoalContext, transport: Arc<dyn TransportCost + Send + Sync>) -> GenericResult<Problem> {
// create 4 jobs when second and forth have fridge requirement
let single_jobs = (1..=4)
.map(|idx| {
SingleBuilder::default()
.id(format!("job{idx}").as_str())
.demand(Demand::delivery(1))
.dimension(|dimens| {
// all jobs have fridge requirements, but only one vehicle will be allowed to serve them
dimens.set_job_hardware("fridge".to_string());
})
.location(idx)?
.build_as_job()
})
.collect::<Result<Vec<_>, _>>()?;
// create 2 vehicles
let vehicles = (1..=2)
.map(|idx| {
VehicleBuilder::default()
.id(format!("v{idx}").as_str())
.add_detail(
VehicleDetailBuilder::default()
// vehicle starts at location with index 0 in routing matrix
.set_start_location(0)
// vehicle should return to location with index 0
.set_end_location(0)
.build()?,
)
.dimension(|dimens| {
if idx % 2 == 0 {
// only one vehicle has a hardware requirement set to 'fridge'
dimens.set_vehicle_hardware(once("fridge".to_string()).collect());
}
})
// each vehicle has capacity=2, so it can serve at most 2 jobs
.capacity(SingleDimLoad::new(2))
.build()
})
.collect::<Result<Vec<_>, _>>()?;
ProblemBuilder::default()
.add_jobs(single_jobs.into_iter())
.add_vehicles(vehicles.into_iter())
.with_goal(goal)
.with_transport_cost(transport)
.build()
}22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
fn define_problem(goal: GoalContext, transport: Arc<dyn TransportCost + Send + Sync>) -> GenericResult<Problem> {
// build two PUDO (pick up/drop off) jobs with demand=1 and permissive time windows (just to show API usage)
let pudos = (1..=2)
.map(|idx| {
let location_idx = if idx == 1 { 1 } else { 3 };
MultiBuilder::default()
.id(format!("pudo{idx}").as_str())
.add_job(
SingleBuilder::default()
.demand(Demand::pudo_pickup(1))
.times(vec![TimeWindow::new(0., 1000.)])?
.duration(10.)?
.location(location_idx)?
.build()?,
)
.add_job(
SingleBuilder::default()
.demand(Demand::pudo_delivery(1))
.times(vec![TimeWindow::new(0., 1000.)])?
.duration(10.)?
.location(location_idx + 1)?
.build()?,
)
.build_as_job()
})
.collect::<Result<Vec<_>, _>>()?;
// define a single vehicle with limited capacity
let vehicle = VehicleBuilder::default()
.id("v1".to_string().as_str())
.add_detail(
VehicleDetailBuilder::default()
// vehicle starts at location with index 0 in routing matrix
.set_start_location(0)
.set_start_time(0.)
// vehicle should return to location with index 0
.set_end_location(0)
.set_end_time(10000.)
.build()?,
)
// the vehicle has capacity=1, so it is forced to do delivery after each pickup
.capacity(SingleDimLoad::new(1))
.build()?;
ProblemBuilder::default()
.add_jobs(pudos.into_iter())
.add_vehicles(once(vehicle))
.with_goal(goal)
.with_transport_cost(transport)
.build()
}Trait Implementations§
source§impl Default for ProblemBuilder
impl Default for ProblemBuilder
source§fn default() -> ProblemBuilder
fn default() -> ProblemBuilder
Auto Trait Implementations§
impl Freeze for ProblemBuilder
impl !RefUnwindSafe for ProblemBuilder
impl !Send for ProblemBuilder
impl !Sync for ProblemBuilder
impl Unpin for ProblemBuilder
impl !UnwindSafe for ProblemBuilder
Blanket Implementations§
source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
source§impl<T> IntoEither for T
impl<T> IntoEither for T
source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moresource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more