Skip to content

BUG: np.expm1 returns unexpected values in special cases with complex numbers #21746

@steff456

Description

@steff456

Describe the issue:

Writing the spec for complex number support in the Array API we found that NumPy currently fails in 4 special cases. The behavior is inconsistent with np.exp(z)-1 and C99 as shown in the code example.

The full list of special cases and specification is detailed in data-apis/array-api#452

cc @kgryte

Reproduce the code example:

import numpy as np

def is_equal(x, y):
    """Test whether two complex numbers are equal with special consideration for NaNs.

    Parameters
    ----------
    x : complex
        First input number.
    y : complex
        Second input number.

    Returns
    -------
    bool
        Boolean indicating whether two complex numbers are equal.

    Examples
    --------
    >>> import numpy as np
    >>> is_equal(complex(np.nan, np.nan), complex(np.nan, np.nan))
    True
    """
    re1 = y.real
    im1 = y.imag

    re2 = x.real
    im2 = x.imag
    if re1 == re1:
        if im1 == im1:
            # Second value has non-NaN real and imaginary components, so test for component equality:
            return (re1 == re2) and (im1 == im2)
        
        if im2 == im2:
            # Second value has a NaN imaginary component, but first value does not:
            return False
        
        # Both values have NaN imaginary components:
        return True

    if im1 == im1:
        # Second value has a NaN real component, but a non-NaN imaginary component...
        if re2 == re2:
            # First value has a non-NaN real component:
            return False
        
        # Both values have NaN real components, so test for imaginary component equality:
        return (im1 == im2)

    if re2 == re2 or im2 == im2:
        # Second value has real and imaginary components which are NaN, but first value does not:
        return False

    # Both values have real and imaginary components which are NaN:
    return True

def compare(v, e):
    actual = np.expm1(v) 
    print('Value: {value}'.format(value=str(v)))
    print('Actual: {actual}'.format(actual=str(actual)))
    print('Expected: {expected}'.format(expected=str(e)))
    print('Equal: {is_equal}'.format(is_equal=str(is_equal(actual, e))))
    print('\n')

# Case 1
v = complex(np.inf, 0.0)
e = complex(np.inf, 0.0)
compare(v, e) # np.exmp1 returns (inf+nanj), vs np.exp(complex(np.inf, 0.0))-1.0 == (inf+0j)

# Case 2
v = complex(-np.inf, np.inf)
e = complex(-1.0, 0.0)
compare(v, e) # np.exmp1 returns (nan+nanj), vs np.exp(complex(-np.inf, np.inf))-1.0 == (-1+0j)

# Case 3
v = complex(-np.inf, np.nan)
e = complex(-1.0, 0.0)
compare(v, e) # np.exmp1 returns (nan+nanj), vs np.exp(complex(-np.inf, np.nan))-1.0 == (-1+0j)

# Case 4
v = complex(np.nan, 0.0)
e = complex(np.nan, 0.0)
compare(v, e) # np.exmp1 returns (nan+nanj), vs np.exp(complex(np.nan, 0.0))-1.0 == (nan+0j)

Error message:

Value: (inf+0j)
Actual: (inf+nanj)
Expected: (inf+0j)
Equal: False

Value: (-inf+infj)
Actual: (nan+nanj)
Expected: (-1+0j)
Equal: False

Value: (-inf+nanj)
Actual: (nan+nanj)
Expected: (-1+0j)
Equal: False

Value: (nan+0j)
Actual: (nan+nanj)
Expected: (nan+0j)
Equal: False

NumPy/Python version information:

v1.22.4

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

      pFad - Phonifier reborn

      Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

      Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


      Alternative Proxies:

      Alternative Proxy

      pFad Proxy

      pFad v3 Proxy

      pFad v4 Proxy