diff --git a/src/config.h b/src/config.h index 444c3d046c583e3122b06423f30cfa03a7c462de..33bb959dba6b2889574a7edbb89688e7f3f00dd5 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 0000000000000000000000000000000000000000..774fb9531017a3978b20ffbbb57c2d9c13fa09c5 --- /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 0000000000000000000000000000000000000000..97cfee4606276f7b332b58051419f945acf1a1c4 --- /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 8735e5ea97ca005096fb8e0307dffa57be5273b3..f85a2d6b48514e7b8ba2666493d98dc7088e2623 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 edd796874a9fa8c100949e5cfcbb63b7f49566e2..aaeeede3114558cd30f7026e15fe970cd1944520 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 cb75473392e3b7f4cf166cf70e8197b26be77fe9..3b381094d2a7a7fe62e67720a2e614f97ba7cf40 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 c4c30e3bafd920c48bb11ce9522d5baad3edd9c4..a2dd1a6aecf3e42a2d3f3a138e9d61d99946c928 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 2c540f1cb8ca461a5f9a905776ffcd99a4dcf598..cb3f55b368fd3e081c1322b4bd38ec241d01e053 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 3de416ba1a81cc2c961688e99d8eb21f3e4571f2..364799bf60b72a731fa647e582d18bde4e0914e3 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 4a7e564a96dbab16cb25c36780ae8421040ad970..96c5dea6b9457f701added464cc7df331658ecee 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 00139bfa4eeff68f78325fb51359c74e414eb4ad..763a3aa3c76550f0d2e2754bafff80e4da9f7186 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 9b61c3ff12cdf64552c0de9a7b47c320e38e3aa5..97f0d84801de4cce1e37eafb207a28d0a663b54e 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