Question

I'm trying to accomplish the following: Have a function computeTspTour(size, start, distance) that gives me an approximation to the shortest tour through size many vertices, starting at start. Here, distance is a function object that takes two indexes and returns the distance between them.

I would like to utilize boost::graph's metric_tsp_approx. For this, I need a complete graph of cardinality size, so I'd like to use an implicitly defined graph for this to avoid creating a useless trivial huge graph structure.

It all appears to work fine, but my problem is that metric_tsp_approx at some point uses dijkstra_shortest_paths, which defines a ColorMap. This leads to the following two problems:

/usr/include/boost/graph/dijkstra_shortest_paths.hpp:373:60: error: no type named 'value_type' in 'struct boost::property_traits<boost::bgl_named_params<boost::detail::_project2nd<double, double>, boost::distance_combine_t, boost::bgl_named_params<std::less<double>, boost::distance_compare_t, boost::bgl_named_params<boost::iterator_property_map<__gnu_cxx::__normal_iterator<long unsigned int*, std::vector<long unsigned int> >, boost::typed_identity_property_map<long unsigned int>, long unsigned int, long unsigned int&>, boost::vertex_predecessor_t, boost::bgl_named_params<EdgeWeightMap<double>, boost::edge_weight_t, boost::bgl_named_params<boost::typed_identity_property_map<long unsigned int>, boost::vertex_index_t, boost::bgl_named_params<long unsigned int, boost::root_vertex_t, boost::no_property> > > > > > >'
typedef typename property_traits<ColorMap>::value_type ColorValue;
                                                       ^

/usr/include/boost/graph/dijkstra_shortest_paths.hpp:374:38: error: no type named 'value_type' in 'struct boost::property_traits<boost::bgl_named_params<boost::detail::_project2nd<double, double>, boost::distance_combine_t, boost::bgl_named_params<std::less<double>, boost::distance_compare_t, boost::bgl_named_params<boost::iterator_property_map<__gnu_cxx::__normal_iterator<long unsigned int*, std::vector<long unsigned int> >, boost::typed_identity_property_map<long unsigned int>, long unsigned int, long unsigned int&>, boost::vertex_predecessor_t, boost::bgl_named_params<EdgeWeightMap<double>, boost::edge_weight_t, boost::bgl_named_params<boost::typed_identity_property_map<long unsigned int>, boost::vertex_index_t, boost::bgl_named_params<long unsigned int, boost::root_vertex_t, boost::no_property> > > > > > >'
typedef color_traits<ColorValue> Color;
                                 ^

However, I do not see how I could fix the traits of the ColorMap from where I am, creating a color property map on my own doesn't do any good.

The code that I use to create the implicit graph and running the tsp_metric_approx on it is the following. I apologize for its length, I hope it's straightforward. What it does is set up a class CompleteGraph, having one template parameter F that specifies the return type of the distance function. This class has the necessary iterators to be a VertexListGraph and an IncidenceGraph, so that tsp_metric_approx can run on it.

#include <algorithm>
#include <functional>
#include <iostream>
#include <iterator>
#include <vector>

#include <boost/iterator/iterator_facade.hpp>
#include <boost/graph/metric_tsp_approx.hpp>

using namespace boost;

typedef std::size_t VertexDescriptor;
typedef std::pair<VertexDescriptor, VertexDescriptor> EdgeDescriptor;

class VertexIterator : public boost::iterator_facade<VertexIterator, VertexDescriptor const, boost::bidirectional_traversal_tag>
{
    public:
        //! Default constructor
        VertexIterator() : pos_(0) {}

        //! Constructor setting the position
        explicit VertexIterator(VertexDescriptor pos) : pos_(pos) {}

        //! Dereference the iterator
        VertexDescriptor const& dereference() const { return pos_; }

        //! Check for equality
        bool equal(VertexIterator const& other) const { return pos_ == other.pos_; }

        //! Increment
        void increment() { ++pos_; }

        //! Decrement
        void decrement() { --pos_; }

    private:
        //! Grant access to boost::iterator_facade
        friend class boost::iterator_core_access;

        //! The current position
        VertexDescriptor pos_ = 0;
};

class OutEdgeIterator : public boost::iterator_facade<OutEdgeIterator, EdgeDescriptor const, boost::bidirectional_traversal_tag>
{
    public:
        //! Constructor setting the source vertex
        explicit OutEdgeIterator(VertexDescriptor source) { const std::size_t target = source == 0 ? 1 : 0; pos_ = EdgeDescriptor(source, target); }

