Documentation

ECAgent.Core module

class ECAgent.Core.Agent(id: str, model: Model, tag: int = None)[source]

Bases: object

This is the base class for Agent objects. Inherit from the class when creating custom agent types.

In ECAgent, An Agent is the Entity in 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

model

The Model the Agent belongs to.

Type:

Model

tag

The value of the Tag associated with the Agent. Defaults to 0 (which is the value NONE) or the default tag value of the Agent’s metaclass.

Type:

int

addComponent(component: Component)[source]

Deprecated. Use add_component instead.

add_component(component: Component)[source]

Adds a Component to the Agent.

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_component instead.

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 ComponentNotFoundError should be raised upon failing to find a component of type component_type. Defaults to False.

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_error is True and no component matching component_type is found.

hasComponent(*args) bool[source]

Deprecated. Use has_component instead.

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 *args so 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 Component classes that will checked.

Returns:

True if Agent has all of the components listed, else False

Return type:

bool

remove_component(component_type: type)[source]

Removes component of type `component_type from 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: Exception

Exception raised for errors when an agent object cannot be found.

Attributes:

a_idstr

id of Agent to search for.

environmentEnvironment

Environment that was searched.

messagestr

Explanation of error.

class ECAgent.Core.Component(agent, model: Model)[source]

Bases: object

This is the base class for Components. Inherit from this class to make your own components.

agent

The agent the component belongs to.

Type:

Agent

model

The model the component’s agent belongs to.

Type:

Model

exception ECAgent.Core.ComponentNotFoundError(agent: <class 'ECAgent.Core.Agent'>, component_type: type)[source]

Bases: Exception

Exception 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: Exception

Exception raised for errors when an agent object already exists in an environment.

Attributes:

a_idstr

id of the Agent.

environmentEnvironment

The Environment the agents exists in.

messagestr

Explanation of error.

class ECAgent.Core.Environment(model, id: str = 'ENVIRONMENT')[source]

Bases: Agent

Base 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 Environment is an Agent that contains other agents.

agents

A dict of 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

model

The Model the Environment belongs to.

Type:

Model

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_agent instead.

add_agent(agent: <class 'ECAgent.Core.Agent'>)[source]

Adds an agent to the environment. Agents cannot have duplicate id values.

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_agent instead.

getAgents(*args)[source]

Deprecated. Use Environment.get_agents instead.

getRandomAgent(*args)[source]

Deprecated. Use Environment.get_random_agent instead.

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:

Agent

Raises:

AgentNotFoundError – If throw_error == True and agent with agent.id == id could 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:

  1. The default case:

    all_agents = environment.get_agents()
    
  2. 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)
    
  3. 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_agents for a guide on how component template and tag searches work. This function uses Agent.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.

removeAgent(a_id: str)[source]

Deprecated. Use Environment.remove_agent instead.

remove_agent(a_id: str)[source]

Removes an agent with agent.id == a_id from the environment.

Parameters:

a_id (str) – The id of the agent to remove.

Raises:

AgentNotFoundError – If no agent with an agent.id == a_id can 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 *args and tag respectively. 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 Component classes 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: object

This 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:

Environment

systems

The SystemManager assigned to the model.

Type:

SystemManager

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 if self.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 n is not an int.

  • ValueError – If n < 1.

is_running() bool[source]

Returns True if model is still running and False if model is complete.

Returns:

That is True if model is still running and False if 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: Exception

Exception 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: IntEnum

Enum 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: object

This is the base class for the systems in ECAgent’s Entity-Component-System (ECS) architecture.

id

The id of the system.

Type:

str

model

The Model the System belongs to.

Type:

Model

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 1 which 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

execute()[source]

Abstract method which, when overridden by a child class, defines the the system’s logic.

Raises:

NotImplementedError

class ECAgent.Core.SystemManager(model: Model)[source]

Bases: object

This class is responsible for managing the adding, removing and executing of Systems.

Every Model will get a SystemManager which they can access using model.systems.

model

The model the SystemManager belongs to.

Type:

Model

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 the Component.

Type:

dict

addSystem(s: System)[source]

Deprecated. Use add_system instead.

add_system(s: System)[source]

Adds System s to the SystemManager and registers it with execution queue.

Parameters:

s (System) – The System being added to the SystemManager

Raises:

KeyError – If system already exists in the execution queue.

deregister_component(component: Component)[source]

Deregisters (removes) a component from the SystemManager component pool.

Parameters:

component (Component) – The Component to deregister.

Raises:

KeyError – When component is not registered with the SystemManager.

execute_systems(throw_error: bool = False)[source]

Function that loops through all systems in the execution_queue and calls the execute() method. The value of SystemManager.timestep is increased by 1 each time this method is called.

If a Model is marked as complete, execute_systems() will do nothing or throw a ModelCompleteError if throw_error == True.

The function uses the System’s start, end and frequency to determine if its execute() 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 ModelComplete error should be thrown if a user tries to call execute_systems on a model marked as complete.

Raises:

ModelCompleteError – If throw_error == True and this function is called on a model marked as complete.

getComponents(component_type: type)[source]

Deprecated. Use get_components instead.

get_components(component_type: type, throw_error: bool = False)[source]

Returns the list of components registered to the SystemManager with a type of component_type. Returns None if there are no components of type component_type registered with the SystemManager.

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 KeyError when it cannot find components of type == component_type. Defaults to False

Returns:

  • list – Of components with type component_type.

  • None – If no components of type component_type can be found.

Raises:

KeyError – If throw_error = True and no components of type == component_type are 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 Component to register.

Raises:

KeyError – When component has already been registered with the SystemManager.

removeSystem(s_id: str)[source]

Deprecated. Use remove_system instead.

remove_system(s_id: str)[source]

Removes System with System.id == s_id from the SystemManager.

Parameters:

s_id (str) – The id of the system to be removed.

Raises:

SystemNotFoundError – If the no System with System.id == s_id can be found.

exception ECAgent.Core.SystemNotFoundError(s_id: str)[source]

Bases: Exception

Exception raised for errors when systems that don’t exist are accessed.

Attributes:

s_idstr

id of system that was searched for.

messagestr

Explanation of error.

class ECAgent.Core._MetaAgent(name: str, bases: tuple, properties: dict)[source]

Bases: type

This is the base metaclass for Agent classes. 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 value NONE) or the default tag value of the Agent’s metaclass.

Type:

int

add_class_component(component: Component)[source]

Adds a Component to 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 ComponentNotFoundError should be raised upon failing to find a component of type component_type. Defaults to False.

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_error is True and no component matching component_type is 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 *args so 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 Component classes that will checked.

Returns:

True if Agent has all of the components listed, else False

Return type:

bool

remove_class_component(component_type: type)[source]

Removes component of type `component_type from 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: object

