fix initial conditions, check bounds

This commit is contained in:
Gaspard Jankowiak 2024-05-16 12:30:37 +02:00
parent 1aa2170ada
commit f7917094b7
3 changed files with 57 additions and 15 deletions

View file

@ -333,7 +333,7 @@ end
function reduce_to_free_parameters(metadata::Dict, params_init::NamedTuple, lb, ub, q) function reduce_to_free_parameters(metadata::Dict, params_init::NamedTuple, lb, ub, q)
default_params = Float64[params_init...] default_params = Float64[params_init...]
free_idx = get_free_parameters_idx(metadata) free_idx = get_free_parameters_idx(metadata)
P_reduced = view(default_params, free_idx) P_reduced = default_params[free_idx]
lb_reduced = lb[free_idx] lb_reduced = lb[free_idx]
ub_reduced = ub[free_idx] ub_reduced = ub[free_idx]
function reduced(P::AbstractArray{Float64}) function reduced(P::AbstractArray{Float64})

View file

@ -3,12 +3,23 @@ module Utils
import TOML: parsefile import TOML: parsefile
import DelimitedFiles: readdlm import DelimitedFiles: readdlm
import DSP import DSP
import Printf: @sprintf
import Crayons as C
function load_init_params(filename::String) function load_init_params(filename::String)
meta = parsefile(filename)["metadata"] meta = parsefile(filename)["metadata"]
params = NamedTuple(Dict(Symbol(p["symbol"]) => p["initial_value"] for p in meta["parameters"])) param_names = [p["symbol"] for p in meta["parameters"]]
param_initial = [p["initial_value"] for p in meta["parameters"]]
params = NamedTuple{Tuple(map(Symbol, param_names))}(param_initial)
lower_bounds = [p["min"] for p in meta["parameters"]] lower_bounds = [p["min"] for p in meta["parameters"]]
upper_bounds = [p["max"] for p in meta["parameters"]] upper_bounds = [p["max"] for p in meta["parameters"]]
println("Got $(length(param_names)) parameters:")
for (i, k) in enumerate(eachindex(param_names))
lb_str = @sprintf "%.3g" lower_bounds[i]
ub_str = @sprintf "%.3g" upper_bounds[i]
p = @sprintf "%.3g" param_initial[i]
println("$(lpad(param_names[i], 7)): $(lpad(lb_str, 7)) <= $(lpad(p, 7)) <= $(lpad(ub_str, 7)) ")
end
return (meta=meta, params=params, lower_bounds=lower_bounds, upper_bounds=upper_bounds) return (meta=meta, params=params, lower_bounds=lower_bounds, upper_bounds=upper_bounds)
end end
@ -85,9 +96,11 @@ function chi2(I_data, I_model, err)
return sum((I_data .- I_model ./ err) .^ 2) return sum((I_data .- I_model ./ err) .^ 2)
end end
function add_log_barriers(f::Function, constraints::Vector{Tuple{T,T}}; padding_factor=0.1) where {T<:Real} function add_log_barriers(f::Function, constraints::Vector{Tuple{T,T}}; padding_factor=0.1, mode::Symbol=:outer) where {T<:Real}
n = length(constraints) n = length(constraints)
@assert mode in [:outer, :inner]
lower_shifts = Float64[] lower_shifts = Float64[]
upper_shifts = Float64[] upper_shifts = Float64[]
@ -124,21 +137,23 @@ function add_log_barriers(f::Function, constraints::Vector{Tuple{T,T}}; padding_
end end
end end
barrier_sign = mode == :outer ? 1.0 : -1.0
function std_lower_barrier(s::Float64) function std_lower_barrier(s::Float64)
return max(-log(s + 1.0), 0.0)^2 return max(-log(s + barrier_sign), 0.0)^2
end end
function barrier(x::Vector{Float64}) function barrier(x::Vector{Float64})
lb = (x .- lower_shifts) ./ barrier_widths lb = (x .- lower_shifts) ./ barrier_widths
ub = (upper_shifts .- x) ./ barrier_widths ub = (upper_shifts .- x) ./ barrier_widths
lb_violations = findall(<=(-1.0), lb) lb_violations = findall(<=(-barrier_sign), lb)
ub_violations = findall(<=(-1.0), ub) ub_violations = findall(<=(-barrier_sign), ub)
if !isempty(lb_violations) if !isempty(lb_violations)
err_msg = "" err_msg = ""
for i in lb_violations for i in lb_violations
err_msg *= " · x[$i] = $(x[i]) < $(constraints[i][1]), width = $(barrier_widths[i])" err_msg *= "\n · x[$i] = $(x[i]) < $(constraints[i][1]), width = $(barrier_widths[i])"
end end
@error err_msg @error err_msg
throw("lower bound violation for indice(s) $(lb_violations)") throw("lower bound violation for indice(s) $(lb_violations)")
@ -160,4 +175,20 @@ function add_log_barriers(f::Function, constraints::Vector{Tuple{T,T}}; padding_
return x::Vector{Float64} -> f(x) .+ barrier(x) return x::Vector{Float64} -> f(x) .+ barrier(x)
end end
function print_check_bounds(param_names, param_values, lb, ub)
for (i, v) in enumerate(param_values)
lb_str = @sprintf "%.3g" lb[i]
ub_str = @sprintf "%.3g" ub[i]
p = @sprintf "%.3g" v
if lb[i] <= v <= ub[i]
color = :white
bold = false
else
color = :light_red
bold = true
end
println("$(lpad(param_names[i], 7)): ", C.Crayon(foreground=color, bold=bold), "$(lpad(lb_str, 7)) <= $(lpad(p, 7)) <= $(lpad(ub_str, 7)) ")
end
end
end # module Utils end # module Utils

27
test.jl
View file

@ -11,6 +11,7 @@ include("Utils.jl")
# @views q, I, err = d[filtered_idx, 1], d[filtered_idx, 2], d[filtered_idx, 3] # @views q, I, err = d[filtered_idx, 1], d[filtered_idx, 2], d[filtered_idx, 3]
meta, params_init, lower_bounds, upper_bounds, qie_data = Utils.load_config("Data/PAR_POPC-test.toml") meta, params_init, lower_bounds, upper_bounds, qie_data = Utils.load_config("Data/PAR_POPC-test.toml")
param_names = keys(params_init)
best_5k_full = begin best_5k_full = begin
f_χ2 = open("POPC-test-5k/Results_collection-X2.dat", "r") f_χ2 = open("POPC-test-5k/Results_collection-X2.dat", "r")
@ -61,10 +62,7 @@ intensity_reduced, P_reduced, lb_reduced, ub_reduced = PLUV.reduce_to_free_param
simple_bounds = collect(zip(lb_reduced, ub_reduced)) simple_bounds = collect(zip(lb_reduced, ub_reduced))
simple_init = 0.5 * (lb_reduced .+ ub_reduced) simple_init = 0.5 * (lb_reduced .+ ub_reduced)
padding_factor = fill(0.1, length(simple_init)) padding_factor = fill(1e-1, length(simple_init))
padding_factor[17] = 10.0
padding_factor[18] = 10.0
bounds = MH.boxconstraints(lb=lb_reduced, ub=ub_reduced) bounds = MH.boxconstraints(lb=lb_reduced, ub=ub_reduced)
@ -82,7 +80,7 @@ function obj_residuals(P)
return factor * residuals return factor * residuals
end end
barriered_obj = Utils.add_log_barriers(obj_residuals, simple_bounds; padding_factor=padding_factor) barriered_obj = Utils.add_log_barriers(obj_residuals, simple_bounds; padding_factor=padding_factor, mode=:outer)
information = MH.Information(f_optimum=0.0) information = MH.Information(f_optimum=0.0)
information = MH.Information() information = MH.Information()
@ -96,10 +94,23 @@ I_mean_5k, _ = PLUV.intensity(mean_5k_full, q_all)
# Gauss-Newton # Gauss-Newton
if true if true
_, result = GN.optimize(barriered_obj, simple_init) initial_guess = PLUV.reduce_to_free_parameters(meta, collect(Float64, values(params_init)))
initial_guess = best_5k_params
_, result = GN.optimize(barriered_obj, initial_guess; show_trace=true, iscale=1, ZCP=1e-2, ZCPMIN=1e-2)
#_, result = GN.optimize(barriered_obj, initial_guess)
if GN.has_converged(result) if GN.has_converged(result)
@info "Gauss-Newton converged"
@show result
P_best = result.minimizer P_best = result.minimizer
I_best, _ = intensity_reduced(P_best) I_best, _ = intensity_reduced(P_best)
@info "Simulated Annealing 5k"
Utils.print_check_bounds(param_names, best_5k_params, lb_reduced, ub_reduced)
@info "Our result"
Utils.print_check_bounds(param_names, P_best, lb_reduced, ub_reduced)
else else
@error "Gauss-Newton did not converge" @error "Gauss-Newton did not converge"
end end
@ -116,8 +127,8 @@ end
if true if true
fig = M.Figure() fig = M.Figure()
#ax = M.Axis(fig[1, 1]; xscale=log10, yscale=log10) ax = M.Axis(fig[1, 1]; xscale=log10, yscale=log10)
ax = M.Axis(fig[1, 1]) #ax = M.Axis(fig[1, 1])
M.lines!(ax, q, I_best, label="MH best (julia)") M.lines!(ax, q, I_best, label="MH best (julia)")
M.scatter!(ax, q, I_data, label="data") M.scatter!(ax, q, I_data, label="data")