def waxman_1_topology(n, alpha=0.4, beta=0.1, L=1.0, distance_unit='Km', seed=None): r""" Return a Waxman-1 random topology. The Waxman-1 random topology models assigns link between nodes with probability .. math:: p = \alpha*exp(-d/(\beta*L)). where the distance *d* is chosen randomly in *[0,L]*. Parameters ---------- n : int Number of nodes alpha : float Model parameter chosen in *(0,1]* (higher alpha increases link density) beta : float Model parameter chosen in *(0,1]* (higher beta increases difference between density of short and long links) L : float Maximum distance between nodes. seed : int, optional Seed for random number generator (default=None). Returns ------- G : Topology Notes ----- Each node of G has the attributes *latitude* and *longitude*. These attributes are not expressed in degrees but in *distance_unit*. Each edge of G has the attribute *length*, which is also expressed in *distance_unit*. References ---------- .. [1] B. M. Waxman, Routing of multipoint connections. IEEE J. Select. Areas Commun. 6(9),(1988) 1617-1622. """ # validate input parameters if not isinstance(n, int) or n <= 0: raise ValueError('n must be a positive integer') if alpha > 1 or alpha <= 0 or beta > 1 or beta <= 0: raise ValueError('alpha and beta must be float values in (0,1]') if L <= 0: raise ValueError('L must be a positive number') if seed is not None: random.seed(seed) G = Topology(type='waxman_1', distance_unit=distance_unit) G.name = "waxman_1_topology(%s, %s, %s, %s)" % (n, alpha, beta, L) G.add_nodes_from(range(n)) nodes = G.nodes() while nodes: u = nodes.pop() for v in nodes: d = L * random.random() if random.random() < alpha * math.exp(-d / (beta * L)): G.add_edge(u, v, length=d) return G
def extended_barabasi_albert_topology(n, m, m0, p, q, seed=None): r""" Return a random topology using the extended Barabasi-Albert preferential attachment model. Differently from the original Barabasi-Albert model, this model takes into account the presence of local events, such as the addition of new links or the rewiring of existing links. More precisely, the Barabasi-Albert topology is built as follows. First, a topology with *m0* isolated nodes is created. Then, at each step: with probability *p* add *m* new links between existing nodes, selected with probability: .. math:: \Pi(i) = \frac{deg(i) + 1}{\sum_{v \in V} (deg(v) + 1)} with probability *q* rewire *m* links. Each link to be rewired is selected as follows: a node i is randomly selected and a link is randomly removed from it. The node i is then connected to a new node randomly selected with probability :math:`\Pi(i)`, with probability :math:`1-p-q` add a new node and attach it to m nodes of the existing topology selected with probability :math:`\Pi(i)` Repeat the previous step until the topology comprises n nodes in total. Parameters ---------- n : int Number of nodes m : int Number of edges to attach from a new node to existing nodes m0 : int Number of edges initially attached to the network p : float The probability that new links are added q : float The probability that existing links are rewired seed : int, optional Seed for random number generator (default=None). Returns ------- G : Topology References ---------- .. [1] A. L. Barabasi and R. Albert "Topology of evolving networks: local events and universality", Physical Review Letters 85(24), 2000. """ def calc_pi(G): """Calculate extended-BA Pi function for all nodes of the graph""" degree = G.degree() den = float(sum(degree.values()) + G.number_of_nodes()) return {node: (degree[node] + 1) / den for node in G.nodes_iter()} # input parameters if n < 1 or m < 1 or m0 < 1: raise ValueError('n, m and m0 must be a positive integer') if m >= m0: raise ValueError('m must be <= m0') if n < m0: raise ValueError('n must be > m0') if p > 1 or p < 0: raise ValueError('p must be included between 0 and 1') if q > 1 or q < 0: raise ValueError('q must be included between 0 and 1') if p + q > 1: raise ValueError('p + q must be <= 1') if seed is not None: random.seed(seed) G = Topology(type='extended_ba') G.name = "ext_ba_topology(%d, %d, %d, %f, %f)" % (n, m, m0, p, q) # Step 1: Add m0 isolated nodes G.add_nodes_from(range(m0)) while G.number_of_nodes() < n: pi = calc_pi(G) r = random.random() if r <= p: # add m new links with probability p n_nodes = G.number_of_nodes() n_edges = G.number_of_edges() max_n_edges = (n_nodes * (n_nodes - 1)) / 2 if n_edges + m > max_n_edges: # cannot add m links continue # rewire or add nodes new_links = 0 while new_links < m: u = random_from_pdf(pi) v = random_from_pdf(pi) if u is not v and not G.has_edge(u, v): G.add_edge(u, v) new_links += 1 elif r > p and r <= p + q: # rewire m links with probability q rewired_links = 0 while rewired_links < m: i = random.choice(G.nodes()) # pick up node randomly (uniform) if len(G.edge[i]) is 0: # if i has no edges, I cannot rewire break j = random.choice(list( G.edge[i].keys())) # node to be disconnected k = random_from_pdf(pi) # new node to be connected if i is not k and j is not k and not G.has_edge(i, k): G.remove_edge(i, j) G.add_edge(i, k) rewired_links += 1 else: # add a new node with probability 1 - p - q new_node = G.number_of_nodes() G.add_node(new_node) new_links = 0 while new_links < m: existing_node = random_from_pdf(pi) if not G.has_edge(new_node, existing_node): G.add_edge(new_node, existing_node) new_links += 1 return G
def waxman_2_topology(n, alpha=0.4, beta=0.1, domain=(0, 0, 1, 1), distance_unit='Km', seed=None): r"""Return a Waxman-2 random topology. The Waxman-2 random topology models place n nodes uniformly at random in a rectangular domain. Two nodes u, v are connected with a link with probability .. math:: p = \alpha*exp(-d/(\beta*L)). where the distance *d* is the Euclidean distance between the nodes u and v. and *L* is the maximum distance between all nodes in the graph. Parameters ---------- n : int Number of nodes alpha : float Model parameter chosen in *(0,1]* (higher alpha increases link density) beta : float Model parameter chosen in *(0,1]* (higher beta increases difference between density of short and long links) domain : tuple of numbers, optional Domain size (xmin, ymin, xmax, ymax) seed : int, optional Seed for random number generator (default=None). Returns ------- G : Topology Notes ----- Each edge of G has the attribute *length* References ---------- .. [1] B. M. Waxman, Routing of multipoint connections. IEEE J. Select. Areas Commun. 6(9),(1988) 1617-1622. """ # validate input parameters if not isinstance(n, int) or n <= 0: raise ValueError('n must be a positive integer') if alpha > 1 or alpha <= 0 or beta > 1 or beta <= 0: raise ValueError('alpha and beta must be float values in (0,1]') if not isinstance(domain, tuple) or len(domain) != 4: raise ValueError('domain must be a tuple of 4 number') (xmin, ymin, xmax, ymax) = domain if xmin > xmax: raise ValueError('In domain, xmin cannot be greater than xmax') if ymin > ymax: raise ValueError('In domain, ymin cannot be greater than ymax') if seed is not None: random.seed(seed) G = Topology(type='waxman_2', distance_unit=distance_unit) G.name = "waxman_2_topology(%s, %s, %s)" % (n, alpha, beta) G.add_nodes_from(range(n)) for v in G.nodes_iter(): G.node[v]['latitude'] = (ymin + (ymax - ymin)) * random.random() G.node[v]['longitude'] = (xmin + (xmax - xmin)) * random.random() l = {} nodes = G.nodes() while nodes: u = nodes.pop() for v in nodes: x_u = G.node[u]['longitude'] x_v = G.node[v]['longitude'] y_u = G.node[u]['latitude'] y_v = G.node[v]['latitude'] l[(u, v)] = math.sqrt((x_u - x_v)**2 + (y_u - y_v)**2) L = max(l.values()) for (u, v), d in l.items(): if random.random() < alpha * math.exp(-d / (beta * L)): G.add_edge(u, v, length=d) return G
def waxman_1_topology(n, alpha=0.4, beta=0.1, L=1.0, distance_unit='Km', seed=None): r""" Return a Waxman-1 random topology. The Waxman-1 random topology models assigns link between nodes with probability .. math:: p = \alpha*exp(-d/(\beta*L)). where the distance *d* is chosen randomly in *[0,L]*. Parameters ---------- n : int Number of nodes alpha : float Model parameter chosen in *(0,1]* (higher alpha increases link density) beta : float Model parameter chosen in *(0,1]* (higher beta increases difference between density of short and long links) L : float Maximum distance between nodes. seed : int, optional Seed for random number generator (default=None). Returns ------- G : Topology Notes ----- Each node of G has the attributes *latitude* and *longitude*. These attributes are not expressed in degrees but in *distance_unit*. Each edge of G has the attribute *length*, which is also expressed in *distance_unit*. References ---------- .. [1] B. M. Waxman, Routing of multipoint connections. IEEE J. Select. Areas Commun. 6(9),(1988) 1617-1622. """ # validate input parameters if not isinstance(n, int) or n <= 0: raise ValueError('n must be a positive integer') if alpha > 1 or alpha <= 0 or beta > 1 or beta <= 0: raise ValueError('alpha and beta must be float values in (0,1]') if L <= 0: raise ValueError('L must be a positive number') if seed is not None: random.seed(seed) G = Topology(type='waxman_1', distance_unit=distance_unit) G.name = "waxman_1_topology(%s, %s, %s, %s)" % (n, alpha, beta, L) G.add_nodes_from(range(n)) nodes = list(G.nodes()) while nodes: u = nodes.pop() for v in nodes: d = L * random.random() if random.random() < alpha * math.exp(-d / (beta * L)): G.add_edge(u, v, length=d) return G
def extended_barabasi_albert_topology(n, m, m0, p, q, seed=None): r""" Return a random topology using the extended Barabasi-Albert preferential attachment model. Differently from the original Barabasi-Albert model, this model takes into account the presence of local events, such as the addition of new links or the rewiring of existing links. More precisely, the Barabasi-Albert topology is built as follows. First, a topology with *m0* isolated nodes is created. Then, at each step: with probability *p* add *m* new links between existing nodes, selected with probability: .. math:: \Pi(i) = \frac{deg(i) + 1}{\sum_{v \in V} (deg(v) + 1)} with probability *q* rewire *m* links. Each link to be rewired is selected as follows: a node i is randomly selected and a link is randomly removed from it. The node i is then connected to a new node randomly selected with probability :math:`\Pi(i)`, with probability :math:`1-p-q` add a new node and attach it to m nodes of the existing topology selected with probability :math:`\Pi(i)` Repeat the previous step until the topology comprises n nodes in total. Parameters ---------- n : int Number of nodes m : int Number of edges to attach from a new node to existing nodes m0 : int Number of edges initially attached to the network p : float The probability that new links are added q : float The probability that existing links are rewired seed : int, optional Seed for random number generator (default=None). Returns ------- G : Topology References ---------- .. [1] A. L. Barabasi and R. Albert "Topology of evolving networks: local events and universality", Physical Review Letters 85(24), 2000. """ def calc_pi(G): """Calculate extended-BA Pi function for all nodes of the graph""" degree = dict(G.degree()) den = float(sum(degree.values()) + G.number_of_nodes()) return {node: (degree[node] + 1) / den for node in G.nodes()} # input parameters if n < 1 or m < 1 or m0 < 1: raise ValueError('n, m and m0 must be a positive integer') if m >= m0: raise ValueError('m must be <= m0') if n < m0: raise ValueError('n must be > m0') if p > 1 or p < 0: raise ValueError('p must be included between 0 and 1') if q > 1 or q < 0: raise ValueError('q must be included between 0 and 1') if p + q > 1: raise ValueError('p + q must be <= 1') if seed is not None: random.seed(seed) G = Topology(type='extended_ba') G.name = "ext_ba_topology(%d, %d, %d, %f, %f)" % (n, m, m0, p, q) # Step 1: Add m0 isolated nodes G.add_nodes_from(range(m0)) while G.number_of_nodes() < n: pi = calc_pi(G) r = random.random() if r <= p: # add m new links with probability p n_nodes = G.number_of_nodes() n_edges = G.number_of_edges() max_n_edges = (n_nodes * (n_nodes - 1)) / 2 if n_edges + m > max_n_edges: # cannot add m links continue # rewire or add nodes new_links = 0 while new_links < m: u = random_from_pdf(pi) v = random_from_pdf(pi) if u is not v and not G.has_edge(u, v): G.add_edge(u, v) new_links += 1 elif r > p and r <= p + q: # rewire m links with probability q rewired_links = 0 while rewired_links < m: i = random.choice(list(G.nodes())) # pick up node randomly (uniform) if len(G.adj[i]) is 0: # if i has no edges, I cannot rewire break j = random.choice(list(G.adj[i].keys())) # node to be disconnected k = random_from_pdf(pi) # new node to be connected if i is not k and j is not k and not G.has_edge(i, k): G.remove_edge(i, j) G.add_edge(i, k) rewired_links += 1 else: # add a new node with probability 1 - p - q new_node = G.number_of_nodes() G.add_node(new_node) new_links = 0 while new_links < m: existing_node = random_from_pdf(pi) if not G.has_edge(new_node, existing_node): G.add_edge(new_node, existing_node) new_links += 1 return G
def waxman_2_topology(n, alpha=0.4, beta=0.1, domain=(0, 0, 1, 1), distance_unit='Km', seed=None): r"""Return a Waxman-2 random topology. The Waxman-2 random topology models place n nodes uniformly at random in a rectangular domain. Two nodes u, v are connected with a link with probability .. math:: p = \alpha*exp(-d/(\beta*L)). where the distance *d* is the Euclidean distance between the nodes u and v. and *L* is the maximum distance between all nodes in the graph. Parameters ---------- n : int Number of nodes alpha : float Model parameter chosen in *(0,1]* (higher alpha increases link density) beta : float Model parameter chosen in *(0,1]* (higher beta increases difference between density of short and long links) domain : tuple of numbers, optional Domain size (xmin, ymin, xmax, ymax) seed : int, optional Seed for random number generator (default=None). Returns ------- G : Topology Notes ----- Each edge of G has the attribute *length* References ---------- .. [1] B. M. Waxman, Routing of multipoint connections. IEEE J. Select. Areas Commun. 6(9),(1988) 1617-1622. """ # validate input parameters if not isinstance(n, int) or n <= 0: raise ValueError('n must be a positive integer') if alpha > 1 or alpha <= 0 or beta > 1 or beta <= 0: raise ValueError('alpha and beta must be float values in (0,1]') if not isinstance(domain, tuple) or len(domain) != 4: raise ValueError('domain must be a tuple of 4 number') (xmin, ymin, xmax, ymax) = domain if xmin > xmax: raise ValueError('In domain, xmin cannot be greater than xmax') if ymin > ymax: raise ValueError('In domain, ymin cannot be greater than ymax') if seed is not None: random.seed(seed) G = Topology(type='waxman_2', distance_unit=distance_unit) G.name = "waxman_2_topology(%s, %s, %s)" % (n, alpha, beta) G.add_nodes_from(range(n)) for v in G.nodes(): G.node[v]['latitude'] = (ymin + (ymax - ymin)) * random.random() G.node[v]['longitude'] = (xmin + (xmax - xmin)) * random.random() l = {} nodes = list(G.nodes()) while nodes: u = nodes.pop() for v in nodes: x_u = G.node[u]['longitude'] x_v = G.node[v]['longitude'] y_u = G.node[u]['latitude'] y_v = G.node[v]['latitude'] l[(u, v)] = math.sqrt((x_u - x_v) ** 2 + (y_u - y_v) ** 2) L = max(l.values()) for (u, v), d in l.items(): if random.random() < alpha * math.exp(-d / (beta * L)): G.add_edge(u, v, length=d) return G