-
Notifications
You must be signed in to change notification settings - Fork 927
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
14 changed files
with
303 additions
and
59 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
74 changes: 74 additions & 0 deletions
74
mesa/examples/basic/alliance_formation_model/alliance_formation/app.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
import matplotlib.pyplot as plt | ||
import networkx as nx | ||
import solara | ||
from matplotlib.figure import Figure | ||
from multi_level_alliance.model import AllianceModel | ||
|
||
from mesa.mesa_logging import DEBUG, log_to_stderr | ||
from mesa.visualization import SolaraViz | ||
from mesa.visualization.utils import update_counter | ||
|
||
log_to_stderr(DEBUG) | ||
|
||
model_params = { | ||
"seed": { | ||
"type": "InputText", | ||
"value": 42, | ||
"label": "Random Seed", | ||
}, | ||
"n": { | ||
"type": "SliderInt", | ||
"value": 50, | ||
"label": "Number of agents:", | ||
"min": 10, | ||
"max": 100, | ||
"step": 1, | ||
}, | ||
} | ||
|
||
# Create visualization elements. The visualization elements are solara components | ||
# that receive the model instance as a "prop" and display it in a certain way. | ||
# Under the hood these are just classes that receive the model instance. | ||
# You can also author your own visualization elements, which can also be functions | ||
# that receive the model instance and return a valid solara component. | ||
|
||
|
||
@solara.component | ||
def plot_network(model): | ||
update_counter.get() | ||
g = model.network | ||
pos = nx.kamada_kawai_layout(g) | ||
fig = Figure() | ||
ax = fig.subplots() | ||
labels = {agent.unique_id: agent.unique_id for agent in model.agents} | ||
node_sizes = [g.nodes[node]["size"] for node in g.nodes] | ||
node_colors = [g.nodes[node]["size"] for node in g.nodes()] | ||
|
||
nx.draw( | ||
g, | ||
pos, | ||
node_size=node_sizes, | ||
node_color=node_colors, | ||
cmap=plt.cm.coolwarm, | ||
labels=labels, | ||
ax=ax, | ||
) | ||
|
||
solara.FigureMatplotlib(fig) | ||
|
||
|
||
# Create initial model instance | ||
model = AllianceModel(50) | ||
|
||
# Create the SolaraViz page. This will automatically create a server and display the | ||
# visualization elements in a web browser. | ||
# Display it using the following command in the example directory: | ||
# solara run app.py | ||
# It will automatically update and display any changes made to this file | ||
page = SolaraViz( | ||
model, | ||
components=[plot_network], | ||
model_params=model_params, | ||
name="Alliance Formation Model", | ||
) | ||
page # noqa |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
10 changes: 10 additions & 0 deletions
10
mesa/examples/basic/alliance_formation_model/multi_level_alliance/__init__.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
import logging | ||
|
||
# Configure logging | ||
logging.basicConfig( | ||
level=logging.DEBUG, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s" | ||
) | ||
|
||
# Example usage of logging | ||
logger = logging.getLogger(__name__) | ||
logger.info("Logging is configured and ready to use.") |
72 changes: 72 additions & 0 deletions
72
mesa/examples/basic/alliance_formation_model/multi_level_alliance/agents.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
import mesa | ||
from mesa.experimental.meta_agents import create_multi_levels | ||
|
||
|
||
def calculate_shapley_value(calling_agent, other_agent): | ||
""" | ||
Calculate the Shapley value of the two agents | ||
""" | ||
new_position = 1 - abs(calling_agent.position - other_agent.position) | ||
potential_utility = (calling_agent.power + other_agent.power) * 1.1 * new_position | ||
value_me = 0.5 * calling_agent.power + 0.5 * (potential_utility - other_agent.power) | ||
value_other = 0.5 * other_agent.power + 0.5 * ( | ||
potential_utility - calling_agent.power | ||
) | ||
|
||
# Determine if there is value in the alliance | ||
if value_me > calling_agent.power and value_other > other_agent.power: | ||
if other_agent.level > calling_agent.level: | ||
level = other_agent.level | ||
elif other_agent.level == calling_agent.level: | ||
level = calling_agent.level + 1 | ||
else: | ||
level = calling_agent.level | ||
|
||
return (potential_utility, new_position, level) | ||
else: | ||
return None | ||
|
||
|
||
class AllianceAgent(mesa.Agent): | ||
""" | ||
Agent has three attributes power (float), position (float) and level (int) | ||
""" | ||
|
||
def __init__(self, model, power, position, level=0): | ||
super().__init__(model) | ||
self.power = power | ||
self.position = position | ||
self.level = level | ||
|
||
def form_alliance(self): | ||
# Randomly select another agent of the same type | ||
other_agents = [ | ||
agent for agent in self.model.agents_by_type[type(self)] if agent != self | ||
] | ||
|
||
# Determine if there is a beneficial alliance | ||
if other_agents: | ||
other_agent = self.random.choice(other_agents) | ||
shapley_value = calculate_shapley_value(self, other_agent) | ||
if shapley_value: | ||
class_name = f"MetaAgentLevel{shapley_value[2]}" | ||
meta = create_multi_levels( | ||
self.model, | ||
class_name, | ||
{other_agent, self}, | ||
meta_attributes={ | ||
"level": shapley_value[2], | ||
"power": shapley_value[0], | ||
"position": shapley_value[1], | ||
}, | ||
retain_subagent_functions=True, | ||
) | ||
|
||
# Update the network if a new meta agent instance created | ||
if meta: | ||
self.model.network.add_node( | ||
meta.unique_id, | ||
size=(meta.level + 1) * 300, | ||
level=meta.level, | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
39 changes: 39 additions & 0 deletions
39
mesa/examples/basic/alliance_formation_model/multi_level_alliance/model.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
import networkx as nx | ||
import numpy as np | ||
|
||
import mesa | ||
from mesa.examples.basic.alliance_formation_model.multi_level_alliance.agents import AllianceAgent | ||
|
||
|
||
class AllianceModel(mesa.Model): | ||
def __init__(self, n=50, mean=0.5, std_dev=0.1, seed=42): | ||
super().__init__(seed=seed) | ||
self.population = n | ||
self.network = nx.Graph() # Initialize the network | ||
self.datacollector = mesa.DataCollector(model_reporters={"Network": "network"}) | ||
|
||
# Create Agents | ||
power = self.rng.normal(mean, std_dev, n) | ||
power = np.clip(power, 0, 1) | ||
position = self.rng.normal(mean, std_dev, n) | ||
position = np.clip(position, 0, 1) | ||
AllianceAgent.create_agents(self, n, power, position) | ||
agent_ids = [ | ||
(agent.unique_id, {"size": 300, "level": 0}) for agent in self.agents | ||
] | ||
self.network.add_nodes_from(agent_ids) | ||
|
||
def add_link(self, meta_agent, agents): | ||
for agent in agents: | ||
self.network.add_edge(meta_agent.unique_id, agent.unique_id) | ||
|
||
def step(self): | ||
for agent_class in list( | ||
self.agent_types | ||
): # Convert to list to avoid modification during iteration | ||
self.agents_by_type[agent_class].shuffle_do("form_alliance") | ||
|
||
# Update graph | ||
if agent_class is not AllianceAgent: | ||
for meta_agent in self.agents_by_type[agent_class]: | ||
self.add_link(meta_agent, meta_agent.subset) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
"""Implementation of Mesa's meta agent capability.""" | ||
|
||
from mesa.agent import Agent, AgentSet | ||
|
||
|
||
class MetaAgent(Agent): | ||
"""A MetaAgent is an agent that contains other agents as components.""" | ||
|
||
def __init__(self, model, agents): | ||
"""Create a new MetaAgent.""" | ||
super().__init__(model) | ||
self._subset = AgentSet(agents or [], random=model.random) | ||
|
||
# Add ref to meta_agent in subagents | ||
for agent in self._subset: | ||
agent.meta_agent = self # TODO: Make a set for meta_agents | ||
|
||
@property | ||
def subset(self): | ||
"""Read-only access to components as an AgentSet.""" | ||
return self._subset | ||
|
||
def add_subagents(self, new_agents: set[Agent]): | ||
"""Add an agent as a component. | ||
Args: | ||
new_agents (Agent): The agents to add to MetaAgent subset | ||
""" | ||
for agent in new_agents: | ||
self._subset.add(agent) | ||
|
||
for agent in new_agents: | ||
agent.meta_agent = self # TODO: Make a set for meta_agents | ||
|
||
def remove_subagents(self, remove_agents: set[Agent]): | ||
"""Remove an agent component. | ||
Args: | ||
remove_agents (Agent): The agents to remove from MetAgents | ||
""" | ||
for agent in remove_agents: | ||
self._subset.discard(agent) | ||
|
||
for agent in remove_agents: | ||
agent.meta_agent = None # TODO: Remove meta_agent from set | ||
|
||
def step(self): | ||
"""Perform the agent's step. | ||
Override this method to define the meta agent's behavior. | ||
By default, does nothing. | ||
""" | ||
|
||
def __len__(self): | ||
"""Return the number of components.""" | ||
return len(self._subset) | ||
|
||
def __iter__(self): | ||
"""Iterate over components.""" | ||
return iter(self._subset) | ||
|
||
def __contains__(self, agent): | ||
"""Check if an agent is a component.""" | ||
return agent in self._subset |
Oops, something went wrong.