Files
ortools-clone/documentation/tutorials/cplusplus/routing_common/tsplib_reader.h
nikolaj.van.omme@gmail.com 7fc4c48e28 Doc automatic update
2015-02-10 19:24:05 +00:00

619 lines
20 KiB
C++

// Copyright 2011-2014 Google
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//
// TSPLIBReader.
//
// Only valid for:
// - TSP
// - ATSP
// - CVRP
// - CCPP (this is an extension)
#ifndef OR_TOOLS_TUTORIALS_CPLUSPLUS_TSPLIB_READER_H
#define OR_TOOLS_TUTORIALS_CPLUSPLUS_TSPLIB_READER_H
#include <cmath>
#include <limits>
#include <vector>
#include "base/integral_types.h"
#include "base/filelinereader.h"
#include "base/split.h"
#include "base/strtoint.h"
#include "routing_common/routing_common.h"
#include "routing_common/routing_data.h"
#include "routing_common/tsplib.h"
namespace operations_research {
class TSPLIBReader : public RoutingData {
public:
typedef std::vector<RoutingModel::NodeIndex>::iterator solution_iterator;
typedef std::vector<RoutingModel::NodeIndex>::const_iterator const_solution_iterator;
explicit TSPLIBReader(const std::string & filename) :
RoutingData(0),
line_number_(0),
visualizable_(false),
two_dimension_(false),
symmetric_(false),
need_to_compute_distances_(false),
tsplib_state_unknown_(true),
tsplib_state_(TSPLIB_STATES_UNDEFINED),
name_(""),
type_(TSPLIB_PROBLEM_TYPES_UNDEFINED),
comment_(""),
capacity_(-1),
edge_weight_type_(TSPLIB_EDGE_WEIGHT_TYPES_UNDEFINED),
edge_weight_format_type_(TSPLIB_EDGE_WEIGHT_FORMAT_TYPES_UNDEFINED),
edge_data_format_type_(TSPLIB_EDGE_DATA_FORMAT_TYPES_UNDEFINED),
node_coord_type_(TWOD_COORDS), // If no coord type is given, we assume 2D.
display_data_type_(TSPLIB_DISPLAY_DATA_TYPE_TYPES_UNDEFINED)
{
LoadInstance(filename);
if (depots_.size() == 0) {
depots_.push_back(RoutingModel::kFirstNode);
}
SetRoutingDataInstanciated();
}
TSPLIB_PROBLEM_TYPES_enum TSPLIBType () const {
return type_;
}
RoutingModel::NodeIndex Depot() const {
return depots_[0];
}
std::vector<RoutingModel::NodeIndex> Depots() const {
return depots_;
}
int32 Capacity() const {
return capacity_;
}
int64 Demand(RoutingModel::NodeIndex i) const {
return demands_[i.value()];
}
bool HasDimensionTwo() const {
return two_dimension_;
}
TSPLIB_NODE_COORD_TYPE_TYPES_enum NodeCoordinateType() const {
return node_coord_type_;
}
TSPLIB_DISPLAY_DATA_TYPE_TYPES_enum DisplayDataType() const {
return display_data_type_;
}
TSPLIB_EDGE_WEIGHT_TYPES_enum EdgeWeightType() const {
return edge_weight_type_;
}
TSPLIB_EDGE_WEIGHT_FORMAT_TYPES_enum EdgeWeightTypeFormat() const {
return edge_weight_format_type_;
}
solution_iterator solution_begin() {
return tsp_sol_.begin();
}
const_solution_iterator solution_begin() const {
return tsp_sol_.begin();
}
solution_iterator solution_end() {
return tsp_sol_.end();
}
const_solution_iterator solution_end() const {
return tsp_sol_.end();
}
protected:
void LoadInstance(const std::string& filename);
// Helper function
int64& SetMatrix(int i, int j) {
return distances_.Cost(RoutingModel::NodeIndex(i), RoutingModel::NodeIndex(j));
}
private:
void ProcessNewLine(char* const line);
std::vector<RoutingModel::NodeIndex> depots_;
int line_number_;
bool visualizable_;
bool two_dimension_;
bool symmetric_;
bool need_to_compute_distances_;
TSPLIB_STATES_enum tsplib_state_;
bool tsplib_state_unknown_;
TSPLIB_PROBLEM_TYPES_enum type_;
std::string name_;
std::string comment_;
int32 capacity_;
TSPLIB_EDGE_WEIGHT_TYPES_enum edge_weight_type_;
TSPLIB_EDGE_WEIGHT_FORMAT_TYPES_enum edge_weight_format_type_;
TSPLIB_EDGE_DATA_FORMAT_TYPES_enum edge_data_format_type_;
TSPLIB_NODE_COORD_TYPE_TYPES_enum node_coord_type_;
TSPLIB_DISPLAY_DATA_TYPE_TYPES_enum display_data_type_;
TWOD_distance_function TWOD_dist_fun_;
THREED_distance_function THREED_dist_fun_;
std::vector<RoutingModel::NodeIndex> tsp_sol_;
std::vector<int64> demands_;
};
void TSPLIBReader::LoadInstance(const std::string& filename)
{
FileLineReader reader(filename.c_str());
reader.set_line_callback(NewPermanentCallback(
this,
&TSPLIBReader::ProcessNewLine));
reader.Reload();
if (!reader.loaded_successfully()) {
LOG(FATAL) << "Could not open TSPLIB instance file: " << filename;
}
}
void TSPLIBReader::ProcessNewLine(char*const line) {
++line_number_;
VLOG(2) << "Line " << line_number_ << ": " << line;
// Must always be -1 outside a section
static int32 nodes_nbr = -1;
static bool read_matrix_done = false;
static const char kWordDelimiters[] = " :";
std::vector<std::string> words;
words = strings::Split(line, kWordDelimiters, strings::SkipEmpty());
// Empty lines
if (words.size() == 0) {
return;
}
// FIND TSPLIB KEYWORD
if (tsplib_state_unknown_) {
bool keyword_found = false;
//read_matrix_done = false;
tsplib_state_ = FindEnumKeyword(TSPLIB_STATES_KEYWORDS, words[0], TSPLIB_STATES_COUNT);
if (tsplib_state_ != TSPLIB_STATES_UNDEFINED) {
keyword_found = true;
}
// separate test because "EOF" is sometimes redefined
if (words[0] == kTSPLIBEndFileDelimiter) {
return;
}
if (!keyword_found) {
PrintFatalLog("Unknown keyword", words[0], line_number_);
}
tsplib_state_unknown_ = false;
}
// SWITCH FOLLOWING TSPLIB KEYWORD
switch (tsplib_state_) {
case NAME: {
name_ = words[1];
tsplib_state_unknown_ = true;
break;
}
case TYPE: {
type_ = FindOrDieEnumKeyword(TSPLIB_PROBLEM_TYPES_KEYWORDS, words[1], TSPLIB_PROBLEM_TYPES_COUNT, "Unknown problem type", line_number_);
tsplib_state_unknown_ = true;
break;
}
case COMMENT: {
if (words.size() > 1) {
for (int index = 1; index < words.size(); ++index) {
comment_ = StrCat(comment_, words[index] + " ");
}
}
tsplib_state_unknown_ = true;
break;
}
case DIMENSION: {
int32 size = atoi32(words[1]);
CreateRoutingData(size);
tsplib_state_unknown_ = true;
break;
}
case CAPACITY: {
capacity_ = atoi32(words[1]);
tsplib_state_unknown_ = true;
break;
}
case DEPOT_SECTION: {
if (nodes_nbr == -1) {
// titel
++nodes_nbr;
break;
}
if (atoi32(words[0]) == -1) {
nodes_nbr = -1;
tsplib_state_unknown_ = true;
break;
}
depots_.push_back(RoutingModel::NodeIndex(atoi32(words[1]) - 1));
++nodes_nbr;
break;
}
case DEMAND_SECTION: {
if (nodes_nbr == -1) {
// titel
demands_.resize(Size());
++nodes_nbr;
break;
}
if (nodes_nbr == Size() - 1) {
tsplib_state_unknown_ = true;
nodes_nbr = -1;
break;
}
CHECK_EQ(words.size(), 2) << "Demand section should only contain node_id and demand on line " << line_number_;
CHECK_LE(atoi32(words[0]), Size()) << "Node with node_id bigger than size of instance on line " << line_number_;
demands_[atoi32(words[0]) - 1] = atoi32(words[1]);
++nodes_nbr;
break;
}
case TOUR_SECTION: {
if (nodes_nbr == -1) {
// titel
tsp_sol_.resize(Size());
++nodes_nbr;
break;
}
if (nodes_nbr == Size()) {
CHECK_EQ(atoi32(words[0]), -1) << "Tour is supposed to end with -1.";
tsplib_state_unknown_ = true;
break;
}
RoutingModel::NodeIndex node(atoi32(words[0]) - 1);
tsp_sol_[nodes_nbr] = node;
++nodes_nbr;
break;
}
case EDGE_WEIGHT_TYPE: {
edge_weight_type_ = FindOrDieEnumKeyword(TSPLIB_EDGE_WEIGHT_TYPES_KEYWORDS,
words[1],
TSPLIB_EDGE_WEIGHT_TYPES_COUNT,
"Unknown edge weight type",
line_number_);
// Do we need to compute the distances?
switch (edge_weight_type_) {
case EXPLICIT: {
need_to_compute_distances_ = false;
break;
}
case EUC_2D: {
need_to_compute_distances_ = true;
two_dimension_ = true;
symmetric_ = true;
visualizable_ = true;
TWOD_dist_fun_ = &TSPLIBDistanceFunctions::TWOD_euc_2d_distance;
break;
}
case EUC_3D: {
need_to_compute_distances_ = true;
two_dimension_ = false;
symmetric_ = true;
visualizable_ = true;
THREED_dist_fun_ = &TSPLIBDistanceFunctions::THREED_euc_3d_distance;
break;
}
case MAX_2D: {
need_to_compute_distances_ = true;
two_dimension_ = true;
symmetric_ = true;
visualizable_ = true;
TWOD_dist_fun_ = &TSPLIBDistanceFunctions::TWOD_max_2d_distance;
break;
}
case MAX_3D: {
need_to_compute_distances_ = true;
two_dimension_ = false;
symmetric_ = true;
visualizable_ = true;
THREED_dist_fun_ = &TSPLIBDistanceFunctions::THREED_max_3d_distance;
break;
}
case MAN_2D: {
need_to_compute_distances_ = true;
two_dimension_ = true;
symmetric_ = true;
visualizable_ = true;
TWOD_dist_fun_ = &TSPLIBDistanceFunctions::TWOD_man_2d_distance;
break;
}
case MAN_3D: {
need_to_compute_distances_ = true;
two_dimension_ = false;
symmetric_ = true;
visualizable_ = true;
THREED_dist_fun_ = &TSPLIBDistanceFunctions::THREED_man_3d_distance;
break;
}
case CEIL_2D: {
need_to_compute_distances_ = true;
two_dimension_ = true;
symmetric_ = true;
visualizable_ = true;
TWOD_dist_fun_ = &TSPLIBDistanceFunctions::TWOD_ceil_2d_distance;
break;
}
case CEIL_3D: {
need_to_compute_distances_ = true;
two_dimension_ = false;
symmetric_ = true;
visualizable_ = true;
THREED_dist_fun_ = &TSPLIBDistanceFunctions::THREED_ceil_3d_distance;
break;
}
case GEO:
case GEOM: {
need_to_compute_distances_ = true;
two_dimension_ = true;
symmetric_ = true;
visualizable_ = true;
TWOD_dist_fun_ = &TSPLIBDistanceFunctions::TWOD_geo_distance;
break;
}
case ATT: {
need_to_compute_distances_ = true;
two_dimension_ = true;
symmetric_ = true;
visualizable_ = true;
TWOD_dist_fun_ = &TSPLIBDistanceFunctions::TWOD_att_distance;
break;
}
} // switch (edge_weight_type_)
tsplib_state_unknown_ = true;
break;
}
case EDGE_WEIGHT_FORMAT: {
edge_weight_format_type_ = FindOrDieEnumKeyword(TSPLIB_EDGE_WEIGHT_FORMAT_TYPES_KEYWORDS,
words[1],
TSPLIB_EDGE_WEIGHT_FORMAT_TYPES_COUNT,
"Unknown edge weight format type",
line_number_);
tsplib_state_unknown_ = true;
break;
}
case EDGE_DATA_FORMAT: {
edge_data_format_type_ = FindOrDieEnumKeyword(TSPLIB_EDGE_DATA_FORMAT_TYPES_KEYWORDS,
words[1],
TSPLIB_EDGE_DATA_FORMAT_TYPES_COUNT,
"Unknown edge data format type",
line_number_);
tsplib_state_unknown_ = true;
break;
}
case NODE_COORD_TYPE: {
node_coord_type_ = FindOrDieEnumKeyword(TSPLIB_NODE_COORD_TYPE_TYPES_KEYWORDS,
words[1],
TSPLIB_NODE_COORD_TYPE_TYPES_COUNT,
"Unknown node coord format type",
line_number_);
tsplib_state_unknown_ = true;
break;
}
case DISPLAY_DATA_TYPE: {
display_data_type_ = FindOrDieEnumKeyword(TSPLIB_DISPLAY_DATA_TYPE_TYPES_KEYWORDS,
words[1],
TSPLIB_DISPLAY_DATA_TYPE_TYPES_COUNT,
"Unknown display data format type",
line_number_);
switch (display_data_type_) {
case NO_DISPLAY:
break;
case COORD_DISPLAY:
case TWOD_DISPLAY:
visualizable_ = true;
break;
}
tsplib_state_unknown_ = true;
break;
}
case NODE_COORD_SECTION: {
if (nodes_nbr == -1) {
++nodes_nbr;
visualizable_ = true;
break;
}
++nodes_nbr;
switch (node_coord_type_) {
case TWOD_COORDS: {
CHECK_EQ(words.size(), 3) << "Node coord data not conform on line " << line_number_;
CHECK_LE(atoi32(words[0].c_str()), size_) << "Unknown node number " << atoi32(words[0].c_str()) << " on line " << line_number_;
coordinates_[atoi32(words[0].c_str()) -1] = Point(atof(words[1].c_str()), atof(words[2].c_str()));
break;
}
case THREED_COORDS: {
CHECK_EQ(words.size(), 4) << "Node coord data not conform on line " << line_number_;
CHECK_LE(atoi32(words[0].c_str()), size_) << "Unknown node number " << atoi32(words[0].c_str()) << " on line " << line_number_;
coordinates_[atoi32(words[0].c_str()) -1] = Point(atof(words[1].c_str()), atof(words[2].c_str()), atof(words[3].c_str()));
break;
}
case NO_COORDS: {
LOG(FATAL) << "Coordinate is non existent but there is a node coordinate section???";
break;
}
default:
LOG(FATAL) << "Coordinate type is not defined.";
}
if (nodes_nbr == size_) {
SetHasCoordinates();
// Compute distance if needed
TSPLIBDistanceFunctions tsplib_dist_function(node_coord_type_, edge_weight_type_);
int64 dist = 0;
if (need_to_compute_distances_) {
LG << "Computing distance matrix...";
// TWO DIMENSION
if (two_dimension_) {
// SYMMETRIC
if (symmetric_) {
for (int i = 0; i < size_; ++i) {
for (int j = i + 1; j < size_; ++j ) {
dist = tsplib_dist_function.TWOD_distance(coordinates_[i], coordinates_[j]);
SetMatrix(i,j) = dist;
SetMatrix(j,i) = dist;
}
}
for (int i = 0; i < size_; ++i) {
SetMatrix(i,i) = 0LL;
}
// NOT SYMMETRIC
} else {
for (int i = 0; i < size_; ++i) {
for (int j = 0; j < size_; ++j ) {
if (i == j) {
SetMatrix(i,j) = 0LL;
} else {
SetMatrix(i,j) = dist;
}
}
}
}
// THREE DIMENSION
} else {
}
LG << "Computing distance matrix... Done!";
} // if (tsplib_dist_function.NeedToComputeDistances())
tsplib_state_unknown_ = true;
nodes_nbr = -1;
}
break;
} // case NODE_COORD_SECTION:
case DISPLAY_DATA_SECTION: {
if (nodes_nbr == -1) {
++nodes_nbr;
break;
}
if (display_data_type_ == TWOD_DISPLAY) {
CHECK_EQ(words.size(), 3) << "Display data not conform on line " << line_number_;
CHECK_LE(atoi32(words[0].c_str()), size_) << "Unknown node number " << atoi32(words[0].c_str()) << " on line " << line_number_;
display_coords_[atoi32(words[0].c_str()) -1] = Point(atof(words[1].c_str()), atof(words[2].c_str()));
++nodes_nbr;
if (nodes_nbr == size_) {
SetHasDisplayCoordinates();
tsplib_state_unknown_ = true;
nodes_nbr = -1;
}
} else {
tsplib_state_unknown_ = true;
nodes_nbr = -1;
}
break;
}
case EDGE_DATA_SECTION: {
if (words.size() == 1 && words[0] == "-1") {
// complete matrix
//TO DO
read_matrix_done = true;
tsplib_state_unknown_ = true;
break;
}
switch(edge_data_format_type_) {
case EDGE_LIST: {
CHECK_EQ(words.size(), 2) << "Edge not well defined on line " << line_number_;
break;
}
case ADJ_LIST: {
break;
}
}
}
case EDGE_WEIGHT_SECTION: {
if (nodes_nbr == -1) {
++nodes_nbr;
read_matrix_done = false;
break;
}
switch (edge_weight_format_type_) {
case FULL_MATRIX: {
CHECK_EQ(words.size(),size_) << "Matrix not full on line " << line_number_;
for (int index = 0; index < size_; ++index) {
int64 dist = atoi64(words[index].c_str());
SetMatrix(nodes_nbr, index) = dist;
}
if (nodes_nbr == size_ - 1) {
read_matrix_done = true;
}
break;
}
case UPPER_ROW: {
CHECK_EQ(words.size(), size_ - nodes_nbr - 1) << " Wrong number of tokens on line " << line_number_;
SetMatrix(nodes_nbr, nodes_nbr) = 0LL;
for (int index = 0; index < size_ - nodes_nbr - 1; ++index) {
int64 dist = atoi64(words[index].c_str());
SetMatrix(nodes_nbr, index + nodes_nbr + 1) = dist;
SetMatrix(index + nodes_nbr + 1, nodes_nbr) = dist;
}
if (nodes_nbr == size_ - 2) {
read_matrix_done = true;
}
break;
}
case UPPER_DIAG_ROW: { // BUGGY?
CHECK_EQ(words.size(), size_ - nodes_nbr - 1);
for (int index = 0; index < size_ - nodes_nbr ; ++index) {
int64 dist = atoi64(words[index].c_str());
std::cout << dist << " ";
SetMatrix(nodes_nbr, index + nodes_nbr) = dist;
SetMatrix(index + nodes_nbr , nodes_nbr) = dist;
}
std::cout << std::endl;
if (nodes_nbr == size_ - 2) {
read_matrix_done = true;
}
break;
}
case LOWER_ROW: { // TO BE CHECKED
CHECK_EQ(words.size(), nodes_nbr + 1);
SetMatrix(nodes_nbr, nodes_nbr) = 0LL;
for (int index = 0; index < nodes_nbr + 1; ++index) {
int64 dist = atoi64(words[index].c_str());
std::cout << dist << " ";
SetMatrix(nodes_nbr, index) = dist;
SetMatrix(index , nodes_nbr) = dist;
}
std::cout << std::endl;
if (nodes_nbr == size_ - 2) {
read_matrix_done = true;
break;
}
break;
}
} // switch (edge_weight_format_type_)
if (read_matrix_done) {
tsplib_state_unknown_ = true;
nodes_nbr = -1;
break;
}
++nodes_nbr;
} // case EDGE_WEIGHT_SECTION:
} // switch
} // ProcessNewLine()
} // namespace operations_research
#endif // OR_TOOLS_TUTORIALS_CPLUSPLUS_TSPLIB_READER_H