Skip to content
Snippets Groups Projects
Commit 54e16ee5 authored by awenjb's avatar awenjb
Browse files

add Solution Components

- add basic solution Checker
- add random destroy Operator
- revised some parts of the code
parent 2fc0df0b
No related branches found
No related tags found
No related merge requests found
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
#include <vector> #include <vector>
#include "input/pdptw_data.h"
#include "input/time_window.h" #include "input/time_window.h"
/** /**
...@@ -15,13 +16,6 @@ private: ...@@ -15,13 +16,6 @@ private:
std::vector<int> route; std::vector<int> route;
int cost; 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: public:
...@@ -33,11 +27,20 @@ public: ...@@ -33,11 +27,20 @@ public:
// get Location // get Location
int getLocation(int index) const; 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; 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); void insertAt(int locationIndex, int position);
/* /*
......
#include "solution.h" #include "solution.h"
#include <utility> #include "config.h"
#include "input/data.h" #include "input/data.h"
#include "input/time_window.h" #include "input/time_window.h"
#include "lns/constraints/capacity/capacity_constraint.h" #include "lns/constraints/capacity/capacity_constraint.h"
#include "lns/constraints/time_window/time_window_constraint.h" #include "lns/constraints/time_window/time_window_constraint.h"
#include "config.h"
#include "lns/solution/route.h" #include "lns/solution/route.h"
#include <utility>
void Solution::initPairBank() void Solution::initPairBank()
{ {
pairBank.clear(); pairBank.clear();
for (const Pair & pair : getData().getPairs()) for (Pair const &pair: getData().getPairs())
{ {
pairBank.push_back(pair.getID()); pairBank.push_back(pair.getID());
} }
...@@ -35,14 +34,14 @@ void Solution::computeAndStoreSolutionCost() ...@@ -35,14 +34,14 @@ void Solution::computeAndStoreSolutionCost()
{ {
routeCost = computeSolutionCost(); routeCost = computeSolutionCost();
// add penalty for solution in the pairBank ? // add penalty for solution in the pairBank ?
totalCost = routeCost + computePenalization(); totalCost = routeCost + computePenalization();
} }
double Solution::computeSolutionCost() const double Solution::computeSolutionCost() const
{ {
double cost = 0; double cost = 0;
for (const Route & route : getRoutes()) for (Route const &route: getRoutes())
{ {
cost += data::routeCost(data, route); cost += data::routeCost(data, route);
} }
...@@ -55,7 +54,6 @@ double Solution::computePenalization() const ...@@ -55,7 +54,6 @@ double Solution::computePenalization() const
return getBank().size() * EXCLUSION_PENALTY; return getBank().size() * EXCLUSION_PENALTY;
} }
void Solution::init() void Solution::init()
{ {
initPairBank(); initPairBank();
...@@ -64,43 +62,40 @@ void Solution::init() ...@@ -64,43 +62,40 @@ void Solution::init()
computeAndStoreSolutionCost(); 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) : Solution::Solution(PDPTWData const &data) : data(data)
data(data),
pairBank(std::move(pairbank)),
routes(std::move(routes)),
routeCost(routeCost),
totalCost(totalCost) {}
Solution::Solution(const PDPTWData &data) : data(data)
{ {
init(); init();
} }
Solution Solution::emptySolution(const PDPTWData &data) Solution Solution::emptySolution(PDPTWData const &data)
{ {
Solution s = Solution(data); Solution s = Solution(data);
return s; return s;
} }
const Solution::PairBank & Solution::getBank() const Solution::PairBank const &Solution::getBank() const
{ {
return pairBank; return pairBank;
} }
const std::vector<Route> & Solution::getRoutes() const std::vector<Route> const &Solution::getRoutes() const
{ {
return routes; return routes;
} }
std::vector<Route> & Solution::getRoutes() std::vector<Route> &Solution::getRoutes()
{ {
return routes; 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); spdlog::error("Invalid route index: {}", routeIndex);
throw std::out_of_range("Invalid route index."); throw std::out_of_range("Invalid route index.");
...@@ -108,9 +103,9 @@ Route & Solution::getRoute(int routeIndex) ...@@ -108,9 +103,9 @@ Route & Solution::getRoute(int routeIndex)
return routes[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); spdlog::error("Invalid route index: {}", routeIndex);
throw std::out_of_range("Invalid route index."); throw std::out_of_range("Invalid route index.");
...@@ -123,23 +118,68 @@ double Solution::getCost() const ...@@ -123,23 +118,68 @@ double Solution::getCost() const
return totalCost; return totalCost;
} }
const PDPTWData & Solution::getData() const PDPTWData const &Solution::getData() const
{ {
return data; 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 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(); id.print();
} }
std::cout << "Banques : \n"; std::cout << "Pair Bank : \n";
for (const int id : getBank()) for (int const id: getBank())
{ {
std::cout << id << ", "; std::cout << id << ", ";
} }
......
#pragma once #pragma once
#include <vector> #include "input/pair.h"
#include <iostream>
#include <spdlog/spdlog.h>
#include "lns/constraints/constraint.h" #include "lns/constraints/constraint.h"
#include "route.h" #include "route.h"
#include "input/pair.h"
#include <iostream>
#include <spdlog/spdlog.h>
#include <vector>
class AtomicModification; class AtomicModification;
class AtomicRecreation; class AtomicRecreation;
...@@ -24,68 +23,101 @@ public: ...@@ -24,68 +23,101 @@ public:
using PairBank = std::vector<int>; using PairBank = std::vector<int>;
private: 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; PairBank pairBank;
/* /*
* Vector of routes representing the solution * Vector of routes representing the solution
*/ */
std::vector<Route> routes; std::vector<Route> routes;
double routeCost; double routeCost;
double totalCost; double totalCost;
std::vector<std::unique_ptr<Constraint>> constraints; std::vector<std::unique_ptr<Constraint>> constraints;
public: public:
/** /**
* Expected way to construct a solution. * Expected way to construct a solution.
*/ */
static Solution emptySolution(const PDPTWData &data); static Solution emptySolution(PDPTWData const &data);
explicit Solution(const PDPTWData &data); explicit Solution(PDPTWData const &data);
/** /**
* For testing/debugging. * For testing/debugging.
* Use emptySolution(const PDPTWData &data) to create an initial empty solution. * 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); Solution(PDPTWData const &data, Solution::PairBank pairbank, std::vector<Route> routes, double routeCost,
double totalCost);
PairBank const & getBank() const; PairBank const &getBank() const;
std::vector<Route> const & getRoutes() const; std::vector<Route> const &getRoutes() const;
Route const & getRoute(int routeIndex) const; Route const &getRoute(int routeIndex) const;
const PDPTWData & getData() const; PDPTWData const &getData() const;
double getCost() 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 // For route modification
std::vector<Route> & getRoutes(); std::vector<Route> &getRoutes();
Route & getRoute(int routeIndex); Route &getRoute(int routeIndex);
void print() const;
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(); void init();
/** /**
* Init/reset the constraints of the problem * Init/reset the constraints of the problem
*/ */
void initConstraints(); void initConstraints();
void initRoutes(); void initRoutes();
void initPairBank(); void initPairBank();
void computeAndStoreSolutionCost(); void computeAndStoreSolutionCost();
/** /**
* Compute the cost of the solution (routes cost) * Compute the cost of the solution (routes cost)
*/ */
double computeSolutionCost() const; double computeSolutionCost() const;
double computePenalization() const; double computePenalization() const;
}; };
\ No newline at end of file
...@@ -42,82 +42,5 @@ int main(int argc, char const *argv[]) ...@@ -42,82 +42,5 @@ int main(int argc, char const *argv[])
Solution emptySol = Solution::emptySolution(data); Solution emptySol = Solution::emptySolution(data);
emptySol.print(); 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; return 0;
} }
#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);
}
#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
#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);
}
#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
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment