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
Network Agents (``NetworkAgent``): Agents inherit from
NetworkAgent, which provides them with anetworkattribute to access the network structure.Network Structure: The
CovidModelnow creates aNetworkobject. 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 inSimulatorScenarios.csv.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_typecolumn inSimulatorScenarios.csvspecifies whether to use “watts_strogatz” or “barabasi_albert”.Network Parameters: Columns like
network_param_k,network_param_p, andnetwork_param_mprovide 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")