Open eas342 opened 2 years ago
I may have found a way to do this using Potential
in pymc3 with the following lines inside the model.
import pymc3 as pm
b_map = starry.Map(ydeg=self.degree)
# Add another constraint that the map should be physical
map_evaluate = b_map.render(projection='rect',res=100)
## number of points that are less than zero
num_bad = pm.math.sum(pm.math.lt(map_evaluate,0))
## check if there are any "bad" points less than zero
badmap_check = pm.math.gt(num_bad, 0)
## Set log probability to negative infinity if there are bad points. Otherwise set to 0.
switch = pm.math.switch(badmap_check,-np.inf,0)
## Assign a potential to avoid these maps
nonneg_map = pm.Potential('nonneg_map', switch)
@eas342 Sorry for my slow reply. In recent versions of starry
there's a minimize method that returns the value of the minimum intensity across the map. You could simply check if that's less than zero and that would reproduce the behavior of is_physical
in previous versions. But that's quite inefficient. A better way, similar to what you suggested, can sometimes be to actually sample in the pixel intensities themselves. Then you can specify a pm.Uniform
prior on those pixels directly. This is likely faster and easier for NUTS to sample than the example you provided. To compute the flux, simply apply the spherical harmonic transform matrix A
to vector of pixel intensities as follows:
_, _, _, A, _, _ = map.get_pixel_transforms(oversample=2)
npix = A.shape[1]
p = pm.Uniform("p", lower=0.0, upper=1.0, shape=(npix,))
y = tt.dot(A, p)
Examples of this can be found here: https://starry.readthedocs.io/en/latest/notebooks/PixelSampling/
Let me know if this helps!
Ah, thanks for the tutorial on pixel sampling! Should have seen that earlier.
How about enforcing that limb darkening is physical? I attempted a similar method as above with the potential:
## make sure that the limb darkening law is physical
is_physical = pm.math.eq(star_map.limbdark_is_physical(), 1)
switch = pm.math.switch(is_physical,-np.inf,0)
# Assign a potential to avoid these maps
physical_LD = pm.Potential('physical_ld', switch)
However, I get a NonImplementedError with theano version 1.1.2.
The conceptual problem with this approach is that PyMC needs to work on a differentiable logp function, with support everywhere: (-inf, inf) in all the parameters. This prior doesn't have that behavior since it introduces a part of parameter space with zero support. I'm not entirely sure where the theano issue is coming from, but it's probably not high priority to track down. I know that @rodluger and I have chatted about ideas for generally physical limb darkening parameterizations, but I don't remember if he came up with a robust proposal!
Thank you @dfm. I found that this switch function worked decently for eclipse mapping to constrain the spherical harmonic coefficients to give a nonnegative map and got similar (and in some cases better) results as pixel sampling. Maybe it was violating some assumptions of PyMC and I just got lucky.
Perhaps someone has worked out higher dimensional sampling in physical space like the Kipping 2013 re-parameterization. In the conclusions it says "when N parameters are mutually constrained by N + 1 non-parallel boundary conditions leading to tetrahedral sampling and hyper-tetrahedral sampling."
Working out that reparameterization would definitely be the best way to go about this, if possible. I haven't looked into this, but it would be cool to see!
And yeah, unfortunately those kinds of -inf log priors are going to break all sorts of things giving incorrect results and poor performance unless you get very lucky!
Should the solutions described above still work when the map coefficients and amplitude are marginalised, like in https://starry.readthedocs.io/en/latest/notebooks/EclipsingBinary_FullSolution/ ?
I have applied a positive pixel constraint to the example in https://starry.readthedocs.io/en/latest/notebooks/EclipsingBinary_PyMC3/ (following the example in https://starry.readthedocs.io/en/latest/notebooks/PixelSampling/), which is doable because sys.flux is explicitly included in the pymc3 model in that case.
However, for the case in https://starry.readthedocs.io/en/latest/notebooks/EclipsingBinary_FullSolution/ the flux isn't explicitly accessible and so you can't plug in a prior on the pixels in the same way as before.
Is there another way of applying the positive map prior in this case? Thanks for any help!
Is your feature request related to a problem? Please describe. When fitting lightcurves to spherical harmonics in some experiments, significant fractions of the posterior maps are unphysical with minimum
fluxesintensities that are negative.Describe the solution you'd like It would be nice if there is a way to put in a prior that a map be positive. Is there a way to do this already? It looks like it could be done with pymc3 priors using Luger et al. 2018 equation 40 for ell=1 but maybe not so easily for higher orders. It looks like earlier
starry
versions had.is_physical()
but I can't find that in newer starry versions.Describe alternatives you've considered Perhaps one can look at the samples after the fact to see if they are physical with rendering on a course grid. Or go back to earlier versions of starry that use
.is_physical()
inemcee
with the prior function.Additional context