###################################### DRAW DATA ######################################
# function draw_overview_plot()
#     # ground truth dataset
#     dat = sim_PredatorPrey_event_data(λ0s = [50., 50])
#     # Visualize simulated dataset
#     color_codes = palette(:tab10)
#     color_codes = palette(:seaborn_bright)
#     # color_codes = Gray.(palette(:seaborn_bright))
#     p1 = Plots.plot(
#             0:0.001:1,
#             dat.Λ[:, 1:dat.W]',
#             lw=2,
#             labels=permutedims(["Prey", "Predator"]),
#             size=(500,200),
#             palette = color_codes,
#             labelfontsize=12,
#             legendfontsize=7,
#             xlabel="time",
#             ylabel="intensity",
#             legend=:topleft,
#             margin=2mm,
#             fontfamily="Times"
#         )
#         # hline!([0], ls=:dot,lc=:black,lw=2, label=:none)

#     p3 = Plots.scatter(
#             dat.times[1],
#             fill(1.04,length(dat.times[1])),
#             marker=:+, color=color_codes[1], markerstrokewidth=2, ml=2)
#         Plots.scatter!(
#             dat.times[2],
#             fill(1.02,length(dat.times[2])),
#             marker=:+, color=color_codes[2], markerstrokewidth=2, ml=2)
#         annotate!(-0.07, 1.03, Plots.text("data", "Times", 12))
#         ylims!(1.01,1.05, yaxis=false, yticks=false, legend=:none, xaxis=false, xticks=false, size=(500, 100))

#     p_overview = Plots.plot(
#         p3, p1,
#         layout=Plots.grid(2, 1, heights=[0.15, 0.85]), size=(500,200),
#         fontfamily="Times"
#     )
#     return p_overview
# end

# function draw_simulated_data_for_sir(dat)
#     # Visualize simulated dataset
#     # color_codes = ["#CB3C33", "#4063D8"]
#     color_codes = [Gray(0.0), Gray(0.4), Gray(0.6)]
#     p1 = Plots.plot(dat.ticks, dat.Λ[:, 1:dat.W]', lw=2,
#             labels=permutedims(["S", "I", "R"]),
#             size=(500,200),
#             palette = palette(color_codes),
#             labelfontsize=10,
#             legendfontsize=7,
#             xlabel="time",
#             ylabel="latent ODE state",
#             legend=:right,
#             margin=2mm,
#             fontfamily="Times"
#         )
#         hline!([0], ls=:dot,lc=:black,lw=2, label=:none)
#         title!("Latent SIR dynamics", titlefontsize=12)

#     p2 = Plots.plot(
#             dat.ticks, dat.X[:, 1:dat.W]', lw=2,
#             labels=permutedims(["S", "I", "R"]),
#             size=(500,200),
#             palette = palette(color_codes),
#             labelfontsize=10,
#             legendfontsize=7,
#             xlabel="time",
#             ylabel="logarithm of\\n latent ODE state",
#             legend=:bottomright,
#             margin=5mm,
#             fontfamily="Times"
#         )
#         hline!([0], ls=:dot,lc=:black,lw=2, label=:none)
#         title!("Log-transformed SIR dynamics", titlefontsize=12)

#     p3 = Plots.scatter(dat.times[1], fill(1.06,length(dat.times[1])), marker=:+, color=color_codes[1], markerstrokewidth=2, ml=2)
#         Plots.scatter!(dat.times[2], fill(1.04,length(dat.times[2])), marker=:+, color=color_codes[2], markerstrokewidth=2, ml=2)
#         Plots.scatter!(dat.times[3], fill(1.02,length(dat.times[3])), marker=:+, color=color_codes[3], markerstrokewidth=2, ml=2)
#         ylims!(1.01,1.07, yaxis=false, yticks=false, legend=:none, xaxis=false, xticks=false, size=(500, 100))

#     p_overview = Plots.plot(
#         p1, p3,
#         layout=Plots.grid(2, 1, heights=[0.8, 0.2]), size=(500,250),
#         fontfamily="Times"
#     )
#     return p1, p2, p3, p_overview
# end


# function draw_simulated_data_for_predatorprey(dat)
#     # Visualize simulated dataset
#     color_codes = [Gray(0.6), Gray(0.0)]
#     p1 = Plots.plot(0:0.001:1, dat.Λ[:, 1:dat.W]', lw=2,
#             labels=permutedims(["Prey", "Predator"]),
#             size=(500,200),
#             palette = palette(color_codes),
#             labelfontsize=12,
#             legendfontsize=7,
#             xlabel="time",
#             ylabel="latent ODE state",
#             legend=:topleft,
#             margin=2mm,
#             fontfamily="Times"
#         )
#         hline!([0], ls=:dot,lc=:black,lw=2, label=:none)
#         title!("Latent LVPP dynamics", titlefontsize=12)
#     p2 = Plots.plot(
#             0:0.001:1, dat.X[:, 1:dat.W]', lw=2,
#             labels=permutedims(["Prey", "Predator"]),
#             size=(500,200),
#             palette = palette(color_codes),
#             labelfontsize=10,
#             legendfontsize=7,
#             xlabel="time",
#             ylabel="logarithm of\\n latent ODE state",
#             legend=:topleft,
#             margin=5mm,
#             fontfamily="Times"
#         )
#         hline!([0], ls=:dot,lc=:black,lw=2, label=:none)
#         title!("Log-transformed LVPP dynamics", titlefontsize=12)
#     p3 = Plots.scatter(dat.times[1], fill(1.04,length(dat.times[1])), marker=:+, color=color_codes[1], markerstrokewidth=2, ml=2)
#         Plots.scatter!(dat.times[2], fill(1.02,length(dat.times[2])), marker=:+, color=color_codes[2], markerstrokewidth=2, ml=2)
#         ylims!(1.01,1.05, yaxis=false, yticks=false, legend=:none, xaxis=false, xticks=false, size=(500, 100))
#     p_overview = Plots.plot(
#         p3, p1,
#         layout=Plots.grid(2, 1, heights=[0.15, 0.85]), size=(500,250),
#         fontfamily="Times"
#     )
#     return p1, p2, p3, p_overview
# end


# function draw_simulated_data_for_competition(dat)
#     # Visualize simulated dataset
#     # color_codes = ["#CB3C33", "#4063D8"]
#     color_codes = [Gray(0.0), Gray(0.4), Gray(0.6), Gray(0.8)]
#     p1 = Plots.plot(dat.ticks, dat.Λ[:, 1:dat.W]', lw=2,
#             labels=permutedims(["C1", "C2", "C3", "C4"]),
#             size=(500,200),
#             palette = palette(color_codes),
#             labelfontsize=10,
#             legendfontsize=7,
#             xlabel="time",
#             ylabel="latent ODE state",
#             legend=:right,
#             margin=2mm,
#             fontfamily="Times"
#         )
#         hline!([0], ls=:dot,lc=:black,lw=2, label=:none)
#         title!("Latent LVC dynamics", titlefontsize=12)
#     p2 = Plots.plot(
#             dat.ticks, dat.X[:, 1:dat.W]', lw=2,
#             labels=permutedims(["C1", "C2", "C3", "C4"]),
#             size=(500,200),
#             palette = palette(color_codes),
#             labelfontsize=10,
#             legendfontsize=7,
#             xlabel="time",
#             ylabel="logarithm of\\n latent ODE state",
#             legend=:bottomright,
#             margin=5mm,
#             fontfamily="Times"
#         )
#         hline!([0], ls=:dot,lc=:black,lw=2, label=:none)
#         title!("Log-transformed LVC dynamics", titlefontsize=12)
#     p3 = Plots.plot()
#         for i in 1:length(dat.times)
#             Plots.scatter!(dat.times[i], fill(1+0.02*i,length(dat.times[i])), marker=:+, color=color_codes[i], markerstrokewidth=2, ml=2)
#         end
#         ylims!(1.01,1.09, yaxis=false, yticks=false, legend=:none, xaxis=false, xticks=false, size=(500, 100))
#     p_overview = Plots.plot(
#             p1, p3,
#             layout=Plots.grid(2, 1, heights=[0.8, 0.2]), size=(500,250),
#             fontfamily="Times"
#         )
#     return p1, p2, p3, p_overview
# end

############################ DRAW POSTERIOR SIMULATION RESULTS ############################
# function get_simulated_intensity_statistics_with_posterior_θ(data, result; ode)

#     function get_statics_Λ(Λs, q=[0.125, 0.875])
#         mea = mapslices(mean, Λs, dims=2)
#         ql = mapslices(x -> quantile(x, q[1]), Λs, dims=2)
#         med = mapslices(x -> quantile(x, 0.50), Λs, dims=2)
#         qh = mapslices(x -> quantile(x, q[2]), Λs, dims=2);
#         return mea, ql, med, qh
#     end

