# How to simulate friends on a GridSpace in Agents.jl

**URL:** <https://discourse.julialang.org/t/how-to-simulate-friends-on-a-gridspace-in-agents-jl/73484>\
**Category:** General Usage\
**Tags:** agents\
**Created:** [December 22, 2021, 12:56pm UTC](https://discourse.julialang.org/t/how-to-simulate-friends-on-a-gridspace-in-agents-jl/73484 "2021-12-22T12:56:52Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![sbacelar](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/sbacelar/32/4359_2.png) [@sbacelar](https://discourse.julialang.org/u/sbacelar)\
**Post date:** [December 22, 2021, 12:56pm UTC](https://discourse.julialang.org/t/how-to-simulate-friends-on-a-gridspace-in-agents-jl/73484/1 "2021-12-22T12:56:52Z")

</div>

I am using `Agents.jl` and I want to create for each `agent` a list of (the ids) other agents that are his friends. I am working on a `GridSpace` (not in a `GraphSpace`).  
For each agent, the list of friends has a random size between `[a,b]` and `age` between `[agent.age - c, agent.age + d]`.  
I have tried the following:

```julia
mutable struct Person <: AbstractAgent
    id::Int
    pos::NTuple{2, Int}
    age::Int
    friends::Array ## list of ids of other agents
end

```

but when initializing the model, I should have the ages of all agents before defining the friends of each one. How can I do that? Using a `sample` for each agent?

---

<div class="post-metadata">

**Author:** ![fbanning](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/fbanning/32/14972_2.png) [@fbanning](https://discourse.julialang.org/u/fbanning)\
**Post date:** [December 22, 2021, 3:09pm UTC](https://discourse.julialang.org/t/how-to-simulate-friends-on-a-gridspace-in-agents-jl/73484/2 "2021-12-22T15:09:17Z")

</div>

> [@sbacelar](#):
>
> but when initializing the model, I should have the ages of all agents before defining the friends of each one. How can I do that? Using a `sample` for each agent?

So if I understood you correctly, each agent should have a list of friends with a length between `a` and `b` and the age of the friends inside that list should be between `agent.age - c` and `agent.age - d`.  
There are definitely multiple ways to do it. Hm, I think I would do the following:

```julia
using Random
using Pipe

mutable struct Person <: AbstractAgent
    id::Int
    pos::NTuple{2, Int}
    age::Int
    friends::Vector{Int} # use vector of integers because typeof(agent.id) == Int
end

# inside the model initialisation function:
for n in 1:numagents
    agent = Person(
        n, # id
        (1, 1), # position
        rand(model.rng, 0:80), # random age between 0 and 80
        fill(n, rand(model.rng, a:b)) # placeholder vector of friends with length between a and b
    )
    add_agent!(agent, model) # place agents at a random position on the grid
end

for agent in allagents(model)
    # first retrieve a list of possible friends in the correct age range
    possible_friends = @pipe collect(allids(model)) |> # collect all existing ids
        filter!(id -> id != agent.id, _) |> # remove self from id list
        filter!(id -> agent.age - c < model[id].age < agent.age + d, _) |> # remove ids of agents that are not in the correct age range
        shuffle!(model.rng, _) # shuffle the list to randomise the draw
    # then replace values in agent.friends with a random subset from possible_friends
    agent.friends .= possible_friends[begin:length(agent.friends)] # 
end

```

Couldn’t test it because I don’t have your full model but hopefully you got some ideas out of my write-up. It’s probably not the most performant way to do things but it should work. 🙂

I’ve also tried to be a bit liberal with the comments, since I don’t know about your current level of coding skills. If you didn’t understand something, feel free to ask for more info.

---

<div class="post-metadata">

**Author:** ![sbacelar](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/sbacelar/32/4359_2.png) [@sbacelar](https://discourse.julialang.org/u/sbacelar)\
**Post date:** [December 22, 2021, 7:02pm UTC](https://discourse.julialang.org/t/how-to-simulate-friends-on-a-gridspace-in-agents-jl/73484/3 "2021-12-22T19:02:05Z")

</div>

Thank you for your great help. I am an untalented amateur in coding but I understood almost all your code, without reading your helpful comments (except for the `fill` line 🙂 ).  
I tried to run your code in my (almost) complete model but I got a bounds error that I must debug.  
I have changed the `age` bounds and also gave values to the variables `a, b, c, d`.  
I am sending now the complete code and error message:

```julia
using Agents
using Random
using Pipe

mutable struct Person <: AbstractAgent
    id::Int
    pos::NTuple{2, Int}
    age::Int
    friends::Vector{Int} # use vector of integers because typeof(agent.id) == Int
end

function initialize(; numagents = 81, griddims = (81, 100), seed = 125)
    space = GridSpace(griddims, periodic = false)
    rng = Random.MersenneTwister(seed)
    model = ABM(
        Person, space;
        rng, scheduler = Schedulers.randomly
    )
a = 10; b = 25; c = 5; d = 5
# inside the model initialisation function:
for n in 1:numagents
    agent = Person(
        n, # id
        (1, 1), # position
        rand(model.rng, 20:100), # random age between 20 and 100
        fill(n, rand(model.rng, a:b)) # placeholder vector of friends with length between a and b
    )
    add_agent!(agent, model) # place agents at a random position on the grid
end

for agent in allagents(model)
    # first retrieve a list of possible friends in the correct age range
    possible_friends = @pipe collect(allids(model)) |> # collect all existing ids
        filter!(id -> id != agent.id, _) |> # remove self from id list
        filter!(id -> agent.age - c < model[id].age < agent.age + d, _) |> # remove ids of agents that are not in the correct age range
        shuffle!(model.rng, _) # shuffle the list to randomise the draw
    # then replace values in agent.friends with a random subset from possible_friends
    agent.friends .= possible_friends[begin:length(agent.friends)] # 
end

return model
end

model = initialize()

```

The error message is:

```julia
ERROR: LoadError: BoundsError: attempt to access 7-element Vector{Int64} at index [1:16]
Stacktrace:
 [1] throw_boundserror(A::Vector{Int64}, I::Tuple{UnitRange{Int64}})
   @ Base .\abstractarray.jl:691
 [2] checkbounds
   @ .\abstractarray.jl:656 [inlined]
 [3] getindex
   @ .\array.jl:867 [inlined]
 [4] initialize(; numagents::Int64, griddims::Tuple{Int64, Int64}, seed::Int64)
   @ Main c:\Users\sbac\Dropbox\JULIA\coordTransSocNets\disc.jl:38
 [5] initialize()
   @ Main c:\Users\sbac\Dropbox\JULIA\coordTransSocNets\disc.jl:13
 [6] top-level scope
   @ c:\Users\sbac\Dropbox\JULIA\coordTransSocNets\disc.jl:44
in expression starting at c:\Users\sbac\Dropbox\JULIA\coordTransSocNets\disc.jl:44

```

---

<div class="post-metadata">

**Author:** ![fbanning](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/fbanning/32/14972_2.png) [@fbanning](https://discourse.julialang.org/u/fbanning)\
**Post date:** [December 22, 2021, 7:35pm UTC](https://discourse.julialang.org/t/how-to-simulate-friends-on-a-gridspace-in-agents-jl/73484/4 "2021-12-22T19:35:08Z")

</div>

The problem most likely lies in this statement `possible_friends[begin:length(agent.friends)`.  
If your list of possible\_friends is too small (e.g. only 7 `id`s in there) but your agent should have more friends than that (e.g. the random draw between `a` and `b` was 16), then you will encounter such a `BoundsError`.  
So what you’re facing here is the fact that you don’t know about the final length of the friends vector during the initialisation process (because it depends dynamically on `a`,`b`, `age`, `c`, and `d`). The preallocation that I’ve tried with the `fill` function will likely not work properly then.

Here’s how you _could_ solve it:

```julia
# adjust the agent struct to save the maximum number of friends an agent can have
mutable struct Person <: AbstractAgent
    id::Int
    pos::NTuple{2, Int}
    age::Int
    max_friends::Int
    friends::Vector{Int}
end

# and inside the init function:
for n in 1:numagents
    agent = Person(
        n,
        (1, 1),
        rand(model.rng, 20:100),
        rand(model.rng, a:b), # random draw for maximum number of friends
        [] # initialise an empty vector of friends
    )
    add_agent!(agent, model)
end

for agent in allagents(model)
    possible_friends = @pipe collect(allids(model)) |>
        filter!(id -> id != agent.id, _) |>
        filter!(id -> agent.age - c < model[id].age < agent.age + d, _) |> 
        shuffle!(model.rng, _) # shuffle the list to randomise the draw
    for id in possible_friends
        push!(agent.friends, id) # add this id to the friends vector
        if length(agent.friends) == agent.max_friends # if friends vector has reached desired length
            break # break the for-loop
        end
    end
end

```

---

<div class="post-metadata">

**Author:** ![sbacelar](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/sbacelar/32/4359_2.png) [@sbacelar](https://discourse.julialang.org/u/sbacelar)\
**Post date:** [December 23, 2021, 6:54pm UTC](https://discourse.julialang.org/t/how-to-simulate-friends-on-a-gridspace-in-agents-jl/73484/5 "2021-12-23T18:54:22Z")

</div>

Thank you for your help. Now the script works fine. The only issue is that `max_friends` is in fact `number_of_friends`, as I wanted.  
I have yet a long way to finish my project, but I learned a lot from your code.
