Skip to content

Commit

Permalink
Skills available with scheduling
Browse files Browse the repository at this point in the history
  • Loading branch information
fonsecadeline committed May 28, 2021
1 parent 2a9eee1 commit 37c7312
Show file tree
Hide file tree
Showing 9 changed files with 31 additions and 17 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
## [v1.8.0-dev] - Unreleased

### Added
- Support skills in periodic heuristic (`first_solution_strategy='periodic'`) [#194](https://github.com/Mapotempo/optimizer-api/pull/194)

### Changed

Expand Down
1 change: 1 addition & 0 deletions lib/heuristics/concerns/scheduling_data_initialisation.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ def generate_route_structure(vrp)
capacity = compute_capacities(vehicle[:capacities], true)
vrp.units.reject{ |unit| capacity.has_key?(unit[:id]) }.each{ |unit| capacity[unit[:id]] = 0.0 }
@candidate_routes[vehicle.original_id][vehicle.global_day_index] = {
vehicle: vehicle,
vehicle_id: vehicle.id,
global_day_index: vehicle.global_day_index,
tw_start: (vehicle.timewindow.start < 84600) ? vehicle.timewindow.start : vehicle.timewindow.start - vehicle.global_day_index * 86400,
Expand Down
9 changes: 8 additions & 1 deletion lib/heuristics/scheduling_heuristic.rb
Original file line number Diff line number Diff line change
Expand Up @@ -842,8 +842,15 @@ def compatible_days(service_id, day)
!@services_data[service_id][:raw].unavailable_days.include?(day)
end

def compatible_vehicle(service_id, route_data)
# WARNING : this does not consider vehicle alternative skills properly
# we would need to know which skill_set is required in order that all services on same vehicle are compatible
route_data[:vehicle].skills.any?{ |skill_set| (@services_data[service_id][:raw].skills - skill_set).empty? }
end

def service_compatible_with_route(service_id, route_data)
compatible_days(service_id, route_data[:global_day_index])
compatible_days(service_id, route_data[:global_day_index]) &&
compatible_vehicle(service_id, route_data)
end

def find_best_index(service_id, route_data, first_visit = true)
Expand Down
Binary file modified test/fixtures/add_missing_visits_candidate_routes.bindump
Binary file not shown.
Binary file not shown.
8 changes: 0 additions & 8 deletions test/lib/heuristics/scheduling_instance_validity_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -64,14 +64,6 @@ def test_reject_if_vehicle_distance
assert_includes OptimizerWrapper.config[:services][:ortools].inapplicable_solve?(TestHelper.create(problem)), :assert_no_vehicle_distance_if_heuristic
end

def test_reject_if_vehicle_skills
problem = VRP.scheduling
problem[:vehicles].first[:skills] = [['skill']]
problem[:services].first[:skills] = ['skill']

assert_includes OptimizerWrapper.config[:services][:ortools].inapplicable_solve?(TestHelper.create(problem)), :assert_no_skills_if_heuristic
end

def test_reject_if_vehicle_free_approach_return
problem = VRP.scheduling
problem[:vehicles].first[:free_approach] = true
Expand Down
14 changes: 14 additions & 0 deletions test/lib/heuristics/scheduling_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -553,6 +553,20 @@ def test_sticky_in_scheduling
refute_includes result[:routes].find{ |r| r[:activities].any?{ |stop| stop[:service_id] == 'service_6_1_1' } }[:vehicle_id], 'vehicle_0_'
end

def test_skills_in_scheduling
vrp = VRP.lat_lon_scheduling_two_vehicles
result = OptimizerWrapper.wrapper_vrp('ortools', { services: { vrp: [:ortools] }}, TestHelper.create(vrp), nil)
assigned_route = result[:routes].find{ |r| r[:activities].any?{ |stop| stop[:service_id] == 'service_6_1_1' } }
assert_includes assigned_route[:vehicle_id], 'vehicle_0_' # default result

vrp = VRP.lat_lon_scheduling_two_vehicles
vrp[:vehicles][1][:skills] = [[:compatible]]
vrp[:services].find{ |s| s[:id] == 'service_6' }[:skills] = [:compatible]
result = OptimizerWrapper.wrapper_vrp('ortools', { services: { vrp: [:ortools] }}, TestHelper.create(vrp), nil)
assigned_route = result[:routes].find{ |r| r[:activities].any?{ |stop| stop[:service_id] == 'service_6_1_1' } }
refute_includes assigned_route[:vehicle_id], 'vehicle_0_'
end

def test_with_activities
vrp = VRP.lat_lon_scheduling_two_vehicles
vrp[:configuration][:resolution][:minimize_days_worked] = true
Expand Down
2 changes: 1 addition & 1 deletion wrappers/ortools.rb
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ def solver_constraints
:assert_vehicles_objective,
:assert_vehicles_no_capacity_initial,
:assert_vehicles_no_alternative_skills,
:assert_vehicles_no_alternative_skills_with_periodic_heuristic,
:assert_zones_only_size_one_alternative,
:assert_only_empty_or_fill_quantities,
:assert_points_same_definition,
Expand All @@ -55,7 +56,6 @@ def solver_constraints
:assert_no_vehicle_overall_duration_if_heuristic,
:assert_no_vehicle_distance_if_heuristic,
:assert_possible_to_get_distances_if_maximum_ride_distance,
:assert_no_skills_if_heuristic,
:assert_no_vehicle_free_approach_or_return_if_heuristic,
:assert_no_vehicle_limit_if_heuristic,
:assert_no_same_point_day_if_no_heuristic,
Expand Down
13 changes: 6 additions & 7 deletions wrappers/wrapper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,12 @@ def assert_vehicles_no_capacity_initial(vrp)
end

def assert_vehicles_no_alternative_skills(vrp)
vrp.vehicles.empty? || vrp.vehicles.none?{ |vehicle|
!vehicle.skills || vehicle.skills.size > 1
}
vrp.vehicles.none?{ |vehicle| vehicle.skills.size > 1 }
end

def assert_vehicles_no_alternative_skills_with_periodic_heuristic(vrp)
!vrp.preprocessing_first_solution_strategy.include?('periodic') ||
vrp.vehicles.none?{ |vehicle| vehicle.skills.size > 1 }
end

def assert_no_direct_shipments(vrp)
Expand Down Expand Up @@ -295,10 +298,6 @@ def assert_possible_to_get_distances_if_maximum_ride_distance(vrp)
vrp.vehicles.none?(&:maximum_ride_distance) || (vrp.points.all?{ |point| point.location&.lat } || vrp.matrices.all?{ |matrix| matrix.distance && !matrix.distance.empty? })
end

def assert_no_skills_if_heuristic(vrp)
vrp.services.none?{ |service| !service.skills.empty? } || vrp.vehicles.none?{ |vehicle| !vehicle.skills.flatten.empty? } || vrp.preprocessing_first_solution_strategy.to_a.first != 'periodic' || !vrp.preprocessing_partitions.empty?
end

def assert_no_vehicle_free_approach_or_return_if_heuristic(vrp)
vrp.vehicles.none?{ |vehicle| vehicle.free_approach || vehicle.free_return } || vrp.preprocessing_first_solution_strategy.to_a.first != 'periodic'
end
Expand Down

0 comments on commit 37c7312

Please sign in to comment.