Program Listing for File genetic_algorithm.cpp

Return to documentation for file (src/genetic_algorithm.cpp)

#include "cvrp/genetic_algorithm.hpp"

#include <algorithm>
#include <iostream>
#include <random>
#include <set>

constexpr int total_percentage = 100;

GASolution::GASolution(const Problem &p, const int n_chromosomes,
                       const int generations)
    : Solution(p.nodes_, p.vehicles_, p.distanceMatrix_),
      n_chromosomes_(n_chromosomes),
      generations_(generations),
      n_nucleotide_pairs_(nodes_.size() - 1),
      costs_(std::vector<double>(n_chromosomes)),
      n_vehicles_(vehicles_.size()) {
  GenerateRandomSolutions();
  for (int i = 0; i < n_chromosomes; i++) {
    MakeValid(i);
  }
  GenerateGreedySolutions();
  CalculateTotalCost();
  best_ = std::min_element(costs_.begin(), costs_.end()) - costs_.begin();
}

GASolution::GASolution(const Solution &s, const int n_chromosomes,
                       const int generations)
    : Solution(s),
      n_chromosomes_(n_chromosomes),
      generations_(generations),
      n_nucleotide_pairs_(nodes_.size() - 1),
      costs_(std::vector<double>(n_chromosomes)),
      n_vehicles_(vehicles_.size()) {
  std::vector<int> temp_c;
  std::vector<int> temp_i{0};
  for (const auto &v : vehicles_) {
    for (size_t i = 1; i < v.nodes_.size() - 1; i++) {
      temp_c.push_back(v.nodes_[i]);
    }
    temp_i.push_back(temp_c.size());
  }

  // Reset vehicles_ to allow GenerateGreedySolutions() to run currectly
  for (auto &v : vehicles_) {
    v.nodes_.clear();
    v.nodes_.push_back(0);
    v.load_ = capacity_;
  }

  for (auto &n : nodes_) {
    n.is_routed_ = false;
  }
  nodes_[0].is_routed_ = true;

  GenerateRandomSolutions();
  for (int i = 0; i < n_chromosomes; i++) {
    MakeValid(i);
  }
  GenerateGreedySolutions();
  // Replacing the greedy solution (1st chromosome) with the solution given as
  // input
  chromosomes_[0] = temp_c;
  iterators_[0] = temp_i;
  if (!checkValidity(0) ||
      chromosomes_[0].size() != size_t(n_nucleotide_pairs_)) {
    // Extra sanity check for size of solution
    std::cout << "The input solution is invalid. Exiting." << '\n';
    exit(0);
  }
  CalculateTotalCost();
  best_ = std::min_element(costs_.begin(), costs_.end()) - costs_.begin();
}

GASolution::GASolution(const std::vector<Node> &nodes,
                       const std::vector<Vehicle> &vehicles,
                       const std::vector<std::vector<double>> &distanceMatrix,
                       const int n_chromosomes, const int generations)
    : Solution(nodes, vehicles, distanceMatrix),
      n_chromosomes_(n_chromosomes),
      generations_(generations),
      n_nucleotide_pairs_(nodes_.size() - 1),
      costs_(std::vector<double>(n_chromosomes)),
      n_vehicles_(vehicles_.size()) {
  GenerateRandomSolutions();
  for (int i = 0; i < n_chromosomes; i++) {
    MakeValid(i);
  }
  GenerateGreedySolutions();
  CalculateTotalCost();
  best_ = std::min_element(costs_.begin(), costs_.end()) - costs_.begin();
}

std::vector<int> GASolution::GenerateRandomSolution() const {
  std::vector<int> temp(n_nucleotide_pairs_);
  for (int i = 0; i < n_nucleotide_pairs_; ++i) {
    temp[i] = i + 1;
  }
  unsigned seed =
      rand();  // std::chrono::system_clock::now().time_since_epoch().count();
  std::shuffle(temp.begin(), temp.end(), std::default_random_engine(seed));
  return temp;
}

std::vector<int> GASolution::GenerateRandomIterSolution() const {
  std::vector<int> temp(n_vehicles_ + 1);
  std::unordered_set<int> added;
  temp[0] = 0;
  added.insert(0);
  for (size_t i = 1; i < n_vehicles_; ++i) {
    size_t n = rand() % n_nucleotide_pairs_;
    if (added.find(n) != added.end()) {
      n = n_nucleotide_pairs_;
    }
    temp[i] = n;
  }
  temp[n_vehicles_] = n_nucleotide_pairs_;
  std::sort(temp.begin(), temp.end());
  return temp;
}

void GASolution::GenerateRandomSolutions() {
  std::vector<int> temp(n_nucleotide_pairs_);
  for (int i = 0; i < n_nucleotide_pairs_; ++i) {
    temp[i] = i + 1;
  }
  for (int i = 0; i < n_chromosomes_; i++) {
    chromosomes_.push_back(temp);
  }
  for (int i = 0; i < n_chromosomes_; ++i) {
    unsigned seed =
        rand();  // std::chrono::system_clock::now().time_since_epoch().count();
    std::shuffle(chromosomes_[i].begin(), chromosomes_[i].end(),
                 std::default_random_engine(seed));
  }
  for (int j = 0; j < n_chromosomes_; j++) {
    std::vector<int> temp_i(n_vehicles_ + 1, 0);
    std::unordered_set<int> added;
    for (int i = 1; i < n_vehicles_; ++i) {
      size_t n = rand() % n_nucleotide_pairs_;
      if (added.find(n) != added.end()) {
        n = n_nucleotide_pairs_;
      }
      temp_i[i] = n;
    }
    temp_i[n_vehicles_] = n_nucleotide_pairs_;
    std::sort(temp_i.begin(), temp_i.end());
    iterators_.push_back(temp_i);
  }
}

void GASolution::GenerateGreedySolutions() {
  std::vector<int> gs;
  auto vehicles_2 = vehicles_;
  std::vector<int> iter;
  iter.push_back(0);
  for (auto &v : vehicles_2) {
    while (true) {
      const auto [found, closest_node] = find_closest(v);
      if (found && v.load_ - closest_node.demand_ >= 0) {
        v.load_ -= closest_node.demand_;
        v.cost_ += distanceMatrix_[v.nodes_.back()][closest_node.id_];
        v.nodes_.push_back(closest_node.id_);
        gs.push_back(closest_node.id_);
        nodes_[closest_node.id_].is_routed_ = true;
      } else {
        iter.push_back(iter.back() + v.nodes_.size() - 1);
        v.cost_ += distanceMatrix_[v.nodes_.back()][depot_.id_];
        v.nodes_.push_back(depot_.id_);
        break;
      }
    }
  }
  if (gs.size() != size_t(n_nucleotide_pairs_)) {
    std::cout << "Initial solution does not contain all the nodes_. Exiting\n";
    exit(0);
  }
  chromosomes_[0] = gs;
  iterators_[0] = iter;
  costs_[0] = CalculateCost(0);
  constexpr double percentage_of_chromosome = 0.2;
  for (int j = 1; j < percentage_of_chromosome * n_chromosomes_; ++j) {
    gs.clear();
    iter.clear();
    vehicles_2 = vehicles_;
    iter.push_back(0);
    for (auto &n : nodes_) {
      n.is_routed_ = false;
    }
    nodes_[depot_.id_].is_routed_ = true;
    int count = 0;
    for (auto &v : vehicles_2) {
      while (true) {
        Node closest_node;
        bool found = false;
        if (count == 0) {
          size_t i = rand() % (n_nucleotide_pairs_ - 1) + 1;
          closest_node = nodes_[i];
          count++;
        } else {
          std::tie(found, closest_node) = find_closest(v);
        }
        if (found && v.load_ - closest_node.demand_ >= 0) {
          v.load_ -= closest_node.demand_;
          v.cost_ += distanceMatrix_[v.nodes_.back()][closest_node.id_];
          v.nodes_.push_back(closest_node.id_);
          gs.push_back(closest_node.id_);
          nodes_[closest_node.id_].is_routed_ = true;
        } else {
          iter.push_back(iter.back() + v.nodes_.size() - 1);
          v.cost_ += distanceMatrix_[v.nodes_.back()][depot_.id_];
          v.nodes_.push_back(depot_.id_);
          break;
        }
      }
    }
    chromosomes_[j] = gs;
    if (gs.size() != size_t(n_nucleotide_pairs_)) {
      std::cout << "Initial solutions do not contain all the nodes_. Exiting\n";
      exit(0);
    }
    iterators_[j] = iter;
    MakeValid(j);
    costs_[j] = CalculateCost(j);
  }
}

