Exemple #1
0
def star_topology(n):
    """
    Return a star (a.k.a hub-and-spoke) topology of :math:`n+1` nodes

    The root (hub) node has id 0 while all leaf (spoke) nodes have id
    :math:`(1, n+1)`.

    Each node has the attribute type which can either be *root* (for node 0) or
    *leaf* for all other nodes

    Parameters
    ----------
    n : int
        The number of leaf nodes

    Returns
    -------
    topology : A Topology object
    """
    if not isinstance(n, int):
        raise TypeError('n argument must be of int type')
    if n < 1:
        raise ValueError('n argument must be a positive integer')
    G = Topology(nx.star_graph(n))
    G.name = "star_topology(%d)" % (n)
    G.graph['type'] = 'star'
    G.node[0]['type'] = 'root'
    for v in range(1, n + 1):
        G.node[v]['type'] = 'leaf'
    return G
Exemple #2
0
def parse_ashiip(path):
    """
    Parse a topology from an output file generated by the aShiip topology 
    generator
    
    Parameters
    ----------
    path : str
        The path to the aShiip output file
    
    Returns
    -------
    topology : Topology
    """
    topology = Topology(type='ashiip')
    
    for line in open(path, "r").readlines():
        # There is no documented aShiip format but we assume that if the line
        # does not start with a number it is not part of the topology
        if line[0].isdigit():
            node_ids = re.findall("\d+", line)
            if len(node_ids) < 3:
                raise ValueError('Invalid input file. Parsing failed while '\
                                 'trying to parse a line')
            node = int(node_ids[0])
            level = int(node_ids[1])
            topology.add_node(node, level=level)
            for i in range(2, len(node_ids)):
                topology.add_edge(node, int(node_ids[i]))
    return topology
Exemple #3
0
def from_mininet(topology):
    """Convert a Mininet topology to an FNSS one.
    
    Parameters
    ----------
    topology : Mininet Topo
        A Mininet topology object
    
    Returns
    -------
    topology : Topology
        An FNSS Topology object
    """
    fnss_topo = Topology(capacity_unit='Mbps')
    for v in topology.switches():
        fnss_topo.add_node(v, type='switch')
    for v in topology.hosts():
        fnss_topo.add_node(v, type='host')
    for u, v in topology.links():
        fnss_topo.add_edge(u, v)
        opts = topology.linkInfo(u, v)
        if 'bw' in opts:
            fnss_topo.edge[u][v]['capacity'] = opts['bw']
        if 'delay' in opts:
            delay = opts['delay']
            val = re.findall("\d+\.?\d*", delay)[0]
            unit = delay.strip(val).strip(' ')
            set_delays_constant(fnss_topo, val, unit, [(u,v)])
    return fnss_topo
Exemple #4
0
def k_ary_tree_topology(k, h):
    """
    Return a balanced k-ary tree topology of with depth h
    
    Each node has two attributes:
     * type: which can either be *root*, *intermediate* or *leaf*
     * depth:math:`(0, h)` the height of the node in the tree, where 0 is the
       root and h are leaves.
    
    Parameters
    ----------
    k : int
        The branching factor of the tree
    h : int 
        The height or depth of the tree

    Returns
    -------
    topology : A Topology object
    """
    if not isinstance(k, int) or not isinstance(h, int):
        raise TypeError('k and h arguments must be of int type')
    if k <= 1:
        raise ValueError("Invalid k parameter. It should be > 1")
    if h < 1:
        raise ValueError("Invalid h parameter. It should be >=1")
    G = Topology(nx.balanced_tree(k, h))
    G.name = "k_ary_tree_topology(%d,%d)" % (k, h)
    G.graph['type'] = 'tree'
    G.graph['k'] = k
    G.graph['h'] = h
    G.node[0]['type'] = 'root'
    G.node[0]['depth'] = 0
    # Iterate through the tree to assign labels to nodes
    v = 1
    for depth in range(1, h + 1):
        for _ in range(k**depth):
            G.node[v]['depth'] = depth
            if depth == h:
                G.node[v]['type'] = 'leaf'
            else:
                G.node[v]['type'] = 'intermediate'
            v += 1                   
    return G
