Ranging from predator - prey system, to trade markets, automobile traffic, thermodynamics, fluid dynamics and so on, agent-based models (ABM) encompass a wide variety of applications . Seemingly trivial appearing update rules (~equations of motion) yield a plethora of unexpected complexity, such as swarm synchronization, phase transitions, or the spread of diseases . Thus it comes to no surprise that many programming languages tailored to the execution of ABMs have been developed since the inception of ABMs in the late 1940’s. While plenty of different adaptions of ABMs for Mathematica have been explored, a concise and intuitive implementation yet has to be shaped. The aim of this work is to grant a simple agent simulation function encompassing both, pre-defined models as well as customizability hosting many of the applications above. In the following, the two implemented models (Predator-Prey & Conway’s Game of Life) are laid out in short, followed by two motivations on how-to create different models building on the existing paradigm.

Available Models

In general, the model is divided into different parts:
◼
  • Model | Static
  • ◼
  • Grid/Network | Static
  • ◼
  • Agents | Dynamic
  • ◼
  • Individual Update Rules | Dynamic on creation of new agent
  • ◼
  • Collision/Interaction | Static
  • First, a grid/network is generated, which is held static throughout the simulation. Then, agents are generated based on the model and the related model parameters. Every single agent is equipped with a set of update rules, which informs it about is surroundings and then how to act upon new information. The user can also add/overwrite any rule that can be attributed to a single agent. Unless such an update rule makes changes to the rules themselves, these rules do not change throughout the lifetime of the agent. On Collision, however, new agents can be created in the case of the predator-prey model. The new generation inherits the update rules of one of their parents at random. A collision, in this framework, is triggered, when there are at least two agents sharing the same node of the network.

    Predator Prey

    Idea

    Individuals run around, grazing and chasing, reproducing and dying from hunger. In general, the movement of the agents can depend on their vision, although, in the base model, prey just wiggles around randomly. On collision, agents reproduce and predators eat prey.

    Attributes

    Information that each individual has together with pre-defined dynamic behavior.
    ◼
  • Index (Name) | Static, depends on parent generation
  • ◼
  • Coordinate | Dynamic
  • ◼
  • VertexNumber | Dynamic
  • ◼
  • Rile (Predator/Prey) | Static
  • ◼
  • Birth Rate | Static, identical for predators and prey
  • ◼
  • Death Rates | Dynamic for predators and constant 0 for prey
  • ◼
  • Velocity -> 2D Vector | Dynamic
  • ◼
  • Maximum Speed |Static/Dynamic: dependent on nearest neighbor network
  • ◼
  • VisionRadius | Static (5)
  • ◼
  • Neighbours | Dynamic, euclidian distance
  • ◼
  • UpdateFunctions | Static in the base model
  • Rules

    Individual
    ◼
  • observe --> Update Neighbours
  • ◼
  • die --> Die at random according to deathrate
  • ◼
  • accelerate --> Change velocity based on individual attributes
  • ​
    ​Move: It follows a movement phase based on the current velocity of the agents. For the case of agents sharing momentum, bounce back boundaries can be employed.
    Collision
    ◼
  • eat --> All prey that shares a node with a predator is deleted
  • ◼
  • starve --> If predators meet without eating, their death rate increases
  • ◼
  • procreate --> According to the fertility of all colliding agents, a new agents spawns with its attributes inherited from on of its parents
  • Conway’s Game of Life

    Idea

    Each node on a 2D grid of set dimensions has either property among alive/dead. This state changes based on the local neighbourhood and it is the defining system variable.

    Attributes

    ◼
  • VertexNumber | Static
  • ◼
  • Coordinate | Static
  • ◼
  • Neighbourhood | Static
  • ◼
  • Neighbours | Dynamic (while the neighbours don’t change their position, their “alive” state changes nevertheless)
  • ◼
  • Alive (True/False) | Dynamic
  • ◼
  • UpdateFunctions | Static
  • Rules

    ◼
  • observe --> Update Neighbours
  • ◼
  • If alive with 2-3 Neighbours --> Alive
  • ◼
  • If dead with exactly 3 Neighbours --> Alive
  • ◼
  • All other cases --> Dead
  • Possible Adaptions
    

    Topology

    Bounce Back Boundary

    Grid

    In[]:=
    createGridBounceBack[n_Integer?Positive,m_Integer?Positive,gridType_:"Rectangular"]:=Module[​​{surface,neigbourCoordinates,bulkDegree},​​(*Functions*)​​surface[grid_,boundaryVertex_]:=Module[​​{​​surfaceVector,​​position=PropertyValue[{grid,boundaryVertex},VertexCoordinates]​​},​​surfaceVector={​​position[[1]]/.{1.->-1.,N@n->1.,_->0},​​position[[2]]/.{1.->-1.,N@m->1.,_->0.}​​};​​surfaceVector/Norm@surfaceVector​​];​​​​​​​​(*Values*)​​Clear[grid,gridCoordinatesToIndex,gridBoundaries,gridNeighbourhood,gridBoundaryNormalVectors];​​​​Switch[gridType,​​"Rectangular",​​grid=GridGraph[{m,n}];​​bulkDegree=4,​​​​"RectangularDiag",​​grid=NearestNeighborGraph[GraphEmbedding@GridGraph[{m,n}],DistanceFunction->ChessboardDistance];​​bulkDegree=8;​​​​];​​​​gridCoordinatesToIndex=AssociationThread[GraphEmbedding@grid,VertexList[grid]​​];​​​​gridBoundaries=VertexList[grid,_?(VertexDegree[grid,#]<bulkDegree&)];​​gridBoundaryNormalVectors=AssociationThread[gridBoundaries,surface[grid,#]&/@gridBoundaries];​​​​gridNeighbourhood=generateNeighbourhoods@grid;​​​​]

    Actual Bounce Back Move

    In[]:=
    moveBounceBack[Agent_,gridBoundaryNormalVector_]:=Module[{​​projection=Agent["Velocity"].gridBoundaryNormalVector,​​newVelocity,​​newCoordinate,​​newVertexNumber​​},​​newVelocity=Agent["Velocity"]-2projectiongridBoundaryNormalVector;​​newCoordinate=Agent["Coordinate"]-projectiongridBoundaryNormalVector;​​newVertexNumber=newCoordinate/.gridCoordinatesToIndex;​​​​ReplacePart[Agent,{​​"Velocity"->newVelocity,​​"Coordinate"->newCoordinate,​​"VertexNumber"->newVertexNumber​​}]​​]

    Closed Boundary

    In[]:=
    Clear@createGridWall​​createGridWall[n_Integer?Positive,m_Integer?Positive,gridType_:"Rectangular"]:=Module[​​{neigbourCoordinates,gridFunction},​​Clear[grid,gridCoordinatesToIndex];​​​​Switch[gridType,​​"Rectangular",​​grid=GridGraph[{m,n}],​​​​"RectangularDiag",​​grid=NearestNeighborGraph[GraphEmbedding@GridGraph[{m,n}],DistanceFunction->ChessboardDistance];​​​​];​​​​gridCoordinatesToIndex=AssociationThread[GraphEmbedding@grid,VertexList[grid]​​];​​​​gridNeighbourhood=generateNeighbourhoods@grid;​​]​​

    GeneralFunctions

    Generate Neighbourhood List

    In[]:=
    generateNeighbourhoods[grid_]:=With[​​{emb=GraphEmbedding[grid],vertexIndex=PositionIndex[VertexList[grid]][[All,1]]​​},​​AssociationMap[​​emb[[Lookup[vertexIndex,AdjacencyList[grid,#]]]]-Threaded[emb[[vertexIndex[#]]]]&,​​VertexList[grid]​​]​​];
    Agents
    
    Conways Game of Life

    Attribute Changing Functions
    

    Creation
    

    Update Rules

    Model Independent Agent Functions
    

    Initialize Model

    Grid and Agents

    Set an Agent Distribution if none was given by the User
    
    If no Network has been provided
    
    If a Network has been provided
    

    Updater
    

    <Main>
    
    Usage

    Manual

    The function “simulateAgents[model, modelParameters]” expects the following
    ◼
  • model == PredatorPreyClosedB/PredatorPreyBB with modelParameters containing
  • ◼
  • ​
  • ◼
  • “ΔdeathRate” (how much does the death rate of predators increase after they meet one another)
  • ◼
  • Concerning grid if it was not provided through options:
  • ◼
  • “GridType”
  • ◼
  • “Dimension”
  • ◼
  • Concerning creation of initial agents if no “AgentDistribution” is provided:
  • ◼
  • “TotalPop” (< Times@@dimensions)
  • ◼
  • “PredatorPreyRatio” (<1)
  • ​
    Further, modelParameters always should contain
    ◼
  • “Iterations”
  • ◼
  • “Visualize” (True if a 2D list plot should be shown, depicting the current state of the system)
  • Options

    The functionality of "simulateAgents" can be customized through:
    ◼
  • “Domain” -> custom Graph
  • ◼
  • “DomainBoundaries” -> VertexList. Needed for custom Graph with bounce back boundary conditions or when implementing lattice gas manually.
  • ◼
  • “BoundaryNormalVectors” -> VectorList ordered according to DomainBoundaries. Needed for custom Graph with bounce back boundary conditions or when implementing lattice gas manually.
  • ◼
  • “AgentDistribution” -> function[agent] -> (PredatorPrey:Predator|Prey|False ; GoL:True|False)
  • ◼
  • “AgentUpdateFunctions” -> {{Condition[agent], Rule[Attribute, value[agent] ]},{..},...} ; Condition returns True|False and the update function should be a function, which returns a rule based on the agent attributes
  • Examples

    1

    In[]:=
    simulateAgents[​​"PredatorPreyClosedB",​​<|​​"Dimensions"->{40,40},​​"GridType"->"RectangularDiag",​​"Iterations"->100,​​"TotalPop"->40,​​"PredatorPreyRatio"->1/5,​​"ΔdeathRate"->0.1,​​"Visualize"->True​​|>​​]

    2

    In[]:=
    simulateAgents​​"ConwaysGameOfLife",​​<|"Dimensions"->{10,10},​​"GridType"->"RectangularDiag",​​"Iterations"->100,​​"PopRatio"->
    3
    9
    ,​​"Visualize"->True​​|>​​

    3

    In[]:=
    gridPredefined=NearestNeighborGraph[GraphEmbedding@GridGraph[{11,17}],DistanceFunction->ChessboardDistance]
    In[]:=
    simulateAgents​​"ConwaysGameOfLife",​​<|"Iterations"->10,​​"PopRatio"->
    3
    9
    ,​​"Visualize"->True​​|>,​​"Domain"->gridPredefined​​

    4

    customUpdateRules={{(Random[]<0.2&),"switcheroo"->(Rule["Alive",True]&),-1},{(Random[]<0.3&),"switcheroo2"->(Rule["Alive",RandomChoice[{True,False}]]&),-2}};
    In[]:=
    simulateAgents​​"ConwaysGameOfLife",​​<|"Dimensions"->{10,10},​​"GridType"->"RectangularDiag",​​"Iterations"->1000,​​"PopRatio"->
    3
    9
    ,​​"Visualize"->True​​|>,​​"AgentUpdateFunctions"->customUpdateRules​​

    CITE THIS NOTEBOOK

    General framework for agent based modeling​
    by Moritz Kalhöfer-Köchling​
    Wolfram Community, STAFF PICKS, July 12, 2023
    ​https://community.wolfram.com/groups/-/m/t/2959189