2

I'm using OR-Tools' VRP solver (in Python) to schedule vehicle routes for the following problem:

There is a list of customer locations that must be visited within a given week (Mon-Sun), but we don't care which day they're visited.

We have multiple teams available to send out each day to customers (each team has one vehicle), and we can send any team to any customer.

Therefore, in my model, a "vehicle" is defined as a team-day; e.g., Team 1 on Monday is a vehicle, and Team 2 on Tuesday is treated as a separate vehicle, etc. So if there are 6 teams and 7 days in the week, I tell the solver I have 42 vehicles available to cover all of the locations. I'm optimizing travel times and each of these "vehicles" may have a different maximum travel time, as a team's availability varies from day to day.

I have all of this working with OR-Tools.

In some of my test cases, where the total available time significantly exceeds the demand, the solution returned includes some vehicles that assigned an empty route (i.e. a team has a day off). This is fine, but often the unassigned day is in the middle of the week. So the solution might give Team 1 customers on Mon, Tues, Wed, Sat, and Sun, and leave Team 1 unassigned on Thur and Fri.

I would prefer to front-load the work week, so in this scenario if possible I'd like Team 1 to work on Mon-Fri and have Sat/Sun off. Is there a way to include this (soft) constraint in OR-Tools?

It's not a matter of just swapping one day's schedule for another, because the team might be available for fewer hours on one day vs. another. e.g. If a team's availability is this

Mon: 6 hours Tues: 6 hours Wed: 8 hours

and the solver returns a solution with the following travel times

Mon: 0 hours Tues: 0 hours Wed: 8 hours

I would prefer it to return Mon: 6 hours Tues: 2+ hours Wed: 0 hours

I'm envisioning this might be solved by "prioritizing" vehicles (Team-1-Monday and Team-2-Monday have priority over Team-1-Tuesday and Team-2-Tuesday when assigning locations, for example), but I'm not sure how to specify this in OR-Tools.

Aaron Dunigan AtLee
  • 1,860
  • 7
  • 18
  • I'm not overly conversant in the OR-Tools library, but the general way I would attack this is through the Objective function, NOT constraints. (What would you constrain?) I would add a little "sugar" to the score for the earlier days in the week on a scale an order of magnitude or so below your basic score such that the solver will seek to fill early slots first. Perhaps even progressively: `sugar[day]*X[day, team] ...` where `sugar` is a parameter scaled by day... `sugar[mon]=0.05, sugar[tue]=0.04, ...` and `X` is your binary variable for delivery... – AirSquid Mar 24 '22 at 21:04
  • Thanks @AirSquid, that approach makes sense to me. I'm not sure which method in OR-Tools does that, though. I've tried `routing.SetFixedCostOfVehicle`, with a greater fixed cost later in the week. That did seem to accomplish it, but I'm not sure if those fixed costs are taking a hit on the "available time" for each team, i.e. does a fixed cost of 30 reduce the vehicle's available time by 30 minutes? – Aaron Dunigan AtLee Mar 24 '22 at 22:16
  • By experimentation I determined that the fixed cost doesn't reduce the available time (i.e. isn't added to the travel time dimension), so that seems to be the answer. – Aaron Dunigan AtLee Mar 24 '22 at 22:31
  • Great. Just be very careful with the scaling of your penalty/bonus. it should be quite small. Inspect some corner cases. For example, you **don't** want it to be the case that on the last day you should make 5 visits and the OBJ function just counts the visits without a scalar multiplier, and your vehicle "fixed cost" is 6, which would result in the model choosing not to make those 5 visits. A tiny incentive is plenty for the solver. – AirSquid Mar 24 '22 at 22:41
  • if the vehicle is used (no route from start node to end node) then this cost is added to the objective function otherwise it is ignored. note: Arc Cost from Start to End is also zeroed by default. – Mizux Mar 25 '22 at 07:03
  • @Mizux Would `SetSpanCostCoefficientForVehicle` accomplish the same thing but in a multiplicative way rather than adding to the objective function as `setFixedCostOfVehicle` does? – Aaron Dunigan AtLee Mar 25 '22 at 15:32
  • Yes it is the coefficient use in (dim.CumulVar(v_end) - dim.CumulVar(v_start)) * coeff. By default it's 0 that's why Routing dimension doesn't participate to objective value by default – Mizux Mar 26 '22 at 08:45

1 Answers1

0

Thanks to AirSquid for the inspiration:

One solution is to add a fixed cost to each vehicle:

for vehicle_id in range(num_vehicles):
    routing.SetFixedCostOfVehicle(
        vehicle_costs[vehicle_id], vehicle_id)

where vehicle_costs[] is greater for vehicles scheduled later in the week.

Aaron Dunigan AtLee
  • 1,860
  • 7
  • 18