1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 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
 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
100
101
102
103
104
105
106
107
108
109
110
111
112
use crate::core::models::common::ValueDimension;
use crate::format::problem::reader::{ApiProblem, ProblemProperties};
use crate::format::problem::BalanceOptions;
use crate::format::problem::Objective::*;
use std::sync::Arc;
use vrp_core::construction::constraints::{ConstraintPipeline, FleetUsageConstraintModule};
use vrp_core::models::common::{MultiDimLoad, SingleDimLoad};
use vrp_core::models::problem::{ObjectiveCost, TargetConstraint, TargetObjective};
use vrp_core::solver::objectives::*;

pub fn create_objective(
    api_problem: &ApiProblem,
    constraint: &mut ConstraintPipeline,
    props: &ProblemProperties,
) -> Arc<ObjectiveCost> {
    Arc::new(if let Some(objectives) = &api_problem.objectives {
        let mut map_objectives = |objectives: &Vec<_>| {
            let mut core_objectives: Vec<TargetObjective> = vec![];
            objectives.iter().for_each(|objective| match objective {
                MinimizeCost => core_objectives.push(Box::new(TotalTransportCost::default())),
                MinimizeTours => {
                    constraint.add_module(Box::new(FleetUsageConstraintModule::new_minimized()));
                    core_objectives.push(Box::new(TotalRoutes::new_minimized()))
                }
                MaximizeTours => {
                    constraint.add_module(Box::new(FleetUsageConstraintModule::new_maximized()));
                    core_objectives.push(Box::new(TotalRoutes::new_maximized()))
                }
                MinimizeUnassignedJobs { breaks } => {
                    if let Some(breaks) = *breaks {
                        core_objectives.push(Box::new(TotalUnassignedJobs::new(Arc::new(move |_, job, _| {
                            job.dimens().get_value::<String>("type").map_or(1., |job_type| {
                                if job_type == "break" {
                                    breaks
                                } else {
                                    1.
                                }
                            })
                        }))))
                    } else {
                        core_objectives.push(Box::new(TotalUnassignedJobs::default()))
                    }
                }
                BalanceMaxLoad { options } => {
                    let (module, objective) = get_load_balance(props, options);
                    constraint.add_module(module);
                    core_objectives.push(objective);
                }
                BalanceActivities { options } => {
                    let (threshold, tolerance) = unwrap_options(options);
                    let (module, objective) = WorkBalance::new_activity_balanced(threshold, tolerance);
                    constraint.add_module(module);
                    core_objectives.push(objective);
                }
                BalanceDistance { options } => {
                    let (threshold, tolerance) = unwrap_options(options);
                    let (module, objective) = WorkBalance::new_distance_balanced(threshold, tolerance);
                    constraint.add_module(module);
                    core_objectives.push(objective);
                }
                BalanceDuration { options } => {
                    let (threshold, tolerance) = unwrap_options(options);
                    let (module, objective) = WorkBalance::new_duration_balanced(threshold, tolerance);
                    constraint.add_module(module);
                    core_objectives.push(objective);
                }
            });
            core_objectives
        };

        let primary_objectives = map_objectives(&objectives.primary);
        let secondary_objectives = map_objectives(&objectives.secondary.clone().unwrap_or_else(Vec::new));

        ObjectiveCost::new(primary_objectives, secondary_objectives)
    } else {
        constraint.add_module(Box::new(FleetUsageConstraintModule::new_minimized()));
        ObjectiveCost::default()
    })
}

fn unwrap_options(options: &Option<BalanceOptions>) -> (Option<f64>, Option<f64>) {
    (options.as_ref().and_then(|o| o.threshold), options.as_ref().and_then(|o| o.tolerance))
}

fn get_load_balance(
    props: &ProblemProperties,
    options: &Option<BalanceOptions>,
) -> (TargetConstraint, TargetObjective) {
    let (threshold, tolerance) = unwrap_options(options);
    if props.has_multi_dimen_capacity {
        WorkBalance::new_load_balanced::<MultiDimLoad>(
            threshold,
            tolerance,
            Arc::new(|loaded, total| {
                let mut max_ratio = 0_f64;

                for (idx, value) in total.load.iter().enumerate() {
                    let ratio = loaded.load[idx] as f64 / *value as f64;
                    max_ratio = max_ratio.max(ratio);
                }

                max_ratio
            }),
        )
    } else {
        WorkBalance::new_load_balanced::<SingleDimLoad>(
            threshold,
            tolerance,
            Arc::new(|loaded, capacity| loaded.value as f64 / capacity.value as f64),
        )
    }
}