Agents.jl Predator-prey dynamic : LoadError: UndefVarError: randomwalk! not defined

Hi all,

I am trying the Predator-prey dynamic and it returns:

LoadError: UndefVarError: randomwalk! not defined

I can run Zombie Outbreak fine. Why there is no randomwalk function defined in the tutorial?

I use Julia REPL and this is the code:

# https://juliadynamics.github.io/Agents.jl/stable/examples/predator_prey/
# Making the model

using Agents, Random

@agent Sheep GridAgent{2} begin
    energy::Float64
    reproduction_prob::Float64
    Δenergy::Float64
end

@agent Wolf GridAgent{2} begin
    energy::Float64
    reproduction_prob::Float64
    Δenergy::Float64
end

function initialize_model(;
        n_sheep = 100,
        n_wolves = 50,
        dims = (20, 20),
        regrowth_time = 30,
        Δenergy_sheep = 4,
        Δenergy_wolf = 20,
        sheep_reproduce = 0.04,
        wolf_reproduce = 0.05,
        seed = 23182,
    )

    rng = MersenneTwister(seed)
    space = GridSpace(dims, periodic = true)
    # Model properties contain the grass as two arrays: whether it is fully grown
    # and the time to regrow. Also have static parameter `regrowth_time`.
    # Notice how the properties are a `NamedTuple` to ensure type stability.
    properties = (
        fully_grown = falses(dims),
        countdown = zeros(Int, dims),
        regrowth_time = regrowth_time,
    )
    model = ABM(Union{Sheep, Wolf}, space;
        properties, rng, scheduler = Schedulers.randomly, warn = false
    )
    # Add agents
    for _ in 1:n_sheep
        energy = rand(model.rng, 1:(Δenergy_sheep*2)) - 1
        add_agent!(Sheep, model, energy, sheep_reproduce, Δenergy_sheep)
    end
    for _ in 1:n_wolves
        energy = rand(model.rng, 1:(Δenergy_wolf*2)) - 1
        add_agent!(Wolf, model, energy, wolf_reproduce, Δenergy_wolf)
    end
    # Add grass with random initial growth
    for p in positions(model)
        fully_grown = rand(model.rng, Bool)
        countdown = fully_grown ? regrowth_time : rand(model.rng, 1:regrowth_time) - 1
        model.countdown[p...] = countdown
        model.fully_grown[p...] = fully_grown
    end
    return model
end

sheepwolfgrass = initialize_model()

# Defining the stepping functions

function sheepwolf_step!(sheep::Sheep, model)
    randomwalk!(sheep, model)
    sheep.energy -= 1
    if sheep.energy < 0
        kill_agent!(sheep, model)
        return
    end
    eat!(sheep, model)
    if rand(model.rng) ≤ sheep.reproduction_prob
        reproduce!(sheep, model)
    end
end

function sheepwolf_step!(wolf::Wolf, model)
    randomwalk!(wolf, model; ifempty=false)
    wolf.energy -= 1
    if wolf.energy < 0
        kill_agent!(wolf, model)
        return
    end
    # If there is any sheep on this grid cell, it's dinner time!
    dinner = first_sheep_in_position(wolf.pos, model)
    !isnothing(dinner) && eat!(wolf, dinner, model)
    if rand(model.rng) ≤ wolf.reproduction_prob
        reproduce!(wolf, model)
    end
end

function first_sheep_in_position(pos, model)
    ids = ids_in_position(pos, model)
    j = findfirst(id -> model[id] isa Sheep, ids)
    isnothing(j) ? nothing : model[ids[j]]::Sheep
end

# Sheep and wolves have separate eat! functions.
function eat!(sheep::Sheep, model)
    if model.fully_grown[sheep.pos...]
        sheep.energy += sheep.Δenergy
        model.fully_grown[sheep.pos...] = false
    end
    return
end

function eat!(wolf::Wolf, sheep::Sheep, model)
    kill_agent!(sheep, model)
    wolf.energy += wolf.Δenergy
    return
end

# Sheep and wolves share a common reproduction method. 

function reproduce!(agent::A, model) where {A}
    agent.energy /= 2
    id = nextid(model)
    offspring = A(id, agent.pos, agent.energy, agent.reproduction_prob, agent.Δenergy)
    add_agent_pos!(offspring, model)
    return
end

# The behavior of grass function differently. If it is fully grown, it is consumable.

function grass_step!(model)
    @inbounds for p in positions(model) # we don't have to enable bound checking
        if !(model.fully_grown[p...])
            if model.countdown[p...] ≤ 0
                model.fully_grown[p...] = true
                model.countdown[p...] = model.regrowth_time
            else
                model.countdown[p...] -= 1
            end
        end
    end
end

# Running the model

using InteractiveDynamics
using CairoMakie

#  define the plotting details for the wolves and sheep
offset(a) = a isa Sheep ? (-0.1, -0.1*rand()) : (+0.1, +0.1*rand())
ashape(a) = a isa Sheep ? :circle : :utriangle
acolor(a) = a isa Sheep ? RGBAf(1.0, 1.0, 1.0, 0.8) : RGBAf(0.2, 0.2, 0.3, 0.8)

# how to plot grass as a heatmap:
grasscolor(model) = model.countdown ./ model.regrowth_time

heatkwargs = (colormap = [:brown, :green], colorrange = (0, 1))

plotkwargs = (;
    ac = acolor,
    as = 25,
    am = ashape,
    offset,
    scatterkwargs = (strokewidth = 1.0, strokecolor = :black),
    heatarray = grasscolor,
    heatkwargs = heatkwargs,
)

sheepwolfgrass = initialize_model()

fig, ax, abmobs = abmplot(sheepwolfgrass;
    agent_step! = sheepwolf_step!,
    model_step! = grass_step!,
plotkwargs...)
fig

# Run the simulation

sheep(a) = a isa Sheep
wolf(a) = a isa Wolf
count_grass(model) = count(model.fully_grown)

sheepwolfgrass = initialize_model()
steps = 1000
adata = [(sheep, count), (wolf, count)]
mdata = [count_grass]
adf, mdf = run!(sheepwolfgrass, sheepwolf_step!, grass_step!, steps; adata, mdata)

# The following plot shows the population dynamics over time. 
function plot_population_timeseries(adf, mdf)
    figure = Figure(resolution = (600, 400))
    ax = figure[1, 1] = Axis(figure; xlabel = "Step", ylabel = "Population")
    sheepl = lines!(ax, adf.step, adf.count_sheep, color = :cornsilk4)
    wolfl = lines!(ax, adf.step, adf.count_wolf, color = RGBAf(0.2, 0.2, 0.3))
    grassl = lines!(ax, mdf.step, mdf.count_grass, color = :green)
    figure[1, 2] = Legend(figure, [sheepl, wolfl, grassl], ["Sheep", "Wolves", "Grass"])
    figure
end

# Altering the input conditions to reach an equilibrium

stable_params = (;
    n_sheep = 140,
    n_wolves = 20,
    dims = (30, 30),
    Δenergy_sheep = 5,
    sheep_reproduce = 0.31,
    wolf_reproduce = 0.06,
    Δenergy_wolf = 30,
    seed = 71758,
)

sheepwolfgrass = initialize_model(;stable_params...)
adf, mdf = run!(sheepwolfgrass, sheepwolf_step!, grass_step!, 2000; adata, mdata)
plot_population_timeseries(adf, mdf)

# Making a video

sheepwolfgrass = initialize_model(;stable_params...)

abmvideo(
    "sheepwolf.mp4",
    sheepwolfgrass,
    sheepwolf_step!,
    grass_step!;
    frames = 100,
    framerate = 8,
    title = "Sheep Wolf Grass",
    plotkwargs...,
)

Did you copy this code from the documentation of some package? If so, can you link to the page you got it from? Are you sure that the documentation is for the same version of the package you are using?

Yes I am following this one:

https://juliadynamics.github.io/Agents.jl/stable/examples/predator_prey/

I am using Agents.jl version 5.6.5

The documentation is for version 5.8, so I’d suggest using that version when trying out code from the docs.

In this particular case, I found that the randomwalk! function requires a step size. In the error message I saw, the Real is highlighted:

Closest candidates are:
  randomwalk!(::AbstractAgent, 
  ::AgentBasedModel{<:Agents.AbstractGridSpace}, ::Real; kwargs...) at  
   ~/.julia/packages/Agents/wuIZW/src/spaces/walk.jl:99`

but the code doesn’t supply a step:

randomwalk!(sheep, model)

The help for randomwalk! says:

for Euclidean metric in a GridSpace, random walks are ill defined and
  hence not supported.

and the model in this example is:

space = GridSpace(dims, periodic = true)

I know very little about Agents - if this is the first tutorial, it looks like a very advanced package. :frowning:

I’d suggest you open an issue on the github and ask for a working example.

1 Like

Yes, not the version is the problem.

I am going to report it on the github.

1 Like