Skip to content

Unit compose and equivalencies - #2559

Merged
mdboom merged 3 commits into
astropy:masterfrom
mdboom:unit/fix-compose
Nov 18, 2014
Merged

mdboom merged 3 commits into
astropy:masterfrom
mdboom:unit/fix-compose

Conversation

@mdboom

@mdboom mdboom commented Jun 6, 2014

Copy link
Copy Markdown
Contributor

In #2544, it was noted that the following does not work in master (nor is resolved by that PR):

u.Unit("rad/s").compose(units=(u.day,), equivalencies=u.dimensionless_angles())
ERROR: UnitsError: Cannot represent unit rad / s in terms of the given units [astropy.units.core]

(even though (1*u.Unit("rad/s")).to(u.day**-1, equivalencies=u.dimensionless_angles()) works fine)

Looking at this further, there seems to be a general problem with using equivalencies in compose; e.g., to me at least, in the following, the first and last are surprising:

In [3]: u.Unit("arcsec").compose(units=(u.pc,), equivalencies=u.parallax())
Out[3]: [Unit("pc")]

In [4]: u.Unit("arcsec").compose(units=(u.degree,), equivalencies=u.parallax())
Out[4]: [Unit("0.000277778 deg")]

In [5]: u.Unit("degree").compose(units=(u.pc,), equivalencies=u.parallax())
Out[5]: [Unit("pc")]

One question this brings up is whether for compose we need equivalencies at all? They would seem to make sense only for quantities, not units (in particular for ones that include an offset, like the various doppler_*).

@eteq

eteq commented May 26, 2014

Copy link
Copy Markdown
Member

I think this is because Quantity uses these mechanisms to make the equivalencies happen? But I could be wrong... perhaps @mdboom knows?

@mdboom mdboom added this to the v0.4.1 milestone May 27, 2014
@mdboom mdboom added the units label May 27, 2014
@mdboom

mdboom commented May 27, 2014

Copy link
Copy Markdown
Contributor

Yes -- equivalencies in compose are broken. I spent some time looking at this last week, and I think it does make sense to fix this independently of #2544, which doesn't actually introduce any regressions in this regard.

@mhvk

mhvk commented May 27, 2014

Copy link
Copy Markdown
Contributor Author

It would seem there is a general problem for equivalencies as applied to units, in that it is not possible to give just a decomposition for equivalencies that involve a scaling (like doppler_*); see also #2496. In that sense, we may have to make a difference between finding units a quantity can in principle be composed into, and an actual composition in those units.

@mdboom mdboom self-assigned this Jun 3, 2014
mdboom added a commit to mdboom/astropy that referenced this pull request Jun 6, 2014
@mdboom

mdboom commented Jun 6, 2014

Copy link
Copy Markdown
Contributor

I've attached a fix that works for the examples provided.

@mhvk: I'm not sure I understand your comment that this doesn't work with parameterized equivalencies. The units returned by compose always contain a scale, and will have an appropriate scale for the given equivalency. The find_equivalent_units method (which calls compose under the hood) will already "strip" these scales and provide a list of units the unit can in principle be composed to, where as "raw" compose will include a specific scale for each.

@mhvk

mhvk commented Jun 6, 2014

Copy link
Copy Markdown
Contributor Author

@mdboom - thinking again, my comment may indeed be incorrect. But I'm not sure and perhaps this is easiest addressed by adding a test of what I would, perhaps naively, believe the answer should be:

    x = (u.nm).compose(units=(u.m, u.s), equivalencies=u.doppler_optical(0.55*u.micron))
    assert x[0].bases == [u.m, u.s]
    assert x[0].powers == [1, -1]
    assert_allclose(x[0].scale, (1.*u.nm/(0.55*u.micron)*constants.c).to(u.m/u.s).value)

A propos tests, I also think it would be good to test explicitly that dimensionless_angles() works for arbitrary powers of radian. So, how about also adding the following:

    x = (u.degree**3).compose(units=(u.dimensionless_unscaled,), equivalencies=u.dimensionless_angles())
    assert x[0] is u.Unit(u.degree.to(u.radian)**3)

(maybe this needs an assert_allclose on the scale instead)

@mdboom

mdboom commented Jun 6, 2014

Copy link
Copy Markdown
Contributor

For the first example, that makes sense, and the result matches

>>> u.nm.to(u.m / u.s, equivalencies=u.doppler_optical(0.55 * u.micron))
-299247380.8036363

not

>>> (1.*u.nm/(0.55*u.micron)*constants.c).to(u.m/u.s).value
545077.1963636364

which is self-consistent. If that's not correct, than the equivalency is different that what you were expecting even in the to case.

As for the second, compose has never done the sort of partial factoring of powers of the whole unit that that would require. It could, I suppose, but it was a semi-deliberate choice not to, as the search space is already big enough... Plus, there's an interesting ambiguity when doing that, by the equivalency, since rad3 is equivalent to rad2, rad**1 and "dimensionless". I suppose that's technically correct, while surprising.

@mhvk

mhvk commented Jun 6, 2014

Copy link
Copy Markdown
Contributor Author

I think the above points to my problem with doppler_* - here, the unit nm is treated as if it were a wavelength of 1 nm, i.e., as if it were a quantity. But it is not: for units, I would expect results to scale, i.e.,

with u.set_enabled_equivalencies(u.doppler_optical(0.55*u.micron):
    assert u.nm.to(u.m/u.s) == 10. * u.Angstrom.to(u.m/u.s)

But the way the equivalency is written, I see that this is essentially impossible.

Note that this is similar to the temperature units, where there is a well-defined scaling between Fahrenheit, Celcius, and Kelvin, which is all one may care about for the units, but also an offset, which is important for actual quantities.

In any case, I'm not sure whether we can handle this here. In practice, I think it is far more important to address #2496, which is much more likely to cause hard-to-debug problems.

@mhvk

mhvk commented Jun 6, 2014

Copy link
Copy Markdown
Contributor Author

p.s. On dimensionless_angles(), indeed logically .compose would lead to an infinite set of possible alternatives. Obviously, that wouldn't do. Both examples really make me wonder whether we should not remove the option to get new units using equivalencies; maybe those should be used in quantities only...

@embray embray modified the milestones: v0.4.2, v0.4.3 Sep 23, 2014
@mdboom

mdboom commented Nov 18, 2014

Copy link
Copy Markdown
Contributor

I'd still like to see this in -- I agree with @mhvk it's not perfect, but I think it's an improvement over what we have. Perhaps a little too large/risky for a bugfix release, so we milestone this for 1.0 instead of 0.4.x?

@embray

embray commented Nov 18, 2014

Copy link
Copy Markdown
Member

Up to you.

mdboom added a commit that referenced this pull request Nov 18, 2014
@mdboom
mdboom merged commit 8a9d317 into astropy:master Nov 18, 2014
mdboom added a commit that referenced this pull request Dec 31, 2014
Unit compose and equivalencies
Conflicts:
	astropy/units/tests/test_equivalencies.py
@mdboom
mdboom deleted the unit/fix-compose branch March 3, 2015 16:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants