CovidContagionNetwork

This example extends the base covid_contagion model by introducing Melodie’s Network module, demonstrating how to simulate infection spreading through a social network.

Network: Project Structure

examples/covid_contagion_network
├── core/
│   ├── agent.py            # NetworkAgent (inherits from NetworkAgent)
│   ├── environment.py      # Handles infection propagation through network edges
│   ├── model.py            # Initializes the network and agent connections
│   ├── scenario.py         # Defines network topology parameters
│   └── ...
├── data/
│   ├── input/
│   │   ├── SimulatorScenarios.csv      # Defines network type and params (k, p, m)
│   │   └── ID_HealthState.csv
│   └── output/
└── main.py

Network: Key Changes

  1. Network Agents (``NetworkAgent``): Agents inherit from NetworkAgent, which provides them with a network attribute to access the network structure.

  2. Network Structure: The CovidModel now creates a Network object. This example supports two types of networks generated by NetworkX: - Watts-Strogatz (Small-World): Characterized by dense local clusters and short average path lengths. - Barabási-Albert (Scale-Free): Characterized by a few highly connected “hub” nodes. The network type and its corresponding parameters (e.g., k and p for Watts-Strogatz) are defined in SimulatorScenarios.csv.

  3. Network Infection: Infection spreads through the network connections. An infected agent can only infect its direct neighbors in the network graph.

Network: Input Data

The CovidScenario class is updated to handle network parameters:

  • Network Type: The network_type column in SimulatorScenarios.csv specifies whether to use “watts_strogatz” or “barabasi_albert”.

  • Network Parameters: Columns like network_param_k, network_param_p, and network_param_m provide the necessary parameters for the chosen network generation algorithm.

Network: Running the Model

Run the model using the provided entry script:

python examples/covid_contagion_network/main.py

This will generate Result_Simulator_Agents.csv and Result_Simulator_Environment.csv in the data/output folder.

Network: Code

This section shows the key code implementation for the network model.

Model Structure

Defined in core/model.py.

 1from typing import TYPE_CHECKING
 2
 3from Melodie import Model
 4
 5from .agent import CovidAgent
 6from .data_collector import CovidDataCollector
 7from .environment import CovidEnvironment
 8from .scenario import CovidScenario
 9
10if TYPE_CHECKING:
11    from Melodie import AgentList
12
13
14class CovidModel(Model):
15    """
16    Network contagion model.
17    Demonstrates the use of `Melodie.Network` for agent interaction.
18    """
19
20    scenario: "CovidScenario"
21    agents: "AgentList[CovidAgent]"
22    environment: CovidEnvironment
23    data_collector: CovidDataCollector
24
25    def create(self) -> None:
26        """
27        Create model components: Agents, Environment, DataCollector, and Network.
28        """
29        self.agents = self.create_agent_list(CovidAgent)
30        self.environment = self.create_environment(CovidEnvironment)
31        self.data_collector = self.create_data_collector(CovidDataCollector)
32        
33        # Create an empty network structure
34        self.network = self.create_network()
35
36    def setup(self) -> None:
37        """
38        Setup model components and build the network.
39        """
40        # 1. Initialize agents
41        self.agents.setup_agents(self.scenario.agent_num)
42
43        # 2. Build network connections based on scenario parameters
44        # This uses NetworkX generators under the hood.
45        self.network.setup_agent_connections(
46            agent_lists=[self.agents],
47            network_type=self.scenario.network_type,
48            network_params=self.scenario.get_network_params(),
49        )
50
51        # 3. Seed initial infections
52        self.environment.seed_infection(self.agents)
53
54    def run(self) -> None:
55        """
56        Main simulation loop.
57        """
58        for t in self.iterator(self.scenario.period_num):
59            self.environment.spread_on_network(self.agents)
60            self.environment.recover(self.agents)
61            self.environment.update_population_stats(self.agents)
62            self.data_collector.collect(t)
63        self.data_collector.save()

Environment Logic

Defined in core/environment.py.

 1from Melodie import Environment
 2from typing import TYPE_CHECKING
 3import random
 4
 5if TYPE_CHECKING:
 6    from Melodie import AgentList
 7    from .agent import CovidAgent
 8    from .scenario import CovidScenario
 9
10
11class CovidEnvironment(Environment):
12    """
13    Environment handles infection/recovery logic on the network structure.
14    """
15
16    scenario: "CovidScenario"
17
18    def setup(self):
19        """
20        Initialize macro-level statistical counters.
21        """
22        self.num_susceptible = 0
23        self.num_infected = 0
24        self.num_recovered = 0
25
26    def seed_infection(self, agents: "AgentList[CovidAgent]"):
27        """
28        Initial seeding of infection among agents.
29        """
30        for agent in agents:
31            agent.seed_infection(self.scenario.initial_infected_percentage)
32
33    def spread_on_network(self, agents: "AgentList[CovidAgent]"):
34        """
35        Network-based infection logic:
36        1. Iterate through all infected agents.
37        2. Find their neighbors in the network.
38        3. If a neighbor is susceptible, infect them with probability `infection_prob`.
39        """
40        newly_infected = []
41        for agent in agents:
42            # Only infected agents can spread the virus
43            if agent.health_state != 1:
44                continue
45            
46            # Get neighbors from the network structure
47            neighbors = self.model.network.get_neighbors(agent)
48            
49            for category, neighbor_id in neighbors:
50                # Retrieve the actual agent object using category and ID
51                neighbor = self.model.network.agent_categories[category].get_agent(neighbor_id)
52                
53                # Infect susceptible neighbors
54                if neighbor.health_state == 0:
55                    if random.random() < self.scenario.infection_prob:
56                        newly_infected.append(neighbor)
57        
58        # Update states after iterating to avoid chain reactions in a single step
59        for agent in newly_infected:
60            agent.health_state = 1
61
62    def recover(self, agents: "AgentList[CovidAgent]"):
63        """
64        Infected agents recover with a certain probability.
65        """
66        for agent in agents:
67            if agent.health_state == 1 and random.random() < self.scenario.recovery_prob:
68                agent.health_state = 2
69
70    def update_population_stats(self, agents: "AgentList[CovidAgent]"):
71        """
72        Update statistical counts for the current step.
73        """
74        self.num_susceptible = 0
75        self.num_infected = 0
76        self.num_recovered = 0
77        for agent in agents:
78            if agent.health_state == 0:
79                self.num_susceptible += 1
80            elif agent.health_state == 1:
81                self.num_infected += 1
82            else:
83                self.num_recovered += 1

Agent Behavior

Defined in core/agent.py.

 1from Melodie import NetworkAgent
 2from typing import TYPE_CHECKING
 3import random
 4
 5if TYPE_CHECKING:
 6    from Melodie import AgentList
 7
 8
 9class CovidAgent(NetworkAgent):
10    """
11    Network-based Covid agent with basic SIR states.
12    
13    Attributes:
14        health_state (int): 0=Susceptible, 1=Infected, 2=Recovered.
15        category (int): Agent category (0 for all agents in this simple model).
16    """
17
18    def setup(self):
19        """
20        Initialize agent properties.
21        """
22        self.health_state: int = 0
23        self.category: int = 0
24
25    def set_category(self):
26        """
27        Required by NetworkAgent to determine the category ID.
28        We use a single category (0) for simplicity.
29        """
30        self.category = 0
31
32    def seed_infection(self, initial_infected_percentage: float):
33        """
34        Randomly infect the agent based on the initial percentage.
35        """
36        if random.random() < initial_infected_percentage:
37            self.health_state = 1

Scenario Definition

Defined in core/scenario.py.

 1from Melodie import Scenario
 2
 3
 4class CovidScenario(Scenario):
 5    """
 6    Scenario class that defines network parameters and contagion probabilities.
 7    """
 8
 9    def setup(self):
10        self.period_num: int = 0
11        self.run_num: int = 1
12        self.agent_num: int = 0
13        
14        # Network parameters
15        self.network_type: str = ""
16        self.network_param_k: int = 0
17        self.network_param_p: float = 0.0
18        self.network_param_m: int = 0
19        
20        # Contagion parameters
21        self.initial_infected_percentage: float = 0.0
22        self.infection_prob: float = 0.0
23        self.recovery_prob: float = 0.0
24
25    def load_data(self):
26        """
27        Load reference data.
28        Note: The '.csv' extension must be explicit if using the default loader.
29        """
30        self.health_states = self.load_dataframe("ID_HealthState.csv")
31
32    def get_network_params(self):
33        """
34        Helper to format network parameters for NetworkX based on the network type.
35        """
36        if self.network_type == "barabasi_albert_graph":
37            return {"m": self.network_param_m}
38        elif self.network_type == "watts_strogatz_graph":
39            return {"k": self.network_param_k, "p": self.network_param_p}
40        else:
41            raise NotImplementedError(f"Unsupported network_type {self.network_type}")

Data Collection Setup

Defined in core/data_collector.py.

 1from Melodie import DataCollector
 2from typing import TYPE_CHECKING
 3
 4if TYPE_CHECKING:
 5    from Melodie import AgentList
 6    from .agent import CovidAgent
 7    from .environment import CovidEnvironment
 8
 9
10class CovidDataCollector(DataCollector):
11    """
12    Collects simulation data.
13    """
14
15    environment: "CovidEnvironment"
16    agents: "AgentList[CovidAgent]"
17
18    def setup(self):
19        """
20        Configure data collection.
21        """
22        # Collect global environment properties (S-I-R counts)
23        self.add_environment_property("num_susceptible")
24        self.add_environment_property("num_infected")
25        self.add_environment_property("num_recovered")
26
27        # Collect agent properties
28        self.add_agent_property("agents", "health_state")