Beispiel #1
0
def __ranking_metrics_heuristic(topology, od_pairs=None):
    """
    Sort OD pairs of a topology according to the Ranking Metrics Heuristics
    method

    Parameters
    ----------
    topology : Topology or DirectedTopology
        The topology
    od_pairs : list, optional
        The OD pairs to be ranked (must be a subset of the OD pairs of the
        topology). If None, then the heuristic is calculated for all the OD
        pairs of the topology

    Returns
    -------
    od_pairs : list
        The sorted list of OD pairs
    """
    # Ranking Metrics Heuristic
    if od_pairs is None:
        od_pairs = od_pairs_from_topology(topology)

    fan_in, fan_out = fan_in_out_capacities(topology)
    degree = topology.degree()
    min_capacity = {(u, v): min(fan_out[u], fan_in[v]) for u, v in od_pairs}
    min_degree = {(u, v): min(degree[u], degree[v]) for u, v in od_pairs}

    # NFUR calculation is expensive, so before calculating it, the code
    # checks if it is really needed, i.e. if there are ties after capacity
    # and degree sorting
    cap_deg_pairs = [(min_capacity[(u, v)], min_degree[(u, v)])
                     for u, v in od_pairs]
    nfur_required = any((val > 1 for val in Counter(cap_deg_pairs).values()))
    if not nfur_required:
        # Sort all OD_pairs
        return sorted(od_pairs,
                      key=lambda od_pair:
                      (min_capacity[od_pair], min_degree[od_pair]))
    # if NFUR is required we calculate it. If fast is True, this function
    # returns the betweenness centrality rather than the NFUR.
    # Use betweenness centrality instead of NFUR if the topology is not trivial
    # for scalability reasons.
    # The threshold of 300 is a conservative value which allows fast execution
    # on most machines.
    parallelize = (topology.number_of_edges() > 100)
    fast = (topology.number_of_edges() > 300)
    nfur = __calc_nfur(topology, fast, parallelize)
    # Note: here we use the opposite of max rather than the inverse of max
    # (which is the formulation of the paper) because we only need to rank
    # in reverse order the max of NFURs. Since all NFURs are >=0,
    # using the opposite yields the same results as the inverse, but there
    # is no risk of incurring in divisions by 0.
    max_inv_nfur = {(u, v): -max(nfur[u], nfur[v]) for u, v in od_pairs}
    # Sort all OD_pairs
    return sorted(
        od_pairs,
        key=lambda od_pair:
        (min_capacity[od_pair], min_degree[od_pair], max_inv_nfur[od_pair]))
Beispiel #2
0
def __ranking_metrics_heuristic(topology, od_pairs=None):
    """
    Sort OD pairs of a topology according to the Ranking Metrics Heuristics
    method

    Parameters
    ----------
    topology : Topology or DirectedTopology
        The topology
    od_pairs : list, optional
        The OD pairs to be ranked (must be a subset of the OD pairs of the
        topology). If None, then the heuristic is calculated for all the OD
        pairs of the topology

    Returns
    -------
    od_pairs : list
        The sorted list of OD pairs
    """
    # Ranking Metrics Heuristic
    if od_pairs is None:
        od_pairs = od_pairs_from_topology(topology)

    fan_in, fan_out = fan_in_out_capacities(topology)
    degree = topology.degree()
    min_capacity = {(u, v): min(fan_out[u], fan_in[v]) for u, v in od_pairs}
    min_degree = {(u, v): min(degree[u], degree[v]) for u, v in od_pairs}

    # NFUR calculation is expensive, so before calculating it, the code
    # checks if it is really needed, i.e. if there are ties after capacity
    # and degree sorting
    cap_deg_pairs = [(min_capacity[(u, v)], min_degree[(u, v)])
                     for u, v in od_pairs]
    nfur_required = any((val > 1 for val in Counter(cap_deg_pairs).values()))
    if not nfur_required:
        # Sort all OD_pairs
        return sorted(od_pairs, key=lambda od_pair: (min_capacity[od_pair],
                                                     min_degree[od_pair]))
    # if NFUR is required we calculate it. If fast is True, this function
    # returns the betweenness centrality rather than the NFUR.
    # Use betweenness centrality instead of NFUR if the topology is not trivial
    # for scalability reasons.
    # The threshold of 300 is a conservative value which allows fast execution
    # on most machines.
    parallelize = (topology.number_of_edges() > 100)
    fast = (topology.number_of_edges() > 300)
    nfur = __calc_nfur(topology, fast, parallelize)
    # Note: here we use the opposite of max rather than the inverse of max
    # (which is the formulation of the paper) because we only need to rank
    # in reverse order the max of NFURs. Since all NFURs are >=0,
    # using the opposite yields the same results as the inverse, but there
    # is no risk of incurring in divisions by 0.
    max_inv_nfur = {(u, v):-max(nfur[u], nfur[v]) for u, v in od_pairs}
    # Sort all OD_pairs
    return sorted(od_pairs, key=lambda od_pair: (min_capacity[od_pair],
                                                 min_degree[od_pair],
                                                 max_inv_nfur[od_pair]))
