2026-06-03 22:48:34 +02:00
|
|
|
#include "SolutionPoolManager.hpp"
|
|
|
|
|
#include <iterator>
|
2026-06-04 21:59:36 +02:00
|
|
|
#include <unordered_map>
|
|
|
|
|
#include <vector>
|
2026-06-03 22:48:34 +02:00
|
|
|
|
|
|
|
|
namespace solverlib {
|
|
|
|
|
std::vector<TrackPlan> SolutionPoolManager::getTrackPlansFromSolution(SASolution& sol, bool withTrash)
|
|
|
|
|
{
|
|
|
|
|
std::vector<TrackPlan> trackPlans;
|
|
|
|
|
trackPlans.reserve(STFMockInstance::tracks.size());
|
|
|
|
|
|
|
|
|
|
//id plan = id voie
|
|
|
|
|
std::vector<TrackPlan> plans;
|
|
|
|
|
plans.resize(STFMockInstance::tracks.size());
|
2026-06-04 21:59:36 +02:00
|
|
|
std::unordered_map<unsigned int, std::vector<std::pair<unsigned short, Decision>>> decisionsOnMachines;
|
|
|
|
|
std::vector<std::set<unsigned int>> machineIdsFromTrackId(STFMockInstance::tracks.size());
|
2026-06-03 22:48:34 +02:00
|
|
|
for(auto& dec : sol.decisions)
|
|
|
|
|
{
|
|
|
|
|
if(!dec.second.excluded)
|
|
|
|
|
{
|
|
|
|
|
plans[dec.second.voie].isTrash = false;
|
|
|
|
|
plans[dec.second.voie].track = dec.second.voie;
|
|
|
|
|
plans[dec.second.voie].schedule[dec.first] = dec.second;
|
2026-06-04 21:59:36 +02:00
|
|
|
decisionsOnMachines[dec.second.empV].push_back({dec.first, dec.second});
|
|
|
|
|
machineIdsFromTrackId[dec.second.voie].insert(dec.second.empV);
|
2026-06-03 22:48:34 +02:00
|
|
|
}
|
|
|
|
|
else if(withTrash) {
|
|
|
|
|
TrackPlan trashTrack;
|
|
|
|
|
trashTrack.source = sol.source;
|
|
|
|
|
trashTrack.isTrash = true;
|
|
|
|
|
trashTrack.mock = sol.mock;
|
|
|
|
|
trashTrack.diagCost = 0;
|
|
|
|
|
trashTrack.track = 0;
|
|
|
|
|
trashTrack.schedule[dec.first] = {
|
|
|
|
|
0,CreneauHoraire(), 0, false, true,0,0,{0,0}
|
|
|
|
|
};
|
|
|
|
|
trashTrack.cost = STFMockInstance::jobs[dec.first]->getPoidsRetard() * MAXIMUM_TIME_OFFSET;
|
|
|
|
|
trackPlans.push_back(trashTrack);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for(auto& plan : plans)
|
|
|
|
|
{
|
|
|
|
|
if(!plan.schedule.empty())
|
|
|
|
|
{
|
2026-06-04 21:59:36 +02:00
|
|
|
|
|
|
|
|
bool feasible = true;
|
|
|
|
|
if(!sol.penalty.isFeasible())
|
|
|
|
|
{
|
|
|
|
|
for(auto& machine : machineIdsFromTrackId[plan.track])
|
|
|
|
|
{
|
|
|
|
|
std::sort(decisionsOnMachines[machine].begin(), decisionsOnMachines[machine].end(), [&](auto& el, auto& el2){
|
|
|
|
|
return el.second.lastCreneau.first < el2.second.lastCreneau.first;
|
|
|
|
|
});
|
|
|
|
|
feasible = checkSequence(decisionsOnMachines[machine], plan.track);
|
|
|
|
|
if(!feasible)
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
if(feasible)
|
|
|
|
|
{
|
|
|
|
|
plan.mock = sol.mock;
|
|
|
|
|
plan.source = sol.source;
|
|
|
|
|
plan.evaluate();
|
|
|
|
|
trackPlans.push_back(plan);
|
|
|
|
|
}
|
2026-06-03 22:48:34 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return trackPlans;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
std::vector<TrackPlan> SolutionPoolManager::transformSolutionsIntoUniqueTrackPlans()
|
|
|
|
|
{
|
|
|
|
|
std::vector<TrackPlan> trackPlans;
|
|
|
|
|
std::set<TrackPlan> uniqueSchedules;
|
|
|
|
|
unsigned long countD = 0;
|
|
|
|
|
trackPlans.reserve(STFMockInstance::tracks.size() * pool.size() + 1);
|
|
|
|
|
while(!pool.empty())
|
|
|
|
|
{
|
2026-06-04 21:59:36 +02:00
|
|
|
auto sol = pool.back();
|
2026-06-03 22:48:34 +02:00
|
|
|
|
|
|
|
|
//id plan = id voie
|
|
|
|
|
std::vector<TrackPlan> plans;
|
|
|
|
|
plans.resize(STFMockInstance::tracks.size());
|
2026-06-04 21:59:36 +02:00
|
|
|
std::unordered_map<unsigned int, std::vector<std::pair<unsigned short, Decision>>> decisionsOnMachines;
|
|
|
|
|
std::vector<std::set<unsigned int>> machineIdsFromTrackId(STFMockInstance::tracks.size());
|
2026-06-03 22:48:34 +02:00
|
|
|
for(auto& dec : sol.decisions)
|
|
|
|
|
{
|
|
|
|
|
if(!dec.second.excluded)
|
|
|
|
|
{
|
|
|
|
|
plans[dec.second.voie].isTrash = false;
|
|
|
|
|
plans[dec.second.voie].track = dec.second.voie;
|
|
|
|
|
plans[dec.second.voie].schedule[dec.first] = dec.second;
|
2026-06-04 21:59:36 +02:00
|
|
|
decisionsOnMachines[dec.second.empV].push_back({dec.first, dec.second});
|
|
|
|
|
machineIdsFromTrackId[dec.second.voie].insert(dec.second.empV);
|
|
|
|
|
|
2026-06-03 22:48:34 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for(auto& plan : plans)
|
|
|
|
|
{
|
|
|
|
|
if(!plan.schedule.empty())
|
|
|
|
|
{
|
2026-06-04 21:59:36 +02:00
|
|
|
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)
|
2026-06-03 22:48:34 +02:00
|
|
|
{
|
2026-06-04 21:59:36 +02:00
|
|
|
plan.mock = sol.mock;
|
|
|
|
|
plan.source = sol.source;
|
|
|
|
|
plan.evaluate();
|
|
|
|
|
auto inserted = uniqueSchedules.insert(plan);
|
|
|
|
|
if(!inserted.second)
|
|
|
|
|
{
|
|
|
|
|
countD++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
2026-06-03 22:48:34 +02:00
|
|
|
countD++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-06-04 21:59:36 +02:00
|
|
|
|
2026-06-03 22:48:34 +02:00
|
|
|
pool.pop_back();
|
|
|
|
|
}
|
|
|
|
|
std::move(uniqueSchedules.begin(), uniqueSchedules.end(), std::back_inserter(trackPlans));
|
|
|
|
|
|
|
|
|
|
for(unsigned short job = 0; job < STFMockInstance::jobs.size(); ++job)
|
|
|
|
|
{
|
|
|
|
|
TrackPlan trashTrack;
|
|
|
|
|
trashTrack.isTrash = true;
|
|
|
|
|
trashTrack.diagCost = 0;
|
|
|
|
|
trashTrack.track = 0;
|
|
|
|
|
trashTrack.source = ESourceTrackPlan::Fake;
|
|
|
|
|
trashTrack.schedule[job] = {
|
|
|
|
|
0,CreneauHoraire(), 0, false, true,0,0,{0,0}
|
|
|
|
|
};
|
|
|
|
|
trashTrack.cost = STFMockInstance::jobs[job]->getPoidsRetard() * MAXIMUM_TIME_OFFSET;
|
|
|
|
|
trackPlans.push_back(trashTrack);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto nbplan = trackPlans.size();
|
|
|
|
|
|
2026-06-04 21:59:36 +02:00
|
|
|
loggerlib::Logger::systemNotify(loggerlib::LOGGER_PROGRESS, "Removed " + std::to_string(countD) + " non-unique or non-feasible track schedules");
|
2026-06-03 22:48:34 +02:00
|
|
|
loggerlib::Logger::systemNotify(loggerlib::LOGGER_PROGRESS, "Total track plans " + std::to_string(nbplan));
|
|
|
|
|
|
|
|
|
|
return trackPlans;
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-04 21:59:36 +02:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-03 22:48:34 +02:00
|
|
|
void SolutionPoolManager::provide(std::vector<SASolution>&& solutions)
|
|
|
|
|
{
|
|
|
|
|
std::move(solutions.begin(), solutions.end(), std::back_inserter(pool));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|