Skip to content

Commit

Permalink
Add more feature weights in rosomaxa
Browse files Browse the repository at this point in the history
  • Loading branch information
reinterpretcat committed Apr 18, 2024
1 parent 8617c67 commit 101c48b
Show file tree
Hide file tree
Showing 3 changed files with 108 additions and 55 deletions.
111 changes: 92 additions & 19 deletions vrp-core/src/construction/heuristics/metrics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,36 @@ pub fn get_max_load_variance(insertion_ctx: &InsertionContext) -> f64 {
get_variance(get_values_from_route_state(insertion_ctx, max_load_key).collect::<Vec<_>>().as_slice())
}

/// Gets max load mean in tours.
pub fn get_max_load_mean(insertion_ctx: &InsertionContext) -> f64 {
let max_load_key = if let Some(capacity_keys) = insertion_ctx.problem.extras.get_capacity_keys() {
capacity_keys.max_load
} else {
return 0.;
};

get_mean_iter(get_values_from_route_state(insertion_ctx, max_load_key))
}

/// Gets tours with max_load at least 0.9.
pub fn get_full_load_ratio(insertion_ctx: &InsertionContext) -> f64 {
let max_load_key = if let Some(capacity_keys) = insertion_ctx.problem.extras.get_capacity_keys() {
capacity_keys.max_load
} else {
return 0.;
};

let total = insertion_ctx.solution.routes.len();
if total == 0 {
return 0.;

Check failure on line 44 in vrp-core/src/construction/heuristics/metrics.rs

View workflow job for this annotation

GitHub Actions / test-build

unneeded `return` statement
} else {
let full_capacity =
get_values_from_route_state(insertion_ctx, max_load_key).filter(|&max_load| max_load > 0.9).count();

full_capacity as f64 / total as f64
}
}

