Code Monkey home page Code Monkey logo

agents.jl's Introduction

Agents.jl

CI codecov Aqua QA Package Downloads

Agents.jl is a pure Julia framework for agent-based modeling (ABM): a computational simulation methodology where autonomous agents react to their environment (including other agents) given a predefined set of rules. Some major highlights of Agents.jl are:

  1. It is fast (faster than MASON, NetLogo, or Mesa)
  2. It is simple: has a very short learning curve and requires writing minimal code
  3. Has an extensive interface of thousands of out-of-the box possible agent actions
  4. Straightforwardly allows simulations on Open Street Maps
  5. Allows both traditional discrete-time ABM simulations as well as continuous time "event queue based" ABM simulations.

The simplicity of Agents.jl is due to the intuitive space-agnostic modelling approach we have implemented: agent actions are specified using generically named functions (such as "move agent" or "find nearby agents") that do not depend on the actual space the agents exist in, nor on the properties of the agents themselves. Overall this leads to ultra fast model prototyping where even changing the space the agents live in is matter of only a couple of lines of code.

More information and an extensive list of features can be found in the documentation, which you can either find online or build locally by running the docs/make.jl file.

Citation

If you use this package in a publication, or simply want to refer to it, please cite the paper below:

@article{Agents.jl,
  doi = {10.1177/00375497211068820},
  url = {https://doi.org/10.1177/00375497211068820},
  year = {2022},
  month = jan,
  publisher = {{SAGE} Publications},
  pages = {003754972110688},
  author = {George Datseris and Ali R. Vahdati and Timothy C. DuBois},
  title = {Agents.jl: a performant and feature-full agent-based modeling software of minimal code complexity},
  journal = {{SIMULATION}},
  volume = {0},
  number = {0},
}

agents.jl's People

Contributors

aayushsabharwal avatar adriankhl avatar datseris avatar def-mycroft avatar dependabot[bot] avatar fbanning avatar github-actions[bot] avatar imgbot[bot] avatar imgbotapp avatar itsdfish avatar jacobusmmsmit avatar jochenkrattenmacher avatar kavir1698 avatar libbum avatar lilithhafner avatar lmiq avatar ma-ramirez avatar marcelovmaciel avatar mastrof avatar maxkerney avatar mrchaos avatar mt-digital avatar mvmaciel-fitec avatar pitmonticone avatar rebekahanne avatar rht avatar tortar avatar ulido avatar xiruizhao avatar xlxs4 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  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  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

agents.jl's Issues

Some things should be more convenient

At the moment the public API has some rough edges, expecting the user to do operations that I think should be quite simple to support generically. I am writing some examples here, but please feel free to edit this comment to add more.

Adding agents

from the forest fire:

p = rand()
      if p  forest.properties[:p]
        bigest_id = maximum(keys(forest.agents))
        treeid = bigest_id +1
        tree = Tree(treeid, (1,1), true)
        add_agent!(tree, node, forest)
      end

it seems that to add a new Agent the user should always try to find the biggest_id and add one. But in these 4 lines of code, what I want to do is just add one "alive" agent at the node. This should require only one line of code, not 4.

Iterating through the nodes of the space

from forest fire:

 shuffled_nodes = Random.shuffle(1:gridsize(forest))
  for node in shuffled_nodes  # randomly go through the cells and

I don't think the user should do the first line, and should definitely not have to use the undocumented function gridsize. I think we should have some high level functions that iterate through nodes:

  1. Randomly
  2. Sequentially (by node ID)
  3. By population; sort the nodes by the amount of agents they have, having the most populated first (this could be very useful for social models on arbitrary graphs)

Empty nodes

We should define isempty(node, model) that returns internally checks length(model.space.agent_positions[node]) == 0 .

In general I do not find it great design if a user has to access a field of a field of a field and check if it is empty.

Reexport nv, ne or define somefunctions that do that

It is useful to know how many grid points there are. and in the end of the day they are displayed when the model is printed. We should re-export nv, ne from LightGraphs.

Error: SimpleGraph not defined in Forest fire model

When running Forest fire model on Jupyter Lab I get:

mutable struct MyGrid{T<:Integer, Y<:AbstractVector} <: AbstractSpace
  dimensions::Tuple{T, T}
  space::SimpleGraph
  agent_positions::Y  # an array of arrays for each grid node
end
UndefVarError: SimpleGraph not defined

Stacktrace:
 [1] top-level scope at none:0 

Reconsider the amount of plotting bundled into Agents.jl

Here is the thing: Agents.jl is a very, very heavy package.

This is not only because it has a vast list of dependencies, but also because these dependencies are very heavy themselves. Some of these dependencies also pose serious problems, e.g. #17 (which I bet will certainly lead to failures at some point)

What really makes me open this issue though is this:

Exploring the simulation results interactively in Data Voyager 2.

Why would this be bundled in Agents.jl as a dependency? To the best of my understanding, the only thing we do is pass the dataframe to data voyager. Shouldn't this one liner be left to the desire of the user as an extra step?


If we really think about it, the fundamental, science-oriented purpose of Agents.jl should only need LightGraphs, DataFrames as dependencies.

Here is what I want to propose and discuss in this post: we can very slightly limit the offered extra functionality, but by doing so we gain:

  • dropping 10s of heavy dependencies
  • making stability much more safe for the future
  • making installation easier
  • making maintaining Agents.jl much easier. (Let me ask: at the moment, how many things of the extra dependencies work?)
  • Not having to always have nightmares like "What do we do if Fontconfig gets abandoned?" (notice by the way that neither of the authors have replied to us in #17 )

What if we limit the extra dependencies so that the only extra dependency is GraphPlot, to plot agents on their graph? How much do we really lose by doing that?

(I also don't understand why multiple plotting packages Cairo, GraphPlots are dependencies of Agents.jl)

Update data collection aspect of step! for v2.0

Based on the changes of #42 we need to update step!. While doing this we should also take into account the discussion of #38 , which (I think) concludes that there shouldn't be both aggregators and propagators, as one superseeds the other.

(don't forget to branch the v2.0 branch instead of the master branch)

grid definition isn't working

Hello I'm trying to ruhn the schelling model example from the docs.

I keep running into the error "function Grid does not accept keyword arguments"

this seems to be caused by the line:
mygrid = MyGrid(griddims, grid(griddims, false, true), agent_positions)
space.jl seems to be miscalling grid().

Attached the file all the code is copied from the docs. test.txt

[DOCS] examples of non-grid spaces

I think it would be great, and useful, if examples of scientific research using non-grid (i.e. arbitrary graphs) as spaces for agent based modelling are presented/cited in the documentation.

After I am done with #24 #36 we could also consider implementing one of these examples in this library.

`Provide valid colornames` error

Hello,

From the example of the forest fire in the docs, this code:

forest = model_initiation(f=0.05, d=0.8, p=0.01, griddims=(20, 20), seed=2)
data = step!(dummystep, forest_step!, forest, 10, agent_properties, collect(1:10))
for i in 1:5
  visualize_2D_agent_distribution(data, forest, Symbol("pos_$i"), types=Symbol("status_$i"), savename="step_$i", cc=Dict(true=>"green", false=>"red"))
end

returns the error:

"Provide valid colornames."
colorrgb(::Array{String,1}) at visualization.jl:139
#visualize_2D_agent_distribution#95(::Symbol, ::String, ::Dict{Bool,String}, ::Function, ::DataFrames.DataFrame, ::Forest{MyGrid{Int64,Array{Array{Int64,1},1}},Array{Tree,1},Float64}, ::Symbol) at visualization.jl:94
(::getfield(Agents, Symbol("#kw##visualize_2D_agent_distribution")))(::NamedTuple{(:types, :savename, :cc),Tuple{Symbol,String,Dict{Bool,String}}}, ::typeof(visualize_2D_agent_distribution), ::DataFrames.DataFrame, ::Forest{MyGrid{Int64,Array{Array{Int64,1},1}},Array{Tree,1},Float64}, ::Symbol) at none:0
top-level scope at agents.jl:100

ERROR: MethodError: no method matching setindex!(::DataFrames.DataFrame, ::Array{Int64,1}, ::typeof(!), ::Symbol)

I'm getting a new error now:

ERROR: MethodError: no method matching setindex!(::DataFrames.DataFrame, ::Array{Int64,1}, ::typeof(!), ::Symbol)
Closest candidates are:
  setindex!(::DataFrames.DataFrame, ::AbstractArray{T,1} where T, ::AbstractArray{Bool,1}, ::Union{Signed, Symbol, Unsigned}) at C:\Users\mthel\.julia\packages\DataFrames\0Em9Q\src\dataframe\dataframe.jl:561
  setindex!(::DataFrames.DataFrame, ::AbstractArray{T,1} where T, ::AbstractArray{#s68,1} where #s68<:Real, ::Union{Signed, Symbol, Unsigned}) at C:\Users\mthel\.julia\packages\DataFrames\0Em9Q\src\dataframe\dataframe.jl:567
  setindex!(::DataFrames.DataFrame, ::Any, ::Colon, ::Any) at C:\Users\mthel\.julia\packages\DataFrames\0Em9Q\src\dataframe\dataframe.jl:692
  ...
Stacktrace:
 [1] #agents_data_complete#51(::Int64, ::Function, ::Array{Symbol,1}, ::SchellingModel{Int64,Array{SchellingAgent,1},MyGrid{Int64,Array{Array{Int64,1},1}}}) at C:\Users\mthel\.julia\packages\Agents\y9YpK\src\data_collector.jl:93
 [2] #agents_data_complete at .\none:0 [inlined]
 [3] data_collector at C:\Users\mthel\.julia\packages\Agents\y9YpK\src\data_collector.jl:157 [inlined]
 [4] step!(::Function, ::SchellingModel{Int64,Array{SchellingAgent,1},MyGrid{Int64,Array{Array{Int64,1},1}}}, ::Int64, ::Array{Symbol,1}, ::Array{Int64,1}) at C:\Users\mthel\.julia\packages\Agents\y9YpK\src\model_component.jl:77
 [5] top-level scope at none:0

This is thrown by the following line from the Schelling example:

data = step!(agent_step!, model, 2, agent_properties, steps_to_collect_data)

I'm using the latest version of Agents Agents#master and I just updated my DataFrames package as well....

Bugs in add_agent!(node, model::ABM, properties...)

  1. If the space is a GridSpace and we provide a correct node in tuples, it doesn't work because it always converts position types (cnode = correct_pos_type(node, model)):
using Agents

mutable struct Agent1 <: AbstractAgent
  id::Int 
  pos::Tuple{Int,Int}
end
model1 = ABM(Agent1, Space((3,3)))

add_agent!((1,1), model1)

ERROR: MethodError: vertex2coord(::Tuple{Int64,Int64}, ::AgentBasedModel{Agent2,Agents.GridSpace{LightGraphs.SimpleGraphs.SimpleGraph{Int64},2,Int64},typeof(as_added),Nothing}) is ambiguous. Candidates:
  vertex2coord(v::Tuple, args...) in Agents at /mnt/d/projects/Agents.jl/src/core/space.jl:291
  vertex2coord(c, model::AgentBasedModel) in Agents at /mnt/d/projects/Agents.jl/src/core/space.jl:218
Possible fix, define
  vertex2coord(::Tuple, ::AgentBasedModel)
Stacktrace:
 [1] correct_pos_type(::Tuple{Int64,Int64}, ::AgentBasedModel{Agent2,Agents.GridSpace{LightGraphs.SimpleGraphs.SimpleGraph{Int64},2,Int64},typeof(as_added),Nothing}) at /mnt/d/projects/Agents.jl/src/core/model.jl:27
 [2] add_agent!(::Tuple{Int64,Int64}, ::AgentBasedModel{Agent2,Agents.GridSpace{LightGraphs.SimpleGraphs.SimpleGraph{Int64},2,Int64},typeof(as_added),Nothing}) at /mnt/d/projects/Agents.jl/src/core/agent_space_interaction.jl:140
 [3] top-level scope at REPL[10]:1
  1. It expects a certain order of agent fields: first id and second pos:
mutable struct Agent2 <: AbstractAgent
  pos::Tuple{Int,Int}
  id::Int 
end

model2 = ABM(Agent2, Space((3,3)))

add_agent!(1, model2)

ERROR: MethodError: Cannot `convert` an object of type Int64 to an object of type Tuple{Int64,Int64}
Closest candidates are:
  convert(::Type{T<:Tuple{Any,Vararg{Any,N} where N}}, ::T<:Tuple{Any,Vararg{Any,N} where N}) where T<:Tuple{Any,Vararg{Any,N} where N} at essentials.jl:303
  convert(::Type{T<:Tuple{Any,Vararg{Any,N} where N}}, ::Tuple{Any,Vararg{Any,N} where N}) where T<:Tuple{Any,Vararg{Any,N} where N} at essentials.jl:304
  convert(::Type{T<:Tuple}, ::CartesianIndex) where T<:Tuple at multidimensional.jl:133
  ...
Stacktrace:
 [1] Agent2(::Int64, ::Tuple{Int64,Int64}) at ./REPL[3]:2
 [2] add_agent!(::Int64, ::AgentBasedModel{Agent2,Agents.GridSpace{LightGraphs.SimpleGraphs.SimpleGraph{Int64},2,Int64},typeof(as_added),Nothing}) at /mnt/d/projects/Agents.jl/src/core/agent_space_interaction.jl:141
 [3] top-level scope at REPL[26]:1

Logo for Agents.jl

Dearest @cormullion ,

I want to develop a logo for this wonderful package Agents.jl that just joined JuliaDynamics. Do you think you could give some ideas? We already had a discussion about this on Slack, but of course all is lost now...

I was thinking something like a cellular automaton whose "live" cells spelled Agents.jl :D

add_agent! default method to use agent's position

Given the question 2. in #36 , I propose to improve add_agent! as follows:

add_agent!(agent::AbstractAgent, model::AbstractModel, pos = agent.pos)

which adds the agent to the specified pos. If you want a random position instead, you can give nothing as the third argument.

What do you think?

Relating ABM with statistics

This is just a suggestion to write some examples relating to ABM and statistical analysis. I have been working with R-Extensions for NetLogo and I think that within Julia it should be easy to incorporate ABM and statistical analysis without recurring to two languages.

Would you like to join JuliaDynamics?

Hey there,

let me start with an important declaration: The discussion here will in no way affect my review at JOSS.

Agents.jl is a Julia package that revolves around agent-based-modeling and cellular automata. I unfortunately was not aware about this until I was given the opportunity to review it... JuliaDynamics is a GitHub organization about dynamical systems and nonlinear dynamics (and also recently for assisting scientific projects). It contains several packages already, one being the award-winning DynamicalSystems.jl. What is notably missing from JuliaDynamics are systems with discrete space and time (i.e. cellular automata).

https://juliadynamics.github.io/JuliaDynamics/

Agents.jl seems to fit in JuliaDynamics from a scientific perspective. In addition, its documentation clearly matches the standards of JuliaDynamics, providing examples, tutorials and scientific motivation on the use of ABM. At JuliaDynamics we try to be a coherent unit ("organization") that can spread word and interest for dynamical systems. It is also very good for users to have packages of similar scientific orientation to organizations. It makes them easier to find and more trustworthy

I would like to invite you to join us at JuliaDynamics. What we will gain is that your excellent work will be part of our collective effort for creating high quality libraries for dynamical systems. What you'll gain by joining:

  • A logo for Agents.jl
  • Help in maintaining the repo
  • Pull Requests review
  • Documentation improvements (if necessary)
  • Be part of an already recognized (and highly active) GitHub org.
  • More exposure: easier to find it if part of JuliaDynamics, and also showcased in our website. I think you could benefit from this.

Let me know what you think! In a similar way that I approached you, I also approached RecurrenceAnalysis.jl.

please notice that for conflict-of-interest purposes, this can only progress from my side after the review of JOSS is over

questions regarding the API

Hello there,

I've got some questions regarding the current API. I am looking specifically at the forest fire example, but I think the same thing applies to all examples.

  1. Why use the lengthy syntax, like for node in 1:gridsize(forest.space.dimensions) instead of using for node in verteces(space) ?
  2. In this loop:
      tree = Tree(node, (1,1), true)
      add_agent!(tree, node, forest) # TODO: why is the agent getting (1,1)?

why do the agents get position (1,1)? Shouldn't they be getting the position of the node? This confuses me a lot...

  1. Later on, there is shuffle(1:gridsize(forest.space.dimensions)) which could instead become shuffle(verteces(forest.space))

In general, while I am working on #20 , I am trying to simplify the API. I am asking these questions to see if there is a fundamental design decision behind them. If not, then I think the simplest, clearest, and shortest version should be used.

I don't think it is beneficial to define a second API on top of the LightGraphs API, which I think is what happens at the moment with gridsize.

Warnings when precompiling

[ Info: Precompiling Agents [46ada45e-f475-11e8-01d0-f70cc89e6671]
┌ Warning: special characters "#{}()[]<>|&*?~;" should now be quoted in commands
│   caller = #shell_parse#351(::String, ::Function, ::String, ::Bool) at shell.jl:100
└ @ Base ./shell.jl:100

Proposal: Scheduler type

Hi there,

I think it might be worth it to implement a Scheduler type that acts as a functor. The inspiration for this comes from:

"""
    partial_activation(model::AbstractModel)
At each step, activates only `activation_prob` number of randomly chosen of individuals with a `activation_prob` probability. `activation_prob` should be a field in the model and between 0 and 1.
"""
function partial_activation(model::AbstractModel)
  agentnum = nagents(model)
  return randsubseq(1:agentnum, model.activation_prob)
end

This function requires the model to have a field activation_prob. This is restrictive. In addition, this activation_prob is (in my intuition) not a property of the model, but specific for the scheduler.

Wouldn't it be more intuitive to define something like:

abstract type Scheduler end
struct AsAdded <: Scheduler end
struct RandomScheduler <: Scheduler end
struct PartialScheduler{R<:Real} <: Scheduler
    probability::R
end

function (s::AsAdded)(model::AbstractModel)
    # stuff
end

function (s::PartialScheduler)(model::AbstractModel)
  agentnum = nagents(model)
  return randsubseq(1:agentnum, s.probability)
end

? just a suggestion, but this would make it easier to document the API and also leverage multiple dispatch.


The documentation would then be straight forward:

  1. Define a subtype of Scheduler.
  2. Define a functor dispatch on (s::Scheduler)(model::AbstractModel)
  3. Be sure the return type of your scheduler is TypeUsedByAgents.jl

BoundsError when adding/removing agents

I am getting an out of bounds error in my model when I use my own agent_step! function. Here, I regularly use kill_agent! and add_agent!. It seems that my error is due to step! using indexing based on agent id while each agent step is run as agent_step(model.agents[index], model), i.e. position in agent list. Is this a bug or am I doing something wrong here?

[Doc] Document visualization

Hi, I am running the examples/schelling.jl file. It runs without a problem, its final output being:

0: blue
1: red
0: blue
1: red

Isn't the file supposed to plot something? Seems like the name of the function visualize_2D_agent_distribution intends to plot. Can someone confirm whether this function does plot for them?

I am running windows 10. Agents master branch. Installation worked fine, but I am getting the problem of #17

Proposal: Refactor documentation to use the examples of `examples`, Literate.jl and Documenter.jl

I have a proposal to make, which will take some effort but it won't bring new features to Agents.jl. Nevertheless, I think it is worth it.

The proposal is to refactor the examples part of the documentation, so that instead of being static code, to be real Julia code that runs and whose output is shown in the documentation. This has the following benefits:

  1. It ensures that the code is runnable. If it isn't the docs won't build properly.
  2. It ensures that everything is up to date.
  3. It also makes it easier to update the documentation after an update on the code, or when changing a parameter.
  4. The examples folder is used directly in the documentation.

Here is how this works. We do it in TimeseriesPrediction.jl. We have some examples here:
https://github.com/JuliaDynamics/TimeseriesPrediction.jl/tree/master/examples
These examples are valid Julia code, which is runnable. They follow the Literate.jl syntax, which means Literate can process then and make them markdown files for the docs. Then in the make.jl we first convert them to markdown, and then documenter makes the doc page.

Here is how it looks: https://juliadynamics.github.io/TimeseriesPrediction.jl/latest/stexamples/

In the TimeseriesPrediction example we do one extra step to include all examples in a single file; I think here this should be avoided and each example should stay a separate file. So don't mind the function replace_includes

I can contribute here once the JOSS review is over.

Update benchmarks for 2.0

A lot of changes happened to the source code on the way to 2.0. This could have impact on performance, possibly even regressions. By re-doing the benchmarks we will be able to identify such regressions.

Fontconfig / Compose problem

On a clean install of Agents (master branch) one gets:


julia> using Agents
[ Info: Recompiling stale cache file C:\Users\datseris\.julia\compiled\v1.1\Agents\nTsV8.ji for Agents [46ada45e-f475-11e8-01d0-f70cc89e6671]
┌ Warning: Error requiring Fontconfig from Compose:
│ LoadError: UndefVarError: Cairo not defined
│ Stacktrace:
│  [1] top-level scope at none:0
│  [2] include at .\boot.jl:326 [inlined]
│  [3] include_relative(::Module, ::String) at .\loading.jl:1038
│  [4] include at .\sysimg.jl:29 [inlined]
│  [5] include(::String) at C:\Users\datseris\.julia\packages\Compose\aVZwV\src\Compose.jl:1
│  [6] top-level scope at C:\Users\datseris\.julia\packages\Compose\aVZwV\src\Compose.jl:178
│  [7] eval at .\boot.jl:328 [inlined]
│  [8] eval at C:\Users\datseris\.julia\packages\Compose\aVZwV\src\Compose.jl:1 [inlined]
│  [9] (::getfield(Compose, Symbol("##118#124")))() at C:\Users\datseris\.julia\packages\Requires\9Jse8\src\require.jl:67
│  [10] err(::getfield(Compose, Symbol("##118#124")), ::Module, ::String) at C:\Users\datseris\.julia\packages\Requires\9Jse8\src\require.jl:38
│  [11] #117 at C:\Users\datseris\.julia\packages\Requires\9Jse8\src\require.jl:66 [inlined]
│  [12] withpath(::getfield(Compose, Symbol("##117#123")), ::String) at C:\Users\datseris\.julia\packages\Requires\9Jse8\src\require.jl:28
│  [13] (::getfield(Compose, Symbol("##116#122")))() at C:\Users\datseris\.julia\packages\Requires\9Jse8\src\require.jl:65
│  [14] #invokelatest#1 at .\essentials.jl:742 [inlined]
│  [15] invokelatest at .\essentials.jl:741 [inlined]
│  [16] #3 at C:\Users\datseris\.julia\packages\Requires\9Jse8\src\require.jl:19 [inlined]
│  [17] iterate at .\generator.jl:47 [inlined]
│  [18] _collect(::Array{Function,1}, ::Base.Generator{Array{Function,1},getfield(Requires, Symbol("##3#4"))}, ::Base.EltypeUnknown, ::Base.HasShape{1}) at .\array.jl:619
│  [19] map at .\array.jl:548 [inlined]
│  [20] loadpkg(::Base.PkgId) at C:\Users\datseris\.julia\packages\Requires\9Jse8\src\require.jl:19
│  [21] #invokelatest#1 at .\essentials.jl:742 [inlined]
│  [22] invokelatest at .\essentials.jl:741 [inlined]
│  [23] _tryrequire_from_serialized(::Base.PkgId, ::UInt64, ::Nothing) at .\loading.jl:651
│  [24] _require_from_serialized(::String) at .\loading.jl:679
│  [25] _require(::Base.PkgId) at .\loading.jl:967
│  [26] require(::Base.PkgId) at .\loading.jl:858
│  [27] require(::Module, ::Symbol) at .\loading.jl:853
│  [28] eval(::Module, ::Any) at .\boot.jl:328
│  [29] eval_user_input(::Any, ::REPL.REPLBackend) at C:\cygwin\home\Administrator\buildbot\worker\package_win64\build\usr\share\julia\stdlib\v1.1\REPL\src\REPL.jl:85
│  [30] macro expansion at C:\cygwin\home\Administrator\buildbot\worker\package_win64\build\usr\share\julia\stdlib\v1.1\REPL\src\REPL.jl:117 [inlined]
│  [31] (::getfield(REPL, Symbol("##26#27")){REPL.REPLBackend})() at .\task.jl:259
│ in expression starting at C:\Users\datseris\.julia\packages\Compose\aVZwV\src\pango.jl:3
└ @ Requires C:\Users\datseris\.julia\packages\Requires\9Jse8\src\require.jl:40

This may become a problem at some point even though currently it works.

Update to new dataframes interface

DataFrames has updated their interface completely, and now deprecation warnings are being thrown at the tests:

Test Summary: | Pass  Total
all the rest  |   10     10
┌ Warning: `setindex!(df::DataFrame, v::AbstractVector, col_ind::ColumnIndex)` is deprecated, use `begin
│     df[!, col_ind] = v
│     df
│ end` instead.
│   caller = #agents_data_complete#51(::Int64, ::Function, ::Array{Symbol,1}, ::Forest) at data_collector.jl:92
└ @ Agents C:\Users\datseris\.julia\packages\Agents\cBaCb\src\data_collector.jl:92
┌ Warning: `setindex!(df::DataFrame, v::AbstractVector, col_ind::ColumnIndex)` is deprecated, use `begin
│     df[!, col_ind] = v
│     df
│ end` instead.
│   caller = #agents_data_complete#51(::Int64, ::Function, ::Array{Symbol,1}, ::Forest) at data_collector.jl:94
└ @ Agents C:\Users\datseris\.julia\packages\Agents\cBaCb\src\data_collector.jl:94
Test Summary:  | Pass  Total
data_collector |    4      4
   Testing Agents tests passed

I'd recommend to fix them asap because in the next version of DataFrames those will be errors instead.

Error running Forest_fire

Another error:
When I do

forest = model_initiation(f=0.05, d=0.8, p=0.01, griddims=(20, 20), seed=2)
data = step!(dummystep, forest_step!, forest, 10, agent_properties, collect(1:10))
for i in 1:10
  visualize_2D_agent_distribution(data, forest, Symbol("pos_$i"), types=Symbol("status_$i"), savename="step_$i", cc=Dict(true=>"green", false=>"red"))
end

I get

ArgumentError: column name :pos_1 not found in the data frame

Stacktrace:
 [1] lookupname at /Users/sergiobacelar/.julia/packages/DataFrames/CZrca/src/other/index.jl:167 [inlined]
 [2] (::getfield(DataFrames, Symbol("##19#20")){DataFrames.Index})(::Symbol) at ./none:0
 [3] collect(::Base.Generator{Array{Symbol,1},getfield(DataFrames, Symbol("##19#20")){DataFrames.Index}}) at ./generator.jl:47
 [4] getindex at /Users/sergiobacelar/.julia/packages/DataFrames/CZrca/src/other/index.jl:177 [inlined]
 [5] getindex(::DataFrames.DataFrame, ::Array{Symbol,1}) at /Users/sergiobacelar/.julia/packages/DataFrames/CZrca/src/dataframe/dataframe.jl:300
 [6] #visualize_2D_agent_distribution#87(::Symbol, ::String, ::Dict{Bool,String}, ::Function, ::DataFrames.DataFrame, ::Forest{MyGrid{Int64,Array{Array{Integer,1},1}},Array{Tree,1},Float64}, ::Symbol) at /Users/sergiobacelar/.julia/packages/Agents/2wiNF/src/visualization.jl:83
 [7] (::getfield(Agents, Symbol("#kw##visualize_2D_agent_distribution")))(::NamedTuple{(:types, :savename, :cc),Tuple{Symbol,String,Dict{Bool,String}}}, ::typeof(visualize_2D_agent_distribution), ::DataFrames.DataFrame, ::Forest{MyGrid{Int64,Array{Array{Integer,1},1}},Array{Tree,1},Float64}, ::Symbol) at ./none:0
 [8] top-level scope at ./In[5]:4

Error when running Schelling.jl (again)

When running Schelling.jl using Juno(in Julia 1.2) when:

# Instantiate the model with 370 agents on a 20 by 20 grid. 
model = instantiate_model(numagents=370, griddims=(20,20), min_to_be_happy=3)

I receive the following error:
function Grid does not accept keyword arguments
I don't know if this is related with (#10)

Error running Cellular Automata first example on Jupyter lab

When running the example in Jupyter lab on macOS 10.15.1 I get:

type Int64 has no field id

Stacktrace:
 [1] getproperty(::Any, ::Symbol) at ./Base.jl:20
 [2] (::getfield(Agents, Symbol("##33#34")))(::Int64) at ./none:0
 [3] iterate at ./generator.jl:47 [inlined]
 [4] collect at ./array.jl:606 [inlined]
 [5] as_added(::Agents.CA1D.Model{Array{Agents.CA1D.Agent{Int64,String},1},Dict{String,String},Agents.CA1D.Space{Int64,Array{Array{Integer,1},1}}}) at /Users/sergiobacelar/.julia/packages/Agents/BYNnZ/src/scheduler.jl:8
 [6] return_activation_order(::Agents.CA1D.Model{Array{Agents.CA1D.Agent{Int64,String},1},Dict{String,String},Agents.CA1D.Space{Int64,Array{Array{Integer,1},1}}}) at /Users/sergiobacelar/.julia/packages/Agents/BYNnZ/src/scheduler.jl:32
 [7] step!(::typeof(dummystep), ::typeof(Agents.CA1D.ca_step!), ::Agents.CA1D.Model{Array{Agents.CA1D.Agent{Int64,String},1},Dict{String,String},Agents.CA1D.Space{Int64,Array{Array{Integer,1},1}}}) at /Users/sergiobacelar/.julia/packages/Agents/BYNnZ/src/model_component.jl:154
 [8] step!(::Function, ::Function, ::Agents.CA1D.Model{Array{Agents.CA1D.Agent{Int64,String},1},Dict{String,String},Agents.CA1D.Space{Int64,Array{Array{Integer,1},1}}}, ::Int64, ::Array{Symbol,1}, ::Array{Int64,1}) at /Users/sergiobacelar/.julia/packages/Agents/BYNnZ/src/model_component.jl:184
 [9] ca_run(::Agents.CA1D.Model{Array{Agents.CA1D.Agent{Int64,String},1},Dict{String,String},Agents.CA1D.Space{Int64,Array{Array{Integer,1},1}}}, ::Int64, ::String) at /Users/sergiobacelar/.julia/packages/Agents/BYNnZ/src/CA1D.jl:72 (repeats 2 times)
 [10] top-level scope at In[15]:3

Extend to GPUs

To consider much larger ABMs, it would be great if we can extend Agents.jl to handle GPUs.

Make space optional

Not all models have spatial structure. It would be better to default the space field of the ABM function to nothing. Of course, none of the agent-space interaction functions will work when there is no space. But that is not an issue for a model that has no spatial structure.

Error with Cairo when using Agents

When I try using Agents I get the following error:

┌ Info: Precompiling Agents [46ada45e-f475-11e8-01d0-f70cc89e6671]
└ @ Base loading.jl:1186
ERROR: LoadError: Cairo not properly installed. Please run
Pkg.build("Cairo")

After trying to Pkg.build("Cairo") I get:

┌ Error: Error building `Cairo`: 
│ 
│ signal (11): Segmentation fault: 11
│ in expression starting at /Users/sergiobacelar/.julia/packages/Cairo/CXPG1/deps/build.jl:165
│ _platform_strcmp at /usr/lib/system/libsystem_platform.dylib (unknown line)
│ Allocations: 19789750 (Pool: 19786916; Big: 2834); GC: 44
└ @ Pkg.Operations /Users/osx/buildbot/slave/package_osx64/build/usr/share/julia/stdlib/v1.1/Pkg/src/Operations.jl:1075

Parallel computing

For parallel simulations to work, we need to modify the code so that one can define the number of workers. We also have to make sure Agents is defined on all the workers.

Update `agent_step!` function in Boltzmann-with-grid example

While making the edits for pull request 26, I noticed that the agent_step! function needs to be modified in the latter part of the tutorial.

In the file boltzmann_example01.md, the first part of the example deals with a Boltzmann model that does not have a spatial grid. Later in the example, a spatial structure is added and the model is modified so that, instead of giving wealth to a random agent, agents give their wealth to an agent that is "nearby".

The agent_step! function at the end of boltzmann_example01.md is the same function as was used earlier in the example. It needs to be modified to accurately reflect the description in the example, then the corresponding script in examples/ should be updated as well (note that after pull request 26 is merged, there will be two scripts in examples/, boltzmann_wealth_distribution.jl and boltzmann_wealth_distribution_with_grid.jl).

[DOC] Statement of the APIs

From my understanding reading the docs, Agents seems to define some APIs that the user-defined types must follow. For example, it seems to me that Model and scheduler functions have a specific form, or should have a specific form.

is this true?

If yes, I think this has to be stated somewhere explicitly and not only via examples. E.g. the expected fields that subtypes of AbstractModel should have.

I am now looking at the Schelling's segregation model doc page. I realize that what I ask here is partly described there. This is (in my personal opinion) not an intuitive mix. API and Package Workflow should have their own dedicated page, and examples should follow after.

My suggestion would be to move the first section of that page to a dedicated page, and enrich it with API instructions for AbstractModel, scheduler, etc...

Error running schelling.jl

Julia> include("schelling.jl")
ERROR: LoadError: type SchellingAgent has no field wealth
Stacktrace:
 [1] getproperty(::Any, ::Symbol) at ./sysimg.jl:18
 [2] agent_step!(::SchellingAgent, ::SchellingModel) at /Users/sergiobacelar/Dropbox/jupyter_notebooks/Julia_Agents_examples/boltzmann_wealth_distribution.jl:67
 [3] step!(::typeof(agent_step!), ::SchellingModel) at /Users/sergiobacelar/.julia/packages/Agents/2wiNF/src/model_component.jl:52
 [4] step!(::Function, ::SchellingModel, ::Int64, ::Array{Symbol,1}, ::Array{Int64,1}) at /Users/sergiobacelar/.julia/packages/Agents/2wiNF/src/model_component.jl:80
 [5] top-level scope at none:0
 [6] include at ./boot.jl:326 [inlined]
 [7] include_relative(::Module, ::String) at ./loading.jl:1038
 [8] include(::Module, ::String) at ./sysimg.jl:29
 [9] include(::String) at ./client.jl:403
 [10] top-level scope at none:0

ERROR: function Grid does not accept keyword arguments

I'm trying to run the Schelling example from the tutorial and I am getting this error:

ERROR: function Grid does not accept keyword arguments

My code looks like this:

using Agents

mutable struct SchellingModel{T<:Integer, Y<:AbstractArray, Z<:AbstractSpace} <: AbstractModel 
    space::Z
    agents::Y
    scheduler::Function
    min_to_be_happy::T
end

mutable struct SchellingAgent{T<:Integer} <: AbstractAgent
    id::T
    pos::Tuple{T, T}
    mood::Bool
    group::T
end

mutable struct MyGrid{T<:Integer, Y<:AbstractArray} <: AbstractSpace
    dimensions::Tuple{T, T}
    space::SimpleGraph
    agent_positions::Y  
end

function instantiate_model(;numagents=320, griddims=(20, 20), min_to_be_happy=3)
    agent_positions = [Int64[] for i in 1:gridsize(griddims)]
    mygrid = MyGrid(griddims, grid(griddims, false, true), agent_positions)
    model = SchellingModel(mygrid, SchellingAgent[], random_activation, min_to_be_happy) 
    agents = vcat([SchellingAgent(Int(i), (1,1), false, 0) for i in 1:(numagents/2)], [SchellingAgent(Int(i), (1,1), false, 1) for i in (numagents/2)+1:numagents])
    for agent in agents
      add_agent_single!(agent, model)
    end
    return model
end

function agent_step!(agent, model)
    if agent.mood == true
      return
    end
    while agent.mood == false
      neighbor_cells = node_neighbors(agent, model)
      count_neighbors_same_group = 0
      for neighbor_cell in neighbor_cells
        node_contents = get_node_contents(neighbor_cell, model)
        if length(node_contents) == 0
          continue
        else
          node_contents = node_contents[1]
        end
        neighbor_agent_group = model.agents[node_contents].group
        if neighbor_agent_group == agent.group
          count_neighbors_same_group += 1
        end
      end
      if count_neighbors_same_group >= model.min_to_be_happy
        agent.mood = true
      else
        move_agent_single!(agent, model)
      end
    end
end

model = instantiate_model(numagents=370, griddims=(20,20), min_to_be_happy=3)

Create unified AgentBasedModel

In PR #52 it a nice change was introduced that makes the model struct unified and all devs liked it.

This should be ported in the v2.0 branch.

Titles of data collection via aggergators should be improved...

In the forest fire model I am doing:

forest = model_initiation(f=0.05, d=0.8, p=0.01, griddims=(20, 20), seed=2)
agent_properties = Dict(:status => [x -> count(x)/400])
when = 1:10

data = step!(forest, dummystep, forest_step!, 10, agent_properties, when=when)

which returns:

2 DataFrames.DataFrame
│ Row │ status_getfield(Main, Symbol("##12#13"))() │ step  │
│     │ Float64                                    │ Int64 │
├─────┼────────────────────────────────────────────┼───────┤
│ 1   │ 0.0575                                     │ 1     │
│ 2   │ 0.06                                       │ 2     │
│ 3   │ 0.0675                                     │ 3     │
│ 4   │ 0.075                                      │ 4     │
│ 5   │ 0.0725                                     │ 5     │
│ 6   │ 0.085                                      │ 6     │
│ 7   │ 0.09                                       │ 7     │
│ 8   │ 0.0925                                     │ 8     │
│ 9   │ 0.1                                        │ 9     │
│ 10  │ 0.105                                      │ 10    │

Okay, you can say that this is because I defined an anonymous function. But I definitely imagine that one of the main use cases of this aggregation will be with anonymous functions.

On the otherhand, using a named function:

average_green(x) = count(x)/400
agent_properties = Dict(:status => [average_green])
forest = model_initiation(f=0.05, d=0.8, p=0.01, griddims=(20, 20), seed=2)
data = step!(forest, dummystep, forest_step!, 10, agent_properties, when=when)

gives

10×2 DataFrames.DataFrame
│ Row │ status_average_green │ step  │
│     │ Float64              │ Int64 │
├─────┼──────────────────────┼───────┤
│ 1   │ 0.8175               │ 1     │
│ 2   │ 0.4975               │ 2     │
│ 3   │ 0.0375               │ 3     │
│ 4   │ 0.015                │ 4     │
│ 5   │ 0.02                 │ 5     │
│ 6   │ 0.0325               │ 6     │
│ 7   │ 0.035                │ 7     │
│ 8   │ 0.0425               │ 8     │
│ 9   │ 0.055                │ 9     │
│ 10  │ 0.0575               │ 10    │

which is better. But maybe it would be even better if we changed the title to use function_name(property), which would display here as average_green(status). I think this is more reasonable. If someone used mean as the function, then the field would display as mean(status) which makes super much sense!

[Doc] Possible improvements of the documentation homepage

Hi there,

As part of the JOSS review, I'd like to suggest the following improvements of the documentation, that you could implement optionally if you want to:

  • Itemize the components of the paragraph starting with Agents.jl provides users with core components that make it easy to build ABMS, run them in batch, collect model outputs, and visualize the results.... Specifically, the bolder words could be made to start items. This will increase readability and make overviewing easier.
  • The documentation starts immediately with the functionality of the package. Although this is fine assuming the newcomer user already knows what ABM is, it may not be optimal for people that check out Agents.jl but are not completely familiar with ABM. I'd suggest to add a small new paragraph in the intro that in a couple of sentences summarizes ABM and provides a physical / social example.
  • Although a very detailed exposure of existing software is made in the introduction, there may be a lack of resources from the scientific perspective. In the aforementioned new paragraph I'd suggest to also link some standard textbooks or webpages on the subject.

(will be updating this as I read more of the Docs)

The methods of the step! function

Hi there,

in the process of redesigning and vastly simplifying the API, we have to face the current step! function. The problem from my eyes, is that this function is unwieldy.

Not only it has 10 methods, but it even does multiple dispatch in 3 different argument positions. Something tells me hat things will be much simpler if we just separate many of its methods into different functions. What was especially confusing for me was the story with the aggregators, propagators, and all that jazz. To my understanding, step! has four (!!!) different functionalities:

  1. Normal stepping.
  2. Collecting agent properties
  3. Appying aggregators
  4. Doing whatever propagg does (I have not understood this at all.

(also, it seems that numbers 2, 3 and 4 cannot happen at the same time)

In my eyes, this is way too much stuff that are done by a single function name. A function should do one, and only one thing. This keeps things clear, and most importantly easy to learn.

@kavir1698 , can you please give some input here, on how we can simplify this approach?

My suggestion would be to have step!, collect!, aggregate!, propaggate! ( still don't know what this is).

Defining a space - Why?

This comes after reading the documentation tutorial, specifically the part "Defining a space".

I have not understood at all why a user has to define a space by creating a struct. At least not at all for the simple grid cases.

Wouldn't it be much better to avoid all these complications and simply provide simple constructors like:

space = Space(x, y, z; periodic = true, Moore = false)

where internally Space does something like

Space(args...; kwargs...) = Space((args...,,), grid(args...; kwags...), instantiate_agents(grid))

and returns the resulting struct? Here Space is the standard Space type of Agents.jl (currently not existing), which would replace the MyGrid of the documentation.

From what I have understood, the only information the user really cares about is simply the dimensionality of the space, and whether it is Moore/periodic. Everything else is so that Agents.jl operates internally. Thus this information should stay internal and the user should have to care about it / create a Space struct.


Of course it should be stated that this Space struct contains the actual agent locations, but still this is advanced usage I feel.

Warnings about setindex and getindex in data_collector.jl and visualization.jl

I wanted to document that there are a couple of warning messages related to setindex and getindex.

When running schelling.jl in examples I see the following warning messages. All of these came from either data_collector.jl or visualization.jl.

I'm on julia version 1.0.3 and Agents v1.1.5.

First setindex warning, data_collector.jl:92:

 Warning: `setindex!(df::DataFrame, v::AbstractVector, col_ind::ColumnIndex)` is deprecated, use `begin
│     df[!, col_ind] = v
│     df
│ end` instead.
│   caller = #agents_data_complete#51(::Int64, ::Function, ::Array{Symbol,1}, ::SchellingModel{Int64,Array{SchellingAgent,1},MyGrid{Int64,Array{Array{Int64,1},1}}}) at data_collector.jl:92
└ @ Agents ~/.julia/packages/Agents/NQyfq/src/data_collector.jl:92

Second setindex warning, data_collector.jl:94:

┌ Warning: `setindex!(df::DataFrame, v::AbstractVector, col_ind::ColumnIndex)` is deprecated, use `begin
│     df[!, col_ind] = v
│     df
│ end` instead.
│   caller = #agents_data_complete#51(::Int64, ::Function, ::Array{Symbol,1}, ::SchellingModel{Int64,Array{SchellingAgent,1},MyGrid{Int64,Array{Array{Int64,1},1}}}) at data_collector.jl:94
└ @ Agents ~/.julia/packages/Agents/NQyfq/src/data_collector.jl:94

Warnings about getindex in visualization.jl lines 83, 84, 103, 104, 109:

┌ Warning: `getindex(df::DataFrame, col_inds::Union{AbstractVector, Regex, Not})` is deprecated, use `df[:, col_inds]` instead.
│   caller = #visualize_2D_agent_distribution#95(::Symbol, ::String, ::Dict{Int64,String}, ::Function, ::DataFrames.DataFrame, ::SchellingModel{Int64,Array{SchellingAgent,1},MyGrid{Int64,Array{Array{Int64,1},1}}}, ::Symbol) at visualization.jl:83
└ @ Agents ~/.julia/packages/Agents/NQyfq/src/visualization.jl:83
┌ Warning: `getindex(df::DataFrame, col_ind::ColumnIndex)` is deprecated, use `df[!, col_ind]` instead.
│   caller = #visualize_2D_agent_distribution#95(::Symbol, ::String, ::Dict{Int64,String}, ::Function, ::DataFrames.DataFrame, ::SchellingModel{Int64,Array{SchellingAgent,1},MyGrid{Int64,Array{Array{Int64,1},1}}}, ::Symbol) at visualization.jl:84
└ @ Agents ~/.julia/packages/Agents/NQyfq/src/visualization.jl:84
┌ Warning: `getindex(df::DataFrame, col_ind::ColumnIndex)` is deprecated, use `df[!, col_ind]` instead.
│   caller = #visualize_2D_agent_distribution#95(::Symbol, ::String, ::Dict{Int64,String}, ::Function, ::DataFrames.DataFrame, ::SchellingModel{Int64,Array{SchellingAgent,1},MyGrid{Int64,Array{Array{Int64,1},1}}}, ::Symbol) at visualization.jl:103
└ @ Agents ~/.julia/packages/Agents/NQyfq/src/visualization.jl:103
┌ Warning: `getindex(df::DataFrame, col_ind::ColumnIndex)` is deprecated, use `df[!, col_ind]` instead.
│   caller = #visualize_2D_agent_distribution#95(::Symbol, ::String, ::Dict{Int64,String}, ::Function, ::DataFrames.DataFrame, ::SchellingModel{Int64,Array{SchellingAgent,1},MyGrid{Int64,Array{Array{Int64,1},1}}}, ::Symbol) at visualization.jl:104
└ @ Agents ~/.julia/packages/Agents/NQyfq/src/visualization.jl:104
┌ Warning: `getindex(df::DataFrame, col_ind::ColumnIndex)` is deprecated, use `df[!, col_ind]` instead.
│   caller = #visualize_2D_agent_distribution#95(::Symbol, ::String, ::Dict{Int64,String}, ::Function, ::DataFrames.DataFrame, ::SchellingModel{Int64,Array{SchellingAgent,1},MyGrid{Int64,Array{Array{Int64,1},1}}}, ::Symbol) at visualization.jl:109
└ @ Agents ~/.julia/packages/Agents/NQyfq/src/visualization.jl:109

doubled information: agent.pos and space.agent_positions

Seems to me that for any agent, the information contained in agent.pos and space.agent_positions is fully equivalent and convertible from one form to another.

Is this true? If it is true, then why should both informations exist?

Can't one get away by completely removing the pos field of the agent struct?

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.