Skip to content

Commit

Permalink
Merge branch 'get-all-cliques'
Browse files Browse the repository at this point in the history
  • Loading branch information
ysitu committed Jun 8, 2014
2 parents 01e5405 + 1443a60 commit 1c00139
Show file tree
Hide file tree
Showing 2 changed files with 202 additions and 60 deletions.
76 changes: 75 additions & 1 deletion networkx/algorithms/clique.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,88 @@
# Pieter Swart <[email protected]>
# All rights reserved.
# BSD license.
from collections import deque
from itertools import chain, islice
try:
from itertools import ifilter as filter
except ImportError:
pass
import networkx
from networkx.utils.decorators import *
__author__ = """Dan Schult ([email protected])"""
__all__ = ['find_cliques', 'find_cliques_recursive', 'make_max_clique_graph',
'make_clique_bipartite' ,'graph_clique_number',
'graph_number_of_cliques', 'node_clique_number',
'number_of_cliques', 'cliques_containing_node',
'project_down', 'project_up']
'project_down', 'project_up', 'enumerate_all_cliques']


@not_implemented_for('directed')
def enumerate_all_cliques(G):
"""Returns all cliques in an undirected graph.
This method returns cliques of size (cardinality)
k = 1, 2, 3, ..., maxDegree - 1.
Where maxDegree is the maximal degree of any node in the graph.
Parameters
----------
G: undirected graph
Returns
-------
generator of lists: generator of list for each clique.
Notes
-----
To obtain a list of all cliques, use
:samp:`list(enumerate_all_cliques(G))`.
Based on the algorithm published by Zhang et al. (2005) [1]_
and adapted to output all cliques discovered.
This algorithm is not applicable on directed graphs.
This algorithm ignores self-loops and parallel edges as
clique is not conventionally defined with such edges.
There are often many cliques in graphs.
This algorithm however, hopefully, does not run out of memory
since it only keeps candidate sublists in memory and
continuously removes exhausted sublists.
References
----------
.. [1] Yun Zhang, Abu-Khzam, F.N., Baldwin, N.E., Chesler, E.J.,
Langston, M.A., Samatova, N.F.,
Genome-Scale Computational Approaches to Memory-Intensive
Applications in Systems Biology.
Supercomputing, 2005. Proceedings of the ACM/IEEE SC 2005
Conference, pp. 12, 12-18 Nov. 2005.
doi: 10.1109/SC.2005.29.
http://ieeexplore.ieee.org/stamp/stamp.jsp?tp=&arnumber=1559964&isnumber=33129
"""
index = {}
nbrs = {}
for u in G:
index[u] = len(index)
# Neighbors of u that appear after u in the iteration order of G.
nbrs[u] = {v for v in G[u] if v not in index}

queue = deque(([u], sorted(nbrs[u], key=index.__getitem__)) for u in G)
# Loop invariants:
# 1. len(base) is nondecreasing.
# 2. (base + cnbrs) is sorted with respect to the iteration order of G.
# 3. cnbrs is a set of common neighbors of nodes in base.
while queue:
base, cnbrs = map(list, queue.popleft())
yield base
for i, u in enumerate(cnbrs):
# Use generators to reduce memory consumption.
queue.append((chain(base, [u]),
filter(nbrs[u].__contains__,
islice(cnbrs, i + 1, None))))


@not_implemented_for('directed')
Expand Down
186 changes: 127 additions & 59 deletions networkx/algorithms/tests/test_clique.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,112 +3,180 @@
import networkx as nx
from networkx import convert_node_labels_to_integers as cnlti


class TestCliques:

def setUp(self):
z=[3,4,3,4,2,4,2,1,1,1,1]
self.G=cnlti(nx.generators.havel_hakimi_graph(z),first_label=1)
self.cl=list(nx.find_cliques(self.G))
H=nx.complete_graph(6)
H=nx.relabel_nodes(H,dict( [(i,i+1) for i in range(6)]))
H.remove_edges_from([(2,6),(2,5),(2,4),(1,3),(5,3)])
self.H=H
z = [3, 4, 3, 4, 2, 4, 2, 1, 1, 1, 1]
self.G = cnlti(nx.generators.havel_hakimi_graph(z), first_label=1)
self.cl = list(nx.find_cliques(self.G))
H = nx.complete_graph(6)
H = nx.relabel_nodes(H, dict([(i, i + 1) for i in range(6)]))
H.remove_edges_from([(2, 6), (2, 5), (2, 4), (1, 3), (5, 3)])
self.H = H

def test_find_cliques1(self):
cl=list(nx.find_cliques(self.G))
rcl=nx.find_cliques_recursive(self.G)
cl = list(nx.find_cliques(self.G))
rcl = nx.find_cliques_recursive(self.G)
expected = [[2, 6, 1, 3], [2, 6, 4], [5, 4, 7], [8, 9], [10, 11]]
assert_equal(sorted(map(sorted,cl)), sorted(map(sorted,rcl)))
assert_equal(sorted(map(sorted,cl)), sorted(map(sorted, expected)))
assert_equal(sorted(map(sorted, cl)), sorted(map(sorted, rcl)))
assert_equal(sorted(map(sorted, cl)), sorted(map(sorted, expected)))

