initialCommit 4 ans en retard
This commit is contained in:
@@ -0,0 +1,506 @@
|
||||
#include "DynamicProgramming.hpp"
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <random>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include "Random.hpp"
|
||||
#include "SimpleGraphManager.hpp"
|
||||
#include "Solution.hpp"
|
||||
#include "sourceSolTrPlan.hpp"
|
||||
namespace solverlib {
|
||||
void DynamicProgramming::buildGraph()
|
||||
{
|
||||
loggerlib::Logger::systemNotify(loggerlib::LOGGER_PROGRESS, "Building graph");
|
||||
|
||||
graphManager = std::make_shared<SimpleGraphManager>(STFMockInstance::jobs.size());
|
||||
std::unordered_map<unsigned int, std::unordered_set<unsigned int>> pairsTreated;
|
||||
|
||||
// Lambda réutilisable : tente d'insérer `insertedJob` à la place de `removedJob`
|
||||
// Retourne {faisable, gain}
|
||||
auto tryInsert = [&](unsigned int insertedJob, unsigned int removedJob) -> std::optional<EdgeSolution>
|
||||
{
|
||||
|
||||
if(solutionPool[0].decisions.find(insertedJob) == solutionPool[0].decisions.end() || solutionPool[0].decisions.find(removedJob) == solutionPool[0].decisions.end())
|
||||
return std::nullopt;
|
||||
|
||||
if(insertedJob >= solutionPool[0].mock->jobDispoVoiesRames.size() || removedJob >= solutionPool[0].mock->jobDispoVoiesRames.size())
|
||||
return std::nullopt;
|
||||
|
||||
const auto& decInserted = solutionPool[0].decisions[insertedJob];
|
||||
const auto& decRemoved = solutionPool[0].decisions[removedJob];
|
||||
std::optional<EdgeSolution> gainSuccess;
|
||||
auto dispFound = std::find_if(
|
||||
solutionPool[0].mock->jobDispoVoiesRames[insertedJob].begin(),
|
||||
solutionPool[0].mock->jobDispoVoiesRames[insertedJob].end(),
|
||||
[&](auto& el) -> bool
|
||||
{
|
||||
DispVoieRame& disp = solutionPool[0].mock->dispoVoiesRames[el];
|
||||
if(disp.dispoVoie != decRemoved.empV)
|
||||
return false;
|
||||
|
||||
auto TrStop = solutionPool[0].mock->trajectoryStops[disp.dispoRame].getDispoStop();
|
||||
auto match = CreneauHoraire::checkSlotsCompatibility(
|
||||
TrStop,
|
||||
STFMockInstance::machines[decRemoved.empV]->getDispo());
|
||||
if(!match.first)
|
||||
return false;
|
||||
|
||||
CreneauHoraire crenInserted(SolverDate::getDateDebut() + disp.match.first,
|
||||
SolverDate::getDateDebut() + disp.match.second);
|
||||
CreneauHoraire crenRemoved (SolverDate::getDateDebut() + decRemoved.lastCreneau.first,
|
||||
SolverDate::getDateDebut() + decRemoved.lastCreneau.second);
|
||||
|
||||
if(!CreneauHoraire::checkSlotsCompatibility(crenInserted, crenRemoved).first)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
if(dispFound != solutionPool[0].mock->jobDispoVoiesRames[insertedJob].end())
|
||||
{
|
||||
// Construire le planning de la voie cible :
|
||||
// - sans removedJob
|
||||
// - avec insertedJob placé dans le créneau de removedJob
|
||||
std::vector<std::pair<unsigned short, Decision>> schedule;
|
||||
long oldCost = 0;
|
||||
auto disp = solutionPool[0].mock->dispoVoiesRames[*dispFound];
|
||||
std::unordered_map<unsigned short, Decision> oldDec;
|
||||
|
||||
for(auto& [id, dec] : solutionPool[0].decisions)
|
||||
{
|
||||
if(dec.excluded) continue;
|
||||
if(dec.empV != decRemoved.empV) continue;
|
||||
|
||||
oldCost += STFMockInstance::jobs[id]->getPoidsRetard() * dec.lastCreneau.first;
|
||||
oldDec[id] = dec;
|
||||
if(id == removedJob)
|
||||
{
|
||||
// Remplacer removedJob par insertedJob avec le même créneau
|
||||
Decision decInsertedCopy = decInserted;
|
||||
decInsertedCopy.lastCreneau = decRemoved.lastCreneau;
|
||||
decInsertedCopy.empV = decRemoved.empV;
|
||||
decInsertedCopy.empR = disp.dispoRame;
|
||||
decInsertedCopy.voie = decRemoved.voie;
|
||||
decInsertedCopy.site = decRemoved.site;
|
||||
decInsertedCopy.timeslotGraphSplited = solutionPool[0].mock->trajectoryStops[disp.dispoRame].getDispoStop();
|
||||
|
||||
schedule.emplace_back(insertedJob, decInsertedCopy);
|
||||
}
|
||||
else
|
||||
{
|
||||
schedule.emplace_back(id, dec);
|
||||
}
|
||||
}
|
||||
|
||||
std::sort(schedule.begin(), schedule.end(), [](auto& a, auto& b){
|
||||
return a.second.lastCreneau.first < b.second.lastCreneau.first;
|
||||
});
|
||||
|
||||
unsigned short lastEnd = STFMockInstance::machines[decRemoved.empV]
|
||||
->getDispo().getDebut().getRelativeDate();
|
||||
long newCost = 0;
|
||||
std::unordered_map<unsigned short, Decision> newSchedule;
|
||||
for(auto& [id, dec] : schedule)
|
||||
{
|
||||
auto TrStop = solutionPool[0].mock->trajectoryStops[dec.empR].getDispoStop();
|
||||
auto match = CreneauHoraire::checkSlotsCompatibility(
|
||||
TrStop,
|
||||
STFMockInstance::machines[dec.empV]->getDispo()).second;
|
||||
auto duration = !dec.rejected * STFMockInstance::jobs[id]->getDuree()
|
||||
+ dec.rejected * STFMockInstance::jobs[id]->getDureeDiag();
|
||||
|
||||
newSchedule[id] = dec;
|
||||
newSchedule[id].lastCreneau.first = std::max(lastEnd, match.first);
|
||||
|
||||
lastEnd = newSchedule[id].lastCreneau.first + duration;
|
||||
|
||||
newSchedule[id].lastCreneau.second = lastEnd;
|
||||
if(lastEnd > match.first + match.second)
|
||||
return std::nullopt; // jobs poussés à droite non faisables
|
||||
|
||||
std::string ma = "insertedJob=" + std::to_string(insertedJob) + " removedJob=" + std::to_string(removedJob) + " Match[" + std::to_string(match.first) + " " + std::to_string(match.first + match.second) + "]";
|
||||
std::string fi = " - Final[" + std::to_string(newSchedule[id].lastCreneau.first) + " " + std::to_string(newSchedule[id].lastCreneau.second)+ "]";
|
||||
|
||||
loggerlib::Logger::systemNotify(loggerlib::LOGGER_DEBUG, ma + fi);
|
||||
|
||||
newCost += STFMockInstance::jobs[id]->getPoidsRetard()
|
||||
* std::max(lastEnd, match.first);
|
||||
}
|
||||
|
||||
return EdgeSolution{static_cast<long>(((oldCost + decInserted.lastCreneau.first * STFMockInstance::jobs[insertedJob]->getPoidsRetard()) - newCost)), newSchedule, oldDec};
|
||||
}
|
||||
return std::nullopt;
|
||||
};
|
||||
|
||||
for(unsigned int job = 0; job < STFMockInstance::jobs.size(); ++job)
|
||||
{
|
||||
for(unsigned int other_job = 0; other_job < STFMockInstance::jobs.size(); ++other_job)
|
||||
{
|
||||
if(other_job == job || pairsTreated[job].count(other_job) || pairsTreated[other_job].count(job))
|
||||
continue;
|
||||
|
||||
pairsTreated[job].insert(other_job);
|
||||
pairsTreated[other_job].insert(job);
|
||||
auto& decJob = solutionPool[0].decisions[job];
|
||||
auto& decOtherJob = solutionPool[0].decisions[other_job];
|
||||
|
||||
if(decJob.excluded || decOtherJob.excluded || decJob.empV == decOtherJob.empV)
|
||||
continue;
|
||||
|
||||
// Arc job → other_job : insérer job à la place de other_job
|
||||
auto edgeSol = tryInsert(job, other_job);
|
||||
if(edgeSol)
|
||||
{
|
||||
// ADD edge job -> other_job avec gain1
|
||||
auto addedEdge = graphManager->addEdge(job, other_job, edgeSol->gain);
|
||||
auto index = graphManager->getEdgeIndex(addedEdge);
|
||||
edgesData[index] = edgeSol.value();
|
||||
}
|
||||
|
||||
// Arc other_job → job : insérer other_job à la place de job
|
||||
auto edgeSol2 = tryInsert(other_job, job);
|
||||
if(edgeSol2)
|
||||
{
|
||||
// ADD edge other_job -> job avec gain2
|
||||
auto addedEdge = graphManager->addEdge(other_job, job, edgeSol2->gain);
|
||||
auto index = graphManager->getEdgeIndex(addedEdge);
|
||||
edgesData[index] = edgeSol2.value();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<unsigned int, unsigned int> DynamicProgramming::evaluate(const std::unordered_map<unsigned short, Decision>& decs)
|
||||
{
|
||||
unsigned int cost = 0;
|
||||
unsigned int diagCost = 0;
|
||||
for(auto& dec : decs)
|
||||
{
|
||||
if(dec.second.excluded)
|
||||
{
|
||||
cost += MAXIMUM_TIME_OFFSET * modellib::STFMockInstance::jobs[dec.first]->getPoidsRetard();
|
||||
}
|
||||
else
|
||||
cost += modellib::STFMockInstance::jobs[dec.first]->getPoidsRetard() * dec.second.lastCreneau.first;
|
||||
|
||||
if(dec.second.rejected)
|
||||
{
|
||||
diagCost += modellib::STFMockInstance::jobs[dec.first]->getPoidsRejet();
|
||||
}
|
||||
}
|
||||
return {cost, diagCost};
|
||||
}
|
||||
|
||||
SASolution DynamicProgramming::solve()
|
||||
{
|
||||
buildGraph();
|
||||
auto bestSol = solutionPool[0];
|
||||
if(!mode)
|
||||
{
|
||||
for(auto& cycle : {3,4, 5, 6})
|
||||
{
|
||||
std::vector<unsigned int> weights(STFMockInstance::jobs.size(), 1);
|
||||
std::discrete_distribution<unsigned int> jobDist(weights.begin(), weights.end());
|
||||
for(unsigned int i = 0; i < weights.size()*0.33; i++)
|
||||
{
|
||||
auto startingNode = jobDist(randomEngine);
|
||||
weights[startingNode] = 0;
|
||||
jobDist = std::discrete_distribution<unsigned int>(weights.begin(), weights.end());
|
||||
std::vector<bool> states;
|
||||
std::vector<std::pair<simple_edge_descriptor,unsigned int>> currentPath;
|
||||
std::vector<std::vector<std::pair<simple_edge_descriptor,unsigned int>>> allPaths;
|
||||
auto res = recursion(startingNode, startingNode, cycle, states, currentPath, allPaths);
|
||||
|
||||
unsigned int nbfeas = 0;
|
||||
for(auto& path : allPaths)
|
||||
{
|
||||
SASolution solution = solutionPool[0];
|
||||
bool pathFeasible = true;
|
||||
|
||||
std::unordered_map<unsigned int, std::unordered_map<unsigned short, Decision>> newlyCreatedDecisionsFromCycle;
|
||||
for(auto& edgeSel : path)
|
||||
{
|
||||
auto index = graphManager->getEdgeIndex(edgeSel.first);
|
||||
|
||||
unsigned int insertedJob = graphManager->getEdgeSource(edgeSel.first);
|
||||
unsigned int removedJob = edgeSel.second;
|
||||
|
||||
auto& decInserted = solution.decisions[insertedJob];
|
||||
auto& decRemoved = solution.decisions[removedJob];
|
||||
|
||||
for(auto& [id, dec] : solution.decisions)
|
||||
{
|
||||
if(dec.excluded || dec.empV != decRemoved.empV) continue;
|
||||
if(id == removedJob)
|
||||
{
|
||||
Decision decInsertedCopy = decInserted;
|
||||
decInsertedCopy.lastCreneau = decRemoved.lastCreneau;
|
||||
decInsertedCopy.empV = decRemoved.empV;
|
||||
decInsertedCopy.empR = edgesData[index].updatedDecisions[insertedJob].empR;
|
||||
decInsertedCopy.voie = decRemoved.voie;
|
||||
decInsertedCopy.site = decRemoved.site;
|
||||
decInsertedCopy.timeslotGraphSplited = solution.mock->trajectoryStops[decInsertedCopy.empR].getDispoStop();
|
||||
|
||||
newlyCreatedDecisionsFromCycle[decRemoved.empV].insert(std::make_pair(insertedJob, decInsertedCopy));
|
||||
}
|
||||
else
|
||||
{
|
||||
newlyCreatedDecisionsFromCycle[decRemoved.empV].insert(std::make_pair(id, dec));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(auto& newSchedule : newlyCreatedDecisionsFromCycle)
|
||||
{
|
||||
std::vector<std::pair<unsigned short, Decision>> schedule;
|
||||
|
||||
for(auto& [id, dec] : newSchedule.second)
|
||||
{
|
||||
schedule.emplace_back(id, dec);
|
||||
}
|
||||
|
||||
std::sort(schedule.begin(), schedule.end(), [](auto& a, auto& b){
|
||||
return a.second.lastCreneau.first < b.second.lastCreneau.first;
|
||||
});
|
||||
|
||||
unsigned short lastEnd = STFMockInstance::machines[newSchedule.first]
|
||||
->getDispo().getDebut().getRelativeDate();
|
||||
bool stepFeasible = true;
|
||||
|
||||
for(auto& [id, dec] : schedule)
|
||||
{
|
||||
auto TrStop = solution.mock->trajectoryStops[dec.empR].getDispoStop();
|
||||
auto match = CreneauHoraire::checkSlotsCompatibility(
|
||||
TrStop,
|
||||
STFMockInstance::machines[dec.empV]->getDispo()).second;
|
||||
auto duration = !dec.rejected * STFMockInstance::jobs[id]->getDuree()
|
||||
+ dec.rejected * STFMockInstance::jobs[id]->getDureeDiag();
|
||||
|
||||
dec.lastCreneau.first = std::max(lastEnd, match.first);
|
||||
lastEnd = dec.lastCreneau.first + duration;
|
||||
dec.lastCreneau.second = lastEnd;
|
||||
|
||||
if(lastEnd > match.first + match.second)
|
||||
{
|
||||
stepFeasible = false;
|
||||
break;
|
||||
}
|
||||
|
||||
// Mettre à jour la solution courante immédiatement
|
||||
solution.decisions.insert_or_assign(id, dec);
|
||||
}
|
||||
|
||||
if(!stepFeasible)
|
||||
{
|
||||
pathFeasible = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(pathFeasible)
|
||||
{
|
||||
auto costs= evaluate(solution.decisions);
|
||||
solution.cost = costs.first;
|
||||
solution.diagCost = costs.second;
|
||||
nbfeas++;
|
||||
solution.source = ESourceTrackPlan::DyProg;
|
||||
if(saveSols)
|
||||
solutionPool.push_back(solution);
|
||||
if(solution.cost < bestSol.cost)
|
||||
{
|
||||
bestSol = solution;
|
||||
}
|
||||
}
|
||||
}
|
||||
//if(!allPaths.empty())
|
||||
//loggerlib::Logger::systemNotify(loggerlib::LOGGER_PROGRESS, std::to_string(nbfeas) + " turned out feasible ");
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
std::uniform_int_distribution<unsigned int> jobDist(0, STFMockInstance::jobs.size()-1);
|
||||
for(auto& cycle : {3,4, 5, 6})
|
||||
{
|
||||
auto startingNode = jobDist(randomEngine);
|
||||
std::vector<bool> states;
|
||||
std::vector<std::pair<simple_edge_descriptor,unsigned int>> currentPath;
|
||||
std::vector<std::vector<std::pair<simple_edge_descriptor,unsigned int>>> allPaths;
|
||||
auto res = recursion(startingNode, startingNode, cycle, states, currentPath, allPaths);
|
||||
|
||||
unsigned int nbfeas = 0;
|
||||
for(auto& path : allPaths)
|
||||
{
|
||||
SASolution solution = solutionPool[0];
|
||||
bool pathFeasible = true;
|
||||
|
||||
std::unordered_map<unsigned int, std::unordered_map<unsigned short, Decision>> newlyCreatedDecisionsFromCycle;
|
||||
for(auto& edgeSel : path)
|
||||
{
|
||||
auto index = graphManager->getEdgeIndex(edgeSel.first);
|
||||
|
||||
unsigned int insertedJob = graphManager->getEdgeSource(edgeSel.first);
|
||||
unsigned int removedJob = edgeSel.second;
|
||||
|
||||
auto& decInserted = solution.decisions[insertedJob];
|
||||
auto& decRemoved = solution.decisions[removedJob];
|
||||
|
||||
for(auto& [id, dec] : solution.decisions)
|
||||
{
|
||||
if(dec.excluded || dec.empV != decRemoved.empV) continue;
|
||||
if(id == removedJob)
|
||||
{
|
||||
Decision decInsertedCopy = decInserted;
|
||||
decInsertedCopy.lastCreneau = decRemoved.lastCreneau;
|
||||
decInsertedCopy.empV = decRemoved.empV;
|
||||
decInsertedCopy.empR = edgesData[index].updatedDecisions[insertedJob].empR;
|
||||
decInsertedCopy.voie = decRemoved.voie;
|
||||
decInsertedCopy.site = decRemoved.site;
|
||||
decInsertedCopy.timeslotGraphSplited = solution.mock->trajectoryStops[decInsertedCopy.empR].getDispoStop();
|
||||
|
||||
newlyCreatedDecisionsFromCycle[decRemoved.empV].insert(std::make_pair(insertedJob, decInsertedCopy));
|
||||
}
|
||||
else
|
||||
{
|
||||
newlyCreatedDecisionsFromCycle[decRemoved.empV].insert(std::make_pair(id, dec));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(auto& newSchedule : newlyCreatedDecisionsFromCycle)
|
||||
{
|
||||
std::vector<std::pair<unsigned short, Decision>> schedule;
|
||||
|
||||
for(auto& [id, dec] : newSchedule.second)
|
||||
{
|
||||
schedule.emplace_back(id, dec);
|
||||
}
|
||||
|
||||
std::sort(schedule.begin(), schedule.end(), [](auto& a, auto& b){
|
||||
return a.second.lastCreneau.first < b.second.lastCreneau.first;
|
||||
});
|
||||
|
||||
unsigned short lastEnd = STFMockInstance::machines[newSchedule.first]
|
||||
->getDispo().getDebut().getRelativeDate();
|
||||
bool stepFeasible = true;
|
||||
|
||||
for(auto& [id, dec] : schedule)
|
||||
{
|
||||
auto TrStop = solution.mock->trajectoryStops[dec.empR].getDispoStop();
|
||||
auto match = CreneauHoraire::checkSlotsCompatibility(
|
||||
TrStop,
|
||||
STFMockInstance::machines[dec.empV]->getDispo()).second;
|
||||
auto duration = !dec.rejected * STFMockInstance::jobs[id]->getDuree()
|
||||
+ dec.rejected * STFMockInstance::jobs[id]->getDureeDiag();
|
||||
|
||||
dec.lastCreneau.first = std::max(lastEnd, match.first);
|
||||
lastEnd = dec.lastCreneau.first + duration;
|
||||
dec.lastCreneau.second = lastEnd;
|
||||
|
||||
if(lastEnd > match.first + match.second)
|
||||
{
|
||||
stepFeasible = false;
|
||||
break;
|
||||
}
|
||||
|
||||
// Mettre à jour la solution courante immédiatement
|
||||
solution.decisions.insert_or_assign(id, dec);
|
||||
}
|
||||
|
||||
if(!stepFeasible)
|
||||
{
|
||||
pathFeasible = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(pathFeasible)
|
||||
{
|
||||
auto costs= evaluate(solution.decisions);
|
||||
solution.cost = costs.first;
|
||||
solution.diagCost = costs.second;
|
||||
nbfeas++;
|
||||
solution.source = ESourceTrackPlan::DyProg;
|
||||
if(saveSols)
|
||||
solutionPool.push_back(solution);
|
||||
|
||||
if(solution.cost < bestSol.cost)
|
||||
{
|
||||
bestSol = solution;
|
||||
}
|
||||
}
|
||||
}
|
||||
//if(!allPaths.empty())
|
||||
//loggerlib::Logger::systemNotify(loggerlib::LOGGER_PROGRESS, std::to_string(nbfeas) + " turned out feasible ");
|
||||
|
||||
}
|
||||
}
|
||||
return bestSol;
|
||||
}
|
||||
|
||||
long DynamicProgramming::recursion(unsigned int startingNode, unsigned int node, unsigned int cycleLength, std::vector<bool>& states, std::vector<std::pair<simple_edge_descriptor,unsigned int>>& currentPath, std::vector<std::vector<std::pair<simple_edge_descriptor,unsigned int>>>& allPaths)
|
||||
{
|
||||
if(node == startingNode && cycleLength == 0 && !currentPath.empty())
|
||||
{
|
||||
allPaths.push_back(currentPath);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(cycleLength == 0)
|
||||
{
|
||||
return MINUS_INFINITY;
|
||||
}
|
||||
|
||||
unsigned int visitCount = std::count_if(
|
||||
currentPath.begin(), currentPath.end(),
|
||||
[&](auto& el){ return el.second == node; }
|
||||
);
|
||||
if(visitCount > 1)
|
||||
return MINUS_INFINITY;
|
||||
|
||||
auto& decNode = solutionPool[0].decisions[node];
|
||||
|
||||
/*bool voieAlreadyUsed = std::any_of(
|
||||
currentPath.begin(), currentPath.end(),
|
||||
[&](auto& el){
|
||||
unsigned int prevRemoved = graphManager->getEdgeTarget(el.first);
|
||||
unsigned int prevInserted = graphManager->getEdgeSource(el.first);
|
||||
if(prevInserted == startingNode) return false;
|
||||
// la voie destination du job précédemment inséré
|
||||
return solutionPool[0].decisions[prevRemoved].empV == decNode.empV;
|
||||
}
|
||||
);
|
||||
if(voieAlreadyUsed)
|
||||
return MINUS_INFINITY;*/
|
||||
|
||||
simple_out_edge_iterator edgeIterator, edgesEnd;
|
||||
std::pair<simple_out_edge_iterator, simple_out_edge_iterator> outEdges = graphManager->getOutEdges(node);
|
||||
std::vector<long> results;
|
||||
for (SimpleGraphManager::map(edgeIterator, edgesEnd) = outEdges; edgeIterator != edgesEnd; edgeIterator++)
|
||||
{
|
||||
auto outVert = graphManager->getEdgeTarget(*edgeIterator);
|
||||
currentPath.push_back(std::make_pair(*edgeIterator,outVert));
|
||||
|
||||
results.push_back(recursion(startingNode, outVert, cycleLength-1, states, currentPath, allPaths) + graphManager->getWeights()[*edgeIterator]);
|
||||
|
||||
currentPath.pop_back();
|
||||
}
|
||||
if (results.empty())
|
||||
return MINUS_INFINITY;
|
||||
return *std::max_element(results.begin(), results.end());
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
#ifndef DYNAMICPROGRAMMING_HPP
|
||||
#define DYNAMICPROGRAMMING_HPP
|
||||
|
||||
#include <limits>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "Random.hpp"
|
||||
#include "Solution.hpp"
|
||||
#include "SimpleGraphManager.hpp"
|
||||
|
||||
#define MINUS_INFINITY std::numeric_limits<long>::min();
|
||||
|
||||
namespace solverlib {
|
||||
class DynamicProgramming
|
||||
{
|
||||
private:
|
||||
struct EdgeSolution {
|
||||
long gain;
|
||||
std::unordered_map<unsigned short, Decision> updatedDecisions;
|
||||
std::unordered_map<unsigned short, Decision> oldDecisions;
|
||||
};
|
||||
|
||||
std::vector<SASolution> solutionPool;
|
||||
std::shared_ptr<SimpleGraphManager> graphManager = nullptr;
|
||||
std::unordered_map<unsigned long, EdgeSolution> edgesData;
|
||||
std::mt19937 randomEngine;
|
||||
|
||||
public:
|
||||
|
||||
|
||||
bool mode = false;
|
||||
bool saveSols = true;
|
||||
DynamicProgramming(const SASolution& sol){
|
||||
randomEngine = random::makeEngine();
|
||||
solutionPool.push_back(sol);
|
||||
};
|
||||
void buildGraph();
|
||||
long recursion(unsigned int startingNode, unsigned int node, unsigned int cycleLength, std::vector<bool>& states, std::vector<std::pair<simple_edge_descriptor,unsigned int>>& currentPath, std::vector<std::vector<std::pair<simple_edge_descriptor,unsigned int>>>& allPaths);
|
||||
SASolution solve();
|
||||
std::pair<unsigned int, unsigned int> evaluate(const std::unordered_map<unsigned short, Decision>& decs);
|
||||
|
||||
|
||||
std::vector<SASolution>&& getSolutionPool(){return std::move(solutionPool);};
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,609 @@
|
||||
#include "ILPTrackPlans.hpp"
|
||||
#include "TrackPlan.hpp"
|
||||
#include "gurobi_c++.h"
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include "../../../General/Model/STFMockInstance.hpp"
|
||||
#include "gurobi_c.h"
|
||||
|
||||
namespace solverlib {
|
||||
using namespace modellib;
|
||||
|
||||
ILPTrackPlans::ILPTrackPlans(std::vector<TrackPlan>& plans): m_env(true), trackPlans(plans)
|
||||
{
|
||||
init();
|
||||
}
|
||||
|
||||
void ILPTrackPlans::init()
|
||||
{
|
||||
loggerlib::Logger::systemNotify(loggerlib::LOGGER_PROGRESS, "Initializing Gurobi env");
|
||||
|
||||
//m_env.set("LogFile", "ILP.log");
|
||||
//m_env.set(GRB_IntParam_ThreadLimit, 1);
|
||||
m_env.start();
|
||||
m_model = std::make_unique<GRBModel>(m_env);
|
||||
m_model->set(GRB_DoubleParam_TuneTimeLimit, 3600);
|
||||
m_model->set(GRB_DoubleParam_TimeLimit, 600);
|
||||
|
||||
/*
|
||||
MIPFocus 2
|
||||
OBBT 1
|
||||
Cuts 0
|
||||
PrePasses 2
|
||||
*/
|
||||
m_model->set(GRB_IntParam_MIPFocus, 2);
|
||||
m_model->set(GRB_IntParam_Cuts, 0);
|
||||
m_model->set(GRB_IntParam_PrePasses, 2);
|
||||
m_model->set(GRB_IntParam_OBBT, 1);
|
||||
|
||||
loggerlib::Logger::systemNotify(loggerlib::LOGGER_PROGRESS, "Fetching swap information");
|
||||
|
||||
initSwaps();
|
||||
|
||||
loggerlib::Logger::systemNotify(loggerlib::LOGGER_PROGRESS, "Initializing variables");
|
||||
|
||||
initVariables();
|
||||
|
||||
loggerlib::Logger::systemNotify(loggerlib::LOGGER_PROGRESS, "Initializing constraints");
|
||||
|
||||
addAllConstraints();
|
||||
/*m_model->tune();
|
||||
m_model->getTuneResult(0);
|
||||
m_model->write("tuning.prm");*/
|
||||
}
|
||||
|
||||
void ILPTrackPlans::initSwaps()
|
||||
{
|
||||
loggerlib::Logger::systemNotify(loggerlib::LOGGER_PROGRESS, "Getting unique swaps and mandatory selections");
|
||||
|
||||
std::unordered_set<std::shared_ptr<CroisementUM>> swapsU;
|
||||
unsigned int p = 0;
|
||||
swapsOfTrackPlans.resize(trackPlans.size());
|
||||
for(auto& plan : trackPlans)
|
||||
{
|
||||
if(!plan.isTrash)
|
||||
{
|
||||
for(auto& sw : plan.mock->swaps)
|
||||
{
|
||||
swapsU.insert(sw);
|
||||
swap_setoftrackPlan[sw].push_back(p);
|
||||
}
|
||||
}
|
||||
++p;
|
||||
}
|
||||
|
||||
if(!swapsU.empty())
|
||||
{
|
||||
std::move(swapsU.begin(), swapsU.end(), std::back_inserter(swaps));
|
||||
std::sort(swaps.begin(), swaps.end(), [&](auto& s1, auto& s2){
|
||||
return s1->getPotentialSwapSlot().getDebut().getRelativeDate() < s2->getPotentialSwapSlot().getDebut().getRelativeDate();
|
||||
});
|
||||
|
||||
// Maintenant que swaps est trié et indexé, on peut remplir swapsOfTrackPlans
|
||||
for(unsigned int i = 0; i < swaps.size(); ++i)
|
||||
{
|
||||
for(auto& planIdx : swap_setoftrackPlan[swaps[i]])
|
||||
{
|
||||
swapsOfTrackPlans[planIdx].push_back(i);
|
||||
}
|
||||
}
|
||||
|
||||
loggerlib::Logger::systemNotify(loggerlib::LOGGER_PROGRESS, "Checking incompatible swaps");
|
||||
|
||||
for(unsigned int i = 0; i < swaps.size(); ++i)
|
||||
{
|
||||
auto& swap = swaps[i];
|
||||
const auto& plansOfSwap = swap_setoftrackPlan[swap];
|
||||
|
||||
for(unsigned int j = 0; j < swaps.size(); ++j)
|
||||
{
|
||||
if(i == j) continue;
|
||||
auto& swapComp = swaps[j];
|
||||
const auto& plansOfSwapComp = swap_setoftrackPlan[swapComp];
|
||||
|
||||
// Vérifier si une rame est en commun
|
||||
bool sharedRame = false;
|
||||
for(auto& r : swap->um_crit.ramesInfo)
|
||||
if(swapComp->find(r.id)) { sharedRame = true; break; }
|
||||
if(!sharedRame)
|
||||
for(auto& r : swap->um_sane.ramesInfo)
|
||||
if(swapComp->find(r.id)) { sharedRame = true; break; }
|
||||
|
||||
if(!sharedRame) continue;
|
||||
|
||||
// Vérifier si les deux croisements ont au moins un trackplan commun
|
||||
bool hasCommonPlan = false;
|
||||
for(auto& planIdx : plansOfSwap)
|
||||
{
|
||||
for(auto& planIdxComp : plansOfSwapComp)
|
||||
{
|
||||
if(planIdx == planIdxComp) { hasCommonPlan = true; break; }
|
||||
}
|
||||
if(hasCommonPlan) break;
|
||||
}
|
||||
//hasCommonPlan = plansOfSwap == plansOfSwapComp;
|
||||
|
||||
// Incompatibles si rame commune ET aucun trackplan commun
|
||||
if(!hasCommonPlan)
|
||||
{
|
||||
swap_incompswap[swap].push_back(j);
|
||||
}
|
||||
}
|
||||
|
||||
for(unsigned int j = 0; j < swaps.size(); ++j)
|
||||
{
|
||||
if(j != i)
|
||||
{
|
||||
if(swaps[i]->getPotentialSwapSlot().getDebut().getRelativeDate() > swaps[j]->getPotentialSwapSlot().getDebut().getRelativeDate())
|
||||
{
|
||||
auto sw = swaps[i];
|
||||
bool foundRame = false;
|
||||
for(auto& ramInfList : {sw->um_sane.ramesInfo, sw->um_crit.ramesInfo})
|
||||
{
|
||||
for(auto& rameInf : ramInfList)
|
||||
{
|
||||
foundRame = swaps[j]->find(rameInf.id);
|
||||
if(foundRame)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(foundRame)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(foundRame && swap_setoftrackPlan[sw] == swap_setoftrackPlan[swaps[j]])
|
||||
{
|
||||
swapDependsOnSwaps[i].push_back(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
for(unsigned int tr = 0; tr < trackPlans.size(); ++tr)
|
||||
{
|
||||
if(!trackPlans[tr].isTrash)// && trackPlans[tr].mock->swaps.empty())
|
||||
{
|
||||
trackPlanNoSWIncompSwap[tr];
|
||||
unsigned int s = 0;
|
||||
for(auto& sw : swaps)
|
||||
{
|
||||
if(trackPlans[tr].mock->swaps.empty() || std::find(swap_setoftrackPlan[sw].begin(), swap_setoftrackPlan[sw].end(), tr) == swap_setoftrackPlan[sw].end())
|
||||
{
|
||||
bool foundRame = false;
|
||||
for(auto& ramInfList : {sw->um_sane.ramesInfo, sw->um_crit.ramesInfo})
|
||||
{
|
||||
for(auto& rameInf : ramInfList)
|
||||
{
|
||||
foundRame = trackPlans[tr].isRameInScheduleAfterDate(rameInf.id, sw->getPotentialSwapSlot().getDebut().getRelativeDate());
|
||||
if(foundRame)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(foundRame)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(foundRame)
|
||||
{
|
||||
trackPlanNoSWIncompSwap[tr].push_back(s);
|
||||
}
|
||||
}
|
||||
++s;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ILPTrackPlans::initVariables()
|
||||
{
|
||||
x_ks.resize(trackPlans.size());
|
||||
for(unsigned int i = 0; i < trackPlans.size(); ++i)
|
||||
{
|
||||
std::string name = "x_" + std::to_string(i);
|
||||
x_ks[i] = m_model->addVar(0.0,1.0,0.0, GRB_BINARY, name.c_str());
|
||||
}
|
||||
|
||||
swap_ls.resize(swaps.size());
|
||||
for(unsigned int i = 0; i < swaps.size(); ++i)
|
||||
{
|
||||
std::string name = "swap_" + std::to_string(i);
|
||||
swap_ls[i] = m_model->addVar(0.0,1.0,0.0, GRB_BINARY, name.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<Planification> ILPTrackPlans::solve()
|
||||
{
|
||||
m_model->optimize();
|
||||
//printSol();
|
||||
return buildSolution();
|
||||
}
|
||||
|
||||
std::optional<Planification> ILPTrackPlans::buildSolution()
|
||||
{
|
||||
Planification planif;
|
||||
int status = m_model->get(GRB_IntAttr_Status);
|
||||
std::vector<TrackPlan> finalTrackPlans;
|
||||
std::vector<std::shared_ptr<CroisementUM>> finalSwaps;
|
||||
|
||||
std::vector<unsigned int> finalVar;
|
||||
if(status != GRB_INFEASIBLE)
|
||||
{
|
||||
unsigned int i = 0;
|
||||
for(auto& var : x_ks)
|
||||
{
|
||||
if(var.get(GRB_DoubleAttr_X) > 0)
|
||||
{
|
||||
finalTrackPlans.push_back(trackPlans[i]);
|
||||
finalVar.push_back(i);
|
||||
}
|
||||
++i;
|
||||
}
|
||||
|
||||
unsigned int s = 0;
|
||||
for(auto& var : swap_ls)
|
||||
{
|
||||
if(var.get(GRB_DoubleAttr_X) > 0)
|
||||
{
|
||||
finalSwaps.push_back(swaps[s]);
|
||||
//std::cout << swaps[s]->um_crit.ramesInfo[0].id << " - " << swaps[s]->um_sane.ramesInfo[0].id << " at " << swaps[s]->getPotentialSwapSlot().getDebut().getRelativeDate() << std::endl;
|
||||
}
|
||||
++s;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return std::nullopt;
|
||||
}
|
||||
if(!finalTrackPlans.empty())
|
||||
{
|
||||
//<job, trackPlanPos in finaltrackplans>
|
||||
std::unordered_map<unsigned short, std::vector<unsigned int>> doublonsInfos;
|
||||
bool hasDoubles = false;
|
||||
unsigned int i = 0;
|
||||
for(auto& trSch : finalTrackPlans)
|
||||
{
|
||||
for(auto& dec : trSch.schedule)
|
||||
{
|
||||
doublonsInfos[dec.first].push_back(i);
|
||||
if(doublonsInfos[dec.first].size() > 1)
|
||||
hasDoubles= true;
|
||||
}
|
||||
++i;
|
||||
}
|
||||
|
||||
if(!hasDoubles)
|
||||
{
|
||||
loggerlib::Logger::systemNotify(loggerlib::LOGGER_PROGRESS, "Solution from ILP is valid");
|
||||
}
|
||||
else {
|
||||
loggerlib::Logger::systemNotify(loggerlib::LOGGER_PROGRESS, "Removing jobs appearing multiple types");
|
||||
for(auto& doublon : doublonsInfos)
|
||||
{
|
||||
std::string tracksAppear = "";
|
||||
for(auto& el : doublon.second)
|
||||
{
|
||||
tracksAppear += std::to_string(!finalTrackPlans[el].isTrash ? finalTrackPlans[el].track : finalVar[el]) + " ";
|
||||
}
|
||||
loggerlib::Logger::systemNotify(loggerlib::LOGGER_PROGRESS, "Job " + std::to_string(doublon.first) + " : " + tracksAppear);
|
||||
|
||||
if(doublon.second.size() > 1)
|
||||
{
|
||||
std::map<int,std::pair<unsigned int, TrackPlan>> modifiedTrPlans;
|
||||
for(auto& trSchid: doublon.second)
|
||||
{
|
||||
auto trSchCop = finalTrackPlans[trSchid];
|
||||
auto cost = trSchCop.cost;
|
||||
auto suppDec= trSchCop.schedule[doublon.first];
|
||||
trSchCop.schedule.erase(doublon.first);
|
||||
trSchCop.shiftAndRecomputeCostsOnMachine(suppDec.empV);
|
||||
auto costAfter = trSchCop.cost;
|
||||
int gain = costAfter - cost;
|
||||
modifiedTrPlans[gain] = std::make_pair(trSchid,trSchCop);
|
||||
}
|
||||
unsigned int j = 0;
|
||||
for(auto modTrPlan = modifiedTrPlans.begin(); modTrPlan != modifiedTrPlans.end(); ++modTrPlan)
|
||||
{
|
||||
if(j == modifiedTrPlans.size()-1)
|
||||
break;
|
||||
finalTrackPlans[modTrPlan->second.first] = modTrPlan->second.second;
|
||||
++j;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
unsigned int totalCost = 0;
|
||||
unsigned int totalDiagCost = 0;
|
||||
unsigned int totalNbExclu = 0;
|
||||
std::shared_ptr<STFMockInstance> newMock = STFMockInstance::copy(STFInstance::getCurrentInstance()->mockInstance);
|
||||
std::sort(finalSwaps.begin(), finalSwaps.end(), [&](auto& el1, auto& el2){return el1->getPotentialSwapSlot().getDebut().getRelativeDate() < el2->getPotentialSwapSlot().getDebut().getRelativeDate(); });
|
||||
for(auto& sw : finalSwaps)
|
||||
{
|
||||
newMock->swap(*sw);
|
||||
newMock->swaps.push_back(sw);
|
||||
|
||||
}
|
||||
planif.setPlanificationState(newMock);
|
||||
for(auto& plan : finalTrackPlans)
|
||||
{
|
||||
totalCost += plan.cost;
|
||||
totalDiagCost += plan.diagCost;
|
||||
totalNbExclu += plan.isTrash && !plan.schedule.empty();
|
||||
std::cout << "Track " << (!plan.isTrash ? std::to_string(plan.track) : "trash") << " ";
|
||||
std::vector<std::pair<unsigned short, decision>> decsSorted;
|
||||
for(auto& dec : plan.schedule)
|
||||
{
|
||||
decsSorted.push_back(dec);
|
||||
}
|
||||
std::sort(decsSorted.begin(), decsSorted.end(), [&](auto& e1, auto& e2){ return e1.second.lastCreneau.first < e2.second.lastCreneau.second;});
|
||||
std::cout << "SOURCE : " << std::to_string(static_cast<int>(plan.source)) << " ";
|
||||
for(auto& dec : decsSorted)
|
||||
{
|
||||
std::cout << STFMockInstance::jobs[dec.first]->getOpId() << " : " << dec.second.lastCreneau.first << "-" << dec.second.lastCreneau.second << " | ";
|
||||
}
|
||||
std::cout << std::endl;
|
||||
|
||||
auto& decisions = plan.schedule;
|
||||
//auto& swaps = finalSwaps;
|
||||
auto& trStops = newMock->trajectoryStops;
|
||||
for(auto& d : decisions)
|
||||
{
|
||||
if(!d.second.excluded)
|
||||
{
|
||||
unsigned int rameId = STFMockInstance::rameOfOperations[d.first];
|
||||
STFMockInstance::jobs[d.first]->setIdRame(STFMockInstance::rames[rameId]->getId());
|
||||
STFMockInstance::jobs[d.first]->setNumeroEF(STFMockInstance::rames[rameId]->getNumeroEF());
|
||||
bool isPCr = trStops[d.second.empR].typeDispo.first == typeStop::RLT_POST_CROISEMENT_SUBIT || trStops[d.second.empR].typeDispo.first == typeStop::RLT_POST_CROISEMENT_VOULU;
|
||||
std::pair<unsigned short, unsigned short> slot = d.second.rejected ? std::make_pair(d.second.lastCreneau.first, d.second.lastCreneau.first + STFMockInstance::jobs[d.first]->getDureeDiag()) : std::make_pair(d.second.lastCreneau.first, d.second.lastCreneau.first + STFMockInstance::jobs[d.first]->getDuree());
|
||||
OperationPlanifie opPlan(d.first, d.second.voie, d.second.site, d.second.empR, d.second.empV, slot, d.second.rejected, isPCr);
|
||||
planif.addOperation(opPlan);
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned int rameId = STFMockInstance::rameOfOperations[d.first];
|
||||
STFMockInstance::jobs[d.first]->setIdRame(STFMockInstance::rames[rameId]->getId());
|
||||
STFMockInstance::jobs[d.first]->setNumeroEF(STFMockInstance::rames[rameId]->getNumeroEF());
|
||||
planif.getOpImplanifiables().push_back(d.first);
|
||||
}
|
||||
}
|
||||
}
|
||||
for(auto& s : finalSwaps)
|
||||
{
|
||||
planif.getCroisementsEffectues().push_back(*s);
|
||||
}
|
||||
std::cout << "Total cost = " << totalCost << std::endl;
|
||||
std::cout << "Total diagcost = " << totalDiagCost << std::endl;
|
||||
std::cout << "Nb exclusions = " << totalNbExclu << std::endl;
|
||||
return planif;
|
||||
}
|
||||
|
||||
void ILPTrackPlans::warmStart(std::vector<TrackPlan>& plans)
|
||||
{
|
||||
std::vector<unsigned int> varToSet;
|
||||
|
||||
for(auto& plan : plans)
|
||||
{
|
||||
if(plan.isTrash)
|
||||
{
|
||||
auto find = std::find_if(trackPlans.begin(), trackPlans.end(), [&](const TrackPlan& p) {
|
||||
return p.isTrash && p.schedule.begin()->first == plan.schedule.begin()->first;
|
||||
});
|
||||
if(find != trackPlans.end())
|
||||
{
|
||||
varToSet.push_back(std::distance(trackPlans.begin(), find));
|
||||
}
|
||||
}
|
||||
else {
|
||||
auto find = std::find_if(trackPlans.begin(), trackPlans.end(), [&](const TrackPlan& p) {
|
||||
return p == plan;
|
||||
});
|
||||
if(find != trackPlans.end())
|
||||
{
|
||||
varToSet.push_back(std::distance(trackPlans.begin(), find));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for(auto& var : x_ks)
|
||||
{
|
||||
var.set(GRB_DoubleAttr_Start, 0);
|
||||
}
|
||||
for(auto& var : swap_ls)
|
||||
{
|
||||
var.set(GRB_DoubleAttr_Start, 0);
|
||||
}
|
||||
unsigned int cost = 0;
|
||||
for(auto j : varToSet)
|
||||
{
|
||||
x_ks[j].set(GRB_DoubleAttr_Start, 1);
|
||||
cost += trackPlans[j].cost;
|
||||
for(auto& var : swapsOfTrackPlans[j])
|
||||
{
|
||||
swap_ls[var].set(GRB_DoubleAttr_Start, 1);
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << "Cost warm : " << cost << std::endl;
|
||||
}
|
||||
|
||||
void ILPTrackPlans::addSwapsSelectedAreCompatible()
|
||||
{
|
||||
std::unordered_map<unsigned int, std::unordered_set<unsigned int>> pairs;
|
||||
|
||||
for(unsigned int p = 0; p < x_ks.size(); ++p)
|
||||
{
|
||||
for(auto& cols : swapsOfTrackPlans[p])
|
||||
{
|
||||
std::string name = "P1: swap " +std::to_string(cols) +" of trplan " + std::to_string(p) + " must be selected if trplan is";
|
||||
auto sw = swaps[cols];
|
||||
bool foundRame = false;
|
||||
for(auto& ramInfList : {sw->um_sane.ramesInfo, sw->um_crit.ramesInfo})
|
||||
{
|
||||
for(auto& rameInf : ramInfList)
|
||||
{
|
||||
foundRame = trackPlans[p].isRameInScheduleAfterDate(rameInf.id, sw->getPotentialSwapSlot().getDebut().getRelativeDate());
|
||||
if(foundRame)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(foundRame)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(foundRame)
|
||||
m_model->addConstr(swap_ls[cols] >= x_ks[p], name.c_str()); // si constrSw=0 alors swap=0
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
for(unsigned int id_swap = 0; id_swap < swaps.size(); ++id_swap)
|
||||
{
|
||||
for(auto& cols : swap_incompswap[swaps[id_swap]])
|
||||
{
|
||||
if(pairs[id_swap].find(cols) == pairs[id_swap].end() && pairs[cols].find(id_swap) == pairs[cols].end())
|
||||
{
|
||||
pairs[id_swap].insert(cols);
|
||||
pairs[cols].insert(id_swap);
|
||||
std::string incomp = "swap " + std::to_string(id_swap) + " and swap " + std::to_string(cols) + " cannot be selected together";
|
||||
m_model->addConstr(swap_ls[id_swap] + swap_ls[cols] <= 1, incomp.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(auto tr_incomp_swap : trackPlanNoSWIncompSwap)
|
||||
{
|
||||
for(auto& sw : tr_incomp_swap.second)
|
||||
{
|
||||
std::string incomp = "swap " + std::to_string(sw) + " and tr " + std::to_string(tr_incomp_swap.first) + " cannot be selected together";
|
||||
m_model->addConstr(swap_ls[sw] + x_ks[tr_incomp_swap.first] <= 1, incomp.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
for(auto swapComp : swapDependsOnSwaps)
|
||||
{
|
||||
for(auto& sw : swapComp.second)
|
||||
{
|
||||
std::string incomp = "if swap " + std::to_string(sw) + " is selected then swap " + std::to_string(swapComp.first) + " must be selected";
|
||||
m_model->addConstr(swap_ls[sw] >= swap_ls[swapComp.first], incomp.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void ILPTrackPlans::printSol()
|
||||
{
|
||||
unsigned int i = 0;
|
||||
unsigned int cost = 0;
|
||||
unsigned int nbExcl = 0;
|
||||
int status = m_model->get(GRB_IntAttr_Status);
|
||||
if(status != GRB_INFEASIBLE)
|
||||
{
|
||||
for(auto& var : x_ks)
|
||||
{
|
||||
if(var.get(GRB_DoubleAttr_X) > 0)
|
||||
{
|
||||
std::cout << "Track " + (!trackPlans[i].isTrash ? std::to_string(trackPlans[i].track) : "trash" + std::to_string(i)) << std::endl;
|
||||
std::cout << "Jobs : ";
|
||||
for(auto& job : trackPlans[i].schedule)
|
||||
{
|
||||
std::cout << job.first << " ";
|
||||
}
|
||||
std::cout << std::endl;
|
||||
|
||||
if(trackPlans[i].isTrash)
|
||||
{
|
||||
nbExcl++;
|
||||
}
|
||||
cost += trackPlans[i].cost;
|
||||
}
|
||||
++i;
|
||||
}
|
||||
std::cout << "Nb EXcl : " << nbExcl << std::endl;
|
||||
std::cout << "Total cost : " << cost << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void ILPTrackPlans::addAllConstraints()
|
||||
{
|
||||
addTrackAppearsOneTime();
|
||||
addJobAppearsOneTime();
|
||||
addSwapsSelectedAreCompatible();
|
||||
addEpsilonConstraintDiagnosis();
|
||||
addObjectiveFunction();
|
||||
}
|
||||
|
||||
|
||||
void ILPTrackPlans::addTrackAppearsOneTime()
|
||||
{
|
||||
std::unordered_map<unsigned int, GRBLinExpr> trackExpressions;
|
||||
for(unsigned int id_col = 0; id_col < trackPlans.size(); ++id_col)
|
||||
{
|
||||
if(!trackPlans[id_col].isTrash)
|
||||
{
|
||||
trackExpressions[trackPlans[id_col].track] += x_ks[id_col];
|
||||
}
|
||||
}
|
||||
|
||||
for(auto& trackExpr : trackExpressions)
|
||||
{
|
||||
std::string name = "track " + std::to_string(trackExpr.first) + " must appear at most once";
|
||||
m_model->addConstr(trackExpr.second <= 1, name.c_str());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void ILPTrackPlans::addJobAppearsOneTime()
|
||||
{
|
||||
for(unsigned int id_job = 0; id_job < STFMockInstance::jobs.size(); ++id_job)
|
||||
{
|
||||
GRBLinExpr job_once = 0;
|
||||
for(unsigned int id_col = 0; id_col < trackPlans.size(); ++id_col)
|
||||
{
|
||||
if(trackPlans[id_col].isJobOnTrack(id_job))
|
||||
{
|
||||
job_once += x_ks[id_col];
|
||||
}
|
||||
}
|
||||
std::string name = "job " + std::to_string(id_job) + " must appear at least once";
|
||||
m_model->addConstr(job_once >= 1, name.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void ILPTrackPlans::addObjectiveFunction()
|
||||
{
|
||||
GRBLinExpr sum_T = 0;
|
||||
for(unsigned int id_col = 0; id_col < trackPlans.size(); ++id_col)
|
||||
{
|
||||
sum_T += x_ks[id_col]*trackPlans[id_col].cost;
|
||||
}
|
||||
m_model->setObjective(sum_T,GRB_MINIMIZE);
|
||||
}
|
||||
|
||||
void ILPTrackPlans::addEpsilonConstraintDiagnosis()
|
||||
{
|
||||
GRBLinExpr sum_epsilon = 0;
|
||||
for(unsigned int id_col = 0; id_col < trackPlans.size(); ++id_col)
|
||||
{
|
||||
sum_epsilon += x_ks[id_col]*trackPlans[id_col].diagCost;
|
||||
}
|
||||
std::string name = "Diagnosis cost must be below or equal to epsilon = " + std::to_string(configlib::Configuration::Global.EPSILON);
|
||||
m_model->addConstr(sum_epsilon <= configlib::Configuration::Global.EPSILON, name.c_str());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
#ifndef ILPTRACKPLANS_HPP
|
||||
#define ILPTRACKPLANS_HPP
|
||||
#include "TrackPlan.hpp"
|
||||
#include <gurobi_c++.h>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace solverlib {
|
||||
class ILPTrackPlans{
|
||||
private:
|
||||
GRBEnv m_env;
|
||||
std::unique_ptr<GRBModel> m_model;
|
||||
|
||||
std::vector<GRBVar> x_ks;
|
||||
std::vector<GRBVar> swap_ls;
|
||||
|
||||
std::vector<std::shared_ptr<CroisementUM>> swaps;
|
||||
std::vector<TrackPlan> trackPlans;
|
||||
|
||||
std::unordered_map<std::shared_ptr<CroisementUM>, std::vector<unsigned int>> swap_setoftrackPlan;
|
||||
std::unordered_map<std::shared_ptr<CroisementUM>, std::vector<unsigned int>> swap_incompswap;
|
||||
std::unordered_map<unsigned int, std::vector<unsigned int>> swapDependsOnSwaps;
|
||||
std::vector<std::vector<unsigned int>> swapsOfTrackPlans;
|
||||
std::unordered_map<unsigned int, std::vector<unsigned int>> trackPlanNoSWIncompSwap;
|
||||
|
||||
|
||||
public:
|
||||
ILPTrackPlans(std::vector<TrackPlan>& trackPlans);
|
||||
void warmStart(std::vector<TrackPlan>& trackPlansFeasible);
|
||||
void init();
|
||||
void initSwaps();
|
||||
void addAllConstraints();
|
||||
void addTrackAppearsOneTime();
|
||||
void addJobAppearsOneTime();
|
||||
void addObjectiveFunction();
|
||||
void addEpsilonConstraintDiagnosis();
|
||||
void addSwapsSelectedAreCompatible();
|
||||
std::optional<Planification> solve();
|
||||
void initVariables();
|
||||
void printSol();
|
||||
std::optional<Planification> buildSolution();
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,18 @@
|
||||
#ifndef RANDOM_HPP
|
||||
#define RANDOM_HPP
|
||||
|
||||
#include <random>
|
||||
namespace solverlib {
|
||||
namespace random {
|
||||
inline std::mt19937 makeEngine(uint32_t seed) {
|
||||
return std::mt19937(seed);
|
||||
}
|
||||
inline std::mt19937 makeEngine() {
|
||||
return std::mt19937(std::random_device{}());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,107 @@
|
||||
// SimpleGraphManager.cpp
|
||||
#include "SimpleGraphManager.hpp"
|
||||
|
||||
namespace solverlib {
|
||||
|
||||
SimpleGraphManager::SimpleGraphManager(unsigned int nb)
|
||||
: nbVertices(nb)
|
||||
{
|
||||
pgraph = new SimpleGraph(nbVertices);
|
||||
weights = boost::get(boost::edge_weight, *pgraph);
|
||||
edgeIndexMap = boost::get(boost::edge_index, *pgraph);
|
||||
}
|
||||
|
||||
SimpleGraphManager::~SimpleGraphManager()
|
||||
{
|
||||
pgraph->clear();
|
||||
delete pgraph;
|
||||
pgraph = nullptr;
|
||||
}
|
||||
|
||||
simple_edge_descriptor SimpleGraphManager::addEdge(
|
||||
simple_vertex_descriptor origin,
|
||||
simple_vertex_descriptor destination,
|
||||
long weight)
|
||||
{
|
||||
auto [exists, _] = findEdge(origin, destination);
|
||||
if(exists || origin >= nbVertices || destination >= nbVertices)
|
||||
return simple_edge_descriptor{}; // arc déjà existant ou invalide
|
||||
|
||||
unsigned int index = edgeCount++;
|
||||
auto [e, success] = boost::add_edge(
|
||||
boost::vertex(origin, *pgraph),
|
||||
boost::vertex(destination, *pgraph),
|
||||
*pgraph
|
||||
);
|
||||
put(edgeIndexMap, e, index);
|
||||
weights[e] = weight;
|
||||
edges.push_back(e);
|
||||
return e;
|
||||
}
|
||||
|
||||
std::pair<bool, simple_edge_descriptor>
|
||||
SimpleGraphManager::findEdge(simple_vertex_descriptor origin,
|
||||
simple_vertex_descriptor destination)
|
||||
{
|
||||
if(origin >= nbVertices || destination >= nbVertices)
|
||||
return {false, simple_edge_descriptor{}};
|
||||
|
||||
simple_out_edge_iterator it, end;
|
||||
for(boost::tie(it, end) = boost::out_edges(origin, *pgraph); it != end; ++it)
|
||||
{
|
||||
if(boost::target(*it, *pgraph) == destination)
|
||||
return {true, *it};
|
||||
}
|
||||
return {false, simple_edge_descriptor{}};
|
||||
}
|
||||
|
||||
std::pair<simple_out_edge_iterator, simple_out_edge_iterator>
|
||||
SimpleGraphManager::getOutEdges(simple_vertex_descriptor v)
|
||||
{
|
||||
if(v >= nbVertices)
|
||||
return {};
|
||||
return boost::out_edges(v, *pgraph);
|
||||
}
|
||||
|
||||
std::pair<simple_vertex_iterator, simple_vertex_iterator>
|
||||
SimpleGraphManager::getVertices()
|
||||
{
|
||||
return boost::vertices(*pgraph);
|
||||
}
|
||||
|
||||
void SimpleGraphManager::addVertex()
|
||||
{
|
||||
boost::add_vertex(*pgraph);
|
||||
nbVertices++;
|
||||
}
|
||||
|
||||
void SimpleGraphManager::removeEdge(simple_vertex_descriptor origin,
|
||||
simple_vertex_descriptor destination)
|
||||
{
|
||||
auto [found, e] = findEdge(origin, destination);
|
||||
if(found)
|
||||
{
|
||||
edges.erase(std::remove(edges.begin(), edges.end(), e), edges.end());
|
||||
boost::remove_edge(origin, destination, *pgraph);
|
||||
}
|
||||
}
|
||||
|
||||
simple_vertex_descriptor SimpleGraphManager::getEdgeSource(simple_edge_descriptor e) { return e.m_source; }
|
||||
simple_vertex_descriptor SimpleGraphManager::getEdgeTarget(simple_edge_descriptor e) { return e.m_target; }
|
||||
|
||||
simple_vertex_mapper SimpleGraphManager::map(simple_vertex_iterator& iterA, simple_vertex_iterator& iterB)
|
||||
{
|
||||
return {iterA, iterB};
|
||||
}
|
||||
|
||||
simple_out_edge_mapper SimpleGraphManager::map(simple_out_edge_iterator& iterA, simple_out_edge_iterator& iterB)
|
||||
{
|
||||
return {iterA, iterB};
|
||||
}
|
||||
|
||||
simple_edge_mapper SimpleGraphManager::map(simple_edge_iterator& iterA, simple_edge_iterator& iterB)
|
||||
{
|
||||
return {iterA, iterB};
|
||||
}
|
||||
|
||||
} // namespace solverlib
|
||||
@@ -0,0 +1,79 @@
|
||||
// SimpleGraphManager.hpp
|
||||
#ifndef SIMPLEGRAPHMANAGER_H
|
||||
#define SIMPLEGRAPHMANAGER_H
|
||||
|
||||
#include <boost/graph/adjacency_list.hpp>
|
||||
#include "boost/graph/properties.hpp"
|
||||
#include <unordered_map>
|
||||
|
||||
namespace solverlib {
|
||||
|
||||
typedef boost::adjacency_list<boost::vecS, boost::vecS, boost::directedS, boost::no_property,
|
||||
boost::property<boost::edge_weight_t, long,
|
||||
boost::property<boost::edge_index_t, long> > > SimpleGraph;
|
||||
|
||||
typedef boost::adjacency_list_traits<boost::vecS, boost::vecS, boost::directedS> SimpleTraits;
|
||||
typedef SimpleTraits::vertex_descriptor simple_vertex_descriptor;
|
||||
typedef SimpleTraits::edge_descriptor simple_edge_descriptor;
|
||||
|
||||
typedef boost::property_map<SimpleGraph, boost::edge_weight_t>::type SimpleWeight;
|
||||
typedef boost::property_map<SimpleGraph, boost::edge_index_t>::type SimpleEdgeIndex;
|
||||
|
||||
typedef boost::graph_traits<SimpleGraph>::vertex_iterator simple_vertex_iterator;
|
||||
typedef boost::graph_traits<SimpleGraph>::edge_iterator simple_edge_iterator;
|
||||
typedef boost::graph_traits<SimpleGraph>::out_edge_iterator simple_out_edge_iterator;
|
||||
|
||||
typedef boost::tuples::detail::tie_mapper<simple_vertex_iterator, simple_vertex_iterator>::type simple_vertex_mapper;
|
||||
typedef boost::tuples::detail::tie_mapper<simple_out_edge_iterator, simple_out_edge_iterator>::type simple_out_edge_mapper;
|
||||
typedef boost::tuples::detail::tie_mapper<simple_edge_iterator, simple_edge_iterator>::type simple_edge_mapper;
|
||||
|
||||
class SimpleGraphManager {
|
||||
private:
|
||||
unsigned int nbVertices = 0;
|
||||
unsigned int edgeCount = 0;
|
||||
|
||||
SimpleGraph* pgraph = nullptr;
|
||||
SimpleWeight weights;
|
||||
SimpleEdgeIndex edgeIndexMap;
|
||||
|
||||
std::vector<simple_edge_descriptor> edges;
|
||||
|
||||
public:
|
||||
explicit SimpleGraphManager(unsigned int nbVertices);
|
||||
~SimpleGraphManager();
|
||||
|
||||
[[nodiscard]] inline unsigned int getNbVertices() const { return nbVertices; }
|
||||
[[nodiscard]] inline unsigned int getNbEdges() const { return edgeCount; }
|
||||
|
||||
inline SimpleGraph* getGraph() { return pgraph; }
|
||||
inline SimpleWeight& getWeights() { return weights; }
|
||||
inline std::vector<simple_edge_descriptor>& getEdges() { return edges; }
|
||||
inline unsigned int getEdgeIndex(simple_edge_descriptor e) { return get(edgeIndexMap, e); }
|
||||
|
||||
// Retourne l'edge_descriptor de l'arc créé
|
||||
simple_edge_descriptor addEdge(simple_vertex_descriptor origin,
|
||||
simple_vertex_descriptor destination,
|
||||
long weight);
|
||||
|
||||
std::pair<bool, simple_edge_descriptor> findEdge(simple_vertex_descriptor origin,
|
||||
simple_vertex_descriptor destination);
|
||||
|
||||
std::pair<simple_out_edge_iterator, simple_out_edge_iterator>
|
||||
getOutEdges(simple_vertex_descriptor v);
|
||||
|
||||
std::pair<simple_vertex_iterator, simple_vertex_iterator> getVertices();
|
||||
|
||||
void addVertex();
|
||||
void removeEdge(simple_vertex_descriptor origin, simple_vertex_descriptor destination);
|
||||
|
||||
static simple_vertex_descriptor getEdgeSource(simple_edge_descriptor e);
|
||||
static simple_vertex_descriptor getEdgeTarget(simple_edge_descriptor e);
|
||||
|
||||
static simple_vertex_mapper map(simple_vertex_iterator& iterA, simple_vertex_iterator& iterB);
|
||||
static simple_out_edge_mapper map(simple_out_edge_iterator& iterA, simple_out_edge_iterator& iterB);
|
||||
static simple_edge_mapper map(simple_edge_iterator& iterA, simple_edge_iterator& iterB);
|
||||
};
|
||||
|
||||
} // namespace solverlib
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,924 @@
|
||||
#include "SimulatedAnnealing.hpp"
|
||||
#include "../PseCarlierRivreau/omp.h"
|
||||
#include "DynamicProgramming.hpp"
|
||||
#include "Solution.hpp"
|
||||
#include "TrackPlan.hpp"
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <ctime>
|
||||
#include <iterator>
|
||||
#include <optional>
|
||||
#include <random>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include "Random.hpp"
|
||||
#include "sourceSolTrPlan.hpp"
|
||||
|
||||
namespace solverlib {
|
||||
using namespace random;
|
||||
|
||||
bool StatSimulatedAnnealing::activate = true;
|
||||
bool SimulatedAnnealing::withDynProg = false;
|
||||
|
||||
|
||||
std::unordered_map<EMovingOperators, std::string> StatSimulatedAnnealing::names = {
|
||||
{EMovingOperators::CHANGE_MODE_WITHOUT_CARLIER, "CHANGE_MODE"},
|
||||
{EMovingOperators::INSERT_WITHOUT_CARLIER, "INSERT"},
|
||||
{EMovingOperators::REMOVE_WITHOUT_CARLIER, "REMOVE"},
|
||||
{EMovingOperators::SWAP_WITHOUT_CARLIER, "SWAP"},
|
||||
{EMovingOperators::SWAP_WITHIN_INTERVAL, "SWAP_WITHIN_SEQUENCE"},
|
||||
{EMovingOperators::DYN_PROG, "DYN_PROG"},
|
||||
{EMovingOperators::MOVE, "MOVE"}
|
||||
};
|
||||
|
||||
|
||||
SimulatedAnnealing::SimulatedAnnealing(std::unordered_map<unsigned short, Decision>& decs, std::shared_ptr<modellib::STFMockInstance> mock, ESourceTrackPlan source)
|
||||
{
|
||||
randomEngine = solverlib::random::makeEngine();
|
||||
addSolutionToPool(decs, mock, source);
|
||||
}
|
||||
|
||||
void SimulatedAnnealing::addSolutionToPool(std::unordered_map<unsigned short, Decision>& decs, std::shared_ptr<modellib::STFMockInstance> mock, ESourceTrackPlan source, bool isPutFirst)
|
||||
{
|
||||
auto costs = evaluate(decs);
|
||||
if(!isPutFirst)
|
||||
solutions.push_back({source, mock, decs, costs.first, costs.second});
|
||||
else
|
||||
{
|
||||
solutions.insert(solutions.begin(), {source, mock, decs, costs.first, costs.second});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
std::pair<unsigned int, unsigned int> SimulatedAnnealing::evaluate(const std::unordered_map<unsigned short, Decision>& decs)
|
||||
{
|
||||
unsigned int cost = 0;
|
||||
unsigned int diagCost = 0;
|
||||
for(auto& dec : decs)
|
||||
{
|
||||
if(dec.second.excluded)
|
||||
{
|
||||
cost += MAXIMUM_TIME_OFFSET * modellib::STFMockInstance::jobs[dec.first]->getPoidsRetard();
|
||||
}
|
||||
else
|
||||
cost += modellib::STFMockInstance::jobs[dec.first]->getPoidsRetard() * dec.second.lastCreneau.first;
|
||||
|
||||
if(dec.second.rejected)
|
||||
{
|
||||
diagCost += modellib::STFMockInstance::jobs[dec.first]->getPoidsRejet();
|
||||
}
|
||||
}
|
||||
return {cost, diagCost};
|
||||
}
|
||||
|
||||
std::pair<unsigned int, unsigned int> SimulatedAnnealing::getCostOfSequence(std::vector<std::pair<unsigned short, decision>>& jobsSeq)
|
||||
{
|
||||
unsigned int cost = 0;
|
||||
unsigned int costDiag = 0;
|
||||
for(auto& el : jobsSeq)
|
||||
{
|
||||
cost += STFMockInstance::jobs[el.first]->getPoidsRetard()*el.second.lastCreneau.first;
|
||||
costDiag += STFMockInstance::jobs[el.first]->getPoidsRejet()*el.second.rejected;
|
||||
}
|
||||
return {cost, costDiag};
|
||||
}
|
||||
|
||||
|
||||
// tire un opérateur uniformément
|
||||
EMovingOperators SimulatedAnnealing::pick_operator(double temp, double tmax) {
|
||||
int N = withDynProg ? static_cast<int>(EMovingOperators::DYN_PROG)+1 : static_cast<int>(EMovingOperators::MOVE)+1;
|
||||
std::vector<float> weights(N, 1);
|
||||
|
||||
std::vector<float> base_weights = {
|
||||
1.0f,
|
||||
1.0f,
|
||||
1.0f,
|
||||
1.0f,
|
||||
1.0f,
|
||||
1.0f
|
||||
// DYN_PROG ajouté si besoin
|
||||
};
|
||||
if (withDynProg) base_weights.push_back(0.5f); // DYN_PROG
|
||||
|
||||
const float remove_base = 1.0f; // poids max du remove (en début de recuit)
|
||||
const float t = temp / tmax; // 1.0 → 0.0
|
||||
|
||||
// Poids remove
|
||||
const float w_remove = remove_base * t;
|
||||
|
||||
// Le budget récupéré est redistribué proportionnellement aux autres
|
||||
const float base_sum = std::accumulate(base_weights.begin(), base_weights.end(), 0.0f);
|
||||
const float bonus = remove_base * (1.0f - t);
|
||||
|
||||
// Index du REMOVE dans ton enum — à adapter
|
||||
constexpr int REMOVE_IDX = 2;
|
||||
|
||||
for (int i = 0; i < N; ++i) {
|
||||
if (i == REMOVE_IDX) {
|
||||
weights[i] = w_remove;
|
||||
} else {
|
||||
weights[i] = base_weights[i] + bonus * (base_weights[i] / base_sum);
|
||||
}
|
||||
}
|
||||
return static_cast<EMovingOperators>(
|
||||
//std::uniform_int_distribution<int>(0, N)(randomEngine)
|
||||
std::discrete_distribution<int>(weights.begin(), weights.end())(randomEngine)
|
||||
);
|
||||
}
|
||||
|
||||
// Applique un opérateur et retourne un voisin (nullopt si infaisable)
|
||||
std::optional<SASolution> SimulatedAnnealing::apply_operator(const SASolution& current, EMovingOperators op)
|
||||
{
|
||||
switch (op)
|
||||
{
|
||||
case EMovingOperators::SWAP_WITHOUT_CARLIER: return move_swap_WC(current);
|
||||
case EMovingOperators::INSERT_WITHOUT_CARLIER: return move_insert_WC(current);
|
||||
case EMovingOperators::REMOVE_WITHOUT_CARLIER: return move_remove_WC(current);
|
||||
case EMovingOperators::CHANGE_MODE_WITHOUT_CARLIER: return move_change_mode_WC(current);
|
||||
case EMovingOperators::SWAP_WITHIN_INTERVAL: return move_swap_within_interval(current);
|
||||
case EMovingOperators::MOVE: return move_move_WC(current);
|
||||
case EMovingOperators::DYN_PROG: return move_dynprog(current);
|
||||
break;
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// Boucle principale
|
||||
SASolution SimulatedAnnealing::solve(double T_max, double T_min, double cooling_rate, int iterations_per_temp)
|
||||
{
|
||||
SASolution current = solutions[0];
|
||||
SASolution best = solutions[0];
|
||||
double cost_cur = current.cost;
|
||||
double cost_best= cost_cur;
|
||||
double T = T_max;
|
||||
std::uniform_real_distribution<double> uniform(0.0, 1.0);
|
||||
|
||||
while (T > T_min + 10e-6)
|
||||
{
|
||||
for (int i = 0; i < iterations_per_temp; ++i)
|
||||
{
|
||||
EMovingOperators op = pick_operator(T, T_max);
|
||||
stats.addUsed(op);
|
||||
auto neighbor = apply_operator(current, op);
|
||||
|
||||
if (!neighbor.has_value()) continue;
|
||||
|
||||
auto costs_neighbor = std::make_pair(neighbor->cost, neighbor->diagCost);
|
||||
|
||||
double delta = costs_neighbor.first - cost_cur;
|
||||
|
||||
stats.addFeas(op);
|
||||
if(delta <= 0)
|
||||
stats.addImproved(op);
|
||||
|
||||
if(delta > 0)
|
||||
{
|
||||
stats.addFailInfo(op, getP(delta, T, op), delta, T);
|
||||
}
|
||||
|
||||
if (delta < 0 || uniform(randomEngine) < getP(delta, T, op))
|
||||
{
|
||||
current = std::move(*neighbor);
|
||||
cost_cur = costs_neighbor.first;
|
||||
|
||||
if (cost_cur < cost_best) {
|
||||
best = current;
|
||||
cost_best = cost_cur;
|
||||
}
|
||||
|
||||
solutions.push_back(current);
|
||||
}
|
||||
}
|
||||
|
||||
T *= cooling_rate;
|
||||
//loggerlib::Logger::systemNotify(loggerlib::LOGGER_PROGRESS, "Temperature : " + std::to_string(T));
|
||||
//loggerlib::Logger::systemNotify(loggerlib::LOGGER_PROGRESS, "Solution pool : " + std::to_string(solutions.size()));
|
||||
}
|
||||
//loggerlib::Logger::systemNotify(loggerlib::LOGGER_PROGRESS, "Best solution : " + std::to_string(best.cost));
|
||||
|
||||
return best;
|
||||
}
|
||||
|
||||
double SimulatedAnnealing::getP(double delta, double temperature, EMovingOperators op)
|
||||
{
|
||||
switch (op) {
|
||||
case EMovingOperators::SWAP_WITHOUT_CARLIER:
|
||||
case EMovingOperators::INSERT_WITHOUT_CARLIER:
|
||||
case EMovingOperators::REMOVE_WITHOUT_CARLIER: return std::exp(-delta/(temperature*100));
|
||||
case EMovingOperators::CHANGE_MODE_WITHOUT_CARLIER:
|
||||
case EMovingOperators::SWAP_WITHIN_INTERVAL:
|
||||
case EMovingOperators::MOVE: return std::exp(-delta/(temperature*10));
|
||||
case EMovingOperators::DYN_PROG:
|
||||
break;
|
||||
}
|
||||
return std::exp(-delta/temperature);
|
||||
}
|
||||
|
||||
|
||||
//OPERATEURS
|
||||
|
||||
std::optional<SASolution> SimulatedAnnealing::move_dynprog(const SASolution& sol)
|
||||
{
|
||||
DynamicProgramming prog(sol);
|
||||
prog.mode = true;
|
||||
prog.saveSols = false;
|
||||
auto result = prog.solve();
|
||||
return std::optional<SASolution>(result);
|
||||
}
|
||||
|
||||
std::optional<SASolution> SimulatedAnnealing::move_swap_within_interval(const SASolution& sol)
|
||||
{
|
||||
auto mock = sol.mock;
|
||||
if (!mock) return std::nullopt;
|
||||
|
||||
std::vector<unsigned short> active_ops;
|
||||
for (auto& [op_id, dec] : sol.decisions)
|
||||
if (!dec.excluded) active_ops.push_back(op_id);
|
||||
if (active_ops.size() < 2) return std::nullopt;
|
||||
|
||||
std::uniform_int_distribution<int> dist(0, (int)active_ops.size() - 1);
|
||||
unsigned short op_a = active_ops[dist(randomEngine)];
|
||||
const Decision& dec_a = sol.decisions.at(op_a);
|
||||
|
||||
std::vector<unsigned short> seq_swap(1, op_a);
|
||||
|
||||
for (auto& op_id : active_ops) {
|
||||
if (op_id == op_a) continue;
|
||||
const Decision& dec_b = sol.decisions.at(op_id);
|
||||
if (dec_b.empV != dec_a.empV) continue;
|
||||
|
||||
seq_swap.push_back({
|
||||
op_id
|
||||
});
|
||||
}
|
||||
if (seq_swap.size() == 1) return std::nullopt;
|
||||
|
||||
std::sort(seq_swap.begin(), seq_swap.end(), [&](auto job1, auto job2){
|
||||
const Decision& dec_a = sol.decisions.at(job1);
|
||||
const Decision& dec_b = sol.decisions.at(job2);
|
||||
|
||||
return dec_a.lastCreneau.first < dec_b.lastCreneau.first;
|
||||
});
|
||||
|
||||
auto posA = std::find(seq_swap.begin(), seq_swap.end(), op_a);
|
||||
if(posA == seq_swap.end()) return std::nullopt;
|
||||
|
||||
auto lbdBuildSeq = [&](std::vector<unsigned short>& seq) -> std::optional<SASolution> {
|
||||
SASolution neighbor = sol;
|
||||
auto creneauTrack = STFMockInstance::machines[dec_a.empV]->getDispo();
|
||||
unsigned short lastEnd = 0;
|
||||
unsigned int oldCost = 0;
|
||||
unsigned int newCost = 0;
|
||||
for(auto& job : seq)
|
||||
{
|
||||
Decision& dec_neih = neighbor.decisions.at(job);
|
||||
auto creneauJob = mock->trajectoryStops[dec_neih.empR].getDispoStop();
|
||||
auto match = CreneauHoraire::checkSlotsCompatibility(creneauJob, creneauTrack);
|
||||
oldCost += STFMockInstance::jobs[job]->getPoidsRetard()*sol.decisions.at(job).lastCreneau.first;
|
||||
if(match.first)
|
||||
{
|
||||
if(std::max(match.second.first, lastEnd) + dec_neih.rejected*STFMockInstance::jobs[job]->getDureeDiag() + !dec_neih.rejected*STFMockInstance::jobs[job]->getDuree() > match.second.first + match.second.second)
|
||||
return std::nullopt;
|
||||
|
||||
dec_neih.lastCreneau = {
|
||||
std::max(match.second.first, lastEnd),
|
||||
std::max(match.second.first, lastEnd)+ dec_neih.rejected*STFMockInstance::jobs[job]->getDureeDiag() + !dec_neih.rejected*STFMockInstance::jobs[job]->getDuree()
|
||||
};
|
||||
newCost += STFMockInstance::jobs[job]->getPoidsRetard()*dec_neih.lastCreneau.first;
|
||||
lastEnd = std::max(match.second.first, lastEnd)+ dec_neih.rejected*STFMockInstance::jobs[job]->getDureeDiag() + !dec_neih.rejected*STFMockInstance::jobs[job]->getDuree();
|
||||
}
|
||||
else {
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
neighbor.cost = (neighbor.cost - oldCost) + newCost;
|
||||
neighbor.source = ESourceTrackPlan::SimAn;
|
||||
return neighbor;
|
||||
};
|
||||
|
||||
//TEST SWAP
|
||||
std::uniform_int_distribution<int> posR(0, (int)seq_swap.size()-1);
|
||||
auto job = posR(randomEngine);
|
||||
auto IposA = std::distance(seq_swap.begin(), posA);
|
||||
while(job == IposA)
|
||||
job = posR(randomEngine);
|
||||
|
||||
auto seqCop = seq_swap;
|
||||
auto posJobInt = job;
|
||||
seqCop[posJobInt] = *posA;
|
||||
seqCop[IposA] = seq_swap[job];
|
||||
auto res = lbdBuildSeq(seqCop);
|
||||
if(res != std::nullopt)
|
||||
return res;
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<SASolution> SimulatedAnnealing::move_swap_WC(const SASolution& sol)
|
||||
{
|
||||
auto mock = sol.mock;
|
||||
if (!mock) return std::nullopt;
|
||||
|
||||
std::vector<unsigned short> active_ops;
|
||||
for (auto& [op_id, dec] : sol.decisions)
|
||||
if (!dec.excluded) active_ops.push_back(op_id);
|
||||
if (active_ops.size() < 2) return std::nullopt;
|
||||
|
||||
std::uniform_int_distribution<int> dist(0, (int)active_ops.size() - 1);
|
||||
unsigned short op_a = active_ops[dist(randomEngine)];
|
||||
const Decision& dec_a = sol.decisions.at(op_a);
|
||||
|
||||
struct SwapCandidate {
|
||||
unsigned short op_a;
|
||||
unsigned short op_b;
|
||||
unsigned int empR_a_new; // empR que prendra op_a après le swap
|
||||
unsigned int empR_b_new;
|
||||
};
|
||||
std::vector<SwapCandidate> swap_candidates;
|
||||
|
||||
// Chercher une op dans une EmpV différent compatible
|
||||
//pair < op, empR post swap>
|
||||
for (auto& op_id : active_ops) {
|
||||
if (op_id == op_a) continue;
|
||||
const Decision& dec_b = sol.decisions.at(op_id);
|
||||
if (dec_b.empV == dec_a.empV) continue;
|
||||
|
||||
// Vérifier compatibilité infrastructure
|
||||
if (!STFMockInstance::infraComp[op_a][dec_b.voie]) continue;
|
||||
if (!STFMockInstance::infraComp[op_id][dec_a.voie]) continue;
|
||||
unsigned short empRA = 0;
|
||||
unsigned short empRB = 0;
|
||||
auto findDispOpA = std::find_if(mock->jobDispoVoiesRames[op_a].begin(), mock->jobDispoVoiesRames[op_a].end(), [&](auto& el){
|
||||
return mock->dispoVoiesRames[el].dispoVoie == dec_b.empV;
|
||||
});
|
||||
auto findDispOpB = std::find_if(mock->jobDispoVoiesRames[op_id].begin(), mock->jobDispoVoiesRames[op_id].end(), [&](auto& el){
|
||||
return mock->dispoVoiesRames[el].dispoVoie == dec_a.empV;
|
||||
});
|
||||
if(findDispOpA != mock->jobDispoVoiesRames[op_a].end() && findDispOpB != mock->jobDispoVoiesRames[op_id].end())
|
||||
{
|
||||
empRA = mock->dispoVoiesRames[*findDispOpA].dispoRame;
|
||||
empRB = mock->dispoVoiesRames[*findDispOpB].dispoRame;
|
||||
}
|
||||
else
|
||||
continue;
|
||||
|
||||
swap_candidates.push_back({
|
||||
op_a, op_id, empRA,empRB
|
||||
});
|
||||
}
|
||||
if (swap_candidates.empty()) return std::nullopt;
|
||||
|
||||
// Tirer deuxième op parmi les candidats
|
||||
std::uniform_int_distribution<int> cand_dist(0, (int)swap_candidates.size() - 1);
|
||||
auto swap = swap_candidates[cand_dist(randomEngine)];
|
||||
const Decision& dec_b = sol.decisions.at(swap.op_b);
|
||||
|
||||
// échanger tracks et slots
|
||||
SASolution neighbor = sol;
|
||||
Decision& new_dec_a = neighbor.decisions[op_a];
|
||||
Decision& new_dec_b = neighbor.decisions[swap.op_b];
|
||||
|
||||
new_dec_a.voie = dec_b.voie;
|
||||
new_dec_a.site = dec_b.site;
|
||||
new_dec_a.empV = dec_b.empV;
|
||||
new_dec_a.empR = swap.empR_a_new;
|
||||
new_dec_a.timeslotGraphSplited = mock->trajectoryStops[swap.empR_a_new].getDispoStop();
|
||||
new_dec_a.lastCreneau = {0,0};
|
||||
|
||||
new_dec_b.voie = dec_a.voie;
|
||||
new_dec_b.site = dec_a.site;
|
||||
new_dec_b.empV = dec_a.empV;
|
||||
new_dec_b.empR = swap.empR_b_new;
|
||||
new_dec_b.timeslotGraphSplited = mock->trajectoryStops[swap.empR_b_new].getDispoStop();
|
||||
new_dec_b.lastCreneau = {0,0};
|
||||
|
||||
auto oldCrenA = dec_a.lastCreneau;
|
||||
auto oldCrenB = dec_b.lastCreneau;
|
||||
|
||||
std::vector<std::pair<unsigned short, decision>> jobsEmpVA;
|
||||
std::vector<std::pair<unsigned short, decision>> jobsEmpVB;
|
||||
unsigned int oldCost = dec_a.lastCreneau.first * STFMockInstance::jobs[op_a]->getPoidsRetard() + dec_b.lastCreneau.first * STFMockInstance::jobs[swap.op_b]->getPoidsRetard();
|
||||
for(auto& dec : neighbor.decisions)
|
||||
{
|
||||
if(!dec.second.excluded)
|
||||
{
|
||||
if(dec.second.empV == new_dec_a.empV)
|
||||
{
|
||||
jobsEmpVA.push_back({dec.first, dec.second});
|
||||
oldCost += dec.second.lastCreneau.first * STFMockInstance::jobs[dec.first]->getPoidsRetard();
|
||||
}
|
||||
if(dec.second.empV == new_dec_b.empV)
|
||||
{
|
||||
jobsEmpVB.push_back({dec.first, dec.second});
|
||||
oldCost += dec.second.lastCreneau.first * STFMockInstance::jobs[dec.first]->getPoidsRetard();
|
||||
}
|
||||
}
|
||||
}
|
||||
std::sort(jobsEmpVA.begin(), jobsEmpVA.end(), [&](auto& el1, auto& el2){
|
||||
auto cren1 = el1.second.lastCreneau;
|
||||
auto cren2 = el2.second.lastCreneau;
|
||||
if(cren1.first == 0 && cren1.second == 0)
|
||||
{
|
||||
cren1 = oldCrenB;
|
||||
}
|
||||
if(cren2.first == 0 && cren2.second == 0)
|
||||
{
|
||||
cren2 = oldCrenB;
|
||||
}
|
||||
return cren1.first < cren2.first;
|
||||
});
|
||||
|
||||
std::sort(jobsEmpVB.begin(), jobsEmpVB.end(), [&](auto& el1, auto& el2){
|
||||
auto cren1 = el1.second.lastCreneau;
|
||||
auto cren2 = el2.second.lastCreneau;
|
||||
if(cren1.first == 0 && cren1.second == 0)
|
||||
{
|
||||
cren1 = oldCrenA;
|
||||
}
|
||||
if(cren2.first == 0 && cren2.second == 0)
|
||||
{
|
||||
cren2 = oldCrenA;
|
||||
}
|
||||
return cren1.first < cren2.first;
|
||||
});
|
||||
auto resA = checkSequence(jobsEmpVA, new_dec_a.empV);
|
||||
auto resB = checkSequence(jobsEmpVB, new_dec_b.empV);
|
||||
unsigned int newCost = 0;
|
||||
if(resA && resB)
|
||||
{
|
||||
unsigned int id = 0;
|
||||
for(auto& jobsA : jobsEmpVA)
|
||||
{
|
||||
auto& dec = neighbor.decisions[jobsA.first];
|
||||
dec.lastCreneau = resA.value()[id].second.lastCreneau;
|
||||
newCost += dec.lastCreneau.first * STFMockInstance::jobs[jobsA.first]->getPoidsRetard();
|
||||
++id;
|
||||
}
|
||||
id = 0;
|
||||
for(auto& jobsB : jobsEmpVB)
|
||||
{
|
||||
auto& dec = neighbor.decisions[jobsB.first];
|
||||
dec.lastCreneau = resB.value()[id].second.lastCreneau;
|
||||
newCost += dec.lastCreneau.first * STFMockInstance::jobs[jobsB.first]->getPoidsRetard();
|
||||
++id;
|
||||
}
|
||||
}
|
||||
else
|
||||
return std::nullopt;
|
||||
neighbor.cost = (neighbor.cost - oldCost) + newCost;
|
||||
neighbor.source = ESourceTrackPlan::SimAn;
|
||||
return neighbor;
|
||||
}
|
||||
|
||||
std::optional<SASolution> SimulatedAnnealing::move_insert_WC(const SASolution& sol)
|
||||
{
|
||||
auto mock = sol.mock;
|
||||
if (!mock) return std::nullopt;
|
||||
|
||||
std::vector<unsigned short> inactive_ops;
|
||||
for (auto& [op_id, dec] : sol.decisions)
|
||||
if (dec.excluded) inactive_ops.push_back(op_id);
|
||||
if (inactive_ops.empty()) return std::nullopt;
|
||||
|
||||
std::uniform_int_distribution<int> dist(0, (int)inactive_ops.size() - 1);
|
||||
unsigned short op_a = inactive_ops[dist(randomEngine)];
|
||||
|
||||
std::vector<unsigned int> dispCandidate;
|
||||
for (auto& disp : mock->jobDispoVoiesRames[op_a]) {
|
||||
|
||||
auto dur = mock->dispoVoiesRames[disp].match.second - mock->dispoVoiesRames[disp].match.first;
|
||||
if(dur >= STFMockInstance::jobs[op_a]->getDuree())
|
||||
{
|
||||
dispCandidate.push_back(disp);
|
||||
}
|
||||
else if(dur >= STFMockInstance::jobs[op_a]->getDureeDiag() && sol.diagCost + STFMockInstance::jobs[op_a]->getPoidsRejet() <= configlib::Configuration::Global.EPSILON)
|
||||
{
|
||||
dispCandidate.push_back(disp);
|
||||
}
|
||||
}
|
||||
if(dispCandidate.empty()) return std::nullopt;
|
||||
|
||||
std::uniform_int_distribution<int> cand_dist(0, (int)dispCandidate.size() - 1);
|
||||
auto disp = dispCandidate[cand_dist(randomEngine)];
|
||||
|
||||
//Construire le voisin - try insert
|
||||
SASolution neighbor = sol;
|
||||
Decision& new_dec_a = neighbor.decisions[op_a];
|
||||
auto dur = mock->dispoVoiesRames[disp].match.second - mock->dispoVoiesRames[disp].match.first;
|
||||
|
||||
new_dec_a.rejected = dur >= STFMockInstance::jobs[op_a]->getDuree() ? false : true,
|
||||
new_dec_a.excluded = false;
|
||||
new_dec_a.voie = mock->dispoVoiesRames[disp].voie;
|
||||
new_dec_a.site = mock->dispoVoiesRames[disp].site;
|
||||
new_dec_a.empV = mock->dispoVoiesRames[disp].dispoVoie;
|
||||
new_dec_a.empR = mock->dispoVoiesRames[disp].dispoRame;
|
||||
new_dec_a.timeslotGraphSplited = mock->trajectoryStops[new_dec_a.empR].getDispoStop();
|
||||
new_dec_a.lastCreneau = {0,0};
|
||||
|
||||
unsigned int oldCost = MAXIMUM_TIME_OFFSET*STFMockInstance::jobs[op_a]->getPoidsRetard();
|
||||
unsigned int oldDiagCost = 0;
|
||||
unsigned int newDiagCost = new_dec_a.rejected ? STFMockInstance::jobs[op_a]->getPoidsRejet() : 0;
|
||||
std::vector<std::pair<unsigned short, decision>> jobsEmpVA;
|
||||
for(auto& dec : neighbor.decisions)
|
||||
{
|
||||
if(!dec.second.excluded && dec.first != op_a)
|
||||
{
|
||||
if(dec.second.empV == new_dec_a.empV)
|
||||
{
|
||||
jobsEmpVA.push_back({dec.first, dec.second});
|
||||
oldCost += dec.second.lastCreneau.first * STFMockInstance::jobs[dec.first]->getPoidsRetard();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::sort(jobsEmpVA.begin(), jobsEmpVA.end(), [&](auto& el1, auto& el2){
|
||||
auto cren1 = el1.second.lastCreneau;
|
||||
auto cren2 = el2.second.lastCreneau;
|
||||
return cren1.first < cren2.first;
|
||||
});
|
||||
|
||||
std::uniform_int_distribution<unsigned int> distPos(0,jobsEmpVA.size());
|
||||
auto pos = distPos(randomEngine);
|
||||
|
||||
auto seq = jobsEmpVA;
|
||||
if(pos == jobsEmpVA.size())
|
||||
{
|
||||
seq.insert(seq.end(), std::make_pair(op_a, new_dec_a));
|
||||
}
|
||||
else {
|
||||
seq.insert(seq.begin() + pos, std::make_pair(op_a, new_dec_a));
|
||||
}
|
||||
unsigned int newCost = 0;
|
||||
auto res = checkSequence(seq, new_dec_a.empV);
|
||||
if(res)
|
||||
{
|
||||
unsigned int id = 0;
|
||||
for(auto& jobsA : seq)
|
||||
{
|
||||
auto& dec = neighbor.decisions[jobsA.first];
|
||||
dec.lastCreneau = res.value()[id].second.lastCreneau;
|
||||
newCost += dec.lastCreneau.first * STFMockInstance::jobs[jobsA.first]->getPoidsRetard();
|
||||
++id;
|
||||
}
|
||||
neighbor.diagCost = (neighbor.diagCost - oldDiagCost) + newDiagCost;
|
||||
neighbor.cost = (neighbor.cost - oldCost) + newCost;
|
||||
neighbor.source = ESourceTrackPlan::SimAn;
|
||||
return neighbor;
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<SASolution> SimulatedAnnealing::move_move_WC(const SASolution& sol)
|
||||
{
|
||||
auto mock = sol.mock;
|
||||
if (!mock) return std::nullopt;
|
||||
|
||||
std::vector<unsigned short> active_ops;
|
||||
for (auto& [op_id, dec] : sol.decisions)
|
||||
if (!dec.excluded) active_ops.push_back(op_id);
|
||||
if (active_ops.empty()) return std::nullopt;
|
||||
|
||||
std::uniform_int_distribution<int> dist(0, (int)active_ops.size() - 1);
|
||||
unsigned short op_a = active_ops[dist(randomEngine)];
|
||||
|
||||
std::vector<unsigned int> dispCandidate;
|
||||
const Decision& dec_a = sol.decisions.at(op_a);
|
||||
|
||||
for (auto& disp : mock->jobDispoVoiesRames[op_a]) {
|
||||
if(dec_a.empV == mock->dispoVoiesRames[disp].dispoVoie)
|
||||
continue;
|
||||
|
||||
auto dur = mock->dispoVoiesRames[disp].match.second - mock->dispoVoiesRames[disp].match.first;
|
||||
if(dur >= STFMockInstance::jobs[op_a]->getDuree())
|
||||
{
|
||||
dispCandidate.push_back(disp);
|
||||
}
|
||||
else if(dur >= STFMockInstance::jobs[op_a]->getDureeDiag() && sol.diagCost + STFMockInstance::jobs[op_a]->getPoidsRejet() <= configlib::Configuration::Global.EPSILON)
|
||||
{
|
||||
dispCandidate.push_back(disp);
|
||||
}
|
||||
}
|
||||
if(dispCandidate.empty()) return std::nullopt;
|
||||
|
||||
std::uniform_int_distribution<int> cand_dist(0, (int)dispCandidate.size() - 1);
|
||||
auto disp = dispCandidate[cand_dist(randomEngine)];
|
||||
|
||||
//Construire le voisin - try insert
|
||||
SASolution neighbor = sol;
|
||||
Decision& new_dec_a = neighbor.decisions[op_a];
|
||||
auto dur = mock->dispoVoiesRames[disp].match.second - mock->dispoVoiesRames[disp].match.first;
|
||||
|
||||
new_dec_a.rejected = dur >= STFMockInstance::jobs[op_a]->getDuree() ? false : true,
|
||||
new_dec_a.voie = mock->dispoVoiesRames[disp].voie;
|
||||
new_dec_a.site = mock->dispoVoiesRames[disp].site;
|
||||
new_dec_a.empV = mock->dispoVoiesRames[disp].dispoVoie;
|
||||
new_dec_a.empR = mock->dispoVoiesRames[disp].dispoRame;
|
||||
new_dec_a.timeslotGraphSplited = mock->trajectoryStops[new_dec_a.empR].getDispoStop();
|
||||
new_dec_a.lastCreneau = {0,0};
|
||||
|
||||
unsigned int oldCost = dec_a.lastCreneau.first*STFMockInstance::jobs[op_a]->getPoidsRetard();
|
||||
unsigned int oldDiagCost = dec_a.rejected ? STFMockInstance::jobs[op_a]->getPoidsRejet() : 0;
|
||||
unsigned int newDiagCost = new_dec_a.rejected ? STFMockInstance::jobs[op_a]->getPoidsRejet() : 0;
|
||||
|
||||
std::vector<std::pair<unsigned short, decision>> jobsEmpVA;
|
||||
std::vector<std::pair<unsigned short, decision>> jobsEmpVB;
|
||||
|
||||
for(auto& dec : neighbor.decisions)
|
||||
{
|
||||
if(!dec.second.excluded && dec.first != op_a)
|
||||
{
|
||||
if(dec.second.empV == new_dec_a.empV)
|
||||
{
|
||||
jobsEmpVA.push_back({dec.first, dec.second});
|
||||
oldCost += dec.second.lastCreneau.first * STFMockInstance::jobs[dec.first]->getPoidsRetard();
|
||||
}
|
||||
}
|
||||
if(!dec.second.excluded)
|
||||
{
|
||||
if(dec.second.empV == dec_a.empV)
|
||||
{
|
||||
jobsEmpVB.push_back({dec.first, dec.second});
|
||||
oldCost += dec.second.lastCreneau.first * STFMockInstance::jobs[dec.first]->getPoidsRetard();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::sort(jobsEmpVA.begin(), jobsEmpVA.end(), [&](auto& el1, auto& el2){
|
||||
auto cren1 = el1.second.lastCreneau;
|
||||
auto cren2 = el2.second.lastCreneau;
|
||||
return cren1.first < cren2.first;
|
||||
});
|
||||
std::sort(jobsEmpVB.begin(), jobsEmpVB.end(), [&](auto& el1, auto& el2){
|
||||
auto cren1 = el1.second.lastCreneau;
|
||||
auto cren2 = el2.second.lastCreneau;
|
||||
return cren1.first < cren2.first;
|
||||
});
|
||||
|
||||
unsigned int newCost = 0;
|
||||
//décale à gauche sur track de départ
|
||||
auto resB = checkSequence(jobsEmpVB, dec_a.empV);
|
||||
unsigned int id = 0;
|
||||
for(auto& jobsB : jobsEmpVB)
|
||||
{
|
||||
auto& dec = neighbor.decisions[jobsB.first];
|
||||
dec.lastCreneau = resB.value()[id].second.lastCreneau;
|
||||
newCost+= dec.lastCreneau.first * STFMockInstance::jobs[jobsB.first]->getPoidsRetard();
|
||||
++id;
|
||||
}
|
||||
|
||||
|
||||
std::uniform_int_distribution<unsigned int> distPos(0,jobsEmpVA.size());
|
||||
auto pos = distPos(randomEngine);
|
||||
|
||||
auto seq = jobsEmpVA;
|
||||
if(pos == jobsEmpVA.size())
|
||||
{
|
||||
seq.insert(seq.end(), std::make_pair(op_a, new_dec_a));
|
||||
}
|
||||
else {
|
||||
seq.insert(seq.begin() + pos, std::make_pair(op_a, new_dec_a));
|
||||
}
|
||||
auto res = checkSequence(seq, new_dec_a.empV);
|
||||
if(res)
|
||||
{
|
||||
unsigned int id = 0;
|
||||
for(auto& jobsA : seq)
|
||||
{
|
||||
auto& dec = neighbor.decisions[jobsA.first];
|
||||
dec.lastCreneau = res.value()[id].second.lastCreneau;
|
||||
newCost+= dec.lastCreneau.first * STFMockInstance::jobs[jobsA.first]->getPoidsRetard();
|
||||
++id;
|
||||
}
|
||||
neighbor.diagCost = (neighbor.diagCost - oldDiagCost) + newDiagCost;
|
||||
neighbor.cost = (neighbor.cost - oldCost) + newCost;
|
||||
neighbor.source = ESourceTrackPlan::SimAn;
|
||||
return neighbor;
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<SASolution> SimulatedAnnealing::move_remove_WC(const SASolution& sol)
|
||||
{
|
||||
auto mock = sol.mock;
|
||||
if (!mock) return std::nullopt;
|
||||
|
||||
std::vector<unsigned short> active_ops;
|
||||
for (auto& [op_id, dec] : sol.decisions)
|
||||
if (!dec.excluded) active_ops.push_back(op_id);
|
||||
if (active_ops.empty()) return std::nullopt;
|
||||
|
||||
std::uniform_int_distribution<int> dist(0, (int)active_ops.size() - 1);
|
||||
unsigned short op_a = active_ops[dist(randomEngine)];
|
||||
//const Decision& dec_a = sol.decisions.at(op_a);
|
||||
|
||||
//Construire le voisin - exclure a
|
||||
SASolution neighbor = sol;
|
||||
const Decision& dec_a = sol.decisions.at(op_a);
|
||||
Decision& new_dec_a = neighbor.decisions[op_a];
|
||||
|
||||
new_dec_a.rejected = false,
|
||||
new_dec_a.excluded = true;
|
||||
new_dec_a.voie = 0;
|
||||
new_dec_a.site = 0;
|
||||
new_dec_a.empV = 0;
|
||||
new_dec_a.empR = 0;
|
||||
new_dec_a.timeslotGraphSplited = CreneauHoraire();
|
||||
new_dec_a.lastCreneau = {0,0};
|
||||
|
||||
unsigned int oldCost = dec_a.lastCreneau.first * STFMockInstance::jobs[op_a]->getPoidsRetard();
|
||||
unsigned int oldDiagCost = dec_a.rejected ? STFMockInstance::jobs[op_a]->getPoidsRejet() : 0;
|
||||
unsigned int newDiagCost = 0;
|
||||
|
||||
std::vector<std::pair<unsigned short, decision>> jobsEmpVA;
|
||||
for(auto& dec : neighbor.decisions)
|
||||
{
|
||||
if(!dec.second.excluded)
|
||||
{
|
||||
if(dec.second.empV == dec_a.empV)
|
||||
{
|
||||
jobsEmpVA.push_back({dec.first, dec.second});
|
||||
oldCost += dec.second.lastCreneau.first * STFMockInstance::jobs[dec.first]->getPoidsRetard();
|
||||
}
|
||||
}
|
||||
}
|
||||
unsigned int newCost = MAXIMUM_TIME_OFFSET * STFMockInstance::jobs[op_a]->getPoidsRetard();
|
||||
|
||||
if(!jobsEmpVA.empty())
|
||||
{
|
||||
|
||||
std::sort(jobsEmpVA.begin(), jobsEmpVA.end(), [&](auto& el1, auto& el2){
|
||||
auto cren1 = el1.second.lastCreneau;
|
||||
auto cren2 = el2.second.lastCreneau;
|
||||
return cren1.first < cren2.first;
|
||||
});
|
||||
auto resA = checkSequence(jobsEmpVA, dec_a.empV);
|
||||
if(resA)
|
||||
{
|
||||
unsigned int id = 0;
|
||||
for(auto& jobsA : jobsEmpVA)
|
||||
{
|
||||
auto& dec = neighbor.decisions[jobsA.first];
|
||||
dec.lastCreneau = resA.value()[id].second.lastCreneau;
|
||||
newCost += dec.lastCreneau.first * STFMockInstance::jobs[jobsA.first]->getPoidsRetard();
|
||||
++id;
|
||||
}
|
||||
}
|
||||
else
|
||||
return std::nullopt;
|
||||
}
|
||||
neighbor.diagCost = (neighbor.diagCost - oldDiagCost) + newDiagCost;
|
||||
neighbor.cost = (neighbor.cost - oldCost) + newCost;
|
||||
neighbor.source = ESourceTrackPlan::SimAn;
|
||||
return neighbor;
|
||||
}
|
||||
|
||||
std::optional<SASolution> SimulatedAnnealing::move_change_mode_WC(const SASolution& sol)
|
||||
{
|
||||
auto mock = sol.mock;
|
||||
if (!mock) return std::nullopt;
|
||||
|
||||
std::vector<unsigned short> active_ops;
|
||||
for (auto& [op_id, dec] : sol.decisions)
|
||||
if (!dec.excluded) active_ops.push_back(op_id);
|
||||
if (active_ops.empty()) return std::nullopt;
|
||||
|
||||
std::uniform_int_distribution<int> dist(0, (int)active_ops.size() - 1);
|
||||
unsigned short op_a = active_ops[dist(randomEngine)];
|
||||
const Decision& dec_a = sol.decisions.at(op_a);
|
||||
|
||||
SASolution neighbor = sol;
|
||||
Decision& new_dec_a = neighbor.decisions[op_a];
|
||||
|
||||
unsigned int oldDiagCost = dec_a.rejected ? STFMockInstance::jobs[op_a]->getPoidsRejet() : 0;
|
||||
unsigned int newDiagCost = dec_a.rejected ? 0 : STFMockInstance::jobs[op_a]->getPoidsRejet();
|
||||
unsigned int oldCost = 0;
|
||||
if(dec_a.rejected)
|
||||
{
|
||||
new_dec_a.rejected = false;
|
||||
}
|
||||
else {
|
||||
if(neighbor.diagCost + STFMockInstance::jobs[op_a]->getPoidsRejet() <= configlib::Configuration::Global.EPSILON)
|
||||
{
|
||||
new_dec_a.rejected = true;
|
||||
}
|
||||
else {
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::pair<unsigned short, decision>> jobsEmpVA;
|
||||
for(auto& dec : neighbor.decisions)
|
||||
{
|
||||
if(!dec.second.excluded)
|
||||
{
|
||||
if(dec.second.empV == dec_a.empV)
|
||||
{
|
||||
jobsEmpVA.push_back({dec.first, dec.second});
|
||||
oldCost += dec.second.lastCreneau.first * STFMockInstance::jobs[dec.first]->getPoidsRetard();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::sort(jobsEmpVA.begin(), jobsEmpVA.end(), [&](auto& el1, auto& el2){
|
||||
auto cren1 = el1.second.lastCreneau;
|
||||
auto cren2 = el2.second.lastCreneau;
|
||||
return cren1.first < cren2.first;
|
||||
});
|
||||
unsigned int newCost = 0;
|
||||
if(!jobsEmpVA.empty())
|
||||
{
|
||||
auto resA = checkSequence(jobsEmpVA, dec_a.empV);
|
||||
if(resA)
|
||||
{
|
||||
unsigned int id = 0;
|
||||
for(auto& jobsA : jobsEmpVA)
|
||||
{
|
||||
auto& dec = neighbor.decisions[jobsA.first];
|
||||
dec.lastCreneau = resA.value()[id].second.lastCreneau;
|
||||
newCost += dec.lastCreneau.first * STFMockInstance::jobs[jobsA.first]->getPoidsRetard();
|
||||
++id;
|
||||
}
|
||||
}
|
||||
else
|
||||
return std::nullopt;
|
||||
}
|
||||
neighbor.diagCost = (neighbor.diagCost - oldDiagCost) + newDiagCost;
|
||||
neighbor.cost = (neighbor.cost - oldCost) + newCost;
|
||||
neighbor.source = ESourceTrackPlan::SimAn;
|
||||
return neighbor;
|
||||
}
|
||||
|
||||
std::optional<std::vector<std::pair<unsigned short, Decision>>> SimulatedAnnealing::checkSequence(const std::vector<std::pair<unsigned short, Decision>>& jobsDec, unsigned int machine)
|
||||
{
|
||||
auto machineDisp = STFMockInstance::machines[machine]->getDispo();
|
||||
unsigned short minBegin = machineDisp.getDebut().getRelativeDate();
|
||||
std::vector<std::pair<unsigned short, Decision>> res;
|
||||
for(auto& jobDec : jobsDec)
|
||||
{
|
||||
auto matchWithMachine = CreneauHoraire::checkSlotsCompatibility(jobDec.second.timeslotGraphSplited, machineDisp).second;
|
||||
minBegin = std::max(minBegin, matchWithMachine.first);
|
||||
auto newJobDec = jobDec;
|
||||
unsigned int duration = !jobDec.second.rejected ? STFMockInstance::jobs[jobDec.first]->getDuree() : STFMockInstance::jobs[jobDec.first]->getDureeDiag();
|
||||
newJobDec.second.lastCreneau = {minBegin, minBegin + duration};
|
||||
if(minBegin + duration > matchWithMachine.first + matchWithMachine.second)
|
||||
return std::nullopt;
|
||||
minBegin = minBegin + duration;
|
||||
res.push_back(newJobDec);
|
||||
}
|
||||
return std::optional<std::vector<std::pair<unsigned short, Decision>>>(res);
|
||||
}
|
||||
|
||||
|
||||
std::pair<bool, std::vector<unsigned int>> SimulatedAnnealing::PSE_Carlier_Rivreau(std::vector<std::pair<unsigned short, decision>> &jobs, unsigned int voieMachine) {
|
||||
|
||||
std::stringstream fakeFile;
|
||||
|
||||
for (unsigned int i = 0; i < jobs.size(); ++i) {
|
||||
auto match = CreneauHoraire::checkSlotsCompatibility(jobs[i].second.timeslotGraphSplited,
|
||||
STFMockInstance::machines[voieMachine]->getDispo());
|
||||
fakeFile << i + 1;
|
||||
fakeFile << " " << match.second.first;
|
||||
if (!jobs[i].second.rejected) {
|
||||
fakeFile << " " << STFMockInstance::jobs[jobs[i].first]->getDuree();
|
||||
} else
|
||||
fakeFile << " " << STFMockInstance::jobs[jobs[i].first]->getDureeDiag();
|
||||
fakeFile << " " << match.second.first + match.second.second;
|
||||
fakeFile << std::endl;
|
||||
}
|
||||
std::ifstream file;
|
||||
file.basic_ios<char>::rdbuf(fakeFile.rdbuf());
|
||||
Solution sol((int) jobs.size());
|
||||
OneMachine machine(file);
|
||||
machine.solve(sol);
|
||||
|
||||
std::vector<unsigned int> solution(jobs.size());
|
||||
for (unsigned int i = 0; i < jobs.size(); ++i) {
|
||||
solution[i] = sol.startTime[i+1];
|
||||
}
|
||||
|
||||
return {machine.checkSol(sol) && sol.Lmax <= 0, solution};
|
||||
}
|
||||
|
||||
|
||||
/*unsigned long SimulatedAnnealing::getUniquePlans(std::vector<TrackPlan>& plans)
|
||||
{
|
||||
std::set<TrackPlan> uniqueSchedules;
|
||||
std::vector<TrackPlan> newTMPVec;
|
||||
for(auto& trackSch : plans)
|
||||
{
|
||||
uniqueSchedules.insert(trackSch);
|
||||
}
|
||||
auto uniqueNb = uniqueSchedules.size();
|
||||
std::move(uniqueSchedules.begin(), uniqueSchedules.end(), std::back_inserter(newTMPVec));
|
||||
std::swap(plans, newTMPVec);
|
||||
|
||||
return uniqueNb;
|
||||
}*/
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
#ifndef SIMUlATEDANNEALING_HPP
|
||||
#define SIMUlATEDANNEALING_HPP
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <random>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include "TrackPlan.hpp"
|
||||
#include "Solution.hpp"
|
||||
namespace solverlib {
|
||||
using namespace modellib;
|
||||
|
||||
enum class EMovingOperators{
|
||||
SWAP_WITHOUT_CARLIER,
|
||||
INSERT_WITHOUT_CARLIER,
|
||||
REMOVE_WITHOUT_CARLIER,
|
||||
CHANGE_MODE_WITHOUT_CARLIER,
|
||||
SWAP_WITHIN_INTERVAL,
|
||||
MOVE,
|
||||
DYN_PROG,
|
||||
};
|
||||
|
||||
struct StatSimulatedAnnealing{
|
||||
struct failInf{
|
||||
double prob;
|
||||
unsigned int diff;
|
||||
double temperature;
|
||||
};
|
||||
|
||||
std::unordered_map<EMovingOperators, unsigned int> nbUsed;
|
||||
std::unordered_map<EMovingOperators, unsigned int> nbFeas;
|
||||
std::unordered_map<EMovingOperators, unsigned int> nbImproved;
|
||||
std::unordered_map<EMovingOperators, std::vector<failInf>> failInfos;
|
||||
|
||||
|
||||
|
||||
static std::unordered_map<EMovingOperators, std::string> names;
|
||||
|
||||
static bool activate;
|
||||
|
||||
void addUsed(EMovingOperators op){
|
||||
if(activate) nbUsed[op]++;
|
||||
}
|
||||
void addFeas(EMovingOperators op){
|
||||
if(activate) nbFeas[op]++;
|
||||
}
|
||||
void addImproved(EMovingOperators op){
|
||||
if(activate) nbImproved[op]++;
|
||||
}
|
||||
|
||||
void addFailInfo(EMovingOperators op, double prob, unsigned int diff, double temp)
|
||||
{
|
||||
if(activate) failInfos[op].emplace_back(failInf{prob, diff, temp});
|
||||
}
|
||||
};
|
||||
|
||||
class SimulatedAnnealing{
|
||||
private:
|
||||
std::vector<SASolution> solutions;
|
||||
std::mt19937 randomEngine;
|
||||
public:
|
||||
static bool withDynProg;
|
||||
StatSimulatedAnnealing stats;
|
||||
SimulatedAnnealing() = delete;
|
||||
explicit SimulatedAnnealing(std::unordered_map<unsigned short, Decision>& decs, std::shared_ptr<modellib::STFMockInstance> mock, ESourceTrackPlan source);
|
||||
EMovingOperators pick_operator(double tmax, double t);
|
||||
std::optional<SASolution> apply_operator(const SASolution& current, EMovingOperators op);
|
||||
SASolution solve(double T_max, double T_min, double cooling_rate, int iterations_per_temp);
|
||||
|
||||
std::optional<SASolution> move_swap_within_interval(const SASolution& sol);
|
||||
std::optional<SASolution> move_swap_WC(const SASolution& sol);
|
||||
std::optional<SASolution> move_insert_WC(const SASolution& sol);
|
||||
std::optional<SASolution> move_move_WC(const SASolution& sol);
|
||||
std::optional<SASolution> move_remove_WC(const SASolution& sol);
|
||||
std::optional<SASolution> move_change_mode_WC(const SASolution& sol);
|
||||
std::optional<SASolution> move_dynprog(const SASolution& sol);
|
||||
|
||||
std::optional<std::vector<std::pair<unsigned short, Decision>>> checkSequence(const std::vector<std::pair<unsigned short, Decision>>& jobsDec, unsigned int machine);
|
||||
|
||||
double getP(double delta, double temperature, EMovingOperators op);
|
||||
|
||||
std::pair<unsigned int, unsigned int> evaluate(const std::unordered_map<unsigned short, Decision>& decs);
|
||||
|
||||
std::pair<unsigned int, unsigned int> getCostOfSequence(std::vector<std::pair<unsigned short, decision>>& jobsSeq);
|
||||
|
||||
std::pair<bool, std::vector<unsigned int>> PSE_Carlier_Rivreau(std::vector<std::pair<unsigned short, decision>> &jobs, unsigned int voieMachine);
|
||||
|
||||
void addSolutions(std::vector<SASolution>& solPool){solutions.insert(solutions.end(), solPool.begin(), solPool.end());};
|
||||
|
||||
|
||||
//unsigned long getUniquePlans(std::vector<TrackPlan>& plans);
|
||||
void addSolutionToPool(std::unordered_map<unsigned short, Decision>& decs, std::shared_ptr<modellib::STFMockInstance> mock, ESourceTrackPlan source, bool isFirst = false);
|
||||
|
||||
std::vector<SASolution>&& getSolutionPool(){return std::move(solutions);};
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,20 @@
|
||||
#ifndef SOLUTION_HPP
|
||||
#define SOLUTION_HPP
|
||||
|
||||
#include "../../../General/Model/STFMockInstance.hpp"
|
||||
#include "sourceSolTrPlan.hpp"
|
||||
|
||||
namespace solverlib {
|
||||
using namespace modellib;
|
||||
|
||||
typedef struct _sol{
|
||||
ESourceTrackPlan source;
|
||||
std::shared_ptr<STFMockInstance> mock;
|
||||
std::unordered_map<unsigned short, Decision> decisions;
|
||||
unsigned int cost;
|
||||
unsigned int diagCost;
|
||||
}SASolution;
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,118 @@
|
||||
#include "SolutionPoolManager.hpp"
|
||||
#include <iterator>
|
||||
|
||||
namespace solverlib {
|
||||
std::vector<TrackPlan> SolutionPoolManager::getTrackPlansFromSolution(SASolution& sol, bool withTrash)
|
||||
{
|
||||
std::vector<TrackPlan> trackPlans;
|
||||
trackPlans.reserve(STFMockInstance::tracks.size());
|
||||
|
||||
//id plan = id voie
|
||||
std::vector<TrackPlan> plans;
|
||||
plans.resize(STFMockInstance::tracks.size());
|
||||
for(auto& dec : sol.decisions)
|
||||
{
|
||||
if(!dec.second.excluded)
|
||||
{
|
||||
plans[dec.second.voie].source = sol.source;
|
||||
plans[dec.second.voie].isTrash = false;
|
||||
plans[dec.second.voie].track = dec.second.voie;
|
||||
plans[dec.second.voie].schedule[dec.first] = dec.second;
|
||||
}
|
||||
else if(withTrash) {
|
||||
TrackPlan trashTrack;
|
||||
trashTrack.source = sol.source;
|
||||
trashTrack.isTrash = true;
|
||||
trashTrack.mock = sol.mock;
|
||||
trashTrack.diagCost = 0;
|
||||
trashTrack.track = 0;
|
||||
trashTrack.schedule[dec.first] = {
|
||||
0,CreneauHoraire(), 0, false, true,0,0,{0,0}
|
||||
};
|
||||
trashTrack.cost = STFMockInstance::jobs[dec.first]->getPoidsRetard() * MAXIMUM_TIME_OFFSET;
|
||||
trackPlans.push_back(trashTrack);
|
||||
}
|
||||
}
|
||||
for(auto& plan : plans)
|
||||
{
|
||||
if(!plan.schedule.empty())
|
||||
{
|
||||
plan.mock = sol.mock;
|
||||
plan.source = sol.source;
|
||||
plan.evaluate();
|
||||
trackPlans.push_back(plan);
|
||||
}
|
||||
}
|
||||
|
||||
return trackPlans;
|
||||
}
|
||||
|
||||
|
||||
std::vector<TrackPlan> SolutionPoolManager::transformSolutionsIntoUniqueTrackPlans()
|
||||
{
|
||||
std::vector<TrackPlan> trackPlans;
|
||||
std::set<TrackPlan> uniqueSchedules;
|
||||
unsigned long countD = 0;
|
||||
trackPlans.reserve(STFMockInstance::tracks.size() * pool.size() + 1);
|
||||
while(!pool.empty())
|
||||
{
|
||||
auto& sol = pool.back();
|
||||
|
||||
//id plan = id voie
|
||||
std::vector<TrackPlan> plans;
|
||||
plans.resize(STFMockInstance::tracks.size());
|
||||
for(auto& dec : sol.decisions)
|
||||
{
|
||||
if(!dec.second.excluded)
|
||||
{
|
||||
plans[dec.second.voie].isTrash = false;
|
||||
plans[dec.second.voie].track = dec.second.voie;
|
||||
plans[dec.second.voie].schedule[dec.first] = dec.second;
|
||||
}
|
||||
}
|
||||
for(auto& plan : plans)
|
||||
{
|
||||
if(!plan.schedule.empty())
|
||||
{
|
||||
plan.mock = sol.mock;
|
||||
plan.source = sol.source;
|
||||
plan.evaluate();
|
||||
auto inserted = uniqueSchedules.insert(plan);
|
||||
if(!inserted.second)
|
||||
{
|
||||
countD++;
|
||||
}
|
||||
}
|
||||
}
|
||||
pool.pop_back();
|
||||
}
|
||||
std::move(uniqueSchedules.begin(), uniqueSchedules.end(), std::back_inserter(trackPlans));
|
||||
|
||||
for(unsigned short job = 0; job < STFMockInstance::jobs.size(); ++job)
|
||||
{
|
||||
TrackPlan trashTrack;
|
||||
trashTrack.isTrash = true;
|
||||
trashTrack.diagCost = 0;
|
||||
trashTrack.track = 0;
|
||||
trashTrack.source = ESourceTrackPlan::Fake;
|
||||
trashTrack.schedule[job] = {
|
||||
0,CreneauHoraire(), 0, false, true,0,0,{0,0}
|
||||
};
|
||||
trashTrack.cost = STFMockInstance::jobs[job]->getPoidsRetard() * MAXIMUM_TIME_OFFSET;
|
||||
trackPlans.push_back(trashTrack);
|
||||
}
|
||||
|
||||
auto nbplan = trackPlans.size();
|
||||
|
||||
loggerlib::Logger::systemNotify(loggerlib::LOGGER_PROGRESS, "Removed " + std::to_string(countD) + " non-unique track schedules");
|
||||
loggerlib::Logger::systemNotify(loggerlib::LOGGER_PROGRESS, "Total track plans " + std::to_string(nbplan));
|
||||
|
||||
return trackPlans;
|
||||
}
|
||||
|
||||
void SolutionPoolManager::provide(std::vector<SASolution>&& solutions)
|
||||
{
|
||||
std::move(solutions.begin(), solutions.end(), std::back_inserter(pool));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
#ifndef SOLUTIONPOOLMANAGER_HPP
|
||||
#define SOLUTIONPOOLMANAGER_HPP
|
||||
|
||||
#include "Solution.hpp"
|
||||
#include "TrackPlan.hpp"
|
||||
#include <vector>
|
||||
|
||||
namespace solverlib {
|
||||
class SolutionPoolManager{
|
||||
std::vector<SASolution> pool;
|
||||
public:
|
||||
|
||||
void provide(std::vector<SASolution>&& solutions);
|
||||
|
||||
std::vector<TrackPlan> transformSolutionsIntoUniqueTrackPlans();
|
||||
|
||||
std::vector<TrackPlan> getTrackPlansFromSolution(SASolution& sol, bool withTrash = false);
|
||||
|
||||
const std::vector<SASolution>& getSolutions() const {return pool;};
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,130 @@
|
||||
#ifndef TRACKPLAN_HPP
|
||||
#define TRACKPLAN_HPP
|
||||
#include "../../../General/Model/Decision.hpp"
|
||||
#include "../../../General/Model/STFMockInstance.hpp"
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include "sourceSolTrPlan.hpp"
|
||||
|
||||
namespace solverlib {
|
||||
|
||||
using namespace modellib;
|
||||
|
||||
struct TrackPlan{
|
||||
ESourceTrackPlan source;
|
||||
bool isTrash = false;
|
||||
std::unordered_map<unsigned short, Decision> schedule;
|
||||
unsigned short track;
|
||||
unsigned int cost;
|
||||
unsigned int diagCost;
|
||||
std::shared_ptr<STFMockInstance> mock = nullptr;
|
||||
|
||||
bool isJobOnTrack(unsigned short job){return schedule.find(job) != schedule.end();};
|
||||
|
||||
bool isRameInScheduleAfterDate(unsigned int rame, unsigned int dateToCheck = 0)
|
||||
{
|
||||
return std::find_if(schedule.begin(), schedule.end(), [&](std::pair<unsigned short, Decision> jobDec){
|
||||
return STFMockInstance::rameOfOperations[jobDec.first] == rame && jobDec.second.lastCreneau.first >= dateToCheck;
|
||||
}) != schedule.end();
|
||||
}
|
||||
|
||||
bool operator<(const TrackPlan& other) const{
|
||||
if (track != other.track) return track < other.track;
|
||||
if (isTrash != other.isTrash) return isTrash < other.isTrash;
|
||||
if (cost != other.cost) return cost < other.cost;
|
||||
if (diagCost != other.diagCost) return diagCost < other.diagCost;
|
||||
//if (source != other.source) return source < other.source;
|
||||
//if (mock != other.mock) return mock < other.mock;
|
||||
|
||||
|
||||
if (schedule.size() != other.schedule.size())
|
||||
return schedule.size() < other.schedule.size();
|
||||
|
||||
if(schedule != other.schedule)
|
||||
{
|
||||
for (const auto& [jobId, dec] : schedule) {
|
||||
auto it = other.schedule.find(jobId);
|
||||
if (it == other.schedule.end()) return true;
|
||||
if(dec < it->second) return true;
|
||||
if(it->second < dec) return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool operator==(const TrackPlan& other) const
|
||||
{
|
||||
return track == other.track
|
||||
&& isTrash == other.isTrash
|
||||
&& cost == other.cost
|
||||
&& diagCost == other.diagCost
|
||||
//&& source == other.source
|
||||
//&& mock == other.mock
|
||||
&& schedule == other.schedule;
|
||||
//return !(*this < other || other < *this);
|
||||
}
|
||||
|
||||
void shiftAndRecomputeCostsOnMachine(unsigned short empV)
|
||||
{
|
||||
if(!isTrash)
|
||||
{
|
||||
std::vector<unsigned short> seq;
|
||||
for(auto& job : schedule)
|
||||
{
|
||||
if(job.second.empV == empV)
|
||||
seq.push_back(job.first);
|
||||
}
|
||||
if(seq.size() != 1 && !seq.empty())
|
||||
{
|
||||
std::sort(seq.begin(), seq.end(), [&](auto job1, auto job2){
|
||||
const Decision& dec_a = schedule.at(job1);
|
||||
const Decision& dec_b = schedule.at(job2);
|
||||
return dec_a.lastCreneau.first < dec_b.lastCreneau.first;
|
||||
});
|
||||
}
|
||||
|
||||
auto creneauTrack = STFMockInstance::machines[empV]->getDispo();
|
||||
unsigned short lastEnd = 0;
|
||||
for(auto& job : seq)
|
||||
{
|
||||
Decision& new_dec = schedule.at(job);
|
||||
auto creneauJob = mock->trajectoryStops[new_dec.empR].getDispoStop();
|
||||
auto match = CreneauHoraire::checkSlotsCompatibility(creneauJob, creneauTrack);
|
||||
if(match.first)
|
||||
{
|
||||
new_dec.lastCreneau = {
|
||||
std::max(match.second.first, lastEnd),
|
||||
std::max(match.second.first, lastEnd)+ new_dec.rejected*STFMockInstance::jobs[job]->getDureeDiag() + !new_dec.rejected*STFMockInstance::jobs[job]->getDuree()
|
||||
};
|
||||
lastEnd = std::max(match.second.first, lastEnd)+ new_dec.rejected*STFMockInstance::jobs[job]->getDureeDiag() + !new_dec.rejected*STFMockInstance::jobs[job]->getDuree();
|
||||
}
|
||||
}
|
||||
}
|
||||
evaluate();
|
||||
}
|
||||
|
||||
void evaluate()
|
||||
{
|
||||
cost = 0;
|
||||
diagCost = 0;
|
||||
for(auto& op : schedule)
|
||||
{
|
||||
if(op.second.excluded)
|
||||
{
|
||||
cost += STFMockInstance::jobs[op.first]->getPoidsRetard()*MAXIMUM_TIME_OFFSET;
|
||||
}
|
||||
else {
|
||||
cost += STFMockInstance::jobs[op.first]->getPoidsRetard()*op.second.lastCreneau.first;
|
||||
}
|
||||
|
||||
if(op.second.rejected)
|
||||
{
|
||||
diagCost += STFMockInstance::jobs[op.first]->getPoidsRejet();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,12 @@
|
||||
#ifndef SOURCESOLTRPLAN_HPP
|
||||
#define SOURCESOLTRPLAN_HPP
|
||||
|
||||
enum class ESourceTrackPlan{
|
||||
SimAn,
|
||||
DyProg,
|
||||
ListHeu,
|
||||
RBS,
|
||||
Fake
|
||||
};
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user