Julia入門&導入
目次
Julia入門&導入¶
※一部、所属大学における講義資料として作成中のものを含みます。
Juliaとは¶
Juliaは、MITで開発された、数値計算を得意とするプログラミング言語で、 PythonやR、MATLABなどの言語の良いところを取り入れて開発されていて、高速に動作するという特徴を有する。
作者らの記事Why We Created Juliaから重要な部分を引用:
We want a language that's open source, with a liberal license. We want the speed of C with the dynamism of Ruby. We want a language that's homoiconic, with true macros like Lisp, but with obvious, familiar mathematical notation like Matlab. We want something as usable for general programming as Python, as easy for statistics as R, as natural for string processing as Perl, as powerful for linear algebra as Matlab, as good at gluing programs together as the shell. Something that is dirt simple to learn, yet keeps the most serious hackers happy. We want it interactive and we want it compiled.
ここにJulia言語の理念と特徴が集約されている。
この講義でJuliaを使用する理由は、限られた時間と少ない労力(コーディングや実行時間)でより多くのトピックを学ぶためである。
Pythonとの基本文法の違い¶
Pythonは0-based indexだが、Juliaは1-based indexで、配列arr
の先頭の要素はarr[1]
となる。
その意味では、PythonはC言語に近く、JuliaはMATLABやFortranに近いと言える。
Pythonの場合は、インデントを使ってブロックを表現するが、Juliaの場合は、begin
とend
を使ってブロックを表現する。
また、for
文やif
文の後には、明示的にend
を書く必要がある。
なお、Pythonで関数や条件式などを書く際に必要だったコロン:
は不要になる。
個人的には、インデントを文法とするPythonの方が、コードの見た目がスッキリしやすいようにも思うが、 エディタの設定によっては、インデントがタブとスペースが混在してしまったり、 編集の過程でインデントが崩れてしまい、バグの温床になったりすることがあるので、 Juliaの方が、ブロックに対してambiguityが少ないというメリットがあるように思う。
またNumpyで言うところのブロードキャストをdefaultで有していて、v
がFloat64の一次元配列とすると
u = a .* v
でスカラー倍をしたベクトルu
を作ることができる。
また、
v .= a .* u
と書くと、v
(が既に定義されていれば)の各要素にa .* u
の各要素を代入してくれる。
単に
v = a .* u
A = a .* B
などと書くと、v
やA
を新たに定義してしまい、メモリ確保が発生する。
ブロードキャストは、特に大次元のベクトルや行列を操作する際、メモリを節約したり更新操作を速く行うために有用である。
また、@.
というマクロをつけることで、ブロードキャストを明示的に書く必要がなくなり、
@. A = a * B * C
などとしても\(A .= a .* (B .* C)\)と同じ意味になるが、 なれないうちは、ブロードキャストを明示的に書いた方が良い。
対応早見表¶
Julia |
Python |
|
---|---|---|
文字列の出力 |
|
|
変数の定義 |
|
|
配列の定義 |
|
|
配列の先頭の要素の取得 |
|
|
配列の末尾の要素の取得 |
|
|
配列の要素の変更 |
|
|
配列の長さ |
|
|
行列積 |
|
|
関数の定義 |
|
|
値なし |
|
|
高速化のtips¶
JuliaはPythonのように動的に使用することができるが、 Just-In-Time(JIT)コンパイルといって、実行時にソースコードを"幾つかの工程"を踏んで機械語に翻訳して実行することで (JITコンパイル自体の時間が生じるものの)非常に高速に実行することができる。
Juliaでコードを書いて高速に実行させるtipsとしては、コンパイラにちゃんとヒントを与えてやる事が挙げられる。 Juliaでは明示的に型を指定することは必須ではなく、きちんとコードを書けば勝手に速いコードを生成・実行してくれる。 ただし、演算の途中で型の不一致などがあると、勝手に速いコードを生成する妨げになり、コードが遅くなるため、 そうした想定外の型の不一致を避けるために、関数の引数や返り値の型を明示的に指定することが有用となる。
特に、行列やベクトルを定義するときは、その型(とサイズ)を明示的に指定して定義するのが良い。
A = zeros(Float64,n,m) # 倍精度floatを要素に持つn×m行列を0行列で定義
B = randn(Float32,n,m) # 単精度floatを要素に持つn×m行列を正規乱数をつかって定義
その他、高速化のtipsについては公式ドキュメントのPerformance Tipsが詳しい。
Juliaの実行環境¶
Juliaを動かす方法は幾つかあるが、この講義では、
Juliaをインストールしてターミナル(VS code経由など含む)ないし、JupyterNotebook環境で実行する方法を推奨とする。
一度インストールしてIJulia.jl
パッケージをインストールすれば、.ipynb形式のファイルのJupyter用カーネルを用意することができる。
Juliaのインストール¶
では、実際にJuliaをインストールする方法をOSごとに紹介しよう。
(著者の意見では)Juliaの環境構築はPythonの環境構築よりも遥かに簡単で安全である。
以下に示すOSごとの手順のほか、最近(v1.10くらいの頃から?)は公式ページでも、後述のjuliaupを用いる方法が推奨されている。
Macの場合¶
Julia言語の公式ページから、macOS用の.dmgファイル(長期サポート(LTS)のv1.6か最新版)をダウンロードする。
※Apple Siliconチップの場合は、macOS(Apple Silicon)を選択するほうが性能上良いが、どちらでも動かすことは可能。ダウンロードしたファイルを開き、Juliaアプリケーションをアプリケーションフォルダに移動する。
PATHを通すために、ターミナルを開き下記を実行する。
※Julia-1.6の部分やbashrcの部分は、適宜ダウンロードしたJuliaのバージョンに置き換えたり使用しているシェルに合わせて変更すること。echo 'export PATH="/Applications/Julia-1.6.app/Contents/Resources/julia/bin:$PATH"' >> ~/.bashrc
Mac OSの場合、(いつからだったか)標準のシェルがbashからzshに変更されているので、zshを使う場合は、
~/.bashrc
ではなく~/.zshrc
に追記すること。ターミナルで
source ~/.bashrc
などを行い変更を反映したのち、julia
と入力して、JuliaのREPL(対話環境)が起動することを確認する。
Dockerを使う方法もあるが、その場合は(とくにApple Siliconの場合は)適切なLinuxイメージを用いれば、以降のLinuxの場合の手順に従えばよい。
Windowsの場合¶
WSL2をインストールする。
下記のLinuxの場合の手順に従う。
Linux(WSL)の場合¶
Julia言語の公式ページから、Linux用のtar.gzファイル(長期サポート(LTS)のv1.6か最新版)をダウンロードする。
※ アーキテクチャに応じたものを選択すること。tar.gzを解凍すると
julia-1.9.0
というフォルダができる。この中のjulia-1.9.0/bin/julia
が実行ファイルなので、 この実行ファイルにPATHを通すために、ターミナルを開き下記のように実行する。echo 'export PATH="/path/to/julia-1.9.0/bin:$PATH"' >> ~/.bashrc
1.9.0の部分はダウンロードしたバージョンに依る。
/path/to/
は適宜ファイルの場所に置き換えること。とくにWSLの場合は、ダウンロードしたものもLinuxのホーム以下に置いておこう。
例えば、/home/username/julia-1.9.0/bin/julia
というパスにjuliaの実行ファイルjulia
がある場合、export PATH=/home/username/julia-1.9.0/bin
をホーム直下の隠しファイルである~/.bashrc
等に追記する。bashrcとは、bashシェルが起動する際に読み込まれる設定ファイルであり、zchなど他のシェルを用いる場合は、それぞれのシェルに合わせた設定ファイルに追記すること。Ubuntuは(少なくともこれを執筆した当時は)デフォルトでbashを使っているので、bashrcに追記する。
ターミナルで
source ~/.bashrc
などを行い変更を反映したのち、julia
と入力して、JuliaのREPL(対話環境)が起動することを確認する。
Juliaパッケージ¶
Juliaには、独自のパッケージマネージャが組み込まれており、それ自体がPkg
というパッケージになっている。
Juliaでパッケージを使用する際は、using パッケージ名
としてやればよい。これが、Pythonでいうimport
に相当する。
Juliaパッケージのインストールは、Pkg.add("パッケージ名")
とする。
相当複雑な依存関係を含むものでない限り、これで問題なくインストールできる。
PyCall
やPyPlot
など、Pythonとの連携を行うためのパッケージを導入する場合は、環境によっては、少し手間がかかることもある。
また、Juliaの対話環境REPLで]
キーを打鍵すると、パッケージマネージャの対話環境に入ることができる。
そこでは単にadd package_name
とするだけで、パッケージをインストールできる。
package_nameの部分はもちろん、自分の使いたいパッケージ名に置き換えること。
インストールされたパッケージは、とくに指定しなければホームディレクトリに~/.julia
という隠しフォルダが
生成され、~/.julia/packages
以下にインストールされる。
またJuliaの優れた特徴として、自分が今いるディレクトリ(環境やプロジェクト)ごとに自身が使用するパッケージやそのバージョン&依存関係を
指定することも簡単にできる。
例として、線形代数演算を用いるLinearAlgebra
を使ってみよう。
ランダム行列を作って、行列のdeterminant(行列式)を求めてみる。
using LinearAlgebra
A = randn(Float64,5,5)
det(A)
0.942391333164314
細かいことを言うと、using
でパッケージを読み込むと、そのパッケージ内でexport
されている関数や型を、そのまま使うことができる。
import パッケージ名
とすると、パッケージ内の関数や型を、パッケージ名.関数名
のようにして呼び出す必要がある。
たとえ異なるパッケージで同一の関数名が定義されていても、Juliaには多重ディスパッチで関数を呼び出す仕組みがあるので、
問題が起こる(複数のライブラリ間で関数の定義が完全に衝突する)事例は稀なので、基本的にusing
を使えばよい。
プロジェクト¶
Juliaでは、複数のパッケージを含むプロジェクトを作成することができる。
自作パッケージなどのプロジェクトフォルダ内にProject.toml
とManifest.toml
というファイルを作成することで、
複雑な依存関係を含むパッケージの管理を行いながら、開発・実行することができる。
(Pythonでもrequirements.txtやpyproject.tomlなどを用いたパッケージ管理だが、私は使ったことがないので詳しくない)
とくに自作パッケージを作成する場合は必須事項となるので、Pkg.jlのドキュメントや
日本語の書籍であればJuliaプログラミング大全や実践Julia入門を参照してほしい。
bashrc(など)に、
export JULIA_PROJECT=@.
と書いておけば、julia
を実行した際に、そのディレクトリがJuliaプロジェクトであれば、tomlファイルを介してそのプロジェクトの環境を読み込んでくれる。
GitHubにあるJuliaパッケージのレポジトリをクローンないしダウンロードしてきてサンプルコードをサクッと使いたい場合は、こちらの方法も有用である。
Juliaのバージョン管理¶
juliaup
というJuliaのバージョンを管理するためのツールが便利で、Juliaのバージョンを切り替えることができる。juliaupのレポジトリに従ってインストールしておこう。
Jupyter Notebookの実行¶
この講義の資料のように、ipynb形式のファイルを実行する場合、JuliaのREPLで
]add IJulia
などとして、IJuliaパッケージをインストールする。
その後、コードの実行時などにJuliaカーネルを選択することで、Juliaのコードを実行することができる。
小話
筆者の環境(VS Code)でJupyter環境でJuliaコードを実行すると、たまに停止やカーネルの再起動といった操作を受け付けず、プロセスが残り続けることがある。 講義では、資料の配布のしやすさからipynb形式を採用しているが、実際のコード開発や実行において筆者がJupyter環境を使うことはまず無い。
サンプルコード: Python vs Julia¶
ランダム行列の特異値分解のコードを書いてみよう。どちらも同じくらいシンプルになる。
#ランダム行列を生成し特異値分解するPythonコード
import numpy as np
def mat_svd(dim):
A = np.random.randn(dim,dim)
U, s, V = np.linalg.svd(A)
return U, s, V
if __name__ == '__main__':
dim = 500
itnum = 1000
for it in range(itnum):
mat_svd(dim)
#ランダム行列を生成し特異値分解するJuliaコード
using LinearAlgebra
function mat_svd(dim)
A = randn(dim,dim)
U, s, V = svd(A)
return U, s, V
end
function main()
dim = 500
itnum = 1000
for it = 1:itnum
mat_svd(dim)
end
end
main()
NuclearToolkit.jlの導入¶
この講義では、筆者が2022年に公開したJuliaパッケージNuclearToolkit.jlの実行結果の紹介を行う。
このパッケージの概要と、導入方法などをまとめておく。 また、パッケージには一応ドキュメントがあるので、 そちらも必要に応じて参照してほしい。
NuclearToolkit.jlはいくつかのbuilding blockからなるパッケージで、整理されていないsrc
フォルダを見ると分かるように
5つほどのサブディレクトリがあり、それぞれが独立したコード群となっている。
ChiEFTint: 主に相互作用ファイルの作成を行う
hartreeforck: 主にHF/HF-MBPT計算や、IMSRG法のインターフェースを与える
IMSRG.jl: IMSRG法
ShellModel: 殻模型計算やEigenvector Continuation法
NuclData.jl: AME(atomic mass evaluation)2020から限られたデータを切り出したもの。
だいたい原子核分野のコードというとこれら一つ一つに対してFortranやC++で書かれた5万-10万行のコードが普通なように思うが、 NuclearToolkit.jlではそれぞれ数千行、合計2万行程度なので、比較的小規模である。 性能はともかくとして、この規模で、核力から核構造計算までをカバーするコードは、筆者の知る限りでは他にない。
パッケージのインストールは簡単で、
using Pkg
Pkg.add("NuclearToolkit")
とするだけ。
パッケージはGitHub Actionsを用いて、Linux, macOS, Windowsの3つのOSでCIを回しているので、少なくともv1.9以降のJuliaであれば、どのOSでも動くはずであるが、本資料の執筆時点で筆者が使用している、Julia v1.10.0及びNuclearToolkit.jlのv0.4.1(2024/03/04)以降での実行を推奨とする。
注釈
ぜひ、Juliaの導入, NuclearToolkit.jlの導入ののち、JuliaのREPLでusing NuclearToolkit
として、パッケージを読み込むところまでを事前に試してください。もし、エラーが出たり、環境構築でわからないことがあれば、Slack or 講義の前など、気軽に声をかけてください。