Cosmo extension for gwpopulation#88
Conversation
…e over cosmological parameters
ColmTalbot
left a comment
There was a problem hiding this comment.
Hi @HuiTong5 thanks for the contribution! I think this is definitely a change worth having in experimental for a while. I think that until there is a numpy/jax/cupy agnostic cosmology calculator, it should probably stay there.
It looks like the pre-commits are failing, you run these locally following these instructions.
We should also add some tests for this, you can probably base it off the tests for the Redshift models and think about some unit tests for the cosmology conversions.
I'll add that if you would prefer to package this up independently you can see a template here, but that isn't my preference in this case.
| if self.astropy_conv == True: | ||
|
|
||
| samples['redshift'] = xp.asarray([z_at_value(cosmo.luminosity_distance, d*u.Mpc,zmax=self.z_max) for d in to_numpy(data['luminosity_distance'])]) | ||
| samples['mass_1'] = data['mass_1']/(1+samples['redshift']) |
There was a problem hiding this comment.
This conditional block seems overly restrictive and unnecessarily complicated, for example, what if someone is only fitting mass_1? This will raise an error in this case.
I think we should take some time to find a neater solution to this. Two possibilities come to mind:
- having a preset list of mass parameters that are checked for, e.g,
["mass_1", "mass_2", "total_mass", "chirp_mass"]. Even better would be to have these be passed as arguments to__init__, something like"redshifted_keys".for key in data: if key in self.redshift_keys: samples[key] = data[key] / (1 + samples["redshift"]) else: samples[key] = data[key]
- looking for a specific flag in keys, i.e., make all redshifted parameters have a
_detectorsuffix,"mass_1_detector"etc. This breaks more strongly fromBilby, but that is already pretty broken by the difference in definition of"mass_1".This can also be combined with the explicitfor key in data: if key.endswith("_detector"): data[key[:-9]] = data[key] / (1 + data["redshift"])
"redshifted_keys"to avoid looping over the entire dictionary and just looking for specific parameters.
Either of these avoid hardcoding any value and in here we just need. Personally, I think the second option is neater as it avoids having the same parameter names meaning different things and potentially also avoides making a new dictionary.
There was a problem hiding this comment.
The second option sounds more consistent with your other comments on gwpopulation_pipe. I may try that.
| interp_dl_to_z = splrep(dl,zs,s=0) | ||
|
|
||
| samples['redshift'] = xp.nan_to_num(xp.asarray(splev(to_numpy(data['luminosity_distance']),interp_dl_to_z,ext=0))) | ||
| samples['mass_1'] = data['mass_1']/(1+samples['redshift']) |
There was a problem hiding this comment.
Everything from here to L157 is just repeating what was done before, regardless of how we wind up dealing with the implementation of the redshifting, only the following (and equivalent above) should be in the if/else.
zs = to_numpy(self.zs)
dl = cosmo.luminosity_distance(to_numpy(zs)).value
interp_dl_to_z = splrep(dl,zs,s=0)
samples['redshift'] = xp.nan_to_num(xp.asarray(splev(to_numpy(data['luminosity_distance']),interp_dl_to_z,ext=0)))| samples = dict() | ||
| if self.astropy_conv == True: | ||
|
|
||
| samples['redshift'] = xp.asarray([z_at_value(cosmo.luminosity_distance, d*u.Mpc,zmax=self.z_max) for d in to_numpy(data['luminosity_distance'])]) |
There was a problem hiding this comment.
z_at_value is now automatically vectorized, so you can remove this list comprehension and just do
| samples['redshift'] = xp.asarray([z_at_value(cosmo.luminosity_distance, d*u.Mpc,zmax=self.z_max) for d in to_numpy(data['luminosity_distance'])]) | |
| samples['redshift'] = xp.asarray(z_at_value(cosmo.luminosity_distance, to_numpy(data['luminosity_distance']) * u.Mpc, zmax=self.z_max)) |
We can also possibly avoid repeated transfer between array types by setting
data["luminosity_distance"] = to_numpy(data["luminosity_distance"]) somewhere.There was a problem hiding this comment.
When fitting cosmology I think this will cause a slowdown when transferring computations between astropy and gwpopulation. I think this class should include a function like (assuming flatLambdaCDM):
def comoving_distance(z):
E_z = xp.sqrt(Om * (1+self.zs)**3 + (1-Om))
return c/H0 * cumtrapz(Ez, 0, z)
def luminosity_distance(z):
return (1+z) * self.comoving_distance(z)
This way these transformations can be handled all natively in the CPU/GPU agnostic manner. To be generic there could be an option to have these computed with astropy.
There was a problem hiding this comment.
Indeed, not only this conversion, but also some other calculations by astropy may introduce a significant slowdown. I've implemented some interpolation to replace astropy.cosmo call somewhere else. I will have a more comprehensive thinking of possible approximation of all the astropy calculations in this PR.
|
|
||
| return samples | ||
|
|
||
| def dL_by_dz(self, z, dl, **parameters): |
There was a problem hiding this comment.
dl should be included in the docstring.
Also, please use full variable names, redshift, luminosity_distance for readability. The method name is probably fine, but luminosity distance_to_redshift_jacobian would be better.
There was a problem hiding this comment.
I've just noticed that this is taking both the redshift and distance when there should be a deterministic mapping between them. I worry that this is inviting user error.
What if you move the logic to convert from distance to redshift to inside this method (or another method that calls this) and then return both redshift and dl_by_dz (lowercase for variable names)?
There was a problem hiding this comment.
docstring and variables names fixed in 597fc2c.
There was a problem hiding this comment.
updated in 78835d where I combine the conversion of samples from detector frame to source frame and the calculation of Jabobian term in one func which only need detector frame samples as input.
| def dL_by_dz(self, z, dl, **parameters): | ||
|
|
||
| """ | ||
| Calculates the detector frame to source frame Jacobian d_det/d_sour for dL and z |
There was a problem hiding this comment.
Again, we don't need to use abbreviations here, a longer docstring will be helpful. Ideally, with an equation, there are docs here that explain some of the subtleties of getting this to render correctly in the online docs.
Co-authored-by: Colm Talbot <talbotcolm@gmail.com>
Codecov ReportAttention: Patch coverage is
❗ Your organization needs to install the Codecov GitHub app to enable full functionality. Additional details and impacted files@@ Coverage Diff @@
## main #88 +/- ##
==========================================
- Coverage 94.74% 94.18% -0.56%
==========================================
Files 11 11
Lines 799 808 +9
==========================================
+ Hits 757 761 +4
- Misses 42 47 +5
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. |
|
@afarah18 has some code based on this paper that we might be able to adapt to handle the cosmological conversions. It isn't as flexible as astropy, it currently just handles variable |
|
Thanks for sharing! I will take a look at the paper and Amenda's code when I have time. I have pushed some updates based on our previous discussion. I haven't done any actual calculations/testing, so there may be a lot of bugs in it. I'll do some testing soon. But I think the overall framework has improved to some extent. |
|
The new changes generally look good! Amanda and I are working on packaging up the other cosmology implementation and I'll probably match the astropy API closely so it should be a drop in replacement that I can do later. |
…anyone would like to turn it on given the enormous time cost. Can add astropy_conv back later with other cosmology implementation. Aslo, minor fix for vt surveyed_hypervolume
|
I made some changes to the way the modules that need the backend to change are specified, the short version is that you should now add the new module to the I also pushed a version of the cosmology implementation that @afarah18 and I put together in https://github.com/ColmTalbot/wcosmo. It will be a fairly simple drop-in change once this has been merged and I added some similar code to what you have in this PR so that I can use it as a free-standing version in the meantime. |
|
The new repo for cosmology implementation looks great! I noticed there content in models.py in the new repo is almost the same as the new module cosmo_models.py in this PR. What's the plan for this PR? Should I call the functions in the new repo to avoid repetitive code? |
That repo is essentially a holding place so that I can use the code while we get the changes into gwpopulation. I should have added an attribution to you in there as it was strongly influenced by this PR, sorry! I'll do that today. My plan is:
The latter stage would involve removing |
|
I had a quick test on the time cost for single likelihood evaluation on the head node of pcdev3. The cosmo one costed roughly 0.53s and non cosmo one costed roughly 0.15s. |
Nice! Which backend is that using?
|
|
I was using cupy |
I don't think I plan to have any other changes if you are happy with this. What label should I have? Should I have DEV or I just need to simply remove the "Draft". |
|
Great, I removed |
Try to add cosmology extension for gwpopulation which allows joint cosmology & population analysis, i.e., spectral siren.
The main changes are:
bilby.hyper.model.Modelfor cosmology related calculation