From 056b4f35d065ce4f2589d45ecfdf9f2855f6c540 Mon Sep 17 00:00:00 2001
From: awenjb <126257927+awenjb@users.noreply.github.com>
Date: Wed, 5 Mar 2025 10:43:42 +0100
Subject: [PATCH] Add Basic Operators

- add enumeration of all Insert position for a pair
- add shuffle strategy
---
 CMakeLists.txt                                |  2 +
 src/lns/modification/atomic_recreation.h      |  7 +++
 src/lns/modification/pair/insert_pair.cpp     |  5 ++
 src/lns/modification/pair/insert_pair.h       |  1 +
 src/lns/modification/route/insert_route.cpp   |  4 ++
 src/lns/modification/route/insert_route.h     |  2 +
 src/lns/operators/generators/enumerate.cpp    | 32 +++++++++++
 src/lns/operators/generators/enumerate.h      | 23 ++++++++
 .../generators/modification_generator.cpp     | 33 +++++++++++
 .../generators/modification_generator.h       | 47 +++++++++++++++
 .../reconstruction/list_heuristic_insertion.h | 39 +++++++++++++
 .../list_heuristic_insertion.hpp              | 57 +++++++++++++++++++
 src/lns/operators/sorting_strategy.cpp        |  2 +-
 src/lns/operators/sorting_strategy.h          |  4 +-
 src/lns/solution/solution.cpp                 | 15 +++++
 src/lns/solution/solution.h                   |  6 ++
 16 files changed, 276 insertions(+), 3 deletions(-)
 create mode 100644 src/lns/operators/generators/enumerate.cpp
 create mode 100644 src/lns/operators/generators/enumerate.h
 create mode 100644 src/lns/operators/generators/modification_generator.cpp
 create mode 100644 src/lns/operators/generators/modification_generator.h
 create mode 100644 src/lns/operators/reconstruction/list_heuristic_insertion.h
 create mode 100644 src/lns/operators/reconstruction/list_heuristic_insertion.hpp

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 80c9ac9..e022547 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 1d726a4..227b4f9 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 bf717d0..4206f3d 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 84fe918..14376aa 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 96cf368..7a6be79 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 e5d6413..8775489 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 0000000..b06b817
--- /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 0000000..0e5a827
--- /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 0000000..be8b0a2
--- /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 0000000..23026bc
--- /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 0000000..4570caf
--- /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 0000000..11fb749
--- /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 08a1629..a639b8e 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 55f46b4..590b698 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 f24844f..21d0c48 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 d5c1cd5..aa3ff6c 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.
-- 
GitLab