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:
@@ -1,2 +1,4 @@
|
||||
build
|
||||
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/"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
Vendored
-17
@@ -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 "../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);
|
||||
|
||||
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;
|
||||
auto IposA = std::distance(seq_swap.begin(), posA);
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
// 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;
|
||||
std::swap(seqCop[IposA], seqCop[posB]);
|
||||
|
||||
// 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;
|
||||
};
|
||||
|
||||
//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)
|
||||
@@ -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,30 +454,22 @@ 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;
|
||||
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;
|
||||
}
|
||||
|
||||
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)
|
||||
@@ -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,18 +677,15 @@ 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,26 +697,22 @@ 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;
|
||||
}
|
||||
auto res = checkSequence(seq, new_dec_a.empV, penA, feasA);
|
||||
if(!res) return std::nullopt;
|
||||
|
||||
applySequenceResult(neighbor, seq, *res, newCost, penA, feasA);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<SASolution> SimulatedAnnealing::move_remove_WC(const SASolution& sol)
|
||||
{
|
||||
auto mock = sol.mock;
|
||||
@@ -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,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())
|
||||
{
|
||||
|
||||
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;
|
||||
auto resA = checkSequence(jobsEmpVA, dec_a.empV, penA, feasA);
|
||||
if(!resA) return std::nullopt;
|
||||
|
||||
applySequenceResult(neighbor, jobsEmpVA, *resA, remaining, penA, feasA);
|
||||
}
|
||||
}
|
||||
else
|
||||
return std::nullopt;
|
||||
}
|
||||
neighbor.diagCost = (neighbor.diagCost - oldDiagCost) + newDiagCost;
|
||||
neighbor.cost = (neighbor.cost - oldCost) + newCost;
|
||||
|
||||
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,23 +848,17 @@ 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;
|
||||
}
|
||||
|
||||
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;
|
||||
@@ -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 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 <unordered_map>
|
||||
#include <vector>
|
||||
#include "Penalty.hpp"
|
||||
#include "TrackPlan.hpp"
|
||||
#include "Solution.hpp"
|
||||
namespace solverlib {
|
||||
using namespace modellib;
|
||||
|
||||
enum class EMovingOperators{
|
||||
SWAP_WITHOUT_CARLIER,
|
||||
INSERT_WITHOUT_CARLIER,
|
||||
REMOVE_WITHOUT_CARLIER,
|
||||
CHANGE_MODE_WITHOUT_CARLIER,
|
||||
SWAP,
|
||||
INSERT,
|
||||
REMOVE,
|
||||
CHANGE_MODE,
|
||||
SWAP_WITHIN_INTERVAL,
|
||||
MOVE,
|
||||
DYN_PROG,
|
||||
@@ -24,13 +25,18 @@ namespace solverlib {
|
||||
struct StatSimulatedAnnealing{
|
||||
struct failInf{
|
||||
double prob;
|
||||
unsigned int diff;
|
||||
long diff;
|
||||
double fictiveDiff;
|
||||
double temperature;
|
||||
bool isfeasible = true;
|
||||
};
|
||||
|
||||
std::unordered_map<EMovingOperators, unsigned int> nbUsed;
|
||||
std::unordered_map<EMovingOperators, unsigned int> nbFeas;
|
||||
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;
|
||||
|
||||
|
||||
@@ -48,10 +54,17 @@ namespace solverlib {
|
||||
void addImproved(EMovingOperators op){
|
||||
if(activate) nbImproved[op]++;
|
||||
}
|
||||
void addImprovedReal(EMovingOperators op){
|
||||
if(activate) nbImprovedReal[op]++;
|
||||
}
|
||||
|
||||
void addFailInfo(EMovingOperators op, double prob, unsigned int diff, double temp)
|
||||
void addInfeasible(EMovingOperators op){
|
||||
if(activate) nbInfeasible[op]++;
|
||||
}
|
||||
|
||||
void addFailInfo(EMovingOperators op, double prob, long diff, double fictiveDiff, double temp, bool isFeasible)
|
||||
{
|
||||
if(activate) failInfos[op].emplace_back(failInf{prob, diff, temp});
|
||||
if(activate) failInfos[op].emplace_back(failInf{prob, diff, fictiveDiff, temp});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -59,12 +72,47 @@ namespace solverlib {
|
||||
private:
|
||||
std::vector<SASolution> solutions;
|
||||
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:
|
||||
static bool withDynProg;
|
||||
static bool authorizeInfeasible;
|
||||
StatSimulatedAnnealing stats;
|
||||
SimulatedAnnealing() = delete;
|
||||
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);
|
||||
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<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> 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());};
|
||||
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#define SOLUTION_HPP
|
||||
|
||||
#include "../../../General/Model/STFMockInstance.hpp"
|
||||
#include "Penalty.hpp"
|
||||
#include "sourceSolTrPlan.hpp"
|
||||
|
||||
namespace solverlib {
|
||||
@@ -13,6 +14,11 @@ namespace solverlib {
|
||||
std::unordered_map<unsigned short, Decision> decisions;
|
||||
unsigned int cost;
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#include "SolutionPoolManager.hpp"
|
||||
#include <iterator>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace solverlib {
|
||||
std::vector<TrackPlan> SolutionPoolManager::getTrackPlansFromSolution(SASolution& sol, bool withTrash)
|
||||
@@ -10,14 +12,17 @@ namespace solverlib {
|
||||
//id plan = id voie
|
||||
std::vector<TrackPlan> plans;
|
||||
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)
|
||||
{
|
||||
if(!dec.second.excluded)
|
||||
{
|
||||
plans[dec.second.voie].source = sol.source;
|
||||
plans[dec.second.voie].isTrash = false;
|
||||
plans[dec.second.voie].track = dec.second.voie;
|
||||
plans[dec.second.voie].schedule[dec.first] = dec.second;
|
||||
decisionsOnMachines[dec.second.empV].push_back({dec.first, dec.second});
|
||||
machineIdsFromTrackId[dec.second.voie].insert(dec.second.empV);
|
||||
}
|
||||
else if(withTrash) {
|
||||
TrackPlan trashTrack;
|
||||
@@ -36,6 +41,23 @@ namespace solverlib {
|
||||
for(auto& plan : plans)
|
||||
{
|
||||
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.source = sol.source;
|
||||
@@ -43,6 +65,7 @@ namespace solverlib {
|
||||
trackPlans.push_back(plan);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return trackPlans;
|
||||
}
|
||||
@@ -56,11 +79,13 @@ namespace solverlib {
|
||||
trackPlans.reserve(STFMockInstance::tracks.size() * pool.size() + 1);
|
||||
while(!pool.empty())
|
||||
{
|
||||
auto& sol = pool.back();
|
||||
auto sol = pool.back();
|
||||
|
||||
//id plan = id voie
|
||||
std::vector<TrackPlan> plans;
|
||||
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)
|
||||
{
|
||||
if(!dec.second.excluded)
|
||||
@@ -68,11 +93,30 @@ namespace solverlib {
|
||||
plans[dec.second.voie].isTrash = false;
|
||||
plans[dec.second.voie].track = dec.second.voie;
|
||||
plans[dec.second.voie].schedule[dec.first] = dec.second;
|
||||
decisionsOnMachines[dec.second.empV].push_back({dec.first, dec.second});
|
||||
machineIdsFromTrackId[dec.second.voie].insert(dec.second.empV);
|
||||
|
||||
}
|
||||
}
|
||||
for(auto& plan : plans)
|
||||
{
|
||||
if(!plan.schedule.empty())
|
||||
{
|
||||
bool feasible = true;
|
||||
if(!sol.penalty.isFeasible())
|
||||
{
|
||||
for(auto& machine : machineIdsFromTrackId[plan.track])
|
||||
{
|
||||
std::sort(decisionsOnMachines[machine].begin(), decisionsOnMachines[machine].end(), [&](auto& el, auto& el2){
|
||||
return el.second.lastCreneau.first < el2.second.lastCreneau.first;
|
||||
});
|
||||
feasible = checkSequence(decisionsOnMachines[machine], plan.track);
|
||||
if(!feasible)
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
if(feasible)
|
||||
{
|
||||
plan.mock = sol.mock;
|
||||
plan.source = sol.source;
|
||||
@@ -83,7 +127,12 @@ namespace solverlib {
|
||||
countD++;
|
||||
}
|
||||
}
|
||||
else {
|
||||
countD++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pool.pop_back();
|
||||
}
|
||||
std::move(uniqueSchedules.begin(), uniqueSchedules.end(), std::back_inserter(trackPlans));
|
||||
@@ -104,12 +153,31 @@ namespace solverlib {
|
||||
|
||||
auto nbplan = trackPlans.size();
|
||||
|
||||
loggerlib::Logger::systemNotify(loggerlib::LOGGER_PROGRESS, "Removed " + std::to_string(countD) + " non-unique track schedules");
|
||||
loggerlib::Logger::systemNotify(loggerlib::LOGGER_PROGRESS, "Removed " + std::to_string(countD) + " non-unique or non-feasible track schedules");
|
||||
loggerlib::Logger::systemNotify(loggerlib::LOGGER_PROGRESS, "Total track plans " + std::to_string(nbplan));
|
||||
|
||||
return trackPlans;
|
||||
}
|
||||
|
||||
bool SolutionPoolManager::checkSequence(const std::vector<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)
|
||||
{
|
||||
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);
|
||||
|
||||
bool checkSequence(const std::vector<std::pair<unsigned short, Decision>>& jobsDec, unsigned int machine);
|
||||
|
||||
const std::vector<SASolution>& getSolutions() const {return pool;};
|
||||
};
|
||||
|
||||
|
||||
@@ -853,14 +853,14 @@ namespace solverlib {
|
||||
std::ostringstream csv;
|
||||
|
||||
// En-têtes
|
||||
csv << "category;operator;index;probability;objectiveDiff;temperature \n";
|
||||
csv << "category;operator;index;probability;objectiveDiff;costDiff;temperature;isFeasible\n";
|
||||
|
||||
// worseProb (map de vector)
|
||||
for(auto& [key, val] : simAnn.stats.failInfos)
|
||||
{
|
||||
for(size_t i = 0; i < val.size(); ++i)
|
||||
{
|
||||
csv << ";" << simAnn.stats.names[key] << ";" << i << ";" << val[i].prob << ";" << val[i].diff << ";" << val[i].temperature << "\n";
|
||||
csv << ";" << simAnn.stats.names[key] << ";" << i << ";" << val[i].prob << ";" << val[i].diff << ";" << val[i].fictiveDiff << ";" << val[i].temperature << ";" << val[i].isfeasible << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -869,11 +869,16 @@ namespace solverlib {
|
||||
for(auto& [map, label] : std::vector<std::pair<std::unordered_map<EMovingOperators, unsigned int>, std::string>>{
|
||||
{simAnn.stats.nbUsed, "used"},
|
||||
{simAnn.stats.nbFeas, "feas"},
|
||||
{simAnn.stats.nbImproved, "improved"}})
|
||||
{simAnn.stats.nbImproved, "improved"},
|
||||
{simAnn.stats.nbImprovedReal, "improvedRealCost"},
|
||||
{simAnn.stats.nbInfeasible, "withPenalty"}
|
||||
|
||||
})
|
||||
{
|
||||
for(auto& [key, val] : map)
|
||||
{
|
||||
if(label == "used") nbIte += val;
|
||||
|
||||
csv << label << ";" << simAnn.stats.names[key] << ";" << ";" << val << "\n";
|
||||
}
|
||||
}
|
||||
@@ -939,7 +944,7 @@ namespace solverlib {
|
||||
std::vector<PipelineConfig> pipelines = {
|
||||
|
||||
//{{ALGO::LH, ALGO::DYNPROG }, false, false},
|
||||
{{ALGO::LH, ALGO::SIMANN}, false, false},
|
||||
{{ALGO::LH, ALGO::SIMANN, ALGO::ILP}, false, false, 7},
|
||||
|
||||
/*{{ALGO::LH, ALGO::SIMANN, ALGO::ILP}, false, false},
|
||||
{{ALGO::LH, ALGO::SIMANN, ALGO::ILP}, false, false, 1},
|
||||
@@ -994,7 +999,10 @@ namespace solverlib {
|
||||
if(i > 0)
|
||||
{
|
||||
std::uniform_int_distribution<unsigned int> distSol(0, poolManager.getSolutions().size()-1);
|
||||
do{
|
||||
sol = poolManager.getSolutions()[distSol(rng)];
|
||||
|
||||
}while(!sol.penalty.isFeasible());
|
||||
}
|
||||
auto res = runSimAnn(sol, poolManager, false);
|
||||
result.stepsRes.push_back(res);
|
||||
@@ -1049,6 +1057,11 @@ namespace solverlib {
|
||||
}
|
||||
}
|
||||
}
|
||||
SASolution sol = result.best.sol;
|
||||
SolutionPoolManager sp;
|
||||
sp.provide({sol});
|
||||
auto res = runILP(sol, sp);
|
||||
|
||||
double timeFinal = 0.0;
|
||||
nlohmann::json arrStep = nlohmann::json::array();
|
||||
for(auto& stepFinished : result.stepsRes)
|
||||
|
||||
Reference in New Issue
Block a user