Building a network generator

Network generators abstract a class of networks which we sample to run experiments.

An experiment consists of two parts: a process to run ot execute the experiment, and the network over which that process runs. Both process and network are initialised for each experiment.

Each process is an instance of (a sub-class of) Process: a typical example is an SIR compartmented model process.

Each network is generated by a network generator, an instance of (a sub-class of) NetworkGenerator. There are several standard classes of networks used extensively in network science – primarily ER networks and BA networks – and epydemic provides built-in generators for these: see Standard network generators for a list.

However, there are a huge range of network classes that epydemic doesn’t include, and there are two basic ways to use such networks in your simulations.

The easy way: Create the network you need

The easiest way to make use of a network is to create it and pass it to the experiment directly. For example, suppose you wanted to make use of one of the network generator functions in networkx, such as networkx.random_regular_graph(). You might then do the following:

g = networkx.random_regular_graph(20, 1000)

params = dict()

e = epydemic.StochasticDynamics(SIR(), g)
rc = e.set(params).run()

The advantages of this approach are simplicity and flexibility: you can use all the features of networkx, in a small amount of code. The disadvantages are that the parameters that you used to create the network are unrecorded, and you need to restructure your code if you want to re-use the same class of network. You also have to be careful if you decided to run your experiments at scale to avoid passing large networks to a compute cluster.

All these disadvantages are solved immediately by writing a custom generator.

The flexible way: Writing a custom network generator

Let’s build a generator for the class of random regular graphs. We need to do three things:

  1. We need to decide what parameters will control the creation of the network;

  2. We need to provide the code to build the network; and

  3. We need to indicate which topology an experiment has used.

The first is straightforward in this case. The parameters are stated in the networkx documentation as the degree d and the number of ndoes n. We define keys for use in experimental parameter dicts for both these values.

The second is straightforward too in this case. We simply unpack the parameters and pass them to the appropriate networkx function.

The third means defining a method that returns an arbitrary string to identify this topology, which will be included into an experiment’s parameters as a clue to the topology of network that underlies the experiment. By convention we use a string that relates to the names of the parameters that control the distribution, but any string is fine.

Putting these things together, we get:

class RandomRegularNetwork(epydemic.NetworkGenerator):
    '''Generate random regular networks.'''

    DEGREE = 'RRG.degree'  #: Experimental parameter for the degree of nodes
    N = 'RRG.N'            #: Experimental parameter for the number of nodes

    def __init__(self, params=None, limit=None):
        super(RandomRegularNetwork, self).__init__(params, limit)

    def topology(self):
       '''Return a flag to identify the topology.

       :returns: the topology flag'''
       return 'RRG'

    def _generate(self, params):
        '''Generate a random regular network.

        :param params: the experimental parameters
        :returns: a network'''

        d = params[self.DEGREE]
        n = params[self.N]

        g = networkx.random_regular_graph(d, n)
        return g

We then pass this generator to the the experiment:

params = dict()
params[RandomRegularNetwork.DEGREE] = 20
params[RandomRegularNetwork.N] = 1000

e = epydemic.StochasticDynamics(SIR(), RandomRegularNetwork())
rc = e.set(params).run()

If we were to examine the rc results dict we’d find, recorded in the experimental parameters, the parameters describing the network over which the process was run. Furthermore if we decided to make use of a compute cluster we would use exactly the same code, which would then generate the networks needed by the experiment alongside the experiment itself, minimising the use of bandwidth.

Note

For a larger example of a network generator that doesn’t simply call an existing function, see the cookbook recipe on Modelling human contact networks.