Exemple #5
0
def k_ary_tree_topology(k, h):
    """
    Return a balanced k-ary tree topology of with depth h

    Each node has two attributes:
     * type: which can either be *root*, *intermediate* or *leaf*
     * depth:math:`(0, h)` the height of the node in the tree, where 0 is the
       root and h are leaves.

    Parameters
    ----------
    k : int
        The branching factor of the tree
    h : int
        The height or depth of the tree

    Returns
    -------
    topology : A Topology object
    """
    if not isinstance(k, int) or not isinstance(h, int):
        raise TypeError('k and h arguments must be of int type')
    if k <= 1:
        raise ValueError("Invalid k parameter. It should be > 1")
    if h < 1:
        raise ValueError("Invalid h parameter. It should be >=1")
    G = Topology(nx.balanced_tree(k, h))
    G.name = "k_ary_tree_topology(%d,%d)" % (k, h)
    G.graph['type'] = 'tree'
    G.graph['k'] = k
    G.graph['h'] = h
    G.node[0]['type'] = 'root'
    G.node[0]['depth'] = 0
    # Iterate through the tree to assign labels to nodes
    v = 1
    for depth in range(1, h + 1):
        for _ in range(k**depth):
            G.node[v]['depth'] = depth
            G.node[v]['type'] = 'leaf' if (depth == h) else 'intermediate'
            v += 1
    return G
Exemple #6
0
def full_mesh_topology(n):
    """
    Return a fully connected mesh topology of n nodes

    Parameters
    ----------
    n : int
        The number of nodes

    Returns
    -------
    topology : A Topology object
    """
    if not isinstance(n, int):
        raise TypeError('n argument must be of int type')
    if n < 1:
        raise ValueError('n argument must be a positive integer')
    G = Topology(nx.complete_graph(n))
    G.name = "full_mesh_topology(%d)" % (n)
    G.graph['type'] = 'full_mesh'
    return G
Exemple #7
0
def full_mesh_topology(n):
    """
    Return a fully connected mesh topology of n nodes

    Parameters
    ----------
    n : int
        The number of nodes

    Returns
    -------
    topology : A Topology object
    """
    if not isinstance(n, int):
        raise TypeError('n argument must be of int type')
    if n < 1:
        raise ValueError('n argument must be a positive integer')
    G = Topology(nx.complete_graph(n))
    G.name = "full_mesh_topology(%d)" % (n)
    G.graph['type'] = 'full_mesh'
    return G
Exemple #8
0
def star_topology(n):
    """
    Return a star (a.k.a hub-and-spoke) topology of :math:`n+1` nodes

    The root (hub) node has id 0 while all leaf (spoke) nodes have id
    :math:`(1, n+1)`.

    Each node has the attribute type which can either be *root* (for node 0) or
    *leaf* for all other nodes

    Parameters
    ----------
    n : int
        The number of leaf nodes

    Returns
    -------
    topology : A Topology object
    """
    if not isinstance(n, int):
        raise TypeError('n argument must be of int type')
    if n < 1:
        raise ValueError('n argument must be a positive integer')
    G = Topology(nx.star_graph(n))
    G.name = "star_topology(%d)" % (n)
    G.graph['type'] = 'star'
    G.node[0]['type'] = 'root'
    for v in range(1, n + 1):
        G.node[v]['type'] = 'leaf'
    return G
Exemple #9
0
def parse_ashiip(path):
    """
    Parse a topology from an output file generated by the aShiip topology
    generator

    Parameters
    ----------
    path : str
        The path to the aShiip output file

    Returns
    -------
    topology : Topology
    """
    topology = Topology(type='ashiip')

    for line in open(path, "r").readlines():
        # There is no documented aShiip format but we assume that if the line
        # does not start with a number it is not part of the topology
        if line[0].isdigit():
            node_ids = re.findall("\d+", line)
            if len(node_ids) < 3:
                raise ValueError('Invalid input file. Parsing failed while '\
                                 'trying to parse a line')
            node = int(node_ids[0])
            level = int(node_ids[1])
            topology.add_node(node, level=level)
            for i in range(2, len(node_ids)):
                topology.add_edge(node, int(node_ids[i]))
    return topology
