Skip to content

Feature : Resource Contrained Shortest Pahts to every vertices from source#239

Open
SimonPiCarter wants to merge 6 commits into
boostorg:developfrom
SimonPiCarter:feature/r_c_shortest_paths_to_n
Open

Feature : Resource Contrained Shortest Pahts to every vertices from source#239
SimonPiCarter wants to merge 6 commits into
boostorg:developfrom
SimonPiCarter:feature/r_c_shortest_paths_to_n

Conversation

@SimonPiCarter
Copy link
Copy Markdown

See #238

Closes #238

If formatting or anything is to be changed let me know.

Comment on lines +437 to +444
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);
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not satisfied with those lines but could not find a better way to implement this without impacting performance (very slightly) for the old usage.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 🙏🏽

Comment on lines +1 to +4
// 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)
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copyright should be to be changed as the test is from me but I did not know the exact procedure for this.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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)
//=======================================================================

@SimonPiCarter
Copy link
Copy Markdown
Author

builds seem to timeout on windows for mscv for unknown reason?

@Becheler
Copy link
Copy Markdown
Collaborator

Becheler commented Jun 4, 2026

Hi @SimonPiCarter ! I am a bit late to the game but reading through your PR.
Are you still around ?
Thanks again for contributing ❤️

@SimonPiCarter
Copy link
Copy Markdown
Author

Hello,
I completely forgot about this PR
I am still around let me know if there is anything I can do.

@Becheler
Copy link
Copy Markdown
Collaborator

Becheler commented Jun 4, 2026

@SimonPiCarter amazing 🚀
There are conflicts, would you mind rebasing your branch on top of develop to resync the last years changes ? Thank you !

@SimonPiCarter
Copy link
Copy Markdown
Author

@Becheler i'll look into it probably tomorow or during the weekend.
Hope there are not too many diffs

@Becheler
Copy link
Copy Markdown
Collaborator

Becheler commented Jun 4, 2026

Amazing thank you ! 😄
My guess is that there should be little to no changes affecting directly your modifications, but let me know if you have any issue 😉

@SimonPiCarter SimonPiCarter force-pushed the feature/r_c_shortest_paths_to_n branch from 03ea9ba to 0cb8bb8 Compare June 5, 2026 08:17
@SimonPiCarter
Copy link
Copy Markdown
Author

SimonPiCarter commented Jun 5, 2026

Alright rebase has been done and ci is under way (looking good)

Copy link
Copy Markdown
Collaborator

@Becheler Becheler left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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));
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 v in the graph, run r_c_shortest_paths(g, s, v, ...) individually to get paths to just v
  • Assert that the paths in the to_all result that end at v match the individual result for v

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,
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 😄

Comment on lines +1 to +4
// 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)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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,
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>

} // detail

That 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 v3

Happy to help with further details if you feel this is too much work 😄

Comment on lines +437 to +444
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);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

little typo 😛

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;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

prefer the using x = y syntax in C++14 😄

return os_p;
}

/// @brief get all paths from a source to all reachable destination
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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-empty

That 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 👍🏽

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Resource Contrained Shortest Pahts to every vertices from source : r_c_shortest_paths_to_n

3 participants