Repository navigation
Add BaseCoordinateFrame.cache and doc inplace modification. - #5575
Conversation
|
ping @taldcroft @eteq @mhvk |
d730d57 to
47a269f
Compare
| modifiying the values of the representation data as follows:: | ||
|
|
||
| c = SkyCoord([1,2],[3,4], unit='deg') | ||
| c.data.lon[:] = [10, 20] * u.deg |
There was a problem hiding this comment.
Use c.data.lon[()] = .. since this syntax also works for a scalar (which is a 0-d array internally).
There was a problem hiding this comment.
Might want to comment on the difference between lon and ra. Then give a hint about mapping from frame attribute names to representation component names by examining c.representation_component_names.
| `~astropy.coordinates.BaseCoordinateFrame.realize_frame`, this is the safest way | ||
| to change the data in a frame as it creates a new frame with that data. | ||
|
|
||
| Due to the fact that this creates a new frame it is quite slow, and in some |
There was a problem hiding this comment.
You could leave out the quite, and perhaps even use relatively.
| changes to the frame for maximum speed. This modification can be done by | ||
| modifiying the values of the representation data as follows:: | ||
|
|
||
| c = SkyCoord([1,2],[3,4], unit='deg') |
There was a problem hiding this comment.
You should make these tested examples and show that it actually works.
| clear this cache so future calculations of new representations are recomputed | ||
| using the new data. This can be achieved by doing:: | ||
|
|
||
| del c.frame.cache['representation'] |
There was a problem hiding this comment.
If you write del c.cache['representation'] then the example should work for either a SkyCoord or Frame. (I think so anway, needs to be verified).
| clear this cache so future calculations of new representations are recomputed | ||
| using the new data. This can be achieved by doing:: | ||
|
|
||
| del c.frame.cache['representation'] |
There was a problem hiding this comment.
Obvious, but this PR needs tests and changelog. In general the new code looks OK to me.
taldcroft
left a comment
There was a problem hiding this comment.
Minor tweaks, otherwise this looks good to merge.
|
|
||
| - A new ``BaseCoordinateFrame.cache`` dictionary has been created to expose | ||
| the internal cache. This is useful when modifying representation data | ||
| inplace without using ``realize_frame`` [#5575] |
There was a problem hiding this comment.
Needs a period at the end of the sentence. Then add one more sentence "Added new documentation describing details of such inplace modification of an existing coordinate object." Though we don't typically mention doc updates in the changelog, in this case it seems worthwhile.
| del i.cache['representation'] | ||
|
|
||
| # Old repr and new repr should be different (this checks for removal of the cache) | ||
| assert a != repr(i) |
There was a problem hiding this comment.
Test that it actually worked as expected: i.ra == 10 * u.deg, i.dec == 2 * u.deg.
|
|
||
| will not work as a different representation object is used when acessing the | ||
| aliased component names. | ||
| aliased component names. If you wish to inspect the mapping between frame |
There was a problem hiding this comment.
I think that splitting a sentence with a code fragment is difficult to read, and it's usually better to say what's important about a code snippet as a complete sentence prior to the snippet. So here: "... for components on the frame. For instance the following example will not work ... component names::".
Likewise in the next bit, say " ... you can look at the following dictionary::".
|
Test failure looks unrelated (network related?), I restarted the job. |
|
@Cadair - this looks good! One comment (both for the docstring and the documentation): since |
|
@mhvk from my testing it appears that doing |
29076f5 to
4d2cd27
Compare
|
@Cadair - can you confirm that the current documentation example |
|
I was just an end-user in that respect, so I don't have much to add except being curious how this affects ejeschke/ginga#113, ejeschke/ginga#181, and ejeschke/ginga#254. The last time I tried to use it (which was a while ago), |
|
@taldcroft yeah I tested it for both. And in fact the doctest does it for SkyCoord and the test does it for frame. @pllim I will try and get back into ginga at some point and see how what I have learned from all this plays out. |
|
@Cadair - yes, I can see that it wouldn't work -- we'd need to define a |
|
@mhvk I am not sure I agree with that, a frame with data is explicitly a combination of a frame and some data, if you change the data the frame doesn't change. I can foresee a situation where we cache something related to the frame and not related to the data (i.e. a transformation matrix), this should not be deleted if the data changes. This actually goes back to my original motivation for the previous PR, the concept to me has always been that you should be able to setup a frame and then quickly swap out the data because the frame is a representation of the system and the numbers are just points in that system. So to me the |
|
I see your point, but was thinking more of @eteq's example of a KD-tree, which would have to be invalidated if the coordinates changed. But my general feeling is that a cache should be very temporary (I don't like having to have one, really) and certainly one should always be free to delete it. Furthermore, how we store the representations perhaps should be an implementation detail. |
|
I agree with the being free to delete the cache concept, I wonder maybe if we want to have a non-data related cache in the future we could call it I added an example of removing the whole cache. |
eteq
left a comment
There was a problem hiding this comment.
This is looking great, but one thing is missing: the kdtree that's used in the matching code should be updated to use the cache instead of its own private variable. I can do this myself, though, if you like?
|
|
||
| - A new ``BaseCoordinateFrame.cache`` dictionary has been created to expose | ||
| the internal cache. This is useful when modifying representation data | ||
| inplace without using ``realize_frame``. Additionally, documentation for |
| `~astropy.coordinates.BaseCoordinateFrame.realize_frame`, this is the safest way | ||
| to change the data in a frame as it creates a new frame with that data. | ||
|
|
||
| Due to the fact that this creates a new frame it is relatively slow, and in some |
There was a problem hiding this comment.
Found this sentence very hard to understand. Maybe: "Creating a new frame can be relatively slow, however, particularly for scalar coordinates. Hence some situations may require that data by changed in-place in an already existing frame object. This modification ..."
| >>> del c.frame.cache | ||
|
|
||
| which removes the whole cache, but can only be done on frame instances and not | ||
| `~astropy.coordinates.SkyCoord` instances. |
There was a problem hiding this comment.
I think this last clause is not needed - it makes it sounds like you can't do this with SkyCoord, while what you actually mean is just that you have to do c.frame (as the example shows). So I'd just go with "which removes the whole cache from the frame object." or similar.
| This changes the longitude values for the frame, however | ||
| `~astropy.coordinates.SkyCoord` and `~astropy.coordinates.BaseCoordinateFrame` | ||
| cache representations of the data held within the frame as they are calculated | ||
| to speed up future operations of different representations of the same data. If |
There was a problem hiding this comment.
Also had trouble with this, both understanding what it says, and the fact that it specifically mentions ['representation'] but not anything else that might be cached. So I'd change it to:
This changes the longitude values of the frame. Unfortunately, doing just this introduces
problems: `~astropy.coordinates.SkyCoord` and `~astropy.coordinates.BaseCoordinateFrame`
cache various kinds of information to speed up some repeated operations. So we need to tell
the cache that it should be cleared so that it can be re-calculated from the new data.
This can be achieved by doing::
>>> del c.cache.clear()
or::
>>> del c.frame.cache
There was a problem hiding this comment.
We should advertise just one way of doing it, and I think it should be del c.cache. This should be enabled per the earlier comment from @mhvk to add a __delitem__ to SkyCoord that just does del self._sky_coord_frame.cache.
Agree with @eteq that I can't really see a use-case for users doing fine-tuned management of the cache. As developers we should at least be able to assume an all-or-nothing strategy in user cache management.
|
Oops, did my review before reading all of this... But I think the whole point of this cache is that anyone can use it, even user code (which we can sort of think of the matching stuff with the KD trees as being an example of, as it's not really a part of the frame classes themselves). I also agree one should always be free to clear the cache, but as my comment above shows, I think there's an easy way around the And although it may not be clear from my inline comment , I definitely agree with @taldcroft that Also, I don't undestand this at all:
I don't think anything in the cache would ever care about the frame, right? Isn't it always only about the data? |
d54d699 to
6aa66d8
Compare
|
I have made most of the changes but not added a I am not convinced it's needed, I actually like the |
You would know much better than me, I was just hypothesising about some transformation getting setup based on frame attributes and cached for later. The general opinion seems pretty clear and I have no concrete use case anyway, so I have flipped the docs to delete the whole dict. |
|
|
||
| assert len(i.cache['representation']) == 2 | ||
|
|
||
| del i.cache['representation'] |
There was a problem hiding this comment.
Just make this del i.cache for consistency?
There was a problem hiding this comment.
Given what I just stated for the documentation, I think this should be i.cache.clear() instead.
|
|
||
|
|
||
| def _get_cartesian_kdtree(coord, attrname_or_kdt='_kdtree', forceunit=None): | ||
| def _get_cartesian_kdtree(coord, attrname_or_kdt='kdtree', forceunit=None): |
There was a problem hiding this comment.
Very much a personal preference, but I'd call this argument just kdt.
| i.data.lon[()] = 10*u.deg | ||
|
|
||
| # Clear the cache | ||
| del i.cache['representation'] |
| assert len(i.cache['representation']) == 0 | ||
|
|
||
|
|
||
| def test_inplace_change(): |
There was a problem hiding this comment.
I feel we should have a test for a SkyCoord as well.
There was a problem hiding this comment.
It is tested for SkyCoord in a doctest, but I can add one here as well.
|
|
||
| >>> c.cache.clear() | ||
|
|
||
| or:: |
There was a problem hiding this comment.
I'm very happy with the c.cache.clear() way of clearing the cache. I think it should be immediately obvious that this works for frames as well, so would suggest removing the alternative (one right way to do it!).
|
Clearing with |
|
p.s. I really like how this came together, from what was a bit of a kludge to something that made enough sense to help simplify the kdtree implementation! |
also cache.clear() everywhere
Yes this did turn out quite nice, and faster than the original hack as well :) |
| assert i.dec == 2 * u.deg | ||
|
|
||
|
|
||
| def test_cache_clear_sc(): |
There was a problem hiding this comment.
Sorry to be a nit-pick, but this should go to test_skycoord.py...
|
All looks good to me now! |
|
Very glad to see this come together and looking forward to testing it! |
|
A few minor things, but it'll be easier for me to just do those and send them up to this branch... then will merge and backport to 1.3 (already confirmed with @astrofrog that backporting this is ok) |
|
Everything passed, so merging! |
|
Woop! Thanks everyone for a really constructive review! |
Add BaseCoordinateFrame.cache and doc inplace modification.
|
backported to 1.3.x in 950eb26 |
This is the result of the discussion in #3797 It adds a section to the documentation about inplace modification of data and the requirements to clear the new cache when doing this.
This also refactors the cache to be a lazyproperty
cacheofBaseCoordinateFramewhich is adefaultdict(dict)and the_rep_cacheis replaced bycache['representation']