Break is not part of the solution

Hi,

i am currently trying some of the functionalities of jsprit. I have made a test case with 2 vehicles and two shipments. Both shipments can only be performed by one vehicle. The first shipment has a skill-constraint, which is not fulfilled by both vehicles, the second shipment has timewindow constraint, which is not fulfilled by one of the vehicles, because of a break.

I am wondering why the correct solution depends on the number of iterations. In the following code, if I set algorithm.setMaxIterations(20); I receive the expected solution:

±-------------------------------------------------------------------------------------------------------------------------------+
| detailed solution |
±--------±---------------------±----------------------±----------------±----------------±----------------±----------------+
| route | vehicle | activity | job | arrTime | endTime | costs |
±--------±---------------------±----------------------±----------------±----------------±----------------±----------------+
| 1 | v1, s2 | start | - | undef | 0 | 0 |
| 1 | v1, s2 | pickupShipment | b → a (s2) | 28 | 105 | 105 |
| 1 | v1, s2 | deliverShipment | b → a (s2) | 119 | 119 | 119 |
| 1 | v1, s2 | end | - | 133 | undef | 133 |
±--------±---------------------±----------------------±----------------±----------------±----------------±----------------+
| 2 | v2, s1,s2 | start | - | undef | 0 | 0 |
| 2 | v2, s1,s2 | pickupShipment | a → b (s1) | 14 | 14 | 14 |
| 2 | v2, s1,s2 | deliverShipment | a → b (s1) | 28 | 28 | 28 |
| 2 | v2, s1,s2 | end | - | 57 | undef | 57 |
±-------------------------------------------------------------------------------------------------------------------------------+

However, if I set algorithm.maxIterations(10); the break of vehicle2 is completely ignored and I get a wrong solution, where vehicle2 can performs shipment2, while vehicle2 actually would have to make a break, in the timewindow, when shipment2 can be performed.

±-------------------------------------------------------------------------------------------------------------------------------+
| detailed solution |
±--------±---------------------±----------------------±----------------±----------------±----------------±----------------+
| route | vehicle | activity | job | arrTime | endTime | costs |
±--------±---------------------±----------------------±----------------±----------------±----------------±----------------+
| 1 | v2, s1,s2 | start | - | undef | 0 | 0 |
| 1 | v2, s1,s2 | pickupShipment | a → b (s1) | 14 | 14 | 14 |
| 1 | v2, s1,s2 | deliverShipment | a → b (s1) | 28 | 28 | 28 |
| 1 | v2, s1,s2 | pickupShipment | b → a (s2) | 28 | 105 | 105 |
| 1 | v2, s1,s2 | deliverShipment | b → a (s2) | 119 | 119 | 119 |
| 1 | v2, s1,s2 | end | - | 133 | undef | 133 |
±-------------------------------------------------------------------------------------------------------------------------------+

Is there any explanation how this can be avoided? How could I know how much iterations are enough so that such things cannot happen?

I tried with the following code:

/*
*
*/
package com.graphhopper.jsprit.examples;

import java.util.Collection;

import com.graphhopper.jsprit.analysis.toolbox.Plotter;
import com.graphhopper.jsprit.core.algorithm.VehicleRoutingAlgorithm;
import com.graphhopper.jsprit.core.algorithm.box.Jsprit;
import com.graphhopper.jsprit.core.problem.Location;
import com.graphhopper.jsprit.core.problem.VehicleRoutingProblem;
import com.graphhopper.jsprit.core.problem.job.Break;
import com.graphhopper.jsprit.core.problem.job.Shipment;
import com.graphhopper.jsprit.core.problem.solution.VehicleRoutingProblemSolution;
import com.graphhopper.jsprit.core.problem.solution.route.activity.TimeWindow;
import com.graphhopper.jsprit.core.problem.vehicle.VehicleImpl;
import com.graphhopper.jsprit.core.problem.vehicle.VehicleType;
import com.graphhopper.jsprit.core.problem.vehicle.VehicleTypeImpl;
import com.graphhopper.jsprit.core.reporting.SolutionPrinter;
import com.graphhopper.jsprit.core.util.Solutions;

