def weighted_content_placement(topology, contents, source_weights, seed=None): """Places content objects to source nodes randomly according to the weight of the source node. Parameters ---------- topology : Topology The topology object contents : iterable Iterable of content objects source_weights : dict Dict mapping nodes nodes of the topology which are content sources and the weight according to which content placement decision is made. Returns ------- cache_placement : dict Dictionary mapping content objects to source nodes Notes ----- A deterministic placement of objects (e.g., for reproducing results) can be achieved by using a fix seed value """ random.seed(seed) norm_factor = float(sum(source_weights.values())) source_pdf = dict((k, v / norm_factor) for k, v in source_weights.items()) content_placement = collections.defaultdict(set) for c in contents: content_placement[random_from_pdf(source_pdf)].add(c) apply_content_placement(content_placement, topology)
def set_capacities_random(topology, capacity_pdf, capacity_unit='Mbps'): """ Set random link capacities according to a given probability density function Parameters ---------- topology : Topology The topology to which link capacities will be set capacity_pdf : dict A dictionary representing the probability that a capacity value is assigned to a link capacity_unit : str, optional The unit in which capacity value is expressed (e.g. Mbps, Gbps etc..) links : list, optional List of links, represented as (u, v) tuples to which capacity will be set. If None or not specified, the capacity will be applied to all links. Examples -------- >>> import fnss >>> topology = fnss.erdos_renyi_topology(50, 0.1) >>> pdf = {10: 0.5, 100: 0.2, 1000: 0.3} >>> fnss.set_capacities_constant(topology, pdf, 'Mbps') """ if not capacity_unit in capacity_units: raise ValueError("The capacity_unit argument is not valid") if any((capacity < 0 for capacity in capacity_pdf.keys())): raise ValueError('All capacities in capacity_pdf must be positive') topology.graph['capacity_unit'] = capacity_unit for u, v in topology.edges(): topology.adj[u][v]['capacity'] = random_from_pdf(capacity_pdf) return
def add_m_links(G, pi): """Add m links between existing nodes to the graph""" 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 add_node(G, pi) # add a new node instead # return in any case because before doing another operation # (add node or links) we need to recalculate pi return new_links = 0 while new_links < m: u = random_from_pdf(pi) v = random_from_pdf(pi) if u != v and not G.has_edge(u, v): G.add_edge(u, v) new_links += 1
def add_node(G, pi): """Add one node to the graph and connect it to m existing nodes""" 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
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 barabasi_albert_topology(n, m, m0, seed=None): r""" Return a random topology using Barabasi-Albert preferential attachment model. A topology of n nodes is grown by attaching new nodes each with m links that are preferentially attached to existing nodes with high degree. More precisely, the Barabasi-Albert topology is built as follows. First, a line topology with m0 nodes is created. Then at each step, one node is added and connected to m existing nodes. These nodes are selected randomly with probability .. math:: \Pi(i) = \frac{deg(i)}{sum_{v \in V} deg V}. Where i is the selected node and V is the set of nodes of the graph. Parameters ---------- n : int Number of nodes m : int Number of edges to attach from a new node to existing nodes m0 : int Number of nodes initially attached to the network seed : int, optional Seed for random number generator (default=None). Returns ------- G : Topology Notes ----- The initialization is a graph with with m nodes connected by :math:`m -1` edges. It does not use the Barabasi-Albert method provided by NetworkX because it does not allow to specify *m0* parameter. There are no disconnected subgraphs in the topology. References ---------- .. [1] A. L. Barabasi and R. Albert "Emergence of scaling in random networks", Science 286, pp 509-512, 1999. """ def calc_pi(G): """Calculate BA Pi function for all nodes of the graph""" degree = G.degree() den = float(sum(degree.values())) return {node: degree[node] / 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 positive integers') if m >= m0: raise ValueError('m must be <= m0') if n < m0: raise ValueError('n must be > m0') if seed is not None: random.seed(seed) # Step 1: Add m0 nodes. These nodes are interconnected together # because otherwise they will end up isolated at the end G = Topology(nx.path_graph(m0)) G.name = "ba_topology(%d,%d,%d)" % (n, m, m0) G.graph['type'] = 'ba' # Step 2: Add one node and connect it with m links while G.number_of_nodes() < n: pi = calc_pi(G) u = G.number_of_nodes() G.add_node(u) new_links = 0 while new_links < m: v = random_from_pdf(pi) if not G.has_edge(u, v): G.add_edge(u, v) new_links += 1 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 barabasi_albert_topology(n, m, m0, seed=None): r""" Return a random topology using Barabasi-Albert preferential attachment model. A topology of n nodes is grown by attaching new nodes each with m links that are preferentially attached to existing nodes with high degree. More precisely, the Barabasi-Albert topology is built as follows. First, a line topology with m0 nodes is created. Then at each step, one node is added and connected to m existing nodes. These nodes are selected randomly with probability .. math:: \Pi(i) = \frac{deg(i)}{sum_{v \in V} deg V}. Where i is the selected node and V is the set of nodes of the graph. Parameters ---------- n : int Number of nodes m : int Number of edges to attach from a new node to existing nodes m0 : int Number of nodes initially attached to the network seed : int, optional Seed for random number generator (default=None). Returns ------- G : Topology Notes ----- The initialization is a graph with with m nodes connected by :math:`m -1` edges. It does not use the Barabasi-Albert method provided by NetworkX because it does not allow to specify *m0* parameter. There are no disconnected subgraphs in the topology. References ---------- .. [1] A. L. Barabasi and R. Albert "Emergence of scaling in random networks", Science 286, pp 509-512, 1999. """ def calc_pi(G): """Calculate BA Pi function for all nodes of the graph""" degree = dict(G.degree()) den = float(sum(degree.values())) return {node: degree[node] / 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 positive integers') if m >= m0: raise ValueError('m must be <= m0') if n < m0: raise ValueError('n must be > m0') if seed is not None: random.seed(seed) # Step 1: Add m0 nodes. These nodes are interconnected together # because otherwise they will end up isolated at the end G = Topology(nx.path_graph(m0)) G.name = "ba_topology(%d,%d,%d)" % (n, m, m0) G.graph['type'] = 'ba' # Step 2: Add one node and connect it with m links while G.number_of_nodes() < n: pi = calc_pi(G) u = G.number_of_nodes() G.add_node(u) new_links = 0 while new_links < m: v = random_from_pdf(pi) if not G.has_edge(u, v): G.add_edge(u, v) new_links += 1 return G
def weighted_repo_content_placement(topology, contents, freshness_per, shelf_life, msg_size, topics_weights, types_weights, max_replications, source_weights, service_weights, max_label_nos, seed=None): """Places content objects to source nodes randomly according to the weight of the source node. TODO: This should be modified, or another one created, to include content placement parameters, like the freshness periods, shelf-lives, topics/types of labels and placement possibilities, maybe depending on hashes, placement of nodes and possibly other scenario-specific/service-specific parameters. ADD SERVICE TYPE TO MESSAGE PROPERTIES! Parameters ---------- topology : Topology The topology object contents : iterable Iterable of content objects topics : types : freshness_per : shelf_life : msg_size : source_weights : dict Dict mapping nodes of the topology which are content sources and the weight according to which content placement decision is made. Returns ------- cache_placement : dict Dictionary mapping content objects to source nodes Notes ----- A deterministic placement of objects (e.g., for reproducing results) can be achieved by using a fix seed value """ # TODO: This is the format that each datum (message) shuold have # placed_data = {content, msg_topics, msg_type, freshness_per, # shelf_life, msg_size} placed_data = dict() random.seed(seed) norm_factor = float(sum(source_weights.values())) # TODO: These ^\/^\/^ might need redefining, to make label-specific # source weights, and then the labels distributed according to these. # OR the other way around, distributing sources according to label weights if types_weights is not None: types_labels_norm_factor = float(sum(types_weights.values())) types_labels_pdf = dict((k, v / types_labels_norm_factor) for k, v in types_weights.items()) topics_labels_norm_factor = float(sum(topics_weights.values())) service_labels_norm_factor = float(sum(service_weights.values())) # TODO: Think about a way to randomise, but still maintain a certain # distribution among the users that receive data with certain labels. # Maybe associate the pdf with labels, rather than contents, SOMEHOW! source_pdf = dict((k, v / norm_factor) for k, v in source_weights.items()) topics_labels_pdf = dict( (k, v / topics_labels_norm_factor) for k, v in topics_weights.items()) service_labels_pdf = dict((k, v / service_labels_norm_factor) for k, v in service_weights.items()) service_association = collections.defaultdict(set) labels_association = collections.defaultdict(set) content_placement = collections.defaultdict(set) # Further TODO: Add all the other data characteristics and maybe place # content depending on those at a later point (create other # placement strategies) # NOTE: All label names will come as a list of strings for c in contents: alter = False if freshness_per is not None: if placed_data.has_key(contents[c]['content']): placed_data[contents[c]['content']].update( freshness_per=freshness_per) else: placed_data[contents[c]['content']] = dict() placed_data[contents[c] ['content']]['freshness_per'] = freshness_per if shelf_life is not None: placed_data[contents[c]['content']].update(shelf_life=shelf_life) if max_replications: placed_data[contents[c]['content']].update( max_replications=max_replications) placed_data[contents[c]['content']].update(replications=0) service_association[random_from_pdf(service_labels_pdf)].add(c) placed_data[contents[c]['content']].update(content=c) placed_data[contents[c]['content']].update(msg_size=msg_size) placed_data[contents[c]['content']]["receiveTime"] = 0 placed_data[contents[c]['content']]['labels'] = contents[c]['labels'] placed_data[contents[c]['content']]['service_type'] = "non-proc" if not placed_data[contents[c]['content']]['labels']: for i in range(0, max_label_nos): if types_weights is not None and not alter: labels_association[random_from_pdf(types_labels_pdf)].add( c) alter = True elif topics_weights is not None and alter: labels_association[random_from_pdf(topics_labels_pdf)].add( c) alter = False elif topics_weights is not None: labels_association[random_from_pdf(topics_labels_pdf)].add( c) placed_data = apply_labels_association(labels_association, placed_data) #placed_data = apply_service_association(service_association, placed_data) for d in placed_data: rand = random_from_pdf(source_pdf) if not content_placement[rand]: content_placement[rand] = dict() if content_placement[rand].has_key(d): content_placement[rand][d].update(placed_data[d]) else: content_placement[rand][d] = dict() content_placement[rand][d] = placed_data[d] apply_content_placement(content_placement, topology) topology.placed_data = placed_data