From 7c42750274f9cc575de379eb9fd1482ff31b6ea4 Mon Sep 17 00:00:00 2001 From: awenjb <126257927+awenjb@users.noreply.github.com> Date: Thu, 27 Mar 2025 16:15:25 +0100 Subject: [PATCH] Some fixes - call clean empty route for every new best solution - add new elements in the config file - clean up some of the code --- src/config.h | 29 ++- .../simulated_annealing_acceptance.cpp | 3 + .../simulated_annealing_acceptance.h | 9 + src/lns/lns.cpp | 8 +- .../destruction/clean_empty_route.cpp | 1 - .../operators/destruction/random_destroy.cpp | 2 +- src/lns/solution/route.h | 49 ++--- src/lns/solution/solution.cpp | 81 +++----- src/lns/solution/solution.h | 183 +++++++++++------- src/mains/main.cpp | 10 +- src/types.h | 3 +- src/utils.cpp | 2 +- 12 files changed, 216 insertions(+), 164 deletions(-) create mode 100644 src/lns/acceptance/simulated_annealing_acceptance.cpp create mode 100644 src/lns/acceptance/simulated_annealing_acceptance.h diff --git a/src/config.h b/src/config.h index 444c3d0..33bb959 100644 --- a/src/config.h +++ b/src/config.h @@ -1,7 +1,28 @@ -const int EXCLUSION_PENALTY = 1000000; +// Parameters -const int ROUTE_PENALTY = 1000; +// Penalty for excluding a certain pickup-delivery pair, aims to minimize the number of excluded pairs. +int const EXCLUSION_PENALTY = 1000000; -const int RANDOM_SEED = 100; +// Penalty applied to a route, aims to minimize the number of routes. +int const ROUTE_PENALTY = 0; + +// Number of vehicles involved at the start of the optimization process (cannot be increased). +int const NUMBER_VEHICLE = 20; + +// Number of iterations for the algorithm or simulation to run. +int const NUMBER_ITERATION = 500; + +// Flags + +// Flag indicating whether the random seed has been set (true means it is set). +bool const SEED_SET = true; + +// Seed for random number generation (ensures reproducibility). +int const RANDOM_SEED = 100; + +// Flag indicating whether we print during the execution. +bool const PRINT = true; + +// Flag indicating whether the final solution is stored. +bool const STORE_SOLUTION = true; -const int NUMBER_VEHICLE = 20; \ No newline at end of file diff --git a/src/lns/acceptance/simulated_annealing_acceptance.cpp b/src/lns/acceptance/simulated_annealing_acceptance.cpp new file mode 100644 index 0000000..774fb95 --- /dev/null +++ b/src/lns/acceptance/simulated_annealing_acceptance.cpp @@ -0,0 +1,3 @@ +#include "simulated_annealing_acceptance.h" + + diff --git a/src/lns/acceptance/simulated_annealing_acceptance.h b/src/lns/acceptance/simulated_annealing_acceptance.h new file mode 100644 index 0000000..97cfee4 --- /dev/null +++ b/src/lns/acceptance/simulated_annealing_acceptance.h @@ -0,0 +1,9 @@ +#pragma once + +#include "acceptance_function.h" + + +class SimulatedAnnealingAcceptance : public AcceptanceFunction +{ + +}; \ No newline at end of file diff --git a/src/lns/lns.cpp b/src/lns/lns.cpp index 8735e5e..f85a2d6 100644 --- a/src/lns/lns.cpp +++ b/src/lns/lns.cpp @@ -1,8 +1,10 @@ #include "lns.h" #include "lns/acceptance/acceptance_function.h" +#include "lns/operators/destruction/clean_empty_route.h" #include "lns/operators/selector/operator_selector.h" #include "output/solution_checker.h" +#include "config.h" #include <chrono> @@ -81,7 +83,7 @@ output::LnsOutput lns::runLns(Solution const &initialSolution, OperatorSelector LnsRuntimeData runtime = {actualSolution}; // temporary fixed iteration - int iterationMax = 100; + int iterationMax = NUMBER_ITERATION; while (iterationMax > 0) { // Init iteration @@ -107,6 +109,10 @@ output::LnsOutput lns::runLns(Solution const &initialSolution, OperatorSelector std::cout << "\n > new Best Solution \n"; checker::checkAll(candidateSolution, candidateSolution.getData(), false); + // remove empty route from the solution + CleanEmptyRoute clean = CleanEmptyRoute(); + clean.destroySolution(candidateSolution); + runtime.bestSolution = candidateSolution; opSelector.betterSolutionFound(); } diff --git a/src/lns/operators/destruction/clean_empty_route.cpp b/src/lns/operators/destruction/clean_empty_route.cpp index edd7968..aaeeede 100644 --- a/src/lns/operators/destruction/clean_empty_route.cpp +++ b/src/lns/operators/destruction/clean_empty_route.cpp @@ -5,7 +5,6 @@ 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/operators/destruction/random_destroy.cpp b/src/lns/operators/destruction/random_destroy.cpp index cb75473..3b38109 100644 --- a/src/lns/operators/destruction/random_destroy.cpp +++ b/src/lns/operators/destruction/random_destroy.cpp @@ -11,7 +11,7 @@ void RandomDestroy::destroySolution(Solution &solution) const { std::cout << "RD "; - int nbRequests = solution.requestsFulFilledCount(); + int nbRequests = solution.requestsFulfilledCount(); int actualNumberOfPairsToDestroy = std::min(nbRequests, numberOfPairsToDestroy); int remainingPairToDelete = actualNumberOfPairsToDestroy; diff --git a/src/lns/solution/route.h b/src/lns/solution/route.h index c4c30e3..a2dd1a6 100644 --- a/src/lns/solution/route.h +++ b/src/lns/solution/route.h @@ -1,57 +1,58 @@ #pragma once #include <vector> - #include "input/pdptw_data.h" #include "input/time_window.h" /** - * Represent a route for the PDPTW - * A route does not include the depot at the begining and the end ! + * Represents a route for the PDPTW. + * A route does not include the depot at the beginning and the end. */ class Route { - private: - std::vector<int> route; - int cost; - - -public: + std::vector<int> route; // Stores the route as a sequence of location IDs. + int cost; // The cost of the route. +public: + // Constructors Route(); Route(std::vector<int> route, int cost); + + // Getters int getCost() const; - const std::vector<int> & getRoute() const; + const std::vector<int>& getRoute() const; - // get Location - int getLocation(int index) const; + // Get the location at a given index in the route + int getLocation(int index) const; /** - * Given a locationID, return the Index in the route. - * return -1 if no such location. + * Given a locationID, returns the index in the route. + * @return -1 if the locationID is not found in the route. */ int getIndex(int locationID) const; - + /** - * Given the position of a location in a route, return the paired location position. - * Example, a pair of location ID 3 and 4 are in a route at position 7 and 18, - * getPairLocationPosition(7) must return 18 and vice versa. + * Given the position of a location in the route, returns the paired location position. + * Example: if location ID 3 and 4 are in the route at positions 7 and 18, + * getPairLocationPosition(7) should return 18 and vice versa. */ - int getPairLocationPosition(int position, const PDPTWData &data) const; - + int getPairLocationPosition(int position, const PDPTWData& data) const; + // Utility function to print the route details void print() const; /** - * Add a location index in the route (does not update the route cost) + * Adds a location index in the route at a specified position. + * Does not update the route cost. */ void insertAt(int locationIndex, int position); - /* - * Remove the element at "position" - */ + /** + * Removes the element at the specified position in the route. + */ void deleteAt(int position); + // Returns the number of locations in the route int getSize() const; }; \ No newline at end of file diff --git a/src/lns/solution/solution.cpp b/src/lns/solution/solution.cpp index 2c540f1..cb3f55b 100644 --- a/src/lns/solution/solution.cpp +++ b/src/lns/solution/solution.cpp @@ -30,7 +30,7 @@ void Solution::initRoutes() routes.clear(); for (unsigned int i = 0; i < NUMBER_VEHICLE; ++i) { - this->routes.emplace_back(); + routes.emplace_back(); } } @@ -44,8 +44,6 @@ void Solution::initConstraints() void Solution::computeAndStoreSolutionCost() { rawCost = computeSolutionCost(); - - // add penalty for solution in the pairBank ? totalCost = rawCost + computePenalisation(); } @@ -64,20 +62,19 @@ double Solution::computePenalisation() const return getBank().size() * EXCLUSION_PENALTY + getNumberOfRoutes() * ROUTE_PENALTY; } -int Solution::getNumberOfRoutes() const +int Solution::getNumberOfRoutes() const { - int cpt = 0; - for (Route const &route : getRoutes()) + int count = 0; + for (Route const &route: getRoutes()) { - if (!(route.getRoute().empty())) + if (!route.getRoute().empty()) { - cpt++; + count++; } } - return cpt; + return count; } - void Solution::init() { initPairBank(); @@ -98,7 +95,7 @@ Solution::Solution(PDPTWData const &data) : data(data) Solution Solution::emptySolution(PDPTWData const &data) { - Solution sol = Solution(data); + Solution sol(data); return sol; } @@ -111,19 +108,14 @@ Solution::Solution(Solution const &rhs) : Solution(rhs.getData()) Solution &Solution::operator=(Solution const &rhs) { - if (&rhs == this) - { - return *this; - } + if (this == &rhs) return *this; data = rhs.data; rawCost = rhs.rawCost; totalCost = rhs.totalCost; pairBank = rhs.pairBank; - routes.clear(); routes = rhs.routes; - constraints.clear(); std::ranges::transform(rhs.constraints, std::back_inserter(constraints), [this](auto const &constraintPtr) { return constraintPtr->clone(*this); @@ -132,21 +124,18 @@ Solution &Solution::operator=(Solution const &rhs) return *this; } -Solution::Solution(Solution &&sol) noexcept : data(sol.data) +Solution::Solution(Solution &&sol) noexcept : data(std::move(sol.data)) { *this = std::move(sol); } Solution &Solution::operator=(Solution &&sol) noexcept { - if (&sol == this) - { - return *this; - } + if (this == &sol) return *this; - data = sol.data; - rawCost = sol.rawCost; - totalCost = sol.totalCost; + data = std::move(sol.data); + rawCost = std::move(sol.rawCost); + totalCost = std::move(sol.totalCost); pairBank = std::move(sol.pairBank); routes = std::move(sol.routes); @@ -225,7 +214,7 @@ std::vector<std::unique_ptr<Constraint>> const &Solution::getConstraints() const return constraints; } -int Solution::requestsFulFilledCount() const +int Solution::requestsFulfilledCount() const { int count = 0; for (Route const &route: getRoutes()) @@ -256,34 +245,30 @@ unsigned int Solution::missingPairCount() const return pairBank.size(); } - bool Solution::checkModification(AtomicRecreation const &modification) const { - //std::cout << "--- Check Modification Validity : "; ModificationCheckVariant const &checkVariant = modification.asCheckVariant(); - // visitor pattern - for (std::unique_ptr<Constraint> const &constraint: constraints) + + for (auto const &constraint: constraints) { if (!constraint->checkVariant(checkVariant)) { - //std::cout << "\n"; return false; } } - //std::cout << "\n"; return true; } void Solution::beforeApplyModification(AtomicModification &modification) { - // pre modification check + // Pre-modification check check(); } void Solution::afterApplyModification(AtomicModification &modification) { - // constraint status update - for (std::unique_ptr<Constraint> &constraint: constraints) + // Constraint status update + for (auto &constraint: constraints) { constraint->applyVariant(modification.asApplyVariant()); } @@ -292,9 +277,9 @@ void Solution::afterApplyModification(AtomicModification &modification) void Solution::applyRecreateSolution(AtomicRecreation &modification) { beforeApplyModification(modification); - modification.modifySolution(*this); - // we update the request bank + + // Update the request bank if (int pairID = modification.getAddedPairs()) { pairBank.erase(std::ranges::find(pairBank, pairID)); @@ -306,12 +291,10 @@ void Solution::applyRecreateSolution(AtomicRecreation &modification) void Solution::applyDestructSolution(AtomicDestruction &modification) { beforeApplyModification(modification); - modification.modifySolution(*this); - // updating request bank - std::vector<int> const &deletedPair = modification.getDeletedPairs(); - //pairBank.reserve(pairBank.size() + deletedPair.size()); + // Update the request bank + std::vector<int> const &deletedPair = modification.getDeletedPairs(); pairBank.insert(pairBank.end(), deletedPair.begin(), deletedPair.end()); afterApplyModification(modification); @@ -324,17 +307,16 @@ void Solution::check() const void Solution::print() const { - std::cout << "\nRawCost : " << rawCost << "\n" - << "TotalCost : " << totalCost << "\n"; - - std::cout << "Pair Bank : \n"; + std::cout << "\nRawCost: " << rawCost << "\n" + << "TotalCost: " << totalCost << "\n"; + std::cout << "Pair Bank:\n"; for (int const id: getBank()) { std::cout << id << ", "; } - std::cout << "\nRoutes : \n"; + std::cout << "\nRoutes:\n"; int i = 0; for (Route const &route: getRoutes()) { @@ -343,10 +325,11 @@ void Solution::print() const ++i; } - std::cout << "Constraints : \n"; - for (std::unique_ptr<Constraint> const &constraint: constraints) + std::cout << "Constraints:\n"; + for (auto const &constraint: constraints) { constraint->print(); } + std::cout << "\n"; -} \ No newline at end of file +} diff --git a/src/lns/solution/solution.h b/src/lns/solution/solution.h index 3de416b..364799b 100644 --- a/src/lns/solution/solution.h +++ b/src/lns/solution/solution.h @@ -6,6 +6,7 @@ #include "route.h" #include <iostream> +#include <memory> #include <spdlog/spdlog.h> #include <vector> @@ -15,8 +16,8 @@ class AtomicDestruction; class TimeWindowConstraint; class CapacityConstraint; -/** - * Represent a solution of PDPTW +/* + * Represents a solution of the PDPTW problem. */ class Solution { @@ -24,135 +25,169 @@ public: using PairBank = std::vector<int>; private: - std::reference_wrapper<PDPTWData const> data; - //PDPTWData const &data; - /* - * Store IDs of a pairs (Pickup & Delivery) that are not assigned yet to a route. - */ - PairBank pairBank; - /* - * Vector of routes representing the solution - */ - std::vector<Route> routes; + std::reference_wrapper<const PDPTWData> data;// Reference to PDPTW data + PairBank pairBank; // Unassigned pairs (Pickup & Delivery) + std::vector<Route> routes; // Routes in the solution double rawCost; double totalCost; std::vector<std::unique_ptr<Constraint>> constraints; public: - //========CONSTRUCTORS, COPY, MOVE, DESTRUCTORS=========== - /** - * Expected way to construct a solution. - * Generate an empty solution with all pairs in the pairBank and one empty route. + /* + * Creates an empty solution with all pairs in the pairBank and one empty route. */ static Solution emptySolution(PDPTWData const &data); - /** - * In depth copy of the solution + + /* + * In-depth copy of the solution. */ Solution(Solution const &); - /** - * In depth copy of the solution + /* + * Copy assignment. */ Solution &operator=(Solution const &); + /* + * Move constructor. + */ Solution(Solution &&) noexcept; + /* + * Move assignment. + */ Solution &operator=(Solution &&) noexcept; + /* + * Destructor. + */ ~Solution() noexcept; - + /* + * Constructs a solution with given data. + */ explicit Solution(PDPTWData const &data); - - /** - * For testing/debugging. - * Use emptySolution(const PDPTWData &data) to create an initial empty solution. + /* + * Constructs a solution with specified parameters. */ - Solution(PDPTWData const &data, Solution::PairBank pairbank, std::vector<Route> routes, double routeCost, - double totalCost); - + Solution(PDPTWData const &data, PairBank pairbank, std::vector<Route> routes, double routeCost, double totalCost); + /* + * Returns the pair bank. + */ PairBank const &getBank() const; + /* + * Returns the pair bank. + */ PairBank const &getPairBank() const; + /* + * Returns a mutable reference to the pair bank. + */ PairBank &getPairBank(); + /* + * Returns the list of routes. + */ std::vector<Route> const &getRoutes() const; + /* + * Returns a mutable reference to a specific route. + */ + Route &getRoute(int routeIndex); + /* + * Returns a constant reference to a specific route. + */ Route const &getRoute(int routeIndex) const; + /* + * Returns the PDPTW data reference. + */ PDPTWData const &getData() const; + /* + * Returns the raw cost of the solution. + */ double getRawCost() const; + /* + * Returns the total cost of the solution. + */ double getCost() const; + /* + * Returns the number of routes. + */ int getNumberOfRoutes() const; + /* + * Returns the count of missing pairs. + */ unsigned int missingPairCount() const; + /* + * Returns the constraints applied to the solution. + */ std::vector<std::unique_ptr<Constraint>> const &getConstraints() const; - /** - * Return the route index associated to the given location ID. - * -1 if the location is not in a route. + /* + * Returns the route index associated with a given location ID, or -1 if not in a route. */ int getRouteIDOf(int locationID) const; - - /** - * Return the number of fullfilled requests. - * The solution must be consistent ! - * (compute this number by looking at the size of each routes) + /* + * Returns the number of fulfilled requests. */ - int requestsFulFilledCount() const; - - /** - * Check that the modification is valid regarding all the constraints - * @param modification - * @return true if the modification is valid + int requestsFulfilledCount() const; + /* + * Checks if the modification is valid according to all constraints. */ bool checkModification(AtomicRecreation const &modification) const; - - // using solution checker to verify that the solution is correct. Most used for debug as it is correctness oriented and not performance oriented + /* + * Verifies the correctness of the solution, mainly for debugging. + */ void check() const; - /** - * Pre modification check. - * @param modification Must be a valid modification. + /* + * Pre-modification check. The modification must be valid. */ void beforeApplyModification(AtomicModification &modification); - - /** - * Update constraints, it is expected that the solution has already been modified. - * @param modification Must be a valid modification. + /* + * Updates constraints after modification is applied. */ void afterApplyModification(AtomicModification &modification); - /** - * Apply the given the modification on the solution. Does not check validity. - * @param modification Must be a valid recreation. + /* + * Applies recreation modification to the solution without validity checks. */ void applyRecreateSolution(AtomicRecreation &modification); - - /** - * Apply the given the modification on the solution. Does not check validity. - * @param modification Must be a destruction. + /* + * Applies destruction modification to the solution without validity checks. */ void applyDestructSolution(AtomicDestruction &modification); + /* + * Computes and returns the penalty value. + */ double computePenalisation() const; - - // For route modification + /* + * Returns a mutable reference to the routes. + */ std::vector<Route> &getRoutes(); - Route &getRoute(int routeIndex); - + /* + * Prints the solution details. + */ void print() const; - + /* + * Computes and stores the total solution cost. + */ void computeAndStoreSolutionCost(); private: - /** - * Does the initialisation of the object, called by the constructor + /* + * Initializes the object, called by the constructor. */ void init(); - - /** - * Init/reset the constraints of the problem + /* + * Initializes or resets the problem constraints. */ void initConstraints(); + /* + * Initializes the routes. + */ void initRoutes(); + /* + * Initializes the pair bank. + */ void initPairBank(); - - /** - * Compute the cost of the solution (routes cost) + /* + * Computes the total cost of the solution. */ - double computeSolutionCost() const; - -}; \ No newline at end of file + double computeSolutionCost() const; +}; diff --git a/src/mains/main.cpp b/src/mains/main.cpp index 4a7e564..96c5dea 100644 --- a/src/mains/main.cpp +++ b/src/mains/main.cpp @@ -49,7 +49,7 @@ void simpleLNS(PDPTWData const &data, Solution &startingSolution) int pairs = requests * 2 / 100; int manyPairs = requests * 40 / 100; - // threshold function to do + // threshold function ThresholdAcceptance acceptor(0.05); // lns operators @@ -73,11 +73,7 @@ void simpleLNS(PDPTWData const &data, Solution &startingSolution) // run lns output::LnsOutput result = lns::runLns(startingSolution, smallLargeSelector, acceptor); - Solution sol = result.getBestSolution(); - - CleanEmptyRoute clean = CleanEmptyRoute(); - clean.destroySolution(sol); - sol.print(); + result.getBestSolution().print(); } int main(int argc, char **argv) @@ -87,7 +83,7 @@ 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/lc101.json"; + std::string filepath = "/home/a24jacqb/Documents/Code/pdptw-main/data_in/pdp_100/lc103.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"; diff --git a/src/types.h b/src/types.h index 00139bf..763a3aa 100644 --- a/src/types.h +++ b/src/types.h @@ -19,5 +19,4 @@ using TimeInteger = double; TimeInteger constexpr UNDEF_TIMESTAMP = std::numeric_limits<TimeInteger>::max(); -using Matrix = std::vector<std::vector<double>>; - +using Matrix = std::vector<std::vector<double>>; diff --git a/src/utils.cpp b/src/utils.cpp index 9b61c3f..97f0d84 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -7,7 +7,7 @@ namespace// anonymous namespace { std::mt19937_64 randomGenerator;// NOLINT(*-msc51-cpp) => deterministic random! - bool seedSet = false; + bool seedSet = SEED_SET; std::uniform_real_distribution<> distribution(0, 1); }// namespace -- GitLab