Пример #1
0
def GetStaticEigenvectorCentrality(t, model='SECOND'):
    """Computes eigenvector centralities of nodes in the second-order aggregate network, 
    and aggregates eigenvector centralities to obtain the eigenvector centrality of nodes in the 
    first-order network.
    
    @param t: The temporalnetwork instance to work on
    @param model: either C{"SECOND"} or C{"NULL"}, where C{"SECOND"} is the 
      the default value."""

    if (model is "SECOND" or "NULL") == False:
        raise ValueError("model must be one of \"SECOND\" or \"NULL\"")

    name_map = Utilities.firstOrderNameMap(t)

    if model == 'SECOND':
        g2 = t.igraphSecondOrder()
    else:
        g2 = t.igraphSecondOrderNull()

    # Compute eigenvector centrality in second-order network
    A = Utilities.getSparseAdjacencyMatrix(g2,
                                           attribute="weight",
                                           transposed=True)
    evcent_2 = Utilities.StationaryDistribution(A, False)

    # Aggregate to obtain first-order eigenvector centrality
    evcent_1 = np.zeros(len(name_map))
    sep = t.separator
    for i in range(len(evcent_2)):
        # Get name of target node
        target = g2.vs()[i]["name"].split(sep)[1]
        evcent_1[name_map[target]] += np.real(evcent_2[i])

    return np.real(evcent_1 / sum(evcent_1))
Пример #2
0
def GetStaticEigenvectorCentrality(t, model='SECOND'):
    """Computes eigenvector centralities of nodes in the second-order aggregate network, 
    and aggregates eigenvector centralities to obtain the eigenvector centrality of nodes in the 
    first-order network.
    
    @param t: The temporalnetwork instance to work on
    @param model: either C{"SECOND"} or C{"NULL"}, where C{"SECOND"} is the 
      the default value."""

    if (model is "SECOND" or "NULL") == False:
        raise ValueError("model must be one of \"SECOND\" or \"NULL\"")

    name_map = Utilities.firstOrderNameMap(t)
    
    if model == 'SECOND':
        g2 = t.igraphSecondOrder()
    else:
        g2 = t.igraphSecondOrderNull()
    
    # Compute eigenvector centrality in second-order network
    A = Utilities.getSparseAdjacencyMatrix( g2, attribute="weight", transposed=True )
    evcent_2 = Utilities.StationaryDistribution( A, False )
    
    # Aggregate to obtain first-order eigenvector centrality
    evcent_1 = np.zeros(len(name_map))
    sep = t.separator
    for i in range(len(evcent_2)):
        # Get name of target node
        target = g2.vs()[i]["name"].split(sep)[1]
        evcent_1[name_map[target]] += np.real(evcent_2[i])
    
    return np.real(evcent_1/sum(evcent_1))
Пример #3
0
def EigenValueGap(t):
    """Returns the eigenvalue gap in the second-order transition matrix of a 
    temporal network, as well as in the corresponding null model. Returns 
    the tuple (lambda(T2), lambda(T2_null))
    
    @param t: The temporalnetwork instance to work on
    """

    #NOTE to myself: most of the time goes for construction of the 2nd order
    #NOTE            null graph, then for the 2nd order null transition matrix

    g2 = t.igraphSecondOrder().components(mode="STRONG").giant()
    g2n = t.igraphSecondOrderNull().components(mode="STRONG").giant()

    Log.add('Calculating eigenvalue gap ... ', Severity.INFO)

    # Build transition matrices
    T2 = Utilities.RWTransitionMatrix(g2)
    T2n = Utilities.RWTransitionMatrix(g2n)

    # Compute eigenvector sequences
    # NOTE: ncv=13 sets additional auxiliary eigenvectors that are computed
    # NOTE: in order to be more confident to find the one with the largest
    # NOTE: magnitude, see
    # NOTE: https://github.com/scipy/scipy/issues/4987
    w2 = sla.eigs(T2, which="LM", k=2, ncv=13, return_eigenvectors=False)
    evals2_sorted = np.sort(-np.absolute(w2))

    w2n = sla.eigs(T2n, which="LM", k=2, ncv=13, return_eigenvectors=False)
    evals2n_sorted = np.sort(-np.absolute(w2n))

    Log.add('finished.', Severity.INFO)

    return (np.abs(evals2_sorted[1]), np.abs(evals2n_sorted[1]))
Пример #4
0
def SlowDownFactor(t):
    """Returns a factor S that indicates how much slower (S>1) or faster (S<1)
    a diffusion process in the temporal network evolves on a second-order model 
    compared to a first-order model. This value captures the effect of order
    correlations on a diffusion process in the temporal network.
    
    @param t: The temporalnetwork instance to work on
    """

    #NOTE to myself: most of the time goes for construction of the 2nd order
    #NOTE            null graph, then for the 2nd order null transition matrix

    g2 = t.igraphSecondOrder().components(mode="STRONG").giant()
    g2n = t.igraphSecondOrderNull().components(mode="STRONG").giant()

    Log.add('Calculating slow down factor ... ', Severity.INFO)

    # Build transition matrices
    T2 = Utilities.RWTransitionMatrix(g2)
    T2n = Utilities.RWTransitionMatrix(g2n)

    # Compute eigenvector sequences
    # NOTE: ncv=13 sets additional auxiliary eigenvectors that are computed
    # NOTE: in order to be more confident to find the one with the largest
    # NOTE: magnitude, see
    # NOTE: https://github.com/scipy/scipy/issues/4987
    w2 = sla.eigs(T2, which="LM", k=2, ncv=13, return_eigenvectors=False)
    evals2_sorted = np.sort(-np.absolute(w2))

    w2n = sla.eigs(T2n, which="LM", k=2, ncv=13, return_eigenvectors=False)
    evals2n_sorted = np.sort(-np.absolute(w2n))

    Log.add('finished.', Severity.INFO)

    return np.log(np.abs(evals2n_sorted[1])) / np.log(np.abs(evals2_sorted[1]))
Пример #5
0
def RWDiffusion(g, samples=5, epsilon=0.01, max_iterations=100000):
    """Computes the average number of steps requires by a random walk process
    to fall below a total variation distance below epsilon (TVD computed between the momentary 
    visitation probabilities \pi^t and the stationary distribution \pi = \pi^{\infty}. This time can be 
    used to measure diffusion speed in a given (weighted and directed) network."""
    avg_speed = 0

    T = Utilities.RWTransitionMatrix(g)
    pi = Utilities.StationaryDistribution(T)

    n = len(g.vs())
    for s in range(samples):
        x = np.zeros(n)
        seed = np.random.randint(n)
        x[seed] = 1
        while Utilities.TVD(x, pi) > epsilon:
            avg_speed += 1
            # NOTE x * T = (T^T * x^T)^T
            # NOTE T is already transposed to get the left EV
            x = (T.dot(x.transpose())).transpose()
            if avg_speed > max_iterations:
                Log.add("x[0:10] = " + str(x[0:10]))
                Log.add("pi[0:10] = " + str(pi[0:10]))
                raise RuntimeError(
                    "Failed to converge within maximal number of iterations. Start of current x and pi are printed above"
                )

    return avg_speed / samples
Пример #6
0
def exportDiffusionMovieFrames(g,
                               file_prefix='diffusion',
                               visual_style=None,
                               steps=100,
                               initial_index=-1):
    """Exports an animation showing the evolution of a diffusion
           process on the network"""

    T = Utilities.RWTransitionMatrix(g)

    if visual_style == None:
        visual_style = {}
        visual_style["vertex_color"] = "lightblue"
        visual_style["vertex_label"] = g.vs["name"]
        visual_style["edge_curved"] = .5
        visual_style["vertex_size"] = 30

    # lambda expression for the coloring of nodes according to some quantity p \in [0,1]
    # p = 1 ==> color red
    # p = 0 ==> color white
    color_p = lambda p: "rgb(255," + str(int(
        (1 - p) * 255)) + "," + str(int((1 - p) * 255)) + ")"

    # Initial state of random walker
    if initial_index < 0:
        initial_index = np.random.randint(0, len(g.vs()))

    x = np.zeros(len(g.vs()))
    x[initial_index] = 1

    # compute stationary state
    pi = Utilities.StationaryDistribution(T)

    scale = np.mean(np.abs(x - pi))

    # Plot network (useful as poster frame of video)
    igraph.plot(g, file_prefix + "_network.pdf", **visual_style)

    # Create frames
    for i in range(0, steps):
        visual_style["vertex_color"] = [color_p(p**0.1) for p in x]
        igraph.plot(g, file_prefix + "_frame_" + str(i).zfill(5) + ".png",
                    **visual_style)
        if i % 10 == 0:
            Log.add('Step' + str(i) + '\tTVD = ' + str(Utilities.TVD(x, pi)),
                    Severity.INFO)
        # NOTE x * T = (T^T * x^T)^T
        # NOTE T is already transposed to get the left EV
        x = (T.dot(x.transpose())).transpose()
Пример #7
0
def GetTemporalClosenessInstantaneous(t, start_t=0, delta=1):
    """Calculates the temporal closeness values of 
    all nodes for a given start time start_t in a temporal network t.
    This function returns a numpy array of (temporal) closeness centrality values. 
    The ordering of these values corresponds to the ordering of nodes in the vertex 
    sequence of the igraph first order time-aggregated network. A mapping between node names
    and array indices can be found in Utilities.firstOrderNameMap().
    
    @param t: the temporal network for which temporal closeness centralities will be computed
    @param start_t: the start time for which to consider time-respecting paths (default 0). This is 
        important, since any unambigious definition of a shortest time-respecting path between
        two nodes must include the time range to be considered (c.f. Holme and Saramäki, Phys. Rep., 2012)
    @param delta: the maximum time difference time used in the time-respecting path definition (default 1)
        Note that this parameter is independent from the delta used internally for the extraction of two-paths
        by the class TemporalNetwork.
    """
    
    closeness = np.array([0.]*len(t.nodes))

    # Calculate all shortest time-respecting paths
    D, paths = Paths.GetTemporalDistanceMatrix(t, start_t, delta, collect_paths=False)

    # Get a mapping between node names and matrix indices
    name_map = Utilities.firstOrderNameMap( t )

    # Calculate closeness for each node u, by summing the reciprocal of its 
    # distances to all other nodes. Note that this definition of closeness centrality 
    # is required for directed networks that are not strongly connected. 
    for u in t.nodes:
        for v in t.nodes:
            if u!=v:
                closeness[name_map[u]] += 1./D[name_map[v], name_map[u]]

    return closeness
Пример #8
0
def GetTemporalBetweenness(t, delta=1, normalized=False):
    """Calculates the temporal betweenness centralities of all nodes 
    in a temporal network t based on the shortest time-respecting paths with a 
    maximum waiting time of delta. This function returns a numpy array of temporal betweenness centrality values of 
    nodes. The ordering of these values corresponds to the ordering of nodes in the vertex sequence 
    of the igraph first order time-aggregated network. A mapping between node names and array 
    indices can be found in  Utilities.firstOrderNameMap().
    
    @param t: the temporal network for which temporal closeness centralities will be computed    
    @param delta: the maximum time difference used in the time-respecting path definition (default 1).
        Note that this parameter is independent from the delta used internally for the extraction of two-paths
        by the class TemporalNetwork
    @param normalized: whether or not to normalize centralities by dividing each value byt the total number 
        of shortest time-respecting paths.
    """

    bw = np.array([0] * len(t.nodes))
    S = 0

    name_map = Utilities.firstOrderNameMap(t)

    minD, minPaths = Paths.GetMinTemporalDistance(t,
                                                  delta=1,
                                                  collect_paths=True)

    for v in t.nodes:
        for w in t.nodes:
            for p in minPaths[v][w]:
                for i in range(1, len(p) - 1):
                    bw[name_map[p[i][0]]] += 1
                    S += 1
    return bw
Пример #9
0
def GetStaticPageRank(t, model='SECOND'):
    """Computes PageRank of nodes based on the second-order aggregate network, 
    and aggregates PageRank values to obtain the PageRank of nodes in the
    first-order network.
    
    @param t: The temporalnetwork instance to work on
    @param model: either C{"SECOND"} or C{"NULL"}, where C{"SECOND"} is the 
      the default value.
    """

    if (model is "SECOND" or "NULL") == False:
        raise ValueError("model must be one of \"SECOND\" or \"NULL\"")

    name_map = Utilities.firstOrderNameMap( t )

    if model == 'SECOND':
        g2 = t.igraphSecondOrder()
    else:
        g2 = t.igraphSecondOrderNull()    

    # Compute betweenness centrality in second-order network
    pagerank_2 = np.array(g2.pagerank(weights=g2.es()['weight'], directed=True))
    
    # Aggregate to obtain first-order eigenvector centrality
    pagerank_1 = np.zeros(len(name_map))
    sep = t.separator
    for i in range(len(pagerank_2)):
        # Get name of target node
        target = g2.vs()[i]["name"].split(sep)[1]
        pagerank_1[name_map[target]] += pagerank_2[i]
    
    return pagerank_1/sum(pagerank_1)
Пример #10
0
def GetStaticCloseness(t, model='SECOND'):
    """Computes closeness centralities of nodes based on the first- or second-order time-aggregated network.
    
    @param t: The temporal network instance for which closeness centralities will be computed
    @param model: either C{"FIRST"}, C{"SECOND"} or C{"SECONDNULL"}, where C{"SECOND"} is the 
      the default value.
    """

    if model =='FIRST':
        D = Paths.GetFirstOrderDistanceMatrix(t)
    else:
        D = Paths.GetSecondOrderDistanceMatrix(t, model)

    name_map = Utilities.firstOrderNameMap( t )

    closeness = np.zeros(len(name_map))

    # Calculate closeness for each node u, by summing the reciprocal of its 
    # distances to all other nodes. Note that this definition of closeness centrality 
    # is required for directed networks that are not strongly connected. 
    for u in t.nodes:
        for v in t.nodes:
            if u!=v:
                closeness[name_map[u]] += 1./D[name_map[v], name_map[u]] 
    return closeness
Пример #11
0
def GetTemporalCloseness(t, delta=1):
    """Calculates the temporal closeness centralities of all nodes 
    in a temporal network t, based on the minimal shortest time-respecting paths with a 
    maximum time difference of delta. This function then returns a numpy 
    array of average (temporal) closeness centrality values of nodes. The ordering of these 
    values corresponds to the ordering of nodes in the vertex sequence of the igraph first order 
    time-aggregated network. A mapping between node names and array indices can be found in 
    Utilities.firstOrderNameMap().
    
    @param t: the temporal network for which temporal closeness centralities will be computed    
    @param delta: the maximum waiting time used in the time-respecting path definition (default 1)      
        Note that this parameter is independent from the delta used internally for the extraction of two-paths
        by the class TemporalNetwork     
    """

    cl = np.array([0.]*len(t.nodes))

    name_map = Utilities.firstOrderNameMap( t )

    minD, minPaths = Paths.GetMinTemporalDistance(t, delta, collect_paths=False)

    for u in t.nodes:
        for v in t.nodes:
            if u!= v:
                cl[name_map[v]] += 1./minD[name_map[u], name_map[v]]
    return cl
