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
#[cfg(test)]
#[path = "../../tests/unit/validation/objectives_test.rs"]
mod objectives_test;
use super::*;
use crate::format::problem::Objective::*;
fn check_e1600_empty_objective(objectives: &[&Objective]) -> Result<(), FormatError> {
if objectives.is_empty() {
Err(FormatError::new(
"E1600".to_string(),
"an empty objective specified".to_string(),
"remove objectives property completely to use default".to_string(),
))
} else {
Ok(())
}
}
fn check_e1601_duplicate_objectives(objectives: &[&Objective]) -> Result<(), FormatError> {
let mut duplicates = objectives
.iter()
.fold(HashMap::new(), |mut acc, objective| {
match objective {
MinimizeCost => acc.entry("minimize-cost"),
MinimizeTours => acc.entry("minimize-tours"),
MaximizeTours => acc.entry("maximize-tours"),
MinimizeUnassignedJobs { .. } => acc.entry("minimize-unassigned"),
BalanceMaxLoad { .. } => acc.entry("balance-max-load"),
BalanceActivities { .. } => acc.entry("balance-activities"),
BalanceDistance { .. } => acc.entry("balance-distance"),
BalanceDuration { .. } => acc.entry("balance-duration"),
}
.and_modify(|count| *count += 1)
.or_insert(1_usize);
acc
})
.iter()
.filter_map(|(name, count)| if *count > 1 { Some((*name).to_string()) } else { None })
.collect::<Vec<_>>();
duplicates.sort();
if duplicates.is_empty() {
Ok(())
} else {
Err(FormatError::new(
"E1601".to_string(),
"duplicate objective specified".to_string(),
"remove duplicate objectives".to_string(),
))
}
}
fn check_e1602_no_cost_value_objective(objectives: &[&Objective]) -> Result<(), FormatError> {
let min_costs = objectives.iter().filter(|objective| matches!(objective, MinimizeCost)).count();
if min_costs == 0 {
Err(FormatError::new(
"E1602".to_string(),
"missing cost objective".to_string(),
"specify 'minimize-cost' objective".to_string(),
))
} else {
Ok(())
}
}
fn get_objectives<'a>(ctx: &'a ValidationContext) -> Option<Vec<&'a Objective>> {
ctx.problem.objectives.as_ref().map(|objectives| {
Some(&objectives.primary)
.iter()
.chain(objectives.secondary.as_ref().iter())
.flat_map(|objectives| objectives.iter())
.collect()
})
}
pub fn validate_objectives(ctx: &ValidationContext) -> Result<(), Vec<FormatError>> {
if let Some(objectives) = get_objectives(ctx) {
combine_error_results(&[
check_e1600_empty_objective(&objectives),
check_e1601_duplicate_objectives(&objectives),
check_e1602_no_cost_value_objective(&objectives),
])
} else {
Ok(())
}
}