334 lines
9.3 KiB
C++
334 lines
9.3 KiB
C++
//=======================================================================
|
|
// Copyright 2008
|
|
// Author: Matyas W Egyhazy
|
|
//
|
|
// Distributed under the Boost Software License, Version 1.0. (See
|
|
// accompanying file LICENSE_1_0.txt or copy at
|
|
// http://www.boost.org/LICENSE_1_0.txt)
|
|
//=======================================================================
|
|
|
|
#include <iostream>
|
|
#include <vector>
|
|
#include <fstream>
|
|
#include <set>
|
|
#include <ctime>
|
|
|
|
#include <boost/assert.hpp>
|
|
#include <boost/lexical_cast.hpp>
|
|
#include <boost/random.hpp>
|
|
#include <boost/timer/timer.hpp>
|
|
#include <boost/integer_traits.hpp>
|
|
#include <boost/graph/adjacency_matrix.hpp>
|
|
#include <boost/graph/adjacency_list.hpp>
|
|
#include <boost/graph/simple_point.hpp>
|
|
#include <boost/graph/metric_tsp_approx.hpp>
|
|
#include <boost/graph/graphviz.hpp>
|
|
|
|
// TODO: Integrate this into the test system a little better. We need to run
|
|
// the test with some kind of input file.
|
|
|
|
template < typename PointType > struct cmpPnt
|
|
{
|
|
bool operator()(const boost::simple_point< PointType >& l,
|
|
const boost::simple_point< PointType >& r) const
|
|
{
|
|
return (l.x > r.x);
|
|
}
|
|
};
|
|
|
|
// add edges to the graph (for each node connect it to all other nodes)
|
|
template < typename VertexListGraph, typename PointContainer,
|
|
typename WeightMap, typename VertexIndexMap >
|
|
void connectAllEuclidean(VertexListGraph& g, const PointContainer& points,
|
|
WeightMap wmap, // Property maps passed by value
|
|
VertexIndexMap vmap, // Property maps passed by value
|
|
int /*sz*/)
|
|
{
|
|
using namespace boost;
|
|
using namespace std;
|
|
typedef typename graph_traits< VertexListGraph >::edge_descriptor Edge;
|
|
typedef typename graph_traits< VertexListGraph >::vertex_iterator VItr;
|
|
|
|
Edge e;
|
|
bool inserted;
|
|
|
|
pair< VItr, VItr > verts(vertices(g));
|
|
for (VItr src(verts.first); src != verts.second; src++)
|
|
{
|
|
for (VItr dest(src); dest != verts.second; dest++)
|
|
{
|
|
if (dest != src)
|
|
{
|
|
double weight(
|
|
sqrt(pow(static_cast< double >(
|
|
points[vmap[*src]].x - points[vmap[*dest]].x),
|
|
2.0)
|
|
+ pow(static_cast< double >(
|
|
points[vmap[*dest]].y - points[vmap[*src]].y),
|
|
2.0)));
|
|
|
|
boost::tie(e, inserted) = add_edge(*src, *dest, g);
|
|
|
|
wmap[e] = weight;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Create a randomly generated point
|
|
// scatter time execution
|
|
void testScalability(unsigned numpts)
|
|
{
|
|
using namespace boost;
|
|
using namespace std;
|
|
|
|
typedef adjacency_matrix< undirectedS, no_property,
|
|
property< edge_weight_t, double, property< edge_index_t, int > > >
|
|
Graph;
|
|
typedef graph_traits< Graph >::vertex_descriptor Vertex;
|
|
typedef property_map< Graph, edge_weight_t >::type WeightMap;
|
|
typedef set< simple_point< double >, cmpPnt< double > > PointSet;
|
|
typedef vector< Vertex > Container;
|
|
|
|
boost::mt19937 rng(std::time(0));
|
|
uniform_real<> range(0.01, (numpts * 2));
|
|
variate_generator< boost::mt19937&, uniform_real<> > pnt_gen(rng, range);
|
|
|
|
PointSet points;
|
|
simple_point< double > pnt;
|
|
|
|
while (points.size() < numpts)
|
|
{
|
|
pnt.x = pnt_gen();
|
|
pnt.y = pnt_gen();
|
|
points.insert(pnt);
|
|
}
|
|
|
|
Graph g(numpts);
|
|
WeightMap weight_map(get(edge_weight, g));
|
|
vector< simple_point< double > > point_vec(points.begin(), points.end());
|
|
|
|
connectAllEuclidean(g, point_vec, weight_map, get(vertex_index, g), numpts);
|
|
|
|
Container c;
|
|
boost::timer::cpu_timer t;
|
|
t.start();
|
|
double len = 0.0;
|
|
|
|
// Run the TSP approx, creating the visitor on the fly.
|
|
metric_tsp_approx(
|
|
g, make_tsp_tour_len_visitor(g, back_inserter(c), len, weight_map));
|
|
|
|
cout << "Number of points: " << num_vertices(g) << endl;
|
|
cout << "Number of edges: " << num_edges(g) << endl;
|
|
cout << "Length of tour: " << len << endl;
|
|
cout << "Elapsed: " << boost::timer::format(t.elapsed()) << endl;
|
|
}
|
|
|
|
template < typename PositionVec > void checkAdjList(PositionVec v)
|
|
{
|
|
using namespace std;
|
|
using namespace boost;
|
|
|
|
typedef adjacency_list< listS, listS, undirectedS > Graph;
|
|
typedef graph_traits< Graph >::vertex_descriptor Vertex;
|
|
typedef graph_traits< Graph >::edge_descriptor Edge;
|
|
typedef vector< Vertex > Container;
|
|
typedef map< Vertex, std::size_t > VertexIndexMap;
|
|
typedef map< Edge, double > EdgeWeightMap;
|
|
typedef associative_property_map< VertexIndexMap > VPropertyMap;
|
|
typedef associative_property_map< EdgeWeightMap > EWeightPropertyMap;
|
|
typedef graph_traits< Graph >::vertex_iterator VItr;
|
|
|
|
Container c;
|
|
EdgeWeightMap w_map;
|
|
VertexIndexMap v_map;
|
|
VPropertyMap v_pmap(v_map);
|
|
EWeightPropertyMap w_pmap(w_map);
|
|
|
|
Graph g(v.size());
|
|
|
|
// create vertex index map
|
|
VItr vi, ve;
|
|
int idx(0);
|
|
for (boost::tie(vi, ve) = vertices(g); vi != ve; ++vi)
|
|
{
|
|
Vertex v(*vi);
|
|
v_pmap[v] = idx;
|
|
idx++;
|
|
}
|
|
|
|
connectAllEuclidean(g, v, w_pmap, v_pmap, v.size());
|
|
|
|
metric_tsp_approx_from_vertex(g, *vertices(g).first, w_pmap, v_pmap,
|
|
tsp_tour_visitor< back_insert_iterator< Container > >(
|
|
back_inserter(c)));
|
|
|
|
cout << "adj_list" << endl;
|
|
for (Container::iterator itr = c.begin(); itr != c.end(); ++itr)
|
|
{
|
|
cout << v_map[*itr] << " ";
|
|
}
|
|
cout << endl << endl;
|
|
|
|
c.clear();
|
|
}
|
|
|
|
static void usage()
|
|
{
|
|
using namespace std;
|
|
cerr << "To run this program properly please place a "
|
|
<< "file called graph.txt" << endl
|
|
<< "into the current working directory." << endl
|
|
<< "Each line of this file should be a coordinate specifying the"
|
|
<< endl
|
|
<< "location of a vertex" << endl
|
|
<< "For example: " << endl
|
|
<< "1,2" << endl
|
|
<< "20,4" << endl
|
|
<< "15,7" << endl
|
|
<< endl;
|
|
}
|
|
|
|
int main(int argc, char* argv[])
|
|
{
|
|
using namespace boost;
|
|
using namespace std;
|
|
|
|
typedef vector< simple_point< double > > PositionVec;
|
|
typedef adjacency_matrix< undirectedS, no_property,
|
|
property< edge_weight_t, double > >
|
|
Graph;
|
|
typedef graph_traits< Graph >::vertex_descriptor Vertex;
|
|
typedef vector< Vertex > Container;
|
|
typedef property_map< Graph, edge_weight_t >::type WeightMap;
|
|
typedef property_map< Graph, vertex_index_t >::type VertexMap;
|
|
|
|
// Make sure that the the we can parse the given file.
|
|
if (argc < 2)
|
|
{
|
|
usage();
|
|
// return -1;
|
|
return 0;
|
|
}
|
|
|
|
// Open the graph file, failing if one isn't given on the command line.
|
|
ifstream fin(argv[1]);
|
|
if (!fin)
|
|
{
|
|
usage();
|
|
// return -1;
|
|
return 0;
|
|
}
|
|
|
|
string line;
|
|
PositionVec position_vec;
|
|
|
|
int n(0);
|
|
while (getline(fin, line))
|
|
{
|
|
simple_point< double > vertex;
|
|
|
|
size_t idx(line.find(","));
|
|
string xStr(line.substr(0, idx));
|
|
string yStr(line.substr(idx + 1, line.size() - idx));
|
|
|
|
vertex.x = lexical_cast< double >(xStr);
|
|
vertex.y = lexical_cast< double >(yStr);
|
|
|
|
position_vec.push_back(vertex);
|
|
n++;
|
|
}
|
|
|
|
fin.close();
|
|
|
|
Container c;
|
|
Graph g(position_vec.size());
|
|
WeightMap weight_map(get(edge_weight, g));
|
|
VertexMap v_map = get(vertex_index, g);
|
|
|
|
connectAllEuclidean(g, position_vec, weight_map, v_map, n);
|
|
|
|
metric_tsp_approx_tour(g, back_inserter(c));
|
|
|
|
for (vector< Vertex >::iterator itr = c.begin(); itr != c.end(); ++itr)
|
|
{
|
|
cout << *itr << " ";
|
|
}
|
|
cout << endl << endl;
|
|
|
|
c.clear();
|
|
|
|
checkAdjList(position_vec);
|
|
|
|
metric_tsp_approx_from_vertex(g, *vertices(g).first, get(edge_weight, g),
|
|
get(vertex_index, g),
|
|
tsp_tour_visitor< back_insert_iterator< vector< Vertex > > >(
|
|
back_inserter(c)));
|
|
|
|
for (vector< Vertex >::iterator itr = c.begin(); itr != c.end(); ++itr)
|
|
{
|
|
cout << *itr << " ";
|
|
}
|
|
cout << endl << endl;
|
|
|
|
c.clear();
|
|
|
|
double len(0.0);
|
|
try
|
|
{
|
|
metric_tsp_approx(
|
|
g, make_tsp_tour_len_visitor(g, back_inserter(c), len, weight_map));
|
|
}
|
|
catch (const bad_graph& e)
|
|
{
|
|
cerr << "bad_graph: " << e.what() << endl;
|
|
return -1;
|
|
}
|
|
|
|
cout << "Number of points: " << num_vertices(g) << endl;
|
|
cout << "Number of edges: " << num_edges(g) << endl;
|
|
cout << "Length of Tour: " << len << endl;
|
|
|
|
int cnt(0);
|
|
pair< Vertex, Vertex > triangleEdge;
|
|
for (vector< Vertex >::iterator itr = c.begin(); itr != c.end();
|
|
++itr, ++cnt)
|
|
{
|
|
cout << *itr << " ";
|
|
|
|
if (cnt == 2)
|
|
{
|
|
triangleEdge.first = *itr;
|
|
}
|
|
if (cnt == 3)
|
|
{
|
|
triangleEdge.second = *itr;
|
|
}
|
|
}
|
|
cout << endl << endl;
|
|
c.clear();
|
|
|
|
testScalability(1000);
|
|
|
|
// if the graph is not fully connected then some of the
|
|
// assumed triangle-inequality edges may not exist
|
|
remove_edge(edge(triangleEdge.first, triangleEdge.second, g).first, g);
|
|
|
|
// Make sure that we can actually trap incomplete graphs.
|
|
bool caught = false;
|
|
try
|
|
{
|
|
double len = 0.0;
|
|
metric_tsp_approx(
|
|
g, make_tsp_tour_len_visitor(g, back_inserter(c), len, weight_map));
|
|
}
|
|
catch (const bad_graph& e)
|
|
{
|
|
caught = true;
|
|
}
|
|
BOOST_ASSERT(caught);
|
|
|
|
return 0;
|
|
}
|