From 3c94d56120d47186579a649ceb7cd4846362268a Mon Sep 17 00:00:00 2001 From: tom Date: Thu, 4 Jun 2026 21:59:36 +0200 Subject: [PATCH] =?UTF-8?q?Recuit=20simul=C3=A9=20ajout=20de=20remove=20et?= =?UTF-8?q?=20insert=20d=C3=A9pendant=20de=20la=20progression=20du=20recui?= =?UTF-8?q?t=20+=20autoriser=20le=20remove=20=C3=A0=20couter=20quedalle=20?= =?UTF-8?q?au=20d=C3=A9but=20puis=20un=20max=20et=20de=20m=C3=AAme=20pour?= =?UTF-8?q?=20les=20violation=20d'intervales=20(pour=20l'instant=20une=20s?= =?UTF-8?q?eule=20violation=20autoris=C3=A9e=20et=20compatibilis=C3=A9e=20?= =?UTF-8?q?=C3=A0=20la=20detection)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 4 +- .../CurrentVersionLabTesting.code-workspace | 37 -- src/OrdoCorr/.vscode/launch.json | 17 - src/OrdoCorr/Solver/TrackPlansILP/Penalty.hpp | 59 ++ .../TrackPlansILP/SimulatedAnnealing.cpp | 555 +++++++++--------- .../TrackPlansILP/SimulatedAnnealing.hpp | 76 ++- .../Solver/TrackPlansILP/Solution.hpp | 6 + .../TrackPlansILP/SolutionPoolManager.cpp | 92 ++- .../TrackPlansILP/SolutionPoolManager.hpp | 2 + src/OrdoCorr/Solver/solverAPI.cpp | 23 +- 10 files changed, 515 insertions(+), 356 deletions(-) delete mode 100644 src/OrdoCorr/.vscode/CurrentVersionLabTesting.code-workspace delete mode 100644 src/OrdoCorr/.vscode/launch.json create mode 100644 src/OrdoCorr/Solver/TrackPlansILP/Penalty.hpp diff --git a/.gitignore b/.gitignore index 61fd441..aa2907d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ build -lib \ No newline at end of file +lib +Data +.cache \ No newline at end of file diff --git a/src/OrdoCorr/.vscode/CurrentVersionLabTesting.code-workspace b/src/OrdoCorr/.vscode/CurrentVersionLabTesting.code-workspace deleted file mode 100644 index 05668a9..0000000 --- a/src/OrdoCorr/.vscode/CurrentVersionLabTesting.code-workspace +++ /dev/null @@ -1,37 +0,0 @@ -{ - "folders": [ - { - "path": "../../.." - } - ], - "settings": {}, - "launch": { - "version": "0.2.0", - "configurations": [ - { - "type": "lldb", - "request": "launch", - "name": "Debug", - "program": "${workspaceFolder}/build/bin/Debug/OrdonnancementCorrectif", - "args": ["--solve", "-TRACKPLANS", "1_3_10_100_100_0.json"], - "cwd": "${workspaceFolder}/build/bin/Debug/" - }, - { - "type": "lldb", - "request": "launch", - "name": "DebugPrevRep", - "program": "${workspaceFolder}/build/bin/Debug/OrdonnancementCorrectif", - "args": ["--solve", "-PREVREP", "1_6_20_150_50_2.json"], - "cwd": "${workspaceFolder}/build/bin/Debug/" - }, - { - "type": "lldb", - "request": "launch", - "name": "DebugIns", - "program": "${workspaceFolder}/build/bin/Debug/InstanceGenerator", - "args": [], - "cwd": "${workspaceFolder}/build/bin/Debug/" - } - ] - } -} \ No newline at end of file diff --git a/src/OrdoCorr/.vscode/launch.json b/src/OrdoCorr/.vscode/launch.json deleted file mode 100644 index 1855566..0000000 --- a/src/OrdoCorr/.vscode/launch.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - // Use IntelliSense to learn about possible attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 - "version": "0.2.0", - "configurations": [ - { - "type": "lldb", - "request": "launch", - "name": "Debug", - "program": "${workspaceFolder}/build/bin/Debug/OrdonnancementCorrectif", - "args": ["--solve", "-HEURISTIC", "soutenance.json"], - "cwd": "${workspaceFolder}/build/bin/Debug/" - } - - ] -} \ No newline at end of file diff --git a/src/OrdoCorr/Solver/TrackPlansILP/Penalty.hpp b/src/OrdoCorr/Solver/TrackPlansILP/Penalty.hpp new file mode 100644 index 0000000..778a3c6 --- /dev/null +++ b/src/OrdoCorr/Solver/TrackPlansILP/Penalty.hpp @@ -0,0 +1,59 @@ +#ifndef PENALTY_HPP +#define PENALTY_HPP +#include +#include + +namespace solverlib { + + enum class EPenaltyType { + TIME_WINDOW_OVERRUN, // job dépasse sa fenêtre + }; + + struct Penalty { + std::unordered_map components; + + void add(EPenaltyType type, double value) { + components[type] += value; + } + + void subtract(EPenaltyType type, double value) { + components[type] = std::max(0.0, components[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 total = 0.0; + for (auto& [type, val] : components) + { + auto it = lambdas.find(type); + if (it != lambdas.end()) + total += it->second * val; + } + return total; + } + + double total() const { + double sum = 0.0; + for (auto& [_, v] : components) sum += v; + return sum; + } + + bool isFeasible() const { return total() == 0.0; } + + Penalty operator+(const Penalty& o) const { + Penalty res = *this; + for (auto& [type, val] : o.components) + res.components[type] += val; + return res; + } + + Penalty operator-(const Penalty& o) const { + Penalty res = *this; + for (auto& [type, val] : o.components) + res.components[type] = std::max(0.0, res.components[type] - val); + return res; + } + }; +} + +#endif \ No newline at end of file diff --git a/src/OrdoCorr/Solver/TrackPlansILP/SimulatedAnnealing.cpp b/src/OrdoCorr/Solver/TrackPlansILP/SimulatedAnnealing.cpp index 054b4ca..15d4ec1 100644 --- a/src/OrdoCorr/Solver/TrackPlansILP/SimulatedAnnealing.cpp +++ b/src/OrdoCorr/Solver/TrackPlansILP/SimulatedAnnealing.cpp @@ -1,6 +1,7 @@ #include "SimulatedAnnealing.hpp" #include "../PseCarlierRivreau/omp.h" #include "DynamicProgramming.hpp" +#include "Penalty.hpp" #include "Solution.hpp" #include "TrackPlan.hpp" #include @@ -10,24 +11,28 @@ #include #include #include +#include #include #include #include #include "Random.hpp" #include "sourceSolTrPlan.hpp" + namespace solverlib { using namespace random; bool StatSimulatedAnnealing::activate = true; bool SimulatedAnnealing::withDynProg = false; + bool SimulatedAnnealing::authorizeInfeasible = true; + constexpr double decreaseFunction(double p){return std::exp(-0.75*(1.0-p));}; std::unordered_map 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::CHANGE_MODE, "CHANGE_MODE"}, + {EMovingOperators::INSERT, "INSERT"}, + {EMovingOperators::REMOVE, "REMOVE"}, + {EMovingOperators::SWAP, "SWAP"}, {EMovingOperators::SWAP_WITHIN_INTERVAL, "SWAP_WITHIN_SEQUENCE"}, {EMovingOperators::DYN_PROG, "DYN_PROG"}, {EMovingOperators::MOVE, "MOVE"} @@ -37,7 +42,11 @@ namespace solverlib { 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{}; } void SimulatedAnnealing::addSolutionToPool(std::unordered_map& decs, std::shared_ptr mock, ESourceTrackPlan source, bool isPutFirst) @@ -73,58 +82,24 @@ namespace solverlib { return {cost, diagCost}; } - std::pair SimulatedAnnealing::getCostOfSequence(std::vector>& 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(EMovingOperators::DYN_PROG)+1 : static_cast(EMovingOperators::MOVE)+1; - std::vector weights(N, 1); + EMovingOperators SimulatedAnnealing::pick_operator() { + double p = progress(); + float w_remove = static_cast(2*std::max(0.0,1.0 - decreaseFunction(p))); + float w_insert = static_cast(2*std::min(1.0, decreaseFunction(p))); std::vector base_weights = { - 1.0f, - 1.0f, - 1.0f, - 1.0f, - 1.0f, - 1.0f + 1, + w_insert, + w_remove, + 1, + 1, + 1 // DYN_PROG ajouté si besoin }; - if (withDynProg) base_weights.push_back(0.5f); // DYN_PROG + if (withDynProg) base_weights.push_back(1.0f); // 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( - //std::uniform_int_distribution(0, N)(randomEngine) - std::discrete_distribution(weights.begin(), weights.end())(randomEngine) + std::discrete_distribution(base_weights.begin(), base_weights.end())(randomEngine) ); } @@ -133,10 +108,10 @@ namespace solverlib { { 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: return move_swap_WC(current); + case EMovingOperators::INSERT: return move_insert_WC(current); + case EMovingOperators::REMOVE: return move_remove_WC(current); + case EMovingOperators::CHANGE_MODE: 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); @@ -150,16 +125,22 @@ namespace solverlib { { SASolution current = solutions[0]; SASolution best = solutions[0]; + current.fictiveCost = current.cost; + best.fictiveCost = current.cost; + + Tmax = T_max; double cost_cur = current.cost; double cost_best= cost_cur; - double T = T_max; + T = Tmax; + Tmin = T_min; + rate = cooling_rate; std::uniform_real_distribution uniform(0.0, 1.0); - while (T > T_min + 10e-6) + while (T > Tmin + 10e-6) { for (int i = 0; i < iterations_per_temp; ++i) { - EMovingOperators op = pick_operator(T, T_max); + EMovingOperators op = pick_operator(); stats.addUsed(op); auto neighbor = apply_operator(current, op); @@ -167,23 +148,33 @@ namespace solverlib { auto costs_neighbor = std::make_pair(neighbor->cost, neighbor->diagCost); - double delta = costs_neighbor.first - cost_cur; + double delta = effectiveCost(*neighbor) - effectiveCost(current); + double diff = (double)neighbor->cost - (double)current.cost; + + if(neighbor->penalty.isFeasible()) + stats.addFeas(op); - stats.addFeas(op); if(delta <= 0) stats.addImproved(op); + if(diff <= 0 && neighbor->penalty.isFeasible()) + stats.addImprovedReal(op); + + if(!neighbor->penalty.isFeasible()) + stats.addInfeasible(op); + if(delta > 0) { - stats.addFailInfo(op, getP(delta, T, op), delta, T); + 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, T, op)) + if (delta < 0 || uniform(randomEngine) < getP(delta, op)) { current = std::move(*neighbor); cost_cur = costs_neighbor.first; - if (cost_cur < cost_best) { + if (current.penalty.isFeasible() && cost_cur < cost_best) { best = current; cost_best = cost_cur; } @@ -201,22 +192,43 @@ namespace solverlib { return best; } - double SimulatedAnnealing::getP(double delta, double temperature, EMovingOperators op) + double SimulatedAnnealing::effectiveCost(const SASolution& s) const { + return s.fictiveCost + s.penalty.weighted(effectiveLambdas()); + } + + std::unordered_map SimulatedAnnealing::effectiveLambdas() const { + double p = progress(); + return { + {EPenaltyType::TIME_WINDOW_OVERRUN, std::exp(8*(p-0.2))-0.8}, + }; + } + + double SimulatedAnnealing::progress() const { + //return 1.0 - (T - Tmin) / (Tmax - Tmin); // 0 au début, 1 à la fin + return 1.0 - (std::log(T) - std::log(Tmin)) / (std::log(Tmax) - std::log(Tmin)); + } + + double SimulatedAnnealing::fictiveCostExcluded(unsigned short op_id) const { + return decreaseFunction(progress()) * MAXIMUM_TIME_OFFSET + * STFMockInstance::jobs[op_id]->getPoidsRetard(); + } + + + double SimulatedAnnealing::getP(double delta, 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: + case EMovingOperators::INSERT: return std::exp(-delta/(T*50)); + case EMovingOperators::REMOVE: return std::exp(-delta/(T*50)); + case EMovingOperators::CHANGE_MODE: case EMovingOperators::SWAP_WITHIN_INTERVAL: - case EMovingOperators::MOVE: return std::exp(-delta/(temperature*10)); + case EMovingOperators::MOVE: return std::exp(-delta/(T*20)); case EMovingOperators::DYN_PROG: break; } - return std::exp(-delta/temperature); + return std::exp(-delta/T); } - //OPERATEURS std::optional SimulatedAnnealing::move_dynprog(const SASolution& sol) @@ -265,55 +277,48 @@ namespace solverlib { auto posA = std::find(seq_swap.begin(), seq_swap.end(), op_a); if(posA == seq_swap.end()) return std::nullopt; - auto lbdBuildSeq = [&](std::vector& seq) -> std::optional { - 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 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); + + // Tirer un index différent de IposA pour le swap + std::uniform_int_distribution posR(0, (int)seq_swap.size() - 1); + int posB = posR(randomEngine); + while (posB == IposA) + posB = 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; + std::swap(seqCop[IposA], seqCop[posB]); - return std::nullopt; + // Reconstruire le vecteur de décisions dans le bon ordre + std::vector> jobsSeq; + jobsSeq.reserve(seqCop.size()); + for (auto& job_id : seqCop) + jobsSeq.push_back({job_id, sol.decisions.at(job_id)}); + + Penalty oldPen = sol.penaltyPerMachine.count(dec_a.empV) + ? sol.penaltyPerMachine.at(dec_a.empV) + : Penalty{}; + + unsigned int oldCost = 0; + for (auto& job_id : seq_swap) + oldCost += STFMockInstance::jobs[job_id]->getPoidsRetard() + * sol.decisions.at(job_id).lastCreneau.first; + + Penalty newPen; bool feasible = true; + auto res = checkSequence(jobsSeq, dec_a.empV, newPen, feasible); + if (!res) return std::nullopt; + + // Construire le voisin + SASolution neighbor = sol; + unsigned int newCost = 0; + applySequenceResult(neighbor, jobsSeq, *res, newCost, newPen, feasible); + + neighbor.penalty = sol.penalty - oldPen + newPen; + neighbor.penaltyPerMachine[dec_a.empV] = newPen; + neighbor.isFeasible = neighbor.penalty.isFeasible(); + neighbor.fictiveCost = (neighbor.fictiveCost - oldCost) + newCost; + neighbor.cost = (neighbor.cost - oldCost) + newCost; + neighbor.source = ESourceTrackPlan::SimAn; + return neighbor; } std::optional SimulatedAnnealing::move_swap_WC(const SASolution& sol) @@ -397,6 +402,12 @@ namespace solverlib { auto oldCrenA = dec_a.lastCreneau; 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{}; + 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(); @@ -443,32 +454,24 @@ namespace solverlib { } return cren1.first < cren2.first; }); - auto resA = checkSequence(jobsEmpVA, new_dec_a.empV); - auto resB = checkSequence(jobsEmpVB, new_dec_b.empV); + + Penalty penA; Penalty penB; + bool feasA = true, feasB = true; + auto resA = checkSequence(jobsEmpVA, new_dec_a.empV, penA, feasA); + auto resB = checkSequence(jobsEmpVB, new_dec_b.empV, penB, feasB); 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; + if(!resA || !resB) return std::nullopt; + + applySequenceResult(neighbor, jobsEmpVA, *resA, newCost, penA, feasA); + applySequenceResult(neighbor, jobsEmpVB, *resB, newCost, penB, feasB); + + neighbor.penalty = sol.penalty - oldPenA - oldPenB + penA + penB; + neighbor.penaltyPerMachine[new_dec_a.empV] = penA; + neighbor.penaltyPerMachine[new_dec_b.empV] = penB; + neighbor.isFeasible = neighbor.penalty.isFeasible(); + neighbor.fictiveCost = (neighbor.fictiveCost - oldCost) + newCost; + neighbor.cost = (neighbor.cost - oldCost) + newCost; + neighbor.source = ESourceTrackPlan::SimAn; return neighbor; } @@ -517,8 +520,12 @@ namespace solverlib { 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(); + // 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{}; + unsigned int oldDiagCost = 0; + unsigned int oldCostScheduled = 0; unsigned int newDiagCost = new_dec_a.rejected ? STFMockInstance::jobs[op_a]->getPoidsRejet() : 0; std::vector> jobsEmpVA; for(auto& dec : neighbor.decisions) @@ -528,7 +535,7 @@ namespace solverlib { 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(); + oldCostScheduled += dec.second.lastCreneau.first * STFMockInstance::jobs[dec.first]->getPoidsRetard(); } } } @@ -550,25 +557,31 @@ namespace solverlib { 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; - } + Penalty penA; bool feasA = true; + auto res = checkSequence(seq, new_dec_a.empV, penA, feasA); + + if(!res) return std::nullopt; - return std::nullopt; + applySequenceResult(neighbor, seq, *res, newCost, penA, feasA); + + double oldFictive = sol.fictiveExcludedCosts.count(op_a) + ? fictiveCostExcluded(op_a) + : MAXIMUM_TIME_OFFSET * STFMockInstance::jobs[op_a]->getPoidsRetard(); + + unsigned int oldCostExcluded = MAXIMUM_TIME_OFFSET * STFMockInstance::jobs[op_a]->getPoidsRetard(); + + neighbor.penalty = sol.penalty - oldPen + penA; + 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.diagCost = (neighbor.diagCost - oldDiagCost) + newDiagCost; + neighbor.cost = (neighbor.cost - oldCostExcluded - oldCostScheduled) + newCost; + neighbor.source = ESourceTrackPlan::SimAn; + + return neighbor; } std::optional SimulatedAnnealing::move_move_WC(const SASolution& sol) @@ -619,6 +632,12 @@ namespace solverlib { new_dec_a.timeslotGraphSplited = mock->trajectoryStops[new_dec_a.empR].getDispoStop(); 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{}; + 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; @@ -658,17 +677,14 @@ namespace solverlib { }); unsigned int newCost = 0; + Penalty penA; Penalty penB; + bool feasA = true; bool feasB = true; //décale à gauche sur track de départ - auto resB = checkSequence(jobsEmpVB, dec_a.empV); - unsigned int id = 0; - for(auto& jobsB : jobsEmpVB) + if(!jobsEmpVB.empty()) { - auto& dec = neighbor.decisions[jobsB.first]; - dec.lastCreneau = resB.value()[id].second.lastCreneau; - newCost+= dec.lastCreneau.first * STFMockInstance::jobs[jobsB.first]->getPoidsRetard(); - ++id; + auto resB = checkSequence(jobsEmpVB, dec_a.empV, penB, feasB); + applySequenceResult(neighbor, jobsEmpVB, *resB, newCost, penB, feasB); } - std::uniform_int_distribution distPos(0,jobsEmpVA.size()); auto pos = distPos(randomEngine); @@ -681,24 +697,20 @@ namespace solverlib { 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; - } + auto res = checkSequence(seq, new_dec_a.empV, penA, feasA); + if(!res) return std::nullopt; + + applySequenceResult(neighbor, seq, *res, newCost, penA, feasA); - return std::nullopt; + neighbor.penalty = sol.penalty - oldPenSrc - oldPenDst + penB + penA; + neighbor.penaltyPerMachine[dec_a.empV] = penB; + neighbor.penaltyPerMachine[new_dec_a.empV] = penA; + neighbor.isFeasible = neighbor.penalty.isFeasible(); + neighbor.fictiveCost = (neighbor.fictiveCost - oldCost) + newCost; + neighbor.diagCost = (neighbor.diagCost - oldDiagCost) + newDiagCost; + neighbor.cost = (neighbor.cost - oldCost) + newCost; + neighbor.source = ESourceTrackPlan::SimAn; + return neighbor; } std::optional SimulatedAnnealing::move_remove_WC(const SASolution& sol) @@ -729,9 +741,12 @@ namespace solverlib { new_dec_a.timeslotGraphSplited = CreneauHoraire(); 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{}; + 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> jobsEmpVA; for(auto& dec : neighbor.decisions) @@ -745,34 +760,33 @@ namespace solverlib { } } } - unsigned int newCost = MAXIMUM_TIME_OFFSET * STFMockInstance::jobs[op_a]->getPoidsRetard(); + unsigned int remaining = 0.0; + Penalty penA; bool feasA = true; 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; + auto resA = checkSequence(jobsEmpVA, dec_a.empV, penA, feasA); + if(!resA) return std::nullopt; + + applySequenceResult(neighbor, jobsEmpVA, *resA, remaining, penA, feasA); } - neighbor.diagCost = (neighbor.diagCost - oldDiagCost) + newDiagCost; - neighbor.cost = (neighbor.cost - oldCost) + newCost; - neighbor.source = ESourceTrackPlan::SimAn; + + double fictivePenalty = fictiveCostExcluded(op_a); + unsigned int excludedCost = MAXIMUM_TIME_OFFSET * STFMockInstance::jobs[op_a]->getPoidsRetard(); + + neighbor.penalty = sol.penalty - oldPen + penA; + neighbor.penaltyPerMachine[dec_a.empV] = penA; + neighbor.isFeasible = neighbor.penalty.isFeasible(); + neighbor.fictiveExcludedCosts[op_a] = fictivePenalty; + neighbor.fictiveCost = (neighbor.fictiveCost - oldCost) + fictivePenalty + remaining; + neighbor.diagCost = neighbor.diagCost - oldDiagCost; + neighbor.cost = (neighbor.cost - oldCost) + excludedCost + remaining; + neighbor.source = ESourceTrackPlan::SimAn; return neighbor; } @@ -793,9 +807,9 @@ namespace solverlib { 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; @@ -810,6 +824,11 @@ namespace solverlib { } } + // Pénalité ancienne O(1) + Penalty oldPen = sol.penaltyPerMachine.count(dec_a.empV) + ? sol.penaltyPerMachine.at(dec_a.empV) : Penalty{}; + + unsigned int oldCost = 0; std::vector> jobsEmpVA; for(auto& dec : neighbor.decisions) { @@ -829,26 +848,20 @@ namespace solverlib { 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; + + Penalty penA; bool feasA = true; + auto resA = checkSequence(jobsEmpVA, dec_a.empV, penA, feasA); + if(!resA) return std::nullopt; + + applySequenceResult(neighbor, jobsEmpVA, *resA, newCost, penA, feasA); + + neighbor.penalty = sol.penalty - oldPen + penA; + neighbor.penaltyPerMachine[dec_a.empV] = penA; + neighbor.isFeasible = neighbor.penalty.isFeasible(); + neighbor.fictiveCost = (neighbor.fictiveCost - oldCost) + newCost; + neighbor.diagCost = (neighbor.diagCost - oldDiagCost) + newDiagCost; + neighbor.cost = (neighbor.cost - oldCost) + newCost; + neighbor.source = ESourceTrackPlan::SimAn; return neighbor; } @@ -872,53 +885,53 @@ namespace solverlib { return std::optional>>(res); } - - std::pair> SimulatedAnnealing::PSE_Carlier_Rivreau(std::vector> &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::rdbuf(fakeFile.rdbuf()); - Solution sol((int) jobs.size()); - OneMachine machine(file); - machine.solve(sol); - - std::vector 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& plans) + std::optional>> + SimulatedAnnealing::checkSequence( + const std::vector>& jobsDec, + unsigned int machine, + Penalty& outPenalty, + bool& outFeasible) { - std::set uniqueSchedules; - std::vector newTMPVec; - for(auto& trackSch : plans) + auto machineDisp = STFMockInstance::machines[machine]->getDispo(); + unsigned short minBegin = machineDisp.getDebut().getRelativeDate(); + std::vector> res; + outFeasible = true; + + for (auto& jobDec : jobsDec) { - uniqueSchedules.insert(trackSch); + auto matchWithMachine = CreneauHoraire::checkSlotsCompatibility( + jobDec.second.timeslotGraphSplited, machineDisp).second; + + minBegin = std::max(minBegin, matchWithMachine.first); + unsigned int duration = !jobDec.second.rejected + ? STFMockInstance::jobs[jobDec.first]->getDuree() + : STFMockInstance::jobs[jobDec.first]->getDureeDiag(); + + unsigned int windowEnd = matchWithMachine.first + matchWithMachine.second; + unsigned int jobEnd = minBegin + duration; + + if (jobEnd > windowEnd) + { + if (!authorizeInfeasible) + return std::nullopt; + + // Planification forcée + pénalité + outFeasible = false; + double overrun = static_cast(jobEnd - windowEnd); + outPenalty.add(EPenaltyType::TIME_WINDOW_OVERRUN, overrun); + } + + auto newJobDec = jobDec; + newJobDec.second.lastCreneau = {minBegin, jobEnd}; + minBegin = jobEnd; + res.push_back(newJobDec); } - auto uniqueNb = uniqueSchedules.size(); - std::move(uniqueSchedules.begin(), uniqueSchedules.end(), std::back_inserter(newTMPVec)); - std::swap(plans, newTMPVec); - - return uniqueNb; - }*/ - - - + for (size_t i = 1; i < res.size(); ++i) { + if (res[i].second.lastCreneau.first < res[i-1].second.lastCreneau.second && res[i].second.lastCreneau.first >= res[i-1].second.lastCreneau.first) { + std::cout << res[i].second.lastCreneau.first << " " << res[i].second.lastCreneau.second << " " << res[i-1].second.lastCreneau.first << " " << res[i-1].second.lastCreneau.second << std::endl; + throw std::logic_error("Chevauchement détecté"); + } + } + return res; + } } \ No newline at end of file diff --git a/src/OrdoCorr/Solver/TrackPlansILP/SimulatedAnnealing.hpp b/src/OrdoCorr/Solver/TrackPlansILP/SimulatedAnnealing.hpp index 90c6e1c..f646709 100644 --- a/src/OrdoCorr/Solver/TrackPlansILP/SimulatedAnnealing.hpp +++ b/src/OrdoCorr/Solver/TrackPlansILP/SimulatedAnnealing.hpp @@ -6,16 +6,17 @@ #include #include #include +#include "Penalty.hpp" #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, + INSERT, + REMOVE, + CHANGE_MODE, SWAP_WITHIN_INTERVAL, MOVE, DYN_PROG, @@ -24,13 +25,18 @@ namespace solverlib { struct StatSimulatedAnnealing{ struct failInf{ double prob; - unsigned int diff; + long diff; + double fictiveDiff; double temperature; + bool isfeasible = true; }; std::unordered_map nbUsed; std::unordered_map nbFeas; std::unordered_map nbImproved; + std::unordered_map nbImprovedReal; + std::unordered_map nbInfeasible; + std::unordered_map> failInfos; @@ -48,10 +54,17 @@ namespace solverlib { void addImproved(EMovingOperators op){ if(activate) nbImproved[op]++; } + void addImprovedReal(EMovingOperators op){ + if(activate) nbImprovedReal[op]++; + } - void addFailInfo(EMovingOperators op, double prob, unsigned int diff, double temp) + void addInfeasible(EMovingOperators op){ + if(activate) nbInfeasible[op]++; + } + + void addFailInfo(EMovingOperators op, double prob, long diff, double fictiveDiff, double temp, bool isFeasible) { - if(activate) failInfos[op].emplace_back(failInf{prob, diff, temp}); + if(activate) failInfos[op].emplace_back(failInf{prob, diff, fictiveDiff, temp}); } }; @@ -59,12 +72,47 @@ namespace solverlib { private: std::vector solutions; std::mt19937 randomEngine; + double T; + double Tmax; + double Tmin; + double maxCostOfJob; + double rate; + + void applySequenceResult( + SASolution& neighbor, + const std::vector>& seq, + const std::vector>& resolved, + unsigned int& newCost, + Penalty penaltyDelta, // pénalité de cette séquence + bool feasible) + { + unsigned int id = 0; + 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(); + ++id; + } + neighbor.penalty = neighbor.penalty + penaltyDelta; + if (!feasible) neighbor.isFeasible = false; + } + + std::unordered_map penaltyLambdas; + + // Initialisation dans le constructeur ou avant solve() + void setPenaltyWeights() { + penaltyLambdas[EPenaltyType::TIME_WINDOW_OVERRUN] = 1.0; + } + std::unordered_map 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); - EMovingOperators pick_operator(double tmax, double t); + 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); @@ -77,15 +125,17 @@ namespace solverlib { std::optional move_dynprog(const SASolution& sol); std::optional>> checkSequence(const std::vector>& jobsDec, unsigned int machine); + std::optional>> checkSequence(const std::vector>& jobsDec, unsigned int machine, Penalty& outPenalty, bool& outFeasible); - double getP(double delta, double temperature, EMovingOperators op); + double getP(double delta, EMovingOperators op); + double effectiveCost(const SASolution& s) const; + + double progress() const; + + double fictiveCostExcluded(unsigned short op_id) const; std::pair evaluate(const std::unordered_map& decs); - std::pair getCostOfSequence(std::vector>& jobsSeq); - - std::pair> PSE_Carlier_Rivreau(std::vector> &jobs, unsigned int voieMachine); - void addSolutions(std::vector& solPool){solutions.insert(solutions.end(), solPool.begin(), solPool.end());}; diff --git a/src/OrdoCorr/Solver/TrackPlansILP/Solution.hpp b/src/OrdoCorr/Solver/TrackPlansILP/Solution.hpp index ead1f53..2494aa1 100644 --- a/src/OrdoCorr/Solver/TrackPlansILP/Solution.hpp +++ b/src/OrdoCorr/Solver/TrackPlansILP/Solution.hpp @@ -2,6 +2,7 @@ #define SOLUTION_HPP #include "../../../General/Model/STFMockInstance.hpp" +#include "Penalty.hpp" #include "sourceSolTrPlan.hpp" namespace solverlib { @@ -13,6 +14,11 @@ namespace solverlib { std::unordered_map decisions; unsigned int cost; unsigned int diagCost; + bool isFeasible = true; + Penalty penalty; + std::unordered_map penaltyPerMachine; + double fictiveCost = 0.0; + std::unordered_map fictiveExcludedCosts; }SASolution; } diff --git a/src/OrdoCorr/Solver/TrackPlansILP/SolutionPoolManager.cpp b/src/OrdoCorr/Solver/TrackPlansILP/SolutionPoolManager.cpp index 467fb71..d9c72d2 100644 --- a/src/OrdoCorr/Solver/TrackPlansILP/SolutionPoolManager.cpp +++ b/src/OrdoCorr/Solver/TrackPlansILP/SolutionPoolManager.cpp @@ -1,5 +1,7 @@ #include "SolutionPoolManager.hpp" #include +#include +#include namespace solverlib { std::vector SolutionPoolManager::getTrackPlansFromSolution(SASolution& sol, bool withTrash) @@ -10,14 +12,17 @@ namespace solverlib { //id plan = id voie std::vector plans; plans.resize(STFMockInstance::tracks.size()); + std::unordered_map>> decisionsOnMachines; + std::vector> machineIdsFromTrackId(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; + decisionsOnMachines[dec.second.empV].push_back({dec.first, dec.second}); + machineIdsFromTrackId[dec.second.voie].insert(dec.second.empV); } else if(withTrash) { TrackPlan trashTrack; @@ -37,10 +42,28 @@ namespace solverlib { { if(!plan.schedule.empty()) { - plan.mock = sol.mock; - plan.source = sol.source; - plan.evaluate(); - trackPlans.push_back(plan); + + bool feasible = true; + if(!sol.penalty.isFeasible()) + { + for(auto& machine : machineIdsFromTrackId[plan.track]) + { + std::sort(decisionsOnMachines[machine].begin(), decisionsOnMachines[machine].end(), [&](auto& el, auto& el2){ + return el.second.lastCreneau.first < el2.second.lastCreneau.first; + }); + feasible = checkSequence(decisionsOnMachines[machine], plan.track); + if(!feasible) + break; + } + + } + if(feasible) + { + plan.mock = sol.mock; + plan.source = sol.source; + plan.evaluate(); + trackPlans.push_back(plan); + } } } @@ -56,11 +79,13 @@ namespace solverlib { trackPlans.reserve(STFMockInstance::tracks.size() * pool.size() + 1); while(!pool.empty()) { - auto& sol = pool.back(); + auto sol = pool.back(); //id plan = id voie std::vector plans; plans.resize(STFMockInstance::tracks.size()); + std::unordered_map>> decisionsOnMachines; + std::vector> machineIdsFromTrackId(STFMockInstance::tracks.size()); for(auto& dec : sol.decisions) { if(!dec.second.excluded) @@ -68,22 +93,46 @@ namespace solverlib { 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); + } } 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) + bool feasible = true; + if(!sol.penalty.isFeasible()) { + for(auto& machine : machineIdsFromTrackId[plan.track]) + { + std::sort(decisionsOnMachines[machine].begin(), decisionsOnMachines[machine].end(), [&](auto& el, auto& el2){ + return el.second.lastCreneau.first < el2.second.lastCreneau.first; + }); + feasible = checkSequence(decisionsOnMachines[machine], plan.track); + if(!feasible) + break; + } + + } + if(feasible) + { + plan.mock = sol.mock; + plan.source = sol.source; + plan.evaluate(); + auto inserted = uniqueSchedules.insert(plan); + if(!inserted.second) + { + countD++; + } + } + else { countD++; } } } + pool.pop_back(); } std::move(uniqueSchedules.begin(), uniqueSchedules.end(), std::back_inserter(trackPlans)); @@ -104,12 +153,31 @@ namespace solverlib { 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, "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)); return trackPlans; } + bool SolutionPoolManager::checkSequence(const std::vector>& jobsDec, unsigned int machine) + { + auto machineDisp = STFMockInstance::machines[machine]->getDispo(); + unsigned short minBegin = machineDisp.getDebut().getRelativeDate(); + std::vector> 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 false; + minBegin = minBegin + duration; + } + return true; + } + void SolutionPoolManager::provide(std::vector&& solutions) { std::move(solutions.begin(), solutions.end(), std::back_inserter(pool)); diff --git a/src/OrdoCorr/Solver/TrackPlansILP/SolutionPoolManager.hpp b/src/OrdoCorr/Solver/TrackPlansILP/SolutionPoolManager.hpp index 2b2e4a9..fdac28d 100644 --- a/src/OrdoCorr/Solver/TrackPlansILP/SolutionPoolManager.hpp +++ b/src/OrdoCorr/Solver/TrackPlansILP/SolutionPoolManager.hpp @@ -16,6 +16,8 @@ namespace solverlib { std::vector getTrackPlansFromSolution(SASolution& sol, bool withTrash = false); + bool checkSequence(const std::vector>& jobsDec, unsigned int machine); + const std::vector& getSolutions() const {return pool;}; }; diff --git a/src/OrdoCorr/Solver/solverAPI.cpp b/src/OrdoCorr/Solver/solverAPI.cpp index a582532..7692518 100644 --- a/src/OrdoCorr/Solver/solverAPI.cpp +++ b/src/OrdoCorr/Solver/solverAPI.cpp @@ -853,14 +853,14 @@ namespace solverlib { std::ostringstream csv; // En-têtes - csv << "category;operator;index;probability;objectiveDiff;temperature \n"; + csv << "category;operator;index;probability;objectiveDiff;costDiff;temperature;isFeasible\n"; // worseProb (map de vector) for(auto& [key, val] : simAnn.stats.failInfos) { for(size_t i = 0; i < val.size(); ++i) { - csv << ";" << simAnn.stats.names[key] << ";" << i << ";" << val[i].prob << ";" << val[i].diff << ";" << val[i].temperature << "\n"; + csv << ";" << simAnn.stats.names[key] << ";" << i << ";" << val[i].prob << ";" << val[i].diff << ";" << val[i].fictiveDiff << ";" << val[i].temperature << ";" << val[i].isfeasible << "\n"; } } @@ -869,11 +869,16 @@ namespace solverlib { for(auto& [map, label] : std::vector, std::string>>{ {simAnn.stats.nbUsed, "used"}, {simAnn.stats.nbFeas, "feas"}, - {simAnn.stats.nbImproved, "improved"}}) + {simAnn.stats.nbImproved, "improved"}, + {simAnn.stats.nbImprovedReal, "improvedRealCost"}, + {simAnn.stats.nbInfeasible, "withPenalty"} + + }) { for(auto& [key, val] : map) { if(label == "used") nbIte += val; + csv << label << ";" << simAnn.stats.names[key] << ";" << ";" << val << "\n"; } } @@ -939,7 +944,7 @@ namespace solverlib { std::vector pipelines = { //{{ALGO::LH, ALGO::DYNPROG }, false, false}, - {{ALGO::LH, ALGO::SIMANN}, false, false}, + {{ALGO::LH, ALGO::SIMANN, ALGO::ILP}, false, false, 7}, /*{{ALGO::LH, ALGO::SIMANN, ALGO::ILP}, false, false}, {{ALGO::LH, ALGO::SIMANN, ALGO::ILP}, false, false, 1}, @@ -994,7 +999,10 @@ namespace solverlib { if(i > 0) { std::uniform_int_distribution distSol(0, poolManager.getSolutions().size()-1); - sol = poolManager.getSolutions()[distSol(rng)]; + do{ + sol = poolManager.getSolutions()[distSol(rng)]; + + }while(!sol.penalty.isFeasible()); } auto res = runSimAnn(sol, poolManager, false); result.stepsRes.push_back(res); @@ -1049,6 +1057,11 @@ 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(); for(auto& stepFinished : result.stepsRes)