Merge pull request #2381 from polgreen/depth_limited_search

Depth limited search
This commit is contained in:
Michael Tautschnig 2018-06-27 07:28:52 +01:00 committed by GitHub
commit b9014de31f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 186 additions and 0 deletions

View File

@ -70,3 +70,29 @@ std::set<irep_idt> get_reaching_functions(
{ {
return get_connected_functions(graph, function, false); return get_connected_functions(graph, function, false);
} }
std::set<irep_idt> get_functions_reachable_within_n_steps(
const call_grapht::directed_grapht &graph,
const std::set<irep_idt> &start_functions,
std::size_t &n)
{
std::vector<std::size_t> start_indices;
std::set<irep_idt> result;
for(const auto &func : start_functions)
start_indices.push_back(*(graph.get_node_index(func)));
for(const auto &index : graph.depth_limited_search(start_indices, n))
result.insert(graph[index].function);
return result;
}
std::set<irep_idt> get_functions_reachable_within_n_steps(
const call_grapht::directed_grapht &graph,
const irep_idt &start_function,
std::size_t &n)
{
std::set<irep_idt> start_functions({ start_function });
return get_functions_reachable_within_n_steps(graph, start_functions, n);
}

View File

@ -49,4 +49,28 @@ std::set<irep_idt> get_reachable_functions(
std::set<irep_idt> get_reaching_functions( std::set<irep_idt> get_reaching_functions(
const call_grapht::directed_grapht &graph, const irep_idt &function); const call_grapht::directed_grapht &graph, const irep_idt &function);
/// Get either callers or callees reachable from a given
/// list of functions within N steps
/// \param graph: call graph
/// \param start_functions: set of start functions
/// \param n: number of steps to consider
/// \return set of functions that can be reached from the start function
/// including the start function
std::set<irep_idt> get_functions_reachable_within_n_steps(
const call_grapht::directed_grapht &graph,
const std::set<irep_idt> &start_functions,
std::size_t &n);
/// Get either callers or callees reachable from a given
/// list of functions within N steps
/// \param graph: call graph
/// \param start_function: single start function
/// \param n: number of steps to consider
/// \return set of functions that can be reached from the start function
/// including the start function
std::set<irep_idt> get_functions_reachable_within_n_steps(
const call_grapht::directed_grapht &graph,
const irep_idt &start_function,
std::size_t &n);
#endif #endif

View File

@ -255,6 +255,18 @@ public:
void disconnect_unreachable(node_indext src); void disconnect_unreachable(node_indext src);
void disconnect_unreachable(const std::vector<node_indext> &src); void disconnect_unreachable(const std::vector<node_indext> &src);
std::vector<node_indext> depth_limited_search(
const node_indext &src,
std::size_t &limit,
bool forwards) const;
std::vector<typename N::node_indext>
depth_limited_search(typename N::node_indext src, std::size_t limit) const;
std::vector<typename N::node_indext> depth_limited_search(
std::vector<typename N::node_indext> &src,
std::size_t limit) const;
void make_chordal(); void make_chordal();
// return value: number of connected subgraphs // return value: number of connected subgraphs
@ -278,6 +290,11 @@ public:
std::function<void(const node_indext &)> f) const; std::function<void(const node_indext &)> f) const;
protected: protected:
std::vector<typename N::node_indext> depth_limited_search(
std::vector<typename N::node_indext> &src,
std::size_t limit,
std::vector<bool> &visited) const;
class tarjant class tarjant
{ {
public: public:
@ -584,6 +601,83 @@ std::vector<typename N::node_indext> grapht<N>::get_reachable(
return result; return result;
} }
/// Run recursive depth-limited search on the graph, starting
/// from multiple source nodes, to find the nodes reachable within n steps.
/// This function initialises the search.
/// \param src The node to start the search from.
/// \param limit limit on steps
/// \return a vector of reachable node indices
template <class N>
std::vector<typename N::node_indext> grapht<N>::depth_limited_search(
const typename N::node_indext src,
std::size_t limit) const
{
std::vector<node_indext> start_vector(1, src);
return depth_limited_search(start_vector, limit);
}
/// Run recursive depth-limited search on the graph, starting
/// from multiple source nodes, to find the nodes reachable within n steps.
/// This function initialises the search.
/// \param src The nodes to start the search from.
/// \param limit limit on steps
/// \return a vector of reachable node indices
template <class N>
std::vector<typename N::node_indext> grapht<N>::depth_limited_search(
std::vector<typename N::node_indext> &src,
std::size_t limit) const
{
std::vector<bool> visited(nodes.size(), false);
for(const auto &node : src)
{
PRECONDITION(node < nodes.size());
visited[node] = true;
}
return depth_limited_search(src, limit, visited);
}
/// Run recursive depth-limited search on the graph, starting
// from multiple source nodes, to find the nodes reachable within n steps
/// \param src The nodes to start the search from.
/// \param limit limit on steps
/// \param visited vector of booleans indicating whether a node has been visited
/// \return a vector of reachable node indices
template <class N>
std::vector<typename N::node_indext> grapht<N>::depth_limited_search(
std::vector<typename N::node_indext> &src,
std::size_t limit,
std::vector<bool> &visited) const
{
if(limit == 0)
return src;
std::vector<node_indext> next_ring;
for(const auto &n : src)
{
for(const auto &o : nodes[n].out)
{
if(!visited[o.first])
{
next_ring.push_back(o.first);
visited[o.first] = true;
}
}
}
if(next_ring.empty())
return src;
limit--;
for(const auto &succ : depth_limited_search(next_ring, limit, visited))
src.push_back(succ);
return src;
}
template<class N> template<class N>
std::size_t grapht<N>::connected_subgraphs( std::size_t grapht<N>::connected_subgraphs(
std::vector<node_indext> &subgraph_nr) std::vector<node_indext> &subgraph_nr)

View File

@ -223,6 +223,48 @@ SCENARIO("call_graph",
REQUIRE(exported.has_edge(nodes_by_name["B"], nodes_by_name["D"])); REQUIRE(exported.has_edge(nodes_by_name["B"], nodes_by_name["D"]));
} }
THEN("We expect {A,B} to be reachable from {A} in 1 step")
{
irep_idt function_name = "A";
std::size_t depth = 1;
std::set<irep_idt> reachable = get_functions_reachable_within_n_steps(
exported, function_name, depth);
REQUIRE(reachable.size() == 2);
REQUIRE(reachable.count("A"));
REQUIRE(reachable.count("B"));
}
THEN("We expect {A,B,C,D} to be reachable from {A} in 2 and 3 steps")
{
irep_idt function_name = "A";
std::size_t depth = 2;
std::set<irep_idt> reachable = get_functions_reachable_within_n_steps(
exported, function_name, depth);
REQUIRE(reachable.size() == 4);
REQUIRE(reachable.count("A"));
REQUIRE(reachable.count("B"));
REQUIRE(reachable.count("C"));
REQUIRE(reachable.count("D"));
depth = 3;
reachable = get_functions_reachable_within_n_steps(
exported, function_name, depth);
REQUIRE(reachable.size() == 4);
REQUIRE(reachable.count("A"));
REQUIRE(reachable.count("B"));
REQUIRE(reachable.count("C"));
REQUIRE(reachable.count("D"));
}
THEN("We expect only {A} to be reachable from {A} in 0 steps")
{
irep_idt function_name = "A";
std::size_t depth = 0;
std::set<irep_idt> reachable = get_functions_reachable_within_n_steps(
exported, function_name, depth);
REQUIRE(reachable.size() == 1);
REQUIRE(reachable.count("A"));
}
THEN("We expect A to have successors {A, B}") THEN("We expect A to have successors {A, B}")
{ {
std::set<irep_idt> successors = get_callees(exported, "A"); std::set<irep_idt> successors = get_callees(exported, "A");