diff --git a/automata/euclidean_distance_automaton.jl b/automata/euclidean_distance_automaton.jl index 22d633a427c5feca938599e4708b657ae151b30d..f4c09aaa033e13ee63dba034dd136d57ec257255 100644 --- a/automata/euclidean_distance_automaton.jl +++ b/automata/euclidean_distance_automaton.jl @@ -5,13 +5,21 @@ function create_euclidean_distance_automaton(m::ContinuousTimeModel, timeline::A @assert length(timeline) == length(observations) "Timeline and observations vectors don't have the same length" nbr_observations = length(observations) + # Creation of the automaton types + lha_name = :EuclideanDistanceAutomaton + edge_type = :EdgeEuclideanDistanceAutomaton + check_constraints = Symbol("check_constraints_$(lha_name)") + update_state! = Symbol("update_state_$(lha_name)!") + @everywhere @eval abstract type $(edge_type) <: Edge end + @everywhere @eval $(MarkovProcesses.generate_code_lha_type_def(lha_name, edge_type)) + # Locations locations = [:l0, :l1, :l2] ## Invariant predicates - true_inv_predicate(x::Vector{Int}) = true - Λ_F = Dict(:l0 => true_inv_predicate, :l1 => true_inv_predicate, - :l2 => true_inv_predicate) + @everywhere true_inv_predicate(x::Vector{Int}) = true + Λ_F = Dict{Symbol,Function}(: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] @@ -25,90 +33,81 @@ function create_euclidean_distance_automaton(m::ContinuousTimeModel, timeline::A :l2 => vector_flow) ## Edges - map_edges = Dict{Location, Dict{Location, Vector{Edge}}}() - for loc in locations - map_edges[loc] = Dict{Location, Vector{Edge}}() - end - - idx_obs_var = getfield(m, :map_var_idx)[sym_obs] - idx_var_t = map_var_automaton_idx[:t] - idx_var_n = map_var_automaton_idx[:n] - idx_var_d = map_var_automaton_idx[:d] - idx_var_idx = map_var_automaton_idx[:idx] - - nbr_rand = rand(1:1000) - basename_func = "$(replace(m.name, ' '=>'_'))_$(nbr_rand)" - basename_func = replace(basename_func, '-'=>'_') - func_name(type_func::Symbol, from_loc::Location, to_loc::Location, edge_number::Int) = - Symbol("$(type_func)_eucl_dist_aut_$(basename_func)_$(from_loc)$(to_loc)_$(edge_number)$(type_func == :us ? "!" : "")") - meta_elementary_functions = quote + idx_obs_var = m.map_var_idx[sym_obs] + to_idx(var::Symbol) = map_var_automaton_idx[var] + + id = MarkovProcesses.newid() + model_name = Symbol(typeof(m)) + basename_func = "$(model_name)_$(id)" + edge_name(from_loc::Location, to_loc::Location, edge_number::Int) = + Symbol("Edge_$(lha_name)_$(basename_func)_$(from_loc)$(to_loc)_$(edge_number)") + + ## check_constraints & update_state! + meta_elementary_func = quote # l0 loc # l0 => l1 - @everywhere $(func_name(:cc, :l0, :l1, 1))(S_time::Float64, S_values::Vector{Float64}, x::Vector{Int}, p::Vector{Float64}) = true - @everywhere $(func_name(:us, :l0, :l1, 1))(S_time::Float64, S_values::Vector{Float64}, x::Vector{Int}, p::Vector{Float64}) = - (setindex!(S_values, x[$(idx_obs_var)], $(idx_var_n)); - setindex!(S_values, 0.0, $(idx_var_d)); - setindex!(S_values, 1.0, $(idx_var_idx)); + struct $(edge_name(:l0, :l1, 1)) <: $(edge_type) transitions::Union{Nothing,Vector{Symbol}} end + $(check_constraints)(edge::$(edge_name(:l0, :l1, 1)), S_time::Float64, S_values::Vector{Float64}, x::Vector{Int}, p::Vector{Float64}) = true + $(update_state!)(edge::$(edge_name(:l0, :l1, 1)), S_time::Float64, S_values::Vector{Float64}, x::Vector{Int}, p::Vector{Float64}) = + (setindex!(S_values, x[$(idx_obs_var)], $(to_idx(:n))); + setindex!(S_values, 0.0, $(to_idx(:d))); + setindex!(S_values, 1.0, $(to_idx(:idx))); :l1) # l1 loc # l1 => l1 # Defined below - @everywhere $(func_name(:cc, :l1, :l1, 1))(S_time::Float64, S_values::Vector{Float64}, x::Vector{Int}, p::Vector{Float64}) = + struct $(edge_name(:l1, :l1, 1)) <: $(edge_type) transitions::Union{Nothing,Vector{Symbol}} end + $(check_constraints)(edge::$(edge_name(:l1, :l1, 1)), S_time::Float64, S_values::Vector{Float64}, x::Vector{Int}, p::Vector{Float64}) = (tml = $(Tuple(timeline)); - tml_idx = tml[convert(Int, S_values[$(idx_var_idx)])]; - S_values[$(idx_var_t)] >= tml_idx) - @everywhere $(func_name(:us, :l1, :l1, 1))(S_time::Float64, S_values::Vector{Float64}, x::Vector{Int}, p::Vector{Float64}) = + tml_idx = tml[convert(Int, S_values[$(to_idx(:idx))])]; + S_values[$(to_idx(:t))] >= tml_idx) + $(update_state!)(edge::$(edge_name(:l1, :l1, 1)), S_time::Float64, S_values::Vector{Float64}, x::Vector{Int}, p::Vector{Float64}) = (y_obs = $(Tuple(observations)); - y_obs_idx = y_obs[convert(Int, S_values[$(idx_var_idx)])]; - setindex!(S_values, S_values[$(idx_var_d)] + (S_values[$(idx_var_n)] - y_obs_idx)^2, - $(idx_var_d)); - setindex!(S_values, S_values[$(idx_var_idx)] + 1.0, $(idx_var_idx)); + y_obs_idx = y_obs[convert(Int, S_values[$(to_idx(:idx))])]; + setindex!(S_values, S_values[$(to_idx(:d))] + (S_values[$(to_idx(:n))] - y_obs_idx)^2, + $(to_idx(:d))); + setindex!(S_values, S_values[$(to_idx(:idx))] + 1.0, $(to_idx(:idx))); :l1) - @everywhere $(func_name(:cc, :l1, :l1, 2))(S_time::Float64, S_values::Vector{Float64}, x::Vector{Int}, p::Vector{Float64}) = true - @everywhere $(func_name(:us, :l1, :l1, 2))(S_time::Float64, S_values::Vector{Float64}, x::Vector{Int}, p::Vector{Float64}) = - (setindex!(S_values, x[$(idx_obs_var)], $(idx_var_n)); + struct $(edge_name(:l1, :l1, 2)) <: $(edge_type) transitions::Union{Nothing,Vector{Symbol}} end + $(check_constraints)(edge::$(edge_name(:l1, :l1, 2)), S_time::Float64, S_values::Vector{Float64}, x::Vector{Int}, p::Vector{Float64}) = true + $(update_state!)(edge::$(edge_name(:l1, :l1, 2)), S_time::Float64, S_values::Vector{Float64}, x::Vector{Int}, p::Vector{Float64}) = + (setindex!(S_values, x[$(idx_obs_var)], $(to_idx(:n))); :l1) # l1 => l2 - @everywhere $(func_name(:cc, :l1, :l2, 1))(S_time::Float64, S_values::Vector{Float64}, x::Vector{Int}, p::Vector{Float64}) = - S_values[$(idx_var_idx)] >= ($nbr_observations + 1) - @everywhere $(func_name(:us, :l1, :l2, 1))(S_time::Float64, S_values::Vector{Float64}, x::Vector{Int}, p::Vector{Float64}) = - (setindex!(S_values, sqrt(S_values[$(idx_var_d)]), $(idx_var_d)); + struct $(edge_name(:l1, :l2, 1)) <: $(edge_type) transitions::Union{Nothing,Vector{Symbol}} end + $(check_constraints)(edge::$(edge_name(:l1, :l2, 1)), S_time::Float64, S_values::Vector{Float64}, x::Vector{Int}, p::Vector{Float64}) = + S_values[$(to_idx(:idx))] >= ($nbr_observations + 1) + $(update_state!)(edge::$(edge_name(:l1, :l2, 1)), S_time::Float64, S_values::Vector{Float64}, x::Vector{Int}, p::Vector{Float64}) = + (setindex!(S_values, sqrt(S_values[$(to_idx(:d))]), $(to_idx(:d))); :l2) end - eval(meta_elementary_functions) - - # l0 loc - # l0 => l1 - edge1 = Edge(nothing, getfield(Main, func_name(:cc, :l0, :l1, 1)), getfield(Main, func_name(:us, :l0, :l1, 1))) - map_edges[:l0][:l1] = [edge1] - - # l1 loc - # l1 => l1 - #= - edge1 = Edge([:ALL], getfield(Main, func_name(:cc, :l1, :l1, 1)), getfield(Main, func_name(:us, :l1, :l2, 1))) - map_edges[:l1][:l1] = [edge1] - for i = 1:nbr_observations - meta_edge_i = quote - @everywhere $(func_name(:cc, :l1, :l1, 1+i))(S_time::Float64, S_values::Vector{Float64}, x::Vector{Int}, p::Vector{Float64}) = - S[:t] >= $(timeline[i]) - @everywhere $(func_name(:us, :l1, :l1, 1+i))(S_time::Float64, S_values::Vector{Float64}, x::Vector{Int}, p::Vector{Float64}) = - (setindex!(S_values, S[:d] + (S[:n] - $(observations[i]))^2, $(idx_var_d)); - setindex!(S_values, S[:idx] + 1.0, $(idx_var_idx))) - end - eval(meta_edge_i) - push!(map_edges[:l1][:l1], Edge(nothing, getfield(Main, func_name(:cc, :l1, :l1, 1+i)), getfield(Main, func_name(:us, :l1, :l1, 1+i)))) - end - =# - edge1 = Edge(nothing, getfield(Main, func_name(:cc, :l1, :l1, 1)), getfield(Main, func_name(:us, :l1, :l1, 1))) - edge2 = Edge([:ALL], getfield(Main, func_name(:cc, :l1, :l1, 2)), getfield(Main, func_name(:us, :l1, :l1, 2))) - map_edges[:l1][:l1] = [edge1, edge2] + @everywhere eval($(meta_elementary_func)) + + @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 = getfield(Main, $(Meta.quot(edge_name(:l0, :l1, 1))))(nothing) + map_edges[:l0][:l1] = [edge1] - # l1 => l2 - edge1 = Edge(nothing, getfield(Main, func_name(:cc, :l1, :l2, 1)), getfield(Main, func_name(:us, :l1, :l2, 1))) - map_edges[:l1][:l2] = [edge1] + # l1 loc + # l1 => l1 + edge1 = getfield(Main, $(Meta.quot(edge_name(:l1, :l1, 1))))(nothing) + edge2 = getfield(Main, $(Meta.quot(edge_name(:l1, :l1, 2))))([:ALL]) + map_edges[:l1][:l1] = [edge1, edge2] + + # l1 => l2 + edge1 = getfield(Main, $(Meta.quot(edge_name(:l1, :l2, 1))))(nothing) + map_edges[:l1][:l2] = [edge1] + end ## Constants constants = Dict{Symbol,Float64}(:nbr_obs => nbr_observations) @@ -117,10 +116,15 @@ function create_euclidean_distance_automaton(m::ContinuousTimeModel, timeline::A constants[Symbol("y_$(convert(Float64, i))")] = observations[i] end - A = LHA("Euclidean distance", m.transitions, locations, Λ_F, locations_init, locations_final, - map_var_automaton_idx, flow, map_edges, constants, m.map_var_idx) + # Updating next_state! + @everywhere @eval $(MarkovProcesses.generate_code_next_state(lha_name, edge_type, check_constraints, update_state!)) + @everywhere @eval $(MarkovProcesses.generate_code_synchronized_simulation(lha_name, edge_type, m.f!, m.isabsorbing)) + + @eval begin + A = $(lha_name)($(m.transitions), $(locations), $(Λ_F), $(locations_init), $(locations_final), + $(map_var_automaton_idx), $(flow), $(map_edges), $(constants), $(m.map_var_idx)) + end + return A end -export create_euclidean_distance_automaton - diff --git a/core/lha.jl b/core/lha.jl index 54f86ca0f6b35b6256b9df4630650c1ef4ac412a..acf19da07fe833d81062124f372d7706c08e3222 100644 --- a/core/lha.jl +++ b/core/lha.jl @@ -3,7 +3,7 @@ length_var(A::LHA) = length(getfield(A, :map_var_automaton_idx)) get_value(S::StateLHA, x::Vector{Int}, var::VariableModel) = x[getfield(getfield(S, :A), :map_var_model_idx)[var]] get_value(A::LHA, x::Vector{Int}, var::VariableModel) = x[getfield(A, :map_var_model_idx)[var]] -copy(S::StateLHA) = StateLHA(getfield(S, :A), getfield(S, :loc), getfield(S, :values), getfield(S, :time)) +copy(S::StateLHA) = StateLHA(getfield(S, :A), getfield(S, :loc), copy(getfield(S, :values)), getfield(S, :time)) # Not overring getproperty, setproperty to avoid a conversion Symbol => String for the dict key # From the variable automaton var symbol this function get the index in S.values @@ -18,7 +18,7 @@ setindex!(S::StateLHA, val::Int, var::VariableAutomaton) = S[var] = convert(Floa setindex!(S::StateLHA, val::Bool, var::VariableAutomaton) = S[var] = convert(Float64, val) function Base.show(io::IO, A::LHA) - print(io, "$(A.name) automaton (LHA)\n") + print(io, "$(Symbol(typeof(A))) automaton (LHA)\n") print(io, "- initial locations : $(join(A.locations_init,','))\n") print(io, "- final locations : $(join(A.locations_final,','))\n") print(io, "- labeling prop Λ :\n") @@ -60,10 +60,6 @@ end function Base.show(io::IO, E::Edge) print(io, "($(typeof(E)): ") print(io, (E.transitions == nothing) ? "Asynchronous #)" : ("Synchronized with " * join(E.transitions,',') * ")")) - #=print(io, Symbol(E.check_constraints)) - print(io, ", ") - print(io, Symbol(E.update_state!)) - print(io, ")")=# end function Base.copyto!(Sdest::StateLHA, Ssrc::StateLHA) @@ -79,6 +75,7 @@ end # of one of the LHA fields isaccepted(S::StateLHA) = (getfield(S, :loc) in getfield(getfield(S, :A), :locations_final)) +isaccepted(loc::Symbol, A::LHA) = loc in A.locations_final # Methods for synchronize / read the trajectory function init_state(A::LHA, x0::Vector{Int}, t0::Float64) @@ -92,19 +89,21 @@ function init_state(A::LHA, x0::Vector{Int}, t0::Float64) return S0 end -# A push! method implementend by myself because of preallocation of edge_candidates -function _push_edge!(edge_candidates::Vector{Edge}, edge::Edge, nbr_candidates::Int) - if nbr_candidates < 2 - @inbounds edge_candidates[nbr_candidates+1] = edge - else - push!(edge_candidates, edge) - end -end - -function generate_next_state_code() +function generate_code_next_state(lha_name::Symbol, edge_type::Symbol, + check_constraints::Symbol, update_state!::Symbol) + return quote - function _find_edge_candidates!(edge_candidates::Vector{Edge}, - edges_from_current_loc::Dict{Location,Vector{Edge}}, + # A push! method implementend by myself because of preallocation of edge_candidates + function _push_edge!(edge_candidates::Vector{<:$(edge_type)}, edge::$(edge_type), nbr_candidates::Int) + if nbr_candidates < length(edge_candidates) + @inbounds edge_candidates[nbr_candidates+1] = edge + else + push!(edge_candidates, edge) + end + end + + function _find_edge_candidates!(edge_candidates::Vector{$(edge_type)}, + edges_from_current_loc::Dict{Location,Vector{$(edge_type)}}, Λ::Dict{Location,Function}, S_time::Float64, S_values::Vector{Float64}, x::Vector{Int}, p::Vector{Float64}, @@ -113,14 +112,14 @@ function generate_next_state_code() 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 check_constraints(edge, S_time, S_values, x, p) + if $(check_constraints)(edge, S_time, S_values, x, p) if getfield(edge, :transitions) == nothing - MarkovProcesses._push_edge!(edge_candidates, edge, nbr_candidates) + _push_edge!(edge_candidates, edge, nbr_candidates) nbr_candidates += 1 return nbr_candidates else if !only_asynchronous - MarkovProcesses._push_edge!(edge_candidates, edge, nbr_candidates) + _push_edge!(edge_candidates, edge, nbr_candidates) nbr_candidates += 1 end end @@ -130,7 +129,7 @@ function generate_next_state_code() return nbr_candidates end - function _get_edge_index(edge_candidates::Vector{Edge}, nbr_candidates::Int, + function _get_edge_index(edge_candidates::Vector{$(edge_type)}, nbr_candidates::Int, detected_event::Bool, tr_nplus1::Transition) ind_edge = 0 bool_event = detected_event @@ -152,16 +151,14 @@ function generate_next_state_code() return (ind_edge, bool_event) end - function next_state!(Snplus1::StateLHA, A::LHA, + function next_state!(A::$(lha_name), + ptr_loc_state::Vector{Symbol}, values_state::Vector{Float64}, ptr_time_state::Vector{Float64}, xnplus1::Vector{Int}, tnplus1::Float64, tr_nplus1::Transition, - Sn::StateLHA, xn::Vector{Int}, p::Vector{Float64}, - edge_candidates::Vector{Edge}; verbose::Bool = false) + xn::Vector{Int}, p::Vector{Float64}, + edge_candidates::Vector{$(edge_type)}; 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 - current_values = getfield(Snplus1, :values) - current_time = getfield(Snplus1, :time) - current_loc = getfield(Snplus1, :loc) Λ = getfield(A, :Λ) flow = getfield(A, :flow) map_edges = getfield(A, :map_edges) @@ -176,10 +173,10 @@ function generate_next_state_code() while true turns += 1 #edge_candidates = empty!(edge_candidates) - edges_from_current_loc = map_edges[current_loc] + 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, Λ, - current_time, current_values, xn, p, true) + 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) @@ -188,7 +185,7 @@ function generate_next_state_code() #first_round = false if ind_edge > 0 firing_edge = edge_candidates[ind_edge] - current_loc = getfield(Main, :update_state!)(firing_edge, current_time, current_values, xn, p) + ptr_loc_state[1] = $(update_state!)(firing_edge, ptr_time_state[1], values_state, xn, p) else if verbose println("No edge fired") end break @@ -197,9 +194,9 @@ function generate_next_state_code() @show turns @show edge_candidates @show ind_edge, detected_event, nbr_candidates - @show current_loc - @show current_time - @show current_values + @show ptr_loc_state[1] + @show ptr_time_state[1] + @show values_state if turns == 500 @warn "We've reached 500 turns" end @@ -212,9 +209,6 @@ function generate_next_state_code() @show length(edge_candidates) @show tnplus1, tr_nplus1, xnplus1 @show edge_candidates - for edge in edge_candidates - @show getfield(edge, :check_constraints)(time_S, values_S, xn, p) - end error("Unpredicted behavior automaton") end =# @@ -223,31 +217,31 @@ function generate_next_state_code() println("Time flies with the flow...") end # Now time flies according to the flow - for i in eachindex(current_values) - @inbounds coeff_deriv = flow[current_loc][i] + for i in eachindex(values_state) + @inbounds coeff_deriv = flow[ptr_loc_state[1]][i] if coeff_deriv > 0 - @inbounds current_values[i] += coeff_deriv*(tnplus1 - current_time) + @inbounds values_state[i] += coeff_deriv*(tnplus1 - ptr_time_state[1]) end end - current_time = tnplus1 + ptr_time_state[1] = tnplus1 if verbose - @show current_loc - @show current_time - @show current_values + @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[current_loc] + 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, Λ, - current_time, current_values, xnplus1, p, false) + 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] - current_loc = getfield(Main, :update_state!)(firing_edge, current_time, current_values, xnplus1, p) + ptr_loc_state[1] = $(update_state!)(firing_edge, ptr_time_state[1], values_state, xnplus1, p) end if ind_edge == 0 || detected_event if verbose @@ -257,9 +251,9 @@ function generate_next_state_code() @show edge_candidates @show ind_edge, detected_event, nbr_candidates @show detected_event - @show current_loc - @show current_time - @show current_values + @show ptr_loc_state[1] + @show ptr_time_state[1] + @show values_state else println("No edge fired") end @@ -271,9 +265,9 @@ function generate_next_state_code() @show edge_candidates @show ind_edge, detected_event, nbr_candidates @show detected_event - @show current_loc - @show current_time - @show current_values + @show ptr_loc_state[1] + @show ptr_time_state[1] + @show values_state if turns == 500 @warn "We've reached 500 turns" end @@ -286,17 +280,15 @@ function generate_next_state_code() @show length(edge_candidates) @show tnplus1, tr_nplus1, xnplus1 @show edge_candidates - for edge in edge_candidates - @show getfield(edge, :check_constraints)(time_S, values_S, x, p) - end error("Unpredicted behavior automaton") end =# end - setfield!(Snplus1, :loc, current_loc) - setfield!(Snplus1, :time, current_time) + #= + setfield!(Snplus1, :loc, ptr_loc_state[1]) + setfield!(Snplus1, :time, ptr_time_state[1]) + =# if verbose - @show Snplus1 println("##### End next_state!") end end diff --git a/core/model.jl b/core/model.jl index 5d2b0220bf670799207f611c50960d4811ff5399..e56f9d50d764698ea72df844eb75ff03ff681e0e 100644 --- a/core/model.jl +++ b/core/model.jl @@ -132,28 +132,41 @@ function generate_code_simulation(model_name::Symbol, f!::Symbol, isabsorbing::S end end -function generate_synchronized_simulation_code() +function simulate(product::SynchronizedModel; + p::Union{Nothing,AbstractVector{Float64}} = nothing, verbose::Bool = false) + m = getfield(product, :m) + A = getfield(product, :automaton) + p_sim = getfield(m, :p) + if p != nothing + p_sim = p + end + return simulate(m, A, product, p_sim, verbose) +end + +function volatile_simulate(product::SynchronizedModel; + p::Union{Nothing,AbstractVector{Float64}} = nothing, verbose::Bool = false) + m = product.m + A = product.automaton + p_sim = getfield(m, :p) + if p != nothing + p_sim = p + end + return volatile_simulate(m, A, p_sim, verbose) +end + +function generate_code_synchronized_simulation(lha_name::Symbol, edge_type::Symbol, f!::Symbol, isabsorbing::Symbol) return quote - import MarkovProcesses: simulate + import MarkovProcesses: simulate, volatile_simulate - function simulate(product::SynchronizedModel; p::Union{Nothing,AbstractVector{Float64}} = nothing, - verbose::Bool = false) - m = getfield(product, :m) - A = getfield(product, :automaton) - p_sim = getfield(m, :p) - func_f! = getfield(m, :f!) - func_isabsorbing = getfield(m, :isabsorbing) - if p != nothing - p_sim = p - end + function simulate(m::ContinuousTimeModel, A::$(lha_name), product::SynchronizedModel, + p_sim::AbstractVector{Float64}, verbose::Bool) x0 = getfield(m, :x0) t0 = getfield(m, :t0) time_bound = getfield(m, :time_bound) - S0 = init_state(A, x0, t0) buffer_size = getfield(m, :buffer_size) estim_min_states = getfield(m, :estim_min_states) - edge_candidates = Vector{Edge}(undef, 2) + edge_candidates = Vector{$(edge_type)}(undef, 2) # First alloc full_values = Vector{Vector{Int}}(undef, getfield(m, :dim_state)) for i = eachindex(full_values) full_values[i] = zeros(Int, estim_min_states) end @@ -163,19 +176,22 @@ function generate_synchronized_simulation_code() for i = eachindex(full_values) full_values[i][1] = x0[i] end times[1] = t0 transitions[1] = nothing + S = init_state(A, x0, t0) + ptr_loc_state = [S.loc] + values_state = S.values + ptr_time_state = [S.time] # Values at time n n = 1 xn = copy(x0) tn = copy(t0) - Sn = copy(S0) - next_state!(Sn, A, x0, t0, nothing, S0, x0, p_sim, edge_candidates; verbose = verbose) - isabsorbing::Bool = func_isabsorbing(p_sim,xn) - isacceptedLHA::Bool = isaccepted(Sn) + next_state!(A, ptr_loc_state, values_state, ptr_time_state, + x0, t0, nothing, x0, p_sim, edge_candidates; verbose = verbose) + isabsorbing::Bool = $(isabsorbing)(p_sim,xn) + isacceptedLHA::Bool = isaccepted(ptr_loc_state[1], A) # Alloc of vectors where we stock n+1 values vec_x = zeros(Int, getfield(m, :dim_state)) l_t = Float64[0.0] l_tr = Transition[nothing] - Snplus1 = deepcopy(Sn) # If x0 is absorbing if isabsorbing || isacceptedLHA MarkovProcesses._resize_trajectory!(full_values, times, transitions, 1) @@ -184,27 +200,28 @@ function generate_synchronized_simulation_code() MarkovProcesses._finish_bounded_trajectory!(values, times, transitions, time_bound) end if isabsorbing && !isacceptedLHA - next_state!(Snplus1, A, xn, time_bound, nothing, Sn, xn, p_sim, edge_candidates; verbose = verbose) - copyto!(Sn, Snplus1) + next_state!(A, ptr_loc_state, values_state, ptr_time_state, + xn, time_bound, nothing, xn, p_sim, edge_candidates; verbose = verbose) end - return SynchronizedTrajectory(Sn, product, values, times, transitions) + setfield!(S, :loc, ptr_loc_state[1]) + setfield!(S, :time, ptr_time_state[1]) + return SynchronizedTrajectory(S, product, values, times, transitions) end # First we fill the allocated array for i = 2:estim_min_states - func_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] isabsorbing = (vec_x == xn) break end n += 1 - next_state!(Snplus1, A, vec_x, l_t[1], l_tr[1], Sn, 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) 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) - copyto!(Sn, Snplus1) - isacceptedLHA = isaccepted(Snplus1) + isacceptedLHA = isaccepted(ptr_loc_state[1], A) if isabsorbing || isacceptedLHA break end @@ -217,10 +234,12 @@ function generate_synchronized_simulation_code() MarkovProcesses._finish_bounded_trajectory!(values, times, transitions, time_bound) end if isabsorbing && !isacceptedLHA - next_state!(Snplus1, A, xn, time_bound, nothing, Sn, xn, p_sim, edge_candidates; verbose = verbose) - copyto!(Sn, Snplus1) + next_state!(A, ptr_loc_state, values_state, ptr_time_state, + xn, time_bound, nothing, xn, p_sim, edge_candidates; verbose = verbose) end - return SynchronizedTrajectory(Sn, product, values, times, transitions) + setfield!(S, :loc, ptr_loc_state[1]) + setfield!(S, :time, ptr_time_state[1]) + return SynchronizedTrajectory(S, product, values, times, transitions) end # Otherwise, buffering system size_tmp = 0 @@ -230,7 +249,7 @@ function generate_synchronized_simulation_code() i = 0 while i < buffer_size i += 1 - func_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] i -= 1 @@ -241,14 +260,13 @@ function generate_synchronized_simulation_code() i -= 1 break end - next_state!(Snplus1, A, vec_x, l_t[1], l_tr[1], Sn, 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) 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) - copyto!(Sn, Snplus1) - isacceptedLHA = isaccepted(Snplus1) + xn, tn, tr_n, estim_min_states+size_tmp+i) + isacceptedLHA = isaccepted(ptr_loc_state[1], A) if isabsorbing || isacceptedLHA break end @@ -261,17 +279,20 @@ function generate_synchronized_simulation_code() n += i end values = full_values[getfield(m, :_g_idx)] - if isbounded(m) && !isaccepted(Sn) + if isbounded(m) && !isaccepted(ptr_loc_state[1], A) # Add last value: the convention is the last transition is nothing, # the trajectory is bounded MarkovProcesses._finish_bounded_trajectory!(values, times, transitions, time_bound) end if isabsorbing && !isacceptedLHA - next_state!(Snplus1, A, xn, time_bound, nothing, Sn, xn, p_sim, edge_candidates; verbose = verbose) - copyto!(Sn, Snplus1) + next_state!(A, ptr_loc_state, values_state, ptr_time_state, + xn, time_bound, nothing, xn, p_sim, edge_candidates; verbose = verbose) end - return SynchronizedTrajectory(Sn, product, values, times, transitions) + setfield!(S, :loc, ptr_loc_state[1]) + setfield!(S, :time, ptr_time_state[1]) + return SynchronizedTrajectory(S, product, values, times, transitions) end + """ `volatile_simulate(sm::SynchronizedModel; p, verbose)` @@ -279,44 +300,39 @@ function generate_synchronized_simulation_code() 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) - m = product.m - A = product.automaton - p_sim = getfield(m, :p) - func_f! = getfield(m, :f!) - func_isabsorbing = getfield(m, :isabsorbing) - if p != nothing - p_sim = p - end + function volatile_simulate(m::ContinuousTimeModel, A::$(lha_name), p_sim::AbstractVector{Float64}, verbose::Bool) x0 = getfield(m, :x0) t0 = getfield(m, :t0) time_bound = getfield(m, :time_bound) - S0 = init_state(A, x0, t0) - edge_candidates = Vector{Edge}(undef, 2) + S = init_state(A, x0, t0) + ptr_loc_state = [S.loc] + values_state = S.values + ptr_time_state = [S.time] + edge_candidates = Vector{$(edge_type)}(undef, 2) # Values at time n n = 1 xn = copy(x0) tn = copy(t0) - Sn = copy(S0) - next_state!(Sn, A, x0, t0, nothing, S0, x0, p_sim, edge_candidates; verbose = verbose) - isabsorbing::Bool = func_isabsorbing(p_sim,xn) - isacceptedLHA::Bool = isaccepted(Sn) + next_state!(A, ptr_loc_state, values_state, ptr_time_state, + x0, t0, nothing, x0, p_sim, edge_candidates; verbose = verbose) + isabsorbing::Bool = $(isabsorbing)(p_sim,xn) + isacceptedLHA::Bool = isaccepted(ptr_loc_state[1], A) # Alloc of vectors where we stock n+1 values vec_x = zeros(Int, getfield(m, :dim_state)) l_t = Float64[0.0] l_tr = Transition[nothing] - Snplus1 = deepcopy(Sn) # If x0 is absorbing if isabsorbing || isacceptedLHA if !isacceptedLHA - next_state!(Snplus1, A, xn, time_bound, nothing, Sn, xn, p_sim, edge_candidates; verbose = verbose) - copyto!(Sn, Snplus1) + next_state!(A, ptr_loc_state, values_state, ptr_time_state, + xn, time_bound, nothing, xn, p_sim, edge_candidates; verbose = verbose) end - return Sn + setfield!(S, :loc, ptr_loc_state[1]) + setfield!(S, :time, ptr_time_state[1]) + return S end while !isabsorbing && tn <= time_bound && !isacceptedLHA - func_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] break @@ -325,19 +341,21 @@ function generate_synchronized_simulation_code() isabsorbing = true break end - next_state!(Snplus1, A, vec_x, l_t[1], l_tr[1], Sn, 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) copyto!(xn, vec_x) tn = l_t[1] tr_n = l_tr[1] - copyto!(Sn, Snplus1) - isacceptedLHA = isaccepted(Snplus1) + isacceptedLHA = isaccepted(ptr_loc_state[1], A) n += 1 end if isabsorbing && !isacceptedLHA - next_state!(Snplus1, A, xn, time_bound, nothing, Sn, xn, p_sim, edge_candidates; verbose = verbose) - copyto!(Sn, Snplus1) + next_state!(A, ptr_loc_state, values_state, ptr_time_state, + xn, time_bound, nothing, xn, p_sim, edge_candidates; verbose = verbose) end - return Sn + setfield!(S, :loc, ptr_loc_state[1]) + setfield!(S, :time, ptr_time_state[1]) + return S end end end @@ -471,7 +489,7 @@ function check_consistency(m::ContinuousTimeModel) @assert length(m.g) <= m.dim_state @assert length(m._g_idx) == length(m.g) @assert m.buffer_size >= 0 - @assert typeof(getfield(Main, m.isabsorbing)(m.p, m.x0)) == Bool + #@assert typeof(getfield(Main, m.isabsorbing)(m.p, m.x0)) == Bool return true end