impact-hiv / NepidemiX

Python library for simulating contact processes on network.
BSD 3-Clause "New" or "Revised" License
6 stars 6 forks source link

How to set specific node to I(nfectious) status in a SIR Model #5

Open ghost opened 7 years ago

ghost commented 7 years ago

I want to study the power of influence of a specific node with SIR model, hence I want to know how to set a specific node to Infectious status, I would be grateful for any suggestions. Thanks.

ahrenberg commented 7 years ago

Hi,

Unfortunately I don't believe that there is an easy way to set a specific node to a specific status in the current version of NepidemiX. Sorry for that. Will consider if this could be added somehow in the future.

If you know some Python scripting one way to work around is to modify a graph saved from a previous simulation using e.g. the networkx module, manually setting the state. Then load it to a new simulation, and setting network_init = off.

If you want to go this route let me know how you specify your process and I can try to provide you an example. I am afraid it is not very elegant however.

ghost commented 7 years ago
from nepidemix.process import ExplicitStateProcess

from nepidemix.utilities.networkxtra import attributeCount, neighbors_data_iter

import numpy

class SIRProcess(ExplicitStateProcess):
    """
    S I R process,

    Attributes
    ----------
    beta - Infection rate.
    gamma - Recovery rate.
    """
    def __init__(self, beta, gamma):

        super(SIRProcess, self).__init__(['S', 'I', 'R'],
                                         [],
                                         runNodeUpdate = True,
                                         runEdgeUpdate = False,
                                         runNetworkUpdate = False,
                                         constantTopology = True)
        self.beta = float(beta)
        self.gamma = float(gamma)

    def nodeUpdateRule(self, node, srcNetwork, dt):
        # Read original node state.
        srcState = node[1][self.STATE_ATTR_NAME]
        # By default we have not changed states, so set
        # the destination state to be the same as the source state.
        dstState = srcState

        # Start out with a dictionary of zero neighbors in each state.
        nNSt = dict(zip(self.nodeStateIds,[0]*len(self.nodeStateIds)))
        # Calculate the actual numbers and update dictionary.
        nNSt.update(attributeCount(neighbors_data_iter(srcNetwork, node[0]),
                                   self.STATE_ATTR_NAME))

        # Pick a random number.
        eventp = numpy.random.random_sample()
        # Go through each state name, and chose an action.
        if srcState == 'S':
            if eventp < self.beta*nNSt['I']*dt:
                dstState = 'I'
        elif srcState == 'I':
            if eventp < self.gamma*dt:
                dstState = 'R'

        node[1][self.STATE_ATTR_NAME] = dstState

        return node

I use the sample code provided in the tutorial, since most of the code do not need to be changed, except for the setting of status of a specific node.

ghost commented 7 years ago

Hi, Are you still there? I have read the source code, but I think it is difficult to modify source code, so I write myself a new code. Thanks anyway.

ahrenberg commented 7 years ago

Hi, still here an actually looking at your problem atm. Though did take weekend off. ;-) Think you can solve the problem by overloading the initializeNetorkNodes method. Will be easier than externally modifying the network. This, like the nodeUpdateRule is a pre-defined method of the ExplicitStateProcess class. It is responsible for randomly distributing the node states over the network.

You could do something like:

 def initializeNetworkNodes(self, network, *args, **kwargs):
        """ Overloaded initializeNetworkNodes, sets node # 0 to I """
        # First call super class method
        network = super(SIRProcess, self).initializeNetworkNodes(network, *args, **kwargs)
        # Now the network is initialized with random states as normal.
        # NOTE: This ASSUMES that the string 'I' is among the
        # states given to the __init__ method above. Anything else
        # will give unexpected results.
        # Let's set node with id 0 to 'I'
        network.node[0][self.STATE_ATTR_NAME] = 'I'
        return network

For instance to set node 0 to 'I'. If you make sure to set all nodes to 'S' in your simulation config code you will then have all but one node set to 'S'. More advanced usages are also possible of course. The drawback here is that you will need to know the index of the node you wish to set.

Update: tested example and fixed one bug.

Not sure if it is needed if you have figured it out, but perhaps for someone else.

In any case, good luck!

Cheers,

.L