[go: nahoru, domu]

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

Handle NaN gracefully in sampler.MarkovChain #69

Merged
merged 1 commit into from
Feb 21, 2021
Merged

Conversation

dvandyk
Copy link
Collaborator
@dvandyk dvandyk commented Jun 27, 2020

Lieber Fred,

I sometimes encounter NaNs in the log(target) in corners of the parameter space. It would be very helpful, if this modification or something similar to it could end up in a released version of pypmc.

I am also happy to change the name of the keyword argument if you disagree. I had considered e.g. ignore_NaN. Let my know what you think!

Cheers
Danny

@dvandyk
Copy link
Collaborator Author
dvandyk commented Sep 20, 2020

@fredRos Bump :-)

Copy link
Collaborator
@fredRos fredRos left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For me , it was always useful to bail out on NaN because our target density shouldn't return that. You can always wrap your target function to catch NaN, so I am not sure this change is really needed. What is your use case that NaN is an OK value for the target density?

else:
this_run[i_N] = self.current_point
#do not need to update self.current
#self.current = self.current
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove commented code

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

pypmc/sampler/markov_chain.py Show resolved Hide resolved
#self.current = self.current
else:
# accept if rho = 1
if log_rho >=0:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How could I allow this code duplication? All of this should disappear and we can just check for log_rho >= _np.log(self.rng.rand())

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer if we deduplicated code in a separate commit, or not at all. It is very readable in this way. But it's your decision in the end!

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, it comes to mind that your proposed condition changes the number of evaluations of rng. Would make old computation non-repeatable. This is not the case for my changes, since previous computations would simply fail.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I vaguely remember we used to do something different in the two branches but we don't anymore. Yep, it's a different issue. So let's leave as is. And good point about rng evaluation. Easily solved with short-circuit semantics

if log_rho >=0 or log_rho >= _np.log(self.rng.rand()):

pypmc/sampler/markov_chain.py Show resolved Hide resolved
@dvandyk
Copy link
Collaborator Author
dvandyk commented Sep 28, 2020

For me , it was always useful to bail out on NaN because our target density shouldn't return that.

In my case I run into edges of the parameter space in which the target density is not defined. I cannot easily cut these regions out, since the boundaries of the parameter space cannot be determined analytically.

You can always wrap your target function to catch NaN, so I am not sure this change is really needed.

Yes, but then I need to assign a regular value for the target density instead, with a non-zero probability that the Markov chain accepts it nevertheless. In this way, I can reject the invalid point entirely and deterministically.

What is your use case that NaN is an OK value for the target density?

It's a light-cone sum rule calculation in EOS. The likelihood contains observables that are ill defined in a pocket of the (30dim) parameter space.

I'd also like to add that the PMC sample continues on NaN in the target density. Changing this here make both samplers work for the same set of target densities.

@jPhy
Copy link
Collaborator
jPhy commented Sep 28, 2020 via email

@fredRos
Copy link
Collaborator
fredRos commented Sep 28, 2020

Yes, I agree with @jPhy that -inf is fine to set as a valid log probability such that the proposed point is always rejected.

I'd also like to add that the PMC sample continues on NaN in the target density. Changing this here make both samplers work for the same set of target densities.

Good point. Should create a separate issue for that. I don't know if the update equations are correct with nan values.

It's a light-cone sum rule calculation in EOS. The likelihood contains observables that are ill defined in a pocket of the (30dim) parameter space.

I see. In the end, it boils down to a question of convenience. What should happen and who has control. You might just want to continue, and could just return -inf instead of NaN. Another use might want to count the NaNs, or log a message or raise a different kind of exception. So I think the best solution is to leave this in the hands of the user.

The other question is: is it a good idea to raise an exception by default? I think yes because at some point I didn't have this check in there and then the output of the chain was not from the target density because some checks failed as NaN comparisons come out False. Now I know that subtle problem and can make my code robust.
Of course I could output a warning instead of an exception but this might swamp logs. I will sleep over this one night :)

@fredRos
Copy link
Collaborator
fredRos commented Oct 3, 2020

@dvandyk I thought about it. In terms of customer orientation, I'm happy to make that little change if it makes life easier for you. If you or anyone else wants more control over handling NaNs, that should be done in the client code; i.e. outside pypmc

Copy link
Collaborator
@fredRos fredRos left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nearly done, only cosmetics left

#self.current = self.current
else:
# accept if rho = 1
if log_rho >=0:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I vaguely remember we used to do something different in the two branches but we don't anymore. Yep, it's a different issue. So let's leave as is. And good point about rng evaluation. Easily solved with short-circuit semantics

if log_rho >=0 or log_rho >= _np.log(self.rng.rand()):

pypmc/sampler/markov_chain.py Show resolved Hide resolved
@@ -107,6 +107,11 @@ def run(self, N=1):

An int which defines the number of steps to run the chain.

:param continue_on_NaN:

A boolean flag defining the behaviour when encountering an NaN.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
A boolean flag defining the behaviour when encountering an NaN.
A boolean flag defining the behavior when encountering an NaN in the user-supplied target density for a proposed point.

:param continue_on_NaN:

A boolean flag defining the behaviour when encountering an NaN.
Default: False (-> raise ValueError)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Default: False (-> raise ValueError)
Default: ``False`` (-> raise ``ValueError``). If ``True``, reject the proposed point and continue.

# reject if not accepted
else:
if _np.isnan(log_rho):
# raise error if so desired
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

useless comment. Please remove

@fredRos fredRos mentioned this pull request Oct 3, 2020
@fredRos
Copy link
Collaborator
fredRos commented Feb 14, 2021

@dvandyk THere were only small things left on this PR, let's take it over the finish line

@fredRos
Copy link
Collaborator
fredRos commented Feb 20, 2021

@dvandyk Friendly ping

…ampling with Markov chains

Reviewed-by: Frederik Beaujean <beaujean@mpp.mpg.de>
@dvandyk
Copy link
Collaborator Author
dvandyk commented Feb 21, 2021

Sorry for the delay, here it is!

@fredRos
Copy link
Collaborator
fredRos commented Feb 21, 2021

passed tests https://travis-ci.org/github/fredRos/pypmc/builds/759851896, well done!

@fredRos fredRos merged commit 6b4f9ee into pypmc:master Feb 21, 2021
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 this pull request may close these issues.

None yet

3 participants