def test_selfloops(self):
self.G.add_edge(1,1)
cl=list(nx.find_cliques(self.G))
rcl=nx.find_cliques_recursive(self.G)
assert_equal(sorted(map(sorted,cl)), sorted(map(sorted,rcl)))
self.G.add_edge(1, 1)
cl = list(nx.find_cliques(self.G))
rcl = nx.find_cliques_recursive(self.G)
assert_equal(sorted(map(sorted, cl)), sorted(map(sorted, rcl)))
assert_equal(cl,
[[2, 6, 1, 3], [2, 6, 4], [5, 4, 7], [8, 9], [10, 11]])

def test_find_cliques2(self):
hcl=list(nx.find_cliques(self.H))
assert_equal(sorted(map(sorted,hcl)),
hcl = list(nx.find_cliques(self.H))
assert_equal(sorted(map(sorted, hcl)),
[[1, 2], [1, 4, 5, 6], [2, 3], [3, 4, 6]])

def test_clique_number(self):
G=self.G
assert_equal(nx.graph_clique_number(G),4)
assert_equal(nx.graph_clique_number(G,cliques=self.cl),4)
G = self.G
assert_equal(nx.graph_clique_number(G), 4)
assert_equal(nx.graph_clique_number(G, cliques=self.cl), 4)

def test_number_of_cliques(self):
G=self.G
assert_equal(nx.graph_number_of_cliques(G),5)
assert_equal(nx.graph_number_of_cliques(G,cliques=self.cl),5)
assert_equal(nx.number_of_cliques(G,1),1)
assert_equal(list(nx.number_of_cliques(G,[1]).values()),[1])
assert_equal(list(nx.number_of_cliques(G,[1,2]).values()),[1, 2])
assert_equal(nx.number_of_cliques(G,[1,2]),{1: 1, 2: 2})
assert_equal(nx.number_of_cliques(G,2),2)
G = self.G
assert_equal(nx.graph_number_of_cliques(G), 5)
assert_equal(nx.graph_number_of_cliques(G, cliques=self.cl), 5)
assert_equal(nx.number_of_cliques(G, 1), 1)
assert_equal(list(nx.number_of_cliques(G, [1]).values()), [1])
assert_equal(list(nx.number_of_cliques(G, [1, 2]).values()), [1, 2])
assert_equal(nx.number_of_cliques(G, [1, 2]), {1: 1, 2: 2})
assert_equal(nx.number_of_cliques(G, 2), 2)
assert_equal(nx.number_of_cliques(G),
{1: 1, 2: 2, 3: 1, 4: 2, 5: 1,
6: 2, 7: 1, 8: 1, 9: 1, 10: 1, 11: 1})
assert_equal(nx.number_of_cliques(G,nodes=G.nodes()),
assert_equal(nx.number_of_cliques(G, nodes=G.nodes()),
{1: 1, 2: 2, 3: 1, 4: 2, 5: 1,
6: 2, 7: 1, 8: 1, 9: 1, 10: 1, 11: 1})
assert_equal(nx.number_of_cliques(G,nodes=[2,3,4]),
assert_equal(nx.number_of_cliques(G, nodes=[2, 3, 4]),
{2: 2, 3: 1, 4: 2})
assert_equal(nx.number_of_cliques(G,cliques=self.cl),
assert_equal(nx.number_of_cliques(G, cliques=self.cl),
{1: 1, 2: 2, 3: 1, 4: 2, 5: 1,
6: 2, 7: 1, 8: 1, 9: 1, 10: 1, 11: 1})
assert_equal(nx.number_of_cliques(G,G.nodes(),cliques=self.cl),
assert_equal(nx.number_of_cliques(G, G.nodes(), cliques=self.cl),
{1: 1, 2: 2, 3: 1, 4: 2, 5: 1,
6: 2, 7: 1, 8: 1, 9: 1, 10: 1, 11: 1})



def test_node_clique_number(self):
G=self.G
assert_equal(nx.node_clique_number(G,1),4)
assert_equal(list(nx.node_clique_number(G,[1]).values()),[4])
assert_equal(list(nx.node_clique_number(G,[1,2]).values()),[4, 4])
assert_equal(nx.node_clique_number(G,[1,2]),{1: 4, 2: 4})
assert_equal(nx.node_clique_number(G,1),4)
G = self.G
assert_equal(nx.node_clique_number(G, 1), 4)
assert_equal(list(nx.node_clique_number(G, [1]).values()), [4])
assert_equal(list(nx.node_clique_number(G, [1, 2]).values()), [4, 4])
assert_equal(nx.node_clique_number(G, [1, 2]), {1: 4, 2: 4})
assert_equal(nx.node_clique_number(G, 1), 4)
assert_equal(nx.node_clique_number(G),
{1: 4, 2: 4, 3: 4, 4: 3, 5: 3, 6: 4,
7: 3, 8: 2, 9: 2, 10: 2, 11: 2})
assert_equal(nx.node_clique_number(G,cliques=self.cl),
assert_equal(nx.node_clique_number(G, cliques=self.cl),
{1: 4, 2: 4, 3: 4, 4: 3, 5: 3, 6: 4,
7: 3, 8: 2, 9: 2, 10: 2, 11: 2})

def test_cliques_containing_node(self):
G=self.G
assert_equal(nx.cliques_containing_node(G,1),
G = self.G
assert_equal(nx.cliques_containing_node(G, 1),
[[2, 6, 1, 3]])
assert_equal(list(nx.cliques_containing_node(G,[1]).values()),
assert_equal(list(nx.cliques_containing_node(G, [1]).values()),
[[[2, 6, 1, 3]]])
assert_equal(list(nx.cliques_containing_node(G,[1,2]).values()),
assert_equal(list(nx.cliques_containing_node(G, [1, 2]).values()),
[[[2, 6, 1, 3]], [[2, 6, 1, 3], [2, 6, 4]]])
assert_equal(nx.cliques_containing_node(G,[1,2]),
assert_equal(nx.cliques_containing_node(G, [1, 2]),
{1: [[2, 6, 1, 3]], 2: [[2, 6, 1, 3], [2, 6, 4]]})
assert_equal(nx.cliques_containing_node(G,1),
assert_equal(nx.cliques_containing_node(G, 1),
[[2, 6, 1, 3]])
assert_equal(nx.cliques_containing_node(G,2),
assert_equal(nx.cliques_containing_node(G, 2),
[[2, 6, 1, 3], [2, 6, 4]])
assert_equal(nx.cliques_containing_node(G,2,cliques=self.cl),
assert_equal(nx.cliques_containing_node(G, 2, cliques=self.cl),
[[2, 6, 1, 3], [2, 6, 4]])
assert_equal(len(nx.cliques_containing_node(G)),11)
assert_equal(len(nx.cliques_containing_node(G)), 11)

def test_make_clique_bipartite(self):
G=self.G
B=nx.make_clique_bipartite(G)
G = self.G
B = nx.make_clique_bipartite(G)
assert_equal(sorted(B.nodes()),
[-5, -4, -3, -2, -1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11])
H=nx.project_down(B)
assert_equal(H.adj,G.adj)
H1=nx.project_up(B)
assert_equal(H1.nodes(),[1, 2, 3, 4, 5])
H2=nx.make_max_clique_graph(G)
assert_equal(H1.adj,H2.adj)
H = nx.project_down(B)
assert_equal(H.adj, G.adj)
H1 = nx.project_up(B)
assert_equal(H1.nodes(), [1, 2, 3, 4, 5])
H2 = nx.make_max_clique_graph(G)
assert_equal(H1.adj, H2.adj)

@raises(nx.NetworkXNotImplemented)
def test_directed(self):
cliques=nx.find_cliques(nx.DiGraph())
cliques = nx.find_cliques(nx.DiGraph())


class TestEnumerateAllCliques:

def test_paper_figure_4(self):
# Same graph as given in Fig. 4 of paper enumerate_all_cliques is
# based on.
# http://ieeexplore.ieee.org/stamp/stamp.jsp?tp=&arnumber=1559964&isnumber=33129
G = nx.Graph()
edges_fig_4 = [('a', 'b'), ('a', 'c'), ('a', 'd'), ('a', 'e'),
('b', 'c'), ('b', 'd'), ('b', 'e'),
('c', 'd'), ('c', 'e'),
('d', 'e'),
('f', 'b'), ('f', 'c'), ('f', 'g'),
('g', 'f'), ('g', 'c'), ('g', 'd'), ('g', 'e')]
G.add_edges_from(edges_fig_4)

cliques = list(nx.enumerate_all_cliques(G))
clique_sizes = list(map(len, cliques))
assert_equal(sorted(clique_sizes), clique_sizes)

expected_cliques = [['a'],
['b'],
['c'],
['d'],
['e'],
['f'],
['g'],
['a', 'b'],
['a', 'b', 'd'],
['a', 'b', 'd', 'e'],
['a', 'b', 'e'],
['a', 'c'],
['a', 'c', 'd'],
['a', 'c', 'd', 'e'],
['a', 'c', 'e'],
['a', 'd'],
['a', 'd', 'e'],
['a', 'e'],
['b', 'c'],
['b', 'c', 'd'],
['b', 'c', 'd', 'e'],
['b', 'c', 'e'],
['b', 'c', 'f'],
['b', 'd'],
['b', 'd', 'e'],
['b', 'e'],
['b', 'f'],
['c', 'd'],
['c', 'd', 'e'],
['c', 'd', 'e', 'g'],
['c', 'd', 'g'],
['c', 'e'],
['c', 'e', 'g'],
['c', 'f'],
['c', 'f', 'g'],
['c', 'g'],
['d', 'e'],
['d', 'e', 'g'],
['d', 'g'],
['e', 'g'],
['f', 'g'],
['a', 'b', 'c'],
['a', 'b', 'c', 'd'],
['a', 'b', 'c', 'd', 'e'],
['a', 'b', 'c', 'e']]

assert_equal(sorted(map(sorted, cliques)),
sorted(map(sorted, expected_cliques)))

0 comments on commit 1c00139

Please sign in to comment.