Skip to content

BUG: Surprising difference between array scalar and array in casting in ufuncs #23247

Closed

Description

@mhvk

Describe the issue:

In selecting an inner loop for a ufunc in which one argument is a scalar, array scalars are not treated the same way as regular arrays, which leads to surprises.

It would seem best to treat any array the same way with regards to loop selection.

Reproduce the code example:

import numpy as np
a = np.array(np.pi, dtype='f4')
a <= np.pi
# False
a[np.newaxis] <= np.pi
# array([ True])

Error message:

No response

Runtime information:

1.25.0.dev0+606.ga1f3cbd85
3.11.1 (main, Dec 31 2022, 10:23:59) [GCC 12.2.0]

Context for the issue:

In astropy's Latitude class, a test is done that input angles are between -90 and 90 degrees, or, equivalently, between -pi/2 and pi/2. The tests fail with scalar array float32 inputs of pi/2.

Activity

  1. seberg commented on Feb 20, 2023

    @seberg
    Member

    Should have a few duplicate issues about this, as it is the "normal" value-based promotion rule. I.e.:

    In [1]: import numpy as np
    In [2]: np._set_promotion_state("weak")
    In [3]: a = np.array(np.pi, dtype='f4')
       ...: a <= np.pi
       ...: 
    Out[3]: True
    
    In [4]: a[np.newaxis] <= np.pi
    Out[4]: array([ True])
    

    (Better using the env variable probably.)

    I will admit, I am still struggling with the whole ufuncs are the same as operators story though, w.r.t. NEP 50. There is always a problem whether a user should expect weak promotion for a function call:

    some_function(float32_arr, 234.1)
    ``
    or not.  If we would not use weak promotion for "normal" ufunc calls, then the pattern seems easier to make clear to me (always convert, never weak).  OTOH, ufuncs and operators are slightly different then.  Which makes that `OperatorMixin` that falls back the ufunc complicated to ensure identical behavior.
    
  2. mhvk commented on Feb 20, 2023

    @mhvk
    ContributorAuthor

    Nice to see that it is possible to avoid! Is this meant to be in 2.0?

    Though here perhaps the main question is whether how an array is treated should depend on its dimension in any way... I feel it should not, though realize it still is super tricky.

    For my specific problem, I wish I could think of something smarter (right now, I ended up just adding an axis before doing the comparison to force doing it at the array dtype).

  3. seberg commented on Feb 20, 2023

    @seberg
    Member

    🤷 it doesn't depend on it the shape with weak promotion. There is also np.geater_requal.resolve_dtypes() now which implicitly opts you in to weak promotion, but that will hardly help you for now (more something that might be useful in the future).

    Yes, this is part of what I would want in a 2.0 (probably the biggest single item). The main issue is making sure we get those details right...

  4. seberg commented on Feb 20, 2023

    @seberg
    Member

    In a sense, both results could be acceptable above, one enforcing that the Python float is a float64, the other not. The issue is mainly the shape dependency.

    There is a fun N.B. to the above, since we could have a Constants DType that specifically always does weak casting, so that Constant(pi) could have arbitrary precision in theory, but always defers to the other sides precision.

  5. mhvk commented on Feb 20, 2023

    @mhvk
    ContributorAuthor

    The always-defer dtype seems a nice idea - covers both explicit is better than implicit and refuse the temptation to guess!

  6. mhvk commented on Jan 11, 2024

    @mhvk
    ContributorAuthor

    Closing this, since the particular issue is solved in 2.0, and a clear choice was made.

    (There still is the nice idea of a Constants DType that always defers, but not sure making that its own issue will help anything)

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions