Distributions.jl icon indicating copy to clipboard operation
Distributions.jl copied to clipboard

add InverseFunctions support

Open aplavin opened this issue 2 years ago • 13 comments

Simple implementation, adds no additional dependencies - InverseFunctions.jl is already a dep of Distributions.jl:

(Distributions) pkg> why InverseFunctions
  DensityInterface → InverseFunctions
  StatsFuns → InverseFunctions

aplavin avatar Feb 25 '23 19:02 aplavin

Codecov Report

Patch coverage: 100.00% and project coverage change: +0.02 :tada:

Comparison is base (ec68da3) 85.82% compared to head (6498137) 85.84%.

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #1685      +/-   ##
==========================================
+ Coverage   85.82%   85.84%   +0.02%     
==========================================
  Files         137      138       +1     
  Lines        8315     8328      +13     
==========================================
+ Hits         7136     7149      +13     
  Misses       1179     1179              
Impacted Files Coverage Δ
ext/DistributionsInverseFunctionsExt.jl 100.00% <100.00%> (ø)

Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here.

:umbrella: View full report at Codecov.
:loudspeaker: Do you have feedback about the report comment? Let us know in this issue.

codecov-commenter avatar Feb 25 '23 20:02 codecov-commenter

Can you make it a weak dependency? Regardless of whether DensityInterface will make it a weak dependency (I assume so), probably we will make DensityInterface a weak dependency in which case InverseFunctions won't be an indirect dependency anymore.

devmotion avatar Feb 25 '23 20:02 devmotion

There's also StatsFuns.

aplavin avatar Feb 25 '23 20:02 aplavin

Ah, I guess StatsFuns can also switch to extension at some point :) Made inverses an extension here.

aplavin avatar Feb 25 '23 21:02 aplavin

There's already a PR for StatsFuns, just requires someone's approval.

devmotion avatar Feb 25 '23 22:02 devmotion

Fixed those issues, and rebased to master

aplavin avatar Mar 09 '23 12:03 aplavin

Bump @devmotion

aplavin avatar Mar 31 '23 15:03 aplavin

Bump...

aplavin avatar Apr 20 '23 12:04 aplavin

Bump @devmotion

aplavin avatar May 05 '23 06:05 aplavin

I'm still not completely convinced by this PR, it's a bit unclear to me when these definitions are actually useful and hence justify the increased code complexity (in particular due to the complications arising from undefined inverses of cdf for p = 0 and p = 1). So I'd put a "pending clear need" label (as used e.g. in ChainRules) here to indicate that the PR needs a convincing use case first.

In its current state, it also does not support Julia < 1.9 which seems unfortunate. Since InverseFunctions is a lightweight interface package, maybe it would be reasonable to make it a regular dependency on Julia < 1.9. EDIT: Due to the dependency on DensityInterface on these Julia versions, it seems InverseFunctions is an indirect dependency anyway.

Any thoughts @ararslan or e.g. @sethaxen?

devmotion avatar May 22 '23 20:05 devmotion

A reason why I have this in my personal utility package, and why propose to add here:

julia> using Distributions, Accessors
julia> obj = (x=1, y=2, prob=1e-3)
julia> dist = truncated(Normal(); lower=0)

# works already:
julia> cquantile(dist, obj.prob)  # how many "sigmas" of significance `prob` corresponds to?
3.290526731491931

# requires this PR:
julia> @set obj.prob |> cquantile(dist, _) += 1  # update obj by adding "1 sigma" to the significance level
(x = 1, y = 2, prob = 1.7824982709635506e-5)

# can wrap in a function, still works automatically:
julia> @accessor nsigma(p) = cquantile(truncated(Normal(); lower=0), p)
julia> nsigma(obj.prob)
3.290526731491931
julia> @set nsigma(obj.prob) += 1
(x = 1, y = 2, prob = 1.7824982709635506e-5)

Most commonly useful with Normal, but sometimes with others as well - eg Chisq.

Also:

undefined inverses of cdf for p = 0 and p = 1

Btw that's an existing bug in Distributions, either in behavior or docs for eg invlogcdf (see my comment above).

does not support Julia < 1.9 which seems unfortunate

I don't care myself about new features supported on older Julias, but if you prefer so - it can easily be added.

aplavin avatar May 23 '23 11:05 aplavin

I agree with @devmotion here.

A reason why I have this in my personal utility package, and why propose to add here

Can you explain how the code you've shown is related to InverseFunctions? Admittedly I don't quite follow it.

ararslan avatar May 25 '23 18:05 ararslan

Can you explain how the code you've shown is related to InverseFunctions?

Accessors uses inverse when it exists, in order to modify values. For example,

julia> x = (a=1,)
julia> y = @set log10(x.a) = 3
(a = 1000.0,)

works because it knows how to invert log10 through InverseFunctions.

This PR adds inverses for distribution-related functions where they make sense. They will work with any package relying on InverseFunctions or Accessors. As yet another example where this is useful, here's a PlutoTables-based interface where one sets the number of sigmas, that are automatically converted to the underlying probability: image Again, this only works automatically because it knows how to invert cquantile.

For more evidence of inverse being desired for distributions functions, see eg https://github.com/JuliaStats/Distributions.jl/issues/707#issuecomment-481814272.

UPD: for now this implementation is added to AccessorsExtra.jl using piracy.

aplavin avatar Jun 15 '23 14:06 aplavin