Exemple #10
0
def line_topology(n):
    """
    Return a line topology of n nodes

    Parameters
    ----------
    n : int
        The number of nodes

    Returns
    -------
    topology : A Topology object

    """
    if not isinstance(n, int):
        raise TypeError('n argument must be of int type')
    if n < 1:
        raise ValueError('n argument must be a positive integer')
    G = Topology(nx.path_graph(n))
    G.name = "line_topology(%d)" % (n)
    G.graph['type'] = 'line'
    return G
Exemple #11
0
def line_topology(n):
    """
    Return a line topology of n nodes

    Parameters
    ----------
    n : int
        The number of nodes

    Returns
    -------
    topology : A Topology object

    """
    if not isinstance(n, int):
        raise TypeError('n argument must be of int type')
    if n < 1:
        raise ValueError('n argument must be a positive integer')
    G = Topology(nx.path_graph(n))
    G.name = "line_topology(%d)" % (n)
    G.graph['type'] = 'line'
    return G
Exemple #12
0
def erdos_renyi_topology(n, p, seed=None, fast=False):
    r"""Return a random graph :math:`G_{n,p}` (Erdos-Renyi graph, binomial
    graph).

    Chooses each of the possible edges with probability p.

    Parameters
    ----------
    n : int
        The number of nodes.
    p : float
        Probability for edge creation.
    seed : int, optional
        Seed for random number generator (default=None).
    fast : boolean, optional
        Uses the algorithm proposed by [3]_, which is faster for small p

    References
    ----------
    .. [1] P. Erdos and A. Renyi, On Random Graphs, Publ. Math. 6, 290 (1959).
    .. [2] E. N. Gilbert, Random Graphs, Ann. Math. Stat., 30, 1141 (1959).
    .. [3] Vladimir Batagelj and Ulrik Brandes,
       "Efficient generation of large random networks",
       Phys. Rev. E, 71, 036113, 2005.
    """
    # validate input parameters
    if not isinstance(n, int) or n < 0:
        raise ValueError('n must be a positive integer')
    if p > 1 or p < 0:
        raise ValueError('p must be a value in (0,1)')
    if fast:
        G = Topology(nx.fast_gnp_random_graph(n, p, seed=seed))
    else:
        G = Topology(nx.gnp_random_graph(n, p, seed=seed))
    G.name = "erdos_renyi_topology(%s, %s)" % (n, p)
    G.graph['type'] = 'er'
    return G
Exemple #13
0
def erdos_renyi_topology(n, p, seed=None, fast=False):
    r"""Return a random graph :math:`G_{n,p}` (Erdos-Renyi graph, binomial
    graph).

    Chooses each of the possible edges with probability p.

    Parameters
    ----------
    n : int
        The number of nodes.
    p : float
        Probability for edge creation.
    seed : int, optional
        Seed for random number generator (default=None).
    fast : boolean, optional
        Uses the algorithm proposed by [3]_, which is faster for small p

    References
    ----------
    .. [1] P. Erdos and A. Renyi, On Random Graphs, Publ. Math. 6, 290 (1959).
    .. [2] E. N. Gilbert, Random Graphs, Ann. Math. Stat., 30, 1141 (1959).
    .. [3] Vladimir Batagelj and Ulrik Brandes,
       "Efficient generation of large random networks",
       Phys. Rev. E, 71, 036113, 2005.
    """
    # validate input parameters
    if not isinstance(n, int) or n < 0:
        raise ValueError('n must be a positive integer')
    if p > 1 or p < 0:
        raise ValueError('p must be a value in (0,1)')
    if fast:
        G = Topology(nx.fast_gnp_random_graph(n, p, seed=seed))
    else:
        G = Topology(nx.gnp_random_graph(n, p, seed=seed))
    G.name = "erdos_renyi_topology(%s, %s)" % (n, p)
    G.graph['type'] = 'er'
    return G
