0

I am using JSprit to solve Multi travelling salesman problem with time windows : I have a salesman that must visit n clients during a week as fast as possible with time constraints. I have configured a vehicle for each day and a service for each client.

It is actually working but I can't find a way to deal with jobs planned at a specific time. For example a salesman has 15 clients to see but on Monday at 3PM he has an appointment scheduled. I would like the route to be optimised considering that constraint.

I have tried to specify a job with a time window corresponding to the appointment but the job is usually not assigned whereas there are still vehicles available ! I have tried to put a required skill on the job and on the vehicle attached to the right day and same result.

So here are my problems : - I can't set a job as obligatory (can't be not assigned) and at a specific time. - I don't understand why there is unassigned jobs while there are vehicles not used.

EDIT : I modify my code to add priority and enlarge time windows as advice. No luck, the job 1 is still unassigned.

public class test {


private ArrayList<Service> serviceList; // list of services
private ArrayList<Location> locationList; // list of location
private Location dep;   // departure & arrival for each day
private String parameters;
// distances between locations
private double times[][]; 



public test()
{
    this.serviceList = new ArrayList<>();
    this.locationList = new ArrayList<>();
    this.parameters = "";

}

public void test(int nb)
{
   this.times = new double[nb+1][nb+1];

   // collect data from json for the list of geocoded services

     try (InputStream in = new ByteArrayInputStream(json.getBytes(StandardCharsets.UTF_8));
     JsonReader read = javax.json.Json.createReader(in)) {

        JsonObject obj = read.readObject();
        JsonArray results = obj.getJsonArray("clients");

        // departure
        dep = Location.Builder.newInstance().setId("0").setIndex(0).setCoordinate(Coordinate.newInstance(48.88626, 2.22135)).build();
        // parameters for OSRM request
        parameters = "&loc=48.88626, 2.22135";

        locationList.add(dep);

        // get locations from json
       for(int i=0; i<nb;i++)
       {
           JsonObject result = results.getValuesAs(JsonObject.class).get(i);
           Location l = Location.Builder.newInstance().setId(Integer.toString(i+1)).setIndex(i+1).setCoordinate(Coordinate.newInstance(Double.valueOf(result.getString("latitude")), Double.valueOf(result.getString("longitude")))).build();
           locationList.add(l);
           // parameters for OSRM request
           parameters += "&loc="+result.getString("latitude")+","+result.getString("longitude");
       }

    // Vehicles     
    VehicleTypeImpl.Builder vehicleTypeBuilder = VehicleTypeImpl.Builder.newInstance("vehicleType");
    VehicleType vehicleType = vehicleTypeBuilder.setCostPerTransportTime(1).setCostPerWaitingTime(1).setCostPerServiceTime(1).setCostPerDistance(0).build();

    // vehicule for each day
    VehicleImpl vehicleMon = VehicleImpl.Builder.newInstance("vehicleMon").setType(vehicleType).setStartLocation(dep).setEarliestStart(32400).setLatestArrival(64800).build();
    VehicleImpl vehicleTue = VehicleImpl.Builder.newInstance("vehicleTue").setType(vehicleType).setStartLocation(dep).setEarliestStart(118800).setLatestArrival(151200).build();
    VehicleImpl vehicleWed = VehicleImpl.Builder.newInstance("vehicleWed").setType(vehicleType).setStartLocation(dep).setEarliestStart(205200).setLatestArrival(237600).build();
    VehicleImpl vehicleThu = VehicleImpl.Builder.newInstance("vehicleThu").setType(vehicleType).setStartLocation(dep).setEarliestStart(291600).setLatestArrival(324000).build();
    VehicleImpl vehicleFry = VehicleImpl.Builder.newInstance("vehicleFry").setType(vehicleType).setStartLocation(dep).setEarliestStart(378000).setLatestArrival(410400).build();
    VehicleImpl vehicleSat = VehicleImpl.Builder.newInstance("vehicleSat").setType(vehicleType).setStartLocation(dep).setEarliestStart(464400).setLatestArrival(496800).build();
    VehicleImpl vehicleSun = VehicleImpl.Builder.newInstance("vehicleSun").setType(vehicleType).setStartLocation(dep).setEarliestStart(550800).setLatestArrival(583200).build();


    // the job with a specific time window which represent a scheduled appointment 
    Service serviceTW = Service.Builder.newInstance(Integer.toString(1))
                .setName("1")
                .addTimeWindow(124200, 131400) // 2 hours window
                .setPriority(1) // high priority 
                .setServiceTime(3600).setLocation(locationList.get(1)).build(); 

        serviceList.add(serviceTW);

    // jobs
    for(int i=1; i<nb; i++) // we skip the first one which is above
    {
        Service service = Service.Builder.newInstance(Integer.toString(i+1))
                .setName(Integer.toString(i+1))
                .setPriority(3) // low priority
                .addTimeWindow(32400, 583200) // time window for all week
                .setServiceTime(3600).setLocation(locationList.get(i+1)).build();
        serviceList.add(service);
    }

    // matrix time
        // retrieve traveling time from OSRM
    getOSRM_times();

    VehicleRoutingTransportCostsMatrix.Builder costMatrixBuilder = VehicleRoutingTransportCostsMatrix.Builder.newInstance(true);

    for(int i=0; i<nb ;i++)
    {
        for(int j=0; j<nb+1;j++)
        {
            costMatrixBuilder.addTransportTime(Integer.toString(i), Integer.toString(j), times[i][j]/10);
        }

    }
    // adding vehicles
    VehicleRoutingTransportCosts costMatrix = costMatrixBuilder.build();
    VehicleRoutingProblem.Builder vrpBuilder = VehicleRoutingProblem.Builder.newInstance();
    vrpBuilder
            .addVehicle(vehicleMon)
            .addVehicle(vehicleTue)
            .addVehicle(vehicleWed)
            .addVehicle(vehicleThu)
            .addVehicle(vehicleFry)
            .addVehicle(vehicleSat)
            .addVehicle(vehicleSun)
            .addAllJobs(serviceList).setFleetSize(FleetSize.FINITE).setRoutingCost(costMatrix);


// Problem/Solution
VehicleRoutingProblem problem = vrpBuilder.build();
VehicleRoutingAlgorithm algorithm = Jsprit.createAlgorithm(problem);
Collection<VehicleRoutingProblemSolution> solutions = algorithm.searchSolutions();
VehicleRoutingProblemSolution bestSolution = Solutions.bestOf(solutions);

File dir = new File("output");
    // if the directory does not exist, create it
    if (!dir.exists()){
        System.out.println("creating directory ./output");
        boolean result = dir.mkdir();  
        if(result) System.out.println("./output created");  
    }


    // RESULTS
        //xml
    new VrpXMLWriter(problem, solutions).write("output/problem-with-solution.xml");
        // console
   SolutionPrinter.print(problem, bestSolution, Print.VERBOSE);
        // image
   new Plotter(problem,bestSolution).setLabel(Plotter.Label.ID).plot("output/solution.png", "solution");

   }catch (IOException ex) {Logger.getLogger(Json.class.getName()).log(Level.SEVERE, null, ex);}

}

I noticed that if I change the day of my fixed job, the vehicle assigned for this day will always be full and the job unassigned.

why
  • 1
  • 2
  • 1
    I suggest you look at the newest release that includes [job priority](https://github.com/graphhopper/jsprit/issues/242). However, I believe you are incorrect with your statement that there are vehicle available. You only have one vehicle that is available on each individual day and without priorities, there is no reason for jsprit to favour your fixed appointment over the other jobs you have allocated with multiple time windows – roganjosh May 30 '16 at 16:24
  • I agree with you, when my job is unassigned vehicleTue is busy all day by other jobs. Nevertheless seeing that, why jsprit hadn't assigned another vehicle for theses jobs and used vehicleTue for the fixed job 1 ? – why May 31 '16 at 13:55

1 Answers1

1

Actually, I think you have over-complicated the definition of the service time windows that might be causing an issue. Jsprit cannot assign a service to take place when there are no vehicles available. So, I would continue to define your vehicles on a daily basis, as you have, but I would create each job with .addTimeWindow(32400, 583200). Each can only be serviced once and can only be serviced when a vehicle is available, so it serves the purpose you are looking for without huge overhead of juggling time windows.

If a fixed commitment is more important than something that could be handled at any point during the week, it's a great time to test out the new feature of job priorities :)

roganjosh
  • 12,594
  • 4
  • 29
  • 46