Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • a24jacqb/pdptw-main
1 result
Show changes
Showing
with 898 additions and 152 deletions
#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;
};
#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
#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;
};
#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;
// Insert into existing routes
for (Route const &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);
//std::cout << std::get<0>(index) << " " << std::get<1>(index) << " " << std::get<2>(index) << "\n";
consumeModification(InsertPair(index, pair));
}
}
++routeIndex;
}
}
}// namespace enumeration
\ No newline at end of file
#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
#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))
{
std::cout << " => Insert Modification" << "\n";
list.push_front(std::make_unique<ModificationType>(modification));
}
};
std::cout << " => Insert Modification" << "\n";
}
void AllTypedModifications<InsertPair>::populate(Solution const &solution, Pair const &pair,
std::forward_list<std::unique_ptr<AtomicRecreation>> &list)
{
std::cout << "\n MODIFICATION GENERATOR : \n";
enumeration::enumerateAllInsertPair(
solution, pair, addToListIfValidTemplate<InsertPair>(solution, list));
}
}// namespace generator
#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
#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);
};
#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).sortPairs();
AtomicRecreationPtr recreation;
for (int pairID: sortedPairs)
{
Pair const &pair = solution.getData().getPair(pairID);
recreation = ListHeuristicInsertion::choosingStrategy(solution, pair, blinkRate);
if (recreation)
{
std::cout << "\n --- Apply recreation --- \n";
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
#include "sorting_strategy.h"
#include "utils.h"
#include <algorithm>
#include <ranges>
std::vector<int> const &sorting_strategy::Shuffle::sortPairs() const
{
auto &bank = getSolution().getPairBank();
std::ranges::shuffle(bank, util::getRawRandom());
return bank;
}
\ No newline at end of file
#pragma once
#include "lns/solution/solution.h"
namespace sorting_strategy
{
/**
* Interface for sorting the requests in the request bank. Does modify directly the solution request bank
*/
class SortingStrategy
{
public:
explicit SortingStrategy(Solution &solution) : solution(solution) {}
Solution &getSolution() const { return solution; }
virtual std::vector<int> const &sortPairs() const = 0;
virtual ~SortingStrategy() = default;
private:
// non const to sort in place
Solution &solution;
};
/**
* Shuffle the requests
*/
class Shuffle : public SortingStrategy
{
using SortingStrategy::SortingStrategy;
public:
std::vector<int> const &sortPairs() const override;
};
}// namespace sorting_strategy
\ No newline at end of file
......@@ -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
......@@ -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);
/*
......
#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 "output/solution_checker.h"
#include <bits/ranges_util.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());
}
......@@ -22,6 +23,7 @@ void Solution::initPairBank()
void Solution::initRoutes()
{
routes.clear();
routes.emplace_back();
}
void Solution::initConstraints()
......@@ -35,18 +37,17 @@ 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);
}
std::cout << "le cout " << cost << " \n";
return cost;
}
......@@ -55,7 +56,6 @@ double Solution::computePenalization() const
return getBank().size() * EXCLUSION_PENALTY;
}
void Solution::init()
{
initPairBank();
......@@ -64,43 +64,50 @@ 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;
}
Solution::PairBank const &Solution::getPairBank() const
{
return pairBank;
}
const std::vector<Route> & Solution::getRoutes() const
Solution::PairBank &Solution::getPairBank()
{
return pairBank;
}
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 +115,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 +130,100 @@ double Solution::getCost() const
return totalCost;
}
const PDPTWData & Solution::getData() const
PDPTWData const &Solution::getData() const
{
return data.get();
}
int Solution::requestsFulFilledCount() const
{
int count = 0;
for (Route const &route: getRoutes())
{
count += route.getRoute().size() / 2;
}
return count;
}
bool Solution::checkModification(AtomicRecreation const &modification) const
{
std::cout << "--- Check Modification Validity : ";
ModificationCheckVariant const &checkVariant = modification.asCheckVariant();
// visitor pattern
for (std::unique_ptr<Constraint> const &constraint: constraints)
{
if (!constraint->checkVariant(checkVariant))
{
std::cout << "\n";
return false;
}
}
std::cout << "\n";
return true;
}
void Solution::beforeApplyModification(AtomicModification &modification)
{
// pre modification check
check();
}
void Solution::afterApplyModification(AtomicModification &modification)
{
// constraint status update
for (std::unique_ptr<Constraint> &constraint: constraints)
{
constraint->applyVariant(modification.asApplyVariant());
}
}
void Solution::applyRecreateSolution(AtomicRecreation &modification)
{
beforeApplyModification(modification);
modification.modifySolution(*this);
// we update the request bank
if (int pairID = modification.getAddedPairs())
{
pairBank.erase(std::ranges::find(pairBank, pairID));
}
afterApplyModification(modification);
}
void Solution::applyDestructSolution(AtomicDestruction &modification)
{
beforeApplyModification(modification);
modification.modifySolution(*this);
// updating request bank
std::vector<int> const &deletedPair = modification.getDeletedPairs();
//pairBank.reserve(pairBank.size() + deletedPair.size());
pairBank.insert(pairBank.end(), deletedPair.begin(), deletedPair.end());
afterApplyModification(modification);
}
void Solution::check() const
{
return data;
checker::checkSolutionCoherence(*this, getData());
}
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 << ", ";
}
......
#pragma once
#include <vector>
#include <iostream>
#include <spdlog/spdlog.h>
#include "input/pair.h"
#include "input/pdptw_data.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 +24,113 @@ public:
using PairBank = std::vector<int>;
private:
PDPTWData const & data;
std::reference_wrapper<PDPTWData const> data;
//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.
* Generate an empty solution with all pairs in the pairBank and one empty route.
*/
static Solution emptySolution(PDPTWData const &data);
explicit Solution(PDPTWData const &data);
/**
* 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;
PairBank const &getPairBank() const;
PairBank &getPairBank();
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;
/**
* Expected way to construct a solution.
*/
static Solution emptySolution(const PDPTWData &data);
* 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;
// using solution checker to verify that the solution is correct. Most used for debug as it is correctness oriented and not performance oriented
void check() const;
explicit Solution(const PDPTWData &data);
/**
* Pre modification check.
* @param modification Must be a valid modification.
*/
void beforeApplyModification(AtomicModification &modification);
/**
* 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);
* 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);
PairBank const & getBank() const;
std::vector<Route> const & getRoutes() const;
Route const & getRoute(int routeIndex) const;
const PDPTWData & getData() const;
double getCost() const;
// 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
......@@ -8,6 +8,7 @@
#include <spdlog/spdlog.h>
#include "input/location.h"
#include "input/pdptw_data.h"
#include "input/time_window.h"
#include "input/json_parser.h"
#include "input/data.h"
......@@ -19,8 +20,31 @@
#include "lns/modification/pair/remove_pair.h"
#include "lns/modification/route/remove_route.h"
#include "lns/operators/reconstruction/list_heuristic_insertion.h"
#include "lns/operators/reconstruction/list_heuristic_insertion.hpp"
#include "lns/operators/generators/modification_generator.h"
#include "lns/operators/sorting_strategy.h"
using json = nlohmann::json;
void printForwardList(const std::forward_list<std::unique_ptr<AtomicRecreation>>& list) {
int cpt = 1;
for (const auto& item : list) {
std::cout << cpt << "{ \n";
// Affichage de l'emplacement ajouté à la solution
int pair = item->getAddedPairs();
if (pair > 0) {
std::cout << "Added Location ID: \n";
std::cout << pair << "\n";
} else {
std::cout << "No location added.\n";
}
std::cout << "} \n";
++cpt;
}
}
int main(int argc, char const *argv[])
{
//std::string filepath = "/home/a24jacqb/Documents/Code/pdptw-main/data_in/test_inst.json";
......@@ -39,85 +63,24 @@ int main(int argc, char const *argv[])
/*
* test
*/
Solution emptySol = Solution::emptySolution(data);
emptySol.print();
Solution solution = Solution::emptySolution(data);
std::cout << "--- Empty Solution --- \n";
solution.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;
generator::AllTypedModifications<InsertPair> generator;
generator::ModificationContainer modificationList;
// 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);
//ListHeuristicInsertion<std::derived_from<sorting_strategy::SortingStrategy> Strategy, std::derived_from<generator::ModificationGenerator> Generator>
// cap_const.initCapacities();
// cap_const.print();
// std::cout << "Is Valid ? : " << cap_const.checkVariant(opInsert) << "\n";
double blinkRate = 0;
// cap_const.applyVariant(opInsert);
std::cout << "\n --- Operator <SHUFFLE - ALL_INSERTPAIR> -> reconstruction (NO COST UPDATE)\n";
// 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();
ListHeuristicInsertion<sorting_strategy::Shuffle, generator::AllTypedModifications<InsertPair>> operatorInstance;
operatorInstance.reconstructSolution(solution, blinkRate);
solution.print();
return 0;
}
#include "solution_checker.h"
#include "input/data.h"
#include "input/location.h"
#include "input/pdptw_data.h"
#include "lns/solution/solution.h"
#include "types.h"
#include <spdlog/spdlog.h>
#include <vector>
void checker::checkSolutionCoherence(Solution const &sol, PDPTWData const &data)
{
std::cout << " --- Solution Coherence checker : ";
bool errorFlag = false;
// Vector that will store the route ID serving the location, (-1 if no routes)
// Caution ! check is indexed from 0 to n-1 and Location ID are indexed from 1 to n
std::vector<int> check(data.getLocations().size(), -1);
// checking routes coherence
int routeID = 0;
for (Route const &route: sol.getRoutes())
{
std::cout << "#";
// skip if it is an empty route
if (!route.getRoute().empty())
{
for (int LocID: route.getRoute())
{
if (check.at(LocID -1) != -1)
{
std::cout << "#";
// Error the location is already attributed (doublon)
spdlog::error("Location {} has already been visited.", LocID);
errorFlag = true;
}
check.at(LocID -1) = routeID;
// if the location is a delivery, check if the pickup location has already been visited in the same route
if ((data.getLocation(LocID).getLocType() == LocType::DELIVERY) &&
(check.at(data.getLocation(LocID).getPair() - 1) != 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).", LocID);
errorFlag = true;
}
}
}
++routeID;
}
std::cout << "#";
// checking PairBank coherence (given the routes)
std::vector<int> testbank = sol.getBank();
for (int pairID: sol.getBank())
{
// check goes from 0 et n-1
if ((check.at(pairID - 1) != -1) || (check.at(data.getLocation(pairID).getPair() - 1) != -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)
{
sol.print();
throw SolutionConstraintError("Error in the consistency of the solution.", sol);
}
std::cout << " : checker end ---\n";
}
void checker::checkCapacity(Solution const &sol, PDPTWData const &data)
{
bool errorFlag = false;
int capa = 0;
int routeID = 0;
for (Route const &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 (Route const &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);
}
#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