Agent.jl 6.57 KB
Newer Older
Victor's avatar
Victor committed
1
2
abstract type Ancestors{T} end
abstract type Rates{T} end
3

Victor's avatar
Victor committed
4
5
abstract type AbstractAgent{A<:Ancestors,R<:Rates} end # tc for time contingency, fit for fitness coeff
AbstractAgentM = Union{Missing,AbstractAgent}
6
export AbstractAgentM
7
8
9

"""
$(TYPEDEF)
Victor's avatar
Victor committed
10
11
12
13
14
15

!!! note "Specificities"
    The type `Agent` has two important composite types

    - `Ancestors{bool}` : when `bool = true`, the ancestors traits are stored,
    - `Rates{bool}` : when `bool = true`, the rates `d` and `b` of agents are updated at each time step. This is needed in e.g. `Gillepsie` algorithm.
16
"""
Victor's avatar
Victor committed
17
mutable struct Agent{A<:Ancestors,R<:Rates,T<:Tuple,U,V} <: AbstractAgent{A,R}
18
    # history of traits for geotraits
Victor's avatar
Victor committed
19
    x_history::Vector
20
    # birth time of ancestors
Victor's avatar
Victor committed
21
    t_history::Vector{U}
22
23
24
25
26
27
    # death rate
    d::V
    #birth rate
    b::V
end

28
29
import Base:eltype
# This definition of eltype is to be discussed
30
31
eltype(a::Agent{A,R,T,U,V}) where {A,R,T,U,V} = T

Victor's avatar
Victor committed
32
# infers position type and zeros
Victor's avatar
Victor committed
33
34
function _initpos(s::S) where {S<:AbstractSpacesTuple}
    _nd = ndims.(s)
Victor's avatar
Victor committed
35
36
    T = eltype.(s)
    TT = _get_types_dim(s)
Victor's avatar
Victor committed
37
38
39
40
    pos = Union{TT...}[]
    for i in 1:length(TT)
        if _nd[i] > 1
            push!(pos,ones(T[i],_nd[i]))
Victor's avatar
Victor committed
41
        else
Victor's avatar
Victor committed
42
            pos = push!(pos,one(T[i]))
Victor's avatar
Victor committed
43
44
        end
    end
Victor's avatar
Victor committed
45
    Tuple{T...},pos
Victor's avatar
Victor committed
46
47
end

48
49
# default initialiser
"""
Victor's avatar
Victor committed
50
51
52
53
54
55
56
57
    Agent(s; ancestors=false,rates=true)
    Agent(s, pos;ancestors=false,rates=true)

Returns an `Agent` living on the underlying space `s` 
with initial position `pos`. If `pos` not provided, 
initialises agent with 0 values everywhere

# Keyword arguments
Victor's avatar
Victor committed
58
* `rates`. Set `rates=true` when agents fitness needs to be updated at each time step. This 
Victor's avatar
Victor committed
59
is required for the Gillepsie algorithm, but not for CFM algorithm
Victor's avatar
Victor committed
60
* `ancestors`. Set `ancestors=true` when you want to store ancestors traits.
Victor's avatar
Victor committed
61
"""
Victor's avatar
Victor committed
62
function Agent(s::S; ancestors=false,rates=true) where {S  <: AbstractSpacesTuple}
Victor's avatar
Victor committed
63
    T,pos = _initpos(s)
Victor's avatar
Victor committed
64
    t = 0.
65
    U =  Float64
Victor's avatar
Victor committed
66
67
68
    d = rates ?  Float64(.0) : nothing
    b = d
    V = rates ?  Float64 : Nothing
Victor's avatar
Victor committed
69
    Agent{Ancestors{ancestors},Rates{rates},T,U,V}([pos],[t],d,b)
Victor's avatar
Victor committed
70
71
end

Victor's avatar
Victor committed
72
73
74
# here pos t should be provided
# this allows to initilise agents from any time steps knowing ancestors traits
function Agent(s::S,pos_t::Vector,t::Vector{U};ancestors=false,rates=true) where {S <: AbstractSpacesTuple, U <: AbstractFloat}
75
    T = eltype.(s)
Victor's avatar
Victor committed
76
    TT = _get_types_dim(s)
Victor's avatar
Victor committed
77
78
79
80
81
82
    length(pos_t) == length(t) ? nothing : ArgumentError("length of `pos` should match length of `t`")
    # we convert all
    pos2_t = Vector{Union{TT...}}[]
    for pos in pos_t
        pos2 = Union{TT...}[]
        for (i,p) in enumerate(pos)
Victor's avatar
Victor committed
83
            try
Victor's avatar
Victor committed
84
                push!(pos2,convert(TT[i],p))
Victor's avatar
Victor committed
85
            catch e
Victor's avatar
Victor committed
86
                throw(ArgumentError("Position at dimension $i provided does not match with underlying space"))
Victor's avatar
Victor committed
87
88
            end
        end
Victor's avatar
Victor committed
89
        push!(pos2_t,pos2)
Victor's avatar
Victor committed
90
    end
Victor's avatar
Victor committed
91
    # U =  Float64
Victor's avatar
Victor committed
92
93
94
    d = rates ?  Float64(.0) : nothing
    b = d
    V = rates ?  Float64 : Nothing
Victor's avatar
Victor committed
95
    Agent{Ancestors{ancestors},Rates{rates},Tuple{T...},U,V}(pos2_t,t,d,b)
Victor's avatar
Victor committed
96
97
end

Victor's avatar
Victor committed
98
99
100
Agent(s, pos; ancestors=false, rates=true) = Agent(s, [pos], [0.],ancestors=ancestors, rates=rates)


Victor's avatar
Victor committed
101
import Base:copy,show
Victor's avatar
Victor committed
102

Victor's avatar
Victor committed
103
Base.copy(a::A) where {A<:AbstractAgent} = A(copy(a.x_history),copy(a.t_history),copy(a.d),copy(a.b))
Victor's avatar
Victor committed
104

Victor's avatar
Victor committed
105
106
# this function only copies the trait history and time (x,t), and set birth and death rates to 0.
copyxt(a::Agent{A,R,T,U,V}) where {A,R,T,U,V<:Number} = Agent{A,R,T,U,V}(copy(a.x_history),copy(a.t_history),zero(V),zero(V))
Victor's avatar
Victor committed
107

Victor's avatar
Victor committed
108
copyxt(a::Agent{A,R,T,U,Nothing}) where {A,R,T,U} = Agent{A,R,T,U,Nothing}(copy(a.x_history),copy(a.t_history),nothing,nothing)
Victor's avatar
Victor committed
109

Victor's avatar
Victor committed
110
# this has to be overloaded for Base.copy(a::Agent) to work properly
111
Base.copy(m::Missing) = missing
Victor's avatar
Victor committed
112

113
Base.copy(n::Nothing) = nothing
114

Victor's avatar
Victor committed
115
function Base.show(io::IO, a::Agent{A,R,T,U,V}) where {A,R,T,U,V}
Victor's avatar
Victor committed
116
     println(io, "Agent with indices of type ", T)
Victor's avatar
Victor committed
117
end
Victor's avatar
Victor committed
118
119
120

Base.summary(A::AbstractAgent) = string(TYPE_COLOR,nameof(typeof(a)),NO_COLOR," with uType ",TYPE_COLOR,eltype(a.x_history))

Victor's avatar
Victor committed
121
122
123
124
#####################
###Agent accessors###
#####################

Victor's avatar
Victor committed
125
126
Base.getindex(a::Agent,i) = a.x_history[end][i]

127
"""
Victor's avatar
Victor committed
128
    get_x(a::Agent)
Victor's avatar
Victor committed
129
Returns trait i of the agent.
130
"""
Victor's avatar
Victor committed
131
132
get_x(a::Agent) = a.x_history[end]
@deprecate get_x(a) a[:]
Victor's avatar
Victor committed
133
134

"""
Victor's avatar
Victor committed
135
$(SIGNATURES)
Victor's avatar
Victor committed
136
Returns geotrait of agent `a` at time `t`.
Victor's avatar
Victor committed
137
"""
Victor's avatar
Victor committed
138
function get_geo(a::Agent{A,R,T,U,V},t::Number) where {A<:Ancestors{true},R,T,U,V}
Victor's avatar
Victor committed
139
    tarray = vcat(a.t_history[2:end],convert(U,t))
140
141
142
    tarray .-= a.t_history
    return sum(get_xhist(a,1) .* tarray)
end
Victor's avatar
Victor committed
143

144
# This method can acces geotrait, while the second not
Victor's avatar
Victor committed
145
"""
Victor's avatar
Victor committed
146
$(SIGNATURES)
Victor's avatar
Victor committed
147
148
149
Returns trait `i` of the agent.
Geotrait corresponds to dimension `i=0`.
"""
150
get_x(a::AbstractAgent,t::Number,i::Integer) = i > 0 ? a.x_history[end][Int(i)] : get_geo(a,t)
Victor's avatar
Victor committed
151
152

"""
Victor's avatar
Victor committed
153
$(SIGNATURES)
Victor's avatar
Victor committed
154
155
Get time when agent born.
"""
Victor's avatar
Victor committed
156
157
158
get_t(a::Agent) = a.t_history[end]

# TODO: change this with getindex
159
get_xhist(a::AbstractAgent,i::Number) = [a.x_history[t][Int(i)] for t in 1:length(a.x_history)]
Victor's avatar
Victor committed
160

161
get_xhist(a::AbstractAgent) = a.x_history
Victor's avatar
Victor committed
162

163
get_thist(a::AbstractAgent) = a.t_history
164

Victor's avatar
Victor committed
165
get_d(a::AbstractAgent) = a.d
166

Victor's avatar
Victor committed
167
168
169
170
get_b(a::AbstractAgent) = a.b

get_fitness(a::AbstractAgent) = a.b - a.d

171
172
173
# TODO : we can surely extract N in Agent{A,R,Tuple{Vararg{S,N}},U,V}
# Inspiration : where U <: Union{Missing,Agent{T}} where T
Base.length(a::AbstractAgent) = length(a.x_history[end])
Victor's avatar
Victor committed
174
175

nancestors(a::Agent) = length(a.x_history)
176

177
import Base.zero
Victor's avatar
Victor committed
178

179
Base.zero(t::Tuple{Vararg{Union{Number,Tuple{Vararg{Number}}}}}) = [zero.(e) for e in t]
Victor's avatar
Victor committed
180

181
182
183
184
import Base.(+)
(+)(t1::Tuple{Vararg{T,N}},t2::Tuple{Vararg{T,N}}) where {T<:Number,N}= tuple([t1[i] + t2[i] for i in 1:length(t1)]...)

function _get_xinc(a::AbstractAgent,s::AbstractSpacesTuple,p::Dict,t::Number)
Victor's avatar
Victor committed
185
    @unpack D,mu = p
186
    _x = deepcopy(get_x(a))
187
    for (i,ss) in enumerate(s)
Victor's avatar
Victor committed
188
189
        E = eltype(mu[i])
        S = eltype(ss)
vboussange's avatar
vboussange committed
190
        if length(mu[i]) > 1
Victor's avatar
Victor committed
191
            mut = (rand(E,ndims(ss)) .< mu[i]) .|> S
192
193
            _x[i] .+= mut .* get_inc(_x[i],D[i],ss,t)
        else
Victor's avatar
Victor committed
194
195
196
197
            mut = rand(E) < mu[i]
            if mut
                _x[i] += get_inc(_x[i],D[i],ss,t)
            end
Victor's avatar
Victor committed
198
        end
199
    end
200
    _x
201
202
203
204
205
206
207
208
209
end

## Modifiers
"""
    $(SIGNATURES)
This function increments agent by random numbers specified in p
ONLY FOR CONTINUOUS DOMAINS
"""
function increment_x!(a::AbstractAgent{A,R},s::AbstractSpacesTuple,p::Dict,t::T) where {A<:Ancestors{true},R,T}
Victor's avatar
Victor committed
210
    push!(a.t_history,t)
211
212
    a.x_history = push!(a.x_history,_get_xinc(a,s,p,t))
    return a
213
214
end

215
function increment_x!(a::AbstractAgent{A,R},s::AbstractSpacesTuple,p::Dict,t::T) where {A<:Ancestors{false},R,T}
Victor's avatar
Victor committed
216
217
    a.t_history[1] = t
    a.x_history[1] = _get_xinc(a,s,p,t)
218
    return a
219
end