Skip to content

Commit

Permalink
Add topological_sort (#311)
Browse files Browse the repository at this point in the history
* add static_queue for utl

* add topological_sort

* update tests
  • Loading branch information
alifahrri authored Dec 24, 2024
1 parent 88ca7e1 commit 03ad8ac
Show file tree
Hide file tree
Showing 5 changed files with 447 additions and 2 deletions.
73 changes: 72 additions & 1 deletion include/nmtools/utility/ct_digraph.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "nmtools/utility/at.hpp"
#include "nmtools/utility/ct_map.hpp"
#include "nmtools/utl/static_vector.hpp"
#include "nmtools/utl/static_queue.hpp"
#include "nmtools/assert.hpp"

namespace nmtools::utility
Expand Down Expand Up @@ -103,6 +104,27 @@ namespace nmtools::utility
});

return nmtools_tuple{list,id_map};
} // adjacency_list

template <typename adjacency_list_t>
constexpr auto in_degree(const adjacency_list_t& list)
{
// assume static_vector or array
// TODO: check for another type
constexpr auto N = meta::bounded_size_v<typename adjacency_list_t::value_type>;

using result_t = utl::static_vector<nm_size_t,N>;
auto result = result_t {};

auto n = len(list);
result.resize(n);

for (nm_size_t i=0; i<(nm_size_t)n; i++) {
for (nm_size_t j=0; j<(nm_size_t)list[i].size(); j++) {
result[list[i][j]]++;
}
}
return result;
}

template <typename nodes_t=nmtools_tuple<>, typename edges_t=nmtools_tuple<>, typename node_data_t=nmtools_tuple<>>
Expand Down Expand Up @@ -354,6 +376,55 @@ namespace nmtools::utility

return result;
}
}

template <typename adjacency_list_t>
constexpr auto topological_sort(const adjacency_list_t& adjacency_list)
{
// assume static_vector or array
// TODO: check for another type
constexpr auto N = meta::bounded_size_v<typename adjacency_list_t::value_type>;

auto in_degree = utility::in_degree(adjacency_list);

auto queue = utl::static_queue<nm_size_t,N>();
auto result = utl::static_vector<nm_size_t,N>();

// initialize queue with input (in_degree 0)
for (nm_size_t i=0; i<adjacency_list.size(); i++) {
if (in_degree[i] == 0) {
queue.push(i);
}
}
for (; !queue.empty();) {
auto node = queue.pop();
result.push_back(node);
for (nm_size_t j=0; j<(nm_size_t)adjacency_list[node].size(); j++) {
auto neighbor = adjacency_list[node][j];
in_degree[neighbor] -= 1;
if (in_degree[neighbor] == 0) {
queue.push(neighbor);
}
}
}

return result;
} // topological_sort

template <typename nodes_t, typename edges_t, typename node_data_t>
constexpr auto topological_sort(const ct_digraph<nodes_t,edges_t,node_data_t>& graph)
{
constexpr auto adjacency_result = adjacency_list(decltype(graph.digraph){});
constexpr auto adjacency_list = nmtools::get<0>(adjacency_result);
constexpr auto src_id_map = nmtools::get<1>(adjacency_result);

constexpr auto sorted = topological_sort(adjacency_list);
constexpr auto N = sorted.size();

return meta::template_reduce<N>([&](auto init, auto index){
auto node_id = meta::ct_v<at(src_id_map,at(sorted,index))>;
return tuple_append(init,node_id);
},nmtools_tuple{});
}
} // nmtools::utility

#endif // NMTOOLS_UTILITY_CT_DIGRAPH_HPP
69 changes: 69 additions & 0 deletions include/nmtools/utl/static_queue.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
#ifndef NMTOOLS_UTL_STATIC_QUEUE_HPP
#define NMTOOLS_UTL_STATIC_QUEUE_HPP

#include "nmtools/def.hpp"
#include "nmtools/platform.hpp"
#include "nmtools/assert.hpp"
#include "nmtools/utl/common.hpp"
#include "nmtools/utl/array.hpp"
#include "nmtools/meta/bits/array/resize_bounded_size.hpp"

namespace nmtools::utl
{
template <typename T, nm_size_t N>
struct static_queue
{
using buffer_type = utl::array<T,N>;
using size_type = nm_size_t;

buffer_type buffer_;
size_type size_;

constexpr static_queue()
: buffer_{}
, size_{0}
{}

constexpr auto front() const noexcept
{
return buffer_[0];
}

constexpr auto back() const noexcept
{
return buffer_[size_-1];
}

constexpr auto size() const noexcept
{
return size_;
}

constexpr auto empty() const noexcept
{
return size_ == 0;
}

constexpr auto push(const T& t)
{
nmtools_assert( size_ < N
, "static_queue already full");

size_ += 1;
buffer_[size_-1] = t;
}

constexpr auto pop()
{
size_ -= 1;
auto res = buffer_[0];
// simply move all elements
for (nm_size_t i=0; i<(nm_size_t)size_; i++) {
buffer_[i] = buffer_[i+1];
}
return res;
}
};
}

#endif // NMTOOLS_UTL_STATIC_QUEUE_HPP
Loading

0 comments on commit 03ad8ac

Please sign in to comment.