void GASolution::RemoveSimilarSolutions() {
  std::set<int> to_delete;
  constexpr double weight_95 = 0.95;
  constexpr double weight_105 = 1.05;
  for (int i = 0; i < n_chromosomes_; i++) {
    for (int j = 0; j < n_chromosomes_; j++) {
      if (j == i || j == best_) {
        continue;
      }
      int count = 0;
      for (int k = 0; k < n_nucleotide_pairs_; k++) {
        if (chromosomes_[i][k] == chromosomes_[j][k]) {
          count++;
        }
      }
      if (count > weight_95 * n_nucleotide_pairs_ &&
          ((costs_[i] > weight_95 * costs_[j] &&
            costs_[i] < weight_105 * costs_[j]) ||
           (costs_[j] > weight_95 * costs_[i] &&
            costs_[j] < weight_105 * costs_[i]))) {
        if (costs_[i] > costs_[j]) {
          to_delete.insert(i);
        } else {
          to_delete.insert(j);
        }
      }
    }
  }
  for (const auto i : to_delete) {
    constexpr int min_percentage = 15;
    if (rand() % total_percentage > min_percentage) {
      chromosomes_[i] = GenerateRandomSolution();
      iterators_[i] = GenerateRandomIterSolution();
      MakeValid(i);
      costs_[i] = CalculateCost(i);
    }
  }
}

double GASolution::CalculateCost(const int i) const {
  double cost = 0;
  for (size_t k = 0; k < iterators_[0].size() - 1; k++) {
    if (iterators_[i][k] == n_nucleotide_pairs_) {
      break;
    }
    int j = iterators_[i][k];
    if (j < iterators_[i][k + 1]) {
      cost += distanceMatrix_[0][chromosomes_[i][j]];
    }
    while (j + 1 < iterators_[i][k + 1]) {
      cost += distanceMatrix_[chromosomes_[i][j]][chromosomes_[i][j + 1]];
      j++;
    }
    cost += distanceMatrix_[chromosomes_[i][j]][0];
  }
  return cost;
}

void GASolution::CalculateTotalCost() {
  for (int i = 0; i < n_chromosomes_; i++) {
    costs_[i] = CalculateCost(i);
  }
}

constexpr int p_mutate = 50;
constexpr int p_random_swap = 50;
constexpr int p_mutate_within_gene = 50;
constexpr int p_insert_iter_dist = 70;

void GASolution::Solve() {
  int generation = 0;
  while (generation < generations_) {
    // std::cout << "Generation: " << generation << '\n';
    // for(int i=0;i<chromosomes_.size();i++){
    //   if(!checkValidity(i)) std::cout << "Invalid" << '\n';
    // }
    best_ = std::min_element(costs_.begin(), costs_.end()) - costs_.begin();
    if (rand() % 2 == 0) {
      HGreXCrossover();
      best_ = std::min_element(costs_.begin(), costs_.end()) - costs_.begin();
    }
    if (rand() % 2 == 0) {
      int n = rand() % n_chromosomes_;
      auto temp_i = iterators_[n];
      MutateIterLeft(n, rand() % n_vehicles_);
      double c = CalculateCost(n);
      if (c < costs_[n]) {
        costs_[n] = c;
      } else {
        iterators_[n] = temp_i;
      }
      best_ = std::min_element(costs_.begin(), costs_.end()) - costs_.begin();
    } else {
      int n = rand() % n_chromosomes_;
      auto temp_i = iterators_[n];
      MutateIterRight(n, rand() % n_vehicles_);
      double c = CalculateCost(n);
      if (c < costs_[n]) {
        costs_[n] = c;
      } else {
        iterators_[n] = temp_i;
      }
      best_ = std::min_element(costs_.begin(), costs_.end()) - costs_.begin();
    }
    if (rand() % total_percentage < p_mutate) {
      Mutate();
      best_ = std::min_element(costs_.begin(), costs_.end()) - costs_.begin();
    }
    if (rand() % total_percentage < p_random_swap) {
      RandomSwap();
      best_ = std::min_element(costs_.begin(), costs_.end()) - costs_.begin();
    }
    if (rand() % total_percentage < p_mutate_within_gene) {
      MutateWhithinGene();
      best_ = std::min_element(costs_.begin(), costs_.end()) - costs_.begin();
    }
    if (rand() % total_percentage < p_insert_iter_dist) {
      InsertIterDist();
      best_ = std::min_element(costs_.begin(), costs_.end()) - costs_.begin();
    }
    // if(rand()%total_percentage<5) {
    //   Addbest();
    //   best_ = std::min_element(costs_.begin(), costs_.end()) -
    //   costs_.begin();
    // }
    // constexpr int n_attempts = 20;
    // if(rand()%total_percentage < n_attempts) {
    //   DeleteBadChromosome();
    // }
    CalculateTotalCost();
    generation++;
    // if(generation%total_percentage==0){
    //   RemoveSimilarSolutions();
    // }

    // NOTE: Kept as a remonder of way to print solution
    // TODO(vss): Remove
    // solution_string_ = std::to_string(depot_.id_);
    // for(size_t k=0;k<iterators_[0].size()-1;k++){
    //   if(iterators_[best_][k]==n_nucleotide_pairs_) break;
    //   int j=iterators_[best_][k];
    //   if(j<iterators_[best_][k+1]){
    //     solution_string_ += ',' + std::to_string(chromosomes_[best_][j]);
    //   }
    //   while(j+1<iterators_[best_][k+1]){
    //     solution_string_ += ',' + std::to_string(chromosomes_[best_][j+1]);
    //     j++;
    //   }
    //   solution_string_ += ',' + std::to_string(depot_.id_);
    // }
  }
  GenerateBestSolution();
}

void GASolution::HGreXCrossover() {
  const int p1 = TournamentSelection();
  const int p2 = TournamentSelection();
  std::vector<int> child;
  std::unordered_set<int> reached;
  auto *itp1 = &(chromosomes_[p1]);
  auto *itp2 = &(chromosomes_[p2]);
  child.push_back((*itp1)[0]);
  reached.insert(child.back());
  while (child.size() < size_t(n_nucleotide_pairs_)) {
    auto it_1 = find((*itp1).begin(), (*itp1).end(), child.back());
    auto it_2 = find((*itp2).begin(), (*itp2).end(), child.back());
    int n1 = 0;
    int n2 = 0;
    // if it = itp1.end() there is something wrong as both chromosomes should
    // contain all the nodes
    if (it_1 != itp1->end() - 1 && reached.find(*(it_1 + 1)) == reached.end()) {
      n1 = *(it_1 + 1);
    } else {
      while (true) {
        it_1++;
        if (it_1 == itp1->end()) {
          it_1 = itp1->begin();
        }
        if (reached.find(*it_1) == reached.end()) {
          n1 = *(it_1);
          break;
        }
      }
    }
    if (it_2 != itp2->end() - 1 && reached.find(*(it_2 + 1)) == reached.end()) {
      n2 = *(it_2 + 1);
    } else {
      while (true) {
        it_2++;
        if (it_2 == itp2->end()) {
          it_2 = itp2->begin();
        }
        if (reached.find(*it_2) == reached.end()) {
          n2 = *(it_2);
          break;
        }
      }
    }
    if (distanceMatrix_[child.back()][n1] > distanceMatrix_[child.back()][n2]) {
      std::swap(n1, n2);
    }
    child.push_back(n1);
    reached.insert(n1);
  }
  chromosomes_.push_back(child);
  int temp = rand() % total_percentage;
  constexpr int p_emplace_random_iter = 40;
  constexpr int p_emplace_iter_1 = 60;
  if (temp < p_emplace_random_iter) {
    iterators_.emplace_back(GenerateRandomIterSolution());
  } else if (temp < p_emplace_iter_1) {
    iterators_.emplace_back(iterators_[p1]);
  } else {
    iterators_.emplace_back(iterators_[p2]);
  }
  MakeValid(n_chromosomes_);
  if (checkValidity(n_chromosomes_)) {
    costs_.emplace_back(CalculateCost(n_chromosomes_));
    InsertionBySimilarity();
  } else {
    iterators_.erase(iterators_.begin() + n_chromosomes_ - 1);
    chromosomes_.erase(chromosomes_.begin() + n_chromosomes_ - 1);
  }
}

// Works if and only if a solution is possible. No check on validity after
// function executes
void GASolution::MakeValid(const int i) {
  for (int j = 0; j < n_vehicles_ - 1; j++) {
    int load = capacity_;
    int iter = iterators_[i][j];
    while (iter < iterators_[i][j + 1]) {
      load -= nodes_[chromosomes_[i][iter]].demand_;
      ++iter;
    }
    if (load < 0) {
      iterators_[i][j + 1]--;
      j--;
    }
  }

  for (int j = n_vehicles_; j > 1; j--) {
    int load = capacity_;
    int iter = iterators_[i][j] - 1;
    while (iter >= iterators_[i][j - 1]) {
      load -= nodes_[chromosomes_[i][iter]].demand_;
      --iter;
    }
    if (load < 0) {
      iterators_[i][j - 1]++;
      j++;
    }
  }
}

void GASolution::DeleteBadChromosome() {
  const int i = TournamentSelectionBad();
  chromosomes_[i] = GenerateRandomSolution();
}

int GASolution::TournamentSelection(const int n) const {
  std::vector<int> indices(n);
  generate(indices.begin(), indices.end(),
           [this]() { return rand() % chromosomes_.size(); });
  return *std::min_element(
      std::begin(indices), std::end(indices),
      [this](const int i1, const int i2) { return costs_[i1] < costs_[i2]; });
}

int GASolution::TournamentSelectionBad(const int n) const {
  std::vector<int> indices(n);
  generate(indices.begin(), indices.end(),
           [this]() { return rand() % chromosomes_.size(); });
  return *std::max_element(
      std::begin(indices), std::end(indices),
      [this](const int i1, const int i2) { return costs_[i1] < costs_[i2]; });
}

void GASolution::InsertionBySimilarity() {
  best_ = std::min_element(costs_.begin(), costs_.end()) - costs_.begin();
  bool flag = true;
  for (int i = 0; i < n_nucleotide_pairs_; ++i) {
    if (i != best_ &&
        costs_.back() - costs_[i] < 2 * (costs_[best_] / total_percentage)) {
      costs_.erase(costs_.begin() + i);
      chromosomes_.erase(chromosomes_.begin() + i);
      iterators_.erase(iterators_.begin() + i);
      flag = false;
      break;
    }
  }
  if (flag) {
    DeleteRandomChromosome();
  }
}

void GASolution::DeleteRandomChromosome() {
  int r = rand() % n_chromosomes_;
  while (r == best_) {
    r = rand() % n_chromosomes_;
  }
  chromosomes_[r] = chromosomes_.back();
  iterators_[r] = iterators_.back();
  chromosomes_.erase(chromosomes_.begin() + chromosomes_.size() - 1);
  iterators_.erase(iterators_.begin() + iterators_.size() - 1);
}

void GASolution::Mutate() {
  int count = 0;
  constexpr int n_attempts = 20;
  while (count < n_attempts) {
    best_ = std::min_element(costs_.begin(), costs_.end()) - costs_.begin();
    int r = rand() % n_chromosomes_;
    while (r == best_) {
      r = rand() % n_chromosomes_;
    }
    size_t i1 = rand() % n_nucleotide_pairs_;
    size_t i2 = rand() % n_nucleotide_pairs_;
    if (i1 > i2) {
      std::swap(i1, i2);
    }
    auto temp_it = iterators_[r];
    std::reverse(chromosomes_[r].begin() + i1, chromosomes_[r].begin() + i2);
    MakeValid(r);
    const double p = costs_[r];
    costs_[r] = CalculateCost(r);
    if (p < costs_[r]) {
      std::reverse(chromosomes_[r].begin() + i1, chromosomes_[r].begin() + i2);
      iterators_[r] = temp_it;
      count++;
      costs_[r] = p;
    } else if (checkValidity(r)) {
      break;
    }
  }
}

void GASolution::SwapWhithinGene() {
  int count = 0;
  constexpr int n_attempts = 20;
  while (count < n_attempts) {
    best_ = std::min_element(costs_.begin(), costs_.end()) - costs_.begin();
    int r = rand() % n_chromosomes_;
    // while(r==best_) r = rand()%n_chromosomes_;
    int v = rand() % n_vehicles_;
    int delta = iterators_[r][v + 1] - iterators_[r][v];
    if (delta < 1) {
      return;
    }
    int i1 = iterators_[r][v] + rand() % delta;
    int i2 = iterators_[r][v] + rand() % delta;
    std::swap(chromosomes_[r][i1], chromosomes_[r][i2]);
    auto temp_it = iterators_[r];
    MakeValid(r);
    double p = costs_[r];
    costs_[r] = CalculateCost(r);
    if (p < costs_[r]) {
      std::swap(chromosomes_[r][i1], chromosomes_[r][i2]);
      iterators_[r] = temp_it;
      count++;
      costs_[r] = p;
    } else if (checkValidity(r)) {
      break;
    }
  }
}