Exemple #14
0
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
Exemple #15
0
def dumbbell_topology(m1, m2):
    """
    Return a dumbbell topology consisting of two star topologies
    connected by a path.

    More precisely, two star graphs :math:`K_{m1}` form the left and right
    bells, and are connected by a path :math:`P_{m2}`.

    The :math:`2*m1+m2`  nodes are numbered as follows.
     * :math:`0,...,m1-1` for the left barbell,
     * :math:`m1,...,m1+m2-1` for the path,
     * :math:`m1+m2,...,2*m1+m2-1` for the right barbell.

    The 3 subgraphs are joined via the edges :math:`(m1-1,m1)` and
    :math:`(m1+m2-1,m1+m2)`. If m2 = 0, this is merely two star topologies
    joined together.

    Please notice that this dumbbell topology is different from the barbell
    graph generated by networkx's barbell_graph function. That barbell graph
    consists of two complete graphs connected by a path. This consists of two
    stars whose roots are connected by a path. This dumbbell topology is
    particularly useful for simulating transport layer protocols.

    All nodes and edges of this topology have an attribute *type* which can be
    either *right bell*, *core* or *left_bell*

    Parameters
    ----------
    m1 : int
        The number of nodes in each bell
    m2 : int
        The number of nodes in the path

    Returns
    -------
    topology : A Topology object
    """
    if not isinstance(m1, int) or not isinstance(m2, int):
        raise TypeError('m1 and m2 arguments must be of int type')
    if m1 < 2:
        raise ValueError("Invalid graph description, m1 should be >= 2")
    if m2 < 1:
        raise ValueError("Invalid graph description, m2 should be >= 1")

    G = Topology(type='dumbbell')
    G.name = "dumbbell_topology(%d,%d)" % (m1, m2)

    # left bell
    G.add_node(m1)
    for v in range(m1):
        G.add_node(v, type='left_bell')
        G.add_edge(v, m1, type='left_bell')

    # right bell
    for v in range(m1):
        G.add_node(v + m1 + m2, type='right_bell')
        G.add_edge(v + m1 + m2, m1 + m2 - 1, type='right_bell')

    # connecting path
    for v in range(m1, m1 + m2 - 1):
        G.node[v]['type'] = 'core'
        G.add_edge(v, v + 1, type='core')
    G.node[m1 + m2 - 1]['type'] = 'core'

    return G
Exemple #16
0
def dumbbell_topology(m1, m2):
    """
    Return a dumbbell topology consisting of two star topologies
    connected by a path.

    More precisely, two star graphs :math:`K_{m1}` form the left and right
    bells, and are connected by a path :math:`P_{m2}`.

    The :math:`2*m1+m2`  nodes are numbered as follows.
     * :math:`0,...,m1-1` for the left barbell,
     * :math:`m1,...,m1+m2-1` for the path,
     * :math:`m1+m2,...,2*m1+m2-1` for the right barbell.

    The 3 subgraphs are joined via the edges :math:`(m1-1,m1)` and
    :math:`(m1+m2-1,m1+m2)`. If m2 = 0, this is merely two star topologies
    joined together.

    Please notice that this dumbbell topology is different from the barbell
    graph generated by networkx's barbell_graph function. That barbell graph
    consists of two complete graphs connected by a path. This consists of two
    stars whose roots are connected by a path. This dumbbell topology is
    particularly useful for simulating transport layer protocols.

    All nodes and edges of this topology have an attribute *type* which can be
    either *right bell*, *core* or *left_bell*

    Parameters
    ----------
    m1 : int
        The number of nodes in each bell
    m2 : int
        The number of nodes in the path

    Returns
    -------
    topology : A Topology object
    """
    if not isinstance(m1, int) or not isinstance(m2, int):
        raise TypeError('m1 and m2 arguments must be of int type')
    if m1 < 2:
        raise ValueError("Invalid graph description, m1 should be >= 2")
    if m2 < 1:
        raise ValueError("Invalid graph description, m2 should be >= 1")

    G = Topology(type='dumbbell')
    G.name = "dumbbell_topology(%d,%d)" % (m1, m2)

    # left bell
    G.add_node(m1)
    for v in range(m1):
        G.add_node(v, type='left_bell')
        G.add_edge(v, m1, type='left_bell')

    # right bell
    for v in range(m1):
        G.add_node(v + m1 + m2, type='right_bell')
        G.add_edge(v + m1 + m2, m1 + m2 - 1, type='right_bell')

    # connecting path
    for v in range(m1, m1 + m2 - 1):
        G.node[v]['type'] = 'core'
        G.add_edge(v, v + 1, type='core')
    G.node[m1 + m2 - 1]['type'] = 'core'

    return G
