Commit 07196449 authored by Tiago Peixoto's avatar Tiago Peixoto

Implement max_cliques()

parent 3c84519f
Pipeline #495 failed with stage
in 545 minutes and 56 seconds
......@@ -24,6 +24,7 @@ libgraph_tool_topology_la_SOURCES = \
graph_dominator_tree.cc \
graph_isomorphism.cc \
graph_kcore.cc \
graph_maximal_cliques.cc \
graph_maximal_planar.cc \
graph_maximal_vertex_set.cc \
graph_minimum_spanning_tree.cc \
......@@ -46,6 +47,7 @@ libgraph_tool_topology_la_SOURCES = \
libgraph_tool_topology_la_include_HEADERS = \
graph_components.hh \
graph_kcore.hh \
graph_maximal_cliques.hh \
graph_percolation.hh \
graph_similarity.hh \
graph_vertex_similarity.hh
// graph-tool -- a general graph modification and manipulation thingy
//
// Copyright (C) 2006-2019 Tiago de Paula Peixoto <tiago@skewed.de>
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 3
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "graph_tool.hh"
#include "numpy_bind.hh"
#include "coroutine.hh"
#include "graph_python_interface.hh"
#include "graph_maximal_cliques.hh"
using namespace std;
using namespace graph_tool;
boost::python::object get_max_cliques(GraphInterface& gi)
{
#ifdef HAVE_BOOST_COROUTINE
auto dispatch = [&](auto& yield)
{
run_action<>()
(gi,
[&](auto& g)
{
max_cliques(g,
[&](auto& s)
{
std::vector<size_t> v(s.begin(), s.end());
auto c = wrap_vector_owned(v);
yield(c);
});
})();
};
return boost::python::object(CoroGenerator(dispatch));
#else
throw GraphException("This functionality is not available because boost::coroutine was not found at compile-time");
#endif // HAVE_BOOST_COROUTINE
}
boost::python::list get_max_cliques_list(GraphInterface& gi)
{
boost::python::list cliques;
run_action<>()
(gi,
[&](auto& g)
{
max_cliques(g,
[&](auto& s)
{
std::vector<size_t> v(s.begin(), s.end());
cliques.append(wrap_vector_owned(v));
});
})();
return cliques;
}
void export_max_cliques()
{
boost::python::def("max_cliques", &get_max_cliques);
boost::python::def("max_cliques_list", &get_max_cliques_list);
};
// graph-tool -- a general graph modification and manipulation thingy
//
// Copyright (C) 2006-2019 Tiago de Paula Peixoto <tiago@skewed.de>
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 3
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef GRAPH_MAXIMAL_CLIQUES_HH
#define GRAPH_MAXIMAL_CLIQUES_HH
#include <array>
#include <deque>
namespace graph_tool
{
using namespace boost;
template <class Graph, class Visitor>
void max_cliques(Graph& g, Visitor&& vis)
{
typedef gt_hash_set<size_t> vset_t;
//typedef std::set<size_t> vset_t;
auto get_pivot_adj = [&](const auto& P, const auto& X, auto& u_adj)
{
auto u = graph_traits<Graph>::null_vertex();
size_t ku = 0;
std::array<const vset_t*, 2> Ss({&P, &X});
for (auto S : Ss)
{
for (auto v : *S)
{
size_t k = 0;
for (auto w : out_neighbors_range(v, g))
{
if (w == v)
continue;
if (P.find(w) != P.end())
++k;
}
if (k >= ku)
{
u = v;
ku = k;
}
}
}
for (auto w : out_neighbors_range(u, g))
{
if (w == u)
continue;
u_adj.insert(w);
}
};
std::deque<std::tuple<vset_t, vset_t, vset_t, vset_t, vset_t::iterator>>
stack;
stack.emplace_back();
auto& P_ = get<1>(stack.back());
for (auto v : vertices_range(g))
P_.insert(v);
get_pivot_adj(get<1>(stack.back()),
get<2>(stack.back()),
get<3>(stack.back()));
get<4>(stack.back()) = P_.begin();
while (!stack.empty())
{
auto& top = stack.back();
vset_t& R = get<0>(top);
vset_t& P = get<1>(top);
vset_t& X = get<2>(top);
vset_t& u_adj = get<3>(top);
auto& viter = get<4>(top);
if (P.empty())
{
if (X.empty() && R.size() > 1)
vis(R);
stack.pop_back();
continue;
}
while (viter != P.end() && u_adj.find(*viter) != u_adj.end())
++viter;
if (viter == P.end())
{
stack.pop_back();
continue;
}
auto v = *viter;
stack.emplace_back();
auto& ntop = stack.back();
auto& nR = get<0>(ntop);
auto& nP = get<1>(ntop);
auto& nX = get<2>(ntop);
auto& nu_adj = get<3>(ntop);
auto& nviter = get<4>(ntop);
if (!R.empty())
nR.insert(R.begin(), R.end());
nR.insert(v);
for (auto w : out_neighbors_range(v, g))
{
if (w == v)
continue;
if (P.find(w) != P.end())
nP.insert(w);
if (X.find(w) != X.end())
nX.insert(w);
}
if (!(nP.empty() && nX.empty()))
get_pivot_adj(nP, nX, nu_adj);
nviter = nP.begin();
X.insert(v);
P.erase(viter++);
}
}
} // graph_tool namespace
#endif // GRAPH_MAXIMAL_CLIQUES_HH
......@@ -64,6 +64,7 @@ void export_diam();
void export_random_matching();
void export_maximal_vertex_set();
void export_vertex_similarity();
void export_max_cliques();
BOOST_PYTHON_MODULE(libgraph_tool_topology)
......@@ -94,4 +95,5 @@ BOOST_PYTHON_MODULE(libgraph_tool_topology)
export_random_matching();
export_maximal_vertex_set();
export_vertex_similarity();
export_max_cliques();
}
......@@ -40,6 +40,7 @@ Summary
isomorphism
subgraph_isomorphism
mark_subgraph
max_cliques
max_cardinality_matching
max_independent_vertex_set
min_spanning_tree
......@@ -80,7 +81,7 @@ from .. import _prop, Vector_int32_t, _check_prop_writable, \
from .. stats import label_self_loops
import random, sys, numpy, collections
__all__ = ["isomorphism", "subgraph_isomorphism", "mark_subgraph",
__all__ = ["isomorphism", "subgraph_isomorphism", "mark_subgraph", "max_cliques",
"max_cardinality_matching", "max_independent_vertex_set",
"min_spanning_tree", "random_spanning_tree", "dominator_tree",
"topological_sort", "transitive_closure", "tsp_tour",
......@@ -797,6 +798,76 @@ def mark_subgraph(g, sub, vmap, vmask=None, emask=None):
return vmask, emask
def max_cliques(g, iterator=True):
"""Return an iterator over the maximal cliques of the graph.
Parameters
----------
g : :class:`~graph_tool.Graph`
Graph to be used.
iterator : ``boolean`` (optional, default: ``True``)
If ``False``, a list is returned instead of an iterator.
Returns
-------
max_cliques : iterator or list over :class:`numpy.ndarray` instances
Iterator (or list) over lists of vertices corresponding to the maximal
cliques.
Notes
-----
This implements the Bron-Kerbosh algorithm [bron_algorithm_1973]_
[bron-kerbosh-wiki]_ with pivoting [tomita_worst-case_2006]_
[cazals_note_2008]_.
The worst-case complexity of this algorithm is :math:`O(3^{V/3})` for a
graph of :math:`V` vertices, but for sparse graphs it is typically much
faster.
Examples
--------
>>> g = gt.collection.data["polblogs"]
>>> for i, c in enumerate(gt.max_cliques(g)):
... print(c)
... if i == 9:
... break
[ 0 1434 1244]
[ 0 643 433 1244]
[ 0 20 1244]
[ 0 640 1130 366 567]
[ 0 640 322 67 54 154]
[ 0 640 67 114 54 154]
[ 0 640 322 240 84 54 154]
[ 0 640 433 114 84 54 154]
[ 0 640 641 20 54 154]
[ 0 640 322 20 54 154]
References
----------
.. [bron_algorithm_1973] Coen Bron and Joep Kerbosch, "Algorithm 457:
finding all cliques of an undirected graph", Commun. ACM 16, 9, 575-577
(1973), :doi:`10.1145/362342.362367`
.. [tomita_worst-case_2006] Etsuji Tomita, Akira Tanaka, and Haruhisa
Takahashi. "The worst-case time complexity for generating all maximal
cliques and computational experiments." Theoretical Computer Science 363.1
28-42 (2006), :doi:`10.1016/j.tcs.2006.06.015`
.. [cazals_note_2008] Frédéric Cazals, and Chinmay Karande, "A note on the
problem of reporting maximal cliques." Theoretical Computer Science 407.1-3
564-568 (2008), :doi:`10.1016/j.tcs.2008.05.010`
.. [bron-kerbosh-wiki] https://en.wikipedia.org/wiki/Bron%E2%80%93Kerbosch_algorithm
"""
if g.is_directed():
g = GraphView(g, directed=False)
if iterator:
return libgraph_tool_topology.max_cliques(g._Graph__graph)
return libgraph_tool_topology.max_cliques_list(g._Graph__graph)
def min_spanning_tree(g, weights=None, root=None, tree_map=None):
"""
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment