NumPyのソースコードを見よう!GitHubリポジトリ詳細解説
はじめに
NumPyは、Pythonで科学計算を行う上で不可欠なライブラリです。強力なN次元配列オブジェクトと、線形代数、フーリエ変換、乱数生成などの関数を提供します。多くのPythonライブラリ(SciPy, scikit-learn, pandasなど)がNumPyに依存しており、データサイエンス、機械学習、科学技術計算の分野で広く利用されています。
普段、私たちはNumPyをimport numpy as np
として利用し、提供される関数やオブジェクトを使っています。しかし、その内部で何が行われているのか、どのように高速な計算を実現しているのか、興味を持ったことはありませんか? NumPyのソースコードは、その疑問に答える宝庫です。
ソースコードを読むことは、ライブラリの仕組みを深く理解するだけでなく、プログラミングスキル、特にPythonと低レベル言語(C/C++)の連携や、大規模なソフトウェアプロジェクトの構造を学ぶ上で非常に有益です。また、将来的にNumPyコミュニティに貢献したいと考えるなら、ソースコードの理解は必須となります。
この記事では、NumPyの公式GitHubリポジトリを詳細に解説し、どのようにソースコードを探索すれば良いのか、主要なディレクトリやファイルには何が含まれているのか、そしてNumPyの開発プロセスや貢献方法について、約5000語を目標に掘り下げていきます。
NumPyの内部構造に触れるエキサイティングな旅に出かけましょう。
1. NumPyのGitHubリポジトリへアクセス
NumPyのソースコードは、GitHub上の公式リポジトリで公開されています。
- リポジトリURL: https://github.com/numpy/numpy
まずはこのURLにアクセスしてみましょう。GitHubのウェブインターフェースが表示されます。ここには、コード、コミット履歴、ブランチ、プルリクエスト、イシュー、プロジェクトのwikiなど、リポジトリに関する様々な情報が集約されています。
GitHubの基本的なインターフェースについて簡単に触れておきます。
- Codeタブ: デフォルトで表示されるタブです。リポジトリの最新のソースコード、ディレクトリ構造、ファイル一覧が表示されます。ここから各ファイルをクリックして内容を見ることができます。
- Issuesタブ: バグ報告や機能要望など、プロジェクトに関する議論や未解決の問題が一覧表示されます。NumPyの課題や開発の方向性を知る上で非常に参考になります。
- Pull requestsタブ: 世界中の開発者から提案されたコードの変更(プルリクエスト)が一覧表示されます。新しい機能の実装やバグ修正の過程を見ることができます。マージ済みのプルリクエストを見ることで、過去にどのような変更が取り込まれたのかを知ることもできます。
- Actionsタブ: CI/CD(継続的インテグレーション/継続的デリバリー)のワークフロー実行状況が表示されます。プルリクエストが作成されたり、コードがプッシュされたりするたびに自動的にテストなどが実行され、コードの品質が保たれています。
- Wikiタブ: プロジェクトに関するドキュメンテーションや情報がまとめられていることがあります。NumPyのWikiには、開発に関する情報や歴史などが掲載されています。
- Securityタブ: セキュリティに関する情報や脆弱性の報告が表示されます。
- Insightsタブ: プロジェクトの活動状況や統計情報(コミット数、貢献者など)を見ることができます。
- Settingsタブ: リポジトリの設定に関するタブですが、通常、一般ユーザーはアクセスできません。
ソースコードを読む上で主に利用するのは「Code」タブです。リポジトリのトップページでは、最新のコミット情報、READMEファイル(プロジェクトの概要や使い方)、CONTRIBUTINGファイル(貢献方法)、LICENSEファイルなどが表示されています。
画面上部にあるブランチ選択ドロップダウン(通常はmain
やmaster
と表示されている)で、読みたいバージョンのソースコードを選択できます。通常は最新の開発版であるmain
ブランチを見るのが良いでしょう。特定のリリースバージョンを見たい場合は、タグ(Tags)を選択することも可能です。
さあ、いよいよNumPyの心臓部、ソースコードの世界に足を踏み入れましょう。まずは、リポジトリのディレクトリ構造から見ていきます。
2. リポジトリの主要ディレクトリ解説
GitHubのCodeタブでリポジトリのルートディレクトリを見ると、様々なファイルやディレクトリが存在していることがわかります。それぞれの役割を見ていきましょう。
/
├── .github/ # GitHub Actions (CI/CD) 設定
├── .circleci/ # CircleCI (古いCIシステム) 設定 (現在非推奨?)
├── .gitattributes # Gitの設定ファイル
├── .gitignore # Gitの無視リスト
├── .mailmap # 貢献者の名前解決設定
├── .pre-commit-config.yaml # pre-commit フック設定
├── BENCHMARKS.md # ベンチマークに関する情報
├── CONTRIBUTING.md # 貢献ガイド
├── INSTALL.rst # インストール手順
├── LICENSE.txt # ライセンス情報
├── MANIFEST.in # パッケージに含めるファイルリスト (distutils)
├── Makefile # ビルドスクリプト
├── README.md # プロジェクトの概要
├── THANKS.txt # 貢献者リスト
├── VERSIONS # バージョン情報ファイル
├── ci/ # CI関連スクリプト
├── doc/ # ドキュメンテーションソース
├── mypy_typing/ # 型ヒント関連 (mypy)
├── numpy/ # NumPyの主要なソースコード (Python, C/C++)
├── pyproject.toml # 新しいビルドシステム (PEP 517/518) 設定
├── runtests.py # テスト実行スクリプト
├── setup.cfg # setup.py の設定ファイル (distutils)
├── setup.py # ビルドスクリプト (distutils)
├── tools/ # 開発・ビルド用ツールスクリプト
├── tsconfig.json # TypeScript 設定 (Webサイトなど関連か?)
└── web_frameworks/ # (これはあまり重要でないかも)
※実際のファイル・ディレクトリ構造は変更される可能性があります。
この中で特に重要なのは、numpy/
ディレクトリです。NumPyの機能の大部分がこの中に収められています。しかし、その前に他の主要なファイルやディレクトリについても簡単に触れておきます。
.github/
: このディレクトリには、GitHub Actionsを使ったCI/CDワークフローの設定ファイルが格納されています。プルリクエストのマージ可能性チェックや、様々な環境でのテスト実行、ドキュメンテーションのビルドなど、NumPyの品質管理を自動化するための重要な設定です。workflows/
ディレクトリの中に.yml
ファイルとして格納されています。CONTRIBUTING.md
: NumPyに貢献したい場合に、どのようにすれば良いか、どのようなルールがあるのかが詳細に書かれています。初めて貢献する際は必ず読むべきファイルです。INSTALL.rst
: NumPyのインストール方法に関するドキュメントです。LICENSE.txt
: NumPyのライセンス(Modified BSD License)が記載されています。README.md
: NumPyとは何か、簡単なインストール方法、利用方法などが書かれています。リポジトリのトップページで最初に表示される情報です。doc/
: NumPyの公式ドキュメンテーションのソースコードが格納されています。Sphinxというツールを使って構築されており、reStructuredText形式で書かれています。NumPyの関数の詳細な説明や使い方を知りたい場合は、このディレクトリを読むのが一番正確です。例えば、doc/source/reference/generated/
の中には、各関数やクラスのドキュメンテーションファイルが自動生成されています。pyproject.toml
,setup.py
,setup.cfg
: これらはNumPyをビルドしてインストールするための設定ファイルです。setup.py
は伝統的なdistutils
/setuptools
ベースのビルドスクリプトですが、近年では新しい標準であるPEP 517/518に基づくpyproject.toml
が重要になってきています。これらを読むことで、NumPyがどのようにコンパイルされ、どのファイルがインストールされるのかを知ることができます。NumPyはCやFortranのコードを含んでいるため、ビルドプロセスは比較的複雑です。runtests.py
: NumPyのテストスイートを実行するためのスクリプトです。開発中にコード変更が正しく機能するかを確認するために使われます。tools/
: 開発やビルドを補助する様々なスクリプトが含まれています。例えば、Cコードからドキュメンテーションを抽出するスクリプトや、リリースプロセスを自動化するスクリプトなどがあります。
さて、いよいよ核心である numpy/
ディレクトリを見ていきましょう。
2.1. numpy/
ディレクトリ
numpy/
ディレクトリは、NumPyライブラリの本体です。Pythonモジュールとしてインポートされる際のトップレベルパッケージに相当します。この中には、NumPyの様々な機能を提供するサブモジュールや、C言語で書かれた拡張モジュールのソースが含まれています。
numpy/
├── __init__.py # パッケージの初期化
├── _globals.py # グローバル設定など
├── _pytestrunner.py # テストランナー
├── _version.py # バージョン情報
├── array_api/ # NumPy Array API標準関連
├── arrayprint.py # 配列の表示に関するロジック
├── core/ # NumPyの最も低レベルで重要な機能
│ ├── __init__.py
│ ├── _add_newdocs.py # ドキュメント生成補助
│ ├── _asarray.py # 配列変換補助
│ ├── _dtype_ctypes.py # dtypeとctypesの連携
│ ├── _multiarray_umath.py # C拡張モジュール本体の読み込み
│ ├── _rational_type.py # Rational型 (開発中?)
│ ├── _ufunc_config.py # UFunc設定
│ ├── fromnumeric.py # 配列メソッドのラッパー (np.sum, np.meanなど)
│ ├── include/ # Cヘッダーファイル
│ │ └── numpy/ # 公開用C APIヘッダー
│ ├── overrides.py # __array_function__ 実装など
│ ├── src/ # C/C++ソースコードの本体
│ │ ├── multiarray/ # ndarrayオブジェクト、インデックス、ブロードキャストなど
│ │ └── umath/ # ユニバーサル関数 (UFunc) の実装
│ ├── tests/ # coreモジュールのテスト
│ ├── type_check.py # 型チェックユーティリティ
│ └── ...
├── distutils/ # ビルドシステム関連 (レガシー)
├── fft/ # 高速フーリエ変換
│ ├── __init__.py
│ ├── helper.py
│ ├── tests/
│ └── ...
├── lib/ # 標準ライブラリ的な高レベル関数
│ ├── __init__.py
│ ├── arraysetops.py # 集合演算 (unique, intersect1dなど)
│ ├── arrayterator.py # 配列イテレータ
│ ├── format.py # フォーマット関連
│ ├── function_base.py # linspace, logspaceなど
│ ├── index_tricks.py # 配列生成補助 (mgrid, ogridなど)
│ ├── info.py # モジュール情報
│ ├── npyio.py # ファイル入出力 (.npy, .npz, .txt)
│ ├── polynomial.py # 多項式関連
│ ├── scimath.py # 複素数対応数学関数
│ ├── stride_tricks.py # strides を利用したトリック (as_stridedなど)
│ ├── tests/
│ └── ...
├── linalg/ # 線形代数
│ ├── __init__.py
│ ├── lapack_lite/ # 簡略化されたLAPACK実装 (古い/Fallback用?)
│ ├── linalg.py # 線形代数関数 (Pythonラッパー + 外部ライブラリ連携)
│ ├── tests/
│ └── ...
├── random/ # 乱数生成
│ ├── __init__.py
│ ├── _generator.py # 新しい乱数生成器 (Generator)
│ ├── _pcg64.py # PCG64 ビットジェネレータ
│ ├── _philox.py # Philox ジェネレータ
│ ├── _sfc64.py # SFC64 ビットジェネレータ
│ ├── _mt19937.py # Mersenne Twister (レガシー)
│ ├── bit_generators/ # C/C++で実装されたビットジェネレータ
│ │ ├── __init__.py
│ │ ├── src/
│ │ └── ...
│ ├── distributions.py # 確率分布からのサンプリング
│ ├── tests/
│ └── ...
├── tests/ # トップレベルのテスト
└── typing/ # 型ヒント定義
※これも一部抜粋・簡略化されており、実際の構造は変更されえます。
2.1.1. numpy/core/
– NumPyの心臓部
numpy/core/
は、NumPyの根幹を成す部分です。配列オブジェクト(ndarray
)、データ型(dtype
)、Universal Functions (UFuncs)、インデキシング、ブロードキャストなど、NumPyの最も基本的な機能がここに集まっています。性能が重要な部分はC言語で実装されており、そのソースコードは numpy/core/src/
ディレクトリに格納されています。
numpy/core/__init__.py
:core
サブパッケージの初期化を行います。ここでC拡張モジュールである_multiarray_umath
やmultiarray
,umath
(これらは主に後方互換性や内部的なものになりつつあり、多くの機能は_multiarray_umath
に統合されています) がインポートされます。numpy/core/src/multiarray/
: このディレクトリには、ndarray
オブジェクト自体の実装、配列の作成、データ型(dtype
)の処理、インデキシング、スライシング、ブロードキャスト、アグリゲーションメソッド(sum
,mean
など)の一部などのC言語ソースコードが含まれています。arraytypes.c
,buffer.c
,methods.c
,scalartypes.c
,shape.c
,usertypes.c
など、多くの.c
ファイルがあります。配列の低レベルな挙動を理解したい場合は、このディレクトリのコードを読む必要があります。numpy/core/src/umath/
: このディレクトリには、Universal Functions (UFuncs) のC言語ソースコードが含まれています。UFuncsは、配列の要素wiseな演算(加算、減算、三角関数、指数関数など)を高速に実行するための仕組みです。ufunc_object.c
がUFuncオブジェクトの定義を、loops.c.src
から生成されるファイルが実際の計算ループを実装しています。ufunc_generate.py
のようなスクリプトでこれらのCコードが生成されることもあります。numpy/core/include/numpy/
: NumPyのC APIを利用する際にインクルードするヘッダーファイル群です。PythonのC拡張を書く際に、NumPyの機能を利用するための関数や構造体の定義が含まれています。NumPyのC言語レベルでの内部構造を知る上で非常に参考になります。特にndarrayobject.h
はndarray
構造体の定義が含まれており重要です。numpy/core/fromnumeric.py
:np.sum()
,np.mean()
,np.max()
といった、配列のメソッドとして定義されているもの(ndarray.sum()
,ndarray.mean()
など)と同じ名前を持つトップレベルの関数が定義されています。これらの関数は通常、入力がNumPy配列であるかを確認し、内部で対応する配列メソッドを呼び出すラッパーです。ユーザーがnp.sum(arr)
と書くかarr.sum()
と書くかの違いは主にここで吸収されます。numpy/core/_multiarray_umath.py
: このファイル自体は短いPythonコードですが、実際にはここでC拡張モジュール_multiarray_umath
がインポートされます。NumPyの多くの機能は、このC拡張モジュールによって提供されています。PythonコードからCコードへの橋渡しが行われているポイントです。
core
ディレクトリは、NumPyのパフォーマンスの秘密が隠されている場所です。C言語コードが主体となるため、Pythonの知識だけでなくC言語の知識もあるとより深く理解できます。
2.1.2. numpy/lib/
– 標準ライブラリ的な機能
numpy/lib/
ディレクトリには、numpy.core
ほど低レベルではないが、NumPy配列を操作する上で頻繁に利用される便利な関数群が格納されています。ここに含まれる機能は、比較的Pythonで書かれているものが多いですが、内部でcore
の機能やUFuncを利用しています。
numpy/lib/function_base.py
:np.linspace()
,np.logspace()
,np.meshgrid()
といった、配列を生成したり、配列の基本操作を行う関数が含まれています。numpy/lib/npyio.py
: NumPy独自のバイナリフォーマットである.npy
や.npz
ファイル、あるいはテキストファイル (.txt
,.csv
など) の読み書きを行うnp.load()
,np.save()
,np.savetxt()
といった関数が含まれています。numpy/lib/arraysetops.py
: 配列の集合演算を行う関数が含まれています。np.unique()
,np.intersect1d()
,np.union1d()
,np.setdiff1d()
などです。numpy/lib/index_tricks.py
: 配列のインデックス操作を補助する便利なオブジェクトや関数が含まれています。np.mgrid
,np.ogrid
,np.r_
,np.c_
など、特に科学計算やデータ処理で配列を効率的に生成・結合する際に役立ちます。これらは特殊なオブジェクトやスライス機構を利用しています。numpy/lib/stride_tricks.py
: 配列のstrides属性を利用して、データをコピーせずに形状を変更したり、スライディングウィンドウを作成したりするための関数が含まれています。np.lib.stride_tricks.as_strided()
は、メモリ上で連続していないデータを連続しているかのように見せかける強力な(そして注意が必要な)機能です。
lib
ディレクトリは、NumPyが提供する豊富な機能の幅広さを示しています。ここを読むことで、様々な配列操作テクニックのPython実装を知ることができます。
2.1.3. その他の主要なサブモジュール
numpy/linalg/
: 線形代数に関する機能を提供します。行列の積、行列式、逆行列、固有値分解、特異値分解などの関数が含まれています。これらの関数の多くは、LAPACKやBLASといった外部の高性能な線形代数ライブラリを利用しています。linalg.py
はこれらの外部ライブラリへのインターフェースを提供するPythonラッパーとして機能しています。numpy/fft/
: 高速フーリエ変換(FFT)に関する機能を提供します。1次元、2次元、N次元のFFTおよび逆変換、周波数解析に関する関数が含まれています。こちらも、FFTWのような高性能なFFTライブラリを利用することが多いですが、NumPy独自の簡易的な実装や、外部ライブラリへのインターフェースがPythonコードで書かれています。numpy/random/
: 乱数生成に関する機能を提供します。様々な確率分布からのサンプリング機能が含まれています。NumPy 1.17以降は、新しい乱数生成アーキテクチャが導入され、ビットジェネレータと分布サンプラーが分離されています。numpy/random/bit_generators/
ディレクトリには、C/C++で実装された様々なビットジェネレータ(PCG64, Philox, SFC64など)のソースコードがあります。numpy/random/distributions.py
には、これらのビットジェネレータを利用して様々な確率分布からサンプリングするPythonコードが含まれています。NumPyの乱数生成は、再現性や性能が重要なため、興味深い実装が多く含まれています。
3. NumPyのソースコードを読むためのヒント
NumPyのソースコードは量が多く、PythonだけでなくC/C++も含まれているため、どこから手をつけて良いか迷うかもしれません。以下にいくつかのヒントを示します。
3.1. Pythonコードから追跡する
最も一般的なアプローチは、普段自分が使っているNumPyの関数やオブジェクトが、ソースコードのどこで定義されているかを追いかけることです。
例えば、np.sum()
関数がどのように機能しているかを知りたいとします。
- まず、PythonインタプリタやIDEで
np.sum?
やhelp(np.sum)
を実行してみます。これにより、関数のドキュメンテーションが表示され、定義されているファイル名と行番号が示されることがあります。(例:File: .../site-packages/numpy/core/fromnumeric.py
) - GitHubリポジトリでそのファイル (
numpy/core/fromnumeric.py
) を開きます。 -
ファイル内で
sum
という関数を探します。定義を見ると、多くの場合、入力配列のsum()
メソッドを呼び出していることがわかります。python
def sum(a, axis=None, dtype=None, out=None, keepdims=False,
initial=None, where=True):
# ... docstring ...
return _reduction(a, axis=axis, dtype=dtype, out=out,
keepdims=keepdims, initial=initial, where=where,
ufunc=add)
ここで_reduction
という内部関数が呼ばれています。さらに_reduction
を追いかけると、最終的には入力配列a
のメソッドa.sum(...)
を呼び出している部分が見つかるはずです。python
def _reduction(a, axis, dtype, out, keepdims, initial, where, ufunc):
# ... many checks and preparations ...
res = umr_methods(a, axis, dtype, out, keepdims, initial, where, ufunc)
# ...
return res
ここでumr_methods
というさらに内部的な関数が登場します。これを追いかけると、NumPy配列オブジェクト(ndarray
)の内部メソッドを呼び出している箇所にたどり着きます。このメソッド自体は、NumPyのC言語コードで実装されています。
このように、Pythonコードをステップバイステップで追っていくことで、目的の機能がどのファイルのどの部分で定義されているかを特定できます。そして、Pythonコードが内部でC/C++コードを呼び出しているポイントを見つけることができます。
3.2. C/C++コードの役割理解
NumPyのパフォーマンスクリティカルな部分はC/C++で書かれています。これは、Pythonのコードだけでは実現できない計算効率を達成するためです。Cコードは、主に以下の役割を担っています。
ndarray
オブジェクトの実装: メモリ管理、データ型の処理、形状やストライドの計算、基本的なインデキシングやスライシング。- Universal Functions (UFuncs) の実装: 要素wiseな演算(
+
,-
,sin
,cos
など)を、様々なデータ型、次元数、ブロードキャストに対応して高速に実行するためのループ処理。 - その他の低レベルなアルゴリズム: ソート、検索、一部の数学関数など。
Pythonコードは、ユーザーからの入力を受け取り、データ型や形状のチェック、Cコードに渡す引数の準備など、高レベルなロジックを担当します。そして、実際の計算処理はCコードに委譲します。
NumPyのCコードを読むには、PythonのC APIに関する知識があると役立ちますが、必須ではありません。まずはCコードがどのような目的で書かれているのか(配列のメモリ操作なのか、要素ごとの計算なのかなど)を理解することから始めると良いでしょう。特に、numpy/core/include/numpy/ndarrayobject.h
や ufuncobject.h
といったヘッダーファイルには、NumPyの内部構造に関する重要な定義が含まれています。
3.3. PythonとC/C++の連携
NumPyは主にPythonのC APIを使ってPythonとCコードを連携させています。CythonやPybind11のようなツールも使われることがありますが、NumPyのコア部分は伝統的なC APIが多いです。
C拡張モジュールは、Pythonのモジュールとしてコンパイルされ、Pythonから import
されます。例えば、numpy.core.__init__.py
で from . import _multiarray_umath
のようにC拡張モジュールがインポートされます。
Cコードで定義された関数は、Pythonから呼び出せるようにエクスポートされます。このエクスポートの仕組みや、PythonオブジェクトとCのデータ構造の間でのデータ変換(例えばPythonのリストをCの配列に変換したり、Cの計算結果をNumPy配列オブジェクトとしてPythonに返したり)がC APIを通じて行われます。
PythonコードでC関数呼び出しの痕跡を見つけたら、次は対応するCソースコード (numpy/core/src/multiarray/
や umath/
など) を探してみましょう。関数名がPython側とC側で少し異なる場合もありますが、関連するファイル名やコードの構造から推測できます。
3.4. ドキュメンテーション (doc/
) やテストコード (numpy/testing/
, */tests/
) の活用
ソースコードを読む上で、ドキュメンテーションやテストコードは非常に役立ちます。
- ドキュメンテーション (
doc/
): 関数やクラスの公式な説明です。機能の目的、引数、戻り値、利用例などが記載されており、コードの挙動を理解する上で不可欠です。doc/source/reference/generated/
以下にあるファイルは、多くの場合、ソースコード中のDocstringから自動生成されています。 - テストコード (
numpy/*/tests/
): 各機能がどのように使われるべきか、どのような入力に対してどのような出力が期待されるかを示す具体的な例の宝庫です。テストコードを読むことで、その機能の意図やエッジケースでの挙動を理解できます。また、バグ修正のプルリクエストを読む際には、追加されたテストコードを見ることが、修正内容を理解する近道となります。numpy/core/tests/
,numpy/lib/tests/
,numpy/linalg/tests/
など、各サブモジュールに対応するtests/
ディレクトリが存在します。
3.5. Git Blameの利用
GitHubのファイル表示画面では、「Blame」ボタン(またはメニュー)を使うことができます。これは、ファイルの各行がどのコミットで誰によって変更されたかを表示する機能です。特定のコード行がなぜこのように書かれているのか、過去にどのような変更があったのかを知りたい場合に非常に有効です。バグの履歴を追ったり、特定の機能がいつどのように導入されたかを調べたりするのに役立ちます。
3.6. IssueやPull Requestの履歴を追う
特定の機能やバグに興味がある場合、GitHubのIssuesタブやPull requestsタブで関連するキーワードを検索してみましょう。過去の議論や変更履歴を見ることができます。これにより、その機能がなぜ必要とされたのか、実装にあたってどのような課題があったのか、どのような議論が交わされたのかといった背景情報を得られます。クローズされたIssueやマージされたPull Requestも重要な情報源です。
4. 特定の機能のソースコードを追う例
より具体的に理解するために、いくつかの簡単なNumPy機能について、ソースコードのどこを見れば良いかを例示します。
4.1. 例1: np.sum()
(UFuncを利用した集計)
np.sum()
関数は、配列の要素の合計を計算します。前述の通り、Pythonコードとしては numpy/core/fromnumeric.py
に定義されています。この関数は内部で配列の sum()
メソッドを呼び出し、最終的にUniversal Function (UFunc) の仕組みを利用して実際の計算を行います。
- Pythonラッパー:
numpy/core/fromnumeric.py
のsum
関数 definition。 - 内部処理:
_reduction
関数などを経由し、配列のsum()
メソッド呼び出しへ。 - C言語による実装:
ndarray
オブジェクトのsum
メソッドの実体はCコード (numpy/core/src/multiarray/methods.c
など) に実装されています。このメソッドは、データの型や軸(axis)に応じて適切なUFuncの「reduction」機能(軸に沿って演算を適用し、次元を減らす機能)を呼び出します。 - UFuncの実装: 合算を行うUFuncである
add
が利用されます。add
UFuncの各データ型に対する計算ループの実装は、numpy/core/src/umath/
ディレクトリ内のCコード(例えばadd.c
や、自動生成されたファイル)にあります。これらのファイルには、整数、浮動小数点数など、様々なデータ型に対して要素ごとの加算を行うための効率的なループ処理が書かれています。
このパスを追うことで、ユーザーが np.sum(arr)
と書いたコードが、どのようにPythonコードからCコードに渡され、最終的にハードウェアで高速に実行される低レベルな計算ループにつながるのかを理解できます。
4.2. 例2: np.array()
(配列生成)
np.array()
は、PythonのリストやタプルなどのシーケンスからNumPy配列を作成する最も基本的な関数です。
- Pythonのトップレベル関数:
numpy/__init__.py
を見ると、array
関数がcore.array
としてインポートされていることがわかります。これはnumpy/core/__init__.py
で定義されているarray
関数を指しています。 - 主要な配列生成関数:
numpy/core/records.py
にarray
関数が定義されていることがあります。(※実際の配置はバージョンにより異なる可能性あり。探す場合はgrep
やIDEの機能を使うと便利です。)この関数は、入力オブジェクト、データ型、コピーするかどうかなどの引数を受け取ります。 - C言語での配列オブジェクト作成:
np.array()
の内部では、最終的にNumPyのC API関数であるPyArray_FromAny
やPyArray_NewFromDescr
などが呼び出されます。これらの関数は、numpy/core/src/multiarray/
ディレクトリのCコード (ctors.c
,convert_datatype.c
など) に実装されています。これらのCコードは、入力データ(Pythonオブジェクト)を受け取り、メモリを確保し、NumPy配列オブジェクト (PyArrayObject
構造体)を初期化し、データをコピーまたは参照する処理を行います。データ型の推論や、非連続なデータから連続な配列を作成する処理などもここで行われます。
np.array()
の実装を追うことで、Pythonの様々なデータ構造がどのようにNumPyの効率的なCベースの配列構造に変換されるのかを知ることができます。メモリ管理やデータ型の扱いなど、NumPyの基本的な仕組みが理解できます。
5. NumPyの開発プロセス
NumPyは大規模なオープンソースプロジェクトであり、世界中の多くの貢献者によって開発されています。その開発は、GitHub上で透明に進められています。
- Issue Driven: バグ報告、機能要望、改善提案などは、GitHubのIssuesタブで管理されます。新しい開発は、しばしば既存のIssueに基づいて開始されます。Issueには議論や関連するプルリクエストが紐付けられています。
- Pull Request Based Development: コードの変更はすべてプルリクエスト(PR)として提案されます。貢献者は自分のGitHubリポジトリにNumPyをフォークし、そこで変更を加えてから、本家リポジトリに対してプルリクエストを作成します。
- Code Review: 作成されたプルリクエストは、NumPyのコア開発者や他のコミュニティメンバーによってレビューされます。コードの品質、設計、テストの有無、ドキュメンテーションの更新などがチェックされ、コメントや改善提案が行われます。このコードレビュープロセスは、コードの品質を高く保つ上で非常に重要です。
- Testing: すべてのプルリクエストに対して、自動テストが実行されます。これはGitHub ActionsなどのCIシステムによって行われます。NumPyには広範なテストスイート (
numpy/*/tests/
) があり、様々なプラットフォームやPython/NumPyのバージョンでコードが正しく動作することを確認します。テストに失敗するPRは、通常マージされません。 - Continuous Integration (CI): コードがリポジトリにプッシュされたり、プルリクエストが更新されたりするたびに、自動的にビルド、テスト、リンティングなどが実行されます。これにより、問題が早期に発見されます。
- Consensus and Discussion: 新しい機能の追加や大きな変更については、Issueやプルリクエスト、開発者メーリングリスト(NumPy Developers list)などで議論が行われ、コミュニティの合意形成が図られます。
- Release Cycle: NumPyは定期的に新しいバージョンがリリースされます。リリース計画はコミュニティで管理され、ベータ版やリリース候補版を経て正式リリースとなります。
doc/release/
ディレクトリにはリリースに関する情報や変更ログが格納されています。
GitHubのIssuesタブやPull requestsタブを眺めるだけでも、NumPyコミュニティの活発な活動や、開発者がどのような課題に取り組んでいるのかを知ることができます。
6. NumPyへの貢献
NumPyはオープンソースプロジェクトであり、誰でも貢献することができます。「ソースコードを読む」というステップは、「ソースコードに貢献する」ための最初の、そして最も重要なステップです。
NumPyへの貢献は、コードを書くことだけではありません。以下のような様々な形で貢献できます。
- バグ報告: NumPyを使っていてバグを見つけたら、Issueとして報告する。
- ドキュメンテーションの改善: ドキュメントの誤字脱字を修正したり、分かりにくい説明を改善したり、新しい機能のドキュメントを追加したりする。これは貢献しやすい入り口の一つです。
doc/source/
ディレクトリの.rst
ファイルを編集します。 - テストの追加: バグ報告に関連して、そのバグを再現するテストケースを追加する。新しい機能に対してテストを書く。
- コードの修正・実装: バグを修正したり、新しい機能を実装したりする。簡単なバグ修正から始めるのがおすすめです。
- コードレビュー: 他の人のプルリクエストをレビューし、改善点やコメントを提供する。
貢献のステップ(概要):
- 貢献ガイドを読む: まず
CONTRIBUTING.md
ファイルを読みましょう。貢献のポリシー、コード規約、開発環境のセットアップ方法、プルリクエストの作成方法などが詳しく書かれています。 - 開発環境のセットアップ: NumPyのソースコードをビルドできる環境を整えます。これには、適切なバージョンのPython、C/C++コンパイラ、Fortranコンパイラ(LAPACK/BLASを使う場合)、そしてビルドに必要なライブラリなどが含まれます。
INSTALL.rst
やCONTRIBUTING.md
に手順が記載されています。多くの場合、pip install -e .
のように開発モードでインストールすることで、ソースコードの変更がすぐに反映されるようになります。 - 解決したい課題を見つける: GitHubのIssuesタブで、初心者向けのIssue(
good first issue
やeasy
などのラベルが付いていることが多い)を探すか、自分が遭遇したバグや改善したい点を見つけます。 - コードを書く: 課題を解決するコードを書きます。既存のコードを参考にしたり、ドキュメンテーションを読んだりしながら進めます。コード規約(NumPy Style Guide)に従うことが重要です。
- テストを書く: 変更によって既存の機能が壊れていないか、新しい機能が意図通りに動くかを確認するためのテストコードを書きます。
- テストを実行する: ローカル環境でテストを実行し、パスすることを確認します。
runtests.py
スクリプトが便利です。 - コミットとプッシュ: 変更をGitでコミットし、自分のGitHubリポジトリのブランチにプッシュします。
- プルリクエストを作成する: 自分のリポジトリのブランチから、本家
numpy/numpy
リポジトリのmain
ブランチに対してプルリクエストを作成します。プルリクエストの説明には、どのような変更を行ったのか、なぜその変更が必要なのか(関連するIssueをリンクする)、テスト結果などを明確に記載します。 - レビューと修正: コア開発者やメンテナからレビューコメントが付きます。コメントに基づいてコードを修正し、再度プッシュします。このやり取りを繰り返して、コードが完成度を高めていきます。
- マージ: コードがレビューを通過し、CIテストがすべてパスすれば、プルリクエストが
main
ブランチにマージされます。あなたの変更がNumPyに取り込まれたことになります!
貢献は、NumPyをより良くするだけでなく、自分自身のスキルアップにもつながる素晴らしい経験です。
7. よくある疑問と回答
- Q: NumPyはどのプログラミング言語で書かれていますか?
- A: 主にPythonとC言語で書かれています。一部、Fortran(特に線形代数やFFTで外部ライブラリを利用する場合)、Cython(ごく一部)、そしてビルドシステムにはPythonやシェルスクリプトが使われています。コアの計算部分はC言語が中心です。
- Q: ソースコードをビルドするにはどうすれば良いですか?
- A: GitHubリポジトリをクローンし、適切な開発環境(コンパイラなど)をセットアップした後、リポジトリのルートディレクトリで
pip install .
または開発モードでpip install -e .
を実行します。NumPyのビルドプロセスは複雑な部分もあるため、INSTALL.rst
やCONTRIBUTING.md
を参照して、依存関係や特定のビルドオプションについて確認することが重要です。
- A: GitHubリポジトリをクローンし、適切な開発環境(コンパイラなど)をセットアップした後、リポジトリのルートディレクトリで
- Q: C言語部分はどのようにPythonから呼び出されますか?
- A: PythonのC APIを使用しています。Cコードで定義された関数やデータ構造(
PyArrayObject
など)は、Pythonからimport
されたり、Pythonオブジェクトのメソッドとして呼び出されたりするように設定されています。NumPy独自の型システムやUFuncの仕組みも、Python C API上に構築されています。
- A: PythonのC APIを使用しています。Cコードで定義された関数やデータ構造(
- Q: 特定の機能のコードを探すには、どこから始めれば良いですか?
- A: まずは、その機能に対応するNumPyのトップレベル関数やオブジェクトが、どのサブモジュール(
core
,lib
,linalg
,fft
,random
など)に属するかを推測します。Pythonインタプリタでhelp()
を使うか、公式ドキュメントでその機能の説明ページを見つけるのが手っ取り早いです。次に、そのサブモジュールの__init__.py
ファイルや、機能名に対応するPythonファイル(例:linalg.py
,function_base.py
)を探します。そこでC拡張モジュールがインポートされていたり、C APIを呼び出すコードが見つかれば、対応するCソースディレクトリ(core/src/multiarray/
,core/src/umath/
など)をさらに調べます。GitHubのコード検索機能も便利です。
- A: まずは、その機能に対応するNumPyのトップレベル関数やオブジェクトが、どのサブモジュール(
- Q: ソースコード中のFortranファイルは何ですか?
- A:
numpy/linalg/lapack_lite/
のようなディレクトリに存在するFortranファイルは、NumPyが内部で使用する線形代数ライブラリ(LAPACKやBLAS)の簡易実装やラッパーであることがあります。NumPyは通常、システムにインストールされている高性能なLAPACKやBLASライブラリ(OpenBLAS, MKLなど)を優先して使用しますが、これらが利用できない場合のフォールバックや、特定機能の実装のためにFortranコードが含まれていることがあります。
- A:
- Q:
numpy/distutils/
は何ですか?- A:
numpy.distutils
は、Fortranコードを含む科学技術計算ライブラリをPythonのセットアップツールでビルドするためにNumPy開発チームが作成した拡張機能です。かつてはNumPy自身のビルドにも使われていましたが、現在はより一般的なPythonのビルドシステム(PEP 517/518など)への移行が進んでおり、distutils
ディレクトリはレガシーな部分や互換性のために残っていることが多いです。新しいビルドプロセスでは、pyproject.toml
やsetup.py
が主要なエントリーポイントとなります。
- A:
8. まとめ
この記事では、NumPyのGitHubリポジトリを詳細に解説し、そのソースコードの構造、主要なディレクトリやファイル、そしてコードを読むための具体的なアプローチについて説明しました。
NumPyのソースコードは、Python、C、そしてそれらを繋ぐ複雑な仕組みが組み合わさった大規模なプロジェクトです。最初は戸惑うかもしれませんが、以下のステップを踏むことで、徐々に理解を深めることができます。
- リポジトリの全体構造を把握する: 各ディレクトリがどのような役割を担っているのかを知る。
- Pythonコードから追跡する: 普段使う関数やオブジェクトから出発し、コードの呼び出し階層を追いかける。
- Cコードの役割を理解する: どの部分がCで書かれているのか、なぜCが使われているのか(パフォーマンス、低レベル操作)を理解する。
- ドキュメンテーションやテストコードを活用する: コードの意図や使用例を理解する助けとする。
- Gitの機能を使う: Blame機能やコミット履歴でコードの背景を知る。
- コミュニティのリソースを活用する: IssueやPull Requestの議論を追う。
ソースコードを読むことは、NumPyという強力なツールをより深く理解するための最高の学習方法です。NumPyがどのように高性能を実現しているのか、Pythonと低レベル言語がどのように連携しているのか、大規模なオープンソースプロジェクトがどのように組織されているのかなど、多くのことを学ぶことができます。
そして、ソースコードを読むことに慣れてきたら、ぜひNumPyコミュニティへの貢献にも挑戦してみてください。ドキュメンテーションの小さな修正からでも歓迎されます。あなたの貢献が、世界中のNumPyユーザーにとっての改善につながります。
NumPyのソースコードは広大で深い世界ですが、一歩ずつ探求することで、きっと新たな発見や学びがあるはずです。この記事が、あなたのNumPyソースコード探求の旅のガイドとなれば幸いです。Happy code reading!