Slide 1

Slide 1 text

Pythonの数学機能を学ぼう! その仕組も学ぼう!! PyCon JP 2024.9.28 Track3 11:10 - 11:40 Recustomer株式会社 CTO Shugo Manabe @curekoshimizu

Slide 2

Slide 2 text

Profile : 眞鍋 秀悟 ( X: @curekoshimizu ) 略歴 ● 京都大学 / 大学院 ○ 入試一位合格 ○ 数学系 (多倍長数値計算を専門) [今回のお話と少し関係が深い] ● Fixstars ○ Executive Engineer ● Mujin ○ Architect ● Preferred Networks ○ Engineering Mananger ● Hacobu ○ 研究開発部部長・CTO室室長 ● want.jp ○ VPoP ● [Now] Recustomer ○ CTO かなり長い間 Pythonを 業務で使ってきた 2

Slide 3

Slide 3 text

今日のわかることの一例 3

Slide 4

Slide 4 text

-5と-6は違う! 4

Slide 5

Slide 5 text

-5と-6はやっぱり違う! 5

Slide 6

Slide 6 text

とかとか 6

Slide 7

Slide 7 text

● Pythonにおける数値型について ● 整数型について深堀り ○ SMALL_INTの導入 ○ CPythonにおける整数型の実装 ○ 整数型と多倍長整数 ○ 四則演算について ● 浮動小数点型について深堀り ● math moduleの関数について深堀り ○ 関数の呼び出し方 ○ Python3.13とmath moduleの展望 ● 最後に 本日お話をすること 7

Slide 8

Slide 8 text

数学的な機能で最も重要なのは? 8

Slide 9

Slide 9 text

数の概念 9

Slide 10

Slide 10 text

Pythonはどのような数を 表現できるのであろうか? 10

Slide 11

Slide 11 text

Pythonが標準的に公開している数表現 1. 整数型 (int) 2. 浮動小数点数型 (float) 3. 複素数型 (complex) 4. 10進多倍長演算型 (Decimal) 5. 有理数型 (Fraction) 11

Slide 12

Slide 12 text

Pythonが標準的に公開している数表現 1. 整数型 (int) 2. 浮動小数点数型 (float) 3. 複素数型 (complex) 4. 10進多倍長演算型 (Decimal) 5. 有理数型 (Fraction) 本日は普段我々が最も使っているであろう、 int・floatという2つの型についてお話していく 12

Slide 13

Slide 13 text

数学の世界との対応 整数 有理数 実数 複素数 13

Slide 14

Slide 14 text

一般的な数学の世界との対応 整数型 (int 型) 有理数型 (Fraction型) ・浮動小数点数型 (float型) ・10進数多倍長型 (Decimal型) ・ 整数型 (int型) : (固定小数点と思うと) 複素数型 (complex型) 14

Slide 15

Slide 15 text

● Pythonにおける数値型について ● 整数型について深堀り ○ SMALL_INTの導入 ○ CPythonにおける整数型の実装 ○ 整数型と多倍長整数 ○ 四則演算について ● 浮動小数点型について深堀り ● math moduleの関数について深堀り ○ 関数の呼び出し方 ○ Python3.13とmath moduleの展望 ● 最後に 本日お話をすること 15

Slide 16

Slide 16 text

その前に あなたにとって その前に理想の整数型とは? 16

Slide 17

Slide 17 text

高速であること? 17

Slide 18

Slide 18 text

n + 1 の計算が 絶対にできること? 18

Slide 19

Slide 19 text

例えば同じスクリプト言語として 比較されることもある JavaScript / TypeScript の場合 19

Slide 20

Slide 20 text

例えば同じスクリプト言語として 比較されることもある JavaScript / TypeScript の場合 n + 1 != n となる 20

Slide 21

Slide 21 text

例えば同じスクリプト言語として 比較されることもある JavaScript / TypeScript の場合 n + 1 != n となる n + 1 == n な場合がある 21

Slide 22

Slide 22 text

n + 1 の計算が n と 違うことを保証するのも プログラミング言語に よって違う (JavaScriptはこれを保証していない言語) 22

Slide 23

Slide 23 text

Go言語のようなコンパイル言語の場合 n + 1 をすると 0 などの裏回った値をとりうる 18446744073709551614 + 1 = 18446744073709551615 18446744073709551615 + 1 = 0 23

Slide 24

Slide 24 text

