API
The API of Agents.jl is defined on top of the fundamental structures AgentBasedModel
, Space, AbstractAgent
which are described in the Tutorial page. In this page we list the remaining API functions, which constitute the bulk of Agents.jl functionality.
Concrete ABM implementations
Agents.StandardABM
— TypeStandardABM(AgentType [, space]; properties, kwargs...) → model
The most standard concrete implementation of an AgentBasedModel
, as well as the default version of the generic AgentBasedModel
constructor. StandardABM
stores agents in a dictionary mapping unique Int
IDs to agents. See also UnremovableABM
.
Agents.UnremovableABM
— TypeUnremovableABM(AgentType [, space]; properties, kwargs...) → model
Similar to StandardABM
, but agents cannot be removed, only added. This allows storing agents more efficiently in a standard Julia Vector
(as opposed to the Dict
used by StandardABM
, yielding faster retrieval and iteration over agents.
It is mandatory that the agent ID is exactly the same as the agent insertion order (i.e., the 5th agent added to the model must have ID 5). If not, an error will be thrown by add_agent!
.
Agent/model retrieval and access
Base.getindex
— Methodmodel[id]
-getindex(model::ABM, id::Integer)
Return an agent given its ID.
Base.getproperty
— Methodmodel.prop
-getproperty(model::ABM, :prop)
Return a property with name :prop
from the current model
, assuming the model properties
are either a dictionary with key type Symbol
or a Julia struct. For example, if a model has the set of properties Dict(:weight => 5, :current => false)
, retrieving these values can be obtained via model.weight
.
The property names :agents, :space, :scheduler, :properties, :maxid
are internals and should not be accessed by the user. In the next release, getting those will error.
Agents.random_agent
— Functionrandom_agent(model) → agent
Return a random agent from the model.
random_agent(model, condition; optimistic=true, alloc = false) → agent
Return a random agent from the model that satisfies condition(agent) == true
. The function generates a random permutation of agent IDs and iterates through them. If no agent satisfies the condition, nothing
is returned instead.
Keywords
optimistic = true
changes the algorithm used to be non-allocating but potentially more variable in performance. This should be faster if the condition is true
for a large proportion of the population (for example if the agents are split into groups).
alloc
can be used to employ a different fallback strategy in case the optimistic version doesn't find any agent satisfying the condition: if the filtering condition is expensive an allocating fallback can be more performant.
Agents.nagents
— Functionnagents(model::ABM)
Return the number of agents in the model
.
Agents.allagents
— Functionallagents(model)
Return an iterator over all agents of the model.
Agents.allids
— Functionallids(model)
Return an iterator over all agent IDs of the model.
Agents.abmproperties
— Functionabmproperties(model::ABM)
Return the properties container stored in the model
.
Agents.abmrng
— Functionabmrng(model::ABM)
Return the random number generator stored in the model
.
Agents.abmscheduler
— Functionabmscheduler(model)
Return the default scheduler stored in model
.
Agents.abmspace
— Functionabmspace(model::ABM)
Return the space instance stored in the model
.
Available spaces
Here we list the spaces that are available "out of the box" from Agents.jl. To create your own, see Creating a new space type.
Discrete spaces
Agents.GraphSpace
— TypeGraphSpace(graph::AbstractGraph)
Create a GraphSpace
instance that is underlined by an arbitrary graph from Graphs.jl. GraphSpace
represents a space where each node (i.e. position) of a graph can hold an arbitrary amount of agents, and each agent can move between the nodes of the graph. The position type for this space is Int
, use GraphAgent
for convenience.
Graphs.nv
and Graphs.ne
can be used in a model with a GraphSpace
to obtain the number of nodes or edges in the graph. The underlying graph can be altered using add_vertex!
and rem_vertex!
.
An example using GraphSpace
is SIR model for the spread of COVID-19.
If you want to model social networks, where each agent is equivalent with a node of a graph, you're better of using nothing
as the model space, and using a graph from Graphs.jl directly in the model parameters, as shown in the Social networks with Graphs.jl integration example.
Distance specification
In functions like nearby_ids
, distance for GraphSpace
means the degree of neighbors in the graph (thus distance is always an integer). For example, for r=2
includes first and second degree neighbors. For 0 distance, the search occurs only on the origin node.
In functions like nearby_ids
the keyword neighbor_type=:default
can be used to select differing neighbors depending on the underlying graph directionality type.
:default
returns neighbors of a vertex (position). If graph is directed, this is equivalent to:out
. For undirected graphs, all options are equivalent to:out
.:all
returns both:in
and:out
neighbors.:in
returns incoming vertex neighbors.:out
returns outgoing vertex neighbors.
Agents.GridSpace
— TypeGridSpace(d::NTuple{D, Int}; periodic = true, metric = :chebyshev)
Create a GridSpace
that has size given by the tuple d
, having D ≥ 1
dimensions. Optionally decide whether the space will be periodic and what will be the distance metric. The position type for this space is NTuple{D, Int}
, use GridAgent
for convenience. Valid positions have indices in the range 1:d[i]
for the i
-th dimension.
An example using GridSpace
is Schelling's segregation model.
Distance specification
The typical terminology when searching neighbors in agent based modelling is "Von Neumann" neighborhood or "Moore" neighborhoods. However, because Agents.jl provides a much more powerful infrastructure for finding neighbors, both in arbitrary dimensions but also of arbitrary neighborhood size, this established terminology is no longer appropriate. Instead, distances that define neighborhoods are specified according to a proper metric space, that is both well defined for any distance, and applicable to any dimensionality.
The allowed metrics are (and see docs online for a plotted example):
:chebyshev
metric means that ther
-neighborhood of a position are all positions within the hypercube having side length of2*floor(r)
and being centered in the origin position. This is similar to "Moore" forr = 1
and two dimensions.:manhattan
metric means that ther
-neighborhood of a position are all positions whose cartesian indices have Manhattan distance≤ r
from the cartesian index of the origin position. This similar to "Von Neumann" forr = 1
and two dimensions.:euclidean
metric means that ther
-neighborhood of a position are all positions whose cartesian indices have Euclidean distance≤ r
from the cartesian index of the origin position.
Advanced dimension-dependent distances in Chebyshev metric
If metric = :chebyshev
, some advanced specification of distances is allowed when providing r
to functions like nearby_ids
.
r::NTuple{D,Int}
such asr = (5, 2)
. This would mean a distance of 5 in the first dimension and 2 in the second. This can be useful when different coordinates in the space need to be searched with different ranges, e.g., if the space corresponds to a full building, with the third dimension the floor number.r::Vector{Tuple{Int,UnitRange{Int}}}
such asr = [(1, -1:1), (3, 1:2)]
. This allows explicitly specifying the difference between position indices in each specified dimension. The exampler = [(1, -1:1), (3, 1:2)]
when given to e.g.,nearby_ids
, would search dimension 1 one step of either side of the current position (as well as the current position since0 ∈ -1:1
) and would search the third dimension one and two positions above current. Unspecified dimensions (like the second in this example) are searched throughout all their possible ranges.
See the Battle Royale example for usage of this advanced specification of dimension-dependent distances where one dimension is used as a categorical one.
Agents.GridSpaceSingle
— TypeGridSpaceSingle(d::NTuple{D, Int}; periodic = true, metric = :chebyshev)
This is a specialized version of GridSpace
that allows only one agent per position, and utilizes this knowledge to offer significant performance gains versus GridSpace
.
This space reserves agent ID = 0 for internal usage. Agents should be initialized with non-zero IDs, either positive or negative. This is not checked internally.
All arguments and keywords behave exactly as in GridSpace
.
Here is a specification of how the metrics look like:
Continuous spaces
Agents.ContinuousSpace
— TypeContinuousSpace(extent::NTuple{D, <:Real}; kwargs...)
Create a D
-dimensional ContinuousSpace
in range 0 to (but not including) extent
. Your agent positions (field pos
) must be of type SVector{D, <:Real}
, and it is strongly recommend that agents also have a field vel::SVector{D, <:Real}
to use in conjunction with move_agent!
. Use ContinuousAgent
for convenience.
ContinuousSpace
is a representation of agent dynamics on a continuous medium where agent position, orientation, and speed, are true floats. In addition, support is provided for representing spatial properties in a model that contains a ContinuousSpace
. Spatial properties (which typically are contained in the model properties) can either be functions of the position vector, f(pos) = value
, or AbstractArrays
, representing discretizations of spatial data that may not be available in analytic form. In the latter case, the position is automatically mapped into the discretization represented by the array. Use get_spatial_property
to access spatial properties in conjunction with ContinuousSpace
.
See also Continuous space exclusives on the online docs for more functionality. An example using continuous space is the Flocking model.
Distance specification
Distances specified by r
in functions like nearby_ids
are always based on the Euclidean distance between two points in ContinuousSpace
.
In ContinuousSpace
nearby_*
searches are accelerated using a grid system, see discussion around the keyword spacing
below. nearby_ids
is not an exact search, but can be a possible over-estimation, including agent IDs whose distance slightly exceeds r
with "slightly" being as much as spacing
. If you want exact searches use the slower nearby_ids_exact
.
Keywords
periodic = true
: Whether the space is periodic or not. If set tofalse
an error will occur if an agent's position exceeds the boundary.spacing::Real = minimum(extent)/20
: Configures an internal compartment spacing that is used to accelerate nearest neighbor searches likenearby_ids
. The compartments are actually a full instance ofGridSpace
in which agents move. All dimensions inextent
must be completely divisible byspacing
. There is no best choice for the value ofspacing
and if you need optimal performance it's advised to set up a benchmark over a range of choices. The finer the spacing, the faster and more accurate the inexact version ofnearby_ids
becomes. However, a finer spacing also means slowermove_agent!
, as agents change compartments more often.update_vel!
: A function,update_vel!(agent, model)
that updates the agent's velocity before the agent has been moved, seemove_agent!
. You can of course change the agents' velocities during the agent interaction, theupdate_vel!
functionality targets spatial force fields acting on the agents individually (e.g. some magnetic field). If you useupdate_vel!
, the agent type must have a fieldvel::SVector{D, <:Real}
.
Agents.OpenStreetMapSpace
— TypeOpenStreetMapSpace(path::AbstractString; kwargs...)
Create a space residing on the Open Street Map (OSM) file provided via path
. This space represents the underlying map as a continuous entity choosing accuracy over performance. The map is represented as a graph, consisting of nodes connected by edges. Nodes are not necessarily intersections, and there may be multiple nodes on a road joining two intersections. Agents move along the available roads of the map using routing, see below.
The functionality related to Open Street Map spaces is in the submodule OSM
. An example of its usage can be found in Zombie Outbreak in a City.
The OSMAgent
The base properties for an agent residing on an OSMSpace
are as follows:
mutable struct Agent <: AbstractAgent
+API · Agents.jl API
The API of Agents.jl is defined on top of the fundamental structures AgentBasedModel
, Space, AbstractAgent
which are described in the Tutorial page. In this page we list the remaining API functions, which constitute the bulk of Agents.jl functionality.
Concrete ABM implementations
Agents.StandardABM
— TypeStandardABM(AgentType [, space]; properties, kwargs...) → model
The most standard concrete implementation of an AgentBasedModel
, as well as the default version of the generic AgentBasedModel
constructor. StandardABM
stores agents in a dictionary mapping unique Int
IDs to agents. See also UnremovableABM
.
sourceAgents.UnremovableABM
— TypeUnremovableABM(AgentType [, space]; properties, kwargs...) → model
Similar to StandardABM
, but agents cannot be removed, only added. This allows storing agents more efficiently in a standard Julia Vector
(as opposed to the Dict
used by StandardABM
, yielding faster retrieval and iteration over agents.
It is mandatory that the agent ID is exactly the same as the agent insertion order (i.e., the 5th agent added to the model must have ID 5). If not, an error will be thrown by add_agent!
.
sourceAgent/model retrieval and access
Base.getindex
— Methodmodel[id]
+getindex(model::ABM, id::Integer)
Return an agent given its ID.
sourceBase.getproperty
— Methodmodel.prop
+getproperty(model::ABM, :prop)
Return a property with name :prop
from the current model
, assuming the model properties
are either a dictionary with key type Symbol
or a Julia struct. For example, if a model has the set of properties Dict(:weight => 5, :current => false)
, retrieving these values can be obtained via model.weight
.
The property names :agents, :space, :scheduler, :properties, :maxid
are internals and should not be accessed by the user. In the next release, getting those will error.
sourceAgents.random_agent
— Functionrandom_agent(model) → agent
Return a random agent from the model.
sourcerandom_agent(model, condition; optimistic=true, alloc = false) → agent
Return a random agent from the model that satisfies condition(agent) == true
. The function generates a random permutation of agent IDs and iterates through them. If no agent satisfies the condition, nothing
is returned instead.
Keywords
optimistic = true
changes the algorithm used to be non-allocating but potentially more variable in performance. This should be faster if the condition is true
for a large proportion of the population (for example if the agents are split into groups).
alloc
can be used to employ a different fallback strategy in case the optimistic version doesn't find any agent satisfying the condition: if the filtering condition is expensive an allocating fallback can be more performant.
sourceAgents.nagents
— Functionnagents(model::ABM)
Return the number of agents in the model
.
sourceAgents.allagents
— Functionallagents(model)
Return an iterator over all agents of the model.
sourceAgents.allids
— Functionallids(model)
Return an iterator over all agent IDs of the model.
sourceAgents.abmproperties
— Functionabmproperties(model::ABM)
Return the properties container stored in the model
.
sourceAgents.abmrng
— Functionabmrng(model::ABM)
Return the random number generator stored in the model
.
sourceAgents.abmscheduler
— Functionabmscheduler(model)
Return the default scheduler stored in model
.
sourceAgents.abmspace
— Functionabmspace(model::ABM)
Return the space instance stored in the model
.
sourceAvailable spaces
Here we list the spaces that are available "out of the box" from Agents.jl. To create your own, see Creating a new space type.
Discrete spaces
Agents.GraphSpace
— TypeGraphSpace(graph::AbstractGraph)
Create a GraphSpace
instance that is underlined by an arbitrary graph from Graphs.jl. GraphSpace
represents a space where each node (i.e. position) of a graph can hold an arbitrary amount of agents, and each agent can move between the nodes of the graph. The position type for this space is Int
, use GraphAgent
for convenience.
Graphs.nv
and Graphs.ne
can be used in a model with a GraphSpace
to obtain the number of nodes or edges in the graph. The underlying graph can be altered using add_vertex!
and rem_vertex!
.
An example using GraphSpace
is SIR model for the spread of COVID-19.
If you want to model social networks, where each agent is equivalent with a node of a graph, you're better of using nothing
as the model space, and using a graph from Graphs.jl directly in the model parameters, as shown in the Social networks with Graphs.jl integration example.
Distance specification
In functions like nearby_ids
, distance for GraphSpace
means the degree of neighbors in the graph (thus distance is always an integer). For example, for r=2
includes first and second degree neighbors. For 0 distance, the search occurs only on the origin node.
In functions like nearby_ids
the keyword neighbor_type=:default
can be used to select differing neighbors depending on the underlying graph directionality type.
:default
returns neighbors of a vertex (position). If graph is directed, this is equivalent to :out
. For undirected graphs, all options are equivalent to :out
.:all
returns both :in
and :out
neighbors.:in
returns incoming vertex neighbors.:out
returns outgoing vertex neighbors.
sourceAgents.GridSpace
— TypeGridSpace(d::NTuple{D, Int}; periodic = true, metric = :chebyshev)
Create a GridSpace
that has size given by the tuple d
, having D ≥ 1
dimensions. Optionally decide whether the space will be periodic and what will be the distance metric. The position type for this space is NTuple{D, Int}
, use GridAgent
for convenience. Valid positions have indices in the range 1:d[i]
for the i
-th dimension.
An example using GridSpace
is Schelling's segregation model.
Distance specification
The typical terminology when searching neighbors in agent based modelling is "Von Neumann" neighborhood or "Moore" neighborhoods. However, because Agents.jl provides a much more powerful infrastructure for finding neighbors, both in arbitrary dimensions but also of arbitrary neighborhood size, this established terminology is no longer appropriate. Instead, distances that define neighborhoods are specified according to a proper metric space, that is both well defined for any distance, and applicable to any dimensionality.
The allowed metrics are (and see docs online for a plotted example):
:chebyshev
metric means that the r
-neighborhood of a position are all positions within the hypercube having side length of 2*floor(r)
and being centered in the origin position. This is similar to "Moore" for r = 1
and two dimensions.
:manhattan
metric means that the r
-neighborhood of a position are all positions whose cartesian indices have Manhattan distance ≤ r
from the cartesian index of the origin position. This similar to "Von Neumann" for r = 1
and two dimensions.
:euclidean
metric means that the r
-neighborhood of a position are all positions whose cartesian indices have Euclidean distance ≤ r
from the cartesian index of the origin position.
Advanced dimension-dependent distances in Chebyshev metric
If metric = :chebyshev
, some advanced specification of distances is allowed when providing r
to functions like nearby_ids
.
r::NTuple{D,Int}
such as r = (5, 2)
. This would mean a distance of 5 in the first dimension and 2 in the second. This can be useful when different coordinates in the space need to be searched with different ranges, e.g., if the space corresponds to a full building, with the third dimension the floor number.r::Vector{Tuple{Int,UnitRange{Int}}}
such as r = [(1, -1:1), (3, 1:2)]
. This allows explicitly specifying the difference between position indices in each specified dimension. The example r = [(1, -1:1), (3, 1:2)]
when given to e.g., nearby_ids
, would search dimension 1 one step of either side of the current position (as well as the current position since 0 ∈ -1:1
) and would search the third dimension one and two positions above current. Unspecified dimensions (like the second in this example) are searched throughout all their possible ranges.
See the Battle Royale example for usage of this advanced specification of dimension-dependent distances where one dimension is used as a categorical one.
sourceAgents.GridSpaceSingle
— TypeGridSpaceSingle(d::NTuple{D, Int}; periodic = true, metric = :chebyshev)
This is a specialized version of GridSpace
that allows only one agent per position, and utilizes this knowledge to offer significant performance gains versus GridSpace
.
This space reserves agent ID = 0 for internal usage. Agents should be initialized with non-zero IDs, either positive or negative. This is not checked internally.
All arguments and keywords behave exactly as in GridSpace
.
sourceHere is a specification of how the metrics look like:
Continuous spaces
Agents.ContinuousSpace
— TypeContinuousSpace(extent::NTuple{D, <:Real}; kwargs...)
Create a D
-dimensional ContinuousSpace
in range 0 to (but not including) extent
. Your agent positions (field pos
) must be of type SVector{D, <:Real}
, and it is strongly recommend that agents also have a field vel::SVector{D, <:Real}
to use in conjunction with move_agent!
. Use ContinuousAgent
for convenience.
ContinuousSpace
is a representation of agent dynamics on a continuous medium where agent position, orientation, and speed, are true floats. In addition, support is provided for representing spatial properties in a model that contains a ContinuousSpace
. Spatial properties (which typically are contained in the model properties) can either be functions of the position vector, f(pos) = value
, or AbstractArrays
, representing discretizations of spatial data that may not be available in analytic form. In the latter case, the position is automatically mapped into the discretization represented by the array. Use get_spatial_property
to access spatial properties in conjunction with ContinuousSpace
.
See also Continuous space exclusives on the online docs for more functionality. An example using continuous space is the Flocking model.
Distance specification
Distances specified by r
in functions like nearby_ids
are always based on the Euclidean distance between two points in ContinuousSpace
.
In ContinuousSpace
nearby_*
searches are accelerated using a grid system, see discussion around the keyword spacing
below. nearby_ids
is not an exact search, but can be a possible over-estimation, including agent IDs whose distance slightly exceeds r
with "slightly" being as much as spacing
. If you want exact searches use the slower nearby_ids_exact
.
Keywords
periodic = true
: Whether the space is periodic or not. If set to false
an error will occur if an agent's position exceeds the boundary.spacing::Real = minimum(extent)/20
: Configures an internal compartment spacing that is used to accelerate nearest neighbor searches like nearby_ids
. The compartments are actually a full instance of GridSpace
in which agents move. All dimensions in extent
must be completely divisible by spacing
. There is no best choice for the value of spacing
and if you need optimal performance it's advised to set up a benchmark over a range of choices. The finer the spacing, the faster and more accurate the inexact version of nearby_ids
becomes. However, a finer spacing also means slower move_agent!
, as agents change compartments more often.update_vel!
: A function, update_vel!(agent, model)
that updates the agent's velocity before the agent has been moved, see move_agent!
. You can of course change the agents' velocities during the agent interaction, the update_vel!
functionality targets spatial force fields acting on the agents individually (e.g. some magnetic field). If you use update_vel!
, the agent type must have a field vel::SVector{D, <:Real}
.
sourceAgents.OpenStreetMapSpace
— TypeOpenStreetMapSpace(path::AbstractString; kwargs...)
Create a space residing on the Open Street Map (OSM) file provided via path
. This space represents the underlying map as a continuous entity choosing accuracy over performance. The map is represented as a graph, consisting of nodes connected by edges. Nodes are not necessarily intersections, and there may be multiple nodes on a road joining two intersections. Agents move along the available roads of the map using routing, see below.
The functionality related to Open Street Map spaces is in the submodule OSM
. An example of its usage can be found in Zombie Outbreak in a City.
The OSMAgent
The base properties for an agent residing on an OSMSpace
are as follows:
mutable struct Agent <: AbstractAgent
id::Int
pos::Tuple{Int,Int,Float64}
end
Current pos
ition tuple is represented as (first intersection index, second intersection index, distance travelled). The indices are the indices of the nodes of the graph that internally represents the map. Functions like OSM.nearest_node
or OSM.nearest_road
can help find those node indices from a (lon, lat) real world coordinate. The distance travelled is in the units of weight_type
. This ensures that the map is a continuous kind of space, as an agent can truly be at any possible point on an existing road.
Use OSMAgent
for convenience.
Obtaining map files
Maps files can be downloaded using the functions provided by LightOSM.jl. Agents.jl also re-exports OSM.download_osm_network
, the main function used to download maps and provides a test map in OSM.test_map
. An example usage to download the map of London to "london.json"
:
OSM.download_osm_network(
:place_name;
place_name = "London",
save_to_file_location = "london.json"
-)
The length of an edge between two nodes is specified in the units of the map's weight_type
as listed in the documentation for LightOSM.OSMGraph
. The possible weight_type
s are:
:distance
: The distance in kilometers of an edge:time
: The time in hours to travel along an edge at the maximum speed allowed on that road:lane_efficiency
: Time scaled by number of lanes
The default weight_type
used is :distance
.
All kwargs
are propagated to LightOSM.graph_from_file
.
Routing with OSM
You can use plan_route!
or plan_random_route!
. To actually move along a planned route use move_along_route!
.
sourceAdding agents
Agents.add_agent!
— Functionadd_agent!([pos,] A::Type, model::ABM, args...) → newagent
+)
The length of an edge between two nodes is specified in the units of the map's weight_type
as listed in the documentation for LightOSM.OSMGraph
. The possible weight_type
s are:
:distance
: The distance in kilometers of an edge:time
: The time in hours to travel along an edge at the maximum speed allowed on that road:lane_efficiency
: Time scaled by number of lanes
The default weight_type
used is :distance
.
All kwargs
are propagated to LightOSM.graph_from_file
.
Routing with OSM
You can use plan_route!
or plan_random_route!
. To actually move along a planned route use move_along_route!
.
sourceAdding agents
Agents.add_agent!
— Functionadd_agent!([pos,] A::Type, model::ABM, args...) → newagent
add_agent!([pos,] A::Type, model::ABM; kwargs...) → newagent
Use one of these two versions to create and add a new agent to the model using the constructor of the agent type of the model. Optionally provide a position to add the agent to as first argument, which must match the space position type.
This function takes care of setting the agent's id and position. The extra provided args...
or kwargs...
are propagated to other fields of the agent constructor (see example below). Mixing args...
and kwargs...
is not possible, only one of the two can be used to set the fields.
add_agent!([pos,] A::Type, model::ABM, args...) → newagent
add_agent!([pos,] A::Type, model::ABM; kwargs...) → newagent
Use one of these two versions for mixed agent models, with A
the agent type you wish to create, because it is otherwise not possible to deduce a constructor for A
.
Example
using Agents
@agent struct Agent(GraphAgent)
@@ -21,7 +21,7 @@
add_agent!(model, 0.5, true) # correct: w becomes 0.5
add_agent!(5, model, 0.5, true) # add at position 5, w becomes 0.5
add_agent!(model; w = 0.5) # use keywords: w becomes 0.5, k becomes false
-add_agent!(model; w = 0.5, k = true) # use keywords: w becomes 0.5, k becomes true
sourceadd_agent!(agent::AbstractAgent [, pos], model::ABM) → agent
Add the agent
to the model in the given position. If pos
is not given, the agent
is added to a random position. The agent
's position is always updated to match position
, and therefore for add_agent!
the position of the agent
is meaningless. Use add_agent_pos!
to use the agent
's position.
The type of pos
must match the underlying space position type.
sourceAgents.replicate!
— Functionreplicate!(agent, model; kwargs...)
Add a new agent to the model
copying the values of the fields of the given agent. With the kwargs
it is possible to override the values by specifying new ones for some fields (except for the id
field which is set to a new one automatically). Return the new agent instance.
Example
using Agents
+add_agent!(model; w = 0.5, k = true) # use keywords: w becomes 0.5, k becomes true
sourceadd_agent!(agent::AbstractAgent [, pos], model::ABM) → agent
Add the agent
to the model in the given position. If pos
is not given, the agent
is added to a random position. The agent
's position is always updated to match position
, and therefore for add_agent!
the position of the agent
is meaningless. Use add_agent_pos!
to use the agent
's position.
The type of pos
must match the underlying space position type.
sourceAgents.replicate!
— Functionreplicate!(agent, model; kwargs...)
Add a new agent to the model
copying the values of the fields of the given agent. With the kwargs
it is possible to override the values by specifying new ones for some fields (except for the id
field which is set to a new one automatically). Return the new agent instance.
Example
using Agents
@agent struct A(GridAgent{2})
k::Float64
w::Float64
@@ -29,24 +29,24 @@
model = ABM(A, GridSpace((5, 5)))
a = A(1, (2, 2), 0.5, 0.5)
-b = replicate!(a, model; w = 0.8)
sourceAgents.nextid
— Functionnextid(model::ABM) → id
Return a valid id
for creating a new agent with it.
sourceAgents.random_position
— Functionrandom_position(model) → pos
Return a random position in the model's space (always with appropriate Type).
sourceMoving agents
Agents.move_agent!
— Functionmove_agent!(agent [, pos], model::ABM) → agent
Move agent to the given position, or to a random one if a position is not given. pos
must have the appropriate position type depending on the space type.
The agent's position is updated to match pos
after the move.
sourcemove_agent!(agent::A, model::ABM{<:ContinuousSpace,A}, dt::Real)
Propagate the agent forwards one step according to its velocity, after updating the agent's velocity (if configured using update_vel!
, see ContinuousSpace
).
For this continuous space version of move_agent!
, the "time evolution" is a trivial Euler scheme with dt
the step size, i.e. the agent position is updated as agent.pos += agent.vel * dt
.
Unlike move_agent!(agent, [pos,] model)
, this function respects the space size. For non-periodic spaces, agents will walk up to, but not reach, the space extent. For periodic spaces movement properly wraps around the extent.
sourceAgents.walk!
— Functionwalk!(agent, direction::NTuple, model::ABM{<:AbstractGridSpace}; ifempty = true)
-walk!(agent, direction::SVector, model::ABM{<:ContinuousSpace})
Move agent in the given direction
respecting periodic boundary conditions. For non-periodic spaces, agents will walk to, but not exceed the boundary value. Available for both AbstractGridSpace
and ContinuousSpace
s.
The type of direction
must be the same as the space position. AbstractGridSpace
asks for Int
tuples, and ContinuousSpace
for Float64
static vectors, describing the walk distance in each direction. direction = (2, -3)
is an example of a valid direction on a AbstractGridSpace
, which moves the agent to the right 2 positions and down 3 positions. Agent velocity is ignored for this operation in ContinuousSpace
.
Keywords
ifempty
will check that the target position is unoccupied and only move if that's true. Available only on AbstractGridSpace
.
Example usage in Battle Royale.
sourcewalk!(agent, rand, model)
Invoke a random walk by providing the rand
function in place of direction
. For AbstractGridSpace
, the walk will cover ±1 positions in all directions, ContinuousSpace
will reside within [-1, 1].
This functionality is deprecated. Use randomwalk!
instead.
sourceAgents.randomwalk!
— Functionrandomwalk!(agent, model::ABM{<:AbstractGridSpace}, r::Real = 1; kwargs...)
Move agent
for a distance r
in a random direction respecting boundary conditions and space metric. For Chebyshev and Manhattan metric, the step size r
is rounded to floor(Int,r)
; for Euclidean metric in a GridSpace, random walks are ill defined and hence not supported.
For example, for Chebyshev
metric and r=1
, this will move the agent with equal probability to any of the 8 surrounding cells. For Manhattan metric, it will move to any of the 4 surrounding cells.
Keywords
ifempty
will check that the target position is unoccupied and only move if that's true. So if ifempty
is true, this can result in the agent not moving even if there are available positions. By default this is true, set it to false if different agents can occupy the same position. In a GridSpaceSingle
, agents cannot overlap anyways and this keyword has no effect.force_motion
has an effect only if ifempty
is true or the space is a GridSpaceSingle
. If set to true, the search for the random walk will be done only on the empty positions, so in this case the agent will always move if there is at least one empty position to choose from. By default this is false.
sourcerandomwalk!(agent, model::ABM{<:ContinuousSpace} [, r];
+b = replicate!(a, model; w = 0.8)
sourceAgents.nextid
— Functionnextid(model::ABM) → id
Return a valid id
for creating a new agent with it.
sourceAgents.random_position
— Functionrandom_position(model) → pos
Return a random position in the model's space (always with appropriate Type).
sourceMoving agents
Agents.move_agent!
— Functionmove_agent!(agent [, pos], model::ABM) → agent
Move agent to the given position, or to a random one if a position is not given. pos
must have the appropriate position type depending on the space type.
The agent's position is updated to match pos
after the move.
sourcemove_agent!(agent::A, model::ABM{<:ContinuousSpace,A}, dt::Real)
Propagate the agent forwards one step according to its velocity, after updating the agent's velocity (if configured using update_vel!
, see ContinuousSpace
).
For this continuous space version of move_agent!
, the "time evolution" is a trivial Euler scheme with dt
the step size, i.e. the agent position is updated as agent.pos += agent.vel * dt
.
Unlike move_agent!(agent, [pos,] model)
, this function respects the space size. For non-periodic spaces, agents will walk up to, but not reach, the space extent. For periodic spaces movement properly wraps around the extent.
sourceAgents.walk!
— Functionwalk!(agent, direction::NTuple, model::ABM{<:AbstractGridSpace}; ifempty = true)
+walk!(agent, direction::SVector, model::ABM{<:ContinuousSpace})
Move agent in the given direction
respecting periodic boundary conditions. For non-periodic spaces, agents will walk to, but not exceed the boundary value. Available for both AbstractGridSpace
and ContinuousSpace
s.
The type of direction
must be the same as the space position. AbstractGridSpace
asks for Int
tuples, and ContinuousSpace
for Float64
static vectors, describing the walk distance in each direction. direction = (2, -3)
is an example of a valid direction on a AbstractGridSpace
, which moves the agent to the right 2 positions and down 3 positions. Agent velocity is ignored for this operation in ContinuousSpace
.
Keywords
ifempty
will check that the target position is unoccupied and only move if that's true. Available only on AbstractGridSpace
.
Example usage in Battle Royale.
sourcewalk!(agent, rand, model)
Invoke a random walk by providing the rand
function in place of direction
. For AbstractGridSpace
, the walk will cover ±1 positions in all directions, ContinuousSpace
will reside within [-1, 1].
This functionality is deprecated. Use randomwalk!
instead.
sourceAgents.randomwalk!
— Functionrandomwalk!(agent, model::ABM{<:AbstractGridSpace}, r::Real = 1; kwargs...)
Move agent
for a distance r
in a random direction respecting boundary conditions and space metric. For Chebyshev and Manhattan metric, the step size r
is rounded to floor(Int,r)
; for Euclidean metric in a GridSpace, random walks are ill defined and hence not supported.
For example, for Chebyshev
metric and r=1
, this will move the agent with equal probability to any of the 8 surrounding cells. For Manhattan metric, it will move to any of the 4 surrounding cells.
Keywords
ifempty
will check that the target position is unoccupied and only move if that's true. So if ifempty
is true, this can result in the agent not moving even if there are available positions. By default this is true, set it to false if different agents can occupy the same position. In a GridSpaceSingle
, agents cannot overlap anyways and this keyword has no effect.force_motion
has an effect only if ifempty
is true or the space is a GridSpaceSingle
. If set to true, the search for the random walk will be done only on the empty positions, so in this case the agent will always move if there is at least one empty position to choose from. By default this is false.
sourcerandomwalk!(agent, model::ABM{<:ContinuousSpace} [, r];
[polar=Uniform(-π,π), azimuthal=Arccos(-1,1)]
-)
Re-orient and move agent
for a distance r
in a random direction respecting space boundary conditions. By default r = norm(agent.vel)
.
The ContinuousSpace
version is slightly different than the grid space. Here, the agent's velocity is updated by the random vector generated for the random walk.
Uniform/isotropic random walks are supported in any number of dimensions while an angles distribution can be specified for 2D and 3D random walks. In this case, the velocity vector is rotated using random angles given by the distributions for polar (2D and 3D) and azimuthal (3D only) angles, and scaled to have measure r
. After the re-orientation the agent is moved for r
in the new direction.
Anything that supports rand
can be used as an angle distribution instead. This can be useful to create correlated random walks.
sourceAgents.get_direction
— Functionget_direction(from, to, model::ABM)
Return the direction vector from the position from
to position to
taking into account periodicity of the space.
sourceMovement with paths
For OpenStreetMapSpace
, and GridSpace
/ContinuousSpace
using Pathfinding.Pathfinder
, a special movement method is available.
Agents.plan_route!
— Functionplan_route!(agent, dest, pathfinder::AStar{D})
Calculate and store the shortest path to move the agent from its current position to dest
(a position e.g. (1, 5)
or (1.3, 5.2)
) using the provided pathfinder
.
Use this method in conjunction with move_along_route!
.
sourceplan_route!(agent, dest, model::ABM{<:OpenStreetMapSpace};
- return_trip = false, kwargs...) → success
Plan a route from the current position of agent
to the location specified in dest
, which can be an intersection or a point on a road. Overwrite any existing route.
If return_trip = true
, a route will be planned from start ⟶ finish ⟶ start. All other keywords are passed to LightOSM.shortest_path
.
Return true
if a path to dest
exists, and hence the route planning was successful. Otherwise return false
. Specifying return_trip = true
also requires the existence of a return path for a route to be planned.
sourceAgents.plan_best_route!
— Functionplan_best_route!(agent, dests, pathfinder::AStar{D}; kwargs...)
Calculate, store, and return the best path to move the agent from its current position to a chosen destination taken from dests
using pathfinder
.
The condition = :shortest
keyword returns the shortest path which is shortest out of the possible destinations. Alternatively, the :longest
path may also be requested.
Return the position of the chosen destination. Return nothing
if none of the supplied destinations are reachable.
sourceAgents.move_along_route!
— Functionmove_along_route!(agent, model::ABM{<:GridSpace{D}}, pathfinder::AStar{D})
Move agent
for one step along the route toward its target set by plan_route!
For pathfinding in models with GridSpace
.
If the agent does not have a precalculated path or the path is empty, it remains stationary.
sourcemove_along_route!(agent, model::ABM{<:ContinuousSpace{D}}, pathfinder::AStar{D}, speed, dt = 1.0)
Move agent
for one step along the route toward its target set by plan_route!
at the given speed
and timestep dt
.
For pathfinding in models with ContinuousSpace
If the agent does not have a precalculated path or the path is empty, it remains stationary.
sourcemove_along_route!(agent, model::ABM{<:OpenStreetMapSpace}, distance::Real) → remaining
Move an agent by distance
along its planned route. Units of distance are as specified by the underlying graph's weight_type
. If the provided distance
is greater than the distance to the end of the route, return the remaining distance. Otherwise, return 0
. 0
is also returned if is_stationary(agent, model)
.
sourceAgents.is_stationary
— Functionis_stationary(agent, astar::AStar)
Same, but for pathfinding with A*.
sourceis_stationary(agent, model)
Return true
if agent has reached the end of its route, or no route has been set for it. Used in setups where using move_along_route!
is valid.
sourceRemoving agents
Agents.remove_agent!
— FunctionPathfinding.remove_agent!(agent, model, pathfinder)
The same as remove_agent!(agent, model)
, but also removes the agent's path data from pathfinder
.
sourceremove_agent!(agent::AbstractAgent, model::ABM)
-remove_agent!(id::Int, model::ABM)
Remove an agent from the model.
sourceAgents.remove_all!
— Functionremove_all!(model::ABM)
Remove all the agents of the model.
sourceremove_all!(model::ABM, n::Int)
Remove the agents whose IDs are larger than n.
sourceremove_all!(model::ABM, IDs)
Remove the agents with the given IDs.
sourceremove_all!(model::ABM, f::Function)
Remove all agents where the function f(agent)
returns true
.
sourceAgents.sample!
— Functionsample!(model::ABM, n [, weight]; kwargs...)
Replace the agents of the model
with a random sample of the current agents with size n
.
Optionally, provide a weight
: Symbol (agent field) or function (input agent out put number) to weight the sampling. This means that the higher the weight
of the agent, the higher the probability that this agent will be chosen in the new sampling.
Keywords
replace = true
: whether sampling is performed with replacement, i.e. all agents can
be chosen more than once.
Example usage in Wright-Fisher model of evolution.
sourceSpace utility functions
Agents.normalize_position
— Functionnormalize_position(pos, model::ABM{<:Union{AbstractGridSpace,ContinuousSpace}})
Return the position pos
normalized for the extents of the space of the given model
. For periodic spaces, this wraps the position along each dimension, while for non-periodic spaces this clamps the position to the space extent.
sourceAgents.spacesize
— Functionspacesize(model::ABM)
Return the size of the model's space. Works for AbstractGridSpace
and ContinuousSpace
.
sourceDiscrete space exclusives
Agents.positions
— Functionpositions(model::ABM{<:DiscreteSpace}) → ns
Return an iterator over all positions of a model with a discrete space.
positions(model::ABM{<:DiscreteSpace}, by::Symbol) → ns
Return all positions of a model with a discrete space, sorting them using the argument by
which can be:
:random
- randomly sorted:population
- positions are sorted depending on how many agents they accommodate. The more populated positions are first.
sourceAgents.npositions
— Functionnpositions(model::ABM{<:DiscreteSpace})
Return the number of positions of a model with a discrete space.
sourceAgents.ids_in_position
— Functionids_in_position(position, model::ABM{<:DiscreteSpace})
-ids_in_position(agent, model::ABM{<:DiscreteSpace})
Return the ids of agents in the position corresponding to position
or position of agent
.
sourceAgents.id_in_position
— Functionid_in_position(pos, model::ABM{<:GridSpaceSingle}) → id
Return the agent ID in the given position. This will be 0
if there is no agent in this position.
This is similar to ids_in_position
, but specialized for GridSpaceSingle
. See also isempty
.
sourceAgents.agents_in_position
— Functionagents_in_position(position, model::ABM{<:DiscreteSpace})
-agents_in_position(agent, model::ABM{<:DiscreteSpace})
Return an iterable of the agents in position
, or in the position of
agent`.
sourceAgents.random_id_in_position
— Functionrandom_id_in_position(pos, model::ABM, [f, alloc = false]) → id
Return a random id in the position specified in pos
.
A filter function f(id)
can be passed so that to restrict the sampling on only those agents for which the function returns true
. The argument alloc
can be used if the filtering condition is expensive since in this case the allocating version can be more performant. nothing
is returned if no nearby position satisfies f
.
Use random_nearby_id
instead to return the id
of a random agent near the position of a given agent
.
sourceAgents.random_agent_in_position
— Functionrandom_agent_in_position(pos, model::ABM, [f, alloc = false]) → agent
Return a random agent in the position specified in pos
.
A filter function f(agent)
can be passed so that to restrict the sampling on only those agents for which the function returns true
. The argument alloc
can be used if the filtering condition is expensive since in this case the allocating version can be more performant. nothing
is returned if no nearby position satisfies f
.
Use random_nearby_agent
instead to return a random agent near the position of a given agent
.
sourceAgents.fill_space!
— Functionfill_space!([A ,] model::ABM{<:DiscreteSpace,A}, args...; kwargs...)
-fill_space!([A ,] model::ABM{<:DiscreteSpace,A}, f::Function; kwargs...)
Add one agent to each position in the model's space. Similarly with add_agent!
, the function creates the necessary agents and the args...; kwargs...
are propagated into agent creation. If instead of args...
a function f
is provided, then args = f(pos)
is the result of applying f
where pos
is each position (tuple for grid, integer index for graph).
An optional first argument is an agent type to be created, and targets mixed agent models where the agent constructor cannot be deduced (since it is a union).
sourceAgents.has_empty_positions
— Functionhas_empty_positions(model::ABM{<:DiscreteSpace})
Return true
if there are any positions in the model without agents.
sourceAgents.empty_positions
— Functionempty_positions(model)
Return a list of positions that currently have no agents on them.
sourceAgents.empty_nearby_positions
— Functionempty_nearby_positions(pos, model::ABM{<:DiscreteSpace}, r = 1; kwargs...)
-empty_nearby_positions(agent, model::ABM{<:DiscreteSpace}, r = 1; kwargs...)
Return an iterable of all empty positions within radius r
from the given position or the given agent.
The value of r
and possible keywords operate identically to nearby_positions
.
sourceAgents.random_empty
— Functionrandom_empty(model::ABM{<:DiscreteSpace})
Return a random position without any agents, or nothing
if no such positions exist.
sourceAgents.add_agent_single!
— Functionadd_agent_single!(model::ABM{<:DiscreteSpace}, properties...; kwargs...)
Same as add_agent!(model, properties...; kwargs...)
but ensures that it adds an agent into a position with no other agents (does nothing if no such position exists).
sourceadd_agent_single!(A, model::ABM{<:DiscreteSpace}, properties...; kwargs...)
Same as add_agent!(A, model, properties...; kwargs...)
but ensures that it adds an agent into a position with no other agents (does nothing if no such position exists).
sourceadd_agent_single!(agent, model::ABM{<:DiscreteSpace}) → agent
Add the agent
to a random position in the space while respecting a maximum of one agent per position, updating the agent's position to the new one.
This function does nothing if there aren't any empty positions.
sourceAgents.move_agent_single!
— Functionmove_agent_single!(agent, model::ABM{<:DiscreteSpace}; cutoff) → agent
Move agent to a random position while respecting a maximum of one agent per position. If there are no empty positions, the agent won't move.
The keyword cutoff = 0.998
is sent to random_empty
.
sourceAgents.swap_agents!
— Functionswap_agents!(agent1, agent2, model::ABM{<:DiscreteSpace})
Swap the given agents positions, moving each of them to the position of the other agent.
sourceBase.isempty
— Methodisempty(position, model::ABM{<:DiscreteSpace})
Return true
if there are no agents in position
.
sourceGraphSpace
exclusives
Graphs.SimpleGraphs.add_edge!
— Functionadd_edge!(model::ABM{<:GraphSpace}, args...; kwargs...)
Add a new edge (relationship between two positions) to the graph. Returns a boolean, true if the operation was successful.
args
and kwargs
are directly passed to the add_edge!
dispatch that acts the underlying graph type.
sourceGraphs.SimpleGraphs.rem_edge!
— Functionrem_edge!(model::ABM{<:GraphSpace}, n, m)
Remove an edge (relationship between two positions) from the graph. Returns a boolean, true if the operation was successful.
sourceGraphs.SimpleGraphs.add_vertex!
— Functionadd_vertex!(model::ABM{<:GraphSpace})
Add a new node (i.e. possible position) to the model's graph and return it. You can connect this new node with existing ones using add_edge!
.
sourceGraphs.SimpleGraphs.rem_vertex!
— Functionrem_vertex!(model::ABM{<:GraphSpace}, n::Int)
Remove node (i.e. position) n
from the model's graph. All agents in that node are removed from the model.
Warning: Graphs.jl (and thus Agents.jl) swaps the index of the last node with that of the one to be removed, while every other node remains as is. This means that when doing rem_vertex!(n, model)
the last node becomes the n
-th node while the previous n
-th node (and all its edges and agents) are deleted.
sourceContinuousSpace
exclusives
Agents.nearby_ids_exact
— Functionnearby_ids_exact(x, model, r = 1)
Return an iterator over agent IDs nearby x
(a position or an agent). Only valid for ContinuousSpace
models. Use instead of nearby_ids
for a slower, but 100% accurate version. See ContinuousSpace
for more details.
sourceAgents.nearest_neighbor
— Functionnearest_neighbor(agent, model::ABM{<:ContinuousSpace}, r) → nearest
Return the agent that has the closest distance to given agent
. Return nothing
if no agent is within distance r
.
sourceAgents.get_spatial_property
— Functionget_spatial_property(pos, property::AbstractArray, model::ABM)
Convert the continuous agent position into an appropriate index
of property
, which represents some discretization of a spatial field over a ContinuousSpace
. Then, return property[index]
. To get the index
directly, for e.g. mutating the property
in-place, use get_spatial_index
.
sourceget_spatial_property(pos, property::Function, model::ABM)
Literally equivalent with property(pos, model)
, provided just for syntax consistency.
sourceAgents.get_spatial_index
— Functionget_spatial_index(pos, property::AbstractArray, model::ABM)
Convert the continuous agent position into an appropriate index
of property
, which represents some discretization of a spatial field over a ContinuousSpace
.
The dimensionality of property
and the continuous space do not have to match. If property
has lower dimensionality than the space (e.g. representing some surface property in 3D space) then the front dimensions of pos
will be used to index.
sourceAgents.interacting_pairs
— Functioninteracting_pairs(model, r, method; scheduler = abmscheduler(model)) → piter
Return an iterator that yields unique pairs of agents (a, b)
that are close neighbors to each other, within some interaction radius r
.
This function is usefully combined with model_step!
, when one wants to perform some pairwise interaction across all pairs of close agents once (and does not want to trigger the event twice, both with a
and with b
, which would be unavoidable when using agent_step!
). This means, that if a pair (a, b)
exists, the pair (b, a)
is not included in the iterator!
Use piter.pairs
to get a vector of pair IDs from the iterator.
The argument method
provides three pairing scenarios
:all
: return every pair of agents that are within radius r
of each other, not only the nearest ones.:nearest
: agents are only paired with their true nearest neighbor (existing within radius r
). Each agent can only belong to one pair, therefore if two agents share the same nearest neighbor only one of them (sorted by distance, then by next id in scheduler
) will be paired.:types
: For mixed agent models only. Return every pair of agents within radius r
(similar to :all
), only capturing pairs of differing types. For example, a model of Union{Sheep,Wolf}
will only return pairs of (Sheep, Wolf)
. In the case of multiple agent types, e.g. Union{Sheep, Wolf, Grass}
, skipping pairings that involve Grass
, can be achieved by a scheduler
that doesn't schedule Grass
types, i.e.: scheduler(model) = (a.id for a in allagents(model) if !(a isa Grass))
.
The following keywords can be used:
scheduler = abmscheduler(model)
, which schedulers the agents during iteration for finding pairs. Especially in the :nearest
case, this is important, as different sequencing for the agents may give different results (if b
is the nearest agent for a
, but a
is not the nearest agent for b
, whether you get the pair (a, b)
or not depends on whether a
was scheduler first or not).nearby_f = nearby_ids_exact
is the function that decides how to find nearby IDs in the :all, :types
cases. Must be nearby_ids_exact
or nearby_ids
.
Example usage in https://juliadynamics.github.io/AgentsExampleZoo.jl/dev/examples/growing_bacteria/.
Better performance with CellListMap.jl Notice that in most applications that interacting_pairs
is useful, there is significant (10x-100x) performance gain to be made by integrating with CellListMap.jl. Checkout the Integrating Agents.jl with CellListMap.jl integration example for how to do this.
sourceAgents.elastic_collision!
— Functionelastic_collision!(a, b, f = nothing) → happened
Resolve a (hypothetical) elastic collision between the two agents a, b
. They are assumed to be disks of equal size touching tangentially. Their velocities (field vel
) are adjusted for an elastic collision happening between them. This function works only for two dimensions. Notice that collision only happens if both disks face each other, to avoid collision-after-collision.
If f
is a Symbol
, then the agent property f
, e.g. :mass
, is taken as a mass to weight the two agents for the collision. By default no weighting happens.
One of the two agents can have infinite "mass", and then acts as an immovable object that specularly reflects the other agent. In this case momentum is not conserved, but kinetic energy is still conserved.
Return a boolean encoding whether the collision happened.
Example usage in Continuous space social distancing.
sourceAgents.euclidean_distance
— Functioneuclidean_distance(a, b, model::ABM)
Return the euclidean distance between a
and b
(either agents or agent positions), respecting periodic boundary conditions (if in use). Works with any space where it makes sense: currently AbstractGridSpace
and ContinuousSpace
.
Example usage in the Flocking model.
sourceAgents.manhattan_distance
— Functionmanhattan_distance(a, b, model::ABM)
Return the manhattan distance between a
and b
(either agents or agent positions), respecting periodic boundary conditions (if in use). Works with any space where it makes sense: currently AbstractGridSpace
and ContinuousSpace
.
sourceOpenStreetMapSpace
exclusives
Agents.OSM
— ModuleOSM
Submodule for functionality related to OpenStreetMapSpace
. See the docstring of the space for more info.
sourceAgents.OSM.lonlat
— FunctionOSM.lonlat(pos, model)
-OSM.lonlat(agent, model)
Return (longitude, latitude)
of current road or intersection position.
sourceAgents.OSM.nearest_node
— FunctionOSM.nearest_node(lonlat::Tuple{Float64,Float64}, model::ABM{<:OpenStreetMapSpace})
Return the nearest intersection position to (longitude, latitude). Quicker, but less precise than OSM.nearest_road
.
sourceAgents.OSM.nearest_road
— FunctionOSM.nearest_road(lonlat::Tuple{Float64,Float64}, model::ABM{<:OpenStreetMapSpace})
Return a location on a road nearest to (longitude, latitude). Slower, but more precise than OSM.nearest_node
.
sourceAgents.OSM.random_road_position
— FunctionOSM.random_road_position(model::ABM{<:OpenStreetMapSpace})
Similar to random_position
, but rather than providing only intersections, this method returns a location somewhere on a road heading in a random direction.
sourceAgents.OSM.plan_random_route!
— FunctionOSM.plan_random_route!(agent, model::ABM{<:OpenStreetMapSpace}; kwargs...) → success
Plan a new random route for the agent, by selecting a random destination and planning a route from the agent's current position. Overwrite any existing route.
The keyword limit = 10
specifies the limit on the number of attempts at planning a random route, as no connection may be possible given the random destination. Return true
if a route was successfully planned, false
otherwise. All other keywords are passed to plan_route!
sourceAgents.OSM.road_length
— FunctionOSM.road_length(start::Int, finish::Int, model)
-OSM.road_length(pos::Tuple{Int,Int,Float64}, model)
Return the road length between two intersections. This takes into account the direction of the road, so OSM.road_length(pos_1, pos_2, model)
may not be the same as OSM.road_length(pos_2, pos_1, model)
. Units of the returned quantity are as specified by the underlying graph's weight_type
. If start
and finish
are the same or pos[1]
and pos[2]
are the same, then return 0.
sourceAgents.OSM.route_length
— FunctionOSM.route_length(agent, model::ABM{<:OpenStreetMapSpace})
Return the length of the route planned for the given agent
, correctly taking into account the amount of route already traversed by the agent
. Return 0 if is_stationary(agent, model)
.
sourceAgents.OSM.same_position
— FunctionOSM.same_position(a::Tuple{Int,Int,Float64}, b::Tuple{Int,Int,Float64}, model::ABM{<:OpenStreetMapSpace})
Return true
if the given positions a
and b
are (approximately) identical
sourceAgents.OSM.same_road
— FunctionOSM.same_road(a::Tuple{Int,Int,Float64}, b::Tuple{Int,Int,Float64})
Return true
if both points lie on the same road of the graph
sourceAgents.OSM.test_map
— FunctionOSM.test_map()
Download a small test map of Göttingen as an artifact. Return a path to the downloaded file.
Using this map requires network_type = :none
to be passed as a keyword to OSMSpace
. The unit of distance used for this map is :time
.
sourceLightOSM.download_osm_network
— Functiondownload_osm_network(download_method::Symbol;
+)
Re-orient and move agent
for a distance r
in a random direction respecting space boundary conditions. By default r = norm(agent.vel)
.
The ContinuousSpace
version is slightly different than the grid space. Here, the agent's velocity is updated by the random vector generated for the random walk.
Uniform/isotropic random walks are supported in any number of dimensions while an angles distribution can be specified for 2D and 3D random walks. In this case, the velocity vector is rotated using random angles given by the distributions for polar (2D and 3D) and azimuthal (3D only) angles, and scaled to have measure r
. After the re-orientation the agent is moved for r
in the new direction.
Anything that supports rand
can be used as an angle distribution instead. This can be useful to create correlated random walks.
sourceAgents.get_direction
— Functionget_direction(from, to, model::ABM)
Return the direction vector from the position from
to position to
taking into account periodicity of the space.
sourceMovement with paths
For OpenStreetMapSpace
, and GridSpace
/ContinuousSpace
using Pathfinding.Pathfinder
, a special movement method is available.
Agents.plan_route!
— Functionplan_route!(agent, dest, pathfinder::AStar{D})
Calculate and store the shortest path to move the agent from its current position to dest
(a position e.g. (1, 5)
or (1.3, 5.2)
) using the provided pathfinder
.
Use this method in conjunction with move_along_route!
.
sourceplan_route!(agent, dest, model::ABM{<:OpenStreetMapSpace};
+ return_trip = false, kwargs...) → success
Plan a route from the current position of agent
to the location specified in dest
, which can be an intersection or a point on a road. Overwrite any existing route.
If return_trip = true
, a route will be planned from start ⟶ finish ⟶ start. All other keywords are passed to LightOSM.shortest_path
.
Return true
if a path to dest
exists, and hence the route planning was successful. Otherwise return false
. Specifying return_trip = true
also requires the existence of a return path for a route to be planned.
sourceAgents.plan_best_route!
— Functionplan_best_route!(agent, dests, pathfinder::AStar{D}; kwargs...)
Calculate, store, and return the best path to move the agent from its current position to a chosen destination taken from dests
using pathfinder
.
The condition = :shortest
keyword returns the shortest path which is shortest out of the possible destinations. Alternatively, the :longest
path may also be requested.
Return the position of the chosen destination. Return nothing
if none of the supplied destinations are reachable.
sourceAgents.move_along_route!
— Functionmove_along_route!(agent, model::ABM{<:GridSpace{D}}, pathfinder::AStar{D})
Move agent
for one step along the route toward its target set by plan_route!
For pathfinding in models with GridSpace
.
If the agent does not have a precalculated path or the path is empty, it remains stationary.
sourcemove_along_route!(agent, model::ABM{<:ContinuousSpace{D}}, pathfinder::AStar{D}, speed, dt = 1.0)
Move agent
for one step along the route toward its target set by plan_route!
at the given speed
and timestep dt
.
For pathfinding in models with ContinuousSpace
If the agent does not have a precalculated path or the path is empty, it remains stationary.
sourcemove_along_route!(agent, model::ABM{<:OpenStreetMapSpace}, distance::Real) → remaining
Move an agent by distance
along its planned route. Units of distance are as specified by the underlying graph's weight_type
. If the provided distance
is greater than the distance to the end of the route, return the remaining distance. Otherwise, return 0
. 0
is also returned if is_stationary(agent, model)
.
sourceAgents.is_stationary
— Functionis_stationary(agent, model)
Return true
if agent has reached the end of its route, or no route has been set for it. Used in setups where using move_along_route!
is valid.
sourceis_stationary(agent, astar::AStar)
Same, but for pathfinding with A*.
sourceRemoving agents
Agents.remove_agent!
— Functionremove_agent!(agent::AbstractAgent, model::ABM)
+remove_agent!(id::Int, model::ABM)
Remove an agent from the model.
sourcePathfinding.remove_agent!(agent, model, pathfinder)
The same as remove_agent!(agent, model)
, but also removes the agent's path data from pathfinder
.
sourceAgents.remove_all!
— Functionremove_all!(model::ABM)
Remove all the agents of the model.
sourceremove_all!(model::ABM, n::Int)
Remove the agents whose IDs are larger than n.
sourceremove_all!(model::ABM, IDs)
Remove the agents with the given IDs.
sourceremove_all!(model::ABM, f::Function)
Remove all agents where the function f(agent)
returns true
.
sourceAgents.sample!
— Functionsample!(model::ABM, n [, weight]; kwargs...)
Replace the agents of the model
with a random sample of the current agents with size n
.
Optionally, provide a weight
: Symbol (agent field) or function (input agent out put number) to weight the sampling. This means that the higher the weight
of the agent, the higher the probability that this agent will be chosen in the new sampling.
Keywords
replace = true
: whether sampling is performed with replacement, i.e. all agents can
be chosen more than once.
Example usage in Wright-Fisher model of evolution.
sourceSpace utility functions
Agents.normalize_position
— Functionnormalize_position(pos, model::ABM{<:Union{AbstractGridSpace,ContinuousSpace}})
Return the position pos
normalized for the extents of the space of the given model
. For periodic spaces, this wraps the position along each dimension, while for non-periodic spaces this clamps the position to the space extent.
sourceAgents.spacesize
— Functionspacesize(model::ABM)
Return the size of the model's space. Works for AbstractGridSpace
and ContinuousSpace
.
sourceDiscrete space exclusives
Agents.positions
— Functionpositions(model::ABM{<:DiscreteSpace}) → ns
Return an iterator over all positions of a model with a discrete space.
positions(model::ABM{<:DiscreteSpace}, by::Symbol) → ns
Return all positions of a model with a discrete space, sorting them using the argument by
which can be:
:random
- randomly sorted:population
- positions are sorted depending on how many agents they accommodate. The more populated positions are first.
sourceAgents.npositions
— Functionnpositions(model::ABM{<:DiscreteSpace})
Return the number of positions of a model with a discrete space.
sourceAgents.ids_in_position
— Functionids_in_position(position, model::ABM{<:DiscreteSpace})
+ids_in_position(agent, model::ABM{<:DiscreteSpace})
Return the ids of agents in the position corresponding to position
or position of agent
.
sourceAgents.id_in_position
— Functionid_in_position(pos, model::ABM{<:GridSpaceSingle}) → id
Return the agent ID in the given position. This will be 0
if there is no agent in this position.
This is similar to ids_in_position
, but specialized for GridSpaceSingle
. See also isempty
.
sourceAgents.agents_in_position
— Functionagents_in_position(position, model::ABM{<:DiscreteSpace})
+agents_in_position(agent, model::ABM{<:DiscreteSpace})
Return an iterable of the agents in position
, or in the position of
agent`.
sourceAgents.random_id_in_position
— Functionrandom_id_in_position(pos, model::ABM, [f, alloc = false]) → id
Return a random id in the position specified in pos
.
A filter function f(id)
can be passed so that to restrict the sampling on only those agents for which the function returns true
. The argument alloc
can be used if the filtering condition is expensive since in this case the allocating version can be more performant. nothing
is returned if no nearby position satisfies f
.
Use random_nearby_id
instead to return the id
of a random agent near the position of a given agent
.
sourceAgents.random_agent_in_position
— Functionrandom_agent_in_position(pos, model::ABM, [f, alloc = false]) → agent
Return a random agent in the position specified in pos
.
A filter function f(agent)
can be passed so that to restrict the sampling on only those agents for which the function returns true
. The argument alloc
can be used if the filtering condition is expensive since in this case the allocating version can be more performant. nothing
is returned if no nearby position satisfies f
.
Use random_nearby_agent
instead to return a random agent near the position of a given agent
.
sourceAgents.fill_space!
— Functionfill_space!([A ,] model::ABM{<:DiscreteSpace,A}, args...; kwargs...)
+fill_space!([A ,] model::ABM{<:DiscreteSpace,A}, f::Function; kwargs...)
Add one agent to each position in the model's space. Similarly with add_agent!
, the function creates the necessary agents and the args...; kwargs...
are propagated into agent creation. If instead of args...
a function f
is provided, then args = f(pos)
is the result of applying f
where pos
is each position (tuple for grid, integer index for graph).
An optional first argument is an agent type to be created, and targets mixed agent models where the agent constructor cannot be deduced (since it is a union).
sourceAgents.has_empty_positions
— Functionhas_empty_positions(model::ABM{<:DiscreteSpace})
Return true
if there are any positions in the model without agents.
sourceAgents.empty_positions
— Functionempty_positions(model)
Return a list of positions that currently have no agents on them.
sourceAgents.empty_nearby_positions
— Functionempty_nearby_positions(pos, model::ABM{<:DiscreteSpace}, r = 1; kwargs...)
+empty_nearby_positions(agent, model::ABM{<:DiscreteSpace}, r = 1; kwargs...)
Return an iterable of all empty positions within radius r
from the given position or the given agent.
The value of r
and possible keywords operate identically to nearby_positions
.
sourceAgents.random_empty
— Functionrandom_empty(model::ABM{<:DiscreteSpace})
Return a random position without any agents, or nothing
if no such positions exist.
sourceAgents.add_agent_single!
— Functionadd_agent_single!(model::ABM{<:DiscreteSpace}, properties...; kwargs...)
Same as add_agent!(model, properties...; kwargs...)
but ensures that it adds an agent into a position with no other agents (does nothing if no such position exists).
sourceadd_agent_single!(A, model::ABM{<:DiscreteSpace}, properties...; kwargs...)
Same as add_agent!(A, model, properties...; kwargs...)
but ensures that it adds an agent into a position with no other agents (does nothing if no such position exists).
sourceadd_agent_single!(agent, model::ABM{<:DiscreteSpace}) → agent
Add the agent
to a random position in the space while respecting a maximum of one agent per position, updating the agent's position to the new one.
This function does nothing if there aren't any empty positions.
sourceAgents.move_agent_single!
— Functionmove_agent_single!(agent, model::ABM{<:DiscreteSpace}; cutoff) → agent
Move agent to a random position while respecting a maximum of one agent per position. If there are no empty positions, the agent won't move.
The keyword cutoff = 0.998
is sent to random_empty
.
sourceAgents.swap_agents!
— Functionswap_agents!(agent1, agent2, model::ABM{<:DiscreteSpace})
Swap the given agents positions, moving each of them to the position of the other agent.
sourceBase.isempty
— Methodisempty(position, model::ABM{<:DiscreteSpace})
Return true
if there are no agents in position
.
sourceGraphSpace
exclusives
Graphs.SimpleGraphs.add_edge!
— Functionadd_edge!(model::ABM{<:GraphSpace}, args...; kwargs...)
Add a new edge (relationship between two positions) to the graph. Returns a boolean, true if the operation was successful.
args
and kwargs
are directly passed to the add_edge!
dispatch that acts the underlying graph type.
sourceGraphs.SimpleGraphs.rem_edge!
— Functionrem_edge!(model::ABM{<:GraphSpace}, n, m)
Remove an edge (relationship between two positions) from the graph. Returns a boolean, true if the operation was successful.
sourceGraphs.SimpleGraphs.add_vertex!
— Functionadd_vertex!(model::ABM{<:GraphSpace})
Add a new node (i.e. possible position) to the model's graph and return it. You can connect this new node with existing ones using add_edge!
.
sourceGraphs.SimpleGraphs.rem_vertex!
— Functionrem_vertex!(model::ABM{<:GraphSpace}, n::Int)
Remove node (i.e. position) n
from the model's graph. All agents in that node are removed from the model.
Warning: Graphs.jl (and thus Agents.jl) swaps the index of the last node with that of the one to be removed, while every other node remains as is. This means that when doing rem_vertex!(n, model)
the last node becomes the n
-th node while the previous n
-th node (and all its edges and agents) are deleted.
sourceContinuousSpace
exclusives
Agents.nearby_ids_exact
— Functionnearby_ids_exact(x, model, r = 1)
Return an iterator over agent IDs nearby x
(a position or an agent). Only valid for ContinuousSpace
models. Use instead of nearby_ids
for a slower, but 100% accurate version. See ContinuousSpace
for more details.
sourceAgents.nearest_neighbor
— Functionnearest_neighbor(agent, model::ABM{<:ContinuousSpace}, r) → nearest
Return the agent that has the closest distance to given agent
. Return nothing
if no agent is within distance r
.
sourceAgents.get_spatial_property
— Functionget_spatial_property(pos, property::AbstractArray, model::ABM)
Convert the continuous agent position into an appropriate index
of property
, which represents some discretization of a spatial field over a ContinuousSpace
. Then, return property[index]
. To get the index
directly, for e.g. mutating the property
in-place, use get_spatial_index
.
sourceget_spatial_property(pos, property::Function, model::ABM)
Literally equivalent with property(pos, model)
, provided just for syntax consistency.
sourceAgents.get_spatial_index
— Functionget_spatial_index(pos, property::AbstractArray, model::ABM)
Convert the continuous agent position into an appropriate index
of property
, which represents some discretization of a spatial field over a ContinuousSpace
.
The dimensionality of property
and the continuous space do not have to match. If property
has lower dimensionality than the space (e.g. representing some surface property in 3D space) then the front dimensions of pos
will be used to index.
sourceAgents.interacting_pairs
— Functioninteracting_pairs(model, r, method; scheduler = abmscheduler(model)) → piter
Return an iterator that yields unique pairs of agents (a, b)
that are close neighbors to each other, within some interaction radius r
.
This function is usefully combined with model_step!
, when one wants to perform some pairwise interaction across all pairs of close agents once (and does not want to trigger the event twice, both with a
and with b
, which would be unavoidable when using agent_step!
). This means, that if a pair (a, b)
exists, the pair (b, a)
is not included in the iterator!
Use piter.pairs
to get a vector of pair IDs from the iterator.
The argument method
provides three pairing scenarios
:all
: return every pair of agents that are within radius r
of each other, not only the nearest ones.:nearest
: agents are only paired with their true nearest neighbor (existing within radius r
). Each agent can only belong to one pair, therefore if two agents share the same nearest neighbor only one of them (sorted by distance, then by next id in scheduler
) will be paired.:types
: For mixed agent models only. Return every pair of agents within radius r
(similar to :all
), only capturing pairs of differing types. For example, a model of Union{Sheep,Wolf}
will only return pairs of (Sheep, Wolf)
. In the case of multiple agent types, e.g. Union{Sheep, Wolf, Grass}
, skipping pairings that involve Grass
, can be achieved by a scheduler
that doesn't schedule Grass
types, i.e.: scheduler(model) = (a.id for a in allagents(model) if !(a isa Grass))
.
The following keywords can be used:
scheduler = abmscheduler(model)
, which schedulers the agents during iteration for finding pairs. Especially in the :nearest
case, this is important, as different sequencing for the agents may give different results (if b
is the nearest agent for a
, but a
is not the nearest agent for b
, whether you get the pair (a, b)
or not depends on whether a
was scheduler first or not).nearby_f = nearby_ids_exact
is the function that decides how to find nearby IDs in the :all, :types
cases. Must be nearby_ids_exact
or nearby_ids
.
Example usage in https://juliadynamics.github.io/AgentsExampleZoo.jl/dev/examples/growing_bacteria/.
Better performance with CellListMap.jl Notice that in most applications that interacting_pairs
is useful, there is significant (10x-100x) performance gain to be made by integrating with CellListMap.jl. Checkout the Integrating Agents.jl with CellListMap.jl integration example for how to do this.
sourceAgents.elastic_collision!
— Functionelastic_collision!(a, b, f = nothing) → happened
Resolve a (hypothetical) elastic collision between the two agents a, b
. They are assumed to be disks of equal size touching tangentially. Their velocities (field vel
) are adjusted for an elastic collision happening between them. This function works only for two dimensions. Notice that collision only happens if both disks face each other, to avoid collision-after-collision.
If f
is a Symbol
, then the agent property f
, e.g. :mass
, is taken as a mass to weight the two agents for the collision. By default no weighting happens.
One of the two agents can have infinite "mass", and then acts as an immovable object that specularly reflects the other agent. In this case momentum is not conserved, but kinetic energy is still conserved.
Return a boolean encoding whether the collision happened.
Example usage in Continuous space social distancing.
sourceAgents.euclidean_distance
— Functioneuclidean_distance(a, b, model::ABM)
Return the euclidean distance between a
and b
(either agents or agent positions), respecting periodic boundary conditions (if in use). Works with any space where it makes sense: currently AbstractGridSpace
and ContinuousSpace
.
Example usage in the Flocking model.
sourceAgents.manhattan_distance
— Functionmanhattan_distance(a, b, model::ABM)
Return the manhattan distance between a
and b
(either agents or agent positions), respecting periodic boundary conditions (if in use). Works with any space where it makes sense: currently AbstractGridSpace
and ContinuousSpace
.
sourceOpenStreetMapSpace
exclusives
Agents.OSM
— ModuleOSM
Submodule for functionality related to OpenStreetMapSpace
. See the docstring of the space for more info.
sourceAgents.OSM.lonlat
— FunctionOSM.lonlat(pos, model)
+OSM.lonlat(agent, model)
Return (longitude, latitude)
of current road or intersection position.
sourceAgents.OSM.nearest_node
— FunctionOSM.nearest_node(lonlat::Tuple{Float64,Float64}, model::ABM{<:OpenStreetMapSpace})
Return the nearest intersection position to (longitude, latitude). Quicker, but less precise than OSM.nearest_road
.
sourceAgents.OSM.nearest_road
— FunctionOSM.nearest_road(lonlat::Tuple{Float64,Float64}, model::ABM{<:OpenStreetMapSpace})
Return a location on a road nearest to (longitude, latitude). Slower, but more precise than OSM.nearest_node
.
sourceAgents.OSM.random_road_position
— FunctionOSM.random_road_position(model::ABM{<:OpenStreetMapSpace})
Similar to random_position
, but rather than providing only intersections, this method returns a location somewhere on a road heading in a random direction.
sourceAgents.OSM.plan_random_route!
— FunctionOSM.plan_random_route!(agent, model::ABM{<:OpenStreetMapSpace}; kwargs...) → success
Plan a new random route for the agent, by selecting a random destination and planning a route from the agent's current position. Overwrite any existing route.
The keyword limit = 10
specifies the limit on the number of attempts at planning a random route, as no connection may be possible given the random destination. Return true
if a route was successfully planned, false
otherwise. All other keywords are passed to plan_route!
sourceAgents.OSM.road_length
— FunctionOSM.road_length(start::Int, finish::Int, model)
+OSM.road_length(pos::Tuple{Int,Int,Float64}, model)
Return the road length between two intersections. This takes into account the direction of the road, so OSM.road_length(pos_1, pos_2, model)
may not be the same as OSM.road_length(pos_2, pos_1, model)
. Units of the returned quantity are as specified by the underlying graph's weight_type
. If start
and finish
are the same or pos[1]
and pos[2]
are the same, then return 0.
sourceAgents.OSM.route_length
— FunctionOSM.route_length(agent, model::ABM{<:OpenStreetMapSpace})
Return the length of the route planned for the given agent
, correctly taking into account the amount of route already traversed by the agent
. Return 0 if is_stationary(agent, model)
.
sourceAgents.OSM.same_position
— FunctionOSM.same_position(a::Tuple{Int,Int,Float64}, b::Tuple{Int,Int,Float64}, model::ABM{<:OpenStreetMapSpace})
Return true
if the given positions a
and b
are (approximately) identical
sourceAgents.OSM.same_road
— FunctionOSM.same_road(a::Tuple{Int,Int,Float64}, b::Tuple{Int,Int,Float64})
Return true
if both points lie on the same road of the graph
sourceAgents.OSM.test_map
— FunctionOSM.test_map()
Download a small test map of Göttingen as an artifact. Return a path to the downloaded file.
Using this map requires network_type = :none
to be passed as a keyword to OSMSpace
. The unit of distance used for this map is :time
.
sourceLightOSM.download_osm_network
— Functiondownload_osm_network(download_method::Symbol;
network_type::Symbol=:drive,
metadata::Bool=false,
download_format::Symbol=:json,
save_to_file_location::Union{String,Nothing}=nothing,
download_kwargs...
- )::Union{XMLDocument,Dict{String,Any}}
Downloads an OpenStreetMap network by querying with a place name, bounding box, or centroid point.
Arguments
download_method::Symbol
: Download method, choose from :place_name
, :bbox
or :point
.network_type::Symbol=:drive
: Network type filter, pick from :drive
, :drive_service
, :walk
, :bike
, :all
, :all_private
, :none
, :rail
metadata::Bool=false
: Set true to return metadata.download_format::Symbol=:json
: Download format, either :osm
, :xml
or json
.save_to_file_location::Union{String,Nothing}=nothing
: Specify a file location to save downloaded data to disk.
Required Kwargs for each Download Method
download_method=:place_name
place_name::String
: Any place name string used as a search argument to the Nominatim API.
download_method=:bbox
minlat::AbstractFloat
: Bottom left bounding box latitude coordinate.minlon::AbstractFloat
: Bottom left bounding box longitude coordinate.maxlat::AbstractFloat
: Top right bounding box latitude coordinate.maxlon::AbstractFloat
: Top right bounding box longitude coordinate.
download_method=:point
point::GeoLocation
: Centroid point to draw the bounding box around.radius::Number
: Distance (km) from centroid point to each bounding box corner.
download_method=:polygon
polygon::AbstractVector
: Vector of longitude-latitude pairs.
download_method=:custom_filters
custom_filters::String
: Filters for the query, e.g. polygon filter, highways only, traffic lights only, etc.metadata::Bool=false
: Set true to return metadata.download_format::Symbol=:json
: Download format, either :osm
, :xml
or json
.bbox::Union{Vector{AbstractFloat},Nothing}=nothing
: Optional bounding box filter.
Network Types
:drive
: Motorways excluding private and service ways.:drive_service
: Motorways including private and service ways.:walk
: Walkways only.:bike
: Cycleways only.:all
: All motorways, walkways and cycleways excluding private ways.:all_private
: All motorways, walkways and cycleways including private ways.:none
: No network filters.:rail
: Railways excluding proposed and platform.
Return
Union{XMLDocument,Dict{String,Any}}
: OpenStreetMap network data parsed as either XML or Dictionary object depending on the download method.
Nearby Agents
Agents.nearby_ids
— Functionnearby_ids(position, model::ABM, r = 1; kwargs...) → ids
Return an iterable over the IDs of the agents within distance r
(inclusive) from the given position
. The position
must match type with the spatial structure of the model
. The specification of what "distance" means depends on the space, hence it is explained in each space's documentation string. Keyword arguments are space-specific and also described in each space's documentation string.
nearby_ids
always includes IDs with 0 distance to position
.
sourcenearby_ids(agent::AbstractAgent, model::ABM, r=1)
Same as nearby_ids(agent.pos, model, r)
but the iterable excludes the given agent
's id.
sourceAgents.nearby_agents
— Functionnearby_agents(agent, model::ABM, r = 1; kwargs...) -> agent
Return an iterable of the agents near the position of the given agent
.
The value of the argument r
and possible keywords operate identically to nearby_ids
.
sourceAgents.nearby_positions
— Functionnearby_positions(position, model::ABM{<:DiscreteSpace}, r=1; kwargs...)
Return an iterable of all positions within "radius" r
of the given position
(which excludes given position
). The position
must match type with the spatial structure of the model
.
The value of r
and possible keywords operate identically to nearby_ids
.
This function only exists for discrete spaces with a finite amount of positions.
nearby_positions(position, model::ABM{<:OpenStreetMapSpace}; kwargs...) → positions
For OpenStreetMapSpace
this means "nearby intersections" and operates directly on the underlying graph of the OSM, providing the intersection nodes nearest to the given position.
sourcenearby_positions(agent::AbstractAgent, model::ABM, r=1)
Same as nearby_positions(agent.pos, model, r)
.
sourceAgents.random_nearby_id
— Functionrandom_nearby_id(agent, model::ABM, r = 1, f = nothing, alloc = false; kwargs...) → id
Return the id
of a random agent near the position of the given agent
using an optimized algorithm from Reservoir sampling. Return nothing
if no agents are nearby.
The value of the argument r
and possible keywords operate identically to nearby_ids
.
A filter function f(id)
can be passed so that to restrict the sampling on only those ids for which the function returns true
. The argument alloc
can be used if the filtering condition is expensive since in this case the allocating version can be more performant. nothing
is returned if no nearby id satisfies f
.
For discrete spaces, use random_id_in_position
instead to return a random id at a given position.
sourceAgents.random_nearby_agent
— Functionrandom_nearby_agent(agent, model::ABM, r = 1, f = nothing, alloc = false; kwargs...) → agent
Return a random agent near the position of the given agent
or nothing
if no agent is nearby.
The value of the argument r
and possible keywords operate identically to nearby_ids
.
A filter function f(agent)
can be passed so that to restrict the sampling on only those agents for which the function returns true
. The argument alloc
can be used if the filtering condition is expensive since in this case the allocating version can be more performant. nothing
is returned if no nearby agent satisfies f
.
For discrete spaces, use random_agent_in_position
instead to return a random agent at a given position.
sourceAgents.random_nearby_position
— Functionrandom_nearby_position(position, model::ABM, r=1, f = nothing, alloc = false; kwargs...) → position
Return a random position near the given position
. Return nothing
if the space doesn't allow for nearby positions.
The value of the argument r
and possible keywords operate identically to nearby_positions
.
A filter function f(pos)
can be passed so that to restrict the sampling on only those positions for which the function returns true
. The argument alloc
can be used if the filtering condition is expensive since in this case the allocating version can be more performant. nothing
is returned if no nearby position satisfies f
.
sourceA note on iteration
Most iteration in Agents.jl is dynamic and lazy, when possible, for performance reasons.
Dynamic means that when iterating over the result of e.g. the ids_in_position
function, the iterator will be affected by actions that would alter its contents. Specifically, imagine the scenario
using Agents
+ )::Union{XMLDocument,Dict{String,Any}}
Downloads an OpenStreetMap network by querying with a place name, bounding box, or centroid point.
Arguments
download_method::Symbol
: Download method, choose from :place_name
, :bbox
or :point
.network_type::Symbol=:drive
: Network type filter, pick from :drive
, :drive_service
, :walk
, :bike
, :all
, :all_private
, :none
, :rail
metadata::Bool=false
: Set true to return metadata.download_format::Symbol=:json
: Download format, either :osm
, :xml
or json
.save_to_file_location::Union{String,Nothing}=nothing
: Specify a file location to save downloaded data to disk.
Required Kwargs for each Download Method
download_method=:place_name
place_name::String
: Any place name string used as a search argument to the Nominatim API.
download_method=:bbox
minlat::AbstractFloat
: Bottom left bounding box latitude coordinate.minlon::AbstractFloat
: Bottom left bounding box longitude coordinate.maxlat::AbstractFloat
: Top right bounding box latitude coordinate.maxlon::AbstractFloat
: Top right bounding box longitude coordinate.
download_method=:point
point::GeoLocation
: Centroid point to draw the bounding box around.radius::Number
: Distance (km) from centroid point to each bounding box corner.
download_method=:polygon
polygon::AbstractVector
: Vector of longitude-latitude pairs.
download_method=:custom_filters
custom_filters::String
: Filters for the query, e.g. polygon filter, highways only, traffic lights only, etc.metadata::Bool=false
: Set true to return metadata.download_format::Symbol=:json
: Download format, either :osm
, :xml
or json
.bbox::Union{Vector{AbstractFloat},Nothing}=nothing
: Optional bounding box filter.
Network Types
:drive
: Motorways excluding private and service ways.:drive_service
: Motorways including private and service ways.:walk
: Walkways only.:bike
: Cycleways only.:all
: All motorways, walkways and cycleways excluding private ways.:all_private
: All motorways, walkways and cycleways including private ways.:none
: No network filters.:rail
: Railways excluding proposed and platform.
Return
Union{XMLDocument,Dict{String,Any}}
: OpenStreetMap network data parsed as either XML or Dictionary object depending on the download method.
Nearby Agents
Agents.nearby_ids
— Functionnearby_ids(position, model::ABM, r = 1; kwargs...) → ids
Return an iterable over the IDs of the agents within distance r
(inclusive) from the given position
. The position
must match type with the spatial structure of the model
. The specification of what "distance" means depends on the space, hence it is explained in each space's documentation string. Keyword arguments are space-specific and also described in each space's documentation string.
nearby_ids
always includes IDs with 0 distance to position
.
sourcenearby_ids(agent::AbstractAgent, model::ABM, r=1)
Same as nearby_ids(agent.pos, model, r)
but the iterable excludes the given agent
's id.
sourceAgents.nearby_agents
— Functionnearby_agents(agent, model::ABM, r = 1; kwargs...) -> agent
Return an iterable of the agents near the position of the given agent
.
The value of the argument r
and possible keywords operate identically to nearby_ids
.
sourceAgents.nearby_positions
— Functionnearby_positions(position, model::ABM{<:DiscreteSpace}, r=1; kwargs...)
Return an iterable of all positions within "radius" r
of the given position
(which excludes given position
). The position
must match type with the spatial structure of the model
.
The value of r
and possible keywords operate identically to nearby_ids
.
This function only exists for discrete spaces with a finite amount of positions.
nearby_positions(position, model::ABM{<:OpenStreetMapSpace}; kwargs...) → positions
For OpenStreetMapSpace
this means "nearby intersections" and operates directly on the underlying graph of the OSM, providing the intersection nodes nearest to the given position.
sourcenearby_positions(agent::AbstractAgent, model::ABM, r=1)
Same as nearby_positions(agent.pos, model, r)
.
sourceAgents.random_nearby_id
— Functionrandom_nearby_id(agent, model::ABM, r = 1, f = nothing, alloc = false; kwargs...) → id
Return the id
of a random agent near the position of the given agent
using an optimized algorithm from Reservoir sampling. Return nothing
if no agents are nearby.
The value of the argument r
and possible keywords operate identically to nearby_ids
.
A filter function f(id)
can be passed so that to restrict the sampling on only those ids for which the function returns true
. The argument alloc
can be used if the filtering condition is expensive since in this case the allocating version can be more performant. nothing
is returned if no nearby id satisfies f
.
For discrete spaces, use random_id_in_position
instead to return a random id at a given position.
sourceAgents.random_nearby_agent
— Functionrandom_nearby_agent(agent, model::ABM, r = 1, f = nothing, alloc = false; kwargs...) → agent
Return a random agent near the position of the given agent
or nothing
if no agent is nearby.
The value of the argument r
and possible keywords operate identically to nearby_ids
.
A filter function f(agent)
can be passed so that to restrict the sampling on only those agents for which the function returns true
. The argument alloc
can be used if the filtering condition is expensive since in this case the allocating version can be more performant. nothing
is returned if no nearby agent satisfies f
.
For discrete spaces, use random_agent_in_position
instead to return a random agent at a given position.
sourceAgents.random_nearby_position
— Functionrandom_nearby_position(position, model::ABM, r=1, f = nothing, alloc = false; kwargs...) → position
Return a random position near the given position
. Return nothing
if the space doesn't allow for nearby positions.
The value of the argument r
and possible keywords operate identically to nearby_positions
.
A filter function f(pos)
can be passed so that to restrict the sampling on only those positions for which the function returns true
. The argument alloc
can be used if the filtering condition is expensive since in this case the allocating version can be more performant. nothing
is returned if no nearby position satisfies f
.
sourceA note on iteration
Most iteration in Agents.jl is dynamic and lazy, when possible, for performance reasons.
Dynamic means that when iterating over the result of e.g. the ids_in_position
function, the iterator will be affected by actions that would alter its contents. Specifically, imagine the scenario
using Agents
# We don't need to make a new agent type here,
# we use the minimal agent for 4-dimensional grid spaces
model = ABM(GridAgent{4}, GridSpace((5, 5, 5, 5)))
@@ -61,9 +61,9 @@
3
You will notice that only 1 agent was removed. This is simply because the final state of the iteration of ids_in_position
was reached unnaturally, because the length of its output was reduced by 1 during iteration. To avoid problems like these, you need to collect
the iterator to have a non dynamic version.
Lazy means that when possible the outputs of the iteration are not collected and instead are generated on the fly. A good example to illustrate this is nearby_ids
, where doing something like
a = random_agent(model)
sort!(nearby_ids(random_agent(model), model))
leads to error, since you cannot sort!
the returned iterator. This can be easily solved by adding a collect
in between:
a = random_agent(model)
sort!(collect(nearby_agents(a, model)))
1-element Vector{GridAgent{4}}:
- GridAgent{4}(3, (2, 1, 1, 1))
Higher-order interactions
There may be times when pair-wise, triplet-wise or higher interactions need to be accounted for across most or all of the model's agent population. The following methods provide an interface for such calculation.
These methods follow the conventions outlined above in A note on iteration.
Agents.iter_agent_groups
— Functioniter_agent_groups(order::Int, model::ABM; scheduler = Schedulers.by_id)
Return an iterator over all agents of the model, grouped by order. When order = 2
, the iterator returns agent pairs, e.g (agent1, agent2)
and when order = 3
: agent triples, e.g. (agent1, agent7, agent8)
. order
must be larger than 1
but has no upper bound.
Index order is provided by the model scheduler by default, but can be altered with the scheduler
keyword.
sourceAgents.map_agent_groups
— Functionmap_agent_groups(order::Int, f::Function, model::ABM; kwargs...)
-map_agent_groups(order::Int, f::Function, model::ABM, filter::Function; kwargs...)
Applies function f
to all grouped agents of an iter_agent_groups
iterator. kwargs
are passed to the iterator method. f
must take the form f(NTuple{O,AgentType})
, where the dimension O
is equal to order
.
Optionally, a filter
function that accepts an iterable and returns a Bool
can be applied to remove unwanted matches from the results. Note: This option cannot keep matrix order, so should be used in conjunction with index_mapped_groups
to associate agent ids with the resultant data.
sourceAgents.index_mapped_groups
— Functionindex_mapped_groups(order::Int, model::ABM; scheduler = Schedulers.by_id)
-index_mapped_groups(order::Int, model::ABM, filter::Function; scheduler = Schedulers.by_id)
Return an iterable of agent ids in the model, meeting the filter
criteria if used.
sourceMinimal agent types
The @agent
macro can be used to define new agent types from the minimal agent types that are listed below:
Agents.NoSpaceAgent
— TypeNoSpaceAgent <: AbstractAgent
The minimal agent struct for usage with nothing
as space (i.e., no space). It has the field id::Int
, and potentially other internal fields that are not documented as part of the public API. See also @agent
.
sourceAgents.GraphAgent
— TypeGraphAgent <: AbstractAgent
The minimal agent struct for usage with GraphSpace
. It has an additional pos::Int
field. See also @agent
.
sourceAgents.GridAgent
— TypeGridAgent{D} <: AbstractAgent
The minimal agent struct for usage with D
-dimensional GridSpace
. It has an additional pos::NTuple{D,Int}
field. See also @agent
.
sourceAgents.ContinuousAgent
— TypeContinuousAgent{D,T} <: AbstractAgent
The minimal agent struct for usage with D
-dimensional ContinuousSpace
. It has the additional fields pos::SVector{D,T}, vel::SVector{D,T}
where T
can be any AbstractFloat
type. See also @agent
.
sourceAgents.OSMAgent
— TypeOSMAgent <: AbstractAgent
The minimal agent struct for usage with OpenStreetMapSpace
. It has an additional field pos::Tuple{Int,Int,Float64}
. See also @agent
.
sourceParameter scanning
Agents.paramscan
— Functionparamscan(parameters::AbstractDict, initialize; kwargs...) → adf, mdf
Perform a parameter scan of an ABM simulation output by collecting data from all parameter combinations into dataframes (one for agent data, one for model data). The dataframes columns are both the collected data (as in run!
) but also the input parameter values used.
parameters
is a dictionary with key type Symbol
. Each entry of the dictionary maps a parameter key to the parameter values that should be scanned over (or to a single parameter value that will remain constant throughout the scans). The approach regarding parameters
is as follows:
- If the value of a specific key is a
Vector
, all values of the vector are expended as values for the parameter to scan over. - If the value of a specific key is not a
Vector
, it is assumed that whatever this value is, it corresponds to a single and constant parameter value and therefore it is not expanded or scanned over.
This is done so that parameter values that are inherently iterable (such as a String
) are not wrongly expanded into their constituents. (if the value of a parameter is itself a Vector
, then you need to pass in a vector of vectors to scan the parameter)
The second argument initialize
is a function that creates an ABM and returns it. It must accept keyword arguments which are the keys of the parameters
dictionary. Since the user decides how to use input arguments to make an ABM, parameters
can be used to affect model properties, space type and creation as well as agent properties, see the example below.
Keywords
The following keywords modify the paramscan
function:
include_constants::Bool = false
: by default, only the varying parameters (Vector
values in parameters
) will be included in the output DataFrame
. If true
, constant parameters (non-Vector in parameters
) will also be included.parallel::Bool = false
whether Distributed.pmap
is invoked to run simulations in parallel. This must be used in conjunction with @everywhere
(see Performance Tips).showprogress::Bool = false
whether a progressbar will be displayed to indicate % runs finished.
All other keywords are propagated into run!
. Furthermore, agent_step!, model_step!, n
are also keywords here, that are given to run!
as arguments. Naturally, stepping functions and the number of time steps (agent_step!
, model_step!
, and n
) and at least one of adata, mdata
are mandatory. The adata, mdata
lists shouldn't contain the parameters that are already in the parameters
dictionary to avoid duplication.
Example
A runnable example that uses paramscan
is shown in Schelling's segregation model. There, we define
function initialize(; numagents = 320, griddims = (20, 20), min_to_be_happy = 3)
+ GridAgent{4}(2, (1, 1, 1, 1))
Higher-order interactions
There may be times when pair-wise, triplet-wise or higher interactions need to be accounted for across most or all of the model's agent population. The following methods provide an interface for such calculation.
These methods follow the conventions outlined above in A note on iteration.
Agents.iter_agent_groups
— Functioniter_agent_groups(order::Int, model::ABM; scheduler = Schedulers.by_id)
Return an iterator over all agents of the model, grouped by order. When order = 2
, the iterator returns agent pairs, e.g (agent1, agent2)
and when order = 3
: agent triples, e.g. (agent1, agent7, agent8)
. order
must be larger than 1
but has no upper bound.
Index order is provided by the model scheduler by default, but can be altered with the scheduler
keyword.
sourceAgents.map_agent_groups
— Functionmap_agent_groups(order::Int, f::Function, model::ABM; kwargs...)
+map_agent_groups(order::Int, f::Function, model::ABM, filter::Function; kwargs...)
Applies function f
to all grouped agents of an iter_agent_groups
iterator. kwargs
are passed to the iterator method. f
must take the form f(NTuple{O,AgentType})
, where the dimension O
is equal to order
.
Optionally, a filter
function that accepts an iterable and returns a Bool
can be applied to remove unwanted matches from the results. Note: This option cannot keep matrix order, so should be used in conjunction with index_mapped_groups
to associate agent ids with the resultant data.
sourceAgents.index_mapped_groups
— Functionindex_mapped_groups(order::Int, model::ABM; scheduler = Schedulers.by_id)
+index_mapped_groups(order::Int, model::ABM, filter::Function; scheduler = Schedulers.by_id)
Return an iterable of agent ids in the model, meeting the filter
criteria if used.
sourceMinimal agent types
The @agent
macro can be used to define new agent types from the minimal agent types that are listed below:
Agents.NoSpaceAgent
— TypeNoSpaceAgent <: AbstractAgent
The minimal agent struct for usage with nothing
as space (i.e., no space). It has the field id::Int
, and potentially other internal fields that are not documented as part of the public API. See also @agent
.
sourceAgents.GraphAgent
— TypeGraphAgent <: AbstractAgent
The minimal agent struct for usage with GraphSpace
. It has an additional pos::Int
field. See also @agent
.
sourceAgents.GridAgent
— TypeGridAgent{D} <: AbstractAgent
The minimal agent struct for usage with D
-dimensional GridSpace
. It has an additional pos::NTuple{D,Int}
field. See also @agent
.
sourceAgents.ContinuousAgent
— TypeContinuousAgent{D,T} <: AbstractAgent
The minimal agent struct for usage with D
-dimensional ContinuousSpace
. It has the additional fields pos::SVector{D,T}, vel::SVector{D,T}
where T
can be any AbstractFloat
type. See also @agent
.
sourceAgents.OSMAgent
— TypeOSMAgent <: AbstractAgent
The minimal agent struct for usage with OpenStreetMapSpace
. It has an additional field pos::Tuple{Int,Int,Float64}
. See also @agent
.
sourceParameter scanning
Agents.paramscan
— Functionparamscan(parameters::AbstractDict, initialize; kwargs...) → adf, mdf
Perform a parameter scan of an ABM simulation output by collecting data from all parameter combinations into dataframes (one for agent data, one for model data). The dataframes columns are both the collected data (as in run!
) but also the input parameter values used.
parameters
is a dictionary with key type Symbol
. Each entry of the dictionary maps a parameter key to the parameter values that should be scanned over (or to a single parameter value that will remain constant throughout the scans). The approach regarding parameters
is as follows:
- If the value of a specific key is a
Vector
, all values of the vector are expended as values for the parameter to scan over. - If the value of a specific key is not a
Vector
, it is assumed that whatever this value is, it corresponds to a single and constant parameter value and therefore it is not expanded or scanned over.
This is done so that parameter values that are inherently iterable (such as a String
) are not wrongly expanded into their constituents. (if the value of a parameter is itself a Vector
, then you need to pass in a vector of vectors to scan the parameter)
The second argument initialize
is a function that creates an ABM and returns it. It must accept keyword arguments which are the keys of the parameters
dictionary. Since the user decides how to use input arguments to make an ABM, parameters
can be used to affect model properties, space type and creation as well as agent properties, see the example below.
Keywords
The following keywords modify the paramscan
function:
include_constants::Bool = false
: by default, only the varying parameters (Vector
values in parameters
) will be included in the output DataFrame
. If true
, constant parameters (non-Vector in parameters
) will also be included.parallel::Bool = false
whether Distributed.pmap
is invoked to run simulations in parallel. This must be used in conjunction with @everywhere
(see Performance Tips).showprogress::Bool = false
whether a progressbar will be displayed to indicate % runs finished.
All other keywords are propagated into run!
. Furthermore, agent_step!, model_step!, n
are also keywords here, that are given to run!
as arguments. Naturally, stepping functions and the number of time steps (agent_step!
, model_step!
, and n
) and at least one of adata, mdata
are mandatory. The adata, mdata
lists shouldn't contain the parameters that are already in the parameters
dictionary to avoid duplication.
Example
A runnable example that uses paramscan
is shown in Schelling's segregation model. There, we define
function initialize(; numagents = 320, griddims = (20, 20), min_to_be_happy = 3)
space = GridSpaceSingle(griddims, periodic = false)
properties = Dict(:min_to_be_happy => min_to_be_happy)
model = ABM(SchellingAgent, space;
@@ -81,7 +81,7 @@
:griddims => (20, 20), # not Vector = not expanded
)
-adf, _ = paramscan(parameters, initialize; adata, agent_step!, n = 3)
sourceData collection
The central simulation function is run!
, which is mentioned in our Tutorial. But there are other functions that are related to simulations listed here. Specifically, these functions aid in making custom data collection loops, instead of using the run!
function.
For example, the core loop of run!
is just
df_agent = init_agent_dataframe(model, adata)
+adf, _ = paramscan(parameters, initialize; adata, agent_step!, n = 3)
sourceData collection
The central simulation function is run!
, which is mentioned in our Tutorial. But there are other functions that are related to simulations listed here. Specifically, these functions aid in making custom data collection loops, instead of using the run!
function.
For example, the core loop of run!
is just
df_agent = init_agent_dataframe(model, adata)
df_model = init_model_dataframe(model, mdata)
s = 0
@@ -95,7 +95,7 @@
step!(model, agent_step!, model_step!, 1)
s += 1
end
-return df_agent, df_model
(here until
and should_we_collect
are internal functions)
run!
uses the following functions:
Agents.init_agent_dataframe
— Functioninit_agent_dataframe(model, adata) → agent_df
Initialize a dataframe to add data later with collect_agent_data!
.
sourceAgents.collect_agent_data!
— Functioncollect_agent_data!(df, model, properties, step = 0; obtainer = identity)
Collect and add agent data into df
(see run!
for the dispatch rules of properties
and obtainer
). step
is given because the step number information is not known.
sourceAgents.init_model_dataframe
— Functioninit_model_dataframe(model, mdata) → model_df
Initialize a dataframe to add data later with collect_model_data!
. mdata
can be a Vector
or generator Function
.
sourceAgents.collect_model_data!
— Functioncollect_model_data!(df, model, properties, step = 0, obtainer = identity)
Same as collect_agent_data!
but for model data instead. properties
can be a Vector
or generator Function
.
sourceAgents.dataname
— Functiondataname(k) → name
Return the name of the column of the i
-th collected data where k = adata[i]
(or mdata[i]
). dataname
also accepts tuples with aggregate and conditional values.
sourceSchedulers
Agents.Schedulers
— ModuleSchedulers
Submodule containing all predefined schedulers of Agents.jl and the scheduling API. Schedulers have a very simple interface. They are functions that take as an input the ABM and return an iterator over agent IDs. Notice that this iterator can be a "true" iterator (non-allocated) or can be just a standard vector of IDs. You can define your own scheduler according to this API and use it when making an AgentBasedModel
. You can also use the function schedule(model)
to obtain the scheduled ID list, if you prefer to write your own step!
-like loop.
See also Advanced scheduling for making more advanced schedulers.
Notice that schedulers can be given directly to model creation, and thus become the "default" scheduler a model uses, but they can just as easily be incorporated in a model_step!
function as shown in Advanced stepping. The scheduler
that is stored in the model is only meaningful if an agent-stepping function is defined for step!
or run!
, otherwise a user decides a scheduler in the model-stepping function, as illustrated in the Advanced stepping part of the tutorial.
sourcePredefined schedulers
Some useful schedulers are available below as part of the Agents.jl API:
Agents.Schedulers.fastest
— FunctionSchedulers.fastest
A scheduler that activates all agents once per step in the order dictated by the agent's container, which is arbitrary (the keys sequence of a dictionary). This is the fastest way to activate all agents once per step.
sourceAgents.Schedulers.ByID
— TypeSchedulers.ByID()
A non-allocating scheduler that activates all agents at each step according to their id.
sourceAgents.Schedulers.Randomly
— TypeSchedulers.Randomly()
A non-allocating scheduler that activates all agents once per step in a random order. Different random ordering is used at each different step.
sourceAgents.Schedulers.Partially
— TypeSchedulers.Partially(p)
A non-allocating scheduler that at each step activates only p
percentage of randomly chosen agents.
sourceAgents.Schedulers.ByProperty
— TypeSchedulers.ByProperty(property)
A non-allocating scheduler that at each step activates the agents in an order dictated by their property
, with agents with greater property
acting first. property
can be a Symbol
, which just dictates which field of the agents to compare, or a function which inputs an agent and outputs a real number.
sourceAgents.Schedulers.ByType
— TypeSchedulers.ByType(shuffle_types::Bool, shuffle_agents::Bool, agent_union)
A non-allocating scheduler useful only for mixed agent models using Union
types.
- Setting
shuffle_types = true
groups by agent type, but randomizes the type order.
Otherwise returns agents grouped in order of appearance in the Union
.
shuffle_agents = true
randomizes the order of agents within each group, false
returns
the default order of the container (equivalent to Schedulers.fastest
).
agent_union
is a Union
of all valid agent types (as passed to ABM
)
sourceSchedulers.ByType((C, B, A), shuffle_agents::Bool)
A non-allocating scheduler that activates agents by type in specified order (since Union
s are not order preserving). shuffle_agents = true
randomizes the order of agents within each group.
sourceAdvanced scheduling
You can use Function-like objects to make your scheduling possible of arbitrary events. For example, imagine that after the n
-th step of your simulation you want to fundamentally change the order of agents. To achieve this you can define
mutable struct MyScheduler
+return df_agent, df_model
(here until
and should_we_collect
are internal functions)
run!
uses the following functions:
Agents.init_agent_dataframe
— Functioninit_agent_dataframe(model, adata) → agent_df
Initialize a dataframe to add data later with collect_agent_data!
.
sourceAgents.collect_agent_data!
— Functioncollect_agent_data!(df, model, properties, step = 0; obtainer = identity)
Collect and add agent data into df
(see run!
for the dispatch rules of properties
and obtainer
). step
is given because the step number information is not known.
sourceAgents.init_model_dataframe
— Functioninit_model_dataframe(model, mdata) → model_df
Initialize a dataframe to add data later with collect_model_data!
. mdata
can be a Vector
or generator Function
.
sourceAgents.collect_model_data!
— Functioncollect_model_data!(df, model, properties, step = 0, obtainer = identity)
Same as collect_agent_data!
but for model data instead. properties
can be a Vector
or generator Function
.
sourceAgents.dataname
— Functiondataname(k) → name
Return the name of the column of the i
-th collected data where k = adata[i]
(or mdata[i]
). dataname
also accepts tuples with aggregate and conditional values.
sourceSchedulers
Agents.Schedulers
— ModuleSchedulers
Submodule containing all predefined schedulers of Agents.jl and the scheduling API. Schedulers have a very simple interface. They are functions that take as an input the ABM and return an iterator over agent IDs. Notice that this iterator can be a "true" iterator (non-allocated) or can be just a standard vector of IDs. You can define your own scheduler according to this API and use it when making an AgentBasedModel
. You can also use the function schedule(model)
to obtain the scheduled ID list, if you prefer to write your own step!
-like loop.
See also Advanced scheduling for making more advanced schedulers.
Notice that schedulers can be given directly to model creation, and thus become the "default" scheduler a model uses, but they can just as easily be incorporated in a model_step!
function as shown in Advanced stepping. The scheduler
that is stored in the model is only meaningful if an agent-stepping function is defined for step!
or run!
, otherwise a user decides a scheduler in the model-stepping function, as illustrated in the Advanced stepping part of the tutorial.
sourcePredefined schedulers
Some useful schedulers are available below as part of the Agents.jl API:
Agents.Schedulers.fastest
— FunctionSchedulers.fastest
A scheduler that activates all agents once per step in the order dictated by the agent's container, which is arbitrary (the keys sequence of a dictionary). This is the fastest way to activate all agents once per step.
sourceAgents.Schedulers.ByID
— TypeSchedulers.ByID()
A non-allocating scheduler that activates all agents at each step according to their id.
sourceAgents.Schedulers.Randomly
— TypeSchedulers.Randomly()
A non-allocating scheduler that activates all agents once per step in a random order. Different random ordering is used at each different step.
sourceAgents.Schedulers.Partially
— TypeSchedulers.Partially(p)
A non-allocating scheduler that at each step activates only p
percentage of randomly chosen agents.
sourceAgents.Schedulers.ByProperty
— TypeSchedulers.ByProperty(property)
A non-allocating scheduler that at each step activates the agents in an order dictated by their property
, with agents with greater property
acting first. property
can be a Symbol
, which just dictates which field of the agents to compare, or a function which inputs an agent and outputs a real number.
sourceAgents.Schedulers.ByType
— TypeSchedulers.ByType(shuffle_types::Bool, shuffle_agents::Bool, agent_union)
A non-allocating scheduler useful only for mixed agent models using Union
types.
- Setting
shuffle_types = true
groups by agent type, but randomizes the type order.
Otherwise returns agents grouped in order of appearance in the Union
.
shuffle_agents = true
randomizes the order of agents within each group, false
returns
the default order of the container (equivalent to Schedulers.fastest
).
agent_union
is a Union
of all valid agent types (as passed to ABM
)
sourceSchedulers.ByType((C, B, A), shuffle_agents::Bool)
A non-allocating scheduler that activates agents by type in specified order (since Union
s are not order preserving). shuffle_agents = true
randomizes the order of agents within each group.
sourceAdvanced scheduling
You can use Function-like objects to make your scheduling possible of arbitrary events. For example, imagine that after the n
-th step of your simulation you want to fundamentally change the order of agents. To achieve this you can define
mutable struct MyScheduler
n::Int # step number
w::Float64
end
and then define a calling method for it like so
function (ms::MyScheduler)(model::ABM)
@@ -110,7 +110,7 @@
return ids
end
end
and pass it to e.g. step!
by initializing it
ms = MyScheduler(100, 0.5)
-step!(model, agentstep, modelstep, 100; scheduler = ms)
Ensemble runs and Parallelization
Agents.ensemblerun!
— Functionensemblerun!(models::Vector, agent_step!, model_step!, n; kwargs...)
Perform an ensemble simulation of run!
for all model ∈ models
. Each model
should be a (different) instance of an AgentBasedModel
but probably initialized with a different random seed or different initial agent distribution. All models obey the same rules agent_step!, model_step!
and are evolved for n
.
Similarly to run!
this function will collect data. It will furthermore add one additional column to the dataframe called :ensemble
, which has an integer value counting the ensemble member. The function returns agent_df, model_df, models
.
If you want to scan parameters and at the same time run multiple simulations at each parameter combination, simply use seed
as a parameter, and use that parameter to tune the model's initial random seed and/or agent distribution.
See example usage in Schelling's segregation model.
Keywords
The following keywords modify the ensemblerun!
function:
parallel::Bool = false
whether Distributed.pmap
is invoked to run simulations in parallel. This must be used in conjunction with @everywhere
(see Performance Tips).showprogress::Bool = false
whether a progressbar will be displayed to indicate % runs finished.
All other keywords are propagated to run!
as-is.
sourceensemblerun!(generator, agent_step!, model_step!, n; kwargs...)
Generate many ABM
s and propagate them into ensemblerun!(models, ...)
using the provided generator
which is a one-argument function whose input is a seed.
This method has additional keywords ensemble = 5, seeds = rand(UInt32, ensemble)
.
sourceHow to use Distributed
To use the parallel=true
option of ensemblerun!
you need to load Agents
and define your fundamental types at all processors. How to do this is shown in Ensembles and distributed computing section of Schelling's Segregation Model example. See also the Performance Tips page for parallelization.
Path-finding
Agents.Pathfinding
— ModulePathfinding
Submodule containing functionality for path-finding based on the A* algorithm. Currently available for GridSpace
and ContinuousSpace
. Discretization of ContinuousSpace
is taken care of internally.
You can enable path-finding and set its options by creating an instance of a Pathfinding.AStar
struct. This must be passed to the relevant pathfinding functions during the simulation. Call plan_route!
to set the destination for an agent. This triggers the algorithm to calculate a path from the agent's current position to the one specified. You can alternatively use plan_best_route!
to choose the best target from a list. Once a target has been set, you can move an agent one step along its precalculated path using the move_along_route!
function.
Refer to the Maze Solver, Mountain Runners and Rabbit, Fox, Hawk examples using path-finding and see the available functions below as well.
sourceAgents.Pathfinding.AStar
— TypePathfinding.AStar(space; kwargs...)
Enables pathfinding for agents in the provided space
(which can be a GridSpace
or ContinuousSpace
) using the A* algorithm. This struct must be passed into any pathfinding functions.
For ContinuousSpace
, a walkmap or instance of PenaltyMap
must be provided to specify the level of discretisation of the space.
Keywords
diagonal_movement = true
specifies if movement can be to diagonal neighbors of a tile, or only orthogonal neighbors. Only available for GridSpace
admissibility = 0.0
allows the algorithm to approximate paths to speed up pathfinding. A value of admissibility
allows paths with at most (1+admissibility)
times the optimal length.walkmap = trues(size(space))
specifies the (un)walkable positions of the space. If specified, it should be a BitArray
of the same size as the corresponding GridSpace
. By default, agents can walk anywhere in the space.cost_metric = DirectDistance{D}()
is an instance of a cost metric and specifies the metric used to approximate the distance between any two points.
Utilization of all features of AStar
occurs in the 3D Mixed-Agent Ecosystem with Pathfinding example.
sourceAgents.Pathfinding.penaltymap
— FunctionPathfinding.penaltymap(pathfinder)
Return the penalty map of a Pathfinding.AStar
if the Pathfinding.PenaltyMap
metric is in use, nothing
otherwise.
It is possible to mutate the map directly, for example Pathfinding.penaltymap(pathfinder)[15, 40] = 115
or Pathfinding.penaltymap(pathfinder) .= rand(50, 50)
. If this is mutated, a new path needs to be planned using plan_route!
.
sourceAgents.Pathfinding.nearby_walkable
— FunctionPathfinding.nearby_walkable(position, model::ABM{<:GridSpace{D}}, pathfinder::AStar{D}, r = 1)
Return an iterator over all nearby_positions
within "radius" r
of the given position
(excluding position
), which are walkable as specified by the given pathfinder
.
sourceAgents.Pathfinding.random_walkable
— FunctionPathfinding.random_walkable(model, pathfinder::AStar{D})
Return a random position in the given model
that is walkable as specified by the given pathfinder
.
sourcePathfinding.random_walkable(pos, model::ABM{<:ContinuousSpace{D}}, pathfinder::AStar{D}, r = 1.0)
Return a random position within radius r
of pos
which is walkable, as specified by pathfinder
. Return pos
if no such position exists.
sourcePathfinding Metrics
Agents.Pathfinding.DirectDistance
— TypePathfinding.DirectDistance{D}([direction_costs::Vector{Int}]) <: CostMetric{D}
Distance is approximated as the shortest path between the two points, provided the walkable
property of Pathfinding.AStar
allows. Optionally provide a Vector{Int}
that represents the cost of going from a tile to the neighboring tile on the i
dimensional diagonal (default is 10√i
).
If diagonal_movement=false
in Pathfinding.AStar
, neighbors in diagonal positions will be excluded. Cost defaults to the first value of the provided vector.
sourceAgents.Pathfinding.MaxDistance
— TypePathfinding.MaxDistance{D}() <: CostMetric{D}
Distance between two tiles is approximated as the maximum of absolute difference in coordinates between them.
sourceAgents.Pathfinding.PenaltyMap
— TypePathfinding.PenaltyMap(pmap::Array{Int,D} [, base_metric::CostMetric]) <: CostMetric{D}
Distance between two positions is the sum of the shortest distance between them and the absolute difference in penalty.
A penalty map (pmap
) is required. For pathfinding in GridSpace
, this should be the same dimensions as the space. For pathfinding in ContinuousSpace
, the size of this map determines the granularity of the underlying grid, and should agree with the size of the walkable
map.
Distance is calculated using Pathfinding.DirectDistance
by default, and can be changed by specifying base_metric
.
An example usage can be found in Mountain Runners.
sourceBuilding a custom metric is straightforward, if the provided ones do not suit your purpose. See the Developer Docs for details.
Save, Load, Checkpoints
There may be scenarios where interacting with data in the form of files is necessary. The following functions provide an interface to save/load data to/from files.
Agents.AgentsIO.save_checkpoint
— FunctionAgentsIO.save_checkpoint(filename, model::ABM)
Write the entire model
to file specified by filename
. The following points should be considered before using this functionality:
- OpenStreetMap data is not saved. The path to the map should be specified when loading the model using the
map
keyword of AgentsIO.load_checkpoint
. - Functions are not saved, including stepping functions, schedulers, and
update_vel!
. The last two can be provided to AgentsIO.load_checkpoint
using the appropriate keyword arguments.
sourceAgents.AgentsIO.load_checkpoint
— FunctionAgentsIO.load_checkpoint(filename; kwargs...)
Load the model saved to the file specified by filename
.
Keywords
scheduler = Schedulers.fastest
specifies what scheduler should be used for the model.warn = true
can be used to disable warnings from type checks on the agent type.
ContinuousSpace
specific:
update_vel!
specifies a function that should be used to update each agent's velocity before it is moved. Refer to ContinuousSpace
for details.
OpenStreetMapSpace
specific:
map
is a path to the OpenStreetMap to be used for the space. This is a required parameter if the space is OpenStreetMapSpace
.use_cache = false
, trim_to_connected_graph = true
refer to OpenStreetMapSpace
sourceAgents.AgentsIO.populate_from_csv!
— FunctionAgentsIO.populate_from_csv!(model, filename [, agent_type, col_map]; row_number_is_id, kwargs...)
Populate the given model
using CSV data contained in filename
. Use agent_type
to specify the type of agent to create (In the case of multi-agent models) or a function that returns an agent to add to the model. The CSV row is splatted into the agent_type
constructor/function.
col_map
is a Dict{Symbol,Int}
specifying a mapping of keyword-arguments to row number. If col_map
is specified, the specified data is splatted as keyword arguments.
The keyword row_number_is_id = false
specifies whether the row number will be passed as the first argument (or as id
keyword) to agent_type
.
Any other keyword arguments are forwarded to CSV.Rows
. If the types
keyword is not specified and agent_type
is a struct, then the mapping from struct field to type will be used. Tuple{...}
fields will be suffixed with _1
, _2
, ... similarly to AgentsIO.dump_to_csv
For example,
struct Foo <: AbstractAgent
+step!(model, agentstep, modelstep, 100; scheduler = ms)
Ensemble runs and Parallelization
Agents.ensemblerun!
— Functionensemblerun!(models::Vector, agent_step!, model_step!, n; kwargs...)
Perform an ensemble simulation of run!
for all model ∈ models
. Each model
should be a (different) instance of an AgentBasedModel
but probably initialized with a different random seed or different initial agent distribution. All models obey the same rules agent_step!, model_step!
and are evolved for n
.
Similarly to run!
this function will collect data. It will furthermore add one additional column to the dataframe called :ensemble
, which has an integer value counting the ensemble member. The function returns agent_df, model_df, models
.
If you want to scan parameters and at the same time run multiple simulations at each parameter combination, simply use seed
as a parameter, and use that parameter to tune the model's initial random seed and/or agent distribution.
See example usage in Schelling's segregation model.
Keywords
The following keywords modify the ensemblerun!
function:
parallel::Bool = false
whether Distributed.pmap
is invoked to run simulations in parallel. This must be used in conjunction with @everywhere
(see Performance Tips).showprogress::Bool = false
whether a progressbar will be displayed to indicate % runs finished.
All other keywords are propagated to run!
as-is.
sourceensemblerun!(generator, agent_step!, model_step!, n; kwargs...)
Generate many ABM
s and propagate them into ensemblerun!(models, ...)
using the provided generator
which is a one-argument function whose input is a seed.
This method has additional keywords ensemble = 5, seeds = rand(UInt32, ensemble)
.
sourceHow to use Distributed
To use the parallel=true
option of ensemblerun!
you need to load Agents
and define your fundamental types at all processors. How to do this is shown in Ensembles and distributed computing section of Schelling's Segregation Model example. See also the Performance Tips page for parallelization.
Path-finding
Agents.Pathfinding
— ModulePathfinding
Submodule containing functionality for path-finding based on the A* algorithm. Currently available for GridSpace
and ContinuousSpace
. Discretization of ContinuousSpace
is taken care of internally.
You can enable path-finding and set its options by creating an instance of a Pathfinding.AStar
struct. This must be passed to the relevant pathfinding functions during the simulation. Call plan_route!
to set the destination for an agent. This triggers the algorithm to calculate a path from the agent's current position to the one specified. You can alternatively use plan_best_route!
to choose the best target from a list. Once a target has been set, you can move an agent one step along its precalculated path using the move_along_route!
function.
Refer to the Maze Solver, Mountain Runners and Rabbit, Fox, Hawk examples using path-finding and see the available functions below as well.
sourceAgents.Pathfinding.AStar
— TypePathfinding.AStar(space; kwargs...)
Enables pathfinding for agents in the provided space
(which can be a GridSpace
or ContinuousSpace
) using the A* algorithm. This struct must be passed into any pathfinding functions.
For ContinuousSpace
, a walkmap or instance of PenaltyMap
must be provided to specify the level of discretisation of the space.
Keywords
diagonal_movement = true
specifies if movement can be to diagonal neighbors of a tile, or only orthogonal neighbors. Only available for GridSpace
admissibility = 0.0
allows the algorithm to approximate paths to speed up pathfinding. A value of admissibility
allows paths with at most (1+admissibility)
times the optimal length.walkmap = trues(size(space))
specifies the (un)walkable positions of the space. If specified, it should be a BitArray
of the same size as the corresponding GridSpace
. By default, agents can walk anywhere in the space.cost_metric = DirectDistance{D}()
is an instance of a cost metric and specifies the metric used to approximate the distance between any two points.
Utilization of all features of AStar
occurs in the 3D Mixed-Agent Ecosystem with Pathfinding example.
sourceAgents.Pathfinding.penaltymap
— FunctionPathfinding.penaltymap(pathfinder)
Return the penalty map of a Pathfinding.AStar
if the Pathfinding.PenaltyMap
metric is in use, nothing
otherwise.
It is possible to mutate the map directly, for example Pathfinding.penaltymap(pathfinder)[15, 40] = 115
or Pathfinding.penaltymap(pathfinder) .= rand(50, 50)
. If this is mutated, a new path needs to be planned using plan_route!
.
sourceAgents.Pathfinding.nearby_walkable
— FunctionPathfinding.nearby_walkable(position, model::ABM{<:GridSpace{D}}, pathfinder::AStar{D}, r = 1)
Return an iterator over all nearby_positions
within "radius" r
of the given position
(excluding position
), which are walkable as specified by the given pathfinder
.
sourceAgents.Pathfinding.random_walkable
— FunctionPathfinding.random_walkable(model, pathfinder::AStar{D})
Return a random position in the given model
that is walkable as specified by the given pathfinder
.
sourcePathfinding.random_walkable(pos, model::ABM{<:ContinuousSpace{D}}, pathfinder::AStar{D}, r = 1.0)
Return a random position within radius r
of pos
which is walkable, as specified by pathfinder
. Return pos
if no such position exists.
sourcePathfinding Metrics
Agents.Pathfinding.DirectDistance
— TypePathfinding.DirectDistance{D}([direction_costs::Vector{Int}]) <: CostMetric{D}
Distance is approximated as the shortest path between the two points, provided the walkable
property of Pathfinding.AStar
allows. Optionally provide a Vector{Int}
that represents the cost of going from a tile to the neighboring tile on the i
dimensional diagonal (default is 10√i
).
If diagonal_movement=false
in Pathfinding.AStar
, neighbors in diagonal positions will be excluded. Cost defaults to the first value of the provided vector.
sourceAgents.Pathfinding.MaxDistance
— TypePathfinding.MaxDistance{D}() <: CostMetric{D}
Distance between two tiles is approximated as the maximum of absolute difference in coordinates between them.
sourceAgents.Pathfinding.PenaltyMap
— TypePathfinding.PenaltyMap(pmap::Array{Int,D} [, base_metric::CostMetric]) <: CostMetric{D}
Distance between two positions is the sum of the shortest distance between them and the absolute difference in penalty.
A penalty map (pmap
) is required. For pathfinding in GridSpace
, this should be the same dimensions as the space. For pathfinding in ContinuousSpace
, the size of this map determines the granularity of the underlying grid, and should agree with the size of the walkable
map.
Distance is calculated using Pathfinding.DirectDistance
by default, and can be changed by specifying base_metric
.
An example usage can be found in Mountain Runners.
sourceBuilding a custom metric is straightforward, if the provided ones do not suit your purpose. See the Developer Docs for details.
Save, Load, Checkpoints
There may be scenarios where interacting with data in the form of files is necessary. The following functions provide an interface to save/load data to/from files.
Agents.AgentsIO.save_checkpoint
— FunctionAgentsIO.save_checkpoint(filename, model::ABM)
Write the entire model
to file specified by filename
. The following points should be considered before using this functionality:
- OpenStreetMap data is not saved. The path to the map should be specified when loading the model using the
map
keyword of AgentsIO.load_checkpoint
. - Functions are not saved, including stepping functions, schedulers, and
update_vel!
. The last two can be provided to AgentsIO.load_checkpoint
using the appropriate keyword arguments.
sourceAgents.AgentsIO.load_checkpoint
— FunctionAgentsIO.load_checkpoint(filename; kwargs...)
Load the model saved to the file specified by filename
.
Keywords
scheduler = Schedulers.fastest
specifies what scheduler should be used for the model.warn = true
can be used to disable warnings from type checks on the agent type.
ContinuousSpace
specific:
update_vel!
specifies a function that should be used to update each agent's velocity before it is moved. Refer to ContinuousSpace
for details.
OpenStreetMapSpace
specific:
map
is a path to the OpenStreetMap to be used for the space. This is a required parameter if the space is OpenStreetMapSpace
.use_cache = false
, trim_to_connected_graph = true
refer to OpenStreetMapSpace
sourceAgents.AgentsIO.populate_from_csv!
— FunctionAgentsIO.populate_from_csv!(model, filename [, agent_type, col_map]; row_number_is_id, kwargs...)
Populate the given model
using CSV data contained in filename
. Use agent_type
to specify the type of agent to create (In the case of multi-agent models) or a function that returns an agent to add to the model. The CSV row is splatted into the agent_type
constructor/function.
col_map
is a Dict{Symbol,Int}
specifying a mapping of keyword-arguments to row number. If col_map
is specified, the specified data is splatted as keyword arguments.
The keyword row_number_is_id = false
specifies whether the row number will be passed as the first argument (or as id
keyword) to agent_type
.
Any other keyword arguments are forwarded to CSV.Rows
. If the types
keyword is not specified and agent_type
is a struct, then the mapping from struct field to type will be used. Tuple{...}
fields will be suffixed with _1
, _2
, ... similarly to AgentsIO.dump_to_csv
For example,
struct Foo <: AbstractAgent
id::Int
pos::NTuple{2,Int}
foo::Tuple{Int,String}
@@ -123,7 +123,7 @@
:pos_2 => Int,
:foo_1 => Int,
:foo_2 => String,
-)
It is not necessary for all these fields to be present as columns in the CSV. Any column names that match will be converted to the appropriate type. There should exist a constructor for Foo
taking the appropriate combination of fields as parameters.
If "test.csv"
contains the following columns: pos_1, pos_2, foo_1, foo_2
, then model
can be populated as AgentsIO.populate_from_csv!(model, "test.csv"; row_number_is_id = true)
.
sourceAgents.AgentsIO.dump_to_csv
— FunctionAgentsIO.dump_to_csv(filename, agents [, fields]; kwargs...)
Dump agents
to the CSV file specified by filename
. agents
is any iterable sequence of types, such as from allagents
. fields
is an iterable sequence of Symbol
s specifying which fields of each agent are dumped. If not explicitly specified, it is automatically inferred using eltype(agents)
. All kwargs...
are forwarded to CSV.write
.
All Tuple{...}
fields are flattened to multiple columns suffixed by _1
, _2
... similarly to AgentsIO.populate_from_csv!
For example,
struct Foo <: AbstractAgent
+)
It is not necessary for all these fields to be present as columns in the CSV. Any column names that match will be converted to the appropriate type. There should exist a constructor for Foo
taking the appropriate combination of fields as parameters.
If "test.csv"
contains the following columns: pos_1, pos_2, foo_1, foo_2
, then model
can be populated as AgentsIO.populate_from_csv!(model, "test.csv"; row_number_is_id = true)
.
sourceAgents.AgentsIO.dump_to_csv
— FunctionAgentsIO.dump_to_csv(filename, agents [, fields]; kwargs...)
Dump agents
to the CSV file specified by filename
. agents
is any iterable sequence of types, such as from allagents
. fields
is an iterable sequence of Symbol
s specifying which fields of each agent are dumped. If not explicitly specified, it is automatically inferred using eltype(agents)
. All kwargs...
are forwarded to CSV.write
.
All Tuple{...}
fields are flattened to multiple columns suffixed by _1
, _2
... similarly to AgentsIO.populate_from_csv!
For example,
struct Foo <: AbstractAgent
id::Int
pos::NTuple{2,Int}
foo::Tuple{Int,String}
@@ -131,5 +131,5 @@
model = ABM(Foo, ...)
...
-AgentsIO.dump_to_csv("test.csv", allagents(model))
The resultant "test.csv"
file will contain the following columns: id
, pos_1
, pos_2
, foo_1
, foo_2
.
sourceIt is also possible to write data to file at predefined intervals while running your model, instead of storing it in memory:
Agents.offline_run!
— Functionoffline_run!(model, agent_step! [, model_step!], n::Integer; kwargs...)
-offline_run!(model, agent_step!, model_step!, n::Function; kwargs...)
Do the same as run
, but instead of collecting the whole run into an in-memory dataframe, write the output to a file after collecting data writing_interval
times and empty the dataframe after each write. Useful when the amount of collected data is expected to exceed the memory available during execution.
Keywords
backend=:csv
: backend to use for writing data. Currently supported backends: :csv
, :arrow
adata_filename="adata.$backend"
: a file to write agent data on. Appends to the file if it already exists, otherwise creates the file.mdata_filename="mdata.$backend"
: a file to write the model data on. Appends to the file if it already exists, otherwise creates the file.writing_interval=1
: write to file every writing_interval
times data collection is triggered. If the when
keyword is not set, this corresponds to writing to file every writing_interval
steps; otherwise, the data will be written every writing_interval
times the when
condition is satisfied (the same applies to when_model
).
sourceIn case you require custom serialization for model properties, refer to the Developer Docs for details.
Settings
This document was generated with Documenter.jl on Saturday 23 September 2023. Using Julia version 1.9.3.
+AgentsIO.dump_to_csv("test.csv", allagents(model))
The resultant "test.csv"
file will contain the following columns: id
, pos_1
, pos_2
, foo_1
, foo_2
.
It is also possible to write data to file at predefined intervals while running your model, instead of storing it in memory:
Agents.offline_run!
— Functionoffline_run!(model, agent_step! [, model_step!], n::Integer; kwargs...)
+offline_run!(model, agent_step!, model_step!, n::Function; kwargs...)
Do the same as run
, but instead of collecting the whole run into an in-memory dataframe, write the output to a file after collecting data writing_interval
times and empty the dataframe after each write. Useful when the amount of collected data is expected to exceed the memory available during execution.
Keywords
backend=:csv
: backend to use for writing data. Currently supported backends::csv
,:arrow
adata_filename="adata.$backend"
: a file to write agent data on. Appends to the file if it already exists, otherwise creates the file.mdata_filename="mdata.$backend"
: a file to write the model data on. Appends to the file if it already exists, otherwise creates the file.writing_interval=1
: write to file everywriting_interval
times data collection is triggered. If thewhen
keyword is not set, this corresponds to writing to file everywriting_interval
steps; otherwise, the data will be written everywriting_interval
times thewhen
condition is satisfied (the same applies towhen_model
).
In case you require custom serialization for model properties, refer to the Developer Docs for details.