mirror of
https://git.rwth-aachen.de/acs/public/villas/node/
synced 2025-03-09 00:00:00 +01:00
lib: add dependency graph implementation
This commit is contained in:
parent
e590d1a350
commit
35d96ed277
2 changed files with 162 additions and 0 deletions
55
fpga/include/villas/dependency_graph.hpp
Normal file
55
fpga/include/villas/dependency_graph.hpp
Normal file
|
@ -0,0 +1,55 @@
|
|||
#ifndef VILLAS_DEPENDENCY_GRAPH_HPP
|
||||
#define VILLAS_DEPENDENCY_GRAPH_HPP
|
||||
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <algorithm>
|
||||
|
||||
#include "log.hpp"
|
||||
|
||||
namespace villas {
|
||||
namespace utils {
|
||||
|
||||
|
||||
template<typename T>
|
||||
class DependencyGraph {
|
||||
public:
|
||||
using NodeList = std::list<T>;
|
||||
|
||||
/// Create a node without dependencies if it not yet exists, return if a new
|
||||
/// node has been created.
|
||||
bool addNode(const T& node);
|
||||
|
||||
/// Remove a node and all other nodes that depend on it
|
||||
void removeNode(const T& node);
|
||||
|
||||
/// Add a dependency to a node. Will create the node if it not yet exists
|
||||
void addDependency(const T& node, const T& dependency);
|
||||
|
||||
void dump();
|
||||
|
||||
/// Return a sequential evaluation order list. If a circular dependency has been
|
||||
/// detected, all nodes involved will not be part of that list.
|
||||
NodeList getEvaluationOrder() const;
|
||||
|
||||
private:
|
||||
/// Return whether a node already exists or not
|
||||
bool nodeExists(const T& node)
|
||||
{ return graph.find(node) != graph.end(); }
|
||||
|
||||
static bool
|
||||
nodeInList(const NodeList& list, const T& node)
|
||||
{ return list.end() != std::find(list.begin(), list.end(), node); }
|
||||
|
||||
private:
|
||||
using Graph = std::map<T, NodeList>;
|
||||
|
||||
Graph graph;
|
||||
};
|
||||
|
||||
} // namespace utils
|
||||
} // namespace villas
|
||||
|
||||
#include "dependency_graph_impl.hpp"
|
||||
|
||||
#endif // VILLAS_DEPENDENCY_GRAPH_HPP
|
107
fpga/include/villas/dependency_graph_impl.hpp
Normal file
107
fpga/include/villas/dependency_graph_impl.hpp
Normal file
|
@ -0,0 +1,107 @@
|
|||
#ifndef VILLAS_DEPENDENCY_GRAPH_HPP
|
||||
#error "Do not include this file directly, please include depedency_graph.hpp"
|
||||
#endif
|
||||
|
||||
#include "dependency_graph.hpp"
|
||||
#include <sstream>
|
||||
|
||||
namespace villas {
|
||||
namespace utils {
|
||||
|
||||
template<typename T>
|
||||
bool
|
||||
DependencyGraph<T>::addNode(const T &node)
|
||||
{
|
||||
bool existedBefore = nodeExists(node);
|
||||
|
||||
// accessing is enough to create if not exists
|
||||
graph[node];
|
||||
|
||||
return existedBefore;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void
|
||||
DependencyGraph<T>::removeNode(const T &node)
|
||||
{
|
||||
graph.erase(node);
|
||||
|
||||
// check if other nodes depend on this one
|
||||
for(auto& [key, dependencies] : graph) {
|
||||
if(nodeInList(dependencies, node)) {
|
||||
// remove other node that depends on the one to delete
|
||||
removeNode(key);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void
|
||||
DependencyGraph<T>::addDependency(const T &node, const T &dependency)
|
||||
{
|
||||
NodeList& dependencies = graph[node];
|
||||
if(not nodeInList(dependencies, dependency))
|
||||
dependencies.push_back(dependency);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void
|
||||
DependencyGraph<T>::dump() {
|
||||
for(auto& node : graph) {
|
||||
std::stringstream ss;
|
||||
for(auto& dep : node.second) {
|
||||
ss << dep << " ";
|
||||
}
|
||||
cpp_debug << node.first << ": " << ss.str();
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
typename DependencyGraph<T>::NodeList
|
||||
DependencyGraph<T>::getEvaluationOrder() const
|
||||
{
|
||||
// copy graph to preserve information (we have to delete entries later)
|
||||
Graph graph = this->graph;
|
||||
|
||||
// output list
|
||||
NodeList out;
|
||||
|
||||
while(graph.size() > 0) {
|
||||
int added = 0;
|
||||
|
||||
// look for nodes with no dependencies
|
||||
for(auto& [key, dependencies] : graph) {
|
||||
|
||||
for(auto dep = dependencies.begin(); dep != dependencies.end(); ++dep) {
|
||||
if(nodeInList(out, *dep)) {
|
||||
// dependency has been pushed to list in last round
|
||||
dep = dependencies.erase(dep);
|
||||
}
|
||||
}
|
||||
|
||||
// nodes with no dependencies can be pushed to list
|
||||
if(dependencies.empty()) {
|
||||
out.push_back(key);
|
||||
graph.erase(key);
|
||||
added++;
|
||||
}
|
||||
}
|
||||
|
||||
// if a round doesn't add any elements and is not the last, then
|
||||
// there is a circular dependency
|
||||
if(added == 0 and graph.size() > 0) {
|
||||
cpp_error << "Circular dependency detected! IPs not available:";
|
||||
Logger::Indenter indent = cpp_debug.indent();
|
||||
for(auto& [key, value] : graph) {
|
||||
cpp_error << key;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
} // namespace utils
|
||||
} // namespace villas
|
Loading…
Add table
Reference in a new issue