void GASolution::MutateWhithinGene() {
  int count = 0;
  constexpr int n_attempts = 20;
  while (count < n_attempts) {
    best_ = std::min_element(costs_.begin(), costs_.end()) - costs_.begin();
    int r = rand() % n_chromosomes_;
    while (r == best_) {
      r = rand() % n_chromosomes_;
    }
    int v = rand() % n_vehicles_;
    int delta = iterators_[r][v + 1] - iterators_[r][v];
    if (delta < 1) {
      return;
    }
    int i1 = iterators_[r][v] + rand() % delta;
    int i2 = iterators_[r][v] + rand() % delta;
    if (i1 > i2) {
      std::swap(i1, i2);
    }
    std::reverse(chromosomes_[r].begin() + i1, chromosomes_[r].begin() + i2);
    const auto temp_it = iterators_[r];
    MakeValid(r);
    double p = costs_[r];
    costs_[r] = CalculateCost(r);
    if (p < costs_[r]) {
      std::reverse(chromosomes_[r].begin() + i1, chromosomes_[r].begin() + i2);
      iterators_[r] = temp_it;
      count++;
      costs_[r] = p;
    } else if (checkValidity(r)) {
      break;
    }
  }
}

bool GASolution::MutateIterLeft(const int i_chromosome, const int j_in) {
  if (j_in == n_vehicles_ || j_in == 0) {
    return false;
  }

  int i = i_chromosome;
  if (iterators_[i][j_in] > iterators_[i][j_in - 1]) {
    iterators_[i][j_in]--;
  }
  for (int j = j_in; j < n_vehicles_ - 1; j++) {
    int load = capacity_;
    int iter = iterators_[i][j];
    while (iter < iterators_[i][j + 1]) {
      load -= nodes_[chromosomes_[i][iter]].demand_;
      ++iter;
    }
    if (load < 0) {
      iterators_[i][j + 1]--;
      j--;
    }
  }

  for (int j = n_vehicles_; j > 1; j--) {
    int load = capacity_;
    int iter = iterators_[i][j] - 1;
    while (iter >= iterators_[i][j - 1]) {
      load -= nodes_[chromosomes_[i][iter]].demand_;
      --iter;
    }
    if (load < 0) {
      iterators_[i][j - 1]++;
      j++;
    }
  }
  return true;
}

bool GASolution::MutateIterRight(const int i_chromosome, const int j_in) {
  if (j_in == n_vehicles_ || j_in == 0) {
    return false;
  }
  int i = i_chromosome;
  if (iterators_[i][j_in] < iterators_[i][j_in - 1]) {
    iterators_[i][j_in]++;
  }
  for (int j = j_in; j > 1; j--) {
    int load = capacity_;
    int iter = iterators_[i][j] - 1;
    while (iter >= iterators_[i][j - 1]) {
      load -= nodes_[chromosomes_[i][iter]].demand_;
      --iter;
    }
    if (load < 0) {
      iterators_[i][j - 1]++;
      j++;
    }
  }

  for (int j = 0; j < n_vehicles_ - 1; j++) {
    int load = capacity_;
    int iter = iterators_[i][j];
    while (iter < iterators_[i][j + 1]) {
      load -= nodes_[chromosomes_[i][iter]].demand_;
      ++iter;
    }
    if (load < 0) {
      iterators_[i][j + 1]--;
      j--;
    }
  }
  return true;
}

bool GASolution::checkValidity(const int i) const {
  for (int j = 0; j < n_vehicles_; j++) {
    int load = capacity_;
    int iter = iterators_[i][j];
    while (iter < iterators_[i][j + 1]) {
      load -= nodes_[chromosomes_[i][iter]].demand_;
      ++iter;
    }
    if (load < 0) {
      return false;
    }
  }
  return true;
}

