Skip to content

Add complex number support to tanh - #458

Merged
kgryte merged 3 commits into
mainfrom
cmplx-tanh
Aug 22, 2022
Merged

Add complex number support to tanh#458
kgryte merged 3 commits into
mainfrom
cmplx-tanh

Conversation

@kgryte

@kgrytekgryte commented Jun 21, 2022

Copy link
Copy Markdown
Contributor

This PR

  • adds complex number support to tanh by documenting special cases. The hyperbolic tangent is an analytical function in the complex plane and has no branch cuts.
  • updates the input and output array data types to be any floating-point data type, not just real-valued floating-point data types.
  • derives special cases from C99 and tested against NumPy (script found below).
  • adds a warning concerning two special cases where results may differ depending on which C version an array library compiles against (for those libraries which are C compiled).
Details
importnumpyasnpimportmathdefis_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:ifx==0.0andy==0.0:
returnmath.copysign(1.0, x) ==math.copysign(1.0, y)
# Handle NaNs:ifx!=x:
returny!=y# Everything else, including infinities:returnx==ydefis_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 """returnis_equal_float(x.real, y.real) andis_equal_float(x.imag, y.imag)
# Strided array consisting of input values and expected values:values= [
complex(0.0, 0.0), # 0complex(0.0, 0.0), # 0complex(-0.0, -0.0), # 1complex(-0.0, -0.0), # 1complex(1.0, np.inf), # 2complex(np.nan, np.nan), # 2complex(0.0, np.inf), # 3complex(0.0, np.nan), # 3, seems to be a bug in NumPy, which 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)complex(1.0, np.nan), # 4complex(np.nan, np.nan), # 4complex(0.0, np.nan), # 5complex(0.0, np.nan), # 5 seems to be a bug in NumPy, which 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)complex(np.inf, 1.0), # 6complex(1.0, 0.0), # 6complex(np.inf, np.inf), # 7complex(1.0, -0.0), # 7complex(np.inf, np.nan), # 8complex(1.0, 0.0), # 8complex(np.nan, 0.0), # 9complex(np.nan, 0.0), # 9complex(np.nan, 1.0), # 10complex(np.nan, np.nan), # 10complex(np.nan, np.nan), # 11complex(np.nan, np.nan) # 11
]
foriinrange(len(values)//2):
j=i*2v=values[j]
e=values[j+1]
actual=np.tanh(v)
print('Index: {index}'.format(index=str(i)))
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')
Index: 0
Value: 0j
Actual: 0j
Expected: 0j
Equal: True
Index: 1
Value: (-0-0j)
Actual: (-0-0j)
Expected: (-0-0j)
Equal: True
Index: 2
Value: (1+infj)
Actual: (nan+nanj)
Expected: (nan+nanj)
Equal: True
Index: 3
Value: infj
Actual: (nan+nanj)
Expected: nanj
Equal: False
Index: 4
Value: (1+nanj)
Actual: (nan+nanj)
Expected: (nan+nanj)
Equal: True
Index: 5
Value: nanj
Actual: (nan+nanj)
Expected: nanj
Equal: False
Index: 6
Value: (inf+1j)
Actual: (1+0j)
Expected: (1+0j)
Equal: True
/path/to/ctanh.py:107: RuntimeWarning: invalid value encountered in tanh
actual = np.tanh(v)
Index: 7
Value: (inf+infj)
Actual: (1-0j)
Expected: (1-0j)
Equal: True
Index: 8
Value: (inf+nanj)
Actual: (1+0j)
Expected: (1+0j)
Equal: True
Index: 9
Value: (nan+0j)
Actual: (nan+0j)
Expected: (nan+0j)
Equal: True
Index: 10
Value: (nan+1j)
Actual: (nan+nanj)
Expected: (nan+nanj)
Equal: True
Index: 11
Value: (nan+nanj)
Actual: (nan+nanj)
Expected: (nan+nanj)
Equal: True

Notes

  • NumPy deviates from special behavior in two cases: 0 + inf j and 0 + NaN j. NumPy adheres to old C99 behavior; however, this was corrected in the C standard in 2014.

@kgrytekgryte added API change Changes to existing functions or objects in the API. topic: Complex Data Types Complex number data types. labels Jun 21, 2022
@kgryte

Copy link
Copy Markdown
ContributorAuthor

As the changes in this PR are straightforward (no branch cuts, etc) and this PR has been allowed time for review, will go ahead and merge. Any further modifications can be addressed in subsequent PRs...

@kgryte
kgryte merged commit 9dc714e into mainAug 22, 2022
@kgryte
kgryte deleted the cmplx-tanh branch August 22, 2022 18:59
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

API changeChanges to existing functions or objects in the API.topic: Complex Data TypesComplex number data types.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@kgryte