Giter Site home page Giter Site logo

zizroc / villager Goto Github PK

View Code? Open in Web Editor NEW
55.0 55.0 4.0 853 KB

villager is an extensible agent based modeling (ABM) framework for the R language. It supports agents, agent aggregations and their associated resources, as well as flexible data management.

License: Other

R 100.00%
abm agent-based-modeling simulation

villager's People

Contributors

gvaldana avatar ohmannyy avatar thomasthelen avatar zizroc avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

villager's Issues

Export Methods from Source Files

R's packaging system hooks up to the comments in the source code to generate a bunch of things. You MUST document classes and methods a particular way if you want the library to expose them to people (otherwise you run library(villager) but aren't able to use any of it).

Figure out and document the classes and methods so that R will export them.

Rename BaseVillage Class to village

The BaseVillage has that names for historical reasons (There used to be subclasses). We really only need a single village class, so we might as well call it villager. As per tidy naming, its lower cased.

  1. Rename BaseVillage to village
  2. Change the file name to village
  3. Go through all of the documentation and other code, making sure it's all accurate

Pick a License

Right now the package check will fail with

> checking DESCRIPTION meta-information ... WARNING
  Non-standard license specification:
    `use_mit_license()`, `use_gpl3_license()` or friends to pick a
    license
  Standardizable: FALSE

which means we can't publish it and other legally can't use the code.

This issue is to track the progress of selecting a license, which should be done 9/11/20

I would hope that the R command to generate the license actually generates the file too. In addition, we'll want to put a license badge on the README.

Figure out a release schedule

We should be versioning this package so that people can go back and use old versions from old papers. We should of course go with the usual 1.0, 2.0, major.minor.

What do we want to include in what we call 1.0? Everything except trade? Include trade and say that trade+custom dynamics is 1.0? Up for discussion.

Cram Additional Properties Into Subclassed VillageState Objects

Consider the following class that extends VillageState to include a new variable, aggression.

customState <- R6Class("demographicState",
                          inherit = VillageState,
                          public = list(aggression,
                            initialize = function(aggression=NULL) {
                                          # Let the base class initialize all of the other values
                                          super$initialize()
                                          self$aggression <- aggression
                                                              }
                                        )
                          )

We can create a new state object with

initial_condition <- customState$new()

But. We can't do something like (I'm trying to set the variable in the base class)

initial_condition <- customState$new(carryingCapacity=111)

Most languages have support for passing a variable number of arguments (for example pythons kwargs (actually exactly what I want to do)).

Goal: Use some sort of parameter pack/kwarg feature to pass values into the derived class's initialize method and feed them into the parent class.

https://www.geeksforgeeks.org/packing-and-unpacking-arguments-in-python/

Release 1.0

We should get 1.0 out so that we can start modeling basic villages. This issue is to make sure we're all on the same page as to what goes into it.

Requirements

Paraphrasing @zizroc in issue #10,

  • Functional trade between at least two villages
  • The ability to execute reasonable internal dynamics
  • The ability to couple environmental forcings*
  • The ability to support resources*
  • Sufficient documentation on how to use it
  • May be left to the user. For example, environmental forcings might be in the dynamics model, and the resources in a custom VillageState class.

Things completed

The ability to execute reasonable internal dynamics

