mirror of
https://github.com/gsi-upm/soil
synced 2025-08-24 03:52:20 +00:00
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).
This commit is contained in:
@@ -26,14 +26,14 @@ def mygenerator(n=5, n_edges=5):
|
||||
class GeneratorEnv(Environment):
|
||||
"""Using a custom generator for the network"""
|
||||
|
||||
generator: parameters.function = mygenerator
|
||||
generator: parameters.function = staticmethod(mygenerator)
|
||||
|
||||
def init(self):
|
||||
self.create_network(network_generator=self.generator, n=10, n_edges=5)
|
||||
self.init_agents(CounterModel)
|
||||
self.create_network(generator=self.generator, n=10, n_edges=5)
|
||||
self.add_agents(CounterModel)
|
||||
|
||||
|
||||
sim = Simulation(model=GeneratorEnv, max_steps=10, interval=1)
|
||||
|
||||
if __name__ == '__main__':
|
||||
sim.run(dry_run=True)
|
||||
sim.run(dump=False)
|
@@ -30,7 +30,7 @@ from networkx import complete_graph
|
||||
|
||||
class TimeoutsEnv(Environment):
|
||||
def init(self):
|
||||
self.init_network(generator=complete_graph, n=2)
|
||||
self.create_network(generator=complete_graph, n=2)
|
||||
self.add_agent(agent_class=Fibonacci, node_id=0)
|
||||
self.add_agent(agent_class=Odds, node_id=1)
|
||||
|
||||
@@ -38,4 +38,4 @@ class TimeoutsEnv(Environment):
|
||||
sim = Simulation(model=TimeoutsEnv, max_steps=10, interval=1)
|
||||
|
||||
if __name__ == "__main__":
|
||||
sim.run(dry_run=True)
|
||||
sim.run(dump=False)
|
@@ -56,41 +56,25 @@ class City(EventedEnvironment):
|
||||
:param int height: Height of the internal grid
|
||||
:param int width: Width of the internal grid
|
||||
"""
|
||||
n_cars = 1
|
||||
n_passengers = 10
|
||||
height = 100
|
||||
width = 100
|
||||
|
||||
def init(self):
|
||||
self.grid = MultiGrid(width=self.width, height=self.height, torus=False)
|
||||
if not self.agents:
|
||||
self.add_agents(Driver, k=self.n_cars)
|
||||
self.add_agents(Passenger, k=self.n_passengers)
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
*args,
|
||||
n_cars=1,
|
||||
n_passengers=10,
|
||||
height=100,
|
||||
width=100,
|
||||
agents=None,
|
||||
model_reporters=None,
|
||||
**kwargs,
|
||||
):
|
||||
self.grid = MultiGrid(width=width, height=height, torus=False)
|
||||
if agents is None:
|
||||
agents = []
|
||||
for i in range(n_cars):
|
||||
agents.append({"agent_class": Driver})
|
||||
for i in range(n_passengers):
|
||||
agents.append({"agent_class": Passenger})
|
||||
model_reporters = model_reporters or {
|
||||
"earnings": "total_earnings",
|
||||
"n_passengers": "number_passengers",
|
||||
}
|
||||
print("REPORTERS", model_reporters)
|
||||
super().__init__(
|
||||
*args, agents=agents, model_reporters=model_reporters, **kwargs
|
||||
)
|
||||
for agent in self.agents:
|
||||
self.grid.place_agent(agent, (0, 0))
|
||||
self.grid.move_to_empty(agent)
|
||||
|
||||
self.total_earnings = 0
|
||||
self.add_model_reporter("total_earnings")
|
||||
|
||||
@property
|
||||
def total_earnings(self):
|
||||
return sum(d.earnings for d in self.agents(agent_class=Driver))
|
||||
|
||||
@report
|
||||
@property
|
||||
def number_passengers(self):
|
||||
return self.count_agents(agent_class=Passenger)
|
||||
@@ -150,6 +134,7 @@ class Driver(Evented, FSM):
|
||||
while self.move_towards(self.journey.destination, with_passenger=True):
|
||||
yield
|
||||
self.earnings += self.journey.tip
|
||||
self.model.total_earnings += self.journey.tip
|
||||
self.check_passengers()
|
||||
return self.wandering
|
||||
|
||||
@@ -228,13 +213,13 @@ class Passenger(Evented, FSM):
|
||||
except events.TimedOut:
|
||||
pass
|
||||
|
||||
self.info("Got home safe!")
|
||||
self.die()
|
||||
self.die("Got home safe!")
|
||||
|
||||
|
||||
simulation = Simulation(name="RideHailing",
|
||||
model=City,
|
||||
seed="carsSeed",
|
||||
max_time=1000,
|
||||
model_params=dict(n_passengers=2))
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
@@ -1,7 +1,7 @@
|
||||
from soil import Simulation
|
||||
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__":
|
||||
sim.run()
|
||||
|
@@ -53,7 +53,7 @@ class MoneyAgent(MesaAgent):
|
||||
self.give_money()
|
||||
|
||||
|
||||
class SocialMoneyAgent(NetworkAgent, MoneyAgent):
|
||||
class SocialMoneyAgent(MoneyAgent, NetworkAgent):
|
||||
wealth = 1
|
||||
|
||||
def give_money(self):
|
||||
|
@@ -91,10 +91,11 @@ class NewsSpread(Environment):
|
||||
prob_neighbor_cure: probability = 0.05,
|
||||
|
||||
def init(self):
|
||||
self.populate_network([DumbViewer, HerdViewer, WiseViewer], [self.ratio_dumb, self.ratio_herd, self.ratio_wise])
|
||||
self.populate_network([DumbViewer, HerdViewer, WiseViewer],
|
||||
[self.ratio_dumb, self.ratio_herd, self.ratio_wise])
|
||||
|
||||
|
||||
from itertools import permutations
|
||||
from itertools import product
|
||||
from soil import Simulation
|
||||
|
||||
|
||||
@@ -103,27 +104,31 @@ from soil import Simulation
|
||||
# Because the effect of these agents might also depend on the network structure, we will run our simulations on two different networks:
|
||||
# one with a small-world structure and one with a connected structure.
|
||||
|
||||
for [r1, r2, r3] in permutations([0, 0.5, 1.0], 3):
|
||||
counter = 0
|
||||
for [r1, r2] in product([0, 0.5, 1.0], repeat=2):
|
||||
for (generator, netparams) in {
|
||||
"barabasi_albert_graph": {"m": 5},
|
||||
"erdos_renyi_graph": {"p": 0.1},
|
||||
}.items():
|
||||
print(r1, r2, r3, generator)
|
||||
print(r1, r2, 1-r1-r2, generator)
|
||||
# Create new simulation
|
||||
netparams["n"] = 500
|
||||
sim = Simulation(
|
||||
Simulation(
|
||||
name='newspread_sim',
|
||||
model=NewsSpread,
|
||||
model_params={
|
||||
"ratio_dumb": r1,
|
||||
"ratio_herd": r2,
|
||||
"ratio_wise": r3,
|
||||
"network_generator": generator,
|
||||
"network_params": netparams,
|
||||
"prob_neighbor_spread": 0,
|
||||
},
|
||||
num_trials=50,
|
||||
model_params=dict(
|
||||
ratio_dumb=r1,
|
||||
ratio_herd=r2,
|
||||
ratio_wise=1-r1-r2,
|
||||
network_generator=generator,
|
||||
network_params=netparams,
|
||||
prob_neighbor_spread=0,
|
||||
),
|
||||
num_trials=5,
|
||||
max_steps=300,
|
||||
dry_run=True,
|
||||
)
|
||||
dump=False,
|
||||
).run()
|
||||
counter += 1
|
||||
# Run all the necessary instances
|
||||
sim.run()
|
||||
|
||||
print(f"A total of {counter} simulations were run.")
|
@@ -14,7 +14,7 @@ def mygenerator():
|
||||
return G
|
||||
|
||||
|
||||
class MyAgent(agents.FSM):
|
||||
class MyAgent(agents.NetworkAgent, agents.FSM):
|
||||
times_run = 0
|
||||
@agents.default_state
|
||||
@agents.state
|
||||
@@ -29,6 +29,7 @@ class ProgrammaticEnv(Environment):
|
||||
|
||||
def init(self):
|
||||
self.create_network(generator=mygenerator)
|
||||
assert len(self.G)
|
||||
self.populate_network(agent_class=MyAgent)
|
||||
self.add_agent_reporter('times_run')
|
||||
|
||||
@@ -39,7 +40,7 @@ simulation = Simulation(
|
||||
seed='Program',
|
||||
num_trials=1,
|
||||
max_time=100,
|
||||
dry_run=True,
|
||||
dump=False,
|
||||
)
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
@@ -14,7 +14,7 @@ class CityPubs(Environment):
|
||||
pub_capacity: parameters.Integer = 10
|
||||
|
||||
def init(self):
|
||||
pubs = {}
|
||||
self.pubs = {}
|
||||
for i in range(self.number_of_pubs):
|
||||
newpub = {
|
||||
"name": "The awesome pub #{}".format(i),
|
||||
@@ -22,10 +22,11 @@ class CityPubs(Environment):
|
||||
"capacity": self.pub_capacity,
|
||||
"occupancy": 0,
|
||||
}
|
||||
pubs[newpub["name"]] = newpub
|
||||
self.add_agent(agent_class=Police, node_id=0)
|
||||
self["pubs"] = pubs
|
||||
self.populate_network([{"openness": 0.1}, {"openness": 1}], [self.ratio_extroverted, 1-self.ratio_extroverted], agent_class=Patron)
|
||||
self.pubs[newpub["name"]] = newpub
|
||||
self.add_agent(agent_class=Police)
|
||||
self.populate_network([Patron.w(openness=0.1), Patron.w(openness=1)],
|
||||
[self.ratio_extroverted, 1-self.ratio_extroverted])
|
||||
assert all(["agent" in node and isinstance(node["agent"], Patron) for (_, node) in self.G.nodes(data=True)])
|
||||
|
||||
def enter(self, pub_id, *nodes):
|
||||
"""Agents will try to enter. The pub checks if it is possible"""
|
||||
@@ -151,10 +152,10 @@ class Patron(FSM, NetworkAgent):
|
||||
continue
|
||||
if friend.befriend(self):
|
||||
self.befriend(friend, force=True)
|
||||
self.debug("Hooray! new friend: {}".format(friend.id))
|
||||
self.debug("Hooray! new friend: {}".format(friend.unique_id))
|
||||
befriended = True
|
||||
else:
|
||||
self.debug("{} does not want to be friends".format(friend.id))
|
||||
self.debug("{} does not want to be friends".format(friend.unique_id))
|
||||
return befriended
|
||||
|
||||
|
||||
@@ -168,19 +169,20 @@ class Police(FSM):
|
||||
def patrol(self):
|
||||
drunksters = list(self.get_agents(drunk=True, state_id=Patron.drunk_in_pub.id))
|
||||
for drunk in drunksters:
|
||||
self.info("Kicking out the trash: {}".format(drunk.id))
|
||||
self.info("Kicking out the trash: {}".format(drunk.unique_id))
|
||||
drunk.kick_out()
|
||||
else:
|
||||
self.info("No trash to take out. Too bad.")
|
||||
|
||||
|
||||
sim = Simulation(
|
||||
model=CityPubs,
|
||||
name="pubcrawl",
|
||||
num_trials=3,
|
||||
max_steps=10,
|
||||
dry_run=True,
|
||||
dump=False,
|
||||
model_params=dict(
|
||||
generator=nx.empty_graph,
|
||||
network_generator=nx.empty_graph,
|
||||
network_params={"n": 30},
|
||||
model=CityPubs,
|
||||
altercations=0,
|
||||
|
@@ -40,7 +40,7 @@ s = Simulation(
|
||||
model=RandomEnv,
|
||||
num_trials=1,
|
||||
max_time=100,
|
||||
dry_run=True,
|
||||
dump=False,
|
||||
)
|
||||
|
||||
|
||||
|
@@ -5,7 +5,6 @@ from soil.parameters import *
|
||||
|
||||
|
||||
class TerroristEnvironment(Environment):
|
||||
generator: function = nx.random_geometric_graph
|
||||
n: Integer = 100
|
||||
radius: Float = 0.2
|
||||
|
||||
@@ -37,8 +36,11 @@ class TerroristEnvironment(Environment):
|
||||
TerroristNetworkModel.w(state_id='leader'),
|
||||
TrainingAreaModel,
|
||||
HavenModel
|
||||
], [self.ratio_civil, self.ratio_leader, self.ratio_trainig, self.ratio_heaven])
|
||||
], [self.ratio_civil, self.ratio_leader, self.ratio_training, self.ratio_haven])
|
||||
|
||||
@staticmethod
|
||||
def generator(*args, **kwargs):
|
||||
return nx.random_geometric_graph(*args, **kwargs)
|
||||
|
||||
class TerroristSpreadModel(FSM, Geo):
|
||||
"""
|
||||
@@ -50,10 +52,13 @@ class TerroristSpreadModel(FSM, Geo):
|
||||
min_vulnerability (optional else zero)
|
||||
|
||||
max_vulnerability
|
||||
|
||||
prob_interaction
|
||||
"""
|
||||
|
||||
information_spread_intensity = 0.1
|
||||
terrorist_additional_influence = 0.1
|
||||
min_vulnerability = 0
|
||||
max_vulnerability = 1
|
||||
|
||||
def init(self):
|
||||
if self.state_id == self.civilian.id: # Civilian
|
||||
self.mean_belief = self.model.random.uniform(0.00, 0.5)
|
||||
@@ -75,7 +80,7 @@ class TerroristSpreadModel(FSM, Geo):
|
||||
if len(neighbours) > 0:
|
||||
# Only interact with some of the neighbors
|
||||
interactions = list(
|
||||
n for n in neighbours if self.random.random() <= self.prob_interaction
|
||||
n for n in neighbours if self.random.random() <= self.model.prob_interaction
|
||||
)
|
||||
influence = sum(self.degree(i) for i in interactions)
|
||||
mean_belief = sum(
|
||||
@@ -121,7 +126,7 @@ class TerroristSpreadModel(FSM, Geo):
|
||||
)
|
||||
|
||||
# Check if there are any leaders in the group
|
||||
leaders = list(filter(lambda x: x.state.id == self.leader.id, neighbours))
|
||||
leaders = list(filter(lambda x: x.state_id == self.leader.id, neighbours))
|
||||
if not leaders:
|
||||
# Check if this is the potential leader
|
||||
# Stop once it's found. Otherwise, set self as leader
|
||||
@@ -132,12 +137,11 @@ class TerroristSpreadModel(FSM, Geo):
|
||||
|
||||
def ego_search(self, steps=1, center=False, agent=None, **kwargs):
|
||||
"""Get a list of nodes in the ego network of *node* of radius *steps*"""
|
||||
node = agent.node
|
||||
node = agent.node_id
|
||||
G = self.subgraph(**kwargs)
|
||||
return nx.ego_graph(G, node, center=center, radius=steps).nodes()
|
||||
|
||||
def degree(self, agent, force=False):
|
||||
node = agent.node
|
||||
if (
|
||||
force
|
||||
or (not hasattr(self.model, "_degree"))
|
||||
@@ -145,10 +149,9 @@ class TerroristSpreadModel(FSM, Geo):
|
||||
):
|
||||
self.model._degree = nx.degree_centrality(self.G)
|
||||
self.model._last_step = self.now
|
||||
return self.model._degree[node]
|
||||
return self.model._degree[agent.node_id]
|
||||
|
||||
def betweenness(self, agent, force=False):
|
||||
node = agent.node
|
||||
if (
|
||||
force
|
||||
or (not hasattr(self.model, "_betweenness"))
|
||||
@@ -156,7 +159,7 @@ class TerroristSpreadModel(FSM, Geo):
|
||||
):
|
||||
self.model._betweenness = nx.betweenness_centrality(self.G)
|
||||
self.model._last_step = self.now
|
||||
return self.model._betweenness[node]
|
||||
return self.model._betweenness[agent.node_id]
|
||||
|
||||
|
||||
class TrainingAreaModel(FSM, Geo):
|
||||
@@ -169,13 +172,12 @@ class TrainingAreaModel(FSM, Geo):
|
||||
Requires TerroristSpreadModel.
|
||||
"""
|
||||
|
||||
def __init__(self, model=None, unique_id=0, state=()):
|
||||
super().__init__(model=model, unique_id=unique_id, state=state)
|
||||
self.training_influence = model.environment_params["training_influence"]
|
||||
if "min_vulnerability" in model.environment_params:
|
||||
self.min_vulnerability = model.environment_params["min_vulnerability"]
|
||||
else:
|
||||
self.min_vulnerability = 0
|
||||
training_influence = 0.1
|
||||
min_vulnerability = 0
|
||||
|
||||
def init(self):
|
||||
self.mean_believe = 1
|
||||
self.vulnerability = 0
|
||||
|
||||
@default_state
|
||||
@state
|
||||
@@ -199,18 +201,19 @@ class HavenModel(FSM, Geo):
|
||||
Requires TerroristSpreadModel.
|
||||
"""
|
||||
|
||||
def __init__(self, model=None, unique_id=0, state=()):
|
||||
super().__init__(model=model, unique_id=unique_id, state=state)
|
||||
self.haven_influence = model.environment_params["haven_influence"]
|
||||
if "min_vulnerability" in model.environment_params:
|
||||
self.min_vulnerability = model.environment_params["min_vulnerability"]
|
||||
else:
|
||||
self.min_vulnerability = 0
|
||||
self.max_vulnerability = model.environment_params["max_vulnerability"]
|
||||
min_vulnerability = 0
|
||||
haven_influence = 0.1
|
||||
max_vulnerability = 0.5
|
||||
|
||||
def init(self):
|
||||
self.mean_believe = 0
|
||||
self.vulnerability = 0
|
||||
|
||||
def get_occupants(self, **kwargs):
|
||||
return self.get_neighbors(agent_class=TerroristSpreadModel, **kwargs)
|
||||
return self.get_neighbors(agent_class=TerroristSpreadModel,
|
||||
**kwargs)
|
||||
|
||||
@default_state
|
||||
@state
|
||||
def civilian(self):
|
||||
civilians = self.get_occupants(state_id=self.civilian.id)
|
||||
@@ -246,13 +249,10 @@ class TerroristNetworkModel(TerroristSpreadModel):
|
||||
weight_link_distance
|
||||
"""
|
||||
|
||||
def __init__(self, model=None, unique_id=0, state=()):
|
||||
super().__init__(model=model, unique_id=unique_id, state=state)
|
||||
|
||||
self.vision_range = model.environment_params["vision_range"]
|
||||
self.sphere_influence = model.environment_params["sphere_influence"]
|
||||
self.weight_social_distance = model.environment_params["weight_social_distance"]
|
||||
self.weight_link_distance = model.environment_params["weight_link_distance"]
|
||||
sphere_influence: float
|
||||
vision_range: float
|
||||
weight_social_distance: float
|
||||
weight_link_distance: float
|
||||
|
||||
@state
|
||||
def terrorist(self):
|
||||
@@ -316,8 +316,8 @@ sim = Simulation(
|
||||
num_trials=1,
|
||||
name="TerroristNetworkModel_sim",
|
||||
max_steps=150,
|
||||
skip_test=True,
|
||||
dry_run=True,
|
||||
skip_test=False,
|
||||
dump=False,
|
||||
)
|
||||
|
||||
# TODO: integrate visualization
|
||||
|
@@ -1,14 +1,23 @@
|
||||
from soil import Environment, Simulation, CounterModel
|
||||
from soil import Environment, Simulation, CounterModel, report
|
||||
|
||||
|
||||
# Get directory path for current file
|
||||
import os, sys, inspect
|
||||
currentdir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))
|
||||
|
||||
class TorvaldsEnv(Environment):
|
||||
|
||||
def init(self):
|
||||
self.create_network(path='torvalds.edgelist')
|
||||
self.create_network(path=os.path.join(currentdir, 'torvalds.edgelist'))
|
||||
self.populate_network(CounterModel, skill_level='beginner')
|
||||
print("Agentes: ", list(self.network_agents))
|
||||
self.find_one(node_id="Torvalds").skill_level = 'God'
|
||||
self.find_one(node_id="balkian").skill_level = 'developer'
|
||||
self.agent(node_id="Torvalds").skill_level = 'God'
|
||||
self.agent(node_id="balkian").skill_level = 'developer'
|
||||
self.add_agent_reporter("times")
|
||||
|
||||
@report
|
||||
def god_developers(self):
|
||||
return self.count_agents(skill_level='God')
|
||||
|
||||
|
||||
sim = Simulation(name='torvalds_example',
|
||||
max_steps=10,
|
||||
|
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user