一方で Pythonは任意の整数nに対して n+1 が数学的な「n+1」が一致する 計算結果になる特徴をもつ (当たり前のようで当たり前ではない特徴) 24

Slide 25

Slide 25 text

● Pythonにおける数値型について ● 整数型について深堀り ○ SMALL_INTの導入 ○ CPythonにおける整数型の実装 ○ 整数型と多倍長整数 ○ 四則演算について ● 浮動小数点型について深堀り ● math moduleの関数について深堀り ○ 関数の呼び出し方 ○ Python3.13とmath moduleの展望 ● 最後に 本日お話をすること 25

Slide 26

Slide 26 text

整数型 (int) 整数を表すものが整数型 例. ● 0 ● 1 ● 2 ● -5 ● -32 ● 123 ● 10000000000000000000000000000000 26

Slide 27

Slide 27 text

整数型 (int) 整数を表すものが整数型 例. ● 0 ● 1 ● 2 ● -5 ● -32 ● 123 ● 10000000000000000000000000000000 今日はPythonの気持ちで 2つのグループに 分けてみましょう 27

Slide 28

Slide 28 text

正解はいろいろあるのですが あるPythonのお気持ちで分類をするとこんな感じな気がする ● 0 ● 1 ● 2 ● -5 ● 123 ● -32 ● 10000000000000000000000000000000 青グループ 赤グループ 28

Slide 29

Slide 29 text

この分類を正当化するために id 関数を導入することにする 29

Slide 30

Slide 30 text

id 関数 オブジェクトのメモリアドレスを示すもの 上の例では 10 という生成されたオブジェクトの位置がわかる 10 この場所 30

Slide 31

Slide 31 text

id 関数の実行結果例 (環境:Python3.12.6, x86-64) x id(x) 0 4354799288 1 4354799320 2 4354799352 -5 4354799128 123 4354803224 -32 4342934352 10000000000000000000000000000000 4347138224 値がとても近い! 31

Slide 32

Slide 32 text

id 関数の実行結果例2回目 (環境:Python3.12.6, x86-64) x id(x) 0 4354799288 1 4354799320 2 4354799352 -5 4354799128 123 4354803224 -32 4342936240 10000000000000000000000000000000 4347135200 値が不変 値が変化 32

Slide 33

Slide 33 text

id 関数の実行結果例3回目 (環境:Python3.12.6, x86-64) x id(x) 0 4354799288 1 4354799320 2 4354799352 -5 4354799128 123 4354803224 -32 4342934352 10000000000000000000000000000000 4347136640 値が不変 値が変化 33

Slide 34

Slide 34 text

確かに何やらこういう分類がある気がしてきた ● 0 ● 1 ● 2 ● -5 ● 123 ● -32 ● 10000000000000000000000000000000 青グループ 赤グループ 34

Slide 35

Slide 35 text

CPythonより抜粋 -5, -4, .., 255, 256 に属する整数は 実はPython初期化時に 既にオブジェクトが生成されている (CPythonの特徴の一つといえる) 35

Slide 36

Slide 36 text

SMALL_INT -5, -4, .., 255, 256 (SMALL_INT) という数字か否かを確認するコードがあり、特殊扱いされていることがわかる 36

Slide 37

Slide 37 text

同様に初期化時に生成されるオブジェクトとは? -5, -4, .., 255, 256 以外にも初期化時に生成されるオブジェクトがある 例えば True・False・None オブジェクトである。 これらTrue・False・None は x is None などと is 関数で比較することができるオブジェクトである 37

Slide 38

Slide 38 text

is関数のCPythonでの実装 is 関数というのはオブジェクトのPointerが一致しているかを確認している 先ほどの True・False・None や -5, -4, .., 255, 256 という数は 初期化時に一度だけ生成されて、 それ以降はそのオブジェクトを使い回す。 そのため、これらのオブジェクトは isの比較をすることができる 38

Slide 39

Slide 39 text

x=y=True x=y=1 x=5000 y=5000 Trueや1は初期化時に1度だけ生成され、 そのobjectをプログラムで使い回す そのため、objectのポインターがすべて同じになる 5000 は 動的にobjectが生成され、 xとyのobjectは別物。そのためそれぞれのオブ ジェクトのポインターが異なるため is の比較は失敗 39

Slide 40

Slide 40 text

