/
functions.py
196 lines (151 loc) · 7.33 KB
/
functions.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
import networkx as nx
import random
import matplotlib.pyplot as plt
def basic_numbers(g: nx.Graph):
print(f"Number of Nodes (Order): {g.number_of_nodes()}")
print(f"Number of Edges (Size): {g.number_of_edges()}")
# https://networkx.github.io/documentation/stable/reference/generated/networkx.classes.function.density.html
print(f"Density {str(nx.density(g))}")
# https://networkx.github.io/documentation/stable/reference/algorithms/generated/networkx.algorithms.approximation.clustering_coefficient.average_clustering.html
def clustering_coefficient(g: nx.Graph):
if g.is_directed():
print("clustering coefficient not implemented for directed graphs")
else:
print(f"clustering coefficient: {str(nx.average_clustering(g))}")
# https://networkx.github.io/documentation/stable/reference/algorithms/generated/networkx.algorithms.cluster.clustering.html
def clustering_distribution(g: nx.Graph, name: str):
# computes clustering coefficient for each node
# result is a map: node -> coefficient
clustering: dict = nx.clustering(g)
draw_histogram(clustering.values(), f"Clustering coefficient distribution of {name}", "Clustering coefficient")
# https://networkx.github.io/documentation/stable/reference/algorithms/generated/networkx.algorithms.shortest_paths.generic.average_shortest_path_length.html
def average_shortest_path_length(g):
if not g.is_directed() and nx.is_connected(g):
print(f"Average shortest path length: {str(nx.average_shortest_path_length(g))}")
else:
print("cant compute average shortest path length, graph directed or graph not connected")
# https://networkx.github.io/documentation/stable/reference/algorithms/generated/networkx.algorithms.distance_measures.diameter.html
def diameter(g: nx.Graph):
if not g.is_directed() and nx.is_connected(g):
print(f"Diameter: {nx.diameter(g)}")
else:
print("Diameter not computable, graph not connected or is directed")
# https://networkx.github.io/documentation/stable/reference/algorithms/generated/networkx.algorithms.approximation.connectivity.node_connectivity.html
def connectivity(g: nx.Graph):
print(f"Connectivity: {nx.node_connectivity(g)}")
# uses node_connectivity function as above
# node_connectivity is the number of nodes that must be removed to disconnect the graph
def cohesiveness(g: nx.Graph, v):
before = nx.node_connectivity(g)
copy = g.copy()
copy.remove_node(v)
after = nx.node_connectivity(copy)
# print(f"before: {before} after: {after}, cohesiveness of node {v}: {before - after}")
return before - after
def power_law_properties(g: nx.Graph, name: str):
degrees = []
for node_degree_pair in nx.degree(g):
degrees.append(node_degree_pair[1])
draw_histogram(degrees, f"Degree distribution of {name}", "degree", bin_count=100)
def cohesiveness_distribution(g: nx.Graph, name: str):
arr = []
for node in g.nodes:
arr.append(cohesiveness(g, node))
draw_histogram(arr, f"Cohesiveness distribution of {name}", "Cohesiveness")
def edge_persistence_greedy_attack(g: nx.Graph, name: str):
copy = g.copy()
# initialize the vars
removed_nodes_to_zero_degree_dict = dict()
zero_degree_count = 0
removed_nodes_count = 0
# repeat this until all nodes are isolated
while zero_degree_count < copy.number_of_nodes():
highest_tuple = None
zero_degree_count = 0
# find node with highest degree and count nodes with zero degree at the same time
# means zero_degree_count stores the number of isolated nodes after the last removal
# therefore save this before removing a new node and once after the while loop to get all values
for entry in nx.degree(copy):
# entry[0] is the node, entry[1] its degree
if highest_tuple is None:
highest_tuple = entry
elif highest_tuple[1] < entry[1]:
highest_tuple = entry
if entry[1] == 0:
zero_degree_count += 1
# store the current number of isolated nodes mapped to the removed nodes and total nodes
removed_nodes_to_zero_degree_dict[removed_nodes_count] = zero_degree_count # , copy.number_of_nodes()
# now remove the node with the highest degree and increase the counter
# if highest_tuple[1] == 0, then the while loop stops
if highest_tuple[1] > 0:
# print(f"removing node {highest_tuple[0]}") # for debugging
copy.remove_node(highest_tuple[0])
removed_nodes_count += 1
# add for the last removal
removed_nodes_to_zero_degree_dict[removed_nodes_count] = zero_degree_count # , copy.number_of_nodes()
plt.plot(removed_nodes_to_zero_degree_dict.keys(), removed_nodes_to_zero_degree_dict.values())
plt.title("Edge persistence against greedy Attack")
plt.xlabel("Removed nodes count")
plt.ylabel("Isolated nodes count")
plt.savefig("output/edge_persistence_greedy_" + name + ".png")
plt.clf()
def resilience_against_targeted_attacks(g: nx.Graph):
copy = g.copy()
# initialize the vars
removed_nodes_count = 0
# repeat this until the graph is disconnected
# finds nodes with the lowest degree and removes neighbor nodes to disconnect them
while nx.is_connected(copy):
lowest_tuple = None
# find node with lowest degree
for entry in nx.degree(copy):
# entry[0] is the node, entry[1] its degree
if lowest_tuple is None:
lowest_tuple = entry
elif lowest_tuple[1] > entry[1]:
lowest_tuple = entry
# get first neighbor node
neighbor = next(nx.all_neighbors(copy, lowest_tuple[0]))
# now remove the node with the highest degree and increase the counter
print(f"removing node {neighbor}") # for debugging
copy.remove_node(neighbor)
removed_nodes_count += 1
# add for the last removal
print(f"Resilience against targeted attack: {str(removed_nodes_count)} nodes removed until graph disconnect")
def resilience_against_random_attacks(g: nx.Graph):
sum_removed_edges = 0
iterations = 100
# removes random edges until graph is disconnected
for i in range(iterations):
copy = g.copy()
removed_edges = 0
while nx.is_connected(copy):
# remove a random edge
edge = random_edge(copy)
copy.remove_edge(edge[0], edge[1])
removed_edges += 1
# print(f"removing edge {str(edge)}")
sum_removed_edges += removed_edges
# calculate average
avg = sum_removed_edges / iterations
print(f"Resilience against random attacks: removed {str(avg)} edges in average over {str(iterations)} iterations")
# selects a random node
def random_edge(g: nx.Graph):
edge_index = random.randrange(g.number_of_edges())
index = 0
for edge in g.edges:
if index == edge_index:
return edge
index += 1
# https://matplotlib.org/api/pyplot_api.html#matplotlib.pyplot.hist
def draw_histogram(data, name, x_label, y_label="frequency", bin_count=None):
if bin_count is None:
plt.hist(data)
else:
plt.hist(data, bin_count)
# plt.plot(histogram_data.keys(), histogram_data.values())
plt.title(name)
plt.xlabel(x_label)
plt.ylabel(y_label)
plt.savefig("output/" + name + ".png")
plt.clf()