forked from projectmesa/mesa
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Add create meta-agents to experimental - Add tests of meta-agents - Add example with an alliance formation model in basic examples
- Loading branch information
Showing
9 changed files
with
539 additions
and
3 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
# Alliance Formation Model | ||
|
||
## Summary | ||
This model demonstrates Mesa's ability to dynamically create new classes of agents that are composed of existing agents. These meta-agents inherits functions and attributes from their sub-agents and users can specify new functionality or attributes they want the meta agent to have. For example, if a user is doing a factory simulation with autonomous systems, each major component of that system can be a sub-agent of the overall robot agent. Or, if someone is doing a simulation of an organization, individuals can be part of different organizational units that are working for some purpose. | ||
|
||
To provide a simple demonstration of this capability is an alliance formation model. | ||
|
||
In this simulation n agents are created, who have two attributes (1) power and (2) preference. Each attribute is a number between 0 and 1 over a gaussian distribution. Agents then randomly select other agents and use the [bilateral shapley value](https://en.wikipedia.org/wiki/Shapley_value) to determine if they should form an alliance. If the expected utility support an alliances, the agent creates a meta-agent. Subsequent steps may add agents to the meta-agent, create new instances of similar hierarchy, or create a new hierarchy level where meta-agents form an alliance of meta-agents. In this visualization of this model a new meta-agent hierarchy will be a larger node and a new color. | ||
|
||
In its current configuration, agents being part of multiple meta-agents is not supported | ||
|
||
## Installation | ||
|
||
This model requires Mesa's recommended install and scipy | ||
``` | ||
$ pip install mesa[rec] scipy | ||
``` | ||
|
||
## How to Run | ||
|
||
To run the model interactively, in this directory, run the following command | ||
|
||
``` | ||
$ solara run app.py | ||
``` | ||
|
||
## Files | ||
|
||
* ``model.py``: Contains creation of agents, the network and management of agent execution. | ||
* ``agents.py``: Contains logic for forming alliances and creation of new agents | ||
* ``app.py``: Contains the code for the interactive Solara visualization. | ||
|
||
## Further Reading | ||
|
||
The full tutorial describing how the model is built can be found at: | ||
https://mesa.readthedocs.io/en/latest/tutorials/intro_tutorial.html | ||
|
||
An example of the bilateral shapley value in another model: | ||
[Techno-Social Energy Infrastructure Siting: Sustainable Energy Modeling Programming (SEMPro)](https://www.jasss.org/16/3/6.html) | ||
|
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.") |
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,71 @@ | ||
import mesa | ||
from mesa.experimental.meta_agents import create_meta_agent | ||
|
||
|
||
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.hierarchy > calling_agent.hierarchy: | ||
hierarchy = other_agent.hierarchy | ||
elif other_agent.hierarchy == calling_agent.hierarchy: | ||
hierarchy = calling_agent.hierarchy + 1 | ||
else: | ||
hierarchy = calling_agent.hierarchy | ||
|
||
return (potential_utility, new_position, hierarchy) | ||
else: | ||
return None | ||
|
||
|
||
class AllianceAgent(mesa.Agent): | ||
""" | ||
Agent has three attributes power (float), position (float) and hierarchy (int) | ||
""" | ||
|
||
def __init__(self, model, power, position, hierarchy=0): | ||
super().__init__(model) | ||
self.power = power | ||
self.position = position | ||
self.hierarchy = hierarchy | ||
|
||
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"MetaAgentHierarchy{shapley_value[2]}" | ||
meta = create_meta_agent( | ||
self.model, | ||
class_name, | ||
{other_agent, self}, | ||
meta_attributes={ | ||
"hierarchy": shapley_value[2], | ||
"power": shapley_value[0], | ||
"position": shapley_value[1], | ||
}, | ||
) | ||
|
||
# Update the network if a new meta agent instance created | ||
if meta: | ||
self.model.network.add_node( | ||
meta.unique_id, | ||
size=(meta.hierarchy + 1) * 300, | ||
hierarchy=meta.hierarchy, | ||
) |
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 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
import networkx as nx | ||
import numpy as np | ||
from agents import AllianceAgent | ||
|
||
import mesa | ||
|
||
|
||
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 = np.random.normal(mean, std_dev, n) | ||
power = np.clip(power, 0, 1) | ||
position = np.random.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, "hierarchy": 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.agents) |
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,25 @@ | ||
"""This method is for dynamically creating new agents (meta-agents). | ||
Meta-agents are defined as agents composed of existing agents. | ||
Meta-agents are created dynamically with a pointer to the model, name of the meta-agent,, | ||
iterable of agents to belong to the new meta-agents, any new functions for the meta-agent, | ||
any new attributes for the meta-agent, whether to retain sub-agent functions, | ||
whether to retain sub-agent attributes. | ||
Examples of meta-agents: | ||
- An autonomous car where the subagents are the wheels, sensors, | ||
battery, computer etc. and the meta-agent is the car itself. | ||
- A company where the subagents are employees, departments, buildings, etc. | ||
- A city where the subagents are people, buildings, streets, etc. | ||
Currently meta-agents are restricted to one parent agent for each subagent/ | ||
one meta-agent per subagent. | ||
Goal is to assess usage and expand functionality. | ||
""" | ||
|
||
from .meta_agents import create_meta_agent | ||
|
||
__all__ = ["create_meta_agent"] |
Oops, something went wrong.