summaryrefslogtreecommitdiff
path: root/networkx/algorithms
diff options
context:
space:
mode:
authorJarrod Millman <jarrod.millman@gmail.com>2019-11-19 23:12:09 -0800
committerJarrod Millman <jarrod.millman@gmail.com>2019-11-19 23:36:38 -0800
commita9daddcd6dbd3a9323ed892d8d0c8236deb37e2f (patch)
treeed079a14afbf8c4f1b34510b26cbfee1e3576b6b /networkx/algorithms
parentf34040c8e5c72294c4d9b99dad1ff515a0a06eaf (diff)
downloadnetworkx-a9daddcd6dbd3a9323ed892d8d0c8236deb37e2f.tar.gz
Update style
Diffstat (limited to 'networkx/algorithms')
-rw-r--r--networkx/algorithms/connectivity/tests/test_kcomponents.py204
-rw-r--r--networkx/algorithms/connectivity/tests/test_kcutsets.py41
-rw-r--r--networkx/algorithms/flow/tests/test_gomory_hu.py55
-rw-r--r--networkx/algorithms/flow/tests/test_maxflow_large_graph.py76
-rw-r--r--networkx/algorithms/similarity.py431
5 files changed, 500 insertions, 307 deletions
diff --git a/networkx/algorithms/connectivity/tests/test_kcomponents.py b/networkx/algorithms/connectivity/tests/test_kcomponents.py
index f1160261..fbe111ad 100644
--- a/networkx/algorithms/connectivity/tests/test_kcomponents.py
+++ b/networkx/algorithms/connectivity/tests/test_kcomponents.py
@@ -14,14 +14,12 @@ from networkx.algorithms.connectivity.kcomponents import (
def torrents_and_ferraro_graph():
# Graph from https://arxiv.org/pdf/1503.04476v1 p.26
G = nx.convert_node_labels_to_integers(
- nx.grid_graph([5, 5]),
- label_attribute='labels',
+ nx.grid_graph([5, 5]), label_attribute="labels"
)
- rlabels = nx.get_node_attributes(G, 'labels')
+ rlabels = nx.get_node_attributes(G, "labels")
labels = {v: k for k, v in rlabels.items()}
- for nodes in [(labels[(0, 4)], labels[(1, 4)]),
- (labels[(3, 4)], labels[(4, 4)])]:
+ for nodes in [(labels[(0, 4)], labels[(1, 4)]), (labels[(3, 4)], labels[(4, 4)])]:
new_node = G.order() + 1
# Petersen graph is triconnected
P = nx.petersen_graph()
@@ -46,8 +44,7 @@ def torrents_and_ferraro_graph():
# needed because K5s share only one node.
G.add_edge(new_node + 16, new_node + 8)
- for nodes in [(labels[(0, 0)], labels[(1, 0)]),
- (labels[(3, 0)], labels[(4, 0)])]:
+ for nodes in [(labels[(0, 0)], labels[(1, 0)]), (labels[(3, 0)], labels[(4, 0)])]:
new_node = G.order() + 1
# Petersen graph is triconnected
P = nx.petersen_graph()
@@ -137,10 +134,40 @@ def test_karate():
def test_karate_component_number():
karate_k_num = {
- 0: 4, 1: 4, 2: 4, 3: 4, 4: 3, 5: 3, 6: 3, 7: 4, 8: 4, 9: 2,
- 10: 3, 11: 1, 12: 2, 13: 4, 14: 2, 15: 2, 16: 2, 17: 2,
- 18: 2, 19: 3, 20: 2, 21: 2, 22: 2, 23: 3, 24: 3, 25: 3,
- 26: 2, 27: 3, 28: 3, 29: 3, 30: 4, 31: 3, 32: 4, 33: 4
+ 0: 4,
+ 1: 4,
+ 2: 4,
+ 3: 4,
+ 4: 3,
+ 5: 3,
+ 6: 3,
+ 7: 4,
+ 8: 4,
+ 9: 2,
+ 10: 3,
+ 11: 1,
+ 12: 2,
+ 13: 4,
+ 14: 2,
+ 15: 2,
+ 16: 2,
+ 17: 2,
+ 18: 2,
+ 19: 3,
+ 20: 2,
+ 21: 2,
+ 22: 2,
+ 23: 3,
+ 24: 3,
+ 25: 3,
+ 26: 2,
+ 27: 3,
+ 28: 3,
+ 29: 3,
+ 30: 4,
+ 31: 3,
+ 32: 4,
+ 33: 4,
}
G = nx.karate_club_graph()
k_components = nx.k_components(G)
@@ -156,62 +183,64 @@ def test_davis_southern_women():
def test_davis_southern_women_detail_3_and_4():
solution = {
- 3: [{
- 'Nora Fayette',
- 'E10',
- 'Myra Liddel',
- 'E12',
- 'E14',
- 'Frances Anderson',
- 'Evelyn Jefferson',
- 'Ruth DeSand',
- 'Helen Lloyd',
- 'Eleanor Nye',
- 'E9',
- 'E8',
- 'E5',
- 'E4',
- 'E7',
- 'E6',
- 'E1',
- 'Verne Sanderson',
- 'E3',
- 'E2',
- 'Theresa Anderson',
- 'Pearl Oglethorpe',
- 'Katherina Rogers',
- 'Brenda Rogers',
- 'E13',
- 'Charlotte McDowd',
- 'Sylvia Avondale',
- 'Laura Mandeville',
- },
+ 3: [
+ {
+ "Nora Fayette",
+ "E10",
+ "Myra Liddel",
+ "E12",
+ "E14",
+ "Frances Anderson",
+ "Evelyn Jefferson",
+ "Ruth DeSand",
+ "Helen Lloyd",
+ "Eleanor Nye",
+ "E9",
+ "E8",
+ "E5",
+ "E4",
+ "E7",
+ "E6",
+ "E1",
+ "Verne Sanderson",
+ "E3",
+ "E2",
+ "Theresa Anderson",
+ "Pearl Oglethorpe",
+ "Katherina Rogers",
+ "Brenda Rogers",
+ "E13",
+ "Charlotte McDowd",
+ "Sylvia Avondale",
+ "Laura Mandeville",
+ }
],
- 4: [{
- 'Nora Fayette',
- 'E10',
- 'Verne Sanderson',
- 'E12',
- 'Frances Anderson',
- 'Evelyn Jefferson',
- 'Ruth DeSand',
- 'Helen Lloyd',
- 'Eleanor Nye',
- 'E9',
- 'E8',
- 'E5',
- 'E4',
- 'E7',
- 'E6',
- 'Myra Liddel',
- 'E3',
- 'Theresa Anderson',
- 'Katherina Rogers',
- 'Brenda Rogers',
- 'Charlotte McDowd',
- 'Sylvia Avondale',
- 'Laura Mandeville',
- },
+ 4: [
+ {
+ "Nora Fayette",
+ "E10",
+ "Verne Sanderson",
+ "E12",
+ "Frances Anderson",
+ "Evelyn Jefferson",
+ "Ruth DeSand",
+ "Helen Lloyd",
+ "Eleanor Nye",
+ "E9",
+ "E8",
+ "E5",
+ "E4",
+ "E7",
+ "E6",
+ "Myra Liddel",
+ "E3",
+ "Theresa Anderson",
+ "Katherina Rogers",
+ "Brenda Rogers",
+ "Charlotte McDowd",
+ "Sylvia Avondale",
+ "Laura Mandeville",
+ }
],
}
G = nx.davis_southern_women_graph()
@@ -227,24 +256,37 @@ def test_davis_southern_women_detail_3_and_4():
def test_set_consolidation_rosettacode():
# Tests from http://rosettacode.org/wiki/Set_consolidation
def list_of_sets_equal(result, solution):
- assert (
- {frozenset(s) for s in result} ==
- {frozenset(s) for s in solution})
- question = [{'A', 'B'}, {'C', 'D'}]
- solution = [{'A', 'B'}, {'C', 'D'}]
+ assert {frozenset(s) for s in result} == {frozenset(s) for s in solution}
+
+ question = [{"A", "B"}, {"C", "D"}]
+ solution = [{"A", "B"}, {"C", "D"}]
list_of_sets_equal(_consolidate(question, 1), solution)
- question = [{'A', 'B'}, {'B', 'C'}]
- solution = [{'A', 'B', 'C'}]
+ question = [{"A", "B"}, {"B", "C"}]
+ solution = [{"A", "B", "C"}]
list_of_sets_equal(_consolidate(question, 1), solution)
- question = [{'A', 'B'}, {'C', 'D'}, {'D', 'B'}]
- solution = [{'A', 'C', 'B', 'D'}]
+ question = [{"A", "B"}, {"C", "D"}, {"D", "B"}]
+ solution = [{"A", "C", "B", "D"}]
list_of_sets_equal(_consolidate(question, 1), solution)
- question = [{'H', 'I', 'K'}, {'A', 'B'}, {'C', 'D'}, {'D', 'B'}, {'F', 'G', 'H'}]
- solution = [{'A', 'C', 'B', 'D'}, {'G', 'F', 'I', 'H', 'K'}]
+ question = [{"H", "I", "K"}, {"A", "B"}, {"C", "D"}, {"D", "B"}, {"F", "G", "H"}]
+ solution = [{"A", "C", "B", "D"}, {"G", "F", "I", "H", "K"}]
list_of_sets_equal(_consolidate(question, 1), solution)
- question = [{'A', 'H'}, {'H', 'I', 'K'}, {'A', 'B'}, {'C', 'D'}, {'D', 'B'}, {'F', 'G', 'H'}]
- solution = [{'A', 'C', 'B', 'D', 'G', 'F', 'I', 'H', 'K'}]
+ question = [
+ {"A", "H"},
+ {"H", "I", "K"},
+ {"A", "B"},
+ {"C", "D"},
+ {"D", "B"},
+ {"F", "G", "H"},
+ ]
+ solution = [{"A", "C", "B", "D", "G", "F", "I", "H", "K"}]
list_of_sets_equal(_consolidate(question, 1), solution)
- question = [{'H', 'I', 'K'}, {'A', 'B'}, {'C', 'D'}, {'D', 'B'}, {'F', 'G', 'H'}, {'A', 'H'}]
- solution = [{'A', 'C', 'B', 'D', 'G', 'F', 'I', 'H', 'K'}]
+ question = [
+ {"H", "I", "K"},
+ {"A", "B"},
+ {"C", "D"},
+ {"D", "B"},
+ {"F", "G", "H"},
+ {"A", "H"},
+ ]
+ solution = [{"A", "C", "B", "D", "G", "F", "I", "H", "K"}]
list_of_sets_equal(_consolidate(question, 1), solution)
diff --git a/networkx/algorithms/connectivity/tests/test_kcutsets.py b/networkx/algorithms/connectivity/tests/test_kcutsets.py
index 492b309d..4bb07094 100644
--- a/networkx/algorithms/connectivity/tests/test_kcutsets.py
+++ b/networkx/algorithms/connectivity/tests/test_kcutsets.py
@@ -22,15 +22,18 @@ flow_funcs = [
# Some nice synthetic graphs
##
def graph_example_1():
- G = nx.convert_node_labels_to_integers(nx.grid_graph([5, 5]),
- label_attribute='labels')
- rlabels = nx.get_node_attributes(G, 'labels')
+ G = nx.convert_node_labels_to_integers(
+ nx.grid_graph([5, 5]), label_attribute="labels"
+ )
+ rlabels = nx.get_node_attributes(G, "labels")
labels = {v: k for k, v in rlabels.items()}
- for nodes in [(labels[(0, 0)], labels[(1, 0)]),
- (labels[(0, 4)], labels[(1, 4)]),
- (labels[(3, 0)], labels[(4, 0)]),
- (labels[(3, 4)], labels[(4, 4)])]:
+ for nodes in [
+ (labels[(0, 0)], labels[(1, 0)]),
+ (labels[(0, 4)], labels[(1, 4)]),
+ (labels[(3, 0)], labels[(4, 0)]),
+ (labels[(3, 4)], labels[(4, 4)]),
+ ]:
new_node = G.order() + 1
# Petersen graph is triconnected
P = nx.petersen_graph()
@@ -56,13 +59,13 @@ def graph_example_1():
def torrents_and_ferraro_graph():
- G = nx.convert_node_labels_to_integers(nx.grid_graph([5, 5]),
- label_attribute='labels')
- rlabels = nx.get_node_attributes(G, 'labels')
+ G = nx.convert_node_labels_to_integers(
+ nx.grid_graph([5, 5]), label_attribute="labels"
+ )
+ rlabels = nx.get_node_attributes(G, "labels")
labels = {v: k for k, v in rlabels.items()}
- for nodes in [(labels[(0, 4)], labels[(1, 4)]),
- (labels[(3, 4)], labels[(4, 4)])]:
+ for nodes in [(labels[(0, 4)], labels[(1, 4)]), (labels[(3, 4)], labels[(4, 4)])]:
new_node = G.order() + 1
# Petersen graph is triconnected
P = nx.petersen_graph()
@@ -87,8 +90,7 @@ def torrents_and_ferraro_graph():
# This stupid mistake make one reviewer very angry :P
G.add_edge(new_node + 16, new_node + 8)
- for nodes in [(labels[(0, 0)], labels[(1, 0)]),
- (labels[(3, 0)], labels[(4, 0)])]:
+ for nodes in [(labels[(0, 0)], labels[(1, 0)]), (labels[(3, 0)], labels[(4, 0)])]:
new_node = G.order() + 1
# Petersen graph is triconnected
P = nx.petersen_graph()
@@ -209,8 +211,7 @@ def test_disconnected_graph():
def test_alternative_flow_functions():
- graphs = [nx.grid_2d_graph(4, 4),
- nx.cycle_graph(5)]
+ graphs = [nx.grid_2d_graph(4, 4), nx.cycle_graph(5)]
for G in graphs:
node_conn = nx.node_connectivity(G)
for flow_func in flow_funcs:
@@ -261,13 +262,7 @@ def test_cycle_graph():
def test_complete_graph():
G = nx.complete_graph(5)
- solution = [
- {0, 1, 2, 3},
- {0, 1, 2, 4},
- {0, 1, 3, 4},
- {0, 2, 3, 4},
- {1, 2, 3, 4},
- ]
+ solution = [{0, 1, 2, 3}, {0, 1, 2, 4}, {0, 1, 3, 4}, {0, 2, 3, 4}, {1, 2, 3, 4}]
cuts = list(nx.all_node_cuts(G))
assert len(solution) == len(cuts)
for cut in cuts:
diff --git a/networkx/algorithms/flow/tests/test_gomory_hu.py b/networkx/algorithms/flow/tests/test_gomory_hu.py
index b82a32f7..0d9ded84 100644
--- a/networkx/algorithms/flow/tests/test_gomory_hu.py
+++ b/networkx/algorithms/flow/tests/test_gomory_hu.py
@@ -18,10 +18,9 @@ flow_funcs = [
class TestGomoryHuTree:
-
def minimum_edge_weight(self, T, u, v):
- path = nx.shortest_path(T, u, v, weight='weight')
- return min((T[u][v]['weight'], (u, v)) for (u, v) in zip(path, path[1:]))
+ path = nx.shortest_path(T, u, v, weight="weight")
+ return min((T[u][v]["weight"], (u, v)) for (u, v) in zip(path, path[1:]))
def compute_cutset(self, G, T_orig, edge):
T = T_orig.copy()
@@ -34,61 +33,56 @@ class TestGomoryHuTree:
def test_default_flow_function_karate_club_graph(self):
G = nx.karate_club_graph()
- nx.set_edge_attributes(G, 1, 'capacity')
+ nx.set_edge_attributes(G, 1, "capacity")
T = nx.gomory_hu_tree(G)
assert nx.is_tree(T)
for u, v in combinations(G, 2):
cut_value, edge = self.minimum_edge_weight(T, u, v)
- assert (nx.minimum_cut_value(G, u, v) ==
- cut_value)
+ assert nx.minimum_cut_value(G, u, v) == cut_value
def test_karate_club_graph(self):
G = nx.karate_club_graph()
- nx.set_edge_attributes(G, 1, 'capacity')
+ nx.set_edge_attributes(G, 1, "capacity")
for flow_func in flow_funcs:
T = nx.gomory_hu_tree(G, flow_func=flow_func)
assert nx.is_tree(T)
for u, v in combinations(G, 2):
cut_value, edge = self.minimum_edge_weight(T, u, v)
- assert (nx.minimum_cut_value(G, u, v) ==
- cut_value)
+ assert nx.minimum_cut_value(G, u, v) == cut_value
def test_davis_southern_women_graph(self):
G = nx.davis_southern_women_graph()
- nx.set_edge_attributes(G, 1, 'capacity')
+ nx.set_edge_attributes(G, 1, "capacity")
for flow_func in flow_funcs:
T = nx.gomory_hu_tree(G, flow_func=flow_func)
assert nx.is_tree(T)
for u, v in combinations(G, 2):
cut_value, edge = self.minimum_edge_weight(T, u, v)
- assert (nx.minimum_cut_value(G, u, v) ==
- cut_value)
+ assert nx.minimum_cut_value(G, u, v) == cut_value
def test_florentine_families_graph(self):
G = nx.florentine_families_graph()
- nx.set_edge_attributes(G, 1, 'capacity')
+ nx.set_edge_attributes(G, 1, "capacity")
for flow_func in flow_funcs:
T = nx.gomory_hu_tree(G, flow_func=flow_func)
assert nx.is_tree(T)
for u, v in combinations(G, 2):
cut_value, edge = self.minimum_edge_weight(T, u, v)
- assert (nx.minimum_cut_value(G, u, v) ==
- cut_value)
+ assert nx.minimum_cut_value(G, u, v) == cut_value
def test_les_miserables_graph_cutset(self):
G = nx.les_miserables_graph()
- nx.set_edge_attributes(G, 1, 'capacity')
+ nx.set_edge_attributes(G, 1, "capacity")
for flow_func in flow_funcs:
T = nx.gomory_hu_tree(G, flow_func=flow_func)
assert nx.is_tree(T)
for u, v in combinations(G, 2):
cut_value, edge = self.minimum_edge_weight(T, u, v)
- assert (nx.minimum_cut_value(G, u, v) ==
- cut_value)
+ assert nx.minimum_cut_value(G, u, v) == cut_value
def test_karate_club_graph_cutset(self):
G = nx.karate_club_graph()
- nx.set_edge_attributes(G, 1, 'capacity')
+ nx.set_edge_attributes(G, 1, "capacity")
T = nx.gomory_hu_tree(G)
assert nx.is_tree(T)
u, v = 0, 33
@@ -99,18 +93,25 @@ class TestGomoryHuTree:
def test_wikipedia_example(self):
# Example from https://en.wikipedia.org/wiki/Gomory%E2%80%93Hu_tree
G = nx.Graph()
- G.add_weighted_edges_from((
- (0, 1, 1), (0, 2, 7), (1, 2, 1),
- (1, 3, 3), (1, 4, 2), (2, 4, 4),
- (3, 4, 1), (3, 5, 6), (4, 5, 2),
- ))
+ G.add_weighted_edges_from(
+ (
+ (0, 1, 1),
+ (0, 2, 7),
+ (1, 2, 1),
+ (1, 3, 3),
+ (1, 4, 2),
+ (2, 4, 4),
+ (3, 4, 1),
+ (3, 5, 6),
+ (4, 5, 2),
+ )
+ )
for flow_func in flow_funcs:
- T = nx.gomory_hu_tree(G, capacity='weight', flow_func=flow_func)
+ T = nx.gomory_hu_tree(G, capacity="weight", flow_func=flow_func)
assert nx.is_tree(T)
for u, v in combinations(G, 2):
cut_value, edge = self.minimum_edge_weight(T, u, v)
- assert (nx.minimum_cut_value(G, u, v, capacity='weight') ==
- cut_value)
+ assert nx.minimum_cut_value(G, u, v, capacity="weight") == cut_value
def test_directed_raises(self):
with pytest.raises(nx.NetworkXNotImplemented):
diff --git a/networkx/algorithms/flow/tests/test_maxflow_large_graph.py b/networkx/algorithms/flow/tests/test_maxflow_large_graph.py
index e152cb50..dceba17b 100644
--- a/networkx/algorithms/flow/tests/test_maxflow_large_graph.py
+++ b/networkx/algorithms/flow/tests/test_maxflow_large_graph.py
@@ -30,29 +30,27 @@ def gen_pyramid(N):
G = nx.DiGraph()
for i in range(N - 1):
- cap = 1. / (i + 2)
+ cap = 1.0 / (i + 2)
for j in range(i + 1):
- G.add_edge((i, j), (i + 1, j),
- capacity=cap)
- cap = 1. / (i + 1) - cap
- G.add_edge((i, j), (i + 1, j + 1),
- capacity=cap)
- cap = 1. / (i + 2) - cap
+ G.add_edge((i, j), (i + 1, j), capacity=cap)
+ cap = 1.0 / (i + 1) - cap
+ G.add_edge((i, j), (i + 1, j + 1), capacity=cap)
+ cap = 1.0 / (i + 2) - cap
for j in range(N):
- G.add_edge((N - 1, j), 't')
+ G.add_edge((N - 1, j), "t")
return G
def read_graph(name):
dirname = os.path.dirname(__file__)
- path = os.path.join(dirname, name + '.gpickle.bz2')
+ path = os.path.join(dirname, name + ".gpickle.bz2")
return nx.read_gpickle(path)
def validate_flows(G, s, t, soln_value, R, flow_func):
- flow_value = R.graph['flow_value']
+ flow_value = R.graph["flow_value"]
flow_dict = build_flow_dict(G, R)
assert soln_value == flow_value, msg.format(flow_func.__name__)
assert set(G) == set(flow_dict), msg.format(flow_func.__name__)
@@ -61,7 +59,9 @@ def validate_flows(G, s, t, soln_value, R, flow_func):
excess = {u: 0 for u in flow_dict}
for u in flow_dict:
for v, flow in flow_dict[u].items():
- assert flow <= G[u][v].get('capacity', float('inf')), msg.format(flow_func.__name__)
+ assert flow <= G[u][v].get("capacity", float("inf")), msg.format(
+ flow_func.__name__
+ )
assert flow >= 0, msg.format(flow_func.__name__)
excess[u] -= flow
excess[v] += flow
@@ -75,16 +75,15 @@ def validate_flows(G, s, t, soln_value, R, flow_func):
class TestMaxflowLargeGraph:
-
def test_complete_graph(self):
N = 50
G = nx.complete_graph(N)
- nx.set_edge_attributes(G, 5, 'capacity')
- R = build_residual_network(G, 'capacity')
+ nx.set_edge_attributes(G, 5, "capacity")
+ R = build_residual_network(G, "capacity")
kwargs = dict(residual=R)
for flow_func in flow_funcs:
- kwargs['flow_func'] = flow_func
+ kwargs["flow_func"] = flow_func
flow_value = nx.maximum_flow_value(G, 1, 2, **kwargs)
assert flow_value == 5 * (N - 1), msg.format(flow_func.__name__)
@@ -92,56 +91,55 @@ class TestMaxflowLargeGraph:
N = 10
# N = 100 # this gives a graph with 5051 nodes
G = gen_pyramid(N)
- R = build_residual_network(G, 'capacity')
+ R = build_residual_network(G, "capacity")
kwargs = dict(residual=R)
for flow_func in flow_funcs:
- kwargs['flow_func'] = flow_func
- flow_value = nx.maximum_flow_value(G, (0, 0), 't', **kwargs)
- assert almost_equal(flow_value, 1.), msg.format(flow_func.__name__)
+ kwargs["flow_func"] = flow_func
+ flow_value = nx.maximum_flow_value(G, (0, 0), "t", **kwargs)
+ assert almost_equal(flow_value, 1.0), msg.format(flow_func.__name__)
def test_gl1(self):
- G = read_graph('gl1')
+ G = read_graph("gl1")
s = 1
t = len(G)
- R = build_residual_network(G, 'capacity')
+ R = build_residual_network(G, "capacity")
kwargs = dict(residual=R)
# do one flow_func to save time
flow_func = flow_funcs[0]
- validate_flows(G, s, t, 156545, flow_func(G, s, t, **kwargs),
- flow_func)
-# for flow_func in flow_funcs:
-# validate_flows(G, s, t, 156545, flow_func(G, s, t, **kwargs),
-# flow_func)
+ validate_flows(G, s, t, 156545, flow_func(G, s, t, **kwargs), flow_func)
+
+ # for flow_func in flow_funcs:
+ # validate_flows(G, s, t, 156545, flow_func(G, s, t, **kwargs),
+ # flow_func)
def test_gw1(self):
- G = read_graph('gw1')
+ G = read_graph("gw1")
s = 1
t = len(G)
- R = build_residual_network(G, 'capacity')
+ R = build_residual_network(G, "capacity")
kwargs = dict(residual=R)
for flow_func in flow_funcs:
- validate_flows(G, s, t, 1202018, flow_func(G, s, t, **kwargs),
- flow_func)
+ validate_flows(G, s, t, 1202018, flow_func(G, s, t, **kwargs), flow_func)
def test_wlm3(self):
- G = read_graph('wlm3')
+ G = read_graph("wlm3")
s = 1
t = len(G)
- R = build_residual_network(G, 'capacity')
+ R = build_residual_network(G, "capacity")
kwargs = dict(residual=R)
# do one flow_func to save time
flow_func = flow_funcs[0]
- validate_flows(G, s, t, 11875108, flow_func(G, s, t, **kwargs),
- flow_func)
-# for flow_func in flow_funcs:
-# validate_flows(G, s, t, 11875108, flow_func(G, s, t, **kwargs),
-# flow_func)
+ validate_flows(G, s, t, 11875108, flow_func(G, s, t, **kwargs), flow_func)
+
+ # for flow_func in flow_funcs:
+ # validate_flows(G, s, t, 11875108, flow_func(G, s, t, **kwargs),
+ # flow_func)
def test_preflow_push_global_relabel(self):
- G = read_graph('gw1')
+ G = read_graph("gw1")
R = preflow_push(G, 1, len(G), global_relabel_freq=50)
- assert R.graph['flow_value'] == 1202018
+ assert R.graph["flow_value"] == 1202018
diff --git a/networkx/algorithms/similarity.py b/networkx/algorithms/similarity.py
index 985e9500..dfd95efc 100644
--- a/networkx/algorithms/similarity.py
+++ b/networkx/algorithms/similarity.py
@@ -16,12 +16,12 @@ from itertools import product
import networkx as nx
__all__ = [
- 'graph_edit_distance',
- 'optimal_edit_paths',
- 'optimize_graph_edit_distance',
- 'optimize_edit_paths',
- 'simrank_similarity',
- 'simrank_similarity_numpy',
+ "graph_edit_distance",
+ "optimal_edit_paths",
+ "optimize_graph_edit_distance",
+ "optimize_edit_paths",
+ "simrank_similarity",
+ "simrank_similarity_numpy",
]
@@ -29,12 +29,19 @@ def debug_print(*args, **kwargs):
print(*args, **kwargs)
-def graph_edit_distance(G1, G2, node_match=None, edge_match=None,
- node_subst_cost=None, node_del_cost=None,
- node_ins_cost=None,
- edge_subst_cost=None, edge_del_cost=None,
- edge_ins_cost=None,
- upper_bound=None):
+def graph_edit_distance(
+ G1,
+ G2,
+ node_match=None,
+ edge_match=None,
+ node_subst_cost=None,
+ node_del_cost=None,
+ node_ins_cost=None,
+ edge_subst_cost=None,
+ edge_del_cost=None,
+ edge_ins_cost=None,
+ upper_bound=None,
+):
"""Returns GED (graph edit distance) between graphs G1 and G2.
Graph edit distance is a graph similarity measure analogous to
@@ -153,22 +160,38 @@ def graph_edit_distance(G1, G2, node_match=None, edge_match=None,
"""
bestcost = None
- for vertex_path, edge_path, cost in \
- optimize_edit_paths(G1, G2, node_match, edge_match,
- node_subst_cost, node_del_cost, node_ins_cost,
- edge_subst_cost, edge_del_cost, edge_ins_cost,
- upper_bound, True):
+ for vertex_path, edge_path, cost in optimize_edit_paths(
+ G1,
+ G2,
+ node_match,
+ edge_match,
+ node_subst_cost,
+ node_del_cost,
+ node_ins_cost,
+ edge_subst_cost,
+ edge_del_cost,
+ edge_ins_cost,
+ upper_bound,
+ True,
+ ):
# assert bestcost is None or cost < bestcost
bestcost = cost
return bestcost
-def optimal_edit_paths(G1, G2, node_match=None, edge_match=None,
- node_subst_cost=None, node_del_cost=None,
- node_ins_cost=None,
- edge_subst_cost=None, edge_del_cost=None,
- edge_ins_cost=None,
- upper_bound=None):
+def optimal_edit_paths(
+ G1,
+ G2,
+ node_match=None,
+ edge_match=None,
+ node_subst_cost=None,
+ node_del_cost=None,
+ node_ins_cost=None,
+ edge_subst_cost=None,
+ edge_del_cost=None,
+ edge_ins_cost=None,
+ upper_bound=None,
+):
"""Returns all minimum-cost edit paths transforming G1 to G2.
Graph edit path is a sequence of node and edge edit operations
@@ -296,11 +319,20 @@ def optimal_edit_paths(G1, G2, node_match=None, edge_match=None,
"""
paths = list()
bestcost = None
- for vertex_path, edge_path, cost in \
- optimize_edit_paths(G1, G2, node_match, edge_match,
- node_subst_cost, node_del_cost, node_ins_cost,
- edge_subst_cost, edge_del_cost, edge_ins_cost,
- upper_bound, False):
+ for vertex_path, edge_path, cost in optimize_edit_paths(
+ G1,
+ G2,
+ node_match,
+ edge_match,
+ node_subst_cost,
+ node_del_cost,
+ node_ins_cost,
+ edge_subst_cost,
+ edge_del_cost,
+ edge_ins_cost,
+ upper_bound,
+ False,
+ ):
# assert bestcost is None or cost <= bestcost
if bestcost is not None and cost < bestcost:
paths = list()
@@ -309,12 +341,19 @@ def optimal_edit_paths(G1, G2, node_match=None, edge_match=None,
return paths, bestcost
-def optimize_graph_edit_distance(G1, G2, node_match=None, edge_match=None,
- node_subst_cost=None, node_del_cost=None,
- node_ins_cost=None,
- edge_subst_cost=None, edge_del_cost=None,
- edge_ins_cost=None,
- upper_bound=None):
+def optimize_graph_edit_distance(
+ G1,
+ G2,
+ node_match=None,
+ edge_match=None,
+ node_subst_cost=None,
+ node_del_cost=None,
+ node_ins_cost=None,
+ edge_subst_cost=None,
+ edge_del_cost=None,
+ edge_ins_cost=None,
+ upper_bound=None,
+):
"""Returns consecutive approximations of GED (graph edit distance)
between graphs G1 and G2.
@@ -435,20 +474,37 @@ def optimize_graph_edit_distance(G1, G2, node_match=None, edge_match=None,
<10.5220/0005209202710278>. <hal-01168816>
https://hal.archives-ouvertes.fr/hal-01168816
"""
- for vertex_path, edge_path, cost in \
- optimize_edit_paths(G1, G2, node_match, edge_match,
- node_subst_cost, node_del_cost, node_ins_cost,
- edge_subst_cost, edge_del_cost, edge_ins_cost,
- upper_bound, True):
+ for vertex_path, edge_path, cost in optimize_edit_paths(
+ G1,
+ G2,
+ node_match,
+ edge_match,
+ node_subst_cost,
+ node_del_cost,
+ node_ins_cost,
+ edge_subst_cost,
+ edge_del_cost,
+ edge_ins_cost,
+ upper_bound,
+ True,
+ ):
yield cost
-def optimize_edit_paths(G1, G2, node_match=None, edge_match=None,
- node_subst_cost=None, node_del_cost=None,
- node_ins_cost=None,
- edge_subst_cost=None, edge_del_cost=None,
- edge_ins_cost=None,
- upper_bound=None, strictly_decreasing=True):
+def optimize_edit_paths(
+ G1,
+ G2,
+ node_match=None,
+ edge_match=None,
+ node_subst_cost=None,
+ node_del_cost=None,
+ node_ins_cost=None,
+ edge_subst_cost=None,
+ edge_del_cost=None,
+ edge_ins_cost=None,
+ upper_bound=None,
+ strictly_decreasing=True,
+):
"""GED (graph edit distance) calculation: advanced interface.
Graph edit path is a sequence of node and edge edit operations
@@ -603,8 +659,9 @@ def optimize_edit_paths(G1, G2, node_match=None, edge_match=None,
lsa_row_ind[dummy_ind] = lsa_col_ind[subst_ind] + m
lsa_col_ind[dummy_ind] = lsa_row_ind[subst_ind] + n
- return CostMatrix(C, lsa_row_ind, lsa_col_ind,
- C[lsa_row_ind, lsa_col_ind].sum())
+ return CostMatrix(
+ C, lsa_row_ind, lsa_col_ind, C[lsa_row_ind, lsa_col_ind].sum()
+ )
def extract_C(C, i, j, m, n):
# assert(C.shape == (m + n, m + n))
@@ -646,12 +703,18 @@ def optimize_edit_paths(G1, G2, node_match=None, edge_match=None,
N = len(pending_h)
# assert Ce.C.shape == (M + N, M + N)
- g_ind = [i for i in range(M) if pending_g[i][:2] == (u, u) or
- any(pending_g[i][:2] in ((p, u), (u, p))
- for p, q in matched_uv)]
- h_ind = [j for j in range(N) if pending_h[j][:2] == (v, v) or
- any(pending_h[j][:2] in ((q, v), (v, q))
- for p, q in matched_uv)]
+ g_ind = [
+ i
+ for i in range(M)
+ if pending_g[i][:2] == (u, u)
+ or any(pending_g[i][:2] in ((p, u), (u, p)) for p, q in matched_uv)
+ ]
+ h_ind = [
+ j
+ for j in range(N)
+ if pending_h[j][:2] == (v, v)
+ or any(pending_h[j][:2] in ((q, v), (v, q)) for p, q in matched_uv)
+ ]
m = len(g_ind)
n = len(h_ind)
@@ -666,13 +729,16 @@ def optimize_edit_paths(G1, G2, node_match=None, edge_match=None,
for l, j in zip(range(n), h_ind):
h = pending_h[j][:2]
if nx.is_directed(G1) or nx.is_directed(G2):
- if any(g == (p, u) and h == (q, v) or
- g == (u, p) and h == (v, q)
- for p, q in matched_uv):
+ if any(
+ g == (p, u) and h == (q, v) or g == (u, p) and h == (v, q)
+ for p, q in matched_uv
+ ):
continue
else:
- if any(g in ((p, u), (u, p)) and h in ((q, v), (v, q))
- for p, q in matched_uv):
+ if any(
+ g in ((p, u), (u, p)) and h in ((q, v), (v, q))
+ for p, q in matched_uv
+ ):
continue
if g == (u, u):
continue
@@ -681,10 +747,14 @@ def optimize_edit_paths(G1, G2, node_match=None, edge_match=None,
C[k, l] = inf
localCe = make_CostMatrix(C, m, n)
- ij = list((g_ind[k] if k < m else M + h_ind[l],
- h_ind[l] if l < n else N + g_ind[k])
- for k, l in zip(localCe.lsa_row_ind, localCe.lsa_col_ind)
- if k < m or l < n)
+ ij = list(
+ (
+ g_ind[k] if k < m else M + h_ind[l],
+ h_ind[l] if l < n else N + g_ind[k],
+ )
+ for k, l in zip(localCe.lsa_row_ind, localCe.lsa_col_ind)
+ if k < m or l < n
+ )
else:
ij = []
@@ -701,8 +771,9 @@ def optimize_edit_paths(G1, G2, node_match=None, edge_match=None,
else:
return Ce
- def get_edit_ops(matched_uv, pending_u, pending_v, Cv,
- pending_g, pending_h, Ce, matched_cost):
+ def get_edit_ops(
+ matched_uv, pending_u, pending_v, Cv, pending_g, pending_h, Ce, matched_cost
+ ):
"""
Parameters:
matched_uv: partial vertex edit path
@@ -730,57 +801,86 @@ def optimize_edit_paths(G1, G2, node_match=None, edge_match=None,
# assert Cv.C.shape == (m + n, m + n)
# 1) a vertex mapping from optimal linear sum assignment
- i, j = min((k, l) for k, l in zip(Cv.lsa_row_ind, Cv.lsa_col_ind)
- if k < m or l < n)
- xy, localCe = match_edges(pending_u[i] if i < m else None,
- pending_v[j] if j < n else None,
- pending_g, pending_h, Ce, matched_uv)
+ i, j = min(
+ (k, l) for k, l in zip(Cv.lsa_row_ind, Cv.lsa_col_ind) if k < m or l < n
+ )
+ xy, localCe = match_edges(
+ pending_u[i] if i < m else None,
+ pending_v[j] if j < n else None,
+ pending_g,
+ pending_h,
+ Ce,
+ matched_uv,
+ )
Ce_xy = reduce_Ce(Ce, xy, len(pending_g), len(pending_h))
# assert Ce.ls <= localCe.ls + Ce_xy.ls
if prune(matched_cost + Cv.ls + localCe.ls + Ce_xy.ls):
pass
else:
# get reduced Cv efficiently
- Cv_ij = CostMatrix(reduce_C(Cv.C, (i,), (j,), m, n),
- reduce_ind(Cv.lsa_row_ind, (i, m + j)),
- reduce_ind(Cv.lsa_col_ind, (j, n + i)),
- Cv.ls - Cv.C[i, j])
+ Cv_ij = CostMatrix(
+ reduce_C(Cv.C, (i,), (j,), m, n),
+ reduce_ind(Cv.lsa_row_ind, (i, m + j)),
+ reduce_ind(Cv.lsa_col_ind, (j, n + i)),
+ Cv.ls - Cv.C[i, j],
+ )
yield (i, j), Cv_ij, xy, Ce_xy, Cv.C[i, j] + localCe.ls
# 2) other candidates, sorted by lower-bound cost estimate
other = list()
fixed_i, fixed_j = i, j
if m <= n:
- candidates = ((t, fixed_j) for t in range(m + n)
- if t != fixed_i and (t < m or t == m + fixed_j))
+ candidates = (
+ (t, fixed_j)
+ for t in range(m + n)
+ if t != fixed_i and (t < m or t == m + fixed_j)
+ )
else:
- candidates = ((fixed_i, t) for t in range(m + n)
- if t != fixed_j and (t < n or t == n + fixed_i))
+ candidates = (
+ (fixed_i, t)
+ for t in range(m + n)
+ if t != fixed_j and (t < n or t == n + fixed_i)
+ )
for i, j in candidates:
if prune(matched_cost + Cv.C[i, j] + Ce.ls):
continue
- Cv_ij = make_CostMatrix(reduce_C(Cv.C, (i,), (j,), m, n),
- m - 1 if i < m else m,
- n - 1 if j < n else n)
+ Cv_ij = make_CostMatrix(
+ reduce_C(Cv.C, (i,), (j,), m, n),
+ m - 1 if i < m else m,
+ n - 1 if j < n else n,
+ )
# assert Cv.ls <= Cv.C[i, j] + Cv_ij.ls
if prune(matched_cost + Cv.C[i, j] + Cv_ij.ls + Ce.ls):
continue
- xy, localCe = match_edges(pending_u[i] if i < m else None,
- pending_v[j] if j < n else None,
- pending_g, pending_h, Ce, matched_uv)
+ xy, localCe = match_edges(
+ pending_u[i] if i < m else None,
+ pending_v[j] if j < n else None,
+ pending_g,
+ pending_h,
+ Ce,
+ matched_uv,
+ )
if prune(matched_cost + Cv.C[i, j] + Cv_ij.ls + localCe.ls):
continue
Ce_xy = reduce_Ce(Ce, xy, len(pending_g), len(pending_h))
# assert Ce.ls <= localCe.ls + Ce_xy.ls
- if prune(matched_cost + Cv.C[i, j] + Cv_ij.ls + localCe.ls +
- Ce_xy.ls):
+ if prune(matched_cost + Cv.C[i, j] + Cv_ij.ls + localCe.ls + Ce_xy.ls):
continue
other.append(((i, j), Cv_ij, xy, Ce_xy, Cv.C[i, j] + localCe.ls))
yield from sorted(other, key=lambda t: t[4] + t[1].ls + t[3].ls)
- def get_edit_paths(matched_uv, pending_u, pending_v, Cv,
- matched_gh, pending_g, pending_h, Ce, matched_cost):
+ def get_edit_paths(
+ matched_uv,
+ pending_u,
+ pending_v,
+ Cv,
+ matched_gh,
+ pending_g,
+ pending_h,
+ Ce,
+ matched_cost,
+ ):
"""
Parameters:
matched_uv: partial vertex edit path
@@ -833,8 +933,16 @@ def optimize_edit_paths(G1, G2, node_match=None, edge_match=None,
yield matched_uv, matched_gh, matched_cost
else:
- edit_ops = get_edit_ops(matched_uv, pending_u, pending_v, Cv,
- pending_g, pending_h, Ce, matched_cost)
+ edit_ops = get_edit_ops(
+ matched_uv,
+ pending_u,
+ pending_v,
+ Cv,
+ pending_g,
+ pending_h,
+ Ce,
+ matched_cost,
+ )
for ij, Cv_ij, xy, Ce_xy, edit_cost in edit_ops:
i, j = ij
# assert Cv.C[i, j] + sum(Ce.C[t] for t in xy) == edit_cost
@@ -848,20 +956,34 @@ def optimize_edit_paths(G1, G2, node_match=None, edge_match=None,
for x, y in xy:
len_g = len(pending_g)
len_h = len(pending_h)
- matched_gh.append((pending_g[x] if x < len_g else None,
- pending_h[y] if y < len_h else None))
+ matched_gh.append(
+ (
+ pending_g[x] if x < len_g else None,
+ pending_h[y] if y < len_h else None,
+ )
+ )
sortedx = list(sorted(x for x, y in xy))
sortedy = list(sorted(y for x, y in xy))
- G = list((pending_g.pop(x) if x < len(pending_g) else None)
- for x in reversed(sortedx))
- H = list((pending_h.pop(y) if y < len(pending_h) else None)
- for y in reversed(sortedy))
-
- yield from get_edit_paths(matched_uv, pending_u, pending_v,
- Cv_ij,
- matched_gh, pending_g, pending_h,
- Ce_xy,
- matched_cost + edit_cost)
+ G = list(
+ (pending_g.pop(x) if x < len(pending_g) else None)
+ for x in reversed(sortedx)
+ )
+ H = list(
+ (pending_h.pop(y) if y < len(pending_h) else None)
+ for y in reversed(sortedy)
+ )
+
+ yield from get_edit_paths(
+ matched_uv,
+ pending_u,
+ pending_v,
+ Cv_ij,
+ matched_gh,
+ pending_g,
+ pending_h,
+ Ce_xy,
+ matched_cost + edit_cost,
+ )
# backtrack
if u is not None:
@@ -888,13 +1010,21 @@ def optimize_edit_paths(G1, G2, node_match=None, edge_match=None,
n = len(pending_v)
C = np.zeros((m + n, m + n))
if node_subst_cost:
- C[0:m, 0:n] = np.array([node_subst_cost(G1.nodes[u], G2.nodes[v])
- for u in pending_u for v in pending_v]
- ).reshape(m, n)
+ C[0:m, 0:n] = np.array(
+ [
+ node_subst_cost(G1.nodes[u], G2.nodes[v])
+ for u in pending_u
+ for v in pending_v
+ ]
+ ).reshape(m, n)
elif node_match:
- C[0:m, 0:n] = np.array([1 - int(node_match(G1.nodes[u], G2.nodes[v]))
- for u in pending_u for v in pending_v]
- ).reshape(m, n)
+ C[0:m, 0:n] = np.array(
+ [
+ 1 - int(node_match(G1.nodes[u], G2.nodes[v]))
+ for u in pending_u
+ for v in pending_v
+ ]
+ ).reshape(m, n)
else:
# all zeroes
pass
@@ -910,12 +1040,12 @@ def optimize_edit_paths(G1, G2, node_match=None, edge_match=None,
ins_costs = [1] * len(pending_v)
# assert not n or min(ins_costs) >= 0
inf = C[0:m, 0:n].sum() + sum(del_costs) + sum(ins_costs) + 1
- C[0:m, n:n + m] = np.array([del_costs[i] if i == j else inf
- for i in range(m) for j in range(m)]
- ).reshape(m, m)
- C[m:m + n, 0:n] = np.array([ins_costs[i] if i == j else inf
- for i in range(n) for j in range(n)]
- ).reshape(n, n)
+ C[0:m, n : n + m] = np.array(
+ [del_costs[i] if i == j else inf for i in range(m) for j in range(m)]
+ ).reshape(m, m)
+ C[m : m + n, 0:n] = np.array(
+ [ins_costs[i] if i == j else inf for i in range(n) for j in range(n)]
+ ).reshape(n, n)
Cv = make_CostMatrix(C, m, n)
# debug_print('Cv: {} x {}'.format(m, n))
# debug_print(Cv.C)
@@ -928,13 +1058,21 @@ def optimize_edit_paths(G1, G2, node_match=None, edge_match=None,
n = len(pending_h)
C = np.zeros((m + n, m + n))
if edge_subst_cost:
- C[0:m, 0:n] = np.array([edge_subst_cost(G1.edges[g], G2.edges[h])
- for g in pending_g for h in pending_h]
- ).reshape(m, n)
+ C[0:m, 0:n] = np.array(
+ [
+ edge_subst_cost(G1.edges[g], G2.edges[h])
+ for g in pending_g
+ for h in pending_h
+ ]
+ ).reshape(m, n)
elif edge_match:
- C[0:m, 0:n] = np.array([1 - int(edge_match(G1.edges[g], G2.edges[h]))
- for g in pending_g for h in pending_h]
- ).reshape(m, n)
+ C[0:m, 0:n] = np.array(
+ [
+ 1 - int(edge_match(G1.edges[g], G2.edges[h]))
+ for g in pending_g
+ for h in pending_h
+ ]
+ ).reshape(m, n)
else:
# all zeroes
pass
@@ -950,12 +1088,12 @@ def optimize_edit_paths(G1, G2, node_match=None, edge_match=None,
ins_costs = [1] * len(pending_h)
# assert not n or min(ins_costs) >= 0
inf = C[0:m, 0:n].sum() + sum(del_costs) + sum(ins_costs) + 1
- C[0:m, n:n + m] = np.array([del_costs[i] if i == j else inf
- for i in range(m) for j in range(m)]
- ).reshape(m, m)
- C[m:m + n, 0:n] = np.array([ins_costs[i] if i == j else inf
- for i in range(n) for j in range(n)]
- ).reshape(n, n)
+ C[0:m, n : n + m] = np.array(
+ [del_costs[i] if i == j else inf for i in range(m) for j in range(m)]
+ ).reshape(m, m)
+ C[m : m + n, 0:n] = np.array(
+ [ins_costs[i] if i == j else inf for i in range(n) for j in range(n)]
+ ).reshape(n, n)
Ce = make_CostMatrix(C, m, n)
# debug_print('Ce: {} x {}'.format(m, n))
# debug_print(Ce.C)
@@ -966,6 +1104,7 @@ def optimize_edit_paths(G1, G2, node_match=None, edge_match=None,
# initial upper-bound estimate
# NOTE: should work for empty graph
self.value = Cv.C.sum() + Ce.C.sum() + 1
+
maxcost = MaxCost()
def prune(cost):
@@ -979,9 +1118,9 @@ def optimize_edit_paths(G1, G2, node_match=None, edge_match=None,
# Now go!
- for vertex_path, edge_path, cost in \
- get_edit_paths([], pending_u, pending_v, Cv,
- [], pending_g, pending_h, Ce, 0):
+ for vertex_path, edge_path, cost in get_edit_paths(
+ [], pending_u, pending_v, Cv, [], pending_g, pending_h, Ce, 0
+ ):
# assert sorted(G1.nodes) == sorted(u for u, v in vertex_path if u is not None)
# assert sorted(G2.nodes) == sorted(v for u, v in vertex_path if v is not None)
# assert sorted(G1.edges) == sorted(g for g, h in edge_path if g is not None)
@@ -1026,8 +1165,14 @@ def _is_close(d1, d2, atolerance=0, rtolerance=0):
return all(all(_is_close(d1[u][v], d2[u][v]) for v in d1[u]) for u in d1)
-def simrank_similarity(G, source=None, target=None, importance_factor=0.9,
- max_iterations=100, tolerance=1e-4):
+def simrank_similarity(
+ G,
+ source=None,
+ target=None,
+ importance_factor=0.9,
+ max_iterations=100,
+ tolerance=1e-4,
+):
"""Returns the SimRank similarity of nodes in the graph ``G``.
SimRank is a similarity metric that says "two objects are considered
@@ -1121,15 +1266,19 @@ def simrank_similarity(G, source=None, target=None, importance_factor=0.9,
# These functions compute the update to the similarity value of the nodes
# `u` and `v` with respect to the previous similarity values.
- def avg_sim(s): return sum(newsim[w][x] for (w, x) in s) / len(s) if s else 0.0
- def sim(u, v): return importance_factor * avg_sim(list(product(G[u], G[v])))
+ def avg_sim(s):
+ return sum(newsim[w][x] for (w, x) in s) / len(s) if s else 0.0
+
+ def sim(u, v):
+ return importance_factor * avg_sim(list(product(G[u], G[v])))
for _ in range(max_iterations):
if prevsim and _is_close(prevsim, newsim, tolerance):
break
prevsim = newsim
- newsim = {u: {v: sim(u, v) if u is not v else 1
- for v in newsim[u]} for u in newsim}
+ newsim = {
+ u: {v: sim(u, v) if u is not v else 1 for v in newsim[u]} for u in newsim
+ }
if source is not None and target is not None:
return newsim[source][target]
@@ -1138,8 +1287,14 @@ def simrank_similarity(G, source=None, target=None, importance_factor=0.9,
return newsim
-def simrank_similarity_numpy(G, source=None, target=None, importance_factor=0.9,
- max_iterations=100, tolerance=1e-4):
+def simrank_similarity_numpy(
+ G,
+ source=None,
+ target=None,
+ importance_factor=0.9,
+ max_iterations=100,
+ tolerance=1e-4,
+):
"""Calculate SimRank of nodes in ``G`` using matrices with ``numpy``.
The SimRank algorithm for determining node similarity is defined in
@@ -1209,6 +1364,7 @@ def simrank_similarity_numpy(G, source=None, target=None, importance_factor=0.9,
# where C is the importance factor, A is the column normalized
# adjacency matrix, and I is the identity matrix.
import numpy as np
+
adjacency_matrix = nx.to_numpy_array(G)
# column-normalize the ``adjacency_matrix``
@@ -1218,7 +1374,8 @@ def simrank_similarity_numpy(G, source=None, target=None, importance_factor=0.9,
for _ in range(max_iterations):
prevsim = np.copy(newsim)
newsim = importance_factor * np.matmul(
- np.matmul(adjacency_matrix.T, prevsim), adjacency_matrix)
+ np.matmul(adjacency_matrix.T, prevsim), adjacency_matrix
+ )
np.fill_diagonal(newsim, 1.0)
if np.allclose(prevsim, newsim, atol=tolerance):