JuliaDynamics / Agents.jl

Agent-based modeling framework in Julia
https://juliadynamics.github.io/Agents.jl/stable/
MIT License
734 stars 119 forks source link

UndefVarError with @dispatch macro within modules #1062

Closed jessica-reale closed 2 months ago

jessica-reale commented 2 months ago

Hi to everyone, first of all, thank you very much for all your work.

I am implementing a macroeconomic model with multiple agents, and the new multiagent feature is very useful. However, recent changes to the dispatch macro have caused problems in my previously working code. After some debugging, I have the impression that the newly defined dispatch macro within the DynamicSumTypes package does not work within Julia modules, as the functions defined with the dispatch macro encounter UndefVarError. No error is instead encountered outside modules.

Could you please check whether this is the case and fix this issue? Otherwise, what other possible solution would you suggest? For now, I am not upgrading to the new version and I keep using Agents v6.0.14.

I tried to create an easy code example that I attach below.

module MWE
    using Agents, Random

    # Define two subagents Person1 and Person2 with different fields
    @multiagent :opt_speed struct Person(NoSpaceAgent)
        # Person1
        @subagent struct Person1 
            product::Float64 = 0.0
            product_previous_period::Float64 = 0.0
        end
        # Person2
        @subagent struct Person2
            cash::Float64 = 0.0
            cash_previous_period::Float64 = 0.0
        end
    end

    # Define basic model parameters
    Base.@kwdef mutable struct Parameters
        step::Int = 0             # current step 
        number_person1::Int = 50  # number of agents of kind Person1
        number_person2::Int = 100 # number of agents of kind Person2
    end

    # Define function that updates the previous period variable and sets it equal to the current value
   @dispatch function update_previous_variables!(agent::Person1)
        agent.product_previous_period = agent.product
    end

    @dispatch function update_previous_variables!(agent::Person2)
        agent.cash_previous_period = agent.cash
    end

    # Include these functions in the model_step!
    function model_step!(model)
        # Increment model step
        model.step += 1

        # Update previous period variables using "dispatched" functions
        for a in allagents(model)
            update_previous_variables!(a)
        end

        # Just a simple update of agents' fields
        for a in allagents(model)
            if kindof(a) == :Person1 
                a.product += 1
            elseif kindof(a) == :Person2
                a.cash += rand(abmrng(model))
            end
        end
        return model
    end

    # Initialise model and add agents
    function initialise_model()
        model = ABM(Person;
            model_step! = model_step!,
            properties = Parameters(),
            scheduler = Schedulers.Randomly(),
            rng = Xoshiro(UInt32(96100))          
        )

        # Add agents to the model
        for id in 1:model.number_person1
            a = Person1(id = id)
            add_agent!(a, model)
        end
        for id in (model.number_person1 + 1):(model.number_person1 + model.number_person2)
            a = Person2(id = id)
            add_agent!(a, model)
        end
        return model 
    end

end # ends module

using Agents
using .MWE 

model = MWE.initialise_model()
step!(model, 1)

When running this code in Agents v6.0.17, I encounter the following error. Instead, everything works fine with Agents v6.0.14.

ERROR: UndefVarError: `update_previous_variables!` not defined
Stacktrace:
 [1] model_step!(model::StandardABM{…})
   @ Main.MWE ./REPL[1]:41
 [2] step_ahead!
   @ ~/.julia/packages/Agents/DqoBq/src/simulations/step_standard.jl:26 [inlined]
 [3] step!(model::StandardABM{…}, n::Int64)
   @ Agents ~/.julia/packages/Agents/DqoBq/src/simulations/step_standard.jl:5
 [4] top-level scope
   @ REPL[6]:1
Some type information was truncated. Use `show(err)` to see complete types.

I hope this can be useful.

Thank you in advance for your feedback, best Jessica

Tortar commented 2 months ago

you need to call @finalize_patterns at the end of your module to register the various functions...but @multiagent will probably be deprecated in a subsequent version of Agents.jl, because Julia 1.11 is a lot more efficient that the previous version and something like @multiagent is no more necessary for performance code. There is a new methodology in DynamicSumTypes which will probably be mentioned somewhere in the docs. But will be useful just when max performance is required. So I suggest you to stick with normal Julia structs :-)

Tortar commented 2 months ago

Sorry it is @finalize_dispatch actually