ABMEv_Agent.jl 6.19 KB
Newer Older
1
2
3
4
abstract type StdAgent end
abstract type MixedAgent end

mutable struct Agent{T,U}
5
    # history of traits for geotraits
6
    x_history::Array{U}
7
8
9
10
11
12
13
    # death rate
    d::Float64
    #birth rate
    b::Float64
end

# Constructors
14
# This  constructor should be used when one wants to impose the type of the agent (e.g. Mixed)
15
Agent{T}(xhist::Array{U}) where {T,U} = Agent{T,U}(reshape(xhist,:,1),0.,1.)
16
17

# This constructor is used by default
18
Agent(xhist::Array{U}) where {U <: Number} = Agent{StdAgent}(xhist)
19

20
Agent() = Agent(Float64[],0.,1.)
21
import Base.copy
22
copy(a::Agent{T,U}) where {T,U} = Agent{T,U}(a.x_history,a.d,a.b)
23
24
copy(m::Missing) = missing

25
26
27
28
29
30
31
32
33
34
35
36
"""
    function new_world_G(nagents::Int,p::Dict; spread = 1., offset = 0.)
Returns an array of type Array{Union{Missing,Agent}} initialised with normal distribution.
Only relevant for Gillepsie algorithm as of now.
"""
function new_world_G(nagents::Int,p::Dict; spread = 1., offset = 0.)
    typeof(spread) <: Array ? spread = spread[:] : nothing;
    typeof(offset) <: Array ? offset = offset[:] : nothing;
    agent0 = [Agent( spread  .* randn(length(spread)) .+ offset) for i in 1:nagents]
    world0 = vcat(agent0[:],repeat([missing],Int(p["NMax"] - nagents)))
    return world0
end
37
38

# returns trait i of the agent
39
get_xhist(a::Agent,i::Number) = a.x_history[Int(i),:]
40
get_xhist(a::Agent) = a.x_history
41
get_x(a::Agent) = a.x_history[:,end]
42
get_geo(a::Agent) = sum(get_xhist(a,1))
43
get_x(a::Agent,i::Number) = i > 0 ? a.x_history[Int(i),end] : get_geo(a)
44
45
46
get_d(a::Agent) = a.d
get_b(a::Agent) = a.b
get_fitness(a::Agent) = a.b - a.d
47
48
get_dim(a::Agent) = size(a.x_history,1)
get_nancestors(a::Agent) = size(a.x_history,2)
49
"""
50
    get_xarray(world::Array{Agent},trait::Int)
51
If trait = 0 , we return the geotrait.
52
Mainly works for WF-type world
53
54
55
Returns trait of every agents of world in the form of an array which dimensions corresponds to the input.
Particularly suited for an array world corresponding to a timeseries.

56
"""
57
get_xarray(world::Array{T},trait::Int) where {T <: Agent} = trait > 0 ? reshape(hcat(get_x.(world,trait)),size(world,1),size(world,2)) : reshape(hcat(get_geo.(world)),size(world,1),size(world,2))
58

