-
-
Notifications
You must be signed in to change notification settings - Fork 2k
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
Update log1mexp
and remove redundant local reimplementations in the library
#4394
Conversation
…onential.logcdf()`
What about people using double precision? I bet this constant ought to have about twice as many digits... |
Pardon my ignorance, how many digits would that be :p My ipython prints: Edit: I think I misunderstood your question, but anyway adding more digits seems like a no-brainer. |
Codecov Report
@@ Coverage Diff @@
## master #4394 +/- ##
==========================================
+ Coverage 88.04% 88.09% +0.05%
==========================================
Files 88 88
Lines 14482 14538 +56
==========================================
+ Hits 12750 12807 +57
+ Misses 1732 1731 -1
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changes look good, thanks @ricardoV94 👌
Regarding this:
I would further suggest that the log1mexp method be changed to expect negative values [...] The least nice part is that it would probably require a UserWarning about the change when the function is called directly by the user. What do you think?
This change is not implemented in the PR yet, right? Or I didn't see it 🤔
If we do make this change though, a UserWarning
is indeed necessary
No it's not, I wanted to ask other's opinion first. |
@ricardoV94 Let's go with your preference. |
Cool. I will push the changes to this PR |
TODO:
About the last point, what is the best way to avoid UserWarnings from internal API calls to the new What do you think? |
log1mexp
and redundant reimplementation in Exponential.logcdf()
log1mexp
and remove redundant local reimplementations in the library
@ricardoV94 This fixes a bug which we don't need to warn about, and adds support for negative inputs which was previously not allowed, which we also don't need to warn about. Are my assumptions incorrect? |
Yes and no. The PR fixes a small inefficiency in the original method due to using a slightly off value In addition to this, I suggested changing the sign of the expected input (purely on the ground that is is more intuitive). Right now it expects positive numbers which are explicitly negated, and I suggested expecting negative values instead and not negating them anymore. This would break things retroactively: it would no longer work with positive inputs (now it does not work with negative inputs). The other option is to use the TFP approach of negating the absolute. I find it less appealing but it has the advantage of being backwards compatible. However, there is nothing fundamentally wrong with the current approach and the documentation is correct. |
@ricardoV94 Thanks for explaining, I did not understand the full scope before. In that case I think we should stick with the previous one in the interest of backwards compat. |
Ok that's fine. I will just remove the other duplicated implementation that I found and this should be ready to merge. |
Remove TODO comments.
82eecaf
to
fea4a87
Compare
Ready for review / merging :) I think this one does not warrant a release-note. Do you agree? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking good, thanks @ricardoV94 ! No need for a release note indeed I think, as it's really internal maintenance. I'll merge once tests pass 😉
Thanks @ricardoV94! |
The cutoff in
log1mexp
(and its numpy counterpart) is supposed to belog(2) = 0.693
. There was probably a small typo when writing the function as the cutoff was set to0.683
. Pinging @aseyboldtOriginal source of this algorithm: https://cran.r-project.org/web/packages/Rmpfr/vignettes/log1mexp-note.pdf
Example R implementation: https://github.com/cran/Rmpfr/blob/7f15be8d124a7517719012107c132b5d828284f9/inst/doc/log1mexp-note.R#L96
TensorFlow implementation: https://github.com/tensorflow/probability/blob/v0.11.1/tensorflow_probability/python/math/generic.py#L495-L519
Haskell implementation: https://gitlab.haskell.org/ghc/ghc/-/blob/master/libraries/base/GHC/Float.hs#L159-161
I also removed a redundant re-implementation of this function in the
logcdf
method of theExponential
andWeibull
distribution.I would further suggest that the
log1mexp
method be changed to expect negative values (such as logarithms of probabilities) instead of positive values. This is what the Haskell implementation does directly and what the TensorFlow does less directly by first taking the absolute of the input and then negating it. The function would change fromlog(1 - exp(-x))
tolog(1 - exp(x))
(as in Haskell). This is equivalent tolog(1 - exp(-|x|))
(as in TFP), but I like the formula with the absolute less because it seems to just hide mistakes such as askinglog(1 - exp(log(1.1))
. I don't see why one would ever want values larger than 1 after exponentiation to be negated before exponentiation.I have actually implemented this change and it did not seem to have large ramifications. There seem to be only 2 places (3 after #4387) in the library where the function is used directly, and the only thing needed is to negate the input (e.g., the function
logdiffexp
needs to be changed froma + log1mexp(a - b)
toa + log1mexp(b - a)
). I also checked that current unittests would detect all the spotted sections if they were not corrected.The least nice part is that it would probably require a
UserWarning
about the change when the function is called directly by the user. What do you think?Update: This change was rejected to avoid backwards incompatibility