Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Base.show for EKP.jl types #257

Closed
haakon-e opened this issue Feb 28, 2023 · 3 comments · Fixed by #259
Closed

Base.show for EKP.jl types #257

haakon-e opened this issue Feb 28, 2023 · 3 comments · Fixed by #259
Assignees

Comments

@haakon-e
Copy link
Member

Simple ways to print structs in a useful manner:

Constraint:

Base.show(io::IO, cons::Constraint{BoundedBelow}) = print(io, "Bounds: [$(cons.bounds["lower_bound"]), ∞)")
Base.show(io::IO, cons::Constraint{BoundedAbove}) = print(io, "Bounds: (-∞, $(cons.bounds["upper_bound"])]")
Base.show(io::IO, cons::Constraint{Bounded}) = print(io, "Bounds: [$(cons.bounds["lower_bound"]), $(cons.bounds["upper_bound"])]")
Base.show(io::IO, cons::Constraint{NoConstraint}) = print(io, "Bounds: (-∞, ∞)")

ParameterDistribution

function Base.show(io::IO, dist::ParameterDistribution)
    (; distribution, constraint, name) = dist
    n = length(name)
    out = "ParameterDistribution with $n entries: \n"
    for (dist, cons, nam) in zip(distribution, constraint, name)
        dist_string = replace("$dist", "\n" => " ")  # hack to remove `\n` from `Parameterized(FullNormal(...))`
        out *= "'$(nam)' with $(cons) over distribution $dist_string \n"
    end
    print(io, out)
end

Examples:

julia> prior = combine_distributions([
           constrained_gaussian("point_seven", 0.7, 0.15, 0.0, 1.0),
           constrained_gaussian("upper bound", 0.7, 0.15, -Inf, 5.0),
           constrained_gaussian("lower bound", 0.7, 0.15, -5.0, Inf),
           constrained_gaussian("no bound", 0.7, 0.15, -Inf, Inf),
       ])
ParameterDistribution with 4 entries: 
'point_seven' with Bounds: [0.0, 1.0] over distribution Parameterized(Normal{Float64}=0.9581731745582243, σ=0.7851841275859747)) 
'upper bound' with Bounds: (-∞, 5.0] over distribution Parameterized(Normal{Float64}=1.458006955602075, σ=0.03487311564634986)) 
'lower bound' with Bounds: [-5.0, ∞) over distribution Parameterized(Normal{Float64}=1.740120034293624, σ=0.026311235124201148)) 
'no bound' with Bounds: (-∞, ∞) over distribution Parameterized(Normal{Float64}=0.7, σ=0.15)) 

# Note, this doesn't quite work for multivariate distributions since I assume the length of `prior.distribution` is the same as e.g. `prior.constraint`:
# d1, c1, name1 taken from https://clima.github.io/EnsembleKalmanProcesses.jl/dev/parameter_distributions/#Example-combining-several-distributions
julia> u1 = ParameterDistribution(d1, c1, name1)
ParameterDistribution with 1 entries: 
'constrained_mvnormal' with Bounds: [0.0, ∞) over distribution Parameterized(FullNormal( dim: 3 μ: [1.0, 1.0, 1.0] Σ: [0.5 0.25 0.0; 0.25 0.5 0.25; 0.0 0.25 0.5] ) ) 

# compare to printing just the constraint, which correctly shows all three constraints:
julia> c1
3-element Vector{Constraint{BoundedBelow}}:
 Bounds: [0.0, ∞)
 Bounds: [0.0, ∞)
 Bounds: [0.0, ∞)
@odunbar
Copy link
Collaborator

odunbar commented Feb 28, 2023

Thanks @haakon-e, I did not know of these Base.show overloading!

RE the looping over distributions, this is an easy fix that could be done in a PR. We have a "batch" function that returns the dimension indices of the distributions so we can easily handle the lists of different sized objects. (e.g. batch (prior) = [1 2 3 4] while batch(u1) = [ 1:3 ])

@haakon-e
Copy link
Member Author

haakon-e commented Mar 1, 2023

I think a point for discussion is whether it makes more sense to "group" parameters as I indicated above;
i.e. by displaying its name, bounds, and distribution.

I ask because this philosophy kinda breaks down in the example where you actually generate a multivariate distribution as your parameter prior (using MvNormal). In that case, our only option is to list the contents as name, multiple bounds, multivariate distribution.

What do you think? We could just keep it as suggested so that what is printed is as close as possible to how the priors were actually constructed, i.e. with different grouping for "scalar" parameters and "vector" parameters.

@odunbar
Copy link
Collaborator

odunbar commented Mar 1, 2023

I think we should keep the grouping of the parameter distribution as you've done it, as one top-level PD may contain a variety of heterogeneously defined distributions within it. All of these must have one name, and one of the types Parameterized,Samples or VectorOfParameterized.

@haakon-e haakon-e linked a pull request Mar 16, 2023 that will close this issue
bors bot added a commit that referenced this issue Mar 19, 2023
259: implement Base.show for core types r=haakon-e a=haakon-e

See #257 for details.

With this implementation, combining all the examples [here](https://clima.github.io/EnsembleKalmanProcesses.jl/dev/parameter_distributions/#ParameterDistribution-constructor) gives a vector that displays like this:
```julia
julia> combine_distributions([u; prior])
ParameterDistribution with 7 entries: 
'constrained_mvnormal' with Constraint[Bounds: (0, ∞), Bounds: (0, ∞), Bounds: (0, ∞)] over distribution Parameterized(FullNormal( dim: 3 μ: [1.0, 1.0, 1.0] Σ: [0.5 0.25 0.0; 0.25 0.5 0.25; 0.0 0.25 0.5] ) ) 
'constrained_sampled' with Constraint[Bounds: (10, 15), Bounds: (-∞, ∞)] over distribution Samples{Float64}([1.0 5.0 9.0 13.0; 3.0 7.0 11.0 15.0]) 
'Beta' with Constraint[Bounds: (-∞, ∞), Bounds: (-∞, ∞), Bounds: (-∞, ∞), Bounds: (-∞, ∞), Bounds: (-∞, ∞), Bounds: (-∞, ∞), Bounds: (-∞, ∞), Bounds: (-∞, ∞), Bounds: (-∞, ∞), Bounds: (-∞, ∞), Bounds: (-∞, ∞), Bounds: (-∞, ∞), Bounds: (-∞, ∞), Bounds: (-∞, ∞), Bounds: (-∞, ∞), Bounds: (-∞, ∞), Bounds: (-∞, ∞), Bounds: (-∞, ∞), Bounds: (-∞, ∞), Bounds: (-∞, ∞)] over distribution VectorOfParameterized{Beta{Float64}}(Beta{Float64}[Beta{Float64}(α=2.0, β=2.0), Beta{Float64}(α=2.0, β=2.0), Beta{Float64}(α=2.0, β=2.0), Beta{Float64}(α=2.0, β=2.0), Beta{Float64}(α=2.0, β=2.0), Beta{Float64}(α=2.0, β=2.0), Beta{Float64}(α=2.0, β=2.0), Beta{Float64}(α=2.0, β=2.0), Beta{Float64}(α=2.0, β=2.0), Beta{Float64}(α=2.0, β=2.0), Beta{Float64}(α=2.0, β=2.0), Beta{Float64}(α=2.0, β=2.0), Beta{Float64}(α=2.0, β=2.0), Beta{Float64}(α=2.0, β=2.0), Beta{Float64}(α=2.0, β=2.0), Beta{Float64}(α=2.0, β=2.0), Beta{Float64}(α=2.0, β=2.0), Beta{Float64}(α=2.0, β=2.0), Beta{Float64}(α=2.0, β=2.0), Beta{Float64}(α=2.0, β=2.0)]) 
'point_seven' with Constraint[Bounds: (0.0, 1.0)] over distribution Parameterized(Normal{Float64}(μ=0.9581731745582243, σ=0.7851841275859747)) 
'upper bound' with Constraint[Bounds: (-∞, 5.0)] over distribution Parameterized(Normal{Float64}(μ=1.458006955602075, σ=0.03487311564634986)) 
'lower bound' with Constraint[Bounds: (-5.0, ∞)] over distribution Parameterized(Normal{Float64}(μ=1.740120034293624, σ=0.026311235124201148)) 
'no bound' with Constraint[Bounds: (-∞, ∞)] over distribution Parameterized(Normal{Float64}(μ=0.7, σ=0.15)) 
```

Individual constraints render like this:
```julia
julia> no_constraint()
Constraint{NoConstraint} with bounds (-∞, ∞)

julia> bounded_below(-5)
Constraint{BoundedBelow} with bounds (-5, ∞)

julia> bounded_above(-5)
Constraint{BoundedAbove} with bounds (-∞, -5)

julia> bounded(-5,5)
Constraint{Bounded} with bounds (-5, 5)
```

Co-authored-by: Haakon Ludvig Langeland Ervik <[email protected]>
@bors bors bot closed this as completed in #259 Mar 19, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants