Open30

Juliaメモ

ultimatileultimatile
julia> Char <: String
false

julia> SubString <: String
false

julia> Char <: AbstractString
false

julia> SubString <: AbstractString
true

julia> Char <: AbstractChar
true

julia> String <: AbstractString
true
ultimatileultimatile

Juliaup

install

以下[channel]は実際のchannelの名前で置換.

  • julia +[channel]: juliaの起動(例: julia +release)
  • juliaup add [channel]: channelの追加
  • juliaup rm [channel]: channelの削除(juliaup remove)
  • juliaup default [channel]: default channelの切り替え
  • juliaup st: installされたchannel一覧 (juliaup status)
  • juliaup ls: install可能なchannel一覧(juliaup list)
  • juliaup up: channelの更新(juliaup update)

補足など

  • juliaupの更新
    • Homebrew以外:juliaup self update
    • Homebrew: brew upgrade juliaup
  • semantic versioning (x.y.z)以外のchannel
    • release: 最新版, juliaupを入れた時点で入るdefault
    • lts: 安定版
    • alpha, beta, rc: 開発版

あまり使わなさそう

  • juliaup link [channel] [julia_binary]:既存の[julia_binary]を[channel]として登録(juliaupでinstallしていないchannelを作成可能)

他のcommand

ultimatileultimatile

user定義structに四則演算などBaseに定義されている演算子(method)を追加する場合はimport Base.+のようにimportが必要

ultimatileultimatile

他所のPackageにmultiple dispatchを追加

using Hoge
Hoge.some_function(mystruct::MyStruct) = some_function(...)
  • 自分が定義したMystructに対してHogeが定義していたsome_functionsome_function(mystruct::MyStruct)を追加したい場合,Hoge.some_function(mystruct::MyStruct)を定義しないといけない.
  • some_function(mystruct::MyStruct) = some_function(...)とした場合,Hogeに元々定義されていたsome_functionのmultiple dispatchが消えてしまう.
ultimatileultimatile

Simulation directoryとsource code directoryの分離

/my/source/code/directorymain.jlと依存先のincludes.jlがあるとして,/my/source/code/directoryとは別の場所で計算を実行したいとする.
そのまま絶対pathでmain.jlを実行すると依存関係が解決できないので以下のように--projectで依存先を教えてあげる必要がある.

PROJ_DIR=/my/source/code/directory; julia --project=$PROJ_DIR $PROJ_DIR/main.jl
ultimatileultimatile

VScodeのJupyter拡張機能でpackageの環境を使う方法

  • Jupyter notebookのkernel選択で分からなくなったのでメモ.
  • Jupyter kernelは使いたいJuliaのversionのみ指定すれば良い.
  • 最初(Julia環境を有効化したいタイミング)のセルで以下をJupyter notebookから実行すれば良い.
  • activateする場所はpackageのdirectoryを作成してProject.tomlがある場所を絶対pathかnotebookがある場所からの相対pathで指定する.
using Pkg
Pkg.activate("../")
Pkg.instantiate()
ultimatileultimatile

package名の一意性に関する調査

package作成の際にroot dirの名前,Project.tomlname entry,src以下のMyPackage.jlの名前,moduleの名前が不一致のときどうなるのか調べ,何が一致しなければならないのかを確認する.

確認用のPackage作成

julia REPLを起動して以下を実行

# まだaddしていなければ以下を実行
# ] add PkgTemplates

using PkgTemplates
template = Template(; dir=".")
template("MyPackage.jl")

これでREPLを起動したdirectoryにMyPackage.jl directoryが作成される.
MyPackage.jldirectoryにProject.tomlが作成され,

Project.toml
name = "MyPackage"

と記載されていることが確認できる.
またsrc directoryにはMyPackage.jlというjulia fileが作成されていることも確認できる.
MyPackage.jlの中身は

MyPackage.jl
module MyPackage

# Write your package code here.

end

となっている.

作成されたpackageの動作確認

REPLをMyPackage directoryで起動し,以下を実行する

] activate .
using MyPackage

using MyPackageから特にerror出力なく端末が返ってくることが分かる.
なお,] activate .の後はpkg modeを抜けるためにbackspaceを押す必要があることに注意.

変更可能箇所の列挙

