diff --git a/algorithms/_utils_abc.jl b/algorithms/_utils_abc.jl index 9e4803459e4b167aec9d370e8cf922db854a7745..7ba9c84af4e895dc32b216b47930bea6c0ae6d04 100644 --- a/algorithms/_utils_abc.jl +++ b/algorithms/_utils_abc.jl @@ -8,7 +8,7 @@ function _init_abc_automaton!(mat_p_old::Matrix{Float64}, vec_dist::Vector{Float draw!(vec_p, pm) mat_p_old[:,i] = vec_p if l_obs == nothing - S = volatile_simulate(pm, vec_p) + 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)] @@ -101,7 +101,7 @@ function _update_param!(mat_p::Matrix{Float64}, vec_dist::Vector{Float64}, continue end if l_obs == nothing - S = volatile_simulate(pm, vec_p_prime) + 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)] diff --git a/algorithms/abc_smc.jl b/algorithms/abc_smc.jl index 0640de6cd660d323644ae9ff63f67f7672506c1d..ee22a2d942f48472c8d70b7bc46c2fd6ba1d447a 100644 --- a/algorithms/abc_smc.jl +++ b/algorithms/abc_smc.jl @@ -125,6 +125,7 @@ function _abc_smc(pm::ParametricModel, nbr_particles::Int, tolerance::Float64, a l_nbr_sim = zeros(Int, nbr_particles) while (epsilon > last_epsilon) && (current_time - begin_time <= duration_time) && (nbr_tot_sim <= bound_sim) t += 1 + @show pm.m.automaton.ϵ, epsilon begin_time_ite = time() @info "Step $t" # Set new epsilon diff --git a/automata/abc_euclidean_distance_automaton.jl b/automata/abc_euclidean_distance_automaton.jl new file mode 100644 index 0000000000000000000000000000000000000000..d1894b97c385313148abb9602d01734f501edd13 --- /dev/null +++ b/automata/abc_euclidean_distance_automaton.jl @@ -0,0 +1,354 @@ + +@everywhere import FunctionWrappers: FunctionWrapper +@everywhere const ABCCheckConstraintsFunction = FunctionWrapper{Bool,Tuple{Float64,Vector{Float64},Vector{Int},Vector{Float64},Float64}} +@everywhere const ABCUpdateStateFunction = FunctionWrapper{Symbol,Tuple{Float64,Vector{Float64},Vector{Int},Vector{Float64},Float64}} + +@everywhere struct EdgeABCEuclideanDistanceAutomaton <: Edge + transitions::TransitionSet + check_constraints::ABCCheckConstraintsFunction + update_state!::ABCUpdateStateFunction +end + +# Creation of the automaton types +@everywhere mutable struct ABCEuclideanDistanceAutomaton <: LHA + transitions::Vector{Transition} + locations::Vector{Location} + Λ::Dict{Location,InvariantPredicateFunction} + locations_init::Vector{Location} + locations_final::Vector{Location} + map_var_automaton_idx::Dict{VariableAutomaton,Int} # nvar keys : str_var => idx in values + flow::Dict{Location,Vector{Float64}} # output of length nvar + map_edges::Dict{Location, Dict{Location,Vector{EdgeABCEuclideanDistanceAutomaton}}} + constants::Dict{Symbol,Float64} + map_var_model_idx::Dict{VariableModel,Int} # of dim d (of a model) + ϵ::Float64 +end + +# A push! method implementend by myself because of preallocation of edge_candidates +function _push_edge!(edge_candidates::Vector{<:EdgeABCEuclideanDistanceAutomaton}, + edge::EdgeABCEuclideanDistanceAutomaton, nbr_candidates::Int) + if nbr_candidates < length(edge_candidates) + edge_candidates[nbr_candidates+1] = edge + else + push!(edge_candidates, edge) + end +end + +function _find_edge_candidates!(edge_candidates::Vector{EdgeABCEuclideanDistanceAutomaton}, + edges_from_current_loc::Dict{Location,Vector{EdgeABCEuclideanDistanceAutomaton}}, + Λ::Dict{Location,InvariantPredicateFunction}, + S_time::Float64, S_values::Vector{Float64}, + x::Vector{Int}, p::Vector{Float64}, ϵ::Float64, + only_asynchronous::Bool) + nbr_candidates = 0 + for target_loc in keys(edges_from_current_loc) + if !Λ[target_loc](x) continue end + for edge in edges_from_current_loc[target_loc] + if edge.check_constraints(S_time, S_values, x, p, ϵ) + if edge.transitions == nothing + _push_edge!(edge_candidates, edge, nbr_candidates) + nbr_candidates += 1 + return nbr_candidates + else + if !only_asynchronous + _push_edge!(edge_candidates, edge, nbr_candidates) + nbr_candidates += 1 + end + end + end + end + end + return nbr_candidates +end + +function _get_edge_index(edge_candidates::Vector{EdgeABCEuclideanDistanceAutomaton}, nbr_candidates::Int, + detected_event::Bool, tr_nplus1::Transition) + ind_edge = 0 + bool_event = detected_event + for i = 1:nbr_candidates + edge = edge_candidates[i] + # Asynchronous edge detection: we fire it + if edge.transitions == nothing + return (i, detected_event) + end + # Synchronous detection + if !detected_event && tr_nplus1 != nothing + if (edge.transitions[1] == :ALL) || (tr_nplus1 in edge.transitions) + ind_edge = i + bool_event = true + end + end + end + return (ind_edge, bool_event) +end + +function next_state!(A::ABCEuclideanDistanceAutomaton, + ptr_loc_state::Vector{Symbol}, values_state::Vector{Float64}, ptr_time_state::Vector{Float64}, + xnplus1::Vector{Int}, tnplus1::Float64, tr_nplus1::Transition, + xn::Vector{Int}, p::Vector{Float64}, + edge_candidates::Vector{EdgeABCEuclideanDistanceAutomaton}; verbose::Bool = false) + # En fait d'apres observation de Cosmos, après qu'on ait lu la transition on devrait stop. + detected_event::Bool = false + turns = 0 + Λ = getfield(A, :Λ) + flow = getfield(A, :flow) + map_edges = getfield(A, :map_edges) + ϵ = A.ϵ + if verbose + println("##### Begin next_state!") + @show xnplus1, tnplus1, tr_nplus1 + end + # First, we check the asynchronous transitions + while true + turns += 1 + #edge_candidates = empty!(edge_candidates) + edges_from_current_loc = map_edges[ptr_loc_state[1]] + # Save all edges that satisfies transition predicate (asynchronous ones) + nbr_candidates = _find_edge_candidates!(edge_candidates, edges_from_current_loc, Λ, + ptr_time_state[1], values_state, xn, p, ϵ, true) + # Search the one we must chose, here the event is nothing because + # we're not processing yet the next event + ind_edge, detected_event = _get_edge_index(edge_candidates, nbr_candidates, detected_event, nothing) + # Update the state with the chosen one (if it exists) + # Should be xn here + #first_round = false + if ind_edge > 0 + firing_edge = edge_candidates[ind_edge] + ptr_loc_state[1] = firing_edge.update_state!(ptr_time_state[1], values_state, xn, p, ϵ) + else + if verbose println("No edge fired") end + break + end + if verbose + @show turns + @show edge_candidates + @show ind_edge, detected_event, nbr_candidates + @show ptr_loc_state[1] + @show ptr_time_state[1] + @show values_state + if turns == 500 + @warn "We've reached 500 turns" + end + end + # For debug + #= + if turns > 100 + println("Number of turns in next_state! is suspicious") + @show first_round, detected_event + @show length(edge_candidates) + )@show tnplus1, tr_nplus1, xnplus1 + @show edge_candidates + error("Unpredicted behavior automaton") + end + =# + end + if verbose + println("Time flies with the flow...") + end + # Now time flies according to the flow + for i in eachindex(values_state) + coeff_deriv = flow[ptr_loc_state[1]][i] + if coeff_deriv > 0 + values_state[i] += coeff_deriv*(tnplus1 - ptr_time_state[1]) + end + end + ptr_time_state[1] = tnplus1 + if verbose + @show ptr_loc_state[1] + @show ptr_time_state[1] + @show values_state + end + # Now firing an edge according to the event + while true + turns += 1 + edges_from_current_loc = map_edges[ptr_loc_state[1]] + # Save all edges that satisfies transition predicate (synchronous ones) + nbr_candidates = _find_edge_candidates!(edge_candidates, edges_from_current_loc, Λ, + ptr_time_state[1], values_state, xnplus1, p, ϵ, false) + # Search the one we must chose + ind_edge, detected_event = _get_edge_index(edge_candidates, nbr_candidates, detected_event, tr_nplus1) + # Update the state with the chosen one (if it exists) + if ind_edge > 0 + firing_edge = edge_candidates[ind_edge] + ptr_loc_state[1] = firing_edge.update_state!(ptr_time_state[1], values_state, xnplus1, p, ϵ) + end + if ind_edge == 0 || detected_event + if verbose + if detected_event + println("Synchronized with $(tr_nplus1)") + @show turns + @show edge_candidates + @show ind_edge, detected_event, nbr_candidates + @show detected_event + @show ptr_loc_state[1] + @show ptr_time_state[1] + @show values_state + else + println("No edge fired") + end + end + break + end + if verbose + @show turns + @show edge_candidates + @show ind_edge, detected_event, nbr_candidates + @show detected_event + @show ptr_loc_state[1] + @show ptr_time_state[1] + @show values_state + if turns == 500 + @warn "We've reached 500 turns" + end + end + # For debug + #= + if turns > 100 + println("Number of turns in next_state! is suspicious") + @show detected_event + @show length(edge_candidates) + @show tnplus1, tr_nplus1, xnplus1 + @show edge_candidates + error("Unpredicted behavior automaton") + end + =# + end + if verbose + println("##### End next_state!") + end +end + +function create_abc_euclidean_distance_automaton(m::ContinuousTimeModel, timeline::AbstractVector{Float64}, observations::AbstractVector{Float64}, sym_obs::VariableModel) + # Requirements for the automaton + @assert sym_obs in m.g "$(sym_obs) is not observed." + @assert length(timeline) == length(observations) "Timeline and observations vectors don't have the same length" + nbr_observations = length(observations) + + # Automaton types and functions + model_name = Symbol(typeof(m)) + lha_name = :ABCEuclideanDistanceAutomaton + edge_type = :EdgeABCEuclideanDistanceAutomaton + check_constraints = Symbol("check_constraints_$(lha_name)") + update_state! = Symbol("update_state_$(lha_name)!") + + # Locations + locations = [:l0, :l1, :l2] + + ## Invariant predicates + @everywhere true_inv_predicate(x::Vector{Int}) = true + Λ_F = Dict{Location,InvariantPredicateFunction}(:l0 => getfield(Main, :true_inv_predicate), :l1 => getfield(Main, :true_inv_predicate), + :l2 => getfield(Main, :true_inv_predicate)) + + ## Init and final loc + locations_init = [:l0] + locations_final = [:l2] + + map_var_automaton_idx = Dict{VariableAutomaton,Int}(:t => 1, :n => 2, + :d => 3, :idx => 4) + vector_flow = [1.0, 0.0, 0.0, 0.0] + flow = Dict{Location,Vector{Float64}}(:l0 => vector_flow, + :l1 => vector_flow, + :l2 => vector_flow) + + ## Edges + idx_obs_var = m.map_var_idx[sym_obs] + to_idx(var::Symbol) = map_var_automaton_idx[var] + + id = MarkovProcesses.newid() + function check_constraints(from_loc::Location, to_loc::Location, edge_number::Int) + return Symbol("check_constraints_$(edge_type)_$(from_loc)$(to_loc)_$(edge_number)_$(model_name)_$(id)") + end + function update_state!(from_loc::Location, to_loc::Location, edge_number::Int) + return Symbol("update_state_$(edge_type)_$(from_loc)$(to_loc)_$(edge_number)_$(model_name)_$(id)!") + end + + ## check_constraints & update_state! + meta_funcs = quote + # l0 loc + # l0 => l1 + #struct $(edge_name(:l0, :l1, 1)) <: $(edge_type) transitions::Union{Nothing,Vector{Symbol}} end + @everywhere $(check_constraints(:l0, :l1, 1))(S_time::Float64, S_values::Vector{Float64}, x::Vector{Int}, p::Vector{Float64}, ϵ::Float64) = true + @everywhere $(update_state!(:l0, :l1, 1))(S_time::Float64, S_values::Vector{Float64}, x::Vector{Int}, p::Vector{Float64}, ϵ::Float64) = + (S_values[$(to_idx(:n))] = x[$(idx_obs_var)]; + S_values[$(to_idx(:d))] = 0.0; + S_values[$(to_idx(:idx))] = 1.0; + :l1) + + # l1 loc + # l1 => l1 + # Defined below + #struct $(edge_name(:l1, :l1, 1)) <: $(edge_type) transitions::Union{Nothing,Vector{Symbol}} end + @everywhere $(check_constraints(:l1, :l1, 1))(S_time::Float64, S_values::Vector{Float64}, x::Vector{Int}, p::Vector{Float64}, ϵ::Float64) = + (tml = $(Tuple(timeline)); + tml_idx = tml[convert(Int, S_values[$(to_idx(:idx))])]; + S_values[$(to_idx(:t))] >= tml_idx) + @everywhere $(update_state!(:l1, :l1, 1))(S_time::Float64, S_values::Vector{Float64}, x::Vector{Int}, p::Vector{Float64}, ϵ::Float64) = + (y_obs = $(Tuple(observations)); + y_obs_idx = y_obs[convert(Int, S_values[$(to_idx(:idx))])]; + S_values[$(to_idx(:d))] += (S_values[$(to_idx(:n))]-y_obs_idx)^2; + S_values[$(to_idx(:idx))] += 1.0; + :l1) + + #struct $(edge_name(:l1, :l1, 2)) <: $(edge_type) transitions::Union{Nothing,Vector{Symbol}} end + @everywhere $(check_constraints(:l1, :l1, 2))(S_time::Float64, S_values::Vector{Float64}, x::Vector{Int}, p::Vector{Float64}, ϵ::Float64) = true + @everywhere $(update_state!(:l1, :l1, 2))(S_time::Float64, S_values::Vector{Float64}, x::Vector{Int}, p::Vector{Float64}, ϵ::Float64) = + (S_values[$(to_idx(:n))] = x[$(idx_obs_var)]; + :l1) + + # l1 => l2 + #struct $(edge_name(:l1, :l2, 1)) <: $(edge_type) transitions::Union{Nothing,Vector{Symbol}} end + @everywhere $(check_constraints(:l1, :l2, 1))(S_time::Float64, S_values::Vector{Float64}, x::Vector{Int}, p::Vector{Float64}, ϵ::Float64) = + S_values[$(to_idx(:idx))] >= ($nbr_observations + 1) + @everywhere $(update_state!(:l1, :l2, 1))(S_time::Float64, S_values::Vector{Float64}, x::Vector{Int}, p::Vector{Float64}, ϵ::Float64) = + (S_values[$(to_idx(:d))] = sqrt(S_values[$(to_idx(:d))]); + :l2) + + @everywhere $(check_constraints(:l1, :l2, 2))(S_time::Float64, S_values::Vector{Float64}, x::Vector{Int}, p::Vector{Float64}, ϵ::Float64) = + (S_values[$(to_idx(:d))] > ϵ^2) + @everywhere $(update_state!(:l1, :l2, 2))(S_time::Float64, S_values::Vector{Float64}, x::Vector{Int}, p::Vector{Float64}, ϵ::Float64) = + (S_values[$(to_idx(:d))] = Inf; + :l2) + end + eval(meta_funcs) + + @eval begin + map_edges = Dict{Location,Dict{Location,Vector{$(edge_type)}}}() + for loc in $(locations) + map_edges[loc] = Dict{Location,Vector{$(edge_type)}}() + end + + ## Edges + # l0 loc + # l0 => l1 + edge1 = EdgeABCEuclideanDistanceAutomaton(nothing, $(check_constraints(:l0, :l1, 1)), $(update_state!(:l0, :l1, 1))) + map_edges[:l0][:l1] = [edge1] + + # l1 loc + # l1 => l1 + edge1 = EdgeABCEuclideanDistanceAutomaton(nothing, $(check_constraints(:l1, :l1, 1)), $(update_state!(:l1, :l1, 1))) + edge2 = EdgeABCEuclideanDistanceAutomaton([:ALL], $(check_constraints(:l1, :l1, 2)), $(update_state!(:l1, :l1, 2))) + map_edges[:l1][:l1] = [edge1, edge2] + + # l1 => l2 + edge1 = EdgeABCEuclideanDistanceAutomaton(nothing, $(check_constraints(:l1, :l2, 1)), $(update_state!(:l1, :l2, 1))) + edge2 = EdgeABCEuclideanDistanceAutomaton(nothing, $(check_constraints(:l1, :l2, 2)), $(update_state!(:l1, :l2, 2))) + map_edges[:l1][:l2] = [edge1,edge2] + end + + ## Constants + constants = Dict{Symbol,Float64}(:nbr_obs => nbr_observations) + for i = 1:nbr_observations + constants[Symbol("tml_$(convert(Float64, i))")] = timeline[i] + constants[Symbol("y_$(convert(Float64, i))")] = observations[i] + end + + # Updating types and simulation methods + @everywhere @eval $(MarkovProcesses.generate_code_synchronized_model_type_def(model_name, lha_name)) + @everywhere @eval $(MarkovProcesses.generate_code_synchronized_simulation(model_name, lha_name, edge_type, m.f!, m.isabsorbing)) + + A = ABCEuclideanDistanceAutomaton(m.transitions, locations, Λ_F, locations_init, locations_final, + map_var_automaton_idx, flow, map_edges, constants, m.map_var_idx, Inf) + + return A +end + diff --git a/core/MarkovProcesses.jl b/core/MarkovProcesses.jl index e9f285cf83b2695d64eee87d2231d4b64665db79..9a17dd2f80af7538a4cd538fc943830815167080 100644 --- a/core/MarkovProcesses.jl +++ b/core/MarkovProcesses.jl @@ -10,7 +10,9 @@ import Distributed: @everywhere, @distributed import Distributions: Product, Uniform, Normal import Distributions: Distribution, Univariate, Continuous, UnivariateDistribution, MultivariateDistribution, product_distribution +import Distributions: insupport, pdf import FunctionWrappers: FunctionWrapper +import Random: rand, rand! import StaticArrays: SVector, @SVector ## Exports diff --git a/core/model.jl b/core/model.jl index ef32b376a21b8addc241cab52d2ac0af2c7124f4..92e556af61878fd042d87f6141e6d4c4672a94c5 100644 --- a/core/model.jl +++ b/core/model.jl @@ -1,7 +1,4 @@ -import Random: rand, rand! -import Distributions: insupport, pdf - function _resize_trajectory!(values::Vector{Vector{Int}}, times::Vector{Float64}, transitions::Vector{Transition}, size::Int) for i = eachindex(values) resize!((values[i]), size) end @@ -66,7 +63,7 @@ function generate_code_simulation(model_name::Symbol, f!::Symbol, isabsorbing::S end return Trajectory(m, values, times, transitions) end - # Alloc of vectors where we stock n+1 values + # Alloc of vectors where we store n+1 values vec_x = zeros(Int, getfield(m, :dim_state)) l_t = Float64[0.0] l_tr = Transition[nothing] @@ -74,10 +71,12 @@ function generate_code_simulation(model_name::Symbol, f!::Symbol, isabsorbing::S for i = 2:estim_min_states $(f!)(vec_x, l_t, l_tr, xn, tn, p_sim) tn = l_t[1] - if tn > time_bound || vec_x == xn - isabsorbing = (vec_x == xn) + isabsorbing = vec_x == xn + #isabsorbing = $(isabsorbing)(p_sim,xn) + if isabsorbing || tn > time_bound break end + # n+1 values are now n values n += 1 copyto!(xn, vec_x) MarkovProcesses._update_values!(full_values, times, transitions, xn, tn, l_tr[1], i) @@ -101,15 +100,13 @@ function generate_code_simulation(model_name::Symbol, f!::Symbol, isabsorbing::S i += 1 $(f!)(vec_x, l_t, l_tr, xn, tn, p_sim) tn = l_t[1] - if tn > time_bound - i -= 1 - break - end - if vec_x == xn - isabsorbing = true + isabsorbing = vec_x == xn + #isabsorbing = $(isabsorbing)(p_sim,xn) + if isabsorbing || tn > time_bound i -= 1 break end + # n+1 values are now n values copyto!(xn, vec_x) MarkovProcesses._update_values!(full_values, times, transitions, xn, tn, l_tr[1], estim_min_states+size_tmp+i) @@ -189,19 +186,19 @@ function generate_code_synchronized_simulation(model_name::Symbol, lha_name::Sym # First we fill the allocated array for i = 2:estim_min_states $(f!)(vec_x, l_t, l_tr, xn, tn, p_sim) - if l_t[1] > time_bound || vec_x == xn - tn = l_t[1] - isabsorbing = (vec_x == xn) + tn = l_t[1] + isabsorbing = vec_x == xn + if isabsorbing || tn > time_bound break end - n += 1 next_state!(A, ptr_loc_state, values_state, ptr_time_state, vec_x, l_t[1], l_tr[1], xn, p_sim, edge_candidates; verbose = verbose) + # n+1 values are now n values + n += 1 copyto!(xn, vec_x) - tn = l_t[1] tr_n = l_tr[1] MarkovProcesses._update_values!(full_values, times, transitions, xn, tn, tr_n, i) isacceptedLHA = isaccepted(ptr_loc_state[1], A) - if isabsorbing || isacceptedLHA + if isacceptedLHA break end end @@ -229,24 +226,20 @@ function generate_code_synchronized_simulation(model_name::Symbol, lha_name::Sym while i < buffer_size i += 1 $(f!)(vec_x, l_t, l_tr, xn, tn, p_sim) - if l_t[1] > time_bound - tn = l_t[1] - i -= 1 - break - end - if vec_x == xn - isabsorbing = true + tn = l_t[1] + isabsorbing = vec_x == xn + if isabsorbing || tn > time_bound i -= 1 break end next_state!(A, ptr_loc_state, values_state, ptr_time_state, vec_x, l_t[1], l_tr[1], xn, p_sim, edge_candidates; verbose = verbose) + # n+1 values are now n values copyto!(xn, vec_x) - tn = l_t[1] tr_n = l_tr[1] MarkovProcesses._update_values!(full_values, times, transitions, xn, tn, tr_n, estim_min_states+size_tmp+i) isacceptedLHA = isaccepted(ptr_loc_state[1], A) - if isabsorbing || isacceptedLHA + if isacceptedLHA break end end @@ -259,7 +252,7 @@ function generate_code_synchronized_simulation(model_name::Symbol, lha_name::Sym end values = full_values[getfield(m, :_g_idx)] if isbounded(m) && !isaccepted(ptr_loc_state[1], A) - # Add last value: the convention is the last transition is nothing, + # Add last value: the convention is that if the last transition is nothing, # the trajectory is bounded MarkovProcesses._finish_bounded_trajectory!(values, times, transitions, time_bound) end @@ -272,7 +265,9 @@ function generate_code_synchronized_simulation(model_name::Symbol, lha_name::Sym return SynchronizedTrajectory(S, product, values, times, transitions) end - function volatile_simulate(m::$(model_name), A::$(lha_name), p_sim::AbstractVector{Float64}, verbose::Bool) + function volatile_simulate(m::$(model_name), A::$(lha_name), p_sim::AbstractVector{Float64}, + epsilon::Float64, verbose::Bool) + if $(Meta.quot(lha_name)) == :ABCEuclideanDistanceAutomaton A.ϵ = epsilon end x0 = getfield(m, :x0) t0 = getfield(m, :t0) time_bound = getfield(m, :time_bound) @@ -309,12 +304,13 @@ function generate_code_synchronized_simulation(model_name::Symbol, lha_name::Sym tn = l_t[1] break end - if vec_x == xn - isabsorbing = true + isabsorbing = vec_x == xn + if isabsorbing break end next_state!(A, ptr_loc_state, values_state, ptr_time_state, vec_x, l_t[1], l_tr[1], xn, p_sim, edge_candidates; verbose = verbose) + # n+1 values are now n values copyto!(xn, vec_x) tn = l_t[1] tr_n = l_tr[1] @@ -340,14 +336,14 @@ in order to improve performance. It returns the last state of the simulation `S::StateLHA` not a trajectory `σ::SynchronizedTrajectory`. """ function volatile_simulate(product::SynchronizedModel; - p::Union{Nothing,AbstractVector{Float64}} = nothing, verbose::Bool = false) + p::Union{Nothing,AbstractVector{Float64}} = nothing, epsilon::Float64 = 0.0, verbose::Bool = false) m = product.m A = product.automaton p_sim = getfield(m, :p) if p != nothing p_sim = p end - S = volatile_simulate(m, A, p_sim, verbose) + S = volatile_simulate(m, A, p_sim, epsilon, verbose) return S end @@ -363,7 +359,6 @@ function simulate(product::SynchronizedModel; return σ end - """ `simulate(pm::ParametricModel, p_prior::AbstractVector{Float64}) @@ -387,14 +382,10 @@ It returns `S::StateLHA`, not a trajectory. function volatile_simulate(pm::ParametricModel, p_prior::AbstractVector{Float64}; epsilon::Union{Nothing,Float64} = nothing) @assert typeof(pm.m) <: SynchronizedModel - # ABC related automata - if @isdefined(EuclideanDistanceABCAutomaton) && typeof(pm.m.automaton) <: EuclideanDistanceABCAutomaton - nothing - end full_p = copy(get_proba_model(pm).p) full_p[pm._param_idx] = p_prior - return volatile_simulate(pm.m; p = full_p) + return volatile_simulate(pm.m; p = full_p, epsilon = epsilon) end """ `distribute_mean_value_lha(sm::SynchronizedModel, sym_var::Symbol, nbr_stim::Int)` diff --git a/tests/automata/euclidean_distance.jl b/tests/automata/euclidean_distance.jl index 0365a54fabf0f0b015d763e908cd118fed84f104..3bb1dc3a4deb84f8229d6d1d7769e1be3994d868 100644 --- a/tests/automata/euclidean_distance.jl +++ b/tests/automata/euclidean_distance.jl @@ -5,6 +5,7 @@ import Distributions: Uniform load_automaton("euclidean_distance_automaton") load_automaton("euclidean_distance_automaton_2") +load_automaton("abc_euclidean_distance_automaton") load_model("SIR") load_model("ER") observe_all!(SIR) @@ -46,7 +47,18 @@ for i = 1:nbr_sim else test2 = true end - global test_all = test_all && test && test2 + sync_SIR = SIR * create_abc_euclidean_distance_automaton(SIR, tml_obs, y_obs, :I) + σ = simulate(sync_SIR) + test3 = euclidean_distance(σ, :I, tml_obs, y_obs) == σ.state_lha_end[:d] + if !test3 + @show test3, euclidean_distance(σ, :I, tml_obs, y_obs), σ.state_lha_end[:d] + global err = σ + global tml = tml_obs + global y = y_obs + global sync_model = sync_SIR + break + end + global test_all = test_all && test && test2 && test3 end end @@ -82,7 +94,18 @@ for i = 1:nbr_sim else test2 = true end - global test_all = test_all && test && test2 + sync_ER = ER * create_abc_euclidean_distance_automaton(ER, tml_obs, y_obs, :P) + σ = simulate(sync_ER) + test3 = euclidean_distance(σ, :P, tml_obs, y_obs) == σ.state_lha_end[:d] + if !test3 + @show test3, euclidean_distance(σ, :P, tml_obs, y_obs), σ.state_lha_end[:d] + global err = σ + global tml = tml_obs + global y = y_obs + global sync_model = sync_ER + break + end + global test_all = test_all && test && test2 && test3 end end diff --git a/tests/automata/euclidean_distance_single.jl b/tests/automata/euclidean_distance_single.jl index b6f6891614f7fb933d62e09ff5a345462f627e41..dae30d13a66552c755f6798266f58eb683c67450 100644 --- a/tests/automata/euclidean_distance_single.jl +++ b/tests/automata/euclidean_distance_single.jl @@ -16,12 +16,21 @@ sync_SIR = SIR * aut1 σ = simulate(sync_SIR) test = euclidean_distance(σ, :I, tml_obs, y_obs) == σ.state_lha_end[:d] +if !test + @show euclidean_distance(σ, :I, tml_obs, y_obs), σ.state_lha_end[:d] + @show σ +end + if MAKE_SECOND_AUTOMATON_TESTS load_automaton("euclidean_distance_automaton_2") aut2 = create_euclidean_distance_automaton_2(SIR, tml_obs, y_obs, :I) sync_SIR = SIR * aut2 σ = simulate(sync_SIR) test2 = euclidean_distance(σ, :I, tml_obs, y_obs) == σ.state_lha_end[:d] + if !test2 + @show euclidean_distance(σ, :I, tml_obs, y_obs), σ.state_lha_end[:d] + @show σ + end else test2 = true end diff --git a/tests/automaton_abc/abc_euclidean_distance_automaton.jl b/tests/automaton_abc/abc_euclidean_distance_automaton.jl new file mode 100644 index 0000000000000000000000000000000000000000..01a1c03004cedc77a3b6adff83a8c70c23cefd5a --- /dev/null +++ b/tests/automaton_abc/abc_euclidean_distance_automaton.jl @@ -0,0 +1,31 @@ + +using Plots +using MarkovProcesses +import LinearAlgebra: dot + +# ER model +load_model("ER") +set_param!(ER, [:k1, :k2, :k3], [0.2, 40.0, 1.0]) + +# Observations +timeline = 0.0:0.1:2.0 +observations = [vectorize(simulate(ER), :P, timeline)] +epsilon = 0.1 * sqrt(dot(observations[1], observations[1])) + +load_automaton("abc_euclidean_distance_automaton") +aut = create_abc_euclidean_distance_automaton(ER, timeline, observations[1], :P) +sync_ER = ER * aut +pm_sync_ER = ParametricModel(sync_ER, (:k1, Uniform(0.0, 100.0)), (:k2, Uniform(0.0, 100.0))) + +path_res = "abc_eucl_aut/" +res_abc = automaton_abc(pm_sync_ER; nbr_particles = 100, tolerance = epsilon) + +#= +samples_abc_post = res_abc.mat_p_end +samples_weights = res_abc.weights +histogram2d(samples_abc_post[1,:], samples_abc_post[2,:], weights = samples_weights, normalize = :density) +savefig(path_res * "/histogram.svg") +=# + +return true + diff --git a/tests/run_abc_smc.jl b/tests/run_abc_smc.jl index 8848094b1460ebd2bdd87d1afc5bc88f447130d7..a11f01d46c53b0206d509b75b40c434f5ae636e8 100644 --- a/tests/run_abc_smc.jl +++ b/tests/run_abc_smc.jl @@ -4,6 +4,7 @@ using Test @testset "ABC SMC and automaton-ABC tests" begin @test include("automaton_abc/R1.jl") @test include("automaton_abc/distributed_R1.jl") + @test include("automaton_abc/abc_euclidean_distance.jl") #@test test_distributed_R1 end