Skip to content

AI-generated content

This chapter was automatically generated by an AI agent and has not been reviewed by a human. Read with caution.

Building Models from Scratch

Epiworld's built-in models cover many common compartmental structures, but the real power of the library lies in defining your own states and update functions. This chapter shows you how.

Defining States with add_state()

Every state in the model is registered through Model::add_state(). The function takes a human-readable name and an optional update function:

epiworld::Model<> model;

// A state with a custom update function
model.add_state("Susceptible", epiworld::default_update_susceptible<>);

// A state whose update function is provided separately (or is absorbing)
auto infected_id = model.add_state("Infected", epiworld::default_update_exposed<>);
auto removed_id  = model.add_state("Removed");
model <- ModelSIR(
  name              = "a virus",
  prevalence        = 0.01,
  transmission_rate = 0.8,
  recovery_rate     = 0.5
)
# In R, built-in models register their states automatically.
# For fully custom models, see epiworldR::Model and add_state().

add_state() returns the integer ID of the new state. You will need these IDs when configuring viruses (see Viruses, Tools & Global Events).

Writing Update Functions

An update function is called once per day for every agent in that state. Its signature is:

template<typename TSeq>
void my_update(epiworld::Agent<TSeq>* agent, epiworld::Model<TSeq>* model);

Inside the function you can:

  • Inspect or change the agent's state (agent->change_state()).
  • Add or remove viruses (agent->add_virus(), agent->rm_virus()).
  • Add or remove tools (agent->add_tool(), agent->rm_tool()).
  • Query the agent's neighbors (agent->get_neighbors()).
  • Access the model's random number generator (model->runif()).
  • Read or write model parameters (model->par()).

Example: A Custom "Isolated" State

template<typename TSeq>
void update_isolated(
    epiworld::Agent<TSeq>* agent,
    epiworld::Model<TSeq>* model
) {
    // Each day there is a 5% chance the agent leaves isolation
    if (model->runif() < 0.05)
        agent->change_state(
            *model, model->get_state_id("Susceptible")
        );
}

// Register it:
model.add_state("Isolated", update_isolated<>);

Building a Simple SIR from Scratch

Putting it all together, here is a minimal SIR model that does not use the built-in ModelSIR:

#include "epiworld.hpp"
using namespace epiworld;

int main() {
    Model<> model;

    // 1. Define states
    model.add_state("Susceptible", default_update_susceptible<>);
    auto infected_id = model.add_state("Infected", default_update_exposed<>);
    auto removed_id  = model.add_state("Removed");

    // 2. Create a virus
    Virus<> covid("covid 19", 0.05, true);
    covid.set_prob_infecting(0.8);
    covid.set_state(infected_id, removed_id, removed_id);
    model.add_virus(covid);

    // 3. Create a tool (vaccine)
    Tool<> vax("vaccine", 0.5, true);
    vax.set_susceptibility_reduction(0.95);
    model.add_tool(vax);

    // 4. Generate a contact network
    model.agents_from_adjlist(
        rgraph_smallworld(1000, 5, 0.01, false, model)
    );

    // 5. Run
    model.run(100, 123);
    model.print();

    return 0;
}

The key insight is that default_update_susceptible and default_update_exposed are library-provided update functions that implement standard infection and recovery logic. You can replace them with entirely custom functions when you need non-standard behavior.

Custom Network Structures

By default, agents_smallworld() or agents_from_adjlist() create the contact network. You can also supply a rewiring function that modifies the network at each time step:

template<typename TSeq>
void quarantine_rewire(
    std::vector<epiworld::Agent<TSeq>>* agents,
    epiworld::Model<TSeq>* model,
    epiworld_double proportion
) {
    for (auto& agent : *agents) {
        if (agent.get_status() == model->get_state_id("Infected"))
            agent.clear_neighbors();
    }
}

model.set_rewire_fun(quarantine_rewire<>);
model.set_rewire_prop(0.1);

When model.rewire() is called (automatically at each step), the function executes and can add, remove, or rearrange edges. This is useful for modeling social distancing, school closures, or other behavioral interventions.

What's Next