Skip to content

Commit

Permalink
add dict-based u->c and c->u, also add dict-based get_all_constraints
Browse files Browse the repository at this point in the history
update ParameterDistributions and tests

get_all_constraints unit test

unit test

format;

codecov

format
  • Loading branch information
odunbar committed Jan 31, 2023
1 parent 5109b7f commit 923fbbc
Show file tree
Hide file tree
Showing 2 changed files with 165 additions and 49 deletions.
151 changes: 110 additions & 41 deletions src/ParameterDistributions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,23 @@ function ParameterDistribution(

end

"""
ParameterDistribution(distribution_samples::AbstractMatrix,
constraint::Union{ConstraintType,AbstractVector{ConstraintType}},
name::AbstractString;
params_are_columns::Bool = true)
constructor of a Samples ParameterDistribution from a matrix `distribution_samples` of parameters stored as columns by defaut, (array of) `constraint`, `name`.
"""
function ParameterDistribution(
distribution_samples::AbstractMatrix,
constraint::Union{ConstraintType, AbstractVector},
name::AbstractString;
params_are_columns::Bool = true,
)
distribution = Samples(distribution_samples, params_are_columns = params_are_columns)
return ParameterDistribution(distribution, constraint, name)
end

## Functions

Expand Down Expand Up @@ -449,11 +466,23 @@ function get_n_samples(pd::ParameterDistribution)
end

"""
get_all_constraints(pd::ParameterDistribution)
get_all_constraints(pd::ParameterDistribution; return_dict = false)
Returns the (flattened) array of constraints of the parameter distribution.
Returns the (flattened) array of constraints of the parameter distribution. or as a dictionary ("param_name" => constraints)
"""
get_all_constraints(pd::ParameterDistribution) = pd.constraint
function get_all_constraints(pd::ParameterDistribution; return_dict = false)
if return_dict
pns = get_name(pd)
batch_ids = batch(pd)
ret = Dict()
for (pn, id) in zip(pns, batch_ids)
ret[pn] = pd.constraint[id]
end
return ret
else
return pd.constraint
end
end

"""
batch(pd::ParameterDistribution)
Expand Down Expand Up @@ -718,70 +747,110 @@ end
#apply transforms

"""
transform_constrained_to_unconstrained(pd::ParameterDistribution, xarray::Array{<:Real,1})
transform_constrained_to_unconstrained(pd::ParameterDistribution, x::Array{<:Real,1})
Apply the transformation to map (possibly constrained) parameters `xarray` into the unconstrained space.
Apply the transformation to map (possibly constrained) parameters `x` into the unconstrained space.
"""
function transform_constrained_to_unconstrained(
pd::ParameterDistribution,
xarray::AbstractVector{FT},
) where {FT <: Real}
return cat([c.constrained_to_unconstrained(xarray[i]) for (i, c) in enumerate(pd.constraint)]..., dims = 1)
function transform_constrained_to_unconstrained(pd::ParameterDistribution, x::AbstractVector{FT}) where {FT <: Real}
return cat([c.constrained_to_unconstrained(x[i]) for (i, c) in enumerate(pd.constraint)]..., dims = 1)
end

"""
transform_constrained_to_unconstrained(pd::ParameterDistribution, xarray::Array{<:Real,2})
transform_constrained_to_unconstrained(pd::ParameterDistribution, x::Array{<:Real,2})
Apply the transformation to map (possibly constrained) parameter samples `xarray` into the unconstrained space.
Here, `xarray` contains parameters as columns and samples as rows.
Apply the transformation to map (possibly constrained) parameter samples `x` into the unconstrained space.
Here, `x` contains parameters as columns and samples as rows.
"""
function transform_constrained_to_unconstrained(
pd::ParameterDistribution,
xarray::AbstractMatrix{FT},
) where {FT <: Real}
return Array(hcat([c.constrained_to_unconstrained.(xarray[i, :]) for (i, c) in enumerate(pd.constraint)]...)')
function transform_constrained_to_unconstrained(pd::ParameterDistribution, x::AbstractMatrix{FT}) where {FT <: Real}
return Array(hcat([c.constrained_to_unconstrained.(x[i, :]) for (i, c) in enumerate(pd.constraint)]...)')
end


"""
transform_unconstrained_to_constrained(pd::ParameterDistribution, xarray::Array{<:Real,1})
transform_constrained_to_unconstrained(pd::ParameterDistribution, x::Dict)
Apply the transformation to map parameters `xarray` from the unconstrained space into (possibly constrained) space.
Apply the transformation to map (possibly constrained) parameter samples `x` into the unconstrained space.
Here, `x` contains parameter names as keys, and 1- or 2-arrays as parameter samples.
"""
function transform_unconstrained_to_constrained(
pd::ParameterDistribution,
xarray::AbstractVector{FT},
) where {FT <: Real}
return cat([c.unconstrained_to_constrained(xarray[i]) for (i, c) in enumerate(pd.constraint)]..., dims = 1)
function transform_constrained_to_unconstrained(pd::ParameterDistribution, x::Dict)
param_names = get_name(pd)
pd_batch_idxs = batch(pd) # e.g. [collect(1:2), collect(3:3), collect(5:9)]
pd_dists = get_distribution(pd)
pd_constraints = get_all_constraints(pd, return_dict = true)

ret = Dict()
for (key, val, idxs) in zip(keys(x), values(x), pd_batch_idxs)
ret[key] = Array(
transform_constrained_to_unconstrained(
ParameterDistribution(pd_dists[key], pd_constraints[key], key),
x[key],
),
)
end
return ret

end


"""
transform_unconstrained_to_constrained(pd::ParameterDistribution, xarray::Array{<:Real,2})
transform_unconstrained_to_constrained(pd::ParameterDistribution, x::Array{<:Real,1})
Apply the transformation to map parameter samples `xarray` from the unconstrained space into (possibly constrained) space.
Here, `xarray` contains parameters as columns and samples as rows.
Apply the transformation to map parameters `x` from the unconstrained space into (possibly constrained) space.
"""
function transform_unconstrained_to_constrained(
pd::ParameterDistribution,
xarray::AbstractMatrix{FT},
) where {FT <: Real}
return Array(hcat([c.unconstrained_to_constrained.(xarray[i, :]) for (i, c) in enumerate(pd.constraint)]...)')
function transform_unconstrained_to_constrained(pd::ParameterDistribution, x::AbstractVector{FT}) where {FT <: Real}
return cat([c.unconstrained_to_constrained(x[i]) for (i, c) in enumerate(pd.constraint)]..., dims = 1)
end

"""
transform_unconstrained_to_constrained(pd::ParameterDistribution, x::Array{<:Real,2})
Apply the transformation to map parameter samples `x` from the unconstrained space into (possibly constrained) space.
Here, `x` contains parameters as columns and samples as rows.
"""
function transform_unconstrained_to_constrained(pd::ParameterDistribution, x::AbstractMatrix{FT}) where {FT <: Real}
return Array(hcat([c.unconstrained_to_constrained.(x[i, :]) for (i, c) in enumerate(pd.constraint)]...)')
end

"""
transform_unconstrained_to_constrained(pd::ParameterDistribution, x::Dict)
Apply the transformation to map parameter samples `x` from the unconstrained space into (possibly constrained) space.
Here, `x` contains parameter names as keys, and 1- or 2-arrays as parameter samples.
"""
function transform_unconstrained_to_constrained(pd::ParameterDistribution, x::Dict)
param_names = get_name(pd)
pd_batch_idxs = batch(pd) # e.g. [collect(1:2), collect(3:3), collect(5:9)]
pd_dists = get_distribution(pd)
pd_constraints = get_all_constraints(pd, return_dict = true)

ret = Dict()
for (key, val, idxs) in zip(keys(x), values(x), pd_batch_idxs)
ret[key] = Array(
transform_unconstrained_to_constrained(
ParameterDistribution(pd_dists[key], pd_constraints[key], key),
x[key],
),
)
end
return ret

end

"""
transform_unconstrained_to_constrained(pd::ParameterDistribution, xarray::Array{Array{<:Real,2},1})
transform_unconstrained_to_constrained(pd::ParameterDistribution, x::Array{Array{<:Real,2},1})
Apply the transformation to map parameter sample ensembles `xarray` from the unconstrained space into (possibly constrained) space.
Here, `xarray` is an iterable of parameters sample ensembles for different EKP iterations.
Apply the transformation to map parameter sample ensembles `x` from the unconstrained space into (possibly constrained) space.
Here, `x` is an iterable of parameters sample ensembles for different EKP iterations.
"""
function transform_unconstrained_to_constrained(
pd::ParameterDistribution,
xarray, # ::Iterable{AbstractMatrix{FT}},
x, # ::Iterable{AbstractMatrix{FT}},
) where {FT <: Real}
transf_xarray = []
for elem in xarray
push!(transf_xarray, transform_unconstrained_to_constrained(pd, elem))
transf_x = []
for elem in x
push!(transf_x, transform_unconstrained_to_constrained(pd, elem))
end
return transf_xarray
return transf_x
end

# -------------------------------------------------------------------------------------
Expand Down
63 changes: 55 additions & 8 deletions test/ParameterDistributions/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -114,16 +114,22 @@ using EnsembleKalmanProcesses.ParameterDistributions
name3 = "constrained_sampled"
u3 = ParameterDistribution(d3, c3, name3)

d4 = Samples([1.0 3.0 5.0 7.0; 9.0 11.0 13.0 15.0])
c4 = [bounded(10, 15), no_constraint()]
name4 = "constrained_sampled"
u4 = ParameterDistribution(d4, c4, name4)

@test_throws ArgumentError ParameterDistribution([u1, u2])

u = combine_distributions([u1, u2, u3])
@test u.distribution == [d1, d2, d3]
@test u.constraint == cat([[c1], c2, c3]..., dims = 1)
@test u.name == [name1, name2, name3]
u = combine_distributions([u1, u2, u3, u4])
@test u.distribution == [d1, d2, d3, d4]
@test u.constraint == cat([[c1], c2, c3, c4]..., dims = 1)
@test u.name == [name1, name2, name3, name4]

#equality
@test u == u
@test !(u2 == u3)
@test u3 == u4

end

Expand Down Expand Up @@ -248,6 +254,8 @@ using EnsembleKalmanProcesses.ParameterDistributions

# Test for get_all_constraints
@test get_all_constraints(u) == cat([c1, c2, c3]..., dims = 1)
constraint_dict = Dict(name1 => c1, name2 => c2, name3 => c3)
@test get_all_constraints(u; return_dict = true) == constraint_dict
end

@testset "statistics functions" begin
Expand Down Expand Up @@ -504,14 +512,14 @@ using EnsembleKalmanProcesses.ParameterDistributions
tol = 1e-8
d1 = Parameterized(MvNormal(zeros(4), 0.1 * I))
c1 = [no_constraint(), bounded_below(-1.0), bounded_above(0.4), bounded(-0.1, 0.2)]
name1 = "constrained_mvnormal"
param_dict1 = Dict("distribution" => d1, "constraint" => c1, "name" => name1)
n1 = "constrained_mvnormal"
param_dict1 = Dict("distribution" => d1, "constraint" => c1, "name" => n1)
u1 = ParameterDistribution(param_dict1)

d2 = Samples([1.0 3.0 5.0 7.0; 9.0 11.0 13.0 15.0])
c2 = [bounded(10, 15), no_constraint()]
name2 = "constrained_sampled"
param_dict2 = Dict("distribution" => d2, "constraint" => c2, "name" => name2)
n2 = "constrained_sampled"
param_dict2 = Dict("distribution" => d2, "constraint" => c2, "name" => n2)
u2 = ParameterDistribution(param_dict2)

param_dict = [param_dict1, param_dict2]
Expand Down Expand Up @@ -557,6 +565,45 @@ using EnsembleKalmanProcesses.ParameterDistributions
[x_real_constrained2, x_real_constrained2];
atol = tol,
)

# with transforming samples distributions - using the dict from get_distributions
d3 = Samples([-10.0 10.0 30.0 -30.0])
c3 = [bounded_below(0)]
n3 = "pos_samples"
param_dict3 = Dict("distribution" => d3, "constraint" => c3, "name" => n3)
u3 = ParameterDistribution(param_dict3)
samples_dist = ParameterDistribution([param_dict2, param_dict3]) #combine two samples distributions
samples_dict_unconstrained = get_distribution(samples_dist) # gives name-> values dict
samples_dict_constrained = transform_unconstrained_to_constrained(samples_dist, samples_dict_unconstrained)
samples_dict_unconstrained_again =
transform_constrained_to_unconstrained(samples_dist, samples_dict_constrained)

@test isapprox(
transform_unconstrained_to_constrained(u3, samples_dict_unconstrained[n3]) - samples_dict_constrained[n3],
zeros(size(samples_dict_constrained[n3]));
atol = tol,
)

@test isapprox(
transform_unconstrained_to_constrained(u2, samples_dict_unconstrained[n2]) - samples_dict_constrained[n2],
zeros(size(samples_dict_constrained[n2]));
atol = tol,
)

@test isapprox(
transform_constrained_to_unconstrained(u3, samples_dict_constrained[n3]) -
samples_dict_unconstrained_again[n3],
zeros(size(samples_dict_constrained[n3]));
atol = tol,
)

@test isapprox(
transform_constrained_to_unconstrained(u2, samples_dict_constrained[n2]) -
samples_dict_unconstrained_again[n2],
zeros(size(samples_dict_constrained[n2]));
atol = tol,
)

end

@testset "constrained_gaussian" begin
Expand Down

0 comments on commit 923fbbc

Please sign in to comment.