609 lines
24 KiB
C++
609 lines
24 KiB
C++
|
|
#include "ILPTrackPlans.hpp"
|
||
|
|
#include "TrackPlan.hpp"
|
||
|
|
#include "gurobi_c++.h"
|
||
|
|
#include <algorithm>
|
||
|
|
#include <iterator>
|
||
|
|
#include <memory>
|
||
|
|
#include <optional>
|
||
|
|
#include <set>
|
||
|
|
#include <string>
|
||
|
|
#include <unordered_map>
|
||
|
|
#include <unordered_set>
|
||
|
|
#include "../../../General/Model/STFMockInstance.hpp"
|
||
|
|
#include "gurobi_c.h"
|
||
|
|
|
||
|
|
namespace solverlib {
|
||
|
|
using namespace modellib;
|
||
|
|
|
||
|
|
ILPTrackPlans::ILPTrackPlans(std::vector<TrackPlan>& plans): m_env(true), trackPlans(plans)
|
||
|
|
{
|
||
|
|
init();
|
||
|
|
}
|
||
|
|
|
||
|
|
void ILPTrackPlans::init()
|
||
|
|
{
|
||
|
|
loggerlib::Logger::systemNotify(loggerlib::LOGGER_PROGRESS, "Initializing Gurobi env");
|
||
|
|
|
||
|
|
//m_env.set("LogFile", "ILP.log");
|
||
|
|
//m_env.set(GRB_IntParam_ThreadLimit, 1);
|
||
|
|
m_env.start();
|
||
|
|
m_model = std::make_unique<GRBModel>(m_env);
|
||
|
|
m_model->set(GRB_DoubleParam_TuneTimeLimit, 3600);
|
||
|
|
m_model->set(GRB_DoubleParam_TimeLimit, 600);
|
||
|
|
|
||
|
|
/*
|
||
|
|
MIPFocus 2
|
||
|
|
OBBT 1
|
||
|
|
Cuts 0
|
||
|
|
PrePasses 2
|
||
|
|
*/
|
||
|
|
m_model->set(GRB_IntParam_MIPFocus, 2);
|
||
|
|
m_model->set(GRB_IntParam_Cuts, 0);
|
||
|
|
m_model->set(GRB_IntParam_PrePasses, 2);
|
||
|
|
m_model->set(GRB_IntParam_OBBT, 1);
|
||
|
|
|
||
|
|
loggerlib::Logger::systemNotify(loggerlib::LOGGER_PROGRESS, "Fetching swap information");
|
||
|
|
|
||
|
|
initSwaps();
|
||
|
|
|
||
|
|
loggerlib::Logger::systemNotify(loggerlib::LOGGER_PROGRESS, "Initializing variables");
|
||
|
|
|
||
|
|
initVariables();
|
||
|
|
|
||
|
|
loggerlib::Logger::systemNotify(loggerlib::LOGGER_PROGRESS, "Initializing constraints");
|
||
|
|
|
||
|
|
addAllConstraints();
|
||
|
|
/*m_model->tune();
|
||
|
|
m_model->getTuneResult(0);
|
||
|
|
m_model->write("tuning.prm");*/
|
||
|
|
}
|
||
|
|
|
||
|
|
void ILPTrackPlans::initSwaps()
|
||
|
|
{
|
||
|
|
loggerlib::Logger::systemNotify(loggerlib::LOGGER_PROGRESS, "Getting unique swaps and mandatory selections");
|
||
|
|
|
||
|
|
std::unordered_set<std::shared_ptr<CroisementUM>> swapsU;
|
||
|
|
unsigned int p = 0;
|
||
|
|
swapsOfTrackPlans.resize(trackPlans.size());
|
||
|
|
for(auto& plan : trackPlans)
|
||
|
|
{
|
||
|
|
if(!plan.isTrash)
|
||
|
|
{
|
||
|
|
for(auto& sw : plan.mock->swaps)
|
||
|
|
{
|
||
|
|
swapsU.insert(sw);
|
||
|
|
swap_setoftrackPlan[sw].push_back(p);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
++p;
|
||
|
|
}
|
||
|
|
|
||
|
|
if(!swapsU.empty())
|
||
|
|
{
|
||
|
|
std::move(swapsU.begin(), swapsU.end(), std::back_inserter(swaps));
|
||
|
|
std::sort(swaps.begin(), swaps.end(), [&](auto& s1, auto& s2){
|
||
|
|
return s1->getPotentialSwapSlot().getDebut().getRelativeDate() < s2->getPotentialSwapSlot().getDebut().getRelativeDate();
|
||
|
|
});
|
||
|
|
|
||
|
|
// Maintenant que swaps est trié et indexé, on peut remplir swapsOfTrackPlans
|
||
|
|
for(unsigned int i = 0; i < swaps.size(); ++i)
|
||
|
|
{
|
||
|
|
for(auto& planIdx : swap_setoftrackPlan[swaps[i]])
|
||
|
|
{
|
||
|
|
swapsOfTrackPlans[planIdx].push_back(i);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
loggerlib::Logger::systemNotify(loggerlib::LOGGER_PROGRESS, "Checking incompatible swaps");
|
||
|
|
|
||
|
|
for(unsigned int i = 0; i < swaps.size(); ++i)
|
||
|
|
{
|
||
|
|
auto& swap = swaps[i];
|
||
|
|
const auto& plansOfSwap = swap_setoftrackPlan[swap];
|
||
|
|
|
||
|
|
for(unsigned int j = 0; j < swaps.size(); ++j)
|
||
|
|
{
|
||
|
|
if(i == j) continue;
|
||
|
|
auto& swapComp = swaps[j];
|
||
|
|
const auto& plansOfSwapComp = swap_setoftrackPlan[swapComp];
|
||
|
|
|
||
|
|
// Vérifier si une rame est en commun
|
||
|
|
bool sharedRame = false;
|
||
|
|
for(auto& r : swap->um_crit.ramesInfo)
|
||
|
|
if(swapComp->find(r.id)) { sharedRame = true; break; }
|
||
|
|
if(!sharedRame)
|
||
|
|
for(auto& r : swap->um_sane.ramesInfo)
|
||
|
|
if(swapComp->find(r.id)) { sharedRame = true; break; }
|
||
|
|
|
||
|
|
if(!sharedRame) continue;
|
||
|
|
|
||
|
|
// Vérifier si les deux croisements ont au moins un trackplan commun
|
||
|
|
bool hasCommonPlan = false;
|
||
|
|
for(auto& planIdx : plansOfSwap)
|
||
|
|
{
|
||
|
|
for(auto& planIdxComp : plansOfSwapComp)
|
||
|
|
{
|
||
|
|
if(planIdx == planIdxComp) { hasCommonPlan = true; break; }
|
||
|
|
}
|
||
|
|
if(hasCommonPlan) break;
|
||
|
|
}
|
||
|
|
//hasCommonPlan = plansOfSwap == plansOfSwapComp;
|
||
|
|
|
||
|
|
// Incompatibles si rame commune ET aucun trackplan commun
|
||
|
|
if(!hasCommonPlan)
|
||
|
|
{
|
||
|
|
swap_incompswap[swap].push_back(j);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
for(unsigned int j = 0; j < swaps.size(); ++j)
|
||
|
|
{
|
||
|
|
if(j != i)
|
||
|
|
{
|
||
|
|
if(swaps[i]->getPotentialSwapSlot().getDebut().getRelativeDate() > swaps[j]->getPotentialSwapSlot().getDebut().getRelativeDate())
|
||
|
|
{
|
||
|
|
auto sw = swaps[i];
|
||
|
|
bool foundRame = false;
|
||
|
|
for(auto& ramInfList : {sw->um_sane.ramesInfo, sw->um_crit.ramesInfo})
|
||
|
|
{
|
||
|
|
for(auto& rameInf : ramInfList)
|
||
|
|
{
|
||
|
|
foundRame = swaps[j]->find(rameInf.id);
|
||
|
|
if(foundRame)
|
||
|
|
{
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if(foundRame)
|
||
|
|
{
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if(foundRame && swap_setoftrackPlan[sw] == swap_setoftrackPlan[swaps[j]])
|
||
|
|
{
|
||
|
|
swapDependsOnSwaps[i].push_back(j);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
for(unsigned int tr = 0; tr < trackPlans.size(); ++tr)
|
||
|
|
{
|
||
|
|
if(!trackPlans[tr].isTrash)// && trackPlans[tr].mock->swaps.empty())
|
||
|
|
{
|
||
|
|
trackPlanNoSWIncompSwap[tr];
|
||
|
|
unsigned int s = 0;
|
||
|
|
for(auto& sw : swaps)
|
||
|
|
{
|
||
|
|
if(trackPlans[tr].mock->swaps.empty() || std::find(swap_setoftrackPlan[sw].begin(), swap_setoftrackPlan[sw].end(), tr) == swap_setoftrackPlan[sw].end())
|
||
|
|
{
|
||
|
|
bool foundRame = false;
|
||
|
|
for(auto& ramInfList : {sw->um_sane.ramesInfo, sw->um_crit.ramesInfo})
|
||
|
|
{
|
||
|
|
for(auto& rameInf : ramInfList)
|
||
|
|
{
|
||
|
|
foundRame = trackPlans[tr].isRameInScheduleAfterDate(rameInf.id, sw->getPotentialSwapSlot().getDebut().getRelativeDate());
|
||
|
|
if(foundRame)
|
||
|
|
{
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if(foundRame)
|
||
|
|
{
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if(foundRame)
|
||
|
|
{
|
||
|
|
trackPlanNoSWIncompSwap[tr].push_back(s);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
++s;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void ILPTrackPlans::initVariables()
|
||
|
|
{
|
||
|
|
x_ks.resize(trackPlans.size());
|
||
|
|
for(unsigned int i = 0; i < trackPlans.size(); ++i)
|
||
|
|
{
|
||
|
|
std::string name = "x_" + std::to_string(i);
|
||
|
|
x_ks[i] = m_model->addVar(0.0,1.0,0.0, GRB_BINARY, name.c_str());
|
||
|
|
}
|
||
|
|
|
||
|
|
swap_ls.resize(swaps.size());
|
||
|
|
for(unsigned int i = 0; i < swaps.size(); ++i)
|
||
|
|
{
|
||
|
|
std::string name = "swap_" + std::to_string(i);
|
||
|
|
swap_ls[i] = m_model->addVar(0.0,1.0,0.0, GRB_BINARY, name.c_str());
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
std::optional<Planification> ILPTrackPlans::solve()
|
||
|
|
{
|
||
|
|
m_model->optimize();
|
||
|
|
//printSol();
|
||
|
|
return buildSolution();
|
||
|
|
}
|
||
|
|
|
||
|
|
std::optional<Planification> ILPTrackPlans::buildSolution()
|
||
|
|
{
|
||
|
|
Planification planif;
|
||
|
|
int status = m_model->get(GRB_IntAttr_Status);
|
||
|
|
std::vector<TrackPlan> finalTrackPlans;
|
||
|
|
std::vector<std::shared_ptr<CroisementUM>> finalSwaps;
|
||
|
|
|
||
|
|
std::vector<unsigned int> finalVar;
|
||
|
|
if(status != GRB_INFEASIBLE)
|
||
|
|
{
|
||
|
|
unsigned int i = 0;
|
||
|
|
for(auto& var : x_ks)
|
||
|
|
{
|
||
|
|
if(var.get(GRB_DoubleAttr_X) > 0)
|
||
|
|
{
|
||
|
|
finalTrackPlans.push_back(trackPlans[i]);
|
||
|
|
finalVar.push_back(i);
|
||
|
|
}
|
||
|
|
++i;
|
||
|
|
}
|
||
|
|
|
||
|
|
unsigned int s = 0;
|
||
|
|
for(auto& var : swap_ls)
|
||
|
|
{
|
||
|
|
if(var.get(GRB_DoubleAttr_X) > 0)
|
||
|
|
{
|
||
|
|
finalSwaps.push_back(swaps[s]);
|
||
|
|
//std::cout << swaps[s]->um_crit.ramesInfo[0].id << " - " << swaps[s]->um_sane.ramesInfo[0].id << " at " << swaps[s]->getPotentialSwapSlot().getDebut().getRelativeDate() << std::endl;
|
||
|
|
}
|
||
|
|
++s;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
return std::nullopt;
|
||
|
|
}
|
||
|
|
if(!finalTrackPlans.empty())
|
||
|
|
{
|
||
|
|
//<job, trackPlanPos in finaltrackplans>
|
||
|
|
std::unordered_map<unsigned short, std::vector<unsigned int>> doublonsInfos;
|
||
|
|
bool hasDoubles = false;
|
||
|
|
unsigned int i = 0;
|
||
|
|
for(auto& trSch : finalTrackPlans)
|
||
|
|
{
|
||
|
|
for(auto& dec : trSch.schedule)
|
||
|
|
{
|
||
|
|
doublonsInfos[dec.first].push_back(i);
|
||
|
|
if(doublonsInfos[dec.first].size() > 1)
|
||
|
|
hasDoubles= true;
|
||
|
|
}
|
||
|
|
++i;
|
||
|
|
}
|
||
|
|
|
||
|
|
if(!hasDoubles)
|
||
|
|
{
|
||
|
|
loggerlib::Logger::systemNotify(loggerlib::LOGGER_PROGRESS, "Solution from ILP is valid");
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
loggerlib::Logger::systemNotify(loggerlib::LOGGER_PROGRESS, "Removing jobs appearing multiple types");
|
||
|
|
for(auto& doublon : doublonsInfos)
|
||
|
|
{
|
||
|
|
std::string tracksAppear = "";
|
||
|
|
for(auto& el : doublon.second)
|
||
|
|
{
|
||
|
|
tracksAppear += std::to_string(!finalTrackPlans[el].isTrash ? finalTrackPlans[el].track : finalVar[el]) + " ";
|
||
|
|
}
|
||
|
|
loggerlib::Logger::systemNotify(loggerlib::LOGGER_PROGRESS, "Job " + std::to_string(doublon.first) + " : " + tracksAppear);
|
||
|
|
|
||
|
|
if(doublon.second.size() > 1)
|
||
|
|
{
|
||
|
|
std::map<int,std::pair<unsigned int, TrackPlan>> modifiedTrPlans;
|
||
|
|
for(auto& trSchid: doublon.second)
|
||
|
|
{
|
||
|
|
auto trSchCop = finalTrackPlans[trSchid];
|
||
|
|
auto cost = trSchCop.cost;
|
||
|
|
auto suppDec= trSchCop.schedule[doublon.first];
|
||
|
|
trSchCop.schedule.erase(doublon.first);
|
||
|
|
trSchCop.shiftAndRecomputeCostsOnMachine(suppDec.empV);
|
||
|
|
auto costAfter = trSchCop.cost;
|
||
|
|
int gain = costAfter - cost;
|
||
|
|
modifiedTrPlans[gain] = std::make_pair(trSchid,trSchCop);
|
||
|
|
}
|
||
|
|
unsigned int j = 0;
|
||
|
|
for(auto modTrPlan = modifiedTrPlans.begin(); modTrPlan != modifiedTrPlans.end(); ++modTrPlan)
|
||
|
|
{
|
||
|
|
if(j == modifiedTrPlans.size()-1)
|
||
|
|
break;
|
||
|
|
finalTrackPlans[modTrPlan->second.first] = modTrPlan->second.second;
|
||
|
|
++j;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
unsigned int totalCost = 0;
|
||
|
|
unsigned int totalDiagCost = 0;
|
||
|
|
unsigned int totalNbExclu = 0;
|
||
|
|
std::shared_ptr<STFMockInstance> newMock = STFMockInstance::copy(STFInstance::getCurrentInstance()->mockInstance);
|
||
|
|
std::sort(finalSwaps.begin(), finalSwaps.end(), [&](auto& el1, auto& el2){return el1->getPotentialSwapSlot().getDebut().getRelativeDate() < el2->getPotentialSwapSlot().getDebut().getRelativeDate(); });
|
||
|
|
for(auto& sw : finalSwaps)
|
||
|
|
{
|
||
|
|
newMock->swap(*sw);
|
||
|
|
newMock->swaps.push_back(sw);
|
||
|
|
|
||
|
|
}
|
||
|
|
planif.setPlanificationState(newMock);
|
||
|
|
for(auto& plan : finalTrackPlans)
|
||
|
|
{
|
||
|
|
totalCost += plan.cost;
|
||
|
|
totalDiagCost += plan.diagCost;
|
||
|
|
totalNbExclu += plan.isTrash && !plan.schedule.empty();
|
||
|
|
std::cout << "Track " << (!plan.isTrash ? std::to_string(plan.track) : "trash") << " ";
|
||
|
|
std::vector<std::pair<unsigned short, decision>> decsSorted;
|
||
|
|
for(auto& dec : plan.schedule)
|
||
|
|
{
|
||
|
|
decsSorted.push_back(dec);
|
||
|
|
}
|
||
|
|
std::sort(decsSorted.begin(), decsSorted.end(), [&](auto& e1, auto& e2){ return e1.second.lastCreneau.first < e2.second.lastCreneau.second;});
|
||
|
|
std::cout << "SOURCE : " << std::to_string(static_cast<int>(plan.source)) << " ";
|
||
|
|
for(auto& dec : decsSorted)
|
||
|
|
{
|
||
|
|
std::cout << STFMockInstance::jobs[dec.first]->getOpId() << " : " << dec.second.lastCreneau.first << "-" << dec.second.lastCreneau.second << " | ";
|
||
|
|
}
|
||
|
|
std::cout << std::endl;
|
||
|
|
|
||
|
|
auto& decisions = plan.schedule;
|
||
|
|
//auto& swaps = finalSwaps;
|
||
|
|
auto& trStops = newMock->trajectoryStops;
|
||
|
|
for(auto& d : decisions)
|
||
|
|
{
|
||
|
|
if(!d.second.excluded)
|
||
|
|
{
|
||
|
|
unsigned int rameId = STFMockInstance::rameOfOperations[d.first];
|
||
|
|
STFMockInstance::jobs[d.first]->setIdRame(STFMockInstance::rames[rameId]->getId());
|
||
|
|
STFMockInstance::jobs[d.first]->setNumeroEF(STFMockInstance::rames[rameId]->getNumeroEF());
|
||
|
|
bool isPCr = trStops[d.second.empR].typeDispo.first == typeStop::RLT_POST_CROISEMENT_SUBIT || trStops[d.second.empR].typeDispo.first == typeStop::RLT_POST_CROISEMENT_VOULU;
|
||
|
|
std::pair<unsigned short, unsigned short> slot = d.second.rejected ? std::make_pair(d.second.lastCreneau.first, d.second.lastCreneau.first + STFMockInstance::jobs[d.first]->getDureeDiag()) : std::make_pair(d.second.lastCreneau.first, d.second.lastCreneau.first + STFMockInstance::jobs[d.first]->getDuree());
|
||
|
|
OperationPlanifie opPlan(d.first, d.second.voie, d.second.site, d.second.empR, d.second.empV, slot, d.second.rejected, isPCr);
|
||
|
|
planif.addOperation(opPlan);
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
unsigned int rameId = STFMockInstance::rameOfOperations[d.first];
|
||
|
|
STFMockInstance::jobs[d.first]->setIdRame(STFMockInstance::rames[rameId]->getId());
|
||
|
|
STFMockInstance::jobs[d.first]->setNumeroEF(STFMockInstance::rames[rameId]->getNumeroEF());
|
||
|
|
planif.getOpImplanifiables().push_back(d.first);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
for(auto& s : finalSwaps)
|
||
|
|
{
|
||
|
|
planif.getCroisementsEffectues().push_back(*s);
|
||
|
|
}
|
||
|
|
std::cout << "Total cost = " << totalCost << std::endl;
|
||
|
|
std::cout << "Total diagcost = " << totalDiagCost << std::endl;
|
||
|
|
std::cout << "Nb exclusions = " << totalNbExclu << std::endl;
|
||
|
|
return planif;
|
||
|
|
}
|
||
|
|
|
||
|
|
void ILPTrackPlans::warmStart(std::vector<TrackPlan>& plans)
|
||
|
|
{
|
||
|
|
std::vector<unsigned int> varToSet;
|
||
|
|
|
||
|
|
for(auto& plan : plans)
|
||
|
|
{
|
||
|
|
if(plan.isTrash)
|
||
|
|
{
|
||
|
|
auto find = std::find_if(trackPlans.begin(), trackPlans.end(), [&](const TrackPlan& p) {
|
||
|
|
return p.isTrash && p.schedule.begin()->first == plan.schedule.begin()->first;
|
||
|
|
});
|
||
|
|
if(find != trackPlans.end())
|
||
|
|
{
|
||
|
|
varToSet.push_back(std::distance(trackPlans.begin(), find));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
auto find = std::find_if(trackPlans.begin(), trackPlans.end(), [&](const TrackPlan& p) {
|
||
|
|
return p == plan;
|
||
|
|
});
|
||
|
|
if(find != trackPlans.end())
|
||
|
|
{
|
||
|
|
varToSet.push_back(std::distance(trackPlans.begin(), find));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
for(auto& var : x_ks)
|
||
|
|
{
|
||
|
|
var.set(GRB_DoubleAttr_Start, 0);
|
||
|
|
}
|
||
|
|
for(auto& var : swap_ls)
|
||
|
|
{
|
||
|
|
var.set(GRB_DoubleAttr_Start, 0);
|
||
|
|
}
|
||
|
|
unsigned int cost = 0;
|
||
|
|
for(auto j : varToSet)
|
||
|
|
{
|
||
|
|
x_ks[j].set(GRB_DoubleAttr_Start, 1);
|
||
|
|
cost += trackPlans[j].cost;
|
||
|
|
for(auto& var : swapsOfTrackPlans[j])
|
||
|
|
{
|
||
|
|
swap_ls[var].set(GRB_DoubleAttr_Start, 1);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
std::cout << "Cost warm : " << cost << std::endl;
|
||
|
|
}
|
||
|
|
|
||
|
|
void ILPTrackPlans::addSwapsSelectedAreCompatible()
|
||
|
|
{
|
||
|
|
std::unordered_map<unsigned int, std::unordered_set<unsigned int>> pairs;
|
||
|
|
|
||
|
|
for(unsigned int p = 0; p < x_ks.size(); ++p)
|
||
|
|
{
|
||
|
|
for(auto& cols : swapsOfTrackPlans[p])
|
||
|
|
{
|
||
|
|
std::string name = "P1: swap " +std::to_string(cols) +" of trplan " + std::to_string(p) + " must be selected if trplan is";
|
||
|
|
auto sw = swaps[cols];
|
||
|
|
bool foundRame = false;
|
||
|
|
for(auto& ramInfList : {sw->um_sane.ramesInfo, sw->um_crit.ramesInfo})
|
||
|
|
{
|
||
|
|
for(auto& rameInf : ramInfList)
|
||
|
|
{
|
||
|
|
foundRame = trackPlans[p].isRameInScheduleAfterDate(rameInf.id, sw->getPotentialSwapSlot().getDebut().getRelativeDate());
|
||
|
|
if(foundRame)
|
||
|
|
{
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if(foundRame)
|
||
|
|
{
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if(foundRame)
|
||
|
|
m_model->addConstr(swap_ls[cols] >= x_ks[p], name.c_str()); // si constrSw=0 alors swap=0
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
for(unsigned int id_swap = 0; id_swap < swaps.size(); ++id_swap)
|
||
|
|
{
|
||
|
|
for(auto& cols : swap_incompswap[swaps[id_swap]])
|
||
|
|
{
|
||
|
|
if(pairs[id_swap].find(cols) == pairs[id_swap].end() && pairs[cols].find(id_swap) == pairs[cols].end())
|
||
|
|
{
|
||
|
|
pairs[id_swap].insert(cols);
|
||
|
|
pairs[cols].insert(id_swap);
|
||
|
|
std::string incomp = "swap " + std::to_string(id_swap) + " and swap " + std::to_string(cols) + " cannot be selected together";
|
||
|
|
m_model->addConstr(swap_ls[id_swap] + swap_ls[cols] <= 1, incomp.c_str());
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
for(auto tr_incomp_swap : trackPlanNoSWIncompSwap)
|
||
|
|
{
|
||
|
|
for(auto& sw : tr_incomp_swap.second)
|
||
|
|
{
|
||
|
|
std::string incomp = "swap " + std::to_string(sw) + " and tr " + std::to_string(tr_incomp_swap.first) + " cannot be selected together";
|
||
|
|
m_model->addConstr(swap_ls[sw] + x_ks[tr_incomp_swap.first] <= 1, incomp.c_str());
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
for(auto swapComp : swapDependsOnSwaps)
|
||
|
|
{
|
||
|
|
for(auto& sw : swapComp.second)
|
||
|
|
{
|
||
|
|
std::string incomp = "if swap " + std::to_string(sw) + " is selected then swap " + std::to_string(swapComp.first) + " must be selected";
|
||
|
|
m_model->addConstr(swap_ls[sw] >= swap_ls[swapComp.first], incomp.c_str());
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
void ILPTrackPlans::printSol()
|
||
|
|
{
|
||
|
|
unsigned int i = 0;
|
||
|
|
unsigned int cost = 0;
|
||
|
|
unsigned int nbExcl = 0;
|
||
|
|
int status = m_model->get(GRB_IntAttr_Status);
|
||
|
|
if(status != GRB_INFEASIBLE)
|
||
|
|
{
|
||
|
|
for(auto& var : x_ks)
|
||
|
|
{
|
||
|
|
if(var.get(GRB_DoubleAttr_X) > 0)
|
||
|
|
{
|
||
|
|
std::cout << "Track " + (!trackPlans[i].isTrash ? std::to_string(trackPlans[i].track) : "trash" + std::to_string(i)) << std::endl;
|
||
|
|
std::cout << "Jobs : ";
|
||
|
|
for(auto& job : trackPlans[i].schedule)
|
||
|
|
{
|
||
|
|
std::cout << job.first << " ";
|
||
|
|
}
|
||
|
|
std::cout << std::endl;
|
||
|
|
|
||
|
|
if(trackPlans[i].isTrash)
|
||
|
|
{
|
||
|
|
nbExcl++;
|
||
|
|
}
|
||
|
|
cost += trackPlans[i].cost;
|
||
|
|
}
|
||
|
|
++i;
|
||
|
|
}
|
||
|
|
std::cout << "Nb EXcl : " << nbExcl << std::endl;
|
||
|
|
std::cout << "Total cost : " << cost << std::endl;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void ILPTrackPlans::addAllConstraints()
|
||
|
|
{
|
||
|
|
addTrackAppearsOneTime();
|
||
|
|
addJobAppearsOneTime();
|
||
|
|
addSwapsSelectedAreCompatible();
|
||
|
|
addEpsilonConstraintDiagnosis();
|
||
|
|
addObjectiveFunction();
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
void ILPTrackPlans::addTrackAppearsOneTime()
|
||
|
|
{
|
||
|
|
std::unordered_map<unsigned int, GRBLinExpr> trackExpressions;
|
||
|
|
for(unsigned int id_col = 0; id_col < trackPlans.size(); ++id_col)
|
||
|
|
{
|
||
|
|
if(!trackPlans[id_col].isTrash)
|
||
|
|
{
|
||
|
|
trackExpressions[trackPlans[id_col].track] += x_ks[id_col];
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
for(auto& trackExpr : trackExpressions)
|
||
|
|
{
|
||
|
|
std::string name = "track " + std::to_string(trackExpr.first) + " must appear at most once";
|
||
|
|
m_model->addConstr(trackExpr.second <= 1, name.c_str());
|
||
|
|
}
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
void ILPTrackPlans::addJobAppearsOneTime()
|
||
|
|
{
|
||
|
|
for(unsigned int id_job = 0; id_job < STFMockInstance::jobs.size(); ++id_job)
|
||
|
|
{
|
||
|
|
GRBLinExpr job_once = 0;
|
||
|
|
for(unsigned int id_col = 0; id_col < trackPlans.size(); ++id_col)
|
||
|
|
{
|
||
|
|
if(trackPlans[id_col].isJobOnTrack(id_job))
|
||
|
|
{
|
||
|
|
job_once += x_ks[id_col];
|
||
|
|
}
|
||
|
|
}
|
||
|
|
std::string name = "job " + std::to_string(id_job) + " must appear at least once";
|
||
|
|
m_model->addConstr(job_once >= 1, name.c_str());
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void ILPTrackPlans::addObjectiveFunction()
|
||
|
|
{
|
||
|
|
GRBLinExpr sum_T = 0;
|
||
|
|
for(unsigned int id_col = 0; id_col < trackPlans.size(); ++id_col)
|
||
|
|
{
|
||
|
|
sum_T += x_ks[id_col]*trackPlans[id_col].cost;
|
||
|
|
}
|
||
|
|
m_model->setObjective(sum_T,GRB_MINIMIZE);
|
||
|
|
}
|
||
|
|
|
||
|
|
void ILPTrackPlans::addEpsilonConstraintDiagnosis()
|
||
|
|
{
|
||
|
|
GRBLinExpr sum_epsilon = 0;
|
||
|
|
for(unsigned int id_col = 0; id_col < trackPlans.size(); ++id_col)
|
||
|
|
{
|
||
|
|
sum_epsilon += x_ks[id_col]*trackPlans[id_col].diagCost;
|
||
|
|
}
|
||
|
|
std::string name = "Diagnosis cost must be below or equal to epsilon = " + std::to_string(configlib::Configuration::Global.EPSILON);
|
||
|
|
m_model->addConstr(sum_epsilon <= configlib::Configuration::Global.EPSILON, name.c_str());
|
||
|
|
}
|
||
|
|
|
||
|
|
}
|