Numbaで扱える各種の型とそれらの型指定まとめ

## はじめに
Numbaで扱える変数の型については、主な対象は数値とNumPy配列 (numpy.ndarray) だがそれ以外にもいろいろある。
しかしそれらは使用方法や型指定方法が公式ドキュメントに整備されていないので各種型の使用方法と型指定方法を試して結果をまとめた。

:::note warn
多くは公式ドキュメントに書かれていないため筆者が手元で試した結果をまとめたものです。
応用的な型の型指定方法については公式ドキュメントに書かれている範囲でさえ[安定版でない](https://numba.readthedocs.io/en/stable/reference/types.html#advanced-types)と書かれています。
:::

:::note warn
普通に関数を`@jit`で高速化する使い方であれば型指定なしの `@jit(nopython=True, cache=True)` でよく、型指定に速度上のメリットはないと思われます ([以前の記事](https://qiita.com/tariaki/items/83a9115d672fe9c3becb#4-1-jit%E3%81%A7%E5%9E%8B%E6%8C%87%E5%AE%9A%E3%81%97%E3%81%A6%E3%82%82%E9%80%9F%E5%BA%A6%E3%81%AF%E5%90%91%E4%B8%8A%E3%81%97%E3%81%AA%E3%81%84%E3%81%97%E3%81%AA%E3%81%84%E6%96%B9%E3%81%8C%E9%80%9F%E3%81%84%E3%81%8B%E3%82%82%E3%81%97%E3%82%8C%E3%81%AA%E3%81%84))。
型指定は jitclass, AOTコンパイル, objmode などで必要です。
:::

:::note info
Numbaで型指定を与えるには方法が何通りかあります。
  - `numba`モジュール内のNumba型指定オブジェクトを与える
  - `numba.typeof` 関数から得られるNumba型指定オブジェクトを与える
  - 文字列で与える

これらのうち `typeof` 関数はたいていの場合に便利ですが一部の型を表現できません。
また、jitclassの型指定は文字列で与えられないようです。
:::

:::note info
記事中では理解しやすさを優先し、モジュールのインポート名を原則フルパスにしました。実際に使うときは使いやすい名前をインポートすることをすすめます。
:::


検証環境 (両方で確認): 
- Python 3.12.5, NumPy 1.26.4, Numba 0.60.0
- Python 3.11.3, NumPy 1.24.3, Numba 0.57.0 

## 目次を兼ねた要約


- [0. たいていの型指定を簡便に可能な `typeof` 関数](#0-%E3%81%9F%E3%81%84%E3%81%A6%E3%81%84%E3%81%AE%E5%9E%8B%E6%8C%87%E5%AE%9A%E3%82%92%E7%B0%A1%E4%BE%BF%E3%81%AB%E5%8F%AF%E8%83%BD%E3%81%AA-typeof-%E9%96%A2%E6%95%B0)


- [1. 数値型（真偽値型含む）: `numba.boolean`, `numba.uint64`, `numba.int64`, `numba.float64` など](#1-%E6%95%B0%E5%80%A4%E5%9E%8B%E7%9C%9F%E5%81%BD%E5%80%A4%E5%9E%8B%E5%90%AB%E3%82%80-numbaboolean-numbauint64-numbaint64-numbafloat64-%E3%81%AA%E3%81%A9)

- [2. NumPy配列](#2-numpy%E9%85%8D%E5%88%97)
  - [2-1. NumPy配列-簡易指定: 中身の型+スライス記号](#2-1-numpy%E9%85%8D%E5%88%97-%E7%B0%A1%E6%98%93%E6%8C%87%E5%AE%9A-%E4%B8%AD%E8%BA%AB%E3%81%AE%E5%9E%8B%E3%82%B9%E3%83%A9%E3%82%A4%E3%82%B9%E8%A8%98%E5%8F%B7)
  - [2-2. NumPy配列-メモリレイアウト配置を指定: スライス表記末尾を`::1` もしくは `numba.types.Array(中身の型, 次元数, "C")`](#2-2-numpy%E9%85%8D%E5%88%97-%E3%83%A1%E3%83%A2%E3%83%AA%E3%83%AC%E3%82%A4%E3%82%A2%E3%82%A6%E3%83%88%E9%85%8D%E7%BD%AE%E3%82%92%E6%8C%87%E5%AE%9A-%E3%82%B9%E3%83%A9%E3%82%A4%E3%82%B9%E8%A1%A8%E8%A8%98%E6%9C%AB%E5%B0%BE%E3%82%921-%E3%82%82%E3%81%97%E3%81%8F%E3%81%AF-numbatypesarray%E4%B8%AD%E8%BA%AB%E3%81%AE%E5%9E%8B-%E6%AC%A1%E5%85%83%E6%95%B0-c)


- [3. None型とOptionalType型](#3-none%E5%9E%8B%E3%81%A8optional%E5%9E%8B)
  - [3-1. None型: `numba.void` もしくは `numba.none`](#3-1-none%E5%9E%8B--numbavoid-%E3%82%82%E3%81%97%E3%81%8F%E3%81%AF-numbanone)

  - [3-2. Optional型(Noneともう一つ型の両方に対応): `numba.optional(もう一つの型)`](#3-2-optional%E5%9E%8Bnone%E3%81%A8%E3%82%82%E3%81%86%E4%B8%80%E3%81%A4%E5%9E%8B%E3%81%AE%E4%B8%A1%E6%96%B9%E3%81%AB%E5%AF%BE%E5%BF%9C--numbaoptional%E3%82%82%E3%81%86%E4%B8%80%E3%81%A4%E3%81%AE%E5%9E%8B)


- [4. タプル](#4-%E3%82%BF%E3%83%97%E3%83%AB)
  - [4-1. (異種型混合可能な)タプル: `numba.types.Tuple((中身の型0, 中身の型1, 中身の型2, ...))`](#4-1-%E7%95%B0%E7%A8%AE%E5%9E%8B%E6%B7%B7%E5%90%88%E5%8F%AF%E8%83%BD%E3%81%AA%E3%82%BF%E3%83%97%E3%83%AB-numbatypestuple%E4%B8%AD%E8%BA%AB%E3%81%AE%E5%9E%8B0-%E4%B8%AD%E8%BA%AB%E3%81%AE%E5%9E%8B1-%E4%B8%AD%E8%BA%AB%E3%81%AE%E5%9E%8B2-)
  - [4-2. 中身の型が統一されたタプル: `numba.types.UniTuple(中身の型, 個数)`](#4-2-%E4%B8%AD%E8%BA%AB%E3%81%AE%E5%9E%8B%E3%81%8C%E7%B5%B1%E4%B8%80%E3%81%95%E3%82%8C%E3%81%9F%E3%82%BF%E3%83%97%E3%83%AB-numbatypesunituple%E4%B8%AD%E8%BA%AB%E3%81%AE%E5%9E%8B-%E5%80%8B%E6%95%B0)


- [5. リスト系コンテナ](#5-%E3%83%AA%E3%82%B9%E3%83%88%E7%B3%BB%E3%82%B3%E3%83%B3%E3%83%86%E3%83%8A)
  - [5-1. reflected-list-(reflected=True): `numba.types.List(中身の型, reflected=True)`](#5-1-reflected-list-reflectedtrue-numbatypeslist%E4%B8%AD%E8%BA%AB%E3%81%AE%E5%9E%8B-reflectedtrue)

  - [5-2. reflected-list-(reflected=False): `numba.types.List(中身の型, reflected=False)`](#5-2-reflected-list-reflectedfalse-numbatypeslist%E4%B8%AD%E8%BA%AB%E3%81%AE%E5%9E%8B-reflectedfalse)

  - [5-3. typed-list: `numba.types.ListType(中身の型)`](#5-3-typed-list-numbatypeslisttype%E4%B8%AD%E8%BA%AB%E3%81%AE%E5%9E%8B)

- [6. 辞書系コンテナ](#6-%E8%BE%9E%E6%9B%B8%E7%B3%BB%E3%82%B3%E3%83%B3%E3%83%86%E3%83%8A)
  - [6-1. typed-dict: `numba.types.DictType(キーの型, バリューの型)`](#6-1-typed-dict-numbatypesdicttype%E3%82%AD%E3%83%BC%E3%81%AE%E5%9E%8B-%E3%83%90%E3%83%AA%E3%83%A5%E3%83%BC%E3%81%AE%E5%9E%8B)


- [7. 集合系コンテナ](#7-%E9%9B%86%E5%90%88%E7%B3%BB%E3%82%B3%E3%83%B3%E3%83%86%E3%83%8A)
  - [7-1. reflected-set-(reflected=True): `numba.types.Set(中身の型, reflected=True)`](#7-1-reflected-set-reflectedtrue-numbatypesset%E4%B8%AD%E8%BA%AB%E3%81%AE%E5%9E%8B-reflectedtrue)

  - [7-2. reflected-set-(reflected=False): `numba.types.Set(中身の型, reflected=False)`](#7-2-reflected-set-reflectedfalse-numbatypesset%E4%B8%AD%E8%BA%AB%E3%81%AE%E5%9E%8B-reflectedfalse)


- [8. 文字列(str): `numba.types.unicode_type`](#8-%E6%96%87%E5%AD%97%E5%88%97str-numbatypesunicode_type)


- [9. バイト列系シーケンス](#9-%E3%83%90%E3%82%A4%E3%83%88%E5%88%97%E7%B3%BB%E3%82%B7%E3%83%BC%E3%82%B1%E3%83%B3%E3%82%B9)

- [10. Numba化関数オブジェクト](#10-numba%E5%8C%96%E9%96%A2%E6%95%B0%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88)

- [11. jitclassインスタンスオブジェクト : `MyJitclass.class_type.instance_type`](#11-jitclass%E3%82%A4%E3%83%B3%E3%82%B9%E3%82%BF%E3%83%B3%E3%82%B9%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88--myjitclassclass_typeinstance_type)

- [12. 再帰的な型指定のための、後で定義するNumba型 : `numba.deferred_type()`](#12-%E5%86%8D%E5%B8%B0%E7%9A%84%E3%81%AA%E5%9E%8B%E6%8C%87%E5%AE%9A%E3%81%AE%E3%81%9F%E3%82%81%E3%81%AE%E5%BE%8C%E3%81%A7%E5%AE%9A%E7%BE%A9%E3%81%99%E3%82%8Bnumba%E5%9E%8B--numbadeferred_type)






## 0. たいていの型指定を簡便に可能な `typeof` 関数

`numba.typeof` 関数に対象の値を与えるとたいていの場合は適切な型指定オブジェクトが得られる。  
ただし一部にtypeof関数で指定できない型がある(後述)

動作確認コード

```Python
import numpy as np
import numba

print(f"{numba.typeof(3.14) == numba.float64 =}")  # True
print(f"{numba.typeof(1) == numba.int64 =}")  # True
print(f"{numba.typeof(True) == numba.boolean =}")  # True
print(f"{numba.typeof(None) == numba.void =}")  # True

print()
print(
    f"""{numba.typeof(np.ones((4,3,2,),dtype=np.float64)) 
    == numba.types.Array(numba.float64, 3, 'C')
    =}""")
# True

print()
tuple_of_4 = 1.11, 1, False, np.array([[1.11]], dtype=np.float64)
print(f"{tuple_of_4 =}")  #
print(f"""{numba.typeof(tuple_of_4)
    == numba.types.Tuple((
        numba.float64, numba.int64, numba.boolean, numba.types.Array(numba.float64, 2, 'C'),
        ))
    =}""")
# True
```


## 1. 数値型（真偽値型含む）: `numba.boolean`, `numba.uint64`, `numba.int64`, `numba.float64` など
基本の数値型の型指定は[公式ドキュメント](https://numba.readthedocs.io/en/stable/reference/types.html#basic-types)に説明あり。
一覧表にすると以下になる

|Python組み込み型|NumPy型|Numba型指定オブジェクト|Numba型指定文字列|
|:----|:----|:----|:----|
|`bool`| |`numba.boolean`, `numba.bool_`, `numba.b1`|`boolean`, `bool_`, `b1`|
|`int`| |`numba.int64`, `numba.i8`|`int64`, `i8`|
|`float`| |`numba.float64`, `numba.f8`|`float64`, `f8`|
|`complex`| |`numba.complex128`, `numba.c16`|`complex128`, `c16`|
| |`numpy.bool_`|`numba.boolean`, `numba.bool_`, `numba.b1`|`boolean`, `bool_`, `b1`|
| |`numpy.uint8`|`numba.uint8`, `numba.u1`|`uint8`, `u1`|
| |`numpy.uint16`|`numba.uint16`, `numba.u2`|`uint16`, `u2`|
| |`numpy.uint32`|`numba.uint32`, `numba.u4`|`uint32`, `u4`|
| |`numpy.uint64`|`numba.uint64`, `numba.u8`|`uint64`, `u8`|
| |`numpy.int8`|`numba.int8`, `numba.i1`|`int8`, `i1`|
| |`numpy.int16`|`numba.int16`, `numba.i2`|`int16`, `i2`|
| |`numpy.int32`|`numba.int32`, `numba.i4`|`int32`, `i4`|
| |`numpy.int64`|`numba.int64`, `numba.i8`|`int64`, `i8`|
| |`numpy.float32`|`numba.float32`, `numba.f4`|`float32`, `f4`|
| |`numpy.float64`|`numba.float64`, `numba.f8`|`float64`, `f8`|
| |`numpy.complex64`|`numba.complex64`, `numba.c8`|`complex64`, `c8`|
| |`numpy.complex128`|`numba.complex128`, `numba.c16`|`complex128`, `c16`|

真偽値の型名がまちがえやすい。
数値型の型指定はエイリアスが多い。
数値型の型指定オブジェクトは `numba.XXX` と同等なものが `numba.types.XXX` にもある模様。
```Python
import numba

print(f"""{numba.typeof(3.14) 
    == numba.float64 
    == numba.f8
    == numba.types.float64
    == numba.types.f8
    =}""")
# True

print(f"""{numba.typeof(True) 
    == numba.boolean
    == numba.bool_
    == numba.b1
    == numba.types.boolean
    == numba.types.bool_
    == numba.types.b1
    =}""")
 # True
```

(補足: NumPy 2.0 では 真偽値の型名は `numpy.bool` が正式名になり、それまでの正式名`numpy.bool_` はエイリアスになるとのこと ([NumPy公式](https://numpy.org/devdocs/release/2.0.0-notes.html#changes)) 。  
ただし少し前のNumPyでは `numpy.bool` はPython組み込み `bool` のエイリアスで、 `numpy.bool_` とは別物だったので注意)



## 2. NumPy配列
   
### 2-1. NumPy配列-簡易指定: `中身の型+スライス記号`
数値型の型指定にスライス記号を付ければNumPy配列の型指定になる。[公式](https://numba.readthedocs.io/en/stable/reference/types.html#arrays)。
例えばfloat64の配列で、1次元なら `numba.float64[:]` 、2次元なら `numba.float64[:,:]` 、3次元なら `numba.float64[:,:,:]`。
  
ただし、この記述方法はメモリレイアウト指定なしの簡易指定になり、たいていの場合は問題ないが一部の使い方でコンパイルエラーになった。詳細と対応は次項で説明。
  
### 2-2. NumPy配列-メモリレイアウト配置を指定: スライス表記末尾を`::1` もしくは `numba.types.Array(中身の型, 次元数, "C")`
前項の簡易指定による型指定は普通に生成したNumPy配列を `typeof` に渡したときの型指定と厳密には異なる模様。
型指定オブジェクトを `print` すると詳細を確認でき、簡易指定のものは `A`、`typeof` したものは `C` と確認できる。
大抵の状況では簡易指定で `A` と指定した場所へ `C` のデータを与えても動作した (`C` はC言語式メモリレイアウト、`A` はAnyを意味するとのこと)。 
ただし一部の用途では `A` と `C` の違いが吸収されずコンパイルエラーになった。(後述のコードなど)

その場合に以下のようにすると `C` レイアウトで型指定でき、エラーを予防できた。
  - ダミーで作成したNumPy配列を `numba.typeof` 関数に与える (簡単)
  - スライス記号で最後の次元を `::1` とする。例えば、
    - float64の1次元なら `numba.float64[::1]` 
    - float64の2次元なら `numba.float64[:,::1]` 
    - float64の3次元なら `numba.float64[:,:,::1]` 
  - NumPy配列用型指定オブジェクト生成関数 `numba.types.Array(中身の型, 次元, "C")` で明示的に指定する。例えば、
    - float64の1次元なら `numba.types.Array(numba.float64, 1, "C")`
    - float64の2次元なら `numba.types.Array(numba.float64, 2, "C")`
    - float64の3次元なら `numba.types.Array(numba.float64, 3, "C")`

型指定の動作確認コード
[外部リンク](https://github.com/tariaki/numba_behavior_test/blob/main/2a_Array_layout.py)

## 3. None型とOptional型

### 3-1. None型:  `numba.void` もしくは `numba.none`
関数が返り値なしの時に。

### 3-2. Optional型(Noneともう一つ型の両方に対応):  `numba.optional(もう一つの型)`
numbaは静的型付けコンパイルを行うため、原則コンパイル時に変数の型が決定していなければならないが、<b>None型だけは他の型との混在が許される模様</b>。
そのような変数を扱う際は `if x is None: (Noneの場合の処理) else: (もう一つの型の場合の処理) ` のようにチェックしてから処理すると動いてくれる (明確に分岐内で処理しないとエラーになりやすい)。
軽く速度を測定したところ、単純な数値型の関数で同等のif文を伴った場合と同程度であった。
関数に`@jit`する場合などは型指定なしでもOptional型に型推論してくれる。jitclassなど型指定が必要な場合は `numba.optional(もう一つの型)` と指定する。
<b>これは `typeof` 関数では指定できない模様</b>。


jitclassで型指定の動作確認
[外部リンク](https://github.com/tariaki/numba_behavior_test/blob/main/3a_optional_type.py)

## 4. タプル
NumbaのタプルはPythonのそれと異なり、コンパイル時に中身の数と各型が決定している必要がある。
そのためかNumba関数内でのタプル生成はカンマ区切り記法のみで、`tuple` 関数は使えない。
いくつかの例で速度を測ったところNumPy配列と同等以上に速かった。

### 4-1. (異種型混合可能な)タプル: `numba.types.Tuple((中身の型0, 中身の型1, 中身の型2, ...))`
要素の数はいくつにも指定可能、異なる型の混在OK。
型指定の方法は、例えば3要素のタプルだと
- `typeof`関数  （簡単）
- 型指定オブジェクト生成関数 `numba.types.Tuple((中身の型0, 中身の型1, 中身の型2))`
- 文字列方式での型指定は `Tuple((中身の型0, 中身の型1, 中身の型2))`

丸括弧が二重になることに注意。  
`typeof` 関数を使った場合、次の`UniTuple`が使用可能な場合は`UniTuple`を優先して出力し、混合時のみ`Tuple`になる模様。

```Python
import numba

print(f"""{numba.typeof((1.11, 2,))
    == numba.types.Tuple((numba.float64, numba.int64,)) 
    =}""")
# => True
```

### 4-2. 中身の型が統一されたタプル: `numba.types.UniTuple(中身の型, 個数)` 
要素の型が統一されている場合こちらも使える。
型指定の方法は、
- `typeof`関数  （簡単）
- 型指定オブジェクト生成関数 `numba.types.UniTuple(中身の型, 個数)`
- 文字列方式での型指定は `UniTuple(中身の型, 個数)`

```Python
import numba

print(f"""{numba.typeof((1.11, 2.22, 3.33, 4.44, 5.55,))
    == numba.types.UniTuple(numba.float64, 5)
    =}""")
# => True
```

### 動作確認コード

jitclassで型指定の動作確認
[外部リンク](https://github.com/tariaki/numba_behavior_test/blob/main/4c_tuple_jitclass.py)


## 5. リスト系コンテナ
個数の不定なデータを格納するのに適したリストだが、現在のNumbaでは可変長コンテナ類は開発途上なうえリストだけで数種類あり得意不得意が分かれている。
いずれも中身の型は統一されている必要がある。
気づきにくい注意点がいろいろありエラーに悩まされやすい。

todo: Numbaで可変長コンテナを使うときの注意点をまとめた記事を別に作る

### 5-1. reflected-list-(reflected=True): `numba.types.List(中身の型, reflected=True)`
使用方法
- Python領域でPython組み込み`list`を作成し、中身のある状態でNumba関数へ引数渡しする

型指定
- `typeof`関数に中身のある`list`を与える（簡単）
- 型指定オブジェクト: `numba.types.List(中身の型, reflected=True)`
- 文字列方式での型指定: `List(中身の型, reflected=True)`

特徴
- 処理速度を測定すると、NumPy配列には負けるものの、<b>Pythonのlistから大幅に高速化された</b>
- <b>可変長コンテナ類との入れ子が不可能</b>、例えば2重リストなどができない
  - "Numpy配列を要素とするリスト" は可能で、これなら異なる長さの配列を格納できた
    - この場合の型指定はレイアウト `C` を指定する必要があった (レイアウト `A` がエラーになった) 
- 要素数が多いとPythonの`list`から変換されるとき少し変換時間が発生した
- Numba化関数の返り値でPython領域に返すとPython組み込み`list`に逆変換された 
- Numba化関数内でリストをin-place変更すると、変更内容が元のPythonの`list`に反映された (これがreflect動作)
- 使用すると `NumbaPendingDeprecationWarning` 警告が発生する(非表示にはできる)


### 5-2. reflected-list-(reflected=False): `numba.types.List(中身の型, reflected=False)`
Python領域で生成したリストを引数渡ししたものとNumba化関数内で生成したリストで扱いが異なる。

使用方法
- Numba化関数内で角括弧 `[]` 記法や `list` 関数を用いてリストを作成 + 型推論させる
- なお、Numba化関数内での ndarray.tolist() メソッドは未実装
- Numba化関数の返り値で<b>Python領域に返すとPython組み込み`list`に変換され、元に戻せなくなる模様</b>

型指定
- 型指定オブジェクト: `numba.types.List(中身の型, reflected=False)`、`numba.types.List(中身の型)`
- 文字列方式での型指定: `List(中身の型, reflected=False)`, `List(中身の型)`
- <b> `typeof` 関数では指定できない模様 </b>

特徴
- 処理速度を測定するとNumPy配列には負けるものの、<b>Pythonのlistから大幅に高速化された</b>
- reflected-list-(reflected=False) 同士や reflected-set-(reflected=False) との<b>多重コンテナが可能</b>
  - 辞書系コンテナとの多重コンテナはできなかった (次項のtyped-listを使うことになる)
- <b>入力データをPython領域からNumba化関数内へ持ち込む方法を工夫する必要がある</b>
- 使用すると `NumbaPendingDeprecationWarning` 警告が発生する (非表示にはできる)
- <b>現時点でのリスト系コンテナではこれが一番速いと思われる</b>
- デバッグがやややりにくい


### 5-3. typed-list: `numba.types.ListType(中身の型)`
使用方法
- Python領域もしくはNumba関数内で、以下のtyped-list生成関数を使用 (2つは同等) + 型推論させる
  - `numba.typed.typedlist.List`
  - `numba.typed.List`
- Python領域もしくはNumba関数内で、以下の型指定付きtyped-list生成関数に型を渡す (2つは同等) 
  - `numba.typed.typedlist.List.empty_list(中身の型)`
  - `numba.typed.List.empty_list(中身の型)`

型指定
- typed-listを生成し、中身の型を確定させた後、`typeof`関数に渡す 
- 型指定オブジェクト: `numba.types.ListType(中身の型)`
- 文字列方式での型指定: `ListType(中身の型)`


特徴
- 今のところ開発途上で、処理速度を測定すると素のPythonのlistから<b>それなりに高速化するもののreflected-listに劣った</b>
- <b>typed-list 同士や typed-dict との多重コンテナが可能</b>
- reflected-listと異なり、<b>Python領域とNumba化関数内の間で受け渡しても変換なし</b>
- typed-listをPython領域内で動作させたときの処理速度は遅かった

### 動作確認コード

#### Numbaのリスト3種が区別されることの確認

```Python
import numba


@numba.jit(nopython=True)
def type_checker_1(arg):
    return arg

# Python領域のpython-listをNumba化関数へ与える
pylist = [1.11, 2.22, 3.33]
type_checker_1(pylist)
print(f"{type_checker_1.signatures =}")
# => [(List(float64, True),)]


@numba.jit(nopython=True)
def type_checker_2(arg):
    return arg

# Numba化関数内で角括弧記法でリストを生成
@numba.jit(nopython=True)
def make_reflectedlistFalse(n):
    reflectedlistFalse = [i * 1.11 for i in range(n)]
    type_checker_2(reflectedlistFalse)
make_reflectedlistFalse(3)
print(f"{type_checker_2.signatures =}")
# => [(List(float64, False),)]


@numba.jit(nopython=True)
def type_checker_3(arg):
    return arg

# typed-list生成関数で生成
typedlist = numba.typed.List([1.11, 2.22, 3.33])
type_checker_3(typedlist)
print(f"{type_checker_3.signatures =}")
# => [(ListType(float64),)]
```

#### jitclassで型指定の動作確認

- 1重リスト
  - [reflected-list-(reflected=True)](https://github.com/tariaki/numba_behavior_test/blob/main/5_2a_list1d_reflectedlist_True.py)
  - [reflected-list-(reflected=False)](https://github.com/tariaki/numba_behavior_test/blob/main/5_2b_list1d_reflectedlist_False.py)
  - [typed-list](https://github.com/tariaki/numba_behavior_test/blob/main/5_2c_list1d_Typedlist.py) 

- 2重リスト
  - [reflected-list-(reflected=True) は多重にできない](https://github.com/tariaki/numba_behavior_test/blob/main/5_3a_list2d_fail_reflectedlist_True.py) 
  - [2重のreflected-list-(reflected=False) ](https://github.com/tariaki/numba_behavior_test/blob/main/5_3b_list2d_reflectedlist_False.py) 
  - [2重のtyped-list](https://github.com/tariaki/numba_behavior_test/blob/main/5_3c_list2d_typedlist.py) 
  - [1重のreflected-list-(reflected=True) に NumPy 配列を格納して2重リストを模擬](https://github.com/tariaki/numba_behavior_test/blob/main/5_3d_list2d_rlTrue_of_array.py) 


### 6. 辞書系コンテナ

Numba は Python 組込の`dict`に非対応。Python領域からNumba関数に与えても型指定に関係なく拒否される。  
```Python
import numba

@numba.jit(nopython=True)
def receiver(arg):
    return arg

pydict = {1: 1.11, 2: 2.22, 3:3.33}

try:
    res = receiver(pydict)
    print(res)
except Exception as e:
    print(type(e), e)
# => Pythonのdictは受け入れ拒否される
# <class 'numba.core.errors.TypingError'> Failed in nopython mode pipeline (step: nopython frontend)
# non-precise type pyobject
# - argument 0: Cannot determine Numba type of <class 'dict'>
```


Numba 関数内で dict を作ろうとすると後述の typed-dict が生成する（リスト、セットと挙動が異なる）。

typed-dict という Numba 独自のデータ構造があり、Numba化関数内で辞書系コンテナを使うにはこれを使う。しかし、開発途上で素のPython-dictよりほとんど速くならない。
<b>高速化のためには辞書そのものを避けたい</b>ところ。
さらに、リスト同様にいろいろ注意点がありエラーに悩まされやすい。




### 6-1. typed-dict: `numba.types.DictType(キーの型, バリューの型)`
使用方法
- Python領域もしくはNumba関数内で、以下のtyped-dict生成関数を使用(2つは同等) + 型推論させる
  - `numba.typed.typeddict.Dict()`
  - `numba.typed.Dict()`
- Python領域もしくはNumba関数内で、以下の型指定付きtyped-dict生成関数に中身の型を渡す(2つは同等)  
  - `numba.typed.typeddict.Dict.empty(キーの型, バリューの型)`
  - `numba.typed.Dict.empty(キーの型, バリューの型)`
- Numba化関数内で波括弧 `{}` 記法 や `dict` 関数で辞書を生成 + 型推論させる
  - 辞書内包表記を使える。内包表記を使うと型推論を失敗しにくい
  - 問題が起こったときのデバッグがやややりにくい
 
型指定
- `typeof`関数に中身の型が決定済のtyped-dictを渡す
- 型指定オブジェクト:  `numba.types.DictType(キーの型, バリューの型)`
- 文字列方式での型指定: `DictType(キーの型, バリューの型)`

特徴
- Numba化関数内で辞書を使うにはこれ
- typed-listとの多重コンテナ可能
- 変換なしにPython領域とNumba化関数内でデータを受け渡しできる
- <b>処理速度を測定すると、Numba化関数内のtyped-dictでも素のPythonの`dict`とあまり変わらなかった</b>
- typed-dictをPython領域で動作させるとかなり遅かった


### 動作確認コード


Numba用辞書は `numba.typed.Dict`使用とNumba化関数内での波括弧記法で同種のものが生成する
```Python
import numba


@numba.jit(nopython=True)
def type_checker_1(arg):
    return arg

# Python領域でtyped-dict生成関数を使用して生成
typeddict_in_py = numba.typed.Dict()
typeddict_in_py[1] = 1.11
typeddict_in_py[2] = 2.22
type_checker_1(typeddict_in_py)
print(f"{type_checker_1.signatures =}")
# => [(DictType(int64, float64),)]


@numba.jit(nopython=True)
def type_checker_2(arg):
    return arg

# Numba化関数内でtyped-dict生成関数を使用して生成
@numba.jit(nopython=True)
def make_typeddict_in_numba_explicitly(n):
    typeddict_in_numba_explicitly = numba.typed.Dict()
    typeddict_in_numba_explicitly[1] = 1.11
    typeddict_in_numba_explicitly[2] = 1.11
    type_checker_2(typeddict_in_numba_explicitly)
make_typeddict_in_numba_explicitly(3)
print(f"{type_checker_2.signatures =}")
# => [(DictType(int64, float64),)]


@numba.jit(nopython=True)
def type_checker_3(arg):
    return arg

# Numba化関数内で波括弧記法で辞書を生成
@numba.jit(nopython=True)
def make_typeddict_in_numba_by_braces(n):
    typeddict_in_numba_by_braces = {i: i * 1.11 for i in range(n)}
    type_checker_3(typeddict_in_numba_by_braces)
make_typeddict_in_numba_by_braces(3)
print(f"{type_checker_3.signatures =}")
# => [(DictType(int64, float64),)]
```

jitclassで型指定の動作確認
[外部リンク](https://github.com/tariaki/numba_behavior_test/blob/main/6_1b_typeddict.py)

### 7. 集合系コンテナ

集合系コンテナは現在のNumbaでは reflected-set 系統のみ。
将来的には "typed-set" を実装してそちらに切換える計画とのことだが未実装。
やはりいろいろ注意点がありエラーに悩まされやすい。
開発途上で<b>素のPythonからわずかしか速くならない</b>。

またfrozensetには未対応な模様。Python領域からNumba化関数に与えても指定に関係なく拒否された。

#### 7-1. reflected-set-(reflected=True): `numba.types.Set(中身の型, reflected=True)`
使用方法
 - Python領域でPython組み込み`set`を作成し、Numba関数へ引数渡しする

型指定
- `typeof`関数に中身のあるsetを与える（簡単）
- 型指定オブジェクト `numba.types.Set(中身の型, reflected=True)`
- 文字列方式での型指定は `Set(中身の型, reflected=True)`

特徴
- <b>可変長コンテナ類との入れ子が不可能</b>、例えばsetをlistに格納してPython領域から渡すと拒否された
- 要素数が多いとPythonのsetから変換されるとき少し変換時間が発生した
- Numba化関数の返り値でPython領域に返すとPython組み込みsetに逆変換された
- Numba化関数内でセットをin-place変更すると、変更内容が元のPythonのsetに反映された (これがreflected動作らしい)
- 使用すると `NumbaPendingDeprecationWarning` 警告が発生する(非表示にはできる)


### 7-2. reflected-set-(reflected=False): `numba.types.Set(中身の型, reflected=False)`
Python領域で生成したセットを引数渡ししたものとNumba化関数内で生成したセットで扱いが異なる。

使用方法
- Numba関数内で角括弧 `{}` 記法や `set` 関数を用いてセットを作成する
- Numba化関数の返り値で<b>Python領域に返すとPython組み込みsetに変換され、元に戻せなくなる</b>

型指定
- 型指定オブジェクト: `numba.types.Set(中身の型, reflected=False)`、`numba.types.Set(中身の型)`
- 文字列方式での型指定: `Set(中身の型, reflected=False)`、`Set(中身の型)`
- <b> `typeof` 関数では指定できない模様</b>

特徴
- 処理速度を測定すると、<b>素のPythonの`set`よりわずかに速い程度だった</b>
- reflected-list-(reflected=False) に格納できた (多重コンテナ)
- 入力データをPython領域からNumba化関数内へ持ち込む方法を工夫する必要がある
- 使用すると `NumbaPendingDeprecationWarning` 警告が発生する(非表示にはできる)

### jitclassで型指定の動作確認

- セット
  - [reflected-set-(reflected=True)](https://github.com/tariaki/numba_behavior_test/blob/main/7a_reflectedset_True.py)
  - [reflected-set-(reflected=False)](https://github.com/tariaki/numba_behavior_test/blob/main/7b_reflectedset_False.py)

## 8. 文字列(str): `numba.types.unicode_type`
使用方法
- Python領域のstrをNumba化関数へ引数渡しする
- Numba化関数内で文字列リテラルや `str` 関数を使用して生成

型指定
- typeof関数を利用
- 型指定オブジェクト: `numba.types.unicode_type`, `numba.types.string`
- 文字列方式では: `unicode_type`, `string`

[型指定の動作確認コード](https://github.com/tariaki/numba_behavior_test/blob/main/8a_unicode.py)

<b>速度が遅い機能があちこちにある</b>ので、むしろ<b>Numba化関数内で文字列を不用意に使わない</b>ように注意
さらに文字列から数値への変換 `int(s: str)` などが未実装のため使う機会は少なそう
数値から文字列への変換 `str(x)` は、整数系は成功したが、float系はエラーにならないものの適切な表記が得られなかった

## 9. バイト列系シーケンス
bytesやそれをNumPy配列に格納したものを扱えた。
bytearrayやmemoryviewも扱えた。

### 9-1. バイト列(bytes): `numba.types.Bytes(numba.uint8, 1, "C")`

使用方法
- Python領域のbytesをNumba化関数へ引数渡しできた
- Numba化関数内でバイト列リテラルで生成可能
- なお、Numba化関数内での `bytes` 関数を試してみたがエラーになった
- また、Numba化関数内での `ndarray.tobytes()` メソッドは未実装だった

型指定
- typeof関数を利用
- 型指定オブジェクト `numba.types.Bytes(numba.uint8, 1, "C")`
- 文字列方式では `Bytes(uint8, 1, "C")`

注意点
- Numba関数内で使えるメソッドがかなり少ない
  - 試した範囲では `.startswith`, `.endswith`, スライス, `np.frombuffer` などはできた

### 9-2. bytearray: `numba.types.ByteArray(numba.uint8, 1, "C")`
- Python領域から引数渡しできた
- Numba化関数内での `bytearray` 関数を試してみたがエラーになった
- 型指定オブジェクト: `numba.types.ByteArray(numba.uint8, 1, "C"),`

### 9-3. memoryview: `numba.types.MemoryView(numba.uint8, 1, "C", readonly=True)`
- Python領域から引数渡しできた
- Numba化関数内での `memoryview` 関数を試してみたがエラーになった
- 型指定オブジェクト: `numba.types.MemoryView(numba.uint8, 1, "C", readonly=True)`

### 型指定の動作確認コード
[外部リンク](https://github.com/tariaki/numba_behavior_test/blob/main/9a_bytes.py)

## 10. Numba化関数オブジェクト
Numba化関数の引数として別のNumba化関数を関数オブジェクトとして渡せる。
詳しくは[以前の記事](https://qiita.com/tariaki/items/e11b98b3f5c9cec693ec)

### コンパイル時に固定される関数オブジェクトの型指定
Numba化関数オブジェクトを引数に渡すときの型指定は、関数名を`myjitfunc`とした場合、次のいずれでも得られた。
  - `numba.typeof(myjitfunc)`
  - `myjitfunc._type`
  - `myjitfunc._numba_type`
関数宣言後ならコンパイル前でも型指定オブジェクトがとれた。

ただし、このやり方は入出力型が同一型の個別の関数は別々の型と見なされた。
関数の`@jit`で型指定しなければ気にする必要はないが、 jitclass や AOT で引数に関数オブジェクトを渡して処理の一部を切り換えさせるような利用法は次の入れ替え対応の型指定をする必要があると思われる。


[動作確認コード(以前の記事)](https://qiita.com/tariaki/items/e11b98b3f5c9cec693ec#%E5%AE%9A%E7%BE%A9%E6%B8%88%E3%81%BF%E9%96%A2%E6%95%B0%E3%81%8B%E3%82%89%E5%9E%8B%E3%82%92%E5%BE%97%E3%82%89%E3%82%8C%E3%82%8B%E3%81%8C%E3%81%9D%E3%81%AE%E5%A0%B4%E5%90%88%E3%81%AF%E9%96%A2%E6%95%B0%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E3%81%AE%E5%85%A5%E3%82%8C%E6%9B%BF%E3%81%88%E3%81%AB%E9%9D%9E%E5%AF%BE%E5%BF%9C%E3%81%AB%E3%81%AA%E3%82%8B)


### 関数オブジェクトをコンパイル後に入れ替えさせたい場合の型指定 `numba.types.FunctionType(返り値の型(引数の型のタプル))`
- 型指定オブジェクト: `numba.types.FunctionType(返り値の型(引数の型のタプル))` 
- 型指定文字列: `FunctionType(返り値の型(引数の型のカンマ区切り))`
- <b>`typeof` 関数では得られない</b>

関数オブジェクトを入れ替え可能にするならこちらの型指定方法で。

[動作確認コード(以前の記事)](https://qiita.com/tariaki/items/e11b98b3f5c9cec693ec#%E9%96%A2%E6%95%B0%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E3%81%AE%E5%85%A5%E3%82%8C%E6%9B%BF%E3%81%88%E3%81%AB%E5%AF%BE%E5%BF%9C%E3%81%99%E3%82%8B%E5%9E%8B%E6%8C%87%E5%AE%9A--numbatypesfunctiontype%E8%BF%94%E3%82%8A%E5%80%A4%E3%81%AE%E5%9E%8B%E5%BC%95%E6%95%B0%E3%81%AE%E5%9E%8B)

todo: 入れ替え可能にして処理速度が低下しないか測定する

古いNumbaには無いようで、Numba 0.49 からソースコード (*.py の class * ) に分岐の追加を確認できる


### 11. jitclassインスタンスオブジェクト : `MyJitclass.class_type.instance_type` 
jitclassのインスタンス変数の型として他種のjitclassインスタンスを指定するときなどに。

型指定オブジェクトの取得は次のいずれでも得られた:
- クラス名を`MyJitclass`とした場合
   - `MyJitclass.class_type.instance_type`
   - `numba.extending.as_numba_type(MyJitclass)`
- Python領域に生成したインスタンス名を`myins`とした場合
   - `numba.typeof(myins)`
   - `myins._numba_type_`

`typeof`関数に与えられるのはインスタンス。クラスを与えるとエラーになったので注意。
Numbaの質問サイトやコード例では基本的に `MyJitclass.class_type.instance_type` が使用されていた。


[動作確認コード](https://github.com/tariaki/numba_behavior_test/blob/main/11a_jitclass_instance.py)


### 12. 再帰的な型指定のための、後で定義するNumba型 : `numba.deferred_type()`
連結リストノードや木構造ノードなど再帰的な型をjitclass化しようとすると、jitclassの定義時開始時に自身の型を指定することが必要になる。
このような状況は `numba.deferred_type()` を使うと具体的な型指定を後回しにできるので解決する。

[動作確認コード](https://github.com/tariaki/numba_behavior_test/blob/main/12a_deferred_type.py)

## 終わり
Numbaは数値型、NunPy配列、タプル以外もいろいろな型を使える。
ただし注意点が増えるし、速度を確認していない型を使うと高速化を損ねるおそれがある。
optional型, reflected-list あたりは速度もかなり出るのでNumPy配列では不向きな処理に採用してよさそう。
型指定は全般的にややこしい。
また、一部の型は `typeof`関数で指定できない。

## 参考
https://numba.readthedocs.io/en/stable/reference/pysupported.html
https://stackoverflow.com/questions/53900084/problem-with-reflected-list-signature-in-numba
https://computational-sediment-hyd.hatenablog.jp/entry/2020/07/16/200000