Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions include/graph/algorithm/common_shortest_paths.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,8 @@ class _null_range_type : public std::vector<size_t> {
_null_range_type(_null_range_type&& other) noexcept {}
_null_range_type(_null_range_type&& other, const Allocator& alloc) {}
_null_range_type(std::initializer_list<T> init, const Allocator& alloc = Allocator()) {}

size_t* vertex_record(_null_range_type& cont, size_t const& id) { return nullptr; }
};

inline static _null_range_type _null_predecessors;
Expand Down
2 changes: 2 additions & 0 deletions include/graph/algorithm/dijkstra_clrs.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ class _null_range_type : public std::vector<size_t> {
_null_range_type(_null_range_type&& other) noexcept {}
_null_range_type(_null_range_type&& other, const Allocator& alloc) {}
_null_range_type(std::initializer_list<T> init, const Allocator& alloc = Allocator()) {}

size_t* vertex_record(_null_range_type&, size_t const&) { return nullptr; }
};

inline static _null_range_type null_predecessors;
Expand Down
169 changes: 101 additions & 68 deletions include/graph/algorithm/dijkstra_shortest_paths.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,62 @@
#include <ranges>
#include <format>


#ifndef GRAPH_DIJKSTRA_SHORTEST_PATHS_HPP
# define GRAPH_DIJKSTRA_SHORTEST_PATHS_HPP