Exemple #17
0
def parse_brite(path,
                capacity_unit='Mbps',
                delay_unit='ms',
                distance_unit='Km',
                directed=True):
    """
    Parse a topology from an output file generated by the BRITE topology
    generator

    Parameters
    ----------
    path : str
        The path to the BRITE output file
    capacity_unit : str, optional
        The unit in which link capacity values are expresses in the BRITE file
    delay_unit : str, optional
        The unit in which link delay values are expresses in the BRITE file
    distance_unit : str, optional
        The unit in which node coordinates are expresses in the BRITE file
    directed : bool, optional
        If True, the topology is parsed as directed topology.

    Returns
    -------
    topology : Topology or DirectedTopology

    Notes
    -----
    Each node of the returned topology object is labeled with *latitude* and
    *longitude* attributes. These attributes are not expressed in degrees but
    in *distance_unit*.
    """
    # BRITE output format:
    # http://www.cs.bu.edu/brite/user_manual/node29.html
    topology = DirectedTopology() if directed else Topology()
    topology.graph = {
        'type': 'brite',
        'capacity_unit': capacity_unit,
        'delay_unit': delay_unit,
        'distance_unit': distance_unit
    }
    line_type = None
    for line in open(path, "r").readlines():
        if line.startswith('Nodes:'):
            line_type = 'node'
        elif line.startswith('Edges:'):
            line_type = 'edge'
        elif line[0].isdigit():
            elements = line.strip().split("\t")
            if line_type == 'node':
                # Parse node
                try:
                    node_id = int(elements[0])
                    longitude = float(elements[1])
                    latitude = float(elements[2])
                    # indegree = int(elements[3])
                    # outdegree = int(elements[4])
                    as_id = int(elements[5])
                    # Node type can be:
                    # AS-only: AS_NODE
                    # Router-only: RT_NODE
                    # Top-down: RT_NODE, RT_BORDER
                    # Bottom-up: RT_NODE
                    node_type = elements[6]
                except (ValueError, IndexError):
                    raise ValueError('Invalid input file. Parsing failed '\
                                     'while trying to parse a node')
                topology.add_node(node_id,
                                  latitude=latitude,
                                  longitude=longitude,
                                  type=node_type)
                if as_id > 0:
                    topology.node[node_id]['AS'] = as_id
            elif line_type == 'edge':
                # Parse link
                try:
                    edge_id = int(elements[0])
                    from_node = int(elements[1])
                    to_node = int(elements[2])
                    length = float(elements[3])
                    delay = float(elements[4])
                    capacity = float(elements[5])
                    # from_as = elements[6]
                    # to_as = elements[7]
                    # Link type can be:
                    # AS-only: E_AS
                    # Router-only: E_RT
                    # Top-down: E_AS, E_RT
                    # bottom-up: E_RT
                    link_type = elements[8]
                except (ValueError, IndexError):
                    raise ValueError('Invalid input file. Parsing failed '\
                                     'while trying to parse a link')
                topology.add_edge(from_node,
                                  to_node,
                                  id=edge_id,
                                  length=length,
                                  delay=delay,
                                  capacity=capacity,
                                  type=link_type)
            else:
                continue
    return topology
