#include "SimulatedAnnealing.hpp" #include "../PseCarlierRivreau/omp.h" #include "DynamicProgramming.hpp" #include "Solution.hpp" #include "TrackPlan.hpp" #include #include #include #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; 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::SWAP_WITHIN_INTERVAL, "SWAP_WITHIN_SEQUENCE"}, {EMovingOperators::DYN_PROG, "DYN_PROG"}, {EMovingOperators::MOVE, "MOVE"} }; SimulatedAnnealing::SimulatedAnnealing(std::unordered_map& decs, std::shared_ptr mock, ESourceTrackPlan source) { randomEngine = solverlib::random::makeEngine(); addSolutionToPool(decs, mock, source); } 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}); } } std::pair SimulatedAnnealing::evaluate(const std::unordered_map& decs) { unsigned int cost = 0; unsigned int diagCost = 0; for(auto& dec : decs) { if(dec.second.excluded) { cost += MAXIMUM_TIME_OFFSET * modellib::STFMockInstance::jobs[dec.first]->getPoidsRetard(); } else cost += modellib::STFMockInstance::jobs[dec.first]->getPoidsRetard() * dec.second.lastCreneau.first; if(dec.second.rejected) { diagCost += modellib::STFMockInstance::jobs[dec.first]->getPoidsRejet(); } } return {cost, diagCost}; } std::pair 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); std::vector base_weights = { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f // DYN_PROG ajouté si besoin }; if (withDynProg) base_weights.push_back(0.5f); // DYN_PROG const float remove_base = 1.0f; // poids max du remove (en début de recuit) const float t = temp / tmax; // 1.0 → 0.0 // Poids remove const float w_remove = remove_base * t; // Le budget récupéré est redistribué proportionnellement aux autres const float base_sum = std::accumulate(base_weights.begin(), base_weights.end(), 0.0f); const float bonus = remove_base * (1.0f - t); // Index du REMOVE dans ton enum — à adapter constexpr int REMOVE_IDX = 2; for (int i = 0; i < N; ++i) { if (i == REMOVE_IDX) { weights[i] = w_remove; } else { weights[i] = base_weights[i] + bonus * (base_weights[i] / base_sum); } } return static_cast( //std::uniform_int_distribution(0, N)(randomEngine) std::discrete_distribution(weights.begin(), weights.end())(randomEngine) ); } // Applique un opérateur et retourne un voisin (nullopt si infaisable) std::optional SimulatedAnnealing::apply_operator(const SASolution& current, EMovingOperators op) { switch (op) { case EMovingOperators::SWAP_WITHOUT_CARLIER: return move_swap_WC(current); case EMovingOperators::INSERT_WITHOUT_CARLIER: return move_insert_WC(current); case EMovingOperators::REMOVE_WITHOUT_CARLIER: return move_remove_WC(current); case EMovingOperators::CHANGE_MODE_WITHOUT_CARLIER: return move_change_mode_WC(current); case EMovingOperators::SWAP_WITHIN_INTERVAL: return move_swap_within_interval(current); case EMovingOperators::MOVE: return move_move_WC(current); case EMovingOperators::DYN_PROG: return move_dynprog(current); break; } return std::nullopt; } // Boucle principale SASolution SimulatedAnnealing::solve(double T_max, double T_min, double cooling_rate, int iterations_per_temp) { SASolution current = solutions[0]; SASolution best = solutions[0]; double cost_cur = current.cost; double cost_best= cost_cur; double T = T_max; std::uniform_real_distribution uniform(0.0, 1.0); while (T > T_min + 10e-6) { for (int i = 0; i < iterations_per_temp; ++i) { EMovingOperators op = pick_operator(T, T_max); stats.addUsed(op); auto neighbor = apply_operator(current, op); if (!neighbor.has_value()) continue; auto costs_neighbor = std::make_pair(neighbor->cost, neighbor->diagCost); double delta = costs_neighbor.first - cost_cur; stats.addFeas(op); if(delta <= 0) stats.addImproved(op); if(delta > 0) { stats.addFailInfo(op, getP(delta, T, op), delta, T); } if (delta < 0 || uniform(randomEngine) < getP(delta, T, op)) { current = std::move(*neighbor); cost_cur = costs_neighbor.first; if (cost_cur < cost_best) { best = current; cost_best = cost_cur; } solutions.push_back(current); } } T *= cooling_rate; //loggerlib::Logger::systemNotify(loggerlib::LOGGER_PROGRESS, "Temperature : " + std::to_string(T)); //loggerlib::Logger::systemNotify(loggerlib::LOGGER_PROGRESS, "Solution pool : " + std::to_string(solutions.size())); } //loggerlib::Logger::systemNotify(loggerlib::LOGGER_PROGRESS, "Best solution : " + std::to_string(best.cost)); return best; } double SimulatedAnnealing::getP(double delta, double temperature, EMovingOperators op) { switch (op) { case EMovingOperators::SWAP_WITHOUT_CARLIER: case EMovingOperators::INSERT_WITHOUT_CARLIER: case EMovingOperators::REMOVE_WITHOUT_CARLIER: return std::exp(-delta/(temperature*100)); case EMovingOperators::CHANGE_MODE_WITHOUT_CARLIER: case EMovingOperators::SWAP_WITHIN_INTERVAL: case EMovingOperators::MOVE: return std::exp(-delta/(temperature*10)); case EMovingOperators::DYN_PROG: break; } return std::exp(-delta/temperature); } //OPERATEURS std::optional SimulatedAnnealing::move_dynprog(const SASolution& sol) { DynamicProgramming prog(sol); prog.mode = true; prog.saveSols = false; auto result = prog.solve(); return std::optional(result); } std::optional SimulatedAnnealing::move_swap_within_interval(const SASolution& sol) { auto mock = sol.mock; 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); 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); 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); if (dec_b.empV != dec_a.empV) continue; seq_swap.push_back({ op_id }); } if (seq_swap.size() == 1) return std::nullopt; std::sort(seq_swap.begin(), seq_swap.end(), [&](auto job1, auto job2){ const Decision& dec_a = sol.decisions.at(job1); const Decision& dec_b = sol.decisions.at(job2); return dec_a.lastCreneau.first < dec_b.lastCreneau.first; }); auto posA = std::find(seq_swap.begin(), seq_swap.end(), op_a); if(posA == seq_swap.end()) return std::nullopt; auto lbdBuildSeq = [&](std::vector& 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); auto seqCop = seq_swap; auto posJobInt = job; seqCop[posJobInt] = *posA; seqCop[IposA] = seq_swap[job]; auto res = lbdBuildSeq(seqCop); if(res != std::nullopt) return res; return std::nullopt; } std::optional SimulatedAnnealing::move_swap_WC(const SASolution& sol) { auto mock = sol.mock; 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); 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); struct SwapCandidate { unsigned short op_a; unsigned short op_b; unsigned int empR_a_new; // empR que prendra op_a après le swap unsigned int empR_b_new; }; std::vector swap_candidates; // Chercher une op dans une EmpV différent compatible //pair < op, empR post swap> for (auto& op_id : active_ops) { if (op_id == op_a) continue; const Decision& dec_b = sol.decisions.at(op_id); if (dec_b.empV == dec_a.empV) continue; // Vérifier compatibilité infrastructure if (!STFMockInstance::infraComp[op_a][dec_b.voie]) continue; if (!STFMockInstance::infraComp[op_id][dec_a.voie]) continue; unsigned short empRA = 0; unsigned short empRB = 0; auto findDispOpA = std::find_if(mock->jobDispoVoiesRames[op_a].begin(), mock->jobDispoVoiesRames[op_a].end(), [&](auto& el){ return mock->dispoVoiesRames[el].dispoVoie == dec_b.empV; }); auto findDispOpB = std::find_if(mock->jobDispoVoiesRames[op_id].begin(), mock->jobDispoVoiesRames[op_id].end(), [&](auto& el){ return mock->dispoVoiesRames[el].dispoVoie == dec_a.empV; }); if(findDispOpA != mock->jobDispoVoiesRames[op_a].end() && findDispOpB != mock->jobDispoVoiesRames[op_id].end()) { empRA = mock->dispoVoiesRames[*findDispOpA].dispoRame; empRB = mock->dispoVoiesRames[*findDispOpB].dispoRame; } else continue; swap_candidates.push_back({ op_a, op_id, empRA,empRB }); } if (swap_candidates.empty()) return std::nullopt; // Tirer deuxième op parmi les candidats std::uniform_int_distribution cand_dist(0, (int)swap_candidates.size() - 1); auto swap = swap_candidates[cand_dist(randomEngine)]; const Decision& dec_b = sol.decisions.at(swap.op_b); // échanger tracks et slots SASolution neighbor = sol; Decision& new_dec_a = neighbor.decisions[op_a]; Decision& new_dec_b = neighbor.decisions[swap.op_b]; new_dec_a.voie = dec_b.voie; new_dec_a.site = dec_b.site; new_dec_a.empV = dec_b.empV; new_dec_a.empR = swap.empR_a_new; new_dec_a.timeslotGraphSplited = mock->trajectoryStops[swap.empR_a_new].getDispoStop(); new_dec_a.lastCreneau = {0,0}; new_dec_b.voie = dec_a.voie; new_dec_b.site = dec_a.site; new_dec_b.empV = dec_a.empV; new_dec_b.empR = swap.empR_b_new; new_dec_b.timeslotGraphSplited = mock->trajectoryStops[swap.empR_b_new].getDispoStop(); new_dec_b.lastCreneau = {0,0}; auto oldCrenA = dec_a.lastCreneau; auto oldCrenB = dec_b.lastCreneau; std::vector> 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(); for(auto& dec : neighbor.decisions) { if(!dec.second.excluded) { if(dec.second.empV == new_dec_a.empV) { jobsEmpVA.push_back({dec.first, dec.second}); oldCost += dec.second.lastCreneau.first * STFMockInstance::jobs[dec.first]->getPoidsRetard(); } if(dec.second.empV == new_dec_b.empV) { jobsEmpVB.push_back({dec.first, dec.second}); oldCost += dec.second.lastCreneau.first * STFMockInstance::jobs[dec.first]->getPoidsRetard(); } } } std::sort(jobsEmpVA.begin(), jobsEmpVA.end(), [&](auto& el1, auto& el2){ auto cren1 = el1.second.lastCreneau; auto cren2 = el2.second.lastCreneau; if(cren1.first == 0 && cren1.second == 0) { cren1 = oldCrenB; } if(cren2.first == 0 && cren2.second == 0) { cren2 = oldCrenB; } return cren1.first < cren2.first; }); std::sort(jobsEmpVB.begin(), jobsEmpVB.end(), [&](auto& el1, auto& el2){ auto cren1 = el1.second.lastCreneau; auto cren2 = el2.second.lastCreneau; if(cren1.first == 0 && cren1.second == 0) { cren1 = oldCrenA; } if(cren2.first == 0 && cren2.second == 0) { cren2 = oldCrenA; } return cren1.first < cren2.first; }); auto resA = checkSequence(jobsEmpVA, new_dec_a.empV); auto resB = checkSequence(jobsEmpVB, new_dec_b.empV); unsigned int newCost = 0; if(resA && resB) { unsigned int id = 0; for(auto& jobsA : jobsEmpVA) { auto& dec = neighbor.decisions[jobsA.first]; dec.lastCreneau = resA.value()[id].second.lastCreneau; newCost += dec.lastCreneau.first * STFMockInstance::jobs[jobsA.first]->getPoidsRetard(); ++id; } id = 0; for(auto& jobsB : jobsEmpVB) { auto& dec = neighbor.decisions[jobsB.first]; dec.lastCreneau = resB.value()[id].second.lastCreneau; newCost += dec.lastCreneau.first * STFMockInstance::jobs[jobsB.first]->getPoidsRetard(); ++id; } } else return std::nullopt; neighbor.cost = (neighbor.cost - oldCost) + newCost; neighbor.source = ESourceTrackPlan::SimAn; return neighbor; } std::optional SimulatedAnnealing::move_insert_WC(const SASolution& sol) { auto mock = sol.mock; 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); if (inactive_ops.empty()) return std::nullopt; std::uniform_int_distribution dist(0, (int)inactive_ops.size() - 1); unsigned short op_a = inactive_ops[dist(randomEngine)]; std::vector dispCandidate; for (auto& disp : mock->jobDispoVoiesRames[op_a]) { auto dur = mock->dispoVoiesRames[disp].match.second - mock->dispoVoiesRames[disp].match.first; if(dur >= STFMockInstance::jobs[op_a]->getDuree()) { dispCandidate.push_back(disp); } else if(dur >= STFMockInstance::jobs[op_a]->getDureeDiag() && sol.diagCost + STFMockInstance::jobs[op_a]->getPoidsRejet() <= configlib::Configuration::Global.EPSILON) { dispCandidate.push_back(disp); } } if(dispCandidate.empty()) return std::nullopt; std::uniform_int_distribution cand_dist(0, (int)dispCandidate.size() - 1); auto disp = dispCandidate[cand_dist(randomEngine)]; //Construire le voisin - try insert SASolution neighbor = sol; Decision& new_dec_a = neighbor.decisions[op_a]; auto dur = mock->dispoVoiesRames[disp].match.second - mock->dispoVoiesRames[disp].match.first; new_dec_a.rejected = dur >= STFMockInstance::jobs[op_a]->getDuree() ? false : true, new_dec_a.excluded = false; new_dec_a.voie = mock->dispoVoiesRames[disp].voie; new_dec_a.site = mock->dispoVoiesRames[disp].site; new_dec_a.empV = mock->dispoVoiesRames[disp].dispoVoie; new_dec_a.empR = mock->dispoVoiesRames[disp].dispoRame; new_dec_a.timeslotGraphSplited = mock->trajectoryStops[new_dec_a.empR].getDispoStop(); new_dec_a.lastCreneau = {0,0}; unsigned int oldCost = MAXIMUM_TIME_OFFSET*STFMockInstance::jobs[op_a]->getPoidsRetard(); unsigned int oldDiagCost = 0; unsigned int newDiagCost = new_dec_a.rejected ? STFMockInstance::jobs[op_a]->getPoidsRejet() : 0; std::vector> jobsEmpVA; for(auto& dec : neighbor.decisions) { if(!dec.second.excluded && dec.first != op_a) { if(dec.second.empV == new_dec_a.empV) { jobsEmpVA.push_back({dec.first, dec.second}); oldCost += dec.second.lastCreneau.first * STFMockInstance::jobs[dec.first]->getPoidsRetard(); } } } std::sort(jobsEmpVA.begin(), jobsEmpVA.end(), [&](auto& el1, auto& el2){ auto cren1 = el1.second.lastCreneau; auto cren2 = el2.second.lastCreneau; return cren1.first < cren2.first; }); std::uniform_int_distribution distPos(0,jobsEmpVA.size()); auto pos = distPos(randomEngine); auto seq = jobsEmpVA; if(pos == jobsEmpVA.size()) { seq.insert(seq.end(), std::make_pair(op_a, new_dec_a)); } else { seq.insert(seq.begin() + pos, std::make_pair(op_a, new_dec_a)); } unsigned int newCost = 0; auto res = checkSequence(seq, new_dec_a.empV); if(res) { unsigned int id = 0; for(auto& jobsA : seq) { auto& dec = neighbor.decisions[jobsA.first]; dec.lastCreneau = res.value()[id].second.lastCreneau; newCost += dec.lastCreneau.first * STFMockInstance::jobs[jobsA.first]->getPoidsRetard(); ++id; } neighbor.diagCost = (neighbor.diagCost - oldDiagCost) + newDiagCost; neighbor.cost = (neighbor.cost - oldCost) + newCost; neighbor.source = ESourceTrackPlan::SimAn; return neighbor; } return std::nullopt; } std::optional SimulatedAnnealing::move_move_WC(const SASolution& sol) { auto mock = sol.mock; 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); 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); for (auto& disp : mock->jobDispoVoiesRames[op_a]) { if(dec_a.empV == mock->dispoVoiesRames[disp].dispoVoie) continue; auto dur = mock->dispoVoiesRames[disp].match.second - mock->dispoVoiesRames[disp].match.first; if(dur >= STFMockInstance::jobs[op_a]->getDuree()) { dispCandidate.push_back(disp); } else if(dur >= STFMockInstance::jobs[op_a]->getDureeDiag() && sol.diagCost + STFMockInstance::jobs[op_a]->getPoidsRejet() <= configlib::Configuration::Global.EPSILON) { dispCandidate.push_back(disp); } } if(dispCandidate.empty()) return std::nullopt; std::uniform_int_distribution cand_dist(0, (int)dispCandidate.size() - 1); auto disp = dispCandidate[cand_dist(randomEngine)]; //Construire le voisin - try insert SASolution neighbor = sol; Decision& new_dec_a = neighbor.decisions[op_a]; auto dur = mock->dispoVoiesRames[disp].match.second - mock->dispoVoiesRames[disp].match.first; new_dec_a.rejected = dur >= STFMockInstance::jobs[op_a]->getDuree() ? false : true, new_dec_a.voie = mock->dispoVoiesRames[disp].voie; new_dec_a.site = mock->dispoVoiesRames[disp].site; new_dec_a.empV = mock->dispoVoiesRames[disp].dispoVoie; new_dec_a.empR = mock->dispoVoiesRames[disp].dispoRame; new_dec_a.timeslotGraphSplited = mock->trajectoryStops[new_dec_a.empR].getDispoStop(); new_dec_a.lastCreneau = {0,0}; unsigned int oldCost = dec_a.lastCreneau.first*STFMockInstance::jobs[op_a]->getPoidsRetard(); unsigned int oldDiagCost = dec_a.rejected ? STFMockInstance::jobs[op_a]->getPoidsRejet() : 0; unsigned int newDiagCost = new_dec_a.rejected ? STFMockInstance::jobs[op_a]->getPoidsRejet() : 0; std::vector> jobsEmpVA; std::vector> jobsEmpVB; for(auto& dec : neighbor.decisions) { if(!dec.second.excluded && dec.first != op_a) { if(dec.second.empV == new_dec_a.empV) { jobsEmpVA.push_back({dec.first, dec.second}); oldCost += dec.second.lastCreneau.first * STFMockInstance::jobs[dec.first]->getPoidsRetard(); } } if(!dec.second.excluded) { if(dec.second.empV == dec_a.empV) { jobsEmpVB.push_back({dec.first, dec.second}); oldCost += dec.second.lastCreneau.first * STFMockInstance::jobs[dec.first]->getPoidsRetard(); } } } std::sort(jobsEmpVA.begin(), jobsEmpVA.end(), [&](auto& el1, auto& el2){ auto cren1 = el1.second.lastCreneau; auto cren2 = el2.second.lastCreneau; return cren1.first < cren2.first; }); std::sort(jobsEmpVB.begin(), jobsEmpVB.end(), [&](auto& el1, auto& el2){ auto cren1 = el1.second.lastCreneau; auto cren2 = el2.second.lastCreneau; return cren1.first < cren2.first; }); unsigned int newCost = 0; //décale à gauche sur track de départ auto resB = checkSequence(jobsEmpVB, dec_a.empV); unsigned int id = 0; for(auto& jobsB : jobsEmpVB) { auto& dec = neighbor.decisions[jobsB.first]; dec.lastCreneau = resB.value()[id].second.lastCreneau; newCost+= dec.lastCreneau.first * STFMockInstance::jobs[jobsB.first]->getPoidsRetard(); ++id; } std::uniform_int_distribution distPos(0,jobsEmpVA.size()); auto pos = distPos(randomEngine); auto seq = jobsEmpVA; if(pos == jobsEmpVA.size()) { seq.insert(seq.end(), std::make_pair(op_a, new_dec_a)); } else { seq.insert(seq.begin() + pos, std::make_pair(op_a, new_dec_a)); } auto res = checkSequence(seq, new_dec_a.empV); if(res) { unsigned int id = 0; for(auto& jobsA : seq) { auto& dec = neighbor.decisions[jobsA.first]; dec.lastCreneau = res.value()[id].second.lastCreneau; newCost+= dec.lastCreneau.first * STFMockInstance::jobs[jobsA.first]->getPoidsRetard(); ++id; } neighbor.diagCost = (neighbor.diagCost - oldDiagCost) + newDiagCost; neighbor.cost = (neighbor.cost - oldCost) + newCost; neighbor.source = ESourceTrackPlan::SimAn; return neighbor; } return std::nullopt; } std::optional SimulatedAnnealing::move_remove_WC(const SASolution& sol) { auto mock = sol.mock; 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); 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); //Construire le voisin - exclure a SASolution neighbor = sol; const Decision& dec_a = sol.decisions.at(op_a); Decision& new_dec_a = neighbor.decisions[op_a]; new_dec_a.rejected = false, new_dec_a.excluded = true; new_dec_a.voie = 0; new_dec_a.site = 0; new_dec_a.empV = 0; new_dec_a.empR = 0; new_dec_a.timeslotGraphSplited = CreneauHoraire(); new_dec_a.lastCreneau = {0,0}; unsigned int oldCost = dec_a.lastCreneau.first * STFMockInstance::jobs[op_a]->getPoidsRetard(); unsigned int oldDiagCost = dec_a.rejected ? STFMockInstance::jobs[op_a]->getPoidsRejet() : 0; unsigned int newDiagCost = 0; std::vector> jobsEmpVA; for(auto& dec : neighbor.decisions) { if(!dec.second.excluded) { if(dec.second.empV == dec_a.empV) { jobsEmpVA.push_back({dec.first, dec.second}); oldCost += dec.second.lastCreneau.first * STFMockInstance::jobs[dec.first]->getPoidsRetard(); } } } unsigned int newCost = MAXIMUM_TIME_OFFSET * STFMockInstance::jobs[op_a]->getPoidsRetard(); if(!jobsEmpVA.empty()) { std::sort(jobsEmpVA.begin(), jobsEmpVA.end(), [&](auto& el1, auto& el2){ auto cren1 = el1.second.lastCreneau; auto cren2 = el2.second.lastCreneau; return cren1.first < cren2.first; }); auto resA = checkSequence(jobsEmpVA, dec_a.empV); if(resA) { unsigned int id = 0; for(auto& jobsA : jobsEmpVA) { auto& dec = neighbor.decisions[jobsA.first]; dec.lastCreneau = resA.value()[id].second.lastCreneau; newCost += dec.lastCreneau.first * STFMockInstance::jobs[jobsA.first]->getPoidsRetard(); ++id; } } else return std::nullopt; } neighbor.diagCost = (neighbor.diagCost - oldDiagCost) + newDiagCost; neighbor.cost = (neighbor.cost - oldCost) + newCost; neighbor.source = ESourceTrackPlan::SimAn; return neighbor; } std::optional SimulatedAnnealing::move_change_mode_WC(const SASolution& sol) { auto mock = sol.mock; 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); 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); SASolution neighbor = sol; Decision& new_dec_a = neighbor.decisions[op_a]; unsigned int oldDiagCost = dec_a.rejected ? STFMockInstance::jobs[op_a]->getPoidsRejet() : 0; unsigned int newDiagCost = dec_a.rejected ? 0 : STFMockInstance::jobs[op_a]->getPoidsRejet(); unsigned int oldCost = 0; if(dec_a.rejected) { new_dec_a.rejected = false; } else { if(neighbor.diagCost + STFMockInstance::jobs[op_a]->getPoidsRejet() <= configlib::Configuration::Global.EPSILON) { new_dec_a.rejected = true; } else { return std::nullopt; } } std::vector> jobsEmpVA; for(auto& dec : neighbor.decisions) { if(!dec.second.excluded) { if(dec.second.empV == dec_a.empV) { jobsEmpVA.push_back({dec.first, dec.second}); oldCost += dec.second.lastCreneau.first * STFMockInstance::jobs[dec.first]->getPoidsRetard(); } } } std::sort(jobsEmpVA.begin(), jobsEmpVA.end(), [&](auto& el1, auto& el2){ auto cren1 = el1.second.lastCreneau; auto cren2 = el2.second.lastCreneau; return cren1.first < cren2.first; }); unsigned int newCost = 0; if(!jobsEmpVA.empty()) { auto resA = checkSequence(jobsEmpVA, dec_a.empV); if(resA) { unsigned int id = 0; for(auto& jobsA : jobsEmpVA) { auto& dec = neighbor.decisions[jobsA.first]; dec.lastCreneau = resA.value()[id].second.lastCreneau; newCost += dec.lastCreneau.first * STFMockInstance::jobs[jobsA.first]->getPoidsRetard(); ++id; } } else return std::nullopt; } neighbor.diagCost = (neighbor.diagCost - oldDiagCost) + newDiagCost; neighbor.cost = (neighbor.cost - oldCost) + newCost; neighbor.source = ESourceTrackPlan::SimAn; return neighbor; } std::optional>> SimulatedAnnealing::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 std::nullopt; minBegin = minBegin + duration; res.push_back(newJobDec); } 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::set uniqueSchedules; std::vector newTMPVec; for(auto& trackSch : plans) { uniqueSchedules.insert(trackSch); } auto uniqueNb = uniqueSchedules.size(); std::move(uniqueSchedules.begin(), uniqueSchedules.end(), std::back_inserter(newTMPVec)); std::swap(plans, newTMPVec); return uniqueNb; }*/ }