59
60
61
62
63
"""
    get_xarray(world::Array{Agent,1})
Returns every traits of every agents of world in the form of an array
If geotrait = true, then a last trait dimension is added, corresponding to geotrait.
"""
64
function get_xarray(world::Array{T,1},geotrait::Bool=false) where {T <: Agent}
65
66
67
68
    xarray = hcat(get_x.(world)...)
    if geotrait
        xarray = vcat( xarray, get_geo.(world)')
    end
Victor's avatar
Victor committed
69
    return xarray
70
71
end

72
"""
73
    get_xhist(world::Vector{Agent},geotrait = false)
74
75
76
77
78
79
80
81
82
Returns the trait history of every agents of world in the form of an 3 dimensional array,
with
- first dimension as the agent index
- second as time index
- third as trait index
If geotrait = true, then a last trait dimension is added, corresponding to geotrait.
Note that because number of ancestors are different between agents, we return an array which size corresponds to the minimum of agents ancestors,
and return the last generations, dropping the youngest ones
"""
Victor's avatar
Victor committed
83
function get_xhist(world::Vector{T},geotrait = false) where {T <: Agent}
84
    hist = minimum(get_nancestors.(world))
Victor's avatar
Victor committed
85
86
87
    ntraits = get_dim(first(world));
    xhist = zeros(length(world), hist, ntraits + geotrait);
    for (i,a) in enumerate(world)
88
89
90
91
92
93
94
95
        xhist[i,:,1:end-geotrait] = get_xhist(a)[:,end-hist+1:end]';
        if geotrait
            xhist[i,:,ntraits+geotrait] = cumsum(get_xhist(a,1))[end-hist+1:end]
        end
    end
    return xhist
end

96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
"""
    world2df(world::Array{T,1}; geotrait = false) where {T <: Agent}
Converts the array of agent world to a datafram, where each column corresponds to a trait of the
agent, and an extra column captures fitness.
Each row corresponds to an agent
"""
function world2df(world::Array{T,1}; geotrait = false) where {T <: Agent}
    xx = get_xarray(world)
    dfw = DataFrame(:f => get_fitness.(world))
    for i in 1:size(xx,1)
        dfw[Meta.parse("x$i")] = xx[i,:]
    end
    if geotrait
        dfw[:g] = get_geo.(world)
    end
    return dfw
end


115

116
"""
117
118
increment_x!(a::Agent{StdAgent,U},p::Dict)
    This function increments agent by random numbers specified in p
119
120
    ONLY FOR CONTINUOUS DOMAINS
"""
121
function increment_x!(a::Agent{StdAgent,U},p::Dict) where U
122
    tdim = length(p["D"])
Victor's avatar
Victor committed
123
    reflected = haskey(p,"reflected") ? p["reflected"] : false
124
    if reflected
Victor Boussange's avatar
Victor Boussange committed
125
        inc = [get_inc_reflected(get_x(a,1),p["D"][1] *randn())]
126
        if  tdim > 1
Victor Boussange's avatar
Victor Boussange committed
127
            inc = vcat(inc,(rand(tdim-1) < p["mu"][2:end]) .* p["D"][2:end] .* randn(tdim-1))
128
129
        end
    else
Victor Boussange's avatar
Victor Boussange committed
130
131
        # inc = yes no mutation * mutation
        inc = (rand(tdim) < vec(p["mu"])) .* vec(p["D"][:]) .* randn(tdim)
132
133
134
135
136
    end
    a.x_history = hcat(a.x_history, get_x(a) + reshape(inc,:,1));
 end

 """
137
138
     function increment_x!(a::Agent{MixedAgent,U},p::Dict)
 This function increments first trait of agent with integer values, that are automatically reflected between 1 and p["nodes"].
139
140
Other traits are incremented as usual.
TODO : make it work for a graph type landscape, where domain is not a line anymore.
141
 """
142
143
144
145
146
147
148
149
150
 function increment_x!(a::Agent{MixedAgent,U},p::Dict) where U
     tdim = length(p["D"])
     inc = [round(get_inc_reflected(get_x(a,1),p["D"][1] *randn(),1,p["nodes"]))]
     if  tdim > 1
         inc = vcat(inc,(rand(tdim-1) < p["mu"][2:end]) .* p["D"][2:end] .* randn(tdim-1))
     end
     a.x_history = hcat(a.x_history, get_x(a) + reshape(inc,:,1));
end

151
152
153
154


"""
get_inc_reflected(x::Number,inc::Number,s=-1,e=1)
155
156
    Here we increment the trajectory of trait 1 such that it follows a reflected brownian motion (1D)
"""
157
function get_inc_reflected(x::Number,inc::Number,s=-1,e=1)
158
    if x + inc < s
159
        inc = 2 * ( s - x ) - inc
160
    elseif  x + inc > e
161
        inc = 2 * ( e - x ) - inc
162
163
164
    else
        return inc
    end
165
    get_inc_reflected(x,inc,s,e)
166
167
168
end

"""
169
    function tin(t::Number,a::Number,b::Number)
170
171
172
if t in [a,b) returns 1. else returns 0
"""

173
function tin(t::Number,a::Number,b::Number)
174
175
176
177
178
179
180
181
182
183
    return t>=a && t<b ? 1. : 0.
end

function split_move(t)
    return .0 + 1/100*(t-20.)*tin(t,20.,120.) + tin(t,120.,Inf64)
end

function split_merge_move(t)
    return .0 + 1/30*(t-10.)*tin(t,10.,40.) + tin(t,40.,70.) + (1- 1/30*(t-70.))*tin(t,70.,100.)
end