mirror of https://github.com/gsi-upm/soil
Large set of changes for v0.30
The examples weren't being properly tested in the last commit. When we fixed that a lot of bugs in the new implementation of environment and agent were found, which accounts for most of these changes. The main difference is the mechanism to load simulations from a configuration file. For that to work, we had to rework our module loading code in `serialization` and add a `source_file` attribute to configurations (and simulations, for that matter).mesa
parent
73282530fd
commit
feab0ba79e
@ -1,7 +1,7 @@
|
|||||||
from soil import Simulation
|
from soil import Simulation
|
||||||
from social_wealth import MoneyEnv, graph_generator
|
from social_wealth import MoneyEnv, graph_generator
|
||||||
|
|
||||||
sim = Simulation(name="mesa_sim", dry_run=True, max_steps=10, interval=2, model=MoneyEnv, model_params=dict(generator=graph_generator, N=10, width=50, height=50))
|
sim = Simulation(name="mesa_sim", dump=False, max_steps=10, interval=2, model=MoneyEnv, model_params=dict(generator=graph_generator, N=10, width=50, height=50))
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
sim.run()
|
sim.run()
|
||||||
|
File diff suppressed because one or more lines are too long
@ -1,267 +1,2 @@
|
|||||||
from __future__ import annotations
|
def load_config(cfg):
|
||||||
|
return cfg
|
||||||
from enum import Enum
|
|
||||||
from pydantic import BaseModel, ValidationError, validator, root_validator
|
|
||||||
|
|
||||||
import yaml
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
|
|
||||||
|
|
||||||
from typing import Any, Callable, Dict, List, Optional, Union, Type
|
|
||||||
from pydantic import BaseModel, Extra
|
|
||||||
|
|
||||||
from . import environment, utils
|
|
||||||
|
|
||||||
import networkx as nx
|
|
||||||
|
|
||||||
|
|
||||||
# Could use TypeAlias in python >= 3.10
|
|
||||||
nodeId = int
|
|
||||||
|
|
||||||
|
|
||||||
class Node(BaseModel):
|
|
||||||
id: nodeId
|
|
||||||
state: Optional[Dict[str, Any]] = {}
|
|
||||||
|
|
||||||
|
|
||||||
class Edge(BaseModel):
|
|
||||||
source: nodeId
|
|
||||||
target: nodeId
|
|
||||||
value: Optional[float] = 1
|
|
||||||
|
|
||||||
|
|
||||||
class Topology(BaseModel):
|
|
||||||
nodes: List[Node]
|
|
||||||
directed: bool
|
|
||||||
links: List[Edge]
|
|
||||||
|
|
||||||
|
|
||||||
class NetConfig(BaseModel):
|
|
||||||
params: Optional[Dict[str, Any]]
|
|
||||||
fixed: Optional[Union[Topology, nx.Graph]]
|
|
||||||
path: Optional[str]
|
|
||||||
|
|
||||||
class Config:
|
|
||||||
arbitrary_types_allowed = True
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def default():
|
|
||||||
return NetConfig(topology=None, params=None)
|
|
||||||
|
|
||||||
@root_validator
|
|
||||||
def validate_all(cls, values):
|
|
||||||
if "params" not in values and "topology" not in values:
|
|
||||||
raise ValueError(
|
|
||||||
"You must specify either a topology or the parameters to generate a graph"
|
|
||||||
)
|
|
||||||
return values
|
|
||||||
|
|
||||||
|
|
||||||
class EnvConfig(BaseModel):
|
|
||||||
@staticmethod
|
|
||||||
def default():
|
|
||||||
return EnvConfig()
|
|
||||||
|
|
||||||
|
|
||||||
class SingleAgentConfig(BaseModel):
|
|
||||||
agent_class: Optional[Union[Type, str]] = None
|
|
||||||
unique_id: Optional[int] = None
|
|
||||||
topology: Optional[bool] = False
|
|
||||||
node_id: Optional[Union[int, str]] = None
|
|
||||||
state: Optional[Dict[str, Any]] = {}
|
|
||||||
|
|
||||||
|
|
||||||
class FixedAgentConfig(SingleAgentConfig):
|
|
||||||
n: Optional[int] = 1
|
|
||||||
hidden: Optional[bool] = False # Do not count this agent towards total agent count
|
|
||||||
|
|
||||||
@root_validator
|
|
||||||
def validate_all(cls, values):
|
|
||||||
if values.get("unique_id", None) is not None and values.get("n", 1) > 1:
|
|
||||||
raise ValueError(
|
|
||||||
f"An unique_id can only be provided when there is only one agent ({values.get('n')} given)"
|
|
||||||
)
|
|
||||||
return values
|
|
||||||
|
|
||||||
|
|
||||||
class OverrideAgentConfig(FixedAgentConfig):
|
|
||||||
filter: Optional[Dict[str, Any]] = None
|
|
||||||
|
|
||||||
|
|
||||||
class Strategy(Enum):
|
|
||||||
topology = "topology"
|
|
||||||
total = "total"
|
|
||||||
|
|
||||||
|
|
||||||
class AgentDistro(SingleAgentConfig):
|
|
||||||
weight: Optional[float] = 1
|
|
||||||
strategy: Strategy = Strategy.topology
|
|
||||||
|
|
||||||
|
|
||||||
class AgentConfig(SingleAgentConfig):
|
|
||||||
n: Optional[int] = None
|
|
||||||
distribution: Optional[List[AgentDistro]] = None
|
|
||||||
fixed: Optional[List[FixedAgentConfig]] = None
|
|
||||||
override: Optional[List[OverrideAgentConfig]] = None
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def default():
|
|
||||||
return AgentConfig()
|
|
||||||
|
|
||||||
@root_validator
|
|
||||||
def validate_all(cls, values):
|
|
||||||
if "distribution" in values and (
|
|
||||||
"n" not in values and "topology" not in values
|
|
||||||
):
|
|
||||||
raise ValueError(
|
|
||||||
"You need to provide the number of agents or a topology to extract the value from."
|
|
||||||
)
|
|
||||||
return values
|
|
||||||
|
|
||||||
|
|
||||||
class Config(BaseModel, extra=Extra.allow):
|
|
||||||
version: Optional[str] = "1"
|
|
||||||
|
|
||||||
name: str = "Unnamed Simulation"
|
|
||||||
description: Optional[str] = None
|
|
||||||
group: str = None
|
|
||||||
dir_path: Optional[str] = None
|
|
||||||
num_trials: int = 1
|
|
||||||
max_time: float = 100
|
|
||||||
max_steps: int = -1
|
|
||||||
num_processes: int = 1
|
|
||||||
interval: float = 1
|
|
||||||
seed: str = ""
|
|
||||||
dry_run: bool = False
|
|
||||||
skip_test: bool = False
|
|
||||||
|
|
||||||
model_class: Union[Type, str] = environment.Environment
|
|
||||||
model_params: Optional[Dict[str, Any]] = {}
|
|
||||||
|
|
||||||
visualization_params: Optional[Dict[str, Any]] = {}
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_raw(cls, cfg):
|
|
||||||
if isinstance(cfg, Config):
|
|
||||||
return cfg
|
|
||||||
if cfg.get("version", "1") == "1" and any(
|
|
||||||
k in cfg for k in ["agents", "agent_class", "topology", "environment_class"]
|
|
||||||
):
|
|
||||||
return convert_old(cfg)
|
|
||||||
return Config(**cfg)
|
|
||||||
|
|
||||||
|
|
||||||
def convert_old(old, strict=True):
|
|
||||||
"""
|
|
||||||
Try to convert old style configs into the new format.
|
|
||||||
|
|
||||||
This is still a work in progress and might not work in many cases.
|
|
||||||
"""
|
|
||||||
|
|
||||||
utils.logger.warning(
|
|
||||||
"The old configuration format is deprecated. The converted file MAY NOT yield the right results"
|
|
||||||
)
|
|
||||||
|
|
||||||
new = old.copy()
|
|
||||||
|
|
||||||
network = {}
|
|
||||||
|
|
||||||
if "topology" in old:
|
|
||||||
del new["topology"]
|
|
||||||
network["topology"] = old["topology"]
|
|
||||||
|
|
||||||
if "network_params" in old and old["network_params"]:
|
|
||||||
del new["network_params"]
|
|
||||||
for (k, v) in old["network_params"].items():
|
|
||||||
if k == "path":
|
|
||||||
network["path"] = v
|
|
||||||
else:
|
|
||||||
network.setdefault("params", {})[k] = v
|
|
||||||
|
|
||||||
topology = None
|
|
||||||
if network:
|
|
||||||
topology = network
|
|
||||||
|
|
||||||
agents = {"fixed": [], "distribution": []}
|
|
||||||
|
|
||||||
def updated_agent(agent):
|
|
||||||
"""Convert an agent definition"""
|
|
||||||
newagent = dict(agent)
|
|
||||||
return newagent
|
|
||||||
|
|
||||||
by_weight = []
|
|
||||||
fixed = []
|
|
||||||
override = []
|
|
||||||
|
|
||||||
if "environment_agents" in new:
|
|
||||||
|
|
||||||
for agent in new["environment_agents"]:
|
|
||||||
agent.setdefault("state", {})["group"] = "environment"
|
|
||||||
if "agent_id" in agent:
|
|
||||||
agent["state"]["name"] = agent["agent_id"]
|
|
||||||
del agent["agent_id"]
|
|
||||||
agent["hidden"] = True
|
|
||||||
agent["topology"] = False
|
|
||||||
fixed.append(updated_agent(agent))
|
|
||||||
del new["environment_agents"]
|
|
||||||
|
|
||||||
if "agent_class" in old:
|
|
||||||
del new["agent_class"]
|
|
||||||
agents["agent_class"] = old["agent_class"]
|
|
||||||
|
|
||||||
if "default_state" in old:
|
|
||||||
del new["default_state"]
|
|
||||||
agents["state"] = old["default_state"]
|
|
||||||
|
|
||||||
if "network_agents" in old:
|
|
||||||
agents["topology"] = True
|
|
||||||
|
|
||||||
agents.setdefault("state", {})["group"] = "network"
|
|
||||||
|
|
||||||
for agent in new["network_agents"]:
|
|
||||||
agent = updated_agent(agent)
|
|
||||||
if "agent_id" in agent:
|
|
||||||
agent["state"]["name"] = agent["agent_id"]
|
|
||||||
del agent["agent_id"]
|
|
||||||
fixed.append(agent)
|
|
||||||
else:
|
|
||||||
by_weight.append(agent)
|
|
||||||
del new["network_agents"]
|
|
||||||
|
|
||||||
if "agent_class" in old and (not fixed and not by_weight):
|
|
||||||
agents["topology"] = True
|
|
||||||
by_weight = [{"agent_class": old["agent_class"], "weight": 1}]
|
|
||||||
|
|
||||||
# TODO: translate states properly
|
|
||||||
if "states" in old:
|
|
||||||
del new["states"]
|
|
||||||
states = old["states"]
|
|
||||||
if isinstance(states, dict):
|
|
||||||
states = states.items()
|
|
||||||
else:
|
|
||||||
states = enumerate(states)
|
|
||||||
for (k, v) in states:
|
|
||||||
override.append({"filter": {"node_id": k}, "state": v})
|
|
||||||
|
|
||||||
agents["override"] = override
|
|
||||||
agents["fixed"] = fixed
|
|
||||||
agents["distribution"] = by_weight
|
|
||||||
|
|
||||||
model_params = {}
|
|
||||||
if "environment_params" in new:
|
|
||||||
del new["environment_params"]
|
|
||||||
model_params = dict(old["environment_params"])
|
|
||||||
|
|
||||||
if "environment_class" in old:
|
|
||||||
del new["environment_class"]
|
|
||||||
new["model_class"] = old["environment_class"]
|
|
||||||
|
|
||||||
if "dump" in old:
|
|
||||||
del new["dump"]
|
|
||||||
new["dry_run"] = not old["dump"]
|
|
||||||
|
|
||||||
model_params["topology"] = topology
|
|
||||||
model_params["agents"] = agents
|
|
||||||
|
|
||||||
return Config(version="2", model_params=model_params, **new)
|
|
@ -1,4 +1,6 @@
|
|||||||
def report(f: property):
|
def report(f: property):
|
||||||
print(f.fget)
|
if isinstance(f, property):
|
||||||
setattr(f.fget, "add_to_report", True)
|
setattr(f.fget, "add_to_report", True)
|
||||||
|
else:
|
||||||
|
setattr(f, "add_to_report", True)
|
||||||
return f
|
return f
|
@ -1,49 +0,0 @@
|
|||||||
---
|
|
||||||
version: '2'
|
|
||||||
name: simple
|
|
||||||
group: tests
|
|
||||||
dir_path: "/tmp/"
|
|
||||||
num_trials: 3
|
|
||||||
max_time: 100
|
|
||||||
interval: 1
|
|
||||||
seed: "CompleteSeed!"
|
|
||||||
model_class: Environment
|
|
||||||
model_params:
|
|
||||||
topology:
|
|
||||||
params:
|
|
||||||
generator: complete_graph
|
|
||||||
n: 4
|
|
||||||
agents:
|
|
||||||
agent_class: CounterModel
|
|
||||||
state:
|
|
||||||
group: network
|
|
||||||
times: 1
|
|
||||||
topology: true
|
|
||||||
distribution:
|
|
||||||
- agent_class: CounterModel
|
|
||||||
weight: 0.25
|
|
||||||
state:
|
|
||||||
state_id: 0
|
|
||||||
times: 1
|
|
||||||
- agent_class: AggregatedCounter
|
|
||||||
weight: 0.5
|
|
||||||
state:
|
|
||||||
times: 2
|
|
||||||
override:
|
|
||||||
- filter:
|
|
||||||
node_id: 1
|
|
||||||
state:
|
|
||||||
name: 'Node 1'
|
|
||||||
- filter:
|
|
||||||
node_id: 2
|
|
||||||
state:
|
|
||||||
name: 'Node 2'
|
|
||||||
fixed:
|
|
||||||
- agent_class: BaseAgent
|
|
||||||
hidden: true
|
|
||||||
topology: false
|
|
||||||
state:
|
|
||||||
name: 'Environment Agent 1'
|
|
||||||
times: 10
|
|
||||||
group: environment
|
|
||||||
am_i_complete: true
|
|
@ -1,37 +0,0 @@
|
|||||||
---
|
|
||||||
name: simple
|
|
||||||
group: tests
|
|
||||||
dir_path: "/tmp/"
|
|
||||||
num_trials: 3
|
|
||||||
max_time: 100
|
|
||||||
interval: 1
|
|
||||||
seed: "CompleteSeed!"
|
|
||||||
network_params:
|
|
||||||
generator: complete_graph
|
|
||||||
n: 4
|
|
||||||
network_agents:
|
|
||||||
- agent_class: CounterModel
|
|
||||||
weight: 0.25
|
|
||||||
state:
|
|
||||||
state_id: 0
|
|
||||||
times: 1
|
|
||||||
- agent_class: AggregatedCounter
|
|
||||||
weight: 0.5
|
|
||||||
state:
|
|
||||||
times: 2
|
|
||||||
environment_agents:
|
|
||||||
- agent_id: 'Environment Agent 1'
|
|
||||||
agent_class: BaseAgent
|
|
||||||
state:
|
|
||||||
times: 10
|
|
||||||
environment_class: Environment
|
|
||||||
environment_params:
|
|
||||||
am_i_complete: true
|
|
||||||
agent_class: CounterModel
|
|
||||||
default_state:
|
|
||||||
times: 1
|
|
||||||
states:
|
|
||||||
1:
|
|
||||||
name: 'Node 1'
|
|
||||||
2:
|
|
||||||
name: 'Node 2'
|
|
@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
source_file: "../examples/torvalds_sim.py"
|
||||||
|
model: "TorvaldsEnv"
|
||||||
|
max_steps: 10
|
||||||
|
interval: 2
|
Loading…
Reference in New Issue