diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..0858471 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,12 @@ +sudo: true +language: python + +python: + - "2.7" + - "3.6" + - "3.7-dev" # 3.7 development branch + +install: + +script: + - "python setup.py install" diff --git a/ComplexNetworkSim/__init__.py b/ComplexNetworkSim/__init__.py index 79d324e..7333c2d 100644 --- a/ComplexNetworkSim/__init__.py +++ b/ComplexNetworkSim/__init__.py @@ -1,4 +1,4 @@ -from agents import NetworkAgent -from simulation import NetworkSimulation, Sim -from plotting import PlotCreator -from animation import AnimationCreator \ No newline at end of file +from .agents import NetworkAgent +from .simulation import NetworkSimulation, Sim +from .plotting import PlotCreator +from .animation import AnimationCreator \ No newline at end of file diff --git a/ComplexNetworkSim/agents.py b/ComplexNetworkSim/agents.py index c4a52d0..c85d576 100644 --- a/ComplexNetworkSim/agents.py +++ b/ComplexNetworkSim/agents.py @@ -1,123 +1,123 @@ -''' -Module for the NetworkAgent class that can be subclassed by agents. - -@author: Joe Schaul -''' - -from SimPy import Simulation as Sim -import networkx as nx -import random - -SEED = 123456789 - -ADD_EDGE = "add edge" -REMOVE_EDGE = "remove edge" -ADD_NODE = "add node" -REMOVE_NODE = "remove node" - - -class NetworkAgent(Sim.Process): - '''NetworkAgent class that can be subclassed by agents. ''' - - #class variables, shared between all instances of this class - r = random.Random(SEED) - TIMESTEP_DEFAULT = 1.0 - - def __init__(self, state, initialiser, stateVector=[], name='network_process', **stateParameters): - Sim.Process.__init__(self, name) - self.state = state - self.stateVector = stateVector - self.stateParameters = stateParameters - self.initialize(*initialiser) - - - def initialize(self, id, sim, globalTopo, globalParams): - ''' this gets called automatically ''' - self.id = id - self.sim = sim - self.globalTopology = globalTopo - self.globalSharedParameters = globalParams - - def getAllNodes(self): - return self.globalTopology.nodes() - - def getAllAgents(self, state=None): - neighs = self.getAllNodes() - if state is not None: - return [self.globalTopology.node[n]['agent'] for n in neighs - if self.globalTopology.node[n]['agent'].state == state] - else: - return [self.globalTopology.node[n]['agent'] for n in neighs] - - - - def getNeighbouringAgents(self, state=None): - ''' returns list of neighbours, but as agents, not nodes. - so e.g. one can set result[0].state = INFECTED ''' - neighs = self.globalTopology.neighbors(self.id) - if state is not None: - return [self.globalTopology.node[n]['agent'] for n in neighs - if self.globalTopology.node[n]['agent'].state == state] - else: - return [self.globalTopology.node[n]['agent'] for n in neighs] - - def getNeighbouringAgentsIter(self, state=None): - '''same as getNeighbouringAgents, but returns generator expression, - not list. - ''' - neighs = self.globalTopology.neighbors(self.id) - if state is not None: - return (self.globalTopology.node[n]['agent'] for n in neighs - if self.globalTopology.node[n]['agent'].state == state) - else: - return (self.globalTopology.node[n]['agent'] for n in neighs) - - def getNeighbouringNodes(self): - ''' returns list of neighbours as nodes. - Call self.getAgent() on one of them to get the agent.''' - return self.globalTopology.neighbors(self.id) - - def getAgent(self, id): - '''returns agent of specified ID.''' - return self.globalTopology.node[id]['agent'] - - def addNewNode(self, state): - #add & activate new agent - return self.sim.addNewNode(state) - - #add a random edge - #u = NetworkAgent.r.choice(self.globalTopology.nodes()) - #self.globalTopology.add_edge(u, id) - - def die(self): - self.removeNode(self.id) - - def removeNode(self, id): -# cancel ? self.getAgent(id) - self.globalTopology.remove_node(id) - - def removeEdge(self, node1, node2): - - self.globalTopology.remove_edge(self.id, self.currentSupernodeID) - self.logTopoChange(REMOVE_EDGE, node1, node2) - - - def addEdge(self, node1, node2): - self.globalTopology.add_edge(self.id, self.currentSupernodeID) - self.logTopoChange(ADD_EDGE, node1, node2) - - - def logTopoChange(self, action, node, node2=None): - #TODO: test, add this to netlogger... - print action, node, node2 - - - - - - - - - - +''' +Module for the NetworkAgent class that can be subclassed by agents. + +@author: Joe Schaul +''' + +from SimPy import Simulation as Sim +import networkx as nx +import random + +SEED = 123456789 + +ADD_EDGE = "add edge" +REMOVE_EDGE = "remove edge" +ADD_NODE = "add node" +REMOVE_NODE = "remove node" + + +class NetworkAgent(Sim.Process): + '''NetworkAgent class that can be subclassed by agents. ''' + + #class variables, shared between all instances of this class + r = random.Random(SEED) + TIMESTEP_DEFAULT = 1.0 + + def __init__(self, state, initialiser, stateVector=[], name='network_process', **stateParameters): + Sim.Process.__init__(self, name) + self.state = state + self.stateVector = stateVector + self.stateParameters = stateParameters + self.initialize(*initialiser) + + + def initialize(self, id, sim, globalTopo, globalParams): + ''' this gets called automatically ''' + self.id = id + self.sim = sim + self.globalTopology = globalTopo + self.globalSharedParameters = globalParams + + def getAllNodes(self): + return self.globalTopology.nodes() + + def getAllAgents(self, state=None): + neighs = self.getAllNodes() + if state is not None: + return [self.globalTopology.node[n]['agent'] for n in neighs + if self.globalTopology.node[n]['agent'].state == state] + else: + return [self.globalTopology.node[n]['agent'] for n in neighs] + + + + def getNeighbouringAgents(self, state=None): + ''' returns list of neighbours, but as agents, not nodes. + so e.g. one can set result[0].state = INFECTED ''' + neighs = self.globalTopology.neighbors(self.id) + if state is not None: + return [self.globalTopology.node[n]['agent'] for n in neighs + if self.globalTopology.node[n]['agent'].state == state] + else: + return [self.globalTopology.node[n]['agent'] for n in neighs] + + def getNeighbouringAgentsIter(self, state=None): + '''same as getNeighbouringAgents, but returns generator expression, + not list. + ''' + neighs = self.globalTopology.neighbors(self.id) + if state is not None: + return (self.globalTopology.node[n]['agent'] for n in neighs + if self.globalTopology.node[n]['agent'].state == state) + else: + return (self.globalTopology.node[n]['agent'] for n in neighs) + + def getNeighbouringNodes(self): + ''' returns list of neighbours as nodes. + Call self.getAgent() on one of them to get the agent.''' + return self.globalTopology.neighbors(self.id) + + def getAgent(self, id): + '''returns agent of specified ID.''' + return self.globalTopology.node[id]['agent'] + + def addNewNode(self, state): + #add & activate new agent + return self.sim.addNewNode(state) + + #add a random edge + #u = NetworkAgent.r.choice(self.globalTopology.nodes()) + #self.globalTopology.add_edge(u, id) + + def die(self): + self.removeNode(self.id) + + def removeNode(self, id): +# cancel ? self.getAgent(id) + self.globalTopology.remove_node(id) + + def removeEdge(self, node1, node2): + + self.globalTopology.remove_edge(self.id, self.currentSupernodeID) + self.logTopoChange(REMOVE_EDGE, node1, node2) + + + def addEdge(self, node1, node2): + self.globalTopology.add_edge(self.id, self.currentSupernodeID) + self.logTopoChange(ADD_EDGE, node1, node2) + + + def logTopoChange(self, action, node, node2=None): + #TODO: test, add this to netlogger... + print(action, node, node2) + + + + + + + + + + \ No newline at end of file diff --git a/ComplexNetworkSim/animation.py b/ComplexNetworkSim/animation.py index 6a9904f..94fdb09 100644 --- a/ComplexNetworkSim/animation.py +++ b/ComplexNetworkSim/animation.py @@ -1,86 +1,86 @@ -''' -Module for the generation of visualisations of processes over complex networks -Generates PNG images for each timestep and (if ImageMagick is available) a gif animation file. - -@author: Joe Schaul -''' - -import utils -from matplotlib import pyplot -import networkx as nx -import os -#dependency: ImageMagick for gif creation. Works without too then only creates PNGs - -class AnimationCreator(object): - - def __init__(self, dir, name, title, mapping, trial=0, delay=100): - self.name = name - self.dir = os.path.abspath(dir) - self.delay = delay - self.mapping = mapping - self.trial = trial - self.title = title - self.G = nx.Graph() - - def create_gif(self, verbose=True): - if verbose: print "Creating PNGs ...", - self.createPNGs() - - input = os.path.join(self.dir, self.name + "*.png") - output = os.path.join(self.dir, self.name + ".gif") - - if verbose: print "attempting gif creation ...", - - failure = os.system("convert -delay %i -loop 0 \"%s\" \"%s\"" % (self.delay, input, output)) - if failure: - print "Problem: Could not create gif. \nMaybe ImageMagick not installed correctly, or its 'convert' executable is not on your system path? \nPNG Image files are generated in any case." - else: - print "success! \nAnimation at %s " % str(output) - - - def createPNGs(self): - - states, topos, vector = utils.retrieveTrial(self.dir, self.trial) - - init_topo = topos[0][1] - if topos[0][0] != 0: - print "problem - first topology not starting at 0!" - self.G = init_topo - self.layout = nx.layout.fruchterman_reingold_layout(self.G) - self.nodesToDraw = self.G.nodes() - self.edgesToDraw = self.G.edges() - self.nodesToDraw.sort() - self.edgesToDraw.sort() - i = 1 - j = 0 - - - pyplot.figure() - for t, s in states: - - # start with initial topology, and check the topology tuples - # each time to make sure to have an up-to-date graph topology - if len(topos) > i and t == topos[i][0]: - self.G = topos[i][1] - fixednodes = [node for node in self.nodesToDraw if node in self.G.nodes()] - self.layout = nx.layout.fruchterman_reingold_layout(self.G, pos=self.layout, fixed=fixednodes) - i += 1 - - #convert states to colours and draw a figure - colours = utils.states_to_colourString(s, self.mapping) - pyplot.clf() - self.nodesToDraw = self.G.nodes() - self.nodesToDraw.sort() - self.edgesToDraw = self.G.edges() - self.edgesToDraw.sort() - - nx.draw_networkx_nodes(self.G, nodelist=self.nodesToDraw, pos=self.layout, node_color=colours, with_labels=False) - nx.draw_networkx_edges(self.G, edgelist=self.edgesToDraw, pos=self.layout, with_labels=False) - - pyplot.suptitle("%s\ntime = %i" % (self.title, t)) - pyplot.axis('off') - - pyplot.savefig(os.path.join(self.dir, self.name + "%02d.png" % j)) - j += 1 - +''' +Module for the generation of visualisations of processes over complex networks +Generates PNG images for each timestep and (if ImageMagick is available) a gif animation file. + +@author: Joe Schaul +''' + +from . import utils +from matplotlib import pyplot +import networkx as nx +import os +#dependency: ImageMagick for gif creation. Works without too then only creates PNGs + +class AnimationCreator(object): + + def __init__(self, dir, name, title, mapping, trial=0, delay=100): + self.name = name + self.dir = os.path.abspath(dir) + self.delay = delay + self.mapping = mapping + self.trial = trial + self.title = title + self.G = nx.Graph() + + def create_gif(self, verbose=True): + if verbose: print("Creating PNGs ...", end=' ') + self.createPNGs() + + input = os.path.join(self.dir, self.name + "*.png") + output = os.path.join(self.dir, self.name + ".gif") + + if verbose: print("attempting gif creation ...", end=' ') + + failure = os.system("convert -delay %i -loop 0 \"%s\" \"%s\"" % (self.delay, input, output)) + if failure: + print("Problem: Could not create gif. \nMaybe ImageMagick not installed correctly, or its 'convert' executable is not on your system path? \nPNG Image files are generated in any case.") + else: + print("success! \nAnimation at %s " % str(output)) + + + def createPNGs(self): + + states, topos, vector = utils.retrieveTrial(self.dir, self.trial) + + init_topo = topos[0][1] + if topos[0][0] != 0: + print("problem - first topology not starting at 0!") + self.G = init_topo + self.layout = nx.layout.fruchterman_reingold_layout(self.G) + self.nodesToDraw = self.G.nodes() + self.edgesToDraw = self.G.edges() + self.nodesToDraw.sort() + self.edgesToDraw.sort() + i = 1 + j = 0 + + + pyplot.figure() + for t, s in states: + + # start with initial topology, and check the topology tuples + # each time to make sure to have an up-to-date graph topology + if len(topos) > i and t == topos[i][0]: + self.G = topos[i][1] + fixednodes = [node for node in self.nodesToDraw if node in self.G.nodes()] + self.layout = nx.layout.fruchterman_reingold_layout(self.G, pos=self.layout, fixed=fixednodes) + i += 1 + + #convert states to colours and draw a figure + colours = utils.states_to_colourString(s, self.mapping) + pyplot.clf() + self.nodesToDraw = self.G.nodes() + self.nodesToDraw.sort() + self.edgesToDraw = self.G.edges() + self.edgesToDraw.sort() + + nx.draw_networkx_nodes(self.G, nodelist=self.nodesToDraw, pos=self.layout, node_color=colours, with_labels=False) + nx.draw_networkx_edges(self.G, edgelist=self.edgesToDraw, pos=self.layout, with_labels=False) + + pyplot.suptitle("%s\ntime = %i" % (self.title, t)) + pyplot.axis('off') + + pyplot.savefig(os.path.join(self.dir, self.name + "%02d.png" % j)) + j += 1 + \ No newline at end of file diff --git a/ComplexNetworkSim/logging.py b/ComplexNetworkSim/logging.py index 26c2a3b..b30d6f2 100644 --- a/ComplexNetworkSim/logging.py +++ b/ComplexNetworkSim/logging.py @@ -1,55 +1,55 @@ -''' -Special agent automatically initialised by the simulation that logs network -states and topologies at every defined timestep. - -@author: Joe Schaul -''' - -import networkx as nx -from SimPy import Simulation as Sim - -import utils - -class NetworkLogger(Sim.Process): - - def __init__(self, sim, directory, logging_interval): - Sim.Process.__init__(self, sim=sim) - self.sim = sim - self.directory = directory - self.interval = logging_interval - self.stateTuples = [] - self.stateVectorTuples = [] - self.topology = nx.Graph() - self.topoTuples = [] - - def Run(self): - while True: - self.logCurrentState() - yield Sim.hold, self, self.interval - - def logCurrentState(self): - - nodes = self.sim.G.nodes(data=True) - states = [node[1]['agent'].state for node in nodes] - stateVectors = [node[1]['agent'].stateVector for node in nodes] - - #log states - self.stateTuples.append((self.sim.now(), states)) - self.stateVectorTuples.append((self.sim.now(), stateVectors)) - - #log new topology if it changed. - if not nx.fast_could_be_isomorphic(self.topology, nx.Graph(self.sim.G)): - self.topology = utils.create_copy_without_data(self.sim.G) - self.topoTuples.append((self.sim.now(), self.topology)) - - - def logTrialToFiles(self, id): - if not self.topoTuples: - self.topoTuples.append((0, self.topology)) - #write states, topologies, and state vectors to file - utils.logAllToFile(self.stateTuples, self.topoTuples, - self.stateVectorTuples, self.directory, id) - - - +''' +Special agent automatically initialised by the simulation that logs network +states and topologies at every defined timestep. + +@author: Joe Schaul +''' + +import networkx as nx +from SimPy import Simulation as Sim + +from . import utils + +class NetworkLogger(Sim.Process): + + def __init__(self, sim, directory, logging_interval): + Sim.Process.__init__(self, sim=sim) + self.sim = sim + self.directory = directory + self.interval = logging_interval + self.stateTuples = [] + self.stateVectorTuples = [] + self.topology = nx.Graph() + self.topoTuples = [] + + def Run(self): + while True: + self.logCurrentState() + yield Sim.hold, self, self.interval + + def logCurrentState(self): + + nodes = self.sim.G.nodes(data=True) + states = [node[1]['agent'].state for node in nodes] + stateVectors = [node[1]['agent'].stateVector for node in nodes] + + #log states + self.stateTuples.append((self.sim.now(), states)) + self.stateVectorTuples.append((self.sim.now(), stateVectors)) + + #log new topology if it changed. + if not nx.fast_could_be_isomorphic(self.topology, nx.Graph(self.sim.G)): + self.topology = utils.create_copy_without_data(self.sim.G) + self.topoTuples.append((self.sim.now(), self.topology)) + + + def logTrialToFiles(self, id): + if not self.topoTuples: + self.topoTuples.append((0, self.topology)) + #write states, topologies, and state vectors to file + utils.logAllToFile(self.stateTuples, self.topoTuples, + self.stateVectorTuples, self.directory, id) + + + \ No newline at end of file diff --git a/ComplexNetworkSim/plotting.py b/ComplexNetworkSim/plotting.py index 852696f..0e5e168 100644 --- a/ComplexNetworkSim/plotting.py +++ b/ComplexNetworkSim/plotting.py @@ -1,66 +1,66 @@ -''' -Module for the generation of plots of system states. - -@author: Joe Schaul -''' - -import utils -import statistics -import os - - -from matplotlib import pyplot - -class PlotCreator(object): - '''Constructor - set examples=1 to get plot lines of first trial as well as the average''' - def __init__(self, directory, name, title, statesToMonitor, colours, labels, examples=0): - self.directory = os.path.abspath(directory) - self.name = name - self.statesToMonitor = statesToMonitor - self.colours = colours - self.labels = labels - self.examples = examples - self.title = title - - def plotSimulation(self, ret=False, show=False, verbose=True): - '''plots a simulation, time on x axis, nodes on y. - Set ret=True for return of lines and no image creation. ''' - - states, topologies, vectors = utils.retrieveAllTrialsInDirectory(self.directory) - stats = statistics.TrialStats(states, topologies) - av = stats.trialAverage - - pyplot.figure() - lines = [] - for i in range(len(self.statesToMonitor)): - if self.examples > 0: - t0 = stats.trialstates[0] - pyplot.plot(t0.times, t0.stateCounterForStateX[self.statesToMonitor[i]], "k", alpha=0.5) - - try: - pyplot.plot(av.times, av.stateCounterForStateX[self.statesToMonitor[i]], self.colours[i], label=self.labels[i]) - lines.append((av.times, av.stateCounterForStateX[self.statesToMonitor[i]], self.colours[i])) - except KeyError: - try: - print "Plotting warning: skipping state = %s, colour = %s, label = %s, %s" \ - % (str(self.statesToMonitor[i]), str(self.colours[i]), str(self.labels[i]), - "because no node is ever in this state in this trial") - except KeyError: - print "Plotting error: one of 'colours' or 'labels' has fewer elements", - " than 'statesToMonitor'. Skipping this state." - - pyplot.legend(loc=0) - pyplot.title(self.title) - pyplot.xlabel("Time") - pyplot.ylabel("Nodes") - if ret: - return lines - else: - output_path = os.path.join(self.directory, "plot_" + self.name + ".png") - pyplot.savefig(output_path) - if verbose: - print "Plot at", output_path - - if show: - pyplot.show() +''' +Module for the generation of plots of system states. + +@author: Joe Schaul +''' + +from . import utils +from . import statistics +import os + + +from matplotlib import pyplot + +class PlotCreator(object): + '''Constructor - set examples=1 to get plot lines of first trial as well as the average''' + def __init__(self, directory, name, title, statesToMonitor, colours, labels, examples=0): + self.directory = os.path.abspath(directory) + self.name = name + self.statesToMonitor = statesToMonitor + self.colours = colours + self.labels = labels + self.examples = examples + self.title = title + + def plotSimulation(self, ret=False, show=False, verbose=True): + '''plots a simulation, time on x axis, nodes on y. + Set ret=True for return of lines and no image creation. ''' + + states, topologies, vectors = utils.retrieveAllTrialsInDirectory(self.directory) + stats = statistics.TrialStats(states, topologies) + av = stats.trialAverage + + pyplot.figure() + lines = [] + for i in range(len(self.statesToMonitor)): + if self.examples > 0: + t0 = stats.trialstates[0] + pyplot.plot(t0.times, t0.stateCounterForStateX[self.statesToMonitor[i]], "k", alpha=0.5) + + try: + pyplot.plot(av.times, av.stateCounterForStateX[self.statesToMonitor[i]], self.colours[i], label=self.labels[i]) + lines.append((av.times, av.stateCounterForStateX[self.statesToMonitor[i]], self.colours[i])) + except KeyError: + try: + print("Plotting warning: skipping state = %s, colour = %s, label = %s, %s" \ + % (str(self.statesToMonitor[i]), str(self.colours[i]), str(self.labels[i]), + "because no node is ever in this state in this trial")) + except KeyError: + print("Plotting error: one of 'colours' or 'labels' has fewer elements", end=' ') + " than 'statesToMonitor'. Skipping this state." + + pyplot.legend(loc=0) + pyplot.title(self.title) + pyplot.xlabel("Time") + pyplot.ylabel("Nodes") + if ret: + return lines + else: + output_path = os.path.join(self.directory, "plot_" + self.name + ".png") + pyplot.savefig(output_path) + if verbose: + print("Plot at", output_path) + + if show: + pyplot.show() \ No newline at end of file diff --git a/ComplexNetworkSim/simulation.py b/ComplexNetworkSim/simulation.py index b13122f..e485b96 100644 --- a/ComplexNetworkSim/simulation.py +++ b/ComplexNetworkSim/simulation.py @@ -1,117 +1,117 @@ -''' -Simulation support for agents in a complex network. - -Can run multiple fresh trials with the same input parameters. Writes system -state evolution to file (states & network topologies). Supports addition and -deletion of edges and nodes during simulation - -@author: Joe Schaul -''' - -import os -from SimPy import Simulation as Sim -import networkx as nx - -from logging import NetworkLogger - -class NetworkSimulation(Sim.Simulation): - """Simulation support for agents in a complex network. - - Can run multiple fresh trials with the same input parameters. Writes system - state evolution to file (states & network topologies) Supports - addition/deletion of edges and nodes during simulation - - Parameters - ---------- - - topology: a networkx graph - - states: a list of initial states corresponding to the nodes in the topology - - agentClass: the class of agent being activated on each node - - directory_name: path to where output should be stored. It is recommended to - have a different directory per simulation. - - maxTime : how long the simulation should run - - Optional parameters - ------------------- - - no_trials: the number of individual simulation trials - - environmentAgent: the class of agent operating on the network topology - globally, e.g. by changing the structure externally. - - globalSharedParameters: dictionary of additional parameters shared globally - - """ - def __init__(self, topology, states, agentClass, - directory_name, maxTime, no_trials=3, - environmentAgent=None, **globalSharedParameters): - - Sim.Simulation.__init__(self) - - self.G = nx.convert.convert_to_undirected(topology) - self.initial_topology = self.G.copy() - self.id_counter = len(topology.nodes()) - self.agentClass = agentClass - self.no_trials = no_trials - self.until = maxTime - self.initial_states = states - self.directory_name = os.path.abspath(directory_name) - self.environmentAgent = environmentAgent - self.globalSharedParameters = globalSharedParameters - - def runSimulation(self): - print "Starting simulations..." - - for i in range(self.no_trials): - print "---Trial " + str(i) + " ---" - self.runTrial(i) - - print "Simulation completed. " - - def runTrial(self, id): - self.initialize() #Sim.Simulation initialisation - - #initialise class variables (that are shared between agents) - #freshly for each trial (as they may change during simulation) - self.G = self.initial_topology.copy() - self.params = self.globalSharedParameters.copy() - - print "set up agents..." - #set up agents - for i in self.G.nodes(): - agent = self.agentClass(self.initial_states[i], (i, self, self.G, self.params)) - self.G.node[i]['agent'] = agent - self.activate(agent, agent.Run()) - - #set up environment agent - if self.environmentAgent: - environment = self.environmentAgent(0, ("env", self, self.G, self.params)) - self.activate(environment, environment.Run(), prior=True) - - #set up logging - logging_interval = 1 - logger = NetworkLogger(self, self.directory_name, logging_interval) - self.activate(logger, logger.Run(), prior=True) - - #run simulation trial - self.simulate(self.until) - - #output to Files - logger.logTrialToFiles(id) - - - def addNewNode(self, state, agentClass=None): - """Add new node to network and activate an agent on it""" - - if not agentClass: - agentClass = self.agentClass - agent = agentClass(state, (self.id_counter, self, self.G, self.params)) - self.G.add_node(self.id_counter, {'agent': agent}) - self.activate(agent, agent.Run()) - self.id_counter += 1 - return self.id_counter - 1 +''' +Simulation support for agents in a complex network. + +Can run multiple fresh trials with the same input parameters. Writes system +state evolution to file (states & network topologies). Supports addition and +deletion of edges and nodes during simulation + +@author: Joe Schaul +''' + +import os +from SimPy import Simulation as Sim +import networkx as nx + +from .logging import NetworkLogger + +class NetworkSimulation(Sim.Simulation): + """Simulation support for agents in a complex network. + + Can run multiple fresh trials with the same input parameters. Writes system + state evolution to file (states & network topologies) Supports + addition/deletion of edges and nodes during simulation + + Parameters + ---------- + + topology: a networkx graph + + states: a list of initial states corresponding to the nodes in the topology + + agentClass: the class of agent being activated on each node + + directory_name: path to where output should be stored. It is recommended to + have a different directory per simulation. + + maxTime : how long the simulation should run + + Optional parameters + ------------------- + + no_trials: the number of individual simulation trials + + environmentAgent: the class of agent operating on the network topology + globally, e.g. by changing the structure externally. + + globalSharedParameters: dictionary of additional parameters shared globally + + """ + def __init__(self, topology, states, agentClass, + directory_name, maxTime, no_trials=3, + environmentAgent=None, **globalSharedParameters): + + Sim.Simulation.__init__(self) + + self.G = nx.convert.convert_to_undirected(topology) + self.initial_topology = self.G.copy() + self.id_counter = len(topology.nodes()) + self.agentClass = agentClass + self.no_trials = no_trials + self.until = maxTime + self.initial_states = states + self.directory_name = os.path.abspath(directory_name) + self.environmentAgent = environmentAgent + self.globalSharedParameters = globalSharedParameters + + def runSimulation(self): + print("Starting simulations...") + + for i in range(self.no_trials): + print("---Trial " + str(i) + " ---") + self.runTrial(i) + + print("Simulation completed. ") + + def runTrial(self, id): + self.initialize() #Sim.Simulation initialisation + + #initialise class variables (that are shared between agents) + #freshly for each trial (as they may change during simulation) + self.G = self.initial_topology.copy() + self.params = self.globalSharedParameters.copy() + + print("set up agents...") + #set up agents + for i in self.G.nodes(): + agent = self.agentClass(self.initial_states[i], (i, self, self.G, self.params)) + self.G.node[i]['agent'] = agent + self.activate(agent, agent.Run()) + + #set up environment agent + if self.environmentAgent: + environment = self.environmentAgent(0, ("env", self, self.G, self.params)) + self.activate(environment, environment.Run(), prior=True) + + #set up logging + logging_interval = 1 + logger = NetworkLogger(self, self.directory_name, logging_interval) + self.activate(logger, logger.Run(), prior=True) + + #run simulation trial + self.simulate(self.until) + + #output to Files + logger.logTrialToFiles(id) + + + def addNewNode(self, state, agentClass=None): + """Add new node to network and activate an agent on it""" + + if not agentClass: + agentClass = self.agentClass + agent = agentClass(state, (self.id_counter, self, self.G, self.params)) + self.G.add_node(self.id_counter, {'agent': agent}) + self.activate(agent, agent.Run()) + self.id_counter += 1 + return self.id_counter - 1 \ No newline at end of file diff --git a/ComplexNetworkSim/statistics.py b/ComplexNetworkSim/statistics.py index 98507d7..c762e9e 100644 --- a/ComplexNetworkSim/statistics.py +++ b/ComplexNetworkSim/statistics.py @@ -1,62 +1,63 @@ -''' -Module for basic averaging of system states across multiple trials. -Used in plotting. - -@author: Joe Schaul -''' - -class TrialState(object): - - def __init__(self, trial_id, times, systemStates, uniqueStates, stateCounterForStateX): - self.trial_id = trial_id - self.times = times - self.systemStates = systemStates - self.uniqueStates = uniqueStates - self.stateCounterForStateX = stateCounterForStateX - - -class TrialStats(object): - - def __init__(self, allTrialStateTuples, allTrialTopologyTuples): - self.stateTuples = allTrialStateTuples - self.topoTuples = allTrialTopologyTuples - self.trialstates = [] - self.trialAverage = None - - self.calculateAllStateCounts() - self.calculateAverageStateCount() - - def calculateAllStateCounts(self): - - for trial in range(len(self.stateTuples)): - times = [t for (t,s) in self.stateTuples[trial]] - systemStates = [s for (t,s) in self.stateTuples[trial]] - uniqueStates = reduce(lambda x, y: set(y).union(set(x)), systemStates) - stateCounterDict = {} - for x in uniqueStates: - stateXCounts = [state.count(x) for state in systemStates] - stateCounterDict[x] = stateXCounts - #store info about this trial - self.trialstates.append(TrialState(trial, times, systemStates, uniqueStates, stateCounterDict)) - - def calculateAverageStateCount(self): - times = self.trialstates[0].times - uniqueStates = self.trialstates[0].uniqueStates - for trial in self.trialstates: - try: - uniqueStates = set(trial.uniqueStates).union(set(uniqueStates)) - except: - pass - - stateCounterDict = {} - dummy = [0 for x in trial.systemStates] - for x in uniqueStates: - array = [trial.stateCounterForStateX.get(x, dummy) for trial in self.trialstates] - averages = [sum(value)/len(self.trialstates) for value in zip(*array)] - stateCounterDict[x] = averages - - self.trialAverage = TrialState(-1, times, None, uniqueStates, stateCounterDict) - - - +''' +Module for basic averaging of system states across multiple trials. +Used in plotting. + +@author: Joe Schaul +''' +from functools import reduce + +class TrialState(object): + + def __init__(self, trial_id, times, systemStates, uniqueStates, stateCounterForStateX): + self.trial_id = trial_id + self.times = times + self.systemStates = systemStates + self.uniqueStates = uniqueStates + self.stateCounterForStateX = stateCounterForStateX + + +class TrialStats(object): + + def __init__(self, allTrialStateTuples, allTrialTopologyTuples): + self.stateTuples = allTrialStateTuples + self.topoTuples = allTrialTopologyTuples + self.trialstates = [] + self.trialAverage = None + + self.calculateAllStateCounts() + self.calculateAverageStateCount() + + def calculateAllStateCounts(self): + + for trial in range(len(self.stateTuples)): + times = [t for (t,s) in self.stateTuples[trial]] + systemStates = [s for (t,s) in self.stateTuples[trial]] + uniqueStates = reduce(lambda x, y: set(y).union(set(x)), systemStates) + stateCounterDict = {} + for x in uniqueStates: + stateXCounts = [state.count(x) for state in systemStates] + stateCounterDict[x] = stateXCounts + #store info about this trial + self.trialstates.append(TrialState(trial, times, systemStates, uniqueStates, stateCounterDict)) + + def calculateAverageStateCount(self): + times = self.trialstates[0].times + uniqueStates = self.trialstates[0].uniqueStates + for trial in self.trialstates: + try: + uniqueStates = set(trial.uniqueStates).union(set(uniqueStates)) + except: + pass + + stateCounterDict = {} + dummy = [0 for x in trial.systemStates] + for x in uniqueStates: + array = [trial.stateCounterForStateX.get(x, dummy) for trial in self.trialstates] + averages = [sum(value)/len(self.trialstates) for value in zip(*array)] + stateCounterDict[x] = averages + + self.trialAverage = TrialState(-1, times, None, uniqueStates, stateCounterDict) + + + \ No newline at end of file diff --git a/ComplexNetworkSim/unittests/utilsTests.py b/ComplexNetworkSim/unittests/utilsTests.py index 2ef8812..b2f6e6e 100644 --- a/ComplexNetworkSim/unittests/utilsTests.py +++ b/ComplexNetworkSim/unittests/utilsTests.py @@ -1,97 +1,97 @@ -''' -Unittests to test correct working of some utils methods - -@author: Joe -''' -import unittest -import os - -from ComplexNetworkSim import utils - -TMP_DIRECTORY = "temp_unittests" - -class globalTest(object): - - directory = os.path.abspath(TMP_DIRECTORY) - - def __init__(self): - self.globalSetUp() - - def globalSetUp(self): - if not os.path.exists(globalTest.directory): - os.makedirs(globalTest.directory) - - def globalTearDown(self): - files = os.listdir(globalTest.directory) - print "Deleting... " - for file in files: - if ("." in file and utils.PYTHON_PICKLE_EXTENSION not in file): - raise Exception("directory contains non anticipated file.") - path = globalTest.directory + os.sep + file - print path - os.remove(path) - print "Removal of temporary files done." - - - -class TestDump(unittest.TestCase): - - - def setUp(self): - self.directory = globalTest.directory - self.filename = self.directory + os.sep + "test_log" - - - - def test_dump_multiple(self): - state1 = [1,2,3,4] - state2 = [3,5,6,99] - states = [] - states.append(state1) - states.append(state2) - - - stateV1 = [[1, 1],[27, 8],[34,3]] - stateV2 = [[1, 1],[27, 8], [999, 9]] - statesV = [] - statesV.append(stateV1) - statesV.append(stateV2) - - topo1 = [(1,2), (2,3)] - topo2 = [(1,4), (5,3)] - topos = [] - topos.append(topo1) - topos.append(topo2) - - utils.logAllToFile(state1, topo1, stateV1, self.directory, 7) - utils.logAllToFile(state2, topo2, stateV2, self.directory, 9) - - states2, topos2, statesV2 = utils.retrieveAllTrialsInDirectory(self.directory) - self.assertEqual(states, states2) - self.assertEqual(statesV, statesV2) - self.assertEqual(topos, topos2) - - - def test_dump_one(self): - x = [(1,2), (3,4)] - utils.logToFile(x, self.filename) - x2 = utils.retrieve(self.filename) - self.assertEqual(x, x2) - - - - - - - - - -if __name__ == "__main__": - #import sys;sys.argv = ['', 'Test.testName'] -# unittest.main() - bla = globalTest() - - suite = unittest.TestLoader().loadTestsFromTestCase(TestDump) - unittest.TextTestRunner(verbosity=2).run(suite) - +''' +Unittests to test correct working of some utils methods + +@author: Joe +''' +import unittest +import os + +from ComplexNetworkSim import utils + +TMP_DIRECTORY = "temp_unittests" + +class globalTest(object): + + directory = os.path.abspath(TMP_DIRECTORY) + + def __init__(self): + self.globalSetUp() + + def globalSetUp(self): + if not os.path.exists(globalTest.directory): + os.makedirs(globalTest.directory) + + def globalTearDown(self): + files = os.listdir(globalTest.directory) + print("Deleting... ") + for file in files: + if ("." in file and utils.PYTHON_PICKLE_EXTENSION not in file): + raise Exception("directory contains non anticipated file.") + path = globalTest.directory + os.sep + file + print(path) + os.remove(path) + print("Removal of temporary files done.") + + + +class TestDump(unittest.TestCase): + + + def setUp(self): + self.directory = globalTest.directory + self.filename = self.directory + os.sep + "test_log" + + + + def test_dump_multiple(self): + state1 = [1,2,3,4] + state2 = [3,5,6,99] + states = [] + states.append(state1) + states.append(state2) + + + stateV1 = [[1, 1],[27, 8],[34,3]] + stateV2 = [[1, 1],[27, 8], [999, 9]] + statesV = [] + statesV.append(stateV1) + statesV.append(stateV2) + + topo1 = [(1,2), (2,3)] + topo2 = [(1,4), (5,3)] + topos = [] + topos.append(topo1) + topos.append(topo2) + + utils.logAllToFile(state1, topo1, stateV1, self.directory, 7) + utils.logAllToFile(state2, topo2, stateV2, self.directory, 9) + + states2, topos2, statesV2 = utils.retrieveAllTrialsInDirectory(self.directory) + self.assertEqual(states, states2) + self.assertEqual(statesV, statesV2) + self.assertEqual(topos, topos2) + + + def test_dump_one(self): + x = [(1,2), (3,4)] + utils.logToFile(x, self.filename) + x2 = utils.retrieve(self.filename) + self.assertEqual(x, x2) + + + + + + + + + +if __name__ == "__main__": + #import sys;sys.argv = ['', 'Test.testName'] +# unittest.main() + bla = globalTest() + + suite = unittest.TestLoader().loadTestsFromTestCase(TestDump) + unittest.TextTestRunner(verbosity=2).run(suite) + bla.globalTearDown() \ No newline at end of file diff --git a/ComplexNetworkSim/utils.py b/ComplexNetworkSim/utils.py index da8c300..db20cf1 100644 --- a/ComplexNetworkSim/utils.py +++ b/ComplexNetworkSim/utils.py @@ -1,134 +1,134 @@ -''' -Module with a range of utility methods, such as storing and retrieving states -and topologies. - -@author: Joe Schaul -''' - -from cPickle import Pickler, Unpickler -import os -import glob -import networkx as nx - -from customexceptions import * - -PYTHON_PICKLE_EXTENSION = ".pickled" -TOPO = "_topology" -STATE = "_states" -STATEVECTOR = "_statevectors" -BASE = os.sep + "log_trial_" - - -def states_to_colourString(states, mapping=None): - ''' - takes a list of states (integers) and a mapping, e.g. - - mapping = { 0 : "g", - 1 : "r", - 2 : "k"} - - Returns a list of colours to use in animation. Assumes matplotlib style - colour strings. Defaults to black ("k") if state not found in mapping, and - defaults to all nodes being black if no mapping defined. - ''' - return [mapping.get(state, "k") for state in states] - - - -#def create_copy_without_data(G): -# """Return a copy of the graph G with all of the data -# removed. -# """ -# H=G.__class__() -# H.name='graphOnlyOf '+G.name -# H.add_nodes_from(G.nodes()) -# H.add_edges_from(G.edges()) -# -# return H - -def create_copy_without_data(G): - """Return a copy of the graph G with all of the data - removed. More efficient than above. - """ - H = nx.Graph(G) - for i in H.nodes_iter(): - H.node[i] = {} - return H - - -def makeFileNameState(dir, id): - return dir + BASE + str(id) + STATE + PYTHON_PICKLE_EXTENSION -def makeFileNameStateVector(dir, id): - return dir + BASE + str(id) + STATEVECTOR + PYTHON_PICKLE_EXTENSION -def makeFileNameTopo(dir, id): - return dir + BASE + str(id) + TOPO + PYTHON_PICKLE_EXTENSION - -def retrieveTrial(directory, trial_number): - ''' For a specific trial in a given directory, retrieve states, topologies - and statevectors. - ''' - states = retrieve(makeFileNameState(directory, trial_number)) - topos = retrieve(makeFileNameTopo(directory, trial_number)) - stateVectors = retrieve(makeFileNameStateVector(directory, trial_number)) - return states, topos, stateVectors - -def retrieveAllTrialsInDirectory(directory): - '''Retrieves all trials in a directory ''' - file_list = glob.glob(os.path.join(directory, "*" + PYTHON_PICKLE_EXTENSION)) - states = [] - stateVectors = [] - topos = [] - for f in file_list: - if TOPO in f: - topos.append(f) - elif STATE in f: - states.append(f) - elif STATEVECTOR in f: - stateVectors.append(f) - - - state_list = [retrieve(f) for f in states] - topo_list = [retrieve(f) for f in topos] - stateVector_list = [retrieve(f) for f in stateVectors] - - return (state_list, topo_list, stateVector_list) - -def logAllToFile(stateTuples, topoTuples, stateVectorTuples, directory, trial_id): - '''Store states, topologies and statevectors to pickled files''' - logToFile(stateTuples, makeFileNameState(directory, str(trial_id))) - logToFile(topoTuples, makeFileNameTopo(directory, str(trial_id))) - logToFile(stateVectorTuples, makeFileNameStateVector(directory, str(trial_id))) - - - -def logToFile(stuff, filename, verbose=True): - ''' Store one item (e.g. state list or networkx graph) to file. ''' - filename = os.path.normcase(filename) - directory = os.path.dirname(filename) - if not os.path.exists(directory): - os.makedirs(directory) - - f = open(filename, 'wb') - p = Pickler(f, protocol=2) - p.dump(stuff) - f.close() - if verbose: - total = len(stuff) - print "Written %i items to pickled binary file: %s" % (total, filename) - return filename - - -def retrieve(filename): - ''' Retrieve a pickled object (e.g. state list or networkx graph) from file - ''' - filename = os.path.normcase(filename) - try: - f = open(filename, 'rb') - u = Unpickler(f) - stuff = u.load() - f.close() - return stuff - except IOError: - raise LogOpeningError("No file found for %s" % filename, filename) - - +''' +Module with a range of utility methods, such as storing and retrieving states +and topologies. + +@author: Joe Schaul +''' + +from pickle import Pickler, Unpickler +import os +import glob +import networkx as nx + +from .customexceptions import * + +PYTHON_PICKLE_EXTENSION = ".pickled" +TOPO = "_topology" +STATE = "_states" +STATEVECTOR = "_statevectors" +BASE = os.sep + "log_trial_" + + +def states_to_colourString(states, mapping=None): + ''' + takes a list of states (integers) and a mapping, e.g. + + mapping = { 0 : "g", + 1 : "r", + 2 : "k"} + + Returns a list of colours to use in animation. Assumes matplotlib style + colour strings. Defaults to black ("k") if state not found in mapping, and + defaults to all nodes being black if no mapping defined. + ''' + return [mapping.get(state, "k") for state in states] + + + +#def create_copy_without_data(G): +# """Return a copy of the graph G with all of the data +# removed. +# """ +# H=G.__class__() +# H.name='graphOnlyOf '+G.name +# H.add_nodes_from(G.nodes()) +# H.add_edges_from(G.edges()) +# +# return H + +def create_copy_without_data(G): + """Return a copy of the graph G with all of the data + removed. More efficient than above. + """ + H = nx.Graph(G) + for i in H.nodes_iter(): + H.node[i] = {} + return H + + +def makeFileNameState(dir, id): + return dir + BASE + str(id) + STATE + PYTHON_PICKLE_EXTENSION +def makeFileNameStateVector(dir, id): + return dir + BASE + str(id) + STATEVECTOR + PYTHON_PICKLE_EXTENSION +def makeFileNameTopo(dir, id): + return dir + BASE + str(id) + TOPO + PYTHON_PICKLE_EXTENSION + +def retrieveTrial(directory, trial_number): + ''' For a specific trial in a given directory, retrieve states, topologies + and statevectors. + ''' + states = retrieve(makeFileNameState(directory, trial_number)) + topos = retrieve(makeFileNameTopo(directory, trial_number)) + stateVectors = retrieve(makeFileNameStateVector(directory, trial_number)) + return states, topos, stateVectors + +def retrieveAllTrialsInDirectory(directory): + '''Retrieves all trials in a directory ''' + file_list = glob.glob(os.path.join(directory, "*" + PYTHON_PICKLE_EXTENSION)) + states = [] + stateVectors = [] + topos = [] + for f in file_list: + if TOPO in f: + topos.append(f) + elif STATE in f: + states.append(f) + elif STATEVECTOR in f: + stateVectors.append(f) + + + state_list = [retrieve(f) for f in states] + topo_list = [retrieve(f) for f in topos] + stateVector_list = [retrieve(f) for f in stateVectors] + + return (state_list, topo_list, stateVector_list) + +def logAllToFile(stateTuples, topoTuples, stateVectorTuples, directory, trial_id): + '''Store states, topologies and statevectors to pickled files''' + logToFile(stateTuples, makeFileNameState(directory, str(trial_id))) + logToFile(topoTuples, makeFileNameTopo(directory, str(trial_id))) + logToFile(stateVectorTuples, makeFileNameStateVector(directory, str(trial_id))) + + + +def logToFile(stuff, filename, verbose=True): + ''' Store one item (e.g. state list or networkx graph) to file. ''' + filename = os.path.normcase(filename) + directory = os.path.dirname(filename) + if not os.path.exists(directory): + os.makedirs(directory) + + f = open(filename, 'wb') + p = Pickler(f, protocol=2) + p.dump(stuff) + f.close() + if verbose: + total = len(stuff) + print("Written %i items to pickled binary file: %s" % (total, filename)) + return filename + + +def retrieve(filename): + ''' Retrieve a pickled object (e.g. state list or networkx graph) from file + ''' + filename = os.path.normcase(filename) + try: + f = open(filename, 'rb') + u = Unpickler(f) + stuff = u.load() + f.close() + return stuff + except IOError: + raise LogOpeningError("No file found for %s" % filename, filename) + + diff --git a/examples/SIR_model/environment_SIR.py b/examples/SIR_model/environment_SIR.py index 78d5691..8e60e71 100644 --- a/examples/SIR_model/environment_SIR.py +++ b/examples/SIR_model/environment_SIR.py @@ -1,31 +1,31 @@ -'''Environment Agent for SIR model: infects random -agent at timestep 7. - -@author: Joe Schaul -''' -from ComplexNetworkSim import NetworkAgent, Sim -from agent_SIR import INFECTED, SUSCEPTIBLE, RECOVERED - -class SIRenvironment(NetworkAgent): - - def __init__(self, state, initialiser): - NetworkAgent.__init__(self, 0, initialiser) - - def Run(self): - while True: - yield Sim.hold, self, 7.0 - self.infectRandomInitialAgent() - yield Sim.passivate, self - - - def infectRandomInitialAgent(self): - target = NetworkAgent.r.choice(self.getAllNodes()) - self.getAgent(target).state = INFECTED - -# def infectHighestConnectedAgent(self): -# import networkx -# topo = networkx.Graph(self.globalTopology) -# target = max([topo.degree(i) for i in self.getAllNodes()]) -# self.getAgent(target).state = INFECTED - +'''Environment Agent for SIR model: infects random +agent at timestep 7. + +@author: Joe Schaul +''' +from ComplexNetworkSim import NetworkAgent, Sim +from .agent_SIR import INFECTED, SUSCEPTIBLE, RECOVERED + +class SIRenvironment(NetworkAgent): + + def __init__(self, state, initialiser): + NetworkAgent.__init__(self, 0, initialiser) + + def Run(self): + while True: + yield Sim.hold, self, 7.0 + self.infectRandomInitialAgent() + yield Sim.passivate, self + + + def infectRandomInitialAgent(self): + target = NetworkAgent.r.choice(self.getAllNodes()) + self.getAgent(target).state = INFECTED + +# def infectHighestConnectedAgent(self): +# import networkx +# topo = networkx.Graph(self.globalTopology) +# target = max([topo.degree(i) for i in self.getAllNodes()]) +# self.getAgent(target).state = INFECTED + \ No newline at end of file diff --git a/examples/SIR_model/model_scale_free.py b/examples/SIR_model/model_scale_free.py index 88712d1..c883ed0 100644 --- a/examples/SIR_model/model_scale_free.py +++ b/examples/SIR_model/model_scale_free.py @@ -1,58 +1,58 @@ -'''Example model specification for SIR over a scale-free network. - -@author: Joe Schaul -''' -import networkx as nx - -from ComplexNetworkSim import NetworkSimulation, AnimationCreator, PlotCreator -from agent_SIR import INFECTED, RECOVERED, SUSCEPTIBLE -from agent_SIR import SIRSimple as agentClass -from environment_SIR import SIRenvironment as environmentAgent - -# Simulation constants -MAX_SIMULATION_TIME = 25.0 -TRIALS = 2 - -def main(nodes=30): - # Model parameters - directory = 'test' - globalSharedParameters = {} - globalSharedParameters['infection_rate'] = 0.3 - globalSharedParameters['inf_dur'] = "self.r.gauss(7, 2)" - - # Output Parameters - statesToMonitor = [INFECTED, SUSCEPTIBLE, RECOVERED] - colours = ["r", "--g", ":k"] - mapping = {SUSCEPTIBLE:"w", INFECTED:"r", RECOVERED:"0.4"} - labels = ["Infected", "Susceptible", "Recovered"] - name = "SIR_scale_free" - titlePlot = "Simulation on scale free graph, %i trials, %i%% infection prob" \ - % (TRIALS, 100 * globalSharedParameters['infection_rate']) - titleVisual = "" - - # Network - G = nx.scale_free_graph(nodes) - states = [SUSCEPTIBLE for n in G.nodes()] - - # run simulation - simulation = NetworkSimulation(G, - states, - agentClass, - directory, - MAX_SIMULATION_TIME, - TRIALS, - environmentAgent, - **globalSharedParameters) - simulation.runSimulation() - - # run visualisation - gif = AnimationCreator(directory, name, titleVisual, mapping) - gif.create_gif() - - # plot results - p = PlotCreator(directory, name, titlePlot, statesToMonitor, - colours, labels) - p.plotSimulation() - -if __name__ == '__main__': - main() +'''Example model specification for SIR over a scale-free network. + +@author: Joe Schaul +''' +import networkx as nx + +from ComplexNetworkSim import NetworkSimulation, AnimationCreator, PlotCreator +from .agent_SIR import INFECTED, RECOVERED, SUSCEPTIBLE +from .agent_SIR import SIRSimple as agentClass +from .environment_SIR import SIRenvironment as environmentAgent + +# Simulation constants +MAX_SIMULATION_TIME = 25.0 +TRIALS = 2 + +def main(nodes=30): + # Model parameters + directory = 'test' + globalSharedParameters = {} + globalSharedParameters['infection_rate'] = 0.3 + globalSharedParameters['inf_dur'] = "self.r.gauss(7, 2)" + + # Output Parameters + statesToMonitor = [INFECTED, SUSCEPTIBLE, RECOVERED] + colours = ["r", "--g", ":k"] + mapping = {SUSCEPTIBLE:"w", INFECTED:"r", RECOVERED:"0.4"} + labels = ["Infected", "Susceptible", "Recovered"] + name = "SIR_scale_free" + titlePlot = "Simulation on scale free graph, %i trials, %i%% infection prob" \ + % (TRIALS, 100 * globalSharedParameters['infection_rate']) + titleVisual = "" + + # Network + G = nx.scale_free_graph(nodes) + states = [SUSCEPTIBLE for n in G.nodes()] + + # run simulation + simulation = NetworkSimulation(G, + states, + agentClass, + directory, + MAX_SIMULATION_TIME, + TRIALS, + environmentAgent, + **globalSharedParameters) + simulation.runSimulation() + + # run visualisation + gif = AnimationCreator(directory, name, titleVisual, mapping) + gif.create_gif() + + # plot results + p = PlotCreator(directory, name, titlePlot, statesToMonitor, + colours, labels) + p.plotSimulation() + +if __name__ == '__main__': + main() diff --git a/examples/SIR_model/model_scale_free_simulationOnly.py b/examples/SIR_model/model_scale_free_simulationOnly.py index f116999..5d4accc 100644 --- a/examples/SIR_model/model_scale_free_simulationOnly.py +++ b/examples/SIR_model/model_scale_free_simulationOnly.py @@ -1,42 +1,42 @@ -'''Example model specification for SIR over a scale-free network. - -This module only executes a simulation and no plotting. -See model_scale_free.py for combined behaviour. - -@author: Joe Schaul -''' -import networkx as nx - -from ComplexNetworkSim import NetworkSimulation -from agent_SIR import INFECTED, RECOVERED, SUSCEPTIBLE -from agent_SIR import SIRSimple as agentClass -from environment_SIR import SIRenvironment as environmentAgent - -# Simulation constants -MAX_SIMULATION_TIME = 25.0 -TRIALS = 2 - -def main(nodes=30): - # Model parameters - directory = 'test' - globalSharedParameters = {} - globalSharedParameters['infection_rate'] = 0.3 - globalSharedParameters['inf_dur'] = "7" - - # Network and initial states - G = nx.scale_free_graph(nodes) - states = [SUSCEPTIBLE for n in G.nodes()] - - # run simulation - simulation = NetworkSimulation(G, - states, - agentClass, - directory, - MAX_SIMULATION_TIME, - TRIALS, - environmentAgent, - **globalSharedParameters) - simulation.runSimulation() - -if __name__ == '__main__': - main() +'''Example model specification for SIR over a scale-free network. + +This module only executes a simulation and no plotting. +See model_scale_free.py for combined behaviour. + +@author: Joe Schaul +''' +import networkx as nx + +from ComplexNetworkSim import NetworkSimulation +from .agent_SIR import INFECTED, RECOVERED, SUSCEPTIBLE +from .agent_SIR import SIRSimple as agentClass +from .environment_SIR import SIRenvironment as environmentAgent + +# Simulation constants +MAX_SIMULATION_TIME = 25.0 +TRIALS = 2 + +def main(nodes=30): + # Model parameters + directory = 'test' + globalSharedParameters = {} + globalSharedParameters['infection_rate'] = 0.3 + globalSharedParameters['inf_dur'] = "7" + + # Network and initial states + G = nx.scale_free_graph(nodes) + states = [SUSCEPTIBLE for n in G.nodes()] + + # run simulation + simulation = NetworkSimulation(G, + states, + agentClass, + directory, + MAX_SIMULATION_TIME, + TRIALS, + environmentAgent, + **globalSharedParameters) + simulation.runSimulation() + +if __name__ == '__main__': + main() diff --git a/examples/Skype_model/model_Skype.py b/examples/Skype_model/model_Skype.py index 9a38c67..d654f3f 100644 --- a/examples/Skype_model/model_Skype.py +++ b/examples/Skype_model/model_Skype.py @@ -1,139 +1,139 @@ -'''Example model specification for a Skype-like system - -@author: Joe Schaul -''' -import networkx as nx - -from ComplexNetworkSim import NetworkSimulation, AnimationCreator, PlotCreator -from agent_skypeClient import DISABLED, ENABLED, ENABLED_S, DISABLED_S -from agent_skypeClient import Skype -from environment_Skype import crash - -# Simulation constants -MAX_SIMULATION_TIME = 50.0 -TRIALS = 1 - -def main(): - combs = [] - - combs.append({}) - combs[0]["nodes"] = 600 - combs[0]["supernodes"] = 100 - combs[0]["permanent_supernodes"] = 1 - combs[0]['threshold'] = 12 - combs[0]['cache_size'] = 50 - combs[0]['kills'] = 40 - combs[0]['restart_time'] = "self.r.expovariate(1/4.0)" - combs[0]['restart_time_str'] = "exp" - combs[0]['agentClass'] = Skype - combs[0]['environmentAgent'] = crash - - combs.append(combs[0].copy()) - combs[1]['cache_size'] = 3 - - combs.append(combs[0].copy()) - combs[2]['kills'] = 60 - - combs.append(combs[0].copy()) - combs[3]['kills'] = 60 - combs[3]['cache_size'] = 3 - -# combs.append(combs[0].copy()) -# combs[4]['kills'] = 40 -# combs[4]['cache_size'] = 1 -# -# combs.append(combs[0].copy()) -# combs[4]['kills'] = 60 -# combs[4]['cache_size'] = 1 - - fixes = [] - for c in combs: - fixes.append(c.copy()) - for fix in fixes: - fix['restart_time'] = "4" - fix['restart_time_str'] = "fix" - - gausses = [] - for c in combs: - gausses.append(c.copy()) - for gauss in gausses: - gauss['restart_time'] = "abs(self.r.gauss(4, 1))" - gauss['restart_time_str'] = "gauss" - - - parameter_combinations = [] - parameter_combinations.extend(combs) - parameter_combinations.extend(fixes) - parameter_combinations.extend(gausses) - - for parameters in parameter_combinations: - dir = '_'.join(("skype600-", - str(parameters['supernodes']), - str(parameters['restart_time_str']), - str(parameters['threshold']), - str(parameters['cache_size']), - str(parameters['kills']), - )) - parameters['directory'] = dir - simulate(**parameters) - - -def simulate(**kwargs): - - # Model parameters - directory = kwargs.get("directory", 'skype') - agentClass = kwargs.get("agentClass", Skype) - environmentAgent = kwargs.get("environmentAgent", crash) - NUMBER_SUPERNODES = kwargs.get("supernodes", 90) - NUMBER_NODES = kwargs.get("nodes", 600) - NUMBER_PERMANENT_SUPERNODES = kwargs.get("permanent_supernodes", 1) - globalSharedParameters = {} - globalSharedParameters['supernodes'] = range(NUMBER_SUPERNODES) - globalSharedParameters['permanent_supernodes'] = range(NUMBER_PERMANENT_SUPERNODES) - globalSharedParameters['threshold'] = kwargs.get("threshold", 12) - globalSharedParameters['cache_size'] = kwargs.get("cache_size", 20) - globalSharedParameters['kills'] = kwargs.get("kills", 30) - globalSharedParameters['restart_time'] = kwargs.get('restart_time', "4.0") - - - # Output Parameters - statesToMonitor = [ENABLED, ENABLED_S] - colours = ["g", "^b", "0.5"] - mapping = {ENABLED:"g", ENABLED_S: "b", DISABLED:"0.2", DISABLED_S: "0.4"} - labels = ["Online nodes", "Online supernodes", "Offline"] - name = "skype" - titlePlot = "Skype simulation, %i trials, threshold=%i, cacheSize=%i \n supernode_restart_distr=%s" % \ - (TRIALS, globalSharedParameters['threshold'], globalSharedParameters['cache_size'], - str(globalSharedParameters['restart_time'])) - titleVisual = "Skype visualisation" - - -#####topology#### - G = nx.Graph(nx.complete_graph(NUMBER_SUPERNODES)) - G.add_nodes_from(xrange(NUMBER_SUPERNODES,NUMBER_NODES+NUMBER_SUPERNODES)) - - states = [DISABLED for node in G.nodes_iter()] - - - # run simulation - simulation = NetworkSimulation(G, - states, - agentClass, - directory, - MAX_SIMULATION_TIME, - TRIALS, - environmentAgent, - **globalSharedParameters) - simulation.runSimulation() - -# # run visualisation -# gif = AnimationCreator(directory, name, titleVisual, mapping) -# gif.create_gif() - - # plot results - p = PlotCreator(directory, name, titlePlot, statesToMonitor, - colours, labels) - p.plotSimulation() - -if __name__ == '__main__': - main() +'''Example model specification for a Skype-like system + +@author: Joe Schaul +''' +import networkx as nx + +from ComplexNetworkSim import NetworkSimulation, AnimationCreator, PlotCreator +from .agent_skypeClient import DISABLED, ENABLED, ENABLED_S, DISABLED_S +from .agent_skypeClient import Skype +from .environment_Skype import crash + +# Simulation constants +MAX_SIMULATION_TIME = 50.0 +TRIALS = 1 + +def main(): + combs = [] + + combs.append({}) + combs[0]["nodes"] = 600 + combs[0]["supernodes"] = 100 + combs[0]["permanent_supernodes"] = 1 + combs[0]['threshold'] = 12 + combs[0]['cache_size'] = 50 + combs[0]['kills'] = 40 + combs[0]['restart_time'] = "self.r.expovariate(1/4.0)" + combs[0]['restart_time_str'] = "exp" + combs[0]['agentClass'] = Skype + combs[0]['environmentAgent'] = crash + + combs.append(combs[0].copy()) + combs[1]['cache_size'] = 3 + + combs.append(combs[0].copy()) + combs[2]['kills'] = 60 + + combs.append(combs[0].copy()) + combs[3]['kills'] = 60 + combs[3]['cache_size'] = 3 + +# combs.append(combs[0].copy()) +# combs[4]['kills'] = 40 +# combs[4]['cache_size'] = 1 +# +# combs.append(combs[0].copy()) +# combs[4]['kills'] = 60 +# combs[4]['cache_size'] = 1 + + fixes = [] + for c in combs: + fixes.append(c.copy()) + for fix in fixes: + fix['restart_time'] = "4" + fix['restart_time_str'] = "fix" + + gausses = [] + for c in combs: + gausses.append(c.copy()) + for gauss in gausses: + gauss['restart_time'] = "abs(self.r.gauss(4, 1))" + gauss['restart_time_str'] = "gauss" + + + parameter_combinations = [] + parameter_combinations.extend(combs) + parameter_combinations.extend(fixes) + parameter_combinations.extend(gausses) + + for parameters in parameter_combinations: + dir = '_'.join(("skype600-", + str(parameters['supernodes']), + str(parameters['restart_time_str']), + str(parameters['threshold']), + str(parameters['cache_size']), + str(parameters['kills']), + )) + parameters['directory'] = dir + simulate(**parameters) + + +def simulate(**kwargs): + + # Model parameters + directory = kwargs.get("directory", 'skype') + agentClass = kwargs.get("agentClass", Skype) + environmentAgent = kwargs.get("environmentAgent", crash) + NUMBER_SUPERNODES = kwargs.get("supernodes", 90) + NUMBER_NODES = kwargs.get("nodes", 600) + NUMBER_PERMANENT_SUPERNODES = kwargs.get("permanent_supernodes", 1) + globalSharedParameters = {} + globalSharedParameters['supernodes'] = list(range(NUMBER_SUPERNODES)) + globalSharedParameters['permanent_supernodes'] = list(range(NUMBER_PERMANENT_SUPERNODES)) + globalSharedParameters['threshold'] = kwargs.get("threshold", 12) + globalSharedParameters['cache_size'] = kwargs.get("cache_size", 20) + globalSharedParameters['kills'] = kwargs.get("kills", 30) + globalSharedParameters['restart_time'] = kwargs.get('restart_time', "4.0") + + + # Output Parameters + statesToMonitor = [ENABLED, ENABLED_S] + colours = ["g", "^b", "0.5"] + mapping = {ENABLED:"g", ENABLED_S: "b", DISABLED:"0.2", DISABLED_S: "0.4"} + labels = ["Online nodes", "Online supernodes", "Offline"] + name = "skype" + titlePlot = "Skype simulation, %i trials, threshold=%i, cacheSize=%i \n supernode_restart_distr=%s" % \ + (TRIALS, globalSharedParameters['threshold'], globalSharedParameters['cache_size'], + str(globalSharedParameters['restart_time'])) + titleVisual = "Skype visualisation" + + +#####topology#### + G = nx.Graph(nx.complete_graph(NUMBER_SUPERNODES)) + G.add_nodes_from(range(NUMBER_SUPERNODES,NUMBER_NODES+NUMBER_SUPERNODES)) + + states = [DISABLED for node in G.nodes_iter()] + + + # run simulation + simulation = NetworkSimulation(G, + states, + agentClass, + directory, + MAX_SIMULATION_TIME, + TRIALS, + environmentAgent, + **globalSharedParameters) + simulation.runSimulation() + +# # run visualisation +# gif = AnimationCreator(directory, name, titleVisual, mapping) +# gif.create_gif() + + # plot results + p = PlotCreator(directory, name, titlePlot, statesToMonitor, + colours, labels) + p.plotSimulation() + +if __name__ == '__main__': + main()