isによる比較ができるものとできないもの このように -5, -4, .., 255, 256 という数は is でも比較できてしまう特殊な実装 になっている。 だから、idも同じ値を返していた。 注意. だからといって、数値型を is をつかって比較すべきではなく、= を使って 比較すべきである 40

Slide 41

Slide 41 text

なぜ特殊実装になっているのか? -5, -4, .., 255, 256 という数は よく使われる可能性が高いためオブジェクトの生成コストを抑えるという 目的で生成されています。 例えば 0 という数は頻繁に登場する値 であり、 生成回数を抑えることにより高速化をしています 41

Slide 42

Slide 42 text

● Pythonにおける数値型について ● 整数型について深堀り ○ SMALL_INTの導入 ○ CPythonにおける整数型の実装 ○ 整数型と多倍長整数 ○ 四則演算について ● 浮動小数点型について深堀り ● math moduleの関数について深堀り ○ 関数の呼び出し方 ○ Python3.13とmath moduleの展望 ● 最後に 本日お話をすること 42

Slide 43

Slide 43 text

PyObjectはすべてのルーツ Pythonではすべてのオブジェクトが PyObject の継承になっています。 そのため、 これからお話をする数値型の構造をお話するには、大前提な型になります。 43

Slide 44

Slide 44 text

PyObject型の大雑把な構造 Python3.11のPyObjectの実装 簡易的だが特徴を掴んでいる内容 44

Slide 45

Slide 45 text

PyObject型をC言語がわからない人向けにざっくり 簡易的だが特徴を掴んでいる内容 Python流に表すとこういう構造体 (全体は16Byteのデータ) 45

Slide 46

Slide 46 text

PyObjectは本当に16Byteなのか? sys.getsizeof:生成されたオブジェクトのサイズをPythonから知る方法 46

Slide 47

Slide 47 text

PyObjectからの継承 PyObject PyLongObject : 整数型 PyComplexObject : 複素数型 PyFloatObject : 浮動小数点数型 PyDecObject:10進数多倍長型 Fraction class :有理数 型 47

Slide 48

Slide 48 text

PyObjectからの継承 PyObject PyLongObject : 整数型 PyComplexObject : 複素数型 PyFloatObject : 浮動小数点数型 PyDecObject:10進数多倍長型 Fraction class :有理数 型 こちらをみていくことに! 48

Slide 49

Slide 49 text

PyLongObject型のC言語の正確な構造 PyObject型 を _PyLongValue で Wrapしているような構成 (PyObject型 + _PyLongValue) (C言語で継承を実現しようとすると例えばこうなる) 49

Slide 50

Slide 50 text

PyLongObject型の正確さを犠牲にしたわかりやすい表現 不正確だがわかりやすくした PyLongObject (整数型) PyLongObject のPython流の表現 継承 50

Slide 51

Slide 51 text

● Pythonにおける数値型について ● 整数型について深堀り ○ SMALL_INTの導入 ○ CPythonにおける整数型の実装 ○ 整数型と多倍長整数 ○ 四則演算について ● 浮動小数点型について深堀り ● math moduleの関数について深堀り ○ 関数の呼び出し方 ○ Python3.13とmath moduleの展望 ● 最後に 本日お話をすること 51

Slide 52

Slide 52 text

ob_digit が Python の整数型の “long” な特徴 可変長な構造をもっている! (longっぽい) 52

Slide 53

Slide 53 text

Longという用語は Python2からの系譜 PyLongObject : 任意精度の整数 (python long integer object) PyIntObject : 固定長整数 Python2 PyLongObject Python3 統合 もはや対比的についていた名前 “long”は比べる相手がいなくなったのだが 名前としては残っている 53

Slide 54

Slide 54 text

ob_digit という特徴 ob_digt[0] ob_digt[1] ob_digt[2] ob_digt[3] ob_digt[n-1] (必ず非0) ・・・ nが定まる 大きい桁 メモリーが許す限りいくらでも大きな整数を表現できる 54

Slide 55

Slide 55 text

小さい数の表現 ob_digt[0] (必ず非0) 小さい数は1つだけで成立する 55 n = 1

Slide 56

Slide 56 text

0の表現 empty list 0の場合には ob_digit は0個で成立する 56 n = 0

Slide 57

Slide 57 text

大きい数になると可変長領域が増えて objectの大きさが大きくなる PyObject : 16Byte 一番小さい PyLongObject は 28Byte 大きい整数になるにつれてPyLongObjectは 大きくなる 57

Slide 58

Slide 58 text

(余談) str から int を作ろうとしたときは 任意の大きさにはつくれない 58

Slide 59

Slide 59 text

メモリーの中身をみてみよう -5 -4 -3 -2 -1 0 1 int型の意味 参照カウント (最大値) の意味 SMALL_INT(-5, -4, …) は連続してメモリー上に生成されていた! type(...)で int が 返ってくる理由 59

Slide 60

Slide 60 text

(余談) 参照カウントはメモリーをみなくとも Pythonから取得できる SMALL_INTである1は初期化時に生成して 以降削除することはないobjectなので、最大 値をとっている。 一方で、一般的なobjectである1000は その参照カウントが その関数での参照と、x、yの3となる。 生成と破棄はガベージコレクションに任され ることを表している。 60

Slide 61

Slide 61 text

● Pythonにおける数値型について ● 整数型について深堀り ○ SMALL_INTの導入 ○ CPythonにおける整数型の実装 ○ 整数型と多倍長整数 ○ 四則演算について ● 浮動小数点型について深堀り ● math moduleの関数について深堀り ○ 関数の呼び出し方 ○ Python3.13とmath moduleの展望 ● 最後に 本日お話をすること 61

Slide 62

Slide 62 text

PyLong同士の足し算を眺める 小さな数 (Compact)とは digits[] が0か1であるかということ 1個だけであれば 足し算をしても、桁上りが発生して、 digitsが増えることはほとんどない だから高速に計算できる! 符号部 * (digits[0]) が実際の数なのでその2数 の足し合わせで計算可能 小さな数同士の足し算の場合 (実用上多くの場合でこのケース) 62

Slide 63

Slide 63 text

PyLong同士の足し算を眺める 符号部を考えて、足し算か引きかを決定 複数のdigitsをもつ計算になる O(digits) の計算量の加減算 (多倍長数の計算になり遅い) 63

Slide 64

Slide 64 text

● Pythonにおける数値型について ● 整数型について深堀り ○ SMALL_INTの導入 ○ CPythonにおける整数型の実装 ○ 整数型と多倍長整数 ○ 四則演算について ● 浮動小数点型について深堀り ● math moduleの関数について深堀り ○ 関数の呼び出し方 ○ Python3.13とmath moduleの展望 ● 最後に 本日お話をすること 64

Slide 65

Slide 65 text

浮動小数点数型 (PyFloatObject) PyObject PyLongObject : 整数型 PyComplexObject : 複素数型 PyFloatObject : 浮動小数点数型 PyDecObject:10進数多倍長型 Fraction class :有理数 型 こちら! 65

Slide 66

Slide 66 text

PyFloatObject は simple な構造 PyObjectに一つ 倍精度浮動小数点数 (8Byte) が付与されている構造 PyFloatObjectのPython流な表現 PyFloatObjectのC言語の実装 66

Slide 67

Slide 67 text

整数型に比べて四則演算がとてもシンプル PyFloatObject型の四則演算は単純 CPUが直接計算できる レジスタレベルの計算 ここの中身を 加減乗除 して終わり と 思って大体間違いではないコードになっている 67

Slide 68

Slide 68 text

● Pythonにおける数値型について ● 整数型について深堀り ○ SMALL_INTの導入 ○ CPythonにおける整数型の実装 ○ 整数型と多倍長整数 ○ 四則演算について ● 浮動小数点型について深堀り ● math moduleの関数について深堀り ○ 関数の呼び出し方 ○ Python3.13とmath moduleの展望 ● 最後に 本日お話をすること 68

Slide 69

Slide 69 text

mathモジュールは PyFloatObject型のための計算機能 ● acos ● acosh ● asin ● asinh ● atan ● atan2 ● atanh ● cbrt ● ceil ● comb ● copysign ● cos ● cosh ● degrees ● dist ● e ● erf ● erfc ● exp ● exp2 ● expm1 ● fabs ● factorial ● floor ● fmod ● frexp ● fsum ● gamma ● gcd ● hypot ● inf ● isclose ● isfinite ● isinf ● isnan ● isqrt ● lcm ● ldexp ● lgamma ● log ● log10 ● log1p ● log2 ● modf ● nan ● nextafter ● perm ● pi ● pow ● prod ● radians ● remainder ● sin ● sinh ● sqrt ● tan ● tanh ● tau ● trunc ● ulp とてもたくさん! 69

