Repository navigation
feat(cosmo): angular_diameter_distance(z1,z2) - #18807
Conversation
|
Thank you for your contribution to Astropy! 🌌 This checklist is meant to remind the package maintainers who will review this pull request of some common things to look for.
|
|
👋 Thank you for your draft pull request! Do you know that you can use |
angular_diameter_distance(z1,z2)
271f609 to
b134e4c
Compare
a4ca6a0 to
55fb48a
Compare
|
@mhvk @neutrinoceros, friendly ping. hopefully an easy review :) |
a5442fd to
59d446b
Compare
|
Rebased while fixing a typo. |
mhvk
left a comment
There was a problem hiding this comment.
Like getting these two together, but see suggestion about signature in-line. This would seem to be the time to have it make sense...
| ---------- | ||
| z : Quantity-like ['redshift'], array-like | ||
| Input redshift. | ||
| z1, z2 : Quantity-like ['redshift'], array-like |
There was a problem hiding this comment.
Are you sure you want to arange (pun intented) things in this order? It reminds be a bit of range and np.arange(), where one argument is taken to mean 0, arg0, while two arguments are used as arg0, arg1. @neutrinoceros recently spent much more time than he would have liked getting that right for np.arange(..., like=u.Quantity)...
Might it be better to have something like angular_diameter_distance(z, /, *, z_obs=0.0)? It means more adaptation for those coming from the _z1z2 variant, but would seem much clearer. (Clear enough, in effect, that you could remove the warning for z2 < z1.)
There was a problem hiding this comment.
I definitely don't want z_obs as a keyword argument as these aren't compatible with most jit operations, which I very much want to be able support as we adopt the array-api.
IMO func(z) and func(z1, z2) are close enough to how these functions are written mathematically (e.g. z_obs, that's just where the calculation stops. In a ratio of distances, for example, $d_A(z1,z2)/$d_A(z_obs,z2)$ comparisons are being made relative to an observer.
IMO these should be positional-only arguments because it's the order that matters, not the variable name.
There was a problem hiding this comment.
It doesn't have to be keyword-only, but effectively having default positional arguments before anything else just does not feel right (for once, typing shows this, as it requires the overload). How about *_distance(z, /, z_ref=0.0) as an alternative? (The mathematical precedent is just as bad; z1 and z2 are just poor choices of names, at least use z_lens, z_src or something like it.)
There was a problem hiding this comment.
but effectively having default positional arguments before anything else just does not feel right
Maybe I'm not understanding. There isn't a default positional arguments before anything else. It proceeds z1.
for once, typing shows this, as it requires the overload
It actually doesn't.
Without the overloads it's:
def angular_diameter_distance(self, z: _InputT, z2: _InputT | None = None, /) -> u.Quantity: ...mypy interprets this as:
- The function always accepts one mandatory argument
z: _InputT. - Optionally accepts a second positional argument
z2: _InputT | None. - The return type is always u.Quantity.
This type-checks fine. The caller can do:
cosmo.angular_diameter_distance(0.5)
cosmo.angular_diameter_distance(0.5, 1.0)What the @overload does is it says you shouldn't do
cosmo.angular_diameter_distance(0.5, None)
# Mypy error: None not allowed for 2-argument overloadNow that I'm looking into this, maybe I should remove the @overload! None isn't pretty — it's better to omit the argument — but it is valid.
z1 and z2 are just poor choices of names
But they are positional-only parameters. Their names are meaningless. Which appears to be an advantage, since z_lens and z_src are use-case specific. z_ref is fine, but it's not that semantically meaningful, less so than it's argument position.
Edit: I removed the overloads!
There was a problem hiding this comment.
these aren't compatible with most jit operations
first time I'm hearing about this. Is this specific to a particular JIT compiler, or is it general ? if not, what JIT do you have in mind ?
About making the signature tighter, have you considered having just one positional-only argument, that would accept either a single Quantity or a pair of quantities ? I find this kind of pattern pretty intuitive to use, and it got so much easier to implement and maintain with structural pattern matching (match/case) that I think they are an attractive idea now.
There was a problem hiding this comment.
Hum I'll admit I didn't think of that, but it does seem like a 2 element 1D array doesn't actually match the structure you'd write for a 2-element tuple
>>> a = np.array((1, 2))
>>> match a:
... case (_, _):
... print('pair')
... case _:
... print('default')
...
defaultis this a peculiarity in numpy, or would that be general to all Array API implementers ? if the latter, it looks to me that my idea still works as designed.
There was a problem hiding this comment.
(btw, starting with keyword-only arguments and maybe later allow them to be passed positionally too would be backwards compatible, so we don't need to allow positional use from the get go to get the benefit of JIT compilation when we're ready to support it)
There was a problem hiding this comment.
but it does seem like a 2 element 1D array doesn't actually match the structure you'd write for a 2-element tuple
But we want to support tuples as valid input as they are ArrayLike. So while 2-elt arrays may be distinguishable (though I have my doubts in general because of e.g. batch axes), the tuple behaviour makes this a non-starter.
There was a problem hiding this comment.
starting with keyword-only arguments and maybe later allow them to be passed positionally too would be backwards compatible.
I'm just not sure how this is an improvement. I have yet to see a semantically meaningful variable name I actually like, while the argument position follows standard mathematical convention and thus clearly conveys its meaning. Any confusion should be alleviated by the docstring (if it isn't then we should improve the docstrings).
And keyword arguments are incompatible with mapping ops ("Oh, thanks for catching that. I meant mapping operators" — #18807 (comment)) so IMO it's best to be forward-thinking about this.
A Positional-Or-Keyword argument (so moving the "/") would be fine by me, but only if we had a good semantically meaningful variable name. @mhvk's suggestion of z_ref is definitely the best I've heard so far and IMO it's only meh (is it a reference redshift or the redshift of reference / observer or observee?).
The advantage of starting with Positional-Only is that it's backwards compatible with making an argument Positional-Or-Keyword. So IMO we should start with that and if ever there's a good variable name think about renaming z2 (it's positional and thus has no true name) and making it Positional-Or-Keyword.
There was a problem hiding this comment.
@mhvk if you're interested where the overloads originally came from, it's https://cosmology.readthedocs.io/projects/api/latest/api/reference/cosmology.api.HasAngularDiameterDistance.angular_diameter_distance.html#cosmology.api.HasAngularDiameterDistance.angular_diameter_distance,
where we customized Sphinx to read the overloads to build more informative docs.
In that API document we've thought through improvements to the Astropy Cosmology API and I've been slowly working to transition us towards these improvements.
59d446b to
a01dbbe
Compare
| @@ -809,10 +809,8 @@ def _integral_age(self, z: u.Quantity | ArrayLike, /) -> FArray: | |||
|
|
|||
| @overload | |||
| def comoving_distance(self, z: _InputT, /) -> u.Quantity: ... | |||
There was a problem hiding this comment.
I can remove the overload in this or a followup PR.
| return self.comoving_transverse_distance(z) / (z + 1.0) | ||
| z1, z2 = (0.0, z) if z2 is None else (z, z2) | ||
| z1, z2 = aszarr(z1), aszarr(z2) | ||
| if np.any(z2 < z1): |
There was a problem hiding this comment.
I'm thinking about removing this warning in the future. But since this function replaces the deprecated angular_diameter_distance_z1z2 and that had the warning I think it's good to keep for now.
There was a problem hiding this comment.
curious, what is the actual issue with z1<z2 ?
There was a problem hiding this comment.
z2<z1 makes the distance go negative.
|
This PR is part of the build-up towards DistanceMeasures traits and then matter-only pedagogical cosmology |
|
@mhvk @neutrinoceros how do we move forward with this? |
|
@neutrinoceros I want to return to this before v8.0 feature freeze. |
|
Hi humans 👋 - this pull request hasn't had any new commits for approximately 4 months. I plan to close this in 30 days if the pull request doesn't have any new commits by then. In lieu of a stalled pull request, please consider closing this and open an issue instead if a reminder is needed to revisit in the future. Maintainers may also choose to add keep-open label to keep this PR open but it is discouraged unless absolutely necessary. If this PR still needs to be reviewed, as an author, you can rebase it to reset the clock. If you believe I commented on this pull request incorrectly, please report this here. |
|
Ping @neutrinoceros for a review, following our chat the other day. |
Signed-off-by: nstarman <[email protected]>
Signed-off-by: nstarman <[email protected]>
a01dbbe to
1af4c5d
Compare
|
@neutrinoceros. Ping for a review, Thanks! |
|
Sorry I got entrenched with my own gigantic PR (#19575). I'm doing reviews all day today ! |
neutrinoceros
left a comment
There was a problem hiding this comment.
I took a good 20min to make sure I understood what I was signing off and I want to lift my previous concerns (bike shed ?) with the signature you elected. As you explaining, allowing these arguments to be passed by name only makes sense if we have good names for them, which so far we don't, so, I'm planning to merge this in a couple days if no objection is raised in the mean time.
| z1, z2 = (0.0, z) if z2 is None else (z, z2) | ||
| z1, z2 = aszarr(z1), aszarr(z2) |
There was a problem hiding this comment.
purely stylistic nit, feel free to ignore, though IMO this is easier to understand at a glance
| z1, z2 = (0.0, z) if z2 is None else (z, z2) | |
| z1, z2 = aszarr(z1), aszarr(z2) | |
| match z2: | |
| case None: | |
| z1 = aszarr(0.0) | |
| z2 = aszarr(z) | |
| case _: | |
| z1 = aszarr(z) |
There was a problem hiding this comment.
Given that this will appear in a few methods, I think in a followup PR these lines should be shunted off to a helper function. I'd want to do timing tests then. Also, I think this helper function + aszarr are good targets for C code.
Given that, I'm going to push off rewrites here.
| return self.comoving_transverse_distance(z) / (z + 1.0) | ||
| z1, z2 = (0.0, z) if z2 is None else (z, z2) | ||
| z1, z2 = aszarr(z1), aszarr(z2) | ||
| if np.any(z2 < z1): |
There was a problem hiding this comment.
curious, what is the actual issue with z1<z2 ?
|
Thanks @neutrinoceros for the review! |
|
I opened #19613 as future follow-up issue. Thanks, all! |
This PR upgrades
angular_diameter_distanceto be a two-argument form, deprecating the now unnecessaryangular_diameter_distance_z1z2. Previously this was done forcomoving_distance,astropy/astropy/cosmology/_src/flrw/base.py
Lines 857 to 863 in 19a688f
Requires #18800Complete!