Commit 961bab06 authored by Bentriou Mahmoud's avatar Bentriou Mahmoud
Browse files

I've implemented another way of dispatching check_constraints and

update_state functions but performance gets worst.
With bench/pkg/abstract_arrays.jl i've highligthed each time a function
is in a collection performance gets worst.
parent 0ffb9f9d
This diff is collapsed.
using Profile
using StaticArrays
using BenchmarkTools
println("Cost of tests:")
f_test(a::Int) = a == 2
@btime for i = 1:20
f_test(3)
end
# First container type
abstract type SuperType end
for i = 1:10
include_string(Main, "struct Type$i <: SuperType v::Symbol end")
include_string(Main, "f(item::Type$i, a::Int) = a == $(i)")
end
vec_types = SuperType[]
for i = 1:20
rand_number_type = rand(1:10)
@eval item = $(Symbol("Type$(rand_number_type)"))(:test)
push!(vec_types, item)
end
println("First super type")
function read(vec_types::Vector{SuperType}, a::Int)
for item in vec_types
f(item, a)
end
end
@btime read(vec_types, 3)
# Second container type
struct AnotherSuperType{T<:Function}
v::Symbol
f::T
end
vec_others_types = AnotherSuperType[]
for i = 1:20
rand_number_type = rand(1:10)
include_string(Main, "f_other_$(i)(a::Int) = a == $(i)")
sym_func = Symbol("f_other_$i")
@eval push!(vec_others_types, AnotherSuperType(:test, $(sym_func)))
end
println("Second super type")
function read(vec_others_types::Vector{AnotherSuperType}, a::Int)
for item in vec_others_types
item.f(a)
end
end
@btime read(vec_others_types, 3)
# With vectors
println("Transitions first:")
vec_tr = Union{Nothing,Symbol}[]
vec_func = Function[]
for i = 1:20
rand_number_type = rand(1:10)
include_string(Main, "f_vec_$(i)(a::Int) = a == $(i)")
sym_func = Symbol("f_vec_$i")
push!(vec_tr, :test)
@eval push!(vec_func, $(sym_func))
end
function read(vec_tr::Vector{Union{Nothing,Symbol}}, vec_func::Vector{Function}, a::Int)
for i = eachindex(vec_tr)
my_func = vec_func[i]
my_func(a)
end
end
@btime read(vec_tr, vec_func, 3)
println("Transitions second:")
vec_tr = Union{Nothing,Symbol}[]
vec_func = Function[]
for i = 1:20
rand_number_type = rand(1:10)
name_func = Symbol("f_vec2_$(i)")
str_func = "$(name_func)(a::Int) = a == $(rand_number_type)"
include_string(Main, str_func)
push!(vec_tr, :test)
@eval push!(vec_func, $(name_func))
end
@btime read(vec_tr, vec_func, 3)
# How to store and read efficiently abstract types ?
d = Dict{Symbol,Dict{Symbol,Vector{Float64}}}()
for i = 1:10
sym_a = Symbol("a$i")
sym_b = Symbol("a$i")
d[sym_a] = Dict{Symbol,Vector{Float64}}()
d[sym_a][sym_b] = zeros(4)
end
function f(dict::Dict)
for key in keys(dict)
for key2 in keys(dict[key])
for i = eachindex(dict[key][key2])
dict[key][key2][i]
end
end
end
end
@btime f(d)
using StaticArrays
using BenchmarkTools
using MarkovProcesses
using Profile
load_model("ER")
observe_all!(ER)
set_param!(ER, [:k1, :k2], [0.2, 40.0])
set_time_bound!(ER, 0.9)
load_automaton("automaton_G_and_F")
x1, x2, t1, t2 = 50.0, 100.0, 0.0, 0.8
x3, x4, t3, t4 = 30.0, 100.0, 0.8, 0.9
A_G_F = create_automaton_G_and_F(ER, x1, x2, t1, t2, :E,
x3, x4, t3, t4, :P)
sync_ER = ER * A_G_F
S = init_state(A_G_F, ER.x0, ER.t0)
time = 0.1
values = S.values
x, p = ER.x0, ER.p
for loc_from in keys(A_G_F.map_edges)
for loc_to in keys(A_G_F.map_edges[loc_from])
println("$loc_from => $loc_to")
for i = eachindex(A_G_F.map_edges[loc_from][loc_to])
println("edge $i:")
global global_edge = A_G_F.map_edges[loc_from][loc_to][i]
#@btime check_constraints_AutomatonGandF(global_edge, time, $(copy(values)), x, p)
#b = @benchmark check_constraints_AutomatonGandF(global_edge, time, $(copy(values)), x, p)
#@show mean(b).time, mean(b).memory
end
end
end
function my_find_edge_candidates!(edge_candidates::Vector{EdgeAutomatonGandF},
edges_from_current_loc::Dict{Location,Vector{EdgeAutomatonGandF}},
Λ::Dict{Location,Function},
S_time::Float64, S_values::Vector{Float64},
x::Vector{Int}, p::Vector{Float64},
only_asynchronous::Bool)
nbr_candidates = 0
for target_loc in keys(edges_from_current_loc)
if !Λ[target_loc](x) continue end
for i = eachindex(edges_from_current_loc[target_loc])
edge = edges_from_current_loc[target_loc][i]
test_cc = check_constraints_AutomatonGandF(edge, S_time, S_values, x, p)
if test_cc
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 svector_find_edge_candidates!(edge_candidates::Vector{EdgeAutomatonGandF},
edges_from_current_loc::Dict,
Λ::Dict{Location,Function},
S_time::Float64, S_values::Vector{Float64},
x::Vector{Int}, p::Vector{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]
test_cc = check_constraints_AutomatonGandF(edge, S_time, S_values, x, p)
if test_cc
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
edge_candidates = Vector{EdgeAutomatonGandF}(undef, 2)
edges_from_current_loc = getfield(A_G_F, :map_edges)[:l1G]
Λ = A_G_F.Λ
function transform_dict_tuple(dict::Dict{Symbol,Dict{Symbol,Vector{EdgeAutomatonGandF}}})
new_dict = Dict()
for from_loc in keys(dict)
for to_loc in keys(dict[from_loc])
new_dict[from_loc] = (to_loc, dict[from_loc][to_loc])
end
end
return new_dict
end
function transform_dict_static(dict::Dict{Symbol,Dict{Symbol,Vector{EdgeAutomatonGandF}}})
new_dict = Dict{Symbol,Dict{Symbol,SVector}}()
for from_loc in keys(dict)
new_dict[from_loc] = Dict{Symbol,SVector}()
for to_loc in keys(dict[from_loc])
d = dict[from_loc][to_loc]
new_dict[from_loc][to_loc] = SVector{length(d)}(d)
end
end
return new_dict
end
new_map_edges = transform_dict_tuple(A_G_F.map_edges)
new_edges_from_current_loc = new_map_edges[:l1G]
vec_edges = new_map_edges[:l1G][2]
#concrete_vec_edges = Vector{typeof(vec_edges[1])}(vec_edges)
svector_map_edges = transform_dict_static(A_G_F.map_edges)
svector_edges_from_current_loc = svector_map_edges[:l1G]
static_vec_edges = SVector{length(vec_edges)}(vec_edges)
tuple_edges = Tuple(vec_edges)
println("Test of the current implementation")
@btime begin
for target_loc in keys(edges_from_current_loc)
for edge in edges_from_current_loc[target_loc]
test_cc = check_constraints_AutomatonGandF(edge, time, values, x, p)
end
end
end
println("Test with pairs")
@btime begin
for pair_target_loc_edges in edges_from_current_loc
for edge in pair_target_loc_edges.second
test_cc = check_constraints_AutomatonGandF(edge, time, values, x, p)
end
end
end
println("New map")
@btime begin
for target_loc_edges in new_edges_from_current_loc
for edge in new_edges_from_current_loc[2]
test_cc = check_constraints_AutomatonGandF(edge, time, values, x, p)
end
end
end
println("Read of a edge vector 1 (collection iteration)")
@btime begin
for edge in vec_edges
test_cc = check_constraints_AutomatonGandF(edge, time, values, x, p)
end
end
println("Read of a edge vector 2 (eachindex)")
@btime begin
for i = eachindex(vec_edges)
test_cc = check_constraints_AutomatonGandF(vec_edges[i], time, values, x, p)
end
end
println("Read of a edge vector 3 (steprange)")
@btime begin
for i = 1:length(vec_edges)
test_cc = check_constraints_AutomatonGandF(vec_edges[i], time, values, x, p)
end
end
println("Read of a edge vector 4 (steprange 2)")
nb_edges = length(vec_edges)
@btime begin
for i = 1:nb_edges
test_cc = check_constraints_AutomatonGandF(vec_edges[i], time, values, x, p)
end
end
println("Read of an edge vector 5 (static vectors)")
@btime begin
for i = eachindex(static_vec_edges)
test_cc = check_constraints_AutomatonGandF(static_vec_edges[i], time, values, x, p)
end
end
println("Read of an edge vector 6 (tuples)")
@show typeof(tuple_edges)
@btime begin
for i = eachindex(tuple_edges)
test_cc = check_constraints_AutomatonGandF(tuple_edges[i], time, values, x, p)
end
end
#=
println("Read of an edge vector 7 (concrete type)")
@show typeof(vec_edges)
@show typeof(concrete_vec_edges)
@btime begin
for edge in concrete_vec_edges
#test_cc = check_constraints_AutomatonGandF(edge, time, values, x, p)
end
end
=#
println("Read of an edge vector 8 (concrete type v2)")
@show vec_edges
concrete_2_vec_edges = Vector{Union{Nothing,Vector{Symbol}}}([e.transitions for e in vec_edges])
@btime begin
for i = eachindex(concrete_2_vec_edges)
concrete_2_vec_edges[i]
#test_cc = check_constraints_AutomatonGandF(edge, time, values, x, p)
end
end
b_find = @benchmark _find_edge_candidates!(edge_candidates, edges_from_current_loc, Λ, time, values, x, p, false)
@show mean(b_find).time, mean(b_find).memory
b_svector_find = @benchmark svector_find_edge_candidates!(edge_candidates, svector_edges_from_current_loc, Λ, time, values, x, p, false)
@show mean(b_svector_find).time, mean(b_svector_find).memory
Profile.clear_malloc_data()
b_myfind = @benchmark my_find_edge_candidates!(edge_candidates, edges_from_current_loc, Λ, time, values, x, p, false)
@show mean(b_myfind).time, mean(b_myfind).memory
#=
The problem is that in each step of these benchmarks, an abstract type is involved
=#
......@@ -18,7 +18,7 @@ export Distribution, Product, Uniform, Normal
# Common types and constructors
export Observations, AbstractTrajectory, Trajectory, SynchronizedTrajectory
export Model, ContinuousTimeModel, SynchronizedModel, ParametricModel
export VariableModel, ParameterModel, Transition
export VariableModel, ParameterModel, Transition, TransitionSet
export LHA, StateLHA, Edge, Location, VariableAutomaton
# Trajectory related methods
......
......@@ -11,6 +11,7 @@ abstract type Edge end
const VariableModel = Symbol
const ParameterModel = Symbol
const Transition = Union{Symbol,Nothing}
const TransitionSet = Union{Vector{Symbol},Nothing}
const Location = Symbol
const VariableAutomaton = Symbol
......@@ -55,6 +56,9 @@ function generate_code_lha_type_def(lha_name::Symbol, edge_type::Symbol)
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{$(edge_type)}}}
map_edges_transitions::Dict{Location, Dict{Location,Vector{TransitionSet}}}
map_edges_check_constraints::Dict{Location, Dict{Location,Vector{Function}}}
map_edges_update_state::Dict{Location, Dict{Location,Vector{Function}}}
constants::Dict{Symbol,Float64}
map_var_model_idx::Dict{VariableModel,Int} # of dim d (of a model)
end
......
......@@ -90,8 +90,234 @@ function init_state(A::LHA, x0::Vector{Int}, t0::Float64)
return S0
end
function generate_code_next_state(lha_name::Symbol, edge_type::Symbol,
check_constraints::Symbol, update_state!::Symbol)
function generate_code_next_state_with_dicts_lha(lha_name::Symbol, edge_type::Symbol)
return quote
# A push! method implementend by myself because of preallocation of edge_candidates
function _push_edge!(edge_id_candidates::Vector{Int}, target_loc_candidates::Vector{Symbol},
edge_id::Int, target_loc::Symbol, nbr_candidates::Int)
if nbr_candidates < length(edge_id_candidates)
edge_id_candidates[nbr_candidates+1] = edge_id
target_loc_candidates[nbr_candidates+1] = target_loc
else
push!(edge_id_candidates, edge_id)
push!(target_loc_candidates, target_loc)
end
end
function _find_edge_candidates!(edge_id_candidates::Vector{Int}, target_loc_candidates::Vector{Symbol},
dict_transitions_from_current_loc::Dict{Location,Vector{TransitionSet}},
dict_check_constraints_from_current_loc::Dict{Location,Vector{Function}},
Λ::Dict{Location,Function},
S_time::Float64, S_values::Vector{Float64},
x::Vector{Int}, p::Vector{Float64},
only_asynchronous::Bool)
nbr_candidates = 0
for target_loc in keys(dict_transitions_from_current_loc)
if !Λ[target_loc](x) continue end
for i = eachindex(dict_transitions_from_current_loc[target_loc])
check_constraints_edge = dict_check_constraints_from_current_loc[target_loc][i]
if check_constraints_edge(S_time, S_values, x, p)
transitions = dict_transitions_from_current_loc[target_loc][i]
if transitions == nothing
_push_edge!(edge_id_candidates, target_loc_candidates, i, target_loc, nbr_candidates)
nbr_candidates += 1
return nbr_candidates
else
if !only_asynchronous
_push_edge!(edge_id_candidates, target_loc_candidates, i, target_loc, nbr_candidates)
nbr_candidates += 1
end
end
end
end
end
return nbr_candidates
end
function _get_edge_index(edge_id_candidates::Vector{Int}, target_loc_candidates::Vector{Symbol}, nbr_candidates::Int,
dict_transitions_from_current_loc::Dict{Location,Vector{TransitionSet}},
detected_event::Bool, tr_nplus1::Transition)
ind_edge = 0
bool_event = detected_event
for i = 1:nbr_candidates
target_loc = target_loc_candidates[i]
edge_id = edge_id_candidates[i]
transitions = dict_transitions_from_current_loc[target_loc][edge_id]
# Asynchronous edge detection: we fire it
if transitions == nothing
return (i, detected_event)
end
# Synchronous detection
if !detected_event && tr_nplus1 != nothing
if (transitions[1] == :ALL) || (tr_nplus1 in transitions)
ind_edge = i
bool_event = true
end
end
end
return (ind_edge, bool_event)
end
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,
xn::Vector{Int}, p::Vector{Float64},
edge_id_candidates::Vector{Int}, target_loc_candidates::Vector{Symbol}; 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 = A.map_edges
map_edges_transitions = A.map_edges_transitions
map_edges_check_constraints = A.map_edges_check_constraints
map_edges_update_state = A.map_edges_update_state
if verbose
println("##### Begin next_state!")
@show xnplus1, tnplus1, tr_nplus1
end
# First, we check the asynchronous transitions
while true
turns += 1
if verbose @show turns end
#edge_candidates = empty!(edge_candidates)
dict_transitions_from_current_loc = map_edges_transitions[ptr_loc_state[1]]
dict_check_constraints_from_current_loc = map_edges_check_constraints[ptr_loc_state[1]]
# Save all edges that satisfies transition predicate (asynchronous ones)
nbr_candidates = _find_edge_candidates!(edge_id_candidates, target_loc_candidates,
dict_transitions_from_current_loc, dict_check_constraints_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_id_candidates, target_loc_candidates, nbr_candidates,
dict_transitions_from_current_loc,
detected_event, nothing)
# Update the state with the chosen one (if it exists)
# Should be xn here
#first_round = false
if ind_edge > 0
edge_target_loc = target_loc_candidates[ind_edge]
edge_id = edge_id_candidates[ind_edge]
firing_update_state! = map_edges_update_state[ptr_loc_state[1]][edge_target_loc][edge_id]
ptr_loc_state[1] = firing_update_state!(ptr_time_state[1], values_state, xn, p)
else
if verbose
println("No edge fired:")
@show ind_edge, detected_event, nbr_candidates
end
break
end
if verbose
println("Edge fired:")
@show edge_id_candidates, target_loc_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
if verbose @show turns end
edges_from_current_loc = map_edges[ptr_loc_state[1]]
dict_transitions_from_current_loc = map_edges_transitions[ptr_loc_state[1]]
dict_check_constraints_from_current_loc = map_edges_check_constraints[ptr_loc_state[1]]
# Save all edges that satisfies transition predicate (synchronous ones)
nbr_candidates = _find_edge_candidates!(edge_id_candidates, target_loc_candidates,
dict_transitions_from_current_loc, dict_check_constraints_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_id_candidates, target_loc_candidates, nbr_candidates,
dict_transitions_from_current_loc,
detected_event, tr_nplus1)
# Update the state with the chosen one (if it exists)
if ind_edge > 0
edge_target_loc = target_loc_candidates[ind_edge]
edge_id = edge_id_candidates[ind_edge]
firing_update_state! = map_edges_update_state[ptr_loc_state[1]][edge_target_loc][edge_id]
ptr_loc_state[1] = firing_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 edge_id_candidates, target_loc_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 edge_id_candidates, target_loc_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
end
end
############################################################################################################
function generate_code_next_state(lha_name::Symbol, edge_type::Symbol)
return quote
# A push! method implementend by myself because of preallocation of edge_candidates
......@@ -113,8 +339,8 @@ function generate_code_next_state(lha_name::Symbol, edge_type::Symbol,
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 getfield(edge, :transitions) == nothing
if edge.check_constraints(S_time, S_values, x, p)
if edge.transitions == nothing