Skip to content

jefish003/NetworkFragility

Repository files navigation

NetworkFragility

Code for "How fragile is your network...." By Jeremie Fish, Mahesh Banavar and Erik Bollt

There are two files containing a number of functions meant to make it easy and convenient to estimate the fragility of various networks, including Barabasi-Albert (BA), Erdos-Renyi (ER) and Watts-Strogatz (WS) networks. We will walk through the Examples.py file which is also provided. Additionally a file that contains an example of how to run all of the methods is uploaded, in this case the example is performed on the Florida ecological network. Below we will work through other examples of how to walk thorugh the code.

First we will start by importing all of the functions we need to estimate fragility and for saving purposes.

from NetworkFragilityClean import fragile_net
from RunNetworkFragilityClean import run_sparse_fn,run_sparse_worw
import scipy.io as IO
import networkx as nx
import numpy as np
from datetime import datetime 

Now we can get started. First get the setup out of the way:

Today = datetime.today().strftime('%d-%m-%Y')
#Setup the net
FN = fragile_net()
#Number of trials
NumTrials = 50 #This will be ignored if the network is the mall network...

#Network parameters
n = 100

## Set delta (between 1/n and (n-1)/n) ##
delta = 0.5 #This corresponds to a paramter c = 50
c = 50 #Since this represents delta = 0.5

#Barabasi-Albert parameter
m = 4
#Note the this is the total number of edges of Barabasi-Albert graph
# with n =100, m = 4
total_edges = 384

#Watts-Strogatz parameters
k = 8
p = 0.25

Types = ['BA', 'ER', 'WS', 'NorthMall']
NetworkType = Types[3]

Notice that in the above section we chose what we want delta to be. Since delta is directly related to c, we can also supply c very easily. Remember that delta is the ratio of the number of nodes in the Largest Connected Component (LCC) to the number of nodes in the network. Also the NorthMall graph is supplied as a mat file. We will finish out the setup now.

if NetworkType =='BA':
    G = nx.barabasi_albert_graph(n,m)
    
elif NetworkType=='ER':
    A = FN.exact_erdos_renyi_graph(n,total_edges)
    G = nx.Graph(A)
    
elif NetworkType =='WS':
    G = nx.watts_strogatz_graph(n,k,p)
    Diff = len(G.edges())-total_edges
    for i in range(Diff):
        Arr = np.array(list(G.edges()))
        np.random.shuffle(Arr)
        G.remove_edge(Arr[0,0],Arr[0,1])
elif NetworkType =='NorthMall':
    LM = IO.loadmat('Amatrix_northmall.mat')
    A = LM['A1']
    G = nx.Graph(A)   

Now we will start destroying the network and estimating the fragility

if NetworkType == 'NorthMall':
    i = 0
    Fragilities = np.zeros(1) #Set the fragilities array
    Output = run_sparse_fn(G,delta,NetworkType+'Net_FullRemoval'+str(i))#
    np.savez(NetworkType+'Net_FullRemoval_' +str(i) + '_Edge' + Today+ '.npz',Output)

Yes it is that easy to destroy the network. Some things will print to the screen as a sanity check since for large networks this can take a really long time to run. We are not done however, we have yet to estimate the fragility, fortunately there is code provided for this as well.

##############################################################
    ##       Fragility estimation step                          ##
    ##############################################################
    FN = fragile_net()
    
    #We must check both the minimum degree attack strategy and the edge betweenness
    #strategy because for different networks and even different values of delta
    #one is typically better than the other.
    MDDict = Output['MinDegree']
    #Grab the final graph after the edges have been removed
    G2 = nx.Graph(MDDict['Graphs'][-1])
    
    #Add the original graph and the final graph to compute fragility
    FN.add_graph(G)
    FN.add_graph_new(G2)
    
    #Complete the final step of the algorithm (so far we have only done the 
    #first two steps not the part for adding edges back which do not affect the
    #LCC) c was defined above based on delta
    G2 = FN.sparse_iterative_add_back(c)
    #Fraction of edges which have been removed from G
    MDFrac = (len(G.edges())-len(G2.edges()))/len(G.edges())
    FN.compute_Fragility(len(G.nodes()),c,MDFrac)
    #The estimate for the minimum degree attack strategy
    MDEstimatedFrag = FN.return_Fragility() 

