Skip to content

Wrap erfa functions with ufuncs and gufuncs - #7502

Merged
mhvk merged 17 commits into
astropy:masterfrom
mhvk:erfa-wrapped-by-gufuncs
Jun 22, 2018
Merged

mhvk merged 17 commits into
astropy:masterfrom
mhvk:erfa-wrapped-by-gufuncs

Conversation

@mhvk

@mhvk mhvk commented May 25, 2018 •

Copy link
Copy Markdown
Contributor

This is an alternative to #7484, with the main difference being that for matrices and vectors now the more logical "generalized ufuncs" are used, which take care of the trailing dimensions of (3,) and (3,3), respectively. EDIT: improved performance is just like for #7484.

In this version, I decided on purpose not to similarly treat pv (trailing dimensions of (2, 3)) and integer decompositions of years, hours, and degrees (ints with trailing dimension of (4,)). I think those really are more logically treated as structured dtype, since their parts have different units - it is really bad to have to remember that pv[..., 1, :] is the velocity part, with units that can be au/day or m/s depending on context - this is what we have Quantity for! But it will be very difficult to have different units for different parts of an array; in contrast, it should be quite trivial to have different units for different parts of a structured dtype. And seeing pv['v'] it is much more obvious what one is looking at.

In the end, I found that surprisingly few changes were required to the code to make this work, and I feel all add to the clarity. Hence, I prefer this PR over #7484.

p.s. The only thing I like a little less is the work-around I need to do for those functions which produce vectors or matrices, but do not take them. This is because currently gufunc signatures cannot indicate that some dimension should be a fixed number (being addressed... EDIT: numpy/numpy#11175). This means I have to generate an empty output with the right dimensions. But this is unhandy for Quantity, so right now I work around that by making the output of a special class. Not too elegant, but works.

@astropy-bot

astropy-bot Bot commented May 25, 2018 •

Copy link
Copy Markdown

Hi there @mhvk 👋 - thanks for the pull request! I'm just a friendly 🤖 that checks for issues related to the changelog and making sure that this pull request is milestoned and labeled correctly. This is mainly intended for the maintainers, so if you are not a maintainer you can ignore this, and a maintainer will let you know if any action is required on your part 😃.

Everything looks good from my point of view! 👍

If there are any issues with this message, please report them here.

@mhvk mhvk added this to the v3.1 milestone May 30, 2018
@mhvk mhvk changed the title Wrap erfa functions with ufuncs, version 2 Wrap erfa functions with ufuncs and gufuncs May 30, 2018
@mhvk

mhvk commented May 30, 2018 •

Copy link
Copy Markdown
Contributor Author

@mhvk

mhvk commented May 30, 2018

Copy link
Copy Markdown
Contributor Author

Sorry, that should be https://gist.github.com/mhvk/a3c17f0db77b91151d50a0d278621d97, as I should at least include one function that needs the fix for when there are only output arguments with a core dimension of 3 (fk5hip).

@mhvk

mhvk commented May 30, 2018

Copy link
Copy Markdown
Contributor Author

More general comment on gists: note how for most functions, the python wrapper does essentially nothing but combine status codes. The only ones where more is done is for signatures where the output has some core dimension (e.g., is a vector) and the input is not, since in that case the gufunc needs an output argument to know what the dimension should be.

It is actually very annoying that one cannot give a fixed size, but this is being fixed in numpy/numpy#11175

@mhvk
mhvk requested review from astrofrog and jwoillez May 30, 2018 21:04
@mhvk
mhvk force-pushed the erfa-wrapped-by-gufuncs branch from c9a55c0 to df37662 Compare May 30, 2018 21:08
mhvk added 14 commits May 31, 2018 11:34
Using ufuncs instead of C routines and a custom iterator has the
advantage that casting to arrays with the right dtype and
iteration is taken care of automatically, in numpy C code.
For small arrays, this means substantially faster execution.

Another advantage is that classes can now override the ufuncs
using __array_ufunc__ - this is important both for ensuring
Quantity input is disallowed (or at least treated correctly
rather than just cast to array), and that a new Distribution
class can work with the erfa functions.
Before, contents of astrom and ldbody were kept as well.
Also simplify the viewing back.
Rest should be kept as structured arrays.
@mhvk
mhvk force-pushed the erfa-wrapped-by-gufuncs branch from df37662 to 370a036 Compare May 31, 2018 18:15
@mhvk

mhvk commented Jun 14, 2018

Copy link
Copy Markdown
Contributor Author

I realize this is a difficult PR to review, but it would be nice to get this in - substantial performance increase, plus more to come with things that are enabled by this. @astrofrog - do you think you will have time to review? Or do you perhaps have a suggestion for someone else?

@astrofrog astrofrog left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I've just reviewed this - I don't understand 100% of the C code, but overall this looks great to me. Just a few minor comments below.

I know that @lpsinger has experience with writing ufuncs - @lpsinger - would you mind taking a look at this? (note that you can view the generated code if you need, see previous comment by @mhvk). In particular, I would be curious to know if there is anything that is being done here that would preclude using OpenMP in future to speed things up?

Comment thread astropy/_erfa/core.py.templ Outdated
except:
raise ValueError("{0} must be of trailing dimensions {1}".format(name, shape))
class D3Fix(numpy.ndarray):
# We do this to help ensure Quantity happily lets this pass...

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Could you provide more details in the comment here? I don't quite understand why this is needed.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yes, how could one... I added a very detailed comment.

Comment thread astropy/_erfa/core.py.templ Outdated


def arrayify_inputs_and_create_d3_fix(inputs, core_dims, out_core_shape, out_dtype):
# Need to ensure we can get a shape

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This comment isn't very clear - could you write a comment to describe more verbosely what this function does?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Now a proper docstring.

# <--------------------------Actual ERFA-wrapping code------------------------>

{% for constant in constants %}
{{ constant.name }} = {{ constant.value }}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The generated code here has brackets that aren't needed:

DR2AS = (206264.8062470963551564734)

This appears to be the case only for floating point values (not integers)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The parentheses are in the erfam.h file. I don't think it hurts to just let them be.


/* Extra/PVMergeExtract */
void eraPav2pv(double p[3], double v[3], double pv[2][3]);
void eraPv2pav(double pv[2][3], double p[3], double v[3]);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Could you add a comment to this file about why it is needed?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done.

Comment thread astropy/_erfa/erfa_generator.py Outdated
or (subsection == "AngleOps")
or (subsection == "SphericalCartesian")
or (subsection == "MatrixVectorProducts")
or (subsection == 'VectorOps')):

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The inner parentheses are not needed here

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I left them as I found them.