/// Gets standard deviation of the number of customer per tour.
pub fn get_customers_deviation(insertion_ctx: &InsertionContext) -> f64 {
let values = insertion_ctx
Expand Down Expand Up @@ -111,7 +141,7 @@ pub fn get_average_distance_between_depot_customer_mean(insertion_ctx: &Insertio
pub fn get_longest_distance_between_depot_customer_mean(insertion_ctx: &InsertionContext) -> f64 {
let transport = insertion_ctx.problem.transport.as_ref();

get_mean_iter(insertion_ctx.solution.routes.iter().map(|route_ctx| {
get_mean_iter(insertion_ctx.solution.routes.iter().filter_map(|route_ctx| {
let depot = route_ctx.route().tour.start().expect("empty tour");

route_ctx
Expand All @@ -128,10 +158,53 @@ pub fn get_longest_distance_between_depot_customer_mean(insertion_ctx: &Insertio
)
})
.max_by(compare_floats_refs)
.unwrap_or(0.)
}))
}

/// Gets the distance between a first job and the depot (mean).
pub fn get_first_distance_customer_mean(insertion_ctx: &InsertionContext) -> f64 {
let transport = insertion_ctx.problem.transport.as_ref();

get_mean_iter(insertion_ctx.solution.routes.iter().filter_map(|route_ctx| {
let route = route_ctx.route();
route.tour.get(1).zip(route.tour.start()).map(|(activity, depot)| {
transport.distance(
route,
depot.place.location,
activity.place.location,
TravelTime::Departure(depot.schedule.departure),
)
})
}))
}

/// Gets the distance between a last job and the depot (mean).
pub fn get_last_distance_customer_mean(insertion_ctx: &InsertionContext) -> f64 {
let transport = insertion_ctx.problem.transport.as_ref();

let distances = insertion_ctx.solution.routes.iter().filter_map(|route_ctx| {
let tour = &route_ctx.route().tour;
if tour.total() < 2 {
return None;
}

// NOTE if it is open VRP, we get a distance between two last customers
let last_idx = tour.total() - 1;
let before_last_idx = last_idx - 1;

tour.get(before_last_idx).zip(tour.get(last_idx)).map(|(activity, depot)| {
transport.distance(
route_ctx.route(),
activity.place.location,
depot.place.location,
TravelTime::Departure(depot.schedule.departure),
)
})
});

get_mean_iter(distances)
}

/// Gets average distance between routes using medoids (S4).
pub fn get_distance_gravity_mean(insertion_ctx: &InsertionContext) -> f64 {
let transport = insertion_ctx.problem.transport.as_ref();
Expand Down Expand Up @@ -161,23 +234,6 @@ pub fn get_distance_gravity_mean(insertion_ctx: &InsertionContext) -> f64 {
}
}

/// Gets medoid location of given route context.
pub fn get_medoid(route_ctx: &RouteContext, transport: &(dyn TransportCost + Send + Sync)) -> Option<usize> {
let profile = &route_ctx.route().actor.vehicle.profile;
let locations = route_ctx.route().tour.all_activities().map(|activity| activity.place.location).collect::<Vec<_>>();
locations
.iter()
.map(|outer_loc| {
let sum = locations
.iter()
.map(|inner_loc| transport.distance_approx(profile, *outer_loc, *inner_loc))
.sum::<f64>();
(sum, *outer_loc)
})
.min_by(|(sum_a, _), (sum_b, _)| compare_floats(*sum_a, *sum_b))
.map(|(_, location)| location)
}

/// A type which represents routes grouped by their proximity.
pub type RouteProximityGroup = Option<Vec<Vec<(usize, Option<f64>)>>>;

Expand Down Expand Up @@ -239,3 +295,20 @@ fn get_values_from_route_state(
.iter()
.map(move |route_ctx| route_ctx.state().get_route_state::<f64>(state_key).cloned().unwrap_or(0.))
}

/// Gets medoid location of given route context.
fn get_medoid(route_ctx: &RouteContext, transport: &(dyn TransportCost + Send + Sync)) -> Option<usize> {
let profile = &route_ctx.route().actor.vehicle.profile;
let locations = route_ctx.route().tour.all_activities().map(|activity| activity.place.location).collect::<Vec<_>>();
locations
.iter()
.map(|outer_loc| {
let sum = locations
.iter()
.map(|inner_loc| transport.distance_approx(profile, *outer_loc, *inner_loc))
.sum::<f64>();
(sum, *outer_loc)
})
.min_by(|(sum_a, _), (sum_b, _)| compare_floats(*sum_a, *sum_b))
.map(|(_, location)| location)
}
21 changes: 16 additions & 5 deletions vrp-core/src/solver/heuristic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -186,19 +186,30 @@ pub fn create_elitism_population(

impl RosomaxaWeighted for InsertionContext {
fn init_weights(&mut self) {
// built a feature vector which is used to classify solution in population
let weights = vec![
// load related features
get_max_load_variance(self),
get_max_load_mean(self),
get_full_load_ratio(self),
// time related features
get_duration_mean(self),
get_distance_mean(self),
get_waiting_mean(self),
// distance related features
get_distance_mean(self),
get_longest_distance_between_customers_mean(self),
get_average_distance_between_depot_customer_mean(self),
get_distance_gravity_mean(self),
get_customers_deviation(self),
get_first_distance_customer_mean(self),
get_last_distance_customer_mean(self),
// depot related features
get_average_distance_between_depot_customer_mean(self),
get_longest_distance_between_depot_customer_mean(self),
self.get_total_cost().unwrap_or_default(),
self.solution.routes.len() as f64,
// tour related features
get_customers_deviation(self),
// default objective related
self.solution.unassigned.len() as f64,
self.solution.routes.len() as f64,
self.get_total_cost().unwrap_or_default(),
];

let heuristic_keys = get_heuristic_keys(self);
Expand Down
31 changes: 0 additions & 31 deletions vrp-core/tests/helpers/construction/features.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,34 +27,3 @@ pub fn create_simple_dynamic_demand(size: i32) -> Demand<SingleDimLoad> {
}
}
}
/*
pub fn create_goal_ctx_with_features(features: Vec<Feature>, feature_map: Vec<Vec<&str>>) -> GoalContext {
let feature_map: Vec<Vec<String>> =
feature_map.iter().map(|names| names.iter().map(|name| name.to_string()).collect()).collect();
let goal = Goal::no_alternatives(feature_map.clone(), feature_map);
GoalContext::new(features.as_slice(), goal).unwrap()
}
pub fn create_goal_ctx_with_feature(feature: Feature) -> GoalContext {
create_goal_ctx_with_features(
vec![feature.clone()],
if feature.objective.is_some() { vec![vec![feature.name.as_str()]] } else { vec![] },
)
}
pub fn create_goal_ctx_with_transport() -> GoalContext {
create_minimize_transport_costs_feature(
"transport",
TestTransportCost::new_shared(),
TestActivityCost::new_shared(),
1,
)
.map(create_goal_ctx_with_feature)
.unwrap()
}
pub fn create_goal_ctx_with_simple_capacity() -> GoalContext {
create_capacity_limit_feature::<SingleDimLoad>("capacity", 2).map(create_goal_ctx_with_feature).unwrap()
}*/

0 comments on commit 101c48b

Please sign in to comment.