システムとモデリング

modelica, Julia, Design Structure Matrix, SysML, 他モデリング全般について。

とあるJuliaユーザーの不満(Discourseより)

現在JuliaのDiscourseで熱い議論が交わされているスレッドを紹介します。 スレッドは↓です

discourse.julialang.org

これはスレ主が自身のブログにJuliaに関する記事を投稿し、それに対してスレッドで感想を求めたことが発端になっています

ブログ記事の内容

こちらがそのブログ記事になります。著作権の問題のありますので全訳は控え、こちらの解釈で要約します。

www.weissmann.pm

著者はスレ主でもあるVolker_Weissmannさんで、プログラミングにはそこまで習熟していないなか、修士過程で物理の数値計算をする際にJuliaを使用したようです。 この記事自体はその時から半年ほど経過した後に書かれているため細かい事実関係があやふやな部分があるようですが、彼はJuliaについて以下の不満を持っているようです。

  1. ライブラリのドキュメント類が簡潔過ぎて役に立たない
  2. エラーメッセージがわかり辛く、デバックに時間がかかる
  3. Julia言語で開発を行うときの"お作法"(ワークフロー)が確立されていない
  4. for loopなどの実行速度はたしかに早いが、プログラムを初めて実行する際の時間が長い(TTFP:Time To First Plotとスレでは表現されていました。)

以上より『Juliaは他の言語よりも短いコードでプログラミングできるが、その短いコードをエラーなく処理させるのに膨大な時間がかかる』とのことで、Volker_WeissmannさんはJuliaからRustに移行したとのことでした。

(なおこの要約は、続くスレッドの文脈からブログ記事を解釈したものです。スレッドで重大視されなかった内容は省かれていたり、スレの情報を用いて記事内容を一部補完しています)

この記事では2. エラーメッセージがわかり辛く、デバックに時間がかかるについて掘り下げていきます。

2. エラーメッセージがわかり辛く、デバックに時間がかかる

Juliaのエラーメッセージは初心者には対応が難しいと書かれています。 Volker_Weissmannさんは例として下記のような状況を挙げておりました。

using SimpleDiffEq
f(u,p,t) = 2u
g(u,p,t) = 1
u0 = 0.5
tspan = (0.0,1.0)
prob = SDEProblem(f,g,u0,tspan)
sol = solve(prob,SimpleEM(),dt=0.25)
g(du,u,p,t) = du .= 2.0 * u
function f(du, u, p, t)
    g(du,u,p,t)
end

using SimpleDiffEq
f(u,p,t) = 2u
g(u,p,t) = 1
u0 = 0.5
tspan = (0.0,1.0)
prob = SDEProblem(f,g,u0,tspan)
sol = solve(prob,SimpleEM(),dt=0.25)
g(du,u,p,t) = du .= 2.0 * u
function f(du, u, p, t)
    g(du,u,p,t)
end

上記プログラムは実行するとエラーとなり、以下のようなエラーメッセージが表示されます。

ERROR: LoadError: MethodError: no method matching copyto!(::Float64, ::Base.Broadcast.Broadcasted{Base.Broadcast.DefaultArrayStyle{0}, Tuple{}, typeof(identity), Tuple{Float64}})
Closest candidates are:
  copyto!(::DiffEqArrayOperator, ::Any) at /home/volker/.julia/packages/SciMLBase/dyqr8/src/operators/basic_operators.jl:112
  copyto!(::Any, ::Base.Broadcast.Broadcasted{var"#s155", Axes, F, Args} where {var"#s155"<:StaticArrays.StaticArrayStyle, Axes, F, Args<:Tuple}) at /home/volker/.julia/packages/StaticArrays/xV8rq/src/broadcast.jl:29
  copyto!(::DiffEqBase.DiffEqBC, ::Base.Broadcast.Broadcasted{var"#s7", Axes, F, Args} where {var"#s7"<:Base.Broadcast.AbstractArrayStyle{0}, Axes, F, Args<:Tuple}) at /home/volker/.julia/packages/DiffEqBase/qntkj/src/diffeqfastbc.jl:42
  ...
Stacktrace:
 [1] materialize!
   @ ./broadcast.jl:894 [inlined]
 [2] materialize!
   @ ./broadcast.jl:891 [inlined]
 [3] g(du::Float64, u::Float64, p::SciMLBase.NullParameters, t::Float64)
   @ Main ~/Sync/DatenVolker/git/sm_with_julia/bug.jl:8
 [4] f(du::Float64, u::Float64, p::SciMLBase.NullParameters, t::Float64)
   @ Main ~/Sync/DatenVolker/git/sm_with_julia/bug.jl:10
 [5] (::SDEFunction{true, typeof(f), typeof(g), LinearAlgebra.UniformScaling{Bool}, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing})(::Float64, ::Vararg{Any, N} where N)
   @ SciMLBase ~/.julia/packages/SciMLBase/dyqr8/src/scimlfunctions.jl:395
 [6] solve(::SDEProblem{Float64, Tuple{Float64, Float64}, true, SciMLBase.NullParameters, Nothing, SDEFunction{true, typeof(f), typeof(g), LinearAlgebra.UniformScaling{Bool}, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing}, typeof(g), Base.Iterators.Pairs{Union{}, Union{}, Tuple{}, NamedTuple{(), Tuple{}}}, Nothing}, ::SimpleEM; dt::Float64)
   @ SimpleDiffEq ~/.julia/packages/SimpleDiffEq/vIpeG/src/euler_maruyama.jl:57
 [7] top-level scope
   @ ~/Sync/DatenVolker/git/sm_with_julia/bug.jl:19
in expression starting at /home/volker/Sync/DatenVolker/git/sm_with_julia/bug.jl:19

このエラーメッセージで、どう対処したら良いか初心者にわかるでしょうか?というのがVolker_Weissmannさんの言いたいことのようです。 たしかに、いきなりcopyto!とかmaterialize!とか出てきてもどうしたら良いかわかりませんよね。 こちらのエラーの原因はドット演算子.=でブロードキャストされているためu0はベクトル形式でなくてはならない。したがってu0 =[0.5]に修正することが必要なようなのですが、このエラーメッセージからその対処法にたどり着くのは初心者にとっては厳しいと思います。正直、私自身もあまり理解できているわけではありません。

こちらに関してはSimpleDiffEqの開発者がエラーメッセージを改良してくれる予定のようです。

エラーメッセージについてはスレ内でもわかり辛いというので多くの方が同意しています。ただ一方で、ライブラリ開発者が懇切丁寧なエラーメッセージを準備する手間暇をかけることは難しいだろうという悲観的な考えもあるようです。

エラーメッセージのわかり辛さが、juliaの普及を妨げている一つの要因かもしれません。

ちなみに、私の環境(SimpleDiffEq v1.5.1およびjulia v1.7.2)では↓のように1つだけのコードブロックだとエラーなく実行できました。残念ながら私はこの理由をうまく説明できず、結局のところ理解不足のようです……

using SimpleDiffEq
f(u,p,t) = 2u
g(u,p,t) = 1
u0 = 0.5
tspan = (0.0,1.0)
prob = SDEProblem(f,g,u0,tspan)
sol = solve(prob,SimpleEM(),dt=0.25)
g(du,u,p,t) = du .= 2.0 * u
function f(du, u, p, t)
    g(du,u,p,t)
end
sol

実行結果↓

retcode: Default
Interpolation: 1st order linear
t: 5-element Vector{Float64}:
 0.0
 0.25
 0.5
 0.75
 1.0
u: 5-element Vector{Float64}:
 0.5
 0.8215251796548537
 1.1443852028675838
 3.2814434837320996
 4.8454911057204

Juliaのデバッグは難しいのか?

果たしてJuliaのデバッグは難しいのでしょうか?Volker_WeissmannさんはJuliaは動的型付けなのでデバッグが困難と言っておりますが、これに関してはスレ内で反論する意見も多くありました。 反論意見の多くは『REPLによるデバッグの使いやすさ』について強調していました。 特に以下のようなシンプルなワークフローで論理エラーについて効果的に対処できるとのことです。

  1. printでプログラムのどこでバグが発生しているのか、そしてそこへの入力は何かを確認する。
  2. プログラムの該当部分をREPLにコピペして挙動を確認してバグを理解する。

ただ型関係のエラーについては私自身思うところがあり、Matlabpythonに比べて型が原因のエラーに見舞われることが多い印象です。スレ内でも同様の指摘がありましたが、これについては『JuliaはMatlabPythonよりも厳密な型システムを持っている』ためのようです。実際Juliaの型システムは優秀と言われておりますが、私のような初心者レベルだとあまり恩恵に感じることができていないだけなのかもしれません。

長くなりましたので今回はここまでにします。 続きの記事でJuliaのドキュメント関係について議論していきたいと思います。