        //! Constructor setting the source vertex and the target
        explicit OutEdgeIterator(VertexDescriptor source, VertexDescriptor target) : pos_(source, target) {}

        //! Dereference the iterator
        EdgeDescriptor const& dereference() const { return pos_; }

        //! Check for equality
        bool equal(OutEdgeIterator const& other) const { return pos_ == other.pos_; }

        //! Increment
        void increment() { ++pos_.second; if(pos_.first == pos_.second) { ++pos_.second; } }

        //! Decrement
        void decrement() { --pos_.second; if(pos_.first == pos_.second) { --pos_.second; } }

    private:
        //! Grant access to boost::iterator_facade
        friend class boost::iterator_core_access;

        //! The current edge
        EdgeDescriptor pos_ = EdgeDescriptor(0, 1);
};

//! Class representing a complete graph
/*!
 * This class works as a complete graph.
 * It defines a distance property map between any two points by calling the passed distance function.
 * \tparam F The return type of the distance function
 */
template<typename F>
class CompleteGraph
{
    public:
        typedef VertexDescriptor vertex_descriptor;
        typedef EdgeDescriptor edge_descriptor;
        typedef void adjacency_iterator;
        typedef OutEdgeIterator out_edge_iterator;
        typedef void in_edge_iterator;
        typedef void edge_iterator;
        typedef VertexIterator vertex_iterator;
        typedef std::size_t degree_size_type;
        typedef std::size_t vertices_size_type;
        typedef std::size_t edges_size_type;
        typedef undirected_tag directed_category;
        typedef disallow_parallel_edge_tag edge_parallel_category;
        typedef vertex_list_graph_tag traversal_category;

        //! Delete default constructor
        CompleteGraph() = delete;

        //! Constructor from a given size
        /*!
         * If no distance is specified, we default to a constant function returning F(1)
         */
        explicit CompleteGraph(std::size_t size) : size_(size), distance_(returnOne) {}

        //! Constructor from a given size and a distance function of type F
        /*!
         * The constructed graph will have size many vertices.
         * Its distance map will be of the following form: The distance between points i and j is distance(i, j).
         * \param[in] size The size the graph should have
         * \param[in] distance Binary function taking std::size_t arguments and returning the distance between two points
         */
        explicit CompleteGraph(std::size_t size, std::function<F(std::size_t, std::size_t)> const& distance) : size_(size), distance_(distance) {}

        //! Access to size_
        std::size_t size() const { return size_; }

        //! Access to distance_
        std::function<F(std::size_t, std::size_t)> const& distance() const { return distance_; }

    private:
        //! The size of the graph
        std::size_t size_;

        //! The distance function used to find the distance between point i and point j
        std::function<F(std::size_t, std::size_t)> const& distance_;

        //! Distance function that just returns F(1)
        std::function<F(std::size_t, std::size_t)> returnOne = [] (std::size_t, std::size_t) { return F(1); };
};

//! Weigth map for all edges
template<typename F>
class EdgeWeightMap
{
    public:
        typedef F value_type;
        typedef F reference_type;
        typedef reference_type reference;
        typedef EdgeDescriptor key_type;
        typedef readable_property_map_tag category;

        //! Constructor from a distance function
        explicit EdgeWeightMap(std::function<F(std::size_t, std::size_t)> const& distance) : distance_(distance) {}

        //! Operator to dereference the property map
        value_type operator[](key_type key) const { return distance_(key.first, key.second); }

        //! Get function
        friend inline value_type get(EdgeWeightMap<F> const& edgeWeightMap, EdgeWeightMap<F>::key_type const& key) { return edgeWeightMap[key]; }

    private:
        //! The distance function
        std::function<F(std::size_t, std::size_t)> const& distance_;
};

//! Return the number of vertices of a CompleteGraph
template<typename F>
std::size_t num_vertices(CompleteGraph<F> const& g) { return g.size(); }

//! Return a pair allowing iteration over all vertices
template<typename F>
std::pair<VertexIterator, VertexIterator> vertices(CompleteGraph<F> const& g) { return std::make_pair(VertexIterator(0), VertexIterator(g.size())); }

//! Return a pair allowing iteration over all outgoing edges of a vertex
template<typename F>
std::pair<OutEdgeIterator, OutEdgeIterator> out_edges(VertexDescriptor s, CompleteGraph<F> const& g) { return std::make_pair(OutEdgeIterator(s), OutEdgeIterator(s, g.size())); }

//! Return the out-degree which is constant size - 1 for all vertices
template<typename F>
std::size_t out_degree(VertexDescriptor, CompleteGraph<F> const& g) { return g.size() - 1; }