Beispiel #3
0
def validate_traffic_matrix(topology, traffic_matrix, validate_load=False):
    """
    Validate whether a given traffic matrix and given topology are compatible.

    Returns True if they are compatible, False otherwise

    This validation includes validating whether the origin-destination pairs of
    the traffic matrix are coincide with or are a subset of the
    origin-destination pairs of the topology. Optionally, this function can
    verify if the volumes of the traffic matrix are compatible too, i.e. if at
    any time, no link has an utilization greater than 1.0.

    Parameters
    ----------
    topology : topology
        The topology agains which the traffic matrix is validated
    tm : TrafficMatrix or TrafficMatrixSequence
        The traffic matrix (or sequence of) to be validated
    validate_load : bool, optional
        Specify whether load compatibility has to be validated or not.
        Default value is False

    Returns
    -------
    is_valid : bool
        True if the topology and the traffic matrix are compatible,
        False otherwise
    """
    if isinstance(traffic_matrix, TrafficMatrix):
        matrices = [traffic_matrix]
    elif isinstance(traffic_matrix, TrafficMatrixSequence):
        matrices = traffic_matrix.matrix
    else:
        raise ValueError('tm must be either a TrafficMatrix or a '\
                         ' TrafficMatrixSequence object')

    topology = topology.copy() if topology.is_directed() \
                               else topology.to_directed()

    od_pairs_topology = od_pairs_from_topology(topology)
    if validate_load:
        shortest_path = nx.all_pairs_dijkstra_path(topology, weight='weight')
    for matrix in matrices:
        od_pairs_tm = matrix.od_pairs()
        # verify that OD pairs in TM are equal or subset of topology
        if not all(((o, d) in od_pairs_topology for o, d in od_pairs_tm)):
            return False
        if validate_load:
            for u, v in topology.edges_iter():
                topology.edge[u][v]['load'] = 0
            capacity_unit = capacity_units[topology.graph['capacity_unit']]
            volume_unit = capacity_units[matrix.attrib['volume_unit']]
            norm_factor = float(volume_unit) / float(capacity_unit)
            for o, d in od_pairs_tm:
                path = shortest_path[o][d]
                if len(path) <= 1:
                    continue
                for u, v in zip(path[:-1], path[1:]):
                    topology.edge[u][v]['load'] += matrix.flow[o][d]
            max_u = max((norm_factor * float(topology.edge[u][v]['load']) \
                         / float(topology.edge[u][v]['capacity'])
                         for u, v in topology.edges_iter()))
            if max_u > 1.0: return False
    return True
