Agents.jl - @multiagent : MethodError: Cannot `convert` an object of type People to an object of type Family

I am a little clueless on how to use @multiagent macro in Agents.jl. For the problem I am trying to solve there are multiple agents and I am trying to add each as subagent but I get the error : MethodError: Cannot convert an object of type People to an object of type Family. Any idea how to fix this

@multiagent struct People(GraphAgent)
    @subagent struct Person
        age::Integer 
        health_condition::Float32 
        gender::Integer
        family::Family
    end
    @subagent struct Family
        number_of_members::Int
    end
end

Person(1,1,32,0.5,1, Family(1,1,5))
>>> MethodError: Cannot convert ...
1 Like
  1. Multiagent agent types are not a real Julia types, they are just symbols - “kinds”. It described in the docs.
kindof(Person(...)) = :Person
kindof(Family(...)) = :Family

allkinds(People) = [:Person, :Family]
  1. What is your Graph space? Is it a family relationships network?
    If yes, then you don’t need a family attribute in the Person subagent.
    If the graph space is unrelated to family relationships, then you can create a separate graph as a property of the model and/or agents using Garphs.jl as in the School Yard example:
  1. If you want to keep things simple and don’t need an actual graph space or graph model/agent property, then you can just store an integer agent id of the Family subagent in the Person subagent, i.e. when initializing the agents, you will need to create Family agent first, and then create all the corresponding Person agents as members of the same Family.
1 Like

Thanks that probably works. Its a covid model and actually I am borrowing some ideas from an open source model called Pyfectious to some what change it work with my problem. In that model each person is a node and family members modeled as a complete directed graph.

If I may give a performance advice:

It appears that there is no reason to have Family be an agent (i.e., a sub-kind of @multiagent) because Family is simply a relational structure, not an entity that can move and die.

What I would recommend instead is that you store all Families in a dictionary as a model property. Families then would be a dictionary mapping integer indices to a Vector{Int}. The integer indices are just the enumerator of the families (the same as an Agent ID); we use dictionaries in case families may die or be created during the model evolution. The Vector{Int} for each family simply stores the agent IDs belonging to that family. It is avector so it can be mutated if family members die.

Each agent has a field family::Int, which, as @nivertech said, it is just a pointer to the family they are part of.

So, given an agent, that’s how you get all its family members:

agent = random_agent(model)
family_id = agent.family_id # this is an integer
family = model.families[family_id] # this is a vector of integers

I believe this would bring you drastic perforamnce increase over having a graph structure for the families. But maybe test it as well, because my method would bring some performance downside when initializing the whole model, as some book keeping on the family indices needs to be made! :smiley:

3 Likes

As @nivertech said they are just variants of the same underlying type People, for this reason you can instead do:

julia> using Agents

julia> @multiagent struct People(GraphAgent)
           @subagent struct Person
               age::Integer 
               health_condition::Float32 
               gender::Integer
               family::People # here you use People
           end
           @subagent struct Family
               number_of_members::Int
           end
       end

julia> Person(1,1,32,0.5,1, Family(1,1,5))
Person(1, 1, 32, 0.5, 1, Family(1, 1, 5)::People)::People

but as @Datseris points out there can be better ways to express this relationship, hope this helps!

2 Likes

maybe this is still confusing so I will add a little more explaination: the Family “type” is actually only a constructor for People variants/kinds, that is to say that typeof(Family(1, 1, 5)) is actually People, so when you try to use a declaration as family::Family you aren’t actually doing what seems to be implied.

At the same time, it could automagically work because we are in a macro context so family::Family could be substituted with family::People before the final type is evaluated, but this is yet not implemented, but maybe I will try to do it in the future :slight_smile:

2 Likes