From 7a89ac7ae6fc6230a973de188a318289d91bb2b7 Mon Sep 17 00:00:00 2001 From: tom Date: Wed, 24 Jun 2026 12:51:33 +0200 Subject: [PATCH] Modifs de SimAnn pour multistart --- src/General/Model/Planification.cpp | 35 ++ src/General/Model/Planification.h | 2 + .../TrackPlansILP/DynamicProgramming.cpp | 132 ++++---- .../TrackPlansILP/DynamicProgramming.hpp | 8 +- .../Solver/TrackPlansILP/ILPTrackPlans.cpp | 72 +++- src/OrdoCorr/Solver/TrackPlansILP/Penalty.hpp | 37 ++- .../TrackPlansILP/SimulatedAnnealing.cpp | 307 +++++++++++++----- .../TrackPlansILP/SimulatedAnnealing.hpp | 23 +- .../Solver/TrackPlansILP/Solution.hpp | 8 +- .../TrackPlansILP/SolutionPoolManager.cpp | 84 +++-- .../TrackPlansILP/SolutionPoolManager.hpp | 101 +++++- .../Solver/TrackPlansILP/sourceSolTrPlan.hpp | 3 +- src/OrdoCorr/Solver/solverAPI.cpp | 243 +++++++------- 13 files changed, 729 insertions(+), 326 deletions(-) diff --git a/src/General/Model/Planification.cpp b/src/General/Model/Planification.cpp index 34fc475..6f27027 100644 --- a/src/General/Model/Planification.cpp +++ b/src/General/Model/Planification.cpp @@ -4,6 +4,7 @@ #include "STFMockInstance.hpp" #include #include +#include #include #include namespace modellib { @@ -242,4 +243,38 @@ namespace modellib { } return map; } + + std::vector> Planification::getVectorOfDecicions() + { + std::vector> vec(STFMockInstance::jobs.size(), std::nullopt); + + for(auto& opPlan : this->getOperationsPlan()) + { + vec[opPlan.getOperationRequise()] = { + opPlan.getRentree(), + state->trajectoryStops[opPlan.getRentree()].getDispoStop(), + opPlan.getTrackInterval(), + opPlan.getRejete(), + false, + opPlan.getVoie(), + opPlan.getSite(), + opPlan.getCreneau() + }; + } + + for(auto& opPlan : this->getOpImplanifiables()) + { + vec[opPlan] = { + 0, + CreneauHoraire(), + 0, + false, + true, + 0, + 0, + {0,0} + }; + } + return vec; + } } \ No newline at end of file diff --git a/src/General/Model/Planification.h b/src/General/Model/Planification.h index b637b7e..bd8addc 100644 --- a/src/General/Model/Planification.h +++ b/src/General/Model/Planification.h @@ -5,6 +5,7 @@ #include "CroisementUM.hpp" #include "PlanificationStats.hpp" #include "RecompoUM.h" +#include #include #include namespace modellib { @@ -118,6 +119,7 @@ class STFMockInstance; nlohmann::json to_json(); std::unordered_map getMapOfDecisions(); + std::vector> getVectorOfDecicions(); }; } diff --git a/src/OrdoCorr/Solver/TrackPlansILP/DynamicProgramming.cpp b/src/OrdoCorr/Solver/TrackPlansILP/DynamicProgramming.cpp index a39cc21..c90b3b0 100644 --- a/src/OrdoCorr/Solver/TrackPlansILP/DynamicProgramming.cpp +++ b/src/OrdoCorr/Solver/TrackPlansILP/DynamicProgramming.cpp @@ -25,7 +25,7 @@ namespace solverlib { auto tryInsert = [&](unsigned int insertedJob, unsigned int removedJob) -> std::optional { - if(solutionPool[0].decisions.find(insertedJob) == solutionPool[0].decisions.end() || solutionPool[0].decisions.find(removedJob) == solutionPool[0].decisions.end()) + if(solutionPool[0].decisions[insertedJob] == std::nullopt || solutionPool[0].decisions[removedJob] == std::nullopt) return std::nullopt; if(insertedJob >= solutionPool[0].mock->jobDispoVoiesRames.size() || removedJob >= solutionPool[0].mock->jobDispoVoiesRames.size()) @@ -40,20 +40,20 @@ namespace solverlib { [&](auto& el) -> bool { DispVoieRame& disp = solutionPool[0].mock->dispoVoiesRames[el]; - if(disp.dispoVoie != decRemoved.empV) + 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()); + 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); + CreneauHoraire crenRemoved (SolverDate::getDateDebut() + (*decRemoved).lastCreneau.first, + SolverDate::getDateDebut() + (*decRemoved).lastCreneau.second); if(!CreneauHoraire::checkSlotsCompatibility(crenInserted, crenRemoved).first) return false; @@ -71,70 +71,74 @@ namespace solverlib { std::vector> schedule; long oldCost = 0; auto disp = solutionPool[0].mock->dispoVoiesRames[*dispFound]; - std::unordered_map oldDec; + std::vector> oldDec; - for(auto& [id, dec] : solutionPool[0].decisions) + unsigned int op_id = 0; + for(auto& dec : solutionPool[0].decisions) { - if(dec.excluded) continue; - if(dec.empV != decRemoved.empV) continue; + if((*dec).excluded) continue; + if((*dec).empV != (*decRemoved).empV) continue; - oldCost += STFMockInstance::jobs[id]->getPoidsRetard() * dec.lastCreneau.first; - oldDec[id] = dec; - if(id == removedJob) + oldCost += STFMockInstance::jobs[op_id]->getPoidsRetard() * (*dec).lastCreneau.first; + oldDec[op_id] = dec; + if(op_id == removedJob) { // Remplacer removedJob par insertedJob avec le même créneau - Decision decInsertedCopy = decInserted; - decInsertedCopy.lastCreneau = decRemoved.lastCreneau; - decInsertedCopy.empV = decRemoved.empV; + Decision decInsertedCopy = (*decInserted); + decInsertedCopy.lastCreneau = (*decRemoved).lastCreneau; + decInsertedCopy.empV = (*decRemoved).empV; decInsertedCopy.empR = disp.dispoRame; - decInsertedCopy.voie = decRemoved.voie; - decInsertedCopy.site = decRemoved.site; + 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); + schedule.emplace_back(op_id, *dec); } + ++op_id; } 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] + unsigned short lastEnd = STFMockInstance::machines[(*decRemoved).empV] ->getDispo().getDebut().getRelativeDate(); long newCost = 0; - std::unordered_map newSchedule; - for(auto& [id, dec] : schedule) + std::vector> newSchedule; + op_id = 0; + for(auto& dec : schedule) { - auto TrStop = solutionPool[0].mock->trajectoryStops[dec.empR].getDispoStop(); + auto TrStop = solutionPool[0].mock->trajectoryStops[dec.second.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(); + STFMockInstance::machines[dec.second.empV]->getDispo()).second; + auto duration = !dec.second.rejected * STFMockInstance::jobs[op_id]->getDuree() + + dec.second.rejected * STFMockInstance::jobs[op_id]->getDureeDiag(); - newSchedule[id] = dec; - newSchedule[id].lastCreneau.first = std::max(lastEnd, match.first); + newSchedule[op_id] = dec.second; + (*newSchedule[op_id]).lastCreneau.first = std::max(lastEnd, match.first); - lastEnd = newSchedule[id].lastCreneau.first + duration; + lastEnd = (*newSchedule[op_id]).lastCreneau.first + duration; - newSchedule[id].lastCreneau.second = lastEnd; + (*newSchedule[op_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)+ "]"; + std::string fi = " - Final[" + std::to_string((*newSchedule[op_id]).lastCreneau.first) + " " + std::to_string((*newSchedule[op_id]).lastCreneau.second)+ "]"; loggerlib::Logger::systemNotify(loggerlib::LOGGER_DEBUG, ma + fi); - newCost += STFMockInstance::jobs[id]->getPoidsRetard() + newCost += STFMockInstance::jobs[op_id]->getPoidsRetard() * std::max(lastEnd, match.first); + ++op_id; } - return EdgeSolution{static_cast(((oldCost + decInserted.lastCreneau.first * STFMockInstance::jobs[insertedJob]->getPoidsRetard()) - newCost)), newSchedule, oldDec}; + return EdgeSolution{static_cast(((oldCost + (*decInserted).lastCreneau.first * STFMockInstance::jobs[insertedJob]->getPoidsRetard()) - newCost)), newSchedule, oldDec}; } return std::nullopt; }; @@ -151,7 +155,7 @@ namespace solverlib { auto& decJob = solutionPool[0].decisions[job]; auto& decOtherJob = solutionPool[0].decisions[other_job]; - if(decJob.excluded || decOtherJob.excluded || decJob.empV == decOtherJob.empV) + if((*decJob).excluded || (*decOtherJob).excluded || (*decJob).empV == (*decOtherJob).empV) continue; // Arc job → other_job : insérer job à la place de other_job @@ -179,23 +183,25 @@ namespace solverlib { } } - std::pair DynamicProgramming::evaluate(const std::unordered_map& decs) + std::pair DynamicProgramming::evaluate(const std::vector>& decs) { unsigned int cost = 0; unsigned int diagCost = 0; + unsigned int id = 0; for(auto& dec : decs) { - if(dec.second.excluded) + if((*dec).excluded) { - cost += MAXIMUM_TIME_OFFSET * modellib::STFMockInstance::jobs[dec.first]->getPoidsRetard(); + cost += MAXIMUM_TIME_OFFSET * modellib::STFMockInstance::jobs[id]->getPoidsRetard(); } else - cost += modellib::STFMockInstance::jobs[dec.first]->getPoidsRetard() * dec.second.lastCreneau.first; + cost += modellib::STFMockInstance::jobs[id]->getPoidsRetard() * (*dec).lastCreneau.first; - if(dec.second.rejected) + if((*dec).rejected) { - diagCost += modellib::STFMockInstance::jobs[dec.first]->getPoidsRejet(); + diagCost += modellib::STFMockInstance::jobs[id]->getPoidsRejet(); } + ++id; } return {cost, diagCost}; } @@ -236,26 +242,27 @@ namespace solverlib { auto& decInserted = solution.decisions[insertedJob]; auto& decRemoved = solution.decisions[removedJob]; - - for(auto& [id, dec] : solution.decisions) + unsigned int id = 0; + for(auto& dec : solution.decisions) { - if(dec.excluded || dec.empV != decRemoved.empV) continue; + 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; + 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)); + newlyCreatedDecisionsFromCycle[(*decRemoved).empV].insert(std::make_pair(insertedJob, decInsertedCopy)); } else { - newlyCreatedDecisionsFromCycle[decRemoved.empV].insert(std::make_pair(id, dec)); + newlyCreatedDecisionsFromCycle[(*decRemoved).empV].insert(std::make_pair(id, *dec)); } + ++id; } } @@ -296,7 +303,7 @@ namespace solverlib { } // Mettre à jour la solution courante immédiatement - solution.decisions.insert_or_assign(id, dec); + solution.decisions[id] = dec; } if(!stepFeasible) @@ -355,24 +362,25 @@ namespace solverlib { auto& decInserted = solution.decisions[insertedJob]; auto& decRemoved = solution.decisions[removedJob]; - for(auto& [id, dec] : solution.decisions) + unsigned int id = 0; + for(auto& dec : solution.decisions) { - if(dec.excluded || dec.empV != decRemoved.empV) continue; + 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; + 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)); + newlyCreatedDecisionsFromCycle[(*decRemoved).empV].insert(std::make_pair(insertedJob, decInsertedCopy)); } else { - newlyCreatedDecisionsFromCycle[decRemoved.empV].insert(std::make_pair(id, dec)); + newlyCreatedDecisionsFromCycle[(*decRemoved).empV].insert(std::make_pair(id, *dec)); } } } @@ -414,7 +422,7 @@ namespace solverlib { } // Mettre à jour la solution courante immédiatement - solution.decisions.insert_or_assign(id, dec); + solution.decisions[id] = dec; } if(!stepFeasible) diff --git a/src/OrdoCorr/Solver/TrackPlansILP/DynamicProgramming.hpp b/src/OrdoCorr/Solver/TrackPlansILP/DynamicProgramming.hpp index 770fd4b..09d852a 100644 --- a/src/OrdoCorr/Solver/TrackPlansILP/DynamicProgramming.hpp +++ b/src/OrdoCorr/Solver/TrackPlansILP/DynamicProgramming.hpp @@ -2,7 +2,9 @@ #define DYNAMICPROGRAMMING_HPP #include +#include #include +#include #include "Random.hpp" #include "Solution.hpp" @@ -16,8 +18,8 @@ namespace solverlib { private: struct EdgeSolution { long gain; - std::unordered_map updatedDecisions; - std::unordered_map oldDecisions; + std::vector> updatedDecisions = std::vector>(STFMockInstance::jobs.size(), std::nullopt); + std::vector> oldDecisions = std::vector>(STFMockInstance::jobs.size(), std::nullopt); }; std::vector solutionPool; @@ -37,7 +39,7 @@ namespace solverlib { void buildGraph(); long recursion(unsigned int startingNode, unsigned int node, unsigned int cycleLength, std::vector& states, std::vector>& currentPath, std::vector>>& allPaths); SASolution solve(); - std::pair evaluate(const std::unordered_map& decs); + std::pair evaluate(const std::vector>& decs); std::vector&& getSolutionPool(){return std::move(solutionPool);}; diff --git a/src/OrdoCorr/Solver/TrackPlansILP/ILPTrackPlans.cpp b/src/OrdoCorr/Solver/TrackPlansILP/ILPTrackPlans.cpp index 70f420e..b571702 100644 --- a/src/OrdoCorr/Solver/TrackPlansILP/ILPTrackPlans.cpp +++ b/src/OrdoCorr/Solver/TrackPlansILP/ILPTrackPlans.cpp @@ -29,7 +29,7 @@ namespace solverlib { m_env.start(); m_model = std::make_unique(m_env); m_model->set(GRB_DoubleParam_TuneTimeLimit, 3600); - m_model->set(GRB_DoubleParam_TimeLimit, 600); + m_model->set(GRB_DoubleParam_TimeLimit, 1800); /* MIPFocus 2 @@ -228,7 +228,8 @@ namespace solverlib { { m_model->optimize(); //printSol(); - return buildSolution(); + auto plan = buildSolution(); + return plan; } std::optional ILPTrackPlans::buildSolution() @@ -392,7 +393,7 @@ namespace solverlib { void ILPTrackPlans::warmStart(std::vector& plans) { std::vector varToSet; - + std::set jobs; for(auto& plan : plans) { if(plan.isTrash) @@ -403,6 +404,7 @@ namespace solverlib { if(find != trackPlans.end()) { varToSet.push_back(std::distance(trackPlans.begin(), find)); + jobs.insert(plan.schedule.begin()->first); } } else { @@ -412,9 +414,73 @@ namespace solverlib { if(find != trackPlans.end()) { varToSet.push_back(std::distance(trackPlans.begin(), find)); + for(auto& sche: plan.schedule) + { + jobs.insert(sche.first ); + } + } + else { + // DEBUG : pourquoi aucun match exact n'a été trouvé pour ce plan + std::cout << "[warmStart][MISS] track=" << plan.track + << " cost=" << plan.cost << " diagCost=" << plan.diagCost + << " nbJobs=" << plan.schedule.size() << std::endl; + + // 1) Y a-t-il au moins une colonne sur la meme track ? + bool sameTrackFound = false; + for(auto& p : trackPlans) + { + if(!p.isTrash && p.track == plan.track) + { + sameTrackFound = true; + std::cout << " candidate same track: cost=" << p.cost + << " diagCost=" << p.diagCost + << " nbJobs=" << p.schedule.size() << std::endl; + + if(p.schedule.size() != plan.schedule.size()) + { + std::cout << " -> mismatch nbJobs" << std::endl; + continue; + } + if(p.cost != plan.cost) + std::cout << " -> mismatch cost (" << p.cost << " vs " << plan.cost << ")" << std::endl; + if(p.diagCost != plan.diagCost) + std::cout << " -> mismatch diagCost (" << p.diagCost << " vs " << plan.diagCost << ")" << std::endl; + + // 2) Comparer job par job + for(auto& [jobId, dec] : plan.schedule) + { + auto it = p.schedule.find(jobId); + if(it == p.schedule.end()) + { + std::cout << " -> job " << jobId << " ABSENT de la colonne candidate" << std::endl; + continue; + } + const Decision& d2 = it->second; + if(!(dec == d2)) + { + std::cout << " -> job " << jobId << " decision differente: " + << "empR(" << dec.empR << "/" << d2.empR << ") " + << "empV(" << dec.empV << "/" << d2.empV << ") " + << "voie(" << (int)dec.voie << "/" << (int)d2.voie << ") " + << "site(" << (int)dec.site << "/" << (int)d2.site << ") " + << "rejected(" << dec.rejected << "/" << d2.rejected << ") " + << "excluded(" << dec.excluded << "/" << d2.excluded << ") " + << "lastCreneau(" << dec.lastCreneau.first << "-" << dec.lastCreneau.second + << " / " << d2.lastCreneau.first << "-" << d2.lastCreneau.second << ")" + << std::endl; + } + } + } + } + if(!sameTrackFound) + std::cout << " -> AUCUNE colonne du pool ILP n'existe pour la track " << plan.track << std::endl; } } } + /*if(jobs.size() == STFMockInstance::jobs.size()) + std::cout << "C OK" << std::endl; + else + std::cout << "C PAS OK" << std::endl;*/ for(auto& var : x_ks) diff --git a/src/OrdoCorr/Solver/TrackPlansILP/Penalty.hpp b/src/OrdoCorr/Solver/TrackPlansILP/Penalty.hpp index 778a3c6..9702461 100644 --- a/src/OrdoCorr/Solver/TrackPlansILP/Penalty.hpp +++ b/src/OrdoCorr/Solver/TrackPlansILP/Penalty.hpp @@ -1,40 +1,43 @@ #ifndef PENALTY_HPP #define PENALTY_HPP -#include #include +#include namespace solverlib { - enum class EPenaltyType { + enum class EPenaltyType : int { TIME_WINDOW_OVERRUN, // job dépasse sa fenêtre }; + using ArrayLambda = std::array; struct Penalty { - std::unordered_map components; + + ArrayLambda components = {0.0}; void add(EPenaltyType type, double value) { - components[type] += value; + components[(int)type] += value; } void subtract(EPenaltyType type, double value) { - components[type] = std::max(0.0, components[type] - value); + components[(int)type] = std::max(0.0, components[(int)type] - value); } // Coût agrégé pondéré — les lambdas sont injectés depuis l'extérieur - double weighted(const std::unordered_map& lambdas) const { + double weighted(const ArrayLambda& lambdas) const { double total = 0.0; - for (auto& [type, val] : components) + unsigned int type = 0; + for (auto& val : components) { - auto it = lambdas.find(type); - if (it != lambdas.end()) - total += it->second * val; + auto it = lambdas[type]; + total += it * val; + type++; } return total; } double total() const { double sum = 0.0; - for (auto& [_, v] : components) sum += v; + for (auto& v : components) sum += v; return sum; } @@ -42,15 +45,23 @@ namespace solverlib { Penalty operator+(const Penalty& o) const { Penalty res = *this; - for (auto& [type, val] : o.components) + unsigned int type= 0; + for (auto& val : o.components) + { res.components[type] += val; + type++; + } return res; } Penalty operator-(const Penalty& o) const { Penalty res = *this; - for (auto& [type, val] : o.components) + unsigned int type = 0; + for (auto& val : o.components) + { res.components[type] = std::max(0.0, res.components[type] - val); + type++; + } return res; } }; diff --git a/src/OrdoCorr/Solver/TrackPlansILP/SimulatedAnnealing.cpp b/src/OrdoCorr/Solver/TrackPlansILP/SimulatedAnnealing.cpp index 15d4ec1..0d84624 100644 --- a/src/OrdoCorr/Solver/TrackPlansILP/SimulatedAnnealing.cpp +++ b/src/OrdoCorr/Solver/TrackPlansILP/SimulatedAnnealing.cpp @@ -22,7 +22,7 @@ namespace solverlib { using namespace random; - bool StatSimulatedAnnealing::activate = true; + bool StatSimulatedAnnealing::activate = false; bool SimulatedAnnealing::withDynProg = false; bool SimulatedAnnealing::authorizeInfeasible = true; @@ -39,7 +39,17 @@ namespace solverlib { }; - SimulatedAnnealing::SimulatedAnnealing(std::unordered_map& decs, std::shared_ptr mock, ESourceTrackPlan source) + /*SimulatedAnnealing::SimulatedAnnealing(std::unordered_map& decs, std::shared_ptr mock, ESourceTrackPlan source) + { + randomEngine = solverlib::random::makeEngine(); + maxCostOfJob = (*std::max_element(STFMockInstance::jobs.begin(), STFMockInstance::jobs.end(), [&](auto op1, auto op2){return op1->getPoidsRetard() < op2->getPoidsRetard();}))->getPoidsRetard(); + setPenaltyWeights(); + addSolutionToPool(decs, mock, source); + for (unsigned int i = 0; i< STFMockInstance::machines.size(); ++i) + solutions[0].penaltyPerMachine[i] = Penalty{}; + }*/ + + SimulatedAnnealing::SimulatedAnnealing(std::vector>& decs, std::shared_ptr mock, ESourceTrackPlan source) { randomEngine = solverlib::random::makeEngine(); maxCostOfJob = (*std::max_element(STFMockInstance::jobs.begin(), STFMockInstance::jobs.end(), [&](auto op1, auto op2){return op1->getPoidsRetard() < op2->getPoidsRetard();}))->getPoidsRetard(); @@ -49,7 +59,19 @@ namespace solverlib { solutions[0].penaltyPerMachine[i] = Penalty{}; } - void SimulatedAnnealing::addSolutionToPool(std::unordered_map& decs, std::shared_ptr mock, ESourceTrackPlan source, bool isPutFirst) + + /*void SimulatedAnnealing::addSolutionToPool(std::unordered_map& decs, std::shared_ptr 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}); + } + }*/ + + void SimulatedAnnealing::addSolutionToPool(std::vector>& decs, std::shared_ptr mock, ESourceTrackPlan source, bool isPutFirst) { auto costs = evaluate(decs); if(!isPutFirst) @@ -61,7 +83,7 @@ namespace solverlib { } - std::pair SimulatedAnnealing::evaluate(const std::unordered_map& decs) + /*std::pair SimulatedAnnealing::evaluate(const std::unordered_map& decs) { unsigned int cost = 0; unsigned int diagCost = 0; @@ -80,6 +102,29 @@ namespace solverlib { } } return {cost, diagCost}; + }*/ + + std::pair SimulatedAnnealing::evaluate(const std::vector>& decs) + { + unsigned int cost = 0; + unsigned int diagCost = 0; + unsigned short id = 0; + for(auto& dec : decs) + { + if((*dec).excluded) + { + cost += MAXIMUM_TIME_OFFSET * modellib::STFMockInstance::jobs[id]->getPoidsRetard(); + } + else + cost += modellib::STFMockInstance::jobs[id]->getPoidsRetard() * (*dec).lastCreneau.first; + + if((*dec).rejected) + { + diagCost += modellib::STFMockInstance::jobs[id]->getPoidsRejet(); + } + ++id; + } + return {cost, diagCost}; } EMovingOperators SimulatedAnnealing::pick_operator() { @@ -136,6 +181,9 @@ namespace solverlib { rate = cooling_rate; std::uniform_real_distribution uniform(0.0, 1.0); + auto bestPlansBeg = pool.getTrackPlansFromSolution(best, false); + for (auto& tp : bestPlansBeg) pool.addTrackPlan(std::move(tp)); + while (T > Tmin + 10e-6) { for (int i = 0; i < iterations_per_temp; ++i) @@ -167,7 +215,6 @@ namespace solverlib { { stats.addFailInfo(op, getP(delta, op), diff, delta, T, neighbor->penalty.isFeasible()); } - //TODO AJOUTER AUTORISER AVEC PENALITE LES INFEASABLES => NECESSITE DE METTRE À JOUR LA GENERATION TRACK PLAN POUR LE ILP ET IGNORER LES TRACKPLANS INF if (delta < 0 || uniform(randomEngine) < getP(delta, op)) { @@ -179,7 +226,12 @@ namespace solverlib { cost_best = cost_cur; } - solutions.push_back(current); + if (current.penalty.isFeasible()) { + auto tps = pool.getTrackPlansFromSolution(current, false); + for (auto& tp : tps) + pool.addTrackPlan(std::move(tp)); // dédup inline + } + //solutions.push_back(current); } } @@ -187,19 +239,70 @@ namespace solverlib { //loggerlib::Logger::systemNotify(loggerlib::LOGGER_PROGRESS, "Temperature : " + std::to_string(T)); //loggerlib::Logger::systemNotify(loggerlib::LOGGER_PROGRESS, "Solution pool : " + std::to_string(solutions.size())); } + + auto bestPlansFin = pool.getTrackPlansFromSolution(best, false); + for (auto& tp : bestPlansFin) pool.addTrackPlan(std::move(tp)); //loggerlib::Logger::systemNotify(loggerlib::LOGGER_PROGRESS, "Best solution : " + std::to_string(best.cost)); return best; } + SASolution SimulatedAnnealing::solveMultiStart( + double T_max, double T_min, double cooling_rate, + int iterations_per_temp, int n_restarts) + { + SASolution globalBest = solve(T_max, T_min, cooling_rate, iterations_per_temp); + loggerlib::Logger::systemNotify(loggerlib::LOGGER_PROGRESS, "Best solution is " + std::string((globalBest.penalty.isFeasible() ? "feasible" : "not feasible"))); + loggerlib::Logger::systemNotify(loggerlib::LOGGER_PROGRESS, "Best solution : " + std::to_string(globalBest.cost)); + + for (int r = 0; r < n_restarts; ++r) + { + loggerlib::Logger::systemNotify(loggerlib::LOGGER_PROGRESS, "Restart :" + std::to_string(r)); + + // Perturbation : repartir du meilleur mais bruité + SASolution perturbed = perturbSolution(globalBest); + solutions[0] = perturbed; + + // Re-run avec température réduite (exploitation locale) + double t_restart = T_max * std::pow(0.5, r % 4); // alterne les températures + SASolution localBest = solve(t_restart, T_min, cooling_rate, iterations_per_temp); + + if (localBest.penalty.isFeasible() && localBest.cost < globalBest.cost) + globalBest = localBest; + + loggerlib::Logger::systemNotify(loggerlib::LOGGER_PROGRESS, "Best solution restart is " + std::string((localBest.penalty.isFeasible() ? "feasible" : "not feasible"))); + loggerlib::Logger::systemNotify(loggerlib::LOGGER_PROGRESS, "Best solution restart : " + std::to_string(localBest.cost)); + } + + loggerlib::Logger::systemNotify(loggerlib::LOGGER_PROGRESS, "Best solution restart : " + std::to_string(globalBest.cost)); + + return globalBest; + } + + SASolution SimulatedAnnealing::perturbSolution(const SASolution& sol) { + SASolution perturbed = sol; + // Forcer N opérateurs aléatoires pour s'éloigner du bassin d'attraction + int n_kicks = 3 + randomEngine() % 5; + for (int k = 0; k < n_kicks; ++k) { + auto op = pick_operator(); + auto neighbor = apply_operator(perturbed, op); + if (neighbor.has_value()) + perturbed = std::move(*neighbor); + } + return perturbed; + } + + + + double SimulatedAnnealing::effectiveCost(const SASolution& s) const { return s.fictiveCost + s.penalty.weighted(effectiveLambdas()); } - std::unordered_map SimulatedAnnealing::effectiveLambdas() const { + ArrayLambda SimulatedAnnealing::effectiveLambdas() const { double p = progress(); return { - {EPenaltyType::TIME_WINDOW_OVERRUN, std::exp(8*(p-0.2))-0.8}, + {std::exp(18*(p-0.3))},//(int)EPenaltyType::TIME_WINDOW_OVERRUN, }; } @@ -226,7 +329,7 @@ namespace solverlib { case EMovingOperators::DYN_PROG: break; } - return std::exp(-delta/T); + return std::exp(-delta/(T)); } //OPERATEURS @@ -246,19 +349,24 @@ namespace solverlib { if (!mock) return std::nullopt; std::vector active_ops; - for (auto& [op_id, dec] : sol.decisions) - if (!dec.excluded) active_ops.push_back(op_id); + unsigned int id = 0; + for (auto& dec : sol.decisions) + { + if (!(*dec).excluded) + active_ops.push_back(id); + ++id; + } if (active_ops.size() < 2) return std::nullopt; std::uniform_int_distribution dist(0, (int)active_ops.size() - 1); unsigned short op_a = active_ops[dist(randomEngine)]; - const Decision& dec_a = sol.decisions.at(op_a); + const Decision& dec_a = *sol.decisions[op_a]; std::vector 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); + const Decision& dec_b = *sol.decisions[op_id]; if (dec_b.empV != dec_a.empV) continue; seq_swap.push_back({ @@ -268,8 +376,8 @@ namespace solverlib { 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); + const Decision& dec_a = *sol.decisions[job1]; + const Decision& dec_b = *sol.decisions[job2]; return dec_a.lastCreneau.first < dec_b.lastCreneau.first; }); @@ -292,16 +400,14 @@ namespace solverlib { std::vector> jobsSeq; jobsSeq.reserve(seqCop.size()); for (auto& job_id : seqCop) - jobsSeq.push_back({job_id, sol.decisions.at(job_id)}); + jobsSeq.push_back({job_id, *sol.decisions[job_id]}); - Penalty oldPen = sol.penaltyPerMachine.count(dec_a.empV) - ? sol.penaltyPerMachine.at(dec_a.empV) - : Penalty{}; + Penalty oldPen = sol.penaltyPerMachine[dec_a.empV]; unsigned int oldCost = 0; for (auto& job_id : seq_swap) oldCost += STFMockInstance::jobs[job_id]->getPoidsRetard() - * sol.decisions.at(job_id).lastCreneau.first; + * (*sol.decisions[job_id]).lastCreneau.first; Penalty newPen; bool feasible = true; auto res = checkSequence(jobsSeq, dec_a.empV, newPen, feasible); @@ -327,13 +433,18 @@ namespace solverlib { if (!mock) return std::nullopt; std::vector active_ops; - for (auto& [op_id, dec] : sol.decisions) - if (!dec.excluded) active_ops.push_back(op_id); + unsigned int id = 0; + for (auto& dec : sol.decisions) + { + if (!(*dec).excluded) + active_ops.push_back(id); + ++id; + } if (active_ops.size() < 2) return std::nullopt; std::uniform_int_distribution dist(0, (int)active_ops.size() - 1); unsigned short op_a = active_ops[dist(randomEngine)]; - const Decision& dec_a = sol.decisions.at(op_a); + const Decision& dec_a = *sol.decisions[op_a]; struct SwapCandidate { unsigned short op_a; @@ -347,7 +458,7 @@ namespace solverlib { //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); + const Decision& dec_b = *sol.decisions[op_id]; if (dec_b.empV == dec_a.empV) continue; // Vérifier compatibilité infrastructure @@ -378,12 +489,12 @@ namespace solverlib { // Tirer deuxième op parmi les candidats std::uniform_int_distribution 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); + const Decision& dec_b = *sol.decisions[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]; + 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; @@ -403,29 +514,29 @@ namespace solverlib { auto oldCrenB = dec_b.lastCreneau; // Pénalités anciennes O(1) - Penalty oldPenA = sol.penaltyPerMachine.count(dec_a.empV) - ? sol.penaltyPerMachine.at(dec_a.empV) : Penalty{}; - Penalty oldPenB = sol.penaltyPerMachine.count(dec_b.empV) - ? sol.penaltyPerMachine.at(dec_b.empV) : Penalty{}; + Penalty oldPenA = sol.penaltyPerMachine[dec_a.empV]; + Penalty oldPenB = sol.penaltyPerMachine[dec_b.empV]; std::vector> jobsEmpVA; std::vector> jobsEmpVB; unsigned int oldCost = dec_a.lastCreneau.first * STFMockInstance::jobs[op_a]->getPoidsRetard() + dec_b.lastCreneau.first * STFMockInstance::jobs[swap.op_b]->getPoidsRetard(); + unsigned int op_id = 0; for(auto& dec : neighbor.decisions) { - if(!dec.second.excluded) + if(!(*dec).excluded) { - if(dec.second.empV == new_dec_a.empV) + if((*dec).empV == new_dec_a.empV) { - jobsEmpVA.push_back({dec.first, dec.second}); - oldCost += dec.second.lastCreneau.first * STFMockInstance::jobs[dec.first]->getPoidsRetard(); + jobsEmpVA.push_back({op_id, (*dec)}); + oldCost += (*dec).lastCreneau.first * STFMockInstance::jobs[op_id]->getPoidsRetard(); } - if(dec.second.empV == new_dec_b.empV) + if((*dec).empV == new_dec_b.empV) { - jobsEmpVB.push_back({dec.first, dec.second}); - oldCost += dec.second.lastCreneau.first * STFMockInstance::jobs[dec.first]->getPoidsRetard(); + jobsEmpVB.push_back({op_id, (*dec)}); + oldCost += (*dec).lastCreneau.first * STFMockInstance::jobs[op_id]->getPoidsRetard(); } } + ++op_id; } std::sort(jobsEmpVA.begin(), jobsEmpVA.end(), [&](auto& el1, auto& el2){ auto cren1 = el1.second.lastCreneau; @@ -481,8 +592,13 @@ namespace solverlib { if (!mock) return std::nullopt; std::vector inactive_ops; - for (auto& [op_id, dec] : sol.decisions) - if (dec.excluded) inactive_ops.push_back(op_id); + unsigned int id = 0; + for (auto& dec : sol.decisions) + { + if ((*dec).excluded) + inactive_ops.push_back(id); + ++id; + } if (inactive_ops.empty()) return std::nullopt; std::uniform_int_distribution dist(0, (int)inactive_ops.size() - 1); @@ -508,7 +624,7 @@ namespace solverlib { //Construire le voisin - try insert SASolution neighbor = sol; - Decision& new_dec_a = neighbor.decisions[op_a]; + 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, @@ -521,23 +637,24 @@ namespace solverlib { new_dec_a.lastCreneau = {0,0}; // Pénalité ancienne O(1) — séquence sans op_a - Penalty oldPen = sol.penaltyPerMachine.count(new_dec_a.empV) - ? sol.penaltyPerMachine.at(new_dec_a.empV) : Penalty{}; + Penalty oldPen = sol.penaltyPerMachine[new_dec_a.empV]; unsigned int oldDiagCost = 0; unsigned int oldCostScheduled = 0; unsigned int newDiagCost = new_dec_a.rejected ? STFMockInstance::jobs[op_a]->getPoidsRejet() : 0; std::vector> jobsEmpVA; + unsigned int op_id = 0; for(auto& dec : neighbor.decisions) { - if(!dec.second.excluded && dec.first != op_a) + if(!(*dec).excluded && op_id != op_a) { - if(dec.second.empV == new_dec_a.empV) + if((*dec).empV == new_dec_a.empV) { - jobsEmpVA.push_back({dec.first, dec.second}); - oldCostScheduled += dec.second.lastCreneau.first * STFMockInstance::jobs[dec.first]->getPoidsRetard(); + jobsEmpVA.push_back({op_id, (*dec)}); + oldCostScheduled += (*dec).lastCreneau.first * STFMockInstance::jobs[op_id]->getPoidsRetard(); } } + ++op_id; } std::sort(jobsEmpVA.begin(), jobsEmpVA.end(), [&](auto& el1, auto& el2){ @@ -566,8 +683,7 @@ namespace solverlib { applySequenceResult(neighbor, seq, *res, newCost, penA, feasA); - double oldFictive = sol.fictiveExcludedCosts.count(op_a) - ? fictiveCostExcluded(op_a) + double oldFictive = neighbor.fictiveExcludedCosts[op_a] > 0.0 ? fictiveCostExcluded(op_a) : MAXIMUM_TIME_OFFSET * STFMockInstance::jobs[op_a]->getPoidsRetard(); unsigned int oldCostExcluded = MAXIMUM_TIME_OFFSET * STFMockInstance::jobs[op_a]->getPoidsRetard(); @@ -576,7 +692,7 @@ namespace solverlib { neighbor.penaltyPerMachine[new_dec_a.empV] = penA; neighbor.isFeasible = neighbor.penalty.isFeasible(); neighbor.fictiveCost = (neighbor.fictiveCost - oldFictive - oldCostScheduled) + newCost; - neighbor.fictiveExcludedCosts.erase(op_a); + neighbor.fictiveExcludedCosts[op_a] = 0.0; neighbor.diagCost = (neighbor.diagCost - oldDiagCost) + newDiagCost; neighbor.cost = (neighbor.cost - oldCostExcluded - oldCostScheduled) + newCost; neighbor.source = ESourceTrackPlan::SimAn; @@ -590,15 +706,21 @@ namespace solverlib { if (!mock) return std::nullopt; std::vector active_ops; - for (auto& [op_id, dec] : sol.decisions) - if (!dec.excluded) active_ops.push_back(op_id); + unsigned int id = 0; + for (auto& dec : sol.decisions) + { + if (!(*dec).excluded) + active_ops.push_back(id); + ++id; + + } if (active_ops.empty()) return std::nullopt; std::uniform_int_distribution dist(0, (int)active_ops.size() - 1); unsigned short op_a = active_ops[dist(randomEngine)]; std::vector dispCandidate; - const Decision& dec_a = sol.decisions.at(op_a); + const Decision& dec_a = *sol.decisions[op_a]; for (auto& disp : mock->jobDispoVoiesRames[op_a]) { if(dec_a.empV == mock->dispoVoiesRames[disp].dispoVoie) @@ -621,7 +743,7 @@ namespace solverlib { //Construire le voisin - try insert SASolution neighbor = sol; - Decision& new_dec_a = neighbor.decisions[op_a]; + 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, @@ -633,10 +755,8 @@ namespace solverlib { new_dec_a.lastCreneau = {0,0}; // Pénalités anciennes O(1) — machine de départ et machine d'arrivée - Penalty oldPenSrc = sol.penaltyPerMachine.count(dec_a.empV) - ? sol.penaltyPerMachine.at(dec_a.empV) : Penalty{}; - Penalty oldPenDst = sol.penaltyPerMachine.count(new_dec_a.empV) - ? sol.penaltyPerMachine.at(new_dec_a.empV) : Penalty{}; + Penalty oldPenSrc = sol.penaltyPerMachine[dec_a.empV]; + Penalty oldPenDst = sol.penaltyPerMachine[new_dec_a.empV]; unsigned int oldCost = dec_a.lastCreneau.first*STFMockInstance::jobs[op_a]->getPoidsRetard(); unsigned int oldDiagCost = dec_a.rejected ? STFMockInstance::jobs[op_a]->getPoidsRejet() : 0; @@ -644,25 +764,26 @@ namespace solverlib { std::vector> jobsEmpVA; std::vector> jobsEmpVB; - + unsigned int op_id = 0; for(auto& dec : neighbor.decisions) { - if(!dec.second.excluded && dec.first != op_a) + if(!(*dec).excluded && op_id != op_a) { - if(dec.second.empV == new_dec_a.empV) + if((*dec).empV == new_dec_a.empV) { - jobsEmpVA.push_back({dec.first, dec.second}); - oldCost += dec.second.lastCreneau.first * STFMockInstance::jobs[dec.first]->getPoidsRetard(); + jobsEmpVA.push_back({op_id, (*dec)}); + oldCost += (*dec).lastCreneau.first * STFMockInstance::jobs[op_id]->getPoidsRetard(); } } - if(!dec.second.excluded) + if(!(*dec).excluded) { - if(dec.second.empV == dec_a.empV) + if((*dec).empV == dec_a.empV) { - jobsEmpVB.push_back({dec.first, dec.second}); - oldCost += dec.second.lastCreneau.first * STFMockInstance::jobs[dec.first]->getPoidsRetard(); + jobsEmpVB.push_back({op_id, (*dec)}); + oldCost += (*dec).lastCreneau.first * STFMockInstance::jobs[op_id]->getPoidsRetard(); } } + ++op_id; } std::sort(jobsEmpVA.begin(), jobsEmpVA.end(), [&](auto& el1, auto& el2){ @@ -719,8 +840,13 @@ namespace solverlib { if (!mock) return std::nullopt; std::vector active_ops; - for (auto& [op_id, dec] : sol.decisions) - if (!dec.excluded) active_ops.push_back(op_id); + unsigned int id = 0; + for (auto& dec : sol.decisions) + { + if (!(*dec).excluded) + active_ops.push_back(id); + ++id; + } if (active_ops.empty()) return std::nullopt; std::uniform_int_distribution dist(0, (int)active_ops.size() - 1); @@ -729,8 +855,8 @@ namespace solverlib { //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]; + const Decision& dec_a = *sol.decisions[op_a]; + Decision& new_dec_a = *neighbor.decisions[op_a]; new_dec_a.rejected = false, new_dec_a.excluded = true; @@ -742,23 +868,24 @@ namespace solverlib { new_dec_a.lastCreneau = {0,0}; // Pénalité ancienne O(1) — inclut la contribution de op_a - Penalty oldPen = sol.penaltyPerMachine.count(dec_a.empV) - ? sol.penaltyPerMachine.at(dec_a.empV) : Penalty{}; + Penalty oldPen = sol.penaltyPerMachine[dec_a.empV]; unsigned int oldCost = dec_a.lastCreneau.first * STFMockInstance::jobs[op_a]->getPoidsRetard(); unsigned int oldDiagCost = dec_a.rejected ? STFMockInstance::jobs[op_a]->getPoidsRejet() : 0; std::vector> jobsEmpVA; + unsigned int op_id = 0; for(auto& dec : neighbor.decisions) { - if(!dec.second.excluded) + if(!(*dec).excluded) { - if(dec.second.empV == dec_a.empV) + if((*dec).empV == dec_a.empV) { - jobsEmpVA.push_back({dec.first, dec.second}); - oldCost += dec.second.lastCreneau.first * STFMockInstance::jobs[dec.first]->getPoidsRetard(); + jobsEmpVA.push_back({op_id, (*dec)}); + oldCost += (*dec).lastCreneau.first * STFMockInstance::jobs[op_id]->getPoidsRetard(); } } + ++op_id; } unsigned int remaining = 0.0; @@ -796,16 +923,21 @@ namespace solverlib { if (!mock) return std::nullopt; std::vector active_ops; - for (auto& [op_id, dec] : sol.decisions) - if (!dec.excluded) active_ops.push_back(op_id); + unsigned int id = 0; + for (auto& dec : sol.decisions) + { + if (!(*dec).excluded) + active_ops.push_back(id); + ++id; + } if (active_ops.empty()) return std::nullopt; std::uniform_int_distribution dist(0, (int)active_ops.size() - 1); unsigned short op_a = active_ops[dist(randomEngine)]; - const Decision& dec_a = sol.decisions.at(op_a); + const Decision& dec_a = *sol.decisions[op_a]; SASolution neighbor = sol; - Decision& new_dec_a = neighbor.decisions[op_a]; + Decision& new_dec_a = *neighbor.decisions[op_a]; unsigned int oldDiagCost = dec_a.rejected ? STFMockInstance::jobs[op_a]->getPoidsRejet() : 0; @@ -825,21 +957,22 @@ namespace solverlib { } // Pénalité ancienne O(1) - Penalty oldPen = sol.penaltyPerMachine.count(dec_a.empV) - ? sol.penaltyPerMachine.at(dec_a.empV) : Penalty{}; + Penalty oldPen = sol.penaltyPerMachine[dec_a.empV]; unsigned int oldCost = 0; std::vector> jobsEmpVA; + unsigned int op_id = 0; for(auto& dec : neighbor.decisions) { - if(!dec.second.excluded) + if(!(*dec).excluded) { - if(dec.second.empV == dec_a.empV) + if((*dec).empV == dec_a.empV) { - jobsEmpVA.push_back({dec.first, dec.second}); - oldCost += dec.second.lastCreneau.first * STFMockInstance::jobs[dec.first]->getPoidsRetard(); + jobsEmpVA.push_back({op_id, (*dec)}); + oldCost += (*dec).lastCreneau.first * STFMockInstance::jobs[op_id]->getPoidsRetard(); } } + ++op_id; } std::sort(jobsEmpVA.begin(), jobsEmpVA.end(), [&](auto& el1, auto& el2){ diff --git a/src/OrdoCorr/Solver/TrackPlansILP/SimulatedAnnealing.hpp b/src/OrdoCorr/Solver/TrackPlansILP/SimulatedAnnealing.hpp index f646709..6541ac5 100644 --- a/src/OrdoCorr/Solver/TrackPlansILP/SimulatedAnnealing.hpp +++ b/src/OrdoCorr/Solver/TrackPlansILP/SimulatedAnnealing.hpp @@ -7,6 +7,7 @@ #include #include #include "Penalty.hpp" +#include "SolutionPoolManager.hpp" #include "TrackPlan.hpp" #include "Solution.hpp" namespace solverlib { @@ -70,6 +71,7 @@ namespace solverlib { class SimulatedAnnealing{ private: + SolutionPoolManager pool; std::vector solutions; std::mt19937 randomEngine; double T; @@ -90,8 +92,8 @@ namespace solverlib { for (auto& job : seq) { auto& dec = neighbor.decisions[job.first]; - dec.lastCreneau = resolved[id].second.lastCreneau; - newCost += dec.lastCreneau.first * STFMockInstance::jobs[job.first]->getPoidsRetard(); + (*dec).lastCreneau = resolved[id].second.lastCreneau; + newCost += (*dec).lastCreneau.first * STFMockInstance::jobs[job.first]->getPoidsRetard(); ++id; } neighbor.penalty = neighbor.penalty + penaltyDelta; @@ -104,18 +106,21 @@ namespace solverlib { void setPenaltyWeights() { penaltyLambdas[EPenaltyType::TIME_WINDOW_OVERRUN] = 1.0; } - std::unordered_map effectiveLambdas() const; + ArrayLambda effectiveLambdas() const; public: static bool withDynProg; static bool authorizeInfeasible; StatSimulatedAnnealing stats; SimulatedAnnealing() = delete; - explicit SimulatedAnnealing(std::unordered_map& decs, std::shared_ptr mock, ESourceTrackPlan source); + //SimulatedAnnealing(std::unordered_map& decs, std::shared_ptr mock, ESourceTrackPlan source); + SimulatedAnnealing(std::vector>& decs, std::shared_ptr mock, ESourceTrackPlan source); + EMovingOperators pick_operator(); std::optional apply_operator(const SASolution& current, EMovingOperators op); SASolution solve(double T_max, double T_min, double cooling_rate, int iterations_per_temp); - + SASolution solveMultiStart(double T_max, double T_min, double cooling_rate, int iterations_per_temp, int n_restarts); + SASolution perturbSolution(const SASolution& sol); std::optional move_swap_within_interval(const SASolution& sol); std::optional move_swap_WC(const SASolution& sol); std::optional move_insert_WC(const SASolution& sol); @@ -134,15 +139,19 @@ namespace solverlib { double fictiveCostExcluded(unsigned short op_id) const; - std::pair evaluate(const std::unordered_map& decs); + //std::pair evaluate(const std::unordered_map& decs); + std::pair evaluate(const std::vector>& decs); + void addSolutions(std::vector& solPool){solutions.insert(solutions.end(), solPool.begin(), solPool.end());}; //unsigned long getUniquePlans(std::vector& plans); - void addSolutionToPool(std::unordered_map& decs, std::shared_ptr mock, ESourceTrackPlan source, bool isFirst = false); + //void addSolutionToPool(std::unordered_map& decs, std::shared_ptr mock, ESourceTrackPlan source, bool isFirst = false); + void addSolutionToPool(std::vector>& decs, std::shared_ptr mock, ESourceTrackPlan source, bool isFirst = false); std::vector&& getSolutionPool(){return std::move(solutions);}; + SolutionPoolManager& getPoolManager(){return pool;}; }; diff --git a/src/OrdoCorr/Solver/TrackPlansILP/Solution.hpp b/src/OrdoCorr/Solver/TrackPlansILP/Solution.hpp index 2494aa1..550cb64 100644 --- a/src/OrdoCorr/Solver/TrackPlansILP/Solution.hpp +++ b/src/OrdoCorr/Solver/TrackPlansILP/Solution.hpp @@ -11,14 +11,14 @@ namespace solverlib { typedef struct _sol{ ESourceTrackPlan source; std::shared_ptr mock; - std::unordered_map decisions; + std::vector> decisions; unsigned int cost; unsigned int diagCost; bool isFeasible = true; - Penalty penalty; - std::unordered_map penaltyPerMachine; + Penalty penalty = Penalty{}; + std::vector penaltyPerMachine = std::vector(STFMockInstance::machines.size(), Penalty{}); double fictiveCost = 0.0; - std::unordered_map fictiveExcludedCosts; + std::vector fictiveExcludedCosts = std::vector(STFMockInstance::jobs.size(), 0.0); }SASolution; } diff --git a/src/OrdoCorr/Solver/TrackPlansILP/SolutionPoolManager.cpp b/src/OrdoCorr/Solver/TrackPlansILP/SolutionPoolManager.cpp index d9c72d2..16652c1 100644 --- a/src/OrdoCorr/Solver/TrackPlansILP/SolutionPoolManager.cpp +++ b/src/OrdoCorr/Solver/TrackPlansILP/SolutionPoolManager.cpp @@ -1,4 +1,5 @@ #include "SolutionPoolManager.hpp" +#include "TrackPlan.hpp" #include #include #include @@ -14,15 +15,18 @@ namespace solverlib { plans.resize(STFMockInstance::tracks.size()); std::unordered_map>> decisionsOnMachines; std::vector> machineIdsFromTrackId(STFMockInstance::tracks.size()); + unsigned int id = 0; for(auto& dec : sol.decisions) { - if(!dec.second.excluded) + if(!dec) continue; + + if(!(*dec).excluded) { - plans[dec.second.voie].isTrash = false; - plans[dec.second.voie].track = dec.second.voie; - plans[dec.second.voie].schedule[dec.first] = dec.second; - decisionsOnMachines[dec.second.empV].push_back({dec.first, dec.second}); - machineIdsFromTrackId[dec.second.voie].insert(dec.second.empV); + plans[(*dec).voie].isTrash = false; + plans[(*dec).voie].track = (*dec).voie; + plans[(*dec).voie].schedule[id] = (*dec); + decisionsOnMachines[(*dec).empV].push_back({id, (*dec)}); + machineIdsFromTrackId[(*dec).voie].insert((*dec).empV); } else if(withTrash) { TrackPlan trashTrack; @@ -31,18 +35,18 @@ namespace solverlib { trashTrack.mock = sol.mock; trashTrack.diagCost = 0; trashTrack.track = 0; - trashTrack.schedule[dec.first] = { + trashTrack.schedule[id] = { 0,CreneauHoraire(), 0, false, true,0,0,{0,0} }; - trashTrack.cost = STFMockInstance::jobs[dec.first]->getPoidsRetard() * MAXIMUM_TIME_OFFSET; + trashTrack.cost = STFMockInstance::jobs[id]->getPoidsRetard() * MAXIMUM_TIME_OFFSET; trackPlans.push_back(trashTrack); } + ++id; } for(auto& plan : plans) { if(!plan.schedule.empty()) { - bool feasible = true; if(!sol.penalty.isFeasible()) { @@ -70,8 +74,7 @@ namespace solverlib { return trackPlans; } - - std::vector SolutionPoolManager::transformSolutionsIntoUniqueTrackPlans() + std::vector SolutionPoolManager::transformSolutionsIntoUniqueTrackPlans(bool makeTrash) { std::vector trackPlans; std::set uniqueSchedules; @@ -86,15 +89,18 @@ namespace solverlib { plans.resize(STFMockInstance::tracks.size()); std::unordered_map>> decisionsOnMachines; std::vector> machineIdsFromTrackId(STFMockInstance::tracks.size()); + unsigned int id = 0; for(auto& dec : sol.decisions) { - if(!dec.second.excluded) + if(!dec) continue; + + if(!(*dec).excluded) { - plans[dec.second.voie].isTrash = false; - plans[dec.second.voie].track = dec.second.voie; - plans[dec.second.voie].schedule[dec.first] = dec.second; - decisionsOnMachines[dec.second.empV].push_back({dec.first, dec.second}); - machineIdsFromTrackId[dec.second.voie].insert(dec.second.empV); + plans[(*dec).voie].isTrash = false; + plans[(*dec).voie].track = (*dec).voie; + plans[(*dec).voie].schedule[id] = (*dec); + decisionsOnMachines[(*dec).empV].push_back({id, (*dec)}); + machineIdsFromTrackId[(*dec).voie].insert((*dec).empV); } } @@ -135,27 +141,30 @@ namespace solverlib { pool.pop_back(); } - std::move(uniqueSchedules.begin(), uniqueSchedules.end(), std::back_inserter(trackPlans)); - - for(unsigned short job = 0; job < STFMockInstance::jobs.size(); ++job) + if(!uniqueSchedules.empty()) + std::move(uniqueSchedules.begin(), uniqueSchedules.end(), std::back_inserter(trackPlans)); + if(makeTrash) { - 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); + 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 or non-feasible track schedules"); - loggerlib::Logger::systemNotify(loggerlib::LOGGER_PROGRESS, "Total track plans " + std::to_string(nbplan)); - + //loggerlib::Logger::systemNotify(loggerlib::LOGGER_PROGRESS, "Removed " + std::to_string(countD) + " non-unique or non-feasible track schedules"); + //loggerlib::Logger::systemNotify(loggerlib::LOGGER_PROGRESS, "Total track plans " + std::to_string(nbplan)); + pool.clear(); return trackPlans; } @@ -183,4 +192,13 @@ namespace solverlib { std::move(solutions.begin(), solutions.end(), std::back_inserter(pool)); } + void SolutionPoolManager::provide(SolutionPoolManager& otherPool) + { + std::move(otherPool.getSolutions().begin(), otherPool.getSolutions().end(), std::back_inserter(pool)); + for(auto& tr : otherPool.getTrackPlans()) + { + uniquePlans.insert(tr); + } + } + } \ No newline at end of file diff --git a/src/OrdoCorr/Solver/TrackPlansILP/SolutionPoolManager.hpp b/src/OrdoCorr/Solver/TrackPlansILP/SolutionPoolManager.hpp index fdac28d..eb212e9 100644 --- a/src/OrdoCorr/Solver/TrackPlansILP/SolutionPoolManager.hpp +++ b/src/OrdoCorr/Solver/TrackPlansILP/SolutionPoolManager.hpp @@ -3,22 +3,119 @@ #include "Solution.hpp" #include "TrackPlan.hpp" +#include #include namespace solverlib { class SolutionPoolManager{ std::vector pool; + + struct TrackPlanHash { + size_t operator()(const TrackPlan& tp) const { + size_t h = std::hash{}(tp.track); + h ^= std::hash{}(tp.cost) + 0x9e3779b9 + (h<<6) + (h>>2); + h ^= std::hash{}(tp.diagCost) + 0x9e3779b9 + (h<<6) + (h>>2); + h ^= std::hash{}(tp.isTrash) + 0x9e3779b9 + (h<<6) + (h>>2); + + std::vector> entries; + entries.reserve(tp.schedule.size()); + for (auto& [jobId, dec] : tp.schedule) + entries.push_back({jobId, dec}); + std::sort(entries.begin(), entries.end(), [](auto& el1, auto& el2){return el1.first < el2.first;}); + + for (auto& [jobId, dec] : entries) { + h ^= std::hash{}(jobId) + 0x9e3779b9 + (h<<6) + (h>>2); + h ^= std::hash{}(dec.empV) + 0x9e3779b9 + (h<<6) + (h>>2); + h ^= std::hash{}(dec.empR) + 0x9e3779b9 + (h<<6) + (h>>2); + h ^= std::hash{}(dec.lastCreneau.first) + 0x9e3779b9 + (h<<6) + (h>>2); + h ^= std::hash{}(dec.lastCreneau.second) + 0x9e3779b9 + (h<<6) + (h>>2); + h ^= std::hash{}(dec.rejected) + 0x9e3779b9 + (h<<6) + (h>>2); + h ^= std::hash{}(dec.site) + 0x9e3779b9 + (h<<6) + (h>>2); + h ^= std::hash{}(dec.voie) + 0x9e3779b9 + (h<<6) + (h>>2); + h ^= std::hash{}(dec.timeslotGraphSplited.getDebut().getRelativeDate()) + 0x9e3779b9 + (h<<6) + (h>>2); + h ^= std::hash{}(dec.timeslotGraphSplited.getFin().getRelativeDate()) + 0x9e3779b9 + (h<<6) + (h>>2); + h ^= std::hash{}(dec.excluded) + 0x9e3779b9 + (h<<6) + (h>>2); + + } + return h; + } + }; + + std::unordered_set uniquePlans; + std::vector trashPlans; public: void provide(std::vector&& solutions); - - std::vector transformSolutionsIntoUniqueTrackPlans(); + void provide(SolutionPoolManager& otherPool); + void provideTrackPlanToSelf() + { + if(!pool.empty()) + { + auto tr = transformSolutionsIntoUniqueTrackPlans(false); + for(auto& trplan : tr) + { + uniquePlans.insert(trplan); + } + } + } + void addTrackPlan(TrackPlan&& tp) { + if (uniquePlans.insert(tp).second){} + } + + std::vector getFeasibleSolutions() + { + std::vector sols; + for(auto& sol : pool) + { + if(sol.penalty.isFeasible()) + sols.push_back(sol); + } + return sols; + } + + std::vector& getTrashTrackPlans() + { + return trashPlans; + } + + void provideTrashPlans() + { + 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; + trashPlans.push_back(trashTrack); + } + } + std::vector transformSolutionsIntoUniqueTrackPlans(bool makeTrash = true); std::vector getTrackPlansFromSolution(SASolution& sol, bool withTrash = false); bool checkSequence(const std::vector>& jobsDec, unsigned int machine); const std::vector& getSolutions() const {return pool;}; + + std::vector getTrackPlans(){ + std::vector trackPlans; + trackPlans.reserve(uniquePlans.size() + trashPlans.size()); + for(auto& tr : uniquePlans) + { + trackPlans.push_back(tr); + } + for(auto& trash : trashPlans) + { + trackPlans.push_back(trash); + } + return trackPlans; + }; + }; } diff --git a/src/OrdoCorr/Solver/TrackPlansILP/sourceSolTrPlan.hpp b/src/OrdoCorr/Solver/TrackPlansILP/sourceSolTrPlan.hpp index 2979eea..e8a3dac 100644 --- a/src/OrdoCorr/Solver/TrackPlansILP/sourceSolTrPlan.hpp +++ b/src/OrdoCorr/Solver/TrackPlansILP/sourceSolTrPlan.hpp @@ -1,7 +1,8 @@ #ifndef SOURCESOLTRPLAN_HPP #define SOURCESOLTRPLAN_HPP -enum class ESourceTrackPlan{ +#include +enum class ESourceTrackPlan: uint8_t{ SimAn, DyProg, ListHeu, diff --git a/src/OrdoCorr/Solver/solverAPI.cpp b/src/OrdoCorr/Solver/solverAPI.cpp index 7692518..a636548 100644 --- a/src/OrdoCorr/Solver/solverAPI.cpp +++ b/src/OrdoCorr/Solver/solverAPI.cpp @@ -582,6 +582,7 @@ namespace solverlib { bool allowSwap; bool saveAllUB; unsigned int multistartSiman = 0; + bool withPenalties = false; }; struct StepResult { @@ -610,7 +611,7 @@ namespace solverlib { sol.cost = best.sommeRetardsPonderes(true); sol.diagCost = best.sommeRejetsPonderes(); sol.mock = best.getPlanificationState(); - sol.decisions = best.getMapOfDecisions(); + sol.decisions = best.getVectorOfDecicions(); auto end = std::chrono::system_clock::now(); std::chrono::duration duration = end - start; @@ -639,7 +640,7 @@ namespace solverlib { best.cost = obj; best.diagCost = bestPlanif.sommeRejetsPonderes(); best.mock = bestPlanif.getPlanificationState(); - best.decisions = bestPlanif.getMapOfDecisions(); + best.decisions = bestPlanif.getVectorOfDecicions(); best.source = ESourceTrackPlan::ListHeu; } } @@ -675,8 +676,17 @@ namespace solverlib { loggerlib::Logger::systemNotify(loggerlib::LOGGER_PROGRESS, "Running ILP"); auto start = std::chrono::system_clock::now(); - auto trackPlans = poolManager.transformSolutionsIntoUniqueTrackPlans(); + poolManager.provideTrackPlanToSelf(); + poolManager.provideTrashPlans(); + auto trackPlans = poolManager.getTrackPlans();//poolManager.transformSolutionsIntoUniqueTrackPlans(); + auto trackPlansBest = poolManager.getTrackPlansFromSolution(bestSoFar, true); + unsigned int co = 0; + for(auto& tr : trackPlansBest) + { + co += tr.cost; + } + std::cout << bestSoFar.cost << " " << co << std::endl; loggerlib::Logger::systemNotify(loggerlib::LOGGER_PROGRESS, "ILP: considering " + std::to_string(trackPlans.size()) + " track schedules"); @@ -700,7 +710,7 @@ namespace solverlib { result.cost = plan->sommeRetardsPonderes(true); result.diagCost = plan->sommeRejetsPonderes(); result.mock = plan->getPlanificationState(); - result.decisions = plan->getMapOfDecisions(); + result.decisions = plan->getVectorOfDecicions(); auto end = std::chrono::system_clock::now(); std::chrono::duration duration = end - start; return { result, ALGO::ILP, false, duration.count() }; @@ -753,7 +763,7 @@ namespace solverlib { } } if (isNew) { - auto map = plan.getMapOfDecisions(); + auto map = plan.getVectorOfDecicions(); SimulatedAnnealing simAnn(map, plan.getPlanificationState(), ESourceTrackPlan::ListHeu); auto tmp = simAnn.solve(10, 0.01, 0.96, 20); poolManager.provide({tmp}); @@ -763,9 +773,8 @@ namespace solverlib { } } } else { - auto map = plan.getMapOfDecisions(); SASolution sol; - sol.decisions = map; + sol.decisions = plan.getVectorOfDecicions(); sol.cost = plan.sommeRetardsPonderes(true); sol.diagCost = plan.sommeRejetsPonderes(); sol.mock = plan.getPlanificationState(); @@ -789,110 +798,139 @@ namespace solverlib { } static StepResult runSimAnn( + Result& resultGlob, SASolution& initial, SolutionPoolManager& poolManager, - bool withDyn = false + bool withDyn = false, + bool withPenalties = false, + unsigned int multistartSiman = 0 ) { + auto rng = random::makeEngine(); loggerlib::Logger::systemNotify(loggerlib::LOGGER_PROGRESS, "Running SimulatedAnnealing"); + unsigned int i = 0; + auto sol = initial; + //do{ - auto start = std::chrono::system_clock::now(); - - SimulatedAnnealing simAnn(initial.decisions, initial.mock, initial.source); - SimulatedAnnealing::withDynProg = withDyn; - auto result = simAnn.solve(75, 0.01, 0.995, 200); + auto start = std::chrono::system_clock::now(); + + SimulatedAnnealing simAnn(sol.decisions, sol.mock, sol.source); + SimulatedAnnealing::withDynProg = withDyn; + SimulatedAnnealing::authorizeInfeasible = withPenalties; + auto result = simAnn.solveMultiStart(80, 0.01, 0.995, 3000, multistartSiman); - auto end = std::chrono::system_clock::now(); - poolManager.provide(simAnn.getSolutionPool()); - - std::chrono::duration duration = end - start; - bool jsonOrCsv = false; - if(jsonOrCsv) - { - if(StatSimulatedAnnealing::activate) + auto end = std::chrono::system_clock::now(); + std::chrono::duration duration = end - start; + poolManager.provide(simAnn.getPoolManager()); + StepResult res = { result, ALGO::SIMANN, false, duration.count() }; + resultGlob.stepsRes.push_back(res); + if(resultGlob.best.sol.cost > res.sol.cost) { - nlohmann::json json = nlohmann::json::object(); - json["worseProb"] = nlohmann::json::object(); - for(auto& [key, val] : simAnn.stats.failInfos) - { - if(!json["worseProb"].contains(simAnn.stats.names[key])) - { - json["worseProb"][simAnn.stats.names[key]] = nlohmann::json::array(); - } - for(auto& fail : val) - { - nlohmann::json obj = nlohmann::json::object(); - obj["prob"] = fail.prob; - obj["diff"] = fail.diff; - obj["temp"] = fail.temperature; + resultGlob.best = res; + } - json["worseProb"][simAnn.stats.names[key]].push_back(obj); - } - } - unsigned int nbIte = 0; - for(auto& map : std::vector, std::string>>{{simAnn.stats.nbUsed, "used"}, {simAnn.stats.nbFeas, "feas"}, {simAnn.stats.nbImproved, "improved"}}) + + bool jsonOrCsv = false; + if(jsonOrCsv) + { + if(StatSimulatedAnnealing::activate) { - json[map.second] = nlohmann::json::object(); - for(auto& [key, val] : map.first) + nlohmann::json json = nlohmann::json::object(); + json["worseProb"] = nlohmann::json::object(); + for(auto& [key, val] : simAnn.stats.failInfos) { - if(map.second == "used") + if(!json["worseProb"].contains(simAnn.stats.names[key])) { - nbIte+=val; + json["worseProb"][simAnn.stats.names[key]] = nlohmann::json::array(); + } + for(auto& fail : val) + { + nlohmann::json obj = nlohmann::json::object(); + obj["prob"] = fail.prob; + obj["diff"] = fail.diff; + obj["temp"] = fail.temperature; + + json["worseProb"][simAnn.stats.names[key]].push_back(obj); } - json[map.second][simAnn.stats.names[key]] = val; } + unsigned int nbIte = 0; + for(auto& map : std::vector, std::string>>{{simAnn.stats.nbUsed, "used"}, {simAnn.stats.nbFeas, "feas"}, {simAnn.stats.nbImproved, "improved"}}) + { + json[map.second] = nlohmann::json::object(); + for(auto& [key, val] : map.first) + { + if(map.second == "used") + { + nbIte+=val; + } + json[map.second][simAnn.stats.names[key]] = val; + } + } + json["NbTotalOperator"] = nbIte; + loggerlib::IOHelper::dump("DumpedStats", json.dump(2), "SimulatedAnnealingStats.json"); } - json["NbTotalOperator"] = nbIte; - loggerlib::IOHelper::dump("DumpedStats", json.dump(2), "SimulatedAnnealingStats.json"); } - } - else - { - if(StatSimulatedAnnealing::activate) + else { - std::ostringstream csv; - - // En-têtes - csv << "category;operator;index;probability;objectiveDiff;costDiff;temperature;isFeasible\n"; - - // worseProb (map de vector) - for(auto& [key, val] : simAnn.stats.failInfos) + if(StatSimulatedAnnealing::activate) { - for(size_t i = 0; i < val.size(); ++i) + std::ostringstream csv; + + // En-têtes + csv << "category;operator;index;probability;objectiveDiff;evaluationDiff;temperature;isFeasible\n"; + + // worseProb (map de vector) + for(auto& [key, val] : simAnn.stats.failInfos) { - csv << ";" << simAnn.stats.names[key] << ";" << i << ";" << val[i].prob << ";" << val[i].diff << ";" << val[i].fictiveDiff << ";" << val[i].temperature << ";" << val[i].isfeasible << "\n"; + for(size_t i = 0; i < val.size(); ++i) + { + csv << ";" << simAnn.stats.names[key] << ";" << i << ";" << val[i].prob << ";" << val[i].diff << ";" << val[i].fictiveDiff << ";" << val[i].temperature << ";" << val[i].isfeasible << "\n"; + } } - } - // used / feas / improved - unsigned int nbIte = 0; - for(auto& [map, label] : std::vector, std::string>>{ - {simAnn.stats.nbUsed, "used"}, - {simAnn.stats.nbFeas, "feas"}, - {simAnn.stats.nbImproved, "improved"}, - {simAnn.stats.nbImprovedReal, "improvedRealCost"}, - {simAnn.stats.nbInfeasible, "withPenalty"} + // used / feas / improved + unsigned int nbIte = 0; + for(auto& [map, label] : std::vector, std::string>>{ + {simAnn.stats.nbUsed, "used"}, + {simAnn.stats.nbFeas, "feas"}, + {simAnn.stats.nbImproved, "improved"}, + {simAnn.stats.nbImprovedReal, "improvedRealCost"}, + {simAnn.stats.nbInfeasible, "withPenalty"} - }) - { - for(auto& [key, val] : map) + }) { - if(label == "used") nbIte += val; + for(auto& [key, val] : map) + { + if(label == "used") nbIte += val; - csv << label << ";" << simAnn.stats.names[key] << ";" << ";" << val << "\n"; + csv << label << ";" << simAnn.stats.names[key] << ";" << ";" << val << "\n"; + } } - } - csv << ";\n"; - // Total - csv << "NbTotalOperator;ALL;;" << nbIte << "\n"; + csv << ";\n"; + // Total + csv << "NbTotalOperator;ALL;;" << nbIte << "\n"; - loggerlib::IOHelper::dump("DumpedStats", csv.str(), "SimulatedAnnealingStats.csv"); + loggerlib::IOHelper::dump("DumpedStats", csv.str(), "SimulatedAnnealingStats.csv"); + } } - } + + /*if (multistartSiman > 0) { + //une itération sur deux part du meilleur connu + if (i % 2 == 0) { + sol = resultGlob.best.sol; + } else { + auto solsF = poolManager.getFeasibleSolutions(); + std::uniform_int_distribution distSol(0, solsF.size()-1); + sol = solsF[distSol(rng)]; + std::cout << solsF.size() << std::endl; + } + } + + poolManager.provideTrackPlanToSelf(); + ++i;*/ + //}while(multistartSiman != 0 && i != multistartSiman); - // On expose le pool pour les étapes aval (DynProg / ILP) - // via un membre accessible, ou on passe simAnn par référence — voir note ci-dessous - return { result, ALGO::SIMANN, false, duration.count() }; + return resultGlob.best; } static nlohmann::json makeStepJson(const StepResult& r) @@ -932,7 +970,7 @@ namespace solverlib { { name += algoNames[step] + "_"; } - name += "_M" + std::to_string(r.multistartSiman) + "_" + (r.allowSwap ? "s" : "ns"); + name += "_M" + std::to_string(r.multistartSiman) + (r.withPenalties ? "P" : "") + "_" + (r.allowSwap ? "s" : "ns"); return name; } @@ -944,7 +982,13 @@ namespace solverlib { std::vector pipelines = { //{{ALGO::LH, ALGO::DYNPROG }, false, false}, - {{ALGO::LH, ALGO::SIMANN, ALGO::ILP}, false, false, 7}, + //{{ALGO::LH, ALGO::SIMANN}, false, false}, + //{{ALGO::LH, ALGO::SIMANN}, false, false, 0, true}, + //{{ALGO::LH, ALGO::SIMANN}, false, false, 7}, + {{ALGO::LH, ALGO::SIMANN, ALGO::ILP}, false, false, 3, true}, + + //{{ALGO::LH, ALGO::SIMANN, ALGO::ILP}, false, false, 7}, + // {{ALGO::LH, ALGO::SIMANN, ALGO::ILP}, false, false, 7, true}, /*{{ALGO::LH, ALGO::SIMANN, ALGO::ILP}, false, false}, {{ALGO::LH, ALGO::SIMANN, ALGO::ILP}, false, false, 1}, @@ -971,7 +1015,6 @@ namespace solverlib { // If the instance is valid nlohmann::json json; - auto rng = random::makeEngine(); if (instance.isValid()) { for(auto& pipeline : pipelines) @@ -993,31 +1036,12 @@ namespace solverlib { case ALGO::LH: result.stepsRes.push_back(runLH(instanceCopy)); result.best = result.stepsRes.back(); break; case ALGO::SIMANN: { - unsigned int i = 0; - do{ - auto sol = result.best.sol; - if(i > 0) - { - std::uniform_int_distribution distSol(0, poolManager.getSolutions().size()-1); - do{ - sol = poolManager.getSolutions()[distSol(rng)]; - - }while(!sol.penalty.isFeasible()); - } - auto res = runSimAnn(sol, poolManager, false); - result.stepsRes.push_back(res); - if(result.best.sol.cost > res.sol.cost) - { - result.best = res; - } - ++i; - }while(pipeline.multistartSiman != 0 && i !=pipeline.multistartSiman); - + auto res = runSimAnn(result, result.best.sol, poolManager, false, pipeline.withPenalties, pipeline.multistartSiman); break; } case ALGO::SIMANN_DYN: { - auto res = runSimAnn(result.best.sol, poolManager, true); + auto res = runSimAnn(result, result.best.sol, poolManager, true); result.stepsRes.push_back(res); if(result.best.sol.cost > res.sol.cost) { @@ -1057,10 +1081,6 @@ namespace solverlib { } } } - SASolution sol = result.best.sol; - SolutionPoolManager sp; - sp.provide({sol}); - auto res = runILP(sol, sp); double timeFinal = 0.0; nlohmann::json arrStep = nlohmann::json::array(); @@ -1072,6 +1092,7 @@ namespace solverlib { json[namep] = makeStepJson(result.best); json[namep]["timeFinal"] = timeFinal; + json[namep]["fullSolution"] = std::find_if(result.best.sol.decisions.begin(), result.best.sol.decisions.end(), [](auto& el){return (*el).excluded;}) == result.best.sol.decisions.end(); json[namep]["stepsDetail"] = arrStep; instanceCopy.clean(); }