Adding network graph edges at each model step

Hi! I’m working on agent-based modelling of a social network graph. I’ve been basing my first attempt on hacking together the Schelling segregation and schoolyard examples. The model is supposed to check whether each agent has a graph edge with its neighbours, and if not, add one, as well as the normal Schelling stuff about whether enough neighbours are in the same group.

I’m aware from this answer that I need to set this up as a model step function with the agent-checking logic inside it, but I don’t know how to get that working. I’ve tried to change the existing agent_step! function in the basic Schelling model to a model_step! function:

using Agents
using InteractiveDynamics

space = GridSpaceSingle((10, 10); periodic = false)
using Random: MersenneTwister


@agent SchellingAgent GridAgent{2} begin
    mood::Bool # whether the agent is happy in its position. (true = happy)
    group::Int # The group of the agent, determines mood as it interacts with neighbors
end

using Random # for reproducibility
function initialize(; total_agents = 320, griddims = (20, 20), min_to_be_happy = 3, seed = 125)
    space = GridSpaceSingle(griddims, periodic = false)
    properties = Dict(:min_to_be_happy => min_to_be_happy)
    rng = Random.Xoshiro(seed)
    model = UnremovableABM(
        SchellingAgent, space;
        properties, rng, scheduler = Schedulers.Randomly()
    )
    # populate the model with agents, adding equal amount of the two types of agents
    # at random positions in the model
    for n in 1:total_agents
        agent = SchellingAgent(n, (1, 1), false, n < total_agents / 2 ? 1 : 2)
        add_agent_single!(agent, model)
    end
    return model
end

model = initialize()

function model_step!(model)
    for agent in allagents(model)
        minhappy = model.min_to_be_happy
        count_neighbors_same_group = 0
        # For each neighbor, get group and compare to current agent's group
        # and increment `count_neighbors_same_group` as appropriately.
        # Here `nearby_agents` (with default arguments) will provide an iterator
        # over the nearby agents one grid point away, which are at most 8.
        for neighbor in nearby_agents(agent, model)
            if agent.group == neighbor.group
                count_neighbors_same_group += 1
            end
        end
        # After counting the neighbors, decide whether or not to move the agent.
        # If count_neighbors_same_group is at least the min_to_be_happy, set the
        # mood to true. Otherwise, move the agent to a random position, and set
        # mood to false.
        if count_neighbors_same_group ≥ minhappy
            agent.mood = true
        else
            agent.mood = false
            move_agent_single!(agent, model)
        end
    end
    
end

using CairoMakie # choosing a plotting backend

groupcolor(a) = a.group == 1 ? :blue : :orange
groupmarker(a) = a.group == 1 ? :circle : :rect
figure, _ = abmplot(model; ac = groupcolor, am = groupmarker, as = 10)
figure 

It accepts the function, but when I try to run step(model, model_step!) I get this error.

ERROR: MethodError: no method matching step(::UnremovableABM{GridSpaceSingle{2, false}, SchellingAgent, Agents.Schedulers.Randomly, Dict{Symbol, Int64}, Xoshiro}, ::typeof(model_step!))
Stacktrace:
 [1] top-level scope
   @ REPL[1]:1

I have another copy of the script where I’ve done some work on building in the network graph part (getting it set as a property of the model etc) but it was throwing the same kind of error, so I thought I should roll everything back to just getting the model step function to work first. I’m new to Julia and agent based modelling, so any advice would be great :slight_smile: Also if anyone knows of an example where network graph edges are added and removed at each step that would be a huge help, thanks!

Not necessarily. If you really only want to 1. check for network edges and create them, and then 2.run the regular Schelling model, then you can leave the agent_step! intact as it is and only add the network related stuff to a new model_step! function.

# create new model stepping function
function model_step!(model)
    # do desired things with your `model.graph`
end

# step the model
step!(model, agent_step!, model_step!)

However, you likely want your graph connections to be checked/created before running the agent_step! function. By default, Agents.jl first activates all agents and then runs the model stepping function (if one is provided). To change this, this requires an additional keyword:

# run model step first, then activate agents according to scheduler
step!(model, agent_step!, model_step!; agents_first = false)

Doing it like this should cause no problems. (Your Error was due to the fact that you passed a model_step! as the second argument of the step! function, which should always be an agent_step!.)