Epiworld is a general framework for epidemiological simulations aimed at modeling infectious diseases and their dynamics.
It is designed for flexibility and scalability, allowing researchers and policymakers to simulate disease outbreaks, interventions, and public health responses.
Built on robust epidemiological models (SIR, SEIR, SIS, etc.), Epiworld enables complex simulations involving individual behaviors and network structures.
Goal of Epiworld
Facilitate the simulation of infectious disease spread across populations.
Incorporates network-based interactions, vaccination strategies, and public health interventions.
Use Cases
Public health policy planning (e.g., vaccination strategies, social distancing policies).
Disease outbreak prediction and monitoring.
Educational tools for epidemiology students and researchers.
Real-time decision-making during localized (or larger) outbreaks.
Architecture (Model Building)
Architecture (Updates)
Wrappers and Methods of Interface
EpiworldR: R wrapper that makes it easy to run simulations in the R environment, favored by data scientistss.
EpiworldPy (In Progress): Python wrapper, enabling the use of Epiworld in Python, which is also favored in data science and machine learning spheres.
EpiworldRShiny: A non-programatic of interacting with Epiworld.
EpiworldWeb (In Progress) : A port of Epiworld to WebAssembly, allowing serverless simulations.
A Look at EpiworldRShiny
Preview of EpiworldWeb
Comparison of Wrappers
What does EpiworldPy exist over EpiworldR?
EpiworldRShiny must be run locally on a computer despite being packaged as a website, whereas EpiworldWeb needs no prerequisites.
Example
Demo: Influenza Outbreak in SLC (Original C++)
/* Creating the model. */epimodels::ModelSEIRCONN<> model("Influenza",/* Name of the virus. */50000,/* Population. */0.01,/* Initial prevalence. */0.9,/* Contact rate. */0.3,/* Transmission rate. */0.5/* Recovery rate. */);/* Running and checking the results. */model.run(50,123);model.print();
Demo: Influenza Outbreak in SLC (EpiworldPy)
import epiworldpy as epiworld# Create the model.model = epiworld.ModelSIRCONN( name ='Influenza', n =50000, prevalence =.001, contact_rate =0.9, transmission_rate =0.3, recovery_rate =0.5)# Running and checking the results.model.run(50, 123)model.print()
Demo: Graphing Model Compartments
import numpy as npimport matplotlib.pyplot as plt# Get the data from the database.history = model.get_db().get_hist_total()# Extract unique states and dates.unique_states = np.unique(history['states'])unique_dates = np.unique(history['dates'])# Initialize a dictionary to store time series data for each state.time_series_data = {state: [] for state in unique_states}# Populate the time series data for each state.for state in unique_states:for date in unique_dates:# Get the count for the current state and date. mask = (history['states'] == state) & (history['dates'] == date) count = history['counts'][mask][0] time_series_data[state].append(count)# Start the plotting!plt.figure(figsize=(10, 6))for state in unique_states: plt.plot(unique_dates, time_series_data[state], label=state)plt.xlabel('Day')plt.ylabel('Count')plt.title('Influenza SEIR Model Data')plt.legend()plt.grid(True)plt.show()
Demo: Graphing Model Compartments
Demo: Effective Reproduction Rates
# Get the data from the database.reproductive_data = model.get_db().get_reproductive_number()# Start the plotting!plt.figure(figsize=(10, 6))for virus_id, virus_data inenumerate(reproductive_data): average_rts =list()for date_data in virus_data:ifnot date_data:continue keys_array = np.array(list(date_data.values()), dtype=np.float64) average_rts.append(np.mean(keys_array)) plt.plot(range(0, len(average_rts)), average_rts, label=f"Virus {virus_id}")plt.xlabel('Date')plt.ylabel('Effective Reproductive Rate')plt.title('Influenza SEIR Model Effective Reproductive Rate')plt.legend()plt.grid(True)plt.show()
Demo: Effective Reproduction Rates
Demo: Case Type Incidence
import pandas as pd# Get the data from the database.transition_matrix = pd.DataFrame(model.get_db().get_hist_transition_matrix(False))grouped = transition_matrix.groupby(['dates', 'states_to'])['counts'].sum().unstack().fillna(0)daily_incidence = grouped.diff().fillna(0)# Plot!plt.figure(figsize=(10, 6))plt.plot(daily_incidence.index, daily_incidence['Infected'], label='New Infected')plt.plot(daily_incidence.index, daily_incidence['Recovered'], label='New Recovered')plt.title('Daily Incidence of Infected and Recovered Cases')plt.xlabel('Days')plt.ylabel('Number of New Cases')plt.legend()plt.grid(True)plt.show()
Demo: Case Type Incidence
Demo: Generation Time
from collections import defaultdict# Get the data from the database.generation_time = model.get_db().get_generation_time()agents = generation_time['agents']viruses = generation_time['viruses']times = generation_time['times']gentimes = generation_time['gentimes']# Data formatting.unique_viruses = np.unique(viruses)data = defaultdict(lambda: defaultdict(list))for agent, virus, time, gentime inzip(agents, viruses, times, gentimes): data[virus][time].append(gentime)average_data = {virus: {} for virus in unique_viruses}for virus, time_dict in data.items():for time, gentime_list in time_dict.items(): average_data[virus][time] = np.mean(gentime_list)# Plotting.plt.figure(figsize=(10, 6))for virus, time_dict in average_data.items(): times =sorted(time_dict.keys()) gentimes = [time_dict[time] for time in times] plt.plot(times, gentimes, label=f'Virus {virus}')plt.xlabel('Date')plt.ylabel('Generation Time')plt.title('Influenza SEIR Model Generation Time')plt.legend()plt.grid(True)plt.show()
Demo: Generation Time
Demo: Agent-Agent Interactions
import networkx as nxfrom matplotlib.animation import FuncAnimation# Get the data from the database.transmissions = model.get_db().get_transmissions()start = transmissions['source_exposure_dates']end = transmissions['dates']source = transmissions['sources']target = transmissions['targets']days =max(end)graph = nx.Graph()fig, ax = plt.subplots(figsize=(6,4))# Animation function.to_track = { source[0] }def update(frame): ax.clear() agents_involved_today =set() agents_relationships_we_care_about = []# Get only the agents involved in the current frame.for i inrange(len(start)):if start[i] <= frame <= end[i]: agents_involved_today.add((source[i], target[i]))# Get only today's agents who have some connection to agents# we've seen before.for agent in agents_involved_today:if agent[0] in to_track or agent[1] in to_track: to_track.add(agent[0]) to_track.add(agent[1]) graph.add_edge(agent[0], agent[1])# Lay and space them out. pos = nx.kamada_kawai_layout(graph) options = {"with_labels": True,"node_size": 300,"font_size": 6,"node_color": "white","edgecolors": "white","linewidths": 1,"width": 1, }# Graph! nx.draw_networkx(graph, pos, **options) ax.set_title(f"Influenza SEIR Model Agent Contact (Day {frame})")ani = FuncAnimation(fig, update, frames=int(days/3), interval=200, repeat=False)plt.show()
Demo: Agent-Agent Interactions
Demo: Multiple Viruses
model = epiworld.Model()model.add_state("Susceptible", epiworld.UpdateFun.default_update_susceptible());model.add_state("Infected", epiworld.UpdateFun.default_update_exposed());model.add_state("Recovered", epiworld.UpdateFun.default());model.add_state("Removed", epiworld.UpdateFun.default());covid19 = epiworld.Virus( name ='covid-19', prevalence =.001, as_proportion =False, prob_infecting =0.2, prob_recovery =0.92, prob_death =0.08, post_immunity =0.7, incubation =5,)flu = epiworld.Virus( name ="flu", prevalence =0.003, as_proportion =False, prob_infecting =1.0, prob_recovery =0.95, prob_death =0.05, post_immunity =0.6, incubation =9)# Black magic.flu.set_state(1, 2, 3);covid19.set_state(1, 2, 3);# Add it to our model.model.add_virus(flu)model.add_virus(covid19)# We need agents to infect.model.agents_smallworld(100000, 4, False, 0.01);# Run things.model.run(100, 223)model.print(False)
Demo: Multiple Viruses
_________________________________________________________________________
Running the model...
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| done.
done.
________________________________________________________________________________
________________________________________________________________________________
SIMULATION STUDY
Name of the model : (none)
Population size : 100000
Agents' data : (none)
Number of entities : 0
Days (duration) : 100 (of 100)
Number of viruses : 2
Last run elapsed t : 2.00ms
Last run speed : 3347.84 million agents x day / second
Rewiring : off
Global events:
(none)
Virus(es):
- flu
- covid-19
Tool(s):
(none)
Model parameters:
(none)
Distribution of the population at time 100:
- (0) Susceptible : 100000 -> 100000
- (1) Infected : 0 -> 0
- (2) Recovered : 0 -> 0
- (3) Removed : 0 -> 0
Transition Probabilities:
- Susceptible 1.00 0.00 0.00 0.00
- Infected - - - -
- Recovered - - - -
- Removed - - - -
<epiworldpy._core.Model at 0x1690dc130>
Demo: Model Control Tools
# Bring this back for simplicity.model = epiworld.ModelSIRCONN( name ='covid-19', n =50000, prevalence =.001, contact_rate =2.0, transmission_rate =0.3, recovery_rate =0.13)vaccine = epiworld.Tool( name ="Vaccine", prevalence =0.5, as_proportion =True, susceptibility_reduction =0.9, transmission_reduction =0.5, recovery_enhancer =0.5, death_reduction =0.9)model.add_tool(vaccine)# Run things.model.run(100, 223)model.print(False)
Demo: Model Control Tools
_________________________________________________________________________
Running the model...
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| done.
done.
________________________________________________________________________________
________________________________________________________________________________
SIMULATION STUDY
Name of the model : Susceptible-Infected-Removed (SIR) (connected)
Population size : 50000
Agents' data : (none)
Number of entities : 0
Days (duration) : 100 (of 100)
Number of viruses : 1
Last run elapsed t : 101.00ms
Last run speed : 49.34 million agents x day / second
Rewiring : off
Global events:
- Update infected individuals (runs daily)
Virus(es):
- covid-19
Tool(s):
- Vaccine
Model parameters:
- Contact rate : 2.0000
- Recovery rate : 0.1300
- Transmission rate : 0.3000
Distribution of the population at time 100:
- (0) Susceptible : 49950 -> 23561
- (1) Infected : 50 -> 61
- (2) Recovered : 0 -> 26378
Transition Probabilities:
- Susceptible 0.99 0.01 0.00
- Infected 0.00 0.85 0.15
- Recovered 0.00 0.00 1.00
<epiworldpy._core.ModelSIRCONN at 0x1690dcfb0>
Demo: Model Control Tools (Graph Proof)
# Get data from the database.transition_matrix = pd.DataFrame(model.get_db().get_hist_transition_matrix(False))grouped = transition_matrix.groupby(['dates', 'states_to'])['counts'].sum().unstack().fillna(0)daily_incidence = grouped.diff().fillna(0)# Plot!plt.figure(figsize=(10, 6))plt.plot(daily_incidence.index, daily_incidence['Infected'], label='New Infected')plt.plot(daily_incidence.index, daily_incidence['Recovered'], label='New Recovered')plt.title('Daily Incidence of Infected and Recovered Cases (w/ Vaccination)')plt.xlabel('Days')plt.ylabel('Number of New Cases')plt.legend()plt.grid(True)plt.show()
Demo: Model Control Tools (Graph Proof)
Demo: Model Control Tools (Graph Proof)
# Get data from the database.reproductive_data = model.get_db().get_reproductive_number()# Start the plotting!plt.figure(figsize=(10, 6))for virus_id, virus_data inenumerate(reproductive_data): average_rts =list()for date_data in virus_data:ifnot date_data:continue keys_array = np.array(list(date_data.values()), dtype=np.float64) average_rts.append(np.mean(keys_array)) plt.plot(range(0, len(average_rts)), average_rts, label=f"Virus {virus_id}")plt.xlabel('Date')plt.ylabel('Effective Reproductive Rate')plt.title('COVID-19 SIR Model Effective Reproductive Rate (w/ Vaccination)')plt.legend()plt.grid(True)plt.show()
Demo: Model Control Tools (Graph Proof)
Closing Thoughts
Closing Thoughts
epiworld is a high-performance C++ library designed for building agent-based epidemiological models.
A flexible framework, allowing callers to define custom states and update dynamics for their models.
It has as been utilized in numerous epidemiological agent-based modeling research projects and studies.