Generating functions

Network scientists often use generating functions as a means of computing with entire probability distributions at once. epydemic includes a small generating functions library suitable for using this approach in conjunction with simulations if desired, for example to check whether a given theoretical prediction is upheld numerically.

Importing the library

Generating functions aren’t imported along with the rest of epydemic. Instead, import the epydemic.gf package:

import epydemic.gf

Alternatively (and better), import only the functions you need:

from epydemic.gf import gf_from_network, gf_er

Construction

epydemic’s generating functions can be constructed in four different ways: from an explicit series, from a sequence of coefficients, from a function that returns the coefficient of a given term, or from a network.

epydemic.gf.gf_from_series(f)

Create a generating function from the function that defines the complete series.

Parameters:

f (Callable[[float], float]) – the series function

Return type:

GF

Returns:

the generating function

epydemic.gf.gf_from_coefficients(cs)

Create a generating function from a list of coefficients.

Parameters:

cs (List[float]) – the list of coefficients

Return type:

GF

Returns:

a generating function

epydemic.gf.gf_from_coefficient_function(f)

Create a generating function from a function that computes the coefficient for the given term.

Parameters:

f (Callable[[int], float]) – the coefficients function

Return type:

GF

Returns:

a generating function

epydemic.gf.gf_from_network(g)

Create a generating function representing the actual distribution of degrees in the given network.

Parameters:

g (Graph) – the network

Return type:

GF

Returns:

a generating function

There are also standard constructors for common generating functions corresponding to common degree distributions.

Warning

When implementing a generating function using a series with gf_from_series() it’s important that the series can be manipulated and computed in certain specific ways. See Providing a custom series for details. (The other construction methods have no similar traps for the unwary.)

In use

Generating functions expose several main functions. Firstly, a generating function can be evaluated by applying it to a value:

isolated_fraction = gf(0)

Secondly, a generating function can be queried to extract the coefficient attached to a particular term:

nodes_of_degree_5 = gf[5]

Thirdly, a generating function can be differentiated one or more times:

G0 = gf_er(50000, 20)

# extract the mean degree
G0prime = G0.dx()
mean_degree = G0prime(1)

# extract the degree-5 nodes (the hard way)
nodes_of_degree_5 = (G0.dx(5))(0) / math.factorial(5)

Finally, generating functions have (some) algebra implemented for them directly. They can be multiplied and divided by a constant, which is use when (for example) computing an epidemic threshold:

N = int(1e4)
kmean = 10
pRemove = 0.002
G0 = gf_er(N, kmean=kmean)

# compute G1 from the first derivative of G0
G1 = G0.dx() / N

# compute the critical threshold
p_c = float(pRemove / G1.dx()(1))

They can also be added together, subtracted one from the other, and multiplied together. These operations all return new generating functions, and so can be combined with differentiation. To check that differentiation is linear, for example, we might do:

# create two generating functions from coefficients
gf1 = gf_from_coefficients([3, 2, 3, 0, 1])
gf2 = gf_from_coefficients([0, 2])

# differentiate twice, two different ways
d1 = (gf1 * gf2).dx(2)
d2 = (gf1 * gf2).dx().dx()

# make sure the results are the same
for i in range(6):
   self.assertEqual(d1[i], d2[i])