void GASolution::RandomSwap() {
  int count = 0;
  constexpr int n_attempts = 20;
  while (count < n_attempts) {
    best_ = std::min_element(costs_.begin(), costs_.end()) - costs_.begin();
    int r = rand() % n_chromosomes_;
    while (r == best_) {
      r = rand() % n_chromosomes_;
    }
    size_t i1 = rand() % n_nucleotide_pairs_;
    size_t i2 = rand() % n_nucleotide_pairs_;
    std::swap(chromosomes_[r][i1], chromosomes_[r][i2]);
    const auto temp_it = iterators_[r];
    MakeValid(r);
    double p = costs_[r];
    costs_[r] = CalculateCost(r);
    if (p < costs_[r]) {
      std::swap(chromosomes_[r][i1], chromosomes_[r][i2]);
      iterators_[r] = temp_it;
      count++;
      costs_[r] = p;
    } else if (checkValidity(r)) {
      break;
    }
  }
}

void GASolution::AddBest() {
  best_ = std::min_element(costs_.begin(), costs_.end()) - costs_.begin();
  const int worst =
      std::min_element(costs_.begin(), costs_.end()) - costs_.begin();
  chromosomes_[worst] = chromosomes_[best_];
  costs_[worst] = costs_[best_];
  iterators_[worst] = iterators_[best_];
}

void GASolution::DeleteWorstChromosome() {
  const auto it = std::max_element(costs_.begin(), costs_.end());
  const int dist = std::distance(costs_.begin(), it);
  costs_.erase(it);
  chromosomes_.erase(std::next(chromosomes_.begin(), dist));
  iterators_.erase(std::next(iterators_.begin(), dist));
}

void GASolution::InsertIterDist() {
  const int n = rand() % n_chromosomes_;
  auto temp = iterators_[n];
  int j = n_vehicles_;
  while (iterators_[n][j] == n_nucleotide_pairs_) {
    j--;
  }
  if (j == n_vehicles_ - 1) {
    return;
  }
  j++;
  // that found the iterator to insert
  double cost = 0;
  int iter_begin = 0;
  int range = 0;
  for (int i = 0; i < n_vehicles_; i++) {
    int c = 0;
    c += distanceMatrix_[0][iterators_[n][i]];
    for (int k = iterators_[n][i]; k < iterators_[n][i + 1] - 1; k++) {
      c += distanceMatrix_[chromosomes_[n][k]][chromosomes_[n][k + 1]];
    }
    if (iterators_[n][i + 1] - iterators_[n][i] < 2) {
      continue;
    }
    c += distanceMatrix_[iterators_[n][i + 1] - 1][0];
    if (c > cost) {
      cost = c;
      iter_begin = i;
      range = iterators_[n][i + 1] - iterators_[n][i];
    }
  }
  int i = iter_begin;
  if (cost == 0 || range < 2) {
    return;
  }
  const int val = iterators_[n][i] + rand() % (range - 1) + 1;
  iterators_[n].erase(iterators_[n].begin() + j);
  iterators_[n].insert(iterators_[n].begin() + i + 1, val);
  MakeValid(n);  // dont think this is req
  if (!checkValidity(n)) {
    std::cout << "Invalid from insertiterdist" << '\n';
  }
  const double c2 = CalculateCost(n);
  if (costs_[n] < c2) {
    iterators_[n] = temp;
  } else {
    costs_[n] = c2;
  }
}

void GASolution::GenerateBestSolution() {
  auto it = std::min_element(costs_.begin(), costs_.end());
  std::cout << "Cost: " << *it << '\n';
  int i = it - costs_.begin();
  auto v = vehicles_.begin();
  for (size_t k = 0; k < iterators_[0].size() - 1; k++, v++) {
    v->cost_ = 0;
    if (iterators_[i][k] == n_nucleotide_pairs_) {
      break;
    }
    int j = iterators_[i][k];
    if (j < iterators_[i][k + 1]) {
      v->cost_ += distanceMatrix_[0][chromosomes_[i][j]];
      v->nodes_.push_back(chromosomes_[i][j]);
      v->load_ -= nodes_[chromosomes_[i][j]].demand_;
    }
    while (j + 1 < iterators_[i][k + 1]) {
      v->cost_ += distanceMatrix_[chromosomes_[i][j]][chromosomes_[i][j + 1]];
      v->nodes_.push_back(chromosomes_[i][j + 1]);
      v->load_ -= nodes_[chromosomes_[i][j + 1]].demand_;
      j++;
    }
    v->cost_ += distanceMatrix_[v->nodes_.back()][depot_.id_];
    v->nodes_.push_back(depot_.id_);
  }
  while (v != vehicles_.end()) {
    v->cost_ = 0;
    ++v;
  }
  std::cout << "Solution valid: " << CheckSolutionValid() << '\n';
}