public class MyBreakExample2
{

public static void main(String[] args)
{

    /*
     * get a vehicle type-builder and build a type with the typeId "vehicleType" and one capacity dimension, i.e. weight, and capacity dimension value of 2
     */
    final int WEIGHT_INDEX = 0;
    VehicleTypeImpl.Builder vehicleTypeBuilder = VehicleTypeImpl.Builder
        .newInstance("vehicleType")
        .addCapacityDimension(WEIGHT_INDEX, 1)
        .setCostPerWaitingTime(1.0)
        .setCostPerServiceTime(1.0)
        .setCostPerTransportTime(1.0);
    VehicleType vehicleType = vehicleTypeBuilder.build();

    /*
     * get a vehicle-builder and build a vehicle located at (10,10) with type "vehicleType"
     */
    VehicleImpl vehicle1 = VehicleImpl.Builder.newInstance("v1, s2")
        .setStartLocation(Location.newInstance(0, 0))
        .setType(vehicleType)
        .addSkill("s2")
        .setEarliestStart(0)
        .setLatestArrival(200)
        .build();

    VehicleImpl vehicle2 = VehicleImpl.Builder.newInstance("v2, s1,s2")
        .setStartLocation(Location.newInstance(0, 0))
        .setType(vehicleType)
        .addSkill("s1")
        .addSkill("s2")
        .setEarliestStart(0)
        .setLatestArrival(200)
        .setBreak(Break.Builder.newInstance("myBreak")
            .setTimeWindow(TimeWindow.newInstance(100, 100))
            .setServiceTime(50)
            .build())
        .build();
    /*
     * build shipments at the required locations, each with a capacity-demand of 1.
     */
    // shipment1
    Shipment shipment1 = Shipment.Builder.newInstance("a -> b (s1)")
        .addSizeDimension(0, 1)
        .addRequiredSkill("s1")
        .setPickupLocation(Location.newInstance(10, 10))
        .setDeliveryLocation(Location.newInstance(20, 20))
        .setDeliveryTimeWindow(TimeWindow.newInstance(10, 40))
        .build();
    // shipment2
    Shipment shipment2 = Shipment.Builder.newInstance("b -> a (s2)")
        .addSizeDimension(0, 1)
        .addRequiredSkill("s2")
        .setPickupLocation(Location.newInstance(20, 20))
        .setPickupTimeWindow(TimeWindow.newInstance(105, 130))
        .setDeliveryLocation(Location.newInstance(10, 10))
        .build();

    VehicleRoutingProblem.Builder vrpBuilder = VehicleRoutingProblem.Builder.newInstance();
    vrpBuilder.addVehicle(vehicle1).addVehicle(vehicle2).addJob(shipment1).addJob(shipment2);

    vrpBuilder.setFleetSize(VehicleRoutingProblem.FleetSize.FINITE);
    VehicleRoutingProblem problem = vrpBuilder.build();

    /*
     * get the algorithm out-of-the-box.
     */
    VehicleRoutingAlgorithm algorithm = Jsprit.Builder.newInstance(problem)
        .buildAlgorithm();
    algorithm.setMaxIterations(20);
    /*
     * and search a solution
     */
    Collection<VehicleRoutingProblemSolution> solutions = algorithm.searchSolutions();

    /*
     * get the best
     */
    VehicleRoutingProblemSolution bestSolution = Solutions.bestOf(solutions);

    SolutionPrinter.print(problem, bestSolution, SolutionPrinter.Print.VERBOSE);

    /*
     * plot
     */
    new Plotter(problem, bestSolution).plot("output/plot", "breaks");

}

}

Best Regards,
Stefan.

By my observation, the initial solution must not fullfill all constraints and thus you begin optimization iterations with bad solution. This means if you have less iteration count the algorithm might not optimize the problem to overcome initial solution failures and you ends up with not fullfilling solution. Ergo a rule of thumb here is to have as much interations as possible and terminate iterations with some premature termination algorithm.