Manage parameters¶
In progress
This document is a work in progress if you see any errors, or exclusions or have any problems, please get in touch with us.
ABSESpy uses hydra-core to manage parameters. hydra-core is a framework for elegantly configuring complex applications. It is installed if you have installed ABSESpy using pip.
For the best practice, start with a .yaml, like this:
time:
end: 3
reports:
model:
var1: 'test'
var2: var2
agents:
Actor:
var1: 'test'
Farmer:
var2: 'test'
final:
var1: 'test'
We will dive into the .yaml configurations step by step. First of all, we need to load and parse the configs into a Python object, -specifically, a OmegaConf object.
In an interactive environment like Jupyter Notebook, you can use the following code to load the .yaml file:
from hydra import compose, initialize
# 加载项目层面的配置
with initialize(
version_base=None, config_path="../../../tests/config"
): # replace with your config path
cfg = compose(config_name="test_config")
type(cfg)
omegaconf.dictconfig.DictConfig
In a Python script, this snippet from hydra-core's documentation would be useful:
import hydra
from omegaconf import DictConfig, OmegaConf
@hydra.main(version_base=None, config_path="conf", config_name="config")
def my_app(cfg : DictConfig) -> None:
print(OmegaConf.to_yaml(cfg))
if __name__ == "__main__":
my_app()
Let's print the configurations out.
from pprint import pprint
pprint(cfg)
cfg.time
{'time': {'end': 3}, 'reports': {'model': {'var1': 'test', 'var2': 'var2'}, 'agents': {'Actor': {'var1': 'test'}, 'Farmer': {'var2': 'test'}}, 'final': {'var1': 'test'}}, 'param1': 0, 'param2': 'c'}
{'end': 3}
Basically, it's just like a nested dictionary, but you can access the configuration attributes simply by .xxx. Such configurations can be passed to a model build by ABSESpy directly:
from abses import MainModel
model = MainModel(parameters=cfg)
model.test = 3
# then, the configuration is available in model.settings
model.settings
{'time': {'end': 3}, 'reports': {'model': {'var1': 'test', 'var2': 'var2'}, 'agents': {'Actor': {'var1': 'test'}, 'Farmer': {'var2': 'test'}}, 'final': {'var1': 'test'}}, 'param1': 0, 'param2': 'c'}
Now, what's the meaning of time and reports?
'time' module configures the time settings. When assigned end = 3 in time module, it means the simulation will end at the 3rd time step.
By default, a running model won't stop unless some configuration tells it. Let's try it out:
model.run_model()
model.time
<TimeDriver: tick[3]>
Ok! It works. That's the basic settings of the time module.
However, the reports module is a bit more complicated. It has two submodules, model and agents.
Remember we assigned an attribute model.test = 3 after initialize it. Why?
The reports config module is used to configure the model's data collector, which is inherited from the mesa framework. This module is used to collect data from the model at each time step. The model submodule is used to collect data from the model, and the agents submodule is used to collect data from the agents.
Talk is cheap, let's see what happened:
model.datacollector.get_model_vars_dataframe()
| var1 | var2 | |
|---|---|---|
| 0 | 3 | None |
| 1 | 3 | None |
| 2 | 3 | None |
It's a bit tricky. The var1 and var2 are the names of the reports. They were converted into columns' name. Why they are 6 and 3?
Notice that the var2 was just referring to the test attribute of the model, and the var1 was a lambda function. The lambda function takes the model as the input and returns the value of the report. The ABSESpy will evaluate the lambda function at the end of each time step.
That means:
var2 -> attribute 'test' -> 3
var1 -> ":lambda m: m.test * 2" -> parsed as a function, and the result is 6
Tips
":" before the keyword `lambda` denotes this is an lambda function rather than attribute string..
Super useful! You can collect any data from the model and agents by configuring the reports module. You know, complex configurations of an agent-based model is a piece of cake with ABSESpy! Guess what result we would get from the agents submodule?
Correct! There wasn't any actor before run. Thus, no values were collected. Let's create a new model with adding some agents and see what happens:
from abses import Actor
model = MainModel(parameters=cfg)
model.test = "Final result"
actors = model.agents.new(Actor, num=2)
actors[0].test = "testing"
actors[1].test = "Hello World"
model.run_model()
model.datacollector.get_agent_vars_dataframe("Actor")
| AgentID | Step | Time | var1 | |
|---|---|---|---|---|
| 0 | 1 | 1 | 2025-02-16 12:57:55.190298 | testing |
| 1 | 2 | 1 | 2025-02-16 12:57:55.190298 | Hello World |
| 0 | 1 | 2 | 2025-02-16 12:57:55.190298 | testing |
| 1 | 2 | 2 | 2025-02-16 12:57:55.190298 | Hello World |
| 0 | 1 | 3 | 2025-02-16 12:57:55.190298 | testing |
| 1 | 2 | 3 | 2025-02-16 12:57:55.190298 | Hello World |
Final result.
model.datacollector.get_final_vars_report(model)
{'var1': 'Final result'}
Super handy, aha. Enjoy coding!