#     # get posterior θ
#     if ode == "sir"
#         samples = get_posterior_samples_sir(result)
#     elseif ode == "predatorprey"
#         samples = get_posterior_samples_predatorprey(result)
#     elseif ode == "competition"
#         samples = get_posterior_samples_competition(result)
#     else
#         error("Invalid ode")
#     end

#     x0 = data.x0
#     C = length(x0)

#     # simulate intensity Λ with posterior ODE paraeter θ
#     if ode == "sir"
#         N = length(samples.lgcpgm.x)
#         Λs_lgcpgm = [sim_SIR_event_data(
#                 x0 = x0,
#                 θ = (
#                     a=samples.lgcpgm.θ.a[i],
#                     b=samples.lgcpgm.θ.b[i]),
#             extrapolation_time_length = 0).Λ for i in 1:N];

#         N = length(samples.gpgm_coarse.x)
#         Λs_gpgmc = [sim_SIR_event_data(
#                 x0 = x0,
#                 θ = (
#                     a=samples.gpgm_coarse.θ.a[i],
#                     b=samples.gpgm_coarse.θ.b[i]),
#             extrapolation_time_length = 0).Λ for i in 1:N];

#         N = length(samples.gpgm_fine.x)
#         Λs_gpgmf = [sim_SIR_event_data(
#                 x0 = x0,
#                 θ = (
#                     a=samples.gpgm_fine.θ.a[i],
#                     b=samples.gpgm_fine.θ.b[i]),
#             extrapolation_time_length = 0).Λ for i in 1:N];

#     elseif ode == "predatorprey"
#         N = length(samples.lgcpgm.x)
#         Λs_lgcpgm = [sim_PredatorPrey_event_data(
#                 x0 = x0,
#                 θ = (
#                     a=samples.lgcpgm.θ.a[i],
#                     b=samples.lgcpgm.θ.b[i],
#                     c=samples.lgcpgm.θ.c[i],
#                     d=samples.lgcpgm.θ.d[i]),
#             extrapolation_time_length = 0).Λ for i in 1:N];

#         N = length(samples.gpgm_coarse.x)
#         Λs_gpgmc = [sim_PredatorPrey_event_data(
#                 x0 = x0,
#                 θ = (
#                     a=samples.gpgm_coarse.θ.a[i],
#                     b=samples.gpgm_coarse.θ.b[i],
#                     c=samples.gpgm_coarse.θ.c[i],
#                     d=samples.gpgm_coarse.θ.d[i]),
#             extrapolation_time_length = 0).Λ for i in 1:N];

#         N = length(samples.gpgm_fine.x)
#         Λs_gpgmf = [sim_PredatorPrey_event_data(
#                 x0 = x0,
#                 θ = (
#                     a=samples.gpgm_fine.θ.a[i],
#                     b=samples.gpgm_fine.θ.b[i],
#                     c=samples.gpgm_fine.θ.c[i],
#                     d=samples.gpgm_fine.θ.d[i]),
#             extrapolation_time_length = 0).Λ for i in 1:N];

#     elseif ode == "competition"
#         N = length(samples.lgcpgm.x)
#         Λs_lgcpgm = [sim_Competition_event_data(
#                 x0 = x0,
#                 θ = (
#                     r=samples.lgcpgm.θ.r[i],
#                     η=samples.lgcpgm.θ.η[i],
#                     A=samples.lgcpgm.θ.A[i]),
#             extrapolation_time_length = 0).Λ for i in 1:N];

#         N = length(samples.gpgm_coarse.x)
#         Λs_gpgmc = [sim_Competition_event_data(
#                 x0 = x0,
#                 θ = (
#                     r=samples.gpgm_coarse.θ.r[i],
#                     η=samples.gpgm_coarse.θ.η[i],
#                     A=samples.gpgm_coarse.θ.A[i]),
#             extrapolation_time_length = 0).Λ for i in 1:N];

#         N = length(samples.gpgm_fine.x)
#         Λs_gpgmf = [sim_Competition_event_data(
#                 x0 = x0,
#                 θ = (
#                     r=samples.gpgm_fine.θ.r[i],
#                     η=samples.gpgm_fine.θ.η[i],
#                     A=samples.gpgm_fine.θ.A[i]),
#             extrapolation_time_length = 0).Λ for i in 1:N];

#     else
#         error("Invalid ode")
#     end

#     # get statistics (mean, 25quantile, median, 75quantile) of simulated inteisity
#     stats_Λ_lgcpgm = []
#     stats_Λ_gpgmc = []
#     stats_Λ_gpgmf = []
#     for c in 1:C
#         m, q25, med, q75 = get_statics_Λ(hcat([Λs[c,:] for Λs in Λs_lgcpgm]...))
#         push!(stats_Λ_lgcpgm, (m=m, q25=q25, med=med, q75=q75))

#         m, q25, med, q75 = get_statics_Λ(hcat([Λs[c,:] for Λs in Λs_gpgmc]...))
#         push!(stats_Λ_gpgmc, (m=m, q25=q25, med=med, q75=q75))

#         m, q25, med, q75 = get_statics_Λ(hcat([Λs[c,:] for Λs in Λs_gpgmf]...))
#         push!(stats_Λ_gpgmf, (m=m, q25=q25, med=med, q75=q75))
#     end

#     stats = (lgcpgm=stats_Λ_lgcpgm, gpgmc=stats_Λ_gpgmc, gpgmf=stats_Λ_gpgmf)
#     return stats
# end

# function draw_simulated_intensity(
#     data, stats;
#     method,
#     colors = [
#         palette(:seaborn_bright)[1],
#         palette(:seaborn_bright)[4],
#         palette(:seaborn_bright)[3],
#         palette(:seaborn_bright)[2],
#         palette(:seaborn_bright)[5]],
#     with_mean=false)

#     p = Plots.plot()
#     C = length(data.x0)
#     if method == :lgcpgm
#         _stats = stats.lgcpgm
#     elseif method == :gpgmc
#         _stats = stats.gpgmc
#     elseif method == :gpgmf
#         _stats = stats.gpgmf
#     else
#         error("Invalid method. choose one from {:lgcpgm, :gpgmc, gpgmf}.")
#     end

#     for c in 1:C
#         _mean, q25, med, q75 = _stats[c];
#         if with_mean
#             Plots.plot!(data.ticks, _mean, c=colors[c], legend=:none)
#         end
#         Plots.plot!(data.ticks, med, ribbon=(med-q25, q75-med), c=colors[c], fillalpha=0.3, legend=:none)
#     end
#     for c in 1:C
#         Plots.plot!(data.ticks, data.Λ[c,1:length(data.ticks)], c=colors[c], s=:dash, lw=1)
#     end
#     return p
# end

################################ ODE PARAMETER ESTIMATION ################################
# function run_estimation_experiments_sir(
#     dat;
#     seed=125,
#     n_burnin=10000,
#     n_mcmc=30000,
#     thin=30,
#     n_keep=1000,
#     U=26,
#     W=100,
#     W_corse=25,
#     kernel_params=[log(5), log(0.2)],
#     σ=0.01,
#     γ=0.3,
#     δ=0.1
#     )
#     # for reproducibility
#     Random.seed!(seed)

#     # data
#     times = vcat(dat.times...)
#     classes = vcat(
#         fill(1, length(dat.times[1])),
#         fill(2, length(dat.times[2])),
#         fill(3, length(dat.times[3]))
#     )

#     # LGCPGM
#     lgcpgm = LGCPGradientMatching.SIRCoxProcess(
#         times, classes, gmtype=:loggaussian, λ0 = dat.λ0s,
#         U=U, W=W, σ=σ, γ=γ, δ=δ,
#         gpgradcov_diagapprox=true,
#         kernel_fn=rbf_kernel,
#         kernel_params=kernel_params
#         );

#         lgcpgm_mcp = Dict(:alg => :HMC, :target => [:y, :x, :θ], :L => 5)
#         lgcpgm_mcp[:gradientmatch] = true
#         lgcpgm_mcp[:state_rec_cycle] = thin
#         lgcpgm_mcp[:n_keep_state_rec] = n_keep
#         lgcpgm_mcp[:block] = Dict(
#             :prob => [0.3, 0.4, 0.3],
#             :target => [[:y], [:x], [:θ]],
#             :ϵ => [0.001, 0.001, 0.001],
#             :accept_counter => [0, 0, 0],
#             :reject_counter => [0, 0, 0]
#         )
#         # sampling (bunrin)
#         lgcpgm_mcp[:burnin] = true
#         train!(lgcpgm, n_burnin, lgcpgm_mcp, show_progress=true)
#         # sampling
#         lgcpgm_mcp[:burnin] = false
#         train!(lgcpgm, n_mcmc, lgcpgm_mcp, show_progress=true)

