@@ -680,10 +680,10 @@ adf
680
680
681
681
# In realistic modelling situations it is often the case the the ABM is composed
682
682
# of different types of agents. Agents.jl supports two approaches for multi-agent ABMs.
683
- # The first uses the `Union` type, and the second uses the [`@multiagent`](@ref)
684
- # command.
685
- # This approach is recommended as default, because in many cases it will have performance advantages
686
- # over the `Union` approach without having tangible disadvantages. However, we strongly recommend you
683
+ # The first uses the `Union` type from Base Julia , and the second uses the [`@multiagent`](@ref)
684
+ # command that we have developed to accelerate handling different types.
685
+ # The `@multiagent` approach is recommended as default, because in many cases it will have performance advantages
686
+ # over the `Union` approach without having tangible disadvantages. However, we strongly recommend you
687
687
# to read through the [comparison of the two approaches](https://juliadynamics.github.io/Agents.jl/stable/performance_tips/#multi_vs_union).
688
688
689
689
# _Note that using multiple agent types is a possibility entirely orthogonal to
694
694
695
695
# The simplest way to add more agent types is to make more of them with
696
696
# [`@agent`](@ref) and then give a `Union` of agent types as the agent type when
697
- # making the `AgentBasedModel`.
697
+ # making the `AgentBasedModel`.
698
698
699
699
# For example, let's say that a new type of agent enters
700
700
# the simulation; a politician that would "attract" a preferred demographic.
@@ -704,16 +704,10 @@ adf
704
704
preferred_demographic:: Int
705
705
end
706
706
707
- # and, when making the model we would specify
708
-
709
- model = StandardABM (
710
- Union{Schelling, Politician}, # type of agents
711
- space; # space they live in
712
- )
713
-
714
- # Naturally, we would have to define a new agent stepping function that would
715
- # act differently depending on the agent type. This could be done by making
716
- # a function that calls other functions depending on the type, such as
707
+ # When defining the agent stepping function, it would (likely)
708
+ # act differently depending on the agent type. This could be done by utilizing
709
+ # [Julia's Multiple Dispatch](https://docs.julialang.org/en/v1/manual/methods/)
710
+ # to add a method to the agent stepping function depending on the type
717
711
718
712
function agent_step! (agent:: Schelling , model)
719
713
# # stuff.
@@ -723,33 +717,62 @@ function agent_step!(agent::Politician, model)
723
717
# # other stuff.
724
718
end
725
719
726
- # and then passing
720
+
721
+ # When making the model we specify the `Union` type for the agents
722
+
723
+ model = StandardABM (
724
+ Union{Schelling, Politician}, # type of agents
725
+ space; # space they live in
726
+ )
727
727
728
728
model = StandardABM (
729
729
Union{Schelling, Politician}, # type of agents
730
730
space; # space they live in
731
731
agent_step!
732
732
)
733
733
734
+
735
+ # When adding agents to the moedel example, we can explicitly make agents
736
+ # with their constructors and add them. However, it is recommended
737
+ # to use the automated [`add_agent!`](@ref) function and provide as a first argument
738
+ # the type of agent to add. For example
739
+
740
+ add_agent_single! (Schelling, model; group = 1 , mood = true )
741
+
742
+ # or
743
+
744
+ add_agent_single! (Politician, model; preferred_demographic = 1 )
745
+
746
+ model
747
+
734
748
# ## Multiple agent types with `@multiagent`
735
749
736
- # By using `@multiagent` it is often possible to improve the
750
+ # By using `@multiagent` it is often possible to improve the
737
751
# computational performance of simulations requiring multiple types,
738
- # while almost everything works the same
752
+ # while almost everything works the same. First we make a
753
+ # multi-agent type from existing agent types
739
754
740
755
@multiagent MultiSchelling (Schelling, Politician) <: AbstractAgent
741
756
742
- # Now you can create instances with
757
+ MultiSchelling
758
+
759
+ # This `MultiSchelling` is not a union type; it is an advanced construct
760
+ # that wraps multipe types. When making a multi-agent directly (although it is not recommended,
761
+ # use `add_agent!` instead), we can wrap the agent type in the multiagent type like so
762
+
763
+ p = MultiSchelling (Politician (; id = 1 , pos = random_position (model), preferred_demographic = 1 ))
764
+
765
+ # or
743
766
744
- p = constructor ( MultiSchelling, Politician)(model; pos = random_position (model), preferred_demographic = 1 )
767
+ h = MultiSchelling ( Schelliing (; id = 1 , pos = random_position (model), mood = true , group = 1 ) )
745
768
746
- # agents are then all of type `MultiSchelling`
769
+ # As you can tell, both of these are of the same `Type`:
747
770
748
- typeof (p)
771
+ typeof (p), typeof (h)
749
772
750
- # and hence you can't use only `typeof` to differentiate them. But you can use
773
+ # and hence you can't use `typeof` to differentiate them. But you can use
751
774
752
- variantof (p)
775
+ variantof (p), variantof (h)
753
776
754
777
# instead. Hence, the agent stepping function should become something like
755
778
@@ -763,50 +786,38 @@ function agent_step!(agent, model, ::Politician)
763
786
# # other stuff.
764
787
end
765
788
766
- # and you need to give `MultiSchelling` as the type of agents in model initialization
789
+ # to utilize Julia's dispatch system.
790
+
791
+ # When constructing the model, we must give the multi-type as the agent type,
792
+ # akin to giving the `Union` as the type before
767
793
768
794
model = StandardABM (
769
795
MultiSchelling, # the multiagent type is given as the type
770
796
space;
771
797
agent_step!
772
798
)
773
799
774
- # Regardless of whether you went down the `Union` or `@multiagent` route,
775
- # the API of Agents.jl has been designed such that there is no difference in subsequent
776
- # usage.
800
+ # Now, when it comes to adding agents to the model, we use the same approach
801
+ # as with the `Union` types but we pass a constructor function as a first argument
802
+ # to [`add_agent!`](@ref). The "type" here must actually be the constructor
777
803
778
- # For example, in the union case we provide the `Union` type when we create the model,
804
+ add_agent_single! ( constructor (MultiSchelling, Schelling), model; group = 1 )
779
805
780
- model = StandardABM (Union{Schelling, Politician}, space)
806
+ # Thankfully, Julia's function composition `∘` simplifies this, and we can do instead
781
807
782
- # we add them by specifying the type
783
-
784
- add_agent_single! (Schelling, model; group = 1 , mood = true )
808
+ add_agent_single! (MultiSchelling ∘ Schelling, model; group = 1 )
785
809
786
810
# or
787
811
788
- add_agent_single! (Politician, model; preferred_demographic = 1 )
812
+ add_agent_single! (MultiSchelling ∘ Politician, model; preferred_demographic = 1 )
789
813
790
814
# and we see
791
815
792
816
collect (allagents (model))
793
817
794
- # For the `@multiagent` case, there is really no difference apart from
795
- # the usage of a custom `constructor` function. We have
818
+ # Because the `∘` is more elegant, we will be using it when using `@multiagent`.
796
819
797
- model = StandardABM (MultiSchelling, space)
798
-
799
- # we add
800
-
801
- add_agent_single! (constructor (MultiSchelling, Schelling), model; group = 1 )
802
-
803
- # or
804
-
805
- add_agent_single! (constructor (MultiSchelling, Politician), model; preferred_demographic = 1 )
806
-
807
- # and we see
808
-
809
- collect (allagents (model))
820
+ # ---
810
821
811
822
# And that's the end of the tutorial!!!
812
823
# You can visit other examples to see other types of usage of Agents.jl,
0 commit comments