Пример #12
0
def GetFirstOrderDistanceMatrix(t):
    """Calculates a matrix D containing the shortest path lengths between all
    pairs of nodes calculated based on the topology of the *first-order* aggregate network. 
    The ordering of rows/columns corresponds to the ordering of nodes in the vertex sequence of 
    the igraph first order time-aggregated network. A mapping between nodes and indices can be 
    found in Utilities.firstOrderNameMap().    
    
    @param t: the temporal network to calculate shortest path lengths for based on a first-order
        aggregate representation    
    """

    # This way of generating the first-order time-aggregated network makes sure that
    # links are not omitted even if they do not contribute to any time-respecting path
    g1 = t.igraphFirstOrder(all_links=False, force=True)

    name_map = Utilities.firstOrderNameMap(t)

    D = np.zeros(shape=(len(t.nodes), len(t.nodes)))
    D.fill(np.inf)
    np.fill_diagonal(D, 0)

    for v in g1.vs()["name"]:
        for w in g1.vs()["name"]:
            # Compute all shortest paths using igraph
            X = g1.get_shortest_paths(v, w)
            for p in X:
                if len(p) > 0:
                    D[name_map[v], name_map[w]] = len(p) - 1
    return D
Пример #13
0
def GetFirstOrderDistanceMatrix(t):        
    """Calculates a matrix D containing the shortest path lengths between all
    pairs of nodes calculated based on the topology of the *first-order* aggregate network. 
    The ordering of rows/columns corresponds to the ordering of nodes in the vertex sequence of 
    the igraph first order time-aggregated network. A mapping between nodes and indices can be 
    found in Utilities.firstOrderNameMap().    
    
    @param t: the temporal network to calculate shortest path lengths for based on a first-order
        aggregate representation    
    """   

    # This way of generating the first-order time-aggregated network makes sure that 
    # links are not omitted even if they do not contribute to any time-respecting path
    g1 = t.igraphFirstOrder(all_links=False, force=True)

    name_map = Utilities.firstOrderNameMap( t )

    D = np.zeros(shape=(len(t.nodes),len(t.nodes)))
    D.fill(np.inf)
    np.fill_diagonal(D, 0)

    for v in g1.vs()["name"]:
        for w in g1.vs()["name"]:
            # Compute all shortest paths using igraph
            X = g1.get_shortest_paths(v,w)
            for p in X:
                if len(p)>0:
                    D[name_map[v], name_map[w]] = len(p)-1
    return D
Пример #14
0
def GetTemporalBetweenness(t, delta=1, normalized=False):
    """Calculates the temporal betweenness centralities of all nodes 
    in a temporal network t based on the shortest time-respecting paths with a 
    maximum waiting time of delta. This function returns a numpy array of temporal betweenness centrality values of 
    nodes. The ordering of these values corresponds to the ordering of nodes in the vertex sequence 
    of the igraph first order time-aggregated network. A mapping between node names and array 
    indices can be found in  Utilities.firstOrderNameMap().
    
    @param t: the temporal network for which temporal closeness centralities will be computed    
    @param delta: the maximum time difference used in the time-respecting path definition (default 1).
        Note that this parameter is independent from the delta used internally for the extraction of two-paths
        by the class TemporalNetwork
    @param normalized: whether or not to normalize centralities by dividing each value byt the total number 
        of shortest time-respecting paths.
    """

    bw = np.array([0]*len(t.nodes))
    S = 0

    name_map = Utilities.firstOrderNameMap(t)

    minD, minPaths = Paths.GetMinTemporalDistance(t, delta=1, collect_paths=True)

    for v in t.nodes:
        for w in t.nodes:
            for p in minPaths[v][w]:
                for i in range(1,len(p)-1):
                    bw[name_map[p[i][0]]] += 1
                    S+=1
    return bw
Пример #15
0
def GetTemporalCloseness(t, delta=1):
    """Calculates the temporal closeness centralities of all nodes 
    in a temporal network t, based on the minimal shortest time-respecting paths with a 
    maximum time difference of delta. This function then returns a numpy 
    array of average (temporal) closeness centrality values of nodes. The ordering of these 
    values corresponds to the ordering of nodes in the vertex sequence of the igraph first order 
    time-aggregated network. A mapping between node names and array indices can be found in 
    Utilities.firstOrderNameMap().
    
    @param t: the temporal network for which temporal closeness centralities will be computed    
    @param delta: the maximum waiting time used in the time-respecting path definition (default 1)      
        Note that this parameter is independent from the delta used internally for the extraction of two-paths
        by the class TemporalNetwork     
    """

    cl = np.array([0.] * len(t.nodes))

    name_map = Utilities.firstOrderNameMap(t)

    minD, minPaths = Paths.GetMinTemporalDistance(t,
                                                  delta,
                                                  collect_paths=False)

    for u in t.nodes:
        for v in t.nodes:
            if u != v:
                cl[name_map[v]] += 1. / minD[name_map[u], name_map[v]]
    return cl
Пример #16
0
def GetStaticCloseness(t, model='SECOND'):
    """Computes closeness centralities of nodes based on the first- or second-order time-aggregated network.
    
    @param t: The temporal network instance for which closeness centralities will be computed
    @param model: either C{"FIRST"}, C{"SECOND"} or C{"SECONDNULL"}, where C{"SECOND"} is the 
      the default value.
    """

    if model == 'FIRST':
        D = Paths.GetFirstOrderDistanceMatrix(t)
    else:
        D = Paths.GetSecondOrderDistanceMatrix(t, model)

    name_map = Utilities.firstOrderNameMap(t)

    closeness = np.zeros(len(name_map))

    # Calculate closeness for each node u, by summing the reciprocal of its
    # distances to all other nodes. Note that this definition of closeness centrality
    # is required for directed networks that are not strongly connected.
    for u in t.nodes:
        for v in t.nodes:
            if u != v:
                closeness[name_map[u]] += 1. / D[name_map[v], name_map[u]]
    return closeness
Пример #17
0
def GetTemporalBetweennessInstantaneous(t,
                                        start_t=0,
                                        delta=1,
                                        normalized=False):
    """Calculates the temporal betweennness values of 
    all nodes fir a given start time start_t in an empirical temporal network t.
    This function returns a numpy array of (temporal) betweenness centrality values. 
    The ordering of these values corresponds to the ordering of nodes in the vertex 
    sequence of the igraph first order time-aggregated network. A mapping between node names
    and array indices can be found in Utilities.firstOrderNameMap().
    
    @param t: the temporal network for which temporal betweenness centralities will be computed
    @param start_t: the start time for which to consider time-respecting paths (default 0). This is 
        important, since any unambigious definition of a shortest time-respecting path between
        two nodes must include the time range to be considered (c.f. Holme and Saramäki, Phys. Rep., 2012)
    @param delta: the maximum waiting time used in the time-respecting path definition (default 1)
        Note that this parameter is independent from the delta used internally for the extraction of two-paths
        by the class TemporalNetwork
    @param normalized: whether or not to normalize the temporal betweenness centrality values by
    dividing by the number of all shortest time-respecting paths in the temporal network.
    """

    bw = np.array([0] * len(t.nodes))

    # First calculate all shortest time-respecting paths starting at time start_t
    D, paths = Paths.GetTemporalDistanceMatrix(t,
                                               start_t,
                                               delta,
                                               collect_paths=True)

    # Get a mapping between node names and matrix indices
    name_map = Utilities.firstOrderNameMap(t)

    # Compute betweenness scores of all nodes based on shortest time-respecting paths
    k = 0
    for u in t.nodes:
        for v in t.nodes:
            if u != v:
                for p in paths[u][v]:
                    for i in range(1, len(p) - 1):
                        bw[name_map[p[i][0]]] += 1
                        k += 1

    # Normalize by dividing by the total number of shortest time-respecting paths
    if normalized:
        bw = bw / k
    return bw
