Skip to content

Commit

Permalink
Merge pull request #264 from senhalil/dev
Browse files Browse the repository at this point in the history
Fix the issues with the merged PRs
  • Loading branch information
fonsecadeline authored Aug 11, 2021
2 parents b31edab + 8728306 commit b87ec0f
Show file tree
Hide file tree
Showing 4 changed files with 37 additions and 15 deletions.
35 changes: 23 additions & 12 deletions models/concerns/validate_data.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
# Expands provided data
module ValidateData
extend ActiveSupport::Concern
POSITION_RELATIONS = %i[order sequence shipment]
POSITION_RELATIONS = %i[order sequence shipment].freeze
def check_consistency(hash)
hash[:services] ||= []
hash[:vehicles] ||= []
Expand All @@ -30,7 +30,7 @@ def check_consistency(hash)
ensure_no_conflicting_skills

configuration = @hash[:configuration]
schedule = configuration[:schedule] if configuration
schedule = configuration && configuration[:schedule]
periodic_heuristic = schedule &&
configuration[:preprocessing] &&
configuration[:preprocessing][:first_solution_strategy].to_a.include?('periodic')
Expand All @@ -40,7 +40,7 @@ def check_consistency(hash)
check_services(schedule)

check_routes(periodic_heuristic)
check_configuration(configuration, periodic_heuristic) if configuration
check_configuration(configuration, periodic_heuristic)
end

def ensure_no_conflicting_skills
Expand Down Expand Up @@ -413,13 +413,17 @@ def check_routes(periodic_heuristic)
end

def check_configuration(configuration, periodic_heuristic)
check_clustering_parameters(configuration) if configuration[:preprocessing]
check_schedule_consistency(configuration[:schedule]) if configuration[:schedule]
check_periodic_consistency(configuration) if periodic_heuristic
check_geometry_parameters(configuration) if configuration[:restitution]
return unless configuration

check_clustering_parameters(configuration)
check_schedule_consistency(configuration[:schedule])
check_periodic_consistency(configuration, periodic_heuristic)
check_geometry_parameters(configuration)
end

def check_clustering_parameters(configuration)
return unless configuration && configuration[:preprocessing]

if @hash[:relations].any?{ |relation| relation[:type].to_sym == :vehicle_trips }
if configuration[:preprocessing][:partitions]&.any?
raise OptimizerWrapper::UnsupportedProblemError.new(
Expand All @@ -434,10 +438,10 @@ def check_clustering_parameters(configuration)

if @hash[:services].any?{ |s|
min_lapse = s[:minimum_lapse]&.floor || 1
max_lapse = s[:maximum_lapse]&.ceil || @hash[:configuration][:schedule][:range_indices][:end]
max_lapse = s[:maximum_lapse]&.ceil || configuration[:schedule][:range_indices][:end]

s[:visits_number].to_i > 1 && (
@hash[:configuration][:schedule][:range_indices][:end] <= 6 ||
configuration[:schedule][:range_indices][:end] <= 6 ||
(min_lapse..max_lapse).none?{ |intermediate_lapse| (intermediate_lapse % 7).zero? }
)
}
Expand All @@ -450,6 +454,8 @@ def check_clustering_parameters(configuration)
end

def check_schedule_consistency(schedule)
return unless schedule

if schedule[:range_indices][:start] > 6
raise OptimizerWrapper::DiscordantProblemError.new('Api does not support schedule start index bigger than 6')
# TODO : allow start bigger than 6 and make code consistent with this
Expand All @@ -460,7 +466,9 @@ def check_schedule_consistency(schedule)
raise OptimizerWrapper::DiscordantProblemError.new('Schedule start index should be less than or equal to end')
end

def check_periodic_consistency(configuration)
def check_periodic_consistency(configuration, periodic_heuristic)
return unless configuration && periodic_heuristic

if @hash[:relations].any?{ |relation| relation[:type] == :vehicle_group_duration_on_months } &&
(!configuration[:schedule] || configuration[:schedule][:range_indice])
raise OptimizerWrapper::DiscordantProblemError.new(
Expand All @@ -481,8 +489,11 @@ def check_periodic_consistency(configuration)
end

def check_geometry_parameters(configuration)
return unless configuration[:restitution][:geometry].any? &&
!@hash[:points].all?{ |pt| pt[:location] }
return unless configuration &&
configuration[:restitution] &&
configuration[:restitution][:geometry]&.any?

return if @hash[:points].all?{ |pt| pt[:location] }

raise OptimizerWrapper::DiscordantProblemError.new('Geometry is not available if locations are not defined')
end
Expand Down
3 changes: 2 additions & 1 deletion test/api/v01/with_solver_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ def app
def test_deleted_job
# using ORtools to make sure that optimization takes enough time to be cut before ending
asynchronously start_worker: true do
@job_id = submit_vrp api_key: 'ortools', vrp: VRP.lat_lon
vrp = VRP.lat_lon.deep_merge!({ configuration: { restitution: { intermediate_solutions: true }}})
@job_id = submit_vrp api_key: 'ortools', vrp: vrp
response = wait_avancement_match @job_id, /run optimization, iterations [0-9]+/, api_key: 'ortools'
refute_empty response['solutions'].to_a, "Solution is missing from the response body: #{response}"
response = delete_job @job_id, api_key: 'ortools'
Expand Down
2 changes: 1 addition & 1 deletion test/fixtures/ortools_open_timewindows.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"vrp":{"units":[],"points":[{"id":"p0","location":{"lat":45.854059,"lon":5.074476}},{"id":"p1","location":{"lat":45.852509,"lon":5.060418}},{"id":"p2","location":{"lat":45.919527,"lon":5.017039}},{"id":"p3","location":{"lat":45.850708,"lon":5.04877}},{"id":"p4","location":{"lat":45.849696,"lon":5.058992}},{"id":"p5","location":{"lat":45.829706,"lon":4.998303}},{"id":"p6","location":{"lat":45.838422,"lon":5.005505}},{"id":"p7","location":{"lat":45.830337,"lon":4.976387}},{"id":"p8","location":{"lat":45.830018,"lon":4.976581}},{"id":"p9","location":{"lat":45.829945,"lon":4.98535}},{"id":"p10","location":{"lat":45.822433,"lon":4.950296}},{"id":"p11","location":{"lat":45.866832,"lon":4.917787}},{"id":"p12","location":{"lat":45.822702,"lon":4.947145}},{"id":"p13","location":{"lat":45.82636,"lon":4.944744}},{"id":"p14","location":{"lat":45.827191,"lon":4.962537}},{"id":"p15","location":{"lat":45.869818,"lon":4.915232}},{"id":"p16","location":{"lat":45.826122,"lon":4.963634}},{"id":"p17","location":{"lat":45.824298,"lon":4.946324}},{"id":"p18","location":{"lat":45.825704,"lon":4.953589}},{"id":"p19","location":{"lat":46.335087,"lon":5.121366}},{"id":"p20","location":{"lat":46.294191,"lon":5.158446}},{"id":"p21","location":{"lat":46.294191,"lon":5.158446}}],"rests":[{"id":"r33097309","timewindows":[{"start":28800,"end":39600}],"duration":1200.0}],"vehicles":[{"id":"v72769","router_mode":"car","router_dimension":"time","speed_multiplier":1.0,"area":[],"speed_multiplier_area":[],"timewindow":{"start":18000,"end":54000},"start_point_id":"p20","end_point_id":"p21","cost_fixed":0.0,"cost_distance_multiplier":0.0,"cost_time_multiplier":1.0,"cost_waiting_time_multiplier":0.0,"cost_late_multiplier":0.3,"force_start":false,"rest_ids":["r33097309"],"capacities":[],"motorway":true,"toll":true,"max_walk_distance":750,"approach":"unrestricted"}],"services":[{"id":"s33097310","type":"service","sticky_vehicle_ids":["v72769"],"activity":{"point_id":"p0","timewindows":[{"start":null,"end":28800}],"duration":360.0,"late_multiplier":null},"quantities":[]},{"id":"s33097311","type":"service","sticky_vehicle_ids":["v72769"],"activity":{"point_id":"p1","timewindows":[{"start":32400,"end":36000}],"duration":360.0,"late_multiplier":null},"quantities":[]},{"id":"s33097312","type":"service","sticky_vehicle_ids":["v72769"],"activity":{"point_id":"p2","timewindows":[{"start":43200,"end":50400}],"duration":360.0,"late_multiplier":null},"quantities":[]},{"id":"s33097313","type":"service","sticky_vehicle_ids":["v72769"],"activity":{"point_id":"p3","timewindows":[{"start":32400,"end":39600}],"duration":360.0,"late_multiplier":null},"quantities":[]},{"id":"s33097314","type":"service","sticky_vehicle_ids":["v72769"],"activity":{"point_id":"p4","timewindows":[{"start":24300,"end":25200}],"duration":360.0,"late_multiplier":null},"quantities":[]},{"id":"s33097315","type":"service","sticky_vehicle_ids":["v72769"],"activity":{"point_id":"p5","timewindows":[{"start":36000,"end":43200}],"duration":360.0,"late_multiplier":null},"quantities":[]},{"id":"s33097316","type":"service","sticky_vehicle_ids":["v72769"],"activity":{"point_id":"p6","timewindows":[{"start":null,"end":36000}],"duration":360.0,"late_multiplier":null},"quantities":[]},{"id":"s33097317","type":"service","sticky_vehicle_ids":["v72769"],"activity":{"point_id":"p7","timewindows":[{"start":46800,"end":50400}],"duration":360.0,"late_multiplier":null},"quantities":[]},{"id":"s33097318","type":"service","sticky_vehicle_ids":["v72769"],"activity":{"point_id":"p8","timewindows":[{"start":36000,"end":43200}],"duration":360.0,"late_multiplier":null},"quantities":[]},{"id":"s33097319","type":"service","sticky_vehicle_ids":["v72769"],"activity":{"point_id":"p9","timewindows":[{"start":32400,"end":43200}],"duration":360.0,"late_multiplier":null},"quantities":[]},{"id":"s33097320","type":"service","sticky_vehicle_ids":["v72769"],"activity":{"point_id":"p10","timewindows":[{"start":null,"end":25200}],"duration":360.0,"late_multiplier":null},"quantities":[]},{"id":"s33097321","type":"service","sticky_vehicle_ids":["v72769"],"activity":{"point_id":"p11","timewindows":[{"start":null,"end":25200}],"duration":360.0,"late_multiplier":null},"quantities":[]},{"id":"s33097322","type":"service","sticky_vehicle_ids":["v72769"],"activity":{"point_id":"p12","timewindows":[{"start":43200,"end":50400}],"duration":360.0,"late_multiplier":null},"quantities":[]},{"id":"s33097323","type":"service","sticky_vehicle_ids":["v72769"],"activity":{"point_id":"p13","timewindows":[{"start":43200,"end":45000}],"duration":360.0,"late_multiplier":null},"quantities":[]},{"id":"s33097324","type":"service","sticky_vehicle_ids":["v72769"],"activity":{"point_id":"p14","timewindows":[{"start":46800,"end":50400}],"duration":360.0,"late_multiplier":null},"quantities":[]},{"id":"s33097325","type":"service","sticky_vehicle_ids":["v72769"],"activity":{"point_id":"p15","timewindows":[{"start":null,"end":25200},{"start":52200,"end":null}],"duration":360.0,"late_multiplier":null},"quantities":[]},{"id":"s33097326","type":"service","sticky_vehicle_ids":["v72769"],"activity":{"point_id":"p16","timewindows":[{"start":43200,"end":46800}],"duration":360.0,"late_multiplier":null},"quantities":[]},{"id":"s33097327","type":"service","sticky_vehicle_ids":["v72769"],"activity":{"point_id":"p17","timewindows":[{"start":43200,"end":50400}],"duration":360.0,"late_multiplier":null},"quantities":[]},{"id":"s33097328","type":"service","sticky_vehicle_ids":["v72769"],"activity":{"point_id":"p18","timewindows":[{"start":38700,"end":40500}],"duration":360.0,"late_multiplier":null},"quantities":[]},{"id":"s33097329","type":"service","sticky_vehicle_ids":["v72769"],"activity":{"point_id":"p19","timewindows":[{"start":43200,"end":54000}],"duration":360.0,"late_multiplier":null},"quantities":[]}],"configuration":{"preprocessing":{"cluster_threshold":0.0,"prefer_short_segment":true},"resolution":{"duration":300000,"iterations_without_improvment":100,"initial_time_out":3000,"time_out_multiplier":2}}}}
{"vrp":{"units":[],"points":[{"id":"p0","location":{"lat":45.854059,"lon":5.074476}},{"id":"p1","location":{"lat":45.852509,"lon":5.060418}},{"id":"p2","location":{"lat":45.919527,"lon":5.017039}},{"id":"p3","location":{"lat":45.850708,"lon":5.04877}},{"id":"p4","location":{"lat":45.849696,"lon":5.058992}},{"id":"p5","location":{"lat":45.829706,"lon":4.998303}},{"id":"p6","location":{"lat":45.838422,"lon":5.005505}},{"id":"p7","location":{"lat":45.830337,"lon":4.976387}},{"id":"p8","location":{"lat":45.830018,"lon":4.976581}},{"id":"p9","location":{"lat":45.829945,"lon":4.98535}},{"id":"p10","location":{"lat":45.822433,"lon":4.950296}},{"id":"p11","location":{"lat":45.866832,"lon":4.917787}},{"id":"p12","location":{"lat":45.822702,"lon":4.947145}},{"id":"p13","location":{"lat":45.82636,"lon":4.944744}},{"id":"p14","location":{"lat":45.827191,"lon":4.962537}},{"id":"p15","location":{"lat":45.869818,"lon":4.915232}},{"id":"p16","location":{"lat":45.826122,"lon":4.963634}},{"id":"p17","location":{"lat":45.824298,"lon":4.946324}},{"id":"p18","location":{"lat":45.825704,"lon":4.953589}},{"id":"p19","location":{"lat":46.335087,"lon":5.121366}},{"id":"p20","location":{"lat":46.294191,"lon":5.158446}},{"id":"p21","location":{"lat":46.294191,"lon":5.158446}}],"rests":[{"id":"r33097309","timewindows":[{"start":28800,"end":39600}],"duration":1200.0}],"vehicles":[{"id":"v72769","router_mode":"car","router_dimension":"time","speed_multiplier":1.0,"area":[],"speed_multiplier_area":[],"timewindow":{"start":18000,"end":54000},"start_point_id":"p20","end_point_id":"p21","cost_fixed":0.0,"cost_distance_multiplier":0.0,"cost_time_multiplier":1.0,"cost_waiting_time_multiplier":0.0,"cost_late_multiplier":0.3,"force_start":false,"rest_ids":["r33097309"],"capacities":[],"motorway":true,"toll":true,"max_walk_distance":750,"approach":"unrestricted"}],"services":[{"id":"s33097310","type":"service","sticky_vehicle_ids":["v72769"],"activity":{"point_id":"p0","timewindows":[{"start":null,"end":28800}],"duration":360.0,"late_multiplier":null},"quantities":[]},{"id":"s33097311","type":"service","sticky_vehicle_ids":["v72769"],"activity":{"point_id":"p1","timewindows":[{"start":32400,"end":36000}],"duration":360.0,"late_multiplier":null},"quantities":[]},{"id":"s33097312","type":"service","sticky_vehicle_ids":["v72769"],"activity":{"point_id":"p2","timewindows":[{"start":43200,"end":50400}],"duration":360.0,"late_multiplier":null},"quantities":[]},{"id":"s33097313","type":"service","sticky_vehicle_ids":["v72769"],"activity":{"point_id":"p3","timewindows":[{"start":32400,"end":39600}],"duration":360.0,"late_multiplier":null},"quantities":[]},{"id":"s33097314","type":"service","sticky_vehicle_ids":["v72769"],"activity":{"point_id":"p4","timewindows":[{"start":24300,"end":25200}],"duration":360.0,"late_multiplier":null},"quantities":[]},{"id":"s33097315","type":"service","sticky_vehicle_ids":["v72769"],"activity":{"point_id":"p5","timewindows":[{"start":36000,"end":43200}],"duration":360.0,"late_multiplier":null},"quantities":[]},{"id":"s33097316","type":"service","sticky_vehicle_ids":["v72769"],"activity":{"point_id":"p6","timewindows":[{"start":null,"end":36000}],"duration":360.0,"late_multiplier":null},"quantities":[]},{"id":"s33097317","type":"service","sticky_vehicle_ids":["v72769"],"activity":{"point_id":"p7","timewindows":[{"start":46800,"end":50400}],"duration":360.0,"late_multiplier":null},"quantities":[]},{"id":"s33097318","type":"service","sticky_vehicle_ids":["v72769"],"activity":{"point_id":"p8","timewindows":[{"start":36000,"end":43200}],"duration":360.0,"late_multiplier":null},"quantities":[]},{"id":"s33097319","type":"service","sticky_vehicle_ids":["v72769"],"activity":{"point_id":"p9","timewindows":[{"start":32400,"end":43200}],"duration":360.0,"late_multiplier":null},"quantities":[]},{"id":"s33097320","type":"service","sticky_vehicle_ids":["v72769"],"activity":{"point_id":"p10","timewindows":[{"start":null,"end":25200}],"duration":360.0,"late_multiplier":null},"quantities":[]},{"id":"s33097321","type":"service","sticky_vehicle_ids":["v72769"],"activity":{"point_id":"p11","timewindows":[{"start":null,"end":25200}],"duration":360.0,"late_multiplier":null},"quantities":[]},{"id":"s33097322","type":"service","sticky_vehicle_ids":["v72769"],"activity":{"point_id":"p12","timewindows":[{"start":43200,"end":50400}],"duration":360.0,"late_multiplier":null},"quantities":[]},{"id":"s33097323","type":"service","sticky_vehicle_ids":["v72769"],"activity":{"point_id":"p13","timewindows":[{"start":43200,"end":45000}],"duration":360.0,"late_multiplier":null},"quantities":[]},{"id":"s33097324","type":"service","sticky_vehicle_ids":["v72769"],"activity":{"point_id":"p14","timewindows":[{"start":46800,"end":50400}],"duration":360.0,"late_multiplier":null},"quantities":[]},{"id":"s33097325","type":"service","sticky_vehicle_ids":["v72769"],"activity":{"point_id":"p15","timewindows":[{"start":null,"end":25200},{"start":52200,"end":null}],"duration":360.0,"late_multiplier":null},"quantities":[]},{"id":"s33097326","type":"service","sticky_vehicle_ids":["v72769"],"activity":{"point_id":"p16","timewindows":[{"start":43200,"end":46800}],"duration":360.0,"late_multiplier":null},"quantities":[]},{"id":"s33097327","type":"service","sticky_vehicle_ids":["v72769"],"activity":{"point_id":"p17","timewindows":[{"start":43200,"end":50400}],"duration":360.0,"late_multiplier":null},"quantities":[]},{"id":"s33097328","type":"service","sticky_vehicle_ids":["v72769"],"activity":{"point_id":"p18","timewindows":[{"start":38700,"end":40500}],"duration":360.0,"late_multiplier":null},"quantities":[]},{"id":"s33097329","type":"service","sticky_vehicle_ids":["v72769"],"activity":{"point_id":"p19","timewindows":[{"start":43200,"end":54000}],"duration":360.0,"late_multiplier":null},"quantities":[]}],"configuration":{"preprocessing":{"cluster_threshold":0.0,"prefer_short_segment":true},"resolution":{"duration":300000,"iterations_without_improvment":100,"initial_time_out":8000,"time_out_multiplier":2},"restitution":{"intermediate_solutions":false}}}}
12 changes: 11 additions & 1 deletion test/real_cases_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -272,18 +272,28 @@ def test_ortools_one_route_with_single_mtws

# Haute-Savoie - A single route with a visit with 2 open timewindows (0 ; x] [y ; ∞)
def test_ortools_open_timewindows
# TODO: the timing of this test can be decreased (by 10x) when optimizer-api/-/issues/841 is fixed
vrp = TestHelper.load_vrp(self)
check_vrp_services_size = vrp.services.size
result = OptimizerWrapper.wrapper_vrp('ortools', { services: { vrp: [:ortools] }}, vrp, nil)
assert result
# Check activities
service_with_endless_tw = vrp.services[15]
begin_time_of_service_with_endless_tw = result[:routes][0][:activities].find{ |activity|
activity[:service_id] == service_with_endless_tw.id # service_with_endless_tw
}[:begin_time]
endless_tw_respected = service_with_endless_tw.activity.timewindows.one?{ |tw|
begin_time_of_service_with_endless_tw >= (tw.start || 0) &&
begin_time_of_service_with_endless_tw <= (tw.end || Float::INFINITY)
}
assert endless_tw_respected, 'Service does not respect endless TW'
assert_equal check_vrp_services_size, (result[:routes].sum{ |r| r[:activities].count{ |a| a[:service_id] } })

# Check total travel time
assert result[:routes].sum{ |r| r[:total_travel_time] } <= 13225, "Too long travel time: #{result[:routes].sum{ |r| r[:total_travel_time] }}"

# Check elapsed time
assert result[:elapsed] < 5000, "Too long elapsed time: #{result[:elapsed]}"
assert result[:elapsed] < 15000, "Too long elapsed time: #{result[:elapsed]}"
end

# Nantes - A single route with an order defining the most part of the route
Expand Down

0 comments on commit b87ec0f

Please sign in to comment.