Skip to main content

library.networks

library.networks

Additional network types: household, cluster, spatial, and theoretical.

HouseholdNet builds households from DHS-style survey data; ClusterNet divides agents into fully connected clusters, and HybridNet is Covasim’s population of households, schools, workplaces, and community; DiskNet connects agents that are spatially close, GeoNet connects them across real places, and GridNet puts them on a lattice; NullNet is useful for debugging, SmallWorldNet and ScaleFreeNet are the classic graphs, and StaticNet turns a NetworkX graph into a network (by default, an Erdős–Rényi graph). Core network classes (ss.RandomNet, ss.DegreeNet, ss.SexualNet, etc.) live in starsim.networks.

Most of these networks run as agents only. Those a compartmental run can approximate by a mean number of contacts are marked in their files.

Used with: agents and compartments; declared and coded modules

Classes

Name Description
ClusterNet Fully connected clusters of agents, e.g. households, schools, or workplaces
DiskNet Disk graph in which edges are made between agents located within a user-defined radius.
GeoNet Random contacts between real places
GridNet Agents on a square grid, in contact with their neighbors
HouseholdNet A household contact network built from DHS-style survey data.
HybridNet Covasim v3’s “hybrid” population: households, schools, workplaces, and community
NullNet A convenience class for a network of size n that only has self-connections with a weight of 0.
ScaleFreeNet A scale-free (Barabási-Albert) network: a few agents have many contacts, and most have few
SmallWorldNet A small-world (Watts-Strogatz) network: mostly local contacts, with a few long-range ones
StaticNet A network class of static partnerships converted from a networkx graph. There’s no formation of new partnerships

ClusterNet

library.networks.ClusterNet(
    pars=None,
    cluster_size=_,
    who=_,
    dynamic=_,
    **kwargs,
)

Fully connected clusters of agents, e.g. households, schools, or workplaces

Each agent (optionally, only those within an age range) is assigned to one cluster, with cluster sizes drawn from cluster_size, and every pair of agents within a cluster is connected. Agents are assigned to clusters in a random order (from a CRN-safe random number per agent), so clusters are not correlated with other networks. Based on make_microstructured_contacts() from Covasim v3: as there, clusters of size 0 are skipped, and the last cluster is truncated to fit.

Note: CRN safe, unless dynamic=True and people are born or die.

Parameters

Name Type Description Default
cluster_size int/ss.Dist the size of each cluster; a number is the mean of a Poisson distribution (a size, like a target number of partners in ss.DegreeNet; for clusters all of one size, give ss.constant(n)) _
who Cond / str / func if given, who takes part: a condition over people (e.g. (ss.person.age >= 6) & (ss.person.age < 22)), as for ss.RandomNet _
dynamic bool if True, the clusters are recreated on each timestep; if False (default), they are created once and never change _
pars dict the same parameters, as a dict None

Example

households = ssl.ClusterNet(name='households', cluster_size=ss.poisson(3))
sim = ss.Sim(diseases='sis', networks=households)
sim.run()

Methods

Name Description
add_pairs Assign agents to clusters, and connect every pair of agents within each cluster
step If dynamic, recreate the clusters; otherwise, do nothing
add_pairs
library.networks.ClusterNet.add_pairs()

Assign agents to clusters, and connect every pair of agents within each cluster

step
library.networks.ClusterNet.step()

If dynamic, recreate the clusters; otherwise, do nothing

DiskNet

library.networks.DiskNet(pars=None, **kwargs)

Disk graph in which edges are made between agents located within a user-defined radius.

Interactions take place within a square with edge length of 1. Agents are initialized to have a random position and orientation within this square. On each time step, agents advance v*dt in the direction they are pointed. When encountering a wall, agents are reflected.

Edges are formed between two agents if they are within r distance of each other.

Note: CRN safe.

Parameters

Name Type Description Default
r float radius within which edges are formed
v rate speed at which agents move, a distance per unit time (e.g. ss.freqperday(0.05))
pars dict the same parameters, as a dict None

Attributes

Name Type Description
x FloatArr x position, in [0, 1]
y FloatArr y position, in [0, 1]
theta FloatArr direction of travel, in radians

Examples

import starsim as ss
import starsim.library as ssl

sim = ss.Sim(diseases='sis', networks=ssl.DiskNet(r=0.05))
sim.run()

Methods