#     # GPGM (coarsely discretized)
#     gpgm_coarse = LGCPGradientMatching.SIRCoxProcess(
#         times, classes, gmtype=:gaussian, λ0 = dat.λ0s,
#         U=U, W=W_corse, γ=γ, δ=δ,
#         gpgradcov_diagapprox=true,
#         kernel_fn=rbf_kernel
#         );

#         # setting
#         gpgm_coarse_mcp = Dict(:alg => :HMC, :target => [:x, :θ], :L => 5)
#         gpgm_coarse_mcp[:gradientmatch] = true
#         gpgm_coarse_mcp[:state_rec_cycle] = thin
#         gpgm_coarse_mcp[:n_keep_state_rec] = n_keep
#         # sampling (bunrin)
#         gpgm_coarse_mcp[:burnin] = true
#         gpgm_coarse_mcp[:block] = Dict(
#             :prob => [0.5, 0.5],
#             :target => [[:γ], [:θ]],
#             :ϵ => [0.001, 0.001],
#             :accept_counter => [0, 0],
#             :reject_counter => [0, 0]
#         )
#         train!(gpgm_coarse, 3000, gpgm_coarse_mcp, show_progress=true)
#         gpgm_coarse.gm.γ = 0.3
#         gpgm_coarse_mcp[:block] = Dict(
#             :prob => [0.5, 0.5],
#             :target => [[:x], [:θ]],
#             :ϵ => [0.001, 0.001],
#             :accept_counter => [0, 0],
#             :reject_counter => [0, 0]
#         )
#         train!(gpgm_coarse, n_burnin, gpgm_coarse_mcp, show_progress=true)
#         # sampling
#         gpgm_coarse_mcp[:burnin] = false
#         train!(gpgm_coarse, n_mcmc, gpgm_coarse_mcp, show_progress=true)

#     # GPGM100 (finely discretized)
#     gpgm_fine = LGCPGradientMatching.SIRCoxProcess(
#         times, classes, gmtype=:gaussian, λ0 = dat.λ0s,
#         U=U, W=W, γ=γ, δ=δ,
#         gpgradcov_diagapprox=true,
#         kernel_fn=rbf_kernel
#         );

#         gpgm_fine_mcp = Dict(:alg => :HMC, :target => [:x, :θ], :L => 5)
#         gpgm_fine_mcp[:gradientmatch] = true
#         gpgm_fine_mcp[:state_rec_cycle] = thin
#         gpgm_fine_mcp[:n_keep_state_rec] = n_keep
#         # sampling (bunrin)
#         gpgm_fine_mcp[:burnin] = true
#         gpgm_fine_mcp[:block] = Dict(
#             :prob => [0.5, 0.5],
#             :target => [[:γ], [:θ]],
#             :ϵ => [0.001, 0.001],
#             :accept_counter => [0, 0],
#             :reject_counter => [0, 0]
#         )
#         train!(gpgm_fine, 3000, gpgm_fine_mcp, show_progress=true)
#         gpgm_fine.gm.γ = 0.3
#         gpgm_fine_mcp[:block] = Dict(
#             :prob => [0.5, 0.5],
#             :target => [[:x], [:θ]],
#             :ϵ => [0.001, 0.001],
#             :accept_counter => [0, 0],
#             :reject_counter => [0, 0]
#         )
#         train!(gpgm_fine, n_burnin, gpgm_fine_mcp, show_progress=true)
#         # sampling
#         gpgm_fine_mcp[:burnin] = false
#         train!(gpgm_fine, n_mcmc, gpgm_fine_mcp, show_progress=true)

#     result = (
#         lgcpgm = (gm=lgcpgm, mcp=lgcpgm_mcp),
#         gpgm_coarse = (gm=gpgm_coarse, mcp=gpgm_coarse_mcp),
#         gpgm_fine = (gm=gpgm_fine, mcp=gpgm_fine_mcp)
#     )
#     return result
# end

# function run_estimation_experiments_predatorprey(
#     dat;
#     seed=125,
#     n_burnin=10000,
#     n_mcmc=30000,
#     thin=30,
#     n_keep=1000,
#     U=26,
#     W=100,
#     W_corse=25,
#     kernel_params=[log(5), log(0.1)],
#     σ=0.01,
#     γ=0.3,
#     δ=0.1
#     )
#     # for reproducibility
#     Random.seed!(seed)

#     # data
#     times = vcat(dat.times...)
#     classes = vcat(fill(1, length(dat.times[1])), fill(2, length(dat.times[2])))

#     # LGCPGM
#     lgcpgm = LGCPGradientMatching.PredatorPreyCoxProcess(
#         times, classes, gmtype=:loggaussian, λ0 = dat.λ0s,
#         U=U, W=W, σ=σ, γ=γ, δ=δ,
#         gpgradcov_diagapprox=true,
#         kernel_fn=rbf_kernel,
#         kernel_params=kernel_params
#         );

#         lgcpgm_mcp = Dict(:alg => :HMC, :target => [:y, :x, :θ], :L => 5)
#         lgcpgm_mcp[:gradientmatch] = true
#         lgcpgm_mcp[:state_rec_cycle] = thin
#         lgcpgm_mcp[:n_keep_state_rec] = n_keep
#         lgcpgm_mcp[:block] = Dict(
#             :prob => [0.3, 0.4, 0.3],
#             :target => [[:y], [:x], [:θ]],
#             :ϵ => [0.001, 0.001, 0.001],
#             :accept_counter => [0, 0, 0],
#             :reject_counter => [0, 0, 0]
#         )
#         # lgcpgm_mcp[:block] = Dict(
#         #     :prob => [0.3, 0.3, 0.3, 0.1],
#         #     :target => [[:y], [:x], [:θ], [:γ]],
#         #     :ϵ => [0.001, 0.001, 0.001, 0.001],
#         #     :accept_counter => [0, 0, 0, 0],
#         #     :reject_counter => [0, 0, 0, 0]
#         # )
#         # sampling (bunrin)
#         lgcpgm_mcp[:burnin] = true
#         train!(lgcpgm, n_burnin, lgcpgm_mcp, show_progress=true)
#         # sampling
#         lgcpgm_mcp[:burnin] = false
#         train!(lgcpgm, n_mcmc, lgcpgm_mcp, show_progress=true)

#     # GPGM (coarsely discretized)
#     gpgm_coarse = LGCPGradientMatching.PredatorPreyModel(
#         times, classes, gmtype=:gaussian, λ0 = dat.λ0s,
#         U=U, W=W_corse, γ=γ, δ=δ,
#         gpgradcov_diagapprox=true,
#         kernel_fn=rbf_kernel
#         );

#         # setting
#         gpgm_coarse_mcp = Dict(:alg => :HMC, :target => [:x, :θ], :L => 5)
#         gpgm_coarse_mcp[:gradientmatch] = true
#         gpgm_coarse_mcp[:state_rec_cycle] = thin
#         gpgm_coarse_mcp[:n_keep_state_rec] = n_keep
#         # sampling (bunrin)
#         gpgm_coarse_mcp[:burnin] = true
#         gpgm_coarse_mcp[:block] = Dict(
#             :prob => [0.5, 0.5],
#             :target => [[:γ], [:θ]],
#             :ϵ => [0.001, 0.001],
#             :accept_counter => [0, 0],
#             :reject_counter => [0, 0]
#         )
#         train!(gpgm_coarse, 3000, gpgm_coarse_mcp, show_progress=true)
#         gpgm_coarse.gm.γ = 0.3
#         gpgm_coarse_mcp[:block] = Dict(
#             :prob => [0.5, 0.5],
#             :target => [[:x], [:θ]],
#             :ϵ => [0.001, 0.001],
#             :accept_counter => [0, 0],
#             :reject_counter => [0, 0]
#         )
#         train!(gpgm_coarse, n_burnin, gpgm_coarse_mcp, show_progress=true)
#         # sampling
#         gpgm_coarse_mcp[:burnin] = false
#         train!(gpgm_coarse, n_mcmc, gpgm_coarse_mcp, show_progress=true)

#     # GPGM100 (finely discretized)
#     gpgm_fine = LGCPGradientMatching.PredatorPreyModel(
#         times, classes, gmtype=:gaussian, λ0 = dat.λ0s,
#         U=U, W=W, γ=γ, δ=δ,
#         gpgradcov_diagapprox=true,
#         kernel_fn=rbf_kernel
#         );

#         gpgm_fine_mcp = Dict(:alg => :HMC, :target => [:x, :θ], :L => 5)
#         gpgm_fine_mcp[:gradientmatch] = true
#         gpgm_fine_mcp[:state_rec_cycle] = thin
#         gpgm_fine_mcp[:n_keep_state_rec] = n_keep
#         # sampling (bunrin)
#         gpgm_fine_mcp[:burnin] = true
#         gpgm_fine_mcp[:block] = Dict(
#             :prob => [0.5, 0.5],
#             :target => [[:γ], [:θ]],
#             :ϵ => [0.001, 0.001],
#             :accept_counter => [0, 0],
#             :reject_counter => [0, 0]
#         )
#         train!(gpgm_fine, 3000, gpgm_fine_mcp, show_progress=true)
#         gpgm_fine.gm.γ = 0.3
#         gpgm_fine_mcp[:block] = Dict(
#             :prob => [0.5, 0.5],
#             :target => [[:x], [:θ]],
#             :ϵ => [0.001, 0.001],
#             :accept_counter => [0, 0],
#             :reject_counter => [0, 0]
#         )
#         train!(gpgm_fine, n_burnin, gpgm_fine_mcp, show_progress=true)
#         # sampling
#         gpgm_fine_mcp[:burnin] = false
#         train!(gpgm_fine, n_mcmc, gpgm_fine_mcp, show_progress=true)

#     result = (
#         lgcpgm = (gm=lgcpgm, mcp=lgcpgm_mcp),
#         gpgm_coarse = (gm=gpgm_coarse, mcp=gpgm_coarse_mcp),
#         gpgm_fine = (gm=gpgm_fine, mcp=gpgm_fine_mcp)
#     )
#     return result
# end

# function run_estimation_experiments_competition(
#     dat;
#     seed=125,
#     n_burnin=10000,
#     n_mcmc=30000,
#     thin=30,
#     n_keep=1000,
#     U=26,
#     W=100,
#     W_corse=25,
#     kernel_params=[log(5), log(0.2)],
#     σ=0.01,
#     γ=0.3,
#     δ=0.1
#     )
#     # for reproducibility
#     Random.seed!(seed)

#     # data
#     times = vcat(dat.times...)
#     C = length(dat.times)
#     classes = vcat([fill(c, length(dat.times[c])) for c in 1:C]...)

#     # LGCPGM
#     lgcpgm = LGCPGradientMatching.CompetitionLGCP(
#         times, classes, gmtype=:loggaussian, λ0 = dat.λ0s,
#         U=U, W=W, σ=σ, γ=γ, δ=δ,
#         gpgradcov_diagapprox=true,
#         kernel_fn=rbf_kernel,
#         kernel_params=kernel_params
#         );

#         lgcpgm_mcp = Dict(:alg => :HMC, :target => [:y, :x, :θ], :L => 5)
#         lgcpgm_mcp[:gradientmatch] = true
#         lgcpgm_mcp[:state_rec_cycle] = thin
#         lgcpgm_mcp[:n_keep_state_rec] = n_keep
#         lgcpgm_mcp[:block] = Dict(
#             :prob => [0.3, 0.4, 0.3],
#             :target => [[:y], [:x], [:θ]],
#             :ϵ => [0.001, 0.001, 0.001],
#             :accept_counter => [0, 0, 0],
#             :reject_counter => [0, 0, 0]
#         )
#         # sampling (bunrin)
#         lgcpgm_mcp[:burnin] = true
#         train!(lgcpgm, n_burnin, lgcpgm_mcp, show_progress=true)
#         # sampling
#         lgcpgm_mcp[:burnin] = false
#         train!(lgcpgm, n_mcmc, lgcpgm_mcp, show_progress=true)

#     # GPGM (coarsely discretized)
#     gpgm_coarse = LGCPGradientMatching.CompetitionLGCP(
#         times, classes, gmtype=:gaussian, λ0 = dat.λ0s,
#         U=U, W=W_corse, γ=γ, δ=δ,
#         gpgradcov_diagapprox=true,
#         kernel_fn=rbf_kernel
#         );

#         # setting
#         gpgm_coarse_mcp = Dict(:alg => :HMC, :target => [:x, :θ], :L => 5)
#         gpgm_coarse_mcp[:gradientmatch] = true
#         gpgm_coarse_mcp[:state_rec_cycle] = thin
#         gpgm_coarse_mcp[:n_keep_state_rec] = n_keep
#         # sampling (bunrin)
#         gpgm_coarse_mcp[:burnin] = true
#         gpgm_coarse_mcp[:block] = Dict(
#             :prob => [0.5, 0.5],
#             :target => [[:γ], [:θ]],
#             :ϵ => [0.001, 0.001],
#             :accept_counter => [0, 0],
#             :reject_counter => [0, 0]
#         )
#         train!(gpgm_coarse, 3000, gpgm_coarse_mcp, show_progress=true)
#         gpgm_coarse.gm.γ = 0.3
#         gpgm_coarse_mcp[:block] = Dict(
#             :prob => [0.5, 0.5],
#             :target => [[:x], [:θ]],
#             :ϵ => [0.001, 0.001],
#             :accept_counter => [0, 0],
#             :reject_counter => [0, 0]
#         )
#         train!(gpgm_coarse, n_burnin, gpgm_coarse_mcp, show_progress=true)
#         # sampling
#         gpgm_coarse_mcp[:burnin] = false
#         train!(gpgm_coarse, n_mcmc, gpgm_coarse_mcp, show_progress=true)

#     # GPGM100 (finely discretized)
#     gpgm_fine = LGCPGradientMatching.CompetitionLGCP(
#         times, classes, gmtype=:gaussian, λ0 = dat.λ0s,
#         U=U, W=W, γ=γ, δ=δ,
#         gpgradcov_diagapprox=true,
#         kernel_fn=rbf_kernel
#         );

#         gpgm_fine_mcp = Dict(:alg => :HMC, :target => [:x, :θ], :L => 5)
#         gpgm_fine_mcp[:gradientmatch] = true
#         gpgm_fine_mcp[:state_rec_cycle] = thin
#         gpgm_fine_mcp[:n_keep_state_rec] = n_keep
#         # sampling (bunrin)
#         gpgm_fine_mcp[:burnin] = true
#         gpgm_fine_mcp[:block] = Dict(
#             :prob => [0.5, 0.5],
#             :target => [[:γ], [:θ]],
#             :ϵ => [0.001, 0.001],
#             :accept_counter => [0, 0],
#             :reject_counter => [0, 0]
#         )
#         train!(gpgm_fine, 3000, gpgm_fine_mcp, show_progress=true)
#         gpgm_fine.gm.γ = 0.3
#         gpgm_fine_mcp[:block] = Dict(
#             :prob => [0.5, 0.5],
#             :target => [[:x], [:θ]],
#             :ϵ => [0.001, 0.001],
#             :accept_counter => [0, 0],
#             :reject_counter => [0, 0]
#         )
#         train!(gpgm_fine, n_burnin, gpgm_fine_mcp, show_progress=true)
#         # sampling
#         gpgm_fine_mcp[:burnin] = false
#         train!(gpgm_fine, n_mcmc, gpgm_fine_mcp, show_progress=true)

#     result = (
#         lgcpgm = (gm=lgcpgm, mcp=lgcpgm_mcp),
#         gpgm_coarse = (gm=gpgm_coarse, mcp=gpgm_coarse_mcp),
#         gpgm_fine = (gm=gpgm_fine, mcp=gpgm_fine_mcp)
#     )
#     return result
# end

# function run_estimation_experiments_infected(
#     dat;
#     seed=125,
#     n_burnin=10000,
#     n_mcmc=30000,
#     thin=30,
#     n_keep=1000,
#     U=26,
#     W=100,
#     W_corse=25,
#     kernel_params=[log(5), log(0.2)],
#     σ=0.01,
#     γ=0.3,
#     δ=0.1
#     )
#     # for reproducibility
#     Random.seed!(seed)

#     # LGCPGM
#     lgcpgm = LGCPGradientMatching.InfectedCoxProcess(
#         dat.times[2],
#         from=0.,
#         to=1.,
#         gmtype=:loggaussian, λ0 = dat.λ0s,
#         U=U, W=W, σ=σ, γ=γ, δ=δ,
#         gpgradcov_diagapprox=true,
#         kernel_fn=rbf_kernel,
#         kernel_params=kernel_params
#         );