Exemple #18
0
def parse_topology_zoo(path):
    """
    Parse a topology from the Topology Zoo dataset.

    Parameters
    ----------
    path : str
        The path to the Topology Zoo file

    Returns
    -------
    topology : Topology or DirectedTopology
        The parsed topology.

    Notes
    -----
    If the parsed topology contains bundled links, i.e. multiple links between
    the same pair or nodes, the topology is parsed correctly but each bundle of
    links is represented as a single link whose capacity is the sum of the
    capacities of the links of the bundle (if capacity values were provided).
    The returned topology has a boolean attribute named *link_bundling* which
    is True if the topology contains at list one bundled link or False
    otherwise. If the topology contains bundled links, then each link has an
    additional boolean attribute named *bundle* which is True if that specific
    link was bundled in the original topology or False otherwise.
    """
    def try_convert_int(value):
        """
        Try to convert a string to an int. If not possible, returns the given
        value unchanged
        """
        if type(value) != int:
            try:
                value = int(value)
            except ValueError:
                pass
        return value

    if path.endswith('.gml'):
        topo_zoo_graph = nx.read_gml(path)
    elif path.endswith('.graphml'):
        topo_zoo_graph = nx.read_graphml(path)
    else:
        raise ValueError('Invalid input file format. It must either be a GML '\
                         'or GraphML file (with extensions .gml or .graphml)')
    topology = DirectedTopology() if topo_zoo_graph.is_directed() \
               else Topology()
    topology.graph['type'] = 'topology_zoo'
    topology.graph['distance_unit'] = 'Km'
    topology.graph['link_bundling'] = True if topo_zoo_graph.is_multigraph() \
                                      else False
    for tv in topo_zoo_graph.nodes_iter():
        v = try_convert_int(tv)
        topology.add_node(v)
        if 'label' in topo_zoo_graph.node[tv]:
            topology.node[v]['label'] = topo_zoo_graph.node[tv]['label']
        try:
            longitude = topo_zoo_graph.node[tv]['Longitude']
            latitude = topo_zoo_graph.node[tv]['Latitude']
            topology.node[v]['longitude'] = longitude
            topology.node[v]['latitude'] = latitude
        except KeyError:
            pass
    for tv, tu in topo_zoo_graph.edges_iter():
        v = try_convert_int(tv)
        u = try_convert_int(tu)
        if u == v:
            continue
        topology.add_edge(v, u)
        if 'Latitude' in topo_zoo_graph.node[tv] and \
                'Longitude' in topo_zoo_graph.node[tv] and \
                'Latitude' in topo_zoo_graph.node[tu] and \
                'Longitude' in topo_zoo_graph.node[tu]:
            lat_v = topo_zoo_graph.node[tv]['Latitude']
            lon_v = topo_zoo_graph.node[tv]['Longitude']
            lat_u = topo_zoo_graph.node[tu]['Latitude']
            lon_u = topo_zoo_graph.node[tu]['Longitude']
            length = geographical_distance(lat_v, lon_v, lat_u, lon_u)
            topology.edge[v][u]['length'] = length
        if topo_zoo_graph.is_multigraph():
            edge = topo_zoo_graph.edge[tv][tu]
            topology.edge[v][u]['bundle'] = True if len(edge) > 1 else False
            capacity = 0
            for edge_attr in list(edge.values()):
                if 'LinkSpeedRaw' in edge_attr:
                    capacity += edge_attr['LinkSpeedRaw']
            if capacity > 0:
                topology.edge[v][u]['capacity'] = capacity
        else:
            if 'LinkSpeedRaw' in topo_zoo_graph.edge[tv][tu]:
                topology.edge[v][u]['capacity'] = \
                        topo_zoo_graph.edge[tv][tu]['LinkSpeedRaw']
    if len(nx.get_edge_attributes(topology, 'capacity')) > 0:
        topology.graph['capacity_unit'] = 'bps'
    return topology
