Visualizations¶
The human eye is a powerful tool for pattern recognition.
Watching a simulation unfold can therefore be more enlightening
than just looking at final plots. epipack
comes with a
light-weight visualization framework that shows an animation of a
simulation of epipack.stochastic_epi_models.StochasticEpiModel
that can be found in epipack.vis()
.
Basics¶
A visualization can be started using epipack.vis.visualize()
. A model
object is passed to this function, alongside a stylized network and a time delta
which represents the amount of simulation time that is supposed to pass between
two consecutive visualization updates. Optionally, a config dictionary can
be passed.
The function then opens a window that comprises three basic elements:
The visualization. Here, the network is displayed including links and after each update, colors are updated according to node status changes. Links are switched off if nodes are quarantined. If you want to use this functionality, pass the argument
quarantine_compartments
(as a list of compartments that are considered quarantined).Plots. Here, the counts of each compartment are drawn as time series. If you do not want to use this functionality, turn it off with
config['show_curves'] = False
Additionally, you can ignore to plot compartment curves by settingplot_ignore_compartments = [ "S", ...]
Legend. A color-coded list of compartment names. Optionally, you can turn it off using
config['show_curves'] = False
.
You can customize many aspects of the visualization by adjusting the corresponding entry in the config dictionary. The default config dictionary is
_default_config = {
'plot_sampled_curve': True,
'draw_links':True,
'draw_nodes':True,
'n_circle_segments':16,
'plot_height':120,
'bgcolor':'#253237',
'curve_stroke_width':4.0,
'node_stroke_width':1.0,
'link_color': '#4b5a62',
'node_stroke_color':'#000000',
'node_color':'#264653',
'bound_increase_factor':1.0,
'update_dt':0.04,
'show_curves':True,
'draw_nodes_as_rectangles':False,
'show_legend': True,
'legend_font_color':None,
'legend_font_size':10,
'padding':10,
'compartment_colors':_colors,
'palette': "dark",
}
Note that you can interact with the visualization up to a certain level:
Zooming: Use your mouse to scroll
Panning: Use your mouse to drag
Pausing: "SPACE" key
Increasing the sampling time delta: repeatedly pressing the "UP" key
Decreasing the sampling time delta: repeatedly pressing the "DOWN" key
Network Layout¶
We will use the "Social circles: Facebook" data from SNAP in the following. First, download the data. In the console, do
wget --no-check-certificate https://snap.stanford.edu/data/facebook_combined.txt.gz
gunzip facebook_combined.txt.gz
which downloads and extracts the combined dataset. Now, epipack
expects a stylized network
like it's produced by Netwulf. Style the network manually like so
import numpy as np
import networkx as nx
import netwulf as nw
# load edges from txt file and construct Graph object
edges = np.loadtxt('facebook_combined.txt')
G = nx.Graph()
G.add_edges_from(edges)
# visualize and save visualization
network, config = nw.visualize(G)
nw.save("FB.json",network,config)
Now you can simply load the stylized network every time you need it.
Let's use this network style to simulate an SIR model that has an additional "X" compartment for quarantine of symptomatic individuals.
import netwulf as nw
from epipack.vis import visualize
from epipack import StochasticEpiModel
# load network
network, config, _ = nw.load('/Users/bfmaier/pythonlib/facebook/FB.json')
# get the network properties
N = len(network['nodes'])
edge_list = [ ( link['source'], link['target'], 1.0 ) for link in network['links'] ]
# define model
model = StochasticEpiModel(list("SIRX"),
N=N,
edge_weight_tuples=edge_list,
)
k0 = model.out_degree.mean()
R0 = 5
recovery_rate = 1/8
quarantine_rate = 1.5 * recovery_rate
infection_rate = R0 * (recovery_rate) / k0
# usual infection process
model.set_link_transmission_processes([
("I","S",infection_rate,"I","I")
])
# standard SIR dynamic with additional quarantine of symptomatic infecteds
model.set_node_transition_processes([
("I",recovery_rate,"R"),
("I",quarantine_rate,"X"),
])
# set initial conditions with a small number of infected
model.set_random_initial_conditions({'I':20,'S':N-20})
# in every step of the simulation/visualization, let a time of `sampling_dt` pass
sampling_dt = 0.12
# simulate and visualize, do not plot the "S" count,
# and remove links from nodes that transition to "X"
visualize(model,
network,
sampling_dt,
ignore_plot_compartments=['S'],
quarantine_compartments=['X'],
)
And this is the result:
Grid Layout¶
Sometimes, the positions of a network are not important. If this is the case, you can simply use a grid layout for the network. You can load the corresponding layout like so:
from epipack.vis import get_grid_layout
layout = get_grid_layout(number_of_nodes,windowwidth=400)
which will produce a window of width 400px.
For such a layout, it's recommended to draw nodes as rectangles.
You can do this by calling the visualize
function with
config['draw_nodes_as_rectangles'] = True
.
You could use this, for instance, to animate a well-mixed system like so
from epipack.vis import visualize, get_grid_layout
from epipack import StochasticSIRModel
# get the layout
N = 100 * 100
layout = get_grid_layout(N)
# define model
R0 = 3
recovery_rate = 1/8
model = StochasticSIRModel(N,R0,recovery_rate)
model.set_random_initial_conditions({'I':20,'S':N-20})
# start visualization where the "S" count won't be shown
sampling_dt = 0.5
visualize(model,network,sampling_dt,
ignore_plot_compartments=['S'],
config={'draw_nodes_as_rectangles':True}
)
Which yields
Lattice simulation¶
The grid layout can also be more than just showing a well-mixed simulation.
It is the natural representation of a lattice. In order to simulate on a 2D lattice,
construct the stochastic model with lattice links that you can get from
epipack.networks.get_2D_lattice_links()
.
from epipack import get_2D_lattice_links
N_side = 100
N = 100**2
links = get_2D_lattice_links(N,periodic=True,diagonal_links=True)
R0 = 3; recovery_rate = 1/8
model = StochasticSIRModel(N,R0,recovery_rate,
edge_weight_tuples=links)
This will produce a lattice network with periodic boundary conditions where nodes are connected to their diagonal neighbors, as well.
In a simulation, make sure to not draw links because they won't be visible anyway, i.e.
config['draw_links'] = False
The complete visualization code:
from epipack.vis import visualize, get_grid_layout
from epipack import StochasticSIRModel, get_2D_lattice_links
# define links and network layout
N_side = 100
N = N_side**2
links = get_2D_lattice_links(N_side, periodic=True, diagonal_links=True)
network = get_grid_layout(N)
# define model
R0 = 3; recovery_rate = 1/8
model = StochasticSIRModel(N,R0,recovery_rate,
edge_weight_tuples=links)
model.set_random_initial_conditions({'I':20,'S':N-20})
sampling_dt = 1
visualize(model,network,sampling_dt,
config={
'draw_nodes_as_rectangles':True,
'draw_links':False,
}
)
with result: