How to pass on kwargs? - updated

Original post below.

It seems the mistake must be somewhere else. I simplified the code to create a running example like this:

using Agents

mutable struct Agent <: AbstractAgent
    id::Int64
end

function my_model_step!(model)
    ## dummy function
end

function my_agent_step!(agent, model)
    ## dummy function
end

function run_my_model!(model, steps::Integer; kwargs...)
    run!(model, my_agent_step!, my_model_step!, steps, agents_first = false; kwargs...)
end

function data_function(agent::Agent)
    ## dummy function
end

function test_my_model()
    model = ABM(Agent)
    add_agent!(Agent(1), model)
    adata = [data_function]
    # run!(model, my_agent_step!, my_model_step!, 1, agents_first = false; adata)
    run_my_model!(model, 1; adata)
end

test_my_model()

and that works.

So, here’s the code that doesn’t work, for a reason that I don’t understand.

using EconoSim # Package is on Github: https://github.com/HapponomyOrg/EconoSim.jl
using Random
using Agents
using DataFrames

NUM_ACTORS = 1000
SIM_LENGTH = 1000

BASIC_SUMSY = SuMSy(2000, 0, 0.1, 30)

function random_purchase(model, actor::Actor)
    target = random_agent(model)
    transfer_asset!(actor.balance, target.balance, SUMSY_DEP, asset_value(actor.balance, SUMSY_DEP) / 10)
end

function balance(actor)
    return asset_value(actor.balance, SUMSY_DEP)
end

function add_actors(model, behaviors::Vector)
    for i in 1:NUM_ACTORS
        actor = Actor()

        for behavior in behaviors
            add_behavior!(actor, behavior)
        end

        add_agent!(actor, model)
    end
end

function run_sumsy_simulation!(sumsy::SuMSy, behavior)
    model = create_sumsy_model(sumsy)
    add_actors(model, [behavior])    

    adata = [balance]
    data, _ = run_econo_model!(model, SIM_LENGTH; adata)
    # this DOES work: data, _ = run!(model, actor_step!, econo_model_step!, SIM_LENGTH, agents_first = false; adata)

    return data
end

run_sumsy_simulation!(BASIC_SUMSY, random_purchase)

The code for run_econoModel!() is:

function run_econo_model!(model, steps::Integer; kwargs...)
    run!(model, actor_step!, econo_model_step!, steps, agents_first = false; kwargs...)
end

Original post

Hi,

I’m implementing a framework build on Agents.jl and want to create a wrapper function for run!() with fewer parameters. In short, I want to do the following:

function run_my_model!(model, steps::Integer; kwargs...)
  run!(model, my_agent_step!, my_model_step!, steps, agents_first = false; kwargs...)
end

All goes well as long as I don’t pass anything after steps. But when I want to substitute:

adata = [data_function]
run!(model, my_agent_step!, my_model_step!, 10, agents_first = false; adata)

which works, with

adata = [data_function]
run_my_model!(model, 10; adata)

which I expected to be the same, I get the following error:

ERROR: MethodError: no method matching run_my_model!(::AgentBasedModel{Nothing, Actor, typeof(Agents.Schedulers.fastest), Dict{Symbol, Any}, TaskLocalRNG}, ::Int64; adata=[data_function])
Closest candidates are:
run_my_model!(::Any, ::Any, ::Any…) at ~/.julia/packages/EconoSim/mpKhJ/src/models/econo_model.jl:44 got unsupported keyword argument “adata”
Stacktrace:
[1] run_sumsy_simulation!(sumsy::SuMSy, behavior::Function)
@ Main ~/Programming/Visual Studio/MoneySim.jl/src/SuMSySimulation.jl:72
[2] top-level scope
@ REPL[7]:1

Actor is a subtype of AbstractActor.
My problem seems to be with passing the kwargs in the correct way but I can’t figure out what I’m doing wrong.

Thanks in advance,
Stef

I suspect you have older method definitions interfering here, but it’s hard to tell without a working example, and it would also help to make the code more readable. Please have a look here:

1 Like

Hi,
I guess in your example adata is not a real keyword argument, because you don’t give it a value. Did you try this one?

 run_my_model!(model, 10; adata=[data_function])

That should not be the issue as that synthax is supported for passing kwargs since v1.5
https://github.com/JuliaLang/julia/issues/29333

2 Likes

Yes, I replied too quickly (sorry). Then I can’t do better than @sijo 's answer.
I tried to reproduce the error, without success.

function run_my_model!(model, steps::Integer; kwargs...)
    print(kwargs...)
end

then

julia> adata="foo"
"foo"

julia> run_my_model!(:model, 10; adata)
:adata => "foo"

No problem here
Something strange though: the three dots after kwargs appear as the unicode character instead of three ‘dot’ character. I got an error when copy-pasting the function to my terminal.
‘…’: Unicode U+2026 (category Po: Punctuation, other)

2 Likes

Firstly, is adata a valid keyword argument in run!? You haven’t shown run!'s definition.

Secondly, it looks like you are misplacing the ; below:

The way you are calling run! indicates that agents_first is a keyword, so either you should move the semicolon, or, if it isn’t a keyword, call run! without the parameter name agents_first. Either

run!(model, my_agent_step!, my_model_step!, steps; agents_first = false, kwargs...)
# or
run!(model, my_agent_step!, my_model_step!, steps, false; kwargs...)
1 Like

The three dots → Unicode … is likely a Discourse thing; it seems to be using a “smart” Markdown processor that does some typographical mapping: for eg., the arrow in the beginning of my reply was actually typed as ->, but got converted to → by Discourse. (As you can see, it doesn’t do the conversion when text is within a code block - but unfortunately OP didn’t use one, hence the …).

1 Like

Didn’t know, thanks !

Thanks! I updated my post.

In Agents.jl run! is defined as follows:

run!(model, agent_step! [, model_step!], n::Integer; kwargs…) → agent_df, model_df
run!(model, agent_step!, model_step!, n::Function; kwargs…) → agent_df, model_df

I updated the post since it seems the error is not where I thought it was but I have no clue where to look for it.

It seems the error is indeed somewhere else but I have no clue where to start looking since calling the wrapper function is what triggers it. Then again, if I simplify the code to the bare minimum, as I did in the updated post, I get no error.

I added the full code (not too big but it does use a self implemented package) in the update. Calling the wrapper function triggers the error while calling run! directly does not for some weird reason.

There are some serious formatting issues with your OP, you have put in some backticks it seems, but it doesn’t work out correctly. Could you take another look at Please read: make it easier to help you and try to clean it up?

Fixed it. I was using <code> instead of [code] blocks.

1 Like

I have no clue what I have done but all of a sudden everything runs. Sorry for the inconvenience.

1 Like

That’s good. Since I cannot find any definition of run!, it’s hard to know what’s happening.

But I still think you should move the semicolon to the front of agents_first=false. The semicolon, if used at all, should come before the kwargs.

Thanks! I’ll keep that in mind.
The run!() method is defined in the Agents.jl package. In the API it’s defined as follows:

run!(model, agent_step! [, model_step!], n::Integer; kwargs...) → agent_df, model_df
run!(model, agent_step!, model_step!, n::Function; kwargs...) → agent_df, model_df

As user @DNF said, this is invalid Julia syntax and in fact has nothing to do with Agents.jl.

You should write instead:

function run_econo_model!(model, steps::Integer; kwargs...)
    run!(model, actor_step!, econo_model_step!, steps, false; kwargs...)
end

or

function run_econo_model!(model, steps::Integer, agents_first = false; kwargs...)
    run!(model, actor_step!, econo_model_step!, steps, agents_first ; kwargs...)
end

because agents_first is not a keyword of the run! function. It is a positional argument.

As far as I understand it, agents_first is ‘one of the kwargs’ of the run! function, as is adata. You can find the documents for it here: Tutorial · Agents.jl

agents_first is specifically mentioned under the section ‘other keywords’.

Oh damn you are right I have forgotten my own function!

1 Like