Program Listing for File utils.cpp

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

#include "cvrp/utils.hpp"

#include <algorithm>
#include <iostream>
#include <limits>
#include <random>
#include <tuple>
#include <utility>

std::ostream &operator<<(std::ostream &os, const Node &node) {
  os << "Node Status" << '\n';
  os << "ID    : " << node.id_ << '\n';
  os << "X     : " << node.x_ << '\n';
  os << "Y     : " << node.y_ << '\n';
  os << "Demand: " << node.demand_ << '\n';
  os << '\n';
  return os;
}

void Vehicle::CalculateCost(
    const std::vector<std::vector<double>> &distanceMatrix) {
  cost_ = 0;
  for (size_t i = 0; i < nodes_.size() - 1; i++) {
    cost_ += distanceMatrix[nodes_[i]][nodes_[i + 1]];
  }
}

std::ostream &operator<<(std::ostream &os, const Vehicle &v) {
  os << "Vehicle Status" << '\n';
  os << "Cost    : " << v.cost_ << '\n';
  os << "ID      : " << v.id_ << '\n';
  os << "Load    : " << v.load_ << '\n';
  os << "Capacity: " << v.capacity_ << '\n';
  os << "Path    : ";
  // the nodes_.size()-1 limit is only added to ensure that there isnt a --->
  // after the last node, which is always the depot, ie node 0.
  for (size_t i = 0; i < v.nodes_.size() - 1; ++i) {
    os << v.nodes_[i] << " ---> ";
  }
  os << "0";
  os << '\n' << '\n';
  return os;
}

void PrintVehicleRoute(const Vehicle &v) {
  for (size_t i = 0; i < v.nodes_.size() - 1; ++i) {
    std::cout << v.nodes_[i] << " ---> ";
  }
  std::cout << "0";
  std::cout << '\n' << '\n';
}

Solution::Solution(std::vector<Node> nodes,
                   const std::vector<Vehicle> &vehicles,
                   std::vector<std::vector<double>> distanceMatrix)
    : nodes_(std::move(nodes)),
      vehicles_(vehicles),
      distanceMatrix_(std::move(distanceMatrix)) {
  depot_ = nodes_[0];
  capacity_ = vehicles[0].load_;
}

Solution::Solution(const Problem &p)
    : nodes_(p.nodes_),
      vehicles_(p.vehicles_),
      distanceMatrix_(p.distanceMatrix_),
      capacity_(p.capacity_) {
  depot_ = nodes_[0];
}

void Solution::CreateInitialSolution() {
  for (auto &v : vehicles_) {
    while (true) {
      const auto [found, closest_node] = find_closest(v);
      if (found && v.load_ - closest_node.demand_ >= 0) {  // }.2*capacity){
        v.load_ -= closest_node.demand_;
        v.cost_ += distanceMatrix_[v.nodes_.back()][closest_node.id_];
        v.nodes_.push_back(closest_node.id_);
        nodes_[closest_node.id_].is_routed_ = true;
      } else {
        v.cost_ += distanceMatrix_[v.nodes_.back()][depot_.id_];
        v.nodes_.push_back(depot_.id_);
        break;
      }
    }
  }
}

std::tuple<bool, Node> Solution::find_closest(const Vehicle &v) const {
  double cost = std::numeric_limits<double>::max();
  size_t id = 0;
  bool found = false;
  for (size_t j = 0; j < distanceMatrix_[0].size(); j++) {
    if (!nodes_[j].is_routed_ && nodes_[j].demand_ <= v.load_ &&
        distanceMatrix_[v.nodes_.back()][j] < cost) {
      cost = distanceMatrix_[v.nodes_.back()][j];
      id = j;
      found = true;
    }
  }
  if (found) {
    return {true, nodes_[id]};
  }
  return {false, Node()};
}

bool Solution::CheckSolutionValid() const {
  // double cost = 0;
  std::vector<bool> check_nodes(nodes_.size(), false);
  check_nodes[0] = true;
  for (const auto &v : vehicles_) {
    int load = capacity_;
    for (const auto &n : v.nodes_) {
      load -= nodes_[n].demand_;
      check_nodes[n] = true;
    }
    if (load < 0) {
      return false;
    }
  }
  return std::all_of(std::begin(check_nodes), std::end(check_nodes),
                     [](const bool b) { return b; });
}

Problem::Problem(const int noc, const int demand_range, const int nov,
                 const int capacity, const int grid_range,
                 std::string distribution, const int n_clusters,
                 const int cluster_range) {
  std::random_device rd;   // obtain a random number from hardware
  std::mt19937 eng(rd());  // seed the generator
  std::uniform_int_distribution<int> ran(-grid_range,
                                         grid_range);  // define the range
  std::uniform_int_distribution<int> ran_d(0,
                                           demand_range);  // define the range
  std::uniform_int_distribution<int> ran_c(-cluster_range, cluster_range);
  Node depot(0, 0, 0, 0, true);
  this->capacity_ = capacity;

  nodes_.push_back(depot);

  if (distribution != "uniform" && distribution != "cluster") {
    distribution = "uniform";
  }
  if (distribution == "uniform") {
    for (int i = 1; i <= noc; ++i) {
      nodes_.emplace_back(ran(eng), ran(eng), i, ran_d(eng), false);
    }
  } else if (distribution == "cluster") {
    int id = 1;
    int n_p_c = noc / n_clusters;
    int remain = noc % n_clusters;
    for (int i = 0; i < n_clusters; i++) {
      int x = ran(eng);
      int y = ran(eng);
      for (int j = 0; j < n_p_c; j++) {
        nodes_.emplace_back(x + ran_c(eng), y + ran_c(eng), id, ran_d(eng),
                            false);
        id++;
      }
    }
    int x = ran(eng);
    int y = ran(eng);
    for (int j = 0; j < remain; j++) {
      nodes_.emplace_back(x + ran_c(eng), y + ran_c(eng), id, ran_d(eng),
                          false);
      id++;
    }
  }

  // for(const auto& n:nodes) std::cout << n << '\n';
  std::vector<double> tmp(nodes_.size());
  for (size_t i = 0; i < nodes_.size(); ++i) {
    distanceMatrix_.push_back(tmp);
  }
  for (size_t i = 0; i < nodes_.size(); ++i) {
    for (size_t j = i; j < nodes_.size(); ++j) {
      distanceMatrix_[i][j] = sqrt(pow((nodes_[i].x_ - nodes_[j].x_), 2) +
                                   pow((nodes_[i].y_ - nodes_[j].y_), 2));
      distanceMatrix_[j][i] = distanceMatrix_[i][j];
    }
  }

  int load = capacity_;
  for (int i = 0; i < nov; ++i) {
    vehicles_.emplace_back(i, load, capacity_);
    vehicles_[i].nodes_.push_back(0);
  }
}

void Solution::PrintSolution(const std::string &option) const {
  double total_cost = 0;
  for (const auto &v : vehicles_) {
    total_cost += v.cost_;
    if (option == "status") {
      PrintVehicleRoute(v);
    } else if (option == "route") {
      std::cout << "Vehicle ID: " << v.id_ << " | ";
      PrintVehicleRoute(v);
    }
  }
  bool valid = CheckSolutionValid();
  std::cout << "Total solution cost: " << total_cost << '\n';
  std::cout << "Solution validity  : " << valid << '\n';
  if (!valid) {
    for (const auto &i : nodes_) {
      if (!i.is_routed_) {
        std::cout << "Unreached node: " << '\n';
        std::cout << i << '\n';
      }
    }
  }
}