Skip to content

Commit

Permalink
new push
Browse files Browse the repository at this point in the history
  • Loading branch information
Antonio committed Sep 13, 2022
1 parent 630710a commit 19007be
Show file tree
Hide file tree
Showing 9 changed files with 302 additions and 181 deletions.
18 changes: 9 additions & 9 deletions config.ini
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
[model]
graph = VD
nodes = [64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768]
degree = [2, 3, 4]
tolerance = [1.5, 2, 3]
edge_node_falling_probability = [0.9, 0.8, 0.7, 0.6, 0.5, 0.4, 0.3, 0.2, 0.1, 0.06, 0.05, 0.02, 0.005, 0.002, 9e-05, 9e-06, 1e-07, 0.0]
graph = ED
nodes = [512]
degree = [4]
tolerance = [1.5]
edge_node_falling_probability = [0.1]
nodes_poisson_rate = None

[simulations]
flooding_protocol = True
simultaions_number = 30
only_spectral_properties = False
flooding_protocol = False
simultaions_number = 1
only_spectral_properties = True
offline_simulation = False

[other_parameters]
gpu_optimization = False
epsilon = 0.005
max_iterations = 100
max_iterations = 50

[output_path]
outputpath = ./Outputs/EdgeDynamics
Expand Down
28 changes: 22 additions & 6 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

## General info
This python package is a dynamic random graphs generator. It allows defining bounded degree dynamic undirected networks i.e., evolving graphs in which each node has bounded degree.

