From 54e16ee5a1a09dd028a364430cc1eafe789b54d2 Mon Sep 17 00:00:00 2001 From: awenjb <126257927+awenjb@users.noreply.github.com> Date: Tue, 4 Mar 2025 15:43:42 +0100 Subject: [PATCH] add Solution Components - add basic solution Checker - add random destroy Operator - revised some parts of the code --- .clang-format | 74 ++++++++ CMakeLists.txt | 8 +- src/config.h | 4 +- src/input/pdptw_data.cpp | 12 ++ src/input/pdptw_data.h | 2 + .../time_window/time_window_constraint.cpp | 15 -- src/lns/modification/atomic_destruction.h | 2 +- src/lns/modification/atomic_modification.h | 14 +- src/lns/modification/pair/insert_pair.cpp | 5 + src/lns/modification/pair/insert_pair.h | 2 + src/lns/modification/pair/remove_pair.cpp | 58 +++--- src/lns/modification/pair/remove_pair.h | 8 +- src/lns/modification/route/remove_route.cpp | 38 ++-- src/lns/modification/route/remove_route.h | 6 +- src/lns/operators/abstract_operator.h | 17 ++ .../operators/destruction/random_destroy.cpp | 64 +++++++ .../operators/destruction/random_destroy.h | 19 ++ .../operators/selector/operator_selection.cpp | 0 .../operators/selector/operator_selector.h | 0 src/lns/solution/route.cpp | 18 ++ src/lns/solution/route.h | 23 ++- src/lns/solution/solution.cpp | 100 +++++++--- src/lns/solution/solution.h | 108 +++++++---- src/mains/main.cpp | 77 -------- src/output/solution_checker.cpp | 178 ++++++++++++++++++ src/output/solution_checker.h | 58 ++++++ src/utils.cpp | 32 ++++ src/utils.h | 24 +++ 28 files changed, 734 insertions(+), 232 deletions(-) create mode 100644 .clang-format create mode 100644 src/lns/operators/abstract_operator.h create mode 100644 src/lns/operators/destruction/random_destroy.cpp create mode 100644 src/lns/operators/destruction/random_destroy.h create mode 100644 src/lns/operators/selector/operator_selection.cpp create mode 100644 src/lns/operators/selector/operator_selector.h create mode 100644 src/utils.cpp create mode 100644 src/utils.h diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..856f134 --- /dev/null +++ b/.clang-format @@ -0,0 +1,74 @@ +BasedOnStyle: LLVM +Language: Cpp +Standard: c++20 +AccessModifierOffset: -4 +AlignAfterOpenBracket: Align +AlignConsecutiveAssignments: None +AlignOperands: Align +AllowAllArgumentsOnNextLine: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: Empty +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: Inline +AllowShortIfStatementsOnASingleLine: Always +AllowShortLambdasOnASingleLine: All +AllowShortLoopsOnASingleLine: true +AlwaysBreakAfterReturnType: None +AlwaysBreakTemplateDeclarations: Yes +BinPackArguments: false +BreakBeforeBraces: Custom +BraceWrapping: + AfterCaseLabel: false + AfterClass: true + AfterStruct: true + AfterControlStatement: Always + AfterEnum: true + AfterFunction: true + AfterNamespace: true + AfterUnion: true + BeforeCatch: false + BeforeElse: true + IndentBraces: false + SplitEmptyFunction: false + SplitEmptyRecord: true +BreakBeforeBinaryOperators: None +BreakBeforeTernaryOperators: true +BreakConstructorInitializers: BeforeColon +BreakInheritanceList: BeforeColon +ColumnLimit: 120 +CompactNamespaces: false +ContinuationIndentWidth: 8 +EmptyLineBeforeAccessModifier: LogicalBlock +IncludeBlocks: Regroup +IndentCaseLabels: true +IndentPPDirectives: None +IndentWidth: 4 +KeepEmptyLinesAtTheStartOfBlocks: false +MaxEmptyLinesToKeep: 2 +NamespaceIndentation: All +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PackConstructorInitializers: BinPack +PointerAlignment: Right +ReferenceAlignment: Right +QualifierAlignment: Right +ReflowComments: false +SeparateDefinitionBlocks: Always +SpaceAfterCStyleCast: true +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: false +SpaceBeforeAssignmentOperators: true +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: ControlStatements +SpaceBeforeRangeBasedForLoopColon: false +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 0 +SpacesInAngles: false +SpacesInCStyleCastParentheses: false +SpacesInContainerLiterals: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +TabWidth: 4 +UseTab: Never diff --git a/CMakeLists.txt b/CMakeLists.txt index 9f66893..2edbfcd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,10 +15,6 @@ find_package(CLI11 REQUIRED) find_package(nlohmann_json REQUIRED) find_package(spdlog REQUIRED) - - - - add_executable(pdptw src/mains/main.cpp src/mains/main_interface.cpp src/input/data.cpp @@ -37,6 +33,10 @@ add_executable(pdptw src/mains/main.cpp src/lns/modification/route/insert_route_with_pair.cpp src/lns/modification/route/insert_route.cpp src/lns/modification/route/remove_route.cpp + src/output/solution_checker.cpp + src/lns/operators/destruction/random_destroy.cpp + src/lns/operators/selector/operator_selection.cpp + src/utils.cpp ) target_link_libraries(pdptw PRIVATE CLI11::CLI11 nlohmann_json::nlohmann_json spdlog::spdlog) diff --git a/src/config.h b/src/config.h index bf368d8..6bb3f2b 100644 --- a/src/config.h +++ b/src/config.h @@ -1,3 +1,5 @@ -const int EXCLUSION_PENALTY = 100; \ No newline at end of file +const int EXCLUSION_PENALTY = 100; + +const int RANDOM_SEED = 10; \ No newline at end of file diff --git a/src/input/pdptw_data.cpp b/src/input/pdptw_data.cpp index 142fbde..706bd09 100644 --- a/src/input/pdptw_data.cpp +++ b/src/input/pdptw_data.cpp @@ -60,6 +60,18 @@ PDPTWData::PDPTWData(int size, int capacity, Location depot, std::vector<Locatio } } +const Pair &PDPTWData::getPair(int id) const +{ + for (const Pair &pair : pairs) + { + if (id == pair.getID()) + { + return pair; + } + } + spdlog::error("Pair not found for ID {}", id); + throw std::runtime_error("Pair not found"); +} void PDPTWData::print() const { diff --git a/src/input/pdptw_data.h b/src/input/pdptw_data.h index ac0ea57..3cfb829 100644 --- a/src/input/pdptw_data.h +++ b/src/input/pdptw_data.h @@ -54,6 +54,8 @@ public: std::vector<Location> const &getLocations() const; std::vector<Pair> const &getPairs() const; + const Pair &getPair(int id) const; + /** * 0 return the depot. * Other numbers return the associated location. diff --git a/src/lns/constraints/time_window/time_window_constraint.cpp b/src/lns/constraints/time_window/time_window_constraint.cpp index b350692..c031048 100644 --- a/src/lns/constraints/time_window/time_window_constraint.cpp +++ b/src/lns/constraints/time_window/time_window_constraint.cpp @@ -134,21 +134,6 @@ void TimeWindowConstraint::ApplyModif(const PDPTWData& data, const Pair & pair, allRouteReachTimes[routeIndex][i] = std::max(allRouteReachTimes[routeIndex][i - 1], startTW) + serviceTime + travelTime; } } -/* - // Adjust the size of reachTimes vector - reachTimes.resize(routeIDs.size(), 0); - // Time to the first location - reachTimes[0] = data.getDepot().getTimeWindow().getStart() + data::TravelTime(data, 0, routeIDs.at(0)); - // Compute other reachTimes (max between arrival and start of the time window + previous service time + travel time) - for (int i = 1; i < routeIDs.size(); ++i) { - TimeInteger travelTime = data::TravelTime(data, routeIDs[i - 1], routeIDs[i]); - // locations are indexed from 0 to n-1, - TimeInteger serviceTime = data.getLocation(routeIDs[i - 1]).getServiceDuration(); - TimeInteger startTW = data.getLocation(routeIDs[i - 1]).getTimeWindow().getStart(); - - reachTimes[i] = std::max(reachTimes[i - 1], startTW) + serviceTime + travelTime; - } -*/ bool TimeWindowConstraint::check(InsertPair const &op) const { diff --git a/src/lns/modification/atomic_destruction.h b/src/lns/modification/atomic_destruction.h index f2ad02f..1506348 100644 --- a/src/lns/modification/atomic_destruction.h +++ b/src/lns/modification/atomic_destruction.h @@ -15,5 +15,5 @@ public: /** * @return the location ID removed from the solution. */ - virtual std::vector<int> const &getDeletedRequests() const = 0; + virtual std::vector<int> const &getDeletedPairs() const = 0; }; diff --git a/src/lns/modification/atomic_modification.h b/src/lns/modification/atomic_modification.h index c189594..701be75 100644 --- a/src/lns/modification/atomic_modification.h +++ b/src/lns/modification/atomic_modification.h @@ -1,8 +1,8 @@ #pragma once -#include <vector> - #include "lns/constraints/constraint.h" + +#include <vector> class Solution; /** @@ -21,11 +21,15 @@ public: */ virtual double evaluate(Solution const &solution) const = 0; - /** + /** * Apply of the modification to the solution. * @note does not check the validity of the modification, does not update solution cost nor constraints */ virtual void modifySolution(Solution &solution) = 0; -}; - + /** + * Visitor pattern double dispatch. + * Only need to be implemented with `return *this;`, update ModificationApplyVariant typedef when adding new modification. + */ + virtual ModificationApplyVariant asApplyVariant() const = 0; +}; diff --git a/src/lns/modification/pair/insert_pair.cpp b/src/lns/modification/pair/insert_pair.cpp index b3f9d4b..bf717d0 100644 --- a/src/lns/modification/pair/insert_pair.cpp +++ b/src/lns/modification/pair/insert_pair.cpp @@ -95,4 +95,9 @@ const Pair &InsertPair::getPair() const Index InsertPair::getIndex() const { return std::make_tuple(routeIndex, pickupInsertion, deliveryInsertion); +} + +ModificationApplyVariant InsertPair::asApplyVariant() const +{ + return (*this); } \ No newline at end of file diff --git a/src/lns/modification/pair/insert_pair.h b/src/lns/modification/pair/insert_pair.h index d806765..84fe918 100644 --- a/src/lns/modification/pair/insert_pair.h +++ b/src/lns/modification/pair/insert_pair.h @@ -51,6 +51,8 @@ public: ~InsertPair() override = default; + ModificationApplyVariant asApplyVariant() const override; + void modifySolution(Solution &solution) override; double evaluate(Solution const &solution) const override; Location const *getAddedLocation() const override; diff --git a/src/lns/modification/pair/remove_pair.cpp b/src/lns/modification/pair/remove_pair.cpp index 1a1c808..6cf567c 100644 --- a/src/lns/modification/pair/remove_pair.cpp +++ b/src/lns/modification/pair/remove_pair.cpp @@ -1,45 +1,35 @@ #include "remove_pair.h" -#include "input/data.h" - - -RemovePair::RemovePair(int routeIndex, int pickupDeletion, int deliveryDeletion, Pair const &pair) : - routeIndex(routeIndex), - pickupDeletion(pickupDeletion), - deliveryDeletion(deliveryDeletion), - pickupLocation(pair.getPickup()), - deliveryLocation(pair.getDelivery()), - pair(pair) {} +#include "input/data.h" -RemovePair::RemovePair(Index position, Pair const &pair) : - routeIndex(std::get<0>(position)), - pickupDeletion(std::get<1>(position)), - deliveryDeletion(std::get<2>(position)), - pickupLocation(pair.getPickup()), - deliveryLocation(pair.getDelivery()), - pair(pair) {} +RemovePair::RemovePair(int routeIndex, int pickupDeletion, int deliveryDeletion, Pair const &pair) + : routeIndex(routeIndex), pickupDeletion(pickupDeletion), deliveryDeletion(deliveryDeletion), + pickupLocation(pair.getPickup()), deliveryLocation(pair.getDelivery()), pair(pair) +{} +RemovePair::RemovePair(Index position, Pair const &pair) + : routeIndex(std::get<0>(position)), pickupDeletion(std::get<1>(position)), deliveryDeletion(std::get<2>(position)), + pickupLocation(pair.getPickup()), deliveryLocation(pair.getDelivery()), pair(pair) +{} void RemovePair::modifySolution(Solution &solution) { Route &route = solution.getRoute(routeIndex); - // update removedLocationID - removedLocationID.push_back(route.getRoute()[pickupDeletion]); - removedLocationID.push_back(route.getRoute()[deliveryDeletion]); + // update removedPairID + + removedPairID.push_back(route.getRoute()[pickupDeletion]); // remove the delivery before (to not have to update the index) route.deleteAt(deliveryDeletion); route.deleteAt(pickupDeletion); - - } double RemovePair::evaluate(Solution const &solution) const { Route const &route = solution.getRoute(routeIndex); - const std::vector<int> & routeIDs = route.getRoute(); - const PDPTWData &data = solution.getData(); + std::vector<int> const &routeIDs = route.getRoute(); + PDPTWData const &data = solution.getData(); int prevPickup = (pickupDeletion == 0) ? 0 : routeIDs[pickupDeletion - 1]; // pickup location should not be at the end of a route anyway @@ -47,9 +37,9 @@ double RemovePair::evaluate(Solution const &solution) const double pickupCost = data::removedCostForSuppression(data, prevPickup, pickupLocation.getId(), nextPickup); - int prevDelivery = (deliveryDeletion == 0) ? 0 : routeIDs[deliveryDeletion - 1]; + int prevDelivery = (deliveryDeletion == 0) ? 0 : routeIDs[deliveryDeletion - 1]; int nextDelivery = (deliveryDeletion >= routeIDs.size()) ? 0 : routeIDs[deliveryDeletion + 1]; - if (deliveryDeletion == pickupDeletion+1) + if (deliveryDeletion == pickupDeletion + 1) { prevDelivery = prevPickup; } @@ -62,7 +52,6 @@ double RemovePair::evaluate(Solution const &solution) const int RemovePair::getPickupDeletion() const { return pickupDeletion; - } int RemovePair::getDeliveryDeletion() const @@ -75,19 +64,19 @@ int RemovePair::getRouteIndex() const return routeIndex; } -Location const & RemovePair::getPickupLocation() const +Location const &RemovePair::getPickupLocation() const { return pickupLocation; } -Location const & RemovePair::getDeliveryLocation() const +Location const &RemovePair::getDeliveryLocation() const { return deliveryLocation; } -std::vector<int> const & RemovePair::getDeletedRequests() const +std::vector<int> const &RemovePair::getDeletedPairs() const { - return removedLocationID; + return removedPairID; } Pair const &RemovePair::getPair() const @@ -95,7 +84,12 @@ Pair const &RemovePair::getPair() const return pair; } -Index RemovePair::getIndex() const +Index RemovePair::getIndex() const { return std::make_tuple(routeIndex, pickupDeletion, deliveryDeletion); +} + +ModificationApplyVariant RemovePair::asApplyVariant() const +{ + return (*this); } \ No newline at end of file diff --git a/src/lns/modification/pair/remove_pair.h b/src/lns/modification/pair/remove_pair.h index ef9dbdb..db85dc2 100644 --- a/src/lns/modification/pair/remove_pair.h +++ b/src/lns/modification/pair/remove_pair.h @@ -41,9 +41,9 @@ class RemovePair : public AtomicDestruction Pair const & pair; /** - * Removed Location ID (empty before ModifySolution is called) + * Removed PairID (= pickupID) (empty before ModifySolution is called) */ - std::vector<int> removedLocationID; + std::vector<int> removedPairID; public: @@ -51,12 +51,14 @@ public: RemovePair(Index position, Pair const &pair); ~RemovePair() override = default; + ModificationApplyVariant asApplyVariant() const override; + void modifySolution(Solution &solution) override; double evaluate(Solution const &solution) const override; /** * Return the location ID of location that has been deleted */ - std::vector<int> const &getDeletedRequests() const override; + std::vector<int> const &getDeletedPairs() const override; int getPickupDeletion() const; int getDeliveryDeletion() const; diff --git a/src/lns/modification/route/remove_route.cpp b/src/lns/modification/route/remove_route.cpp index 006d5df..aa08131 100644 --- a/src/lns/modification/route/remove_route.cpp +++ b/src/lns/modification/route/remove_route.cpp @@ -1,23 +1,35 @@ #include "remove_route.h" +#include "input/location.h" + +#include <algorithm> +#include <ranges> #include <utility> +#include <vector> + +RemoveRoute::RemoveRoute() : routeIndex(-1), removedPairID({}) {} +RemoveRoute::RemoveRoute(int routeIndex) : routeIndex(routeIndex), removedPairID({}) {} -RemoveRoute::RemoveRoute() : routeIndex(-1), removedLocationID({}) {} -RemoveRoute::RemoveRoute(int routeIndex) : routeIndex(routeIndex), removedLocationID({}) {} -RemoveRoute::RemoveRoute(int routeIndex, std::vector<int> removedLocationID) : - routeIndex(routeIndex), - removedLocationID(std::move(removedLocationID)) {} +RemoveRoute::RemoveRoute(int routeIndex, std::vector<int> &&removedPairID) + : routeIndex(routeIndex), removedPairID(std::move(removedPairID)) +{} void RemoveRoute::modifySolution(Solution &solution) { std::vector<Route> &routes = solution.getRoutes(); - - // update removedLocationID - removedLocationID.insert(removedLocationID.end(), - std::make_move_iterator(routes[routeIndex].getRoute().begin()), - std::make_move_iterator(routes[routeIndex].getRoute().end())); - + std::vector<int> const &locationIDs = routes.at(routeIndex).getRoute(); + + // update removedPairID + removedPairID.reserve(routes.at(routeIndex).getSize()); + + for (int id : locationIDs) + { + if (solution.getData().getLocation(id).getLocType() == LocType::PICKUP) + { + removedPairID.push_back(id); + } + } routes.erase(routes.begin() + routeIndex); } @@ -31,7 +43,7 @@ int RemoveRoute::getRouteIndex() const return routeIndex; } -std::vector<int> const &RemoveRoute::getDeletedRequests() const +std::vector<int> const &RemoveRoute::getDeletedPairs() const { - return removedLocationID; + return removedPairID; } \ No newline at end of file diff --git a/src/lns/modification/route/remove_route.h b/src/lns/modification/route/remove_route.h index be981e8..08fb830 100644 --- a/src/lns/modification/route/remove_route.h +++ b/src/lns/modification/route/remove_route.h @@ -21,20 +21,20 @@ class RemoveRoute : public AtomicDestruction /** * Removed Location ID (empty before ModifySolution is called) */ - std::vector<int> removedLocationID; + std::vector<int> removedPairID; public: RemoveRoute(); explicit RemoveRoute(int routeIndex); - RemoveRoute(int routeIndex, std::vector<int> removedLocationID); + RemoveRoute(int routeIndex, std::vector<int> &&removedPairID); void modifySolution(Solution &solution) override; double evaluate(Solution const &solution) const override; /** * Return the location ID of location that has been deleted */ - std::vector<int> const &getDeletedRequests() const override; + std::vector<int> const &getDeletedPairs() const override; int getRouteIndex() const; }; \ No newline at end of file diff --git a/src/lns/operators/abstract_operator.h b/src/lns/operators/abstract_operator.h new file mode 100644 index 0000000..2223e22 --- /dev/null +++ b/src/lns/operators/abstract_operator.h @@ -0,0 +1,17 @@ +#pragma once + +class Solution; + +class DestructionOperator +{ +public: + virtual void destroySolution(Solution &solution) const = 0; + virtual ~DestructionOperator() = default; +}; + +class ReconstructionOperator +{ +public: + virtual void reconstructSolution(Solution &solution, double blinkRate) const = 0; + virtual ~ReconstructionOperator() = default; +}; diff --git a/src/lns/operators/destruction/random_destroy.cpp b/src/lns/operators/destruction/random_destroy.cpp new file mode 100644 index 0000000..975e1a4 --- /dev/null +++ b/src/lns/operators/destruction/random_destroy.cpp @@ -0,0 +1,64 @@ +#include "random_destroy.h" +#include "lns/solution/solution.h" +#include "utils.h" +#include "types.h" + +#include "lns/modification/pair/remove_pair.h" + +#include <algorithm> + +void RandomDestroy::destroySolution(Solution &solution) const +{ + + int nbRequests = solution.requestsFulFilledCount(); + int actualNumberOfPairsToDestroy = std::min(nbRequests, numberOfPairsToDestroy); + int remainingPairToDelete = actualNumberOfPairsToDestroy; + + while (remainingPairToDelete < 0) + { + // too complicated + // Other (simpler) option -> choose random route then choose random pair ? + + // choose a random pair + int pairNumber = util::getRandomInt(0, remainingPairToDelete * 2 - 1); + + int pairID = 0; + int routeID = 0; + int count = 0; + Index index = std::make_tuple(0,0,0); + + // retrieve index (route and position) + for (const Route &route : solution.getRoutes()) + { + count += route.getSize(); + if (pairNumber < count) + { + count = count - pairNumber; + + std::get<0>(index) = routeID; + std::get<1>(index) = count; + std::get<2>(index) = route.getPairLocationPosition(count, solution.getData()); + + // retrieve pickupID (= PairID) + if (solution.getData().getLocation(route.getRoute().at(count)).getLocType() == LocType::PICKUP ) + { + pairID = route.getRoute().at(count); + } + else + { + pairID = route.getRoute().at(std::get<2>(index)); + } + break; + } + ++routeID; + } + + RemovePair remPair = RemovePair(index, solution.getData().getPair(pairID)); + + // appliquer la modif (via solution method -> update correctement la solution) + solution.applyDestructSolution(remPair); + + // update les compteurs + --remainingPairToDelete; + } +} \ No newline at end of file diff --git a/src/lns/operators/destruction/random_destroy.h b/src/lns/operators/destruction/random_destroy.h new file mode 100644 index 0000000..c7e6185 --- /dev/null +++ b/src/lns/operators/destruction/random_destroy.h @@ -0,0 +1,19 @@ +#pragma once + +#include "lns/operators/abstract_operator.h" + +class RandomDestroy : public DestructionOperator +{ +public: + explicit RandomDestroy(int numberOfPairsToDestroy) + : numberOfPairsToDestroy(numberOfPairsToDestroy) + {} + + /** + * This operator removes numberOfPairsToDestroy pairs randomly in the solution. + */ + void destroySolution(Solution &solution) const override; + +private: + int numberOfPairsToDestroy; +}; diff --git a/src/lns/operators/selector/operator_selection.cpp b/src/lns/operators/selector/operator_selection.cpp new file mode 100644 index 0000000..e69de29 diff --git a/src/lns/operators/selector/operator_selector.h b/src/lns/operators/selector/operator_selector.h new file mode 100644 index 0000000..e69de29 diff --git a/src/lns/solution/route.cpp b/src/lns/solution/route.cpp index 6ebee23..eb06aad 100644 --- a/src/lns/solution/route.cpp +++ b/src/lns/solution/route.cpp @@ -59,4 +59,22 @@ int Route::getLocation(int index) const int Route::getSize() const { return route.size(); +} + + +int Route::getPairLocationPosition(int position, const PDPTWData &data) const +{ + int firstID = getRoute().at(position); + int secondID = data.getLocation(firstID).getPair(); + + for (int i=0; i < getSize(); i++) + { + if (getRoute().at(i) == secondID) + { + return i; + } + } + + spdlog::error("Paired location not found for ID {}", firstID); + throw std::runtime_error("Paired location not found in the route"); } \ No newline at end of file diff --git a/src/lns/solution/route.h b/src/lns/solution/route.h index aac7992..32010af 100644 --- a/src/lns/solution/route.h +++ b/src/lns/solution/route.h @@ -2,6 +2,7 @@ #include <vector> +#include "input/pdptw_data.h" #include "input/time_window.h" /** @@ -15,13 +16,6 @@ private: std::vector<int> route; int cost; - - - /* Stocké dans les contraintes - std::vector<TimeInteger> reach_time; // debut d'arrivee - std::vector<double> FTS; // forward time slack - std::vector<std::vector<double>> acc_cap; // inscreasing capacity allowed between two positions - */ public: @@ -33,11 +27,20 @@ public: // get Location int getLocation(int index) 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. + */ + int getPairLocationPosition(int position, const PDPTWData &data) const; + + void print() const; - /* - * Add a location index in the route (does not update the route cost) - */ + /** + * Add a location index in the route (does not update the route cost) + */ void insertAt(int locationIndex, int position); /* diff --git a/src/lns/solution/solution.cpp b/src/lns/solution/solution.cpp index 8c3bf53..f996203 100644 --- a/src/lns/solution/solution.cpp +++ b/src/lns/solution/solution.cpp @@ -1,19 +1,18 @@ #include "solution.h" -#include <utility> - +#include "config.h" #include "input/data.h" #include "input/time_window.h" #include "lns/constraints/capacity/capacity_constraint.h" #include "lns/constraints/time_window/time_window_constraint.h" - -#include "config.h" #include "lns/solution/route.h" +#include <utility> + void Solution::initPairBank() -{ +{ pairBank.clear(); - for (const Pair & pair : getData().getPairs()) + for (Pair const &pair: getData().getPairs()) { pairBank.push_back(pair.getID()); } @@ -35,14 +34,14 @@ void Solution::computeAndStoreSolutionCost() { routeCost = computeSolutionCost(); - // add penalty for solution in the pairBank ? + // add penalty for solution in the pairBank ? totalCost = routeCost + computePenalization(); } double Solution::computeSolutionCost() const { double cost = 0; - for (const Route & route : getRoutes()) + for (Route const &route: getRoutes()) { cost += data::routeCost(data, route); } @@ -55,7 +54,6 @@ double Solution::computePenalization() const return getBank().size() * EXCLUSION_PENALTY; } - void Solution::init() { initPairBank(); @@ -64,43 +62,40 @@ void Solution::init() computeAndStoreSolutionCost(); } +Solution::Solution(PDPTWData const &data, Solution::PairBank pairbank, std::vector<Route> routes, double routeCost, + double totalCost) + : data(data), pairBank(std::move(pairbank)), routes(std::move(routes)), routeCost(routeCost), totalCost(totalCost) +{} -Solution::Solution(const PDPTWData &data, Solution::PairBank pairbank, std::vector<Route> routes, double routeCost, double totalCost) : - data(data), - pairBank(std::move(pairbank)), - routes(std::move(routes)), - routeCost(routeCost), - totalCost(totalCost) {} - -Solution::Solution(const PDPTWData &data) : data(data) +Solution::Solution(PDPTWData const &data) : data(data) { init(); } -Solution Solution::emptySolution(const PDPTWData &data) +Solution Solution::emptySolution(PDPTWData const &data) { Solution s = Solution(data); return s; } -const Solution::PairBank & Solution::getBank() const +Solution::PairBank const &Solution::getBank() const { return pairBank; } -const std::vector<Route> & Solution::getRoutes() const +std::vector<Route> const &Solution::getRoutes() const { return routes; } -std::vector<Route> & Solution::getRoutes() +std::vector<Route> &Solution::getRoutes() { return routes; } -Route & Solution::getRoute(int routeIndex) +Route &Solution::getRoute(int routeIndex) { - if (routeIndex < 0 || routeIndex >= routes.size()) + if (routeIndex < 0 || routeIndex >= routes.size()) { spdlog::error("Invalid route index: {}", routeIndex); throw std::out_of_range("Invalid route index."); @@ -108,9 +103,9 @@ Route & Solution::getRoute(int routeIndex) return routes[routeIndex]; } -const Route & Solution::getRoute(int routeIndex) const +Route const &Solution::getRoute(int routeIndex) const { - if (routeIndex < 0 || routeIndex >= routes.size()) + if (routeIndex < 0 || routeIndex >= routes.size()) { spdlog::error("Invalid route index: {}", routeIndex); throw std::out_of_range("Invalid route index."); @@ -123,23 +118,68 @@ double Solution::getCost() const return totalCost; } -const PDPTWData & Solution::getData() const +PDPTWData const &Solution::getData() const { return data; } +int Solution::requestsFulFilledCount() const +{ + int count = 0; + for (Route const &route: getRoutes()) + { + count += route.getRoute().size() / 2; + } + return count; +} + +void Solution::beforeApplyModification(AtomicModification &modification) +{ + // pre check to do ? +} + +void Solution::afterApplyModification(AtomicModification &modification) +{ + // constraint status update + for (std::unique_ptr<Constraint> &constraint: constraints) + { + constraint->applyVariant(modification.asApplyVariant()); + } +} + +void Solution::applyRecreateSolution(AtomicRecreation &modification) +{ + // apply the modification to the solution + +} + +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()); + pairBank.insert(pairBank.end(), deletedPair.begin(), deletedPair.end()); + + afterApplyModification(modification); +} + void Solution::print() const { - std::cout << "Cost :" << totalCost << "\n" << "Routes : \n"; + std::cout << "Cost : " << totalCost << "\n" + << "Routes : \n"; - for (const Route& id : getRoutes()) + for (Route const &id: getRoutes()) { id.print(); } - std::cout << "Banques : \n"; + std::cout << "Pair Bank : \n"; - for (const int id : getBank()) + for (int const id: getBank()) { std::cout << id << ", "; } diff --git a/src/lns/solution/solution.h b/src/lns/solution/solution.h index 48bffda..31affb5 100644 --- a/src/lns/solution/solution.h +++ b/src/lns/solution/solution.h @@ -1,13 +1,12 @@ #pragma once -#include <vector> -#include <iostream> -#include <spdlog/spdlog.h> - - +#include "input/pair.h" #include "lns/constraints/constraint.h" #include "route.h" -#include "input/pair.h" + +#include <iostream> +#include <spdlog/spdlog.h> +#include <vector> class AtomicModification; class AtomicRecreation; @@ -24,68 +23,101 @@ public: using PairBank = std::vector<int>; private: - PDPTWData const & data; + // ref_wrapper + PDPTWData const &data; /* - * Store IDs of a pairs (Pickup & Delivery) that are not assigned yet to a route. - */ + * Store IDs of a pairs (Pickup & Delivery) that are not assigned yet to a route. + */ PairBank pairBank; /* - * Vector of routes representing the solution - */ + * Vector of routes representing the solution + */ std::vector<Route> routes; double routeCost; double totalCost; std::vector<std::unique_ptr<Constraint>> constraints; public: - /** - * Expected way to construct a solution. - */ - static Solution emptySolution(const PDPTWData &data); + * Expected way to construct a solution. + */ + static Solution emptySolution(PDPTWData const &data); - explicit Solution(const PDPTWData &data); + explicit Solution(PDPTWData const &data); /** - * For testing/debugging. - * Use emptySolution(const PDPTWData &data) to create an initial empty solution. - */ - Solution(const PDPTWData &data, Solution::PairBank pairbank, std::vector<Route> routes, double routeCost, double totalCost); + * For testing/debugging. + * Use emptySolution(const PDPTWData &data) to create an initial empty solution. + */ + Solution(PDPTWData const &data, Solution::PairBank pairbank, std::vector<Route> routes, double routeCost, + double totalCost); - PairBank const & getBank() const; - std::vector<Route> const & getRoutes() const; - Route const & getRoute(int routeIndex) const; - const PDPTWData & getData() const; + PairBank const &getBank() const; + std::vector<Route> const &getRoutes() const; + Route const &getRoute(int routeIndex) const; + PDPTWData const &getData() const; double getCost() const; + /** + * Return the number of fullfilled requests. + * The solution must be consistent ! + * (compute this number by looking at the size of each routes) + */ + int requestsFulFilledCount() const; + + + /** + * Pre modification check. + * @param modification Must be a valid modification. + */ + void beforeApplyModification(AtomicModification &modification); + + /** + * Update constraints, it is expected that the solution has already been modified. + * @param modification Must be a valid modification. + */ + void afterApplyModification(AtomicModification &modification); + + /** + * Apply the given the modification on the solution. Does not check validity. + * @param modification Must be a valid recreation. + */ + void applyRecreateSolution(AtomicRecreation &modification); + + /** + * Apply the given the modification on the solution. Does not check validity. + * @param modification Must be a destruction. + */ + void applyDestructSolution(AtomicDestruction &modification); + + // For route modification - std::vector<Route> & getRoutes(); - Route & getRoute(int routeIndex); - - void print() const; + std::vector<Route> &getRoutes(); + Route &getRoute(int routeIndex); + void print() const; -private : +private: /** - * Does the initialisation of the object, called by the constructor - */ + * Does the initialisation of the object, called by the constructor + */ void init(); /** - * Init/reset the constraints of the problem - */ + * Init/reset the constraints of the problem + */ void initConstraints(); void initRoutes(); void initPairBank(); - + void computeAndStoreSolutionCost(); - + /** - * Compute the cost of the solution (routes cost) - */ + * Compute the cost of the solution (routes cost) + */ double computeSolutionCost() const; - + double computePenalization() const; }; \ No newline at end of file diff --git a/src/mains/main.cpp b/src/mains/main.cpp index 67c7b74..d6fb702 100644 --- a/src/mains/main.cpp +++ b/src/mains/main.cpp @@ -42,82 +42,5 @@ int main(int argc, char const *argv[]) Solution emptySol = Solution::emptySolution(data); emptySol.print(); - - - // // SOLUTION 1-6-2-7 (time ; 104) - - // Solution::PairBank bank {3,4,5,8,9,10}; - - // std::vector<int> path {1,6,2,7}; - // Route route = Route(path, 0); - // std::vector<Route> routes; - // routes.push_back(route); - - // int totalCost = 0; - - // Solution sol = Solution(data, bank, routes, totalCost); - - // sol.print(); - - - // // Couple pickup/delivery - // Location r2P = data.getLocations()[1]; - // Location r2D = data.getLocations()[6]; - // Location r3P = data.getLocations()[2]; - // Location r3D = data.getLocations()[7]; - - // Pair pair2 = Pair(r2P, r2D); - // Pair pair3 = Pair(r3P, r3D); - - - - // /* - - // CapacityConstraint cap_const = CapacityConstraint(sol); - - - // cap_const.initCapacities(); - // cap_const.print(); - - // std::cout << "Is Valid ? : " << cap_const.checkVariant(opInsert) << "\n"; - - // cap_const.applyVariant(opInsert); - - // cap_const.print(); - - // RemovePair opRemove = RemovePair(0, 2, 3, pair3); - - // cap_const.applyVariant(opRemove); - - // cap_const.print(); - // */ - - - // TimeWindowConstraint twconst = TimeWindowConstraint(sol); - - // std::cout << "\n --- : \n"; - - // twconst.print(); - - // twconst.initReachTimes(); - - // twconst.print(); - - // // Modification - // InsertPair opInsert = InsertPair(0, 0, 0, pair3); - - // std::cout << "is valid ? : " << twconst.checkVariant(opInsert) << "\n"; - - // twconst.applyVariant(opInsert); - - // twconst.print(); - // opInsert.modifySolution(sol); - - // RemovePair opRemove = RemovePair(0, 0, 1, pair3); - - // twconst.applyVariant(opRemove); - - // twconst.print(); - return 0; } diff --git a/src/output/solution_checker.cpp b/src/output/solution_checker.cpp index e69de29..9918138 100644 --- a/src/output/solution_checker.cpp +++ b/src/output/solution_checker.cpp @@ -0,0 +1,178 @@ +#include "solution_checker.h" +#include "input/location.h" +#include "input/pdptw_data.h" +#include "lns/solution/solution.h" +#include "types.h" +#include "input/data.h" + +#include <spdlog/spdlog.h> + +void checker::checkSolutionCoherence(Solution const &sol, PDPTWData const &data) +{ + bool errorFlag = false; + + // Vector that will store the route ID serving the location, (-1 if no routes) + std::vector<int> check(data.getLocations().size(), -1); + + // checking routes coherence + int routeID = 0; + for (const Route &route : sol.getRoutes()) + { + for (int id : route.getRoute()) + { + if (check.at(id) != -1) + { + // Error the location is already attributed (doublon) + spdlog::error("Location {} has already been visited.", id); + errorFlag = true; + } + + check.at(id) = routeID; + + // if the location is a delivery, check if the pickup location has already been visited in the same route + if ((data.getLocation(id).getLocType() == LocType::DELIVERY) + && (check[data.getLocation(id).getPair()] != routeID) ) + { + // Error Pickup and Delivery are not in the same route (wrong route) + // OR Delivery before Pickup (wrong order) + spdlog::error("Pair associated to {} is not consistent (route or order problem).", id); + errorFlag = true; + } + } + ++routeID; + } + + // checking PairBank coherence (given the routes) + for (int pairID : sol.getBank()) + { + if ((check.at(pairID) != -1) || (data.getLocation(pairID).getPair() != -1)) + { + // Error Pair in the bank but one location of the pair seems to be visited by a route (doublon) + spdlog::error("Pair associated to {} is in a route and in the bank at the same time.", pairID); + errorFlag = true; + } + } + + if (errorFlag) + { + throw SolutionConstraintError("Error in the consistency of the solution.", sol); + } + +} + + +void checker::checkCapacity(Solution const &sol, PDPTWData const &data) +{ + bool errorFlag = false; + int capa = 0; + int routeID = 0; + + for (const Route &route : sol.getRoutes() ) + { + 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, 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; + } + + if (errorFlag) + { + throw SolutionConstraintError("Error in the capacity constraint of the solution.", sol); + } +} + +void checker::checkTimeWindows(Solution const &sol, PDPTWData const &data) +{ + bool errorFlag = false; + TimeInteger reachTime = 0; + int routeID = 0; + int locID = 0; + + for (const Route &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++) + { + 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; + } + + ++ routeID; + } + + if (errorFlag) + { + throw SolutionConstraintError("Error in the time windows constraint of the solution.", sol); + } +} + +void checker::checkAll(Solution const &sol , PDPTWData const &data, bool checkRequests) +{ + + // TO DO, check in the solution if all locations are visited ! (in checkSolutionCoherence ?) + if (checkRequests) + { + if (!sol.getBank().empty()) + { + // Error, all locations are supposed to be visited, the pairBank is not empty + spdlog::error("All locations are supposed to be visited, the pairBank is not empty."); + } + } + + checker::checkSolutionCoherence(sol, data); + checker::checkCapacity(sol, data); + checker::checkTimeWindows(sol,data); + +} + + +// No logging yet + +checker::SolutionInternalError::SolutionInternalError(std::string reason, Solution const &sol) + : std::logic_error(reason /*+ " More details in logs"*/) +{ + // logging::errorDumpLogger().error("{}\n{}", reason, sol); + // sol.exportDotFile(); +} + +checker::SolutionConstraintError::SolutionConstraintError(std::string reason, Solution const &sol) + : std::logic_error(reason /*+ " More details in logs"*/) +{ + // logging::errorDumpLogger().error("{}\n{}", reason, sol); +} diff --git a/src/output/solution_checker.h b/src/output/solution_checker.h index e69de29..4f84139 100644 --- a/src/output/solution_checker.h +++ b/src/output/solution_checker.h @@ -0,0 +1,58 @@ +#pragma once + +#include <stdexcept> + +#include "input/pdptw_data.h" +#include "input/time_window.h" +#include "lns/solution/solution.h" + + +namespace checker +{ + + /** + * Does a check full check on the solution. + * @param checkRequests, if false will not throw on missing requests + */ + void checkAll(Solution const &, PDPTWData const &data, bool checkRequests = true); + + /** + * Check that the solution represent a PDPTW solution. + * No doublon, Ordered pickup and delivery, pickup and delivery in the same route. + * (don't check the constraints) + */ + void checkSolutionCoherence(Solution const &sol, PDPTWData const &data); + + /** + * Check the capacity constraint. + */ + void checkCapacity(Solution const &sol, PDPTWData const &data); + + /** + * Check the time windows constraint. + * (Maybe a better way to do it than reconstructing the ordo ?) + */ + void checkTimeWindows(Solution const &sol, PDPTWData const &data); + + + /** + * This error means that the internal Solutions values are broken. + * Errors like this are not linked to the model constraint. + */ + class SolutionInternalError : public std::logic_error + { + public: + SolutionInternalError(std::string reason, Solution const &); + }; + + /** + * This exception means that the solution is not valid regarding the PDPTW problem + */ + class SolutionConstraintError : public std::logic_error + { + public: + SolutionConstraintError(std::string reason, Solution const &); + }; + + +}// namespace checker diff --git a/src/utils.cpp b/src/utils.cpp new file mode 100644 index 0000000..6ab381b --- /dev/null +++ b/src/utils.cpp @@ -0,0 +1,32 @@ +#include "utils.h" +#include "config.h" + +#include <random> + +namespace// anonymous namespace +{ + std::mt19937_64 randomGenerator;// NOLINT(*-msc51-cpp) => deterministic random! + bool seedSet = false; + std::uniform_real_distribution<> distribution(0, 1); +}// namespace + + +double util::getRandom() +{ + if (!seedSet) [[unlikely]] + { + randomGenerator.seed(RANDOM_SEED); + seedSet = true; + } + return distribution(randomGenerator); +} + +unsigned int util::getRandomInt(unsigned int min, unsigned int max) +{ + if (!seedSet) [[unlikely]] + { + randomGenerator.seed(RANDOM_SEED); + seedSet = true; + } + return std::uniform_int_distribution<>(min, max)(randomGenerator); +} diff --git a/src/utils.h b/src/utils.h new file mode 100644 index 0000000..323fe4f --- /dev/null +++ b/src/utils.h @@ -0,0 +1,24 @@ +#pragma once + +#include <algorithm> +#include <functional> +#include <memory> +#include <random> +#include <stdexcept> +#include <string> +#include <vector> + +namespace util +{ + /** + * Get a random number + * @return a number between 0 (inclusive) and 1 (exclusive) + */ + double getRandom(); + /** + * @return a random integer number between min (included) and max (included) + */ + unsigned int getRandomInt(unsigned int min, unsigned int max); + + +}// namespace util -- GitLab