-
Bentriou Mahmoud authored
It computes the euclidean distance and finish the simulation if the distance is above the tolerance epsilon. I ha to change some function signature of types in core/model.jl but it was minor changes. I add a new test + change some others related to automata. A minor rewriting core/model for better readability
Bentriou Mahmoud authoredIt computes the euclidean distance and finish the simulation if the distance is above the tolerance epsilon. I ha to change some function signature of types in core/model.jl but it was minor changes. I add a new test + change some others related to automata. A minor rewriting core/model for better readability
_utils_abc.jl 5.87 KiB
function _init_abc_automaton!(mat_p_old::Matrix{Float64}, vec_dist::Vector{Float64},
pm::ParametricModel, sym_var_aut::VariableAutomaton;
l_obs::Union{Nothing,AbstractVector} = nothing,
func_dist::Union{Nothing,Function} = nothing)
vec_p = zeros(pm.df)
for i = eachindex(vec_dist)
draw!(vec_p, pm)
mat_p_old[:,i] = vec_p
if l_obs == nothing
S = volatile_simulate(pm, vec_p; epsilon = Inf)
vec_dist[i] = S[sym_var_aut]
else
l_sim = [simulate(pm, vec_p) for i = 1:length(l_obs)]
vec_dist[i] = func_dist(l_sim, l_obs)
end
end
end
function _compute_epsilon(vec_dist::Vector{Float64}, α::Float64,
old_eps::Float64, last_eps::Float64)
u_vec_dist = unique(vec_dist)
if length(u_vec_dist) == 2
epsilon = quantile(u_vec_dist, α)
else
epsilon = quantile(vec_dist, α)
end
if old_eps == epsilon
print("eps == old_eps, we readjust")
s_dist = sort(u_vec_dist)
max_dist = 0.0
for d in s_dist
if (d < epsilon) && (max_dist < d)
max_dist = d
end
end
max_dist = (max_dist == 0) ? 0.9*epsilon : max_dist
epsilon = max_dist
end
epsilon = (epsilon < last_eps) ? last_eps : epsilon
end
function _task_worker!(d_mat_p::DArray{Float64,2}, d_vec_dist::DArray{Float64,1},
d_wl_current::DArray{Float64,1},
pm::ParametricModel, epsilon::Float64,
wl_old::Vector{Float64}, mat_p_old::Matrix{Float64},
mat_cov::Matrix{Float64}, tree_mat_p::Union{Nothing,KDTree},
kernel_type::String, sym_var_aut::VariableAutomaton;
l_obs::Union{Nothing,AbstractVector} = nothing,
func_dist::Union{Nothing,Function} = nothing)
mat_p = localpart(d_mat_p)
vec_dist = localpart(d_vec_dist)
wl_current = localpart(d_wl_current)
l_nbr_sim = zeros(Int, length(vec_dist))
Threads.@threads for i = eachindex(vec_dist)
_update_param!(mat_p, vec_dist, l_nbr_sim, wl_current, i, pm, epsilon,
wl_old, mat_p_old, mat_cov, tree_mat_p, kernel_type, sym_var_aut;
l_obs = l_obs, func_dist = func_dist)
end
return sum(l_nbr_sim)
end
function _draw_param_kernel!(vec_p_prime::Vector{Float64},
vec_p_star::SubArray{Float64,1},
mat_p_old::Matrix{Float64}, wl_old::Vector{Float64},
mat_cov::Matrix{Float64},
tree_mat_p::Union{KDTree,Nothing}, kernel_type::String)
if kernel_type == "mvnormal"
d_mvnormal = MvNormal(vec_p_star, mat_cov)
rand!(d_mvnormal, vec_p_prime)
elseif kernel_type == "knn_mvnormal"
k = Int(round(0.25 * size(mat_p_old)[2]))
idxs, dist = knn(tree_mat_p, vec_p_star, k, true)
knn_mat_cov = 2 * cov(mat_p_old[:,idxs], ProbabilityWeights(wl_old[idxs]), 2; corrected=false)
d_knn_mvnormal = Distributions.MvNormal(vec_p_star, knn_mat_cov)
rand!(d_knn_mvnormal, vec_p_prime)
return knn_mat_cov
else
error("Unknown specified kernel")
end
end
function _update_param!(mat_p::Matrix{Float64}, vec_dist::Vector{Float64},
l_nbr_sim::Vector{Int}, wl_current::Vector{Float64},
idx::Int,
pm::ParametricModel, epsilon::Float64,
wl_old::Vector{Float64}, mat_p_old::Matrix{Float64},
mat_cov::Matrix{Float64}, tree_mat_p::Union{Nothing,KDTree},
kernel_type::String, sym_var_aut::VariableAutomaton;
l_obs::Union{Nothing,AbstractVector} = nothing,
func_dist::Union{Nothing,Function} = nothing)
d_weights = Categorical(wl_old)
dist_sim = Inf
nbr_sim = 0
vec_p_prime = zeros(pm.df)
while dist_sim > epsilon
ind_p_star = rand(d_weights)
vec_p_star = view(mat_p_old, :, ind_p_star)
knn_mat_cov = _draw_param_kernel!(vec_p_prime, vec_p_star, mat_p_old, wl_old, mat_cov, tree_mat_p, kernel_type)
if !insupport(pm, vec_p_prime)
continue
end
if l_obs == nothing
S = volatile_simulate(pm, vec_p_prime; epsilon = epsilon)
dist_sim = S[sym_var_aut]
else
l_sim = [simulate(pm, vec_p_prime) for i = 1:length(l_obs)]
dist_sim = func_dist(l_sim, l_obs)
end
nbr_sim += 1
end
if kernel_type == "mvnormal"
mat_cov_kernel = mat_cov
elseif kernel_type == "knn_mvnormal"
mat_cov_kernel = knn_mat_cov
else
error("Unknown kernel")
end
# Update
mat_p[:,idx] = vec_p_prime
vec_dist[idx] = dist_sim
l_nbr_sim[idx] = nbr_sim
_update_weight!(wl_current, idx, pm, wl_old, mat_p_old, vec_p_prime, mat_cov_kernel, kernel_type)
end
function _update_weight!(wl_current::Vector{Float64}, idx::Int,
pm::ParametricModel,
wl_old::Vector{Float64}, mat_p_old::Matrix{Float64},
vec_p_prime::Vector{Float64},
mat_cov_kernel::Matrix{Float64}, kernel_type::String)
denom = 0.0
for j in 1:length(wl_old)
#denom += wl_old[j] * Distributions.pdf(d_normal, inv_sqrt_mat_cov * (vec_p_current - mat_p_old[:,j]))::Float64
denom += wl_old[j] * pdf(MvNormal(mat_p_old[:,j], mat_cov_kernel), vec_p_prime)::Float64
end
wl_current[idx] = prior_pdf(pm, vec_p_prime) / denom
end
function effective_sample_size(wl::AbstractVector{Float64})
n_eff = 0.0
wls = sum(wl)
if wls > 0.0
n_eff = wls ^ 2 / sum(wl .^ 2)
end
return n_eff
end