vrp-core 1.25.0

A core algorithms to solve a Vehicle Routing Problem
Documentation
//! Provides the way to control fleet usage.

#[cfg(test)]
#[path = "../../../tests/unit/construction/features/fleet_usage_test.rs"]
mod fleet_usage_test;

use super::*;

/// Creates a feature to minimize used fleet size (affects amount of tours in solution).
pub fn create_minimize_tours_feature(name: &str) -> GenericResult<Feature> {
    FeatureBuilder::default()
        .with_name(name)
        .with_objective(FleetUsageObjective {
            route_estimate_fn: Box::new(|route_ctx| if route_ctx.route().tour.job_count() == 0 { 1. } else { 0. }),
            solution_estimate_fn: Box::new(|solution_ctx| solution_ctx.routes.iter().len() as Cost),
        })
        .build()
}

/// Creates a feature to maximize used fleet size (affects amount of tours in solution).
pub fn create_maximize_tours_feature(name: &str) -> GenericResult<Feature> {
    FeatureBuilder::default()
        .with_name(name)
        .with_objective(FleetUsageObjective {
            route_estimate_fn: Box::new(|route_ctx| if route_ctx.route().tour.job_count() == 0 { -1. } else { 0. }),
            solution_estimate_fn: Box::new(|solution_ctx| -1. * solution_ctx.routes.iter().len() as Cost),
        })
        .build()
}

/// Creates a feature to tries to minimize arrival time of used fleet.
pub fn create_minimize_arrival_time_feature(name: &str) -> GenericResult<Feature> {
    FeatureBuilder::default()
        .with_name(name)
        .with_objective(FleetUsageObjective {
            route_estimate_fn: Box::new(|route_ctx| route_ctx.route().actor.detail.time.start),
            solution_estimate_fn: Box::new(|solution_ctx| {
                if solution_ctx.routes.is_empty() {
                    0.
                } else {
                    let total: Float = solution_ctx
                        .routes
                        .iter()
                        .filter_map(|route_ctx| route_ctx.route().tour.end())
                        .map(|end| end.schedule.arrival)
                        .sum();

                    total / solution_ctx.routes.len() as Float
                }
            }),
        })
        .build()
}

struct FleetUsageObjective {
    route_estimate_fn: Box<dyn Fn(&RouteContext) -> Cost + Send + Sync>,
    solution_estimate_fn: Box<dyn Fn(&SolutionContext) -> Cost + Send + Sync>,
}

impl FeatureObjective for FleetUsageObjective {
    fn fitness(&self, solution: &InsertionContext) -> Cost {
        (self.solution_estimate_fn)(&solution.solution)
    }

    fn estimate(&self, move_ctx: &MoveContext<'_>) -> Cost {
        match move_ctx {
            MoveContext::Route { route_ctx, .. } => (self.route_estimate_fn)(route_ctx),
            _ => Cost::default(),
        }
    }
}