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
Commits on Source (6)
  • awenjb's avatar
    Adapt sorting strategies · 29677002
    awenjb authored
    Requests are divided into two locations; we are adapting current sorting methods to this criterion.
    - Sort by width: take the average of the two time windows (pickup and delivery)
    - End: take the end of the delivery
    - Start: take the start of the pickup
    - Distance: take the average of the two locations
    29677002
  • awenjb's avatar
    Add string removal · 17a9b29f
    awenjb authored
    - add basic string removal operator
    17a9b29f
  • awenjb's avatar
    Add cost update & number of routes · b52eb6e6
    awenjb authored
    - add initial (maximal) number of routes
    - update cost at each iteration of LNS
    b52eb6e6
  • awenjb's avatar
    Add measurements · 09e92279
    awenjb authored
    - add a time measurement
    - add an iteration measurement
    09e92279
  • awenjb's avatar
    Add CleanEmptyRoute operator · 88a1c34d
    awenjb authored
    88a1c34d
  • awenjb's avatar
    Add Forward Time Slack · 60508774
    awenjb authored
    - add FTS in the time window constraint
    60508774
Showing
with 898 additions and 316 deletions
...@@ -28,6 +28,7 @@ add_executable(pdptw src/mains/main.cpp ...@@ -28,6 +28,7 @@ add_executable(pdptw src/mains/main.cpp
src/lns/solution/solution.cpp src/lns/solution/solution.cpp
src/lns/constraints/constraint.cpp src/lns/constraints/constraint.cpp
src/lns/constraints/capacity/capacity_constraint.cpp src/lns/constraints/capacity/capacity_constraint.cpp
src/lns/constraints/time_window/forward_time_slack.cpp
src/lns/constraints/time_window/time_window_constraint.cpp src/lns/constraints/time_window/time_window_constraint.cpp
src/lns/modification/pair/insert_pair.cpp src/lns/modification/pair/insert_pair.cpp
src/lns/modification/pair/remove_pair.cpp src/lns/modification/pair/remove_pair.cpp
...@@ -38,7 +39,9 @@ add_executable(pdptw src/mains/main.cpp ...@@ -38,7 +39,9 @@ add_executable(pdptw src/mains/main.cpp
src/output/solution_checker.cpp src/output/solution_checker.cpp
src/output/solution_exporter.cpp src/output/solution_exporter.cpp
src/lns/operators/sorting_strategy.cpp src/lns/operators/sorting_strategy.cpp
src/lns/operators/destruction/clean_empty_route.cpp
src/lns/operators/destruction/random_destroy.cpp src/lns/operators/destruction/random_destroy.cpp
src/lns/operators/destruction/string_removal.cpp
src/lns/operators/reconstruction/enumerate.cpp src/lns/operators/reconstruction/enumerate.cpp
src/lns/operators/reconstruction/list_heuristic_insertion.cpp src/lns/operators/reconstruction/list_heuristic_insertion.cpp
src/lns/operators/reconstruction/list_heuristic_cost_oriented.cpp src/lns/operators/reconstruction/list_heuristic_cost_oriented.cpp
......
const int EXCLUSION_PENALTY = 100; const int EXCLUSION_PENALTY = 100;
const int RANDOM_SEED = 100; const int RANDOM_SEED = 100;
\ No newline at end of file
const int NUMBER_VEHICLE = 20;
\ No newline at end of file
...@@ -24,23 +24,23 @@ double data::routeCost(PDPTWData const & data, Route const & route) ...@@ -24,23 +24,23 @@ double data::routeCost(PDPTWData const & data, Route const & route)
{ {
const Matrix & matrix = data.getMatrix(); const Matrix & matrix = data.getMatrix();
const std::vector<int> & routeIDs = route.getRoute(); const std::vector<int> & routeIDs = route.getRoute();
double cost = 0; double cost = 0.0;
if (routeIDs.empty()) if (routeIDs.empty())
{ {
return 0; return 0.0;
} }
// cost from and to the depot // cost from and to the depot
cost += matrix.at(0).at(routeIDs.at(0)); cost += matrix.at(0).at(routeIDs.at(0));
//std::cout << "\n route cost : " << matrix.at(0).at(routeIDs.at(0) << " "; // std::cout << "\n route cost : " << matrix.at(0).at(routeIDs.at(0)) << " ";
cost += matrix.at(routeIDs.back()).at(0); cost += matrix.at(routeIDs.back()).at(0);
// cost in the route // cost in the route
for (size_t i = 0; i < routeIDs.size() - 1; ++i) { for (size_t i = 0; i < routeIDs.size() - 1; ++i) {
cost += matrix.at(routeIDs.at(i)).at(routeIDs.at(i+1)); cost += matrix.at(routeIDs.at(i)).at(routeIDs.at(i+1));
//std::cout << matrix.at(routeIDs.at(i).at(routeIDs.at(i+1) << " "; // std::cout << matrix.at(routeIDs.at(i)).at(routeIDs.at(i+1)) << " ";
} }
//std::cout << matrix.at(routeIDs.back()).at(0) << " : " << cost << "\n"; // std::cout << matrix.at(routeIDs.back()).at(0) << " : " << cost << "\n";
return cost; return cost;
} }
......
#include "pdptw_data.h" #include "pdptw_data.h"
#include <iostream> #include "data.h"
#include <algorithm>
#include <fstream> #include <fstream>
#include <iostream>
#include <numeric>
#include <spdlog/spdlog.h> #include <spdlog/spdlog.h>
int PDPTWData::getSize() const int PDPTWData::getSize() const
{ {
return size; return size;
...@@ -15,12 +18,12 @@ int PDPTWData::getCapacity() const ...@@ -15,12 +18,12 @@ int PDPTWData::getCapacity() const
return capacity; return capacity;
} }
std::vector<Location> const &PDPTWData::getLocations() const std::vector<Location> const &PDPTWData::getLocations() const
{ {
return locations; return locations;
} }
std::vector<Pair> const &PDPTWData::getPairs() const std::vector<Pair> const &PDPTWData::getPairs() const
{ {
return pairs; return pairs;
} }
...@@ -37,12 +40,12 @@ std::string PDPTWData::getDataName() const ...@@ -37,12 +40,12 @@ std::string PDPTWData::getDataName() const
Location const &PDPTWData::getLocation(int id) const Location const &PDPTWData::getLocation(int id) const
{ {
if (id==0) if (id == 0)
{ {
return getDepot(); return getDepot();
} }
// location index from 0 to n-1 // location index from 0 to n-1
return locations.at(id -1); return locations.at(id - 1);
} }
Matrix const &PDPTWData::getMatrix() const Matrix const &PDPTWData::getMatrix() const
...@@ -50,24 +53,28 @@ Matrix const &PDPTWData::getMatrix() const ...@@ -50,24 +53,28 @@ Matrix const &PDPTWData::getMatrix() const
return distanceMatrix; return distanceMatrix;
} }
PDPTWData::PDPTWData(std::string dataName, int size, int capacity, Location depot, std::vector<Location> locations, Matrix distanceMatrix) PDPTWData::PDPTWData(std::string dataName, int size, int capacity, Location depot, std::vector<Location> locations,
: dataName(dataName), size(size), capacity(capacity), depot(depot), locations(std::move(locations)), distanceMatrix(std::move(distanceMatrix)) Matrix distanceMatrix)
: dataName(dataName), size(size), capacity(capacity), depot(depot), locations(std::move(locations)),
distanceMatrix(std::move(distanceMatrix))
{ {
// Associate pair of locations // Associate pair of locations
pairs.clear(); pairs.clear();
for (const Location & loc : this->locations) for (Location const &loc: this->locations)
{ {
if( loc.getLocType() == LocType::PICKUP ) if (loc.getLocType() == LocType::PICKUP)
{ {
// vector indexed from 0 / Location indexed from 1 // vector indexed from 0 / Location indexed from 1
pairs.emplace_back(loc, this->locations.at(loc.getPair()-1), loc.getId()); pairs.emplace_back(loc, this->locations.at(loc.getPair() - 1), loc.getId());
} }
} }
// Compute closest location matrix
initClosestLocations();
} }
const Pair &PDPTWData::getPair(int id) const Pair const &PDPTWData::getPair(int id) const
{ {
for (const Pair &pair : pairs) for (Pair const &pair: pairs)
{ {
if (id == pair.getID()) if (id == pair.getID())
{ {
...@@ -83,8 +90,12 @@ int PDPTWData::getPairCount() const ...@@ -83,8 +90,12 @@ int PDPTWData::getPairCount() const
return getPairs().size(); return getPairs().size();
} }
int PDPTWData::getLocationCount() const
{
return getLocations().size();
}
void PDPTWData::print() const void PDPTWData::print() const
{ {
std::cout << "Instance name : " << dataName << "\n"; std::cout << "Instance name : " << dataName << "\n";
std::cout << "Instance size: " << size << "\n"; std::cout << "Instance size: " << size << "\n";
...@@ -93,22 +104,36 @@ void PDPTWData::print() const ...@@ -93,22 +104,36 @@ void PDPTWData::print() const
depot.print(); depot.print();
std::cout << "Locations:\n"; std::cout << "Locations:\n";
for (const auto& loc : locations) { for (auto const &loc: locations)
{
loc.print(); loc.print();
} }
std::cout << "Distance Matrix:\n"; std::cout << "Distance Matrix:\n";
for (const auto& row : distanceMatrix) { for (auto const &row: distanceMatrix)
for (const auto& dist : row) { {
for (auto const &dist: row)
{
std::cout << dist << " "; std::cout << dist << " ";
} }
std::cout << "\n"; std::cout << "\n";
} }
std::cout << "Closest Matrix (no depot):\n";
for (auto const &row: closestLocations)
{
for (auto const &ID: row)
{
std::cout << ID << " ";
}
std::cout << "\n";
}
std::cout << "Pair IDs:\n"; std::cout << "Pair IDs:\n";
for (const auto& pair : pairs) for (auto const &pair: pairs)
{; {
std::cout << pair.getID() << " "; ;
std::cout << pair.getID() << " ";
} }
std::cout << " \n"; std::cout << " \n";
} }
...@@ -127,14 +152,13 @@ void PDPTWData::checkData() const ...@@ -127,14 +152,13 @@ void PDPTWData::checkData() const
} }
} }
bool PDPTWData::checkMatrix() const bool PDPTWData::checkMatrix() const
{ {
// square matrix // square matrix
for (const auto& row : getMatrix()) for (auto const &row: getMatrix())
{ {
if (row.size() != size) if (row.size() != size)
{ {
return true; return true;
} }
...@@ -161,22 +185,23 @@ bool PDPTWData::checkMatrix() const ...@@ -161,22 +185,23 @@ bool PDPTWData::checkMatrix() const
bool PDPTWData::checkLocation() const bool PDPTWData::checkLocation() const
{ {
// check if location id equals the position in the location vector // check if location id equals the position in the location vector
for (size_t i = 0; i < size-1; ++i) { for (size_t i = 0; i < size - 1; ++i)
if (locations.at(i).getId() != static_cast<int>(i)+1) { {
if (locations.at(i).getId() != static_cast<int>(i) + 1)
{
return true; return true;
} }
} }
// check if pair of location are well made (type, id, demand, timeWindow) // check if pair of location are well made (type, id, demand, timeWindow)
for(const Location& loc : getLocations()) for (Location const &loc: getLocations())
{ {
if (loc.getLocType() == LocType::PICKUP) if (loc.getLocType() == LocType::PICKUP)
{ {
if ( (getLocations().at(loc.getPair()-1).getLocType() != LocType::DELIVERY) if ((getLocations().at(loc.getPair() - 1).getLocType() != LocType::DELIVERY) ||
|| (loc.getDemand() != - getLocations().at(loc.getPair()-1).getDemand()) (loc.getDemand() != -getLocations().at(loc.getPair() - 1).getDemand()) ||
|| (loc.getId() != getLocations().at(loc.getPair()-1).getPair()) ) (loc.getId() != getLocations().at(loc.getPair() - 1).getPair()))
{ {
return true; return true;
} }
...@@ -191,6 +216,40 @@ bool PDPTWData::checkLocation() const ...@@ -191,6 +216,40 @@ bool PDPTWData::checkLocation() const
return false; return false;
} }
void PDPTWData::initClosestLocations()
{
closestLocations.reserve(getLocationCount());
for (Location const &location: locations)
{
// sorting from closest to furthest using indexes
std::vector<int> closestLocationsIndexes(getLocationCount());
std::iota(closestLocationsIndexes.begin(), closestLocationsIndexes.end(), 0);
// Do the sorting
std::ranges::sort(closestLocationsIndexes, {}, [this, &location](int index) {
return data::TravelTime(*this, location.getId(), locations.at(index).getId());
});
// we store the indexes in the final container
closestLocations.emplace_back();
closestLocations.back().reserve(getLocationCount());
std::ranges::transform(closestLocationsIndexes, std::back_inserter(closestLocations.back()), [this](int index) {
return std::cref(locations.at(index)).get().getId();
});
}
}
std::vector<int> const &PDPTWData::getClosestLocationsID(int id) const
{
// sanity check
if (id < 1 || id > getLocationCount())
{
spdlog::error("getClosestLocationsID: Location ID {} is out of valid range [1, {}]", id, getLocationCount());
throw std::out_of_range("Error: Location ID is out of valid range [1, " + std::to_string(getLocationCount()) +
"]");
}
return closestLocations.at(id - 1);// vector indexed from 0
}
InputJsonException::InputJsonException(std::string_view reason) InputJsonException::InputJsonException(std::string_view reason)
: reason(fmt::format("Input JSON file is incorrect : {}", reason)) : reason(fmt::format("Input JSON file is incorrect : {}", reason))
......
...@@ -30,6 +30,11 @@ class PDPTWData ...@@ -30,6 +30,11 @@ class PDPTWData
std::vector<Location> locations; std::vector<Location> locations;
std::vector<Pair> pairs;// std::unordered_map<int, Pair> pair; if getPair(index) is needed ? std::vector<Pair> pairs;// std::unordered_map<int, Pair> pair; if getPair(index) is needed ?
Matrix distanceMatrix; Matrix distanceMatrix;
// stores the list of locations ID from closest to furthest for every location (no depot).
std::vector<std::vector<int>> closestLocations;
void initClosestLocations();
public: public:
PDPTWData(); PDPTWData();
...@@ -54,19 +59,25 @@ public: ...@@ -54,19 +59,25 @@ public:
std::vector<Location> const &getLocations() const; std::vector<Location> const &getLocations() const;
int getLocationCount() const;
std::vector<Pair> const &getPairs() const; std::vector<Pair> const &getPairs() const;
int getPairCount() const; int getPairCount() const;
Pair const &getPair(int id) const; Pair const &getPair(int id) const;
/** /**
* 0 return the depot. * 0 return the depot.
* Other numbers return the associated location. * Other numbers return the associated location.
*/ */
Location const &getLocation(int id) const; Location const &getLocation(int id) const;
Location const &getDepot() const; Location const &getDepot() const;
Matrix const &getMatrix() const; Matrix const &getMatrix() const;
/**
* Given a location ID, return a list of location id sorted by proximity.
*/
std::vector<int> const &getClosestLocationsID(int id) const;
int getSize() const; int getSize() const;
int getCapacity() const; int getCapacity() const;
std::string getDataName() const; std::string getDataName() const;
......
#include "capacity_constraint.h" #include "capacity_constraint.h"
CapacityConstraint::CapacityConstraint(Solution const &solution) : Constraint(solution) CapacityConstraint::CapacityConstraint(Solution const &solution) : Constraint(solution)
{ {
// Init an empty vector of reach time for all routes // Init an empty vector of reach time for all routes
//std::vector<CapacityVector> routeCapacities(getSolution().getRoutes().size(), CapacityVector());
routeCapacities.resize(getSolution().getRoutes().size()); routeCapacities.resize(getSolution().getRoutes().size());
} }
std::unique_ptr<Constraint> CapacityConstraint::clone(Solution const &newOwningSolution) const { std::unique_ptr<Constraint> CapacityConstraint::clone(Solution const &newOwningSolution) const
{
std::unique_ptr<CapacityConstraint> clonePtr = std::make_unique<CapacityConstraint>(newOwningSolution); std::unique_ptr<CapacityConstraint> clonePtr = std::make_unique<CapacityConstraint>(newOwningSolution);
clonePtr->routeCapacities = routeCapacities; clonePtr->routeCapacities = routeCapacities;
return clonePtr; return clonePtr;
} }
void CapacityConstraint::initRouteCapacities()
void CapacityConstraint::initCapacities()
{ {
routeCapacities = std::vector<CapacityVector>(); routeCapacities.clear();
for (const Route& route : getSolution().getRoutes()) for (Route const &route: getSolution().getRoutes())
{ {
CapacityVector capacities; CapacityVector capacities;
int currentCapacity = 0; int currentCapacity = 0;
for (int locationID : route.getRoute()) for (int locationID: route.getRoute())
{ {
currentCapacity += getSolution().getData().getLocation(locationID).getDemand(); currentCapacity += getSolution().getData().getLocation(locationID).getDemand();
capacities.push_back(currentCapacity); capacities.push_back(currentCapacity);
} }
routeCapacities.push_back(capacities); routeCapacities.push_back(capacities);
} }
} }
std::vector<int> const & CapacityConstraint::getRouteCapacities(int routeIndex) const std::vector<int> const &CapacityConstraint::getRouteCapacities(int routeIndex) const
{ {
return routeCapacities.at(routeIndex); return routeCapacities.at(routeIndex);
} }
// check for every location between the pickupPosition and the deliveryPosition /*
// not ideal * Check if an insertion is feasible by computing new capacity values between the PickupPosition and DeliveryPosition.
* Assume the constraint is valid beforehand.
* O(n), n the size of the route
*/
bool CapacityConstraint::checkModif(Pair const &pair, int routeIndex, int PickupPosition, int DeliveryPosition) const bool CapacityConstraint::checkModif(Pair const &pair, int routeIndex, int PickupPosition, int DeliveryPosition) const
{ {
int max_capacity = getSolution().getData().getCapacity(); int max_capacity = getSolution().getData().getCapacity();
int demand = pair.getPickup().getDemand();
CapacityVector const &routeCapacity = getRouteCapacities(routeIndex);
// guardrail // if PickupPosition != DeliveryPosition, check from pickup-1 to delivery-1
if (pair.getPickup().getDemand() > max_capacity) // if PickupPosition is 0, don't check -1 ...
for (int i = std::max(0, PickupPosition - 1); i < DeliveryPosition; i++)
{ {
return false; if (routeCapacity.at(i) + demand > max_capacity)
}
// check if the disponible capacity is enough are not
for (int i = PickupPosition; i <= DeliveryPosition; ++i) {
if ((i!=0) && (getRouteCapacities(routeIndex).at(i-1) + pair.getPickup().getDemand() >= max_capacity))
{ {
return false; return false;
} }
} }
return true; return true;
} }
// update the route and the weight // update the route and the weight
// not ideal void CapacityConstraint::applyModif(Pair const &pair, int routeIndex, int PickupPosition, int DeliveryPosition,
void CapacityConstraint::applyModif(Pair const &pair, int routeIndex, int PickupPosition, int DeliveryPosition, bool addPair) bool addPair)
{ {
if (addPair) if (addPair)
{ {
// Insert new values // Insert new values
routeCapacities.at(routeIndex).insert(routeCapacities.at(routeIndex).begin()+DeliveryPosition, 0); routeCapacities.at(routeIndex).insert(routeCapacities.at(routeIndex).begin() + DeliveryPosition, 0);
if (DeliveryPosition != 0) if (DeliveryPosition != 0)
{ {
routeCapacities.at(routeIndex).at(DeliveryPosition) += routeCapacities.at(routeIndex).at(DeliveryPosition-1); routeCapacities.at(routeIndex).at(DeliveryPosition) +=
routeCapacities.at(routeIndex).at(DeliveryPosition - 1);
} }
routeCapacities.at(routeIndex).insert(routeCapacities.at(routeIndex).begin()+PickupPosition, pair.getPickup().getDemand()); routeCapacities.at(routeIndex)
.insert(routeCapacities.at(routeIndex).begin() + PickupPosition, pair.getPickup().getDemand());
if (PickupPosition != 0) if (PickupPosition != 0)
{ {
routeCapacities.at(routeIndex).at(PickupPosition) += routeCapacities.at(routeIndex).at(PickupPosition-1); routeCapacities.at(routeIndex).at(PickupPosition) += routeCapacities.at(routeIndex).at(PickupPosition - 1);
} }
// Update value // Update value
for (int i = PickupPosition + 1; i < DeliveryPosition + 1; ++i) for (int i = PickupPosition + 1; i < DeliveryPosition + 1; ++i)
{ {
routeCapacities.at(routeIndex).at(i) += pair.getPickup().getDemand(); routeCapacities.at(routeIndex).at(i) += pair.getPickup().getDemand();
} }
} }
else else
{ {
for (int i = PickupPosition + 1; i < DeliveryPosition; ++i) for (int i = PickupPosition + 1; i < DeliveryPosition; ++i)
{ {
routeCapacities.at(routeIndex).at(i) += pair.getDelivery().getDemand(); routeCapacities.at(routeIndex).at(i) += pair.getDelivery().getDemand();
} }
...@@ -96,48 +96,48 @@ void CapacityConstraint::applyModif(Pair const &pair, int routeIndex, int Pickup ...@@ -96,48 +96,48 @@ void CapacityConstraint::applyModif(Pair const &pair, int routeIndex, int Pickup
// remove pair // remove pair
routeCapacities.at(routeIndex).erase(routeCapacities.at(routeIndex).begin() + DeliveryPosition); routeCapacities.at(routeIndex).erase(routeCapacities.at(routeIndex).begin() + DeliveryPosition);
routeCapacities.at(routeIndex).erase(routeCapacities.at(routeIndex).begin() + PickupPosition); routeCapacities.at(routeIndex).erase(routeCapacities.at(routeIndex).begin() + PickupPosition);
} }
} }
bool CapacityConstraint::check(InsertPair const &op) const bool CapacityConstraint::check(InsertPair const &op) const
{ {
//std::cout << " #Capa Check"; //std::cout << " #Capa Check";
return checkModif(op.getPair(), op.getRouteIndex(), op.getPickupInsertion(), op.getDeliveryInsertion()); return checkModif(op.getPair(), op.getRouteIndex(), op.getPickupInsertion(), op.getDeliveryInsertion());
} }
void CapacityConstraint::apply(InsertPair const &op) void CapacityConstraint::apply(InsertPair const &op)
{ {
//std::cout << "-> Apply Modification on Capacity \n"; //std::cout << "-> Apply Modification on Capacity \n";
applyModif(op.getPair(), op.getRouteIndex(), op.getPickupInsertion(), op.getDeliveryInsertion(), true); applyModif(op.getPair(), op.getRouteIndex(), op.getPickupInsertion(), op.getDeliveryInsertion(), true);
} }
bool CapacityConstraint::check(InsertRoute const &op) const bool CapacityConstraint::check(InsertRoute const &op) const
{ {
return true; return true;
} }
void CapacityConstraint::apply(InsertRoute const &op) void CapacityConstraint::apply(InsertRoute const &op)
{ {
// add new empty route // add new empty route
routeCapacities.emplace_back(); routeCapacities.emplace_back();
} }
bool CapacityConstraint::check(RemovePair const &op) const bool CapacityConstraint::check(RemovePair const &op) const
{ {
// Remove a pair (always true) // Remove a pair (always true)
return true; return true;
} }
void CapacityConstraint::apply(RemovePair const &op) void CapacityConstraint::apply(RemovePair const &op)
{ {
applyModif(op.getPair(), op.getRouteIndex(), op.getPickupDeletion(), op.getDeliveryDeletion(), false); applyModif(op.getPair(), op.getRouteIndex(), op.getPickupDeletion(), op.getDeliveryDeletion(), false);
} }
bool CapacityConstraint::check(RemoveRoute const &op) const bool CapacityConstraint::check(RemoveRoute const &op) const
{ {
return true; return true;
} }
void CapacityConstraint::apply(RemoveRoute const &op) void CapacityConstraint::apply(RemoveRoute const &op)
{ {
// add new empty route // add new empty route
...@@ -148,16 +148,14 @@ void CapacityConstraint::print() const ...@@ -148,16 +148,14 @@ void CapacityConstraint::print() const
{ {
std::cout << "Used capacity : \n"; std::cout << "Used capacity : \n";
int i = 0; int i = 0;
for (const auto& capaVector : routeCapacities) for (auto const &capaVector: routeCapacities)
{ {
std::cout << "#" << i << " : "; std::cout << "#" << i << " : ";
for (const int capa : capaVector) for (int const capa: capaVector)
{ {
std::cout << capa << ", "; std::cout << capa << ", ";
} }
std::cout << "\n "; std::cout << "\n";
i++; i++;
} }
std::cout << "\n";
} }
#pragma once #pragma once
#include <vector>
#include "lns/constraints/constraint.h" #include "lns/constraints/constraint.h"
#include "lns/solution/solution.h"
#include "lns/modification/pair/insert_pair.h" #include "lns/modification/pair/insert_pair.h"
#include "lns/modification/pair/remove_pair.h" #include "lns/modification/pair/remove_pair.h"
#include "lns/modification/route/insert_route.h" #include "lns/modification/route/insert_route.h"
#include "lns/modification/route/remove_route.h" #include "lns/modification/route/remove_route.h"
#include "lns/solution/solution.h"
#include <vector>
/** /**
* Capacity constraint. * Capacity constraint.
* To check this, we keep track of the sum of all requests and check it against the max capacity.
* TO DO, verification in O(1) by storing a matrix !
*/ */
class CapacityConstraint : public Constraint class CapacityConstraint : public Constraint
{ {
public: public:
using CapacityVector = std::vector<int>; using CapacityVector = std::vector<int>;
private:
private:
/**
* Store the used capacity value when leaving one location in each route.
*/
std::vector<CapacityVector> routeCapacities; std::vector<CapacityVector> routeCapacities;
void apply(InsertPair const &op) override; void apply(InsertPair const &op) override;
...@@ -31,22 +31,21 @@ private: ...@@ -31,22 +31,21 @@ private:
bool check(InsertRoute const &op) const override; bool check(InsertRoute const &op) const override;
bool check(RemovePair const &op) const override; bool check(RemovePair const &op) const override;
bool check(RemoveRoute const &op) const override; bool check(RemoveRoute const &op) const override;
public:
public:
explicit CapacityConstraint(Solution const &); explicit CapacityConstraint(Solution const &);
CapacityConstraint(CapacityConstraint const &) = default; CapacityConstraint(CapacityConstraint const &) = default;
std::unique_ptr<Constraint> clone(Solution const &newOwningSolution) const override; std::unique_ptr<Constraint> clone(Solution const &newOwningSolution) const override;
/* /*
* Given the solution, calculate the capacities when leaving each location * Compute from scratch the routeCapacities vector.
* Modify the routeCapacities vector ! */
*/ void initRouteCapacities();
void initCapacities();
/* /*
* Return capacity vector of a route * Return capacity vector of a route
*/ */
CapacityVector const &getRouteCapacities(int routeIndex) const; CapacityVector const &getRouteCapacities(int routeIndex) const;
/* /*
...@@ -62,7 +61,7 @@ public: ...@@ -62,7 +61,7 @@ public:
/* /*
* Update the weight * Update the weight
*/ */
void applyModif(Pair const &pair, int routeIndex, int PickupPosition, int DeliveryPosition, bool addPair); void applyModif(Pair const &pair, int routeIndex, int PickupPosition, int DeliveryPosition, bool addPair);
void print() const override; void print() const override;
}; };
\ No newline at end of file
#include "forward_time_slack.h"
#include "input/data.h"
#include "lns/solution/solution.h"
std::vector<TimeInteger> const &ForwardTimeSlack::getFTS() const
{
return FTS;
}
std::vector<TimeInteger> const &ForwardTimeSlack::getEarliestArrival() const
{
return earliestArrival;
}
std::vector<TimeInteger> const &ForwardTimeSlack::getLatestArrival() const
{
return latestArrival;
}
void ForwardTimeSlack::initFTS(PDPTWData const &data, Route const &route)
{
int n = route.getSize();
std::vector<int> const &routeIDs = route.getRoute();
earliestArrival = std::vector<TimeInteger>(n + 1, 0);
latestArrival = std::vector<TimeInteger>(n + 1, 0);
FTS = std::vector<TimeInteger>(n + 1, 0);
// Compute arrival times
double depotStart = data.getDepot().getTimeWindow().getStart();
double firstLocationStart = data.getLocation(routeIDs.at(0)).getTimeWindow().getStart();
double depotToFirst = data::TravelTime(data, 0, routeIDs.at(0));
earliestArrival.at(0) = std::max(firstLocationStart, depotStart + depotToFirst);
for (int i = 1; i < n; ++i)
{
double start = data.getLocation(routeIDs.at(i)).getTimeWindow().getStart();
double service = data.getLocation(routeIDs.at(i - 1)).getServiceDuration();
double travelTime = data::TravelTime(data, routeIDs.at(i - 1), routeIDs.at(i));
earliestArrival.at(i) = std::max(start, earliestArrival.at(i - 1) + service + travelTime);
}
// Compute earliest arrival at depot after last location
double lastService = data.getLocation(routeIDs.at(n - 1)).getServiceDuration();
double lastToDepot = data::TravelTime(data, routeIDs.at(n - 1), 0);
earliestArrival.at(n) = earliestArrival.at(n - 1) + lastService + lastToDepot;
// Compute latest arrival times
latestArrival.at(n - 1) = data.getLocation(routeIDs.at(n - 1)).getTimeWindow().getEnd();
for (int i = n - 2; i >= 0; --i)
{
double end = data.getLocation(routeIDs.at(i)).getTimeWindow().getEnd();
double service = data.getLocation(routeIDs.at(i)).getServiceDuration();
double travelTime = data::TravelTime(data, routeIDs.at(i), routeIDs.at(i + 1));
latestArrival.at(i) = std::min(latestArrival.at(i + 1) - service - travelTime, end);
}
// latest arrival at depot
latestArrival.at(n) = data.getDepot().getTimeWindow().getEnd();
// Compute FTS
for (int i = 0; i <= n; ++i)
{
FTS.at(i) = latestArrival.at(i) - earliestArrival.at(i);
}
}
bool ForwardTimeSlack::isPickupDeliveryInsertionValid(PDPTWData const &data, Route const &route, int pickupID,
int deliveryID, int insertPickupIndex,
int insertDeliveryIndex) const
{
std::vector<int> const &routeIDs = route.getRoute();
int n = route.getSize();
// safety
if (insertPickupIndex > insertDeliveryIndex)
{
return false;
}
// if the route is empty, only check de time window
if (routeIDs.empty())
{
double startTW = data.getLocation(pickupID).getTimeWindow().getStart();
double endTW = data.getLocation(deliveryID).getTimeWindow().getEnd();
return (insertPickupIndex == 0) && (insertDeliveryIndex == 0) &&
(!(startTW > data.getDepot().getTimeWindow().getEnd() ||
endTW < data.getDepot().getTimeWindow().getStart()));
}
// Algo
// Pickup
// two specific cases, insertion in first or last
int prev = (insertPickupIndex == 0) ? 0 : routeIDs.at(insertPickupIndex - 1);
int next = (insertPickupIndex == n) ? 0 : routeIDs.at(insertPickupIndex);
double arrivalPrev =
(prev == 0) ? data.getDepot().getTimeWindow().getStart() : getEarliestArrival().at(insertPickupIndex - 1);
double travelToNew = data::TravelTime(data, prev, pickupID);
double serviceNew = data.getLocation(pickupID).getServiceDuration();
double travelNewToNext = data::TravelTime(data, pickupID, next);
double newArrival = std::max(data.getLocation(pickupID).getTimeWindow().getStart(),
arrivalPrev + data.getLocation(prev).getServiceDuration() + travelToNew);
// Check TW
if (!(data.getLocation(pickupID).getTimeWindow().isValid(newArrival)))
{
return false;
}
// Calculate the pickup duration
double arrivalNext =
std::max(data.getLocation(next).getTimeWindow().getStart(), newArrival + serviceNew + travelNewToNext);
double pickupDuration = arrivalNext - earliestArrival.at(insertPickupIndex);
// Check FTS
if (/*next != 0 &&*/ pickupDuration >= getFTS().at(insertPickupIndex))
{
return false;
}
// Delivery
// if insertDeliveryIndex == insertPickupIndex, then Pickup is just before Delivery in the route
int prevDelivery = (insertDeliveryIndex == insertPickupIndex) ? pickupID : routeIDs.at(insertDeliveryIndex - 1);
int nextDelivery = (insertDeliveryIndex >= n) ? 0 : routeIDs.at(insertDeliveryIndex);
// We have to take into account the duration of the pickup
// (previous earliest arrival) + (time taken by the pickup) - (time of traveling between prev and next)
double arrivalPrevDelivery =
(prevDelivery == pickupID) ? newArrival : getEarliestArrival().at(insertDeliveryIndex - 1) + pickupDuration;
double travelToDelivery = data::TravelTime(data, prevDelivery, deliveryID);
double serviceDelivery = data.getLocation(deliveryID).getServiceDuration();
double travelDeliveryToNext = data::TravelTime(data, deliveryID, nextDelivery);
double newArrivalDelivery =
std::max(data.getLocation(deliveryID).getTimeWindow().getStart(),
arrivalPrevDelivery + data.getLocation(prevDelivery).getServiceDuration() + travelToDelivery);
// Check TW
if (!(data.getLocation(deliveryID).getTimeWindow().isValid(newArrivalDelivery)))
{
return false;
}
// Calculate the delivery duration
double arrivalNextDelivery = std::max(data.getLocation(nextDelivery).getTimeWindow().getStart(),
newArrivalDelivery + serviceDelivery + travelDeliveryToNext);
double deliveryDuration = arrivalNextDelivery - earliestArrival.at(insertDeliveryIndex);
// Check FTS validity
if (/*nextDelivery != 0 &&*/ deliveryDuration >= getFTS().at(insertDeliveryIndex))
{
return false;
}
return true;
}
// TO DO some cuts in the loop to avoid recalculate everything
void ForwardTimeSlack::updateFTSAfterInsertion(PDPTWData const &data, Route const &route, int insertPickupIndex,
int insertDeliveryIndex)
{
int n = route.getSize();
std::vector<int> const &routeIDs = route.getRoute();
// Resize vectors to match the updated route size
earliestArrival.resize(n + 1, 0);
latestArrival.resize(n + 1, 0);
FTS.resize(n + 1, 0);
// Recalculate earliest arrival times
for (int i = insertPickupIndex; i <= n; ++i)
{
if (i == 0)// specific case for the first element
{
double depotStart = data.getDepot().getTimeWindow().getStart();
double firstLocationStart = data.getLocation(routeIDs.at(0)).getTimeWindow().getStart();
double depotToFirst = data::TravelTime(data, 0, routeIDs.at(0));
earliestArrival.at(0) = std::max(firstLocationStart, depotStart + depotToFirst);
}
else
{
int prev = routeIDs.at(i - 1);
int curr = (i == n) ? 0 : routeIDs.at(i);// specific case for the last element
double start = data.getLocation(curr).getTimeWindow().getStart();
double service = data.getLocation(prev).getServiceDuration();
double travelTime = data::TravelTime(data, prev, curr);
earliestArrival.at(i) = std::max(start, earliestArrival.at(i - 1) + service + travelTime);
}
}
// try add element in front then recalculate from the delivery index
// Recalculate latest arrival times
latestArrival.at(n) = data.getDepot().getTimeWindow().getEnd();
latestArrival.at(n - 1) = data.getLocation(routeIDs.at(n - 1)).getTimeWindow().getEnd();
for (int i = n - 2; i >= 0; --i)
{
double end = data.getLocation(routeIDs.at(i)).getTimeWindow().getEnd();
double service = data.getLocation(routeIDs.at(i)).getServiceDuration();
double travelTime = data::TravelTime(data, routeIDs.at(i), routeIDs.at(i + 1));
latestArrival.at(i) = std::min(latestArrival.at(i + 1) - service - travelTime, end);
}
// Recalculate FTS
for (int i = 0; i <= n; ++i)
{
FTS.at(i) = latestArrival.at(i) - earliestArrival.at(i);
}
}
// TO DO some cuts in the loop to avoid recalculate everything
void ForwardTimeSlack::updateFTSAfterDeletion(PDPTWData const &data, Route const &route, int removePickupIndex,
int removeDeliveryIndex)
{
int n = route.getSize();
std::vector<int> const &routeIDs = route.getRoute();
// empty route
if (n == 0)
{
earliestArrival.clear();
latestArrival.clear();
FTS.clear();
return;
}
// Resize vectors to match the updated route size
earliestArrival.resize(n + 1, 0);
latestArrival.resize(n + 1, 0);
FTS.resize(n + 1, 0);
// Recalculate earliest arrival times
for (int i = 0; i <= n; ++i)
{
if (i == 0)// Specific case for the first element
{
double depotStart = data.getDepot().getTimeWindow().getStart();
double firstLocationStart = data.getLocation(routeIDs.at(0)).getTimeWindow().getStart();
double depotToFirst = data::TravelTime(data, 0, routeIDs.at(0));
earliestArrival.at(0) = std::max(firstLocationStart, depotStart + depotToFirst);
}
else
{
int prev = routeIDs.at(i - 1);
int curr = (i == n) ? 0 : routeIDs.at(i);
double start = data.getLocation(curr).getTimeWindow().getStart();
double service = data.getLocation(prev).getServiceDuration();
double travelTime = data::TravelTime(data, prev, curr);
earliestArrival.at(i) = std::max(start, earliestArrival.at(i - 1) + service + travelTime);
}
}
// Recalculate latest arrival times
latestArrival.at(n) = data.getDepot().getTimeWindow().getEnd();
latestArrival.at(n - 1) = data.getLocation(routeIDs.at(n - 1)).getTimeWindow().getEnd();
for (int i = n - 2; i >= 0; --i)
{
double end = data.getLocation(routeIDs.at(i)).getTimeWindow().getEnd();
double service = data.getLocation(routeIDs.at(i)).getServiceDuration();
double travelTime = data::TravelTime(data, routeIDs.at(i), routeIDs.at(i + 1));
latestArrival.at(i) = std::min(latestArrival.at(i + 1) - service - travelTime, end);
}
// Recalculate FTS
for (int i = 0; i <= n; ++i)
{
FTS.at(i) = latestArrival.at(i) - earliestArrival.at(i);
}
}
void ForwardTimeSlack::print() const
{
for (TimeInteger timeVal: earliestArrival)
{
std::cout << timeVal << " ";
}
std::cout << std::endl;
for (TimeInteger timeVal: latestArrival)
{
std::cout << timeVal << " ";
}
std::cout << std::endl;
for (TimeInteger ftsVal: FTS)
{
std::cout << ftsVal << " ";
}
}
\ No newline at end of file
#pragma once
#include "lns/solution/route.h"
#include "types.h"
/*
* Represent the forward time slack of a route.
*/
class ForwardTimeSlack
{
private:
std::vector<TimeInteger> earliestArrival;
std::vector<TimeInteger> latestArrival;
std::vector<TimeInteger> FTS;
public:
explicit ForwardTimeSlack() = default;
/*
* Check if the insertion of a new pickup/delivery pair is feasible.
* ForwardTimeSlack is supposed to be correct. Only check FTS and Time windows.
*/
bool isPickupDeliveryInsertionValid(PDPTWData const &data, Route const &route, int pickupID, int deliveryID,
int insertPickupIndex, int insertDeliveryIndex) const;
/*
* Compute FTS values from scratch.
*/
void initFTS(PDPTWData const &data, Route const &route);
/*
* Update FTS.
* Suppose the route has already been updated (but not the FTS)
*/
void updateFTSAfterInsertion(PDPTWData const &data, Route const &route, int insertPickupIndex,
int insertDeliveryIndex);
/*
* Update FTS.
* Suppose the route has already been updated (but not the FTS)
*/
void updateFTSAfterDeletion(PDPTWData const &data, Route const &route, int removePickupIndex,
int removeDeliveryIndex);
std::vector<TimeInteger> const &getFTS() const;
std::vector<TimeInteger> const &getEarliestArrival() const;
std::vector<TimeInteger> const &getLatestArrival() const;
void print() const;
};
\ No newline at end of file
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
#include "input/data.h" #include "input/data.h"
#include "input/pdptw_data.h" #include "input/pdptw_data.h"
#include "input/time_window.h" #include "input/time_window.h"
#include "lns/constraints/time_window/forward_time_slack.h"
#include "lns/modification/pair/insert_pair.h" #include "lns/modification/pair/insert_pair.h"
#include "lns/modification/pair/remove_pair.h" #include "lns/modification/pair/remove_pair.h"
#include "lns/modification/route/remove_route.h" #include "lns/modification/route/remove_route.h"
...@@ -10,176 +11,71 @@ ...@@ -10,176 +11,71 @@
TimeWindowConstraint::TimeWindowConstraint(Solution const &solution) : Constraint(solution) TimeWindowConstraint::TimeWindowConstraint(Solution const &solution) : Constraint(solution)
{ {
allRouteReachTimes.resize(getSolution().getRoutes().size()); FTSContainer.clear();
for (Route const &route: solution.getRoutes())
{
FTSContainer.emplace_back();
}
} }
TimeWindowConstraint::~TimeWindowConstraint() TimeWindowConstraint::~TimeWindowConstraint()
{ {
allRouteReachTimes.clear(); //allRouteReachTimes.clear();
FTSContainer.clear();
} }
std::unique_ptr<Constraint> TimeWindowConstraint::clone(Solution const &newOwningSolution) const std::unique_ptr<Constraint> TimeWindowConstraint::clone(Solution const &newOwningSolution) const
{ {
std::unique_ptr<TimeWindowConstraint> clonePtr = std::make_unique<TimeWindowConstraint>(newOwningSolution); std::unique_ptr<TimeWindowConstraint> clonePtr = std::make_unique<TimeWindowConstraint>(newOwningSolution);
clonePtr->allRouteReachTimes = allRouteReachTimes; //clonePtr->allRouteReachTimes = allRouteReachTimes;
clonePtr->FTSContainer = FTSContainer;
return clonePtr; return clonePtr;
} }
void TimeWindowConstraint::computeReachTimes(PDPTWData const &data, std::vector<int> const &routeIDs, void TimeWindowConstraint::initFTS()
ReachTimeVector &reachTimes) const
{ {
// Adjust the size of reachTimes vector FTSContainer = std::vector<ForwardTimeSlack>();
reachTimes.resize(routeIDs.size(), 0); //reserve
// Time to the first location
reachTimes.at(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.at(i - 1), routeIDs.at(i));
// locations are indexed from 0 to n-1,
TimeInteger serviceTime = data.getLocation(routeIDs.at(i - 1)).getServiceDuration();
TimeInteger startTW = data.getLocation(routeIDs.at(i - 1)).getTimeWindow().getStart();
reachTimes.at(i) = std::max(reachTimes.at(i - 1), startTW) + serviceTime + travelTime;
}
}
void TimeWindowConstraint::initReachTimes()
{
allRouteReachTimes = std::vector<ReachTimeVector>();
int i = 0; int i = 0;
for (Route const &route: getSolution().getRoutes()) for (Route const &route: getSolution().getRoutes())
{ {
// init and calculate reach time value FTSContainer.emplace_back();
allRouteReachTimes.emplace_back(); FTSContainer.at(i).initFTS(getSolution().getData(), route);
computeReachTimes(getSolution().getData(), route.getRoute(), allRouteReachTimes.at(i));
++i; ++i;
} }
} }
// Copie le vecteur de reach time de la route concernée
// Insère les positions pickup/delivery
// refait l'ordo sur le nouveau vecteur
bool TimeWindowConstraint::checkInsertion(PDPTWData const &data, Pair const &pair, int routeIndex, int pickupPos, bool TimeWindowConstraint::checkInsertion(PDPTWData const &data, Pair const &pair, int routeIndex, int pickupPos,
int deliveryPos) const int deliveryPos) const
{ {
ReachTimeVector const &reachTimes = allRouteReachTimes.at(routeIndex); return FTSContainer.at(routeIndex)
.isPickupDeliveryInsertionValid(data,
// COPY route vector getSolution().getRoute(routeIndex),
std::vector<int> route(getSolution().getRoute(routeIndex).getRoute().begin(), pair.getPickup().getId(),
getSolution().getRoute(routeIndex).getRoute().end()); pair.getDelivery().getId(),
// COPY reachTimes vector pickupPos,
ReachTimeVector newReachTimes(reachTimes.begin(), reachTimes.end()); deliveryPos);
// Insert pickup and delivery
route.insert(route.begin() + deliveryPos, pair.getDelivery().getId());
route.insert(route.begin() + pickupPos, pair.getPickup().getId());
// std::cout << "\n";
// for (auto pos : route)
// {
// std::cout << pos << " ";
// }
// std::cout << "\n";
// Compute new reach time
computeReachTimes(data, route, newReachTimes);
// std::cout << "\n";
// for (auto pos : newReachTimes)
// {
// std::cout << pos << " ";
// }
// std::cout << "\n";
// Check Time Windows
for (int i = 0; i < newReachTimes.size(); ++i)
{
if (!data.getLocation(route.at(i)).getTimeWindow().isValid(newReachTimes.at(i)))
{
return false;
}
}
return true;
} }
// A lot of check here because i had problems with this function
void TimeWindowConstraint::ApplyModif(PDPTWData const &data, Pair const &pair, int routeIndex, int pickupPos, void TimeWindowConstraint::ApplyModif(PDPTWData const &data, Pair const &pair, int routeIndex, int pickupPos,
int deliveryPos, bool addPair) int deliveryPos, bool addPair)
{ {
if (addPair)
// Check the routeIndex validity
if (routeIndex < 0 || routeIndex >= getSolution().getRoutes().size())
{
spdlog::error("Error: routeIndex out of bounds ({})", routeIndex);
return;
}
// Copy of the route vector (problem that cause a duplication of the TW)
std::vector<int> routeIDs = getSolution().getRoute(routeIndex).getRoute();
// // Check Position validity
// if (pickupPos < 0 || pickupPos > routeIDs.size() ||
// deliveryPos < 0 || deliveryPos > routeIDs.size()) {
// spdlog::error("Error: Indices pickupPos ({}) or deliveryPos ({}) are invalid for a route size of {}.",
// pickupPos, deliveryPos, routeIDs.size());
// return;
// }
// if (addPair) {
// routeIDs.insert(routeIDs.begin() + deliveryPos, pair.getDelivery().getId());
// routeIDs.insert(routeIDs.begin() + pickupPos, pair.getPickup().getId());
// }
// else {
// if (deliveryPos < routeIDs.size() && pickupPos < routeIDs.size() && pickupPos != deliveryPos) {
// if (deliveryPos > pickupPos) {
// routeIDs.erase(routeIDs.begin() + deliveryPos);
// routeIDs.erase(routeIDs.begin() + pickupPos);
// } else {
// routeIDs.erase(routeIDs.begin() + pickupPos);
// routeIDs.erase(routeIDs.begin() + deliveryPos);
// }
// } else {
// spdlog::error("Error: Invalid indices for removal (pickupPos: {}, deliveryPos: {}).", pickupPos, deliveryPos);
// return;
// }
// }
// the route is empty !
if (routeIDs.empty())
{ {
allRouteReachTimes.at(routeIndex).clear(); FTSContainer.at(routeIndex)
.updateFTSAfterInsertion(data, getSolution().getRoute(routeIndex), pickupPos, deliveryPos);
} }
else else
{ {
allRouteReachTimes.at(routeIndex).resize(routeIDs.size(), 0); FTSContainer.at(routeIndex)
.updateFTSAfterDeletion(data, getSolution().getRoute(routeIndex), pickupPos, deliveryPos);
allRouteReachTimes.at(routeIndex).at(0) =
data.getDepot().getTimeWindow().getStart() + data::TravelTime(data, 0, routeIDs.at(0));
// Compute reach times
for (size_t i = 1; i < routeIDs.size(); ++i)
{
if (i - 1 >= routeIDs.size() || i >= routeIDs.size())
{
spdlog::error("Error: Out-of-bounds access to routeIDs in the computation loop.");
return;
}
TimeInteger travelTime = data::TravelTime(data, routeIDs.at(i - 1), routeIDs.at(i));
TimeInteger serviceTime = data.getLocation(routeIDs.at(i - 1)).getServiceDuration();
TimeInteger startTW = data.getLocation(routeIDs.at(i - 1)).getTimeWindow().getStart();
allRouteReachTimes.at(routeIndex).at(i) =
std::max(allRouteReachTimes.at(routeIndex).at(i - 1), startTW) + serviceTime + travelTime;
}
} }
} }
bool TimeWindowConstraint::check(InsertPair const &op) const bool TimeWindowConstraint::check(InsertPair const &op) const
{ {
//std::cout << " #TW Check";
return checkInsertion(getSolution().getData(), return checkInsertion(getSolution().getData(),
op.getPair(), op.getPair(),
op.getRouteIndex(), op.getRouteIndex(),
...@@ -189,7 +85,6 @@ bool TimeWindowConstraint::check(InsertPair const &op) const ...@@ -189,7 +85,6 @@ bool TimeWindowConstraint::check(InsertPair const &op) const
void TimeWindowConstraint::apply(InsertPair const &op) void TimeWindowConstraint::apply(InsertPair const &op)
{ {
//std::cout << "-> Apply Modification on Time Windows \n";
ApplyModif(getSolution().getData(), ApplyModif(getSolution().getData(),
op.getPair(), op.getPair(),
op.getRouteIndex(), op.getRouteIndex(),
...@@ -205,7 +100,7 @@ bool TimeWindowConstraint::check(InsertRoute const &op) const ...@@ -205,7 +100,7 @@ bool TimeWindowConstraint::check(InsertRoute const &op) const
void TimeWindowConstraint::apply(InsertRoute const &op) void TimeWindowConstraint::apply(InsertRoute const &op)
{ {
allRouteReachTimes.emplace_back(); FTSContainer.emplace_back();
} }
bool TimeWindowConstraint::check(RemovePair const &op) const bool TimeWindowConstraint::check(RemovePair const &op) const
...@@ -230,34 +125,18 @@ bool TimeWindowConstraint::check(RemoveRoute const &op) const ...@@ -230,34 +125,18 @@ bool TimeWindowConstraint::check(RemoveRoute const &op) const
void TimeWindowConstraint::apply(RemoveRoute const &op) void TimeWindowConstraint::apply(RemoveRoute const &op)
{ {
allRouteReachTimes.erase(allRouteReachTimes.begin() + op.getRouteIndex()); FTSContainer.erase(FTSContainer.begin() + op.getRouteIndex());
}
std::vector<TimeWindowConstraint::ReachTimeVector> const &TimeWindowConstraint::getallRouteReachTimes() const
{
return allRouteReachTimes;
} }
void TimeWindowConstraint::print() const void TimeWindowConstraint::print() const
{ {
std::cout << "Reach Times : \n"; std::cout << "Time Window : Earliest / Latest / FTS" << std::endl;
int i = 0; int i = 0;
for (auto const &reachTimes: getallRouteReachTimes()) for (ForwardTimeSlack FTS: FTSContainer)
{ {
//std::cout << reachTimes.size() << ", "; std::cout << "#" << i << std::endl;
std::cout << "#" << i << " : "; FTS.print();
for (const TimeInteger reachTime: reachTimes) std::cout << std::endl;
{
std::cout << reachTime << ", ";
}
std::cout << "\n";
std::cout << "#" << i << " : ";
for (int locID: getSolution().getRoute(i).getRoute())
{
std::cout << locID << ", ";
}
std::cout << "\n ";
i++; i++;
} }
std::cout << "\n";
} }
#pragma once #pragma once
#include "lns/constraints/constraint.h"
#include "input/pair.h" #include "input/pair.h"
#include "input/time_window.h" #include "input/time_window.h"
#include "lns/constraints/constraint.h"
#include "lns/constraints/time_window/forward_time_slack.h"
#include "lns/solution/route.h" #include "lns/solution/route.h"
#include <vector> #include <vector>
/** /**
...@@ -17,59 +19,44 @@ class TimeWindowConstraint : public Constraint ...@@ -17,59 +19,44 @@ class TimeWindowConstraint : public Constraint
public: public:
using ReachTimeVector = std::vector<TimeInteger>; using ReachTimeVector = std::vector<TimeInteger>;
explicit TimeWindowConstraint(Solution const &); explicit TimeWindowConstraint(Solution const &);
TimeWindowConstraint(TimeWindowConstraint const &) = default; TimeWindowConstraint(TimeWindowConstraint const &) = default;
~TimeWindowConstraint() override; ~TimeWindowConstraint() override;
// Given the solution, calculate the reachTimes of each location.
// Modify the routeCapacities vector ! /**
void initReachTimes(); * Initialize the FTS vectors
*/
void initFTS();
std::unique_ptr<Constraint> clone(Solution const &newOwningSolution) const override; std::unique_ptr<Constraint> clone(Solution const &newOwningSolution) const override;
const std::vector<ReachTimeVector> & getallRouteReachTimes() const;
void print() const override;
private:
/** void print() const override;
* For each route, store the reach time of each location
*/
std::vector<ReachTimeVector> allRouteReachTimes;
// Recalcul tous les reachTimes pour une route
ReachTimeVector computeReachTimes(const Route& route) const;
bool updateReachTimes(const PDPTWData& data, Pair & pair,int routeIndex, int pickupPos, int deliveryPos);
/** private:
* Check if the insertion of a pair pickup/delivery is valid or not. std::vector<ForwardTimeSlack> FTSContainer;
* COPY the route where we insert the pair !
*/
bool checkInsertion(const PDPTWData& data, const Pair & pair, int routeIndex, int pickupPos, int deliveryPos) const;
/** /**
* Given route IDs, compute the reachTimes of the route. * Check if the insertion of a pair pickup/delivery is valid or not.
* If a vehicule is in advance, set the reach time to start of the timeWindow. * COPY the route where we insert the pair !
* Modify reachTimes vector ! */
*/ bool checkInsertion(PDPTWData const &data, Pair const &pair, int routeIndex, int pickupPos, int deliveryPos) const;
void computeReachTimes(const PDPTWData& data, const std::vector<int> & routeIDs, ReachTimeVector & reachTimes) const;
/** /**
* Given route IDs, modify the reachTimes * Apply the modification to the time window constraint
*/ */
void ApplyModif(const PDPTWData& data, const Pair & pair, int routeIndex, int pickupPos, int deliveryPos, bool addPair); void ApplyModif(PDPTWData const &data, Pair const &pair, int routeIndex, int pickupPos, int deliveryPos,
bool addPair);
void apply(InsertPair const &op) override; void apply(InsertPair const &op) override;
void apply(InsertRoute const &op) override; void apply(InsertRoute const &op) override;
void apply(RemovePair const &op) override; void apply(RemovePair const &op) override;
void apply(RemoveRoute const &op) override; void apply(RemoveRoute const &op) override;
bool check(InsertPair const &op) const override; bool check(InsertPair const &op) const override;
bool check(InsertRoute const &op) const override; bool check(InsertRoute const &op) const override;
bool check(RemovePair const &op) const override; bool check(RemovePair const &op) const override;
bool check(RemoveRoute const &op) const override; bool check(RemoveRoute const &op) const override;
public:
}; };
\ No newline at end of file
...@@ -4,6 +4,8 @@ ...@@ -4,6 +4,8 @@
#include "lns/operators/selector/operator_selector.h" #include "lns/operators/selector/operator_selector.h"
#include "output/solution_checker.h" #include "output/solution_checker.h"
#include <chrono>
namespace namespace
{ {
bool isBetterSolution(Solution const &candidateSolution, Solution const &bestKnownSol) bool isBetterSolution(Solution const &candidateSolution, Solution const &bestKnownSol)
...@@ -11,22 +13,82 @@ namespace ...@@ -11,22 +13,82 @@ namespace
return candidateSolution.getCost() < bestKnownSol.getCost(); return candidateSolution.getCost() < bestKnownSol.getCost();
} }
using lns_clock = std::chrono::high_resolution_clock;
using lns_time_point = std::chrono::time_point<lns_clock, std::chrono::nanoseconds>;
struct LnsRuntimeData
{
Solution bestSolution;
unsigned int numberOfIteration = 0;
lns_time_point start = lns_clock::now();
};
/**
* @return the number of seconds between point and now (the moment the function is called).
*/
unsigned long getTimeSinceInSec(lns_time_point point)
{
return std::chrono::duration_cast<std::chrono::seconds>(lns_clock::now() - point).count();
}
unsigned long getTimeSinceInMs(lns_time_point point)
{
return std::chrono::duration_cast<std::chrono::milliseconds>(lns_clock::now() - point).count();
}
bool triggerLogProgress(LnsRuntimeData const &)
{
static auto lastTrigger = lns_clock::now();
if (getTimeSinceInSec(lastTrigger) >= 10)
{
lastTrigger = lns_clock::now();
return true;
}
return false;
}
void logProgress(LnsRuntimeData const &runtime, Solution const &actualSolution)
{
if (triggerLogProgress(runtime))
{
unsigned long actualTime = getTimeSinceInMs(runtime.start);
double iterPerSecond = 1000 * runtime.numberOfIteration / static_cast<double>(actualTime);
std::string speedLog;
if (iterPerSecond < 1)
{
speedLog = fmt::format("{}s/100 iterations", 100 / iterPerSecond);
}
else
{
speedLog = fmt::format("{:.1f} i/s", iterPerSecond);
}
std::string missingRequestLog;
long requestsMissing = runtime.bestSolution.missingPairCount();
//... log TO DO
}
}
}// namespace }// namespace
output::LnsOutput lns::runLns(Solution const &initialSolution, OperatorSelector &opSelector, output::LnsOutput lns::runLns(Solution const &initialSolution, OperatorSelector &opSelector,
AcceptanceFunction const &acceptFunctor) AcceptanceFunction const &acceptFunctor)
{ {
/** /**
* The solution is the base solution used to create the neighbor solution. * The solution is the base solution used to create the neighbor solution.
* It is constant unless we accept the candidate solution. * It is constant unless we accept the candidate solution.
*/ */
Solution actualSolution = initialSolution; Solution actualSolution = initialSolution;
Solution bestSolution = initialSolution; LnsRuntimeData runtime = {actualSolution};
// temporary fixed iteration
int iterationMax = 100;
while (iterationMax > 0)
{
// Init iteration
++runtime.numberOfIteration;
logProgress(runtime, actualSolution);
int it = 100; std::cout << "\n" << runtime.numberOfIteration << " : ";
while (it > 0)
{
std::cout << "\n" << 101-it << " : ";
/** /**
* The solution on which we apply the operators. * The solution on which we apply the operators.
* It is discarded at the end of each loop if it is not accepted by the Acceptance Function. * It is discarded at the end of each loop if it is not accepted by the Acceptance Function.
...@@ -38,26 +100,28 @@ output::LnsOutput lns::runLns(Solution const &initialSolution, OperatorSelector ...@@ -38,26 +100,28 @@ output::LnsOutput lns::runLns(Solution const &initialSolution, OperatorSelector
// Apply operators // Apply operators
destructReconstructPair.destructor().destroySolution(candidateSolution); destructReconstructPair.destructor().destroySolution(candidateSolution);
destructReconstructPair.reconstructor().reconstructSolution(candidateSolution, 0.01); destructReconstructPair.reconstructor().reconstructSolution(candidateSolution, 0.01);
candidateSolution.computeAndStoreSolutionCost();
// Update best solution // Update best solution
if (isBetterSolution(candidateSolution, bestSolution)) if (isBetterSolution(candidateSolution, runtime.bestSolution))
{ {
std::cout << "\n > new Best Solution \n"; std::cout << "\n > new Best Solution \n";
checker::checkAll(candidateSolution, candidateSolution.getData(), false); checker::checkAll(candidateSolution, candidateSolution.getData(), false);
bestSolution = candidateSolution; runtime.bestSolution = candidateSolution;
opSelector.betterSolutionFound(); opSelector.betterSolutionFound();
} }
// Check if we use the candidate solution as the new actual solution // Check if we use the candidate solution as the new actual solution
// operator can force to take the new solution // operator can force to take the new solution
if (destructReconstructPair.forceTakeSolution() || if (destructReconstructPair.forceTakeSolution() ||
acceptFunctor(candidateSolution, actualSolution, bestSolution) == AcceptationStatus::ACCEPT) acceptFunctor(candidateSolution, actualSolution, runtime.bestSolution) == AcceptationStatus::ACCEPT)
{ {
actualSolution = std::move(candidateSolution); actualSolution = std::move(candidateSolution);
} }
--it; --iterationMax;
} }
auto result = output::LnsOutput(std::move(bestSolution), it, it);
auto result = output::LnsOutput(
std::move(runtime.bestSolution), runtime.numberOfIteration, getTimeSinceInSec(runtime.start));
return result; return result;
} }
\ No newline at end of file
#include "remove_pair.h" #include "remove_pair.h"
#include "input/data.h" #include "input/data.h"
#include "lns/solution/solution.h"
RemovePair::RemovePair(int routeIndex, int pickupDeletion, int deliveryDeletion, Pair const &pair) RemovePair::RemovePair(int routeIndex, int pickupDeletion, int deliveryDeletion, Pair const &pair)
: routeIndex(routeIndex), pickupDeletion(pickupDeletion), deliveryDeletion(deliveryDeletion), : routeIndex(routeIndex), pickupDeletion(pickupDeletion), deliveryDeletion(deliveryDeletion),
......
...@@ -18,16 +18,19 @@ RemoveRoute::RemoveRoute(int routeIndex, std::vector<int> &&removedPairID) ...@@ -18,16 +18,19 @@ RemoveRoute::RemoveRoute(int routeIndex, std::vector<int> &&removedPairID)
void RemoveRoute::modifySolution(Solution &solution) void RemoveRoute::modifySolution(Solution &solution)
{ {
std::vector<Route> &routes = solution.getRoutes(); std::vector<Route> &routes = solution.getRoutes();
std::vector<int> const &locationIDs = routes.at(routeIndex).getRoute(); if (routes.at(routeIndex).getRoute().empty())
{
std::vector<int> const &locationIDs = routes.at(routeIndex).getRoute();
// update removedPairID // update removedPairID
removedPairID.reserve(routes.at(routeIndex).getSize()); removedPairID.reserve(routes.at(routeIndex).getSize());
for (int id : locationIDs) for (int id: locationIDs)
{
if (solution.getData().getLocation(id).getLocType() == LocType::PICKUP)
{ {
removedPairID.push_back(id); if (solution.getData().getLocation(id).getLocType() == LocType::PICKUP)
{
removedPairID.push_back(id);
}
} }
} }
routes.erase(routes.begin() + routeIndex); routes.erase(routes.begin() + routeIndex);
...@@ -46,4 +49,9 @@ int RemoveRoute::getRouteIndex() const ...@@ -46,4 +49,9 @@ int RemoveRoute::getRouteIndex() const
std::vector<int> const &RemoveRoute::getDeletedPairs() const std::vector<int> const &RemoveRoute::getDeletedPairs() const
{ {
return removedPairID; return removedPairID;
}
ModificationApplyVariant RemoveRoute::asApplyVariant() const
{
return *this;
} }
\ No newline at end of file
...@@ -36,5 +36,8 @@ public: ...@@ -36,5 +36,8 @@ public:
*/ */
std::vector<int> const &getDeletedPairs() const override; std::vector<int> const &getDeletedPairs() const override;
ModificationApplyVariant asApplyVariant() const override;
int getRouteIndex() const; int getRouteIndex() const;
}; };
\ No newline at end of file
#include "clean_empty_route.h"
#include "lns/modification/route/remove_route.h"
CleanEmptyRoute::CleanEmptyRoute() {}
void CleanEmptyRoute::destroySolution(Solution &solution) const
{
std::cout << "Clean ";
for (int routeIndex = solution.getRoutes().size() - 1; routeIndex >= 0; --routeIndex)
{
if (solution.getRoute(routeIndex).getRoute().empty())
{
RemoveRoute remRoute = RemoveRoute(routeIndex);
solution.applyDestructSolution(remRoute);
}
}
}
\ No newline at end of file
#pragma once
#include "lns/operators/abstract_operator.h"
class CleanEmptyRoute : public DestructionOperator
{
public:
explicit CleanEmptyRoute();
/**
* This operator removes empty routes from the solution.
*/
void destroySolution(Solution &solution) const override;
};
...@@ -18,7 +18,7 @@ void RandomDestroy::destroySolution(Solution &solution) const ...@@ -18,7 +18,7 @@ void RandomDestroy::destroySolution(Solution &solution) const
while (remainingPairToDelete > 0) while (remainingPairToDelete > 0)
{ {
// too complicated // too complicated
// Other (simpler) option -> choose random route then choose random pair ? // Other (simpler) option -> choose random route then choose random pair ? (does not respect equiprobability)
// choose a random location // choose a random location
int locationNumber = util::getRandomInt(0, remainingPairToDelete * 2 - 1); int locationNumber = util::getRandomInt(0, remainingPairToDelete * 2 - 1);
......
#include "string_removal.h"
#include "input/location.h"
#include "input/pair.h"
#include "input/pdptw_data.h"
#include "lns/modification/pair/remove_pair.h"
#include "utils.h"
#include <vector>
namespace
{
double computeAverageCardinality(std::vector<Route> const &routes)
{
if (routes.empty())
{
return 0.0;
}
return std::accumulate(routes.begin(),
routes.end(),
0.0,
[](double sum, Route const &route) { return sum + route.getSize(); }) /
routes.size();
}
/**
* Remove a string in the solution
* For now it uses startLocation as the center of the string
* @param routeIndex the path on which the removal happens
* @param stringLength the number of requests to remove, expect it to be inferior to the number of requests in the path
* @param startLocation the location found to start the string, does not mean it will the smallest index removed
*/
void removeString(Solution &solution, int routeIndex, unsigned int stringLength, int startLocation)
{
// ID of pair to remove
std::vector<int> toRemove;
toRemove.reserve(stringLength);
PDPTWData const &data = solution.getData();
Route const &route = solution.getRoute(routeIndex);
std::vector<int> const &routeLocationIDs = route.getRoute();
// Position in the route of the first location
int startPosition = route.getIndex(startLocation);
// the location is not in a route
if (startPosition == -1)
{
return;
}
int left = std::max(0, startPosition - ((int) stringLength / 2));
int right = left + (int) stringLength - 1;
// we don't want to go out of bound
if (right >= routeLocationIDs.size())
{
right = routeLocationIDs.size() - 1;
left = std::max(0, right - (int) stringLength + 1);// keep a number of stringLength elements
}
// collect each locationID and store the pairID in the toRemove vector
for (int i = left; i <= right; ++i)
{
// pickupID = pairID
// if pickup and not already in the list
if (data.getLocation(routeLocationIDs.at(i)).getLocType() == LocType::PICKUP &&
std::find(toRemove.begin(), toRemove.end(), routeLocationIDs.at(i)) == toRemove.end())
{
toRemove.emplace_back(routeLocationIDs.at(i));
}
// or if delivery and pairID not already in the list
else if (data.getLocation(routeLocationIDs.at(i)).getLocType() == LocType::DELIVERY &&
std::find(toRemove.begin(), toRemove.end(), data.getLocation(routeLocationIDs.at(i)).getPair()) ==
toRemove.end())
{
toRemove.emplace_back(data.getLocation(routeLocationIDs.at(i)).getPair());
}
}
//remove the collected pairID
for (int pairID: toRemove)
{
// get index and Pair
int position = solution.getRoute(routeIndex).getIndex(pairID);
Index index = std::make_tuple(routeIndex, position, route.getPairLocationPosition(position, data));
Pair const &pair = data.getPair(pairID);
// apply remove
RemovePair removePair(index, pair);
solution.applyDestructSolution(removePair);
}
}
// Procedure RUIN(s, Lmax, c^bar)
// 1. Compute lmax_s, kmax_s, k_s according to equations (5), (6), and (7)
// 2. cseed_s ← RandomClient(s)
// 3. R ← ∅ // Set of affected tours
// 4. For each client c in Adjacent(cseed_s) Do
// 5. If c ∉ A and |R| < k_s Then
// 6. c*_t ← c
// 7. Compute lmax_t, lt according to equations (8) and (9)
// 8. A ← A ∪ RemoveClient(T, lt, c*_t)
// 9. R ← R ∪ {t}
// 10. End If
// 11. End For
// End Procedure
/**
* Ruin procedure from the SISR paper.
* c^bar = averageNumberRemovedElement.
* L^max = maxStringSize.
*/
void SISRsRuin(Solution &solution, unsigned int maxStringSize, unsigned int averageNumberRemovedElement)
{
// |t∈T| average number of location by route
auto averageRouteCardinality = static_cast<unsigned int>(computeAverageCardinality(solution.getRoutes()));
unsigned int maxSizeOfString = std::min(maxStringSize, averageRouteCardinality); // (5) lmax_s
unsigned int maxNumberOfString = (4 * averageNumberRemovedElement) / (1 + maxSizeOfString) - 1;// (6) kmax_s
unsigned int numberOfString = util::getRandomInt(1, maxNumberOfString + 1); // (7) k_s
int locationSeed = util::getRandomInt(1, solution.getData().getLocationCount());
// store the index of routes where a string was removed
std::vector<int> routeIndexUsed;
routeIndexUsed.reserve(numberOfString);
// getClosestLocationsID returns the list of all location sorted from the closest to furthest
for (int neighbor: solution.getData().getClosestLocationsID(locationSeed))
{
// recover the routeIndex associated to the locationID, returns -1 if in the bank
int routeIndex = solution.getRouteIDOf(neighbor);
if (routeIndex != -1 && std::ranges::find(routeIndexUsed, routeIndex) == routeIndexUsed.end())
{
// (8) lmax_t
unsigned int maxSizeOfThisString =
std::min(static_cast<int>(maxSizeOfString), solution.getRoute(routeIndex).getSize());
// (9) l_t
unsigned int actualSizeOfThisString = util::getRandomInt(1, maxSizeOfThisString);
removeString(solution, routeIndex, actualSizeOfThisString, neighbor);
routeIndexUsed.emplace_back(routeIndex);
if (routeIndexUsed.size() >= numberOfString)
{
break;
}
}
}
}
}// namespace
void StringRemoval::destroySolution(Solution &solution) const
{
std::cout << "SISR ";
SISRsRuin(solution, maxCardinalityOfString, averageNumberRemovedElement);
}
#pragma once
#include "lns/operators/abstract_operator.h"
class StringRemoval : public DestructionOperator
{
public:
explicit StringRemoval(unsigned int maxCardinalityOfString, unsigned int averageNumberRemovedElement)
: maxCardinalityOfString(maxCardinalityOfString), averageNumberRemovedElement(averageNumberRemovedElement)
{}
// Each string has a max number of requests: MAX_CARDINALITY_OF_STRING
// L_max in the paper.
unsigned int maxCardinalityOfString = 10;
// This operator will remove in average AVERAGE_CUSTOMER_REMOVAL of requests
// c with a bar on top in the paper
unsigned int averageNumberRemovedElement = 10;
/**
* This operator removes numberOfPairsToDestroy pairs randomly in the solution.
*/
void destroySolution(Solution &solution) const override;
private:
int numberOfPairsToDestroy;
};