以下Package名をMyPackage.jlとする.

  • Packageのdirectory名
  • MyPackage.jlProject.tomlname entry
  • MyPackage.jl/srcMyPackage.jlのfile名
  • MyPackage.jl/srcMyPackage.jl内のmodule名

packageのdirectory名の変更

まず一番無害そうなpackageのdirectory名を変更してみる

mv MyPackage.jl NewMyPackage.jl

特に問題なく動作する.

以下3パターンを検証する.

Project.tomlname 実行file名 module名
NewMyPackage MyPackage.jl MyPackage
MyPackage NewMyPackage.jl MyPackage
MyPackage MyPackage.jl NewMyPackage

Project.tomlname entryの変更

以下のようにProject.tomlを修正する.

Project.toml
@@ -1,4 +1,4 @@
-name = "MyPackage"
+name = "NewMyPackage"

状況は以下のようになる.

Project.tomlname 実行file名 module名
NewMyPackage MyPackage.jl MyPackage

以下のように動かなくなる.

(@v1.10) pkg> activate .

julia> using MyPackage
ERROR: ArgumentError: Package MyPackage not found in current path.
- Run `import Pkg; Pkg.add("MyPackage")` to install the MyPackage package.

julia> using NewMyPackage
ERROR: ArgumentError: Package NewMyPackage [0fa5efea-465b-4ecc-8393-0dad7f99f1d9] is required but does not seem to be installed:
 - Run `Pkg.instantiate()` to install all recorded dependencies.

src/MyPackage.jlのfile名の変更

続いて,Project.tomlは元に戻して,以下の修正を行う.

MyPackage/src/
mv MyPackage.jl NewMyPackage.jl

状況は以下のようになる.

Project.tomlname 実行file名 module名
MyPackage NewMyPackage.jl MyPackage

以下のように動かなくなる.

(@v1.10) pkg> activate .

julia> using MyPackage
ERROR: ArgumentError: Package MyPackage [0fa5efea-465b-4ecc-8393-0dad7f99f1d9] is required but does not seem to be installed:
 - Run `Pkg.instantiate()` to install all recorded dependencies.

julia> using NewMyPackage
ERROR: ArgumentError: Package NewMyPackage not found in current path.
- Run `import Pkg; Pkg.add("NewMyPackage")` to install the NewMyPackage package.

module名の変更

実行file名を元に戻した後,以下のように修正を行う.

MyPackage.jl
@@ -1,4 +1,4 @@
-module MyPackage
+module NewMyPackage

状況は以下のようになる.

Project.tomlname 実行file名 module名
MyPackage MyPackage.jl NewMyPackage

以下のように動かなくなる.

(@v1.10) pkg> activate .

julia> using MyPackage
Precompiling MyPackage
  1 dependency successfully precompiled in 5 seconds
[ Info: Precompiling MyPackage [0fa5efea-465b-4ecc-8393-0dad7f99f1d9]
┌ Warning: The call to compilecache failed to create a usable precompiled cache file for MyPackage [0fa5efea-465b-4ecc-8393-0dad7f99f1d9]
│   exception = Required dependency MyPackage [0fa5efea-465b-4ecc-8393-0dad7f99f1d9] failed to load from a cache file.
└ @ Base loading.jl:1992
┌ Warning: Replacing module `NewMyPackage`
└ @ Base loading.jl:1855
ERROR: package `MyPackage` did not define the expected module `MyPackage`, check for typos in package module name

julia> using NewMyPackage
ERROR: ArgumentError: Package NewMyPackage not found in current path.
- Run `import Pkg; Pkg.add("NewMyPackage")` to install the NewMyPackage package.

まとめ

Project.tomlnameと実行file名とmodule名は揃えよう.

ultimatileultimatile

条件によってPackageを使い分ける

Julia v1.9以降だとpackage extensionを使うのが良いっぽい
https://pkgdocs.julialang.org/dev/creating-packages/#Conditional-loading-of-code-in-packages-(Extensions)

それ以前だとRequires.jlを使うのが一般的っぽい

ultimatileultimatile

MyPackage.jlを作成していて、Hoge.jlの依存をextensionで入れたい場合、上記のやり方にしたがってMyPackageHogeExt.jl を作成するとusing Hogeをした場合にのみ MyPackageHogeExt.jlの中身がロードされる

ultimatileultimatile

using Hogeで名前空間をちゃんぽんさせていたのを明示的にusingしたい場合,一旦using Hoge: Hogeにしてerrorにして使ってる名前を吐かせると便利
こうするとHogeというmodule名しかusingされてない

ultimatileultimatile

複素数型の実部と虚部はx.rex.imの形でアクセス可能だが、実数型の場合は各fieldが存在しないのでエラーになる。
複素数型も実数型も両方来る可能性がある場所はreal(x)imag(x)でアクセスした方がよい。

ultimatileultimatile

DifferentialEquations.jl

特に\dot{u}(t)=A(u,p,t)u(t)型の(非)自励行列型常微分方程式での使い方(OrdinaryDiffEqLinear)。

  • u(t): 状態ベクトル
  • t: 独立変数
  • A(u,p,t): 問題を定義する行列
  • p: パラメーターパック。ut以外の依存変数を詰めるのに使う

A(u,p,t)を定義することで解きたい常微分方程式の問題を定義することができる。
そのためにSciMLOperators.jlMatrixOperatorを用いる[1]

以下のコード例を示す際に共通の変数を導入する

u0 = [1.0; -1.0] # 初期状態。値やサイズは問題などに合わせて変わる
tspan = (1.0, 6.0) # 問題を解く独立変数の区間。こちらもユーザーの要件で変わる

自励的な場合

A(u,p,t)が引数に依存しない場合はMatrixOperatorにターゲット行列を渡すだけで良い。

A = MatrixOperator([0.0 1.0; 1.0 0.0])
prob = ODEProblem(A, u0, tspan)
sol = solve(prob, LinearExponential())

自励的な場合は行列指数函数を計算すれば解けるのでソルバーはLinearExponential

非自励な場合

MatrixOperatorに渡す行列には時間や状態依存性を定義できない。そのためにMatrixOperatorのキーワード引数であるupdate_func!を定義して渡す

function update_func!(A, u, p, t)
    A[1, 1] = cos(t)
    A[2, 1] = sin(t)
    A[1, 2] = -sin(t)
    A[2, 2] = cos(t)
end
A = MatrixOperator(ones(2, 2), update_func! = update_func!)
prob = ODEProblem(A, u0, tspan)
sol = solve(prob, MagnusGL6(), dt = 1 / 10)
  • 状態uに依存しない場合、MagnusGL6などのMagnus展開法を用いたソルバーが利用できる.
  • dttの刻み幅

パラメーターパックpを使う場合は

function update_func!(A, u, p, t)
    A[1, 1] = p.a*cos(t)
    A[2, 1] = p.a*sin(t)
    A[1, 2] = -p.a*sin(t)
    A[2, 2] = p.a*cos(t)
end
p = (; a = 1)# NamedTuple。この場合Structでも良い

という感じで定義して

prob = ODEProblem(A, u0, tspan, p)

ODEProblemの第4引数に定義したpを渡せば良い。


その他注意点

  • A(u, p, t)が複素数を含む場合(Schrödinger方程式など)、Aの時間非依存部分を指定するMatrixOperatorの第1引数と、ODEProblemの第2引数の初期状態u0の要素型を複素数型にしておかないと複素数型から浮動小数点数型の変換が発生しInexactErrorで止まるので注意
  • MatrixOperatorの第1引数はA(u,p,t)初期行列として使用される。update_func!で触ってない要素はそのまま使用されるので注意。例えば
function update_func!(A, u, p, t)
    A[2, 1] = sin(t)
    A[1, 2] = -sin(t)
end

とした場合、対角要素A_{11}およびA_{22}MatrixOperatorの第1引数に渡されたものの要素が使われる。

  • 公式ドキュメントではout-of-placeのupdate_funcを使うように書いてあるが、これは正しくなく時間依存しない初期行列がソルバーにそのまま渡されるので使用してはいけない。(doc v7.15.0で確認)関連issue
脚注
  1. ドキュメントでは明示的にusing SciMLOperatorsしてあるがDifferentialEquations.jlなどが再exportしているので必要なさそう(SciMLOperatorsSciMLBaseDiffEqBaseOrdinaryDiffEq) ↩︎

ultimatileultimatile

(immutable) structのfieldのmutable structのfieldはmutable

ultimatileultimatile

get(collection, key, default)
collectionからkey指定したアイテムを取得。なければdefaultが返ってくる。

ultimatileultimatile

Distributed.jl

  • julia -p nあるいはUsing Distributed; addprocs(n)するとDistributed.nprocsn+1になる