Beispiel #4
0
def static_traffic_matrix(topology, mean, stddev, max_u=0.9,
                          origin_nodes=None, destination_nodes=None):
    """
    Return a TrafficMatrix object, i.e. a single traffic matrix, representing
    the traffic volume exchanged over a network at a specific point in time

    This matrix is generated by assigning traffic volumes drawn from a
    lognormal distribution and assigned to specific origin-destination pairs
    using the Ranking Metrics Heuristic method proposed by Nucci et al. [1]_

    Parameters
    ----------
    topology : topology
        The topology for which the traffic matrix is calculated. This topology
        can either be directed or undirected. If it is undirected, this
        function assumes that all links are full-duplex.

    mean : float
        The mean volume of traffic among all origin-destination pairs

    stddev : float
        The standard deviation of volumes among all origin-destination pairs.

    max_u : float, optional
        Represent the max link utilization. If specified, traffic volumes are
        scaled so that the most utilized link of the network has an utilization
        equal to max_u. If None, volumes are not scaled, but in this case links
        may end up with an utilization factor greater than 1.0

    origin_nodes : list, optional
        A list of all nodes which can be traffic sources. If not specified,
        all nodes of the topology are traffic sources

    destination_nodes : list, optional
        A list of all nodes which can be traffic destinations. If not
        specified, all nodes of the topology are traffic destinations

    Returns
    -------
    tm : TrafficMatrix

    References
    ----------
    .. [1] Nucci et al., The problem of synthetically generating IP traffic
       matrices: initial recommendations, ACM SIGCOMM Computer Communication
       Review, 35(3), 2005
    """
    try:
        mean = float(mean)
        stddev = float(stddev)
    except ValueError:
        raise ValueError('mean and stddev must be of type float')
    if mean < 0 or stddev < 0:
        raise ValueError('mean and stddev must be not negative')
    topology = topology.copy() if topology.is_directed() \
               else topology.to_directed()
    volume_unit = topology.graph['capacity_unit']
    mu = log(mean ** 2 / sqrt(stddev ** 2 + mean ** 2))
    sigma = sqrt(log((stddev ** 2 / mean ** 2) + 1))
    if origin_nodes is None and destination_nodes is None:
        od_pairs = od_pairs_from_topology(topology)
    else:
        all_nodes = topology.nodes()
        origins = origin_nodes or all_nodes
        destinations = destination_nodes or all_nodes
        od_pairs = [(o, d) for o in origins for d in destinations if o != d]
    nr_pairs = len(od_pairs)
    volumes = sorted(lognormal(mu, sigma, size=nr_pairs))
    # volumes = sorted([lognormvariate(mu, sigma) for _ in range(nr_pairs)])
    if any(isinf(vol) for vol in volumes):
        raise ValueError('Some volumes are too large to be handled by a '\
                         'float type. Set a lower value of mu and try again.')
    sorted_od_pairs = __ranking_metrics_heuristic(topology, od_pairs)
    # check if the matrix matches and scale if needed
    assignments = dict(zip(sorted_od_pairs, volumes))
    if max_u is not None:
        if origin_nodes is not None:
            shortest_path = dict(
                    (node, nx.single_source_dijkstra_path(topology,
                                                          node,
                                                          weight='weight'))
                    for node in origin_nodes)
            # remove OD pairs not connected
            for o, d in itertools.product(shortest_path, destinations):
                if o != d and d not in shortest_path[o]:
                    od_pairs.remove((o, d))
        else:
            shortest_path = nx.all_pairs_dijkstra_path(topology,
                                                       weight='weight')
        for u, v in topology.edges_iter():
            topology.edge[u][v]['load'] = 0.0
        # Find max u
        for o, d in od_pairs:
            path = shortest_path[o][d]
            if len(path) > 1:
                for u, v in zip(path[:-1], path[1:]):
                    topology.edge[u][v]['load'] += assignments[(o, d)]
        # Calculate scaling
        current_max_u = max((float(topology.edge[u][v]['load']) \
                             / float(topology.edge[u][v]['capacity'])
                             for u, v in topology.edges_iter()))
        norm_factor = max_u / current_max_u
        for od_pair in assignments:
            assignments[od_pair] *= norm_factor

    # write to traffic matrix
    traffic_matrix = TrafficMatrix(volume_unit=volume_unit)
    for (o, d), flow in assignments.items():
        traffic_matrix.add_flow(o, d, flow)
    return traffic_matrix
Beispiel #5
0
def validate_traffic_matrix(topology, traffic_matrix, validate_load=False):
    """
    Validate whether a given traffic matrix and given topology are compatible.
    
    Returns True if they are compatible, False otherwise
    
    This validation includes validating whether the origin-destination pairs of 
    the traffic matrix are coincide with or are a subset of the 
    origin-destination pairs of the topology. Optionally, this function can 
    verify if the volumes of the traffic matrix are compatible too, i.e. if at 
    any time, no link has an utilization greater than 1.0.
    
    Parameters
    ----------
    topology : topology
        The topology agains which the traffic matrix is validated
    tm : TrafficMatrix or TrafficMatrixSequence
        The traffic matrix (or sequence of) to be validated
    validate_load : bool, optional
        Specify whether load compatibility has to be validated or not. 
        Default value is False
        
    Returns
    -------
    is_valid : bool
        True if the topology and the traffic matrix are compatible, 
        False otherwise
    """
    if isinstance(traffic_matrix, TrafficMatrix):
        matrices = [traffic_matrix]
    elif isinstance(traffic_matrix, TrafficMatrixSequence):
        matrices = traffic_matrix.matrix
    else:
        raise ValueError('tm must be either a TrafficMatrix or a '\
                         ' TrafficMatrixSequence object')
    
    topology = topology.copy() if topology.is_directed() \
                               else topology.to_directed()
   
    od_pairs_topology = od_pairs_from_topology(topology)
    if validate_load:
        shortest_path = nx.all_pairs_dijkstra_path(topology, weight='weight')
    for matrix in matrices:
        od_pairs_tm = matrix.od_pairs()
        # verify that OD pairs in TM are equal or subset of topology
        if not all(((o, d) in od_pairs_topology for o, d in od_pairs_tm)):
            return False
        if validate_load:
            for u, v in topology.edges_iter():
                topology.edge[u][v]['load'] = 0
            capacity_unit = capacity_units[topology.graph['capacity_unit']]
            volume_unit = capacity_units[matrix.attrib['volume_unit']]
            norm_factor = float(capacity_unit)/float(volume_unit)
            for o, d in od_pairs_tm:
                path = shortest_path[o][d]
                if len(path) <= 1:
                    continue
                for hop in range(len(path) - 1):
                    topology.edge[path[hop]][path[hop + 1]]['load'] \
                            += matrix.flow[o][d]
            max_u = max((norm_factor * float(topology.edge[u][v]['load']) \
                         / float(topology.edge[u][v]['capacity']) 
                         for u, v in topology.edges_iter()))
            if max_u > 1.0: return False
    return True