Slide 70

Slide 70 text

これらの呼び出しで 一体何が起こるのだろうか? math.exp関数を例にとりあげる 70

Slide 71

Slide 71 text

exp関数の呼び出しはどうなっているのだろうか? math_exp 関数を呼ぶと math_1 という関数に exp という関数ポインタ を渡すという実装になっている ここの func が exp 関数の本体! 71

Slide 72

Slide 72 text

exp関数の呼び出しはどうなっているのだろうか? 共有ライブラリ libm の expf64 を呼び出しているだけ (環境は x86_64, linux) 72

Slide 73

Slide 73 text

exp関数の呼び出しはどうなっているのだろうか? ここに書いている通り 「libmのwrapperなのである」 73

Slide 74

Slide 74 text

(余談) Go言語はexp関数を自作している点が面白い 多くのプログラミング言語が libmに依存している中 Go はこういうところに ユニークさをもった言語でもある 74

Slide 75

Slide 75 text

● Pythonにおける数値型について ● 整数型について深堀り ○ SMALL_INTの導入 ○ CPythonにおける整数型の実装 ○ 整数型と多倍長整数 ○ 四則演算について ● 浮動小数点型について深堀り ● math moduleの関数について深堀り ○ 関数の呼び出し方 ○ Python3.13とmath moduleの展望 ● 最後に 本日お話をすること 75

Slide 76

Slide 76 text

mathライブラリに 久々に追加される関数がありますね math.fma が Python3.13 で追加されます 76

Slide 77

Slide 77 text

fma関数とは? fma(x, y, z) をすると x * y + z をする関数です 77

Slide 78

Slide 78 text

そんな普通の関数って なにかすごいんですか? 78

Slide 79

Slide 79 text

fma関数とは? x * y + z という積和演算を1度に計算してくれるんです! (注. CPUがサポートしている場合にのみ) 79

Slide 80

Slide 80 text

いまいち良さがわからないです… という 感想が聞こえてきそう 80

Slide 81

Slide 81 text

コンピュータの 理論ピーク性能ってどうやって 計算するんだったか を思い出してみる 81

Slide 82

Slide 82 text

GeForce RTX 4090 の単精度理論ピーク性能 を計算してみる (注. どんなアーキテクチャでもこの計算方法というわけではない) 16384 (Cores) × 2.52 (GHz) × 2 (FLOPS/CLOCK × Cores)) = 82.58TFLOPS 1度の計算で積和演算 (FMA) という 2回の浮動小数点計算をすることが 前提の「2」 積和演算がないと「1」になってしまう FLOPSとは 1秒間に何回浮動小数点の 計算ができるかの意味 82

Slide 83

Slide 83 text

FMAは計算機の 理論ピーク性能の計算にも 利用されている 性能を引き出すために必要な 命令の一つ 83

Slide 84

Slide 84 text

今日は2024.9.28! Python3.13 2024.10.1 のリリースが より楽しみになりましたね! 84

Slide 85

Slide 85 text

まだまだ進化の止まらない 数学関数の今後に期待です! 85

Slide 86

Slide 86 text

● Pythonにおける数値型について ● 整数型について深堀り ○ SMALL_INTの導入 ○ CPythonにおける整数型の実装 ○ 整数型と多倍長整数 ○ 四則演算について ● 浮動小数点型について深堀り ● math moduleの関数について深堀り ○ 関数の呼び出し方 ○ Python3.13とmath moduleの展望 ● 最後に 本日お話をすること 86

Slide 87

Slide 87 text

本当は喋りたかったがスライドを作ってみた結果 時間の都合上入らなかった内容 ● SMALL_INTだけではなくMEDIUM_INT・COMPACTについて ● INTの多倍長整数演算の実装について ● libmの中身の数学関数の実装について ● Pythonのバイトコードへの展開と工夫について ● 有理数型と浮動小数点数型など数値型の使い分けについて ● 今回登場していたCPythonのデバッグ方法について ● numpyの素晴らしさ (なぜ自前で行列計算ライブラリを実装してはいけないか) いつかこういうお話ができる機会があると 非常にうれしいです! 87

Slide 88

Slide 88 text

全員で7名のエンジニア しかいないため Pythonが大好きな方 大募集中です! 27番ブースでお待ちしております 88

Slide 89

Slide 89 text

ご清聴ありがとうございました @curekoshimizu 89