Documentation
ECAgent.Core module
- class ECAgent.Core.Agent(id: str, model: Model, tag: int = None)[source]
Bases:
objectThis is the base class for Agent objects. Inherit from the class when creating custom agent types.
In ECAgent, An
Agentis theEntityin the Entity-Component-System (ECS) architecture.- components
The components associated with the environment. The key is the Class of the component.
- Type:
dict
- id
The agent’s unique identifier.
- Type:
str
- tag
The value of the Tag associated with the
Agent. Defaults to 0 (which is the valueNONE) or the default tag value of the Agent’s metaclass.- Type:
int
- add_component(component: Component)[source]
Adds a
Componentto theAgent.- Parameters:
component (Component) – The component to add.
- Raises:
ValueError – If the agent already has a component of that type.
- getComponent(component_type: type, throw_error: bool = False)[source]
Deprecated. Use
get_componentinstead.
- get_component(component_type: type, throw_error: bool = False)[source]
Gets a component that is the same type as
component_type.- Parameters:
component_type (type) – The type of component to search for.
throw_error (bool, Optional) – Boolean that specifies whether a
ComponentNotFoundErrorshould be raised upon failing to find a component of typecomponent_type. Defaults toFalse.
- Returns:
Component – A component object matching the class specified by
component_type.None – Returns None if agent does not have a component of class
component_type.
- Raises:
ComponentNotFoundError – If
throw_errorisTrueand no component matchingcomponent_typeis found.
- has_component(*args) bool[source]
Returns a (True/False) bool if the agent (does/does not) have the list of specified components.
The functions uses the
*argsso you can check for multiple components at once:# Will return true if agent has a PositionComponent agent.has_component(PositionComponent) # Will return true if agent has both a PositionComponent and a RotationComponent agent.has_component(PositionComponent, RotationComponent)
- Parameters:
args – The list of
Componentclasses that will checked.- Returns:
TrueifAgenthas all of the components listed, elseFalse- Return type:
bool
- remove_component(component_type: type)[source]
Removes component of type
`component_typefrom the agent.Parameters: component_type : type
Class of component to be removed from agent.
- Raises:
ComponentNotFoundError – If agent does not have a component of class
component_type.
- exception ECAgent.Core.AgentNotFoundError(a_id: str, environment: <class 'ECAgent.Core.Environment'>)[source]
Bases:
ExceptionException raised for errors when an agent object cannot be found.
Attributes:
- a_idstr
idof Agent to search for.- environmentEnvironment
Environment that was searched.
- messagestr
Explanation of error.
- class ECAgent.Core.Component(agent, model: Model)[source]
Bases:
objectThis is the base class for Components. Inherit from this class to make your own components.
- exception ECAgent.Core.ComponentNotFoundError(agent: <class 'ECAgent.Core.Agent'>, component_type: type)[source]
Bases:
ExceptionException raised for errors when components are accessed on agents that do not have them.
Attributes:
- agentAgent
Agent whose components list was accessed.
- component_typetype
Class of Component that was searched for.
- messagestr
Explanation of error.
- exception ECAgent.Core.DuplicateAgentError(a_id: str, environment: <class 'ECAgent.Core.Environment'>)[source]
Bases:
ExceptionException raised for errors when an agent object already exists in an environment.
Attributes:
- a_idstr
idof the Agent.- environmentEnvironment
The
Environmentthe agents exists in.- messagestr
Explanation of error.
- class ECAgent.Core.Environment(model, id: str = 'ENVIRONMENT')[source]
Bases:
AgentBase environment class. It is a void environment which means that is has no spacial properties.
In ECAgent, all environments are treated as agents. This means that they can have components added and removed from them. From a design perspective, An
Environmentis anAgentthat contains other agents.- agents
A
dictof agents occupying the environment. The key is the agent’s id.- Type:
dict
- components
The components associated with the environment. The key is the Class of the component.
- Type:
dict
- id
The agent id of the environment.
- Type:
str
- tag
The value of the Tag associated with the environment. Defaults to 0 (which is the value
NONE).- Type:
int
- addAgent(agent: <class 'ECAgent.Core.Agent'>)[source]
Deprecated. Use
Environment.add_agentinstead.
- add_agent(agent: <class 'ECAgent.Core.Agent'>)[source]
Adds an agent to the environment. Agents cannot have duplicate
idvalues.- Parameters:
agent (Agent) – The agent being added to the environment.
- Raises:
DuplicateAgentError – If the agent already exists in the environment.
- getAgent(id: str, throw_error: bool = False)[source]
Deprecated. Use
Environment.remove_agentinstead.
- get_agent(id: str, throw_error: bool = False)[source]
Gets agent obj based on its
id.Returns None if agent does not exist.
- Parameters:
id (str) – The id of the agent object to search for.
throw_error (bool, Optional) – Determines if the function should raise an AgentNotFoundError when it cannot find an agent object with a matching id.
- Returns:
The agent with
agent.id == id- Return type:
- Raises:
AgentNotFoundError – If
throw_error == Trueand agent withagent.id == idcould not be found.
- get_agents(*args, tag: int = None) list[source]
Returns a list of agents within the environment.
This method is very flexible. There are three (four technically) ways it can be used:
The default case:
all_agents = environment.get_agents()
Using a component template:
# This will return a list of agents with Components of type 'Component1' and 'Component2' template_search = environments.get_agents(Component1, Component2)
Using an agent’s tag::
# This code assumes the tag 'PREY' already exists import ECAgent.Tags as Tags tag_search = environment.get_agents(tag = Tags.PREY)
Additionally, you can specify a component template and tag to search for (although this is a niche case):
# This code assumes the tag 'PREY' already exists import ECAgent.Tags as Tags # This will return a list of agents with Components of type 'Component1' and tag == PREY template_tag_search = environments.get_agents(Component1, tag = Tags.PREY)
- Parameters:
*args (Optional) – A template (list of Components) the returned agents must have.
tag (int, Optional) – Tag that the returned agents must have.
- Returns:
list of Agents
- Return type:
list
- get_random_agent(*args, tag: int = None)[source]
Returns a random agent in the environment.
See
Agent.get_agentsfor a guide on how component template and tag searches work. This function usesAgent.get_agents(*args, tag)to get a list of valid agents to randomly select from.- Parameters:
*args (Optional) – A template (list of Components) the returned agent must have.
tag (int, Optional) – Tag that the returned agent must have.
- Returns:
Agent – A randomly selected agent from a list of agents matching the Component template or tag specified.
None – If no agents exist that match the Component template or tag specified.
- remove_agent(a_id: str)[source]
Removes an agent with
agent.id == a_idfrom the environment.- Parameters:
a_id (str) – The
idof the agent to remove.- Raises:
AgentNotFoundError – If no agent with an
agent.id == a_idcan be found.
- shuffle(*args, tag: int = None)[source]
Returns a list of agents with matching components in a random order.
This method is just a wrapper for calling
self.model.random.shuffle(self.get_agents(*args, tag=tag)). The method finds a list of agents with components and/or tag matching those included in*argsandtagrespectively. This method returns them in random order.The order is random each time the function is called. This is useful if you want to mitigate benefits agents get when executing their behaviour earlier than others.
- Parameters:
args – A list of
Componentclasses that describe a template the agents need to match in order to be included in the list of shuffled agents.tag (int, Optional) – The tag the returned agents need to have in order to be included in the list of shuffled agents. Default to None which disables tag filtering.
- Returns:
Of agents with components matching
*args. The order of the agents is random.- Return type:
list
- class ECAgent.Core.Model(seed: int = None, logger: Logger = None)[source]
Bases:
objectThis is the base class for your Agent-based Models. You inherit this class to again access to all of the ECS functionality
- environment
The model’s environment.
- Type:
- systems
The
SystemManagerassigned to the model.- Type:
- random
The model’s pseudo-random number generator.
- Type:
random.Random
- logger
The model’s logger.
- Type:
logging.Logger
- complete() None[source]
Marks the model as
ModelStatus.COMPLETE. This means it will no longer execute even ifself.systems.execute_systems()is called manually.You should only use this command if your model no longer needs to run.
- execute(n: int = 1)[source]
A wrapper method for calling
model.systems.execute_systems().- Parameters:
n (int, Optional) – The number of times to call
systems.execute(). Defaults to 1.- Raises:
TypeError – If
nis not anint.ValueError – If
n < 1.
- is_running() bool[source]
Returns
Trueif model is still running andFalseif model is complete.- Returns:
That is
Trueif model is still running andFalseif not.- Return type:
bool
- set_environment(env)[source]
Sets the models environment. model.set_environment(new_environment) is equivalent to
model.environment = new_environment.- Parameters:
env (Environment) – The model’s new environment.
- exception ECAgent.Core.ModelCompleteError[source]
Bases:
ExceptionException raised for errors when systems are executed and Model is marked as finished.
Attributes:
- messagestr
Explanation of error.
- class ECAgent.Core.ModelStatus(value, names=None, *, module=None, qualname=None, type=None, start=1, boundary=None)[source]
Bases:
IntEnumEnum that describes the status of a
Model.Values are:
RUNNING = 0 COMPLETE = 1
- class ECAgent.Core.System(id: str, model: Model, priority: int = 0, frequency: int = 1, start: int = 0, end: int = 9223372036854775807)[source]
Bases:
objectThis is the base class for the systems in ECAgent’s Entity-Component-System (ECS) architecture.
- id
The id of the system.
- Type:
str
- priority
The priority of the system. Higher priority systems execute first. Defaults to
0.- Type:
int
- frequency
How often the system should execute. Defaults to
1which means the system will execute every timestep.- Type:
int
- start
The timestep at which the system should start executing. Defaults to
0.- Type:
int
- end
The last timestep at which the system should start executing. Defaults to
sys.maxsize.- Type:
int
- class ECAgent.Core.SystemManager(model: Model)[source]
Bases:
objectThis class is responsible for managing the adding, removing and executing of Systems.
Every
Modelwill get aSystemManagerwhich they can access usingmodel.systems.- timestep
The amount of time that has elapsed in the model.
- Type:
int
- systems
A dictionary containing all of the model’s systems. The key is the system’s id.
- Type:
dict
- execution_queue
A list of containing the order at which the systems execute when
execute_systems()is called.- Type:
list
- component_pools
A dictionary containing lists of all components registered with the
SystemManager. The key is type of theComponent.- Type:
dict
- add_system(s: System)[source]
Adds System s to the
SystemManagerand registers it with execution queue.- Parameters:
s (System) – The
Systembeing added to theSystemManager- Raises:
KeyError – If system already exists in the execution queue.
- deregister_component(component: Component)[source]
Deregisters (removes) a component from the
SystemManagercomponent pool.- Parameters:
component (Component) – The
Componentto deregister.- Raises:
KeyError – When
componentis not registered with theSystemManager.
- execute_systems(throw_error: bool = False)[source]
Function that loops through all systems in the
execution_queueand calls theexecute()method. The value ofSystemManager.timestepis increased by1each time this method is called.If a
Modelis marked as complete,execute_systems()will do nothing or throw aModelCompleteErrorifthrow_error == True.The function uses the System’s
start,endandfrequencyto determine if itsexecute()should be called:- if sys.start <= self.timestep <= sys.end and (sys.start - self.timestep) % sys.frequency == 0:
sys.execute()
- Parameters:
throw_error (Optional, bool) – Determines if a
ModelCompleteerror should be thrown if a user tries to callexecute_systemson a model marked as complete.- Raises:
ModelCompleteError – If
throw_error == Trueand this function is called on a model marked as complete.
- get_components(component_type: type, throw_error: bool = False)[source]
Returns the list of components registered to the
SystemManagerwith a type ofcomponent_type. ReturnsNoneif there are no components of typecomponent_typeregistered with theSystemManager.- Parameters:
component_type (type) – The type of components you want to search for (e.g.
PositionComponent).throw_error (bool, Optional) – Determines if the function should raise a
KeyErrorwhen it cannot find components oftype == component_type. Defaults toFalse
- Returns:
list – Of components with type
component_type.None – If no components of type
component_typecan be found.
- Raises:
KeyError – If
throw_error = Trueand no components oftype == component_typeare found.
- register_component(component: Component)[source]
Registers a component with the
SystemManager.Registered components can be accessed using
SystemManager.component_pools[type(component)].- Parameters:
component (Component) – The
Componentto register.- Raises:
KeyError – When
componenthas already been registered with theSystemManager.
- remove_system(s_id: str)[source]
Removes
SystemwithSystem.id == s_idfrom theSystemManager.- Parameters:
s_id (str) – The id of the system to be removed.
- Raises:
SystemNotFoundError – If the no System with
System.id == s_idcan be found.
- exception ECAgent.Core.SystemNotFoundError(s_id: str)[source]
Bases:
ExceptionException raised for errors when systems that don’t exist are accessed.
Attributes:
- s_idstr
idof system that was searched for.- messagestr
Explanation of error.
- class ECAgent.Core._MetaAgent(name: str, bases: tuple, properties: dict)[source]
Bases:
typeThis is the base metaclass for
Agentclasses. The class is responsible for supporting class components ( components attached to classes as opposed to agents).Class components can be used as follows:
class CustomComponent(Component): def __init__(self, agent, model): super().__init__(agent, model) self.x = 1 self.y = 'Some Text' class CustomAgent(Agent): pass # Add CustomComponent to CustomAgent class model = Model() CustomAgent.add__class_component(CustomComponent(CustomAgent, model) # Get properties of component print(CustomAgent[CustomComponent].x) # Prints '1' print(CustomAgent.get_class_component(CustomComponent).y) # Prints 'Some Text'
You can also set the default tag value of instantiated agents:
import ECAgent.Tags as Tags Tags.add_tag('SHEEP') # Add new sheep tag CustomAgent.tag = Tags.SHEEP # All future CustomAgents will have a tag of 'SHEEP'
- components
The components associated with the environment. The key is the Class of the component.
- Type:
dict
- id
The unique identifier of the class (defaults to the class’ name).
- Type:
str
- tag
The value of the Tag associated with the
Agent. Defaults to 0 (which is the valueNONE) or the default tag value of the Agent’s metaclass.- Type:
int
- add_class_component(component: Component)[source]
Adds a
Componentto the_MetaAgent.- Parameters:
component (Component) – The component to add.
- Raises:
ValueError – If the agent already has a component of that type.
- get_class_component(component_type: type, throw_error: bool = False)[source]
Gets a component that is the same type as
component_type.- Parameters:
component_type (type) – The type of component to search for.
throw_error (bool, Optional) – Boolean that specifies whether a
ComponentNotFoundErrorshould be raised upon failing to find a component of typecomponent_type. Defaults toFalse.
- Returns:
Component – A component object matching the class specified by
component_type.None – Returns None if agent does not have a component of class
component_type.
- Raises:
ComponentNotFoundError – If
throw_errorisTrueand no component matchingcomponent_typeis found.
- has_class_component(*args) bool[source]
Returns a (True/False) bool if the agent class (does/does not) have the list of specified components.
The functions uses the
*argsso you can check for multiple components at once:# Will return true if agent has a PositionComponent agent.has_component(PositionComponent) # Will return true if agent has both a PositionComponent and a RotationComponent agent.has_component(PositionComponent, RotationComponent)
- Parameters:
args – The list of
Componentclasses that will checked.- Returns:
TrueifAgenthas all of the components listed, elseFalse- Return type:
bool
- remove_class_component(component_type: type)[source]
Removes component of type
`component_typefrom the agent class.Parameters: component_type : type
Class of component to be removed from agent.
- Raises:
ComponentNotFoundError – If agent does not have a component of class
component_type.
ECAgent.Environments module
- class ECAgent.Environments.ConstantGenerator(value)[source]
Bases:
objectA functor used to create CellComponents with a constant value.
The idea behind this class is to create a
ConstantGenerator(val)object and supply it as the generator when callingadd_cell_componentto a DiscreteWorld (e.g.GridWorld). The component will then be created with all cells havingvalue == valAssuming a
3x3 DiscreteWorld:env.add_cell_component('constant', ConstantGenerator(1))
will create a cell component called
'constant'which will have values stored in a contiguous array:[1,1,1,1,1,1,1,1,1]which when viewed in 2D looks like:1
1
1
1
1
1
1
1
1
- value
The value you want to set your cell component’s values to.
- Type:
Any
- class ECAgent.Environments.DiscreteWorld(model, width: int, height: int | None = 0, depth: int | None = 0, id: str | None = 'ENVIRONMENT', wrap_env: bool | None = False)[source]
Bases:
SpaceWorldBase Class for all Discrete Spacial Environments. It inherits from ``SpaceWorld``class and contains properties and methods related to grid-based environments.
This class also adds functionality to add cell components. This is a special type of component that stores a single value for every cell in the DiscreteWorld.
Assuming a
3x3 DiscreteWorld:env = DiscreteWorld(model, 3, 3)
You can add a cell component to the environment by calling:
env.add_cell_component('example', generator)
Where
'example'will be the name of cell component. The second argument is known as a generator and is a function object (functor) that populates the gridworld with the value of the cell component.A custom generator can be written as follows:
def custom_generator(pos, cells): # add logic here return value_of_cell
When writing a generator, your function must accept two arguments: the
cellswhich is the pandas dataframe of of the environment andposwhich is a 3-tuple which contains the coordinates(x,y,z)of the grid cell you are generating for. So using the example:def sum_generator(pos, cells): return sum(*pos) env.add_cell_component('sum', sum_generator)
Our original
3x3 DiscreteWorldwill get a cell component called'sum'which will have values stored in a contiguous array:[0,1,2,1,2,3,2,3,4]which when viewed in 2D looks like:2
3
4
1
2
3
0
1
2
Note that all cell components are stored as 1D arrays which you can access using
env.cells[cell_name]''. To translate a 3d coordinate (or PositionComponent) into a unique integer to get the value of a specific cell in a ``DiscreteWorld, use thediscrete_grid_pos_to_idmethod. Additionally, allDiscreteWorldenvironments are initialized with a'pos'cell component which contains the 3d coordinate representation of the cell.- width
The width of the environment.
- Type:
int
- height
The height of the environment. Defaults to
0- Type:
int
- depth
The depth of the environment. Defaults to
0- Type:
int
- cells
A table containing all of the cell components in the environment.
- Type:
Pandas.DataFrame
- id
The id of the environment. Defaults to
'ENVIRONMENT'.- Type:
str
- wrap_env
Determines if the environment is toroidal (i.e. agents wrap around the environment instead of moving out of bounds).
- Type:
bool
- add_cell_component(name: str, generator)[source]
Adds the component supplied by the generator functor to each of the cells. The functor is supplied with the cell’s position
(x,y,z)and the environment pandas dataframe as input.A custom generator can be written as follows:
def custom_generator(pos, cells): # add logic here return value_of_cell
You can also use the one of the included generators:
ConstantGeneratororLookupGenerator. Alternatively, you can populate the cells by directly supplying their data as a 1D contiguous array that is the same size as the environment. Assuming a3x3 GridWorld:data = [0, 1, 2, 3, 4, 5, 6, 7, 8] env.add_cell_component('data', data)
will create a cell component called
'data'which will have values stored in a contiguous array:[0,1,2,3,4,5,6,7,8]which when viewed in 2D looks like:6
7
8
3
4
5
0
1
2
- Parameters:
name (str) – The name of the cell component.
generator (obj | numpy.ndarray | list) – The generator used to populate the cell component. If a obj is supplied, it must have the
__call__method implemented. If anumpy.ndarrayorlistis used, it must be 1-dimensional and of sizewidth * height * depth.
- get_cell(x, y: int = 0, z: int = 0) Series[source]
Returns a
Pandas.Seriescontaining the values of cell components at the specified grid cell.Assuming a
3x1 DiscreteWorldwith two cell components called'rainfall'and'slope':# The cell DataFrame will look something like: [{'pos': (0,0,0), 'rainfall': 10.0, 'slope': 4.0}, {'pos': (1,0,0), 'rainfall': 22.0, 'slope': 21.0}, {'pos': (2,0,0), 'rainfall': 15.0, 'slope': 32.0}] cell = env.get_cell(1) print(cell) # Will print something like: {'pos': (1,0,0), 'rainfall': 22.0, 'slope': 21.0}
- Parameters:
x (int) – The x-coordinate.
y (int, Optional) – The y-coordinate. Defaults to 0.
z (int, Optional) – The z-coordinate. Defaults to 0.
- Returns:
Containing all of the values for the cell components at the indexed grid cell.
- Return type:
Pandas.Series
- Raises:
IndexError – If the specified coordinates are our outside the bound of the environment.
- get_moore_neighbours(cell_pos, radius: int = 1, incl_center: bool = False, ret_type: type = <class 'int'>) list[source]
Returns a list of all cells within the specified moore neighbourhood. If incl_center = true the supplied cell will also be included in that list.
The function accepts three types:
int,tupleorPositionComponent. Ifintis supplied, it will be assumed to be the unique identifier of the cell (i.e. the value returned bydiscrete_grid_pos_to_id(). If atupleis supplied, it is assumed that it will be the coordinates of the cell (e.g.(2,5,3)). If aPositionComponentis supplied, it’s values will be truncated and turned into a 3d integer coordinate (i.e. APositionComponentwith valuex = 2.5, y = 5.9, z = 3.1) will be converted into coordinates(2,5,3).The same functionality applied to the
ret_typeparameters. By default a list of integers containing the unique identifiers of the neighbouring cells are returned. Iftupleis supplied, the function will return the 3d coordinates of the neighbouring will be returned.- Parameters:
cell_pos (int, tuple, PositionComponent) – The cell whose neighbours you want to get.
radius (int, Optional) – The size of the Moore neighbourhood. Defaults to
1.incl_center (bool, Optional) – Flags whether you want the supplied
cell_posto be included in the returnedlistret_type (type, Optional) – The representation of the neighbouring cells, Defaults to
intbut may also betuple.
- Returns:
A list of neighbouring cells in the representation specified by
ret_type. Defaults to a list ofint.- Return type:
list
- Raises:
TypeError – If the type of cell_pos is not
int,tupleorPositionComponentor if the type ofret_typeis notintortuple.
- get_neighbours(cell_pos, radius: int = 1, incl_center: bool = False, ret_type: type = <class 'int'>, mode: str = 'moore') list[source]
Returns a list of all cells within the specified neighbourhood. If incl_center = true the supplied cell will also be included in that list. Both Moore and Von Neumann neighbourhoods are supported.
The function accepts three types:
int,tupleorPositionComponent. Ifintis supplied, it will be assumed to be the unique identifier of the cell (i.e. the value returned bydiscrete_grid_pos_to_id(). If atupleis supplied, it is assumed that it will be the coordinates of the cell (e.g.(2,5,3)). If aPositionComponentis supplied, it’s values will be truncated and turned into a 3d integer coordinate (i.e. APositionComponentwith valuex = 2.5, y = 5.9, z = 3.1) will be converted into coordinates(2,5,3).The same functionality applied to the
ret_typeparameters. By default a list of integers containing the unique identifiers of the neighbouring cells are returned. Iftupleis supplied, the function will return the 3d coordinates of the neighbouring will be returned.- Parameters:
cell_pos (int, tuple, PositionComponent) – The cell whose neighbours you want to get.
radius (int, Optional) – The size of the Moore neighbourhood. Defaults to
1.incl_center (bool, Optional) – Flags whether you want the supplied
cell_posto be included in the returnedlistret_type (type, Optional) – The representation of the neighbouring cells, Defaults to
intbut may also betuple.mode (str) – The type of neighbourhood to return. Can either be
'moore'or'neumann'. Defaults to ``’moore’`.
- Returns:
A list of neighbouring cells in the representation specified by
ret_type. Defaults to a list ofint.- Return type:
list
- Raises:
TypeError – If the type of cell_pos is not
int,tupleorPositionComponentor if the type ofret_typeis notintortuple.KeyError – If the
modesupplied is not'moore'or'neumann'.
- get_neumann_neighbours(cell_pos, radius: int = 1, incl_center: bool = False, ret_type: type = <class 'int'>) list[source]
Returns a list of all cells within the specified von Neumann neighbourhood. If incl_center = true the supplied cell will also be included in that list.
The function accepts three types:
int,tupleorPositionComponent. Ifintis supplied, it will be assumed to be the unique identifier of the cell (i.e. the value returned bydiscrete_grid_pos_to_id(). If atupleis supplied, it is assumed that it will be the coordinates of the cell (e.g.(2,5,3)). If aPositionComponentis supplied, it’s values will be truncated and turned into a 3d integer coordinate (i.e. APositionComponentwith valuex = 2.5, y = 5.9, z = 3.1) will be converted into coordinates(2,5,3).The same functionality applied to the
ret_typeparameters. By default a list of integers containing the unique identifiers of the neighbouring cells are returned. Iftupleis supplied, the function will return the 3d coordinates of the neighbouring will be returned.- Parameters:
cell_pos (int, tuple, PositionComponent) – The cell whose neighbours you want to get.
radius (int, Optional) – The size of the Moore neighbourhood. Defaults to
1.incl_center (bool, Optional) – Flags whether you want the supplied
cell_posto be included in the returnedlistret_type (type, Optional) – The representation of the neighbouring cells, Defaults to
intbut may also betuple.
- Returns:
A list of neighbouring cells in the representation specified by
ret_type. Defaults to a list ofint.- Return type:
list
- Raises:
TypeError – If the type of cell_pos is not
int,tupleorPositionComponentor if the type ofret_typeis notintortuple.
- remove_cell_component(name: str)[source]
Removes a cell component from the environment.
- Parameters:
name (str) – The name of the cell component.
- Raises:
ComponentNotFoundError – If no cell component with the specified name can be found.
- class ECAgent.Environments.GridWorld(model: Model, width: int, height: int, id: str = 'ENVIRONMENT', wrap_env: bool | None = False)[source]
Bases:
DiscreteWorldGridWorld is a discrete environment with 2 axes (x and y). It is a simplified version of its parent
DiscreteWorld.A GridWorld’s dimensions are defined by a
widthandheightproperties.- width
The width of the environment.
- Type:
int
- height
The height of the environment. Defaults to
0- Type:
int
- cells
A table containing all of the cell components in the environment.
- Type:
Pandas.DataFrame
- id
The id of the environment. Defaults to
'ENVIRONMENT'.- Type:
str
- wrap_env
Determines if the environment is toroidal (i.e. agents wrap around the environment instead of moving out of bounds).
- Type:
bool
- class ECAgent.Environments.LineWorld(model: Model, width: int, id: str = 'ENVIRONMENT', wrap_env: bool | None = False)[source]
Bases:
DiscreteWorldLineWorld is a discrete environment with only 1 axis (x-axis). It is a simplified version of its parent
DiscreteWorld.A LineWorld’s dimensions are defined by a
widthproperty.- width
The width of the environment.
- Type:
float
- cells
A table containing all of the cell components in the environment.
- Type:
Pandas.DataFrame
- id
The id of the environment. Defaults to
'ENVIRONMENT'.- Type:
str
- wrap_env
Determines if the environment is toroidal (i.e. agents wrap around the environment instead of moving out of bounds).
- Type:
bool
- class ECAgent.Environments.LookupGenerator(table)[source]
Bases:
objectA functor used to create CellComponents based on a Lookup table.
The intention behind this class is to add a convenient way for users to create Cell Components with different values. This is done by creating a lookup table (of the same dimensions as the environment) and supplying it to the generator
LookupGenerator(lookup_table).Note that coordinates are filled in a [x, y, z] fashion. This means you may need to transform your data (e.g. an image) so that x-coordinates can be referenced first.
Assuming a
3x3 DiscreteWorld:table = [[0, 1, 2], [3, 4, 5], [6, 7, 8]] env.add_cell_component('lookup', LookupGenerator(table))
will create a cell component called
'lookup'which will have values stored in a contiguous array:[0,1,2,3,4,5,6,7,8]which when viewed in 2D looks like:6
7
8
3
4
5
0
1
2
- table
The lookup table.
- Type:
Any
- class ECAgent.Environments.PositionComponent(agent, model, x: float = 0.0, y: float = 0.0, z: float = 0.0)[source]
Bases:
ComponentA position component. It contains three float properties: x, y, z. This component can be used to store the position of an Agent in a 1-3D world. It is used by
DiscreteWorldclasses to do exactly that.- getPosition() -> (<class 'float'>, <class 'float'>, <class 'float'>)[source]
Deprecated. Use
get_position()instead.
- get_position() -> (<class 'float'>, <class 'float'>, <class 'float'>)[source]
Returns the x,y and z values of the component as a tuple
- class ECAgent.Environments.SpaceWorld(model: Model, width: float, height: float | None = 0.0, depth: float | None = 0.0, id: str | None = 'ENVIRONMENT', wrap_env: bool | None = False)[source]
Bases:
EnvironmentBase Class for all Spacial Environments. It inherits from the Environment base class and contains properties related to the spacial extents of the environment. Currently, the environment can, at most, be three dimensional.
Note that the origin is (0,0,0) and is assumed to be located in the bottom left corner of the environment.
- width
The width of the environment. This attribute can be thought of as the spacial extent of the x-axis.
- Type:
float
- height
The height of the environment. This attribute can be thought of as the spacial extent of the y-axis.
- Type:
float
- depth
The depth of the environment. This attribute can be thought of as the spacial extent of the z-axis.
- Type:
float
- wrap_env
Determines if the environment is toroidal (i.e. agents wrap around the environment instead of moving out of bounds).
- Type:
bool
- add_agent(agent: <class 'ECAgent.Core.Agent'>, x_pos: int = 0, y_pos: int = 0, z_pos: int = 0)[source]
Adds an agent to the environment. Overrides the base
Environment.add_agentclass function. This function will also add aPositionComponentto the agent object. If the x, y or z positions are greater than or equal to the width, height and depth of the world (or less than zero), an error will be thrown.- Parameters:
agent (Agent) – The agent being added to the environment.
x_pos (int, Optional) – The starting x-position of the agent. Defaults to 0.
y_pos (int, Optional) – The starting y-position of the agent. Defaults to 0.
z_pos (int, Optional) – The starting z-position of the agent. Defaults to 0.
- Raises:
DuplicateAgentError – If the agent already exists in the environment.
Exception – If the agent’s initial position is outside the environment’s spacial extents.
- get_agents_at(x_pos: float = 0.0, y_pos: float = 0.0, z_pos: float = 0.0, leeway: float = 0.0, x_leeway: float = 0, y_leeway: float = 0, z_leeway: float = 0) Agent'>][source]
Returns a list of agents at position (x_pos, y_pos, z_pos) +/- any leeway.
The function will return
[]empty if no agents are within the specified region.The function also uses the maximum possible leeway for a given axis. So if the following is executed:
env.get_agents_at(10.0, 10.0, 10.0, leeway = 3.0, y_leeway = 5.0)
The function will return a list of agents within
3.0units of the coordinates(10.0, 10.0, 10.0)on the x and z axes and within5.0units of the coordinates(10.0, 10.0, 10.0)on the y-axis.Note: This function respects if the environment is toroidal (i.e.
wrap_mode == True).- Parameters:
x_pos (float, Optional) – The x-coordinate of the search origin point. Defaults to
0.0.y_pos (float, Optional) – The y-coordinate of the search origin point. Defaults to
0.0.z_pos (float, Optional) – The z-coordinate of the search origin point. Defaults to
0.0.leeway (float, Optional) – The general leeway value (i.e. leeway applied to all axes). Defaults to
0.0.x_leeway (float, Optional) – The x-axis leeway (i.e. leeway applied to x-axis). Defaults to
0.0.y_leeway (float, Optional) – The y-axis leeway (i.e. leeway applied to y-axis). Defaults to
0.0.z_leeway (float, Optional) – The z-axis leeway (i.e. leeway applied to z-axis). Defaults to
0.0.
- Returns:
A list of agents within the specified coordinates. An empty list
[]is returned if no agents are found.- Return type:
List[Agent]
- get_dimensions() -> (<class 'int'>, <class 'int'>, <class 'int'>)[source]
Returns a 3-tuple containing the extents of the environment:
(width, height, depth).
- move(agent: <class 'ECAgent.Core.Agent'>, x: float = 0, y: float = 0, z: float = 0)[source]
Moves an agent (x,y,z) units in the environment.
The function automatically clamps agent movement to the range:
(0, 0, 0) <= x < (self.width, self.height, self.depth)
If environment is non-toroidal (i.e.
wrap_env == False).- Parameters:
agent (Agent) – The agent object to be moved.
x (int, Optional) – The number of discrete units to move the agent. Defaults to 0.
y (int, Optional) – The number of discrete units to move the agent. Defaults to 0.
z (int, Optional) – The number of discrete units to move the agent. Defaults to 0.
- Raises:
ComponentNotFoundError – If
agentdoes not have aPositionComponent.
- move_to(agent: <class 'ECAgent.Core.Agent'>, x: float = 0, y: float = 0, z: float = 0)[source]
Moves an agent to position (x,y,z) in the environment.
- Parameters:
agent (Agent) – The agent object to be moved.
x (int, Optional) – The new x-coordinate of the agent. Defaults to 0.
y (int, Optional) – The new y-coordinate of the agent. Defaults to 0.
z (int, Optional) – The new z-coordinate of the agent. Defaults to 0.
- Raises:
ComponentNotFoundError – If
agentdoes not have aPositionComponent.IndexError – If coordinates are out of bounds.
- remove_agent(a_id: str)[source]
Removes the agent from the environment. Overrides the base
Environment.remove_agentmethod. This method will also remove thePositionComponentfrom the agent.- Parameters:
a_id (str) – The
idof the agent to remove.- Raises:
AgentNotFoundError – If no agent with an
agent.id == a_idcan be found.
- ECAgent.Environments.discreteGridPosToID(x: int, y: int = 0, width: int = 0, z: int = 0, height: int = 0)[source]
Deprecated. Use
discrete_grid_pos_to_idinstead.
- ECAgent.Environments.discrete_grid_pos_to_id(x: int, y: int = 0, width: int = 0, z: int = 0, height: int = 0)[source]
Returns a unique number of based on the x, y and z coordinates entered.
- Uniqueness is dimension dependent. The equation for calculating uniqueness is defined as:
(z * width * height) + (y * width) + x
- Parameters:
x (int) – The x-coordinate.
y (int, Optional) – The y-coordinate. Defaults to 0.
width (int, Optional) – The width of the environment. Defaults to 0.
z (int, Optional) – The z-coordinate. Defaults to 0.
height (int, Optional) – The height of the environment. Defaults to 0.
- Returns:
The unique ID.
- Return type:
int
- ECAgent.Environments.distance(a: PositionComponent, b: PositionComponent) float[source]
Calculates the distance from
PositionComponenta toPositionComponentb.- Parameters:
a (PositionComponent) – The first set of coordinates.
b (PositionComponent) – The second set of coordinates.
- Returns:
The distance from a to b.
- Return type:
float
- ECAgent.Environments.distance_sqr(a: PositionComponent, b: PositionComponent) float[source]
Calculates the squared distance from
PositionComponenta toPositionComponentb.This function does not call
math.sqrt()so it is more performant than callingdistance(a,b). This can be useful in situations where you don’t need the exact distance (e.g. when comparing distances).- Parameters:
a (PositionComponent) – The first set of coordinates.
b (PositionComponent) – The second set of coordinates.
- Returns:
The squared distance from a to b.
- Return type:
float
ECAgent.Collectors module
- class ECAgent.Collectors.AgentCollector(model: Model, agentFunc, compositeFunc=None, includeTimstep=False, id='AgentCollector', priority=-1, frequency=1, start=0, end=9223372036854775807)[source]
Bases:
CollectorThis is a collector system specifically designed to iterate through every iteration whenever the system is executed. The AgentCollector defaults its systemID to ‘AgentCollector’. If you are using multiple agent collectors, you must give them unique ids.
An AgentCollector can be supplied with custom lambdas for both agent specific operations as well as composite data collection. The agentFunc must be a function that accepts an agent as input like so:
- def myCustomCollectionFunc(agent):
return data
The result of that function is then stored in a dict that uses the agent’s id as a key. If None is returned, the collector simply skips that agent.
If you want to collect composite/aggregate data about the model (like a gini-index), supplying the compositeFunc property with a lambda that returns a dict of all of the composite data will do the trick. The function might look like so:
- def myCustomCompositeFunction(agents)
return {}
The dict returned is then used to update the dict of that record. Returning None will not update the dict. To see the agent collector in action, see the Environment and Data Collection tutorial.
- collect()[source]
The AgentCollector Collect() function iterates through every agent, a, in the model.environments.agents dict calling the agentFunc lambda like so agentFunc(a). The result of that call is then stored in a temporary dict that is later added to the records list. If includeTimestep is True, the collector will also the record the value of the current timestep in the tmpDict. After calling agentFunc(a) for all agents, the compositeFunc is called and supplied with a dict of all agents. The dict returned from the compositeFunc(agents) operation is then used to update the tmpDict. A record will not be appended to the records list if the tmpDict is empty.
- class ECAgent.Collectors.Collector(id: str, model: Model, priority=-1, frequency=1, start=0, end=9223372036854775807)[source]
Bases:
SystemThis is the Collector base class. Collectors are, by default, Systems and behave the same way. The collector base class adds a ‘records’ list property. This property holds all of the data collected by the collector. When writing your own collector, override the collect() method not the execute() method. The Collector base class automatically calls the collect() method whenever the execute() method is called. If you do need to override the execute method, make sure you also call the collect() method to follow the intended behaviour of a Collector object.
- class ECAgent.Collectors.FileCollector(id: str, model: Model, filename: str, priority=-1, frequency=1, start=0, end=9223372036854775807, filemode: str = 'a', write_count: int = 0, clear_records_on_write: bool = True)[source]
Bases:
CollectorThis is the base class for Collectors that want to write to files. The base implementation simply writes the records of the collector to the specified file name. When implementing your own FileCollector, you may need to override two methods: - The collect() method (As you would if you were writing your own non file-based collector). - The write_records() method which describes how your collector writes content to a file.
ECAgent.Decode module
- class ECAgent.Decode.Decoder[source]
Bases:
objectBase decoder class: ECAgent decoders follow a specific decoding process. The process, as executed by the decoder, is as follows:
pre_model_decode(func, params)
model_init(params)
for each system in ‘systems’:
pre_system_init(func, system, params)
system_init(system, params)
post_system_init(func, system, params)
for each agent in ‘agents’
pre_agent_init(func, agent, params)
agent_init(agent, params)
post_agent_init(func, agent, params)
post_model_decode(func, params)
All pre and post methods invoke the method specified to func and supply the method with ‘params’ as an argument.
If you want to create your own decoder, override the open_file function such that it creates a dictionary with the following content:
- {
- “model”{
“name” : Model_Name, “module(Optional)” : Module_Name, “params” : {
params_1 : value_1, …, params_n : value_n
}
}, “systems”: [
system_1, …, system_n,
], “agents” : [
agent_type_1, …, agent_type_n
], pre_model_decode(Optional): {
“func” : FunctionName, “module(Optional)” : ModuleName, “params” : {
params_1 : value_1, …, params_n : value_n
}
}, post_model_decode(Optional): {
“func” : FunctionName, “module(Optional)” : ModuleName, “params” : {
params_1 : value_1, …, params_n : value_n
}
}
}
where both ‘system’ and ‘agent_type’ follow the same structure as ‘model’
- class ECAgent.Decode.IDecodable[source]
Bases:
objectThis class serves as the base class for Decode submodel. By inheriting from this class your Components, Systems and Agents can be decoded from a text file and built into an executable model
In order to implement this class, the decode() static function must be overwritten.
ECAgent.Tags module
- exception ECAgent.Tags.DuplicateTagError(tag_name: str)[source]
Bases:
ExceptionException raised when a duplicate Tag is created.
Attributes:
- tag_namestr
The name of tag created.
- messagestr
Explanation of error.
- class ECAgent.Tags.TagLibrary[source]
Bases:
objectTag Library Class. It stores a list of agent tags and their unique identifiers.
- NONE
The default tag for all agents. It always has a value of 0.
- Type:
int
- add_tag(tag_name: str)[source]
Adds a tag to the
TagLibrarywith the nametag_name.- Parameters:
tag_name (str) – The name of the tag being created.
- Raises:
DuplicateTagError – If a tag_name that already exists is used.
- get_tag_name(tag_id: int) str[source]
Returns a
strrepresentation of a tag.- Parameters:
tag_id (int) – The id of the tag.
- Returns:
The
strrepresentation of the tag.- Return type:
str
- Raises:
TagNotFoundError – If the tag cannot be found.
- exception ECAgent.Tags.TagNotFoundError(tag_id)[source]
Bases:
ExceptionException raised when a Tag can’t be found.
Attributes:
- tag_idint | str
The name of searched tag.
- messagestr
Explanation of error.
- ECAgent.Tags.add_tag(tag_name: str)[source]
Adds a tag to the global
TagLibrarywith the nametag_name.- Parameters:
tag_name (str) – The name of the tag being created.
- Raises:
DuplicateTagError – If a tag_name that already exists is used.
- ECAgent.Tags.get_tag_name(tag_id: int) str[source]
Returns a
strrepresentation of a tag.The tag is searched for in the global
TagLibrary- Parameters:
tag_id (int) – The id of the tag.
- Returns:
The
strrepresentation of the tag.- Return type:
str
- Raises:
TagNotFoundError – If the tag cannot be found.
ECAgent.Batching module
- class ECAgent.Batching.ParameterList(parameters: Dict[str, Any | Iterable[Any]] | None = None)[source]
Bases:
objectThis class allows for the construction of parameter sets which can be used by
ECAgent.Batchingfunctionality.Each parameter added to the
ParameterListcan be a single value or a list of values. When the parameter list is built, the list will contain unique parameter sets for all values included in a parameter.For example:
import ECAgent.Batching as batching p_list = batching.ParameterList() # Create ParameterList # Add a parameter with one value p_list.add_parameter("size", 10) # Add a parameter with multiple values p_list.add_parameter("num_agents", [10, 20, 30, 40, 50]) # Build parameter list p_set = p_list.build() # p_set will contain the following parameter sets: [ {"size": 10, "num_agents: 10}, {"size": 10, "num_agents: 20}, {"size": 10, "num_agents: 30}, {"size": 10, "num_agents: 40}, {"size": 10, "num_agents: 50} ]
- add_parameter(name: str, values: Any | Iterable[Any])[source]
Adds a parameter to the
ParameterList. May either be a single value (i.e.10) or an iterable (e.g.[1, 2, 4, 4]).- Parameters:
name (str) – The name of the parameter.
values (Union[Any, Iterable[Any]]) – The values the parameter may take on.
- Raises:
AttributeError – If
nameis not of typestr.KeyError – If parameter with
namealready exists within theParameterList.
- class ECAgent.Batching.ScoreMode(value, names=None, *, module=None, qualname=None, type=None, start=1, boundary=None)[source]
Bases:
IntEnumEnum that determines which metric should be used for evaluation when performing a parameter tuning process (e.g. a ``grid_search.
Values are:
MIN = 0 # Return parameter set that returned the lowest score. MAX = 1 # Return parameter set that returned the highest score. MIN_MEAN = 2 # Return parameter set that returned the lowest score (averaged across all repetitions). MAX_MEAN = 3 # Return parameter set that returned the highest score (averaged across all repetitions). MIN_SUM = 4 # Return parameter set that returned the lowest score total across all repetitions. MAX_SUM = 5 # Return parameter set that returned the highest score total across all repetitions. MIN_VARIANCE = 6 # Return parameter set that returned the lowest variance in score across all repetitions. MAX_VARIANCE = 7 # Return parameter set that returned the highest variance in score across all repetitions.
- ECAgent.Batching.batch_run(model_cls: Type[Model], parameters: ParameterList | Dict[str, Any | Iterable[Any]], collectors: str | List[str] | None = None, processes: int | None = 1, max_timesteps: int = 9223372036854775807, repetitions: int = 1) None | Dict[str, List[Any]] | List[Any][source]
Method for executing models in batches over user-specified parameter sets. This function supports the use of a dictionary or
ParameterListas input.For example:
batch_run(ExampleModel, {'num_agents': [10, 20, 30], 'env_size': [10, 20]})
Is equivalent to:
p_list = ParameterList() p_list.add_parameter('num_agent', [10, 20, 30]) p_list.add_parameter('env_size', [10, 20]) batch_run(ExampleModel, p_list)
Both of which will create and run models of type
ExampleModelfor each of the following parameters:[ {'num_agents': 10, 'env_size': 10}, {'num_agents': 10, 'env_size': 20}, {'num_agents': 20, 'env_size': 10}, {'num_agents': 20, 'env_size': 20}, {'num_agents': 30, 'env_size': 10}, {'num_agents': 30, 'env_size': 20}, ]
The function also allows users to specify the name(s) of any data
Collectorsystems whoserecordsproperty will be returned. This value can either be astrorIterable:# For a single collector data = batch_run( ExampleModel, {'num_agents': [10, 20, 30], 'env_size': [10, 20]}, collectors='collector_name' ) # For multiple collectors data = batch_run( ExampleModel, {'num_agents': [10, 20, 30], 'env_size': [10, 20]}, collectors=['collector1', 'collector2'] )
- Parameters:
model_cls (Type[Model]) – The class of the model to build.
parameters (Union[ParameterList, Dict[str, Union[Any, Iterable[Any]]]]) – The set of parameters to build and run models with. May be a
ParameterListordict.collectors (Optional[Union[str, Iterable[str]]]) – The name of the collectors whose data will be returned. Defaults to
None.processes (Optional[int]) – The number of processes to spawn to run models on. Defaults to
1.max_timesteps (Optional[int]) – The maximum number of steps to run the model for. Defaults to
sys.maxsize.repetitions (Optional[int]) – The number of times to repeat each parameter set. Defaults to
1.
- Returns:
The data collected by the specified collectors (if any). If no collector is specified, function returns
None. If one collector is specified, a list containing the records of each model’s collector is returned. If multiple collectors are specified, a list containing dictionaries of each models collectors’ records are returned.- Return type:
Union[None, Dict[str, List[Any]], List[Any]]
- ECAgent.Batching.grid_search(model_cls: Type[Model], parameters: ParameterList | Dict[str, Any | Iterable[Any]], score_func: Callable[[Model], float], processes: int | None = 1, max_timesteps: int = 9223372036854775807, repetitions: int = 1, mode: ScoreMode = ScoreMode.MIN) Tuple[Dict[str, Any], List[Dict[str, Any]]][source]
Method for performing a Grid Search for a model over user-specified parameter sets. A callable object must also be specified. It should accept a
Modelas input and return afloatthat represents the score of the model:def score_example(model : Model) -> float score = # Add logic to calculate score here. return score.
The
score_funcdetermines the type of model behaviour that will be searched for. Consider a scenario where you want to find which parameters maximize the number of prey in aPredatorPreymodel, a reasonable scoring function might count the number of prey agents and return that as the score for a given parameter set.This function supports the use of a dictionary or
ParameterListas input. For example:grid_search(ExampleModel, {'num_agents': [10, 20, 30], 'env_size': [10, 20]}, score_example)
Is equivalent to:
p_list = ParameterList() p_list.add_parameter('num_agent', [10, 20, 30]) p_list.add_parameter('env_size', [10, 20]) grid_search(ExampleModel, p_list, score_example)
Both of which will create and run models of type
ExampleModelfor each of the following parameters:[ {'num_agents': 10, 'env_size': 10}, {'num_agents': 10, 'env_size': 20}, {'num_agents': 20, 'env_size': 10}, {'num_agents': 20, 'env_size': 20}, {'num_agents': 30, 'env_size': 10}, {'num_agents': 30, 'env_size': 20}, ]
Once the Grid Search is complete, the function will return the best parameter set as well a list of all parameter sets investigated:
p_best, p_list = grid_search(ExampleModel, {'num_agents': [10, 20, 30], 'env_size': [10, 20]}, score_example)
To get the scores obtained by each run of a parameter set as well as the overall score obtained, you may access their
'records'and'score'values:p_best['score'] # Returns the final score of the parameter set. p_best['records] # Returns the scores obtained for repetition of the parameter set.
- Parameters:
model_cls (Type[Model]) – The class of the model to build.
parameters (Union[ParameterList, Dict[str, Union[Any, Iterable[Any]]]]) – The set of parameters to build and run models with. May be a
ParameterListordict.score_func (Callable[[Model], float]) – Callable object that determines the score of the model.
processes (Optional[int]) – The number of processes to spawn to run models on. Defaults to
1.max_timesteps (Optional[int]) – The maximum number of steps to run the model for. Defaults to
sys.maxsize.repetitions (Optional[int]) – The number of times to repeat each parameter set. Defaults to
1.mode (ScoreMode) – The function to use when evaluating the score(s) of each model. Defaults to
ScoreMode.MIN.
- Returns:
Tuple containing the best parameter set found as the first element and a list of all parameter sets evaluated as the second element.
- Return type:
Tuple[Dict[str, Any], List[Dict[str, Any]]]