Skip to content

feat(cosmo): angular_diameter_distance(z1,z2) - #18807

Merged
nstarman merged 2 commits into
astropy:mainfrom
nstarman:cosmo/api/angular_diameter_distance
Apr 27, 2026
Merged

nstarman merged 2 commits into
astropy:mainfrom
nstarman:cosmo/api/angular_diameter_distance

Conversation

@nstarman

@nstarman nstarman commented Oct 30, 2025 •

Copy link
Copy Markdown
Member

This PR upgrades angular_diameter_distance to be a two-argument form, deprecating the now unnecessary angular_diameter_distance_z1z2. Previously this was done for comoving_distance,

@overload
def comoving_distance(self, z: _InputT, /) -> u.Quantity: ...
@overload
def comoving_distance(self, z: _InputT, z2: _InputT, /) -> u.Quantity: ...
def comoving_distance(self, z: _InputT, z2: _InputT | None = None, /) -> u.Quantity:

Requires #18800 Complete!

  • By checking this box, the PR author has requested that maintainers do NOT use the "Squash and Merge" button. Maintainers should respect this when possible; however, the final decision is at the discretion of the maintainer that merges the PR.

@github-actions

Copy link
Copy Markdown
Contributor

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.

  • Do the proposed changes actually accomplish desired goals?
  • Do the proposed changes follow the Astropy coding guidelines?
  • Are tests added/updated as required? If so, do they follow the Astropy testing guidelines?
  • Are docs added/updated as required? If so, do they follow the Astropy documentation guidelines?
  • Is rebase and/or squash necessary? If so, please provide the author with appropriate instructions. Also see instructions for rebase and squash.
  • Did the CI pass? If no, are the failures related? If you need to run daily and weekly cron jobs as part of the PR, please apply the "Extra CI" label. Codestyle issues can be fixed by the bot.
  • Is a change log needed? If yes, did the change log check pass? If no, add the "no-changelog-entry-needed" label. If this is a manual backport, use the "skip-changelog-checks" label unless special changelog handling is necessary.
  • Is this a big PR that makes a "What's new?" entry worthwhile and if so, is (1) a "what's new" entry included in this PR and (2) the "whatsnew-needed" label applied?
  • At the time of adding the milestone, if the milestone set requires a backport to release branch(es), apply the appropriate "backport-X.Y.x" label(s) before merge.

@github-actions

Copy link
Copy Markdown
Contributor

👋 Thank you for your draft pull request! Do you know that you can use [ci skip] or [skip ci] in your commit messages to skip running continuous integration tests until you are ready?

@nstarman nstarman changed the title Cosmo/api/angular diameter distance feat(cosmo): angular_diameter_distance(z1,z2) Oct 30, 2025
@nstarman
nstarman force-pushed the cosmo/api/angular_diameter_distance branch 5 times, most recently from 271f609 to b134e4c Compare October 30, 2025 18:33
@nstarman
nstarman force-pushed the cosmo/api/angular_diameter_distance branch 2 times, most recently from a4ca6a0 to 55fb48a Compare November 16, 2025 18:02
@nstarman
nstarman marked this pull request as ready for review November 16, 2025 18:07
@nstarman
nstarman requested a review from a team as a code owner November 16, 2025 18:07
@nstarman nstarman moved this from Triage to In Progress in Cosmology, the Expansion Nov 18, 2025
@nstarman

Copy link
Copy Markdown
Member Author

@mhvk @neutrinoceros, friendly ping. hopefully an easy review :)

@nstarman
nstarman force-pushed the cosmo/api/angular_diameter_distance branch 2 times, most recently from a5442fd to 59d446b Compare November 18, 2025 19:05
@nstarman

Copy link
Copy Markdown
Member Author

Rebased while fixing a typo.

@mhvk mhvk left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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.)

@nstarman nstarman Nov 18, 2025 •

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

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. $d_A(z)$ and $d_A(z1,z2)$) that it's not confusing. Also, the observer doesn't have to be at 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.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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.)