#         lgcpgm.gm.n_observations[:,1] = fill(nothing, W)
#         lgcpgm.gm.n_observations[:,3] = fill(nothing, W)
#         # lgcpgm.gm.n_observations[1:5,3] .= 0
#         lgcpgm.gm.n_observations[1,3] = 0

#         lgcpgm_mcp = Dict(:alg => :HMC, :target => [:y, :x, :θ], :L => 5)
#         lgcpgm_mcp[:gradientmatch] = true
#         lgcpgm_mcp[:state_rec_cycle] = thin
#         lgcpgm_mcp[:n_keep_state_rec] = n_keep
#         lgcpgm_mcp[:block] = Dict(
#             :prob => [0.3, 0.4, 0.3],
#             :target => [[:y], [:x], [:θ]],
#             :ϵ => [0.001, 0.001, 0.001],
#             :accept_counter => [0, 0, 0],
#             :reject_counter => [0, 0, 0]
#         )

#         # NOTE: Additional assumption
#         # Set the initial(t=0) state x of (R) component to exp(-10) ≈ 0
#         # and skip sampling for the state to fix it
#         # lgcpgm.gm.gps[3].x[1] = -8
#         # reflect_x!(lgcpgm.gm.gps[3])
#         # reflect_σ!(lgcpgm.gm.gps[3]);
#         # lgcpgm_mcp[:skip_sample_x_ids] = [2*lgcpgm.gm.U + 1]

#         # sampling (bunrin)
#         lgcpgm_mcp[:burnin] = true
#         train!(lgcpgm, n_burnin, lgcpgm_mcp, show_progress=true)
#         # sampling
#         lgcpgm_mcp[:burnin] = false
#         train!(lgcpgm, n_mcmc, lgcpgm_mcp, show_progress=true)

#     result = (
#         lgcpgm = (gm=lgcpgm, mcp=lgcpgm_mcp)
#     )
#     return result
# end

################################ EXTRAPOLATION ################################
# function run_extrapolation_experiments_sir(
#     dat;
#     seed=125,
#     n_burnin=10000,
#     n_mcmc=30000,
#     thin=30,
#     n_keep=1000,
#     U=26,
#     W=100,
#     W_corse=25,
#     kernel_params=[log(5), log(0.2)],
#     σ=0.01,
#     γ=0.3,
#     δ=0.1
#     )
#     # for reproducibility
#     Random.seed!(seed)

#     # data
#     times = vcat(dat.times...)
#     classes = vcat(
#         fill(1, length(dat.times[1])),
#         fill(2, length(dat.times[2])),
#         fill(3, length(dat.times[3]))
#     )

#     # LGCPGM
#     lgcpgm = LGCPGradientMatching.SIRCoxProcess(
#         times, classes, gmtype=:loggaussian, λ0 = dat.λ0s,
#         U=U, W=W, σ=σ, γ=γ, δ=δ,
#         gpgradcov_diagapprox=true,
#         kernel_fn=rbf_kernel,
#         kernel_params=kernel_params,
#         extrapolation_time_length=dat.extrapolation_time_length, #NOTE: setting for extrapolation
#         );

#         lgcpgm_mcp = Dict(:alg => :HMC, :target => [:y, :x, :θ], :L => 5)
#         lgcpgm_mcp[:gradientmatch] = true
#         lgcpgm_mcp[:state_rec_cycle] = thin
#         lgcpgm_mcp[:n_keep_state_rec] = n_keep
#         lgcpgm_mcp[:block] = Dict(
#             :prob => [0.3, 0.4, 0.3],
#             :target => [[:y], [:x], [:θ]],
#             :ϵ => [0.001, 0.001, 0.001],
#             :accept_counter => [0, 0, 0],
#             :reject_counter => [0, 0, 0]
#         )
#         # sampling (bunrin)
#         lgcpgm_mcp[:burnin] = true
#         train!(lgcpgm, n_burnin, lgcpgm_mcp, show_progress=true)
#         # sampling
#         lgcpgm_mcp[:burnin] = false
#         train!(lgcpgm, n_mcmc, lgcpgm_mcp, show_progress=true)

#     # PoissonLikelihoodGP - GaussianProcesses.jl for inference kernel parameters
#     window_points = lgcpgm.gm.gps[1].times_y[1:lgcpgm.gm.W]
#     m_S = Int.(lgcpgm.gm.n_observations[:,1])
#     m_I = Int.(lgcpgm.gm.n_observations[:,2])
#     m_R = Int.(lgcpgm.gm.n_observations[:,3])

#     k = GaussianProcesses.SEIso(log(0.025), log(1)) + GaussianProcesses.Noise(log(0.1))
#     k_priors = [Normal(log(0.1), 1.), Normal(log(1), 1.), Normal(log(0.1), 1.)]
#     plgp1, plgp_samples1 = train_plgp(
#         window_points, m_S, k, k_priors;
#         n_mcmc=n_mcmc, n_burnin=n_burnin, thin=thin, infer_mean=false, infer_kern=true);
#     plgp2, plgp_samples2 = train_plgp(
#         window_points, m_I, k, k_priors;
#         n_mcmc=n_mcmc, n_burnin=n_burnin, thin=thin, infer_mean=false, infer_kern=true);
#     plgp3, plgp_samples3 = train_plgp(
#         window_points, m_R, k, k_priors;
#         n_mcmc=n_mcmc, n_burnin=n_burnin, thin=thin, infer_mean=false, infer_kern=true);
#     plgp = (S=plgp1, I=plgp2, R=plgp3)
#     plgp_samples = (S=plgp_samples1, I=plgp_samples2, R=plgp_samples3)

#     # LGCP with kernel parameters lerned a priori
#     lgcp = LGCPGradientMatching.SIRCoxProcess(
#         times, classes, gmtype=:loggaussian, λ0 = dat.λ0s,
#         U=U, W=W, σ=σ, γ=γ, δ=δ,
#         gpgradcov_diagapprox=true,
#         kernel_fn=rbf_kernel,
#         kernel_params=kernel_params,
#         extrapolation_time_length=dat.extrapolation_time_length, #NOTE: setting for extrapolation
#         );

#         # set the kernel paraeters learned in inference of PoissonLikelihoodGP
#         for c in 1:length(lgcp.gm.gps)
#             lgcp.gm.gps[c].ϕ.kernel_params = [
#                 log(sqrt(plgp[c].kernel.kleft.σ2)),
#                 log(sqrt(plgp[c].kernel.kleft.ℓ2))]
#             lgcp.gm.gps[c].σ = sqrt(plgp[c].kernel.kright.σ2)
#             reflect_μϕ!(lgcp.gm.gps[c])
#             reflect_x!(lgcp.gm.gps[c])
#             reflect_σ!(lgcp.gm.gps[c])
#         end

#         lgcp_mcp = Dict(:alg => :HMC, :target => [:y, :x], :L => 5)
#         lgcp_mcp[:gradientmatch] = false  # NOTE: setting for Log Gaussian Cox Process inference
#         lgcp_mcp[:state_rec_cycle] = thin
#         lgcp_mcp[:n_keep_state_rec] = n_keep
#         lgcp_mcp[:block] = Dict(
#             :prob => [0.5, 0.5],
#             :target => [[:y], [:x]],
#             :ϵ => [0.001, 0.001],
#             :accept_counter => [0, 0],
#             :reject_counter => [0, 0]
#         )
#         # sampling (bunrin)
#         lgcp_mcp[:burnin] = true
#         train!(lgcp, n_burnin, lgcp_mcp, show_progress=true)
#         # sampling
#         lgcp_mcp[:burnin] = false
#         train!(lgcp, n_mcmc, lgcp_mcp, show_progress=true)

#     result = (
#         lgcpgm = (gm=lgcpgm, mcp=lgcpgm_mcp),        
#         plgp = (gm=plgp, samples=plgp_samples),
#         lgcp = (gm=lgcp, mcp=lgcp_mcp)
#     )
#     return result
# end

# function run_extrapolation_experiments_predatorprey(
#     dat;
#     seed=125,
#     n_burnin=10000,
#     n_mcmc=30000,
#     thin=30,
#     n_keep=1000,
#     U=26,
#     W=100,
#     W_corse=25,
#     kernel_params=[log(5), log(0.1)],
#     σ=0.01,
#     γ=0.3,
#     δ=0.1
#     )
#     # for reproducibility
#     Random.seed!(seed)

#     # data
#     times = vcat(dat.times...)
#     classes = vcat(fill(1, length(dat.times[1])), fill(2, length(dat.times[2])))

#     # LGCPGM
#     lgcpgm = LGCPGradientMatching.PredatorPreyLGCP(
#         times, classes, gmtype=:loggaussian, λ0 = dat.λ0s,
#         U=U, W=W, σ=σ, γ=γ, δ=δ,
#         gpgradcov_diagapprox=true,
#         kernel_fn=rbf_kernel,
#         kernel_params=kernel_params,
#         extrapolation_time_length=dat.extrapolation_time_length, #NOTE: setting for extrapolation
#         );

#         lgcpgm_mcp = Dict(:alg => :HMC, :target => [:y, :x, :θ], :L => 5)
#         lgcpgm_mcp[:gradientmatch] = true
#         lgcpgm_mcp[:state_rec_cycle] = thin
#         lgcpgm_mcp[:n_keep_state_rec] = n_keep
#         lgcpgm_mcp[:block] = Dict(
#             :prob => [0.3, 0.4, 0.3],
#             :target => [[:y], [:x], [:θ]],
#             :ϵ => [0.001, 0.001, 0.001],
#             :accept_counter => [0, 0, 0],
#             :reject_counter => [0, 0, 0]
#         )
#         # sampling (bunrin)
#         lgcpgm_mcp[:burnin] = true
#         train!(lgcpgm, n_burnin, lgcpgm_mcp, show_progress=true)
#         # sampling
#         lgcpgm_mcp[:burnin] = false
#         train!(lgcpgm, n_mcmc, lgcpgm_mcp, show_progress=true)

#     # PoissonLikelihoodGP - GaussianProcesses.jl
#     window_points = lgcpgm.gm.gps[1].times_y[1:lgcpgm.gm.W]
#     m_prey = Int.(lgcpgm.gm.n_observations[:,1]);
#     m_predator = Int.(lgcpgm.gm.n_observations[:,2])

#     k = GaussianProcesses.SEIso(log(0.025), log(1)) + GaussianProcesses.Noise(log(0.1))
#     k_priors = [Normal(log(0.1), 1), Normal(log(1), 1.), Normal(log(0.1), 1)]
#     plgp1, plgp_samples1 = train_plgp(
#         window_points, m_prey, k, k_priors;
#         n_mcmc=n_mcmc, n_burnin=n_burnin, thin=thin, infer_mean=false, infer_kern=true);
#     plgp2, plgp_samples2 = train_plgp(
#         window_points, m_predator, k, k_priors;
#         n_mcmc=n_mcmc, n_burnin=n_burnin, thin=thin, infer_mean=false, infer_kern=true);
#     plgp = (prey=plgp1, predator=plgp2)
#     plgp_samples = (prey=plgp_samples1, predator=plgp_samples2)

#     # LGCP with kernel parameters lerned a priori
#     lgcp = LGCPGradientMatching.PredatorPreyLGCP(
#         times, classes, gmtype=:loggaussian, λ0 = dat.λ0s,
#         U=U, W=W, σ=σ, γ=γ, δ=δ,
#         gpgradcov_diagapprox=true,
#         kernel_fn=rbf_kernel,
#         kernel_params=kernel_params,
#         extrapolation_time_length=dat.extrapolation_time_length, #NOTE: setting for extrapolation
#         );

#         # set the kernel paraeters learned in inference of PoissonLikelihoodGP
#         for c in 1:length(lgcp.gm.gps)
#             lgcp.gm.gps[c].ϕ.kernel_params = [
#                 log(sqrt(plgp[c].kernel.kleft.σ2)),
#                 log(sqrt(plgp[c].kernel.kleft.ℓ2))]
#             lgcp.gm.gps[c].σ = sqrt(plgp[c].kernel.kright.σ2)
#             reflect_μϕ!(lgcp.gm.gps[c])
#             reflect_x!(lgcp.gm.gps[c])
#             reflect_σ!(lgcp.gm.gps[c])
#         end

#         lgcp_mcp = Dict(:alg => :HMC, :target => [:y, :x], :L => 5)
#         lgcp_mcp[:gradientmatch] = false  # NOTE: setting for Log Gaussian Cox Process inference
#         lgcp_mcp[:state_rec_cycle] = thin
#         lgcp_mcp[:n_keep_state_rec] = n_keep
#         lgcp_mcp[:block] = Dict(
#             :prob => [0.5, 0.5],
#             :target => [[:y], [:x]],
#             :ϵ => [0.001, 0.001],
#             :accept_counter => [0, 0],
#             :reject_counter => [0, 0]
#         )
#         # sampling (bunrin)
#         lgcp_mcp[:burnin] = true
#         train!(lgcp, n_burnin, lgcp_mcp, show_progress=true)
#         # sampling
#         lgcp_mcp[:burnin] = false
#         train!(lgcp, n_mcmc, lgcp_mcp, show_progress=true)

#     result = (
#         lgcpgm = (gm=lgcpgm, mcp=lgcpgm_mcp),
#         plgp = (gm=plgp, samples=plgp_samples),
#         lgcp = (gm=lgcp, mcp=lgcp_mcp)
#     )
#     return result
# end

# function run_extrapolation_experiments_competition(
#     dat;
#     seed=125,
#     n_burnin=10000,
#     n_mcmc=30000,
#     thin=30,
#     n_keep=1000,
#     U=26,
#     W=100,
#     W_corse=25,
#     kernel_params=[log(5), log(0.2)],
#     σ=0.01,
#     γ=0.3,
#     δ=0.1
#     )
#     # for reproducibility
#     Random.seed!(seed)

#     # data
#     times = vcat(dat.times...)
#     C = length(dat.times)
#     classes = vcat([fill(c, length(dat.times[c])) for c in 1:C]...)

#     # LGCPGM
#     lgcpgm = LGCPGradientMatching.CompetitionLGCP(
#         times, classes, gmtype=:loggaussian, λ0 = dat.λ0s,
#         U=U, W=W, σ=σ, γ=γ, δ=δ,
#         gpgradcov_diagapprox=true,
#         kernel_fn=rbf_kernel,
#         kernel_params=kernel_params,
#         extrapolation_time_length=dat.extrapolation_time_length, #NOTE: setting for extrapolation
#         );

#         lgcpgm_mcp = Dict(:alg => :HMC, :target => [:y, :x, :θ], :L => 5)
#         lgcpgm_mcp[:gradientmatch] = true
#         lgcpgm_mcp[:state_rec_cycle] = thin
#         lgcpgm_mcp[:n_keep_state_rec] = n_keep
#         lgcpgm_mcp[:block] = Dict(
#             :prob => [0.3, 0.4, 0.3],
#             :target => [[:y], [:x], [:θ]],
#             :ϵ => [0.001, 0.001, 0.001],
#             :accept_counter => [0, 0, 0],
#             :reject_counter => [0, 0, 0]
#         )
#         # sampling (bunrin)
#         lgcpgm_mcp[:burnin] = true
#         train!(lgcpgm, n_burnin, lgcpgm_mcp, show_progress=true)
#         # sampling
#         lgcpgm_mcp[:burnin] = false
#         train!(lgcpgm, n_mcmc, lgcpgm_mcp, show_progress=true)

#     # PoissonLikelihoodGP - GaussianProcesses.jl
#     window_points = lgcpgm.gm.gps[1].times_y[1:lgcpgm.gm.W]
#     m = [Int.(lgcpgm.gm.n_observations[:,c]) for c in 1:C];

#     k = SEIso(log(0.025), log(1)) + Noise(log(0.1))
#     k_priors = [Normal(log(0.1), 1), Normal(log(1), 1.), Normal(log(0.1), 1)]
#     plgps, plgp_samples = [], []
#     for c in 1:C
#         plgp, samples = train_plgp(
#             window_points, m[c], k, k_priors;
#             n_mcmc=n_mcmc, n_burnin=n_burnin, thin=thin, infer_mean=false, infer_kern=true);
#         push!(plgps, plgp)
#         push!(plgp_samples, samples)
#     end

#     # LGCP with kernel parameters lerned a priori
#     lgcp = LGCPGradientMatching.SIRCoxProcess(
#         times, classes, gmtype=:loggaussian, λ0 = dat.λ0s,
#         U=U, W=W, σ=σ, γ=γ, δ=δ,
#         gpgradcov_diagapprox=true,
#         kernel_fn=rbf_kernel,
#         kernel_params=kernel_params,
#         extrapolation_time_length=dat.extrapolation_time_length, #NOTE: setting for extrapolation
#         );

#         # set the kernel paraeters learned in inference of PoissonLikelihoodGP
#         for c in 1:length(lgcp.gm.gps)
#             lgcp.gm.gps[c].ϕ.kernel_params = [
#                 log(sqrt(plgps[c].kernel.kleft.σ2)),
#                 log(sqrt(plgps[c].kernel.kleft.ℓ2))]
#             lgcp.gm.gps[c].σ = sqrt(plgps[c].kernel.kright.σ2)
#             reflect_μϕ!(lgcp.gm.gps[c])
#             reflect_x!(lgcp.gm.gps[c])
#             reflect_σ!(lgcp.gm.gps[c])
#         end

#         lgcp_mcp = Dict(:alg => :HMC, :target => [:y, :x], :L => 5)
#         lgcp_mcp[:gradientmatch] = false  # NOTE: setting for Log Gaussian Cox Process inference
#         lgcp_mcp[:state_rec_cycle] = thin
#         lgcp_mcp[:n_keep_state_rec] = n_keep
#         lgcp_mcp[:block] = Dict(
#             :prob => [0.5, 0.5],
#             :target => [[:y], [:x]],
#             :ϵ => [0.001, 0.001],
#             :accept_counter => [0, 0],
#             :reject_counter => [0, 0]
#         )
#         # sampling (bunrin)
#         lgcp_mcp[:burnin] = true
#         train!(lgcp, n_burnin, lgcp_mcp, show_progress=true)
#         # sampling
#         lgcp_mcp[:burnin] = false
#         train!(lgcp, n_mcmc, lgcp_mcp, show_progress=true)

#     result = (
#         lgcpgm = (gm=lgcpgm, mcp=lgcpgm_mcp),
#         plgp = (gm=plgps, samples=plgp_samples),
#         lgcp = (gm=lgcp, mcp=lgcp_mcp)
#     )
#     return result
# end

################################ GET POSTERIOR SAMPLES ################################
# function get_posterior_samples_sir(result)
#     gpgmc, gpgmc_mcp = result.gpgm_coarse
#     gpgmf, gpgmf_mcp = result.gpgm_fine
#     lgcpgm, lgcpgm_mcp = result.lgcpgm

#     gpgmc_samples = (
#         x = get_posterior_X_samples(gpgmc, gpgmc_mcp),
#         θ = (
#             a = get_posterior_θ₊_samples(gpgmc, gpgmc_mcp, :a),
#             b = get_posterior_θ₊_samples(gpgmc, gpgmc_mcp, :b)
#             )
#         )
#     gpgmf_samples = (
#         x = get_posterior_X_samples(gpgmf, gpgmf_mcp),
#         θ = (
#             a = get_posterior_θ₊_samples(gpgmf, gpgmf_mcp, :a),
#             b = get_posterior_θ₊_samples(gpgmf, gpgmf_mcp, :b)
#         )
#     )

#     lgcpgm_samples = (
#         x = get_posterior_X_samples(lgcpgm, lgcpgm_mcp),
#         θ = (
#             a = get_posterior_θ₊_samples(lgcpgm, lgcpgm_mcp, :a),
#             b = get_posterior_θ₊_samples(lgcpgm, lgcpgm_mcp, :b)
#         )
#     )

#     posterior_samples = (
#         gpgm_coarse = gpgmc_samples,
#         gpgm_fine = gpgmf_samples,
#         lgcpgm = lgcpgm_samples
#     )
#     return posterior_samples
# end

# function get_posterior_samples_predatorprey(result)
#     gpgmc, gpgmc_mcp = result.gpgm_coarse
#     gpgmf, gpgmf_mcp = result.gpgm_fine
#     lgcpgm, lgcpgm_mcp = result.lgcpgm

#     gpgmc_samples = (
#         x = get_posterior_X_samples(gpgmc, gpgmc_mcp),
#         θ = (
#             a = get_posterior_θ₊_samples(gpgmc, gpgmc_mcp, :a),
#             b = get_posterior_θ₊_samples(gpgmc, gpgmc_mcp, :b),
#             c = get_posterior_θ₊_samples(gpgmc, gpgmc_mcp, :c),
#             d = get_posterior_θ₊_samples(gpgmc, gpgmc_mcp, :d)
#             )
#         )
#     gpgmf_samples = (
#         x = get_posterior_X_samples(gpgmf, gpgmf_mcp),
#         θ = (
#             a = get_posterior_θ₊_samples(gpgmf, gpgmf_mcp, :a),
#             b = get_posterior_θ₊_samples(gpgmf, gpgmf_mcp, :b),
#             c = get_posterior_θ₊_samples(gpgmf, gpgmf_mcp, :c),
#             d = get_posterior_θ₊_samples(gpgmf, gpgmf_mcp, :d)
#         )
#     )

#     lgcpgm_samples = (
#         x = get_posterior_X_samples(lgcpgm, lgcpgm_mcp),
#         θ = (
#             a = get_posterior_θ₊_samples(lgcpgm, lgcpgm_mcp, :a),
#             b = get_posterior_θ₊_samples(lgcpgm, lgcpgm_mcp, :b),
#             c = get_posterior_θ₊_samples(lgcpgm, lgcpgm_mcp, :c),
#             d = get_posterior_θ₊_samples(lgcpgm, lgcpgm_mcp, :d)
#         )
#     )

#     posterior_samples = (
#         gpgm_coarse = gpgmc_samples,
#         gpgm_fine = gpgmf_samples,
#         lgcpgm = lgcpgm_samples
#     )
#     return posterior_samples
# end

# function get_posterior_samples_competition(result)
#     gpgmc, gpgmc_mcp = result.gpgm_coarse
#     gpgmf, gpgmf_mcp = result.gpgm_fine
#     lgcpgm, lgcpgm_mcp = result.lgcpgm

#     gpgmc_samples = (
#         x = get_posterior_X_samples(gpgmc, gpgmc_mcp),
#         θ = (
#             r = get_posterior_θ₊_samples(gpgmc, gpgmc_mcp, :r),
#             η = get_posterior_θ₊_samples(gpgmc, gpgmc_mcp, :η),
#             A = get_posterior_θ₊_samples(gpgmc, gpgmc_mcp, :A)
#             )
#         )
#     gpgmf_samples = (
#         x = get_posterior_X_samples(gpgmf, gpgmf_mcp),
#         θ = (
#             r = get_posterior_θ₊_samples(gpgmf, gpgmf_mcp, :r),
#             η = get_posterior_θ₊_samples(gpgmf, gpgmf_mcp, :η),
#             A = get_posterior_θ₊_samples(gpgmf, gpgmf_mcp, :A)
#         )
#     )

#     lgcpgm_samples = (
#         x = get_posterior_X_samples(lgcpgm, lgcpgm_mcp),
#         θ = (
#             r = get_posterior_θ₊_samples(lgcpgm, lgcpgm_mcp, :r),
#             η = get_posterior_θ₊_samples(lgcpgm, lgcpgm_mcp, :η),
#             A = get_posterior_θ₊_samples(lgcpgm, lgcpgm_mcp, :A)
#         )
#     )

#     posterior_samples = (
#         gpgm_coarse = gpgmc_samples,
#         gpgm_fine = gpgmf_samples,
#         lgcpgm = lgcpgm_samples
#     )
#     return posterior_samples
# end

# function get_posterior_samples_infected(result)
#     lgcpgm, lgcpgm_mcp = result

#     lgcpgm_samples = (
#         x = get_posterior_X_samples(lgcpgm, lgcpgm_mcp),
#         θ = (
#             a = get_posterior_θ₊_samples(lgcpgm, lgcpgm_mcp, :a),
#             b = get_posterior_θ₊_samples(lgcpgm, lgcpgm_mcp, :b)
#         )
#     )

#     posterior_samples = (
#         lgcpgm = lgcpgm_samples
#     )
#     return posterior_samples
# end


# function get_posterior_errors_sir(dat, result)
#     gpgmc, gpgmc_mcp = result.gpgm_coarse
#     gpgmf, gpgmf_mcp = result.gpgm_fine
#     lgcpgm, lgcpgm_mcp = result.lgcpgm

#     true_inter_X = dat.X[:, 1:1001]
#     true_X = Matrix(
#         true_inter_X[:, [i in 0:1/(lgcpgm.gm.U-1):1 for i in dat.ticks]]')'

#     gpgmc_errors = (
#         x = mean(get_posterior_X_samples(gpgmc, gpgmc_mcp)) |>
#             postmean_X -> rmsd(postmean_X, exp.(true_X)),
#         θ = mapslices(
#                 mean,
#                 hcat(get_posterior_θ₊_samples(gpgmc, gpgmc_mcp, :a), get_posterior_θ₊_samples(gpgmc, gpgmc_mcp, :b)),
#                 dims=1) |>
#                 postmean_θ -> rmsd(vec(postmean_θ), [dat.θ.a, dat.θ.b])
#         )
#     gpgmf_errors = (
#         x = mean(get_posterior_X_samples(gpgmf, gpgmf_mcp)) |>
#             postmean_X -> rmsd(postmean_X, exp.(true_X)),
#         θ = mapslices(
#                 mean,
#                 hcat(get_posterior_θ₊_samples(gpgmf, gpgmf_mcp, :a), get_posterior_θ₊_samples(gpgmf, gpgmf_mcp, :b)),
#                 dims=1) |>
#                 postmean_θ -> rmsd(vec(postmean_θ), [dat.θ.a, dat.θ.b])
#         )

#     lgcpgm_errors = (
#         x = mean(get_posterior_X_samples(lgcpgm, lgcpgm_mcp)) |>
#             postmean_X -> rmsd(exp.(postmean_X), exp.(true_X)),
#         θ = mapslices(
#                 mean,
#                 hcat(get_posterior_θ₊_samples(lgcpgm, lgcpgm_mcp, :a), get_posterior_θ₊_samples(lgcpgm, lgcpgm_mcp, :b)),
#                 dims=1) |>
#                 postmean_θ -> rmsd(vec(postmean_θ), [dat.θ.a, dat.θ.b])
#         )

#     posterior_errors = (
#         gpgm_coarse = gpgmc_errors,
#         gpgm_fine = gpgmf_errors,
#         lgcpgm = lgcpgm_errors
#     )
#     return posterior_errors
# end

# function get_posterior_errors_predatorprey(dat, result)
#     gpgmc, gpgmc_mcp = result.gpgm_coarse
#     gpgmf, gpgmf_mcp = result.gpgm_fine
#     lgcpgm, lgcpgm_mcp = result.lgcpgm

#     true_inter_X = dat.X[:, 1:1001]
#     true_X = Matrix(
#         true_inter_X[:, [i in 0:1/(lgcpgm.gm.U-1):1 for i in dat.ticks]]')'

#     gpgmc_errors = (
#         x = mean(get_posterior_X_samples(gpgmc, gpgmc_mcp)) |>
#             postmean_X -> rmsd(postmean_X, exp.(true_X)),
#         θ = mapslices(
#                 mean,
#                 hcat(
#                     get_posterior_θ₊_samples(gpgmc, gpgmc_mcp, :a),
#                     get_posterior_θ₊_samples(gpgmc, gpgmc_mcp, :b),
#                     get_posterior_θ₊_samples(gpgmc, gpgmc_mcp, :c),
#                     get_posterior_θ₊_samples(gpgmc, gpgmc_mcp, :d)),
#                 dims=1) |>
#                 postmean_θ -> rmsd(vec(postmean_θ), [dat.θ.a, dat.θ.b, dat.θ.c, dat.θ.d])
#         )

#     gpgmf_errors = (
#         x = mean(get_posterior_X_samples(gpgmf, gpgmf_mcp)) |>
#             postmean_X -> rmsd(postmean_X, exp.(true_X)),
#         θ = mapslices(
#                 mean,
#                 hcat(
#                     get_posterior_θ₊_samples(gpgmf, gpgmf_mcp, :a),
#                     get_posterior_θ₊_samples(gpgmf, gpgmf_mcp, :b),
#                     get_posterior_θ₊_samples(gpgmf, gpgmf_mcp, :c),
#                     get_posterior_θ₊_samples(gpgmf, gpgmf_mcp, :d)),
#                 dims=1) |>
#                 postmean_θ -> rmsd(vec(postmean_θ), [dat.θ.a, dat.θ.b, dat.θ.c, dat.θ.d])
#         )

#     lgcpgm_errors = (
#         x = mean(get_posterior_X_samples(lgcpgm, lgcpgm_mcp)) |>
#             postmean_X -> rmsd(exp.(postmean_X), exp.(true_X)),
#         θ = mapslices(
#                 mean,
#                 hcat(
#                     get_posterior_θ₊_samples(lgcpgm, lgcpgm_mcp, :a),
#                     get_posterior_θ₊_samples(lgcpgm, lgcpgm_mcp, :b),
#                     get_posterior_θ₊_samples(lgcpgm, lgcpgm_mcp, :c),
#                     get_posterior_θ₊_samples(lgcpgm, lgcpgm_mcp, :d)),
#                 dims=1) |>
#                 postmean_θ -> rmsd(vec(postmean_θ), [dat.θ.a, dat.θ.b, dat.θ.c, dat.θ.d])
#         )

#     posterior_errors = (
#         gpgm_coarse = gpgmc_errors,
#         gpgm_fine = gpgmf_errors,
#         lgcpgm = lgcpgm_errors
#     )
#     return posterior_errors
# end

# function get_posterior_errors_competition(dat, result)
#     gpgmc, gpgmc_mcp = result.gpgm_coarse
#     gpgmf, gpgmf_mcp = result.gpgm_fine
#     lgcpgm, lgcpgm_mcp = result.lgcpgm

#     true_inter_X = dat.X[:, 1:1001]
#     true_X = Matrix(
#         true_inter_X[:, [i in 0:1/(lgcpgm.gm.U-1):1 for i in dat.ticks]]')'
#     true_r = dat.θ.r
#     true_η = dat.θ.η
#     true_A = dat.θ.A

#     gpgmc_errors = (
#         x = mean(get_posterior_X_samples(gpgmc, gpgmc_mcp)) |>
#             postmean_X -> rmsd(postmean_X, exp.(true_X)),
#         θ = mapslices(
#             mean,
#             vcat(
#                 hcat(get_posterior_θ₊_samples(gpgmc, gpgmc_mcp, :r)...),
#                 hcat(get_posterior_θ₊_samples(gpgmc, gpgmc_mcp, :η)...),
#                 hcat(nondiagonal_vec.(get_posterior_θ₊_samples(gpgmc, gpgmc_mcp, :A))...)
#             ),
#             dims=2) |>
#             postmean_θ -> rmsd(postmean_θ, vcat(dat.θ.r, dat.θ.η, nondiagonal_vec(dat.θ.A)))
#         )

#     gpgmf_errors = (
#         x = mean(get_posterior_X_samples(gpgmf, gpgmf_mcp)) |>
#             postmean_X -> rmsd(postmean_X, exp.(true_X)),
#         θ = mapslices(
#             mean,
#             vcat(
#                 hcat(get_posterior_θ₊_samples(gpgmf, gpgmf_mcp, :r)...),
#                 hcat(get_posterior_θ₊_samples(gpgmf, gpgmf_mcp, :η)...),
#                 hcat(nondiagonal_vec.(get_posterior_θ₊_samples(gpgmf, gpgmf_mcp, :A))...)
#             ),
#             dims=2) |>
#             postmean_θ -> rmsd(postmean_θ, vcat(dat.θ.r, dat.θ.η, nondiagonal_vec(dat.θ.A)))
#         )

#     lgcpgm_errors = (
#         x = mean(get_posterior_X_samples(lgcpgm, lgcpgm_mcp)) |>
#             postmean_X -> rmsd(exp.(postmean_X), exp.(true_X)),
#         θ = mapslices(
#             mean,
#             vcat(
#                 hcat(get_posterior_θ₊_samples(lgcpgm, lgcpgm_mcp, :r)...),
#                 hcat(get_posterior_θ₊_samples(lgcpgm, lgcpgm_mcp, :η)...),
#                 hcat(nondiagonal_vec.(get_posterior_θ₊_samples(lgcpgm, lgcpgm_mcp, :A))...)
#             ),
#             dims=2) |>
#             postmean_θ -> rmsd(postmean_θ, vcat(dat.θ.r, dat.θ.η, nondiagonal_vec(dat.θ.A)))
#         )

#     posterior_errors = (
#         gpgm_coarse = gpgmc_errors,
#         gpgm_fine = gpgmf_errors,
#         lgcpgm = lgcpgm_errors
#     )
#     return posterior_errors
# end

# function get_posterior_errors_infected(dat, result)
#     lgcpgm, lgcpgm_mcp = result

#     true_inter_X = dat.X[:, 1:1001]
#     true_X = Matrix(
#         true_inter_X[:, [i in 0:1/(lgcpgm.gm.U-1):1 for i in dat.ticks]]')'

#     lgcpgm_errors = (
#         x = get_posterior_X_samples(lgcpgm, lgcpgm_mcp) .|> X -> rmsd(X, true_X),
#         θ = (
#             a = get_posterior_θ₊_samples(lgcpgm, lgcpgm_mcp, :a) .- dat.θ.a,
#             b = get_posterior_θ₊_samples(lgcpgm, lgcpgm_mcp, :b) .- dat.θ.b
#         )
#     )

#     posterior_errors = (
#         lgcpgm = lgcpgm_errors
#     )
#     return posterior_errors
# end
