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
+2
View File
@@ -1,2 +1,4 @@
build build
lib lib
Data
.cache
@@ -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/"
}
]
}
}
-17
View File
@@ -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/"
}
]
}
@@ -0,0 +1,59 @@
#ifndef PENALTY_HPP
#define PENALTY_HPP
#include <unordered_map>
#include <string>
namespace solverlib {
enum class EPenaltyType {
TIME_WINDOW_OVERRUN, // job dépasse sa fenêtre
};
struct Penalty {
std::unordered_map<EPenaltyType, double> 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<EPenaltyType, double>& 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
@@ -1,6 +1,7 @@
#include "SimulatedAnnealing.hpp" #include "SimulatedAnnealing.hpp"
#include "../PseCarlierRivreau/omp.h" #include "../PseCarlierRivreau/omp.h"
#include "DynamicProgramming.hpp" #include "DynamicProgramming.hpp"
#include "Penalty.hpp"
#include "Solution.hpp" #include "Solution.hpp"
#include "TrackPlan.hpp" #include "TrackPlan.hpp"
#include <algorithm> #include <algorithm>
@@ -10,24 +11,28 @@
#include <iterator> #include <iterator>
#include <optional> #include <optional>
#include <random> #include <random>
#include <stdexcept>
#include <string> #include <string>
#include <utility> #include <utility>
#include <vector> #include <vector>
#include "Random.hpp" #include "Random.hpp"
#include "sourceSolTrPlan.hpp" #include "sourceSolTrPlan.hpp"
namespace solverlib { namespace solverlib {
using namespace random; using namespace random;
bool StatSimulatedAnnealing::activate = true; bool StatSimulatedAnnealing::activate = true;
bool SimulatedAnnealing::withDynProg = false; 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 = { std::unordered_map<EMovingOperators, std::string> StatSimulatedAnnealing::names = {
{EMovingOperators::CHANGE_MODE_WITHOUT_CARLIER, "CHANGE_MODE"}, {EMovingOperators::CHANGE_MODE, "CHANGE_MODE"},
{EMovingOperators::INSERT_WITHOUT_CARLIER, "INSERT"}, {EMovingOperators::INSERT, "INSERT"},
{EMovingOperators::REMOVE_WITHOUT_CARLIER, "REMOVE"}, {EMovingOperators::REMOVE, "REMOVE"},
{EMovingOperators::SWAP_WITHOUT_CARLIER, "SWAP"}, {EMovingOperators::SWAP, "SWAP"},
{EMovingOperators::SWAP_WITHIN_INTERVAL, "SWAP_WITHIN_SEQUENCE"}, {EMovingOperators::SWAP_WITHIN_INTERVAL, "SWAP_WITHIN_SEQUENCE"},
{EMovingOperators::DYN_PROG, "DYN_PROG"}, {EMovingOperators::DYN_PROG, "DYN_PROG"},
{EMovingOperators::MOVE, "MOVE"} {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) SimulatedAnnealing::SimulatedAnnealing(std::unordered_map<unsigned short, Decision>& decs, std::shared_ptr<modellib::STFMockInstance> mock, ESourceTrackPlan source)
{ {
randomEngine = solverlib::random::makeEngine(); 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); 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) 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}; return {cost, diagCost};
} }
std::pair<unsigned int, unsigned int> SimulatedAnnealing::getCostOfSequence(std::vector<std::pair<unsigned short, decision>>& jobsSeq) EMovingOperators SimulatedAnnealing::pick_operator() {
{ double p = progress();
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);
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 = { std::vector<float> base_weights = {
1.0f, 1,
1.0f, w_insert,
1.0f, w_remove,
1.0f, 1,
1.0f, 1,
1.0f 1
// DYN_PROG ajouté si besoin // 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>( return static_cast<EMovingOperators>(
//std::uniform_int_distribution<int>(0, N)(randomEngine) std::discrete_distribution<int>(base_weights.begin(), base_weights.end())(randomEngine)
std::discrete_distribution<int>(weights.begin(), weights.end())(randomEngine)
); );
} }
@@ -133,10 +108,10 @@ namespace solverlib {
{ {
switch (op) switch (op)
{ {
case EMovingOperators::SWAP_WITHOUT_CARLIER: return move_swap_WC(current); case EMovingOperators::SWAP: return move_swap_WC(current);
case EMovingOperators::INSERT_WITHOUT_CARLIER: return move_insert_WC(current); case EMovingOperators::INSERT: return move_insert_WC(current);
case EMovingOperators::REMOVE_WITHOUT_CARLIER: return move_remove_WC(current); case EMovingOperators::REMOVE: return move_remove_WC(current);
case EMovingOperators::CHANGE_MODE_WITHOUT_CARLIER: return move_change_mode_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::SWAP_WITHIN_INTERVAL: return move_swap_within_interval(current);
case EMovingOperators::MOVE: return move_move_WC(current); case EMovingOperators::MOVE: return move_move_WC(current);
case EMovingOperators::DYN_PROG: return move_dynprog(current); case EMovingOperators::DYN_PROG: return move_dynprog(current);
@@ -150,16 +125,22 @@ namespace solverlib {
{ {
SASolution current = solutions[0]; SASolution current = solutions[0];
SASolution best = solutions[0]; SASolution best = solutions[0];
current.fictiveCost = current.cost;
best.fictiveCost = current.cost;
Tmax = T_max;
double cost_cur = current.cost; double cost_cur = current.cost;
double cost_best= cost_cur; 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); 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) for (int i = 0; i < iterations_per_temp; ++i)
{ {
EMovingOperators op = pick_operator(T, T_max); EMovingOperators op = pick_operator();
stats.addUsed(op); stats.addUsed(op);
auto neighbor = apply_operator(current, op); auto neighbor = apply_operator(current, op);
@@ -167,23 +148,33 @@ namespace solverlib {
auto costs_neighbor = std::make_pair(neighbor->cost, neighbor->diagCost); 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) if(delta <= 0)
stats.addImproved(op); stats.addImproved(op);
if(diff <= 0 && neighbor->penalty.isFeasible())
stats.addImprovedReal(op);
if(!neighbor->penalty.isFeasible())
stats.addInfeasible(op);
if(delta > 0) 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); current = std::move(*neighbor);
cost_cur = costs_neighbor.first; cost_cur = costs_neighbor.first;
if (cost_cur < cost_best) { if (current.penalty.isFeasible() && cost_cur < cost_best) {
best = current; best = current;
cost_best = cost_cur; cost_best = cost_cur;
} }
@@ -201,22 +192,43 @@ namespace solverlib {
return best; 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) { switch (op) {
case EMovingOperators::SWAP_WITHOUT_CARLIER: case EMovingOperators::SWAP:
case EMovingOperators::INSERT_WITHOUT_CARLIER: case EMovingOperators::INSERT: return std::exp(-delta/(T*50));
case EMovingOperators::REMOVE_WITHOUT_CARLIER: return std::exp(-delta/(temperature*100)); case EMovingOperators::REMOVE: return std::exp(-delta/(T*50));
case EMovingOperators::CHANGE_MODE_WITHOUT_CARLIER: case EMovingOperators::CHANGE_MODE:
case EMovingOperators::SWAP_WITHIN_INTERVAL: 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: case EMovingOperators::DYN_PROG:
break; break;
} }
return std::exp(-delta/temperature); return std::exp(-delta/T);
} }
//OPERATEURS //OPERATEURS
std::optional<SASolution> SimulatedAnnealing::move_dynprog(const SASolution& sol) 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); auto posA = std::find(seq_swap.begin(), seq_swap.end(), op_a);
if(posA == seq_swap.end()) return std::nullopt; if(posA == seq_swap.end()) return std::nullopt;
auto lbdBuildSeq = [&](std::vector<unsigned short>& seq) -> std::optional<SASolution> { auto IposA = std::distance(seq_swap.begin(), posA);
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 = { // Tirer un index différent de IposA pour le swap
std::max(match.second.first, lastEnd), std::uniform_int_distribution<int> posR(0, (int)seq_swap.size() - 1);
std::max(match.second.first, lastEnd)+ dec_neih.rejected*STFMockInstance::jobs[job]->getDureeDiag() + !dec_neih.rejected*STFMockInstance::jobs[job]->getDuree() int posB = posR(randomEngine);
}; while (posB == IposA)
newCost += STFMockInstance::jobs[job]->getPoidsRetard()*dec_neih.lastCreneau.first; posB = posR(randomEngine);
lastEnd = std::max(match.second.first, lastEnd)+ dec_neih.rejected*STFMockInstance::jobs[job]->getDureeDiag() + !dec_neih.rejected*STFMockInstance::jobs[job]->getDuree();
} auto seqCop = seq_swap;
else { 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.cost = (neighbor.cost - oldCost) + newCost;
neighbor.source = ESourceTrackPlan::SimAn; neighbor.source = ESourceTrackPlan::SimAn;
return neighbor; return neighbor;
};
//TEST SWAP
std::uniform_int_distribution<int> posR(0, (int)seq_swap.size()-1);
auto job = posR(randomEngine);
auto IposA = std::distance(seq_swap.begin(), posA);
while(job == IposA)
job = posR(randomEngine);
auto seqCop = seq_swap;
auto posJobInt = job;
seqCop[posJobInt] = *posA;
seqCop[IposA] = seq_swap[job];
auto res = lbdBuildSeq(seqCop);
if(res != std::nullopt)
return res;
return std::nullopt;
} }
std::optional<SASolution> SimulatedAnnealing::move_swap_WC(const SASolution& sol) std::optional<SASolution> SimulatedAnnealing::move_swap_WC(const SASolution& sol)
@@ -397,6 +402,12 @@ namespace solverlib {
auto oldCrenA = dec_a.lastCreneau; auto oldCrenA = dec_a.lastCreneau;
auto oldCrenB = dec_b.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>> jobsEmpVA;
std::vector<std::pair<unsigned short, decision>> jobsEmpVB; 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(); unsigned int oldCost = dec_a.lastCreneau.first * STFMockInstance::jobs[op_a]->getPoidsRetard() + dec_b.lastCreneau.first * STFMockInstance::jobs[swap.op_b]->getPoidsRetard();
@@ -443,30 +454,22 @@ namespace solverlib {
} }
return cren1.first < cren2.first; 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; unsigned int newCost = 0;
if(resA && resB) if(!resA || !resB) return std::nullopt;
{
unsigned int id = 0; applySequenceResult(neighbor, jobsEmpVA, *resA, newCost, penA, feasA);
for(auto& jobsA : jobsEmpVA) applySequenceResult(neighbor, jobsEmpVB, *resB, newCost, penB, feasB);
{
auto& dec = neighbor.decisions[jobsA.first]; neighbor.penalty = sol.penalty - oldPenA - oldPenB + penA + penB;
dec.lastCreneau = resA.value()[id].second.lastCreneau; neighbor.penaltyPerMachine[new_dec_a.empV] = penA;
newCost += dec.lastCreneau.first * STFMockInstance::jobs[jobsA.first]->getPoidsRetard(); neighbor.penaltyPerMachine[new_dec_b.empV] = penB;
++id; neighbor.isFeasible = neighbor.penalty.isFeasible();
} neighbor.fictiveCost = (neighbor.fictiveCost - oldCost) + newCost;
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.cost = (neighbor.cost - oldCost) + newCost;
neighbor.source = ESourceTrackPlan::SimAn; neighbor.source = ESourceTrackPlan::SimAn;
return neighbor; return neighbor;
@@ -517,8 +520,12 @@ namespace solverlib {
new_dec_a.timeslotGraphSplited = mock->trajectoryStops[new_dec_a.empR].getDispoStop(); new_dec_a.timeslotGraphSplited = mock->trajectoryStops[new_dec_a.empR].getDispoStop();
new_dec_a.lastCreneau = {0,0}; 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 oldDiagCost = 0;
unsigned int oldCostScheduled = 0;
unsigned int newDiagCost = new_dec_a.rejected ? STFMockInstance::jobs[op_a]->getPoidsRejet() : 0; unsigned int newDiagCost = new_dec_a.rejected ? STFMockInstance::jobs[op_a]->getPoidsRejet() : 0;
std::vector<std::pair<unsigned short, decision>> jobsEmpVA; std::vector<std::pair<unsigned short, decision>> jobsEmpVA;
for(auto& dec : neighbor.decisions) for(auto& dec : neighbor.decisions)
@@ -528,7 +535,7 @@ namespace solverlib {
if(dec.second.empV == new_dec_a.empV) if(dec.second.empV == new_dec_a.empV)
{ {
jobsEmpVA.push_back({dec.first, dec.second}); 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 { else {
seq.insert(seq.begin() + pos, std::make_pair(op_a, new_dec_a)); 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; unsigned int newCost = 0;
Penalty penA; bool feasA = true;
auto res = checkSequence(seq, new_dec_a.empV, penA, feasA);
if(!res) 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) 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.timeslotGraphSplited = mock->trajectoryStops[new_dec_a.empR].getDispoStop();
new_dec_a.lastCreneau = {0,0}; 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 oldCost = dec_a.lastCreneau.first*STFMockInstance::jobs[op_a]->getPoidsRetard();
unsigned int oldDiagCost = dec_a.rejected ? STFMockInstance::jobs[op_a]->getPoidsRejet() : 0; 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; unsigned int newDiagCost = new_dec_a.rejected ? STFMockInstance::jobs[op_a]->getPoidsRejet() : 0;
@@ -658,18 +677,15 @@ namespace solverlib {
}); });
unsigned int newCost = 0; unsigned int newCost = 0;
Penalty penA; Penalty penB;
bool feasA = true; bool feasB = true;
//décale à gauche sur track de départ //décale à gauche sur track de départ
auto resB = checkSequence(jobsEmpVB, dec_a.empV); if(!jobsEmpVB.empty())
unsigned int id = 0;
for(auto& jobsB : jobsEmpVB)
{ {
auto& dec = neighbor.decisions[jobsB.first]; auto resB = checkSequence(jobsEmpVB, dec_a.empV, penB, feasB);
dec.lastCreneau = resB.value()[id].second.lastCreneau; applySequenceResult(neighbor, jobsEmpVB, *resB, newCost, penB, feasB);
newCost+= dec.lastCreneau.first * STFMockInstance::jobs[jobsB.first]->getPoidsRetard();
++id;
} }
std::uniform_int_distribution<unsigned int> distPos(0,jobsEmpVA.size()); std::uniform_int_distribution<unsigned int> distPos(0,jobsEmpVA.size());
auto pos = distPos(randomEngine); auto pos = distPos(randomEngine);
@@ -681,26 +697,22 @@ namespace solverlib {
else { else {
seq.insert(seq.begin() + pos, std::make_pair(op_a, new_dec_a)); seq.insert(seq.begin() + pos, std::make_pair(op_a, new_dec_a));
} }
auto res = checkSequence(seq, new_dec_a.empV); auto res = checkSequence(seq, new_dec_a.empV, penA, feasA);
if(res) if(!res) return std::nullopt;
{
unsigned int id = 0; applySequenceResult(neighbor, seq, *res, newCost, penA, feasA);
for(auto& jobsA : seq)
{ neighbor.penalty = sol.penalty - oldPenSrc - oldPenDst + penB + penA;
auto& dec = neighbor.decisions[jobsA.first]; neighbor.penaltyPerMachine[dec_a.empV] = penB;
dec.lastCreneau = res.value()[id].second.lastCreneau; neighbor.penaltyPerMachine[new_dec_a.empV] = penA;
newCost+= dec.lastCreneau.first * STFMockInstance::jobs[jobsA.first]->getPoidsRetard(); neighbor.isFeasible = neighbor.penalty.isFeasible();
++id; neighbor.fictiveCost = (neighbor.fictiveCost - oldCost) + newCost;
}
neighbor.diagCost = (neighbor.diagCost - oldDiagCost) + newDiagCost; neighbor.diagCost = (neighbor.diagCost - oldDiagCost) + newDiagCost;
neighbor.cost = (neighbor.cost - oldCost) + newCost; neighbor.cost = (neighbor.cost - oldCost) + newCost;
neighbor.source = ESourceTrackPlan::SimAn; neighbor.source = ESourceTrackPlan::SimAn;
return neighbor; return neighbor;
} }
return std::nullopt;
}
std::optional<SASolution> SimulatedAnnealing::move_remove_WC(const SASolution& sol) std::optional<SASolution> SimulatedAnnealing::move_remove_WC(const SASolution& sol)
{ {
auto mock = sol.mock; auto mock = sol.mock;
@@ -729,9 +741,12 @@ namespace solverlib {
new_dec_a.timeslotGraphSplited = CreneauHoraire(); new_dec_a.timeslotGraphSplited = CreneauHoraire();
new_dec_a.lastCreneau = {0,0}; 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 oldCost = dec_a.lastCreneau.first * STFMockInstance::jobs[op_a]->getPoidsRetard();
unsigned int oldDiagCost = dec_a.rejected ? STFMockInstance::jobs[op_a]->getPoidsRejet() : 0; unsigned int oldDiagCost = dec_a.rejected ? STFMockInstance::jobs[op_a]->getPoidsRejet() : 0;
unsigned int newDiagCost = 0;
std::vector<std::pair<unsigned short, decision>> jobsEmpVA; std::vector<std::pair<unsigned short, decision>> jobsEmpVA;
for(auto& dec : neighbor.decisions) for(auto& dec : neighbor.decisions)
@@ -745,33 +760,32 @@ 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()) if(!jobsEmpVA.empty())
{ {
std::sort(jobsEmpVA.begin(), jobsEmpVA.end(), [&](auto& el1, auto& el2){ std::sort(jobsEmpVA.begin(), jobsEmpVA.end(), [&](auto& el1, auto& el2){
auto cren1 = el1.second.lastCreneau; auto cren1 = el1.second.lastCreneau;
auto cren2 = el2.second.lastCreneau; auto cren2 = el2.second.lastCreneau;
return cren1.first < cren2.first; return cren1.first < cren2.first;
}); });
auto resA = checkSequence(jobsEmpVA, dec_a.empV); auto resA = checkSequence(jobsEmpVA, dec_a.empV, penA, feasA);
if(resA) if(!resA) return std::nullopt;
{
unsigned int id = 0; applySequenceResult(neighbor, jobsEmpVA, *resA, remaining, penA, feasA);
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 double fictivePenalty = fictiveCostExcluded(op_a);
return std::nullopt; unsigned int excludedCost = MAXIMUM_TIME_OFFSET * STFMockInstance::jobs[op_a]->getPoidsRetard();
}
neighbor.diagCost = (neighbor.diagCost - oldDiagCost) + newDiagCost; neighbor.penalty = sol.penalty - oldPen + penA;
neighbor.cost = (neighbor.cost - oldCost) + newCost; 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; neighbor.source = ESourceTrackPlan::SimAn;
return neighbor; return neighbor;
} }
@@ -793,9 +807,9 @@ namespace solverlib {
SASolution neighbor = sol; SASolution neighbor = sol;
Decision& new_dec_a = neighbor.decisions[op_a]; Decision& new_dec_a = neighbor.decisions[op_a];
unsigned int oldDiagCost = dec_a.rejected ? STFMockInstance::jobs[op_a]->getPoidsRejet() : 0; 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 newDiagCost = dec_a.rejected ? 0 : STFMockInstance::jobs[op_a]->getPoidsRejet();
unsigned int oldCost = 0;
if(dec_a.rejected) if(dec_a.rejected)
{ {
new_dec_a.rejected = false; 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; std::vector<std::pair<unsigned short, decision>> jobsEmpVA;
for(auto& dec : neighbor.decisions) for(auto& dec : neighbor.decisions)
{ {
@@ -829,23 +848,17 @@ namespace solverlib {
return cren1.first < cren2.first; return cren1.first < cren2.first;
}); });
unsigned int newCost = 0; unsigned int newCost = 0;
if(!jobsEmpVA.empty())
{ Penalty penA; bool feasA = true;
auto resA = checkSequence(jobsEmpVA, dec_a.empV); auto resA = checkSequence(jobsEmpVA, dec_a.empV, penA, feasA);
if(resA) if(!resA) return std::nullopt;
{
unsigned int id = 0; applySequenceResult(neighbor, jobsEmpVA, *resA, newCost, penA, feasA);
for(auto& jobsA : jobsEmpVA)
{ neighbor.penalty = sol.penalty - oldPen + penA;
auto& dec = neighbor.decisions[jobsA.first]; neighbor.penaltyPerMachine[dec_a.empV] = penA;
dec.lastCreneau = resA.value()[id].second.lastCreneau; neighbor.isFeasible = neighbor.penalty.isFeasible();
newCost += dec.lastCreneau.first * STFMockInstance::jobs[jobsA.first]->getPoidsRetard(); neighbor.fictiveCost = (neighbor.fictiveCost - oldCost) + newCost;
++id;
}
}
else
return std::nullopt;
}
neighbor.diagCost = (neighbor.diagCost - oldDiagCost) + newDiagCost; neighbor.diagCost = (neighbor.diagCost - oldDiagCost) + newDiagCost;
neighbor.cost = (neighbor.cost - oldCost) + newCost; neighbor.cost = (neighbor.cost - oldCost) + newCost;
neighbor.source = ESourceTrackPlan::SimAn; neighbor.source = ESourceTrackPlan::SimAn;
@@ -872,53 +885,53 @@ namespace solverlib {
return std::optional<std::vector<std::pair<unsigned short, Decision>>>(res); return std::optional<std::vector<std::pair<unsigned short, Decision>>>(res);
} }
std::optional<std::vector<std::pair<unsigned short, Decision>>>
std::pair<bool, std::vector<unsigned int>> SimulatedAnnealing::PSE_Carlier_Rivreau(std::vector<std::pair<unsigned short, decision>> &jobs, unsigned int voieMachine) { SimulatedAnnealing::checkSequence(
const std::vector<std::pair<unsigned short, Decision>>& jobsDec,
std::stringstream fakeFile; unsigned int machine,
Penalty& outPenalty,
for (unsigned int i = 0; i < jobs.size(); ++i) { bool& outFeasible)
auto match = CreneauHoraire::checkSlotsCompatibility(jobs[i].second.timeslotGraphSplited,
STFMockInstance::machines[voieMachine]->getDispo());
fakeFile << i + 1;
fakeFile << " " << match.second.first;
if (!jobs[i].second.rejected) {
fakeFile << " " << STFMockInstance::jobs[jobs[i].first]->getDuree();
} else
fakeFile << " " << STFMockInstance::jobs[jobs[i].first]->getDureeDiag();
fakeFile << " " << match.second.first + match.second.second;
fakeFile << std::endl;
}
std::ifstream file;
file.basic_ios<char>::rdbuf(fakeFile.rdbuf());
Solution sol((int) jobs.size());
OneMachine machine(file);
machine.solve(sol);
std::vector<unsigned int> solution(jobs.size());
for (unsigned int i = 0; i < jobs.size(); ++i) {
solution[i] = sol.startTime[i+1];
}
return {machine.checkSol(sol) && sol.Lmax <= 0, solution};
}
/*unsigned long SimulatedAnnealing::getUniquePlans(std::vector<TrackPlan>& plans)
{ {
std::set<TrackPlan> uniqueSchedules; auto machineDisp = STFMockInstance::machines[machine]->getDispo();
std::vector<TrackPlan> newTMPVec; unsigned short minBegin = machineDisp.getDebut().getRelativeDate();
for(auto& trackSch : plans) 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 uniqueNb = uniqueSchedules.size();
std::move(uniqueSchedules.begin(), uniqueSchedules.end(), std::back_inserter(newTMPVec));
std::swap(plans, newTMPVec);
return uniqueNb;
}*/
auto newJobDec = jobDec;
newJobDec.second.lastCreneau = {minBegin, jobEnd};
minBegin = jobEnd;
res.push_back(newJobDec);
}
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;
}
} }
@@ -6,16 +6,17 @@
#include <random> #include <random>
#include <unordered_map> #include <unordered_map>
#include <vector> #include <vector>
#include "Penalty.hpp"
#include "TrackPlan.hpp" #include "TrackPlan.hpp"
#include "Solution.hpp" #include "Solution.hpp"
namespace solverlib { namespace solverlib {
using namespace modellib; using namespace modellib;
enum class EMovingOperators{ enum class EMovingOperators{
SWAP_WITHOUT_CARLIER, SWAP,
INSERT_WITHOUT_CARLIER, INSERT,
REMOVE_WITHOUT_CARLIER, REMOVE,
CHANGE_MODE_WITHOUT_CARLIER, CHANGE_MODE,
SWAP_WITHIN_INTERVAL, SWAP_WITHIN_INTERVAL,
MOVE, MOVE,
DYN_PROG, DYN_PROG,
@@ -24,13 +25,18 @@ namespace solverlib {
struct StatSimulatedAnnealing{ struct StatSimulatedAnnealing{
struct failInf{ struct failInf{
double prob; double prob;
unsigned int diff; long diff;
double fictiveDiff;
double temperature; double temperature;
bool isfeasible = true;
}; };
std::unordered_map<EMovingOperators, unsigned int> nbUsed; std::unordered_map<EMovingOperators, unsigned int> nbUsed;
std::unordered_map<EMovingOperators, unsigned int> nbFeas; std::unordered_map<EMovingOperators, unsigned int> nbFeas;
std::unordered_map<EMovingOperators, unsigned int> nbImproved; std::unordered_map<EMovingOperators, unsigned int> nbImproved;
std::unordered_map<EMovingOperators, unsigned int> nbImprovedReal;
std::unordered_map<EMovingOperators, unsigned int> nbInfeasible;
std::unordered_map<EMovingOperators, std::vector<failInf>> failInfos; std::unordered_map<EMovingOperators, std::vector<failInf>> failInfos;
@@ -48,10 +54,17 @@ namespace solverlib {
void addImproved(EMovingOperators op){ void addImproved(EMovingOperators op){
if(activate) nbImproved[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: private:
std::vector<SASolution> solutions; std::vector<SASolution> solutions;
std::mt19937 randomEngine; std::mt19937 randomEngine;
double T;
double Tmax;
double Tmin;
double maxCostOfJob;
double rate;
void applySequenceResult(
SASolution& neighbor,
const std::vector<std::pair<unsigned short, Decision>>& seq,
const std::vector<std::pair<unsigned short, Decision>>& 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<EPenaltyType, double> penaltyLambdas;
// Initialisation dans le constructeur ou avant solve()
void setPenaltyWeights() {
penaltyLambdas[EPenaltyType::TIME_WINDOW_OVERRUN] = 1.0;
}
std::unordered_map<EPenaltyType, double> effectiveLambdas() const;
public: public:
static bool withDynProg; static bool withDynProg;
static bool authorizeInfeasible;
StatSimulatedAnnealing stats; StatSimulatedAnnealing stats;
SimulatedAnnealing() = delete; SimulatedAnnealing() = delete;
explicit SimulatedAnnealing(std::unordered_map<unsigned short, Decision>& decs, std::shared_ptr<modellib::STFMockInstance> mock, ESourceTrackPlan source); explicit SimulatedAnnealing(std::unordered_map<unsigned short, Decision>& decs, std::shared_ptr<modellib::STFMockInstance> mock, ESourceTrackPlan source);
EMovingOperators pick_operator(double tmax, double t); EMovingOperators pick_operator();
std::optional<SASolution> apply_operator(const SASolution& current, EMovingOperators op); std::optional<SASolution> apply_operator(const SASolution& current, EMovingOperators op);
SASolution solve(double T_max, double T_min, double cooling_rate, int iterations_per_temp); SASolution solve(double T_max, double T_min, double cooling_rate, int iterations_per_temp);
@@ -77,15 +125,17 @@ namespace solverlib {
std::optional<SASolution> move_dynprog(const SASolution& sol); std::optional<SASolution> move_dynprog(const SASolution& sol);
std::optional<std::vector<std::pair<unsigned short, Decision>>> checkSequence(const std::vector<std::pair<unsigned short, Decision>>& jobsDec, unsigned int machine); std::optional<std::vector<std::pair<unsigned short, Decision>>> checkSequence(const std::vector<std::pair<unsigned short, Decision>>& jobsDec, unsigned int machine);
std::optional<std::vector<std::pair<unsigned short, Decision>>> checkSequence(const std::vector<std::pair<unsigned short, Decision>>& 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<unsigned int, unsigned int> evaluate(const std::unordered_map<unsigned short, Decision>& decs); std::pair<unsigned int, unsigned int> evaluate(const std::unordered_map<unsigned short, Decision>& decs);
std::pair<unsigned int, unsigned int> getCostOfSequence(std::vector<std::pair<unsigned short, decision>>& jobsSeq);
std::pair<bool, std::vector<unsigned int>> PSE_Carlier_Rivreau(std::vector<std::pair<unsigned short, decision>> &jobs, unsigned int voieMachine);
void addSolutions(std::vector<SASolution>& solPool){solutions.insert(solutions.end(), solPool.begin(), solPool.end());}; void addSolutions(std::vector<SASolution>& solPool){solutions.insert(solutions.end(), solPool.begin(), solPool.end());};
@@ -2,6 +2,7 @@
#define SOLUTION_HPP #define SOLUTION_HPP
#include "../../../General/Model/STFMockInstance.hpp" #include "../../../General/Model/STFMockInstance.hpp"
#include "Penalty.hpp"
#include "sourceSolTrPlan.hpp" #include "sourceSolTrPlan.hpp"
namespace solverlib { namespace solverlib {
@@ -13,6 +14,11 @@ namespace solverlib {
std::unordered_map<unsigned short, Decision> decisions; std::unordered_map<unsigned short, Decision> decisions;
unsigned int cost; unsigned int cost;
unsigned int diagCost; unsigned int diagCost;
bool isFeasible = true;
Penalty penalty;
std::unordered_map<unsigned int, Penalty> penaltyPerMachine;
double fictiveCost = 0.0;
std::unordered_map<unsigned short, double> fictiveExcludedCosts;
}SASolution; }SASolution;
} }
@@ -1,5 +1,7 @@
#include "SolutionPoolManager.hpp" #include "SolutionPoolManager.hpp"
#include <iterator> #include <iterator>
#include <unordered_map>
#include <vector>
namespace solverlib { namespace solverlib {
std::vector<TrackPlan> SolutionPoolManager::getTrackPlansFromSolution(SASolution& sol, bool withTrash) std::vector<TrackPlan> SolutionPoolManager::getTrackPlansFromSolution(SASolution& sol, bool withTrash)
@@ -10,14 +12,17 @@ namespace solverlib {
//id plan = id voie //id plan = id voie
std::vector<TrackPlan> plans; std::vector<TrackPlan> plans;
plans.resize(STFMockInstance::tracks.size()); plans.resize(STFMockInstance::tracks.size());
std::unordered_map<unsigned int, std::vector<std::pair<unsigned short, Decision>>> decisionsOnMachines;
std::vector<std::set<unsigned int>> machineIdsFromTrackId(STFMockInstance::tracks.size());
for(auto& dec : sol.decisions) for(auto& dec : sol.decisions)
{ {
if(!dec.second.excluded) if(!dec.second.excluded)
{ {
plans[dec.second.voie].source = sol.source;
plans[dec.second.voie].isTrash = false; plans[dec.second.voie].isTrash = false;
plans[dec.second.voie].track = dec.second.voie; plans[dec.second.voie].track = dec.second.voie;
plans[dec.second.voie].schedule[dec.first] = dec.second; 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) { else if(withTrash) {
TrackPlan trashTrack; TrackPlan trashTrack;
@@ -36,6 +41,23 @@ namespace solverlib {
for(auto& plan : plans) for(auto& plan : plans)
{ {
if(!plan.schedule.empty()) if(!plan.schedule.empty())
{
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.mock = sol.mock;
plan.source = sol.source; plan.source = sol.source;
@@ -43,6 +65,7 @@ namespace solverlib {
trackPlans.push_back(plan); trackPlans.push_back(plan);
} }
} }
}
return trackPlans; return trackPlans;
} }
@@ -56,11 +79,13 @@ namespace solverlib {
trackPlans.reserve(STFMockInstance::tracks.size() * pool.size() + 1); trackPlans.reserve(STFMockInstance::tracks.size() * pool.size() + 1);
while(!pool.empty()) while(!pool.empty())
{ {
auto& sol = pool.back(); auto sol = pool.back();
//id plan = id voie //id plan = id voie
std::vector<TrackPlan> plans; std::vector<TrackPlan> plans;
plans.resize(STFMockInstance::tracks.size()); plans.resize(STFMockInstance::tracks.size());
std::unordered_map<unsigned int, std::vector<std::pair<unsigned short, Decision>>> decisionsOnMachines;
std::vector<std::set<unsigned int>> machineIdsFromTrackId(STFMockInstance::tracks.size());
for(auto& dec : sol.decisions) for(auto& dec : sol.decisions)
{ {
if(!dec.second.excluded) if(!dec.second.excluded)
@@ -68,11 +93,30 @@ namespace solverlib {
plans[dec.second.voie].isTrash = false; plans[dec.second.voie].isTrash = false;
plans[dec.second.voie].track = dec.second.voie; plans[dec.second.voie].track = dec.second.voie;
plans[dec.second.voie].schedule[dec.first] = dec.second; 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) for(auto& plan : plans)
{ {
if(!plan.schedule.empty()) if(!plan.schedule.empty())
{
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.mock = sol.mock;
plan.source = sol.source; plan.source = sol.source;
@@ -83,7 +127,12 @@ namespace solverlib {
countD++; countD++;
} }
} }
else {
countD++;
} }
}
}
pool.pop_back(); pool.pop_back();
} }
std::move(uniqueSchedules.begin(), uniqueSchedules.end(), std::back_inserter(trackPlans)); std::move(uniqueSchedules.begin(), uniqueSchedules.end(), std::back_inserter(trackPlans));
@@ -104,12 +153,31 @@ namespace solverlib {
auto nbplan = trackPlans.size(); 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)); loggerlib::Logger::systemNotify(loggerlib::LOGGER_PROGRESS, "Total track plans " + std::to_string(nbplan));
return trackPlans; return trackPlans;
} }
bool SolutionPoolManager::checkSequence(const std::vector<std::pair<unsigned short, Decision>>& jobsDec, unsigned int machine)
{
auto machineDisp = STFMockInstance::machines[machine]->getDispo();
unsigned short minBegin = machineDisp.getDebut().getRelativeDate();
std::vector<std::pair<unsigned short, Decision>> res;
for(auto& jobDec : jobsDec)
{
auto matchWithMachine = CreneauHoraire::checkSlotsCompatibility(jobDec.second.timeslotGraphSplited, machineDisp).second;
minBegin = std::max(minBegin, matchWithMachine.first);
auto newJobDec = jobDec;
unsigned int duration = !jobDec.second.rejected ? STFMockInstance::jobs[jobDec.first]->getDuree() : STFMockInstance::jobs[jobDec.first]->getDureeDiag();
newJobDec.second.lastCreneau = {minBegin, minBegin + duration};
if(minBegin + duration > matchWithMachine.first + matchWithMachine.second)
return false;
minBegin = minBegin + duration;
}
return true;
}
void SolutionPoolManager::provide(std::vector<SASolution>&& solutions) void SolutionPoolManager::provide(std::vector<SASolution>&& solutions)
{ {
std::move(solutions.begin(), solutions.end(), std::back_inserter(pool)); std::move(solutions.begin(), solutions.end(), std::back_inserter(pool));
@@ -16,6 +16,8 @@ namespace solverlib {
std::vector<TrackPlan> getTrackPlansFromSolution(SASolution& sol, bool withTrash = false); std::vector<TrackPlan> getTrackPlansFromSolution(SASolution& sol, bool withTrash = false);
bool checkSequence(const std::vector<std::pair<unsigned short, Decision>>& jobsDec, unsigned int machine);
const std::vector<SASolution>& getSolutions() const {return pool;}; const std::vector<SASolution>& getSolutions() const {return pool;};
}; };
+17 -4
View File
@@ -853,14 +853,14 @@ namespace solverlib {
std::ostringstream csv; std::ostringstream csv;
// En-têtes // 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) // worseProb (map de vector)
for(auto& [key, val] : simAnn.stats.failInfos) for(auto& [key, val] : simAnn.stats.failInfos)
{ {
for(size_t i = 0; i < val.size(); ++i) 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::pair<std::unordered_map<EMovingOperators, unsigned int>, std::string>>{ for(auto& [map, label] : std::vector<std::pair<std::unordered_map<EMovingOperators, unsigned int>, std::string>>{
{simAnn.stats.nbUsed, "used"}, {simAnn.stats.nbUsed, "used"},
{simAnn.stats.nbFeas, "feas"}, {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) for(auto& [key, val] : map)
{ {
if(label == "used") nbIte += val; if(label == "used") nbIte += val;
csv << label << ";" << simAnn.stats.names[key] << ";" << ";" << val << "\n"; csv << label << ";" << simAnn.stats.names[key] << ";" << ";" << val << "\n";
} }
} }
@@ -939,7 +944,7 @@ namespace solverlib {
std::vector<PipelineConfig> pipelines = { std::vector<PipelineConfig> pipelines = {
//{{ALGO::LH, ALGO::DYNPROG }, false, false}, //{{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},
{{ALGO::LH, ALGO::SIMANN, ALGO::ILP}, false, false, 1}, {{ALGO::LH, ALGO::SIMANN, ALGO::ILP}, false, false, 1},
@@ -994,7 +999,10 @@ namespace solverlib {
if(i > 0) if(i > 0)
{ {
std::uniform_int_distribution<unsigned int> distSol(0, poolManager.getSolutions().size()-1); std::uniform_int_distribution<unsigned int> distSol(0, poolManager.getSolutions().size()-1);
do{
sol = poolManager.getSolutions()[distSol(rng)]; sol = poolManager.getSolutions()[distSol(rng)];
}while(!sol.penalty.isFeasible());
} }
auto res = runSimAnn(sol, poolManager, false); auto res = runSimAnn(sol, poolManager, false);
result.stepsRes.push_back(res); 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; double timeFinal = 0.0;
nlohmann::json arrStep = nlohmann::json::array(); nlohmann::json arrStep = nlohmann::json::array();
for(auto& stepFinished : result.stepsRes) for(auto& stepFinished : result.stepsRes)