Add companion services side by side on a route

From a list of services (shipment) some are related because the delivery is in the same time window with pickup and must be made by the same route, but sometimes only one service and not the other are in, as I can define a constraint so that it only locates Services related on a route if both are in the route

Thanks

What is your question and what have you tried (like the Java code)? Was there an error or some unexpected behaviour?

An example of the case is:
I have 50 service shipment, 4 delivery in the same point and in the same time window, and 3 pickup in the same point in the same time window ,establishing a list of 3 services that deliver with 3services that pickup (one to one) to asignt in the same Route (both) and if only one service is used and not the one related, the two are not assigned.

My code is:
the VAR serviciosimul is a hashmap with: KEY: id from first shipment, and Value is id from second shipment

vrpBuilder.setFleetSize(VehicleRoutingProblem.FleetSize.FINITE);

StateManager stateManager = new StateManager(vrp);
            stateManager.updateLoadStates();
            stateManager.updateTimeWindowStates();
            stateManager.updateSkillStates();
            stateManager.addStateUpdater(new UpdateVariableCosts(vrp.getActivityCosts(), vrp.getTransportCosts(), stateManager));
            stateManager.addStateUpdater(new UpdateFutureWaitingTimes(stateManager, vrp.getTransportCosts()));
            stateManager.addStateUpdater(new JobsInRouteMemorizer(stateManager, serviciosimul)); // modificado para optimizar memoria
            ConstraintManager constraintManager = new ConstraintManager(vrp, stateManager);
                       constraintManager.addTimeWindowConstraint();
            constraintManager.addLoadConstraint();

            constraintManager.addConstraint(new TramosSimultaneosConstraint(stateManager, serviciosimul), ConstraintManager.Priority.CRITICAL);
            constraintManager.addConstraint(new PuntosConsecutivosConstraint(stateManager, serviciopunto));
            final RewardAndPenaltiesThroughSoftConstraints softConstraintContributionToOverallObjective = new RewardAndPenaltiesThroughSoftConstraints(vrp, serviciosimul, stateManager);

VehicleRoutingAlgorithm vra = Jsprit.Builder.newInstance(vrp).setStateAndConstraintManager(stateManager, constraintManager)
                    .setProperty(Jsprit.Parameter.THREADS, nhilos) //5
                    //                    .setProperty(Jsprit.Parameter.FAST_REGRET, "true")
                    .setProperty(Jsprit.Parameter.FIXED_COST_PARAM, "1.5")
                    .setProperty(Jsprit.Strategy.CLUSTER_REGRET, "0.3")
                    .setProperty(Jsprit.Strategy.CLUSTER_BEST, factor_agrupamiento) //"0.9")
                    //                .setProperty(Jsprit.Strategy.WORST_BEST, "0.5").setProperty(Jsprit.Strategy.RANDOM_BEST, "0.5")
                    .setProperty(Jsprit.Parameter.VEHICLE_SWITCH, "false")
                    .setProperty(Jsprit.Parameter.THRESHOLD_ALPHA, "0.0")
                    .setProperty(Jsprit.Parameter.MAX_TRANSPORT_COSTS, Double.toString(1.0E8))
                    .setObjectiveFunction(new SolutionCostCalculator() {
                        @Override
                        public double getCosts(VehicleRoutingProblemSolution solution) {
                            double costs = 0.;
                            for (VehicleRoute route : solution.getRoutes()) {
                                costs += route.getVehicle().getType().getVehicleCostParams().fix;
                                TourActivity prevAct = route.getStart();
                                String puntoAnt = route.getStart().getLocation().getId();
                                for (TourActivity act : route.getActivities()) {
                                    costs += vrp.getTransportCosts().getTransportCost(prevAct.getLocation(), act.getLocation(), prevAct.getEndTime(), route.getDriver(), route.getVehicle());
                                    costs += vrp.getActivityCosts().getActivityCost(act, act.getArrTime(), route.getDriver(), route.getVehicle());
//                                    String puntoAct = act.getLocation().getName();
                                    String puntoAct = serviciopunto.get(act.getLocation().getId());
                                    if (!puntoAnt.equalsIgnoreCase(puntoAct) && vrp.getTransportCosts().getTransportCost(prevAct.getLocation(), act.getLocation(), prevAct.getEndTime(), route.getDriver(), route.getVehicle()) < 3) { // calculo el costo si no son iguales y la distancia es mnenos de 2 metros
                                        costs += 100000; // costo por el ingreso la primera vez al punto
                                    }
//                                    if ((act.getTheoreticalEarliestOperationStartTime() - act.getArrTime()) > 0.8) {
//                                        costs += 100000 * Math.floor(act.getTheoreticalEarliestOperationStartTime() - act.getArrTime());
//                                    }
                                    prevAct = act;
                                    puntoAnt = puntoAct;
                                }
                                costs += vrp.getTransportCosts().getTransportCost(prevAct.getLocation(), route.getEnd().getLocation(), prevAct.getEndTime(), route.getDriver(), route.getVehicle());
                                costs += softConstraintContributionToOverallObjective.getCosts(route);
                                if (route.getTourActivities().getActivities().size() < 9) {
                                    costs += 100000;
                                }
                            }
                            costs += solution.getUnassignedJobs().size() * costo_servicio_no_asignado;
                            return costs;
                        }
                    })
                    .buildAlgorithm();


