@@ -24,7 +24,7 @@ def solver_constraints
2424 :assert_no_ride_constraint ,
2525 :assert_no_service_duration_modifiers ,
2626 :assert_vehicles_no_alternative_skills ,
27- :assert_vehicles_no_force_start ,
27+ :assert_vehicles_no_force_start , # Use shift_preference instead
2828 :assert_vehicles_no_initial_load ,
2929 :assert_vehicles_no_late_multiplier ,
3030 :assert_vehicles_no_overload_multiplier ,
@@ -173,7 +173,7 @@ def read_depot_end(vrp, vehicle)
173173 end
174174
175175 def read_reload_depot_trip ( vrp , vehicle , reload_depot_index )
176- reload_depot = @reload_depots [ reload_depot_index ]
176+ reload_depot = @reload_depots [ @depots . size - reload_depot_index ]
177177 return nil if reload_depot . nil?
178178
179179 route_data = compute_route_data ( vrp , vehicle , reload_depot . point )
@@ -224,6 +224,11 @@ def pyvrp_problem(vrp)
224224
225225 # Skills can be considered as capacities
226226 @skills_index_hash = { }
227+
228+ # to keep the client and depot indices consistent, the depots should be built before the clients and the matrices
229+ @point_hash = vrp . points . index_by ( &:id )
230+ depots = build_depots ( vrp )
231+
227232 vrp . vehicles . map ( &:skills ) . flatten . uniq . each_with_index { |skill , index | @skills_index_hash [ skill ] = index }
228233 used_matrices = vrp . vehicles . map ( &:matrix_id ) . uniq
229234 matrices = used_matrices . map { |id | vrp . matrices . find { |m | m . id == id } }
@@ -233,9 +238,6 @@ def pyvrp_problem(vrp)
233238
234239 distance_matrices = duration_matrices if distance_matrices . empty?
235240
236- # to keep the client indices consistent, the depots should be built before the clients
237- depots = build_depots ( vrp )
238-
239241 @reload_depot_index_hash = { }
240242 vrp . reload_depots . each_with_index { |depot , index | @reload_depot_index_hash [ depot . id ] = depots . size + index }
241243 clients , groups = build_clients_and_groups ( vrp )
@@ -253,11 +255,7 @@ def pyvrp_problem(vrp)
253255 end
254256
255257 def expand_matrices ( vrp , distance_matrices , duration_matrices )
256- depot_points =
257- vrp . vehicles . flat_map { |veh |
258- [ veh . start_point , veh . end_point ]
259- } . uniq
260- additive_setups = Array . new ( depot_points . size , 0 )
258+ additive_setups = Array . new ( @depots . size , 0 )
261259
262260 reload_depot_points =
263261 vrp . reload_depots . map ( &:point )
@@ -272,7 +270,7 @@ def expand_matrices(vrp, distance_matrices, duration_matrices)
272270 points
273271 }
274272
275- all_points = ( depot_points + reload_depot_points + client_points )
273+ all_points = ( @depots + reload_depot_points + client_points )
276274
277275 distance_matrices . map! do |matrix |
278276 matrix =
@@ -304,10 +302,6 @@ def distance(matrix, point1, point2)
304302
305303 def build_vehicles ( vrp )
306304 used_matrices = vrp . vehicles . map ( &:matrix_id ) . uniq
307- @depot_index_hash =
308- vrp . vehicles . flat_map { |veh |
309- [ veh . start_point , veh . end_point ]
310- } . uniq . each_with_index . map { |pt , idx | [ pt &.id , idx ] } . to_h
311305 all_units = vrp . units . index_by ( &:id )
312306
313307 vrp . vehicles . map { |veh |
@@ -325,8 +319,8 @@ def build_vehicles(vrp)
325319 {
326320 num_available : 1 ,
327321 capacity : capacity_hash . values + capacity_skills ,
328- start_depot : @depot_index_hash [ veh . start_point & .id ] ,
329- end_depot : @depot_index_hash [ veh . end_point & .id ] ,
322+ start_depot : @vehicle_start_point_index_hash [ veh . id ] ,
323+ end_depot : @vehicle_end_point_index_hash [ veh . id ] ,
330324 fixed_cost : veh . cost_fixed . to_i ,
331325 tw_early : veh . timewindow &.start || 0 ,
332326 tw_late : veh . timewindow &.end || MAX_INT64 ,
@@ -336,7 +330,7 @@ def build_vehicles(vrp)
336330 unit_duration_cost : veh . cost_time_multiplier . to_i ,
337331 profile : used_matrices . index ( veh . matrix_id ) ,
338332 start_late : nil ,
339- reload_depots : veh . reload_depots . map { |depot | @depot_hash [ depot . id ] } ,
333+ reload_depots : veh . reload_depots . map { |depot | @reload_depot_hash [ depot . id ] } ,
340334 max_reloads : veh . maximum_reloads || 0 ,
341335 name : veh . id . to_s
342336 }
@@ -414,26 +408,98 @@ def build_clients_and_groups(vrp)
414408 [ client_list , groups ]
415409 end
416410
411+ def add_depot_point ( point , index_hash , criteria = nil )
412+ return if point . nil?
413+
414+ return index_hash [ point . id ] if index_hash . key? ( point . id ) && index_hash [ point . id ] . is_a? ( Integer )
415+
416+ return index_hash [ point . id ] [ criteria ] if index_hash [ point . id ] . is_a? ( Hash ) && index_hash [ point . id ] . key? ( criteria )
417+
418+ @depots << point
419+ if criteria
420+ index_hash [ point . id ] ||= { }
421+ index_hash [ point . id ] [ criteria ] = index_hash [ point . id ] . size
422+ else
423+ index_hash [ point . id ] = index_hash . size
424+ end
425+ end
426+
417427 def build_depots ( vrp )
418- depot_points = vrp . vehicles . flat_map { |vehicle | [ vehicle . start_point , vehicle . end_point ] } . uniq
419- @reload_depots = Array . new ( depot_points . size , nil )
420- @depot_hash = { }
421- @depot_vehicle_hash = { }
422- depots =
423- depot_points . map do |point |
428+ @depots = [ ]
429+ @vehicle_start_point_index_hash = { }
430+ @vehicle_end_point_index_hash = { }
431+ @depot_points_standard_index_hash = { }
432+ @depot_points_force_start_by_timewindow_start_index_hash = { }
433+ @depot_points_force_end_by_timewindow_end_index_hash = { }
434+ vrp . vehicles . group_by ( &:shift_preference ) . each do |shift_preference , vehicles |
435+ vehicles . group_by ( &:timewindow ) . each do |timewindow , sub_vehicles |
436+ case shift_preference
437+ when :force_start
438+ sub_vehicles . each do |vehicle |
439+ @vehicle_start_point_index_hash [ vehicle . id ] =
440+ add_depot_point ( vehicle . start_point , @depot_points_force_start_by_timewindow_start_index_hash , timewindow . start )
441+ @vehicle_end_point_index_hash [ vehicle . id ] =
442+ add_depot_point ( vehicle . end_point , @depot_points_standard_index_hash )
443+ end
444+ when :force_end
445+ sub_vehicles . each do |vehicle |
446+ @vehicle_start_point_index_hash [ vehicle . id ] =
447+ add_depot_point ( vehicle . start_point , @depot_points_standard_index_hash )
448+ @vehicle_end_point_index_hash [ vehicle . id ] =
449+ add_depot_point ( vehicle . end_point , @depot_points_force_end_by_timewindow_end_index_hash , timewindow . end )
450+ end
451+ when :minimize_span
452+ sub_vehicles . each do |vehicle |
453+ @vehicle_start_point_index_hash [ vehicle . id ] =
454+ add_depot_point ( vehicle . start_point , @depot_points_standard_index_hash )
455+ @vehicle_end_point_index_hash [ vehicle . id ] =
456+ add_depot_point ( vehicle . end_point , @depot_points_standard_index_hash )
457+ end
458+ end
459+ end
460+ end
461+ depots = Array . new ( @depots . size , nil )
462+ @depot_points_standard_index_hash . map { |point_id , index |
463+ depots [ index ] =
424464 {
425- x : point &.location &.lon || 0 ,
426- y : point &.location &.lat || 0 ,
465+ x : @point_hash [ point_id ] &.location &.lon || 0 ,
466+ y : @point_hash [ point_id ] &.location &.lat || 0 ,
427467 tw_early : 0 ,
428468 tw_late : MAX_INT64 ,
429- name : point &. id &. to_s || '_null_store'
469+ name : " #{ point_id } _standard" || '_null_store'
430470 }
431- end
471+ }
472+ @depot_points_force_start_by_timewindow_start_index_hash . each { |point_id , ( timewindow_start , point_indices ) |
473+ point_indices . map { |point_index |
474+ depots [ point_index ] =
475+ {
476+ x : @point_hash [ point_id ] &.location &.lon || 0 ,
477+ y : @point_hash [ point_id ] &.location &.lat || 0 ,
478+ tw_early : timewindow_start || 0 ,
479+ tw_late : timewindow_start ,
480+ name : "#{ point_id } _#{ timewindow_start } _force_start" || '_null_store'
481+ }
482+ }
483+ }
484+ @depot_points_force_end_by_timewindow_end_index_hash . keys . flat_map { |point_id , ( timewindow_end , point_indices ) |
485+ point_indices . map { |point_index |
486+ depots [ point_index ] = {
487+ x : @point_hash [ point_id ] &.location &.lon || 0 ,
488+ y : @point_hash [ point_id ] &.location &.lat || 0 ,
489+ tw_early : timewindow_end || 0 ,
490+ tw_late : timewindow_end || MAX_INT64 ,
491+ name : "#{ point_id } _#{ timewindow_end } _force_end" || '_null_store'
492+ }
493+ }
494+ }
495+
496+ @reload_depots = [ ]
497+ @reload_depot_hash = { }
432498 vrp . reload_depots . each do |depot |
433- next if @depot_hash . key? ( depot . id )
499+ next if @reload_depot_hash . key? ( depot . id )
434500
435501 @reload_depots << depot
436- @depot_hash [ depot . id ] = depot_points . size
502+ @reload_depot_hash [ depot . id ] = @depots . size
437503 depots <<
438504 {
439505 x : depot . point &.location &.lon || 0 ,
@@ -465,18 +531,18 @@ def build_routes(vrp)
465531 def build_trips ( vrp , route , vehicle_type )
466532 trips = [ ]
467533 vehicle = vrp . vehicles [ vehicle_type ]
468- end_depot = @depot_index_hash [ vehicle . end_point & .id ]
534+ end_depot = @vehicle_end_point_index_hash [ vehicle . id ]
469535 current_trip = {
470536 visits : [ ] ,
471537 vehicle_type : vehicle_type ,
472- start_depot : @depot_index_hash [ vehicle . start_point & .id ] ,
538+ start_depot : @vehicle_start_point_index_hash [ vehicle . id ] ,
473539 end_depot : end_depot
474540 }
475541 route . missions . each do |mission |
476542 if mission . is_a? ( Models ::Service )
477543 current_trip [ :visits ] << @service_index_map . find_index { |service | service && service . id == mission . id }
478544 elsif mission . is_a? ( Models ::ReloadDepot )
479- reload_depot = @depot_hash [ mission . id ]
545+ reload_depot = @reload_depot_hash [ mission . id ]
480546 current_trip [ :end_depot ] = reload_depot
481547 trips << current_trip
482548 current_trip = {
0 commit comments