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'}}}
{'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'}}}
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 | Actor[0] | 1 | 2024-06-04 13:29:43.786840 | testing |
1 | Actor[1] | 1 | 2024-06-04 13:29:43.786840 | Hello World |
0 | Actor[0] | 2 | 2024-06-04 13:29:43.786840 | testing |
1 | Actor[1] | 2 | 2024-06-04 13:29:43.786840 | Hello World |
0 | Actor[0] | 3 | 2024-06-04 13:29:43.786840 | testing |
1 | Actor[1] | 3 | 2024-06-04 13:29:43.786840 | Hello World |
Final result.
model.datacollector.get_final_vars_report(model)
{'var1': 'Final result'}
Super handy, aha. Enjoy coding!