Name Description
add_pairs Generate contacts
add_pairs
library.networks.DiskNet.add_pairs()

Generate contacts

GeoNet

library.networks.GeoNet(
    places='Kenya',
    level=1,
    local=0.9,
    c=2.0,
    pars=None,
    **kwargs,
)

Random contacts between real places

Each agent lives in a place, in proportion to its population. Most of an agent’s contacts are in their own place, and the rest are in other places, more of them in places that are large and near, as in a gravity model: the fraction going from place i to place j is in proportion to pop_j/distance_ij**c. Within each place, contacts are paired at random. A country’s places need no download (see ss.geo.geography).

The place of each agent is the state place, an index into geo.names. Like ss.RandomNet, this is not CRN safe when people are born or die.

Parameters

Name Type Description Default
places str/ss.geo.geography a country by name, or a geography with populations and distances 'Kenya'
level int for a country, the administrative level (see ss.geo.geography) 1
local float the fraction of contacts in an agent’s own place 0.9
c float how quickly contacts fall off with distance 2.0
kwargs dict passed to ss.RandomNet, e.g. n_contacts and dur
pars dict the same parameters, as a dict None

Examples

import starsim as ss
import starsim.library as ssl

sim = ss.Sim(n_agents=10_000, diseases='sis', networks=ssl.GeoNet('Kenya', n_contacts=10))
sim.run()

Methods

Name Description
contact_fracs The fraction of the contacts of the people of each place (rows) that are in each place (columns), as a sparse array
pair Find edges (see ss.RandomNet.pair()): each contact is in a place drawn from the fractions of the agent’s own place
contact_fracs
library.networks.GeoNet.contact_fracs()

The fraction of the contacts of the people of each place (rows) that are in each place (columns), as a sparse array

pair
library.networks.GeoNet.pair(inds, n_contacts)

Find edges (see ss.RandomNet.pair()): each contact is in a place drawn from the fractions of the agent’s own place

GridNet

library.networks.GridNet(diagonal=False, periodic=False, pars=None, **kwargs)

Agents on a square grid, in contact with their neighbors

Agents are placed row by row on the smallest square that holds them, as in lattice models of spatial spread. The network is fixed, so agents born later have no contacts. Note: CRN safe.

Parameters

Name Type Description Default
diagonal bool if True, agents have 8 neighbors rather than 4 False
periodic bool if True, the edges of the grid wrap around, so that everyone has the same number of neighbors False
pars dict the same parameters, as a dict None

Examples

import starsim as ss
import starsim.library as ssl

sim = ss.Sim(n_agents=2500, diseases='sir', networks=ssl.GridNet(periodic=True))
sim.run()

HouseholdNet

library.networks.HouseholdNet(
    pars=None,
    dhs_data=None,
    dynamic=None,
    prob_move_out=_,
    **kwargs,
)

A household contact network built from DHS-style survey data.

When initialized, this network overrides the age (and optionally sex) of all agents in the sim and assigns each agent a household ID. Use with caution if other modules depend upon or alter age and sex.

Households are created by selecting a random household from the provided data and setting the age and sex of agents to match, repeating until all agents have been assigned to a household. Ages in the data are typically in integer years; a random fractional year is added so agents don’t share exact ages.

This network assumes only one mother per household. Births are automatically added to their mother’s household network.

Note: not CRN safe unless dynamic=False (the partners who move out are drawn together).

Parameters

Name Type Description Default
dhs_data DataFrame / str A pandas or Sciris dataframe with columns hh_id and ages. Optionally also sexes. The ages column should contain comma-separated age strings (e.g. "72, 17, 30"). If sexes is included, it should contain comma-separated values using DHS convention (1 = male, 2 = female) with the same number of entries as ages. Pass 'default' to use synthetic data (see make_default_data()), e.g. for demos and testing. None
dynamic bool If True, households evolve over time: one female is assigned as head of each household, pregnant non-head females may move out to form new households, and births are added to the mother’s household, which needs ss.Pregnancy. If False, the network is static and step() is a no-op. By default, households evolve if the sim has ss.Pregnancy, and are static otherwise. None
prob_move_out float Probability a non-head female moves out to start her own household, evaluated once at the start of each pregnancy. Default 0.7. Only used when households evolve. _
pars dict the same parameters, as a dict None

To update the households less often than every step of the sim, give the network its own step, e.g. dt=ss.years(1).

The expected dataframe format is::

    hh_id                ages          sexes
