Recuit simulé ajout de remove et insert dépendant de la progression du recuit + autoriser le remove à couter quedalle au début puis un max et de même pour les violation d'intervales (pour l'instant une seule violation autorisée et compatibilisée à la detection)

This commit is contained in:
tom
2026-06-04 21:59:36 +02:00
parent 4fc8a7be89
commit 3c94d56120
10 changed files with 515 additions and 356 deletions
@@ -1,6 +1,7 @@
#include "SimulatedAnnealing.hpp"
#include "../PseCarlierRivreau/omp.h"
#include "DynamicProgramming.hpp"
#include "Penalty.hpp"
#include "Solution.hpp"
#include "TrackPlan.hpp"
#include <algorithm>
@@ -10,24 +11,28 @@
#include <iterator>
#include <optional>
#include <random>
#include <stdexcept>
#include <string>
#include <utility>
#include <vector>
#include "Random.hpp"
#include "sourceSolTrPlan.hpp"
namespace solverlib {
using namespace random;
bool StatSimulatedAnnealing::activate = true;
bool SimulatedAnnealing::withDynProg = false;
bool SimulatedAnnealing::authorizeInfeasible = true;
constexpr double decreaseFunction(double p){return std::exp(-0.75*(1.0-p));};
std::unordered_map<EMovingOperators, std::string> StatSimulatedAnnealing::names = {
{EMovingOperators::CHANGE_MODE_WITHOUT_CARLIER, "CHANGE_MODE"},
{EMovingOperators::INSERT_WITHOUT_CARLIER, "INSERT"},
{EMovingOperators::REMOVE_WITHOUT_CARLIER, "REMOVE"},
{EMovingOperators::SWAP_WITHOUT_CARLIER, "SWAP"},
{EMovingOperators::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<unsigned short, Decision>& decs, std::shared_ptr<modellib::STFMockInstance> 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<unsigned short, Decision>& decs, std::shared_ptr<modellib::STFMockInstance> mock, ESourceTrackPlan source, bool isPutFirst)
@@ -73,58 +82,24 @@ namespace solverlib {
return {cost, diagCost};
}
std::pair<unsigned int, unsigned int> SimulatedAnnealing::getCostOfSequence(std::vector<std::pair<unsigned short, decision>>& jobsSeq)
{
unsigned int cost = 0;
unsigned int costDiag = 0;
for(auto& el : jobsSeq)
{
cost += STFMockInstance::jobs[el.first]->getPoidsRetard()*el.second.lastCreneau.first;
costDiag += STFMockInstance::jobs[el.first]->getPoidsRejet()*el.second.rejected;
}
return {cost, costDiag};
}
// tire un opérateur uniformément
EMovingOperators SimulatedAnnealing::pick_operator(double temp, double tmax) {
int N = withDynProg ? static_cast<int>(EMovingOperators::DYN_PROG)+1 : static_cast<int>(EMovingOperators::MOVE)+1;
std::vector<float> weights(N, 1);
EMovingOperators SimulatedAnnealing::pick_operator() {
double p = progress();
float w_remove = static_cast<float>(2*std::max(0.0,1.0 - decreaseFunction(p)));
float w_insert = static_cast<float>(2*std::min(1.0, decreaseFunction(p)));
std::vector<float> 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<EMovingOperators>(
//std::uniform_int_distribution<int>(0, N)(randomEngine)
std::discrete_distribution<int>(weights.begin(), weights.end())(randomEngine)
std::discrete_distribution<int>(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<double> 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<EPenaltyType, double> 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<SASolution> 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<unsigned short>& seq) -> std::optional<SASolution> {
SASolution neighbor = sol;
auto creneauTrack = STFMockInstance::machines[dec_a.empV]->getDispo();
unsigned short lastEnd = 0;
unsigned int oldCost = 0;
unsigned int newCost = 0;
for(auto& job : seq)
{
Decision& dec_neih = neighbor.decisions.at(job);
auto creneauJob = mock->trajectoryStops[dec_neih.empR].getDispoStop();
auto match = CreneauHoraire::checkSlotsCompatibility(creneauJob, creneauTrack);
oldCost += STFMockInstance::jobs[job]->getPoidsRetard()*sol.decisions.at(job).lastCreneau.first;
if(match.first)
{
if(std::max(match.second.first, lastEnd) + dec_neih.rejected*STFMockInstance::jobs[job]->getDureeDiag() + !dec_neih.rejected*STFMockInstance::jobs[job]->getDuree() > match.second.first + match.second.second)
return std::nullopt;
dec_neih.lastCreneau = {
std::max(match.second.first, lastEnd),
std::max(match.second.first, lastEnd)+ dec_neih.rejected*STFMockInstance::jobs[job]->getDureeDiag() + !dec_neih.rejected*STFMockInstance::jobs[job]->getDuree()
};
newCost += STFMockInstance::jobs[job]->getPoidsRetard()*dec_neih.lastCreneau.first;
lastEnd = std::max(match.second.first, lastEnd)+ dec_neih.rejected*STFMockInstance::jobs[job]->getDureeDiag() + !dec_neih.rejected*STFMockInstance::jobs[job]->getDuree();
}
else {
return std::nullopt;
}
}
neighbor.cost = (neighbor.cost - oldCost) + newCost;
neighbor.source = ESourceTrackPlan::SimAn;
return neighbor;
};
//TEST SWAP
std::uniform_int_distribution<int> posR(0, (int)seq_swap.size()-1);
auto job = posR(randomEngine);
auto IposA = std::distance(seq_swap.begin(), posA);
while(job == IposA)
job = posR(randomEngine);
// Tirer un index différent de IposA pour le swap
std::uniform_int_distribution<int> 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<std::pair<unsigned short, Decision>> 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<SASolution> 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<std::pair<unsigned short, decision>> jobsEmpVA;
std::vector<std::pair<unsigned short, decision>> jobsEmpVB;
unsigned int oldCost = dec_a.lastCreneau.first * STFMockInstance::jobs[op_a]->getPoidsRetard() + dec_b.lastCreneau.first * STFMockInstance::jobs[swap.op_b]->getPoidsRetard();
@@ -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<std::pair<unsigned short, decision>> 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<SASolution> 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<unsigned int> 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<SASolution> 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<std::pair<unsigned short, decision>> 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<std::pair<unsigned short, decision>> 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<std::vector<std::pair<unsigned short, Decision>>>(res);
}
std::pair<bool, std::vector<unsigned int>> SimulatedAnnealing::PSE_Carlier_Rivreau(std::vector<std::pair<unsigned short, decision>> &jobs, unsigned int voieMachine) {
std::stringstream fakeFile;
for (unsigned int i = 0; i < jobs.size(); ++i) {
auto match = CreneauHoraire::checkSlotsCompatibility(jobs[i].second.timeslotGraphSplited,
STFMockInstance::machines[voieMachine]->getDispo());
fakeFile << i + 1;
fakeFile << " " << match.second.first;
if (!jobs[i].second.rejected) {
fakeFile << " " << STFMockInstance::jobs[jobs[i].first]->getDuree();
} else
fakeFile << " " << STFMockInstance::jobs[jobs[i].first]->getDureeDiag();
fakeFile << " " << match.second.first + match.second.second;
fakeFile << std::endl;
}
std::ifstream file;
file.basic_ios<char>::rdbuf(fakeFile.rdbuf());
Solution sol((int) jobs.size());
OneMachine machine(file);
machine.solve(sol);
std::vector<unsigned int> solution(jobs.size());
for (unsigned int i = 0; i < jobs.size(); ++i) {
solution[i] = sol.startTime[i+1];
}
return {machine.checkSol(sol) && sol.Lmax <= 0, solution};
}
/*unsigned long SimulatedAnnealing::getUniquePlans(std::vector<TrackPlan>& plans)
std::optional<std::vector<std::pair<unsigned short, Decision>>>
SimulatedAnnealing::checkSequence(
const std::vector<std::pair<unsigned short, Decision>>& jobsDec,
unsigned int machine,
Penalty& outPenalty,
bool& outFeasible)
{
std::set<TrackPlan> uniqueSchedules;
std::vector<TrackPlan> newTMPVec;
for(auto& trackSch : plans)
auto machineDisp = STFMockInstance::machines[machine]->getDispo();
unsigned short minBegin = machineDisp.getDebut().getRelativeDate();
std::vector<std::pair<unsigned short, Decision>> 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<double>(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;
}
}