Hints about OptaPlanner configuration to solve Vehicle Routing (real-time)

194 views Asked by At

I would be glad for any kind of opinion on this setup for a Vehicle Routing Problem.

First of all, these are my first steps with this tool, so please forgive me if I'm totally out of scope :-) I've made an algorithm without optaplanner, to test a basic rules setup. It worked for a single Vehicle, but optaplanner looked very similar to my original idea to assign points to each satisfied rule, then select the vehicle with the higest score. It's also configurable, and for sure it's better than what I made before.

Let's begin

Looking at the docs, and some videos, seems that VRP problems are solved when all the pickup points have been already defined in a dataset feeded into the Solver.

What about finding a Vehicle for each request in real-time, refusing it in case no vehicle can satisfy the constraints? Consider that there are calls to external (paid) Maps services, that are making the process slower, and have a cost. It's better to avoid redundancy in these calls.

Pickup requests can be done for any future date, but not the current day.

Planning Entity - the pickup request

@PlanningEntity
public class FindBestVehicleRequest
{

  @PlanningId
  private Long id;

  // Shadow variable candidate
  private Double requiredSpace;

  // Shadow variable candidate
  private int requiredAutonomy;

  private String pickupAddress;

 // Shadow variable candidate
 private LatLng pickupPosition;

 private LocalDateTime pickupDateTime;

 @PlanningVariable(valueRangeProviderRefs = "vehicle")
 private Vehicle vehicle;

 ...

}

Calculations involved in each request

I've read into the docs about shadow variables, I'm still far from understanding how to define them, but I suppose that are useful in my case: as stated before, for each request I need to call the Maps Service(Google, OpenStreetMaps, ...) in order to calculate the distance to reach the pickup address from vhere the vehicle is located.

Obtain the Vehicle origin position, some pseudo code of the logic:

if (vehicle.hasOrdersBefore(pickupDateTime) {

   LatLng  origin      = vehicle.lastOrderBefore(pickupDateTime).getPosition();
   String  destination = pickupAddress;
   Integer distance    = mapsServer.getDistance(origin, destination);

   return distance;
}

There are more calculations like this one involved, but there's no need to list all of them, they're similar.

I'm studying all the available algorithm types to find the one that's more indicated for this problem.

ConstraintProvider implementation

public class BestVehicleConstraintProvider implements ConstraintProvider {
    @Override public Constraint[] defineConstraints(ConstraintFactory constraintFactory) {
        return new Constraint[]{
                vehicleHasAutonomy(constraintFactory)
        };
    }

    // One HARD constraint
    private Constraint vehicleHasAutonomy(ConstraintFactory constraintFactory) {
        return constraintFactory.from(FindBestVehicleRequest.class)
                .groupBy(FindBestVehicleRequest::getVehicle, sum(FindBestVehicleRequest::getRequiredAutonomy))
                .filter((vehicle, requiredAutonomy) -> requiredAutonomy > vehicle.getVehicleTypeProperties().getMaxKmAutonomy())
                .penalize("vehicleHasAutonomy", HardSoftScore.ONE_HARD,
                          ((vehicle, requiredSpace) -> vehicle.getVehicleTypeProperties().getMaxKmAutonomy() - requiredSpace));

    }


}

And the final part,

the Solution class

@PlanningSolution
public class FindBestVehicleSolution
{

  @PlanningEntityCollectionProperty
  private List<FindBestVehicleRequest> processes;

  @ProblemFactCollectionProperty
  @ValueRangeProvider(id = "vehicle")
  private List<Vehicle> vehicles;       // <----- I'm fetching a list of active Vehicles in
                                        //        the requested pickupDate, and passing it here

  @ProblemFactProperty
  private String pickupAddress;

  // private LatLng pickupAddressPosition;    //  shadow variable ? how to call the map server 
                                              //                    and populate this field ?


  @PlanningScore
  private HardSoftScore score;

...
}

Ok, so I think that all the code is here. I'm looking for suggestions on proper ways to: - call the maps server to get diestances in an efficient way - avoid repeating the same calculations - (IMPORTANT!) if a Vehicle satisfy certain rules, like if it has no assigned orders in the selected day, end the evaluation process directly (vehicle found!)

Yes I'm asking too much maybe, but the documentations is a bit hard to adapt to this situation, I think that with time I will get better, but I'd like to make some simulations with Optaplanner Workbench soon :-)

Thanks for anyone that will give any kind of suggestion!

0

There are 0 answers