This is currently possible to do with the library. See the vignettes folder for examples (need PR #15)

If there's anything missed or that needs to change leave a comment below!

Create Unit Tests

We should have unit tests for all of the classes and will need them if we plan on releasing to the public.

Add Support for Grid Movement

We still want to support movement around on a 2x2 grid. This grid should be accessible to all agents. Each cell in the grid should be able to hold a reference to the agent that currently occupies the square. Each agent should have a member variable the holds the (x, y) location of where it is on the grid. It might be helpful to implement a random walk across the grid and place it in the agent class.

Add a Linter

I've completely butchered the styling which is probably an artifact of working with a different R package that uses a different styling.

I think we agreed on the tidyverse style?

Add a linter to the package so that we're notified when we deviate.

Create VillageState from Tibble

Random thought: It's kind of a pain having to define custom R6 state objects. It would be great to be able to pass a data frame and tibble into the village's initial_state field. Internally we can convert it to our R6 object (for speed). I'll give an example below and we can see if it makes more sense to go this route.

Increase Test Coverage of village_state and village

We're currently sitting at a coverage 87% which is pretty good; we can easily increase this by improving on the tests in village_state.R and village.R, which are currently in the 60% range for coverage.

Add support for variadic parameters in add_resource & add_winik

It's kind of messy having to call add_resource and add_winik over and over again. We should take advantage of R's ellipsis functionality to fix this.

For example,

  apples  <- resource$new("apple", 5)
  oranges <- resource$new("orange", 10)
  cabbage <- resource$new("cabbage", 20)

  resource_mgr$add_resource(apples)
  resource_mgr$add_resource(oranges)
  resource_mgr$add_resource(cabbage)

should be able to be written as

  apples  <- resource$new("apple", 5)
  oranges <- resource$new("orange", 10)
  cabbage <- resource$new("cabbage", 20)

  resource_mgr$add_resource(apples, oranges, cabbage)

Fix winik states

When a winik's state is a written, it's written as a tibble. See here for reference. It turns out, some of the values may be null, which tibble won't handle and R won't throw an error (even though the tibble is never created).

Figure out of to fix this.

Add support for loading winiks from disk

We should add support to the winik_manager manager class for loading pre-defined winiks and populating the manager with them. The file that's being loaded will most likely be a CSV file where each row represents a new winik.

load()

The pseudo-code for this method looks like

initial_winik_file <- read.csv('winiks.csv)
for (line in initial_winik_file) {
   new_winik <- winik$new(name=line$name, father_id=line$father_id, .....)
   self$add(new_winik)

The file will look something like
winik

Write the code documentation

There's a significant amount of documentation needed around methods, examples of them, and general bookkeeping. This issue is for writing it up. Related to #26 and being completed in the code_docs branch

Create a Hex Sticker

All the cool people are doing it. See here.

We need a color scheme and a photo/library logo to do this.

Check minimum dependencies

We might have a few dependencies defined that we no longer need. Go through an make sure that the minimum ones are defined.

Add support for resources

We'd like to support the notion of resources to villager. Right now, resource information is kept in the VillageState object but will need to be pulled out. See this issue to see how the resources will be integrated.

resource class

A new class should be created to represent each type of resource. The resource class can be thought of as a template for all resources. At a minimum, the resource object should have a name (fish, poultry, etc) and the amount that's currently present. The class should also be an R6 class so that we can modify its state.

When the resource class is finished, new resource objects should be created as

fish <- resource$new(name='fish', quantity=10)
maize <- resource$new(name='maize', quantity=0)

resource_manager class

The resources should be accessed through a new class, called resource_manger. The village class should have an instance of this object, and should also expose it to user defined models (see how winik_manager is used in the propagate function).

This class should have at least two methods: add(resource) and 'get(string resource_name)`.

fish <- resource$new(name='fish', quantity=10)
maize <- resource$new(name='maize', quantity=0)
resource_store <- resource_manager$new()
resource_store$add(fish)
resource_store$add(maize)
maize <- resource_store$get("maize")

Saving & Loading

Eventually we'll want to be able to save the resources and re-load them. This will give similar functionality with the winik manager which will be able to save and load winiks. Like loading winiks, this will allow us to load

The database table describing the resources will look something like the following
resource-table

The resource_manager should have a function, load(), for loading a csv of resources, creating a new resource object for each one, and then adding each new resource object to the manager.

modifying resources in custom models

Users should be able to add and modify resources in their custom models. I added a unit test below (untested so there may be syntax errors) to show what it would look like on the user's end to modify the resource.

test_that("resources can be modified in a model", {
  # Create a model that adds 1 unit of poultry every day, starting with 0
  resource_model <- function(currentState, previousState, modelData, population_manager) {

   # Set the initial conditions at year==1
   if (currentState$year ==1) {
        poultry<- resource$new(name='poultry', quantity=0)
        self$resource_manager$add(poultry)
    }
   else {
         polutry_stock <- resource_manager$get('poultry')
         poultry_stock$quantity <- poultry_stock$quantity + 1
   }
  }

  # Create a default village
  new_state <- VillageState$new()
  plains_village  <- BaseVillage$new(initialState=new_state, models=resource_model)
  # Run for 5 days
  days_to_run <- 5
  new_siumulator <- Simulation$new(length = days_to_run, villages = list(plains_village))
  new_siumulator$run_model()
  testthat::expect_equal(new_siumulator$villages[[1]]$resource_manager$get('poultry'), 5)
})

Unit tests

Two new files, test-winik.R and test-winik_manager.R should be made in the unit test directory. The test_winik tests should test that the constructor works as expected. The test-winik_manager tests should test that

  1. The constructor works
  2. The add method works as expected
  3. The get method works as expected
  4. Modifying a resource's quantity persists

An integrated test should be added (will most likely look like the test I pasted above) to test that

  1. Users can add any number of resources at year==1
  2. Users can modify the quantity of said resources and the quantity persists over time steps

Create README

The README should have at the minimum the appropriate badges, instructions for installing, and instructions for lightly using

Look into List Copying

While writing the docs I'm running across some suspicious stuff in the resource_manager (that I've gotten to). Go back and check to see if there are better ways to add resources to the manager (I think we're copying them at the moment)

Decide on Developer Habits

I'm guilty of being a cowboy coder; working on a ranch long enough might do that to you ;)

I'm sure @zizroc is at this point, tired of my "Oh I literally just pushed 2 minutes ago rebase", "Oh yeah yesterday I made that change, you'll have to pull", "Bro it's going to be a j4ck3d merge when you get the latest changes", etc comments. Continuing on like this would be madness, and it would bee better to keep everyone in the loop before new code is added. This will slow things down which at this point should be okay. We have the base state machine and now we're adding extensions to it (like trade).

I hate too much structure and don't want to opt for a full development process because it makes software a dead black and white dismal thing (personal opinion).

  1. All code is added through a pull request and approved by 2 people
    This means that most of us will have seen the new changes and know that we need to update our branches. It also forces us to keep up with the codebase. Over time we will have seen and judged just about every line that goes in.

  2. Pull requests are linked to issues
    When there's a bug, it's good to get context as to where it came from (so that the fix doesn't break anything). It's also nice just for record keeping (ie why did we pick license X?). I'm pretty relaxed on this one, but think that it might be a good idea.

What do all think?

Add Basic Trade

We need to let villages trade with eachother. The dynamics of trade isn't villager's responsibility, so we need to provide a way to let researchers describe them.

Work has been done on this front that lets a user create their own function that defines the trade dynamics.

This issue is to circle the wagons and take a look at what is currently possible with the base trade system and to beef it up if needed before moving on.

Add support for user defined data writing

Because users can create their own winik & resource classes, we need to provide a way for them to write their data to disk. A new class, data_writer should be created. Users should override a write() method, which will get called at the end of each day.

Fix Winik Ages

Ages are a little undefined. After discussion with @gvaldana we're now modeling ages in terms of days. Right now, ages are not incremented each day-this is something that villager should do so that modelers don't have to remember to do it.

Fix the Linter Warnings

The linter errors in the Travis logs need to be fixed. To see them, visit travis, select a build, and then view the Rscript -e 'lintr::lint_package()' logs.

[Trade] Trade Contracts

Villages should be able to write trade agreements that are executed, based on some condition.

This will most likely work by.....

When village A goes to trade with village B, village A will propose a new state for village B. Village B will have to either accept or reject the trade. If B accepts the new state, then the village B state is updated. I'm not sure how it will look for village A, because it needs to know if village B accepted the trade or not.

Worst case is that we create a new Broker class that keeps track of all of the proposed trades.

Population growth for trading villages

Population growth should be logistic with growth from (1) intrinsic (births and deaths) and (2) due to population "trades" (viz. raiding). Should look something like this, for 2 trading villages.

growth_village_a <- function(currentState, previousState, modelData){
  
  #the growth rate should be conditional, not constant like it is defined below
  growth_rate <- initialVillageState$birthRate - initialVillageState$deathRate
  
  #carrying capacity might be conditional too, but constant is ok for now
  carrying_capacity <- initialVillageState$carryingCapacity
  
  currentState$population = previousState$population + growth_rate*previousState$population*(1 - previousState$popuation/carrying_capacity)
  
}

#same here
growth_village_b <- function(currentState, previousState, modelData){
  
  #the growth rate should be conditional, not constant like it is defined below
  growth_rate <- initialVillageState$birthRate - initialVillageState$deathRate
  
  #carrying capacity might be conditional too, but constant is ok for now
  carrying_capacity <- initialVillageState$carryingCapacity
  
  currentState$population = previousState$population + growth_rate*previousState$population*(1 - previousState$popuation/carrying_capacity)
  
}

Document the Architecture

Now that we have a little better idea what we want to support, we can start planning the overall architecture.

village class

This class represents the entire village, and exposes village features through the manager classes. The users interact with the manager classes in their models, and the village class.

manager classes

The manager classes are used to organize various components of the village and to cleanly expose an interface to village data. Each manager manages an aggregation of objects. For example, the trade manager should manage the trade objects. The winik manager should manage an aggregation of winik objects, etc.

resource, trade, and winik classes

These classes represent individual things: a single winik, a single resource, or a single trade.

In terms of a picture,

villager

Add Support for

We need an easy way to tell how long its been since a winik had a child. Add a method that returns the age of the youngest child for a winik, which is the time since last birth

Hook up With Travis CI

We should be doing automated builds when we push new code and make pull requests. This lets us know if the code coverage decreased (not enough unit tests) or if something broke the package.

We should be automating the testing on both UNIX and macOS-Travis doesn't yet support Windows with R

Revert the Code Base

We should revert the code base to the the state where it had the population and resource managers.

Create an Agent Class

After the 03/14/21 discussion we want to add some sort of 'Agent' class. It'll probably be more defined over time but off the top of my head it should probably (up for discussion),

have a field for at least,

  1. An identifier
  2. A list of other agents that it may contain
  3. Agent data (maybe we'll make this hold an R6 class in the future)

have methods for

  1. adding other agents to its list of agents.
  2. Adding a list of agents to the agents
  3. propagating through time (it should be an empty function)

There should also be some unit tests testing the constructor and and the relevant functions.

vignettes or ...?

figure out whether this can go in documentation or whether we should throw in some examples

Add Community Guidelines

JOSS requires there to be information about the following

Are there clear guidelines for third parties wishing to 1) Contribute to the software 2) Report issues or problems with the software 3) Seek support

We should add this information to the readme

Add paper to new branch

JSOS requires the paper and bibliography exist in a separate branch.

Create a new branch and add the materials there.

Rename population_manager to winik_mgr

In an ideal world, the village class would have a member variable called winik_manager. Unfortunately, R makes you assign classes to variables. Since we named the winik manager winik_manager, it becomes ambiguous if we start creating variables called winik_manager that isn't a reference to the winik_manager class.

This is why the population_manager member variable in the village is called population_mgr instead of population_manager. When I initially wrote the winik_manager integration I didn't think of the possible _mgr ending-so I named it population_manager. There isn't a good synonym for resource, so I think the best option is to keep the first part of the class name, and just append _mgr to the end.

Tidy up the DESCRIPTION File

We should include

  • The version of R that this depends on
  • A more accurate description (now that we know exactly what this is)
  • A better title
  • URL
  • Bug report link

Make Everything Tidy

There's an issue for adding a linter, but converting the syntax and code style to something tidy will be a lot of work-hence this issue.

Requirements

  • Go through each file and convert the syntax to something that matches the tidy style guide (I think there are tools that do this automatically)
  • Search and destroy non-tidy things like for loops and make make them tidy
  • Go through the vignettes and make sure they're tidy

Notes

It would be best to do this while

  • a. There aren't any open pull requests
  • b. No one else is working on anything

Add Support for Days and Months

Background

In the Sept 17th call, we decided that we want to add support for representing time using the Mayan calendar system. The main goal of this is to tie events to particular dates.

Use Cases:

I want to be able to tell, based on a year and month what season it is and what events happen that month.

I want to be able to convert from the Mayan calendar system to the Gregorian system

Other Questions

Which calendar system do we want to support? It looks the the two predominant ones are the 260 day calendar and the 365 day calendar? It's of course possible to support both, but would take more time.

Other Notes

If we're adding support for the month and year-we're going to have to inevitably do it for days too (not a big problem).

Right now we're using the word 'year' as a proxy for the time step. What the year actually represents is dictated by the dynamics defined in the model (my opinion). It sounds like we want to iterate the timestep on the daily resolution, which is fine. I want to make sure we don't lose the ability to simulate on the daily or yearly level either.

To get around this, I'm thinking that we let the user decide the timescale by the way they specify dates. We also don't want to restrict the usage to just the mayan calendar, so we need to define what a mayan date looks like. For example, ISO_8601 clearly defines the format for a date in the Gregorian calendar.

Take the following Gregorian date, 1900-01-01, January first of 1900. Because the year, month, and day were specified-the model runs at the daily resolution. If someone enters the start date as 1900-01, the simulation would run on the monthly level. If someone enters 1900, then it's run on a yearly timescale.

Because the Mayan calendar system is mappable to the Gregorian calendar, I propose we use the Gregorian calendar, within the villager library.

Because dates are important in the models, most of the mayan date time checking will be done outside of villager. For example, if a trade is to be done on each day that is Imix-that logic goes in the researcher's trade function.

Solution

Because the Mayan date time features belong outside of villager, I propose we make a new library (or use an existing, see below) for converting Mayan date times to ISO 8601.

Features

  1. Convert from ISO-8601 to ______ (preferred Mayan calendar)
  2. Given a Mayan calendar date, tell me what the season is
  3. Given a Mayan calendar date, tell me what events are happening on that date (so we can decide whether to trade or not)
  4. Given a Mayan date, give me the pictorial representation (Bonus points)

To Do

  1. Figure out a clever name
  2. Create a repository using the clever name
  3. devtools::create("clever-name")
  4. Create functions for doing features 1-3

Usage

An example of what this would look like if a trade was allowed to happen every 20th day (when events were held)

library(clever-name)

trade <- function(currentState) {

mayan_date <- as.tzolkin(currentState$year)

if (tzolkin.name(mayan_date)) == 'Ajaw') {
# Then do some trade
}


}

Help Wanted: Code Review

We want to submit this to the folks at CRAN and get it approved. If there are any community members that are willing to devote some time (there's not a whole lot of code) to review the library code and submit feedback it would be appreciated! If you're interested in reviewing our code, reply to the issue and we'll get in touch :)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.