Пример #18
0
def GetStaticPageRank(t,
                      model='SECOND',
                      projection='TARGET',
                      normalization=False):
    """Computes PageRank of nodes based on the second-order aggregate network, 
    and aggregates PageRank values to obtain the PageRank of nodes in the
    first-order network.
    
    @param t: The temporalnetwork instance to work on
    @param model: either C{"SECOND"} or C{"NULL"}, where C{"SECOND"} is the 
      the default value.
    """

    assert model is "SECOND" or model is "NULL"
    assert projection is 'TARGET' or projection is 'SOURCE'

    name_map = Utilities.firstOrderNameMap(t)

    if model == 'SECOND':
        g2 = t.igraphSecondOrder()
    else:
        g2 = t.igraphSecondOrderNull()

    # Compute pagerank centrality in second-order network
    pagerank_2 = np.array(g2.pagerank(weights=g2.es()['weight'],
                                      directed=True))

    # Aggregate to obtain first-order pagerank centrality
    pagerank_1 = np.zeros(len(name_map))
    counts = np.array([1] * len(name_map))
    sep = t.separator
    for i in range(len(pagerank_2)):
        # Get name of target node
        if projection == 'TARGET':
            target = g2.vs()[i]["name"].split(sep)[1]
            pagerank_1[name_map[target]] += pagerank_2[i]
            counts[name_map[target]] += 1
        else:
            source = g2.vs()[i]["name"].split(sep)[0]
            pagerank_1[name_map[source]] += pagerank_2[i]
            counts[name_map[source]] += 1

    if normalization == True:
        pagerank_1 = pagerank_1 / counts

    return pagerank_1
Пример #19
0
def GetMinTemporalDistance(t, delta=1, collect_paths=True):
    """ Computes the minimum temporal distance between all pairs of nodes in 
        terms of time-respecting paths (using a given maximum time difference delta), 
        across all possible starting times in the temporal network

        @param t: the temporal network to calculate the distance for
        @param delta: the maximum waiting time to be used for the definition of time-respecting paths.
            Note that this is independent of the delta parameter set in the temporal networks instancd
           for the two-path extraction
        @param collect_paths: whether or not to return all shortest time-respecting paths. If False, only 
            shortest path distances will be returned.
    """

    Log.add('Computing minimum temporal distances for delta = ' +
            str(int(delta)) + ' ...')

    name_map = Utilities.firstOrderNameMap(t)

    minD = np.zeros(shape=(len(t.nodes), len(t.nodes)))
    minD.fill(np.inf)

    # Each node is connected to itself via a path of length zero
    np.fill_diagonal(minD, 0)

    minPaths = defaultdict(lambda: defaultdict(lambda: []))

    for start_t in t.ordered_times:
        D, paths = GetTemporalDistanceMatrix(t, start_t, delta, collect_paths)
        for v in t.nodes:
            for w in t.nodes:
                if D[name_map[v], name_map[w]] < minD[name_map[v],
                                                      name_map[w]]:
                    minD[name_map[v], name_map[w]] = D[name_map[v],
                                                       name_map[w]]
                    minPaths[v][w] = paths[v][w]
                elif D[name_map[v],
                       name_map[w]] == minD[name_map[v], name_map[w]] and minD[
                           name_map[v], name_map[w]] < np.inf:
                    for p in paths[v][w]:
                        if p not in minPaths[v][w]:
                            minPaths[v][w] = minPaths[v][w] + [p]
    Log.add('finished.')
    return minD, minPaths
Пример #20
0
def GetStaticPageRank(t, model='SECOND', projection='TARGET', normalization=False):
    """Computes PageRank of nodes based on the second-order aggregate network, 
    and aggregates PageRank values to obtain the PageRank of nodes in the
    first-order network.
    
    @param t: The temporalnetwork instance to work on
    @param model: either C{"SECOND"} or C{"NULL"}, where C{"SECOND"} is the 
      the default value.
    """

    assert model is "SECOND" or model is "NULL"
    assert projection is 'TARGET' or projection is 'SOURCE'

    name_map = Utilities.firstOrderNameMap( t )

    if model == 'SECOND':
        g2 = t.igraphSecondOrder()
    else:
        g2 = t.igraphSecondOrderNull()    

    # Compute pagerank centrality in second-order network
    pagerank_2 = np.array(g2.pagerank(weights=g2.es()['weight'], directed=True))
    
    # Aggregate to obtain first-order pagerank centrality
    pagerank_1 = np.zeros(len(name_map))
    counts = np.array([1]*len(name_map))
    sep = t.separator
    for i in range(len(pagerank_2)):
        # Get name of target node        
        if projection == 'TARGET':
            target = g2.vs()[i]["name"].split(sep)[1]
            pagerank_1[name_map[target]] += pagerank_2[i]
            counts[name_map[target]] +=1 
        else:
            source = g2.vs()[i]["name"].split(sep)[0]
            pagerank_1[name_map[source]] += pagerank_2[i]
            counts[name_map[source]] +=1 
    
    if normalization == True:
        pagerank_1 = pagerank_1 / counts
    
    return pagerank_1
Пример #21
0
def GetTemporalBetweennessInstantaneous(t, start_t=0, delta=1, normalized=False):
    """Calculates the temporal betweennness values of 
    all nodes fir a given start time start_t in an empirical temporal network t.
    This function returns a numpy array of (temporal) betweenness centrality values. 
    The ordering of these values corresponds to the ordering of nodes in the vertex 
    sequence of the igraph first order time-aggregated network. A mapping between node names
    and array indices can be found in Utilities.firstOrderNameMap().
    
    @param t: the temporal network for which temporal betweenness centralities will be computed
    @param start_t: the start time for which to consider time-respecting paths (default 0). This is 
        important, since any unambigious definition of a shortest time-respecting path between
        two nodes must include the time range to be considered (c.f. Holme and Saramäki, Phys. Rep., 2012)
    @param delta: the maximum waiting time used in the time-respecting path definition (default 1)
        Note that this parameter is independent from the delta used internally for the extraction of two-paths
        by the class TemporalNetwork
    @param normalized: whether or not to normalize the temporal betweenness centrality values by
    dividing by the number of all shortest time-respecting paths in the temporal network.
    """

    bw = np.array([0]*len(t.nodes))

    # First calculate all shortest time-respecting paths starting at time start_t
    D, paths = Paths.GetTemporalDistanceMatrix(t, start_t, delta, collect_paths=True)

    # Get a mapping between node names and matrix indices
    name_map = Utilities.firstOrderNameMap( t )

    # Compute betweenness scores of all nodes based on shortest time-respecting paths
    k=0
    for u in t.nodes:
        for v in t.nodes:
            if u != v:
                for p in paths[u][v]:
                    for i in range(1, len(p)-1):
                        bw[name_map[p[i][0]]] += 1
                        k+=1

    # Normalize by dividing by the total number of shortest time-respecting paths
    if normalized:
        bw = bw/k
    return bw
Пример #22
0
def GetSecondOrderDistanceMatrix(t, model='SECOND'):
    """Calculates a matrix D containing the shortest path lengths between all
    pairs of nodes calculated based on the topology of the *second-order* aggregate network. 
    The ordering of rows/columns corresponds to the ordering of nodes in the vertex sequence of 
    the igraph first order time-aggregated network. A mapping between nodes and indices can be 
    found in Utilities.firstOrderNameMap().    
    
    @param t: the temporal network to calculate shortest path lengths for based on a second-order
        aggregate representation 
    @param model: either C{"SECOND"} or C{"NULL"}, where C{"SECOND"} is the 
      the default value.   
    """

    if (model is "SECOND" or "NULL") == False:
        raise ValueError("model must be one of \"SECOND\" or \"NULL\"")

    name_map = Utilities.firstOrderNameMap(t)

    if model == 'SECOND':
        g2 = t.igraphSecondOrder()
    else:
        g2 = t.igraphSecondOrderNull()

    D = np.zeros(shape=(len(t.nodes), len(t.nodes)))
    D.fill(np.inf)
    np.fill_diagonal(D, 0)

    sep = t.separator

    for v in g2.vs()["name"]:
        source = v.split(sep)[0]
        for w in g2.vs()["name"]:
            target = w.split(sep)[1]
            X = g2.get_shortest_paths(v, w)
            for p in X:
                if len(p) > 0:
                    D[name_map[source], name_map[target]] = min(
                        len(p), D[name_map[source], name_map[target]])
    return D
Пример #23
0
def Laplacian(temporalnet, model="SECOND"):
    """Returns the transposed Laplacian matrix corresponding to the the second-order (model=SECOND) or 
    the second-order null (model=NULL) model for a temporal network.
    
    @param temporalnet: The temporalnetwork instance to work on
    @param model: either C{"SECOND"} or C{"NULL"}, where C{"SECOND"} is the 
      the default value.
    """
    if (model is "SECOND" or "NULL") == False:
        raise ValueError("model must be one of \"SECOND\" or \"NULL\"")

    if model == "SECOND":
        network = temporalnet.igraphSecondOrder().components(
            mode="STRONG").giant()
    elif model == "NULL":
        network = temporalnet.igraphSecondOrderNull().components(
            mode="STRONG").giant()

    T2 = Utilities.RWTransitionMatrix(network)
    I = sparse.identity(len(network.vs()))

    return I - T2
Пример #24
0
def GetMinTemporalDistance(t, delta=1, collect_paths=True):
    """ Computes the minimum temporal distance between all pairs of nodes in 
        terms of time-respecting paths (using a given maximum time difference delta), 
        across all possible starting times in the temporal network

        @param t: the temporal network to calculate the distance for
        @param delta: the maximum waiting time to be used for the definition of time-respecting paths.
            Note that this is independent of the delta parameter set in the temporal networks instancd
           for the two-path extraction
        @param collect_paths: whether or not to return all shortest time-respecting paths. If False, only 
            shortest path distances will be returned.
    """

    Log.add('Computing minimum temporal distances for delta = ' + str(int(delta)) + ' ...')

    name_map = Utilities.firstOrderNameMap( t )

    minD = np.zeros(shape=(len(t.nodes),len(t.nodes)))
    minD.fill(np.inf)

    # Each node is connected to itself via a path of length zero
    np.fill_diagonal(minD, 0)

    minPaths = defaultdict( lambda: defaultdict( lambda: [] ) )

    for start_t in t.ordered_times:
        D, paths = GetTemporalDistanceMatrix(t, start_t, delta, collect_paths)
        for v in t.nodes:
            for w in t.nodes:
                if D[name_map[v], name_map[w]] < minD[name_map[v], name_map[w]]:
                    minD[name_map[v], name_map[w]] = D[name_map[v], name_map[w]]
                    minPaths[v][w] = paths[v][w]
                elif D[name_map[v], name_map[w]] == minD[name_map[v], name_map[w]] and minD[name_map[v], name_map[w]] < np.inf:
                    for p in paths[v][w]:
                        if p not in minPaths[v][w]:
                            minPaths[v][w] = minPaths[v][w] + [p]
    Log.add('finished.')
    return minD, minPaths
Пример #25
0
def GetSecondOrderDistanceMatrix(t, model='SECOND'):
    """Calculates a matrix D containing the shortest path lengths between all
    pairs of nodes calculated based on the topology of the *second-order* aggregate network. 
    The ordering of rows/columns corresponds to the ordering of nodes in the vertex sequence of 
    the igraph first order time-aggregated network. A mapping between nodes and indices can be 
    found in Utilities.firstOrderNameMap().    
    
    @param t: the temporal network to calculate shortest path lengths for based on a second-order
        aggregate representation 
    @param model: either C{"SECOND"} or C{"NULL"}, where C{"SECOND"} is the 
      the default value.   
    """   

    if (model is "SECOND" or "NULL") == False:
        raise ValueError("model must be one of \"SECOND\" or \"NULL\"")

    name_map = Utilities.firstOrderNameMap( t )

    if model == 'SECOND':
        g2 = t.igraphSecondOrder()
    else:
        g2 = t.igraphSecondOrderNull()    

    D = np.zeros(shape=(len(t.nodes),len(t.nodes)))
    D.fill(np.inf)
    np.fill_diagonal(D, 0)

    sep = t.separator

    for v in g2.vs()["name"]:
        source = v.split(sep)[0]
        for w in g2.vs()["name"]:
            target = w.split(sep)[1]
            X = g2.get_shortest_paths(v,w)            
            for p in X:
                if len(p)>0:
                    D[name_map[source], name_map[target]] = min(len(p), D[name_map[source], name_map[target]])
    return D
Пример #26
0
def GetStaticBetweenness(t, model='SECOND'):
    """Computes betweenness centralities of nodes based on the second-order aggregate network, 
    and aggregates betweenness centralities to obtain the betweenness centrality of nodes in the 
    first-order network.
    
    @param t: The temporalnetwork instance to work on
    @param model: either C{"SECOND"} or C{"NULL"}, where C{"SECOND"} is the 
      the default value.
    """

    if (model is "SECOND" or "NULL") == False:
        raise ValueError("model must be one of \"SECOND\" or \"NULL\"")

    D = Paths.GetSecondOrderDistanceMatrix(t)
    name_map = Utilities.firstOrderNameMap(t)

    if model == 'SECOND':
        g2 = t.igraphSecondOrder()
    else:
        g2 = t.igraphSecondOrderNull()

    # Compute betweenness centrality based on second-order network
    bwcent_1 = np.zeros(len(name_map))
    sep = t.separator

    for v in g2.vs()["name"]:
        for w in g2.vs()["name"]:
            s = v.split(sep)[0]
            t = w.split(sep)[1]
            X = g2.get_all_shortest_paths(v, w)
            for p in X:
                if D[name_map[s], name_map[t]] == len(p) and len(p) > 1:
                    for i in range(len(p)):
                        source = g2.vs()["name"][p[i]].split(sep)[0]
                        if i > 0:
                            bwcent_1[name_map[source]] += 1
    return bwcent_1
Пример #27
0
def GetTemporalClosenessInstantaneous(t, start_t=0, delta=1):
    """Calculates the temporal closeness values of 
    all nodes for a given start time start_t in a temporal network t.
    This function returns a numpy array of (temporal) closeness centrality values. 
    The ordering of these values corresponds to the ordering of nodes in the vertex 
    sequence of the igraph first order time-aggregated network. A mapping between node names
    and array indices can be found in Utilities.firstOrderNameMap().
    
    @param t: the temporal network for which temporal closeness centralities will be computed
    @param start_t: the start time for which to consider time-respecting paths (default 0). This is 
        important, since any unambigious definition of a shortest time-respecting path between
        two nodes must include the time range to be considered (c.f. Holme and Saramäki, Phys. Rep., 2012)
    @param delta: the maximum time difference time used in the time-respecting path definition (default 1)
        Note that this parameter is independent from the delta used internally for the extraction of two-paths
        by the class TemporalNetwork.
    """

    closeness = np.array([0.] * len(t.nodes))

    # Calculate all shortest time-respecting paths
    D, paths = Paths.GetTemporalDistanceMatrix(t,
                                               start_t,
                                               delta,
                                               collect_paths=False)

    # Get a mapping between node names and matrix indices
    name_map = Utilities.firstOrderNameMap(t)

    # Calculate closeness for each node u, by summing the reciprocal of its
    # distances to all other nodes. Note that this definition of closeness centrality
    # is required for directed networks that are not strongly connected.
    for u in t.nodes:
        for v in t.nodes:
            if u != v:
                closeness[name_map[u]] += 1. / D[name_map[v], name_map[u]]

    return closeness
Пример #28
0
def GetStaticBetweenness(t, model='SECOND'):
    """Computes betweenness centralities of nodes based on the second-order aggregate network, 
    and aggregates betweenness centralities to obtain the betweenness centrality of nodes in the 
    first-order network.
    
    @param t: The temporalnetwork instance to work on
    @param model: either C{"SECOND"} or C{"NULL"}, where C{"SECOND"} is the 
      the default value.
    """

    if (model is "SECOND" or "NULL") == False:
        raise ValueError("model must be one of \"SECOND\" or \"NULL\"")

    D = Paths.GetSecondOrderDistanceMatrix(t)
    name_map = Utilities.firstOrderNameMap( t )

    if model == 'SECOND':
        g2 = t.igraphSecondOrder()
    else:
        g2 = t.igraphSecondOrderNull()

    # Compute betweenness centrality based on second-order network
    bwcent_1 = np.zeros(len(name_map))
    sep = t.separator

    for v in g2.vs()["name"]:
        for w in g2.vs()["name"]:
            s = v.split(sep)[0]
            t = w.split(sep)[1]
            X = g2.get_all_shortest_paths(v,w)
            for p in X:
                if D[name_map[s], name_map[t]] == len(p) and len(p) > 1:
                    for i in range(len(p)):
                        source = g2.vs()["name"][p[i]].split(sep)[0]
                        if i>0:
                            bwcent_1[name_map[source]] += 1 
    return bwcent_1
Пример #29
0
def EntropyGrowthRateRatio(t, mode='FIRSTORDER', method='MLE'):
    """Computes the ratio between the entropy growth rate ratio between
    the second-order and first-order model of a temporal network t. Ratios smaller
    than one indicate that the temporal network exhibits non-Markovian characteristics"""

    # NOTE to myself: most of the time here goes into computation of the
    # NOTE            EV of the transition matrix for the bigger of the
    # NOTE            two graphs below (either 2nd-order or 2nd-order null)

    assert method == 'MLE' or method == 'Miller'

    # Generate strongly connected component of second-order network
    g2 = t.igraphSecondOrder().components(mode="STRONG").giant()

    Log.add('Calculating entropy growth rate ratio ... ', Severity.INFO)

    # Compute entropy growth rate of observed transition matrix
    T2 = Utilities.RWTransitionMatrix(g2)
    T2_pi = Utilities.StationaryDistribution(T2)

    T2.data *= np.log2(T2.data)

    # For T2, we can apply a Miller correction to the entropy estimation
    if method == 'Miller':
        # K is the number of possible two-paths that can exist based on the
        # time-stamped edge sequence (or observed two paths)
        if len(t.tedges) > 0:
            edges = set((e[0], e[1]) for e in t.tedges)
        else:
            edges = [(tp[0], tp[1]) for tp in t.twopaths]
            for tp in t.twopaths:
                edges.append((tp[1], tp[2]))
            edges = set(edges)
        K = len(Utilities.getPossibleTwoPaths(edges))
        #print('K = ', K)
        # N is the number of observations used to estimate the transition probabilities
        # in the second-order network. This corresponds to the total link weight in the
        # second-order network (accounting for the fractional couting of two-paths)
        N = np.sum(g2.es["weight"])
        #print('N = ', N)
        H2 = np.sum(T2 * T2_pi) + (K - 1) / (2 * N)
    else:
        # simple MLE estimation
        H2 = -np.sum(T2 * T2_pi)

    H2 = np.absolute(H2)

    # Compute entropy rate of null model
    if mode == 'FIRSTORDER':
        g2n = t.igraphFirstOrder().components(mode="STRONG").giant()
    else:
        g2n = t.igraphSecondOrderNull().components(mode="STRONG").giant()

    # For the entropy rate of the null model, no Miller correction is needed
    # since we assume that transitions correspond to the true probabilities
    T2n = Utilities.RWTransitionMatrix(g2n)
    T2n_pi = Utilities.StationaryDistribution(T2n)
    T2n.data *= np.log2(T2n.data)
    H2n = -np.sum(T2n * T2n_pi)
    H2n = np.absolute(H2n)

    Log.add('finished.', Severity.INFO)

    # Return ratio
    return H2 / H2n
