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 ...
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:
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.
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.
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!
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!
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