Now the fragility has been estimated using rewiring and the minimum degree attack method. Note that the third step was implemented in the FN.sparse_iterative_add_back method, which completes the algorithm. However as noted in the paper we are not done for estimating fragility, since this attack method only works well for certain networks and certain deltas. The other attack method used in the paper is the edge betweenness attack method and we implement that method with rewiring and the iterative_add_back algorithm.

  #Reset FN to eliminate graphs
    FN = fragile_net()
    
    EBDict = Output['EdgeBetweenness']
    #Grab the final graph after the edges have been removed
    G2 = nx.Graph(EBDict['Graphs'][-1])
    
    #Add the original graph and the final graph to compute fragility
    FN.add_graph(G)
    FN.add_graph_new(G2)
    
    #Complete the final step of the algorithm (so far we have only done the 
    #first two steps not the part for adding edges back which do not affect the
    #LCC) c was defined above based on delta
    G2 = FN.sparse_iterative_add_back(c)
    #Fraction of edges which have been removed from G
    EBFrac = (len(G.edges())-len(G2.edges()))/len(G.edges())
    FN.compute_Fragility(len(G.nodes()),c,EBFrac)
    #The estimate for the edge betweenness attack strategy
    EBEstimatedFrag = FN.return_Fragility()

Now we have completed the process for both the minimum degree and edge betweenness attack methods. To get the final estimate of fragility we need to take the maximum between the two (i.e the minimum number of edges which needed to be removed).

#Now we can get the true estimated fragility
    Fragility = np.max(np.array([MDEstimatedFrag,EBEstimatedFrag])) #This assumes we are not using pymetis -- pymetis would need to be included in the max if we are using it.
    Fragilities[i] = Fragility
    print("This is the estimated fragility: ", Fragility)

Note that these steps have been computed with rewiring. However if we wish we can also figure out which set of edges would be removed without rewiring as is done below.

#NOTE THAT THIS IS HOW FRAGILITY WAS ESTIMATED, HOWEVER ONE COULD LOOK AT
    #THE SITUATION WITHOUT REWIRING, THIS IS DONE BELOW
    #If desired this can be compared to the results without rewiring as part
    #of the process...
    Output2 = run_sparse_worw(G,delta,NetworkType+'Net_WORW_FullRemoval'+str(i))
    np.savez(NetworkType+'Net_WORW_FullRemoval_' +str(i) + '_Edge' + Today+ '.npz',Output2)

And to run pymetis (note you will have to install pymetis, and can use pip to do so)

import numpy as np
import networkx as nx
#import pygraphviz as pgv
from NetworkFragility import fragile_net
from RunNetworkFragility import run_fragile_net
from datetime import datetime
import pymetis
from datetime import datetime
import sys
import scipy.io as IO

Today = datetime.today().strftime('%d-%m-%Y')
if sys.platform == 'win32':
    Prefix = 'C:/Users/jefis/'
   
else:
    Prefix = '/home/jeremie/'
    

#Delta = 0.5 ....
n = 133

FN = fragile_net()
Complete = FN.frac_comp(n, 66)

MatFile = IO.loadmat(Prefix+'Dropbox/jeremie_erik/jeremie_erik_mahesh/WriteUps/Data/Networks/Amatrix_northmall.mat')
A = MatFile["A1"]
G = nx.Graph(A)


A_list1 = []

for j in range(len(G)):
    A_list1.append(np.array(list(G[j])))    

NumEdges,PartLabels = pymetis.part_graph(2,A_list1)

np.savez("DataForPYMETIS_MALLNETWORK_ONLY_0p5_"+ "_date_"+Today+".npz",NumEdges,PartLabels)

And using Edge Collective Influence (NOTE You must download the Edge Collective Influence code which can be found here: https://github.com/PPNew1/Edge_Collective_Influence/tree/main)

from EdgeCollectiveInfluence import *
from DualCompetitivePercolation import *
import matplotlib.pyplot as plt
from NetworkFragilityClean import fragile_net
from igraph import Graph
import networkx as nx

def nx_to_igraph(G_nx):
    """
    Convert an undirected NetworkX graph to an igraph Graph.
    Node IDs are stored in the 'name' attribute.
    """
    # Get nodes and edges
    nodes = list(G_nx.nodes())
    edges = [(nodes.index(u), nodes.index(v)) for u, v in G_nx.edges()]

    # Create igraph graph
    G_ig = Graph(n=len(nodes), edges=edges, directed=False)
    G_ig.vs["name"] = nodes  # preserve original node IDs    G_ig.vs["name"] = nodes  # preserve original node IDs
    return G_ig


G = nx.barabasi_albert_graph(500,4)
total_edges = 1984
Diff = len(G.edges())-total_edges
#remove the extra edges
for i in range(Diff):
    Arr = np.array(list(G.edges()))
    np.random.shuffle(Arr)
    G.remove_edge(Arr[0,0],Arr[0,1])

g = nx_to_igraph(G)
res=IECIR(g,p=0.5)
FN = fragile_net()
FN.compute_Fragility(500,250,len(res[2][2])/g.ecount())
print(FN.return_Fragility())

That is it! The rest of the Examples.py file simply does all of these steps, but for the other types of networks (BA,ER and WS).

About

Code for "How fragile is your network...."

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages