Skip to content

Allow non-scalar power for quantities, when dimensionless? #4764

Closed

Description

@adonath

The following minimal example currently throws an error:

from astropy import units as u
import numpy as np
a = np.array([2, 2, 2, 2]) * u.Unit('cm/cm')
b = np.array([1, 2, 3, 4]) * u.Unit('')
a ** b

Which results in a ValueError: Quantities and Units may only be raised to a scalar power.

While the same example works, when a is explicitly transformed to dimensionless units:

a.to('') ** b

Is this behaviour intended? Or is this a bug?

It might be more reasonable to try to transform to dimensionless units before raising the power and only when this fails, the error is thrown.

Activity

  1. pllim commented on Apr 8, 2016

    @pllim
    Member

    Given that a is already effectively unitless, I am surprised that the behavior is inconsistent. Sounds like a bug to me.

  2. cdeil commented on Apr 11, 2016

    @cdeil
    Member

    I agree this looks like a bug, or at least it's confusing / inconvenient.

    Does anyone know what the proper fix would be?
    Are there general performance concerns applying it (too many checks for quantity operations)?

  3. pllim commented on Apr 11, 2016

    @pllim
    Member

    c/c @mhvk

  4. MSeifert04 commented on Apr 11, 2016

    @MSeifert04
    Contributor

    A possible fix would be easy. Just replace this line by:

    if function is np.power and result_unit != dimensionless_unscaled:
    

    That would at least don't trigger the validation if the unit is u.m / u.m (same unit as numerator and denominator) but would still fail for u.m / u.cm (only equivalent units as numerator and denominator).

    The problem boils down to:

    a = np.array([2, 2, 2, 2]) * u.Unit('cm/cm')
    a.unit is u.dimensionless_unscaled      # False
    a.unit == u.dimensionless_unscaled      # True
    

    But I don't know if that's performance-critical and I haven't checked the tests. I could do so if you want and there are no objections.

  5. pllim commented on Apr 11, 2016

    @pllim
    Member

    @MSeifert04 , that looks promising. Please submit a PR, thanks. 😃

  6. mhvk commented on Apr 11, 2016

    @mhvk
    Contributor

    This may be part of a solution, but I'm fairly sure it would leave the unit as cm/cm, which I don't think is what we would want, so at the very least it needs to be something like

    if function is np.power:
        if result_unit == dimensionless_unscaled
            result_unit = dimensionless_unscaled
        else:
            ...
    

    But it may be good to think of a method that gets units of u.m/u.cm work too. For that, I think we'd like to keep the present behaviour that, say (2*(u.m/u.cm))**3 yields 8 m3 / cm3, but if it is raised to an array or an irrational number, it would turn to dimensionless. So, I think the solution might be to keep the if statement as is, but further down one would do something like:

    try:
        p = validate_power(p)
    except ValueError as exc:
        # Array power; may be OK for dimensionless input.
        try:
            converters[0] = get_converter(units[0], dimensionless_unscaled)
        except:
            raise exc
    

    I do wonder, though, if that isn't better done in validate_power itself...

  7. MSeifert04 commented on Apr 11, 2016

    @MSeifert04
    Contributor

    @mhvk But wouldn't that be inconsistent? add, subtract also keep the unit of the first operand without any attempt of decomposition.

  8. mhvk commented on Apr 11, 2016

    @mhvk
    Contributor

    It would actually be similar for dimensionless:

    np.array([2, 2, 2, 2]) * u.Unit('cm/m')
    a - 2.
    # <Quantity [-1.98,-1.98,-1.98,-1.98]>
    

    i.e., a conversion to dimensionless is done if that is the only way to make the operation succeed.

    But let's move the discussion to #4770...

  9. MSeifert04 commented on Apr 11, 2016

    @MSeifert04
    Contributor

    @mhvk In that case it's an operation with a NOT quantity. I've also noticed this and was investigating where that came from.

    But just try: a - 2. * u.dimensionless_unscaled. And you'll have a result-unit.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions