Skip to content

Commit 63829f4

Browse files
committed
Start adding example
1 parent aa389fb commit 63829f4

7 files changed

Lines changed: 256 additions & 18 deletions

File tree

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: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
using Distributions
2+
using EnsembleKalmanProcesses
3+
using JLD2
4+
using LinearAlgebra
5+
using Random
6+
7+
include("./models.jl")
8+
9+
## Define custom learning-rate scheduler for better control over timepoints reached
10+
mutable struct CheckpointScheduler <: EnsembleKalmanProcesses.LearningRateScheduler
11+
αs::Vector{Float64}
12+
scheduler
13+
14+
current_index
15+
16+
CheckpointScheduler(αs, scheduler) = new(αs, scheduler, 2)
17+
end
18+
function EnsembleKalmanProcesses.calculate_timestep!(ekp, g, Δt_new, scheduler::CheckpointScheduler)
19+
EnsembleKalmanProcesses.calculate_timestep!(ekp, g, Δt_new, scheduler.scheduler)
20+
if scheduler.current_index <= length(scheduler.αs) && get_algorithm_time(ekp)[end] > scheduler.αs[scheduler.current_index]
21+
get_algorithm_time(ekp)[end] = scheduler.αs[scheduler.current_index]
22+
scheduler.current_index += 1
23+
end
24+
25+
nothing
26+
end
27+
function get_algorithm_time(ekp::EnsembleKalmanProcess) # This is not defined in older package versions; this is temporary here
28+
return accumulate(+, ekp.Δt)
29+
end
30+
31+
rng = Random.MersenneTwister(123)
32+
input_dim = 100
33+
output_dim = 50
34+
αs = [0.0, 0.25, 0.5, 0.75, 1.0]
35+
36+
num_trials = 1
37+
for trial in 1:num_trials
38+
@info "Trial $trial"
39+
40+
prior_cov, y, obs_noise_cov, model, true_parameter = linlinexp(input_dim, output_dim, rng)
41+
prior_obj = ParameterDistribution(
42+
Parameterized(MvNormal(zeros(size(prior_cov, 1)), prior_cov)), fill(no_constraint(), size(prior_cov, 1)), "linlinexp_prior",
43+
)
44+
45+
n_ensemble = 200
46+
initial_ensemble = construct_initial_ensemble(rng, prior_obj, n_ensemble)
47+
ekp = EnsembleKalmanProcess(
48+
initial_ensemble,
49+
y,
50+
obs_noise_cov,
51+
TransformInversion();
52+
rng,
53+
scheduler = CheckpointScheduler(αs, EKSStableScheduler(2.0, 0.01)),
54+
)
55+
56+
n_iters = 0
57+
while vcat([0.0], get_algorithm_time(ekp))[end] < maximum(αs)
58+
n_iters += 1
59+
G_ens = hcat([forward_map(param, model) for param in eachcol(get_ϕ_final(prior_obj, ekp))]...)
60+
terminate = update_ensemble!(ekp, G_ens)
61+
if !isnothing(terminate)
62+
throw("EKI terminated prematurely: $(terminate)! Shouldn't happen...")
63+
end
64+
end
65+
@info "EKP iterations: $n_iters"
66+
@info "Loss over iterations: $(get_error(ekp))"
67+
68+
ekp_samples = Dict()
69+
for α in αs
70+
closest_iter = argmin(0:n_iters) do i
71+
abs- (i == 0 ? 0.0 : get_algorithm_time(ekp)[i]))
72+
end + 1
73+
ekp_samples[α] = (get_u(ekp, closest_iter), closest_iter == n_iters + 1 ? hcat([forward_map(u, model) for u in eachcol(get_u(ekp, closest_iter))]...) : get_g(ekp, closest_iter))
74+
end
75+
76+
77+
#! format: off
78+
save(
79+
"datafiles/ekp_linlinexp_$(trial).jld2",
80+
"ekpobj", ekp,
81+
"ekp_samples", ekp_samples,
82+
"prior_obj", prior_obj,
83+
"y", y,
84+
"obs_noise_cov", obs_noise_cov,
85+
"model", model,
86+
"true_parameter", true_parameter,
87+
)
88+
#! format: on
89+
end

examples/DimensionReduction/datafiles/.gitkeep

Whitespace-only changes.
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
using Distributions
2+
using EnsembleKalmanProcesses
3+
using JLD2
4+
using LinearAlgebra
5+
using Random
6+
7+
# CES
8+
using CalibrateEmulateSample.Emulators
9+
using CalibrateEmulateSample.MarkovChainMonteCarlo
10+
using CalibrateEmulateSample.Utilities
11+
12+
include("./models.jl")
13+
14+
rng = Random.MersenneTwister(123)
15+
input_dim = 100
16+
output_dim = 50
17+
αs = [0.0, 0.25, 0.5, 0.75, 1.0]
18+
19+
num_trials = 1
20+
for trial in 1:num_trials
21+
loaded = load("datafiles/ekp_linlinexp_$(trial).jld2")
22+
ekpobj = loaded["ekpobj"]
23+
ekp_samp = loaded["ekp_samples"]
24+
prior_obj = loaded["prior_obj"]
25+
obs_noise_cov = loaded["obs_noise_cov"]
26+
y = loaded["y"]
27+
model = loaded["model"]
28+
29+
30+
min_iter = 1
31+
max_iter = 7 # number of EKP iterations to use data from is at most this
32+
33+
encoder_schedule_decorrelate = [(decorrelate_structure_mat(; retain_var = 0.7), "in_and_out")]
34+
encoder_schedules_li = [
35+
[(likelihood_informed(; retain_KL = 0.9, alpha = α, use_data_as_samples = false), "in_and_out")] for α in αs
36+
]
37+
38+
em_decorrelate = Emulator(
39+
GaussianProcess(Emulators.GPJL(); kernel = nothing, prediction_type = Emulators.YType(), noise_learn = false),
40+
Utilities.get_training_points(ekpobj, min_iter:max_iter);
41+
encoder_schedule = encoder_schedule_decorrelate,
42+
encoder_kwargs = (; prior_cov = cov(prior_obj), obs_noise_cov = obs_noise_cov),
43+
)
44+
45+
ems_li = [
46+
Emulator(
47+
GaussianProcess(Emulators.GPJL(); kernel = nothing, prediction_type = Emulators.YType(), noise_learn = false),
48+
Utilities.get_training_points(ekpobj, min_iter:max_iter);
49+
encoder_schedule = encoder_schedule,
50+
encoder_kwargs = (; prior_cov = cov(prior_obj), obs_noise_cov = obs_noise_cov, samples_in = ekp_samp[α][1], samples_out = ekp_samp[α][2], observation = y),
51+
)
52+
for (encoder_schedule, α) in zip(encoder_schedules_li, αs)
53+
]
54+
55+
post_means = zeros(input_dim, 0)
56+
for em in vcat(em_decorrelate, ems_li...)
57+
u0 = rand(MvNormal(mean(prior_obj), cov(prior_obj)))
58+
mcmc = MCMCWrapper(RWMHSampling(), y, prior_obj, em; init_params = u0)
59+
new_step = optimize_stepsize(mcmc; init_stepsize = 0.1, N = 2000, discard_initial = 0)
60+
61+
println("Begin MCMC - with step size ", new_step)
62+
chain = MarkovChainMonteCarlo.sample(mcmc, 10_000; stepsize = new_step, discard_initial = 2_000)
63+
64+
posterior = MarkovChainMonteCarlo.get_posterior(mcmc, chain)
65+
66+
post_mean = mean(posterior)
67+
post_cov = cov(posterior)
68+
println("post_mean")
69+
println(post_mean)
70+
println("post_cov")
71+
println(post_cov)
72+
73+
post_means = hcat(post_means, reshape(post_mean, input_dim, 1))
74+
end
75+
76+
println(post_means[1:10,:])
77+
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(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/Utilities.jl

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -243,8 +243,8 @@ function initialize_and_encode_with_schedule!(
243243
prior_cov::Union{Nothing, StructureMatrix} = nothing,
244244
obs_noise_cov::Union{Nothing, StructureMatrix} = nothing,
245245
observation::Union{Nothing, StructureVector} = nothing,
246-
prior_samples_in::Union{Nothing, StructureVector} = nothing,
247-
prior_samples_out::Union{Nothing, StructureVector} = nothing,
246+
samples_in::Union{Nothing, StructureVector} = nothing,
247+
samples_out::Union{Nothing, StructureVector} = nothing,
248248
) where {VV <: AbstractVector, PDC <: PairedDataContainer}
249249
processed_io_pairs = deepcopy(io_pairs)
250250

@@ -259,16 +259,16 @@ function initialize_and_encode_with_schedule!(
259259
end
260260

261261
input_structure_vecs = deepcopy(input_structure_vecs)
262-
if !isnothing(prior_samples_in)
263-
(input_structure_vecs[:prior_samples_in] = prior_samples_in)
262+
if !isnothing(samples_in)
263+
(input_structure_vecs[:samples_in] = samples_in)
264264
end
265265

266266
output_structure_vecs = deepcopy(output_structure_vecs)
267267
if !isnothing(observation)
268268
(output_structure_vecs[:observation] = observation)
269269
end
270-
if !isnothing(prior_samples_out)
271-
(output_structure_vecs[:prior_samples_out] = prior_samples_out)
270+
if !isnothing(samples_out)
271+
(output_structure_vecs[:samples_out] = samples_out)
272272
end
273273

274274
# apply_to is the string "in", "out" etc.

src/Utilities/likelihood_informed.jl

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,15 @@ mutable struct LikelihoodInformed{FT <: Real} <: PairedDataContainerProcessor
1111
dim_criterion::Tuple{Symbol, <:Number}
1212
α::FT
1313
grad_type::Symbol
14-
use_prior_samples::Bool
14+
use_data_as_samples::Bool
1515
end
1616

17-
function likelihood_informed(retain_KL; alpha = 0.0, grad_type = :localsl, use_prior_samples = true)
17+
function likelihood_informed(; retain_KL, alpha = 0.0, grad_type = :localsl, use_data_as_samples = false)
1818
if grad_type [:linreg, :localsl]
1919
@error "Unknown grad_type=$grad_type"
2020
end
2121

22-
LikelihoodInformed(nothing, nothing, nothing, (:retain_KL, retain_KL), alpha, grad_type, use_prior_samples)
22+
LikelihoodInformed(nothing, nothing, nothing, (:retain_KL, retain_KL), alpha, grad_type, use_data_as_samples)
2323
end
2424

2525
get_encoder_mat(li::LikelihoodInformed) = li.encoder_mat
@@ -45,14 +45,13 @@ function initialize_processor!(
4545
else
4646
get_structure_vec(output_structure_vectors, :observation)
4747
end
48-
samples_in, samples_out = if li.use_prior_samples
49-
@assert α 0.0
48+
samples_in, samples_out = if li.use_data_as_samples
49+
(in_data, out_data)
50+
else
5051
(
51-
get_structure_vec(input_structure_vectors, :prior_samples_in),
52-
get_structure_vec(output_structure_vectors, :prior_samples_out),
52+
get_structure_vec(input_structure_vectors, :samples_in),
53+
get_structure_vec(output_structure_vectors, :samples_out),
5354
)
54-
else
55-
(in_data, out_data)
5655
end
5756
obs_noise_cov = get_structure_mat(output_structure_matrices, :obs_noise_cov)
5857
noise_cov_inv = inv(obs_noise_cov)
@@ -79,10 +78,10 @@ function initialize_processor!(
7978

8079
li.encoder_mat = if apply_to == "in" || α 0
8180
decomp = if apply_to == "in"
82-
eigen(mean(grad' * noise_cov_inv * ((1-α)obs_noise_cov + α^2 * (y - g) * (y - g)') * noise_cov_inv * grad for (g, grad) in zip(eachcol(samples_out), grads)), sortby = (-))
81+
eigen(hermitianpart(mean(grad' * noise_cov_inv * ((1-α)obs_noise_cov + α^2 * (y - g) * (y - g)') * noise_cov_inv * grad for (g, grad) in zip(eachcol(samples_out), grads))), sortby = (-))
8382
else
8483
@assert apply_to == "out"
85-
eigen(mean(grad * grad' for grad in grads), obs_noise_cov, sortby = (-))
84+
eigen(hermitianpart(mean(grad * grad' for grad in grads)), obs_noise_cov, sortby = (-))
8685
end
8786

8887
if li.dim_criterion[1] == :retain_KL
@@ -93,6 +92,7 @@ function initialize_processor!(
9392
@assert li.dim_criterion[1] == :dimension
9493
trunc_val = li.dim_criterion[2]
9594
end
95+
@info " truncating at $trunc_val/$(length(sv_cumsum)) retaining $(100.0*sv_cumsum[trunc_val])% of the KL divergence reduction"
9696
li.encoder_mat = decomp.vectors[:, 1:trunc_val]'
9797
else
9898
@assert apply_to == "out"
@@ -135,13 +135,18 @@ function initialize_processor!(
135135
if li.dim_criterion[1] == :retain_KL
136136
retain_KL = li.dim_criterion[2]
137137
ref = f(M, zeros(output_dim, 0))
138-
if f(M, Vs) / ref 1 - retain_KL
138+
val = f(M, Vs)
139+
if val / ref 1 - retain_KL
140+
@info " truncating at $k/$output_dim retaining $(100.0*(1-val/ref))% of the KL divergence reduction"
139141
break # TODO: Start bisecting?
140142
else
141143
k *= 2
142144
end
143145
else
144146
@assert li.dim_criterion[1] == :dimension
147+
ref = f(M, zeros(output_dim, 0))
148+
val = f(M, Vs)
149+
@info " truncating at $(li.dim_criterion[2])/$output_dim retaining $(100.0*val/ref)% of the KL divergence reduction"
145150
break
146151
end
147152
end

0 commit comments

Comments
 (0)