comparison env/lib/python3.9/site-packages/networkx/generators/internet_as_graphs.py @ 0:4f3585e2f14b draft default tip

"planemo upload commit 60cee0fc7c0cda8592644e1aad72851dec82c959"
author shellac
date Mon, 22 Mar 2021 18:12:50 +0000
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:4f3585e2f14b
1 """Generates graphs resembling the Internet Autonomous System network"""
2
3 import networkx as nx
4 from networkx.utils import py_random_state
5
6 __all__ = ["random_internet_as_graph"]
7
8
9 def uniform_int_from_avg(a, m, seed):
10 """ Pick a random integer with uniform probability.
11
12 Returns a random integer uniformly taken from a distribution with
13 minimum value 'a' and average value 'm', X~U(a,b), E[X]=m, X in N where
14 b = 2*m - a.
15
16 Notes
17 -----
18 p = (b-floor(b))/2
19 X = X1 + X2; X1~U(a,floor(b)), X2~B(p)
20 E[X] = E[X1] + E[X2] = (floor(b)+a)/2 + (b-floor(b))/2 = (b+a)/2 = m
21 """
22
23 from math import floor
24
25 assert m >= a
26 b = 2 * m - a
27 p = (b - floor(b)) / 2
28 X1 = int(round(seed.random() * (floor(b) - a) + a))
29 if seed.random() < p:
30 X2 = 1
31 else:
32 X2 = 0
33 return X1 + X2
34
35
36 def choose_pref_attach(degs, seed):
37 """ Pick a random value, with a probability given by its weight.
38
39 Returns a random choice among degs keys, each of which has a
40 probability proportional to the corresponding dictionary value.
41
42 Parameters
43 ----------
44 degs: dictionary
45 It contains the possible values (keys) and the corresponding
46 probabilities (values)
47 seed: random state
48
49 Returns
50 -------
51 v: object
52 A key of degs or None if degs is empty
53 """
54
55 if len(degs) == 0:
56 return None
57 s = sum(degs.values())
58 if s == 0:
59 return seed.choice(list(degs.keys()))
60 v = seed.random() * s
61
62 nodes = list(degs.keys())
63 i = 0
64 acc = degs[nodes[i]]
65 while v > acc:
66 i += 1
67 acc += degs[nodes[i]]
68 return nodes[i]
69
70
71 class AS_graph_generator:
72 """ Generates random internet AS graphs.
73 """
74
75 def __init__(self, n, seed):
76 """ Initializes variables. Immediate numbers are taken from [1].
77
78 Parameters
79 ----------
80 n: integer
81 Number of graph nodes
82 seed: random state
83 Indicator of random number generation state.
84 See :ref:`Randomness<randomness>`.
85
86 Returns
87 -------
88 GG: AS_graph_generator object
89
90 References
91 ----------
92 [1] A. Elmokashfi, A. Kvalbein and C. Dovrolis, "On the Scalability of
93 BGP: The Role of Topology Growth," in IEEE Journal on Selected Areas
94 in Communications, vol. 28, no. 8, pp. 1250-1261, October 2010.
95 """
96
97 self.seed = seed
98 self.n_t = min(n, int(round(self.seed.random() * 2 + 4))) # num of T nodes
99 self.n_m = int(round(0.15 * n)) # number of M nodes
100 self.n_cp = int(round(0.05 * n)) # number of CP nodes
101 self.n_c = max(0, n - self.n_t - self.n_m - self.n_cp) # number of C nodes
102
103 self.d_m = 2 + (2.5 * n) / 10000 # average multihoming degree for M nodes
104 self.d_cp = 2 + (1.5 * n) / 10000 # avg multihoming degree for CP nodes
105 self.d_c = 1 + (5 * n) / 100000 # average multihoming degree for C nodes
106
107 self.p_m_m = 1 + (2 * n) / 10000 # avg num of peer edges between M and M
108 self.p_cp_m = 0.2 + (2 * n) / 10000 # avg num of peer edges between CP, M
109 self.p_cp_cp = 0.05 + (2 * n) / 100000 # avg num of peer edges btwn CP, CP
110
111 self.t_m = 0.375 # probability M's provider is T
112 self.t_cp = 0.375 # probability CP's provider is T
113 self.t_c = 0.125 # probability C's provider is T
114
115 def t_graph(self):
116 """ Generates the core mesh network of tier one nodes of a AS graph.
117
118 Returns
119 -------
120 G: Networkx Graph
121 Core network
122 """
123
124 self.G = nx.Graph()
125 for i in range(self.n_t):
126 self.G.add_node(i, type="T")
127 for r in self.regions:
128 self.regions[r].add(i)
129 for j in self.G.nodes():
130 if i != j:
131 self.add_edge(i, j, "peer")
132 self.customers[i] = set()
133 self.providers[i] = set()
134 return self.G
135
136 def add_edge(self, i, j, kind):
137 if kind == "transit":
138 customer = str(i)
139 else:
140 customer = "none"
141 self.G.add_edge(i, j, type=kind, customer=customer)
142
143 def choose_peer_pref_attach(self, node_list):
144 """ Pick a node with a probability weighted by its peer degree.
145
146 Pick a node from node_list with preferential attachment
147 computed only on their peer degree
148 """
149
150 d = {}
151 for n in node_list:
152 d[n] = self.G.nodes[n]["peers"]
153 return choose_pref_attach(d, self.seed)
154
155 def choose_node_pref_attach(self, node_list):
156 """ Pick a node with a probability weighted by its degree.
157
158 Pick a node from node_list with preferential attachment
159 computed on their degree
160 """
161
162 degs = dict(self.G.degree(node_list))
163 return choose_pref_attach(degs, self.seed)
164
165 def add_customer(self, i, j):
166 """ Keep the dictionaries 'customers' and 'providers' consistent.
167 """
168
169 self.customers[j].add(i)
170 self.providers[i].add(j)
171 for z in self.providers[j]:
172 self.customers[z].add(i)
173 self.providers[i].add(z)
174
175 def add_node(self, i, kind, reg2prob, avg_deg, t_edge_prob):
176 """ Add a node and its customer transit edges to the graph.
177
178 Parameters
179 ----------
180 i: object
181 Identifier of the new node
182 kind: string
183 Type of the new node. Options are: 'M' for middle node, 'CP' for
184 content provider and 'C' for customer.
185 reg2prob: float
186 Probability the new node can be in two different regions.
187 avg_deg: float
188 Average number of transit nodes of which node i is customer.
189 t_edge_prob: float
190 Probability node i establish a customer transit edge with a tier
191 one (T) node
192
193 Returns
194 -------
195 i: object
196 Identifier of the new node
197 """
198
199 regs = 1 # regions in which node resides
200 if self.seed.random() < reg2prob: # node is in two regions
201 regs = 2
202 node_options = set()
203
204 self.G.add_node(i, type=kind, peers=0)
205 self.customers[i] = set()
206 self.providers[i] = set()
207 self.nodes[kind].add(i)
208 for r in self.seed.sample(list(self.regions), regs):
209 node_options = node_options.union(self.regions[r])
210 self.regions[r].add(i)
211
212 edge_num = uniform_int_from_avg(1, avg_deg, self.seed)
213
214 t_options = node_options.intersection(self.nodes["T"])
215 m_options = node_options.intersection(self.nodes["M"])
216 if i in m_options:
217 m_options.remove(i)
218 d = 0
219 while d < edge_num and (len(t_options) > 0 or len(m_options) > 0):
220 if len(m_options) == 0 or (
221 len(t_options) > 0 and self.seed.random() < t_edge_prob
222 ): # add edge to a T node
223 j = self.choose_node_pref_attach(t_options)
224 t_options.remove(j)
225 else:
226 j = self.choose_node_pref_attach(m_options)
227 m_options.remove(j)
228 self.add_edge(i, j, "transit")
229 self.add_customer(i, j)
230 d += 1
231
232 return i
233
234 def add_m_peering_link(self, m, to_kind):
235 """ Add a peering link between two middle tier (M) nodes.
236
237 Target node j is drawn considering a preferential attachment based on
238 other M node peering degree.
239
240 Parameters
241 ----------
242 m: object
243 Node identifier
244 to_kind: string
245 type for target node j (must be always M)
246
247 Returns
248 -------
249 success: boolean
250 """
251
252 # candidates are of type 'M' and are not customers of m
253 node_options = self.nodes["M"].difference(self.customers[m])
254 # candidates are not providers of m
255 node_options = node_options.difference(self.providers[m])
256 # remove self
257 if m in node_options:
258 node_options.remove(m)
259
260 # remove candidates we are already connected to
261 for j in self.G.neighbors(m):
262 if j in node_options:
263 node_options.remove(j)
264
265 if len(node_options) > 0:
266 j = self.choose_peer_pref_attach(node_options)
267 self.add_edge(m, j, "peer")
268 self.G.nodes[m]["peers"] += 1
269 self.G.nodes[j]["peers"] += 1
270 return True
271 else:
272 return False
273
274 def add_cp_peering_link(self, cp, to_kind):
275 """ Add a peering link to a content provider (CP) node.
276
277 Target node j can be CP or M and it is drawn uniformely among the nodes
278 belonging to the same region as cp.
279
280 Parameters
281 ----------
282 cp: object
283 Node identifier
284 to_kind: string
285 type for target node j (must be M or CP)
286
287 Returns
288 -------
289 success: boolean
290 """
291
292 node_options = set()
293 for r in self.regions: # options include nodes in the same region(s)
294 if cp in self.regions[r]:
295 node_options = node_options.union(self.regions[r])
296
297 # options are restricted to the indicated kind ('M' or 'CP')
298 node_options = self.nodes[to_kind].intersection(node_options)
299
300 # remove self
301 if cp in node_options:
302 node_options.remove(cp)
303
304 # remove nodes that are cp's providers
305 node_options = node_options.difference(self.providers[cp])
306
307 # remove nodes we are already connected to
308 for j in self.G.neighbors(cp):
309 if j in node_options:
310 node_options.remove(j)
311
312 if len(node_options) > 0:
313 j = self.seed.sample(node_options, 1)[0]
314 self.add_edge(cp, j, "peer")
315 self.G.nodes[cp]["peers"] += 1
316 self.G.nodes[j]["peers"] += 1
317 return True
318 else:
319 return False
320
321 def graph_regions(self, rn):
322 """ Initializes AS network regions.
323
324 Parameters
325 ----------
326 rn: integer
327 Number of regions
328 """
329
330 self.regions = {}
331 for i in range(rn):
332 self.regions["REG" + str(i)] = set()
333
334 def add_peering_links(self, from_kind, to_kind):
335 """ Utility function to add peering links among node groups.
336 """
337 peer_link_method = None
338 if from_kind == "M":
339 peer_link_method = self.add_m_peering_link
340 m = self.p_m_m
341 if from_kind == "CP":
342 peer_link_method = self.add_cp_peering_link
343 if to_kind == "M":
344 m = self.p_cp_m
345 else:
346 m = self.p_cp_cp
347
348 for i in self.nodes[from_kind]:
349 num = uniform_int_from_avg(0, m, self.seed)
350 for _ in range(num):
351 peer_link_method(i, to_kind)
352
353 def generate(self):
354 """ Generates a random AS network graph as described in [1].
355
356 Returns
357 -------
358 G: Graph object
359
360 Notes
361 -----
362 The process steps are the following: first we create the core network
363 of tier one nodes, then we add the middle tier (M), the content
364 provider (CP) and the customer (C) nodes along with their transit edges
365 (link i,j means i is customer of j). Finally we add peering links
366 between M nodes, between M and CP nodes and between CP node couples.
367 For a detailed description of the algorithm, please refer to [1].
368
369 References
370 ----------
371 [1] A. Elmokashfi, A. Kvalbein and C. Dovrolis, "On the Scalability of
372 BGP: The Role of Topology Growth," in IEEE Journal on Selected Areas
373 in Communications, vol. 28, no. 8, pp. 1250-1261, October 2010.
374 """
375
376 self.graph_regions(5)
377 self.customers = {}
378 self.providers = {}
379 self.nodes = {"T": set(), "M": set(), "CP": set(), "C": set()}
380
381 self.t_graph()
382 self.nodes["T"] = set(list(self.G.nodes()))
383
384 i = len(self.nodes["T"])
385 for _ in range(self.n_m):
386 self.nodes["M"].add(self.add_node(i, "M", 0.2, self.d_m, self.t_m))
387 i += 1
388 for _ in range(self.n_cp):
389 self.nodes["CP"].add(self.add_node(i, "CP", 0.05, self.d_cp, self.t_cp))
390 i += 1
391 for _ in range(self.n_c):
392 self.nodes["C"].add(self.add_node(i, "C", 0, self.d_c, self.t_c))
393 i += 1
394
395 self.add_peering_links("M", "M")
396 self.add_peering_links("CP", "M")
397 self.add_peering_links("CP", "CP")
398
399 return self.G
400
401
402 @py_random_state(1)
403 def random_internet_as_graph(n, seed=None):
404 """ Generates a random undirected graph resembling the Internet AS network
405
406 Parameters
407 ----------
408 n: integer in [1000, 10000]
409 Number of graph nodes
410 seed : integer, random_state, or None (default)
411 Indicator of random number generation state.
412 See :ref:`Randomness<randomness>`.
413
414 Returns
415 -------
416 G: Networkx Graph object
417 A randomly generated undirected graph
418
419 Notes
420 -----
421 This algorithm returns an undirected graph resembling the Internet
422 Autonomous System (AS) network, it uses the approach by Elmokashfi et al.
423 [1] and it grants the properties described in the related paper [1].
424
425 Each node models an autonomous system, with an attribute 'type' specifying
426 its kind; tier-1 (T), mid-level (M), customer (C) or content-provider (CP).
427 Each edge models an ADV communication link (hence, bidirectional) with
428 attributes:
429 - type: transit|peer, the kind of commercial agreement between nodes;
430 - customer: <node id>, the identifier of the node acting as customer
431 ('none' if type is peer).
432
433 References
434 ----------
435 [1] A. Elmokashfi, A. Kvalbein and C. Dovrolis, "On the Scalability of
436 BGP: The Role of Topology Growth," in IEEE Journal on Selected Areas
437 in Communications, vol. 28, no. 8, pp. 1250-1261, October 2010.
438 """
439
440 GG = AS_graph_generator(n, seed)
441 G = GG.generate()
442 return G