initialCommit 4 ans en retard

This commit is contained in:
tom
2026-06-03 22:48:34 +02:00
commit 4fc8a7be89
163 changed files with 28981 additions and 0 deletions
@@ -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