Beispiel #6
0
def static_traffic_matrix(topology, mean, stddev, max_u=0.9, 
                          origin_nodes=None, destination_nodes=None):
    """
    Return a TrafficMatrix object, i.e. a single traffic matrix, representing
    the traffic volume exchanged over a network at a specific point in time
    
    This matrix is generated by assigning traffic volumes drawn from a 
    lognormal distribution and assigned to specific origin-destination pairs
    using the Ranking Metrics Heuristic method proposed by Nucci et al. [1]_
    
    Parameters
    ----------
    topology : topology
        The topology for which the traffic matrix is calculated. This topology
        can either be directed or undirected. If it is undirected, this 
        function assumes that all links are full-duplex.
    
    mean : float
        The mean volume of traffic among all origin-destination pairs
    
    stddev : float
        The standard deviation of volumes among all origin-destination pairs.
    
    max_u : float, optional
        Represent the max link utilization. If specified, traffic volumes are
        scaled so that the most utilized link of the network has an utilization
        equal to max_u. If None, volumes are not scaled, but in this case links
        may end up with an utilization factor greater than 1.0
    
    origin_nodes : list, optional
        A list of all nodes which can be traffic sources. If not specified,
        all nodes of the topology are traffic sources
    
    destination_nodes : list, optional
        A list of all nodes which can be traffic destinations. If not 
        specified, all nodes of the topology are traffic destinations
        
    Returns
    -------
    tm : TrafficMatrix
    
    References
    ----------
    .. [1] Nucci et al., The problem of synthetically generating IP traffic 
       matrices: initial recommendations, ACM SIGCOMM Computer Communication 
       Review, 35(3), 2005
    """
    try:
        mean = float(mean)
        stddev = float(stddev)
    except ValueError:
        raise ValueError('mean and stddev must be of type float')
    if mean < 0 or stddev < 0:
        raise ValueError('mean and stddev must be not negative')
    topology = topology.copy() if topology.is_directed() \
               else topology.to_directed()
    volume_unit = topology.graph['capacity_unit']
    mu = log(mean**2/sqrt(stddev**2 + mean**2))
    sigma = sqrt(log((stddev**2/mean**2) + 1))
    if origin_nodes is None and destination_nodes is None:
        od_pairs = od_pairs_from_topology(topology)
    else:
        all_nodes = topology.nodes()
        origins = origin_nodes if origin_nodes is not None \
                  else all_nodes
        destinations = destination_nodes if destination_nodes is not None \
                       else all_nodes
        od_pairs = [(o, d) for o in origins for d in destinations if o != d]
    nr_pairs = len(od_pairs)
    volumes = sorted(lognormal(mu, sigma, size=nr_pairs))
    #volumes = sorted([lognormvariate(mu, sigma) for _ in range(nr_pairs)])
    if any(isinf(vol) for vol in volumes):
        raise ValueError('Some volumes are too large to be handled by a '\
                         'float type. Set a lower value of mu and try again.')
    sorted_od_pairs = __ranking_metrics_heuristic(topology, od_pairs)
    # check if the matrix matches and scale if needed
    assignments = dict(zip(sorted_od_pairs, volumes))
    if max_u is not None:
        if origin_nodes is not None:
            shortest_path = dict(
                    (node, nx.single_source_dijkstra_path(topology,
                                                          node, 
                                                          weight='weight'))
                    for node in origin_nodes)
            # remove OD pairs not connected
            for o in shortest_path:
                for d in destinations:
                    if o != d and d not in shortest_path[o]:
                        od_pairs.remove((o, d))
        else:
            shortest_path = nx.all_pairs_dijkstra_path(topology, 
                                                       weight='weight')
        for u, v in topology.edges_iter():
            topology.edge[u][v]['load'] = 0.0
        # Find max u
        for o, d in od_pairs:
            path = shortest_path[o][d]
            if len(path) > 1:
                for hop in range(len(path) - 1):
                    topology.edge[path[hop]][path[hop + 1]]['load'] \
                            += assignments[(o, d)]
        # Calculate scaling
        current_max_u = max((float(topology.edge[u][v]['load']) \
                             /float(topology.edge[u][v]['capacity']) 
                             for u, v in topology.edges_iter()))
        norm_factor = max_u/current_max_u
        for od_pair in assignments:
            assignments[od_pair] *= norm_factor
            
    # write to traffic matrix
    traffic_matrix = TrafficMatrix(volume_unit=volume_unit)
    for (o, d), flow in assignments.items():
        traffic_matrix.add_flow(o, d, flow)
    return traffic_matrix