diff --git a/include/boost/graph/dijkstra_shortest_paths.hpp b/include/boost/graph/dijkstra_shortest_paths.hpp index 10e40f82..eb6bcbd7 100644 --- a/include/boost/graph/dijkstra_shortest_paths.hpp +++ b/include/boost/graph/dijkstra_shortest_paths.hpp @@ -53,7 +53,7 @@ namespace boost { * @param old_distance the previous distance to @p vertex */ template - inline void + inline void dijkstra_queue_update(Buffer& Q, Vertex vertex, DistanceType old_distance) { (void)old_distance; @@ -111,107 +111,6 @@ namespace boost { typedef dijkstra_visitor<> default_dijkstra_visitor; namespace detail { - - template - struct dijkstra_bfs_visitor - { - typedef typename property_traits::value_type D; - typedef typename property_traits::value_type W; - - dijkstra_bfs_visitor(UniformCostVisitor vis, UpdatableQueue& Q, - WeightMap w, PredecessorMap p, DistanceMap d, - BinaryFunction combine, BinaryPredicate compare, - D zero) - : m_vis(vis), m_Q(Q), m_weight(w), m_predecessor(p), m_distance(d), - m_combine(combine), m_compare(compare), m_zero(zero) { } - - template - void tree_edge(Edge e, Graph& g) { - bool decreased = relax(e, g, m_weight, m_predecessor, m_distance, - m_combine, m_compare); - if (decreased) - m_vis.edge_relaxed(e, g); - else - m_vis.edge_not_relaxed(e, g); - } - template - void gray_target(Edge e, Graph& g) { - D old_distance = get(m_distance, target(e, g)); - - bool decreased = relax(e, g, m_weight, m_predecessor, m_distance, - m_combine, m_compare); - if (decreased) { - dijkstra_queue_update(m_Q, target(e, g), old_distance); - m_vis.edge_relaxed(e, g); - } else - m_vis.edge_not_relaxed(e, g); - } - - template - void initialize_vertex(Vertex u, Graph& g) - { m_vis.initialize_vertex(u, g); } - template - void non_tree_edge(Edge, Graph&) { } - template - void discover_vertex(Vertex u, Graph& g) { m_vis.discover_vertex(u, g); } - template - void examine_vertex(Vertex u, Graph& g) { m_vis.examine_vertex(u, g); } - template - void examine_edge(Edge e, Graph& g) { - // Test for negative-weight edges: - // - // Reasons that other comparisons do not work: - // - // m_compare(e_weight, D(0)): - // m_compare only needs to work on distances, not weights, and those - // types do not need to be the same (bug 8398, - // https://svn.boost.org/trac/boost/ticket/8398). - // m_compare(m_combine(source_dist, e_weight), source_dist): - // if m_combine is project2nd (as in prim_minimum_spanning_tree), - // this test will claim that the edge weight is negative whenever - // the edge weight is less than source_dist, even if both of those - // are positive (bug 9012, - // https://svn.boost.org/trac/boost/ticket/9012). - // m_compare(m_combine(e_weight, source_dist), source_dist): - // would fix project2nd issue, but documentation only requires that - // m_combine be able to take a distance and a weight (in that order) - // and return a distance. - - // W e_weight = get(m_weight, e); - // sd_plus_ew = source_dist + e_weight. - // D sd_plus_ew = m_combine(source_dist, e_weight); - // sd_plus_2ew = source_dist + 2 * e_weight. - // D sd_plus_2ew = m_combine(sd_plus_ew, e_weight); - // The test here is equivalent to e_weight < 0 if m_combine has a - // cancellation law, but always returns false when m_combine is a - // projection operator. - if (m_compare(m_combine(m_zero, get(m_weight, e)), m_zero)) - boost::throw_exception(negative_edge()); - // End of test for negative-weight edges. - - m_vis.examine_edge(e, g); - - } - template - void black_target(Edge, Graph&) { } - template - void finish_vertex(Vertex u, Graph& g) { m_vis.finish_vertex(u, g); } - - UniformCostVisitor m_vis; - UpdatableQueue& m_Q; - WeightMap m_weight; - PredecessorMap m_predecessor; - DistanceMap m_distance; - BinaryFunction m_combine; - BinaryPredicate m_compare; - D m_zero; - }; - - } // namespace detail - - namespace detail { template struct vertex_property_map_generator_helper {}; @@ -326,7 +225,6 @@ namespace boost { vis); } - // Call breadth first search template )); + typedef graph_traits GTraits; + typedef typename GTraits::vertex_descriptor Vertex; + BOOST_CONCEPT_ASSERT(( ReadWritePropertyMapConcept )); + typedef typename property_traits::value_type ColorValue; + typedef color_traits Color; + typename GTraits::out_edge_iterator ei, ei_end; typedef indirect_cmp IndirectCmp; IndirectCmp icmp(distance, compare); - typedef typename graph_traits::vertex_descriptor Vertex; - #ifdef BOOST_GRAPH_DIJKSTRA_TESTING - if (!dijkstra_relaxed_heap) { + //TODO why is that needed? + /*if (!dijkstra_relaxed_heap) { typedef mutable_queue, IndirectCmp, IndexMap> MutableQueue; @@ -357,7 +261,7 @@ namespace boost { breadth_first_visit(g, s_begin, s_end, Q, bfs_vis, color); return; - } + }*/ #endif // BOOST_GRAPH_DIJKSTRA_TESTING #ifdef BOOST_GRAPH_DIJKSTRA_USE_RELAXED_HEAP @@ -376,11 +280,69 @@ namespace boost { MutableQueue Q(distance, index_in_heap, compare); #endif // Relaxed heap - detail::dijkstra_bfs_visitor - bfs_vis(vis, Q, weight, predecessor, distance, combine, compare, zero); - - breadth_first_visit(g, s_begin, s_end, Q, bfs_vis, color); + for (; s_begin != s_end; ++s_begin) { + Vertex s = *s_begin; + put(color, s, Color::gray()); + vis.discover_vertex(s, g); + Q.push(s); + } + while (! Q.empty()) { + Vertex u = Q.top(); Q.pop(); + vis.examine_vertex(u, g); + for (boost::tie(ei, ei_end) = out_edges(u, g); ei != ei_end; ++ei) { + Vertex v = target(*ei, g); + // Test for negative-weight edges: + // + // Reasons that other comparisons do not work: + // + // compare(e_weight, D(0)): + // compare only needs to work on distances, not weights, and those + // types do not need to be the same (bug 8398, + // https://svn.boost.org/trac/boost/ticket/8398). + // compare(combine(source_dist, e_weight), source_dist): + // if combine is project2nd (as in prim_minimum_spanning_tree), + // this test will claim that the edge weight is negative whenever + // the edge weight is less than source_dist, even if both of those + // are positive (bug 9012, + // https://svn.boost.org/trac/boost/ticket/9012). + // compare(combine(e_weight, source_dist), source_dist): + // would fix project2nd issue, but documentation only requires that + // combine be able to take a distance and a weight (in that order) + // and return a distance. + // + // W e_weight = get(weight, e); + // sd_plus_ew = source_dist + e_weight. + // D sd_plus_ew = combine(source_dist, e_weight); + // sd_plus_2ew = source_dist + 2 * e_weight. + // D sd_plus_2ew = combine(sd_plus_ew, e_weight); + // The test here is equivalent to e_weight < 0 if combine has a + // cancellation law, but always returns false when combine is a + // projection operator. + if (compare(combine(zero, get(weight, *ei)), zero)) + boost::throw_exception(negative_edge()); + // End of test for negative-weight edges. + vis.examine_edge(*ei, g); + bool decreased = relax(*ei, g, weight, predecessor, distance, + combine, compare); + if(decreased) { + ColorValue v_color = get(color, v); + vis.edge_relaxed(*ei, g); + if (v_color == Color::white()) { + put(color, v, Color::gray()); + vis.discover_vertex(v, g); + Q.push(v); + } else { + if (v_color == Color::gray()) { + Q.update(v); + } + } + } else { + vis.edge_not_relaxed(*ei, g); + } + } // end for + put(color, u, Color::black()); + vis.finish_vertex(u, g); + } // end while } // Call breadth first search @@ -406,7 +368,7 @@ namespace boost { template inline void dijkstra_shortest_paths @@ -429,7 +391,7 @@ namespace boost { template inline void dijkstra_shortest_paths diff --git a/test/non_trivial_distance_map.cpp b/test/non_trivial_distance_map.cpp new file mode 100644 index 00000000..ae370205 --- /dev/null +++ b/test/non_trivial_distance_map.cpp @@ -0,0 +1,86 @@ +//======================================================================= +// Copyright 2015 University of Warsaw. +// Authors: Piotr Wygocki +// +// 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) +//======================================================================= +#define BOOST_TEST_DYN_LINK +#define BOOST_TEST_MODULE non_trivial_distance_map_test + +#include + +#include +#include + + +typedef boost::property EdgeProp; +typedef boost::adjacency_list< +boost::vecS, boost::vecS, boost::undirectedS, + boost::property, EdgeProp> Graph; +typedef std::pair Edge; +typedef boost::property_map::const_type idx_type; + +template +class examined_recorder : boost::base_visitor< examined_recorder > { + + //! Stores distances to the closest vertex in a particular layer + ExaminedMap m_examined_map; + +public: + typedef Tag event_filter; + + //! Constructor + explicit examined_recorder(ExaminedMap const examined_map) : m_examined_map(examined_map) {} + + //! Copies examined value from precdessor + template + void operator()(Vertex const v, Graph const &g) const { + put(m_examined_map, v, 1); + } + +}; + + +template + examined_recorder + make_examined_recorder(ExaminedMap examined_map, Tag) { + return examined_recorder(examined_map); + } + + +BOOST_AUTO_TEST_CASE(non_trivial_distance_map_test) { + enum nodes { A, B, C, D, E, num_nodes}; + Edge edge_array[] = { Edge(A, B), Edge(B, C), Edge(C, D), Edge(D, E)}; + int const weights[] = {1, 1, 1, 1}; + std::vector distance(num_nodes, 1); + distance[0] = 0; distance[1] = 100; + int const num_arcs = sizeof(edge_array) / sizeof(Edge); + + Graph g(edge_array, edge_array + num_arcs, weights, num_nodes); + idx_type idx = get(boost::vertex_index, g); + std::vector examined(num_vertices(g), -1); + + boost::dijkstra_shortest_paths_no_init( + g, + A, + boost::dummy_property_map(), + make_iterator_property_map(distance.begin(), idx, distance[0]), + get(boost::edge_weight, g), + idx, + std::less(), + boost::closed_plus(), + 0, + boost::make_dijkstra_visitor(make_examined_recorder( + make_iterator_property_map(examined.begin(), idx, examined[0]), + boost::on_examine_vertex()) + ) + ); + BOOST_CHECK_EQUAL(examined[A], 1); + BOOST_CHECK_EQUAL(examined[B], 1); + BOOST_CHECK_EQUAL(examined[C], -1); + BOOST_CHECK_EQUAL(examined[D], -1); + BOOST_CHECK_EQUAL(examined[E], -1); +} +