0       0          72, 17, 30        1, 1, 2
1       1                  37              2
2       2          13, 55, 36        2, 1, 2
3       3  52, 13, 12, 64, 53     1, 2, 1, 2
4       4              30, 66           1, 1

Data in this format can be obtained from the DHS Program. To prepare a DHS household dataset:

  1. Register and request access at https://dhsprogram.com

  2. Download a Household Recode (HR) dataset in Stata format (e.g. XXHR7xDT.zip)

  3. Use HouseholdNet.load_dhs() to extract the data::

    import starsim as ss; import starsim.library as ssl dhs_data = ssl.networks.HouseholdNet.load_dhs(‘XXHR7xDT/XXHR7xFL.DTA’) sim = ss.Sim(networks=ssl.networks.HouseholdNet(dhs_data=dhs_data, dynamic=False)) sim.run()

If real data are not available, synthetic data can be constructed::

import numpy as np
import sciris as sc
import starsim as ss; import starsim.library as ssl

n = 1000
age_strings = []
for i in range(n):
    household_size = np.random.randint(1, 6)
    ages = np.random.randint(0, 80, household_size)
    age_strings.append(sc.strjoin(ages))
dhs_data = sc.dataframe(hh_id=np.arange(n), ages=age_strings)

household = ssl.networks.HouseholdNet(dhs_data=dhs_data, dynamic=False)
sim = ss.Sim(diseases='sis', networks=household)
sim.run()
sim.plot()

Methods

Name Description
add_pairs Generate contacts by assigning agents to households sampled from the data.
create_new_households Find females that are pregnant and not a head of household.
init_post Add the first edges, and add a random fraction of a year to each age
init_pre Initialize the network: households evolve if the sim has ss.Pregnancy (unless dynamic says otherwise), which they need
load_dhs Load a DHS Household Recode (HR) Stata file and return a dataframe
make_default_data Generate synthetic household data, used when dhs_data='default'.
add_pairs
library.networks.HouseholdNet.add_pairs()

Generate contacts by assigning agents to households sampled from the data.

Households are drawn uniformly at random (with replacement) until they cover the whole population. Sampling, age/sex assignment, edge creation, and head-of-household selection are all vectorized rather than looped per household.

create_new_households
library.networks.HouseholdNet.create_new_households()

Find females that are pregnant and not a head of household. Move them and a randomly sampled male partner to a new household.

init_post
library.networks.HouseholdNet.init_post(add_pairs=True)

Add the first edges, and add a random fraction of a year to each age

Parameters
Name Type Description Default
add_pairs bool whether to add edges True
init_pre
library.networks.HouseholdNet.init_pre(sim)

Initialize the network: households evolve if the sim has ss.Pregnancy (unless dynamic says otherwise), which they need

Parameters
Name Type Description Default
sim Sim the sim being initialized required
load_dhs
library.networks.HouseholdNet.load_dhs(path)

Load a DHS Household Recode (HR) Stata file and return a dataframe suitable for use with HouseholdNet.

Reads the wide-format HR file, extracts per-member age (HV105) and sex (HV104) columns, filters to valid entries (age <= 95 and sex in [1, 2]), and returns a dataframe with columns hh_id, ages, and sexes.

Parameters
Name Type Description Default
path str / Path Path to a DHS Household Recode Stata file (e.g. XXHR7xFL.DTA). required
Returns
Name Type Description
sc.dataframe: A dataframe with columns hh_id, ages, and sexes ready for use with HouseholdNet(dhs_data=...).
Examples
import starsim as ss; import starsim.library as ssl
dhs_data = ssl.networks.HouseholdNet.load_dhs('ZZHR62FL.DTA')
sim = ss.Sim(networks=ssl.networks.HouseholdNet(dhs_data=dhs_data, dynamic=False))
sim.run()
make_default_data
library.networks.HouseholdNet.make_default_data(n=1000, seed=1)

Generate synthetic household data, used when dhs_data='default'.

Creates n households of 1-5 members with ages uniformly distributed between 0 and 80. Intended for demos and testing when real DHS data are not available; see load_dhs() for loading actual survey data.

Parameters
Name Type Description Default
n int number of synthetic households to generate 1000
seed int random seed for reproducibility 1
Returns
Name Type Description
sc.dataframe: A dataframe with columns hh_id and ages ready for use with HouseholdNet(dhs_data=...).

