Avik Sengupta Packt Publishing 2018-11-09
目次
はじめに
最近、
数値計算や最適化の技術を調べていると、
Pythonではなく、
Juliaという新しいプログラミング言語を
使っているのをよく見るようになりました。
The Julia Language
そこで自分のような
Python/C++/MATLABユーザの視点から見た、
Juliaの基本的な使い方をまとめてみたので、
紹介したいと思います。
Juliaとは?
Juliaとは、
2009年からMITの研究者を中心に開発が始まった
スクリプト言語です。
MATLABやPythonなどが得意な
科学技術計算を得意とする言語で、
様々な科学技術計算用のライブラリがデフォルトで内包されています。
それ以外にも、下記のような特徵があります。
特徴1: 計算が高速
Juliaの大きな特徵として、LLVMのフレームを使った
Just in Time(JIT) コンパイラの技術を使うことで、
非常に高速に計算を実施することができます。
下記はjuliaのホームページに掲載されている、
各言語におけるい計算速度のベンチマークです。
C言語の処理を1とし、各言語の比率を計算しています。
下記の結果を見ると分かる通り、
Pythonと比べて、約10倍弱早く、
C言語と比べても2倍ほどしか遅くなっていないことがわかります。
特徴2: 動的型付けのスクリプト言語
言語の特徵としては、
前述のように高速でありながら、
Pythonと同じ動的型付け言語です。
ですのでプロトタイプを作るのも簡単です。
特徵3: 型を指定することもできる
しかし、複雑なシステムを作る時は、
どうしても型が欲しくなります。
最近PythonでもType Hintが追加されました。
qiita.com
Juliaでは、関数の引数における型チェックが、
デフォルト機能として追加されています。
また、型を使ったC++で言う所の関数のオーバロードもサポートしています。
特徵4: 数式に近い表現でソースコードが書ける
Juliaは科学技術計算に強いため、
実際の数式に近い表現でソースコードを書くことができます。
例えばほとんどの言語は掛け算の*を省略できませんが、
Juliaの場合は下記のように書くことができます。
Julia> x^2 + 2x - 1
特徵5: C, C++, Fortran との親和性の高さ
Juliaは十分計算速度が早いのですが、
既存のCやC++,Fortranのコードを使いたい時があります。
Juliaは言語の機能として、
CやC++, Fortranのコードの呼び出しをサポートしています。
これはJuliaがLLVMを利用しているため、
CやC++, Fortranのバイトコードと共通性があるためのようです。
特徵6: Pythonとも親和性がある
こちらは言語の機能ではないですが、
JuliaからPythonのコードを呼ぶパッケージも公開されています。
github.com
個人的にはPythonからJuliaコードを呼ぶライブラリが欲しいですが。。。
特徵7: 並行・並列処理に強い
最近の言語であるgoなどは、
マルチコア時代に対応するためか、
並列、並行処理に特化した機能を言語そのものが有していますが、
Juliaも言語のデフォルト機能として、
並行・並列処理のAPIを有しています。
Pythonでも並行処理は可能ですが、
GIPがあるためかなり無理が多いです。。
myenigma.hatenablog.com
インストール
各プラットフォームでのJuliaのインストール方法です。
基本的には、下記のリンクからインストール用のデータをダウンロードします。
julialang.org
Mac
前述のリンクからdmgファイルをダウンロードし、インストールします。
あとは、/Application/Julia*/Contents/Resources/Julia/bin/juliaを、
/usr/local/binにシンボリックリンクを作成すればOKです。
Linux
前述のリンクから、バイナリをダウンロードし、
/usr/local/bin/juliaにシンボリックリンクを作成すればOKです。
Windows
Windowsでは、前述のリンクからwindowsの実行ファイルを解凍し、
環境変数でパスを設定すればOKです。
MATLAB, Python, C++ユーザがJuliaについて知るべきこと
すでにMATLABやPython, C++を使える人が
新しくJuliaを勉強している場合が多いと思いますが、
下記の記事に各言語との違いがわかりやすくまとめられています。
https://docs.julialang.org/en/stable/manual/noteworthy-differences/#Noteworthy-Differences-from-other-Languages-1
下記は上記の記事の日本語意訳です。
MATLABユーザがJuliaについて知るべきこと
Juliaの文法はMATLABにかなり似ていますが、
まったく同じではありません。
下記が注意すべき相違点です。
Juliaの配列はSquare bracketsでインデックスを指定します。 A[i,j].
Juliaの配列は参照渡しされます。例えばA=Bとした場合、Bの内容を変更すると、Aの内容も変わります(Pythonのリストと同じです)
Juliaでは関数の引数はすべて参照渡しです。関数の中で引数を変更すると、関数の呼び出し側でも変更されます。
Juliaでは配列を自動的に拡張することはしません。例えば、MATLABではa(4) = 3.2とすると、a = [0 0 0 3.2]と0でパディングされた配列が自動生成されます。またa(5) = 7とすると、a = [0 0 0 3.2 7]と自動で配列が拡張されます。しかし、Juliaで同じことをするとエラーを変えすだけです。Juliaではpush!()やappend!()関数があり、これらを使って配列を拡張することができます。
MATLABにおける虚数はi,jですがJuliaではimです。
MATLABではすべての数値がデフォルトでdoubleになりますが、Juliaでは整数値を変数に入力すると変数の型はIntになります。従って、2^-1といった処理はエラーになる場合があります。
Juliaでは関数は、複数の値や、複数の値を一つのタプルとして返すことができます。(例: (a, b) = (1, 1) , a, b = 1, 2 ) MATLABではnargoutが同様の目的のために使われていますが、Juliaにはそのような機能はありません。Juliaではキーワード変数などで同じことを実現することができます。
MATLABではベクトルは一行または一列の行列(Nx1 or 1xN)として表しますが、Juliaではベクトルを表すための一次元の配列があります。
Juliaでは[x ,y, z]と常に3つの要素を持つ配列になります(MATLABのように。もし垂直方向に結合したい場合は、vcat(x,y,z)を使うか、([x;y;z])という記法を使います。水平方向に結合したい場合はhcat(x,y,z)か([x,y,z])という記法を使います。部分行列を組み合わせたい場合は、hvcat()関数か、([a b;c d])という記法を使います。
Juliaではa:b, a:b:cのような記法はRangeオブジェクトを生成します(ベクトルではない)。ベクトルを作る場合は collect(a:b)を使いますが、あまり出番は無いでしょう。Rangeオブジェクトのように遅延評価されるオブジェクトはlinspaceやenumerate, zipなどJuliaでは頻繁に利用されます。
MATLABでは関数定義で定義された変数を返しますが、Juliaの関数は最後の式の結果か、またはreturnキーワードの対象を返します。
Juliaスクリプトには好きな数の関数を入れることができますが、すべての定義はファイルをロードした時に正しいものである必要があります。関数定義は同じWorkingディレクトリでないファイルからも読み込むことができます。
Juliaでは、sum()やprod(), max()のような関数は、配列のすべての要素に適応されます。
Juliaでは、sort()のような列ベースの関数では1xNの行列には特に影響を与えません。もし1xNのベクトルをソートしたい場合は、sort(A,2)のようにaxisを指定する必要があります。
Juliaでは二次元の配列Aを使って、fft(A)とした場合、二次元のFFTが計算されます。注意点としては、fft(A)とfft(A,1)は異なる結果を返すことです。fft(A,1)は一次元のFFTを列ベースで実施します。
Juliaでは、引数がなかったとしても関数には丸括弧が必要です。(例: tic(), toc())
Juliaではコードの末にはセミコロンは起きません。また処理の結果はMATLABのように自動で標準出力に出力されません。もし結果を表示させたい場合は、print()やprintln(), @printf()を使用します。
Juliaでは2つの配列A,Bを A == Bで比較した際には、Boolの配列は返しません。もし配列のそれぞれの要素を比較したい場合はA.==Bを使います。これは他のBool比較演算子である>, <, =も同じ振る舞いをします。
Juliaでは、&, l, v (xor)はMATLABのand or xorと同様にビット比較を実施します。配列の要素毎に比較したい場合はBool配列と組み合わせることで実現できます。しかし演算子の順番を規定するために丸括弧を忘れないようにしましょう(例 Aが1か2であることを比較したい場合 (A .== 1) | (A.==2) )
Juliaではarrayやタプルを関数の引数群に入れたい時にsplat operater …を使うことができます。例えば f(1,2)としたい時に、 in=[1,2] f(in…)としても同じ結果が得られます。
Juliaの特異値分解のsvd()では、対角行列ではなく、特異値のベクトルが帰ってきます。
Juliaでは …はコードを複数行につなげる時には使いません。コードを複数行に渡って書いても自動的に認識されます。
JuliaでもMATLABでも、インタラクティブモードの場合、ansという変数に最終的な計算の結果が格納されます。しかしJuliaの場合、インタラクティブモードではない場合、ansという変数は設定されません。
Juliaの型では、MATLABのクラスのように、動的にフィールドを追加することはできません。そのようなことをしたい場合はJuliaではDictを使ってください。
MATLABでは一つのグローバルなスコープしか持っていませんが、Juliaではそれぞれのモジュール毎のスコープ/名前空間を持っています。
MATLABでは、配列のデータを削除したい場合は、論理インデックスを使います。(例: x(x>3) =[] ) しかし、Juliaでは、より高機能な関数であるfilter()やfilter!()関数を使ってください。例えばxの3以上の要素のみを取得したい場合は、 filter(z->z>3, x)やfilter!(z->z>3, x)を使いましょう. filter!()は一時メモリの消費を減らすことができます。
MATLABではセル配列のすべての要素を抽出するには、vertcat(A{:})を使いますが、Juliaではsplat operatorを使い、vcat(A…)とします。
PythonユーザがJuliaについて知るべきこと
Pythonと異なり、JuliaではStatementブロック(for, if , whilw)にendが必要です。インデントはあまり重要ではありません。また、Juliaにはpassキーワードはありません。
Juliaでは配列のインデックスは0始まりではなく、1始まりになります。
Juliaのスライシングでは最後の要素が含まれます。Pythonにおけるa[1:3]は、Juliaにおける[2:3]です。
Juliaでは負の値のインデックスはサポートされていません。Pythonで最後の要素にアクセスする時は-1を使っていますが、JuliaではMATLABのようにendを使います。
Juliaでは複数行のコードを表すシンタックスはありません。自動でコードの複数行判定がされます。もし強制的に複数のコードを複数行の1コードとしたい場合は丸括弧を使う方法があります。
Juliaの配列は列ベースです(Fortranと同じです)。一方、PythonのNumPyは列ベースです(Cと同じです)。 ループなどで最適なパフォーマンスを実現するには、Numpyとは逆の列ベースのループ処理にすべきです。こちらのリンク参照 https://docs.julialang.org/en/stable/manual/performance-tips/#man-performance-tips-1
Juliaの+=や-=の演算子は、参照元の変数を変更しません。例えばA = ones(4); B=A; B+=3ではAの値は変わりません。B = B + 3でもAの値は変わりません。変えたい場合はB[:]+=3を使えます。
Juliaでは関数の引数のデフォルト値が、関数が呼ばれるたびに評価されます。Pythonでは関数を設定した一回のみ評価されます。
C/C++ユーザがJuliaについて知るべきこと
Juliaの配列はSquare bracketsで指定し、かならず1次元以上データがある必要があります(例A[i,j])。このシンタックスは、C/C++のポインタやアドレスへの参照とか違うものです。詳しくはJuliaの配列のドキュメントを参照ください。
Juliaでは、配列やStringのインデックスは0からではなく、1から始まります。
Juliaの配列は参照渡しでコピーされます。例えばA=Bとしたあと、Bの値を変更するとAの値も変更されます。一方、更新オペレータである+=や-=では、参照元の値は変更されません。これはこれらの計算がA=A+Bと同じ計算であり、右側の計算結果の参照が渡されるからです。
Juliaの配列は列ベースです(Fortranと同じです)。一方、PythonのNumPyは行ベースです(Cと同じです)。 ループなどで最適なパフォーマンスを実現するには、Numpyとは逆の列ベースのループ処理にすべきです。こちらのリンク参照 https://docs.julialang.org/en/stable/manual/performance-tips/#man-performance-tips-1
Juliaでは関数の引数はすべて参照渡しです。関数の中で引数を変更すると、関数の呼び出し側でも変更されます。
Juliaではスペースは重要な役割を果たします。よって、C/C++と異なり、スペースを追加するときや削除する時は、注意を払う必要があります。
Juliaでは、リテラルな数字(例: 42)は、符号付きのIntになります。しかしその数字が機械語の最大値より大きい場合は自動でInt64やInt128, BigIntなどの型に自動変換されます。Juliaには数値的なリテラスサフィックスはありません(例えばL, LL, U, UL, ULLなどの符号付、符号なしサフィックスなど)。10進数の数値はすべて符号付になり、16進数の数値(C/C++のように0xがつく数値)は符号なしになります。C/C++/JavaやJuliaの10進数の数値と異なり、Juliaの16進数の数値はリテラスの0の数によって自動的に型が決定します。例えば0x0や0x00はUInt8になり、0x000や0x0000はUInt16, 同様にUInt32, Uint64, Unit128に変換されます。これは16進数でマスクをする時などに非常に重要になります。例えば、~0xf == 0xf0 と ~0x000f == 0xfff0はまったく違う処理になります。64bitのFloat64と32bitのFloat32はそれぞれリテラルでは, 1.0と1.0f0と表現されます。浮動小数点数のリテラルは自動的に最小の型に変換されます。これはCやC++と似た振る舞いです。0oで表現される8進数や0bで表現される2進数も自動的に符号なしとして表されます。
Juliaでは文字列リテラルは\”か\”\”\”で区切られている必要があります。\”\”\”では間に別の文字を入れてはいけません。Juliaの文字リテラルでは、$variablenameや$(expression)で変数や式をリテラルの中に入れることができ、それらの結果が文字リテラルの中に展開されます。
C/C++では//は一行コメントですが、Juliaでは//は有理数を表します。
Juliaで複数行コメントをする時は、 #=と=#で囲みます。
Juliaの関数は最後の式の結果か、return文の値を返します。また、Juliaの関数は、タプルや複数の値を返すことができます。例: (a,b) = myfunction(), a, b = myfunction()。C/C++のように引数にポインタや参照を渡す必要はありません。
Juliaではステートメントの最後にセミコロン;をつける必要がありません。REPLではセミコロンを付けることで式の結果を表示できます。Juliaでは角括弧と一緒にセミコロンを使う場合もありますが、これは複数行のコードを一行にまとめるために使われます。
C/C++では^が排他的論理和を表しますが、Juliaでは、⊻ (xor)は排他的論理和を表します。しかしJuliaでは、ビットごとの計算はできないため注意が必要です。
Juliaでは^は自乗を表します。
Juliaでは>>と>>>という演算子があります。>>>は算術的な右シフトを表し、>>はロジカルなシフトを表します。これらの意味はそれぞれの変数の型に応じて変化するため注意が必要です。
Juliaでは->ポインタを返したメンバーではなく、無名関数の作成を意味します。
Juliaではforやwhileの条件式で括弧は不要です。例: for i in [1, 2, 3]
Juliaでは、数値の0と1はBool型を表しません。例えば if (1)というコードはJuliaでは使えません。もし同じ処理を書きたい場合は、Juliaでは if true か、if Bool(1), if 1==1を使ってください。
Juliaではif文やwhile, for, 関数などで最後にendが必要です。Juliaでは、一行にif文の処理を記述するために、 if cond; statement; endや、 if cond && statement, if !cond || statementのような表記が可能になっています。上記のstatementで入力をする場合は丸括弧が必要なことに注意が必要です。(例: cond && (x = value))
Juliaでは複数行のコードを表すシンタックスはありません。自動でコードの複数行判定がされます。もし強制的に複数のコードを複数行の1コードとしたい場合は丸括弧を使う方法があります
Julaのマクロはプログラムというより、コードをパースして実行します。これによりより複雑な処理をJuliaに実行させることができます。Juliaではマクロの名前は@で始まります。そして関数のようなシンタックスをもつ場合と、@mymacro(arg1, arg2, arg2), 条件文のようなシンタックスを保つ場合があります。 @mymacro arg1 arg2 arg2. これは関数のような処理をマクロにさせたいときと、ステイトメントのような処理をさせたいときで使い分けることができます。
Juliaでは現状、列挙型Enumを使う場合はマクロである @enum(name, value1, value2, ..)を使うことができます。(例 @enum(Fruit, banana=1, apple, pear) )
これはJuliaコードにおける慣例的なものですが、関数の引数を変更してしまう関数は関数の後ろに!をつけます。(例: push!)
C++では、ダイナミックディスパッチをするには、関数を仮想にする必要がありますが、Juliaではすでにすべての関数が仮想です。
JupyterでJuliaを使えるようにする
続いて、Pythonで便利すぎるJupyterが、
Juliaでも対応しているため、
Jupyter用のカーネルをインストールします。
juliaのコマンドラインから、
Pkg.add("IJulia")
とするだけでインストールされるはずです。
自分の環境では、julia0.5.0を使っていたのですが、
上記のコマンドでインストールしようとした所、
gitのエラーがでました。
そこで、
>$ brew update julia
として0.5.1にアップデートした所、
エラーが消えて、無事インストールできました。
あとは、普通にjupyterを立ち上げると、
選べるカーネルとしてJuliaが追加されているはずです。
加えて、
下記のJuliaBoxというサービスを使えば、
JuliaのJupyter notebookをクラウド上で実行できます。
これでPythonと同じ感じでJuliaを使えますね。
qiita.com
Juliaの利用例
Juliaを使ったシステムを列挙します。
天体カタログの解析
下記の論文によると、UCバークレーの研究グループは、
天体カタログの画像解析にJuliaを利用しているようです。
[1611.03404] Learning an Astronomical Catalog of the Visible Universe through Scalable Bayesian Inference
天体カタログのデータ量は、
一日毎に15テラバイトのデータが増えていくものであり、
それらを解析するために、Juliaの並列計算機能を使って、
8192個のXeonコアをフル活用して処理を実施しているようです。
Juliaの外部パッケージの管理
Pythonのpipやcondaのように、
外部パッケージをインストールしたり、
管理したりする時は、
デフォルトのPkgというパッケージを使います。
外部パッケージのインストール
Juliaのリポジトリに登録されたパッケージである
hogeというパッケージをインストールする場合は、
下記の通りです。
julia> Pkg.add("hoge")
リポジトリに登録されていない
GitHub上のパッケージなどをインストールする場合は、
julia> Pkg.clone("https://github.com/invenia/JuliaFormat.jl")
とします。
インストールパッケージの確認
Pkg.installed()
インストールしているパッケージのバージョンの確認
Pkg.status()
おすすめパッケージ
下記がJuliaのおすすめパッケージです。
最適化モデリングライブラリJuMP
myenigma.hatenablog.com
Juliaを学ぶための資料
下記の資料が分かりやすかったです。
参考資料
myenigma.hatenablog.com
myenigma.hatenablog.com
myenigma.hatenablog.com
myenigma.hatenablog.com
myenigma.hatenablog.com
Avik Sengupta Packt Publishing 2018-11-09
MyEnigma Supporters
もしこの記事が参考になり、
ブログをサポートしたいと思われた方は、
こちらからよろしくお願いします。
myenigma.hatenablog.com