But talking about those: shall just take out the whole clause? We're compiling nearly everything anyway, so there is little point in terms of trying to keep it small or so.

I'll do that as a second commit, so it would be easy to revert.

Comment thread astropy/_erfa/tests/test_erfa.py Outdated
# pvmatarr = np.array([pvmat]*3, order='F')
# astrom4 = erfa.apcs13(jd1, jd2, pvmatarr)
# assert astrom4.shape == (3,)
# np.testing.assert_allclose(astrom4['em'], 1.010428384373318379)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Was this temporarily commented out?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Some of those tests no longer make sense, since the pv vectors are not input as (..., 2, 3) arrays. I'll keep what does work still.

mhvk added 2 commits June 18, 2018 11:54
Also uncomment tests that were not passing in an earlier stage.

@astrofrog astrofrog left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Looks good to me know. I'll leave it up to you as to whether to wait for another review.

@mhvk

mhvk commented Jun 19, 2018 •

Copy link
Copy Markdown
Contributor Author

OK, thanks. @jwoillez, @lpsinger - if you plan to review, could you let me know? I think it is actually all OK, so if I don't hear in the next few days, I'll merge, so I can go on and use the erfa routines in coordinate transformations.

@mhvk
mhvk merged commit 91fc183 into astropy:master Jun 22, 2018
@mhvk
mhvk deleted the erfa-wrapped-by-gufuncs branch June 22, 2018 11:17
@Cadair

Cadair commented Jun 22, 2018

Copy link
Copy Markdown
Member

Merging this into master appears to have broken astropy.coordinates.get_body_barycentric:

>>> get_body_barycentric("earth", Time("2018-01-01T00:00:00"))
---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
<ipython-input-2-d4041245237a> in <module>()
----> 1 get_body_barycentric("earth", Time("2018-01-01T00:00:00"))

~/Git/astropy/astropy/coordinates/solar_system.py in get_body_barycentric(body, time, ephemeris)
    362     """
    363     return _get_body_barycentric_posvel(body, time, ephemeris,
--> 364                                         get_velocity=False)
    365 
    366 

~/Git/astropy/astropy/coordinates/solar_system.py in _get_body_barycentric_posvel(body, time, ephemeris, get_velocity)
    241 
    242         body_pos_bary = CartesianRepresentation(
--> 243             body_pv_bary['p'], unit=u.au, xyz_axis=-1, copy=False)
    244         if get_velocity:
    245             body_vel_bary = CartesianRepresentation(

IndexError: only integers, slices (`:`), ellipsis (`...`), numpy.newaxis (`None`) and integer or boolean arrays are valid indices

@pllim

pllim commented Jun 22, 2018

Copy link
Copy Markdown
Member

@Cadair , best to open a new issue then!

@lpsinger

Copy link
Copy Markdown
Contributor

Whoa. My mind is blown by using jinja2 to generate ufunc wrappers. Your Numpy-fu exceeds my own.

@mhvk

mhvk commented Jun 27, 2018

Copy link
Copy Markdown
Contributor Author

@lpsinger - the jinja2 stuff is really just an adaptation of what @jwoillez came up with... But it is pretty amazing!

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants