Skip to content

Commit ae22812

Browse files
committed
first port from PR 376
typos add pkgs encoder_kwargs in nice form remove dim criterion for now docstring API refinement add example builds encoder, but mcmc bug examples run, bugs for dimensionality and product order resolved noise injection reduced add some get_encoded_dim functions add consistency for both in-and-out dimensions for li, when using multiple distributions runs through with output reduction, logic simplified for now
1 parent e0470a9 commit ae22812

8 files changed

Lines changed: 838 additions & 31 deletions

File tree

Project.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
1919
LinearMaps = "7a12625a-238d-50fd-b39a-03d52299707e"
2020
LowRankApprox = "898213cb-b102-5a47-900c-97e73b919f73"
2121
MCMCChains = "c7f686f2-ff18-58e9-bc7b-31028e88f75d"
22+
Manifolds = "1cead3c2-87b3-11e9-0ccd-23c62b72b94e"
23+
Manopt = "0fc0a36d-df90-57f3-8f93-d78a9fc72bb5"
2224
Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f"
2325
Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7"
2426
ProgressBars = "49802e3a-d2f1-5c88-81d8-b72133a6f568"
@@ -47,6 +49,8 @@ KernelFunctions = "0.10.64"
4749
LinearMaps = "3.11.4"
4850
LowRankApprox = "0.5.5"
4951
MCMCChains = "7"
52+
Manifolds = "0.11.19"
53+
Manopt = "0.5.34"
5054
Plots = "1.41.1"
5155
Printf = "1"
5256
ProgressBars = "1"
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
[deps]
2+
AdvancedMH = "5b7e9947-ddc0-4b3f-9b55-0d8042f74170"
3+
CalibrateEmulateSample = "95e48a1f-0bec-4818-9538-3db4340308e3"
4+
ChunkSplitters = "ae650224-84b6-46f8-82ea-d812ca08434e"
5+
Distributions = "31c24e10-a181-5473-b8eb-7969acd0382f"
6+
DocStringExtensions = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae"
7+
EnsembleKalmanProcesses = "aa8a2aa5-91d8-4396-bcef-d4f2ec43552d"
8+
FFTW = "7a1cc6ca-52ef-59f5-83cd-3a7055c09341"
9+
ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210"
10+
JLD2 = "033835bb-8acc-5ee8-8aae-3f567f8a3819"
11+
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
12+
MCMCChains = "c7f686f2-ff18-58e9-bc7b-31028e88f75d"
13+
Manifolds = "1cead3c2-87b3-11e9-0ccd-23c62b72b94e"
14+
Manopt = "0fc0a36d-df90-57f3-8f93-d78a9fc72bb5"
15+
Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
16+
Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2"
17+
StatsPlots = "f3b207a7-027a-5e70-b257-86293d7955fd"
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
using Distributions
2+
using EnsembleKalmanProcesses
3+
using JLD2
4+
using LinearAlgebra
5+
using Random
6+
7+
include("./models.jl")
8+
9+
datadir = joinpath(@__DIR__,"datafiles")
10+
if !isdir(datadir)
11+
mkpath(datadir)
12+
end
13+
14+
rng = Random.MersenneTwister(41)
15+
input_dim = 100
16+
output_dim = 100
17+
18+
num_trials = 5
19+
for trial in 1:num_trials
20+
@info "Trial $trial"
21+
22+
prior_cov, y, obs_noise_cov, model, true_parameter = linlinexp(input_dim, output_dim, rng)
23+
prior_obj = ParameterDistribution(
24+
Parameterized(MvNormal(zeros(size(prior_cov, 1)), prior_cov)),
25+
fill(no_constraint(), size(prior_cov, 1)),
26+
"linlinexp_prior",
27+
)
28+
29+
n_ensemble = 50
30+
initial_ensemble = construct_initial_ensemble(rng, prior_obj, n_ensemble)
31+
ekp = EnsembleKalmanProcess(
32+
initial_ensemble,
33+
y,
34+
obs_noise_cov,
35+
TransformInversion();
36+
rng,
37+
)
38+
39+
n_iters = 0
40+
for i in 1:50
41+
n_iters += 1
42+
G_ens = hcat([forward_map(param, model) for param in eachcol(get_ϕ_final(prior_obj, ekp))]...)
43+
terminate = update_ensemble!(ekp, G_ens)
44+
if !isnothing(terminate)
45+
break
46+
end
47+
end
48+
@info "EKP iterations: $n_iters"
49+
@info "Loss over iterations: $(get_error(ekp))"
50+
@info "Timesteps: $(ekp.Δt)"
51+
@info "Checkpoints: $(get_algorithm_time(ekp))"
52+
αs = vec([0 get_algorithm_time(ekp)[1:end]...])
53+
ekp_samples = Dict=> (get_u(ekp, i), get_g(ekp, i)) for (i,α) in enumerate(αs[1:end-1]))
54+
55+
# evaluate at final time
56+
G_ens = hcat([forward_map(param, model) for param in eachcol(get_ϕ_final(prior_obj, ekp))]...)
57+
ekp_samples[αs[end]] = (get_u(ekp,length(αs)), G_ens)
58+
59+
#! format: off
60+
save(
61+
joinpath(datadir,"ekp_linlinexp_$(trial).jld2"),
62+
"ekpobj", ekp,
63+
"ekp_samples", ekp_samples,
64+
"prior_obj", prior_obj,
65+
"y", y,
66+
"obs_noise_cov", obs_noise_cov,
67+
"model", model,
68+
"true_parameter", true_parameter,
69+
"alphas", αs,
70+
)
71+
#! format: on
72+
end
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
using Distributions
2+
using EnsembleKalmanProcesses
3+
using JLD2
4+
using LinearAlgebra
5+
using Random
6+
using MCMCChains
7+
using AdvancedMH
8+
9+
# CES
10+
using CalibrateEmulateSample.Emulators
11+
using CalibrateEmulateSample.MarkovChainMonteCarlo
12+
using CalibrateEmulateSample.Utilities
13+
14+
15+
include("./models.jl")
16+
17+
rng = Random.MersenneTwister(41)
18+
input_dim = 100
19+
output_dim = 100
20+
21+
num_trials = 1
22+
for trial in 1:num_trials
23+
loaded = load("datafiles/ekp_linlinexp_$(trial).jld2")
24+
ekpobj = loaded["ekpobj"]
25+
ekp_samples = loaded["ekp_samples"]
26+
prior = loaded["prior_obj"]
27+
obs_noise_cov = loaded["obs_noise_cov"]
28+
y = loaded["y"]
29+
model = loaded["model"]
30+
true_parameter = loaded["true_parameter"]
31+
αs = loaded["alphas"]
32+
33+
min_iter = 1
34+
max_iter = min(10, length(αs)) # number of EKP iterations to use data from is at most this
35+
training_points = [ekp_samples[α] for α in αs[min_iter:max_iter]]
36+
37+
encoder_schedule_ref = [(decorrelate_structure_mat(; retain_var = 1.0), "in_and_out")]
38+
39+
rvs = collect(0.975:0.0125:1.0)
40+
rkls = collect(0.999:0.0005:1.0)
41+
all_errs = zeros(length(rvs), 1 + length(αs))
42+
43+
# as we have more input samples than out stored in ekp. we provide the extended set of outputs to be used in the likelihood informed processor
44+
final_samples_out = ekp_samples[αs[end]][2]
45+
encoder_kwargs = encoder_kwargs_from(ekpobj, prior; final_samples_out=final_samples_out)
46+
47+
flat_io_pairs = get_training_points(ekpobj, min_iter:max_iter, g_final=final_samples_out)
48+
49+
names = ["reference", "decorrelate, PCA-in", ["decorrelate, LI-in 1:$(i)" for i in 1:length(αs)]...]
50+
51+
ni_scaling = 0.01 # noise injection into null space scaling (def. 1)
52+
53+
for (idx, (rv, rkl)) in enumerate(zip(rvs, rkls))
54+
encoder_schedule_decorrelate = [
55+
(decorrelate_structure_mat(retain_var = rv), "in"),
56+
(decorrelate_structure_mat(), "out"),
57+
]
58+
encoder_schedules_li = [
59+
[
60+
(decorrelate_structure_mat(), "in_and_out"),
61+
(likelihood_informed(retain_kl=rkl, iters=1:i), "in"),
62+
] for i in 1:length(αs)
63+
]
64+
65+
em_ref = forward_map_wrapper(
66+
param -> forward_map(param, model),
67+
prior,
68+
flat_io_pairs;
69+
encoder_schedule = encoder_schedule_ref,
70+
encoder_kwargs = deepcopy(encoder_kwargs),
71+
noise_injector_scaling=ni_scaling, # shouldnt be needed
72+
)
73+
74+
em_decorrelate = forward_map_wrapper(
75+
param -> forward_map(param, model),
76+
prior,
77+
flat_io_pairs;
78+
encoder_schedule = encoder_schedule_decorrelate,
79+
encoder_kwargs = deepcopy(encoder_kwargs),
80+
noise_injector_scaling=ni_scaling,
81+
)
82+
83+
ems_li = [
84+
forward_map_wrapper(
85+
param -> forward_map(param, model),
86+
prior,
87+
flat_io_pairs;
88+
encoder_schedule = encoder_schedule,
89+
encoder_kwargs = deepcopy(encoder_kwargs),
90+
noise_injector_scaling=ni_scaling,
91+
) for encoder_schedule in encoder_schedules_li
92+
]
93+
94+
post_means = reshape(true_parameter, input_dim, 1)
95+
post_covs = []
96+
97+
for (nn, em) in zip(names,vcat(em_ref, em_decorrelate, ems_li...))
98+
println(" ")
99+
@info "Encoding name: $(nn)"
100+
E,_ = get_encoder_from_schedule(get_encoder_schedule(em), "in")
101+
if isnothing(E)
102+
@info "No truncation"
103+
else
104+
105+
@info "Truncation criteria (var>$(rv), or kl > $(rkl))"
106+
@info "input dim reduced to: $(size(E,1))"
107+
108+
end
109+
println(" ")
110+
u0 = rand(MvNormal(mean(prior), cov(prior)))
111+
mcmc = MCMCWrapper(RWMHSampling(), y, prior, em; init_params = u0)
112+
new_step = optimize_stepsize(mcmc; init_stepsize = 0.05, N = 2000, discard_initial = 0)
113+
114+
println("Begin MCMC - with step size ", new_step)
115+
mcmc = MCMCWrapper(RWMHSampling(), y, prior, em; init_params = u0)
116+
chain = MarkovChainMonteCarlo.sample(
117+
mcmc,
118+
40_000;
119+
stepsize = new_step,
120+
discard_initial = 5_000,
121+
)
122+
posterior = MarkovChainMonteCarlo.get_posterior(mcmc, chain)
123+
124+
post_mean = mean(posterior)
125+
post_cov = cov(posterior)
126+
127+
post_means = hcat(post_means, reshape(post_mean, input_dim, 1))
128+
push!(post_covs, post_cov)
129+
end
130+
131+
# post_means[:,1] = true parameter
132+
# post_means[:,2] = ref
133+
# post_means[:,3] = decor, LI etc.
134+
# so err cols are normalized diff to ref of (decor, LI etc.) (lower is better)
135+
all_errs[idx, :] =
136+
[norm(post_means[:, 2] - v) / norm(post_means[:, 2]) for v in eachcol(post_means[:, 3:end])]'
137+
end
138+
@info "error of posterior mean to whitened \"reference\" solution. for $(names[2:end])"
139+
display(all_errs)
140+
end
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
using Distributions
2+
using LinearAlgebra
3+
using Statistics
4+
5+
using EnsembleKalmanProcesses
6+
using EnsembleKalmanProcesses.ParameterDistributions
7+
8+
abstract type ForwardMapType end
9+
10+
## Linear-times-exponential model
11+
function linlinexp(input_dim, output_dim, rng)
12+
# prior
13+
γ0 = 4.0
14+
β_γ = -2
15+
Γ = Diagonal([γ0 * (1.0 * j)^β_γ for j in 1:input_dim])
16+
17+
U = qr(randn(rng, (output_dim, output_dim))).Q
18+
V = qr(randn(rng, (input_dim, input_dim))).Q
19+
λ0 = 100.0
20+
β_λ = -1
21+
Λ = Diagonal([λ0 * (1.0 * j)^β_λ for j in 1:output_dim])
22+
A = U * Λ * V[1:output_dim, :] # output x input
23+
model = LinLinExp(input_dim, output_dim, A)
24+
25+
# generate data sample
26+
obs_noise_cov = Diagonal([Float64(j)^(-1 / 2) for j in 1:output_dim])
27+
noise = rand(rng, MvNormal(zeros(output_dim), obs_noise_cov))
28+
# true_parameter = reshape(ones(input_dim), :, 1)
29+
true_parameter = rand(rng, MvNormal(zeros(input_dim), Γ))
30+
y = vec(forward_map(true_parameter, model) + noise)
31+
return Γ, y, obs_noise_cov, model, true_parameter
32+
end
33+
34+
struct LinLinExp{AM <: AbstractMatrix} <: ForwardMapType
35+
input_dim::Int
36+
output_dim::Int
37+
G::AM
38+
end
39+
40+
function forward_map(X::AVorM, model::LinLinExp) where {AVorM <: AbstractVecOrMat}
41+
return model.G * (X .* exp.(0.05X))
42+
end
43+
44+
function jac_forward_map(X::AbstractVector, model::LinLinExp)
45+
return model.G * Diagonal(exp.(0.05X) .* (1 .+ 0.05X))
46+
end
47+
48+
function jac_forward_map(X::AbstractMatrix, model::LinLinExp)
49+
return [jac_forward_map(x, model) for x in eachcol(X)]
50+
end

src/Emulator.jl

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -41,17 +41,17 @@ include(joinpath("MachineLearningTools", "RandomFeature.jl")) # Random Freatures
4141
# etc.
4242

4343
# defaults in error, all MachineLearningTools require these functions.
44-
function throw_define_mlt()
45-
throw(ErrorException("Unknown MachineLearningTool defined, please use a known implementation"))
44+
function throw_define_mlt(mlt)
45+
throw(ErrorException("Unknown MachineLearningTool defined, please use a known implementation. Please check all methods are defined for the MLT received: \n $mlt"))
4646
end
4747
function build_models!(mlt, iopairs, input_structure_mats, output_structure_mats, mlt_kwargs...)
48-
throw_define_mlt()
48+
throw_define_mlt(mlt)
4949
end
5050
function optimize_hyperparameters!(mlt)
51-
throw_define_mlt()
51+
throw_define_mlt(mlt)
5252
end
5353
function predict(mlt, new_inputs; mlt_kwargs...)
54-
throw_define_mlt()
54+
throw_define_mlt(mlt)
5555
end
5656

5757
# We will define the different emulator types after the general statements
@@ -502,8 +502,6 @@ function forward_map_wrapper(
502502
else
503503
push!(encoder_schedule, (decorrelate_sample_cov(), "out"))
504504
end
505-
else
506-
@warn "Please note that only the output encoder is used in this implementation. \nThe input encoder will be initialized if provided, but not used during sampling, which is completed in the full parameter space."
507505
end
508506

509507
encoder_schedule = create_encoder_schedule(encoder_schedule)

0 commit comments

Comments
 (0)