Пример #30
0
def exportDiffusionMovieFramesFirstOrder(t,
                                         file_prefix='diffusion',
                                         visual_style=None,
                                         steps=100,
                                         initial_index=-1,
                                         model='SECOND',
                                         dynamic=False,
                                         NWframesPerRWStep=5):
    """Exports an animation showing the evolution of a diffusion
           process on the first-order aggregate network, where random walk dynamics 
           either follows a first-order (mode='NULL') or second-order (model='SECOND') Markov 
           model"""
    assert model == 'SECOND' or model == 'NULL'

    g1 = t.igraphFirstOrder()

    if model == 'SECOND':
        g2 = t.igraphSecondOrder()
        temporal = tn.TemporalNetwork.ShuffleTwoPaths(t, l=steps)
    elif model == 'NULL':
        g2 = t.igraphSecondOrderNull()
        temporal = tn.TemporalNetwork.ShuffleEdges(t, l=steps)

    T = Utilities.RWTransitionMatrix(g2)

    # visual style is for *first-order* aggregate network
    if visual_style == None:
        visual_style = {}
        visual_style["vertex_color"] = "lightblue"
        visual_style["vertex_label"] = g1.vs["name"]
        visual_style["edge_curved"] = .5
        visual_style["vertex_size"] = 30
        visual_style["edge_color"] = ["darkgrey"] * g1.ecount()
        visual_style["edge_width"] = [.5] * g1.ecount()

    # Initial state of random walker
    if initial_index < 0:
        initial_index = np.random.randint(0, len(g2.vs()))

    exp = 1.0 / .75

    x = np.zeros(len(g2.vs()))
    x[initial_index] = 1

    # This index allows to quickly map node names to indices in the first-order network
    map_name_to_id = {}
    for i in range(len(g1.vs())):
        map_name_to_id[g1.vs()['name'][i]] = i

    # Index to quickly map second-order node indices to first-order node indices
    map_2_to_1 = {}
    for j in range(len(g2.vs())):
        # j is index of node in *second-order* network
        # we first get the name of the *target* of the underlying edge
        node = g2.vs()["name"][j].split(t.separator)[1]

        # we map the target of second-order node j to the index of the *first-order* node
        map_2_to_1[j] = map_name_to_id[node]

    # compute stationary state of random walk process
    pi = Utilities.StationaryDistribution(T)

    scale = np.mean(np.abs(x - pi))

    # lambda expression for the coloring of nodes according to some quantity p \in [0,1]
    # p = 1 ==> color red
    # p = 0 ==> color white
    color_p = lambda p: "rgb(255," + str(int(
        (1 - p) * 255)) + "," + str(int((1 - p) * 255)) + ")"

    visual_style["edge_color"] = ["darkgrey"] * g1.ecount()
    visual_style["edge_width"] = [.5] * g1.ecount()

    # Create video frames
    for i in range(0, steps):
        # based on visitation probabilities in *second-order* aggregate network,
        # we need to compute visitation probabilities of nodes in the *first-order*
        # aggregate network
        x_firstorder = np.zeros(len(g1.vs()))

        # j is the index of nodes in the *second-order* network, which we need to map
        # to nodes in the *first-order* network
        for j in range(len(x)):
            if x[j] > 0:
                x_firstorder[
                    map_2_to_1[j]] = x_firstorder[map_2_to_1[j]] + x[j]

        # Perform some reasonable color scaling
        visual_style["vertex_color"] = [
            color_p(np.power((p - min(x)) / (max(x) - min(x)), exp))
            for p in x_firstorder
        ]

        # Visualize illustrative network dynamics
        if dynamic == True:
            #slice = igraph.Graph(n=len(g1.vs()))
            #slice.vs["name"] = g1.vs["name"]
            L = len(temporal.ordered_times)
            for e in temporal.time[temporal.ordered_times[i % L]]:
                e_id = g1.get_eid(e[0], e[1])
                visual_style["edge_width"][e_id] = 5
                visual_style["edge_color"][e_id] = "black"
                #slice.add_edge(e[0], e[1])
            igraph.plot(g1, file_prefix + "_frame_" + str(i).zfill(5) + ".png",
                        **visual_style)
        else:
            igraph.plot(g1, file_prefix + "_frame_" + str(i).zfill(5) + ".png",
                        **visual_style)

        # Plot first-order aggregate network after 30 RW steps (particularly useful as poster frame of video)
        if i == 30:
            visual_style["edge_color"] = "black"
            visual_style["edge_width"] = 1
            igraph.plot(g1, file_prefix + "_network.pdf", **visual_style)

        # Reset edge color and width
        visual_style["edge_color"] = ["darkgrey"] * g1.ecount()
        visual_style["edge_width"] = [.5] * g1.ecount()

        if i % 50 == 0:
            Log.add('Frame ' + str(i) + '\tTVD = ' + str(Utilities.TVD(x, pi)))

        # every NWframesPerRWStep frames, perform one random walk step
        if i % NWframesPerRWStep == 0:
            # NOTE x * T = (T^T * x^T)^T
            # NOTE T is already transposed to get the left EV
            x = (T.dot(x.transpose())).transpose()
Пример #31
0
def BetweennessPreference(t, v, normalized=False, method='MLE'):
    """Computes the betweenness preference of a node v in a temporal network t
    
    @param t: The temporalnetwork instance to work on
    @param v: Name of the node to compute its BetweennessPreference
    @param normalized: whether or not (default) to normalize
    @param method: which entropy estimation method to use. The method supports 
        'Miller' for Miller-corrected MLE or 'MLE' for a naive MLE estimation. 
    """
    g = t.igraphFirstOrder()

    assert method == 'MLE' or method == 'Miller'

    # If the network is empty, just return zero
    if len(g.vs) == 0:
        return 0.0

    # First create the betweenness preference matrix (equation (2) of the paper)
    B_v = BWPrefMatrix(t, v)

    # Normalize matrix (equation (3) of the paper)
    # NOTE: P_v has the same shape as B_v
    P_v = np.zeros(shape=B_v.shape)
    S = np.sum(B_v)

    if S > 0:
        P_v = B_v / S

    ## Compute marginal probabilities
    ## Marginal probabilities P^v_d = \sum_s'{P_{s'd}}
    marginal_d = np.sum(P_v, axis=0)

    ## Marginal probabilities P^v_s = \sum_d'{P_{sd'}}
    marginal_s = np.sum(P_v, axis=1)

    if method == 'Miller':
        # print('v = ', v)
        v_vertex = g.vs.find(name=v)

        # total number of samples, i.e. observed two-paths
        N = np.sum(B_v)

        #print('N = ', N)
        #print('B = ', B_v)
        #print('marginal_s = ', marginal_s)
        #print('marginal_d = ', marginal_d)

        # marginal entropy H(S)
        H_s = Utilities.Entropy_Miller(marginal_s, len(marginal_s), N)
        # print('H(S) = ', H_s)

        # marginal entropy H(D)
        H_d = Utilities.Entropy_Miller(marginal_d, len(marginal_d), N)
        #print('H(D) = ', H_d)

        # we need the conditional entropy H(D|S)
        H_ds = 0

        for s in range(len(marginal_s)):
            # number of two paths s -> v -> * observed in the data
            N_s = np.sum(B_v[s, :])
            #print('N(s=' + str(s) + ') = ' +  str(N_s))

            # probabilities of all destinations, given the particular source s
            p_ds = B_v[s, :] / np.sum(B_v[s, :])
            #print('P(D|S=' + str(s) + ') = '+ str(p_ds))

            # number of possible destinations d
            K_s = len(p_ds)
            #print('K(s=' + str(s) + ') = ' +  str(K_s))

            # marginal_s[s] is the overall probability of source s
            p_s = marginal_s[s]

            # add to conditional entropy
            H_ds += p_s * Utilities.Entropy_Miller(p_ds, K_s, N_s)

        #print('H(D|S) = ', H_ds)

    else:
        # apply 'naive' MLE estimation, identical to above
        H_s = Utilities.Entropy(marginal_s)
        H_d = Utilities.Entropy(marginal_d)
        H_ds = 0
        for s in range(len(marginal_s)):
            p_ds = P_v[s, :] / np.sum(P_v[s, :])
            H_ds += marginal_s[s] * Utilities.Entropy(p_ds)

        # Alternative calculation (without explicit entropies)
        # build mask for non-zero elements
        # row, col = np.nonzero(P_v)
        # pv = P_v[(row,col)]
        # marginal = np.outer(marginal_s, marginal_d)
        # log_argument = np.divide( pv, marginal[(row,col)] )
        # I = np.dot( pv, np.log2(log_argument) )

    I = H_d - H_ds

    if normalized:
        I = I / np.min([H_s, H_d])

    return I
Пример #32
0
def GetTemporalDistanceMatrix(t, start_t=-1, delta=1, collect_paths=True):
    """A new and faster method to compute the (topologically) shortest time-respecting paths between 
    all pairs of nodes starting at time start_t in an empirical temporal network t.
    This function returns a tuple consisting of 
        1) a matrix D containing the shortest time-respecting path lengths between all
            pairs of nodes. The ordering of rows/columns corresponds to the ordering of nodes 
            in the vertex sequence of the igraph first order time-aggregated network. A
            mapping between nodes and indices can be found in Utilities.firstOrderNameMap().
        2) a list of shortest time-respecting paths, each entry being an ordered sequence 
            of nodes on the corresponding path.
    
    @param t: the temporal network to calculate shortest time-respecting paths for
    @param start_t: the start time for which to consider time-respecting paths (default is t.ordered_times[0])
    @param delta: the maximum time difference to be used in the time-respecting path definition (default 1).
        Note that this parameter is independent from the internal parameter delta used for two-path extraction
        in the class TemporalNetwork
    @param collect_paths: whether or not to collect all shortest time-respecting paths (default = True). If this is 
        set to False, the method will only compute the lengths of shortest time-respecting paths, but not return the actual 
        paths.
        """

    if start_t == -1:
        start_t = t.ordered_times[0]

    # Initialize dictionary taking shortest paths 
    Paths = defaultdict( lambda: defaultdict( lambda: [] ) )

    # Get a mapping between node names and matrix indices
    name_map = Utilities.firstOrderNameMap( t )

    # Initialize topological distance matrix
    # TODO: This may yield a memory problem for large graphs 
    D = np.zeros(shape=(len(t.nodes),len(t.nodes)))
    D.fill(np.inf)

    # For each node v, calculate shortest/fastest paths to all other nodes ... 
    for v in t.nodes:
        
        # Mark node v as visited at the start time start_t... 
        D[name_map[v], name_map[v]] = 0        
        Paths[v][v] = [ [(v,start_t)] ]

        stack = set([ (v, start_t) ])
        
        # While there are nodes, which could possibly continue a time-respecting path
        while len(stack)>0:

            (x,ts) = stack.pop()

            # Get indices of time range within which a time-respecting path via x 
            # can possibly be continued
            min_ix = bisect_left(t.activities[x], ts)
            max_ix = bisect_left(t.activities[x], ts+delta)-1

            # For all time-stamps at which x is a source node ... 
            for j in range(min_ix, max_ix+1):
                time = t.activities[x][j]

                # For all edges starting at node x at this time
                for e in t.sources[time][x]:

                    # We found a new node on a time-respecting path
                    new_node = (e[1], time+1)
                    
                    # This node can again continue time-respecting paths
                    # The set will take care that no duplicates are recorded
                    stack.add( new_node )

                    # Check whether we found a time-respecting path shorter than the current shortest one ... 
                    if D[name_map[v], name_map[e[1]]] > D[name_map[v], name_map[e[0]]] + 1:
                        
                        # In this case we update the distance matrix
                        D[name_map[v], name_map[e[1]]] = D[name_map[v], name_map[e[0]]] + 1

                        if collect_paths == True:
                            # Delete any previous shortest paths 
                            Paths[v][e[1]] = []

                            # Collect all paths to e[0] and concatenate with the current node e[1]
                            for p in Paths[v][e[0]]:
                                Paths[v][e[1]] = Paths[v][e[1]] + [p + [(e[1],time+1)]]

                    # We may also have found a path that has the same length as other shortest paths ...
                    elif collect_paths == True and D[name_map[v], name_map[e[1]]] == D[name_map[v], name_map[e[0]]] + 1:

                        # Collect all paths to e[0] and concatenate with the current node e[1]
                        for p in Paths[v][e[0]]:
                            Paths[v][e[1]] = Paths[v][e[1]] + [p + [(e[1],time+1)]]
        
    # The algorithm terminates as soon as it is impossible to continue any of the time-respecting paths
    return D, Paths
Пример #33
0
def GetTemporalDistanceMatrix(t, start_t=-1, delta=1, collect_paths=True):
    """A new and faster method to compute the (topologically) shortest time-respecting paths between 
    all pairs of nodes starting at time start_t in an empirical temporal network t.
    This function returns a tuple consisting of 
        1) a matrix D containing the shortest time-respecting path lengths between all
            pairs of nodes. The ordering of rows/columns corresponds to the ordering of nodes 
            in the vertex sequence of the igraph first order time-aggregated network. A
            mapping between nodes and indices can be found in Utilities.firstOrderNameMap().
        2) a list of shortest time-respecting paths, each entry being an ordered sequence 
            of nodes on the corresponding path.
    
    @param t: the temporal network to calculate shortest time-respecting paths for
    @param start_t: the start time for which to consider time-respecting paths (default is t.ordered_times[0])
    @param delta: the maximum time difference to be used in the time-respecting path definition (default 1).
        Note that this parameter is independent from the internal parameter delta used for two-path extraction
        in the class TemporalNetwork
    @param collect_paths: whether or not to collect all shortest time-respecting paths (default = True). If this is 
        set to False, the method will only compute the lengths of shortest time-respecting paths, but not return the actual 
        paths.
        """

    if start_t == -1:
        start_t = t.ordered_times[0]

    # Initialize dictionary taking shortest paths
    Paths = defaultdict(lambda: defaultdict(lambda: []))

    # Get a mapping between node names and matrix indices
    name_map = Utilities.firstOrderNameMap(t)

    # Initialize topological distance matrix
    # TODO: This may yield a memory problem for large graphs
    D = np.zeros(shape=(len(t.nodes), len(t.nodes)))
    D.fill(np.inf)

    # For each node v, calculate shortest/fastest paths to all other nodes ...
    for v in t.nodes:

        # Mark node v as visited at the start time start_t...
        D[name_map[v], name_map[v]] = 0
        Paths[v][v] = [[(v, start_t)]]

        stack = set([(v, start_t)])

        # While there are nodes, which could possibly continue a time-respecting path
        while len(stack) > 0:

            (x, ts) = stack.pop()

            # Get indices of time range within which a time-respecting path via x
            # can possibly be continued
            min_ix = bisect_left(t.activities[x], ts)
            max_ix = bisect_left(t.activities[x], ts + delta) - 1

            # For all time-stamps at which x is a source node ...
            for j in range(min_ix, max_ix + 1):
                time = t.activities[x][j]

                # For all edges starting at node x at this time
                for e in t.sources[time][x]:

                    # We found a new node on a time-respecting path
                    new_node = (e[1], time + 1)

                    # This node can again continue time-respecting paths
                    # The set will take care that no duplicates are recorded
                    stack.add(new_node)

                    # Check whether we found a time-respecting path shorter than the current shortest one ...
                    if D[name_map[v],
                         name_map[e[1]]] > D[name_map[v], name_map[e[0]]] + 1:

                        # In this case we update the distance matrix
                        D[name_map[v],
                          name_map[e[1]]] = D[name_map[v], name_map[e[0]]] + 1

                        if collect_paths == True:
                            # Delete any previous shortest paths
                            Paths[v][e[1]] = []

                            # Collect all paths to e[0] and concatenate with the current node e[1]
                            for p in Paths[v][e[0]]:
                                Paths[v][e[1]] = Paths[v][e[1]] + [
                                    p + [(e[1], time + 1)]
                                ]

                    # We may also have found a path that has the same length as other shortest paths ...
                    elif collect_paths == True and D[name_map[v], name_map[
                            e[1]]] == D[name_map[v], name_map[e[0]]] + 1:

                        # Collect all paths to e[0] and concatenate with the current node e[1]
                        for p in Paths[v][e[0]]:
                            Paths[v][e[1]] = Paths[v][e[1]] + [
                                p + [(e[1], time + 1)]
                            ]

    # The algorithm terminates as soon as it is impossible to continue any of the time-respecting paths
    return D, Paths