A 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 calling add_cell_component to a DiscreteWorld (e.g. GridWorld). The component will then be created with all cells having value == val

Assuming 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: SpaceWorld

Base 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 cells which is the pandas dataframe of of the environment and pos which 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 DiscreteWorld will 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 the discrete_grid_pos_to_id method. Additionally, all DiscreteWorld environments are initialized with a 'pos' cell component which contains the 3d coordinate representation of the cell.

model

The Model the environment belongs to.

Type:

Model

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

addCellComponent(name: str, generator)[source]

Deprecated. Use add_cell_component instead.

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: ConstantGenerator or LookupGenerator. 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 a 3x3 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 a numpy.ndarray or list is used, it must be 1-dimensional and of size width * height * depth.

getCell(x, y: int = 0, z: int = 0) Series[source]

Deprecated. Use get_cell instead.

get_cell(x, y: int = 0, z: int = 0) Series[source]

Returns a Pandas.Series containing the values of cell components at the specified grid cell.

Assuming a 3x1 DiscreteWorld with 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, tuple or PositionComponent. If int is supplied, it will be assumed to be the unique identifier of the cell (i.e. the value returned by discrete_grid_pos_to_id(). If a tuple is supplied, it is assumed that it will be the coordinates of the cell (e.g. (2,5,3)). If a PositionComponent is supplied, it’s values will be truncated and turned into a 3d integer coordinate (i.e. A PositionComponent with value x = 2.5, y = 5.9, z = 3.1) will be converted into coordinates (2,5,3).

The same functionality applied to the ret_type parameters. By default a list of integers containing the unique identifiers of the neighbouring cells are returned. If tuple is 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_pos to be included in the returned list

  • ret_type (type, Optional) – The representation of the neighbouring cells, Defaults to int but may also be tuple.

Returns:

A list of neighbouring cells in the representation specified by ret_type. Defaults to a list of int.

Return type:

list

Raises:

TypeError – If the type of cell_pos is not int, tuple or PositionComponent or if the type of ret_type is not int or tuple.

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, tuple or PositionComponent. If int is supplied, it will be assumed to be the unique identifier of the cell (i.e. the value returned by discrete_grid_pos_to_id(). If a tuple is supplied, it is assumed that it will be the coordinates of the cell (e.g. (2,5,3)). If a PositionComponent is supplied, it’s values will be truncated and turned into a 3d integer coordinate (i.e. A PositionComponent with value x = 2.5, y = 5.9, z = 3.1) will be converted into coordinates (2,5,3).

The same functionality applied to the ret_type parameters. By default a list of integers containing the unique identifiers of the neighbouring cells are returned. If tuple is 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_pos to be included in the returned list

  • ret_type (type, Optional) – The representation of the neighbouring cells, Defaults to int but may also be tuple.

  • 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 of int.

Return type:

list

Raises:
  • TypeError – If the type of cell_pos is not int, tuple or PositionComponent or if the type of ret_type is not int or tuple.

  • KeyError – If the mode supplied 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, tuple or PositionComponent. If int is supplied, it will be assumed to be the unique identifier of the cell (i.e. the value returned by discrete_grid_pos_to_id(). If a tuple is supplied, it is assumed that it will be the coordinates of the cell (e.g. (2,5,3)). If a PositionComponent is supplied, it’s values will be truncated and turned into a 3d integer coordinate (i.e. A PositionComponent with value x = 2.5, y = 5.9, z = 3.1) will be converted into coordinates (2,5,3).

The same functionality applied to the ret_type parameters. By default a list of integers containing the unique identifiers of the neighbouring cells are returned. If tuple is 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_pos to be included in the returned list

  • ret_type (type, Optional) – The representation of the neighbouring cells, Defaults to int but may also be tuple.

Returns:

A list of neighbouring cells in the representation specified by ret_type. Defaults to a list of int.

Return type:

list

Raises:

TypeError – If the type of cell_pos is not int, tuple or PositionComponent or if the type of ret_type is not int or tuple.

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: DiscreteWorld

GridWorld 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 width and height properties.

model

The Model the environment belongs to.

Type:

Model

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

get_dimensions() -> (<class 'int'>, <class 'int'>)[source]

Gets the dimension of the GridWorld.

Returns:

The width and height of the GridWorld.

Return type:

(int, int)

class ECAgent.Environments.LineWorld(model: Model, width: int, id: str = 'ENVIRONMENT', wrap_env: bool | None = False)[source]

Bases: DiscreteWorld

LineWorld 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 width property.

model

The Model the environment belongs to.

Type:

Model

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

get_dimensions() int[source]

Gets the dimension of the LineWorld.

Returns:

The width of the LineWorld.

Return type:

int

class ECAgent.Environments.LookupGenerator(table)[source]

Bases: object

A 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: Component

A 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 DiscreteWorld classes 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

xy()[source]

Returns the x and y values of the component as a 2-tuple.

xyz()[source]

Returns the x, y and z values of the component as a 3-tuple.

Equivalent to PositionComponent.get_position()

xz()[source]

Returns the x and z values of the component as a 2-tuple.

yz()[source]

Returns the y and z values of the component as a 2-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: Environment

Base 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_agent class function. This function will also add a PositionComponent to 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.0 units of the coordinates (10.0, 10.0, 10.0) on the x and z axes and within 5.0 units 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 agent does not have a PositionComponent.

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 agent does not have a PositionComponent.

  • IndexError – If coordinates are out of bounds.

remove_agent(a_id: str)[source]

Removes the agent from the environment. Overrides the base Environment.remove_agent method. This method will also remove the PositionComponent from the agent.

Parameters:

a_id (str) – The id of the agent to remove.

Raises:

AgentNotFoundError – If no agent with an agent.id == a_id can 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_id instead.

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 PositionComponent a to PositionComponent b.

Parameters:
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 PositionComponent a to PositionComponent b.

This function does not call math.sqrt() so it is more performant than calling distance(a,b). This can be useful in situations where you don’t need the exact distance (e.g. when comparing distances).

Parameters:
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: Collector

This 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: System

This 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.

collect()[source]

The collect method. This method is overridden to define the data collection behaviour of your Custom Collector.

execute()[source]

This overrides the Systems base execution method. It simply calls the collect method

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: Collector

This 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.

execute()[source]

This overrides the Systems base execution method. It simply calls the collect method

write_records()[source]

Loops through all of the self.records and writes their contents to a file specified by the self.filename property.

ECAgent.Decode module

class ECAgent.Decode.Decoder[source]

Bases: object

Base 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’

open_file(file_name: str) dict[source]

You must overwrite this function if you make your own decoder

class ECAgent.Decode.IDecodable[source]

Bases: object

This 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.

class ECAgent.Decode.JsonDecoder[source]

Bases: Decoder

open_file(file_name: str) dict[source]

You must overwrite this function if you make your own decoder

ECAgent.Tags module

Tags Module for ECAgent

This library contains all “Tag” functionality. In ECAgent, a tag is a unique (int,str) pair that is used to associate similar agents together. Tags are not always useful, but they shine when a model has multiple agent types that share a lot of similar components (i.e. Where component template matching doesn’t work). Tags may also be useful when building containers of similar agent types.

Tags are stored in a TagLibrary. By default a global TagLibrary exists that can be accessed directly through the Tags module.

To add a tag to the global TagLibrary, do the following:

import ECAgent.Tags as Tags

Tags.add_tag("Tag1")

This will register Tag1 with the TagLibrary. Tag1 will then be assigned a unique integer value which can be accessed as follows:

Tags.Tag1

If you want to get the str representation of a tag:

Tags.get_tag_name(Tags.Tag1)

Which will return "Tag1".

Creating a local TagLibrary

If you do not want to use the global TagLibrary to store tags, you can create a local TagLibrary by doing the following:

import ECAgent.Tags as Tags

local_tags = Tags.TagLibrary()

You will then be able to use all of the functionality discussed previously. To add a tag:

local_tags.add_tag("Tag1")

To get the unique integer value of a Tag:

local_tags.Tag1

To get the str representation of a Tag:

local_tags.get_tag_name(local_tags.Tag1)

Tags and Agents

Tags are not particularly useful by themselves. To assign a tag to an Agent, you can either do it at initialization:

import ECAgent.Core as Core
import ECAgent.Tags as Tags

Tags.add_tag('PREY')

model = Core.model()
p1 = Core.Agent('p1', model, tag = Tags.PREY)
model.environment.add_agent(p1)

Or you can assign it post-initialization:

p1.tag = Tags.PREY

To get a list of all agents with a particular tag, use the Environment.get_agents method. Specifying the tag as follows:

prey = environment.getAgents(tag = Tags.PREY)
exception ECAgent.Tags.DuplicateTagError(tag_name: str)[source]

Bases: Exception

Exception 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: object

Tag 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 TagLibrary with the name tag_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 str representation of a tag.

Parameters:

tag_id (int) – The id of the tag.

Returns:

The str representation of the tag.

Return type:

str

Raises:

TagNotFoundError – If the tag cannot be found.

itemize() list[source]

Returns a list of tags in the TagLibrary and their values.

Returns:

In the following form: [('NONE', 0), ('TAG1', 1),..., ('TAGN', N)]

Return type:

list

exception ECAgent.Tags.TagNotFoundError(tag_id)[source]

Bases: Exception

Exception 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 TagLibrary with the name tag_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 str representation of a tag.

The tag is searched for in the global TagLibrary

Parameters:

tag_id (int) – The id of the tag.

Returns:

The str representation of the tag.

Return type:

str

Raises:

TagNotFoundError – If the tag cannot be found.

ECAgent.Tags.itemize() list[source]

Returns a list of tags in the global TagLibrary and their values.

Returns:

In the following form: [('NONE', 0), ('TAG1', 1),..., ('TAGN', N)]

Return type:

list

ECAgent.Batching module

class ECAgent.Batching.ParameterList(parameters: Dict[str, Any | Iterable[Any]] | None = None)[source]

Bases: object

This class allows for the construction of parameter sets which can be used by ECAgent.Batching functionality.

Each parameter added to the ParameterList can 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 name is not of type str.

  • KeyError – If parameter with name already exists within the ParameterList.

build() List[Dict[str, Any]][source]

Builds and returns a parameter set from the ParameterList object.

Returns:

containing dictionaries describing all experiments to investigate.

Return type:

List[Dict[str, Any]]

remove_parameter(name: str)[source]

Removes a parameter from the ParameterList.

Parameters:

name (str) – The name of the parameter to remove.

Raises:

KeyError – If name is not a valid parameter name.

class ECAgent.Batching.ScoreMode(value, names=None, *, module=None, qualname=None, type=None, start=1, boundary=None)[source]

Bases: IntEnum

Enum 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 ParameterList as 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 ExampleModel for 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 Collector systems whose records property will be returned. This value can either be a str or Iterable:

# 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 ParameterList or dict.

  • 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]]

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 Model as input and return a float that represents the score of the model:

def score_example(model : Model) -> float
    score = # Add logic to calculate score here.
    return score.

The score_func determines 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 a PredatorPrey model, 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 ParameterList as 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 ExampleModel for 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 ParameterList or dict.

  • 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]]]