loop of ufunc does not support argument 0 of type int which has no callable XXX methodエラーの対処法

# はじめに
numpyを使っていて、``TypeError: loop of ufunc does not support argument 0 of type int which has no callable XXX method``エラーにハマって時間を溶かしてしまったので、そのときに試した検証や対処法などを紹介します。

# エラーの原因
先にこのエラーの原因を申し上げると、どうやらnumpyの入力可能な**整数型**の範囲が、``-9223372036854775808 ~ 18446744073709551615``のようで、それよりも範囲外の時にこのエラーが出るようです。
この数字と``numpy``のデータ型との対応を書くと、以下のようになります。

| numpy データ型 | 範囲 |
|:-:|:-:|
| int64 | 整数型 (-9223372036854775808 ~ 9223372036854775807) |
| uint64 | 符号なし整数 (0 ~ 18446744073709551615) |

つまり、入力可能な範囲は、下限値が``int64``の下限値に対応し、上限値は``uint64``の上限値ということになります。

# エラーの検証
numpyの三角関数（sin、cos、tan）、指数関数（exp）、対数関数（log）、平方根（sqrt）などの関数にこの整数型の制限が設けられているようです。一応以下のコードで検証しておきます。

Google colabでも同様のコードを書いているので、コードだけで良い方は[こちら](https://colab.research.google.com/drive/1mMHnl_Dkvs6X6vPLGhXJmtmxmVpLiL5h?usp=sharing)をご覧いただければと思います。

なお、このコードの実行時のnumpyのバージョンは1.22.4です。

```python:検証コード
import numpy as np

n_lower_lim = [-9223372036854775810, -9223372036854775809, -9223372036854775808, -9223372036854775807]
n_valid_range = [-1000, -1, 0, 1, 1000]
n_upper_lim = [18446744073709551614, 18446744073709551615, 18446744073709551616, 18446744073709551617]
n_verify = n_lower_lim + n_valid_range + n_upper_lim
for n in n_verify:
    try:
        verify_func = np.sin(n)
    except TypeError:
        print('Type Errorあり', n)
    else:
        print('Type Errorなし', n)
```

```結果
Type Errorあり -9223372036854775810
Type Errorあり -9223372036854775809
Type Errorなし -9223372036854775808
Type Errorなし -9223372036854775807
Type Errorなし -1000
Type Errorなし -1
Type Errorなし 0
Type Errorなし 1
Type Errorなし 1000
Type Errorなし 18446744073709551614
Type Errorなし 18446744073709551615
Type Errorあり 18446744073709551616
Type Errorあり 18446744073709551617
```

確かにその範囲の前後で``Type Error``エラーが確認できます。

# エラーの対処法
## ``float``型にキャストしてから入力
``float``型にキャストしてから入力するとエラーを回避できます。

```python:float型による回避法
n_lower_lim = [-9223372036854775810, -9223372036854775809, -9223372036854775808, -9223372036854775807]
n_valid_range = [-1000, -1, 0, 1, 1000]
n_upper_lim = [18446744073709551614, 18446744073709551615, 18446744073709551616, 18446744073709551617]
n_verify = n_lower_lim + n_valid_range + n_upper_lim
for n in n_verify:
    try:
        verify_func = np.sin(float(n))
    except TypeError:
        print('Type Errorあり', n)
    else:
        print('Type Errorなし', n)
```

```:結果
Type Errorなし -9223372036854775810
Type Errorなし -9223372036854775809
Type Errorなし -9223372036854775808
Type Errorなし -9223372036854775807
Type Errorなし -1000
Type Errorなし -1
Type Errorなし 0
Type Errorなし 1
Type Errorなし 1000
Type Errorなし 18446744073709551614
Type Errorなし 18446744073709551615
Type Errorなし 18446744073709551616
Type Errorなし 18446744073709551617
```

## mathモジュールによる回避
基本的な数学関数は、mathモジュールにも実装されており、mathの関数を使った場合はエラーが出ませんでした。大きな整数値を扱うときは、numpyの代替として、mathを使うのも選択肢としてありだと思います。

```python:mathモジュールによる回避法
import math

n_lower_lim = [-9223372036854775810, -9223372036854775809, -9223372036854775808, -9223372036854775807]
n_valid_range = [-1000, -1, 0, 1, 1000]
n_upper_lim = [18446744073709551614, 18446744073709551615, 18446744073709551616, 18446744073709551617]
n_verify = n_lower_lim + n_valid_range + n_upper_lim
for n in n_verify:
    try:
        verify_func = math.sin(n)
    except TypeError:
        print('Type Errorあり', n)
    else:
        print('Type Errorなし', n)
```

```:結果
Type Errorなし -9223372036854775810
Type Errorなし -9223372036854775809
Type Errorなし -9223372036854775808
Type Errorなし -9223372036854775807
Type Errorなし -1000
Type Errorなし -1
Type Errorなし 0
Type Errorなし 1
Type Errorなし 1000
Type Errorなし 18446744073709551614
Type Errorなし 18446744073709551615
Type Errorなし 18446744073709551616
Type Errorなし 18446744073709551617
```


# まとめ
``loop of ufunc does not support argument 0 of type int which has no callable XXX method``エラーは、``float``型にキャストしてから入力、あるいはmathを使って回避していきましょう。
と言いつつも、筆者は基本numpyで完結したい派なのですが、ただfloat型へのキャストもコードの見栄え悪くなり、さらにこの唐突なキャストは、Python的な発想ではないので、第三者から意図が汲み取れずに削除ということもありコメントで補足する必要があるので、どこかのタイミングで改善されると良いですね。