
villager is a framework for creating and running agent based modelsin R. Its purpose is to provide an extensible framework where modelingcan be done in native R.
villager can be installed from CRAN by running thefollowing,
install.packages("villager")When reading though the Readme and vignettes, it’s important to takenote of a few concepts
villager is about modeling populations with (optional)associated resources. It supports a community level aggregation ofagents, referred to asvillages or an individualvillage. Agents, which are referred to as gender-neutralagents, are members of community level aggregations.
villager compliant modelsmust conform to the functiontemplate below. Theagent_mgr andresource_mgrare responsible for interacting with the individual agents andresources.
test_model <- function(current_state, previous_state, model_data, agent_mgr, resource_mgr) { ... ...}Agents are created by instantiating theagent class.There are a number of agent properties that can be passed to theconstructor.
test_model <- function(current_state, previous_state, model_data, agent_mgr, resource_mgr) { mother <- agent$new(first_name="Kirsten", last_name="Taylor", age=9125) father <- agent$new(first_name="Joshua", last_name="Thompson", age=7300) daughter <- agent$new(first_name="Mariylyyn", last_name="Thompson", age=10220)}To add agents to the simulation, use the providedagent_mgr object to calladd_agent. Becausethe classes are R6, the object can be modified after being added to themanager and the changes will be persisted without needing to re-add thevillager. For example, setting a daughter’s mother and her father below.Note that the standard way is to modify the propertiesbeforehand, although not strictly necessary.
test_model <- function(current_state, previous_state, model_data, agent_mgr, resource_mgr) { agent_mgr <- agent_manager$new() agent_mgr$add_agent(mother, father, daughter) daughter$mother_id <- mother$identifier daughter$father_id <- father$identifier}The agent manager can also be used to pair agents, representative ofa relationship or social bond.
agent_mgr$agent_mgr$connect_agents(mother, father)Resources are similar to agents in that they’re both R6 classes, areinstantiated similarly, and are also managed by an object passed intothe model. An example of creating resources and adding them to thesimulation is given below.
test_model <- function(current_state, previous_state, model_data, agent_mgr, resource_mgr) { corn_resource <- resource$new(name="corn", quantity = 10) fish_resource <- resource$new(name="fish", quantity = 15) corn_resource$quantity=5 resource_mgr <- resource_manager$new() resource_mgr$add_resource(corn_resource, fish_resource) fish_resource$quantity=5}Objects of typevillage,agent, andresourcehave particular states at a particular time. As thesimulation progresses, the state of these change based on model logic.At the end of each time step, the state of each object is saved, givinga complete record of the system’s evolution. The essence of any agentbased model is changing the state at each time step. villager provides amechanism for defining the initial state and for changing the statethroughout the simulation.
Creating the initial state is done by creating a function thatresembles model functions from above. The manager classes are used topopulate the village with an initial population of agents andresources.
initial_condition <- function(current_state, model_data, agent_mgr, resource_mgr) { # Create the initial villagers mother <- agent$new(first_name="Kirsten", last_name="Taylor", age=9125) father <- agent$new(first_name="Joshua", last_name="Thompson", age=7300) daughter <- agent$new(first_name="Mariylyyn", last_name="Thompson", age=10220) daughter$mother_id <- mother$identifier daughter$father_id <- father$identifier # Add the agents to the manager agent_mgr$connect_agents(mother, father) agent_mgr$add_agent(mother, father, daughter) # Create the resources corn_resource <- resource$new(name="corn", quantity = 10) fish_resource <- resource$new(name="fish", quantity = 15) # Add the resources to the manager resource_mgr$add_resource(corn_resource, fish_resource)}Models are tied to particular village instances. This binding is donewhen villages are created, shown below. Models can have names and mustalways be paired with an initial condition function and a modelfunction.
small_village <- village$new("Test Model 1", initial_condition, test_model)Thesimulator class is responsible for runningsimulations. It encapsulatesall of the villages and controlsthe duration of the simulation. The simulator below runs for 100 timesteps: roughly 13 years. The simulator can be paired with any number ofvillages, in the case of the simulator below, there’s only a singlevillage.
simulator <- simulation$new(100, list(small_village))simulator$run_model()We can combine the examples above into a full simulation that…
library(villager)initial_condition <- function(current_state, model_data, agent_mgr, resource_mgr) { # Create the initial villagers mother <- agent$new(first_name="Kirsten", last_name="Taylor", age=9125) father <- agent$new(first_name="Joshua", last_name="Thompson", age=7300) daughter <- agent$new(first_name="Mariylyyn", last_name="Thompson", age=10220) daughter$mother_id <- mother$identifier daughter$father_id <- father$identifier # Add the agents to the manager agent_mgr$connect_agents(mother, father) agent_mgr$add_agent(mother, father, daughter) # Create the resources corn_resource <- resource$new(name="corn", quantity = 10) fish_resource <- resource$new(name="fish", quantity = 15) # Add the resources to the manager resource_mgr$add_resource(corn_resource, fish_resource)}test_model <- function(current_state, previous_state, model_data, agent_mgr, resource_mgr) {print(paste("Step:", current_state$step)) for (agent in agent_mgr$get_living_agents()) { agent$age <- agent$age+1 if (agent$age >= 4383) { agent$profession <- "Farmer" } }}small_village <- village$new("Test Model", initial_condition, test_model)simulator <- simulation$new(4745, list(small_village))simulator$run_model()To demonstrate programatically creating villagers, consider the modelbelow that has the following logic.
library(villager) current_day <- current_state$step print(current_day) if((current_day%%2) == 0) { # Then it's an even day # Create two new agents whose first names are random numbers for (i in 1:2) { name <- runif(1, 0.0, 100) new_agent <- agent$new(first_name <- name, last_name <- "Smith") agent_mgr$add_agent(new_agent) } } else { # It's an odd day living_agents <- agent_mgr$get_living_agents() # Kill the first one living_agents[[1]]$alive <- FALSE } } coastal_village <- village$new("Test village", initial_condition, model) simulator <- simulation$new(4, villages = list(coastal_village)) simulator$run_model() mgr <- simulator$villages[[1]]$agent_mgrIn the examples above, the default properties of agents and resourceswere used. It’s possible that these won’t cover all the needs for morediverse models. There are vignettes on extending the agent and resourceclasses to handle these situations.
Code contributions are welcome as pull requests to thedevelop branch. Bugs, comments, and questions can besubmitted as Github Issues.