Exemple #19
0
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
Exemple #20
0
def parse_inet(path):
    """
    Parse a topology from an output file generated by the Inet topology
    generator

    Parameters
    ----------
    path : str
        The path to the Inet output file

    Returns
    -------
    topology : Topology

    Notes
    -----
    Each node of the returned topology object is labeled with *latitude* and
    *longitude* attributes. These attributes are not expressed in degrees but
    in Kilometers.
    """
    topology = Topology(type='inet', distance_unit='Km')
    lines = open(path, "r").readlines()
    sep = re.compile('[\s\t]')
    first_line = sep.split(lines[0].strip())
    try:
        n_nodes = int(first_line[0])
        n_links = int(first_line[1])
    except (ValueError, IndexError):
        raise ValueError('Invalid input file. '\
                         'Cannot parse the number of nodes and links')
    if len(lines) != 1 + n_nodes + n_links:
        raise ValueError('Invalid input file. '\
                         'It does not have as many lines as expected')
    i = 0
    for line in lines[1:]:
        entry = sep.split(line.strip())
        if i < n_nodes:
            i += 1
            try:
                node_id = int(entry[0])
                longitude = int(entry[1])
                latitude = int(entry[2])
            except (ValueError, IndexError):
                raise ValueError('Invalid input file. Parsing failed while '\
                                 'trying to parse a node')
            topology.add_node(node_id, latitude=latitude, longitude=longitude)
        else:
            try:
                u = int(entry[0])
                v = int(entry[1])
                weight = int(entry[2])
                x_u = topology.node[u]['longitude']
                y_u = topology.node[u]['latitude']
                x_v = topology.node[v]['longitude']
                y_v = topology.node[v]['latitude']
                length = float(math.sqrt((x_v - x_u)**2 + (y_v - y_u)**2))
            except (ValueError, IndexError):
                raise ValueError('Invalid input file. Parsing failed while '\
                                 'trying to parse a link')
            topology.add_edge(u, v, weight=weight, length=length)
    return topology
Exemple #21
0
def parse_inet(path):
    """
    Parse a topology from an output file generated by the Inet topology
    generator
    
    Parameters
    ----------
    path : str
        The path to the Inet output file
        
    Returns
    -------
    topology : Topology
    
    Notes
    -----
    Each node of the returned topology object is labeled with *latitude* and
    *longitude* attributes. These attributes are not expressed in degrees but
    in Kilometers.
    """
    topology = Topology(type='inet', distance_unit='Km')
    lines = open(path, "r").readlines()
    sep = re.compile('[\s\t]')
    first_line = sep.split(lines[0].strip())
    try:
        n_nodes = int(first_line[0])
        n_links = int(first_line[1])
    except (ValueError, IndexError):
        raise ValueError('Invalid input file. '\
                         'Cannot parse the number of nodes and links')
    if len(lines) != 1 + n_nodes + n_links:
        raise ValueError('Invalid input file. '\
                         'It does not have as many lines as expected')
    i = 0
    for line in lines[1:]:
        entry = sep.split(line.strip())
        if i < n_nodes:
            i += 1
            try:
                node_id = int(entry[0])
                longitude = int(entry[1])
                latitude = int(entry[2])
            except (ValueError, IndexError):
                raise ValueError('Invalid input file. Parsing failed while '\
                                 'trying to parse a node')
            topology.add_node(node_id, latitude=latitude, longitude=longitude)
        else:
            try:
                u = int(entry[0])
                v = int(entry[1])
                weight = int(entry[2])
                x_u = topology.node[u]['longitude']
                y_u = topology.node[u]['latitude']
                x_v = topology.node[v]['longitude']
                y_v = topology.node[v]['latitude']
                length = float(math.sqrt((x_v - x_u)**2 + (y_v - y_u)**2))
            except (ValueError, IndexError):
                raise ValueError('Invalid input file. Parsing failed while '\
                                 'trying to parse a link')
            topology.add_edge(u, v, weight=weight, length=length)
    return topology
