From b52eb6e6a27e0d4eb879830b54950d0b70ca97c1 Mon Sep 17 00:00:00 2001 From: awenjb <126257927+awenjb@users.noreply.github.com> Date: Wed, 19 Mar 2025 15:47:40 +0100 Subject: [PATCH] Add cost update & number of routes - add initial (maximal) number of routes - update cost at each iteration of LNS --- src/config.h | 4 +- src/input/data.cpp | 6 +- .../capacity/capacity_constraint.cpp | 3 +- .../time_window/time_window_constraint.cpp | 16 +- src/lns/lns.cpp | 2 +- .../operators/destruction/string_removal.cpp | 165 ++++++------------ src/lns/solution/solution.cpp | 29 +-- src/lns/solution/solution.h | 10 +- src/mains/main.cpp | 24 +-- src/output/solution_checker.cpp | 78 +++++---- 10 files changed, 150 insertions(+), 187 deletions(-) diff --git a/src/config.h b/src/config.h index 9cbc47d..7e6911c 100644 --- a/src/config.h +++ b/src/config.h @@ -2,4 +2,6 @@ const int EXCLUSION_PENALTY = 100; -const int RANDOM_SEED = 100; \ No newline at end of file +const int RANDOM_SEED = 100; + +const int NUMBER_VEHICLE = 5; \ No newline at end of file diff --git a/src/input/data.cpp b/src/input/data.cpp index 10d3f96..a5dc436 100644 --- a/src/input/data.cpp +++ b/src/input/data.cpp @@ -32,15 +32,15 @@ double data::routeCost(PDPTWData const & data, Route const & route) } // cost from and to the depot cost += matrix.at(0).at(routeIDs.at(0)); - //std::cout << "\n route cost : " << matrix.at(0).at(routeIDs.at(0) << " "; + // std::cout << "\n route cost : " << matrix.at(0).at(routeIDs.at(0)) << " "; cost += matrix.at(routeIDs.back()).at(0); // cost in the route for (size_t i = 0; i < routeIDs.size() - 1; ++i) { cost += matrix.at(routeIDs.at(i)).at(routeIDs.at(i+1)); - //std::cout << matrix.at(routeIDs.at(i).at(routeIDs.at(i+1) << " "; + // std::cout << matrix.at(routeIDs.at(i)).at(routeIDs.at(i+1)) << " "; } - //std::cout << matrix.at(routeIDs.back()).at(0) << " : " << cost << "\n"; + // std::cout << matrix.at(routeIDs.back()).at(0) << " : " << cost << "\n"; return cost; } diff --git a/src/lns/constraints/capacity/capacity_constraint.cpp b/src/lns/constraints/capacity/capacity_constraint.cpp index 34ad11f..7ff89d5 100644 --- a/src/lns/constraints/capacity/capacity_constraint.cpp +++ b/src/lns/constraints/capacity/capacity_constraint.cpp @@ -155,9 +155,8 @@ void CapacityConstraint::print() const { std::cout << capa << ", "; } - std::cout << "\n "; + std::cout << "\n"; i++; } - std::cout << "\n"; } diff --git a/src/lns/constraints/time_window/time_window_constraint.cpp b/src/lns/constraints/time_window/time_window_constraint.cpp index ca0c629..7211e4c 100644 --- a/src/lns/constraints/time_window/time_window_constraint.cpp +++ b/src/lns/constraints/time_window/time_window_constraint.cpp @@ -10,7 +10,11 @@ TimeWindowConstraint::TimeWindowConstraint(Solution const &solution) : Constraint(solution) { - allRouteReachTimes.resize(getSolution().getRoutes().size()); + allRouteReachTimes.clear(); + for (unsigned int i = 0; i < solution.getRoutes().size(); ++i) + { + allRouteReachTimes.emplace_back(); + } } TimeWindowConstraint::~TimeWindowConstraint() @@ -63,8 +67,10 @@ void TimeWindowConstraint::initReachTimes() 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()); @@ -108,7 +114,6 @@ bool TimeWindowConstraint::checkInsertion(PDPTWData const &data, Pair const &pai 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()) { @@ -251,13 +256,6 @@ void TimeWindowConstraint::print() const std::cout << reachTime << ", "; } std::cout << "\n"; - std::cout << "#" << i << " : "; - for (int locID: getSolution().getRoute(i).getRoute()) - { - std::cout << locID << ", "; - } - std::cout << "\n "; i++; } - std::cout << "\n"; } diff --git a/src/lns/lns.cpp b/src/lns/lns.cpp index e79bcb7..278cd09 100644 --- a/src/lns/lns.cpp +++ b/src/lns/lns.cpp @@ -38,7 +38,7 @@ output::LnsOutput lns::runLns(Solution const &initialSolution, OperatorSelector // Apply operators destructReconstructPair.destructor().destroySolution(candidateSolution); destructReconstructPair.reconstructor().reconstructSolution(candidateSolution, 0.01); - + candidateSolution.computeAndStoreSolutionCost(); // Update best solution if (isBetterSolution(candidateSolution, bestSolution)) { diff --git a/src/lns/operators/destruction/string_removal.cpp b/src/lns/operators/destruction/string_removal.cpp index 7ee2859..bc3b542 100644 --- a/src/lns/operators/destruction/string_removal.cpp +++ b/src/lns/operators/destruction/string_removal.cpp @@ -15,45 +15,24 @@ namespace { if (routes.empty()) { - return 0; + return 0.0; } - int total_cardinality = 0; - for (Route const &route: routes) - { - total_cardinality += route.getSize(); - } - - return static_cast<double>(total_cardinality) / routes.size(); + return std::accumulate(routes.begin(), + routes.end(), + 0.0, + [](double sum, Route const &route) { return sum + route.getSize(); }) / + routes.size(); } - /** - * Compute equations (8) and (9) from the SISR paper. - * (8) Maximum cardinality of a string - * (9) Cardinality of string to be removed - * @return pair of (global maximum of string size, Cardinality of the string to be removed) - */ - std::pair<unsigned int, unsigned int> computeStringsData(unsigned int maxStringSize, unsigned int routeSize) - { - // (8) "The maximum cardinality of each string is limited by the initial limit lmax_s and the tour’s cardinality |t|" - unsigned int maxSizeOfString = std::min(maxStringSize, routeSize); - - // (9) "the cardinality for the string to be removed from tour t is obtained by rounding down a real value selected from U(Lmin, lmax_t+1)" - unsigned int actualSizeOfString = util::getRandomInt(1, maxSizeOfString); - - return {maxSizeOfString, actualSizeOfString}; - } - - // t, l_t, c^*_t /** * Remove a string in the solution * For now it uses startLocation as the center of the string - * @param pathIndex the path on which the removal happens + * @param routeIndex the path on which the removal happens * @param stringLength the number of requests to remove, expect it to be inferior to the number of requests in the path * @param startLocation the location found to start the string, does not mean it will the smallest index removed */ void removeString(Solution &solution, int routeIndex, unsigned int stringLength, int startLocation) { - std::cout << "remove string" << std::endl; // ID of pair to remove std::vector<int> toRemove; toRemove.reserve(stringLength); @@ -81,9 +60,6 @@ namespace left = std::max(0, right - (int) stringLength + 1);// keep a number of stringLength elements } - ////////////////////: - // try : get the left index and remove stringLength times on the left index - // collect each locationID and store the pairID in the toRemove vector for (int i = left; i <= right; ++i) { @@ -103,13 +79,6 @@ namespace } } - std::cout << std::endl << "PairID (pickup):" << std::endl; - for (int i: toRemove) - { - std::cout << i << " "; - } - std::cout << std::endl; - //remove the collected pairID for (int pairID: toRemove) { @@ -117,96 +86,76 @@ namespace int position = solution.getRoute(routeIndex).getIndex(pairID); Index index = std::make_tuple(routeIndex, position, route.getPairLocationPosition(position, data)); Pair const &pair = data.getPair(pairID); - + // apply remove RemovePair removePair(index, pair); solution.applyDestructSolution(removePair); } } -}// namespace - - -// Procedure RUIN(s, Lmax, c^bar) -// 1. Compute lmax_s, kmax_s, k_s according to equations (5), (6), and (7) -// 2. cseed_s ↠RandomClient(s) -// 3. R ↠∅ // Set of affected tours -// 4. For each client c in Adjacent(cseed_s) Do -// 5. If c ∉ A and |R| < k_s Then -// 6. c*_t ↠c -// 7. Compute lmax_t, lt according to equations (8) and (9) -// 8. A ↠A ∪ RemoveClient(T, lt, c*_t) -// 9. R ↠R ∪ {t} -// 10. End If -// 11. End For -// End Procedure - - -/** - * Ruin procedure from the SISR paper. - * c^bar = averageNumberRemovedElement. - * L^max = maxStringSize. - */ -void SISRsRuin(Solution &solution, unsigned int maxStringSize, unsigned int averageNumberRemovedElement) -{ - // |t∈T| average number of location by route - unsigned int averageRouteCardinality = computeAverageCardinality(solution.getRoutes()); - - // compute equation (5) (6) and (7) from the SISR paper. - // (5) lmax_s - unsigned int maxSizeOfString = - std::min(static_cast<std::size_t>(maxStringSize), static_cast<std::size_t>(averageRouteCardinality)); - // (6) kmax_s - unsigned int maxNumberOfString = (4 * averageNumberRemovedElement) / (1 + maxSizeOfString) - 1; - // (7) k_s - unsigned int numberOfString = util::getRandomInt(1, maxNumberOfString + 1); - - // get Location ID of the location seed - int locationSeed = util::getRandomInt(1, solution.getData().getLocationCount()); - - // store the index of routes where a string was removed - std::vector<int> routeIndexUsed; - routeIndexUsed.reserve(numberOfString); - std::cout << "locationSeed : " << locationSeed << std::endl; + // Procedure RUIN(s, Lmax, c^bar) + // 1. Compute lmax_s, kmax_s, k_s according to equations (5), (6), and (7) + // 2. cseed_s ↠RandomClient(s) + // 3. R ↠∅ // Set of affected tours + // 4. For each client c in Adjacent(cseed_s) Do + // 5. If c ∉ A and |R| < k_s Then + // 6. c*_t ↠c + // 7. Compute lmax_t, lt according to equations (8) and (9) + // 8. A ↠A ∪ RemoveClient(T, lt, c*_t) + // 9. R ↠R ∪ {t} + // 10. End If + // 11. End For + // End Procedure - // getClosestLocationsID returns the list of all location sorted from the closest to furthest - for (int neighbor: solution.getData().getClosestLocationsID(locationSeed)) + /** + * Ruin procedure from the SISR paper. + * c^bar = averageNumberRemovedElement. + * L^max = maxStringSize. + */ + void SISRsRuin(Solution &solution, unsigned int maxStringSize, unsigned int averageNumberRemovedElement) { - // recover the routeIndex associated to the locationID, returns -1 if in the bank - int routeIndex = solution.getRouteIDOf(neighbor); - - // verif de validité - // -> pas dans pairBank - // -> route pas déjà modifiée - if (routeIndex != -1 && std::ranges::find(routeIndexUsed, routeIndex) == routeIndexUsed.end()) - { - std::cout << "in" << std::endl; + // |t∈T| average number of location by route + auto averageRouteCardinality = static_cast<unsigned int>(computeAverageCardinality(solution.getRoutes())); - // (8) lmax_t - unsigned int maxSizeOfThisString = - std::min(static_cast<int>(maxSizeOfString), solution.getRoute(routeIndex).getSize()); - // (9) l_t - unsigned int actualSizeOfThisString = util::getRandomInt(1, maxSizeOfThisString); + unsigned int maxSizeOfString = std::min(maxStringSize, averageRouteCardinality); // (5) lmax_s + unsigned int maxNumberOfString = (4 * averageNumberRemovedElement) / (1 + maxSizeOfString) - 1;// (6) kmax_s + unsigned int numberOfString = util::getRandomInt(1, maxNumberOfString + 1); // (7) k_s - std::cout << "actualSizeOfThisString " << actualSizeOfThisString << " neighbor " << neighbor << std::endl; + int locationSeed = util::getRandomInt(1, solution.getData().getLocationCount()); - // string removal - removeString(solution, routeIndex, actualSizeOfThisString, neighbor); + // store the index of routes where a string was removed + std::vector<int> routeIndexUsed; + routeIndexUsed.reserve(numberOfString); - // update routeIndexUsed - routeIndexUsed.emplace_back(routeIndex); + // getClosestLocationsID returns the list of all location sorted from the closest to furthest + for (int neighbor: solution.getData().getClosestLocationsID(locationSeed)) + { + // recover the routeIndex associated to the locationID, returns -1 if in the bank + int routeIndex = solution.getRouteIDOf(neighbor); - if (routeIndexUsed.size() >= numberOfString) + if (routeIndex != -1 && std::ranges::find(routeIndexUsed, routeIndex) == routeIndexUsed.end()) { - break; + // (8) lmax_t + unsigned int maxSizeOfThisString = + std::min(static_cast<int>(maxSizeOfString), solution.getRoute(routeIndex).getSize()); + // (9) l_t + unsigned int actualSizeOfThisString = util::getRandomInt(1, maxSizeOfThisString); + + removeString(solution, routeIndex, actualSizeOfThisString, neighbor); + routeIndexUsed.emplace_back(routeIndex); + + if (routeIndexUsed.size() >= numberOfString) + { + break; + } } } } -} -// namespace +}// namespace void StringRemoval::destroySolution(Solution &solution) const { + std::cout << "SISR "; SISRsRuin(solution, maxCardinalityOfString, averageNumberRemovedElement); } diff --git a/src/lns/solution/solution.cpp b/src/lns/solution/solution.cpp index fcb7ec1..797f8c7 100644 --- a/src/lns/solution/solution.cpp +++ b/src/lns/solution/solution.cpp @@ -27,7 +27,10 @@ void Solution::initPairBank() void Solution::initRoutes() { routes.clear(); - routes.emplace_back(); + for (unsigned int i = 0; i < NUMBER_VEHICLE; ++i) + { + this->routes.emplace_back(); + } } void Solution::initConstraints() @@ -80,8 +83,8 @@ Solution::Solution(PDPTWData const &data) : data(data) Solution Solution::emptySolution(PDPTWData const &data) { - Solution s = Solution(data); - return s; + Solution sol = Solution(data); + return sol; } Solution::~Solution() noexcept = default; @@ -295,13 +298,8 @@ void Solution::check() const void Solution::print() const { - std::cout << "Cost : " << totalCost << "\n" - << "Routes : \n"; - - for (Route const &id: getRoutes()) - { - id.print(); - } + std::cout << "\nRawCost : " << rawCost << "\n" + << "TotalCost : " << totalCost << "\n"; std::cout << "Pair Bank : \n"; @@ -310,7 +308,16 @@ void Solution::print() const std::cout << id << ", "; } - std::cout << "\nConstraints : \n"; + std::cout << "\nRoutes : \n"; + int i = 0; + for (Route const &route: getRoutes()) + { + std::cout << "#" << i << ": "; + route.print(); + ++i; + } + + std::cout << "Constraints : \n"; for (std::unique_ptr<Constraint> const &constraint: constraints) { constraint->print(); diff --git a/src/lns/solution/solution.h b/src/lns/solution/solution.h index 7c06e5b..59f6a4b 100644 --- a/src/lns/solution/solution.h +++ b/src/lns/solution/solution.h @@ -131,7 +131,8 @@ public: Route &getRoute(int routeIndex); void print() const; - + + void computeAndStoreSolutionCost(); private: /** @@ -145,11 +146,10 @@ private: void initConstraints(); void initRoutes(); void initPairBank(); - - void computeAndStoreSolutionCost(); - + /** * Compute the cost of the solution (routes cost) */ - double computeSolutionCost() const; + double computeSolutionCost() const; + }; \ No newline at end of file diff --git a/src/mains/main.cpp b/src/mains/main.cpp index 83071eb..889b5b3 100644 --- a/src/mains/main.cpp +++ b/src/mains/main.cpp @@ -91,26 +91,28 @@ int main(int argc, char **argv) //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); + Solution startingSolution = Solution::emptySolution(data); + + startingSolution.print(); //data.print(); - //simpleLNS(data, startingSolution); + 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); + // 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(); + // testSolution.print(); - std::cout << "============ \n"; + // std::cout << "============ \n"; - StringRemoval StringRemoval(10,10); - StringRemoval.destroySolution(testSolution); + // StringRemoval StringRemoval(10,10); + // StringRemoval.destroySolution(testSolution); - testSolution.print(); + // testSolution.print(); return 0; } diff --git a/src/output/solution_checker.cpp b/src/output/solution_checker.cpp index dbd7dcc..f1663b9 100644 --- a/src/output/solution_checker.cpp +++ b/src/output/solution_checker.cpp @@ -20,20 +20,20 @@ void checker::checkSolutionCoherence(Solution const &sol, PDPTWData const &data) // checking routes coherence int routeID = 0; for (Route const &route: sol.getRoutes()) - { + { // skip if it is an empty route if (!route.getRoute().empty()) { for (int LocID: route.getRoute()) { - if (check.at(LocID -1) != -1) + if (check.at(LocID - 1) != -1) { // Error the location is already attributed (doublon) spdlog::error("Location {} has already been visited.", LocID); errorFlag = true; } - check.at(LocID -1) = routeID; + check.at(LocID - 1) = routeID; // if the location is a delivery, check if the pickup location has already been visited in the same route if ((data.getLocation(LocID).getLocType() == LocType::DELIVERY) && @@ -77,22 +77,25 @@ void checker::checkCapacity(Solution const &sol, PDPTWData const &data) for (Route const &route: sol.getRoutes()) { - for (int id: route.getRoute()) + if (!route.getRoute().empty()) { - capa += data.getLocation(id).getDemand(); - if (capa > data.getCapacity()) + for (int id: route.getRoute()) + { + capa += data.getLocation(id).getDemand(); + if (capa > data.getCapacity()) + { + // Error, max capacity is exceeded + spdlog::error("Maximum Capacity is exceeded at {} in the route {}.", id, routeID); + errorFlag = true; + } + } + if (capa != 0) { - // Error, max capacity is exceeded - spdlog::error("Maximum Capacity is exceeded at {} in the route {}.", id, routeID); + // Error, all the capacity is supposed to be free at the end of a route + spdlog::error("Some capacity still used at the end of the route {}.", routeID); errorFlag = true; } } - if (capa != 0) - { - // Error, all the capacity is supposed to be free at the end of a route - spdlog::error("Some capacity still used at the end of the route {}.", routeID); - errorFlag = true; - } ++routeID; } @@ -109,38 +112,41 @@ void checker::checkTimeWindows(Solution const &sol, PDPTWData const &data) int routeID = 0; int locID = 0; + for (Route const &route: sol.getRoutes()) { - // travel to the first location - reachTime = data.getDepot().getTimeWindow().getStart() + data::TravelTime(data, 0, route.getRoute().at(0)); - - for (int i = 0; i < route.getRoute().size() - 1; i++) + if (!route.getRoute().empty()) { - locID = route.getRoute().at(i); - // check TimeWindow + // travel to the first location + reachTime = data.getDepot().getTimeWindow().getStart() + data::TravelTime(data, 0, route.getRoute().at(0)); + + for (int i = 0; i < route.getRoute().size() - 1; i++) + { + locID = route.getRoute().at(i); + // check TimeWindow + if (!(data.getLocation(locID).getTimeWindow().isValid(reachTime))) + { + // Error, reach time not valid for the location time window + spdlog::error("Reach time not valid for the location {} time window in route {}.", locID, routeID); + errorFlag = true; + } + + TimeInteger travelTime = data::TravelTime(data, locID, route.getRoute().at(i + 1)); + TimeInteger serviceTime = data.getLocation(locID).getServiceDuration(); + TimeInteger startTW = data.getLocation(locID).getTimeWindow().getStart(); + + reachTime = std::max(reachTime, startTW) + serviceTime + travelTime; + } + + // check last timeWindow + locID = route.getRoute().back(); if (!(data.getLocation(locID).getTimeWindow().isValid(reachTime))) { // Error, reach time not valid for the location time window spdlog::error("Reach time not valid for the location {} time window in route {}.", locID, routeID); errorFlag = true; } - - TimeInteger travelTime = data::TravelTime(data, locID, route.getRoute().at(i + 1)); - TimeInteger serviceTime = data.getLocation(locID).getServiceDuration(); - TimeInteger startTW = data.getLocation(locID).getTimeWindow().getStart(); - - reachTime = std::max(reachTime, startTW) + serviceTime + travelTime; } - - // check last timeWindow - locID = route.getRoute().back(); - if (!(data.getLocation(locID).getTimeWindow().isValid(reachTime))) - { - // Error, reach time not valid for the location time window - spdlog::error("Reach time not valid for the location {} time window in route {}.", locID, routeID); - errorFlag = true; - } - ++routeID; } -- GitLab