@nstarman nstarman Nov 19, 2025 •

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

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 overload

Now 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!

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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')
...
default

is 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.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

(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)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

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.

@nstarman nstarman Nov 20, 2025 •

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

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.

@nstarman nstarman Nov 20, 2025 •

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

@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.

@nstarman
nstarman force-pushed the cosmo/api/angular_diameter_distance branch from 59d446b to a01dbbe Compare November 19, 2025 03:56
@@ -809,10 +809,8 @@ def _integral_age(self, z: u.Quantity | ArrayLike, /) -> FArray:

@overload
def comoving_distance(self, z: _InputT, /) -> u.Quantity: ...

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I can remove the overload in this or a followup PR.

@nstarman
nstarman requested a review from mhvk November 19, 2025 18:19
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):

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

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.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

curious, what is the actual issue with z1<z2 ?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

z2<z1 makes the distance go negative.

@nstarman

Copy link
Copy Markdown
Member Author

This PR is part of the build-up towards DistanceMeasures traits and then matter-only pedagogical cosmology

@nstarman

nstarman commented Dec 5, 2025 •

Copy link
Copy Markdown
Member Author

@mhvk @neutrinoceros how do we move forward with this?
#18807 (comment) indicates we need 2 arguments
#18807 (comment) walks through the problems with kwargs and how pos-only is more future-and-backwards compatible.

@nstarman

nstarman commented Apr 11, 2026 •

Copy link
Copy Markdown
Member Author

@neutrinoceros I want to return to this before v8.0 feature freeze.
A 2-elt tuple breaks array-like input and positional arguments matches the mathematical semantics with which cosmologists are familiar.

@github-actions github-actions Bot added the Close? Tell stale bot that this issue/PR is stale label Apr 18, 2026
@github-actions

Copy link
Copy Markdown
Contributor

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.

@nstarman nstarman added keep-open and removed Close? Tell stale bot that this issue/PR is stale labels Apr 19, 2026
@nstarman

Copy link
Copy Markdown
Member Author

Ping @neutrinoceros for a review, following our chat the other day.

Copilot AI review requested due to automatic review settings April 22, 2026 16:29
@nstarman
nstarman force-pushed the cosmo/api/angular_diameter_distance branch from a01dbbe to 1af4c5d Compare April 22, 2026 16:29

This comment was marked as resolved.

@nstarman

Copy link
Copy Markdown
Member Author

@neutrinoceros. Ping for a review, Thanks!

@neutrinoceros

Copy link
Copy Markdown
Contributor

Sorry I got entrenched with my own gigantic PR (#19575). I'm doing reviews all day today !

@neutrinoceros neutrinoceros left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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.

Comment on lines +1004 to +1005
z1, z2 = (0.0, z) if z2 is None else (z, z2)
z1, z2 = aszarr(z1), aszarr(z2)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

purely stylistic nit, feel free to ignore, though IMO this is easier to understand at a glance

Suggested change
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)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

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):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

curious, what is the actual issue with z1<z2 ?

@github-project-automation github-project-automation Bot moved this from In Progress to Reviewer approved in Cosmology, the Expansion Apr 27, 2026
neutrinoceros

This comment was marked as off-topic.

@nstarman

Copy link
Copy Markdown
Member Author

Thanks @neutrinoceros for the review!

@nstarman
nstarman merged commit ec2ef78 into astropy:main Apr 27, 2026
41 of 42 checks passed
@github-project-automation github-project-automation Bot moved this from Reviewer approved to Done in Cosmology, the Expansion Apr 27, 2026
@nstarman
nstarman deleted the cosmo/api/angular_diameter_distance branch April 27, 2026 13:54
@pllim

pllim commented Apr 29, 2026

Copy link
Copy Markdown
Member

I opened #19613 as future follow-up issue. Thanks, all!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

5 participants