### What is a Dynamic Random Graph?
A dynamic graph is a sequence of graphs <img src="https://bit.ly/35x8Ux6" align="center" border="0" alt="\mathcal{G} =\{G_t = (V_t, E_t) \,:\, t \in \mathbb{N}\}" width="219" height="18" />
where the sets of nodes and edges
Expand All @@ -37,37 +38,52 @@ It is possible to define " external events " that causes the edge or node disapp
### P2P
- **RAES** ( Request a link, then Accept if Enough Space ) proposed by Becchetti et al.
( [More details](https://arxiv.org/abs/1811.10316) ) that allows you to define an Expander Graph in **O**(log n) rounds With High Probability.
- **Edge Dynamic** proposed by Antonio Cruciani and Francesco Pasquale ([More details](link_paper) that is a natural extension of RAES where there exists the probability **p** that, at each time step, each edge of the graph can fall. This is an infinite stochastic process that allow you to construct a good dynamic Expander Graph.
- **Vertex Dynamic** proposed by Antonio Cruciani and Francesco Pasquale ([More details](link_paper) that is a Dynamic Random Graph where at each time step there are new vertices that join the network and vertices that leave it. This is an infinite stochastic process that allow you to construct a good dynamic Expander Graph.
- **Edge Dynamic (ERAES)** proposed by Antonio Cruciani and Francesco Pasquale that is a natural extension of RAES where there exists the probability **p** that, at each time step, each edge of the graph can fall. This is an infinite stochastic process that allow you to construct a good dynamic Expander Graph.
- **Vertex Dynamic (VRAES)** proposed by Antonio Cruciani and Francesco Pasquale that is a Dynamic Random Graph where at each time step there are new vertices that join the network and vertices that leave it. This is an infinite stochastic process that allow you to construct a good dynamic Expander Graph.
- **Mixed Dynamic (EVRAES) proposed by Antonio Cruciani and Francesco Pasquale. This is a Dynamic Graph model in which nodes and edges appear and disappear. Nodes follow the same rules of VRAES and the edge disappearance phenomenon follows the ERAES scheme.

## How to define and execute a model
Given the **config.ini** file set up the model that you want to simulate
Given the **config.ini** file you can set up the the experiments for ERAES and VRAES.

To simulate the EVRAES you can run ```python mixedDynamic.py```. Such simulation is multiprocessing and exploits all your cores. If you want to change the parameters you need to edit the ```mixedDynamic.py``` file.

### Defining the model
### Defining ERAES/VRAES
Modify the **.ini** file under the section: **[model]**

* **graph** use **ED** to define an Edge Dyanamics or **VD** to define a Vertex Dynamic model

* **nodes** is the number of nodes of the dynamic random graph. Must be a list of nodes, if more than one integer is in such list the tool will simulate sequentially the model with all the nodes in such list.

* **degree** is the "target\min" degree that each node must have. Must be a list of integers, if more than one integer is in such list the tool will simulate sequentially the model with all the degrees in such list.

* **tolerance** is the max degree that each node can have. Can be a list of integers or floats, if more than one value is in such list the tool will simulate sequentially the model with all the tolerances in such list.

* **edge_node_falling_probability** is the edge-disappearance probability at each round in case of the Edge Dynamics or the node-exiting probability in case of the Vertex Dynamics. Is a list of floats, must be between 0 and 1, if more than one value is in such list the tool will simulate sequentially the model with all the probabilities in such list.

* **nodes_poisson_rate** is the node-joining rate of the Poisson Process of the Vertex Dynamics. If it is equal to **None** the tool will use as rates the product of the number of nodes **n** and the exiting probabilities **q**. Is a list of floats, if more than one value is in such list the tool will simulate sequentially the model with all the probabilities in such list.

### Simulations parametes
Modify the **.ini** file under the section: **[simulations]**

* **flooding_protocol** If **True** the protocol will simulate the Flooding process.

* **simultaions_number** Number of simulation that you want to perform for each fixed model. Must be an integer

* **only_spectral_properties** If **True** simulate the model saving only the Spectral Gaps at each round.

* **offline_simulation** If **True** saves as a file the adjacency list of the Dynamic Graph before and after the execution of the distributed protocol. **WARNING:** It performs a high amount of file writings on the disk.

### Other parameters
Modify the **.ini** file under the section: **[other_parameters]**
* **gpu_optimization** If **True** utilizes your GPU to calculate the eigenvalues of the adjacency matrix of the dynamic graph at each round. **NOTE:** If you do not have cuda, switch branch to **CPU** in which this option is always **False** and there is no need to install cuda and cupy packages.

* **epsilon** Float value for the epsilon used by the convergence heuristic in the Edge Dynamic model. **NOTE:** the Edge Dynamic automatically estimate the epsilon value.

* **max_iterations** Int value for the maximum number of iteration of the models after their convergence (if you are not simulating the Offline version) or number of steps to perform (if you are simulating the Offline version).

### Output path
Modify the **.ini** file under the section: **[output_path]**
* **output_path** Output path of the simulations

* **output_path** Output path of the simulations, for EVRAES is ./tmp

### Advanced parameters
Modify the **.ini** file under the section: **[advanced]**. Commands for simulating the model on a range of values, every command follows the pattern **< min_param , max_param, step_param >** where:
Expand Down
7 changes: 0 additions & 7 deletions requirements_master_branch.txt

This file was deleted.

109 changes: 62 additions & 47 deletions src/Algorithms/EdgeDynamic.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,16 @@
from src.FileOperations.WriteOnFile import create_file,create_folder,write_on_file_contents
from src.Graphs.Objects.Queue import Queue
from src.StastModules.Snapshot import get_snapshot
from src.StastModules.SpectralAnalysis import get_spectral_gap_transition_matrix,spectral_gap_sparse
from src.StastModules.SpectralAnalysis import spectral_gap_sparse,spectral_gap
import networkx as nx
import time
import math as mt
import os
import logging
from itertools import count
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
import numpy as np
logging.basicConfig(format='%(levelname)s:%(message)s', level=logging.DEBUG)

class EdgeDynamicOutput:
Expand All @@ -27,24 +31,21 @@ class EdgeDynamic:

def __init__(self,d ,c,p,n ,outpath ,flooding = True, epsilon = 0.05 ,model ="Multiple",simNumber = 30 ,maxIter = 100,onlySpectral = False,Offline =False,GPU = False):
"""
:param d: int, minimum degree that each node must have
:param c: float, tolerance constant, c*d is the maximum degree that each node can have
:param p: float, edge-falling probability
:param n: int, number of node in the dynamic graph
:param outpath: str, output path for the results
:param flooding: bool, if True, simulates the flooding process
:param epsilon: float, epsilon value for the heuristic convergence. NOTE: The algorithm will compute the best value anyway
:param model: str, if Multiple each node will sample more than one node at each round, NOTE: Leave it as Multiple
:param simNumber: int, number of experiments to perfom
:param maxIter: int, maximum number of steps for the simulations
:param onlySpectral: bool, if true will save only the spectral properties of the graph
:param Offline: bool, if true, will simulate the model saving the adjacency list of the model at each time step without computing any statistic
:param GPU: bool, if true, will be used the GPU instead of the CPU for solving the eigen-problem
"""



:param d: int, minimum degree that each node must have
:param c: float, tolerance constant, c*d is the maximum degree that each node can have
:param p: float, edge-falling probability
:param n: int, number of node in the dynamic graph
:param outpath: str, output path for the results
:param flooding: bool, if True, simulates the flooding process
:param epsilon: float, epsilon value for the heuristic convergence. NOTE: The algorithm will compute the best value anyway
:param model: str, if Multiple each node will sample more than one node at each round, NOTE: Leave it as Multiple
:param simNumber: int, number of experiments to perfom
:param maxIter: int, maximum number of steps for the simulations
:param onlySpectral: bool, if true will save only the spectral properties of the graph
:param Offline: bool, if true, will simulate the model saving the adjacency list of the model at each time step without computing any statistic
:param GPU: bool, if true, will be used the GPU instead of the CPU for solving the eigen-problem
"""
self.d_list = d
self.c_list = c
self.p_list = p
Expand All @@ -59,7 +60,7 @@ def __init__(self,d ,c,p,n ,outpath ,flooding = True, epsilon = 0.05 ,model ="Mu
self.max_iter = maxIter
self.GPU = GPU
self.epsilon_steps = 25

#logging.debug("MAX ITER = %r"%(self.max_iter))
def run(self):
logging.info("----------------------------------------------------------------")
logging.info("Starting simulation")
Expand All @@ -80,7 +81,7 @@ def run(self):
for c in self.c_list:
logging.info(
"Number of nodes: %r Falling probability: %r Flooding: %r d: %d c: %r" % (n, p, self.flooding, d,c))
if (p != 0):
if (p != 0 and (not self.spectrumSim) and (not self.MC) and False):
logging.info(" Inferring epsilon , please wait")
epsilon = self.get_epsilon(d, c, p, n)
else:
Expand Down Expand Up @@ -180,23 +181,17 @@ def spectral_convergence( epsilon, spectral_gap, spectral_queue):
while(repeat):
G.add_phase()
G.del_phase()
if(self.GPU):
IsinvertibleBefore, spectralGapBefore, lambdaNGapBefore = get_spectral_gap_transition_matrix(G.get_G())

else:
spectralGapBefore = spectral_gap_sparse(G.get_G())
spectralGapBefore = spectral_gap_sparse(G.get_G())

spectral_bef = {
"spectralGapBefore": spectralGapBefore
}
if (p != 0):

G.random_fall()
#if(G.get_converged() == False):
if(self.GPU):
IsinvertibleAfter, spectralGapAfter, lambdaNGapAfter = get_spectral_gap_transition_matrix(G.get_G())
else:
spectralGapAfter = spectral_gap_sparse(G.get_G())

spectralGapAfter = spectral_gap_sparse(G.get_G())

spectral_aft = {
"spectralGap":spectralGapAfter
Expand All @@ -218,7 +213,6 @@ def spectral_convergence( epsilon, spectral_gap, spectral_queue):
}

repeat = False
# final_stats.append({**sim, **stats_bef,**stats_aft, **stats, **flood_dictionary})
final_stats.append({**sim, **stats,**spectral_bef,**spectral_aft, **flood_dictionary})
logging.info("The graph is not connected, flooding will always fail")
logging.info("Exiting")
Expand Down Expand Up @@ -285,6 +279,8 @@ def flood():
G = DynamicGraph(n, d, c, falling_probability=p, model=self.model)

while (repeat):
#logging.debug("ITERATION = %r"%(t))

G.add_phase()
G.del_phase()

Expand Down Expand Up @@ -334,14 +330,21 @@ def EdgeDynamicGeneratorMCConfigurations(self,d,c,p,n,path,sim):
return (-1)
G = DynamicGraph(n, d, c, falling_probability = p,model = self.model)
repeat = True

logging.info("OFFLINE MODE")
try:
# Create sim Directory
os.mkdir(path +"/"+ str(sim))
logging.info("Directory %r sim Created "%(path))
except FileExistsError:
logging.error("Directory %r sim already exists"%(path))

try:
# Create sim Directory
os.mkdir(path + "/"+str(sim) + "/beforeA")
logging.info("Directory %r sim/before Created "%(path))
except FileExistsError:
logging.error("Directory %r sim/before already exists"%(path))

try:
# Create sim Directory
os.mkdir(path + "/"+str(sim) + "/before")
Expand All @@ -355,8 +358,12 @@ def EdgeDynamicGeneratorMCConfigurations(self,d,c,p,n,path,sim):
except FileExistsError:
logging.error("Directory %r sim/after already exists"%(path))
stats = {"d":d,"c":c,"n":n,"p":p}
nx.write_adjlist(G.get_G(), path=path + "/" + str(sim) +"_" + str(t) + ".adjlist")

while (repeat):
G.add_phase()
nx.write_adjlist(G.get_G(), path=path +"/"+ str(sim) + "/beforeA/" + str(t) + ".adjlist")

G.del_phase()
nx.write_adjlist(G.get_G(), path=path +"/"+ str(sim) + "/before/" + str(t) + ".adjlist")

Expand Down Expand Up @@ -388,6 +395,7 @@ def spectral_convergence( epsilon, spectral_gap, spectral_queue):
spectral_differences = []
s_q = spectral_queue.get_queue()
for i in range(0, spectral_queue.get_queue_lenght() - 1):

spectral_differences.append(abs(s_q[i] - s_q[i + 1]))
terminate = True
for j in spectral_differences:
Expand All @@ -414,41 +422,47 @@ def spectral_convergence( epsilon, spectral_gap, spectral_queue):
spectral_queue = Queue(mt.log(n, 2))
G = DynamicGraph(n, d, c, falling_probability = p,model = self.model)
c = 0


while(repeat ):

G.add_phase()
G.del_phase()
if(self.GPU):
IsinvertibleBefore, spectralGapBefore, lambdaNGapBefore = get_spectral_gap_transition_matrix(G.get_G())
else:
spectralGapBefore = spectral_gap_sparse(G.get_G())

#spectralGapBefore,lambda1Before,lambda2Before = spectral_gap(G.get_G())
spectralGapBefore = spectral_gap(G.get_G())

stats_bef = {
"spectralGapBefore": spectralGapBefore,
}

if (p != 0):
G.random_fall()
if (self.GPU):
IsinvertibleAfter, spectralGapAfter, lambdaNGapAfter = get_spectral_gap_transition_matrix(G.get_G())
else:
spectralGapAfter = spectral_gap_sparse(G.get_G())

#spectralGapAfter,lambda1After,lambda2After = spectral_gap(G.get_G())
spectralGapAfter = spectral_gap(G.get_G())

stats_aft = {
"spectralGapAfter": spectralGapAfter,
}

stats = get_snapshot(G, p, G.get_d(), G.get_c(), t)

if (G.get_converged() == False):
spectral_queue = spectral_convergence( epsilon, stats_aft, spectral_queue)
spectral_queue = spectral_convergence( epsilon, spectralGapAfter, spectral_queue)
if (G.get_converged()):

if(c == self.max_iter):
repeat = False
else:
c+=1

if (t == self.max_iter):
repeat = False
final_stats.append({**sim, **stats_bef,**stats_aft, **stats})

#logging.debug("SPECTRAL BEFORE %r SPECTRAL AFTER %r"%(spectralGapBefore,spectralGapAfter))
t+=1
logging.info("Simulation %r | Step %r/%r | Spectral Gap converged? %r"%(sim['simulation'],t,self.max_iter,G.get_converged()))

return (final_stats)


Expand All @@ -462,10 +476,8 @@ def get_epsilon(self,d,c,p,n):
G.add_phase()
G.del_phase()
G.random_fall()
if(self.GPU):
Isinvertible, spectralGap, lambdaNGap = get_spectral_gap_transition_matrix(G.get_G())
else:
spectralGap = spectral_gap_sparse(G.get_G())

spectralGap = spectral_gap(G.get_G())
spectral_list.append(spectralGap)
t+=1
filtered = list(filter(lambda x: (x != 0), spectral_list))
Expand All @@ -481,4 +493,7 @@ def get_epsilon(self,d,c,p,n):
def write_info_dic_as_csv(self, outPath, results):
create_file(outPath, list(results.get_stats()[0][0].keys()))
for i in results.get_stats():
write_on_file_contents(outPath, i)
write_on_file_contents(outPath, i)



Loading

0 comments on commit 19007be

Please sign in to comment.