Feature : Resource Contrained Shortest Pahts to every vertices from source#239
Feature : Resource Contrained Shortest Pahts to every vertices from source#239SimonPiCarter wants to merge 6 commits into
Conversation
| std::list<typename graph_traits< Graph >::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); |
There was a problem hiding this comment.
Not satisfied with those lines but could not find a better way to implement this without impacting performance (very slightly) for the old usage.
There was a problem hiding this comment.
I agree, i think this is a symptom of trying to force the expanded case into the single target implementation 😄 Don't hesitate telling me if you disagree with my understanding 🙏🏽
| // 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) |
There was a problem hiding this comment.
Copyright should be to be changed as the test is from me but I did not know the exact procedure for this.
There was a problem hiding this comment.
Just add your name and date, e.g.
//=======================================================================
// Copyright (C) 2026 Simon Carter
//
// 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)
//=======================================================================
|
builds seem to timeout on windows for mscv for unknown reason? |
|
Hi @SimonPiCarter ! I am a bit late to the game but reading through your PR. |
|
Hello, |
|
@SimonPiCarter amazing 🚀 |
|
@Becheler i'll look into it probably tomorow or during the weekend. |
|
Amazing thank you ! 😄 |
03ea9ba to
0cb8bb8
Compare
|
Alright rebase has been done and ci is under way (looking good) |
Becheler
left a comment
There was a problem hiding this comment.
Thanks a lot for this amazing work 😄
Overall this is very good. My only concern is that a deviation in API is hiding behind the new requirements: the output map should be indexed by target vertices to avoid users having to parse the flat vector heavily, and the combination of passing a boolean to switch mode + passing s as both source and target is, I fear, a recipe for future disasters.
I added comments, feel free to discuss them and push back 😄
| BOOST_TEST(testPathExistence(vectPath_l, manual_path() % "2,0", 0, 0)); | ||
| BOOST_TEST(testPathExistence(vectPath_l, manual_path() % "2,1" % "2,0", 0, 1)); | ||
| } | ||
|
|
There was a problem hiding this comment.
I would be more comfortable adding a test case to lock the two versions correctness:
- Run
r_c_shortest_paths_to_all(g, s, ...)once: get all paths to all vertices - For each vertex
vin the graph, runr_c_shortest_paths(g, s, v, ...)individually to get paths to justv - Assert that the paths in the
to_allresult that end atvmatch the individual result forv
Does it make sense ?
Also I am slowly working towards a "how to write tests" guide, forgive me for the lack of instructions 🙏🏽
| // 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, |
There was a problem hiding this comment.
My understanding is that you are passing the same vertex as both source and target as a necessity to fit the dispatch API. I would rather make this explicit in the type system:
// signature
r_c_shortest_paths_dispatch(g, ..., s, boost::optional<vertex_descriptor> t, ...)
// usage
r_c_shortest_paths_dispatch(g, vertex_index_map, edge_index_map, s, boost::make_optional(s), ...);That would also remove a whole class of future bugs, as it makes the intent explicit (t absent means "all vertices") and a future developer adding a third use of t would naturally write if (t) { do_something(*t) } because the type forces them to handle the null case.
Since the dispatch is not public (lives in detail::), we can safely change its interface: users are not supposed to rely on it.
Does it make sense ?
| std::list<typename graph_traits< Graph >::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) |
There was a problem hiding this comment.
This is pre-C++11 support, but BGL is now C++14.
You can instead write:
for (auto v : boost::make_iterator_range(vertices(g)))
list_vertices.push_back(v);and remove the boost foreach include 😄
| // 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) |
There was a problem hiding this comment.
Just add your name and date, e.g.
//=======================================================================
// Copyright (C) 2026 Simon Carter
//
// 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)
//=======================================================================
| 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_dispatch(const Graph& g, |
There was a problem hiding this comment.
Great job trying to stick to the original API and reuse implementation, but i think this is a meaningful deviation that motivates a different API:
- passing bools to switch modes is a symptom
- passing (s, s) is another symptom
- the long error prone parsing of the output for all is another symptom
So I would rather go that way:
namespace detail {
// single-target implementation used by all four r_c_shortest_paths overloads
void r_c_shortest_paths_impl(g, s, t, solutions, containers, b_all_pareto_optimal_solutions, rc, ref, dominance, la, vis);
// all-targets implementation used by r_c_shortest_paths_to_all overloads
void r_c_shortest_paths_to_all_impl(g, s, paths_map, /* property map keyed by vertex */ rc, ref, dominance, la, vis);
// shared: builds labels for all vertices reachable from s
void r_c_shortest_paths_expand_labels(g, s, vec_vertex_labels, /* internal label storage */ rc, ref, dominance, la, vis, early_termination_vertex); // optional<vertex_descriptor>
} // detailThat would allow users to simplify usage with something like e.g.:
using PathList = std::vector<std::vector<Edge>>;
std::map<Vertex, PathList> storage;
auto paths_map = boost::make_assoc_property_map(storage);
r_c_shortest_paths_to_all(g, get(vertex_index, g), source, paths_map, Resource{0}, REF{}, Dominance{});
auto& paths_to_v2 = get(paths_map, v2); // all pareto-optimal paths to v2
auto& paths_to_v3 = get(paths_map, v3); // all pareto-optimal paths to v3Happy to help with further details if you feel this is too much work 😄
| std::list<typename graph_traits< Graph >::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); |
There was a problem hiding this comment.
I agree, i think this is a symptom of trying to force the expanded case into the single target implementation 😄 Don't hesitate telling me if you disagree with my understanding 🙏🏽
| namespace | ||
| { | ||
| /// | ||
| /// @brief Structure representig the vertices, containing the id exposition and name |
| typedef boost::adjacency_list<boost::vecS, boost::vecS, boost::undirectedS, vertex_t, edge_t> graph_t; | ||
| typedef boost::graph_traits<graph_t>::vertex_descriptor vertex_desc; | ||
| typedef boost::graph_traits<graph_t>::edge_descriptor edge_desc; | ||
| typedef std::map<size_t, std::map<size_t, vertex_desc> > vertex_map_t; |
There was a problem hiding this comment.
prefer the using x = y syntax in C++14 😄
| return os_p; | ||
| } | ||
|
|
||
| /// @brief get all paths from a source to all reachable destination |
There was a problem hiding this comment.
what happens if the a target is not reachable ? With the associative property map output, the user could easily check:
if (storage.count(v) == 0)
// v is unreachable from source
else
auto& paths = get(paths_map, v); // guaranteed non-emptyThat could be cleaner than the current flat vector design where users have to scan all paths and deduce reachability from presence/absence of paths ending at v ?
That would be consistent with existing algorithm returning an empty vector when the target is unreachable.
That would also deserve a test for non-regression 👍🏽
See #238
Closes #238
If formatting or anything is to be changed let me know.