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

[FR] Real Param is <:Real #51

Closed
mmikhasenko opened this issue Dec 9, 2022 · 7 comments
Closed

[FR] Real Param is <:Real #51

mmikhasenko opened this issue Dec 9, 2022 · 7 comments

Comments

@mmikhasenko
Copy link

Impressive package!
Tracking parameters is a problem in complex models. Your solution is elegant and does not require manual tracking.
Fantastic!

I would like to use it for statistics models.
Do you think it is possible for the real Param be <:Real? It would allow combining with Distributions.jl

MWE

using Distributions
using ModelParameters

μ = Param(0.8, bounds=(0.2, 0.9))
σ = Param(0.8, bounds=(1.2, 1.9))
d = Normal(μ, σ) # fails because Normal{T<:Real}

μ isa Real # false
@rafaqz
Copy link
Owner

rafaqz commented Dec 9, 2022

Can't you just extract the values from the wrappers before using?

E.g. model.val for the values, stripparams(model) for the object without Param wrappers

We can't make Param anything besides a Number, and are even thinking about making it not a Number. Usually using stripparams on a model object before use is better anyway.

@ConnectedSystems
Copy link
Collaborator

ConnectedSystems commented Dec 9, 2022

Adding to what @rafaqz said:

μ = Param(0.8, bounds=(0.2, 0.9))
σ = Param(0.8, bounds=(1.2, 1.9))

s_μ = Uniform.bounds...)
s_σ = Uniform.bounds...)

# Alternatively...
# TriangularDist(a, b, c)
s_μ = TriangularDist.bounds..., (μ.val + μ.bounds[1]) /.bounds[2] - σ.bounds[1]))
s_σ = TriangularDist.bounds..., (σ.val + σ.bounds[1]) /.bounds[2] - σ.bounds[1]))

d = Normal(rand(s_μ), rand(s_σ))

You could wrap all this up into your own sample() function or similar.

@rafaqz
Copy link
Owner

rafaqz commented Feb 22, 2023

I just hit this myself. You're right, its a real pain using Distributions.jl with Param. It would be nice if we could build models with the distibutions embedded in them, with parameters inside.

We should be able to just stick a Param in a distribution.

One problem with Real support is that number support in this package is built on AbstractNumbers.jl, which does not have an AbstractReal type. We could add that there, but its a lot of work.

The Real/Number distinction is painful in julia. There are other Number types like units that there are discussions of integrating with Distributions.jl:

JuliaStats/Distributions.jl#1413

@bgroenks96
Copy link
Collaborator

I don't understand... what is the use case where you can't just strip the param types first?

@rafaqz
Copy link
Owner

rafaqz commented Feb 24, 2023

Building the model structure with the distribution embedded in it.

Say you want a field of you model to be some user defined distribution containing some parameters you need to optimise.

Currently you would need to define a field to hold the distribution type and a field to hold a tuple of Param, and have a function combine them somehow later on when your model runs.

Or treat the fields as function and run them at some point after the optimiser rebuilds the object with new parameters:

transitions = let 
    kw = (; bounds=(0, 10.0))
    e1 = Param(1.0; kw...)
    e2 = Param(1.0; kw...)
    e3 = Param(1.0; kw...)
    (;
         a=() -> Exponential(e1),
         b=() -> Exponential(e2),
         c=() -> Exponential(e3),
    )
end

In this case its the anonymous function struct that we can rebuild! But it needs the function call step, and its arcane and noone will be able to understand how it works. Compared to:

kw = (; bounds=(0, 10.0))
transitions =  (;
     a=Exponential(Param(1.0; kw...)),
     b=Exponential(Param(1.0; kw...)),
     c=Exponential(Param(1.0; kw...)),
)

(This is a sub-component of a much larger model)

@rafaqz
Copy link
Owner

rafaqz commented Feb 27, 2023

Maybe we can add a Deferred object:

kw = (; bounds=(0, 10.0))
transitions =  (;
     a=Deferred(Exponential, Param(1.0; kw...)),
     b=Deferred(Exponential, Param(1.0; kw...)),
     c=Defered(Exponential, Param(1.0; kw...)),
)

Then we can construct the distribution when we do stripparams, or something. But it would'nt actually work as a distribution in the model without stripparams.

Basically every option here sucks. Probably defining RealParam is the only option. Then we can have ArrayParam too.

@rafaqz
Copy link
Owner

rafaqz commented Feb 13, 2024

We now have RealParam. So closing.

@rafaqz rafaqz closed this as completed Feb 13, 2024
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

No branches or pull requests

4 participants