diff --git a/CMakeLists.txt b/CMakeLists.txt index 80c9ac985e391ef04812f47cf720329c8cbea4f5..e022547daa62330c6d8b5a06b631134ab94daf33 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -36,6 +36,8 @@ add_executable(pdptw src/mains/main.cpp src/output/solution_checker.cpp src/lns/operators/sorting_strategy.cpp src/lns/operators/destruction/random_destroy.cpp + src/lns/operators/generation/enumerate.cpp + src/lns/operators/generation/modification_generator.cpp src/lns/operators/selector/operator_selection.cpp src/utils.cpp ) diff --git a/src/lns/modification/atomic_recreation.h b/src/lns/modification/atomic_recreation.h index 1d726a43a2bf7d7eeb56f2406803282779afcad2..227b4f913b79e54cd9e4a5e6b4876028c9e4da75 100644 --- a/src/lns/modification/atomic_recreation.h +++ b/src/lns/modification/atomic_recreation.h @@ -12,6 +12,13 @@ class AtomicRecreation : public AtomicModification public: ~AtomicRecreation() override = default; + /** + * Visitor pattern double dispatch. + * Only need to be implemented with `return *this;` + * Update ModificationCheckVariant alias when adding new recreate modification + */ + virtual ModificationCheckVariant asCheckVariant() const = 0; + /** * @return the pickup location added to the solution, nullptr if none were added */ diff --git a/src/lns/modification/pair/insert_pair.cpp b/src/lns/modification/pair/insert_pair.cpp index bf717d0d92c80742e187511107b8bd4febb3e61f..4206f3d7cdbb0110eac092c8c74d44eae32cdbb6 100644 --- a/src/lns/modification/pair/insert_pair.cpp +++ b/src/lns/modification/pair/insert_pair.cpp @@ -57,6 +57,11 @@ double InsertPair::evaluate(Solution const &solution) const { } +ModificationCheckVariant InsertPair::asCheckVariant() const +{ + return *this; +} + int InsertPair::getPickupInsertion() const { return pickupInsertion; diff --git a/src/lns/modification/pair/insert_pair.h b/src/lns/modification/pair/insert_pair.h index 84fe9186a262aa9888d299b60d633cc44f0fdec5..14376aabf796ec6fc51f9a109948fe61e16d18b5 100644 --- a/src/lns/modification/pair/insert_pair.h +++ b/src/lns/modification/pair/insert_pair.h @@ -65,4 +65,5 @@ public: Pair const &getPair() const; Index getIndex() const; + ModificationCheckVariant asCheckVariant() const override; }; \ No newline at end of file diff --git a/src/lns/modification/route/insert_route.cpp b/src/lns/modification/route/insert_route.cpp index 96cf368d0c64c197fb90a08020f3fd5d59bc9e41..7a6be790ae6fb456ad94c3253c0ec0d3565e9018 100644 --- a/src/lns/modification/route/insert_route.cpp +++ b/src/lns/modification/route/insert_route.cpp @@ -19,3 +19,7 @@ Location const *InsertRoute::getAddedLocation() const return nullptr; } +ModificationCheckVariant InsertRoute::asCheckVariant() const +{ + return *this; +} diff --git a/src/lns/modification/route/insert_route.h b/src/lns/modification/route/insert_route.h index e5d64134ce42048e2b06b98f05774ded731cc72f..877548964092882f44ac8292369c71ce2c9d8022 100644 --- a/src/lns/modification/route/insert_route.h +++ b/src/lns/modification/route/insert_route.h @@ -17,4 +17,6 @@ public: void modifySolution(Solution &solution) override; double evaluate(Solution const &solution) const override; Location const *getAddedLocation() const override; + + ModificationCheckVariant asCheckVariant() const override; }; \ No newline at end of file diff --git a/src/lns/operators/generators/enumerate.cpp b/src/lns/operators/generators/enumerate.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b06b8179c0774e710b2a83b76d705fc17c8c1963 --- /dev/null +++ b/src/lns/operators/generators/enumerate.cpp @@ -0,0 +1,32 @@ +#include "enumerate.h" + +namespace enumeration +{ + /** + * Enumerate InsertPair modifications. + * consumeModification is called for each modification. + * Does some checks to cut some loops. (TO DO) + * @param solution + * @param pair + * @param consumeModification called when a modification is created + */ + void enumerateAllInsertPair(Solution const &solution, Pair const &pair, + std::function<void(InsertPair &&)> const &consumeModification) + { + int routeIndex = 0; + for (const Route &route : solution.getRoutes()) + { + for (int p=0; p <= route.getSize(); p++) + { + for (int d=p; d <= route.getSize(); d++) + { + Index index = std::make_tuple(routeIndex, p,d); + consumeModification(InsertPair(index, pair)); + } + } + ++routeIndex; + } + } + + +}// namespace enumeration \ No newline at end of file diff --git a/src/lns/operators/generators/enumerate.h b/src/lns/operators/generators/enumerate.h new file mode 100644 index 0000000000000000000000000000000000000000..0e5a8277fe283278e243d838107d80f2491eec3e --- /dev/null +++ b/src/lns/operators/generators/enumerate.h @@ -0,0 +1,23 @@ +#pragma once + +#include "lns/modification/pair/insert_pair.h" + +#include <functional> + +namespace enumeration +{ + + /** + * Enumerate InsertDelivery modifications. + * consumeModification is called for each modification. + * Does some checks to cut some loops. + * @param solution + * @param request + * @param consumeModification called when a modification is created (ex : keepBestSolution) + */ + void enumerateAllInsertPair(Solution const &solution, Pair const &Pair, + std::function<void(InsertPair &&)> const &consumeModification); + + +}// namespace enumeration + diff --git a/src/lns/operators/generators/modification_generator.cpp b/src/lns/operators/generators/modification_generator.cpp new file mode 100644 index 0000000000000000000000000000000000000000..be8b0a290bd3726b9e72445b2ba02ff97f405248 --- /dev/null +++ b/src/lns/operators/generators/modification_generator.cpp @@ -0,0 +1,33 @@ +#include "modification_generator.h" + +#include "enumerate.h" + +namespace generator +{ + /** + * + * @tparam ModificationType the type of modification to be checked + * @param solution the solution to check the modification validity + * @param list the modification will be added to this list if valid + * @return a function that takes a ModificationType and add it to list iff it is valid + */ + template<std::derived_from<AtomicRecreation> ModificationType> + std::function<void(ModificationType &&)> addToListIfValidTemplate(Solution const &solution, + ModificationContainer &list) + { + return [&](ModificationType &&modification) { + if (solution.checkModification(modification)) + { + list.push_front(std::make_unique<ModificationType>(modification)); + } + }; + } + + void AllTypedModifications<InsertPair>::populate(Solution const &solution, Pair const &request, + std::forward_list<std::unique_ptr<AtomicRecreation>> &list) + { + enumeration::enumerateAllInsertPair( + solution, request, addToListIfValidTemplate<InsertPair>(solution, list)); + } + +}// namespace generator diff --git a/src/lns/operators/generators/modification_generator.h b/src/lns/operators/generators/modification_generator.h new file mode 100644 index 0000000000000000000000000000000000000000..23026bc9899d66e695036474e28b41c0ea605872 --- /dev/null +++ b/src/lns/operators/generators/modification_generator.h @@ -0,0 +1,47 @@ +#pragma once + +#include "input/pair.h" +#include "lns/modification/atomic_recreation.h" +#include "lns/modification/pair/insert_pair.h" + +#include <forward_list> +#include <memory> + +namespace generator +{ + using ModificationContainer = std::forward_list<std::unique_ptr<AtomicRecreation>>; + + /** + * Abstract class to generate valid modifications + */ + class ModificationGenerator + { + public: + /** + * Adds valid insertions of request to the solution in the list provided as argument + */ + virtual void populate(Solution const &solution, Pair const &, ModificationContainer &modificationList) = 0; + + virtual ~ModificationGenerator() = default; + }; + + /** + * Empty template class to generate ALL valid modification of a specific type + * @tparam T the type of modification to generate + */ + template<std::derived_from<AtomicRecreation> T> + class AllTypedModifications : public ModificationGenerator + { + // this assert will always fail. Needs a template specification + static_assert(sizeof(T) == 0, "The generator for type T has not been defined yet."); + }; + + template<> + class AllTypedModifications<InsertPair> : public ModificationGenerator + { + public: + void populate(Solution const &solution, Pair const &, ModificationContainer &list) override; + }; + + +}// namespace generator \ No newline at end of file diff --git a/src/lns/operators/reconstruction/list_heuristic_insertion.h b/src/lns/operators/reconstruction/list_heuristic_insertion.h new file mode 100644 index 0000000000000000000000000000000000000000..4570caf3f153278e59a44e8503c8c6caaebdd6c2 --- /dev/null +++ b/src/lns/operators/reconstruction/list_heuristic_insertion.h @@ -0,0 +1,39 @@ +#pragma once + +#include "input/data.h" +#include "input/pdptw_data.h" +#include "lns/operators/abstract_operator.h" +#include "lns/operators/generators/modification_generator.h" +#include "lns/operators/sorting_strategy.h" +#include "utils.h" + +#include <optional> + +class AtomicRecreation; + +/** + * This is a template class for list heuristic operator. + * Implementation is in the .hpp file + * @tparam Strategy defines in which order we are treating the requests + * @tparam Generator defines which modifications are going to be used + */ +template<std::derived_from<sorting_strategy::SortingStrategy> Strategy, + std::derived_from<generator::ModificationGenerator> Generator> +class ListHeuristicInsertion : public ReconstructionOperator +{ +private: + using AtomicRecreationPtr = std::unique_ptr<AtomicRecreation>; + +public: + explicit ListHeuristicInsertion(); + + void reconstructSolution(Solution &solution, double blinkRate) const override; + +private: + /** + * @param blinkRate probability to ignore the request insertion + * @return the best insertion found + */ + static std::unique_ptr<AtomicRecreation> choosingStrategy(Solution &solution, Pair const &pair, + double blinkRate); +}; diff --git a/src/lns/operators/reconstruction/list_heuristic_insertion.hpp b/src/lns/operators/reconstruction/list_heuristic_insertion.hpp new file mode 100644 index 0000000000000000000000000000000000000000..11fb7492121b61b5e34313d02f52d09484d4328f --- /dev/null +++ b/src/lns/operators/reconstruction/list_heuristic_insertion.hpp @@ -0,0 +1,57 @@ +#pragma once + +#include "list_heuristic_insertion.h" +// This is a header, but it does define templates, so we can't put them in .cpp file +// for forward declaration you can use the .h file, but if you need to use the class, you must include this one instead + +#include "lns/operators/generators/modification_generator.h" + +#include <concepts> + + +template<std::derived_from<sorting_strategy::SortingStrategy> Strategy, + std::derived_from<generator::ModificationGenerator> Generator> +ListHeuristicInsertion<Strategy, Generator>::ListHeuristicInsertion() = default; + +template<std::derived_from<sorting_strategy::SortingStrategy> Strategy, + std::derived_from<generator::ModificationGenerator> Generator> +void ListHeuristicInsertion<Strategy, Generator>::reconstructSolution(Solution &solution, double blinkRate) const +{ + std::vector<int> sortedPairs = Strategy(solution).sortRequests(); + AtomicRecreationPtr recreation; + for (int pairID: sortedPairs) + { + Pair const &pair = solution.getData().getPair(pairID); + recreation = ListHeuristicInsertion::choosingStrategy(solution, pair, blinkRate); + if (recreation) + { + solution.applyRecreateSolution(*recreation); + } + } +} + + +template<std::derived_from<sorting_strategy::SortingStrategy> Strategy, + std::derived_from<generator::ModificationGenerator> Generator> +std::unique_ptr<AtomicRecreation> ListHeuristicInsertion<Strategy, Generator>::choosingStrategy(Solution &solution, + Pair const &pair, + double blinkRate) +{ + AtomicRecreationPtr bestInsertion; + double bestKnownInsertionCost = std::numeric_limits<double>::max(); + generator::ModificationContainer modifications; + Generator().populate(solution, pair, modifications); + for (AtomicRecreationPtr &possibleRecreation: modifications) + { + if (util::getRandom() < 1 - blinkRate) + { + double newInsertionCost = possibleRecreation->evaluate(solution); + if (newInsertionCost < bestKnownInsertionCost) + { + bestKnownInsertionCost = newInsertionCost; + bestInsertion = std::move(possibleRecreation); + } + } + } + return bestInsertion; +} \ No newline at end of file diff --git a/src/lns/operators/sorting_strategy.cpp b/src/lns/operators/sorting_strategy.cpp index 08a16296a5fa064b68a205e56a014a14147ae289..a639b8e996baea1061a4b90d4051da7f9b9fca94 100644 --- a/src/lns/operators/sorting_strategy.cpp +++ b/src/lns/operators/sorting_strategy.cpp @@ -5,7 +5,7 @@ #include <algorithm> #include <ranges> -std::vector<int> const &sorting_strategy::Shuffle::sortRequests() const +std::vector<int> const &sorting_strategy::Shuffle::sortPairs() const { auto &bank = getSolution().getPairBank(); std::ranges::shuffle(bank, util::getRawRandom()); diff --git a/src/lns/operators/sorting_strategy.h b/src/lns/operators/sorting_strategy.h index 55f46b4b31607b80c409add57aff9e068f03ea0b..590b698b4bb43f30c774631db5f4d2f2081f612a 100644 --- a/src/lns/operators/sorting_strategy.h +++ b/src/lns/operators/sorting_strategy.h @@ -14,7 +14,7 @@ namespace sorting_strategy Solution &getSolution() const { return solution; } - virtual std::vector<int> const &sortRequests() const = 0; + virtual std::vector<int> const &sortPairs() const = 0; virtual ~SortingStrategy() = default; private: @@ -30,7 +30,7 @@ namespace sorting_strategy using SortingStrategy::SortingStrategy; public: - std::vector<int> const &sortRequests() const override; + std::vector<int> const &sortPairs() const override; }; diff --git a/src/lns/solution/solution.cpp b/src/lns/solution/solution.cpp index f24844fe15642d69d03b8bd858150d923d819c7f..21d0c48654ccab14c1fc2174b2cfa120b4a6df07 100644 --- a/src/lns/solution/solution.cpp +++ b/src/lns/solution/solution.cpp @@ -143,6 +143,21 @@ int Solution::requestsFulFilledCount() const return count; } +bool Solution::checkModification(AtomicRecreation const &modification) const +{ + ModificationCheckVariant const &checkVariant = modification.asCheckVariant(); + // visitor pattern + for (std::unique_ptr<Constraint> const &constraint: constraints) + { + if (!constraint->checkVariant(checkVariant)) + { + return false; + } + } + return true; +} + + void Solution::beforeApplyModification(AtomicModification &modification) { // pre check to do ? diff --git a/src/lns/solution/solution.h b/src/lns/solution/solution.h index d5c1cd55f9f28c2d228cc4109865ef69bb3354fa..aa3ff6c0c6941312129cc136ace3f4321903b18c 100644 --- a/src/lns/solution/solution.h +++ b/src/lns/solution/solution.h @@ -68,6 +68,12 @@ public: */ int requestsFulFilledCount() const; + /** + * Check that the modification is valid regarding all the constraints + * @param modification + * @return true if the modification is valid + */ + bool checkModification(AtomicRecreation const &modification) const; /** * Pre modification check.