From c87ba3cc79a78ba8d02e012dda99270c88ddb976 Mon Sep 17 00:00:00 2001 From: Piotr Wygocki Date: Wed, 23 Jul 2014 12:50:52 +0200 Subject: [PATCH 1/2] dijkstra_shortest_paths_no_init supports pruning on distances When the distance map is given and the newly discovered distance is worse than the distance stored in the distance map, the vertex is not even added to the priority queue. --- include/boost/graph/dijkstra_shortest_paths.hpp | 190 ++++++++++-------------- 1 file changed, 76 insertions(+), 114 deletions(-) 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 From 4a6d4defb3b2f3935a3e093dfe21363de946df44 Mon Sep 17 00:00:00 2001 From: Piotr Wygocki Date: Tue, 20 Oct 2015 14:40:23 +0200 Subject: [PATCH 2/2] non_trivial_distance_map.cpp --- test/non_trivial_distance_map.cpp | 86 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 test/non_trivial_distance_map.cpp 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); +} +