HybridNet

library.networks.HybridNet(
    pars=None,
    household_size=_,
    n_contacts=_,
    school_ages=_,
    work_ages=_,
    dynamic=_,
    method=_,
    clustered=_,
    **kwargs,
)

Covasim v3’s “hybrid” population: households, schools, workplaces, and community

When added to a sim, this expands into four separate networks: households (h), schools (s), and workplaces (w), each an ssl.ClusterNet, and community (c), an ss.RandomNet with Poisson-distributed contacts. Schools and workplaces are restricted by age. As in Covasim v3, the networks are static; unlike it, schools and workplaces are clusters by default, so that a person’s contacts also know each other (use clustered=False for Covasim v3’s random networks). With the default Poisson sizes, clusters give the same mean number of contacts as a random network. Each network can then be modified separately, e.g. sim.networks.s.

Transmissibility on each network is the disease’s, by network. Covasim v3’s relative transmissibility of the layers is ssl.HybridNet.rel_betas (3 for households, 0.6 for schools and workplaces, and 0.3 for the community), and ssl.HybridNet.betas() gives a disease’s beta for each layer from its beta per contact (by default Covasim’s, 0.016 a day), e.g. ss.SIS(beta=ssl.HybridNet.betas()), which is beta=dict(h=ss.perday(0.048), s=ss.perday(0.0096), w=ss.perday(0.0096), c=ss.perday(0.0048)).

Note: CRN safe, unless dynamic=True and people are born or die.

Parameters

Name Type Description Default
household_size int/ss.Dist the size of each household; a number is the mean of a Poisson distribution, as for ssl.ClusterNet _
n_contacts dict the number of contacts per agent in schools, workplaces, and community: a number, as for ss.RandomNet, is the same for everyone (rounded at random; with clustered=True, a cluster of one more than that), and a distribution gives its spread (default: Covasim’s, ss.poisson(20), ss.poisson(16), and ss.poisson(20)) _
school_ages list the age range of agents in schools _
work_ages list the age range of agents in workplaces _
dynamic bool whether the networks are recreated on each timestep _
method str how the targets of edges in random networks are chosen (see ss.RandomNet); ‘uniform’ as in Covasim v3 _
clustered bool if True (default), schools and workplaces are ssl.ClusterNets, sized to give n_contacts for numbers and Poisson and constant distributions (other distributions are used directly as cluster sizes, which gives a different mean); if False, they are ss.RandomNets, as in Covasim v3 _
pars dict the same parameters, as a dict None

Examples

sim = ss.Sim(diseases='sis', networks=ssl.HybridNet())
sim.run()
print(sim.networks.keys()) # ['h', 's', 'w', 'c']

# Larger households and smaller schools; n_contacts can be partial
hybrid = ssl.HybridNet(household_size=ss.poisson(3), n_contacts=dict(s=ss.poisson(10)))

# Covasim's transmissibility on each layer, for a disease with twice its beta per contact
sir = ss.SIR(beta=ssl.HybridNet.betas(ss.perday(0.032)))
sim = ss.Sim(diseases=sir, networks=ssl.HybridNet(), unit='day', stop=60).run()

Methods

Name Description
betas The beta of a disease on each layer, by name: its beta per contact times the layer’s relative transmissibility (rel_betas, Covasim v3’s)
expand Create the four networks; called automatically when added to a sim
step Not used, since the networks returned by expand() are used instead
betas
library.networks.HybridNet.betas(beta=None)

The beta of a disease on each layer, by name: its beta per contact times the layer’s relative transmissibility (rel_betas, Covasim v3’s)

Parameters
Name Type Description Default
beta float / rate the beta per contact (default Covasim’s, ss.perday(0.016)); a number is in the model’s unit None

Example:

ss.SIS(beta=ssl.HybridNet.betas()) # dict(h=ss.perday(0.048), s=ss.perday(0.0096), w=ss.perday(0.0096), c=ss.perday(0.0048))
expand
library.networks.HybridNet.expand()

Create the four networks; called automatically when added to a sim

step
library.networks.HybridNet.step()

Not used, since the networks returned by expand() are used instead

NullNet

library.networks.NullNet(n_people=None, **kwargs)

A convenience class for a network of size n that only has self-connections with a weight of 0. This network can be useful for debugging purposes or as a placeholder network during development for conditions that require more complex network mechanisms.

