Skip to content

Commit

Permalink
[OO] Add maximum initial quantity to ortools
Browse files Browse the repository at this point in the history
  • Loading branch information
Braktar committed Aug 17, 2021
1 parent 052a93a commit e9778e1
Show file tree
Hide file tree
Showing 6 changed files with 55 additions and 4 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
- Return route day/date and visits' index in result [#196](https://github.com/Mapotempo/optimizer-api/pull/196)
- Treat complex shipments (multi-pickup-single-delivery and single-pickup-multi-delivery) as multiple simple shipments internally to increase performance [#261](https://github.com/Mapotempo/optimizer-api/pull/261)
- Prioritize the vehicles (and trips of `vehicle_trips` relation) via modifying the fixed costs so that first vehicles (and trips) are preferred over the latter ones [#266](https://github.com/Mapotempo/optimizer-api/pull/266)

- Document return codes [#224](https://github.com/Mapotempo/optimizer-api/pull/224)
- OR-Tools wrapper can use `initial` capacity value [#245](https://github.com/Mapotempo/optimizer-api/pull/245)

### Changed

Expand Down
23 changes: 23 additions & 0 deletions test/wrappers/ortools_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5256,6 +5256,29 @@ def test_quantity_precision
}
end

def test_initial_quantity
problem = VRP.basic
problem[:services].first[:quantities] = [{ unit_id: 'kg', value: -1 }]
problem[:services].last[:quantities] = [{ unit_id: 'kg', value: -1 }]
problem[:vehicles].each{ |vehicle|
vehicle[:capacities] = [{ unit_id: 'kg', limit: 3, initial: 0 }]
}

result = OptimizerWrapper.wrapper_vrp('demo', { services: { vrp: [:ortools] }}, TestHelper.create(problem), nil)
assert_equal 2, result[:unassigned].size, 'The result is expected to contain 2 unassigned'

problem = VRP.basic
problem[:services].first[:quantities] = [{ unit_id: 'kg', value: -1 }]
problem[:services].last[:quantities] = [{ unit_id: 'kg', value: 1 }]
problem[:vehicles].each{ |vehicle|
vehicle[:capacities] = [{ unit_id: 'kg', limit: 3, initial: 0 }]
}

result = OptimizerWrapper.wrapper_vrp('demo', { services: { vrp: [:ortools] }}, TestHelper.create(problem), nil)
assert result[:routes].first[:activities].index{ |act| act[:service_id] == problem[:services].last[:id] } <
result[:routes].first[:activities].index{ |act| act[:service_id] == problem[:services].first[:id] }
end

def test_simplify_vehicle_pause_without_timewindow_or_duration
complete_vrp = VRP.pud
complete_vrp[:rests] = [{
Expand Down
29 changes: 27 additions & 2 deletions wrappers/ortools.rb
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ def solver_constraints
super + [
:assert_end_optimization,
:assert_vehicles_objective,
:assert_vehicles_no_capacity_initial,
:assert_vehicles_no_alternative_skills,
:assert_zones_only_size_one_alternative,
:assert_only_empty_or_fill_quantities,
Expand Down Expand Up @@ -99,6 +98,31 @@ def solve(vrp, job, thread_proc = nil, &block)
empty: false
}
}
total_quantities = vrp.units.map{ |unit| [unit.id, 0] }.to_h

vrp.relations.select{ |r| r.type == :shipment }.each{ |r|
vrp.units.each{ |unit|
total_negative = 0
total_positive = 0
r.linked_services.each{ |s|
quantity = s.quantities.find{ |q| q.unit_id == unit.id }&.value || 0
if quantity > 0
total_positive += quantity
else
total_negative -= quantity
end
}
total_quantities[unit.id] += [total_positive, total_negative].max
}
}

vrp.services.each{ |service|
next if service.relations.any?{ |r| r.type == :shipment }

service.quantities.each{ |q|
total_quantities[q.unit.id] += (q.value || 0).abs
}
}

vrp.services.each{ |service|
service.quantities.each{ |quantity|
Expand Down Expand Up @@ -284,7 +308,8 @@ def solve(vrp, job, thread_proc = nil, &block)
OrtoolsVrp::Capacity.new(
limit: (q&.limit && q.limit < 1e+22) ? q.limit : -1,
overload_multiplier: q&.overload_multiplier || 0,
counting: unit&.counting || false
counting: unit&.counting || false,
initial_limit: q&.initial || [total_quantities[unit.id], q&.limit].compact.max
)
},
time_window: OrtoolsVrp::TimeWindow.new(
Expand Down
2 changes: 2 additions & 0 deletions wrappers/ortools_vrp_pb.rb

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions wrappers/vroom.rb
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ def solver_constraints
:assert_matrices_only_one,
:assert_no_distance_limitation,
:assert_no_service_duration_modifiers,
:assert_vehicles_no_capacity_initial,
:assert_vehicles_no_duration_limit,
:assert_vehicles_no_force_start,
:assert_vehicles_no_late_multiplier_or_single_vehicle,
Expand Down
2 changes: 1 addition & 1 deletion wrappers/wrapper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ def assert_vehicles_start_or_end(vrp)

def assert_vehicles_no_capacity_initial(vrp)
vrp.vehicles.none?{ |vehicle|
vehicle.capacities.find{ |c| c.initial&.positive? }
vehicle.capacities.any?{ |c| c.initial && c.initial < c.limit }
}
end

Expand Down

0 comments on commit e9778e1

Please sign in to comment.