...............
..........

   static class RewardAndPenaltiesThroughSoftConstraints {

        private VehicleRoutingProblem vrp;
        Map<String, String> serviciocomp = new HashMap<String, String>();
        private StateManager stateManager;

        public RewardAndPenaltiesThroughSoftConstraints(VehicleRoutingProblem vrp, Map<String, String> serviciocompb, StateManager stateManager) {
            super();
            this.vrp = vrp;
            this.serviciocomp = serviciocompb;
            this.stateManager = stateManager;
        }

        public double getCosts(VehicleRoute route) {
            int costo = 0;
            TourActivity lastAct = route.getStart();
            for (TourActivity act : route.getActivities()) {
                String servicio1 = act.getLocation().getId();
                String servicio2 = serviciocomp.get(servicio1);
                if (servicio2 != null) {
                    VehicleRoute route0 = stateManager.getProblemState(stateManager.createStateId(servicio2), VehicleRoute.class);
                    if (route0 == null) {
                        costo += 100000000;
                        continue;
                    }
                    if (route0 != route) {
                        costo += 100000000;
                        continue;
                    }
                    if (act instanceof JobActivity && lastAct instanceof JobActivity) {
                        if (((JobActivity) act).getJob().getId().equals(servicio1)) {
                            if (((JobActivity) lastAct).getJob().getId().equals(servicio2)) {
                                costo += 0;
                            } else {
                                costo += 100000000;
                                continue;
                            }
                        }
                    }

                }

                lastAct = act;
            }

            return costo;
        }

        private Job getJob(String string) {
            return vrp.getJobs().get(string);
        }
    }


    static class TramosSimultaneosConstraint implements HardActivityConstraint {

        private StateManager stateManager;
        Map<String, String> servicioasoc = new HashMap<String, String>();

        public TramosSimultaneosConstraint(StateManager stateManager, Map<String, String> serviciocompb) {
            super();
            this.stateManager = stateManager;
            this.servicioasoc = serviciocompb;
        }

        @Override
        public ConstraintsStatus fulfilled(JobInsertionContext iFacts, TourActivity prevAct, TourActivity newAct, TourActivity nextAct, double prevActDepTime) {

            String servicio1 = newAct.getLocation().getId();
            String servicio2 = servicioasoc.get(servicio1);

            if (servicio2 != null) {
                if (prevAct instanceof JobActivity && newAct instanceof JobActivity) {
                    if (((JobActivity) prevAct).getJob().getId().equals(servicio1) && ((JobActivity) newAct).getJob().getId().equals(servicio2)) {
                        return ConstraintsStatus.FULFILLED;
                    }
                    if (((JobActivity) newAct).getJob().getId().equals(servicio2)) {
                        return ConstraintsStatus.NOT_FULFILLED_BREAK;
                    }
                }
                if (newAct instanceof JobActivity && nextAct instanceof JobActivity) {
                    if (((JobActivity) newAct).getJob().getId().equals(servicio2) && ((JobActivity) nextAct).getJob().getId().equals(servicio1)) {
                        return ConstraintsStatus.FULFILLED;
                    }
                    if (((JobActivity) nextAct).getJob().getId().equals(servicio1)) {
                        return ConstraintsStatus.NOT_FULFILLED_BREAK;
                    }
                }

            }
            return ConstraintsStatus.FULFILLED;
        }

    }

Its possible create a constraint (HardRouteConstraint) where i can add a job in the route if insert a other job ?
for example:

static class TramosEnRutaSimultaneosConstraint implements HardRouteConstraint {

    private StateManager stateManager;
    Map<String, String> serviciocomp = new HashMap<String, String>();

    public TramosEnRutaSimultaneosConstraint(StateManager stateManager, Map<String, String> serviciocompb) {
        super();
        this.stateManager = stateManager;
        this.serviciocomp = serviciocompb;
    }

    @Override
    public boolean fulfilled(JobInsertionContext insertionContext) {
        String Servicio1 = insertionContext.getJob().getId();
        **String servicio2 = serviciocomp.get(Servicio1);**  // job i can need insert
       
        if (servicio2 != null) {
            VehicleRoute route = stateManager.getProblemState(stateManager.createStateId(servicio2), VehicleRoute.class);
            if (route != null) {
                if (route != insertionContext.getRoute()) {
                    return false;
                }
            }
        }
        return true;
    }
}

Just want to make sure I understand your question correctly:

Say you have a group of jobs A, B and C, you would like them to be served by the same vehicle, or, if one of them has to be unassigned, all should be unassigned.

Is the above what you want?

Best regards,
He