~spidernet/PermutationFlowShopScheduling

1751de3a42ea2ceafe5e815fbcd427ddffc55080 — Gabriele Venturato 1 year, 9 months ago 9f0041a
[LocalSearch] Developed every function needed but it is veeery slow. Need to do faster delta costs
M LocalSearch/include/FP_Basics.hh => LocalSearch/include/FP_Basics.hh +8 -2
@@ 13,10 13,16 @@ class FP_State {
  FP_State& operator=(const FP_State& s);
  size_t operator[](unsigned i) const { return schedule[i]; }
  size_t& operator[](unsigned i) { return schedule[i]; }
  /*void reset();*/
  void ComputeTimes(size_t job = 0);
  size_t getEndTime(size_t j, size_t m) const { return end_times[j][m]; }
  size_t getSchedule(size_t j) const {return schedule[j]; }

 protected:
  const FP_Input& in;
  vector<size_t> schedule;
  vector<vector<size_t>> start_times;
  vector<vector<size_t>> end_times;
};

class SwapJobs {


@@ 27,7 33,7 @@ class SwapJobs {
  friend istream& operator>>(istream& is, SwapJobs& c);

 public:
  SwapJobs(unsigned i = 0, unsigned j = 0);
  unsigned p1, p2;
  SwapJobs(size_t i = 0, size_t j = 0);
  size_t p1, p2;
};
#endif

M LocalSearch/include/FP_Data.hh => LocalSearch/include/FP_Data.hh +0 -10
@@ 37,23 37,13 @@ class FP_Output {
 public:
  FP_Output(const FP_Input& i);
  FP_Output& operator=(const FP_Output& out);
  void reset();
  void addJob(size_t j) { schedule.push_back(j); }
  void runJob(size_t j);
  void addMTime(size_t t, size_t m) { machine_end_times[m] = t; }
  void addJTime(size_t t, size_t j) { job_end_times[j] = t; }
  size_t operator[](size_t j) const { return schedule[j]; }
  size_t& operator[](size_t j) { return schedule[j]; }
  vector<size_t>& getSchedule() { return schedule; }
  size_t getMTime(size_t m) const { return machine_end_times[m]; }
  size_t getJTime(size_t j) const { return job_end_times[j]; }
  size_t computeMakespan() const;
  size_t computeTardiness() const;

 protected:
  const FP_Input& in;
  vector<size_t> schedule;
  vector<size_t> job_end_times;
  vector<size_t> machine_end_times;
};
#endif

M LocalSearch/src/CMakeLists.txt => LocalSearch/src/CMakeLists.txt +1 -1
@@ 1,7 1,7 @@
find_package(Threads REQUIRED)
find_package(Boost 1.58.0 COMPONENTS program_options REQUIRED)

set(CMAKE_CXX_FLAGS "-Wall")
set(CMAKE_CXX_FLAGS "-Wall -O3")
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)

M LocalSearch/src/FP_Basics.cc => LocalSearch/src/FP_Basics.cc +23 -1
@@ 1,8 1,11 @@
// File FP_Basics.cc
#include "FP_Basics.hh"
#include <algorithm>

FP_State::FP_State(const FP_Input& my_in) : in(my_in) {
  schedule.resize(my_in.getJobs());
  start_times.resize(in.getJobs(), vector<size_t>(in.getMachines()));
  end_times.resize(in.getJobs(), vector<size_t>(in.getMachines()));
  for (size_t i = 0; i < my_in.getJobs(); i++) {
    schedule[i] = i;
  }


@@ 28,7 31,26 @@ ostream& operator<<(ostream& os, const FP_State& st) {
  return os;
}

SwapJobs::SwapJobs(unsigned i, unsigned j) {
void FP_State::ComputeTimes(size_t job) {
  size_t j;
  for (size_t m = 0; m < in.getMachines(); ++m) {
    for (size_t i = job; i < in.getJobs(); ++i) {
      j = schedule[i];
      if (m == 0 && i == 0) {
        start_times[j][m] = max<size_t>(0, in.getReleaseDate(schedule[0]));
      } else if (m == 0) {
        start_times[j][m] = max<size_t>(end_times[schedule[i-1]][m], in.getReleaseDate(j));
      } else if (i == 0) {
        start_times[j][m] = end_times[j][m-1];
      } else {
        start_times[j][m] = max<size_t>(end_times[schedule[i-1]][m], end_times[j][m-1]);
      }
      end_times[j][m] = start_times[j][m] + in.getDuration(j,m);
    }
  }
}

SwapJobs::SwapJobs(size_t i, size_t j) {
  p1 = i;
  p2 = j;
}

M LocalSearch/src/FP_Data.cc => LocalSearch/src/FP_Data.cc +3 -81
@@ 112,93 112,15 @@ size_t FP_Input::getTotalDuration(size_t job) const {
}

FP_Output::FP_Output(const FP_Input& my_in)
    : in(my_in), job_end_times(in.getJobs()), machine_end_times(in.getMachines()) {}
    : in(my_in) {
  schedule.resize( in.getJobs() );
}

FP_Output& FP_Output::operator=(const FP_Output& out) {
  schedule = out.schedule;
  machine_end_times = out.machine_end_times;
  job_end_times = out.job_end_times;
  return *this;
}

void FP_Output::reset() {
  for (size_t i = 0; i < in.getJobs(); ++i) {
    job_end_times[i] = 0;
  }

  for (size_t i = 0; i < in.getMachines(); ++i) {
    machine_end_times[i] = 0;
  }
}

/*!
 * @brief Run a specific job on all machines and update its completion
 * time and the completion time of each machine.
 * @param[in] job The job to run.
 */
void FP_Output::runJob(size_t job) {
  // If the release date is <= than the end time of the machine 0
  // then the job will start from the previous end time of the machine 0
  if (in.getReleaseDate(job) <= getMTime(0)) {
    size_t old_time = getMTime(0);

    size_t new_time = old_time + in.getDuration(job, 0);
    addMTime(new_time, 0);
  } else {
    // otherwise it'll start from the value of the release date
    size_t new_time = in.getReleaseDate(job) + in.getDuration(job, 0);
    addMTime(new_time, 0);
  }

  for (size_t i = 1; i < in.getMachines(); ++i) {
    if (getMTime(i) > getMTime(i - 1)) {
      // Start from the old time of machine i
      size_t old_time = getMTime(i);

      size_t new_time = old_time + in.getDuration(job, i);
      addMTime(new_time, i);
    } else {
      // Start from the old time of the previous machine
      size_t old_time = getMTime(i - 1);

      size_t new_time = old_time + in.getDuration(job, i);
      addMTime(new_time, i);
    }
  }

  addJTime(getMTime(in.getMachines() - 1), job);
}

/*!
 * @brief Compute the Makespan.
 * @return The Makespan.
 */
size_t FP_Output::computeMakespan() const {
  // The Makespan is the end time of the last machine run on the last job
  // scheduled.
  return machine_end_times[in.getMachines() - 1];
}

/*!
 * @brief Compute the tardiness of the schedule.
 * @return The tardiness.
 */
size_t FP_Output::computeTardiness() const {
  size_t tardiness = 0;
  for (size_t i = 0; i < in.getJobs(); ++i) {
    int due = in.getDueDates(i);
    // Compute the tardiness only if the job has a due date
    if (due != -1) {
      // and if its end time exceeds the due date.
      if (static_cast<size_t>(due) < job_end_times[i]) {
        tardiness += (job_end_times[i] - due) * in.getWeight(i);
      }
    }
  }

  return tardiness;
}

ostream& operator<<(ostream& os, const FP_Output& out) {
  os << "Schedule: [";
  for (size_t i = 0; i < out.in.getJobs(); ++i) {

M LocalSearch/src/FP_Helpers.cc => LocalSearch/src/FP_Helpers.cc +158 -33
@@ 6,10 6,15 @@
 ***************************************************************************/

void FP_StateManager::RandomState(FP_State& st) {
  for (unsigned i = 0; i < in.getJobs(); i++) {
    unsigned j = Random::Uniform<unsigned>(i, in.getJobs() - 1);
  size_t j;
  for (unsigned i = 0; i < in.getJobs(); ++i) {
    st[i] = i;
  }
  for (size_t i = 0; i < in.getJobs(); i++) {
    j = Random::Uniform<size_t>(i, in.getJobs() - 1);
    swap(st[i], st[j]);
  }
  st.ComputeTimes();
}

/*****************************************************************************


@@ 17,13 22,14 @@ void FP_StateManager::RandomState(FP_State& st) {
 *****************************************************************************/

void FP_OutputManager::InputState(FP_State& st, const FP_Output& out) const {
  for (unsigned i = 0; i < in.getJobs(); i++) {
  for (unsigned i = 0; i < in.getJobs(); ++i) {
    st[i] = out[i];
  }
  st.ComputeTimes();
}

void FP_OutputManager::OutputState(const FP_State& st, FP_Output& out) const {
  for (unsigned i = 0; i < in.getJobs(); i++) {
  for (unsigned i = 0; i < in.getJobs(); ++i) {
    out[i] = st[i];
  }
}


@@ 33,31 39,38 @@ void FP_OutputManager::OutputState(const FP_State& st, FP_Output& out) const {
 ***************************************************************************/

int Makespan::ComputeCost(const FP_State& st) const {
  unsigned cost = 0;
  // Insert the code that computes the cost for component 1
  // of state st
  throw logic_error("FP_Makespan::ComputeCost not implemented yet");
  return cost;
  return static_cast<int>(st.getEndTime(st.getSchedule( in.getJobs()-1 ), in.getMachines()-1));
}

void Makespan::PrintViolations(const FP_State& st, ostream& os) const {
  // Insert the code that prints the violations of component 1
  // of state st
  throw logic_error("FP_Makespan::PrintViolations not implemented yet");
  os << "Makespan is " << ComputeCost(st);
}

int Tardiness::ComputeCost(const FP_State& st) const {
  unsigned cost = 0;
  // Insert the code that computes the cost for component 2
  // of state st
  throw logic_error("Tardiness::ComputeCost not implemented yet");
  size_t j;
  for (unsigned i = 0; i < in.getJobs(); ++i) {
    j = st.getSchedule(i);
    if (in.getDueDates(j) != -1) {
      if (static_cast<size_t>(in.getDueDates(j)) < st.getEndTime(j, in.getMachines()-1)) {
        cost += (st.getEndTime(j, in.getMachines()-1) - in.getDueDates(j)) * in.getWeight(j);
      }
    }
  }
  return cost;
}

void Tardiness::PrintViolations(const FP_State& st, ostream& os) const {
  // Insert the code that prints the violations of component 1
  // of state st
  throw logic_error("Tardiness::PrintViolations not implemented yet");
  size_t j;
  for (unsigned i = 0; i < in.getJobs(); ++i) {
    j = st.getSchedule(i);
    if (in.getDueDates(j) != -1) {
      if (static_cast<size_t>(in.getDueDates(j)) < st.getEndTime(j, in.getMachines()-1)) {
        os << "Tardiness of job " << j << " is " << (st.getEndTime(j, in.getMachines()-1) - in.getDueDates(j)) *
          in.getWeight(j);
      }
    }
  }
}

/*****************************************************************************


@@ 66,15 79,31 @@ void Tardiness::PrintViolations(const FP_State& st, ostream& os) const {

// initial move builder
void SwapJobsNeighborhoodExplorer::RandomMove(const FP_State& st, SwapJobs& mv) const {
  mv.p1 = Random::Uniform<unsigned>(0, in.getJobs() - 1);
  unsigned a[2];
  a[0] = Random::Uniform<unsigned>(0, mv.p1 - 1);
  a[1] = Random::Uniform<unsigned>(mv.p1 + 1, in.getJobs() - 1);
  unsigned c = Random::Uniform<unsigned>(0, 1);
  /*mv.p1 = Random::Uniform<size_t>(0, in.getJobs() - 1);
  size_t a[2], c;

  a[0] = Random::Uniform<size_t>(0, mv.p1 - 1);
  a[1] = Random::Uniform<size_t>(mv.p1 + 1, in.getJobs() - 1);

  if (mv.p1 == 0) {
    c = 1;
  } else if (mv.p2 == in.getJobs()-1) {
    c = 0;
  } else {
    c = Random::Uniform<size_t>(0, 1);
  }

  mv.p2 = a[c];

  // keep always p1 < p2
  if (mv.p1 > mv.p2) swap(mv.p1, mv.p2);
  if (mv.p1 > mv.p2) swap(mv.p1, mv.p2);*/

  mv.p1 = Random::Uniform<size_t>(0, in.getJobs() - 1);
  mv.p2 = Random::Uniform<size_t>(0, in.getJobs() - 2);
  if (mv.p2 >= mv.p1)
    mv.p2++;
  if (mv.p1 > mv.p2) // swap p1 and p2 so that p1 < p2
    swap(mv.p1, mv.p2);
}

// check move feasibility


@@ 84,7 113,12 @@ bool SwapJobsNeighborhoodExplorer::FeasibleMove(const FP_State& st, const SwapJo

// update the state according to the move
void SwapJobsNeighborhoodExplorer::MakeMove(FP_State& st, const SwapJobs& mv) const {
  swap(st[mv.p1], st[mv.p2]);
  if (mv.p1 < mv.p2) {
    swap(st[mv.p1], st[mv.p2]);
    st.ComputeTimes(mv.p1);
  } else {
    cerr << "ASS" << endl;
  }
}

void SwapJobsNeighborhoodExplorer::FirstMove(const FP_State& st, SwapJobs& mv) const {


@@ 106,15 140,106 @@ bool SwapJobsNeighborhoodExplorer::NextMove(const FP_State& st, SwapJobs& mv) co
}

int SwapJobsDeltaMakespan::ComputeDeltaCost(const FP_State& st, const SwapJobs& mv) const {
  int cost = 0;
  // Insert the code that computes the delta cost of component 1 for move mv in state st
  throw logic_error("SwapJobsDeltaMakespan::ComputeDeltaCost not implemented yet");
  return cost;
  int future_makespan = 0;
  int current_makespan = static_cast<int>(st.getEndTime(st.getSchedule( in.getJobs()-1 ), in.getMachines()-1));

  // retrieve starting time from current state
  size_t start_time;
  if (mv.p1 == 0) {
    // if swapping job 0, start time is 0 (take care of release date later)
    start_time = 0;
  } else {
    start_time = st.getEndTime(st.getSchedule(mv.p1-1), 0);
  }

  vector<vector<size_t>> s_times, e_times;
  s_times.resize(in.getJobs(), vector<size_t>(in.getMachines()));
  e_times.resize(in.getJobs(), vector<size_t>(in.getMachines()));

  if (mv.p1) {
    for (size_t m = 0; m < in.getMachines(); ++m) {
      e_times[mv.p1-1][m] = st.getEndTime(mv.p1-1,m);
    }
  }

  size_t j;
  for (size_t m = 0; m < in.getMachines(); ++m) {
    for (size_t i = mv.p1; i < in.getJobs(); ++i) {
      j = st.getSchedule(i);
      if (m == 0 && i == 0) {
        s_times[j][m] = max<size_t>(start_time, in.getReleaseDate(st.getSchedule(0)));
      } else if (m == 0) {
        s_times[j][m] = max<size_t>(e_times[st.getSchedule(i-1)][m], in.getReleaseDate(j));
      } else if (i == 0) {
        s_times[j][m] = e_times[j][m-1];
      } else {
        s_times[j][m] = max<size_t>(e_times[st.getSchedule(i-1)][m], e_times[j][m-1]);
      }
      e_times[j][m] = s_times[j][m] + in.getDuration(j,m);
    }
  }

  future_makespan = static_cast<int>(e_times[st.getSchedule( in.getJobs()-1 )][in.getMachines()-1]);

  return future_makespan - current_makespan;
}

int SwapJobsDeltaTardiness::ComputeDeltaCost(const FP_State& st, const SwapJobs& mv) const {
  int cost = 0;
  // Insert the code that computes the delta cost of component 1 for move mv in state st
  throw logic_error("SwapJobsDeltaTardiness::ComputeDeltaCost not implemented yet");
  return cost;
  int partial_future_tardiness = 0, partial_current_tardiness = 0;

  size_t j;
  for (size_t i = mv.p1; i < in.getJobs(); ++i) {
    j = st.getSchedule(i);
    if (in.getDueDates(j) != -1) {
      if (static_cast<size_t>(in.getDueDates(j)) < st.getEndTime(j, in.getMachines()-1)) {
        partial_current_tardiness += (st.getEndTime(j, in.getMachines()-1) - in.getDueDates(j)) * in.getWeight(j);
      }
    }
  }

  // retrieve starting time from current state
  size_t start_time;
  if (mv.p1 == 0) {
    // if swapping job 0, start time is 0 (take care of release date later)
    start_time = 0;
  } else {
    start_time = st.getEndTime(st.getSchedule(mv.p1-1), 0);
  }

  vector<vector<size_t>> s_times, e_times;
  s_times.resize(in.getJobs(), vector<size_t>(in.getMachines()));
  e_times.resize(in.getJobs(), vector<size_t>(in.getMachines()));

  if (mv.p1) {
    for (size_t m = 0; m < in.getMachines(); ++m) {
      e_times[mv.p1-1][m] = st.getEndTime(mv.p1-1,m);
    }
  }

  for (size_t m = 0; m < in.getMachines(); ++m) {
    for (size_t i = mv.p1; i < in.getJobs(); ++i) {
      j = st.getSchedule(i);
      if (m == 0 && i == 0) {
        s_times[j][m] = max<size_t>(start_time, in.getReleaseDate(st.getSchedule(0)));
      } else if (m == 0) {
        s_times[j][m] = max<size_t>(e_times[st.getSchedule(i-1)][m], in.getReleaseDate(j));
      } else if (i == 0) {
        s_times[j][m] = e_times[j][m-1];
      } else {
        s_times[j][m] = max<size_t>(e_times[st.getSchedule(i-1)][m], e_times[j][m-1]);
      }
      e_times[j][m] = s_times[j][m] + in.getDuration(j,m);
    }
  }

  for (size_t i = mv.p1; i < in.getJobs(); ++i) {
    j = st.getSchedule(i);
    if (in.getDueDates(j) != -1) {
      if (static_cast<size_t>(in.getDueDates(j)) < e_times[j][in.getMachines()-1]) {
        partial_future_tardiness += (e_times[j][in.getMachines()-1] - in.getDueDates(j)) * in.getWeight(j);
      }
    }
  }

  return partial_future_tardiness - partial_current_tardiness;
}

M LocalSearch/src/FP_Main.cc => LocalSearch/src/FP_Main.cc +5 -2
@@ 26,8 26,8 @@ int main(int argc, const char* argv[]) {
  if (seed.IsSet()) Random::SetSeed(seed);

  // cost components: second parameter is the cost, third is the type (true -> hard, false -> soft)
  Makespan cc1(in, 1, true);
  Tardiness cc2(in, 1, true);
  Makespan cc1(in, 1, false);
  Tardiness cc2(in, 1, false);

  SwapJobsDeltaMakespan dcc1(in, cc1);
  SwapJobsDeltaTardiness dcc2(in, cc2);


@@ 50,6 50,9 @@ int main(int argc, const char* argv[]) {
  HillClimbing<FP_Input, FP_State, SwapJobs> FP_hc(in, FP_sm, FP_nhe, "SwapJobsHillClimbing");
  SteepestDescent<FP_Input, FP_State, SwapJobs> FP_sd(in, FP_sm, FP_nhe, "SwapJobsSteepestDescent");
  SimulatedAnnealing<FP_Input, FP_State, SwapJobs> FP_sa(in, FP_sm, FP_nhe, "SwapJobsSimulatedAnnealing");
  TabuSearch<FP_Input, FP_State, SwapJobs> FP_ts(in, FP_sm, FP_nhe, "SwapJobsTabuSearch",
                                                   [](const SwapJobs& m1, const SwapJobs& m2)->bool
                                                   { return m1.p1 == m2.p1 && m1.p2 == m2.p2; });

  // tester
  Tester<FP_Input, FP_Output, FP_State> tester(in, FP_sm, FP_om);