Skip to content

Commit

Permalink
Merge pull request #81 from stdgraph/el_rng
Browse files Browse the repository at this point in the history
El rng
  • Loading branch information
pratzl authored Dec 4, 2023
2 parents 95e8c36 + 487dd19 commit 15a5037
Show file tree
Hide file tree
Showing 11 changed files with 1,045 additions and 602 deletions.
2 changes: 1 addition & 1 deletion example/CppCon2022/germany_routes_example.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ TEST_CASE("Germany Routes Presentation", "[presentation][germany][routes][shorte
cout << "Traverse the vertices & outgoing edges" << endl;
for (auto&& [uid, u] : vertexlist(g)) { // [id,vertex&]
cout << city_id(g, uid) << endl; // city name [id]
for (auto&& [vid, uv] : incidence(g, uid)) { // [target_id,edge&]
for (auto&& [vid, uv] : std::graph::views::incidence(g, uid)) { // [target_id,edge&]
cout << " --> " << city_id(g, vid) << endl;
// "--> "target city" [target_id]
}
Expand Down
77 changes: 77 additions & 0 deletions include/graph/algorithm/dijkstra_clrs.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,27 @@ template <class G, class F>
concept edge_weight_function = // e.g. weight(uv)
copy_constructible<F> && is_arithmetic_v<invoke_result_t<F, edge_reference_t<G>>>;


//edge_weight_function<G, WF> &&
//strict_weak_order<Compare, range_value_t<Distance>, range_value_t<Distance>> &&
//assignable_from <range_reference_t<Distance>, invoke_result_t <Combine, invoke_result_t<WF, edge_t<G>>,

template <class G, class WF, class Distance, class Compare, class Combine>
concept basic_edge_weight_function2 = // e.g. weight(uv)
is_arithmetic_v<ranges::range_value_t<Distance>> &&
strict_weak_order<Compare, ranges::range_value_t<Distance>, ranges::range_value_t<Distance>> &&
assignable_from<ranges::range_reference_t<Distance>, invoke_result_t<Combine, invoke_result_t<WF, edge_t<G>>>>;

template <class G, class WF, class Distance>
concept edge_weight_function2 = // e.g. weight(uv)
is_arithmetic_v<invoke_result_t<WF, edge_t<G>>> &&
basic_edge_weight_function2<G,
WF,
Distance,
less<ranges::range_value_t<Distance>>,
plus<ranges::range_value_t<Distance>>>;


class null_range_type : public std::vector<size_t> {
using T = size_t;
using Allocator = std::allocator<T>;
Expand Down Expand Up @@ -120,4 +141,60 @@ void dijkstra_clrs(
}
}

template <index_adjacency_list G,
ranges::random_access_range Distance,
ranges::random_access_range Predecessor,
class WF = std::function<ranges::range_value_t<Distance>(edge_reference_t<G>)>>
requires is_arithmetic_v<ranges::range_value_t<Distance>> && //
convertible_to<vertex_id_t<G>, ranges::range_value_t<Predecessor>> && //
edge_weight_function<G, WF>
void dijkstra_clrs2(
G&& g, // graph
vertex_id_t<G> seed, // starting vertex_id
Distance& distance, // out: distance[uid] of uid from seed
Predecessor& predecessor, // out: predecessor[uid] of uid in path
WF&& weight =
[](edge_reference_t<G> uv) { return ranges::range_value_t<Distance>(1); }) // default weight(uv) -> 1
{
using id_type = vertex_id_t<G>;
using weight_type = invoke_result_t<WF, edge_reference_t<G>>;

// Remark(Andrew): Do we want to allow null distance? What about if both are null? Still run algorithm at all?

size_t N(size(vertices(g))); // Question(Andrew): Do we want a num_vertices(g) CPO?
assert(seed < N && seed >= 0);

std::ranges::fill(distance, numeric_limits<weight_type>::max());
distance[seed] = 0;

struct weighted_vertex {
id_type vertex_id = id_type();
weight_type weight = weight_type();
};

// Remark(Andrew): We may want to make this parameterizable as different types of heaps give different performance
// But std::priority_queue is probably reasonable for now
using q_compare = decltype([](const weighted_vertex& a, const weighted_vertex& b) { return a.weight > b.weight; });
std::priority_queue<weighted_vertex, vector<weighted_vertex>, q_compare> Q;

// Remark(Andrew): CLRS puts all vertices in the queue to start but standard practice seems to be to enqueue source
Q.push({seed, distance[seed]});

while (!Q.empty()) {

auto uid = Q.top().vertex_id;
Q.pop();

for (auto&& [vid, uv, w] : views::incidence(g, uid, weight)) { // Remark(Andrew): +1
//weight_type w = weight(uv);
if (distance[uid] + w < distance[vid]) {
distance[vid] = distance[uid] + w;
if constexpr (!is_same_v<Predecessor, null_range_type>)
predecessor[vid] = uid;
Q.push({vid, distance[vid]});
}
}
}
}

} // namespace std::graph
2 changes: 1 addition & 1 deletion include/graph/algorithm/shortest_paths.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ template <adjacency_list G,
requires ranges::random_access_range<vertex_range_t<G>> && //
integral<vertex_id_t<G>> && //
is_arithmetic_v<ranges::range_value_t<DistanceRange>> && //
//convertible_to<vertex_id_t<G>, ranges::range_value_t<Predecessor>> && //
//convertible_to<vertex_id_t<G>, ranges::range_value_t<PredecessorRange>> && //
edge_weight_function<G, EVF>
constexpr void dijkstra_shortest_paths(
G&& g,
Expand Down
31 changes: 22 additions & 9 deletions include/graph/graph.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -104,11 +104,13 @@ inline constexpr bool is_undirected_edge_v = false;
* @tparam G The graph type.
*/
template <class G>
concept vertex_range = ranges::forward_range<vertex_range_t<G>> && ranges::sized_range<vertex_range_t<G>> &&
requires(G&& g, vertex_iterator_t<G> ui) {
{ vertices(g) } -> ranges::forward_range;
vertex_id(g, ui);
};
concept vertex_range = ranges::forward_range<vertex_range_t<G>> && ranges::sized_range<vertex_range_t<G>> && //
requires(G&& g, vertex_iterator_t<G> ui) { vertex_id(g, ui); };

template <class G>
concept index_vertex_range = ranges::random_access_range<vertex_range_t<G>> && ranges::sized_range<vertex_range_t<G>> &&
integral<vertex_id_t<G>> && //
requires(G&& g, vertex_iterator_t<G> ui) { vertex_id(g, ui); };

/**
* @ingroup graph_concepts
Expand Down Expand Up @@ -172,20 +174,31 @@ inline constexpr bool is_sourced_edge_v = is_sourced_edge<G, E>::value;
* @tparam G The graph type.
*/
template <class G>
concept index_adjacency_list = vertex_range<G> && targeted_edge<G, edge_t<G>> && requires(G&& g, vertex_id_t<G>& uid) {
concept basic_adjacency_list = vertex_range<G> && targeted_edge<G, edge_t<G>> && requires(G&& g, vertex_id_t<G>& uid) {
{ edges(g, uid) } -> ranges::forward_range;
};

template <class G>
concept basic_index_adjacency_list =
index_vertex_range<G> && targeted_edge<G, edge_t<G>> && requires(G&& g, vertex_id_t<G>& uid) {
{ edges(g, uid) } -> ranges::forward_range;
};

/**
* @ingroup graph_concepts
* @brief Concept for an adjacency list graph.
*
* An adjacency list extends index_adjacency_list to include function edges(g,u) for vertex reference u.
* An adjacency list extends basic_adjacency_list to include function edges(g,u) for vertex reference u.
*
* @tparam G The graph type.
*/
template <class G>
concept adjacency_list = index_adjacency_list<G> && requires(G&& g, vertex_reference_t<G> u) {
concept adjacency_list = basic_adjacency_list<G> && requires(G&& g, vertex_reference_t<G> u) {
{ edges(g, u) } -> ranges::forward_range;
};

template <class G>
concept index_adjacency_list = basic_index_adjacency_list<G> && requires(G&& g, vertex_reference_t<G> u) {
{ edges(g, u) } -> ranges::forward_range;
};

Expand All @@ -208,7 +221,7 @@ concept sourced_adjacency_list =

# ifdef ENABLE_EDGELIST_RANGE
template <class ELR>
concept basic_edgelist_range = ranges::forward_range<ELR> && negation_v<index_adjacency_list<ELR>>;
concept basic_edgelist_range = ranges::forward_range<ELR> && negation_v<basic_adjacency_list<ELR>>;
template <class ELR>
concept edgelist_range = ranges::forward_range<ELR> && negation_v<adjacency_list<ELR>>;
# endif
Expand Down
Loading

0 comments on commit 15a5037

Please sign in to comment.