//! Return the source of an edge
template<typename F>
VertexDescriptor source(EdgeDescriptor e, CompleteGraph<F> const&) { return e.first; }

//! Return the target of an edge
template<typename F>
VertexDescriptor target(EdgeDescriptor e, CompleteGraph<F> const&) { return e.second; }

//! Return the index map
template<typename F>
identity_property_map get(vertex_index_t, CompleteGraph<F> const&) { return identity_property_map(); }

//! Return the distance map
template<typename F>
EdgeWeightMap<F> get(edge_weight_t, CompleteGraph<F> const& g) { return EdgeWeightMap<F>(g.distance()); }

//! Wrapper function for automatic template parameter
template<typename F>
CompleteGraph<F> makeCompleteGraph(std::size_t size, std::function<F(std::size_t, std::size_t)> const& distance) { return CompleteGraph<F>(size, distance); }

//! Compute a metric TSP solution through the points supplied
/*!
 * This function finds a solution through n many points whose pairwise distance is given by a function argument.
 * The supplied distance function needs to satisfy the triangle inequality and must be symmetric.
 * \tparam F The type of the return value of distance
 * \param[in] size The number of points through which the TSP tour should be found
 * \param[in] start The index of the point at which to start
 * \param[in] distance A function taking two std::size_t's and returning the distance between point i and point j
 * \return A vector representing the TSP tour
 */
template<typename F>
std::vector<std::size_t> computeTspTour(std::size_t size, std::size_t start, std::function<F(std::size_t, std::size_t)> const& distance)
{
    std::vector<std::size_t> tour;
    const auto completeGraph = makeCompleteGraph(size, distance);
    metric_tsp_approx_tour_from_vertex(completeGraph, start, std::back_inserter(tour));
    return tour;
}

int main()
{
    typedef std::complex<double> Point;

    const std::vector<Point> points{{.0, .0}, {1.0, 2.0}, {1.0, 5.0}, {2.5, 9.2}, {-100.2, 24.1}, {.1, 10.0}};
    const std::function<double(std::size_t, std::size_t)> distance = [&points] (std::size_t i, std::size_t j) { return std::abs(points[i] - points[j]); };

    const auto tour = computeTspTour(points.size(), 0, distance);

    std::cout << "Found TSP tour:\n";
    std::copy(tour.cbegin(), tour.cend(), std::ostream_iterator<char>(std::cout, " "));

    return EXIT_SUCCESS;
}

I'm also happy if someone has an alternative suggestion that is shorter or avoids creating any graph at all, a complete graph does not really carry any information besides the number of its vertices.

Was it helpful?

Solution

DFS and TSP algorithms require a graph to be both "vertex list" AND "incidence graph" (i.e. a graph with access to vertex neighbors).

Your graph has to have something like

 struct traversal_category
        : public virtual boost::vertex_list_graph_tag
        , public virtual boost::adjacency_graph_tag
        , public virtual boost::incidence_graph_tag
    {
    };

     typedef typename boost::adjacency_iterator_generator<CompleteGraph<F>, vertex_descriptor, out_edge_iterator>::type adjacency_iterator;

instead of

 typedef vertex_list_graph_tag traversal_category;
 typedef void adjacency_iterator;

With these changes plus some cosmetic ones your code passes compilation.

Vertex index map is optional, Boost will wrap your code with VertexMap and ColorMap, likely based on unordered_map. It will be less efficient then "identity" or similar custom maps but will work.

Good luck!

OTHER TIPS

Your code for custom "complete" graph seems OK.

The critical component needed by DFS is "vertex index map": essentially a one-to-one correspondence between vertex_descriptor and int, such that each vertex is mapped to a number in the interval [0, num_vertices(g)). For "standard" graphs such mapping is known, and DFS uses some meta-programming to deduce the type of appropriate ColorMap.

In you case, the vertex_descriptor IS an integer within correct interval and the mapping is "identical map". You only have to express it with code similar to the following:

namespace boost{ 
    template<class F>
    struct property_map< CompleteGraph<F>, vertex_index_t >
    {

        typedef identity_property_map type;

        //or more fancier 
        //typedef CompleteGraph<F> graph_t;
        //typedef typed_identity_property_map<typename graph_t::vertex_descriptor> type;

        typedef type const_type;
    };

    //then you define a "get" function:
    template<class F>
    identity_property_map
      get(vertex_index_t, const CompleteGraph<F>& /*g -- not used */) 
    {
       return identity_property_map();
    }
} //namespace boost

It should be enough. If some algorithm requires other "property_maps" for your graph type, you can define them in a similar manner.

Good luck!

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top