diff --git a/include/boost/graph/r_c_shortest_paths.hpp b/include/boost/graph/r_c_shortest_paths.hpp index 03aa09063..68ae07f88 100644 --- a/include/boost/graph/r_c_shortest_paths.hpp +++ b/include/boost/graph/r_c_shortest_paths.hpp @@ -15,6 +15,7 @@ #include #include +#include #include #include #include @@ -177,6 +178,7 @@ namespace detail pareto_optimal_solutions, std::vector< Resource_Container >& pareto_optimal_resource_containers, bool b_all_pareto_optimal_solutions, + bool b_all_pareto_optimal_solutions_on_all_vertices, // to initialize the first label/resource container // and to carry the type information const Resource_Container& rc, Resource_Extension_Function& ref, @@ -369,6 +371,7 @@ namespace detail } } if (!b_all_pareto_optimal_solutions + && !b_all_pareto_optimal_solutions_on_all_vertices && cur_label->resident_vertex == t) { // the devil don't sleep @@ -430,47 +433,61 @@ namespace detail cur_label.reset(); } } - std::list< Splabel > dsplabels = get(vec_vertex_labels, t); - typename std::list< Splabel >::const_iterator csi = dsplabels.begin(); - typename std::list< Splabel >::const_iterator csi_end = dsplabels.end(); - // if d could be reached from o - if (!dsplabels.empty()) + + std::list::vertex_descriptor> list_vertices; + // add all vertices if asked for + if(b_all_pareto_optimal_solutions_on_all_vertices) + BGL_FORALL_VERTICES_T(i, g, Graph) + list_vertices.push_back(i); + else + // else only target + list_vertices.push_back(t); + + BOOST_FOREACH(typename graph_traits< Graph >::vertex_descriptor vertex, list_vertices) { - for (; csi != csi_end; ++csi) + std::list< Splabel > dsplabels = get(vec_vertex_labels, vertex); + typename std::list< Splabel >::const_iterator csi = dsplabels.begin(); + typename std::list< Splabel >::const_iterator csi_end = dsplabels.end(); + // if d could be reached from o + if (!dsplabels.empty()) { - std::vector< typename graph_traits< Graph >::edge_descriptor > - cur_pareto_optimal_path; - boost::shared_ptr< - r_c_shortest_paths_label< Graph, Resource_Container > > - p_cur_label = *csi; - pareto_optimal_resource_containers.push_back( - p_cur_label->cumulated_resource_consumption); - while (p_cur_label->num != 0) + for (; csi != csi_end; ++csi) { - cur_pareto_optimal_path.push_back(p_cur_label->pred_edge); - p_cur_label = p_cur_label->p_pred_label; - - // assertion b_is_valid beyond this point is not correct if - // the domination function requires resource levels to be - // strictly greater than existing values - // - // Example - // Customers - // id min_arrival max_departure - // 2 0 974 - // 3 0 972 - // 4 0 964 - // 5 678 801 - // - // Path A: 2-3-4-5 (times: 0-16-49-84-678) - // Path B: 3-2-4-5 (times: 0-18-51-62-678) - // The partial path 3-2-4 dominates the other partial path - // 2-3-4, though the path 3-2-4-5 does not strictly dominate - // the path 2-3-4-5 + std::vector< typename graph_traits< Graph >::edge_descriptor > + cur_pareto_optimal_path; + boost::shared_ptr< + r_c_shortest_paths_label< Graph, Resource_Container > > + p_cur_label = *csi; + pareto_optimal_resource_containers.push_back( + p_cur_label->cumulated_resource_consumption); + while (p_cur_label->num != 0) + { + cur_pareto_optimal_path.push_back(p_cur_label->pred_edge); + p_cur_label = p_cur_label->p_pred_label; + + // assertion b_is_valid beyond this point is not correct if + // the domination function requires resource levels to be + // strictly greater than existing values + // + // Example + // Customers + // id min_arrival max_departure + // 2 0 974 + // 3 0 972 + // 4 0 964 + // 5 678 801 + // + // Path A: 2-3-4-5 (times: 0-16-49-84-678) + // Path B: 3-2-4-5 (times: 0-18-51-62-678) + // The partial path 3-2-4 dominates the other partial path + // 2-3-4, though the path 3-2-4-5 does not strictly dominate + // the path 2-3-4-5 + } + pareto_optimal_solutions.push_back(cur_pareto_optimal_path); + if (!b_all_pareto_optimal_solutions + && !b_all_pareto_optimal_solutions_on_all_vertices) + break; } - pareto_optimal_solutions.push_back(cur_pareto_optimal_path); - if (!b_all_pareto_optimal_solutions) - break; } } @@ -548,7 +565,7 @@ void r_c_shortest_paths(const Graph& g, const VertexIndexMap& vertex_index_map, Label_Allocator la, Visitor vis) { r_c_shortest_paths_dispatch(g, vertex_index_map, edge_index_map, s, t, - pareto_optimal_solutions, pareto_optimal_resource_containers, true, rc, + pareto_optimal_solutions, pareto_optimal_resource_containers, true, false, rc, ref, dominance, la, vis); } @@ -578,7 +595,7 @@ void r_c_shortest_paths(const Graph& g, const VertexIndexMap& vertex_index_map, pareto_optimal_solutions; std::vector< Resource_Container > pareto_optimal_resource_containers; r_c_shortest_paths_dispatch(g, vertex_index_map, edge_index_map, s, t, - pareto_optimal_solutions, pareto_optimal_resource_containers, false, rc, + pareto_optimal_solutions, pareto_optimal_resource_containers, false, false, rc, ref, dominance, la, vis); if (!pareto_optimal_solutions.empty()) { @@ -609,7 +626,7 @@ void r_c_shortest_paths(const Graph& g, const VertexIndexMap& vertex_index_map, const Dominance_Function& dominance) { r_c_shortest_paths_dispatch(g, vertex_index_map, edge_index_map, s, t, - pareto_optimal_solutions, pareto_optimal_resource_containers, true, rc, + pareto_optimal_solutions, pareto_optimal_resource_containers, true, false, rc, ref, dominance, default_r_c_shortest_paths_allocator(), default_r_c_shortest_paths_visitor()); } @@ -638,7 +655,7 @@ void r_c_shortest_paths(const Graph& g, const VertexIndexMap& vertex_index_map, pareto_optimal_solutions; std::vector< Resource_Container > pareto_optimal_resource_containers; r_c_shortest_paths_dispatch(g, vertex_index_map, edge_index_map, s, t, - pareto_optimal_solutions, pareto_optimal_resource_containers, false, rc, + pareto_optimal_solutions, pareto_optimal_resource_containers, false, false, rc, ref, dominance, default_r_c_shortest_paths_allocator(), default_r_c_shortest_paths_visitor()); if (!pareto_optimal_solutions.empty()) @@ -650,6 +667,60 @@ void r_c_shortest_paths(const Graph& g, const VertexIndexMap& vertex_index_map, } // r_c_shortest_paths +// r_c_shortest_paths_to_all functions (handle/interface) +// first overload: +// - return all pareto-optimal solutions +// - specify Label_Allocator and Visitor arguments +template < class Graph, class VertexIndexMap, class EdgeIndexMap, + class Resource_Container, class Resource_Extension_Function, + class Dominance_Function, class Label_Allocator, class Visitor > +void r_c_shortest_paths_to_all(const Graph& g, const VertexIndexMap& vertex_index_map, + const EdgeIndexMap& edge_index_map, + typename graph_traits< Graph >::vertex_descriptor s, + // each inner vector corresponds to a pareto-optimal path + std::vector< + std::vector< typename graph_traits< Graph >::edge_descriptor > >& + pareto_optimal_solutions, + std::vector< Resource_Container >& pareto_optimal_resource_containers, + // to initialize the first label/resource container + // and to carry the type information + const Resource_Container& rc, const Resource_Extension_Function& ref, + const Dominance_Function& dominance, + // to specify the memory management strategy for the labels + Label_Allocator la, Visitor vis) +{ + r_c_shortest_paths_dispatch(g, vertex_index_map, edge_index_map, s, s, + pareto_optimal_solutions, pareto_optimal_resource_containers, true, true, rc, + ref, dominance, la, vis); +} + +// second overload: +// - return all pareto-optimal solutions +// - use default Label_Allocator and Visitor +template < class Graph, class VertexIndexMap, class EdgeIndexMap, + class Resource_Container, class Resource_Extension_Function, + class Dominance_Function > +void r_c_shortest_paths_to_all(const Graph& g, const VertexIndexMap& vertex_index_map, + const EdgeIndexMap& edge_index_map, + typename graph_traits< Graph >::vertex_descriptor s, + // each inner vector corresponds to a pareto-optimal path + std::vector< + std::vector< typename graph_traits< Graph >::edge_descriptor > >& + pareto_optimal_solutions, + std::vector< Resource_Container >& pareto_optimal_resource_containers, + // to initialize the first label/resource container + // and to carry the type information + const Resource_Container& rc, const Resource_Extension_Function& ref, + const Dominance_Function& dominance) +{ + r_c_shortest_paths_dispatch(g, vertex_index_map, edge_index_map, s, s, + pareto_optimal_solutions, pareto_optimal_resource_containers, true, true, rc, + ref, dominance, default_r_c_shortest_paths_allocator(), + default_r_c_shortest_paths_visitor()); +} + +// r_c_shortest_paths_to_all + // check_r_c_path function template < class Graph, class Resource_Container, class Resource_Extension_Function > diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index b4fcfd4f3..7ba218c3c 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -121,6 +121,7 @@ alias graph_test_regular : [ run make_maximal_planar_test.cpp ] [ run named_vertices_test.cpp ] [ run r_c_shortest_paths_test.cpp ] + [ run r_c_shortest_paths_to_all_test.cpp ] [ run rcsp_custom_vertex_id.cpp ] [ run is_straight_line_draw_test.cpp ] [ run metric_tsp_approx.cpp /boost/timer//boost_timer : metric_tsp_approx.graph : : ] diff --git a/test/r_c_shortest_paths_to_all_test.cpp b/test/r_c_shortest_paths_to_all_test.cpp new file mode 100644 index 000000000..5f2013621 --- /dev/null +++ b/test/r_c_shortest_paths_to_all_test.cpp @@ -0,0 +1,479 @@ +// Copyright Michael Drexl 2005, 2006. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://boost.org/LICENSE_1_0.txt) + +#include + +#ifdef BOOST_MSVC +#pragma warning(disable : 4267) +#endif + +#include +#include + +#include +#include +#include +#include + +namespace +{ +/// +/// @brief Structure representig the vertices, containing the id exposition and name +/// and x,y for representation +/// +struct vertex_t +{ + size_t id; + size_t x; + size_t y; + size_t movedReq; + double expo; + std::string name; +}; + +/// +/// @brief Structure for edge with an id and two vertex_t extremities +/// +struct edge_t +{ + size_t id; + std::vector extremities; + +}; + +/// +/// @brief Definition of some typedef. +/// +typedef boost::adjacency_list graph_t; +typedef boost::graph_traits::vertex_descriptor vertex_desc; +typedef boost::graph_traits::edge_descriptor edge_desc; +typedef std::map > vertex_map_t; + +/// +/// @brief Function creating the vertices. +/// +vertex_map_t create_vertices(graph_t &g, std::vector > const &pos_p) +{ + vertex_map_t vertex_map; + + size_t idx_l = 0; + // Read all positions + for(size_t i = 0 ; i < pos_p.size() ; ++ i) + { + for(size_t j = 0 ; j < pos_p[i].size() ; ++ j) + { + // Create a vertex based on the index in the vectors + vertex_t cur_vertex; + cur_vertex.id = idx_l++; + cur_vertex.x = i; + cur_vertex.y = j; + // required move is given by the value in the vectors + cur_vertex.movedReq = pos_p[i][j]; + // exposition is always one + cur_vertex.expo = 1; + + cur_vertex.name = boost::lexical_cast(i)+","+boost::lexical_cast(j); + + vertex_desc desc = add_vertex(cur_vertex, g); + vertex_map[i][j] = desc; + } + } + + return vertex_map; +} + +struct not_found{}; + +template +typename Map::mapped_type map_get(typename Map::key_type k, const Map &m) +{ + typename Map::const_iterator search = m.find(k); + if (search == m.end()) throw not_found(); + return search->second; +} + +/// +/// @brief Function creating the edges. +/// +void create_edges(const vertex_map_t &vertex_map, graph_t &g) +{ + size_t index_l = 0; + // We iterate on every vertex of the map + for(size_t i = 0 ; i < vertex_map.size() ; ++ i ) + { + for(size_t j = 0 ; j < map_get(i,vertex_map).size() ; ++ j ) + { + // If not on first row we link every vertex + // with the one directly above it (same column i and row j-1) + if ( j > 0 ) + { + edge_t cur_edge; + cur_edge.id = index_l++; + vertex_desc v1 = map_get(j-1,map_get(i, vertex_map)); + vertex_desc v2 = map_get(j,map_get(i, vertex_map)); + cur_edge.extremities.push_back(g[v1]); + cur_edge.extremities.push_back(g[v2]); + add_edge(v1, v2, cur_edge, g); + } + // If not on first column we link every vertex + // with the one directly on the left (column i-1 and same row j) + if ( i > 0 ) + { + edge_t cur_edge; + cur_edge.id = index_l++; + vertex_desc v1 = map_get(j,map_get(i-1, vertex_map)); + vertex_desc v2 = map_get(j,map_get(i, vertex_map)); + cur_edge.extremities.push_back(g[v1]); + cur_edge.extremities.push_back(g[v2]); + add_edge(v1, v2, cur_edge, g); + } + } + } +} + +/// +/// @brief A simple label wtih movement required and exposition (plus the current vertex) +/// +struct Label { + Label() : v(), id(0), movedReq(0), expo(0.) {} + + vertex_t v; + size_t id; + size_t movedReq; + double expo; + + // Required for r_c_shortest_paths + bool operator<(Label const & other_p ) const + { + // Compare required move first + if(movedReq != other_p.movedReq) + { + return movedReq < other_p.movedReq; + } + // Then exposition + if(std::abs(expo - other_p.expo) < 1e-5) + { + return expo < other_p.expo; + } + // Finaly only id + return id < other_p.id; + } +}; + +/// +/// @brief Dominance function +/// Compare movement required +/// Then exposition +/// +struct DominanceFunction { + /// @brief + inline bool operator()(Label const & a, Label const & b) const + { + if(a.movedReq < b.movedReq) + { + return true; + } else if (a.movedReq > b.movedReq) + { + return false; + } + return a.expo < b.expo; + } +}; + +/// +/// @brief A function that update a label with the data of an edge. +/// +struct ResourceExtensionFunction +{ + ResourceExtensionFunction(size_t maxMove_p) : maxMove(maxMove_p) {}; + + size_t maxMove; + + inline bool operator()( graph_t const & g, + Label & new_cont, + Label const & old_cont, + edge_desc ed ) const + { + return extends(old_cont, new_cont, g[ed]); + } + + inline bool extends(Label const & oldLabel_p, Label & label_p, edge_t const & property_p) const + { + // Get opposite vertex from the edge + vertex_t v = property_p.extremities[0].id == oldLabel_p.id ? property_p.extremities[1] : property_p.extremities[0]; + // uodate label value + label_p.v = v; + label_p.id = v.id; + label_p.movedReq = oldLabel_p.movedReq + v.movedReq; + label_p.expo = oldLabel_p.expo + v.expo; + // check max move allowed + return label_p.movedReq <= maxMove; + } +}; + +/// @brief Path object for result +struct Path +{ + Label label; + std::vector edges; + +}; + +/// @brief Streaming Path to a string +std::ostream &operator<<(std::ostream &os_p, Path const &path_p) +{ + os_p << "Path[target = "< getAllPathsToN(std::vector > const &map_p, std::pair const &orig_p, size_t maxMove_p) +{ + // creating a graph g from vertices and edges of files + graph_t g; + vertex_map_t vertex_map = create_vertices(g, map_p); + vertex_desc source = vertex_map[orig_p.first][orig_p.second]; + create_edges(vertex_map, g); + + // Shortest path + + std::vector< std::vector< edge_desc > > shortestPaths_l; // vector of vector to store all the optimal paths + std::vector< Label > bestLabels_l; // vector of label to store the value of all optimal paths + + Label label_l; + label_l.v = g[source]; + label_l.id = label_l.v.id; + // Call of the shortest path + r_c_shortest_paths_to_all + ( g, // graph + get(&vertex_t::id,g), // map of vertices id + get(&edge_t::id,g), // map of edges id + source, // starting vertex + shortestPaths_l, // optimal paths storage + bestLabels_l, // optimal values storage + label_l, // initial label + ResourceExtensionFunction(maxMove_p), //REF model + DominanceFunction() // dominance function + ); + + std::vector vectPath_l; + + // Create Paths from boost objects + for(size_t i = 0 ; i < shortestPaths_l.size() ; ++ i) + { + Path newPath_l; + std::vector< edge_desc > const & path_l = shortestPaths_l[i]; + Label const & label_l = bestLabels_l[i]; + newPath_l.label = label_l; + BOOST_FOREACH(edge_desc const & edge_desc_l, path_l) + { + newPath_l.edges.push_back(g[edge_desc_l]); + } + vectPath_l.push_back(newPath_l); + } + + return vectPath_l; +} + +/// @brief construct the string representation of a handmade path +/// based on the operator<< of Path +/// used to compare handmade path with real Path +std::string buildString(std::vector const &path_p, size_t moveReq_p, double expo_p) +{ + if(path_p.empty()) + { + return std::string(); + } + std::stringstream ss_l; + // Get correct iterator based on reverse + std::string firstId_l = *path_p.begin(); + ss_l << "Path[target = "< internal_path; +}; + +/// @brief test that the given path exists in the solution +bool testPathExistence(std::vector const &paths_p, manual_path const &manual_path_p, size_t moveReq_p, double expo_p) +{ + std::string refStringOneWay_l = buildString(manual_path_p.internal_path, moveReq_p, expo_p); + BOOST_FOREACH(Path const &path_l, paths_p) + { + if(boost::lexical_cast(path_l) == refStringOneWay_l) + { + return true; + } + } + // fail if not found with the given message + return false; +} + +/// @brief helpers struct to construct a row of position +struct manual_row +{ + manual_row &operator%(size_t moveReq_p) + { + row.push_back(moveReq_p); + return *this; + } + + std::vector row; +}; + +/// @brief helpers struct to construct a map of position +struct manual_map +{ + + manual_map &operator%(manual_row const &row_p) + { + map.push_back(row_p.row); + return *this; + } + + std::vector > map; +}; + +} + +/// +/// All tests are based on a map giving the number of required moves to access each position +/// + +void test_shortest_paths_to_all_no_move_allowed() +{ + // 0 1 2 3 4 + // -------------- + //0 | 1, 1, 1, 1, 1 + //1 | 1, 0, 1, 0, 1 + //2 | 0, 0, 0, 0, 0 + //3 | 1, 0, 1, 0, 1 + //4 | 1, 1, 1, 0, 1 + // + // we read vertical index first + // 2,0 is line 2 and colum 0 + // + manual_map map_l; + map_l % (manual_row() % 1 % 1 % 1 % 1 % 1); + map_l % (manual_row() % 1 % 0 % 1 % 0 % 1); + map_l % (manual_row() % 0 % 0 % 0 % 0 % 0); + map_l % (manual_row() % 1 % 0 % 1 % 0 % 1); + map_l % (manual_row() % 1 % 1 % 1 % 0 % 1); + + std::vector vectPath_l = getAllPathsToN(map_l.map, std::make_pair(2,0), 0); + + // Check all paths to all accessible tiles (no move are allowed meaning that all tile with 1 value are forbidden) + BOOST_TEST(testPathExistence(vectPath_l, manual_path() % "1,1" % "2,1" % "2,0", 0, 2)); + BOOST_TEST(testPathExistence(vectPath_l, manual_path() % "1,3" % "2,3" % "2,2" % "2,1" % "2,0", 0, 4)); + BOOST_TEST(testPathExistence(vectPath_l, manual_path() % "2,0", 0, 0)); + BOOST_TEST(testPathExistence(vectPath_l, manual_path() % "2,1" % "2,0", 0, 1)); + BOOST_TEST(testPathExistence(vectPath_l, manual_path() % "2,2" % "2,1" % "2,0", 0, 2)); + BOOST_TEST(testPathExistence(vectPath_l, manual_path() % "2,3" % "2,2" % "2,1" % "2,0", 0, 3)); + BOOST_TEST(testPathExistence(vectPath_l, manual_path() % "2,4" % "2,3" % "2,2" % "2,1" % "2,0", 0, 4)); + BOOST_TEST(testPathExistence(vectPath_l, manual_path() % "3,1" % "2,1" % "2,0", 0, 2)); + BOOST_TEST(testPathExistence(vectPath_l, manual_path() % "3,3" % "2,3" % "2,2" % "2,1" % "2,0", 0, 4)); + BOOST_TEST(testPathExistence(vectPath_l, manual_path() % "4,3" % "3,3" % "2,3" % "2,2" % "2,1" % "2,0", 0, 5)); + +} + +void test_shortest_paths_to_all_one_move_allowed() +{ + // 0 1 2 3 4 + // -------------- + //0 | 1, 1, 1, 1, 1 + //1 | 1, 1, 1, 0, 1 + //2 | 0, 0, 1, 0, 1 + // + // we read vertical index first + // 2,0 is line 2 and colum 0 + // + manual_map map_l; + map_l % (manual_row() % 1 % 1 % 1 % 1 % 1); + map_l % (manual_row() % 1 % 1 % 1 % 0 % 1); + map_l % (manual_row() % 0 % 0 % 1 % 0 % 1); + + std::vector vectPath_l = getAllPathsToN(map_l.map, std::make_pair(2,0), 1); + + // Check all paths to all accessible tiles (no move are allowed meaning that all tile with 1 value are forbidden) + BOOST_TEST(testPathExistence(vectPath_l, manual_path() % "1,0" % "2,0", 1, 1)); + BOOST_TEST(testPathExistence(vectPath_l, manual_path() % "1,1" % "2,1" % "2,0", 1, 2)); + BOOST_TEST(testPathExistence(vectPath_l, manual_path() % "1,3" % "2,3" % "2,2" % "2,1" % "2,0", 1, 4)); + BOOST_TEST(testPathExistence(vectPath_l, manual_path() % "2,0", 0, 0)); + BOOST_TEST(testPathExistence(vectPath_l, manual_path() % "2,1" % "2,0", 0, 1)); + BOOST_TEST(testPathExistence(vectPath_l, manual_path() % "2,2" % "2,1" % "2,0", 1, 2)); + BOOST_TEST(testPathExistence(vectPath_l, manual_path() % "2,3" % "2,2" % "2,1" % "2,0", 1, 3)); +} + +void test_shortest_paths_to_all_multiple_paths() +{ + // 0 1 2 3 4 + // -------------- + //0 | 1, 1, 1, 1, 1 + //1 | 0, 0, 1, 1, 1 + //2 | 0, 0, 1, 0, 1 + // + // we read vertical index first + // 2,0 is line 2 and colum 0 + // + manual_map map_l; + map_l % (manual_row() % 1 % 1 % 1 % 1 % 1); + map_l % (manual_row() % 0 % 0 % 1 % 0 % 1); + map_l % (manual_row() % 0 % 0 % 1 % 0 % 0); + + std::vector vectPath_l = getAllPathsToN(map_l.map, std::make_pair(2,0), 0); + + // Check all paths to all accessible tiles (no move are allowed meaning that all tile with 1 value are forbidden) + BOOST_TEST(testPathExistence(vectPath_l, manual_path() % "1,0" % "2,0", 0, 1)); + BOOST_TEST(testPathExistence(vectPath_l, manual_path() % "1,1" % "1,0" % "2,0", 0, 2)); + BOOST_TEST(testPathExistence(vectPath_l, manual_path() % "1,1" % "2,1" % "2,0", 0, 2)); + BOOST_TEST(testPathExistence(vectPath_l, manual_path() % "2,0", 0, 0)); + BOOST_TEST(testPathExistence(vectPath_l, manual_path() % "2,1" % "2,0", 0, 1)); +} + +int main(int, char*[]) +{ + test_shortest_paths_to_all_no_move_allowed(); + test_shortest_paths_to_all_one_move_allowed(); + test_shortest_paths_to_all_multiple_paths(); + return boost::report_errors(); +}