-
Notifications
You must be signed in to change notification settings - Fork 79
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
Added qmc_quad based method for estimation of the constrained normalization factor #839
base: main
Are you sure you want to change the base?
Added qmc_quad based method for estimation of the constrained normalization factor #839
Conversation
We currently have a (soft) requirement that all priors should implement a rescale method (currently it will just return Even if we make the base class raise an error when you attempt to rescale it will still be possible to use some samplers in that case. It's possible that people will implement their own prior subclasses that don't support the rescale, so I'm not opposed to keeping a fallback. It may make everything easier if we change |
I think that actually the existing method won't work if the new prior doesn't implement |
I have updated the PR quite a bit. The core logic of the integration of the normalization factor is now handled by one of two functions: either MC-Integration based on samples from I have also optimized the For the example I gave above, the qmc-based implementation is now actually slower than the I have also improved the robustness against bugs by checking if the chosen keys are sufficient to compute the constrained, and if the PriorDict is constrained in the first place. |
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.
This is looking good! I just have a few specific comments/questions.
8e73e20
to
98bd7c6
Compare
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.
This is in great shape, just two last documentation based comments.
5cf5cb8
to
0ccf0c9
Compare
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.
Thanks for pushing this through!
Sorry, while taking a last look, I actually caught a bug. The logic enabling marginalization over joint prior keys was not working quite right. I switched to draft so that this is not merged yet. I'll do one last push shortly with a new test case that checks for JointPriors. |
This turned out to be quite the deep dive. I have implemented two test cases with multivariate Gaussian priors, both with covariance matrices equal to identity. One case has three dimensions and one case two dimensions, and both cases are constrained to the unit circle in the two dimensions shared- which means they should integrate to the same normalization constant ( However, using Halton sequences, the integration of the two-dimensional case consistently failed, while the three-dimensional case worked fine. I have also tested the scipy native While trying to figure out what was going wrong, I also noticed this paragraph in the scipy documentation of the qmc module:
The nature of the integration we want to perform is just that: we always integrate a characteristic function that is 0 somewhere and 1 elsewhere. For these reasons, using QMC appears to be a bad idea after all, although I find it concerning that a standard scipy integration scheme yields (quite) wrong results for such a simple case. I will open an Issue upstream when I find time. Anyway, the changes to the termination of the integration still seem worthwhile to me, and the added test cases are certainly nice to have. I also think it is still worthwhile to have two integration methods, one "from_samples" and one "from_rescale"), which can be used to check the consistency of the methods. The new commits adopt these changes. I'm sorry this was only unearthed so late in this pull request. I wonder if this should be moved to an entirely new PR, as this one is related so much to QMC integration. |
…ray and update in-place once all keys are requested. Changed (Conditional)PriorDict.rescale to always return samples in right shape.
fe6fdaf
to
8133a2c
Compare
…putaional overhead and repeateability
…f rescale method is save
8133a2c
to
1b02af6
Compare
I hope to have a final update on this bug: I have found the bug that led to the correlations in the Quasi-Monte Carlo rescaling step. def _integrate_normalization_factor_from_qmc_quad(self, keys, sampling_chunk):
qrng = Halton(len(keys), seed=random.rng)
theta = qrng.random(sampling_chunk).reshape((len(keys), -1)) <-----1.
samples = self.rescale(keys=keys, theta=theta)
samples = np.array({key: samps for key, samps in zip(keys, samples)}).reshape((len(keys, -1)) <----- 2.
factor = np.mean(self.evaluate_constraints(samples))
return factor
I would leave it up to you to decide whether to use Quasi Monte Carlo or "normal" Monte Carlo. In regard to the failing test, conversion_function and so on now require casts to numpy arrays to deal with lists of samples. See #863 where I have started a discussion on the matter. |
This PR implements a new method to estimate the normalization factor for constrained priors.
The changes are two-fold:
scipy.integrate.qmd_quad
, a quasi-Monte Carlo-based integration routine that is expected to yield better results than regular Monte Carlo integration. However, the routine requires a rescaling step from the unit cube rather than direct sampling.I have tested the two implementations with a relatively easy scenario: A 2D uniform prior on the [-1,1] cube, constrained to an inscribed circle with different radii:
The new method is significantly faster for high normalization factors, and the relative errors show a similar spread.
The implementation is marked as a draft because of the requirement of a
rescale
-method of the priors. Thus, it could be nice to keep the old method as a fallback. Also, the relative-error termination criterion could be applied just as well to the old implementation.Related issue: #835