Commit de9fbd7e authored by Bentriou Mahmoud's avatar Bentriou Mahmoud
Browse files

Add of a new automaton related to ABC.

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
parent e4859870
...@@ -8,7 +8,7 @@ function _init_abc_automaton!(mat_p_old::Matrix{Float64}, vec_dist::Vector{Float ...@@ -8,7 +8,7 @@ function _init_abc_automaton!(mat_p_old::Matrix{Float64}, vec_dist::Vector{Float
draw!(vec_p, pm) draw!(vec_p, pm)
mat_p_old[:,i] = vec_p mat_p_old[:,i] = vec_p
if l_obs == nothing 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] vec_dist[i] = S[sym_var_aut]
else else
l_sim = [simulate(pm, vec_p) for i = 1:length(l_obs)] 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}, ...@@ -101,7 +101,7 @@ function _update_param!(mat_p::Matrix{Float64}, vec_dist::Vector{Float64},
continue continue
end end
if l_obs == nothing 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] dist_sim = S[sym_var_aut]
else else
l_sim = [simulate(pm, vec_p_prime) for i = 1:length(l_obs)] l_sim = [simulate(pm, vec_p_prime) for i = 1:length(l_obs)]
......
...@@ -125,6 +125,7 @@ function _abc_smc(pm::ParametricModel, nbr_particles::Int, tolerance::Float64, a ...@@ -125,6 +125,7 @@ function _abc_smc(pm::ParametricModel, nbr_particles::Int, tolerance::Float64, a
l_nbr_sim = zeros(Int, nbr_particles) l_nbr_sim = zeros(Int, nbr_particles)
while (epsilon > last_epsilon) && (current_time - begin_time <= duration_time) && (nbr_tot_sim <= bound_sim) while (epsilon > last_epsilon) && (current_time - begin_time <= duration_time) && (nbr_tot_sim <= bound_sim)
t += 1 t += 1
@show pm.m.automaton.ϵ, epsilon
begin_time_ite = time() begin_time_ite = time()
@info "Step $t" @info "Step $t"
# Set new epsilon # Set new epsilon
......
@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
...@@ -10,7 +10,9 @@ import Distributed: @everywhere, @distributed ...@@ -10,7 +10,9 @@ import Distributed: @everywhere, @distributed
import Distributions: Product, Uniform, Normal import Distributions: Product, Uniform, Normal
import Distributions: Distribution, Univariate, Continuous, UnivariateDistribution, import Distributions: Distribution, Univariate, Continuous, UnivariateDistribution,
MultivariateDistribution, product_distribution MultivariateDistribution, product_distribution
import Distributions: insupport, pdf
import FunctionWrappers: FunctionWrapper import FunctionWrappers: FunctionWrapper
import Random: rand, rand!
import StaticArrays: SVector, @SVector import StaticArrays: SVector, @SVector
## Exports ## Exports
......
import Random: rand, rand!
import Distributions: insupport, pdf
function _resize_trajectory!(values::Vector{Vector{Int}}, times::Vector{Float64}, function _resize_trajectory!(values::Vector{Vector{Int}}, times::Vector{Float64},
transitions::Vector{Transition}, size::Int) transitions::Vector{Transition}, size::Int)
for i = eachindex(values) resize!((values[i]), size) end for i = eachindex(values) resize!((values[i]), size) end
...@@ -66,7 +63,7 @@ function generate_code_simulation(model_name::Symbol, f!::Symbol, isabsorbing::S ...@@ -66,7 +63,7 @@ function generate_code_simulation(model_name::Symbol, f!::Symbol, isabsorbing::S
end end
return Trajectory(m, values, times, transitions) return Trajectory(m, values, times, transitions)
end 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)) vec_x = zeros(Int, getfield(m, :dim_state))
l_t = Float64[0.0] l_t = Float64[0.0]
l_tr = Transition[nothing] l_tr = Transition[nothing]
...@@ -74,10 +71,12 @@ function generate_code_simulation(model_name::Symbol, f!::Symbol, isabsorbing::S ...@@ -74,10 +71,12 @@ function generate_code_simulation(model_name::Symbol, f!::Symbol, isabsorbing::S
for i = 2:estim_min_states for i = 2:estim_min_states
$(f!)(vec_x, l_t, l_tr, xn, tn, p_sim) $(f!)(vec_x, l_t, l_tr, xn, tn, p_sim)
tn = l_t[1] 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 break
end end
# n+1 values are now n values
n += 1 n += 1
copyto!(xn, vec_x) copyto!(xn, vec_x)
MarkovProcesses._update_values!(full_values, times, transitions, xn, tn, l_tr[1], i) 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 ...@@ -101,15 +100,13 @@ function generate_code_simulation(model_name::Symbol, f!::Symbol, isabsorbing::S
i += 1 i += 1
$(f!)(vec_x, l_t, l_tr, xn, tn, p_sim) $(f!)(vec_x, l_t, l_tr, xn, tn, p_sim)
tn = l_t[1] tn = l_t[1]
if tn > time_bound isabsorbing = vec_x == xn
i -= 1 #isabsorbing = $(isabsorbing)(p_sim,xn)
break if isabsorbing || tn > time_bound
end
if vec_x == xn
isabsorbing = true
i -= 1 i -= 1
break break
end end
# n+1 values are now n values
copyto!(xn, vec_x) copyto!(xn, vec_x)
MarkovProcesses._update_values!(full_values, times, transitions, MarkovProcesses._update_values!(full_values, times, transitions,
xn, tn, l_tr[1], estim_min_states+size_tmp+i) 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 ...@@ -189,19 +186,19 @@ function generate_code_synchronized_simulation(model_name::Symbol, lha_name::Sym
# First we fill the allocated array # First we fill the allocated array
for i = 2:estim_min_states for i = 2:estim_min_states
$(f!)(vec_x, l_t, l_tr, xn, tn, p_sim) $(f!)(vec_x, l_t, l_tr, xn, tn, p_sim)
if l_t[1] > time_bound || vec_x == xn tn = l_t[1]
tn = l_t[1] isabsorbing = vec_x == xn
isabsorbing = (vec_x == xn) if isabsorbing || tn > time_bound
break break
end 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) 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) copyto!(xn, vec_x)
tn = l_t[1]
tr_n = l_tr[1] tr_n = l_tr[1]
MarkovProcesses._update_values!(full_values, times, transitions, xn, tn, tr_n, i) MarkovProcesses._update_values!(full_values, times, transitions, xn, tn, tr_n, i)
isacceptedLHA = isaccepted(ptr_loc_state[1], A) isacceptedLHA = isaccepted(ptr_loc_state[1], A)
if isabsorbing || isacceptedLHA if isacceptedLHA
break break
end end
end end
...@@ -229,24 +226,20 @@ function generate_code_synchronized_simulation(model_name::Symbol, lha_name::Sym ...@@ -229,24 +226,20 @@ function generate_code_synchronized_simulation(model_name::Symbol, lha_name::Sym
while i < buffer_size while i < buffer_size
i += 1 i += 1
$(f!)(vec_x, l_t, l_tr, xn, tn, p_sim) $(f!)(vec_x, l_t, l_tr, xn, tn, p_sim)
if l_t[1] > time_bound tn = l_t[1]
tn = l_t[1] isabsorbing = vec_x == xn
i -= 1 if isabsorbing || tn > time_bound
break
end
if vec_x == xn
isabsorbing = true
i -= 1 i -= 1
break break
end 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) 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) copyto!(xn, vec_x)
tn = l_t[1]
tr_n = l_tr[1] tr_n = l_tr[1]
MarkovProcesses._update_values!(full_values, times, transitions, MarkovProcesses._update_values!(full_values, times, transitions,
xn, tn, tr_n, estim_min_states+size_tmp+i) xn, tn, tr_n, estim_min_states+size_tmp+i)
isacceptedLHA = isaccepted(ptr_loc_state[1], A) isacceptedLHA = isaccepted(ptr_loc_state[1], A)
if isabsorbing || isacceptedLHA if isacceptedLHA
break break
end end
end end
...@@ -259,7 +252,7 @@ function generate_code_synchronized_simulation(model_name::Symbol, lha_name::Sym ...@@ -259,7 +252,7 @@ function generate_code_synchronized_simulation(model_name::Symbol, lha_name::Sym
end end
values = full_values[getfield(m, :_g_idx)] values = full_values[getfield(m, :_g_idx)]
if isbounded(m) && !isaccepted(ptr_loc_state[1], A) 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 # the trajectory is bounded
MarkovProcesses._finish_bounded_trajectory!(values, times, transitions, time_bound) MarkovProcesses._finish_bounded_trajectory!(values, times, transitions, time_bound)
end end
...@@ -272,7 +265,9 @@ function generate_code_synchronized_simulation(model_name::Symbol, lha_name::Sym ...@@ -272,7 +265,9 @@ function generate_code_synchronized_simulation(model_name::Symbol, lha_name::Sym
return SynchronizedTrajectory(S, product, values, times, transitions) return SynchronizedTrajectory(S, product, values, times, transitions)
end 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) x0 = getfield(m, :x0)
t0 = getfield(m, :t0)