Thanks a lot @jie31best for you support.
I’m getting very close to a general solution.
I had to make changes to
Shipment
class and
PickupShipment
class and then implemented the
PickupLocations
interface and the
PickupLocationsImpl
class just like for TimeWindows.
The major changes were done to
ShipmentInsertionCalculator.getInsertionData
and it seems to work fine.
The only thing so far not working as intended is that the solutions aren’t considering the
vehicleBuilder.setLatestArrival
and i’m getting solutions that go beyond the latestArrival.
Any ideas why?
I’m pasting the code below.
> /**
> * Calculates the marginal cost of inserting job i locally. This is based on the
> * assumption that cost changes can entirely covered by only looking at the predecessor i-1 and its successor i+1.
> */
> @Override
> public InsertionData getInsertionData(final VehicleRoute currentRoute, final Job jobToInsert, final Vehicle newVehicle, double newVehicleDepartureTime, final Driver newDriver, final double bestKnownCosts) {
> JobInsertionContext insertionContext = new JobInsertionContext(currentRoute, jobToInsert, newVehicle, newDriver, newVehicleDepartureTime);
> Shipment shipment = (Shipment) jobToInsert;
> TourActivity pickupShipment = activityFactory.createActivities(shipment).get(0);
> TourActivity deliverShipment = activityFactory.createActivities(shipment).get(1);
> insertionContext.getAssociatedActivities().add(pickupShipment);
> insertionContext.getAssociatedActivities().add(deliverShipment);
>
> /*
> check hard route constraints
> */
> InsertionData noInsertion = checkRouteContraints(insertionContext, constraintManager);
> if (noInsertion != null) {
> return noInsertion;
> }
> /*
> check soft route constraints
> */
> double additionalICostsAtRouteLevel = softRouteConstraint.getCosts(insertionContext);
>
> double bestCost = bestKnownCosts;
> additionalICostsAtRouteLevel += additionalAccessEgressCalculator.getCosts(insertionContext);
>
> int pickupInsertionIndex = InsertionData.NO_INDEX;
> int deliveryInsertionIndex = InsertionData.NO_INDEX;
>
> TimeWindow bestPickupTimeWindow = null;
> TimeWindow bestDeliveryTimeWindow = null;
> Location bestPickupLocation = null;
>
> Start start = new Start(newVehicle.getStartLocation(), newVehicle.getEarliestDeparture(), newVehicle.getLatestArrival());
> start.setEndTime(newVehicleDepartureTime);
>
> End end = new End(newVehicle.getEndLocation(), 0.0, newVehicle.getLatestArrival());
>
> ActivityContext pickupContext = new ActivityContext();
>
> TourActivity prevAct = start;
> double prevActEndTime = newVehicleDepartureTime;
>
> List<TourActivity> activities = currentRoute.getTourActivities().getActivities();
>
> List<String> failedActivityConstraints = new ArrayList<>();
> //loops
> int i = 0;
> boolean tourEnd = false;
> //pickupShipmentLoop
> while (!tourEnd) {
> TourActivity nextAct;
> if (i < activities.size()) {
> nextAct = activities.get(i);
> }
> else {
> nextAct = end;
> tourEnd = true;
> }
>
> boolean pickupInsertionNotFulfilledBreak = true;
> for (Location pickupLocation : shipment.getPickupLocations()) {
> for (TimeWindow pickupTimeWindow : shipment.getPickupTimeWindows()) {
> pickupShipment.setTheoreticalEarliestOperationStartTime(pickupTimeWindow.getStart());
> pickupShipment.setTheoreticalLatestOperationStartTime(pickupTimeWindow.getEnd());
> ((PickupShipment) pickupShipment).setLocation(pickupLocation);
> ActivityContext activityContext = new ActivityContext();
> activityContext.setInsertionIndex(i);
> insertionContext.setActivityContext(activityContext);
> ConstraintsStatus pickupShipmentConstraintStatus = fulfilled(insertionContext, prevAct, pickupShipment, nextAct, prevActEndTime, failedActivityConstraints, constraintManager);
> if (pickupShipmentConstraintStatus.equals(ConstraintsStatus.NOT_FULFILLED)) {
> pickupInsertionNotFulfilledBreak = false;
> continue;
> }
> else if (pickupShipmentConstraintStatus.equals(ConstraintsStatus.NOT_FULFILLED_BREAK)) {
> continue;
> }
> else if (pickupShipmentConstraintStatus.equals(ConstraintsStatus.FULFILLED)) {
> pickupInsertionNotFulfilledBreak = false;
> }
> double additionalPickupICosts = softActivityConstraint.getCosts(insertionContext, prevAct, pickupShipment, nextAct, prevActEndTime);
> double pickupAIC = calculate(insertionContext, prevAct, pickupShipment, nextAct, prevActEndTime);
>
> TourActivity prevAct_deliveryLoop = pickupShipment;
> double shipmentPickupArrTime = prevActEndTime + transportCosts.getTransportTime(prevAct.getLocation(), pickupShipment.getLocation(), prevActEndTime, newDriver, newVehicle);
> double shipmentPickupEndTime = Math.max(shipmentPickupArrTime, pickupShipment.getTheoreticalEarliestOperationStartTime()) + activityCosts.getActivityDuration(pickupShipment, shipmentPickupArrTime, newDriver, newVehicle);
>
> pickupContext.setArrivalTime(shipmentPickupArrTime);
> pickupContext.setEndTime(shipmentPickupEndTime);
> pickupContext.setInsertionIndex(i);
> insertionContext.setRelatedActivityContext(pickupContext);
>
> double prevActEndTime_deliveryLoop = shipmentPickupEndTime;
>
> /*
> --------------------------------
> */
> //deliverShipmentLoop
> int j = i;
> boolean tourEnd_deliveryLoop = false;
> while (!tourEnd_deliveryLoop) {
> TourActivity nextAct_deliveryLoop;
> if (j < activities.size()) {
> nextAct_deliveryLoop = activities.get(j);
> }
> else {
> nextAct_deliveryLoop = end;
> tourEnd_deliveryLoop = true;
> }
>
> boolean deliveryInsertionNotFulfilledBreak = true;
> for (TimeWindow deliveryTimeWindow : shipment.getDeliveryTimeWindows()) {
> deliverShipment.setTheoreticalEarliestOperationStartTime(deliveryTimeWindow.getStart());
> deliverShipment.setTheoreticalLatestOperationStartTime(deliveryTimeWindow.getEnd());
> ActivityContext activityContext_ = new ActivityContext();
> activityContext_.setInsertionIndex(j);
> insertionContext.setActivityContext(activityContext_);
> ConstraintsStatus deliverShipmentConstraintStatus = fulfilled(insertionContext, prevAct_deliveryLoop, deliverShipment, nextAct_deliveryLoop, prevActEndTime_deliveryLoop, failedActivityConstraints, constraintManager);
> if (deliverShipmentConstraintStatus.equals(ConstraintsStatus.FULFILLED)) {
> double additionalDeliveryICosts = softActivityConstraint.getCosts(insertionContext, prevAct_deliveryLoop, deliverShipment, nextAct_deliveryLoop, prevActEndTime_deliveryLoop);
> double deliveryAIC = calculate(insertionContext, prevAct_deliveryLoop, deliverShipment, nextAct_deliveryLoop, prevActEndTime_deliveryLoop);
> double totalActivityInsertionCosts = pickupAIC + deliveryAIC
> + additionalICostsAtRouteLevel + additionalPickupICosts + additionalDeliveryICosts;
> if (totalActivityInsertionCosts < bestCost) {
> bestCost = totalActivityInsertionCosts;
> pickupInsertionIndex = i;
> deliveryInsertionIndex = j;
> bestPickupTimeWindow = pickupTimeWindow;
> bestDeliveryTimeWindow = deliveryTimeWindow;
> bestPickupLocation = pickupLocation;
> }
> deliveryInsertionNotFulfilledBreak = false;
> }
> else if (deliverShipmentConstraintStatus.equals(ConstraintsStatus.NOT_FULFILLED)) {
> deliveryInsertionNotFulfilledBreak = false;
> }
> }
> if (deliveryInsertionNotFulfilledBreak) {
> break;
> }
> //update prevAct and endTime
> double nextActArrTime = prevActEndTime_deliveryLoop + transportCosts.getTransportTime(prevAct_deliveryLoop.getLocation(), nextAct_deliveryLoop.getLocation(), prevActEndTime_deliveryLoop, newDriver, newVehicle);
> prevActEndTime_deliveryLoop = Math.max(nextActArrTime, nextAct_deliveryLoop.getTheoreticalEarliestOperationStartTime()) + activityCosts.getActivityDuration(nextAct_deliveryLoop, nextActArrTime, newDriver, newVehicle);
> prevAct_deliveryLoop = nextAct_deliveryLoop;
> j++;
> }
> }
> }
> if (pickupInsertionNotFulfilledBreak) {
> break;
> }
> //update prevAct and endTime
> double nextActArrTime = prevActEndTime + transportCosts.getTransportTime(prevAct.getLocation(), nextAct.getLocation(), prevActEndTime, newDriver, newVehicle);
> prevActEndTime = Math.max(nextActArrTime, nextAct.getTheoreticalEarliestOperationStartTime()) + activityCosts.getActivityDuration(nextAct, nextActArrTime, newDriver, newVehicle);
> prevAct = nextAct;
> i++;
> }
> if (pickupInsertionIndex == InsertionData.NO_INDEX) {
> InsertionData emptyInsertionData = new InsertionData.NoInsertionFound();
> emptyInsertionData.getFailedConstraintNames().addAll(failedActivityConstraints);
> return emptyInsertionData;
> }
> InsertionData insertionData = new InsertionData(bestCost, pickupInsertionIndex, deliveryInsertionIndex, newVehicle, newDriver);
> pickupShipment.setTheoreticalEarliestOperationStartTime(bestPickupTimeWindow.getStart());
> pickupShipment.setTheoreticalLatestOperationStartTime(bestPickupTimeWindow.getEnd());
> ((PickupShipment) pickupShipment).setLocation(bestPickupLocation);
> deliverShipment.setTheoreticalEarliestOperationStartTime(bestDeliveryTimeWindow.getStart());
> deliverShipment.setTheoreticalLatestOperationStartTime(bestDeliveryTimeWindow.getEnd());
> insertionData.setVehicleDepartureTime(newVehicleDepartureTime);
> insertionData.getEvents().add(new InsertActivity(currentRoute, newVehicle, deliverShipment, deliveryInsertionIndex));
> insertionData.getEvents().add(new InsertActivity(currentRoute, newVehicle, pickupShipment, pickupInsertionIndex));
> insertionData.getEvents().add(new SwitchVehicle(currentRoute, newVehicle, newVehicleDepartureTime));
> return insertionData;
> }