Exemple #22
0
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
Exemple #23
0
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
Exemple #24
0
def glp_topology(n, m, m0, p, beta, seed=None):
    r"""
    Return a random topology using the Generalized Linear Preference (GLP)
    preferential attachment model.

    It differs from the extended Barabasi-Albert model in that there is link
    rewiring and a beta parameter is introduced to fine-tune preferential
    attachment.

    More precisely, the GLP topology is built as follows. First, a
    line topology with *m0* 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) - \beta 1}{\sum_{v \in V} (deg(v) - \beta)}

    with probability :math:`1-p`, 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
    beta : float
        Parameter to fine-tune preferntial attachment: beta < 1
    seed : int, optional
        Seed for random number generator (default=None).

    Returns
    -------
    G : Topology

    References
    ----------
    .. [1] T. Bu and D. Towsey "On distinguishing between Internet power law
       topology generators", Proceeding od the 21st IEEE INFOCOM conference.
       IEEE, volume 2, pages 638-647, 2002.
    """
    def calc_pi(G, beta):
        """Calculate GLP Pi function for all nodes of the graph"""
        # validate input parameter
        if beta >= 1:
            raise ValueError('beta must be < 1')
        degree = G.degree()
        den = float(sum(degree.values()) - (G.number_of_nodes() * beta))
        return {node: (degree[node] - beta) / den for node in G.nodes_iter()}

    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

    # validate input parameters
    if n < 1 or m < 1 or m0 < 1:
        raise ValueError('n, m and m0 must be a positive integers')
    if beta >= 1:
        raise ValueError('beta must be < 1')
    if m >= m0:
        raise ValueError('m must be <= m0')
    if p > 1 or p < 0:
        raise ValueError('p must be included between 0 and 1')
    if seed is not None:
        random.seed(seed)
    # step 1: create a graph of m0 nodes connected by n-1 edges
    G = Topology(nx.path_graph(m0))
    G.graph['type'] = 'glp'
    G.name = "glp_topology(%d, %d, %d, %f, %f)" % (n, m, m0, p, beta)
    # Add nodes and links now
    while G.number_of_nodes() < n:
        pi = calc_pi(G, beta)
        if random.random() < p:
            # add m new links with probability p
            add_m_links(G, pi)
        else:
            # add a new node with m new links with probability 1 - p
            add_node(G, pi)
    return G
Exemple #25
0
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
Exemple #26
0
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
Exemple #27
0
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
Exemple #28
0
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
Exemple #29
0
def glp_topology(n, m, m0, p, beta, seed=None):
    r"""
    Return a random topology using the Generalized Linear Preference (GLP)
    preferential attachment model.

    It differs from the extended Barabasi-Albert model in that there is link
    rewiring and a beta parameter is introduced to fine-tune preferential
    attachment.

    More precisely, the GLP topology is built as follows. First, a
    line topology with *m0* 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) - \beta 1}{\sum_{v \in V} (deg(v) - \beta)}

    with probability :math:`1-p`, 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
    beta : float
        Parameter to fine-tune preferntial attachment: beta < 1
    seed : int, optional
        Seed for random number generator (default=None).

    Returns
    -------
    G : Topology

    References
    ----------
    .. [1] T. Bu and D. Towsey "On distinguishing between Internet power law
       topology generators", Proceeding od the 21st IEEE INFOCOM conference.
       IEEE, volume 2, pages 638-647, 2002.
    """
    def calc_pi(G, beta):
        """Calculate GLP Pi function for all nodes of the graph"""
        # validate input parameter
        if beta >= 1:
            raise ValueError('beta must be < 1')
        degree = dict(G.degree())
        den = float(sum(degree.values()) - (G.number_of_nodes() * beta))
        return {node: (degree[node] - beta) / den for node in G.nodes()}

    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

    # validate input parameters
    if n < 1 or m < 1 or m0 < 1:
        raise ValueError('n, m and m0 must be a positive integers')
    if beta >= 1:
        raise ValueError('beta must be < 1')
    if m >= m0:
        raise ValueError('m must be <= m0')
    if p > 1 or p < 0:
        raise ValueError('p must be included between 0 and 1')
    if seed is not None:
        random.seed(seed)
    # step 1: create a graph of m0 nodes connected by n-1 edges
    G = Topology(nx.path_graph(m0))
    G.graph['type'] = 'glp'
    G.name = "glp_topology(%d, %d, %d, %f, %f)" % (n, m, m0, p, beta)
    # Add nodes and links now
    while G.number_of_nodes() < n:
        pi = calc_pi(G, beta)
        if random.random() < p:
            # add m new links with probability p
            add_m_links(G, pi)
        else:
            # add a new node with m new links with probability 1 - p
            add_node(G, pi)
    return G