namespace graph {

namespace detail {
// TODO: improve error messages by returning IDs.

template <adjacency_list G, record_for<G> Distances>
auto& validated_record(Distances& distances, vertex_id_t<G> const& uid)
{
auto* rec = vertex_record(distances, uid);
if (rec == nullptr)
[[unlikely]]
throw std::out_of_range(
"dijkstra_shortest_paths: distances do not have value for a vertex id");
return *rec;
}

template <adjacency_list G, record_for<G> Distances>
void validate_distances_record(G const& g, Distances& distances)
{
// TODO: change the secret `if constexpr` into another customization point
if constexpr (std::ranges::random_access_range<Distances>
&& std::ranges::sized_range<Distances>)
{
if (size(distances) < size(vertices(g))) {
throw std::out_of_range(
std::format("dijkstra_shortest_paths: size of distances of {} is less than the number of vertices {}",
size(distances), size(vertices(g))));
}
}
}
template <adjacency_list G, record_for<G> Predecessor>
void validate_predecessors_record(G const& g, Predecessor& predecessor)
{
if constexpr (std::ranges::random_access_range<Predecessor>
&& std::ranges::sized_range<Predecessor>)
{
if (size(predecessor) < size(vertices(g))) {
throw std::out_of_range(
std::format("dijkstra_shortest_paths: size of predecessor of {} is less than the number of vertices {}",
size(predecessor), size(vertices(g))));
}
}
}
template <adjacency_list G>
void validate_source(G const& g, vertex_id_t<G> const& uid)
{
auto it = find_vertex(g, uid);
if (it == std::ranges::end(vertices(g)))
throw std::out_of_range("dijkstra_shortest_paths: source vertex id is out of range");
}
}

/**
* @brief Dijkstra's single-source shortest paths algorithm with a visitor.
*
Expand Down Expand Up @@ -56,88 +107,75 @@ namespace graph {
* @tparam Compare Comparison function for Distance values. Defaults to less<distance_type>.
* @tparam Combine Combine function for Distance values. Defaults to plus<DistanctValue>.
*/
template <index_adjacency_list G,
template <adjacency_list G,
input_range Sources,
random_access_range Distances,
random_access_range Predecessors,
class WF = function<range_value_t<Distances>(edge_reference_t<G>)>,
record_for<G> Distances,
record_for<G> Predecessors,
class WF = function<record_t<Distances, G>(edge_reference_t<G>)>,
class Visitor = empty_visitor,
class Compare = less<range_value_t<Distances>>,
class Combine = plus<range_value_t<Distances>>>
class Compare = less<record_t<Distances, G>>,
class Combine = plus<record_t<Distances, G>>>
requires convertible_to<range_value_t<Sources>, vertex_id_t<G>> && //
is_arithmetic_v<range_value_t<Distances>> && //
sized_range<Distances> && //
sized_range<Predecessors> && //
convertible_to<vertex_id_t<G>, range_value_t<Predecessors>> &&
basic_edge_weight_function<G, WF, range_value_t<Distances>, Compare, Combine>

convertible_to<vertex_id_t<G>, record_t<Predecessors, G>> &&
basic_edge_weight_function<G, WF, record_t<Distances, G>, Compare, Combine>
constexpr void dijkstra_shortest_paths(
G&& g,
const Sources& sources,
Distances& distances,
Predecessors& predecessor,
WF&& weight = [](edge_reference_t<G> uv) { return range_value_t<Distances>(1); }, // default weight(uv) -> 1
WF&& weight = [](edge_reference_t<G> uv) { return record_t<Distances, G>(1); }, // default weight(uv) -> 1
Visitor&& visitor = empty_visitor(),
Compare&& compare = less<range_value_t<Distances>>(),
Combine&& combine = plus<range_value_t<Distances>>()) {
Compare&& compare = less<record_t<Distances, G>>(),
Combine&& combine = plus<record_t<Distances, G>>()) {
using id_type = vertex_id_t<G>;
using distance_type = range_value_t<Distances>;
using distance_type = record_t<Distances, G>;
using weight_type = invoke_result_t<WF, edge_reference_t<G>>;

// relxing the target is the function of reducing the distance from the source to the target
auto relax_target = [&g, &predecessor, &distances, &compare, &combine] //
(edge_reference_t<G> e, vertex_id_t<G> uid, const weight_type& w_e) -> bool {
const id_type vid = target_id(g, e);
const distance_type d_u = distances[static_cast<size_t>(uid)];
const distance_type d_v = distances[static_cast<size_t>(vid)];
const distance_type d_u = detail::validated_record<G>(distances, uid);
const distance_type d_v = detail::validated_record<G>(distances, vid);

if (compare(combine(d_u, w_e), d_v)) {
distances[static_cast<size_t>(vid)] = combine(d_u, w_e);
detail::validated_record<G>(distances, vid) = combine(d_u, w_e);
if constexpr (!is_same_v<Predecessors, _null_range_type>) {
predecessor[static_cast<size_t>(vid)] = uid;
detail::validated_record<G>(predecessor, vid) = uid;
}
return true;
}
return false;
};

if (size(distances) < size(vertices(g))) {
throw std::out_of_range(
std::format("dijkstra_shortest_paths: size of distances of {} is less than the number of vertices {}",
size(distances), size(vertices(g))));
}
if constexpr (!is_same_v<Predecessors, _null_range_type>) {
if (size(predecessor) < size(vertices(g))) {
throw std::out_of_range(
std::format("dijkstra_shortest_paths: size of predecessor of {} is less than the number of vertices {}",
size(predecessor), size(vertices(g))));
}
}
detail::validate_distances_record(g, distances);
if constexpr (!is_same_v<Predecessors, _null_range_type>)
detail::validate_predecessors_record(g, predecessor);

constexpr auto zero = shortest_path_zero<distance_type>();
constexpr auto infinite = shortest_path_infinite_distance<distance_type>();

const id_type N = static_cast<id_type>(num_vertices(g));

auto qcompare = [&distances](id_type a, id_type b) {
return distances[static_cast<size_t>(a)] > distances[static_cast<size_t>(b)];
auto dist_a = detail::validated_record<G>(distances, a);
auto dist_b = detail::validated_record<G>(distances, b);
return dist_a > dist_b;
};
using Queue = std::priority_queue<id_type, std::vector<id_type>, decltype(qcompare)>;
Queue queue(qcompare);

// (The optimizer removes this loop if on_initialize_vertex() is empty.)
if constexpr (has_on_initialize_vertex<G, Visitor>) {
for (id_type uid = 0; uid < N; ++uid) {
visitor.on_initialize_vertex({uid, *find_vertex(g, uid)});
}
auto&& vtcs = vertices(g);
for (auto it = std::ranges::begin(vtcs), end = std::ranges::end(vtcs); it != end; ++it)
visitor.on_initialize_vertex({vertex_id(g, it), *it});
}

// Seed the queue with the initial vertice(s)
for (auto&& source : sources) {
if (source >= N || source < 0) {
throw std::out_of_range(std::format("dijkstra_shortest_paths: source vertex id '{}' is out of range", source));
}
detail::validate_source(g, source);
queue.push(source);
distances[static_cast<size_t>(source)] = zero; // mark source as discovered
detail::validated_record<G>(distances, source) = zero; // mark source as discovered
if constexpr (has_on_discover_vertex<G, Visitor>) {
visitor.on_discover_vertex({source, *find_vertex(g, source)});
}
Expand Down Expand Up @@ -165,7 +203,7 @@ constexpr void dijkstra_shortest_paths(
}
}

const bool is_neighbor_undiscovered = (distances[static_cast<size_t>(vid)] == infinite);
const bool is_neighbor_undiscovered = (detail::validated_record<G>(distances, vid) == infinite);
const bool was_edge_relaxed = relax_target(uv, uid, w);

if (is_neighbor_undiscovered) {
Expand Down Expand Up @@ -207,27 +245,25 @@ constexpr void dijkstra_shortest_paths(
} // while(!queue.empty())
}

template <index_adjacency_list G,
random_access_range Distances,
random_access_range Predecessors,
template <adjacency_list G,
record_for<G> Distances,
record_for<G> Predecessors,
class WF = function<range_value_t<Distances>(edge_reference_t<G>)>,
class Visitor = empty_visitor,
class Compare = less<range_value_t<Distances>>,
class Combine = plus<range_value_t<Distances>>>
requires is_arithmetic_v<range_value_t<Distances>> && //
sized_range<Distances> && //
sized_range<Predecessors> && //
convertible_to<vertex_id_t<G>, range_value_t<Predecessors>> &&
basic_edge_weight_function<G, WF, range_value_t<Distances>, Compare, Combine>
class Compare = less<record_t<Distances, G>>,
class Combine = plus<record_t<Distances, G>>>
requires
convertible_to<vertex_id_t<G>, record_t<Predecessors, G>> &&
basic_edge_weight_function<G, WF, record_t<Distances, G>, Compare, Combine>
constexpr void dijkstra_shortest_paths(
G&& g,
vertex_id_t<G> source,
Distances& distances,
Predecessors& predecessor,
WF&& weight = [](edge_reference_t<G> uv) { return range_value_t<Distances>(1); }, // default weight(uv) -> 1
Visitor&& visitor = empty_visitor(),
Compare&& compare = less<range_value_t<Distances>>(),
Combine&& combine = plus<range_value_t<Distances>>()) {
Compare&& compare = less<record_t<Distances, G>>(),
Combine&& combine = plus<record_t<Distances, G>>()) {
dijkstra_shortest_paths(g, subrange(&source, (&source + 1)), distances, predecessor, weight,
forward<Visitor>(visitor), forward<Compare>(compare), forward<Combine>(combine));
}
Expand Down Expand Up @@ -260,44 +296,41 @@ constexpr void dijkstra_shortest_paths(
*/
template <index_adjacency_list G,
input_range Sources,
random_access_range Distances,
record_for<G> Distances,
class WF = function<range_value_t<Distances>(edge_reference_t<G>)>,
class Visitor = empty_visitor,
class Compare = less<range_value_t<Distances>>,
class Combine = plus<range_value_t<Distances>>>
requires convertible_to<range_value_t<Sources>, vertex_id_t<G>> && //
sized_range<Distances> && //
is_arithmetic_v<range_value_t<Distances>> && //
basic_edge_weight_function<G, WF, range_value_t<Distances>, Compare, Combine>
basic_edge_weight_function<G, WF, record_t<Distances, G>, Compare, Combine>
constexpr void dijkstra_shortest_distances(
G&& g,
const Sources& sources,
Distances& distances,
WF&& weight = [](edge_reference_t<G> uv) { return range_value_t<Distances>(1); }, // default weight(uv) -> 1
Visitor&& visitor = empty_visitor(),
Compare&& compare = less<range_value_t<Distances>>(),
Combine&& combine = plus<range_value_t<Distances>>()) {
Compare&& compare = less<record_t<Distances, G>>(),
Combine&& combine = plus<record_t<Distances, G>>()) {
dijkstra_shortest_paths(g, sources, distances, _null_predecessors, forward<WF>(weight), forward<Visitor>(visitor),
forward<Compare>(compare), forward<Combine>(combine));
}

template <index_adjacency_list G,
random_access_range Distances,
class WF = function<range_value_t<Distances>(edge_reference_t<G>)>,
record_for<G> Distances,
class WF = function<record_t<Distances, G>(edge_reference_t<G>)>,
class Visitor = empty_visitor,
class Compare = less<range_value_t<Distances>>,
class Combine = plus<range_value_t<Distances>>>
class Compare = less<record_t<Distances, G>>,
class Combine = plus<record_t<Distances, G>>>
requires is_arithmetic_v<range_value_t<Distances>> && //
sized_range<Distances> && //
basic_edge_weight_function<G, WF, range_value_t<Distances>, Compare, Combine>
basic_edge_weight_function<G, WF, record_t<Distances, G>, Compare, Combine>
constexpr void dijkstra_shortest_distances(
G&& g,
vertex_id_t<G> source,
Distances& distances,
WF&& weight = [](edge_reference_t<G> uv) { return range_value_t<Distances>(1); }, // default weight(uv) -> 1
Visitor&& visitor = empty_visitor(),
Compare&& compare = less<range_value_t<Distances>>(),
Combine&& combine = plus<range_value_t<Distances>>()) {
Compare&& compare = less<record_t<Distances, G>>(),
Combine&& combine = plus<record_t<Distances, G>>()) {
dijkstra_shortest_paths(g, subrange(&source, (&source + 1)), distances, _null_predecessors, forward<WF>(weight),
forward<Visitor>(visitor), forward<Compare>(compare), forward<Combine>(combine));
}
Expand Down
Loading
Loading