Skip to content

BUG: tanh returns unexpected values in special cases with complex numbers #21826

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
steff456 opened this issue Jun 22, 2022 · 3 comments
Open
Labels

Comments

@steff456
Copy link
Contributor

Describe the issue:

Writing the spec for complex number support in the Array API we found that NumPy currently fails in 2 special cases. The behavior is inconsistent with the 2014 version C99.

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

cc @kgryte

Reproduce the code example:

import numpy as np
import math

def is_equal_float(x, y):
    """Test whether two floating-point numbers are equal with special consideration for zeros and NaNs.

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

    Returns
    -------
    bool
        Boolean indicating whether two floating-point numbers are equal.

    Examples
    --------
    >>> is_equal_float(0.0, -0.0)
    False
    >>> is_equal_float(-0.0, -0.0)
    True
    """
    # Handle +-0:
    if x == 0.0 and y == 0.0:
        return math.copysign(1.0, x) == math.copysign(1.0, y)

    # Handle NaNs:
    if x != x:
        return y != y

    # Everything else, including infinities:
    return x == y


def is_equal(x, y):
    """Test whether two complex numbers are equal with special consideration for zeros and 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
    """
    return is_equal_float(x.real, y.real) and is_equal_float(x.imag, y.imag)

def compare(v, e):
    actual = np.tanh(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(0.0, np.inf)
e = complex(0.0, np.nan)
compare(v, e) # returns `NaN + NaN j`; however, this does match old C99 behavior (see https://www.open-std.org/jtc1/sc22/wg14/www/docs/n1892.htm#dr_471)

# Case 2
v = complex(0.0, np.nan)
e = complex(0.0, np.nan)
compare(v, e) # returns `NaN + NaN j`; however, this does match old C99 behavior (see https://www.open-std.org/jtc1/sc22/wg14/www/docs/n1892.htm#dr_471)

Error message:

Value: infj
Actual: (nan+nanj)
Expected: nanj
Equal: False

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

NumPy/Python version information:

v1.22.4

@seberg
Copy link
Member

seberg commented Jun 22, 2022

On most platforms we presumably use the C99 function (I am not certain about all platforms, since we do have a fallback).

If the provided math libraries fail to adhere to C99 here, I don't really think we have to make it a priority to work around the issue (unless maybe users actually run into it). We certainly can for our fallback version of course.
But, I don't really see that we should blocklist a provided version or prioritize working around the special cases for such niche use-cases.

@kgryte
Copy link

kgryte commented Jun 22, 2022

@seberg Agreed.

In the current proposed changes to the array API standard adding complex number support to tanh, array libraries which compile against older versions of C are not required to patch those versions in order to address these two special cases.

@steff456
Copy link
Contributor Author

After digging a little bit and trying these cases in multiple OS with different architectures I found that this corner cases are present in macOS Intel, macOS ARM and Windows x64, but the corner cases are working as expected in Linux Intel and Linux ARM so probably the issue is related with the compiler of C99.

Hope this info is useful!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants