Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dev #117

Merged
merged 25 commits into from
Oct 30, 2024
Merged

Dev #117

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion docs/Introduction/introduction_creator.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -609,7 +609,6 @@
" overcrowding: bool | None = None\n",
" only_exact_n_agents: bool = False\n",
" n_locations: int | None = None\n",
" static_weight: bool = False\n",
" recycle: bool = True\n",
"\n",
" def filter(self, agent: p2n.Agent) -> bool:\n",
Expand Down
63 changes: 32 additions & 31 deletions docs/Introduction/introduction_simulations.ipynb

Large diffs are not rendered by default.

44 changes: 35 additions & 9 deletions src/pop2net/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,17 +65,26 @@ def shared_locations(self, agent, location_classes: list | None = None):
location_classes=location_classes,
)

def add_location(self, location: _location.Location) -> None:
def add_location(self, location: _location.Location, weight: float | None = None) -> None:
"""Add this Agent to a given location.

Args:
location: Add agent to this location.
weight (float | None): The edge weight between the agent and the location.
Defaults to None.
"""
self.model.add_agent_to_location(self, location)
self.model.add_agent_to_location(agent=self, location=location, weight=weight)

def add_locations(self, locations: list) -> None:
def add_locations(self, locations: list, weight: float | None = None) -> None:
"""Add this agent to multiple locations.

Args:
locations (list): Add the agent to these locations.
weight (float | None): The edge weight between the agent and the location.
Defaults to None.
"""
for location in locations:
self.add_location(location)
self.add_location(location=location, weight=weight)

def remove_location(self, location: _location.Location) -> None:
"""Remove this Agent from a given location.
Expand All @@ -86,6 +95,11 @@ def remove_location(self, location: _location.Location) -> None:
self.model.remove_agent_from_location(self, location)

def remove_locations(self, locations: list) -> None:
"""Remove this Agent from the given locations.

Args:
locations (list): A list of location instances.
"""
for location in locations:
self.remove_location(location)

Expand All @@ -99,12 +113,14 @@ def locations(self) -> _sequences.LocationList:
return self.model.locations_of_agent(self)

def get_agent_weight(self, agent: Agent, location_classes: list | None = None) -> float:
"""Return the contact weight between this agent and a given other agent.
"""Return the edge weight between this agent and a given other agent.

This is summed over all shared locations.

Args:
agent_v: The other agent.
agent: The other agent.
location_classes (list): A list of location classes to specify the type of locations
which are considered.

Returns:
A weight of the contact between the two agents.
Expand All @@ -115,16 +131,26 @@ def get_agent_weight(self, agent: Agent, location_classes: list | None = None) -
return weight

def get_location_weight(self, location) -> float:
"""Return the edge weight between this agent and a given location.

Args:
location (_type_): A location.

Returns:
float: The edge weight.
"""
return self.model.get_weight(agent=self, location=location)

def connect(self, agent: Agent, location_cls: type):
def connect(self, agent: Agent, location_cls: type, weight: float | None = None):
"""Connects this agent with a given other agent via an instance of a given location class.

Args:
agents (list): An agent to connect with.
agent (list): An agent to connect with.
location_cls (type): The location class that is used to create a location instance.
weight(float | None): The edge weight between the agents and the location.
Defaults to None.
"""
self.model.connect_agents(agents=[self, agent], location_cls=location_cls)
self.model.connect_agents(agents=[self, agent], location_cls=location_cls, weight=weight)

def disconnect(
self,
Expand Down
56 changes: 55 additions & 1 deletion src/pop2net/creator.py
Original file line number Diff line number Diff line change
Expand Up @@ -453,36 +453,59 @@ def create_locations(
location_classes: list,
agents: list | p2n.AgentList | None = None,
clear: bool = False,
# TODO: delete_magic_location_attributes: bool = True,
delete_magic_agent_attributes: bool = True,
) -> p2n.LocationList:
"""Creates location instances and connects them with the given agent population.

Args:
location_classes (list): A list of location classes.
agents (list | p2n.AgentList): A list of agents.
clear (bool): Should the locations already included in the model be removed?
delete_magic_location_attributes (bool): If True, all magic location attributes will be
removed after the creation of the location instances.
delete_magic_agent_attribtues (book): If True, all magic agent attributes will be
removed after the creation of the location instances.

Returns:
p2n.LocationList: A list of locations.
"""
if clear:
# Remove all locations
self.model.remove_locations(self.model.locations)

if agents is None:
# Use the existing agents in the model if no agents are given
agents = self.model.agents

# self._dummy_model = p2n.Model()
# Create a list containing the names of all special location attributes
# to delete those attributes later
magic_agent_attributes = []

# Add the agents to the dummy model
self._dummy_model.add_agents(agents)

for location_cls in location_classes:
dummy_location = self._create_dummy_location(location_cls)
str_location_cls = dummy_location.type
for agent in agents:
setattr(agent, str_location_cls, None)
magic_agent_attributes.append(str_location_cls)

setattr(agent, str_location_cls + "_assigned", False)
magic_agent_attributes.append(str_location_cls + "_assigned")

setattr(agent, str_location_cls + "_id", None)
magic_agent_attributes.append(str_location_cls + "_id")

setattr(agent, str_location_cls + "_position", None)
magic_agent_attributes.append(str_location_cls + "_position")

setattr(agent, str_location_cls + "_head", None)
magic_agent_attributes.append(str_location_cls + "_head")

setattr(agent, str_location_cls + "_tail", None)
magic_agent_attributes.append(str_location_cls + "_tail")

locations = []

Expand Down Expand Up @@ -730,6 +753,31 @@ def melt(self):
if hasattr(agent, attr):
delattr(agent, attr)

# delete magic location attributes
magic_location_attributes = [
"filter",
"setup",
"bridge",
"split",
"weight",
"stick_together",
"nest",
"melt",
"refine",
]
# TODO: delete magic location attributes
if False: # delete_magic_location_attributes:
for cls in location_classes:
del cls.stick_together
del cls.setup

magic_agent_attributes = set(magic_agent_attributes)
if delete_magic_agent_attributes:
for attr in magic_agent_attributes:
for agent in agents:
# if hasattr(agent, attr):
delattr(agent, attr)

return locations

def create(
Expand All @@ -744,6 +792,7 @@ def create(
sample_weight: str | None = None,
replace_sample_level_column: bool = True,
clear: bool = False,
delete_magic_agent_attributes: bool = True,
) -> tuple:
"""Creates agents and locations based on a given dataset.

Expand All @@ -770,6 +819,10 @@ def create(
replace_sample_level_column (bool): Should the original values of the sample level be
overwritten by unique values after sampling to avoid duplicates?
clear (bool): Should the agents and locations already included in the model be removed?
delete_magic_location_attributes (bool): If True, all magic location attributes will be
removed after the creation of the location instances.
delete_magic_agent_attributes (bool): If True, all magic agent attributes will be
removed after the creation of the location instances.

Returns:
tuple: A list of agents and a list of locations.
Expand Down Expand Up @@ -797,6 +850,7 @@ def create(
agents=agents,
location_classes=location_classes,
clear=clear,
delete_magic_agent_attributes=delete_magic_agent_attributes,
)

return agents, locations
Expand Down
64 changes: 30 additions & 34 deletions src/pop2net/location.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,22 +34,29 @@ def agents(self) -> AgentList:
"""
return self.model.agents_of_location(self)

def add_agent(self, agent: _agent.Agent) -> None:
def add_agent(self, agent: _agent.Agent, weight: float | None = None) -> None:
"""Assigns the given agent to this location.

Args:
agent: The agent that should be added to the location.
weight: The edge weight between the agent and the location.
Defaults to None. If weight is set to None, the weight is generated by
location.weight(), which returns 1 by default.
"""
self.model.add_agent_to_location(self, agent)
self.model.add_agent_to_location(self, agent=agent, weight=weight)

def add_agents(self, agents: list) -> None:
def add_agents(self, agents: list, weight: float | None = None) -> None:
"""Add multiple agents at once.

Args:
agents (list): An iterable over agents.
weight(float | None): The edge weight between the agents and the location.
luerhard marked this conversation as resolved.
Show resolved Hide resolved
Defaults to None. If weight is set to None, the weight is generated by
location.weight(), which returns 1 by default.

"""
for agent in agents:
self.add_agent(agent)
self.add_agent(agent=agent, weight=weight)

def remove_agent(self, agent: _agent.Agent) -> None:
"""Removes the given agent from this location.
Expand Down Expand Up @@ -84,14 +91,20 @@ def neighbors(self, agent: _agent.Agent) -> AgentList:
agents.remove(agent)
return agents

def set_weight(self, agent, weight) -> None:
def set_weight(self, agent, weight: float | None = None) -> None:
"""Set the weight of an agent at the current location.

If weight is None the method location.weight() will be used to generate a weight.

Args:
agent (Agent): The agent.
weight (float): The weight.
"""
self.model.set_weight(agent=agent, location=self, weight=weight)
self.model.set_weight(
agent=agent,
location=self,
weight=weight,
)

def get_weight(self, agent: _agent.Agent) -> float:
"""Return the edge weight between an agent and the location.
Expand All @@ -104,6 +117,17 @@ def get_weight(self, agent: _agent.Agent) -> float:
"""
return self.model.get_weight(agent=agent, location=self)

def weight(self, agent) -> float: # noqa: ARG002
"""Generates the edge weight between a given agent and the location instance.

Args:
agent (_type_): An agent.

Returns:
float: The weight between the given agent and the location.
"""
return 1

def project_weights(self, agent1: _agent.Agent, agent2: _agent.Agent) -> float:
"""Calculates the edge weight between two agents assigned to the same location instance.

Expand Down Expand Up @@ -194,21 +218,6 @@ def split(self, agent: _agent.Agent) -> float | str | list | None: # noqa: ARG0
"""
return None

def weight(self, agent: _agent.Agent) -> float | None: # noqa: ARG002
"""Defines the edge weight between the agent and the location instance.

Defines how the edge weight between an agent and the location is determined.
This is a boilerplate implementation of this method which always returns 1; i.e. all
edge weights will be 1. Override this method in your own implementations as you seem fit.

Args:
agent: The agent that is currently processed by the Creator.

Returns:
The edge weight.
"""
return None

def stick_together(self, agent: _agent.Agent) -> float | str:
"""Assigns agents with a shared value on an attribute to the same location instance.

Expand Down Expand Up @@ -246,19 +255,6 @@ def refine(self):
"""An action that is performed after all location instances have been created."""
pass

def _update_weight(self, agent: _agent.Agent) -> None:
"""Create or update the agent-speific weight.

Args:
agent: The agent to be updated.
"""
self.set_weight(agent, self.weight(agent))

def _update_weights(self) -> None:
"""Update the weight of every agent on this location."""
for agent_ in self.agents:
self._update_weight(agent_)

def _subsplit(self, agent: _agent.Agent) -> str | float | list | None: # noqa: ARG002
"""Splits a location instance into sub-instances to create a certain network structure.

Expand Down
Loading
Loading