Skip to content

Commit

Permalink
YT-CPPGL-54: Add vertex degree map getters
Browse files Browse the repository at this point in the history
- Added the `in_degree_map`, `out_degree_map` and `degree_map` functions to the graph class
- Replaced per vertex in-degree calculating in the `topological_sort` algorithm with the `in_degree_map` function
  • Loading branch information
SpectraL519 authored Nov 28, 2024
1 parent 3f185d8 commit 9b3ed9c
Show file tree
Hide file tree
Showing 10 changed files with 387 additions and 59 deletions.
12 changes: 12 additions & 0 deletions docs/graph.md
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,10 @@ Based on the specified traits, the `graph` class defines the following types:
- `vertex_id: types::id_type` – the ID of the vertex for which to calculate the in-degree.
- *Return type*: `types::size_type`

- **`graph.in_degree_map() const`**:
- *Description*: Returns a vector containing the in-degrees of the corresponding vertices (in-degree at index `i` corresponds to the vertex with an ID equal `i`).
- *Return type*: `std::vector<types::size_type>`

- **`graph.out_degree(vertex) const`**:
- *Description*: Returns the out-degree (number of outgoing edges) of the specified vertex.
- *Returned value*:
Expand All @@ -261,6 +265,10 @@ Based on the specified traits, the `graph` class defines the following types:
- `vertex_id: types::id_type` – the ID of the vertex for which to calculate the out-degree.
- *Return type*: `types::size_type`

- **`graph.out_degree_map() const`**:
- *Description*: Returns a vector containing the out-degrees of the corresponding vertices (out-degree at index `i` corresponds to the vertex with an ID equal `i`).
- *Return type*: `std::vector<types::size_type>`

- **`graph.degree(vertex) const`**:
- *Description*: Returns the degree (number of incoming and outgoing edges) of the specified vertex.
- *Returned value*:
Expand All @@ -284,6 +292,10 @@ Based on the specified traits, the `graph` class defines the following types:
- `vertex_id: types::id_type` – the ID of the vertex for which to calculate the degree.
- *Return type*: `types::size_type`

- **`graph.degree_map() const`**:
- *Description*: Returns a vector containing the degrees of the corresponding vertices (degree at index `i` corresponds to the vertex with an ID equal `i`).
- *Return type*: `std::vector<types::size_type>`

<br />

### Edge Operations
Expand Down
13 changes: 5 additions & 8 deletions include/gl/algorithm/topological_sort.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,14 @@ template <

const auto vertex_ids = graph.vertex_ids();

// prepare the vertex in degree list
std::vector<types::size_type> vertex_in_deg_list;
vertex_in_deg_list.reserve(graph.n_vertices());
for (const auto id : vertex_ids)
vertex_in_deg_list.push_back(graph.in_degree(id));
// prepare the vertex in degree map
std::vector<types::size_type> in_degree_map = graph.in_degree_map();

// prepare the initial queue content (source vertices)
std::vector<algorithm::vertex_info> source_vertex_list;
source_vertex_list.reserve(graph.n_vertices());
for (const auto id : vertex_ids)
if (vertex_in_deg_list[id] == constants::default_size)
if (in_degree_map[id] == constants::default_size)
source_vertex_list.emplace_back(id);

std::optional<std::vector<types::id_type>> topological_order_opt =
Expand All @@ -52,11 +49,11 @@ template <
topological_order.push_back(vertex.id());
return true;
},
[&vertex_in_deg_list](const vertex_type& vertex, const edge_type& in_edge)
[&in_degree_map](const vertex_type& vertex, const edge_type& in_edge)
-> std::optional<bool> { // enqueue predicate
if (in_edge.is_loop())
return false;
return --vertex_in_deg_list[vertex.id()] == constants::default_size;
return --in_degree_map[vertex.id()] == constants::default_size;
},
pre_visit,
post_visit
Expand Down
12 changes: 12 additions & 0 deletions include/gl/graph.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,10 @@ class graph final {
return this->_impl.in_degree(vertex_id);
}

[[nodiscard]] gl_attr_force_inline std::vector<types::size_type> in_degree_map() const {
return this->_impl.in_degree_map();
}

[[nodiscard]] gl_attr_force_inline types::size_type out_degree(const vertex_type& vertex
) const {
this->_verify_vertex(vertex);
Expand All @@ -199,6 +203,10 @@ class graph final {
return this->_impl.out_degree(vertex_id);
}

[[nodiscard]] gl_attr_force_inline std::vector<types::size_type> out_degree_map() const {
return this->_impl.out_degree_map();
}

[[nodiscard]] gl_attr_force_inline types::size_type degree(const vertex_type& vertex) const {
this->_verify_vertex(vertex);
return this->_impl.degree(vertex.id());
Expand All @@ -210,6 +218,10 @@ class graph final {
return this->_impl.degree(vertex_id);
}

[[nodiscard]] gl_attr_force_inline std::vector<types::size_type> degree_map() const {
return this->_impl.degree_map();
}

// --- edge methods ---

// clang-format off
Expand Down
12 changes: 12 additions & 0 deletions include/gl/impl/adjacency_list.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,18 @@ class adjacency_list final {
return specialized_impl::degree(*this, vertex_id);
}

[[nodiscard]] gl_attr_force_inline std::vector<types::size_type> in_degree_map() const {
return specialized_impl::in_degree_map(*this);
}

[[nodiscard]] gl_attr_force_inline std::vector<types::size_type> out_degree_map() const {
return specialized_impl::out_degree_map(*this);
}

[[nodiscard]] gl_attr_force_inline std::vector<types::size_type> degree_map() const {
return specialized_impl::degree_map(*this);
}

gl_attr_force_inline void remove_vertex(const vertex_type& vertex) {
specialized_impl::remove_vertex(*this, vertex);
}
Expand Down
12 changes: 12 additions & 0 deletions include/gl/impl/adjacency_matrix.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,18 @@ class adjacency_matrix final {
return specialized_impl::degree(*this, vertex_id);
}

[[nodiscard]] gl_attr_force_inline std::vector<types::size_type> in_degree_map() const {
return specialized_impl::in_degree_map(*this);
}

[[nodiscard]] gl_attr_force_inline std::vector<types::size_type> out_degree_map() const {
return specialized_impl::out_degree_map(*this);
}

[[nodiscard]] gl_attr_force_inline std::vector<types::size_type> degree_map() const {
return specialized_impl::degree_map(*this);
}

gl_attr_force_inline void remove_vertex(const vertex_type& vertex) {
specialized_impl::remove_vertex(*this, vertex.id());
}
Expand Down
68 changes: 63 additions & 5 deletions include/gl/impl/specialized/adjacency_list.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,8 @@ struct directed_adjacency_list {
const impl_type& self, const types::id_type vertex_id
) {
types::size_type in_deg = constants::default_size;
for (types::id_type i = constants::initial_id; i < self._list.size(); ++i) {
const auto& adj_edges = self._list[i];
for (types::id_type id = constants::initial_id; id < self._list.size(); ++id) {
const auto& adj_edges = self._list[id];
if (adj_edges.empty())
continue;

Expand All @@ -92,13 +92,49 @@ struct directed_adjacency_list {
return in_degree(self, vertex_id) + out_degree(self, vertex_id);
}

[[nodiscard]] static std::vector<types::size_type> in_degree_map(const impl_type& self) {
std::vector<types::id_type> in_degree_map(self._list.size(), constants::zero);

for (types::id_type id = constants::initial_id; id < self._list.size(); ++id) {
std::ranges::for_each(self._list[id], [&in_degree_map](const auto& edge) {
++in_degree_map[edge->second_id()];
});
}

return in_degree_map;
}

[[nodiscard]] gl_attr_force_inline static std::vector<types::size_type> out_degree_map(
const impl_type& self
) {
const auto out_degree_view =
self._list
| std::views::transform([](const auto& adj_edges) { return adj_edges.size(); });
return std::vector<types::size_type>(out_degree_view.begin(), out_degree_view.end());
}

[[nodiscard]] static std::vector<types::size_type> degree_map(const impl_type& self) {
std::vector<types::id_type> degree_map(self._list.size(), constants::zero);

for (types::id_type id = constants::initial_id; id < self._list.size(); ++id) {
degree_map[id] += self._list[id].size();

// update in degrees
std::ranges::for_each(self._list[id], [&degree_map](const auto& edge) {
++degree_map[edge->second_id()];
});
}

return degree_map;
}

static void remove_vertex(impl_type& self, const vertex_type& vertex) {
const auto vertex_id = vertex.id();

// remove all edges incident to the vertex
for (types::id_type i = constants::initial_id; i < self._list.size(); ++i) {
auto& adj_edges = self._list[i];
if (i == vertex_id or adj_edges.empty())
for (types::id_type id = constants::initial_id; id < self._list.size(); ++id) {
auto& adj_edges = self._list[id];
if (id == vertex_id or adj_edges.empty())
continue;

const auto rem_subrange = std::ranges::remove_if(
Expand Down Expand Up @@ -194,6 +230,28 @@ struct undirected_adjacency_list {
return degree;
}

[[nodiscard]] gl_attr_force_inline static std::vector<types::size_type> in_degree_map(
const impl_type& self
) {
return degree_map(self);
}

[[nodiscard]] gl_attr_force_inline static std::vector<types::size_type> out_degree_map(
const impl_type& self
) {
return degree_map(self);
}

[[nodiscard]] static std::vector<types::size_type> degree_map(const impl_type& self) {
std::vector<types::size_type> degree_map;
degree_map.reserve(self._list.size());

for (types::id_type id = constants::initial_id; id < self._list.size(); ++id)
degree_map.push_back(degree(self, id));

return degree_map;
}

static void remove_vertex(impl_type& self, const vertex_type& vertex) {
const auto vertex_id = vertex.id();
std::unordered_set<types::id_type> incident_vertex_id_set;
Expand Down
65 changes: 65 additions & 0 deletions include/gl/impl/specialized/adjacency_matrix.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,43 @@ struct directed_adjacency_matrix {
return in_degree(self, vertex_id) + out_degree(self, vertex_id);
}

[[nodiscard]] static std::vector<types::size_type> in_degree_map(const impl_type& self) {
std::vector<types::id_type> in_degree_map(self._matrix.size(), constants::zero);

for (const auto& row : self._matrix)
for (types::id_type id = constants::initial_id; id < self._matrix.size(); ++id)
if (row[id] != nullptr)
++in_degree_map[id];

return in_degree_map;
}

[[nodiscard]] static std::vector<types::size_type> out_degree_map(const impl_type& self) {
std::vector<types::id_type> out_degree_map;
out_degree_map.reserve(self._matrix.size());

for (types::id_type id = constants::initial_id; id < self._matrix.size(); ++id)
out_degree_map.push_back(out_degree(self, id));

return out_degree_map;
}

[[nodiscard]] static std::vector<types::size_type> degree_map(const impl_type& self) {
std::vector<types::id_type> degree_map(self._matrix.size(), constants::zero);

for (types::id_type u_id = constants::initial_id; u_id < self._matrix.size(); ++u_id) {
const auto& row = self._matrix[u_id];
for (types::id_type v_id = constants::initial_id; v_id < self._matrix.size(); ++v_id) {
if (row[v_id] != nullptr) {
++degree_map[u_id];
++degree_map[v_id];
}
}
}

return degree_map;
}

static void remove_vertex(impl_type& self, const types::id_type vertex_id) {
self._n_unique_edges -= self.adjacent_edges(vertex_id).distance();
self._matrix.erase(std::next(std::begin(self._matrix), vertex_id));
Expand Down Expand Up @@ -152,6 +189,34 @@ struct undirected_adjacency_matrix {
return degree;
}

[[nodiscard]] gl_attr_force_inline static std::vector<types::size_type> in_degree_map(
const impl_type& self
) {
return degree_map(self);
}

[[nodiscard]] gl_attr_force_inline static std::vector<types::size_type> out_degree_map(
const impl_type& self
) {
return degree_map(self);
}

[[nodiscard]] static std::vector<types::size_type> degree_map(const impl_type& self) {
std::vector<types::id_type> degree_map(self._matrix.size(), constants::zero);

for (types::id_type u_id = constants::initial_id; u_id < self._matrix.size(); ++u_id) {
const auto& row = self._matrix[u_id];
for (types::id_type v_id = constants::initial_id; v_id <= u_id; ++v_id) {
if (row[v_id] != nullptr) {
++degree_map[u_id];
++degree_map[v_id];
}
}
}

return degree_map;
}

static void remove_vertex(impl_type& self, const types::id_type vertex_id) {
self._n_unique_edges -= self.adjacent_edges(vertex_id).distance();
self._matrix.erase(std::next(std::begin(self._matrix), vertex_id));
Expand Down
Loading

0 comments on commit 9b3ed9c

Please sign in to comment.