diff --git a/CMakeLists.txt b/CMakeLists.txt index 888b4e7243a2a953d117a96c16dafdb934172b81..f8a955139444e0e665ed16ec92f2cc1a3d7a5944 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,6 +28,7 @@ add_executable(pdptw src/mains/main.cpp src/lns/solution/solution.cpp src/lns/constraints/constraint.cpp src/lns/constraints/capacity/capacity_constraint.cpp + src/lns/constraints/time_window/forward_time_slack.cpp src/lns/constraints/time_window/time_window_constraint.cpp src/lns/modification/pair/insert_pair.cpp src/lns/modification/pair/remove_pair.cpp diff --git a/src/config.h b/src/config.h index 886eae394f4365f2fd93665616595ba2fc329d23..967603c0225653fd5396c98d021520b46557e65b 100644 --- a/src/config.h +++ b/src/config.h @@ -1,5 +1,3 @@ - - const int EXCLUSION_PENALTY = 100; const int RANDOM_SEED = 100; diff --git a/src/input/data.cpp b/src/input/data.cpp index a5dc436ea3aab8f884da6eff0009cc5beb1cd0ea..817d0be461f9bd5b51411a2acc3187d87188e910 100644 --- a/src/input/data.cpp +++ b/src/input/data.cpp @@ -24,11 +24,11 @@ double data::routeCost(PDPTWData const & data, Route const & route) { const Matrix & matrix = data.getMatrix(); const std::vector<int> & routeIDs = route.getRoute(); - double cost = 0; + double cost = 0.0; if (routeIDs.empty()) { - return 0; + return 0.0; } // cost from and to the depot cost += matrix.at(0).at(routeIDs.at(0)); diff --git a/src/lns/constraints/capacity/capacity_constraint.cpp b/src/lns/constraints/capacity/capacity_constraint.cpp index 7ff89d58d1b005d6408437dba0f62cccae177255..2827d98c1537c24d522afc7439ca63b49d72e95a 100644 --- a/src/lns/constraints/capacity/capacity_constraint.cpp +++ b/src/lns/constraints/capacity/capacity_constraint.cpp @@ -1,94 +1,94 @@ #include "capacity_constraint.h" - CapacityConstraint::CapacityConstraint(Solution const &solution) : Constraint(solution) { // Init an empty vector of reach time for all routes - //std::vector<CapacityVector> routeCapacities(getSolution().getRoutes().size(), CapacityVector()); routeCapacities.resize(getSolution().getRoutes().size()); } -std::unique_ptr<Constraint> CapacityConstraint::clone(Solution const &newOwningSolution) const { +std::unique_ptr<Constraint> CapacityConstraint::clone(Solution const &newOwningSolution) const +{ std::unique_ptr<CapacityConstraint> clonePtr = std::make_unique<CapacityConstraint>(newOwningSolution); clonePtr->routeCapacities = routeCapacities; return clonePtr; } - -void CapacityConstraint::initCapacities() +void CapacityConstraint::initRouteCapacities() { - routeCapacities = std::vector<CapacityVector>(); - for (const Route& route : getSolution().getRoutes()) + routeCapacities.clear(); + for (Route const &route: getSolution().getRoutes()) { CapacityVector capacities; int currentCapacity = 0; - - for (int locationID : route.getRoute()) + + for (int locationID: route.getRoute()) { currentCapacity += getSolution().getData().getLocation(locationID).getDemand(); capacities.push_back(currentCapacity); } - + routeCapacities.push_back(capacities); } } -std::vector<int> const & CapacityConstraint::getRouteCapacities(int routeIndex) const +std::vector<int> const &CapacityConstraint::getRouteCapacities(int routeIndex) const { return routeCapacities.at(routeIndex); } -// check for every location between the pickupPosition and the deliveryPosition -// not ideal +/* + * Check if an insertion is feasible by computing new capacity values between the PickupPosition and DeliveryPosition. + * Assume the constraint is valid beforehand. + * O(n), n the size of the route + */ bool CapacityConstraint::checkModif(Pair const &pair, int routeIndex, int PickupPosition, int DeliveryPosition) const { int max_capacity = getSolution().getData().getCapacity(); + int demand = pair.getPickup().getDemand(); + CapacityVector const &routeCapacity = getRouteCapacities(routeIndex); - // guardrail - if (pair.getPickup().getDemand() > max_capacity) + // if PickupPosition != DeliveryPosition, check from pickup-1 to delivery-1 + // if PickupPosition is 0, don't check -1 ... + for (int i = std::max(0, PickupPosition - 1); i < DeliveryPosition; i++) { - return false; - } - - // check if the disponible capacity is enough are not - for (int i = PickupPosition; i <= DeliveryPosition; ++i) { - - if ((i!=0) && (getRouteCapacities(routeIndex).at(i-1) + pair.getPickup().getDemand() >= max_capacity)) + if (routeCapacity.at(i) + demand > max_capacity) { return false; } - } + } return true; } // update the route and the weight -// not ideal -void CapacityConstraint::applyModif(Pair const &pair, int routeIndex, int PickupPosition, int DeliveryPosition, bool addPair) +void CapacityConstraint::applyModif(Pair const &pair, int routeIndex, int PickupPosition, int DeliveryPosition, + bool addPair) { if (addPair) - { + { // Insert new values - routeCapacities.at(routeIndex).insert(routeCapacities.at(routeIndex).begin()+DeliveryPosition, 0); + routeCapacities.at(routeIndex).insert(routeCapacities.at(routeIndex).begin() + DeliveryPosition, 0); if (DeliveryPosition != 0) { - routeCapacities.at(routeIndex).at(DeliveryPosition) += routeCapacities.at(routeIndex).at(DeliveryPosition-1); + routeCapacities.at(routeIndex).at(DeliveryPosition) += + routeCapacities.at(routeIndex).at(DeliveryPosition - 1); } - routeCapacities.at(routeIndex).insert(routeCapacities.at(routeIndex).begin()+PickupPosition, pair.getPickup().getDemand()); + routeCapacities.at(routeIndex) + .insert(routeCapacities.at(routeIndex).begin() + PickupPosition, pair.getPickup().getDemand()); if (PickupPosition != 0) { - routeCapacities.at(routeIndex).at(PickupPosition) += routeCapacities.at(routeIndex).at(PickupPosition-1); + routeCapacities.at(routeIndex).at(PickupPosition) += routeCapacities.at(routeIndex).at(PickupPosition - 1); } // Update value - for (int i = PickupPosition + 1; i < DeliveryPosition + 1; ++i) + for (int i = PickupPosition + 1; i < DeliveryPosition + 1; ++i) { routeCapacities.at(routeIndex).at(i) += pair.getPickup().getDemand(); - } + } } - else + else { - for (int i = PickupPosition + 1; i < DeliveryPosition; ++i) + for (int i = PickupPosition + 1; i < DeliveryPosition; ++i) { routeCapacities.at(routeIndex).at(i) += pair.getDelivery().getDemand(); } @@ -96,48 +96,48 @@ void CapacityConstraint::applyModif(Pair const &pair, int routeIndex, int Pickup // remove pair routeCapacities.at(routeIndex).erase(routeCapacities.at(routeIndex).begin() + DeliveryPosition); routeCapacities.at(routeIndex).erase(routeCapacities.at(routeIndex).begin() + PickupPosition); - } + } } - bool CapacityConstraint::check(InsertPair const &op) const { //std::cout << " #Capa Check"; return checkModif(op.getPair(), op.getRouteIndex(), op.getPickupInsertion(), op.getDeliveryInsertion()); } + void CapacityConstraint::apply(InsertPair const &op) { //std::cout << "-> Apply Modification on Capacity \n"; applyModif(op.getPair(), op.getRouteIndex(), op.getPickupInsertion(), op.getDeliveryInsertion(), true); } - bool CapacityConstraint::check(InsertRoute const &op) const { return true; } + void CapacityConstraint::apply(InsertRoute const &op) { // add new empty route routeCapacities.emplace_back(); -} - +} bool CapacityConstraint::check(RemovePair const &op) const { // Remove a pair (always true) return true; } + void CapacityConstraint::apply(RemovePair const &op) { applyModif(op.getPair(), op.getRouteIndex(), op.getPickupDeletion(), op.getDeliveryDeletion(), false); } - bool CapacityConstraint::check(RemoveRoute const &op) const { return true; -} +} + void CapacityConstraint::apply(RemoveRoute const &op) { // add new empty route @@ -148,10 +148,10 @@ void CapacityConstraint::print() const { std::cout << "Used capacity : \n"; int i = 0; - for (const auto& capaVector : routeCapacities) + for (auto const &capaVector: routeCapacities) { std::cout << "#" << i << " : "; - for (const int capa : capaVector) + for (int const capa: capaVector) { std::cout << capa << ", "; } @@ -159,4 +159,3 @@ void CapacityConstraint::print() const i++; } } - diff --git a/src/lns/constraints/capacity/capacity_constraint.h b/src/lns/constraints/capacity/capacity_constraint.h index 1ae640370102d3c9bf2684ed2ab84cc45fd402b3..059bdd3dc91289a55a0184430ac94281747b7411 100644 --- a/src/lns/constraints/capacity/capacity_constraint.h +++ b/src/lns/constraints/capacity/capacity_constraint.h @@ -1,25 +1,25 @@ #pragma once -#include <vector> - #include "lns/constraints/constraint.h" - -#include "lns/solution/solution.h" #include "lns/modification/pair/insert_pair.h" #include "lns/modification/pair/remove_pair.h" #include "lns/modification/route/insert_route.h" #include "lns/modification/route/remove_route.h" +#include "lns/solution/solution.h" + +#include <vector> /** * Capacity constraint. - * To check this, we keep track of the sum of all requests and check it against the max capacity. - * TO DO, verification in O(1) by storing a matrix ! */ class CapacityConstraint : public Constraint { public: using CapacityVector = std::vector<int>; -private: +private: + /** + * Store the used capacity value when leaving one location in each route. + */ std::vector<CapacityVector> routeCapacities; void apply(InsertPair const &op) override; @@ -31,22 +31,21 @@ private: bool check(InsertRoute const &op) const override; bool check(RemovePair const &op) const override; bool check(RemoveRoute const &op) const override; -public: +public: explicit CapacityConstraint(Solution const &); CapacityConstraint(CapacityConstraint const &) = default; std::unique_ptr<Constraint> clone(Solution const &newOwningSolution) const override; /* - * Given the solution, calculate the capacities when leaving each location - * Modify the routeCapacities vector ! - */ - void initCapacities(); + * Compute from scratch the routeCapacities vector. + */ + void initRouteCapacities(); /* - * Return capacity vector of a route - */ + * Return capacity vector of a route + */ CapacityVector const &getRouteCapacities(int routeIndex) const; /* @@ -62,7 +61,7 @@ public: /* * Update the weight */ - void applyModif(Pair const &pair, int routeIndex, int PickupPosition, int DeliveryPosition, bool addPair); + void applyModif(Pair const &pair, int routeIndex, int PickupPosition, int DeliveryPosition, bool addPair); void print() const override; }; \ No newline at end of file diff --git a/src/lns/constraints/time_window/forward_time_slack.cpp b/src/lns/constraints/time_window/forward_time_slack.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8225ee764943ad8f3b1dbc1d11a1803524f8ee53 --- /dev/null +++ b/src/lns/constraints/time_window/forward_time_slack.cpp @@ -0,0 +1,301 @@ +#include "forward_time_slack.h" + +#include "input/data.h" +#include "lns/solution/solution.h" + +std::vector<TimeInteger> const &ForwardTimeSlack::getFTS() const +{ + return FTS; +} + +std::vector<TimeInteger> const &ForwardTimeSlack::getEarliestArrival() const +{ + return earliestArrival; +} + +std::vector<TimeInteger> const &ForwardTimeSlack::getLatestArrival() const +{ + return latestArrival; +} + +void ForwardTimeSlack::initFTS(PDPTWData const &data, Route const &route) +{ + int n = route.getSize(); + std::vector<int> const &routeIDs = route.getRoute(); + + earliestArrival = std::vector<TimeInteger>(n + 1, 0); + latestArrival = std::vector<TimeInteger>(n + 1, 0); + FTS = std::vector<TimeInteger>(n + 1, 0); + + // Compute arrival times + double depotStart = data.getDepot().getTimeWindow().getStart(); + double firstLocationStart = data.getLocation(routeIDs.at(0)).getTimeWindow().getStart(); + double depotToFirst = data::TravelTime(data, 0, routeIDs.at(0)); + + earliestArrival.at(0) = std::max(firstLocationStart, depotStart + depotToFirst); + + for (int i = 1; i < n; ++i) + { + double start = data.getLocation(routeIDs.at(i)).getTimeWindow().getStart(); + double service = data.getLocation(routeIDs.at(i - 1)).getServiceDuration(); + double travelTime = data::TravelTime(data, routeIDs.at(i - 1), routeIDs.at(i)); + + earliestArrival.at(i) = std::max(start, earliestArrival.at(i - 1) + service + travelTime); + } + + // Compute earliest arrival at depot after last location + double lastService = data.getLocation(routeIDs.at(n - 1)).getServiceDuration(); + double lastToDepot = data::TravelTime(data, routeIDs.at(n - 1), 0); + earliestArrival.at(n) = earliestArrival.at(n - 1) + lastService + lastToDepot; + + // Compute latest arrival times + latestArrival.at(n - 1) = data.getLocation(routeIDs.at(n - 1)).getTimeWindow().getEnd(); + + for (int i = n - 2; i >= 0; --i) + { + double end = data.getLocation(routeIDs.at(i)).getTimeWindow().getEnd(); + double service = data.getLocation(routeIDs.at(i)).getServiceDuration(); + double travelTime = data::TravelTime(data, routeIDs.at(i), routeIDs.at(i + 1)); + + latestArrival.at(i) = std::min(latestArrival.at(i + 1) - service - travelTime, end); + } + + // latest arrival at depot + latestArrival.at(n) = data.getDepot().getTimeWindow().getEnd(); + + // Compute FTS + for (int i = 0; i <= n; ++i) + { + FTS.at(i) = latestArrival.at(i) - earliestArrival.at(i); + } +} + +bool ForwardTimeSlack::isPickupDeliveryInsertionValid(PDPTWData const &data, Route const &route, int pickupID, + int deliveryID, int insertPickupIndex, + int insertDeliveryIndex) const +{ + std::vector<int> const &routeIDs = route.getRoute(); + int n = route.getSize(); + + // safety + if (insertPickupIndex > insertDeliveryIndex) + { + return false; + } + + // if the route is empty, only check de time window + if (routeIDs.empty()) + { + double startTW = data.getLocation(pickupID).getTimeWindow().getStart(); + double endTW = data.getLocation(deliveryID).getTimeWindow().getEnd(); + + return (insertPickupIndex == 0) && (insertDeliveryIndex == 0) && + (!(startTW > data.getDepot().getTimeWindow().getEnd() || + endTW < data.getDepot().getTimeWindow().getStart())); + } + + + // Algo + + // Pickup + // two specific cases, insertion in first or last + int prev = (insertPickupIndex == 0) ? 0 : routeIDs.at(insertPickupIndex - 1); + int next = (insertPickupIndex == n) ? 0 : routeIDs.at(insertPickupIndex); + + double arrivalPrev = + (prev == 0) ? data.getDepot().getTimeWindow().getStart() : getEarliestArrival().at(insertPickupIndex - 1); + double travelToNew = data::TravelTime(data, prev, pickupID); + double serviceNew = data.getLocation(pickupID).getServiceDuration(); + double travelNewToNext = data::TravelTime(data, pickupID, next); + + double newArrival = std::max(data.getLocation(pickupID).getTimeWindow().getStart(), + arrivalPrev + data.getLocation(prev).getServiceDuration() + travelToNew); + + + // Check TW + if (!(data.getLocation(pickupID).getTimeWindow().isValid(newArrival))) + { + return false; + } + + // Calculate the pickup duration + double arrivalNext = + std::max(data.getLocation(next).getTimeWindow().getStart(), newArrival + serviceNew + travelNewToNext); + + double pickupDuration = arrivalNext - earliestArrival.at(insertPickupIndex); + // Check FTS + if (/*next != 0 &&*/ pickupDuration >= getFTS().at(insertPickupIndex)) + { + return false; + } + + // Delivery + // if insertDeliveryIndex == insertPickupIndex, then Pickup is just before Delivery in the route + int prevDelivery = (insertDeliveryIndex == insertPickupIndex) ? pickupID : routeIDs.at(insertDeliveryIndex - 1); + int nextDelivery = (insertDeliveryIndex >= n) ? 0 : routeIDs.at(insertDeliveryIndex); + + // We have to take into account the duration of the pickup + // (previous earliest arrival) + (time taken by the pickup) - (time of traveling between prev and next) + double arrivalPrevDelivery = + (prevDelivery == pickupID) ? newArrival : getEarliestArrival().at(insertDeliveryIndex - 1) + pickupDuration; + + double travelToDelivery = data::TravelTime(data, prevDelivery, deliveryID); + double serviceDelivery = data.getLocation(deliveryID).getServiceDuration(); + double travelDeliveryToNext = data::TravelTime(data, deliveryID, nextDelivery); + double newArrivalDelivery = + std::max(data.getLocation(deliveryID).getTimeWindow().getStart(), + arrivalPrevDelivery + data.getLocation(prevDelivery).getServiceDuration() + travelToDelivery); + + // Check TW + if (!(data.getLocation(deliveryID).getTimeWindow().isValid(newArrivalDelivery))) + { + return false; + } + + // Calculate the delivery duration + double arrivalNextDelivery = std::max(data.getLocation(nextDelivery).getTimeWindow().getStart(), + newArrivalDelivery + serviceDelivery + travelDeliveryToNext); + double deliveryDuration = arrivalNextDelivery - earliestArrival.at(insertDeliveryIndex); + + // Check FTS validity + if (/*nextDelivery != 0 &&*/ deliveryDuration >= getFTS().at(insertDeliveryIndex)) + { + return false; + } + + return true; +} + +// TO DO some cuts in the loop to avoid recalculate everything +void ForwardTimeSlack::updateFTSAfterInsertion(PDPTWData const &data, Route const &route, int insertPickupIndex, + int insertDeliveryIndex) +{ + int n = route.getSize(); + std::vector<int> const &routeIDs = route.getRoute(); + + // Resize vectors to match the updated route size + earliestArrival.resize(n + 1, 0); + latestArrival.resize(n + 1, 0); + FTS.resize(n + 1, 0); + + // Recalculate earliest arrival times + for (int i = insertPickupIndex; i <= n; ++i) + { + if (i == 0)// specific case for the first element + { + double depotStart = data.getDepot().getTimeWindow().getStart(); + double firstLocationStart = data.getLocation(routeIDs.at(0)).getTimeWindow().getStart(); + double depotToFirst = data::TravelTime(data, 0, routeIDs.at(0)); + earliestArrival.at(0) = std::max(firstLocationStart, depotStart + depotToFirst); + } + else + { + int prev = routeIDs.at(i - 1); + int curr = (i == n) ? 0 : routeIDs.at(i);// specific case for the last element + double start = data.getLocation(curr).getTimeWindow().getStart(); + double service = data.getLocation(prev).getServiceDuration(); + double travelTime = data::TravelTime(data, prev, curr); + + earliestArrival.at(i) = std::max(start, earliestArrival.at(i - 1) + service + travelTime); + } + } + + // try add element in front then recalculate from the delivery index + // Recalculate latest arrival times + latestArrival.at(n) = data.getDepot().getTimeWindow().getEnd(); + latestArrival.at(n - 1) = data.getLocation(routeIDs.at(n - 1)).getTimeWindow().getEnd(); + for (int i = n - 2; i >= 0; --i) + { + double end = data.getLocation(routeIDs.at(i)).getTimeWindow().getEnd(); + double service = data.getLocation(routeIDs.at(i)).getServiceDuration(); + double travelTime = data::TravelTime(data, routeIDs.at(i), routeIDs.at(i + 1)); + + latestArrival.at(i) = std::min(latestArrival.at(i + 1) - service - travelTime, end); + } + + // Recalculate FTS + for (int i = 0; i <= n; ++i) + { + FTS.at(i) = latestArrival.at(i) - earliestArrival.at(i); + } +} + +// TO DO some cuts in the loop to avoid recalculate everything +void ForwardTimeSlack::updateFTSAfterDeletion(PDPTWData const &data, Route const &route, int removePickupIndex, + int removeDeliveryIndex) +{ + int n = route.getSize(); + std::vector<int> const &routeIDs = route.getRoute(); + + // empty route + if (n == 0) + { + earliestArrival.clear(); + latestArrival.clear(); + FTS.clear(); + return; + } + + // Resize vectors to match the updated route size + earliestArrival.resize(n + 1, 0); + latestArrival.resize(n + 1, 0); + FTS.resize(n + 1, 0); + + // Recalculate earliest arrival times + for (int i = 0; i <= n; ++i) + { + if (i == 0)// Specific case for the first element + { + double depotStart = data.getDepot().getTimeWindow().getStart(); + double firstLocationStart = data.getLocation(routeIDs.at(0)).getTimeWindow().getStart(); + double depotToFirst = data::TravelTime(data, 0, routeIDs.at(0)); + earliestArrival.at(0) = std::max(firstLocationStart, depotStart + depotToFirst); + } + else + { + int prev = routeIDs.at(i - 1); + int curr = (i == n) ? 0 : routeIDs.at(i); + double start = data.getLocation(curr).getTimeWindow().getStart(); + double service = data.getLocation(prev).getServiceDuration(); + double travelTime = data::TravelTime(data, prev, curr); + + earliestArrival.at(i) = std::max(start, earliestArrival.at(i - 1) + service + travelTime); + } + } + + // Recalculate latest arrival times + latestArrival.at(n) = data.getDepot().getTimeWindow().getEnd(); + latestArrival.at(n - 1) = data.getLocation(routeIDs.at(n - 1)).getTimeWindow().getEnd(); + for (int i = n - 2; i >= 0; --i) + { + double end = data.getLocation(routeIDs.at(i)).getTimeWindow().getEnd(); + double service = data.getLocation(routeIDs.at(i)).getServiceDuration(); + double travelTime = data::TravelTime(data, routeIDs.at(i), routeIDs.at(i + 1)); + + latestArrival.at(i) = std::min(latestArrival.at(i + 1) - service - travelTime, end); + } + + // Recalculate FTS + for (int i = 0; i <= n; ++i) + { + FTS.at(i) = latestArrival.at(i) - earliestArrival.at(i); + } +} + +void ForwardTimeSlack::print() const +{ + for (TimeInteger timeVal: earliestArrival) + { + std::cout << timeVal << " "; + } + std::cout << std::endl; + for (TimeInteger timeVal: latestArrival) + { + std::cout << timeVal << " "; + } + std::cout << std::endl; + for (TimeInteger ftsVal: FTS) + { + std::cout << ftsVal << " "; + } +} \ No newline at end of file diff --git a/src/lns/constraints/time_window/forward_time_slack.h b/src/lns/constraints/time_window/forward_time_slack.h new file mode 100644 index 0000000000000000000000000000000000000000..041be4bd5d6c1ab58fff1562848a4af62ff60bb9 --- /dev/null +++ b/src/lns/constraints/time_window/forward_time_slack.h @@ -0,0 +1,50 @@ +#pragma once + +#include "lns/solution/route.h" +#include "types.h" + +/* + * Represent the forward time slack of a route. + */ +class ForwardTimeSlack +{ +private: + std::vector<TimeInteger> earliestArrival; + std::vector<TimeInteger> latestArrival; + std::vector<TimeInteger> FTS; + +public: + explicit ForwardTimeSlack() = default; + + /* + * Check if the insertion of a new pickup/delivery pair is feasible. + * ForwardTimeSlack is supposed to be correct. Only check FTS and Time windows. + */ + bool isPickupDeliveryInsertionValid(PDPTWData const &data, Route const &route, int pickupID, int deliveryID, + int insertPickupIndex, int insertDeliveryIndex) const; + + /* + * Compute FTS values from scratch. + */ + void initFTS(PDPTWData const &data, Route const &route); + + /* + * Update FTS. + * Suppose the route has already been updated (but not the FTS) + */ + void updateFTSAfterInsertion(PDPTWData const &data, Route const &route, int insertPickupIndex, + int insertDeliveryIndex); + /* + * Update FTS. + * Suppose the route has already been updated (but not the FTS) + */ + void updateFTSAfterDeletion(PDPTWData const &data, Route const &route, int removePickupIndex, + int removeDeliveryIndex); + + + std::vector<TimeInteger> const &getFTS() const; + std::vector<TimeInteger> const &getEarliestArrival() const; + std::vector<TimeInteger> const &getLatestArrival() const; + + void print() const; +}; \ No newline at end of file diff --git a/src/lns/constraints/time_window/time_window_constraint.cpp b/src/lns/constraints/time_window/time_window_constraint.cpp index 7211e4cec720d7a92975003c30cee0ba62a5cde3..3126599b82b66b897269a4f1869d938742f10391 100644 --- a/src/lns/constraints/time_window/time_window_constraint.cpp +++ b/src/lns/constraints/time_window/time_window_constraint.cpp @@ -3,6 +3,7 @@ #include "input/data.h" #include "input/pdptw_data.h" #include "input/time_window.h" +#include "lns/constraints/time_window/forward_time_slack.h" #include "lns/modification/pair/insert_pair.h" #include "lns/modification/pair/remove_pair.h" #include "lns/modification/route/remove_route.h" @@ -10,181 +11,71 @@ TimeWindowConstraint::TimeWindowConstraint(Solution const &solution) : Constraint(solution) { - allRouteReachTimes.clear(); - for (unsigned int i = 0; i < solution.getRoutes().size(); ++i) + FTSContainer.clear(); + for (Route const &route: solution.getRoutes()) { - allRouteReachTimes.emplace_back(); + FTSContainer.emplace_back(); } } TimeWindowConstraint::~TimeWindowConstraint() { - allRouteReachTimes.clear(); + //allRouteReachTimes.clear(); + FTSContainer.clear(); } std::unique_ptr<Constraint> TimeWindowConstraint::clone(Solution const &newOwningSolution) const { std::unique_ptr<TimeWindowConstraint> clonePtr = std::make_unique<TimeWindowConstraint>(newOwningSolution); - clonePtr->allRouteReachTimes = allRouteReachTimes; + //clonePtr->allRouteReachTimes = allRouteReachTimes; + clonePtr->FTSContainer = FTSContainer; return clonePtr; } -void TimeWindowConstraint::computeReachTimes(PDPTWData const &data, std::vector<int> const &routeIDs, - ReachTimeVector &reachTimes) const +void TimeWindowConstraint::initFTS() { - // Adjust the size of reachTimes vector - reachTimes.resize(routeIDs.size(), 0); - // Time to the first location - reachTimes.at(0) = data.getDepot().getTimeWindow().getStart() + data::TravelTime(data, 0, routeIDs.at(0)); - // Compute other reachTimes (max between arrival and start of the time window + previous service time + travel time) - for (int i = 1; i < routeIDs.size(); ++i) - { - TimeInteger travelTime = data::TravelTime(data, routeIDs.at(i - 1), routeIDs.at(i)); - // locations are indexed from 0 to n-1, - TimeInteger serviceTime = data.getLocation(routeIDs.at(i - 1)).getServiceDuration(); - TimeInteger startTW = data.getLocation(routeIDs.at(i - 1)).getTimeWindow().getStart(); - - reachTimes.at(i) = std::max(reachTimes.at(i - 1), startTW) + serviceTime + travelTime; - } -} - -void TimeWindowConstraint::initReachTimes() -{ - allRouteReachTimes = std::vector<ReachTimeVector>(); + FTSContainer = std::vector<ForwardTimeSlack>(); + //reserve int i = 0; for (Route const &route: getSolution().getRoutes()) { - // init and calculate reach time value - allRouteReachTimes.emplace_back(); - computeReachTimes(getSolution().getData(), route.getRoute(), allRouteReachTimes.at(i)); + FTSContainer.emplace_back(); + FTSContainer.at(i).initFTS(getSolution().getData(), route); ++i; } } -// Copie le vecteur de reach time de la route concernée -// Insère les positions pickup/delivery -// refait l'ordo sur le nouveau vecteur bool TimeWindowConstraint::checkInsertion(PDPTWData const &data, Pair const &pair, int routeIndex, int pickupPos, int deliveryPos) const { - - ReachTimeVector const &reachTimes = allRouteReachTimes.at(routeIndex); - - - // COPY route vector - std::vector<int> route(getSolution().getRoute(routeIndex).getRoute().begin(), - getSolution().getRoute(routeIndex).getRoute().end()); - // COPY reachTimes vector - ReachTimeVector newReachTimes(reachTimes.begin(), reachTimes.end()); - - // Insert pickup and delivery - route.insert(route.begin() + deliveryPos, pair.getDelivery().getId()); - route.insert(route.begin() + pickupPos, pair.getPickup().getId()); - - - // std::cout << "\n"; - // for (auto pos : route) - // { - // std::cout << pos << " "; - // } - // std::cout << "\n"; - - // Compute new reach time - computeReachTimes(data, route, newReachTimes); - - // std::cout << "\n"; - // for (auto pos : newReachTimes) - // { - // std::cout << pos << " "; - // } - // std::cout << "\n"; - - // Check Time Windows - for (int i = 0; i < newReachTimes.size(); ++i) - { - if (!data.getLocation(route.at(i)).getTimeWindow().isValid(newReachTimes.at(i))) - { - return false; - } - } - return true; + return FTSContainer.at(routeIndex) + .isPickupDeliveryInsertionValid(data, + getSolution().getRoute(routeIndex), + pair.getPickup().getId(), + pair.getDelivery().getId(), + pickupPos, + deliveryPos); } -// A lot of check here because i had problems with this function + void TimeWindowConstraint::ApplyModif(PDPTWData const &data, Pair const &pair, int routeIndex, int pickupPos, int deliveryPos, bool addPair) { - // Check the routeIndex validity - if (routeIndex < 0 || routeIndex >= getSolution().getRoutes().size()) - { - spdlog::error("Error: routeIndex out of bounds ({})", routeIndex); - return; - } - - // Copy of the route vector (problem that cause a duplication of the TW) - std::vector<int> routeIDs = getSolution().getRoute(routeIndex).getRoute(); - - // // Check Position validity - // if (pickupPos < 0 || pickupPos > routeIDs.size() || - // deliveryPos < 0 || deliveryPos > routeIDs.size()) { - // spdlog::error("Error: Indices pickupPos ({}) or deliveryPos ({}) are invalid for a route size of {}.", - // pickupPos, deliveryPos, routeIDs.size()); - // return; - // } - - // if (addPair) { - // routeIDs.insert(routeIDs.begin() + deliveryPos, pair.getDelivery().getId()); - // routeIDs.insert(routeIDs.begin() + pickupPos, pair.getPickup().getId()); - // } - // else { - // if (deliveryPos < routeIDs.size() && pickupPos < routeIDs.size() && pickupPos != deliveryPos) { - // if (deliveryPos > pickupPos) { - // routeIDs.erase(routeIDs.begin() + deliveryPos); - // routeIDs.erase(routeIDs.begin() + pickupPos); - // } else { - // routeIDs.erase(routeIDs.begin() + pickupPos); - // routeIDs.erase(routeIDs.begin() + deliveryPos); - // } - // } else { - // spdlog::error("Error: Invalid indices for removal (pickupPos: {}, deliveryPos: {}).", pickupPos, deliveryPos); - // return; - // } - // } - - // the route is empty ! - if (routeIDs.empty()) + if (addPair) { - allRouteReachTimes.at(routeIndex).clear(); + FTSContainer.at(routeIndex) + .updateFTSAfterInsertion(data, getSolution().getRoute(routeIndex), pickupPos, deliveryPos); } else { - allRouteReachTimes.at(routeIndex).resize(routeIDs.size(), 0); - - allRouteReachTimes.at(routeIndex).at(0) = - data.getDepot().getTimeWindow().getStart() + data::TravelTime(data, 0, routeIDs.at(0)); - - // Compute reach times - for (size_t i = 1; i < routeIDs.size(); ++i) - { - if (i - 1 >= routeIDs.size() || i >= routeIDs.size()) - { - spdlog::error("Error: Out-of-bounds access to routeIDs in the computation loop."); - return; - } - - TimeInteger travelTime = data::TravelTime(data, routeIDs.at(i - 1), routeIDs.at(i)); - TimeInteger serviceTime = data.getLocation(routeIDs.at(i - 1)).getServiceDuration(); - TimeInteger startTW = data.getLocation(routeIDs.at(i - 1)).getTimeWindow().getStart(); - - allRouteReachTimes.at(routeIndex).at(i) = - std::max(allRouteReachTimes.at(routeIndex).at(i - 1), startTW) + serviceTime + travelTime; - } + FTSContainer.at(routeIndex) + .updateFTSAfterDeletion(data, getSolution().getRoute(routeIndex), pickupPos, deliveryPos); } + } bool TimeWindowConstraint::check(InsertPair const &op) const { - //std::cout << " #TW Check"; return checkInsertion(getSolution().getData(), op.getPair(), op.getRouteIndex(), @@ -194,7 +85,6 @@ bool TimeWindowConstraint::check(InsertPair const &op) const void TimeWindowConstraint::apply(InsertPair const &op) { - //std::cout << "-> Apply Modification on Time Windows \n"; ApplyModif(getSolution().getData(), op.getPair(), op.getRouteIndex(), @@ -210,7 +100,7 @@ bool TimeWindowConstraint::check(InsertRoute const &op) const void TimeWindowConstraint::apply(InsertRoute const &op) { - allRouteReachTimes.emplace_back(); + FTSContainer.emplace_back(); } bool TimeWindowConstraint::check(RemovePair const &op) const @@ -235,27 +125,18 @@ bool TimeWindowConstraint::check(RemoveRoute const &op) const void TimeWindowConstraint::apply(RemoveRoute const &op) { - allRouteReachTimes.erase(allRouteReachTimes.begin() + op.getRouteIndex()); -} - -std::vector<TimeWindowConstraint::ReachTimeVector> const &TimeWindowConstraint::getallRouteReachTimes() const -{ - return allRouteReachTimes; + FTSContainer.erase(FTSContainer.begin() + op.getRouteIndex()); } void TimeWindowConstraint::print() const { - std::cout << "Reach Times : \n"; + std::cout << "Time Window : Earliest / Latest / FTS" << std::endl; int i = 0; - for (auto const &reachTimes: getallRouteReachTimes()) + for (ForwardTimeSlack FTS: FTSContainer) { - //std::cout << reachTimes.size() << ", "; - std::cout << "#" << i << " : "; - for (const TimeInteger reachTime: reachTimes) - { - std::cout << reachTime << ", "; - } - std::cout << "\n"; + std::cout << "#" << i << std::endl; + FTS.print(); + std::cout << std::endl; i++; } } diff --git a/src/lns/constraints/time_window/time_window_constraint.h b/src/lns/constraints/time_window/time_window_constraint.h index 915d6f8417f96e190564c98d1a58533dac0b11a7..bb4555801f3fa9eda0be4bb99f7d3a0c1491c437 100644 --- a/src/lns/constraints/time_window/time_window_constraint.h +++ b/src/lns/constraints/time_window/time_window_constraint.h @@ -1,9 +1,11 @@ #pragma once -#include "lns/constraints/constraint.h" #include "input/pair.h" #include "input/time_window.h" +#include "lns/constraints/constraint.h" +#include "lns/constraints/time_window/forward_time_slack.h" #include "lns/solution/route.h" + #include <vector> /** @@ -17,59 +19,44 @@ class TimeWindowConstraint : public Constraint public: using ReachTimeVector = std::vector<TimeInteger>; + explicit TimeWindowConstraint(Solution const &); TimeWindowConstraint(TimeWindowConstraint const &) = default; ~TimeWindowConstraint() override; - // Given the solution, calculate the reachTimes of each location. - // Modify the routeCapacities vector ! - void initReachTimes(); + + /** + * Initialize the FTS vectors + */ + void initFTS(); std::unique_ptr<Constraint> clone(Solution const &newOwningSolution) const override; - - const std::vector<ReachTimeVector> & getallRouteReachTimes() const; - void print() const override; -private: - /** - * For each route, store the reach time of each location - */ - std::vector<ReachTimeVector> allRouteReachTimes; - - // Recalcul tous les reachTimes pour une route - ReachTimeVector computeReachTimes(const Route& route) const; - bool updateReachTimes(const PDPTWData& data, Pair & pair,int routeIndex, int pickupPos, int deliveryPos); + void print() const override; - /** - * Check if the insertion of a pair pickup/delivery is valid or not. - * COPY the route where we insert the pair ! - */ - bool checkInsertion(const PDPTWData& data, const Pair & pair, int routeIndex, int pickupPos, int deliveryPos) const; +private: + std::vector<ForwardTimeSlack> FTSContainer; /** - * Given route IDs, compute the reachTimes of the route. - * If a vehicule is in advance, set the reach time to start of the timeWindow. - * Modify reachTimes vector ! - */ - void computeReachTimes(const PDPTWData& data, const std::vector<int> & routeIDs, ReachTimeVector & reachTimes) const; + * Check if the insertion of a pair pickup/delivery is valid or not. + * COPY the route where we insert the pair ! + */ + bool checkInsertion(PDPTWData const &data, Pair const &pair, int routeIndex, int pickupPos, int deliveryPos) const; /** - * Given route IDs, modify the reachTimes - */ - void ApplyModif(const PDPTWData& data, const Pair & pair, int routeIndex, int pickupPos, int deliveryPos, bool addPair); - + * Apply the modification to the time window constraint + */ + void ApplyModif(PDPTWData const &data, Pair const &pair, int routeIndex, int pickupPos, int deliveryPos, + bool addPair); void apply(InsertPair const &op) override; void apply(InsertRoute const &op) override; void apply(RemovePair const &op) override; void apply(RemoveRoute const &op) override; - + bool check(InsertPair const &op) const override; bool check(InsertRoute const &op) const override; bool check(RemovePair const &op) const override; bool check(RemoveRoute const &op) const override; - -public: - }; \ No newline at end of file diff --git a/src/lns/lns.cpp b/src/lns/lns.cpp index c82a637767f7c1727264a8491a636f3eaedaf882..8735e5ea97ca005096fb8e0307dffa57be5273b3 100644 --- a/src/lns/lns.cpp +++ b/src/lns/lns.cpp @@ -81,7 +81,7 @@ output::LnsOutput lns::runLns(Solution const &initialSolution, OperatorSelector LnsRuntimeData runtime = {actualSolution}; // temporary fixed iteration - int iterationMax = 200; + int iterationMax = 100; while (iterationMax > 0) { // Init iteration diff --git a/src/lns/operators/destruction/clean_empty_route.cpp b/src/lns/operators/destruction/clean_empty_route.cpp index aaeeede3114558cd30f7026e15fe970cd1944520..edd796874a9fa8c100949e5cfcbb63b7f49566e2 100644 --- a/src/lns/operators/destruction/clean_empty_route.cpp +++ b/src/lns/operators/destruction/clean_empty_route.cpp @@ -5,6 +5,7 @@ CleanEmptyRoute::CleanEmptyRoute() {} void CleanEmptyRoute::destroySolution(Solution &solution) const { + std::cout << "Clean "; for (int routeIndex = solution.getRoutes().size() - 1; routeIndex >= 0; --routeIndex) { if (solution.getRoute(routeIndex).getRoute().empty()) diff --git a/src/lns/solution/solution.cpp b/src/lns/solution/solution.cpp index 1bb0132f589df8b07343f6a32ea4f4eb310d7a5b..0a975ed32ea38a1d07fbbb70888fdf6073ab2160 100644 --- a/src/lns/solution/solution.cpp +++ b/src/lns/solution/solution.cpp @@ -4,6 +4,7 @@ #include "input/data.h" #include "input/time_window.h" #include "lns/constraints/capacity/capacity_constraint.h" +#include "lns/constraints/constraint.h" #include "lns/constraints/time_window/time_window_constraint.h" #include "lns/solution/route.h" #include "output/solution_checker.h" @@ -50,7 +51,7 @@ void Solution::computeAndStoreSolutionCost() double Solution::computeSolutionCost() const { - double cost = 0; + double cost = 0.0; for (Route const &route: getRoutes()) { cost += data::routeCost(data, route); @@ -205,6 +206,11 @@ PDPTWData const &Solution::getData() const return data.get(); } +std::vector<std::unique_ptr<Constraint>> const &Solution::getConstraints() const +{ + return constraints; +} + int Solution::requestsFulFilledCount() const { int count = 0; diff --git a/src/lns/solution/solution.h b/src/lns/solution/solution.h index 874c74186ad96bb9c2d2a18fc656fd1cc10f2815..4ad16c4f7e240e525629eca2297b9a63d7ebcf14 100644 --- a/src/lns/solution/solution.h +++ b/src/lns/solution/solution.h @@ -77,6 +77,7 @@ public: double getRawCost() const; double getCost() const; unsigned int missingPairCount() const; + std::vector<std::unique_ptr<Constraint>> const &getConstraints() const; /** * Return the route index associated to the given location ID. diff --git a/src/mains/main.cpp b/src/mains/main.cpp index ddbdd0189ca0019f3ae6845a773d860189949878..1b4227b9cf26a8ff2ae75e3569834a7d28d8885f 100644 --- a/src/mains/main.cpp +++ b/src/mains/main.cpp @@ -6,6 +6,7 @@ #include "input/time_window.h" #include "lns/acceptance/threshold_acceptance.h" #include "lns/constraints/capacity/capacity_constraint.h" +#include "lns/constraints/time_window/forward_time_slack.h" #include "lns/constraints/time_window/time_window_constraint.h" #include "lns/lns.h" #include "lns/modification/pair/insert_pair.h" @@ -24,11 +25,15 @@ #include "lns/operators/sorting_strategy.h" #include "lns/solution/solution.h" #include "mains/main_interface.h" +#include "output/solution_checker.h" #include "output/solution_exporter.h" +#include "types.h" #include <filesystem> #include <fstream> +#include <iomanip> #include <iostream> +#include <limits> #include <nlohmann/json.hpp> #include <spdlog/spdlog.h> #include <string> @@ -51,40 +56,28 @@ void simpleLNS(PDPTWData const &data, Solution &startingSolution) SimpleOperatorSelector RandomDestroy_BestInsert; addAllReconstructor(RandomDestroy_BestInsert); RandomDestroy_BestInsert.addDestructor(RandomDestroy(pairs)); - RandomDestroy_BestInsert.addDestructor(StringRemoval(10,10)); - RandomDestroy_BestInsert.addDestructor(CleanEmptyRoute()); + RandomDestroy_BestInsert.addDestructor(StringRemoval(10, 10)); SimpleOperatorSelector largeSelector; addAllReconstructor(largeSelector); largeSelector.addDestructor(RandomDestroy(manyPairs)); - largeSelector.addDestructor(StringRemoval(10,10)); - largeSelector.addDestructor(CleanEmptyRoute()); + largeSelector.addDestructor(StringRemoval(10, 10)); - // SimpleOperatorSelector veryLargeSelector; - // addAllReconstructor(veryLargeSelector); - // veryLargeSelector.addDestructor(RandomDestroy(pairs)); - - // SimpleOperatorSelector hugeSelector; - // addAllReconstructor(hugeSelector); - // hugeSelector.addDestructor(RandomDestroy(pairs)); - - // SimpleOperatorSelector lastSelector; - // addAllReconstructor(lastSelector); - // lastSelector.addDestructor(RandomDestroy(manyPairs)); std::vector<SmallLargeOperatorSelector::StepSelector> selectors; selectors.emplace_back(10, std::move(RandomDestroy_BestInsert)); selectors.emplace_back(50, std::move(largeSelector)); - // selectors.emplace_back(2, std::move(veryLargeSelector)); - // selectors.emplace_back(2, std::move(hugeSelector)); - // selectors.emplace_back(2, std::move(lastSelector)); + SmallLargeOperatorSelector smallLargeSelector(std::move(selectors)); // run lns output::LnsOutput result = lns::runLns(startingSolution, smallLargeSelector, acceptor); - - result.getBestSolution().print(); - std::cout << result.getNumberOfIteration() << " " << result.getTimeSpent() << std::endl; + + Solution sol = result.getBestSolution(); + + CleanEmptyRoute clean = CleanEmptyRoute(); + clean.destroySolution(sol); + sol.print(); } int main(int argc, char **argv) @@ -93,35 +86,14 @@ int main(int argc, char **argv) /////////////////////////////////////////////////////////////////////// - //std::string filepath = "/home/a24jacqb/Documents/Code/pdptw-main/data_in/n100/bar-n100-1.json"; - std::string filepath = "/home/a24jacqb/Documents/Code/pdptw-main/data_in/pdp_100/lrc201.json"; + std::string filepath = "/home/a24jacqb/Documents/Code/pdptw-main/data_in/n100/bar-n100-1.json"; + //std::string filepath = "/home/a24jacqb/Documents/Code/pdptw-main/data_in/pdp_100/lc102.json"; //std::string filepath = "/home/a24jacqb/Documents/Code/pdptw-main/data_in/Nantes_1.json"; //std::string filepath = "/home/a24jacqb/Documents/Code/pdptw-main/data_in/n5000/bar-n5000-1.json"; PDPTWData data = parsing::parseJson(filepath); Solution startingSolution = Solution::emptySolution(data); - - startingSolution.print(); - - //data.print(); - simpleLNS(data, startingSolution); - - /// - // std::cout << "===== TEST ===== \n"; - // Solution testSolution = Solution::emptySolution(data); - // ListHeuristicCostOriented reconstruction = ListHeuristicCostOriented(SortingStrategyType::SHUFFLE, EnumerationType::ALL_INSERT_PAIR); - // reconstruction.reconstructSolution(testSolution, 0.01); - - // testSolution.print(); - - // std::cout << "============ \n"; - - // StringRemoval StringRemoval(10,10); - // StringRemoval.destroySolution(testSolution); - - // testSolution.print(); - return 0; } diff --git a/src/output/solution_checker.cpp b/src/output/solution_checker.cpp index f1663b919cece492b73338619fbae47e4af50f8b..6be2f13471e5daa5b6c8f7540776bad87b2075a5 100644 --- a/src/output/solution_checker.cpp +++ b/src/output/solution_checker.cpp @@ -3,6 +3,7 @@ #include "input/data.h" #include "input/location.h" #include "input/pdptw_data.h" +#include "lns/operators/destruction/clean_empty_route.h" #include "lns/solution/solution.h" #include "types.h" @@ -64,7 +65,6 @@ void checker::checkSolutionCoherence(Solution const &sol, PDPTWData const &data) if (errorFlag) { - sol.print(); throw SolutionConstraintError("Error in the consistency of the solution.", sol); } }