Guarantees there’s one (1) contact per agent (themselves), and that their connection weight is zero.

For an empty network (i.e., no edges), use ss.RandomNet(n_contacts=0).

Note: CRN safe.

Parameters

Name Type Description Default
n_people int number of agents in the network; defaults to the sim’s n_agents None

Methods

Name Description
init_pre Initialize the network, with the number of people from the sim if not given
step Not used for NullNet
init_pre
library.networks.NullNet.init_pre(sim)

Initialize the network, with the number of people from the sim if not given

Parameters
Name Type Description Default
sim Sim the sim being initialized required
step
library.networks.NullNet.step()

Not used for NullNet

ScaleFreeNet

library.networks.ScaleFreeNet(n_contacts=4, pars=None, **kwargs)

A scale-free (Barabási-Albert) network: a few agents have many contacts, and most have few

Each agent in turn joins half of n_contacts agents, with those who already have many contacts being more likely to be chosen, so the mean number of contacts is n_contacts. The network is fixed. Note: CRN safe.

Parameters

Name Type Description Default
n_contacts int the mean number of contacts of each agent (a whole even number) 4
pars dict the same parameters, as a dict None

SmallWorldNet

library.networks.SmallWorldNet(n_contacts=4, rewire=0.1, pars=None, **kwargs)

A small-world (Watts-Strogatz) network: mostly local contacts, with a few long-range ones

Agents are on a ring, in contact with their n_contacts nearest neighbors, and each contact is rewired to a random agent with probability rewire. The network is fixed. Note: CRN safe.

Parameters

Name Type Description Default
n_contacts int the number of neighbors of each agent (even; an odd number is rounded down) 4
rewire float the probability of rewiring each contact 0.1
pars dict the same parameters, as a dict None

StaticNet

library.networks.StaticNet(graph=None, pars=None, **kwargs)

A network class of static partnerships converted from a networkx graph. There’s no formation of new partnerships and initialized partnerships only end when one of the partners dies. The networkx graph can be created outside Starsim if population size is known. Or the graph can be created by passing a networkx generator function to Starsim.

If “seed=True” is passed as a keyword argument or a parameter in pars, it is replaced with the built-in RNG. The parameter “n” is supplied automatically to be equal to n_agents. With no graph, it is a random (Erdős–Rényi) graph: the one nx.fast_gnp_random_graph() would make, edge for edge, made without NetworkX (see random_graph()).

Note: CRN safe.

Parameters

Name Type Description Default
graph nx.Graph / func a NetworkX graph, or a function that makes one from the number of agents n (and p and seed, if it takes them); with none, a random (Erdős–Rényi) graph None
n_contacts float for a random graph, the mean number of contacts of each agent (used unless p is given)
p float for a random graph, the probability that each pair of agents is in contact
seed bool / int the seed of a graph function; True for the network’s own random numbers
kwargs dict other arguments, passed to the graph function

Examples

# Generate a networkx graph and pass to Starsim
import networkx as nx
import starsim as ss
import starsim.library as ssl
g = nx.scale_free_graph(n=10000)
ssl.StaticNet(graph=g)

# Pass a networkx graph generator to Starsim
ssl.StaticNet(graph=nx.erdos_renyi_graph, p=0.0001, seed=True)

Methods

Name Description
graph_pars Its parameters, and their defaults: a subclass that makes its own graph has its own
init_pre Initialize the network, with a random (Erdős–Rényi) graph if none is given
mean_contacts The mean degree of its graph (see ss.Network.mean_contacts()), or for a random graph not yet made, its number of contacts or its chance of each pair
validate_pop Check that the graph fits the number of agents and has edges
graph_pars
library.networks.StaticNet.graph_pars()

Its parameters, and their defaults: a subclass that makes its own graph has its own

init_pre
library.networks.StaticNet.init_pre(sim)

Initialize the network, with a random (Erdős–Rényi) graph if none is given

Parameters
Name Type Description Default
sim Sim the sim being initialized required
mean_contacts
library.networks.StaticNet.mean_contacts(sim)

The mean degree of its graph (see ss.Network.mean_contacts()), or for a random graph not yet made, its number of contacts or its chance of each pair

Parameters
Name Type Description Default
sim Sim the sim being initialized required
validate_pop
library.networks.StaticNet.validate_pop(n_agents)

Check that the graph fits the number of agents and has edges

Parameters
Name Type Description Default
n_agents int the number of agents required