diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000000000000000000000000000000000000..856f134cdf9cf134b270e3b58b32e8e50279cd39 --- /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 9f668937ccd03e32265224592f491312cffa67d1..2edbfcd3398e760a527838bae587fbdf8a664670 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 bf368d85b4872e612aaa91af11e608fc79ab9e99..6bb3f2bf9fef9c3a67843f5e1c2d1f8093dd2a1a 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 142fbde6f7fa5740f4565f00b6e4ab7281fd09d8..706bd09777f3dabf1b2cc7b7513d738c2fc2d4a4 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 ac0ea57d07eee3b0d22f12b4e31babf812521b04..3cfb82950e8cd08bc17615a670a2f3e7d8a385bc 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 b350692565d9355a55af991a789300494a4c3373..c0310483e1b7dd5b0b8ef0435b5b17a8f8875258 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 f2ad02fdc434bacff27a0abde60e8878b9220421..1506348a69cc5bd183272e1e919f8d1f08db9375 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 c1895949ea22ad2e269703a51af10d5d60dd5fe0..701be758a1553cd2d761dc6860612398539f09a2 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 b3f9d4b9ecd151d76e45d1ae8979a4a510caae35..bf717d0d92c80742e187511107b8bd4febb3e61f 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 d806765091adb2146185abd7b91ab573666b3ad0..84fe9186a262aa9888d299b60d633cc44f0fdec5 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 1a1c808c1cc337e53b53e283c1a533eef569cc43..6cf567c3d5c24619ec48847701418b2357585c46 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 ef9dbdb8dd255b8ad43b2158e2fe9172b348786d..db85dc26fffabbb2b0af2f74ac8b14f355280d26 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 006d5df6e7a16714105bfd6ef70bbb516d1cdcfc..aa08131bb2f14fdcb066a4b1366de8745e1da89b 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 be981e8171ebede51d12a9cf7674644045908a1d..08fb830ac0fd5ebf51b9f6efcbcfb22dbdb019c6 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 0000000000000000000000000000000000000000..2223e22585de3fbe6cd0f7271daa2f91816b8104 --- /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 0000000000000000000000000000000000000000..975e1a469e4d5465957c0b0c728cde248e3f8396 --- /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 0000000000000000000000000000000000000000..c7e6185adf29fe165641dd7f804335173d9c6cfc --- /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 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/src/lns/operators/selector/operator_selector.h b/src/lns/operators/selector/operator_selector.h new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/src/lns/solution/route.cpp b/src/lns/solution/route.cpp index 6ebee237600353e77282a210cdc68d3f4b50337d..eb06aadd6ea7662ada2157f8a651fbfe1e708920 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 aac79920ea2b0e1bbf74534fbb87efe96f89dc0c..32010af46aefd7c2cab0c8737aeb19fb33c392a2 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 8c3bf532650fef0c72face3af56bab4bdc11feed..f996203ae4d26b110a5fa32f97f89e6678ef313f 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 48bffdadfc167b0bf4fd427f6e511b1eada8d72a..31affb5ed7557ac59ca156a72232c74bcfbef4f7 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 67c7b747917efcb3396eb00db2aa060486787b40..d6fb7022565ea0374483464e0c7f0b974221834f 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 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..9918138fad80eecbe706c3214bde01e4293ff9f2 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 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..4f84139a3bc6f9bab745e7abace2997d54097cdf 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 0000000000000000000000000000000000000000..6ab381b9b788f2b9551f92f5e70d27581a79ee7a --- /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 0000000000000000000000000000000000000000..323fe4f2b22c3fa8941a7e2b9f851a9b13871a23 --- /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