Transition Function
After the initialization, the state of the simulation is modified by so called transition functions. See Defining Transition Functions for details.
Vahana.apply!
— Functionapply!(sim, func, call, read, write; [add_existing, with_edge])
Apply the transition function func
to the simulation state.
call
must be a single agent type or a collection of agent types, likewise read
and write
must be a single agent/edge type or a collection of agent/edge types.
call
determines for which agent types the transition function func
is called. Within the transition function, an agent has access to the state of agents (including its own state) and to edges only if their types are in the read
collection. Accordingly, the agent can change its own state and/or create new agents or edges only if their types are in the write
collection.
Assume that T is an agent type that is in call
. In case T is also in read
, the transition function must have the following signature: transition_function(agent::T, id, sim)
, where the type declaration of agent is optional if call
contains only a single type. If T is not in read
, it must have the signature transition_function(::Val{T}, id::AgentID, sim::Simulation)
.
If T is in write
, the transition function must return either an agent of type T or nothing
. If nothing
is returned, the agent will be removed from the simulation, otherwise the agent with id id
will get the returned state after the transition function was called for all agents.
When an edge state type is in write
, the current edges of that type are removed from the simulation. If you want to keep the existing edges for a specific type, you can add this type to the optional add_existing
collection.
Similarly, agent types that are in the write
but not in the call
argument can be part of the add_existing
collection, so that the existing agents of this type are retained and can only be added. For agent types in call
, however, this is achieved by returning their state in the transition function.
With the keyword with_edge
it is possible to restrict the set of agents for which the transition function is called to those agents who are on the target side of edges of the type with_edge
. So
apply!(sim, AT, ET, []) do _, id, sim
if has_edge(sim, id, ET)
do_something
end
end
is equivalent to
apply!(sim, AT, ET, []; with_edge = ET) do _, id, sim
do_something
end
but saves all the has_edge
checks.
This with_edge
keyword should only be set when a small subset of agents possess edges of this particular type, as performance will be adversely impacted in other scenarios. Also the keyword can only be used for edgetypes without the :SingleType hint.
See also apply
and the Applying Transition Function section in the tutorial
Vahana.apply
— Functionapply!(sim, func, call, read, write; add_existing)
Call apply!
with a copy of the simulation so that the state of sim
itself is not changed.
Can be very useful during development, especially if Vahana is used in the REPL. However, for performance reasons, this function should not be used in the final code.
Returns the copy of the simulation.
See also apply!
The agent types must be immutable, but in many cases the agent returned by a transition function will have a different state than the agent specified as a parameter. If only one field is changed, we still need to copy all other fields. The Setfield.jl package can be very useful in this case.
Inside a transition function the following functions can be used to access the state of the simulation:
Globals and Parameters
Vahana.param
— Functionparam(sim::Simulation, name)
Returns the value of the field name
of the params
struct from the Simulation constructor.
See also create_model
Vahana.get_global
— Functionget_global(sim::Simulation, name)
Returns the value of the field name
of the globals
struct for simulation sim
.
See also create_simulation
, set_global!
and push_global!
Get Agent(state)
Vahana.agentstate
— Functionagentstate(sim, id::AgentID, ::Type{T})
Returns the state of an agent of type T.
In the case where the type T is not determinable when writing the code (e.g. since there may be edges between agents of different types, the function edges
may also return agentIDs of different agent types), agentstate_flexible
must be used instead.
if agentstate is called with a Type{T} that does not match the type of the agent with id
and the vahana assertions are disabled via enable_asserts
, then it is possible that the state of an incorrect agent will be returned. When the assertions are active, there is a runtime check that the agent with the ID id
has indeed the type T.
Vahana.agentstate_flexible
— Functionagentstate_flexible(sim, id::AgentID)
Returns the state of an agent with the id
, where the type of the agent is determined at runtime. If the type is known at compile time, using agentstate
is preferable as this improves performance.
To retrieve the states of all agents connected to a target agent through edges of a specific type, the neighborstates_iter
functions can be beneficial. These functions combine the capabilities of neighborids
and agentstate
, allowing you to easy access the desired information.
Vahana.neighborstates
— Functionneighborstates(sim::Simulation, id::AgentID, ::Type{E}, ::Type{A})
Returns the state of the agent with type A
on the source of the edge of type E
with agent id
as target if E
has the hint :SingleEdge, or a vector of these agent states otherwise.
If there is no edge with agent id
as target, neighborstates
returns nothing
.
When the agents on the source side of the edges can have different types, and it is impossible to determine the Type{A} you can use neighborstates_flexible
instead.
neighborstates
is not defined if T has the hint :IgnoreFrom
If the edge type E
does not have the :SingleEdge hint, neighborstates
allocates memory for the vector of agent states. To avoid this allocation, you can use neighborstates_iter
instead.
See also apply!
, edges
, neighborids
, num_edges
, has_edge
and edgestates
Vahana.neighborstates_flexible
— Functionneighborstates_flexible(sim::Simulation, id::AgentID, ::Type{E})
Returns the state of the agent on the source of the edge of type E
with agent id
as target if E
has the hint :SingleEdge, or a vector of these agent states otherwise.
If there is no edge with agent id
as target, neighborstates_flexible
returns nothing
.
neighborstates_flexible
is the type instable version of neighborstates
and should be only used in the case that the type of agent can not be determined.
neighborstates_flexible
is not defined if T has the hint :IgnoreFrom.
If the edge type E
does not have the :SingleEdge hint, neighborstates_flexible
allocates memory for the vector of agent states. To avoid this allocation, you can use neighborstates_flexible_iter
instead.
See also apply!
, edges
, neighborids
, num_edges
, has_edge
and edgestates
Vahana.neighborstates_iter
— Functionneighborstates_iter(sim::Simulation, id::AgentID, ::Type{E}, ::Type{A})
Returns an iterator for the states of the agents with type A
on the source of the edges of type E
with agent id
as target.
If there is no edge with agent id
as target, the function returns nothing
.
When the agents on the source side of the edges can have different types, and it is impossible to determine the Type{A} you can use neighborstates_flexible_iter
instead.
neighborstates_iter
is not defined if T has the hint :IgnoreFrom or :SingleEdge.
See also neighborstates
, apply!
, edges
, neighborids
, num_edges
, has_edge
and edgestates
Vahana.neighborstates_flexible_iter
— Functionneighborstates_flexible_iter(sim::Simulation, id::AgentID, ::Type{E})
Returns an iterator for the states of the agents on the source of the edges of type E
with agent id
as target.
If there is no edge with agent id
as target, the function returns nothing
.
neighborstates_flexible_iter
is the type instable version of neighborstates_iter
and should be only used in the case that the type of agent can not be determined.
neighborstates_flexible_iter
is not defined if T has the hint :IgnoreFrom or the hint :SingleEdge.
See also neighborstates_flexible
, apply!
, edges
, neighborids
, num_edges
, has_edge
and edgestates
Get Edge(state)
Vahana.edges
— Functionedges(sim, id::AgentID, ::Type{E})
Returns the edge of type E
with agent id
as target if E
has the hint :SingleEdge, or a vector of these edges otherwise.
If there is no edge with agent id
as target, edges
returns nothing
.
edges is not defined if E
has the hint :IgnoreFrom or :Stateless.
See also apply!
, neighborids
, edgestates
, num_edges
, has_edge
and neighborstates
Vahana.edgestates
— Functionedgestates(sim, id::AgentID, ::Type{E})
Returns the state of the edge of type E
with agent id
as target if E
has the hint :SingleEdge, or a vector of these states otherwise.
If there is no edge with agent id
as target, edgestates
returns nothing
.
edgestates
is not defined if E
has the hint :Stateless.
If the edge type E
does not have the :Stateless or :SingleEdge hint, edgestates
allocates memory for the vector of states. To avoid this allocation, you can use edgestates_iter
instead.
See also apply!
, edges
, neighborids
, num_edges
, has_edge
and neighborstates
Vahana.edgestates_iter
— Functionedgestates_iter(sim, id::AgentID, ::Type{E})
Returns an iterator of the states of the edges of type E
with agent id
as target.
If there is no edge with agent id
as target, the function returns nothing
.
edgestates_iter
is not defined if E
has the hint :Stateless or :SingleEdge.
See also apply!
, edgestates
, edges
, neighborids
, num_edges
, has_edge
and neighborstates
Vahana.neighborids
— Functionneighborids(sim, id::AgentID, ::Type{E})
Returns the ID of the agent on the source of the edge of type E
with agent id
as target if E
has the hint :SingleEdge, or otherwise a vector of the IDs of the agents on the source side of those edges.
If there is no edge with agent id
as target, neighborids
returns nothing
.
neighborids
is not defined if E
has the hint :IgnoreFrom.
If the edge type E
does not have the :Stateless or :SingleEdge hint, neighborids
allocates memory for the vector of agent ids. To avoid this allocation, you can use neighborids_iter
instead.
See also apply!
, edges
, edgestates
, num_edges
, has_edge
and neighborstates
Vahana.neighborids_iter
— Functionneighborids_iter(sim, id::AgentID, ::Type{E})
Returns an iterator of the IDs of the agents on the source side of edges of type E
with agent id
as target.
If there is no edge with agent id
as target, the function returns nothing
.
neighborids
is not defined if E
has the hint :SingleEdge or :IgnoreFrom.
See also apply!
, neighborids
, edges
, edgestates
, num_edges
, has_edge
and neighborstates
Vahana.num_edges
— Methodnum_edges(sim, id::AgentID, ::Type{E})
Returns the number of edges of type E
with agent id
as target.
num_edges
is not defined if T has the hint :SingleEdge
See also apply!
, edges
, neighborids
, edgestates
, has_edge
and neighborstates
Vahana.has_edge
— Functionhas_edge(sim, id::AgentID, ::Type{E})
Returns true if there is at least one edge of type E
with agent id
as target.
has_edge
is not defined if T has the :SingleEdge and :SingleType hints, with the exception that it has also the :IgnoreFrom and :Stateless hints.
See also apply!
, edges
, neighborids
, edgestates
, num_edges
and neighborstates
For all the function like edges
that returns nothing
in the case that there is no edge with the agent as target, it can be useful to increase the readability of the code by using the checked
function to test for nothing
.
Vahana.checked
— Functionchecked(f, g, itr; kwargs...)
Calls g(f, itr; kwargs...), but only if itr != nothing.
As all the Vahana functions that access the edges of a specific agent can return nothing in the case, that there exist no incoming edge for this agent, it's often necessery to check this case.
Example:
Instead of writing
nids = neighborids(sim, id, Contact)
if nids != nothing
foreach(nids) do nid
add_edge!(sim, id, nid, Inform()
end
end
you can use the checked function to write
checked(foreach, neighborids(sim, id, Contact)) do nid
add_edge!(sim, id, nid, Inform())
end
Add Agents/Edges
The following functions from the [Initialization] section (initialization.md) that add new agents or edges to a simulation can also be used inside a transition function, namely:
Remove Edges
Since v1.2 it's also possible to remove edges:
Vahana.remove_edges!
— Functionremove_edges!(sim::Simulation, to::AgentID, ::Type{E})
Remove all edges of type E
where to
is at the target position.
Can only be called within a transition function, where E
is in the write
argument and also in the add_existing
list of apply!
.
remove_edges!(sim::Simulation, from::AgentID, to::AgentID, ::Type{E})
Removes all edges of type E
with from
at the source position and to
at the target position of an edge. remove_edges!
in this form (with from
as argument) can only be called if the edge type E
does not have the :IgnoreFrom
hint.
Can also only be called within a transition function, where E
is in the write
argument and also in the add_existing
list of apply!
.