|
|
|
|
|
#ifndef DLIB_GRAPH_UTILs_ |
|
#define DLIB_GRAPH_UTILs_ |
|
|
|
#include "../algs.h" |
|
#include <vector> |
|
#include "graph_utils_abstract.h" |
|
#include "../is_kind.h" |
|
#include "../enable_if.h" |
|
#include <algorithm> |
|
#include "../set.h" |
|
#include "../memory_manager.h" |
|
#include "../set_utils.h" |
|
|
|
namespace dlib |
|
{ |
|
|
|
|
|
|
|
template <typename T> |
|
typename enable_if<is_graph<T>,typename T::edge_type>::type& edge( |
|
T& g, |
|
unsigned long idx_i, |
|
unsigned long idx_j |
|
) |
|
{ |
|
|
|
DLIB_ASSERT(g.has_edge(idx_i,idx_j) == true, |
|
"\tT::edge_type& edge(g, idx_i, idx_j)" |
|
<< "\n\t you have requested an invalid edge" |
|
<< "\n\t idx_i: " << idx_i |
|
<< "\n\t idx_j: " << idx_j |
|
); |
|
|
|
for (unsigned long i = 0; i < g.node(idx_i).number_of_neighbors(); ++i) |
|
{ |
|
if (g.node(idx_i).neighbor(i).index() == idx_j) |
|
return g.node(idx_i).edge(i); |
|
} |
|
|
|
|
|
|
|
DLIB_CASSERT(false, |
|
"\tT::edge_type& edge(g, idx_i, idx_j)" |
|
<< "\n\t you have requested an invalid edge" |
|
<< "\n\t idx_i: " << idx_i |
|
<< "\n\t idx_j: " << idx_j |
|
); |
|
} |
|
|
|
template <typename T> |
|
const typename enable_if<is_graph<T>,typename T::edge_type>::type& edge( |
|
const T& g, |
|
unsigned long idx_i, |
|
unsigned long idx_j |
|
) |
|
{ |
|
|
|
DLIB_ASSERT(g.has_edge(idx_i,idx_j) == true, |
|
"\tT::edge_type& edge(g, idx_i, idx_j)" |
|
<< "\n\t you have requested an invalid edge" |
|
<< "\n\t idx_i: " << idx_i |
|
<< "\n\t idx_j: " << idx_j |
|
); |
|
|
|
for (unsigned long i = 0; i < g.node(idx_i).number_of_neighbors(); ++i) |
|
{ |
|
if (g.node(idx_i).neighbor(i).index() == idx_j) |
|
return g.node(idx_i).edge(i); |
|
} |
|
|
|
|
|
|
|
DLIB_CASSERT(false, |
|
"\tT::edge_type& edge(g, idx_i, idx_j)" |
|
<< "\n\t you have requested an invalid edge" |
|
<< "\n\t idx_i: " << idx_i |
|
<< "\n\t idx_j: " << idx_j |
|
); |
|
} |
|
|
|
|
|
|
|
template <typename T> |
|
typename enable_if<is_directed_graph<T>,typename T::edge_type>::type& edge( |
|
T& g, |
|
unsigned long parent_idx, |
|
unsigned long child_idx |
|
) |
|
{ |
|
|
|
DLIB_ASSERT(g.has_edge(parent_idx,child_idx) == true, |
|
"\t T::edge_type& edge(g, parent_idx, child_idx)" |
|
<< "\n\t you have requested an invalid edge" |
|
<< "\n\t parent_idx: " << parent_idx |
|
<< "\n\t child_idx: " << child_idx |
|
); |
|
|
|
for (unsigned long i = 0; i < g.node(parent_idx).number_of_children(); ++i) |
|
{ |
|
if (g.node(parent_idx).child(i).index() == child_idx) |
|
return g.node(parent_idx).child_edge(i); |
|
} |
|
|
|
|
|
|
|
DLIB_CASSERT(false, |
|
"\t T::edge_type& edge(g, parent_idx, child_idx)" |
|
<< "\n\t you have requested an invalid edge" |
|
<< "\n\t parent_idx: " << parent_idx |
|
<< "\n\t child_idx: " << child_idx |
|
); |
|
} |
|
|
|
template <typename T> |
|
const typename enable_if<is_directed_graph<T>,typename T::edge_type>::type& edge( |
|
const T& g, |
|
unsigned long parent_idx, |
|
unsigned long child_idx |
|
) |
|
{ |
|
|
|
DLIB_ASSERT(g.has_edge(parent_idx,child_idx) == true, |
|
"\t T::edge_type& edge(g, parent_idx, child_idx)" |
|
<< "\n\t you have requested an invalid edge" |
|
<< "\n\t parent_idx: " << parent_idx |
|
<< "\n\t child_idx: " << child_idx |
|
); |
|
|
|
for (unsigned long i = 0; i < g.node(parent_idx).number_of_children(); ++i) |
|
{ |
|
if (g.node(parent_idx).child(i).index() == child_idx) |
|
return g.node(parent_idx).child_edge(i); |
|
} |
|
|
|
|
|
|
|
DLIB_ASSERT(false, |
|
"\t T::edge_type& edge(g, parent_idx, child_idx)" |
|
<< "\n\t you have requested an invalid edge" |
|
<< "\n\t parent_idx: " << parent_idx |
|
<< "\n\t child_idx: " << child_idx |
|
); |
|
} |
|
|
|
|
|
|
|
namespace graph_helpers |
|
{ |
|
template <typename T, typename U> |
|
inline bool is_same_object ( |
|
const T& a, |
|
const U& b |
|
) |
|
{ |
|
if (is_same_type<const T,const U>::value == false) |
|
return false; |
|
if ((void*)&a == (void*)&b) |
|
return true; |
|
else |
|
return false; |
|
} |
|
|
|
template < |
|
typename T |
|
> |
|
bool search_for_directed_cycles ( |
|
const T& node, |
|
std::vector<bool>& visited, |
|
std::vector<bool>& temp |
|
) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
{ |
|
if (temp[node.index()] == true) |
|
return true; |
|
|
|
visited[node.index()] = true; |
|
temp[node.index()] = true; |
|
|
|
for (unsigned long i = 0; i < node.number_of_children(); ++i) |
|
{ |
|
if (search_for_directed_cycles(node.child(i), visited, temp)) |
|
return true; |
|
} |
|
|
|
temp[node.index()] = false; |
|
|
|
return false; |
|
} |
|
|
|
|
|
|
|
template < |
|
typename T |
|
> |
|
typename enable_if<is_directed_graph<typename T::graph_type>,bool>::type search_for_undirected_cycles ( |
|
const T& node, |
|
std::vector<bool>& visited, |
|
unsigned long prev = std::numeric_limits<unsigned long>::max() |
|
) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
{ |
|
using namespace std; |
|
if (visited[node.index()] == true) |
|
return true; |
|
|
|
visited[node.index()] = true; |
|
|
|
for (unsigned long i = 0; i < node.number_of_children(); ++i) |
|
{ |
|
if (node.child(i).index() != prev && |
|
search_for_undirected_cycles(node.child(i), visited, node.index())) |
|
return true; |
|
} |
|
|
|
for (unsigned long i = 0; i < node.number_of_parents(); ++i) |
|
{ |
|
if (node.parent(i).index() != prev && |
|
search_for_undirected_cycles(node.parent(i), visited, node.index())) |
|
return true; |
|
} |
|
|
|
return false; |
|
} |
|
|
|
|
|
|
|
template < |
|
typename T |
|
> |
|
typename enable_if<is_graph<typename T::graph_type>,bool>::type search_for_undirected_cycles ( |
|
const T& node, |
|
std::vector<bool>& visited, |
|
unsigned long prev = std::numeric_limits<unsigned long>::max() |
|
) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
{ |
|
using namespace std; |
|
if (visited[node.index()] == true) |
|
return true; |
|
|
|
visited[node.index()] = true; |
|
|
|
for (unsigned long i = 0; i < node.number_of_neighbors(); ++i) |
|
{ |
|
if (node.neighbor(i).index() != prev && |
|
search_for_undirected_cycles(node.neighbor(i), visited, node.index())) |
|
return true; |
|
} |
|
|
|
return false; |
|
} |
|
|
|
} |
|
|
|
|
|
|
|
template < |
|
typename graph_type1, |
|
typename graph_type2 |
|
> |
|
typename enable_if<is_graph<graph_type1> >::type copy_graph_structure ( |
|
const graph_type1& src, |
|
graph_type2& dest |
|
) |
|
{ |
|
COMPILE_TIME_ASSERT(is_graph<graph_type1>::value); |
|
COMPILE_TIME_ASSERT(is_graph<graph_type2>::value); |
|
if (graph_helpers::is_same_object(src,dest)) |
|
return; |
|
|
|
dest.clear(); |
|
dest.set_number_of_nodes(src.number_of_nodes()); |
|
|
|
|
|
for (unsigned long i = 0; i < src.number_of_nodes(); ++i) |
|
{ |
|
for (unsigned long j = 0; j < src.node(i).number_of_neighbors(); ++j) |
|
{ |
|
const unsigned long nidx = src.node(i).neighbor(j).index(); |
|
if (nidx >= i) |
|
{ |
|
dest.add_edge(i,nidx); |
|
} |
|
} |
|
} |
|
} |
|
|
|
template < |
|
typename graph_type1, |
|
typename graph_type2 |
|
> |
|
typename enable_if<is_directed_graph<graph_type1> >::type copy_graph_structure ( |
|
const graph_type1& src, |
|
graph_type2& dest |
|
) |
|
{ |
|
COMPILE_TIME_ASSERT(is_directed_graph<graph_type1>::value); |
|
COMPILE_TIME_ASSERT(is_directed_graph<graph_type2>::value || is_graph<graph_type2>::value ); |
|
if (graph_helpers::is_same_object(src,dest)) |
|
return; |
|
|
|
dest.clear(); |
|
dest.set_number_of_nodes(src.number_of_nodes()); |
|
|
|
|
|
for (unsigned long i = 0; i < src.number_of_nodes(); ++i) |
|
{ |
|
for (unsigned long j = 0; j < src.node(i).number_of_children(); ++j) |
|
{ |
|
const unsigned long nidx = src.node(i).child(j).index(); |
|
if (dest.has_edge(i,nidx) == false) |
|
{ |
|
dest.add_edge(i,nidx); |
|
} |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
template < |
|
typename graph_type1, |
|
typename graph_type2 |
|
> |
|
typename enable_if<is_graph<graph_type1> >::type copy_graph ( |
|
const graph_type1& src, |
|
graph_type2& dest |
|
) |
|
{ |
|
COMPILE_TIME_ASSERT(is_graph<graph_type1>::value); |
|
COMPILE_TIME_ASSERT(is_graph<graph_type2>::value); |
|
if (graph_helpers::is_same_object(src,dest)) |
|
return; |
|
|
|
copy_graph_structure(src,dest); |
|
|
|
|
|
for (unsigned long i = 0; i < src.number_of_nodes(); ++i) |
|
{ |
|
dest.node(i).data = src.node(i).data; |
|
|
|
for (unsigned long j = 0; j < src.node(i).number_of_neighbors(); ++j) |
|
{ |
|
const unsigned long nidx = src.node(i).neighbor(j).index(); |
|
if (nidx >= i) |
|
{ |
|
dest.node(i).edge(j) = src.node(i).edge(j); |
|
} |
|
} |
|
} |
|
} |
|
|
|
template < |
|
typename graph_type1, |
|
typename graph_type2 |
|
> |
|
typename enable_if<is_directed_graph<graph_type1> >::type copy_graph ( |
|
const graph_type1& src, |
|
graph_type2& dest |
|
) |
|
{ |
|
COMPILE_TIME_ASSERT(is_directed_graph<graph_type1>::value); |
|
COMPILE_TIME_ASSERT(is_directed_graph<graph_type2>::value); |
|
if (graph_helpers::is_same_object(src,dest)) |
|
return; |
|
|
|
copy_graph_structure(src,dest); |
|
|
|
|
|
for (unsigned long i = 0; i < src.number_of_nodes(); ++i) |
|
{ |
|
dest.node(i).data = src.node(i).data; |
|
for (unsigned long j = 0; j < src.node(i).number_of_children(); ++j) |
|
{ |
|
dest.node(i).child_edge(j) = src.node(i).child_edge(j); |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
template < |
|
typename T, |
|
typename S |
|
> |
|
typename enable_if<is_graph<typename T::graph_type> >::type find_connected_nodes ( |
|
const T& n, |
|
S& visited |
|
) |
|
{ |
|
if (visited.is_member(n.index()) == false) |
|
{ |
|
unsigned long temp = n.index(); |
|
visited.add(temp); |
|
|
|
for (unsigned long i = 0; i < n.number_of_neighbors(); ++i) |
|
find_connected_nodes(n.neighbor(i), visited); |
|
} |
|
} |
|
|
|
template < |
|
typename T, |
|
typename S |
|
> |
|
typename enable_if<is_directed_graph<typename T::graph_type> >::type find_connected_nodes ( |
|
const T& n, |
|
S& visited |
|
) |
|
{ |
|
if (visited.is_member(n.index()) == false) |
|
{ |
|
unsigned long temp = n.index(); |
|
visited.add(temp); |
|
|
|
for (unsigned long i = 0; i < n.number_of_parents(); ++i) |
|
find_connected_nodes(n.parent(i), visited); |
|
for (unsigned long i = 0; i < n.number_of_children(); ++i) |
|
find_connected_nodes(n.child(i), visited); |
|
} |
|
} |
|
|
|
|
|
|
|
template < |
|
typename T |
|
> |
|
bool graph_is_connected ( |
|
const T& g |
|
) |
|
{ |
|
if (g.number_of_nodes() == 0) |
|
return true; |
|
|
|
set<unsigned long>::kernel_1b_c visited; |
|
find_connected_nodes(g.node(0), visited); |
|
return (visited.size() == g.number_of_nodes()); |
|
} |
|
|
|
|
|
|
|
template < |
|
typename T |
|
> |
|
bool graph_has_symmetric_edges ( |
|
const T& graph |
|
) |
|
{ |
|
for (unsigned long i = 0; i < graph.number_of_nodes(); ++i) |
|
{ |
|
for (unsigned long j = 0; j < graph.node(i).number_of_children(); ++j) |
|
{ |
|
const unsigned long jj = graph.node(i).child(j).index(); |
|
|
|
if (graph.has_edge(jj,i) == false) |
|
return false; |
|
} |
|
|
|
for (unsigned long j = 0; j < graph.node(i).number_of_parents(); ++j) |
|
{ |
|
const unsigned long jj = graph.node(i).parent(j).index(); |
|
|
|
if (graph.has_edge(i,jj) == false) |
|
return false; |
|
} |
|
} |
|
|
|
return true; |
|
} |
|
|
|
|
|
|
|
template < |
|
typename T |
|
> |
|
bool graph_contains_directed_cycle ( |
|
const T& graph |
|
) |
|
{ |
|
using namespace std; |
|
using namespace graph_helpers; |
|
std::vector<bool> visited(graph.number_of_nodes(), false); |
|
std::vector<bool> temp(graph.number_of_nodes(), false); |
|
|
|
while (true) |
|
{ |
|
|
|
unsigned long i; |
|
for (i = 0; i < visited.size(); ++i) |
|
{ |
|
if (visited[i] == false) |
|
break; |
|
} |
|
|
|
|
|
if (i == visited.size()) |
|
return false; |
|
|
|
if (search_for_directed_cycles(graph.node(i), visited, temp)) |
|
return true; |
|
} |
|
} |
|
|
|
|
|
|
|
template < |
|
typename T |
|
> |
|
bool graph_contains_undirected_cycle ( |
|
const T& graph |
|
) |
|
{ |
|
using namespace std; |
|
using namespace graph_helpers; |
|
std::vector<bool> visited(graph.number_of_nodes(), false); |
|
|
|
while (true) |
|
{ |
|
|
|
unsigned long i; |
|
for (i = 0; i < visited.size(); ++i) |
|
{ |
|
if (visited[i] == false) |
|
break; |
|
} |
|
|
|
|
|
if (i == visited.size()) |
|
return false; |
|
|
|
if (search_for_undirected_cycles(graph.node(i), visited)) |
|
return true; |
|
} |
|
} |
|
|
|
|
|
|
|
template < |
|
typename directed_graph_type, |
|
typename graph_type |
|
> |
|
void create_moral_graph ( |
|
const directed_graph_type& g, |
|
graph_type& moral_graph |
|
) |
|
{ |
|
|
|
DLIB_ASSERT(graph_contains_directed_cycle(g) == false, |
|
"\tvoid create_moral_graph(g, moral_graph)" |
|
<< "\n\tYou can only make moral graphs if g doesn't have directed cycles" |
|
); |
|
COMPILE_TIME_ASSERT(is_graph<graph_type>::value); |
|
COMPILE_TIME_ASSERT(is_directed_graph<directed_graph_type>::value); |
|
|
|
copy_graph_structure(g, moral_graph); |
|
|
|
|
|
for (unsigned long i = 0; i < g.number_of_nodes(); ++i) |
|
{ |
|
|
|
for (unsigned long j = 0; j < g.node(i).number_of_parents(); ++j) |
|
{ |
|
for (unsigned long k = 0; k < g.node(i).number_of_parents(); ++k) |
|
{ |
|
const unsigned long p1 = g.node(i).parent(j).index(); |
|
const unsigned long p2 = g.node(i).parent(k).index(); |
|
if (p1 == p2) |
|
continue; |
|
|
|
if (moral_graph.has_edge(p1,p2) == false) |
|
moral_graph.add_edge(p1,p2); |
|
} |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
template < |
|
typename graph_type, |
|
typename sets_of_int |
|
> |
|
bool is_clique ( |
|
const graph_type& g, |
|
const sets_of_int& clique |
|
) |
|
{ |
|
|
|
DLIB_ASSERT(graph_contains_length_one_cycle(g) == false, |
|
"\tvoid is_clique(g, clique)" |
|
<< "\n\tinvalid graph" |
|
); |
|
#ifdef ENABLE_ASSERTS |
|
clique.reset(); |
|
while (clique.move_next()) |
|
{ |
|
const unsigned long x = clique.element(); |
|
DLIB_ASSERT( x < g.number_of_nodes(), |
|
"\tvoid is_clique(g, clique)" |
|
<< "\n\tthe clique set contained an invalid node index" |
|
<< "\n\tx: " << x |
|
<< "\n\tg.number_of_nodes(): " << g.number_of_nodes() |
|
); |
|
} |
|
#endif |
|
|
|
COMPILE_TIME_ASSERT(is_graph<graph_type>::value); |
|
|
|
std::vector<unsigned long> v; |
|
v.reserve(clique.size()); |
|
clique.reset(); |
|
while (clique.move_next()) |
|
{ |
|
v.push_back(clique.element()); |
|
} |
|
|
|
for (unsigned long i = 0; i < v.size(); ++i) |
|
{ |
|
for (unsigned long j = 0; j < v.size(); ++j) |
|
{ |
|
if (v[i] == v[j]) |
|
continue; |
|
if (g.has_edge(v[i], v[j]) == false) |
|
return false; |
|
} |
|
} |
|
|
|
return true; |
|
} |
|
|
|
|
|
|
|
template < |
|
typename graph_type, |
|
typename sets_of_int |
|
> |
|
bool is_maximal_clique ( |
|
const graph_type& g, |
|
const sets_of_int& clique |
|
) |
|
{ |
|
|
|
DLIB_ASSERT(graph_contains_length_one_cycle(g) == false, |
|
"\tvoid is_maximal_clique(g, clique)" |
|
<< "\n\tinvalid graph" |
|
); |
|
DLIB_ASSERT(is_clique(g,clique) == true, |
|
"\tvoid is_maximal_clique(g, clique)" |
|
<< "\n\tinvalid graph" |
|
); |
|
#ifdef ENABLE_ASSERTS |
|
clique.reset(); |
|
while (clique.move_next()) |
|
{ |
|
const unsigned long x = clique.element(); |
|
DLIB_ASSERT( x < g.number_of_nodes(), |
|
"\tvoid is_maximal_clique(g, clique)" |
|
<< "\n\tthe clique set contained an invalid node index" |
|
<< "\n\tx: " << x |
|
<< "\n\tg.number_of_nodes(): " << g.number_of_nodes() |
|
); |
|
} |
|
#endif |
|
|
|
COMPILE_TIME_ASSERT(is_graph<graph_type>::value); |
|
|
|
if (clique.size() == 0) |
|
return true; |
|
|
|
|
|
|
|
|
|
clique.reset(); |
|
clique.move_next(); |
|
const unsigned long idx = clique.element(); |
|
|
|
for (unsigned long i = 0; i < g.node(idx).number_of_neighbors(); ++i) |
|
{ |
|
const unsigned long n = g.node(idx).neighbor(i).index(); |
|
if (clique.is_member(n)) |
|
continue; |
|
|
|
|
|
|
|
bool all_share_edge = true; |
|
clique.reset(); |
|
while (clique.move_next()) |
|
{ |
|
if (g.has_edge(clique.element(), n) == false) |
|
{ |
|
all_share_edge = false; |
|
break; |
|
} |
|
} |
|
|
|
if (all_share_edge == true) |
|
return false; |
|
} |
|
|
|
return true; |
|
} |
|
|
|
|
|
|
|
template < |
|
typename T |
|
> |
|
typename enable_if<is_directed_graph<T>,bool>::type graph_contains_length_one_cycle ( |
|
const T& graph |
|
) |
|
{ |
|
for (unsigned long i = 0; i < graph.number_of_nodes(); ++i) |
|
{ |
|
|
|
for (unsigned long n = 0; n < graph.node(i).number_of_children(); ++n) |
|
{ |
|
if (graph.node(i).child(n).index() == i) |
|
return true; |
|
} |
|
} |
|
|
|
return false; |
|
} |
|
|
|
|
|
|
|
template < |
|
typename T |
|
> |
|
typename enable_if<is_graph<T>,bool>::type graph_contains_length_one_cycle ( |
|
const T& graph |
|
) |
|
{ |
|
for (unsigned long i = 0; i < graph.number_of_nodes(); ++i) |
|
{ |
|
|
|
for (unsigned long n = 0; n < graph.node(i).number_of_neighbors(); ++n) |
|
{ |
|
if (graph.node(i).neighbor(n).index() == i) |
|
return true; |
|
} |
|
} |
|
|
|
return false; |
|
} |
|
|
|
|
|
|
|
namespace graph_helpers |
|
{ |
|
struct pair |
|
{ |
|
unsigned long index; |
|
unsigned long num_neighbors; |
|
|
|
bool operator< (const pair& p) const { return num_neighbors < p.num_neighbors; } |
|
}; |
|
|
|
template < |
|
typename T, |
|
typename S, |
|
typename V |
|
> |
|
void search_graph_for_triangulate ( |
|
const T& n, |
|
S& visited, |
|
V& order_visited |
|
) |
|
{ |
|
|
|
|
|
if (visited.is_member(n.index())) |
|
return; |
|
|
|
|
|
order_visited.push_back(n.index()); |
|
unsigned long temp = n.index(); |
|
visited.add(temp); |
|
|
|
|
|
|
|
|
|
|
|
std::vector<pair> neighbors; |
|
for (unsigned long i = 0; i < n.number_of_neighbors(); ++i) |
|
{ |
|
pair p; |
|
p.index = i; |
|
p.num_neighbors = n.neighbor(i).number_of_neighbors(); |
|
neighbors.push_back(p); |
|
} |
|
|
|
|
|
|
|
std::sort(neighbors.rbegin(), neighbors.rend()); |
|
|
|
|
|
for (unsigned long i = 0; i < neighbors.size(); ++i) |
|
{ |
|
search_graph_for_triangulate(n.neighbor(neighbors[i].index), visited, order_visited); |
|
} |
|
} |
|
} |
|
|
|
template < |
|
typename graph_type, |
|
typename set_of_sets_of_int |
|
> |
|
void triangulate_graph_and_find_cliques ( |
|
graph_type& g, |
|
set_of_sets_of_int& cliques |
|
) |
|
{ |
|
|
|
|
|
DLIB_ASSERT(graph_contains_length_one_cycle(g) == false, |
|
"\tvoid triangulate_graph_and_find_cliques(g, cliques)" |
|
<< "\n\tInvalid graph" |
|
); |
|
DLIB_ASSERT(graph_is_connected(g) == true, |
|
"\tvoid triangulate_graph_and_find_cliques(g, cliques)" |
|
<< "\n\tInvalid graph" |
|
); |
|
|
|
COMPILE_TIME_ASSERT(is_graph<graph_type>::value); |
|
|
|
|
|
using namespace graph_helpers; |
|
using namespace std; |
|
typedef typename set_of_sets_of_int::type set_of_int; |
|
|
|
cliques.clear(); |
|
|
|
|
|
unsigned long max_index = 0; |
|
unsigned long num_neighbors = 0; |
|
for (unsigned long i = 0; i < g.number_of_nodes(); ++i) |
|
{ |
|
if (g.node(i).number_of_neighbors() > num_neighbors) |
|
{ |
|
max_index = i; |
|
num_neighbors = g.node(i).number_of_neighbors(); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
std::vector<unsigned long> order_visited; |
|
set_of_int visited; |
|
search_graph_for_triangulate(g.node(max_index), visited, order_visited); |
|
|
|
set_of_int clique; |
|
|
|
|
|
while (visited.size() > 0) |
|
{ |
|
|
|
|
|
const unsigned long idx = order_visited.back(); |
|
order_visited.pop_back(); |
|
visited.destroy(idx); |
|
|
|
|
|
unsigned long temp = idx; |
|
clique.clear(); |
|
clique.add(temp); |
|
|
|
|
|
|
|
|
|
for (unsigned long i = 0; i < g.node(idx).number_of_neighbors(); ++i) |
|
{ |
|
|
|
unsigned long nidx = g.node(idx).neighbor(i).index(); |
|
|
|
|
|
|
|
if (visited.is_member(nidx) == true && |
|
g.node(nidx).number_of_neighbors() != 1) |
|
{ |
|
|
|
|
|
clique.reset(); |
|
while (clique.move_next()) |
|
{ |
|
if (g.has_edge(nidx, clique.element()) == false) |
|
g.add_edge(nidx, clique.element()); |
|
} |
|
|
|
|
|
clique.add(nidx); |
|
} |
|
} |
|
|
|
if (cliques.is_member(clique) == false && is_maximal_clique(g,clique) ) |
|
{ |
|
cliques.add(clique); |
|
} |
|
|
|
|
|
|
|
|
|
for (unsigned long i = 0; i < g.number_of_nodes(); ++i) |
|
{ |
|
clique.clear(); |
|
if (g.node(i).number_of_neighbors() == 1) |
|
{ |
|
unsigned long temp = i; |
|
clique.add(temp); |
|
temp = g.node(i).neighbor(0).index(); |
|
clique.add(temp); |
|
|
|
if (cliques.is_member(clique) == false) |
|
cliques.add(clique); |
|
} |
|
} |
|
} |
|
|
|
} |
|
|
|
|
|
|
|
template < |
|
typename graph_type, |
|
typename join_tree_type |
|
> |
|
void create_join_tree ( |
|
const graph_type& g, |
|
join_tree_type& join_tree |
|
) |
|
{ |
|
|
|
DLIB_ASSERT(graph_contains_length_one_cycle(g) == false, |
|
"\tvoid create_join_tree(g, join_tree)" |
|
<< "\n\tInvalid graph" |
|
); |
|
DLIB_ASSERT(graph_is_connected(g) == true, |
|
"\tvoid create_join_tree(g, join_tree)" |
|
<< "\n\tInvalid graph" |
|
); |
|
|
|
COMPILE_TIME_ASSERT(is_graph<graph_type>::value); |
|
COMPILE_TIME_ASSERT(is_graph<join_tree_type>::value); |
|
|
|
|
|
|
|
typedef typename join_tree_type::type set_of_int; |
|
typedef typename join_tree_type::edge_type set_of_int_edge; |
|
typedef typename set<set_of_int>::kernel_1b_c set_of_sets_of_int; |
|
|
|
copy_graph_structure(g, join_tree); |
|
|
|
|
|
if (g.number_of_nodes() == 0) |
|
return; |
|
|
|
set_of_sets_of_int cliques; |
|
set_of_int s; |
|
|
|
triangulate_graph_and_find_cliques(join_tree, cliques); |
|
|
|
join_tree.set_number_of_nodes(cliques.size()); |
|
|
|
|
|
for (unsigned long i = 0; i < join_tree.number_of_nodes(); ++i) |
|
{ |
|
cliques.remove_any(s); |
|
s.swap(join_tree.node(i).data); |
|
} |
|
|
|
set_of_int_edge e; |
|
|
|
|
|
for (unsigned long i = 0; i < join_tree.number_of_nodes(); ++i) |
|
{ |
|
for (unsigned long j = i+1; j < join_tree.number_of_nodes(); ++j) |
|
{ |
|
set_intersection( |
|
join_tree.node(i).data, |
|
join_tree.node(j).data, |
|
e); |
|
|
|
if (e.size() > 0) |
|
{ |
|
join_tree.add_edge(i,j); |
|
edge(join_tree,i,j).swap(e); |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
s.clear(); |
|
set_of_int& good = s; |
|
|
|
unsigned long n = 0; |
|
good.add(n); |
|
|
|
std::vector<unsigned long> vtemp; |
|
|
|
while (good.size() < join_tree.number_of_nodes()) |
|
{ |
|
|
|
unsigned long best_bad_idx = 0; |
|
unsigned long best_good_idx = 0; |
|
unsigned long best_overlap = 0; |
|
good.reset(); |
|
while (good.move_next()) |
|
{ |
|
|
|
for (unsigned long i = 0; i < join_tree.node(good.element()).number_of_neighbors(); ++i) |
|
{ |
|
const unsigned long idx = join_tree.node(good.element()).neighbor(i).index(); |
|
if (!good.is_member(idx)) |
|
{ |
|
const unsigned long overlap = join_tree.node(good.element()).edge(i).size(); |
|
|
|
if (overlap > best_overlap) |
|
{ |
|
best_overlap = overlap; |
|
best_bad_idx = idx; |
|
best_good_idx = good.element(); |
|
} |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
for (unsigned long i = 0; i < join_tree.node(best_bad_idx).number_of_neighbors(); ++i) |
|
{ |
|
const unsigned long idx = join_tree.node(best_bad_idx).neighbor(i).index(); |
|
if (idx != best_good_idx && good.is_member(idx)) |
|
{ |
|
vtemp.push_back(idx); |
|
} |
|
} |
|
|
|
for (unsigned long i = 0; i < vtemp.size(); ++i) |
|
join_tree.remove_edge(vtemp[i], best_bad_idx); |
|
|
|
vtemp.clear(); |
|
|
|
|
|
|
|
good.add(best_bad_idx); |
|
} |
|
} |
|
|
|
|
|
|
|
namespace graph_helpers |
|
{ |
|
template < |
|
typename T, |
|
typename U |
|
> |
|
bool validate_join_tree ( |
|
const T& n, |
|
U& deads, |
|
unsigned long parent = 0xffffffff |
|
) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
{ |
|
n.data.reset(); |
|
while (n.data.move_next()) |
|
{ |
|
if (deads.is_member(n.data.element())) |
|
return false; |
|
} |
|
|
|
|
|
for (unsigned long i = 0; i < n.number_of_neighbors(); ++i) |
|
{ |
|
if (n.neighbor(i).index() == parent) |
|
continue; |
|
|
|
|
|
n.data.reset(); |
|
while (n.data.move_next()) |
|
{ |
|
if (n.neighbor(i).data.is_member(n.data.element()) == false) |
|
{ |
|
unsigned long temp = n.data.element(); |
|
deads.add(temp); |
|
} |
|
} |
|
|
|
if (validate_join_tree(n.neighbor(i), deads, n.index()) == false) |
|
return false; |
|
|
|
|
|
n.data.reset(); |
|
while (n.data.move_next()) |
|
{ |
|
if (n.neighbor(i).data.is_member(n.data.element()) == false) |
|
{ |
|
unsigned long temp = n.data.element(); |
|
deads.destroy(temp); |
|
} |
|
} |
|
} |
|
|
|
return true; |
|
} |
|
} |
|
|
|
template < |
|
typename graph_type, |
|
typename join_tree_type |
|
> |
|
bool is_join_tree ( |
|
const graph_type& g, |
|
const join_tree_type& join_tree |
|
) |
|
{ |
|
|
|
|
|
DLIB_ASSERT(graph_contains_length_one_cycle(g) == false, |
|
"\tvoid create_join_tree(g, join_tree)" |
|
<< "\n\tInvalid graph" |
|
); |
|
DLIB_ASSERT(graph_is_connected(g) == true, |
|
"\tvoid create_join_tree(g, join_tree)" |
|
<< "\n\tInvalid graph" |
|
); |
|
|
|
COMPILE_TIME_ASSERT(is_graph<graph_type>::value || is_directed_graph<graph_type>::value); |
|
COMPILE_TIME_ASSERT(is_graph<join_tree_type>::value); |
|
|
|
|
|
if (graph_contains_undirected_cycle(join_tree)) |
|
return false; |
|
|
|
if (graph_is_connected(join_tree) == false) |
|
return false; |
|
|
|
|
|
for (unsigned long i = 0; i < join_tree.number_of_nodes(); ++i) |
|
{ |
|
typename join_tree_type::type deads; |
|
if (graph_helpers::validate_join_tree(join_tree.node(i), deads) == false) |
|
return false; |
|
} |
|
|
|
typename join_tree_type::edge_type e; |
|
typename join_tree_type::edge_type all; |
|
|
|
for (unsigned long i = 0; i < join_tree.number_of_nodes(); ++i) |
|
{ |
|
set_union(all,join_tree.node(i).data, all); |
|
for (unsigned long j = 0; j < join_tree.node(i).number_of_neighbors(); ++j) |
|
{ |
|
set_intersection(join_tree.node(i).data, |
|
join_tree.node(i).neighbor(j).data, |
|
e); |
|
|
|
if (!(e == join_tree.node(i).edge(j))) |
|
return false; |
|
} |
|
} |
|
|
|
|
|
if (all.size() != g.number_of_nodes()) |
|
return false; |
|
all.reset(); |
|
while (all.move_next()) |
|
{ |
|
if (all.element() >= g.number_of_nodes()) |
|
return false; |
|
} |
|
|
|
|
|
return true; |
|
} |
|
|
|
|
|
|
|
} |
|
|
|
#endif |
|
|
|
|
|
|