How to force a delivery to be the last

Is there any hard constraint to prevent a delivery from being the last on the route?

Hi

I believe you would need to manually create a custom HardActivityConstraint that would look something like this (caveat I have not tested this)

    /**
     * endLocationId: the id of the location at the end of this route, 
     *     if none is set this must be the id of the start point as jsprit will treat this as a round trip.
     * stopThatMustNotBeLastId: Id of the stop that you do not want to be last
     */
    private fun buildNotLastStopConstraint(endLocationId: String, stopThatMustNotBeLastId: String) = HardActivityConstraint { insertionContext, prevActivity, currActivity, nextActivity, prevActDepTime ->

        // If the activity we are busy checking is the one we do not want to be last
        if (currActivity.location.id == stopThatMustNotBeLastId) {
            
            // If the next activity is the end of the route
            if (nextActivity.location.id == endLocationId) {
                
                // tell jsprit this is not a valid position
                return@HardActivityConstraint ConstraintsStatus.NOT_FULFILLED
            }
        }
        
        // Otherwise tell jsprit this position is ok
        return@HardActivityConstraint ConstraintsStatus.FULFILLED
    }

It could then be used as follows

      val startLocation = Location
            .Builder()
            .setId("startLocationId")
            .build()

        val vehicle = VehicleImpl
            .Builder
            .newInstance("vehicle")
            .setStartLocation(startLocation)
            .build()

        // Create routing problem
        val problem =
            VehicleRoutingProblem
                .Builder
                .newInstance()
                .setRoutingCost(matrix)
                .addVehicle(vehicle)
                .build()

        // Add constraints
        val constraint = buildNotLastStopConstraint("startLocationId", "otherLocationId")
        
        val stateManager = StateManager(problem)
        val constraintManager = ConstraintManager(problem, stateManager)
        constraintManager.addConstraint(constraint, ConstraintManager.Priority.HIGH)

        val algorithm = Jsprit
            .Builder
            .newInstance(problem)
            .setStateAndConstraintManager(stateManager, constraintManager)
            .buildAlgorithm()
        
        val solutions = algorithm.searchSolutions()

I have found that there are often a lot of edge cases on these constraints and may need some experimentation to make sure you have fulfilled them all

I think I expressed myself badly
Is there any hard restriction to force a delivery to be the last on the route?

Hi

Yes that is easy, when creating the routing problem you will just set that delivery as the endpoint of the route.

        val startLocation = Location
            .Builder()
            .setId("startLocationId")
            .build()

        val endLocation = Location
            .Builder()
            .setId("startLocationId")
            .build()

        val vehicle = VehicleImpl
            .Builder
            .newInstance("vehicle")
            .setStartLocation(startLocation)
            .setEndLocation(endLocation)
            .build()

        // Create routing problem
        val problem =
            VehicleRoutingProblem
                .Builder
                .newInstance()
                .setRoutingCost(matrix)
                .addVehicle(vehicle)
                .build()

        val algorithm = Jsprit
            .Builder
            .newInstance(problem)
            .buildAlgorithm()
        
        val solutions = algorithm.searchSolutions()

It’s not that simple because the endLocation is the vehicle’s return. What I need is a situation where among all the deliveries one of them needs to be the last due to a customer restriction.

Hi @eltonramos

In that case it would be similar to my first answer, you would create a custom hard activity constraint, the activity constraint needs to essentially return ConstraintsStatus.NOT_FULFILLED for the the stop that you want last if it is in any position other than last, this tells jsprit that it can’t be placed there. Something like below (caveat I have not tested this)

  /**
     * endLocationId: the id of the location at the end of this route, 
     *     if none is set this must be the id of the start point as jsprit will treat this as a round trip.
     * stopThatMustBeLastId: Id of the stop that you want to be last
     */
    private fun buildLastStopConstraint(endLocationId: String, stopThatMustBeLastId: String) = HardActivityConstraint { insertionContext, prevActivity, currActivity, nextActivity, prevActDepTime ->

        // If the activity we are busy checking is the one we want to be last
        if (currActivity.location.id == stopThatMustBeLastId) {
            
            // If the next activity is not the end of the route
            if (nextActivity.location.id != endLocationId) {
                
                // tell jsprit this is not a valid position
                return@HardActivityConstraint ConstraintsStatus.NOT_FULFILLED
            }
        }
        
        // Otherwise tell jsprit this position is ok
        return@HardActivityConstraint ConstraintsStatus.FULFILLED
    }