Free Wolfram EngineをJupyterで使う方法

  • 黒木 玄
  • 2019-05-25~2019-06-05

Wolfram Engine とそのJupyterクライアントのインストールの仕方については

で解説した. このノートブックにおける解説はそこでした解説とほぼ同じである.

Jupyterを最も簡単に使えるようにする方法はおそらくAnacondaを入れることである. Julia言語からJupyterを使える環境を整備する方法については

を参照せよ. Juliaは最新の優れたプログラミング言語なので使えるようにしておいた方が便利である.

Wolfram言語を使用するための最も簡単な手段は

を利用することである.

JupyterでFree Wolfram Engineを使えるようにする方法

Free Wolfram Engineのインストール

ポイント

  • Wolfram IDを取得する.
  • Wolfram Engineのライセンスを取得する.
  • wolframscriptの最初の起動時にWolfram IDとパスワードを入力する.

あとディスク容量を4GB程度消費するので注意せよ.

具体的な手順

まず, Free Wolfram Engine for Developers にアクセスして, Wolfram Engineのインストーラーをダウンロードする. 私はWindowsユーザーなのでWindowsを選択した.

インストーラーを起動するとダウンロードが始まる.

ダウンロードが終わるまでしばらくかかるので, ブラウザの側で Wolfram Engine のライセンスの取得の作業を行う. まだ Wolfram ID を持っていない人は ID も取得する. Wolfram ID とそのパスワードは wolframscript の初回起動時に入力を要求されることになる.

ライセンスを取得できると以下のように表示される.

ライセンス取得が終わる頃にはインストーラー側でのダウンロードが終了して, インストールを始めることが可能になっているので, インストールを始める. ディスク容量を4GBほど消費するので, 空き容量が足りなくならないように注意せよ.

インストールが終了すると以下のように表示される. Finishボタンを押すと, wolframscriptが起動される.

wolframscriptの初回起動時には Wolfram ID とパスワードの入力を要求される. ここで Wolfram Engine のライセンスを取得した Wolfram ID とパスワードを入力する. 入力に失敗したら, wolframscriptを自分で立ち上げ直してやり直す.

ライセンスを取得したWolfram IDとそのパスワードを入力を正しく入力すると, Wolfram Engine が wolframscript 経由で使えるようになっている.

これでテキストベースで Wolfram Engine を使用することが可能になった.

Jupyterを使えるようにする

Wolfram Engineでグラフを表示したりするためには, そのためのクライアントを用意しなければいけない.

そのための1つの方法は Jupyter を使えるようにすることである.

そのためには Python および関連のライブラリをまとめてインストールする必要がある.

そのための適切な手続きは各人の事情によって異なる. 自己責任でJupyterを使えるようにして欲しい.

おそらく, Jupyterを使えるようにするための最も簡単な手段は Anaconda をインストールすることである.

最新の優れたプログラミング言語である Julia の観点からの Jupyter のインストールの仕方に関しては

を参照して欲しい. 個人的にJuliaはとても便利なので, せっかくJupyterを使えるようにするならば, Juliaも使えるようにしておくべきだと思う.

注意: Jupyterの綴りが「木星」を意味するJupiterと異なることに注意せよ. iがyになっている. このようになっている理由はJupyterがJUlia PYThon R の略になっているかららしい. (eはどこから来たのか?)

Wolfram EngineをJupyterで使えるようにする

ポイント

  • Jupyterをすでに使えるようになっている.
  • コマンドプロンプトでの作業をできる.

具体的な手順

以下はWindowsユーザー限定の解説になる.

Wolfram Engineのインストールが終了したら、環境変数のPathに

C:\Program Files (x86)\Wolfram Research\WolframScript\

が追加されていた. ここにwolframscript.exeがおいてあった. WolframEngine本体は

C:\Program Files\Wolfram Research\Wolfram Engine

の方にインストールされていた. ポイントはWindows環境にWolfram Engineをインストールすると, wolframscript へのパスが通った状態にすでになっていることである.

次にやりたいことはJupyterで Wolfram Engine を使えるようにすることである.

そのためには

をインストールしなければいけない. そこにアクセスして以下のようにすればZIPファイルの形式で必要なものをダウンロードできる. (gitを使える人はgitを使ってもよい.)

ダウンロードしたZIPファイルを展開し, コマンドプロンプトなどを使って, そのディレクトリで作業することになる. 実行したいのは configure-jupyter.wls add である. しかし, その前に configure-jupyter.wls の中身を閲覧し, 安全を確かめる. そして,

configure-jupyter.wls help

もしくは

wolframscript configure-jupyter.wls help

を実行して configure-jupyter.wls の使い方を確認する(実際には help を付けなくてもヘルプが表示される).

ここでトラブル発生!.

RunProcess::pnfd: Program cmd not found.  Check Environment["PATH"].
configure-jupyter.wls: An unknown error has occurred.

とエラーメッセージが表示され, configure-jupyter.wls を起動できない.

その原因は私の環境でPATHが通っている場所に cmd.exe が2つ存在したからである.

$ where cmd
C:\msys64\usr\bin\cmd
C:\Windows\System32\cmd.exe

そこで私は C:\msys64\usr\bin\cmd および C:\msys64\usr\bin\cmd.exe の名前を変えて, cmd.exe が1つだけになるようにした.

注意: 実際にはPATHの設定を変えて作業を行ったが, このようにした方が簡単であった. 私が実際に行った作業は, コマンドプロンプトで

set PATH=C:\Windows\system32;C:\Program Files (x86)\Wolfram Research\WolframScript
configure-jupyter.wls help

を実行することである. 必要最小限のPATHを設定してトラブルを避けた.

これで configure-jupyter.wls help が実行可能になった. 色々確認して

configure-jupyter.wls add

を正常に実行可能ならば Wolfram Engine を Jupyter で使えるようになる.

しかし, ここでもトラブル発生!

configure-jupyter.wls add を実行したら,

configure-jupyter.wls: Jupyter installation on Environment["PATH"] not found.

とエラーが表示された. これは configure-jupyter.wls がPATHが通っている場所に jupyter.exe を発見することに失敗していることを意味している. だから, PATHに jupyter.exe のある場所を追加してやればこのトラブルは解消する. 私の環境で jupyter.exe

C:\Users\genkuroki\.julia\conda\3\Scripts

にあるので, コマンドプロンプトで

set PATH=C:\Users\genkuroki\.julia\conda\3\Scripts;%PATH%

を実行してから,

configure-jupyter.wls add

を実行した. すると

Updating from Wolfram Research server ...

と表示されて, configure-jupyter.wls add が正常に実行された.

Anaconda3を C:\Anadonda3 以下にインストールした人は C:\Anaconda3\Scriptsjupyter.exe が置いてあるので,

set PATH=C:\Anaconda3\Scripts;%PATH%

を実行してから, configure-jupyter.wls add を実行すればよいだろう.

注意: configure-jupyter.wls add を実行できない場合には wolframscript がパスに入っていることを確認してから wolframscript configure-jupyter.wls help を実行すればうまく行く可能性がある. wolframscript がパスに入っていない場合には wolframscript をフルパスで指定して実行する必要がある. $\text{□}$

以上のように configure-jupyter.wls add を正しく実行できれば, 以下のようにJupyter側のメニューに Wolfram Language が現われるようになる.

実際にそれを選択して, Wolfram Language の Jupyter notebook を立ち上げると以下のようなことをできる.

3次元のプロットが表示されており, 不定積分の計算結果も表示されている.

注意: これを書いている時点では, 数式は「テキストの画像」で表示されてしまう. この点は残念なので誰かが改良することが望まれる. Wolfram Language には [TeXForm](https://reference.wolfram.com/language/ref/TeXForm.html) という函数があって, 数式をLaTeX形式に変換してくれる. その函数を適切に利用できれば, Jupyter notebook でも数式をきれいに表示できる可能性がある.

ここまでたどり着けば, あとは楽しむだけである!

Wolfram Language kernel for Jupyter notebooks の細かな修正

以上の作業に成功してWolfram言語をJupyterで利用できるようになった人は, 以下の問題にすぐに気が付くと思う:

  1. コードの色付けがおかしい.
  2. 数式が画像で表示される.

以下ではこの2つの問題の解決方法について説明する.

上で実行した configure-jupyter.wls がおいてあるディレクトリを以下では WolframLanguageForJupyter/ と表す. 上の2つの問題の解決に必要な作業は

WolframLanguageForJupyter/WolframLanguageForJupyter/Resources/

においてあるファイルの変更である. この作業によって今までJupyterで使えていたWolfram言語が使えなくなる可能性があるので注意深く作業を行って欲しい. diffファイルが

においてある.

コードの色付けがおかしいという問題の解決

コードの色付けをCodeMirrorのmethematicaモードで行うようにするために,

WolframLanguageForJupyter/WolframLanguageForJupyter/Resources/KernelForWolframLanguageForJupyter.wl

86行目

\"codemirror_mode\": \"python\"

の部分を

\"codemirror_mode\": \"mathematica\"

に書き直す.

数式をTeX形式できれいに表示する方法

WolframLanguageForJupyter/WolframLanguageForJupyter/Resources/OutputHandlingUtilities.wl

ToOutText函数

(* generate HTML for the textual form of a result *)
    toOutText[result_] := 
        StringJoin[
            (* preformatted *)
            "<pre style=\"",
            (* use Courier *)
            StringJoin[{"&#", ToString[#1], ";"} & /@ ToCharacterCode["font-family: \"Courier New\",Courier,monospace;", "Unicode"]], 
            "\">",
            (* the textual form of the result *)
            (* NOTE: the OutputForm (which ToString uses) of any expressions wrapped with, say, InputForm should
                be identical to the string result of an InputForm-wrapped expression itself *)
            StringJoin[{"&#", ToString[#1], ";"} & /@ ToCharacterCode[ToString[result], "Unicode"]],
            (* end the element *)
            "</pre>"
        ];

を次のように書き直す:

(* generate HTML for the textual form of a result *)
    toOutText[result_] := 
        Switch[JupyterOutTextForm, 
        "Plain",
        StringJoin[{"&#", ToString[#1], ";"} & /@ ToCharacterCode[ToString[result], "Unicode"]],
        "TeX",
        StringJoin[
            "$$",
            StringJoin[{"&#", ToString[#1], ";"} & /@ ToCharacterCode[ToString[result], "Unicode"]],
            "$$"
        ],
        _,
        StringJoin[
            (* preformatted *)
            "<pre style=\"",
            (* use Courier *)
            StringJoin[{"&#", ToString[#1], ";"} & /@ ToCharacterCode["font-family: \"Courier New\",Courier,monospace;", "Unicode"]], 
            "\">",
            (* the textual form of the result *)
            (* NOTE: the OutputForm (which ToString uses) of any expressions wrapped with, say, InputForm should
                be identical to the string result of an InputForm-wrapped expression itself *)
            StringJoin[{"&#", ToString[#1], ";"} & /@ ToCharacterCode[ToString[result], "Unicode"]],
            (* end the element *)
            "</pre>"
        ]
        ];

これによって, 変数 JupyterOutTextForm が "Plain" と "TeX" の場合にのみテキストの表示のスタイルが変化する:

  • Plain → そのまま出力
  • TeX → \$\\$~\$\\$ で囲んで出力
  • デフォルト → <pre>~</pre\> で囲んで出力(上の変更前の動作)

数式などの画像表示の解像度の変更

このノートブックはさらに以下のような変更を

WolframLanguageForJupyter/WolframLanguageForJupyter/Resources/OutputHandlingUtilities.wl

ToOutImage函数

(* generate HTML for the rasterized form of a result *)
    toOutImage[result_] := 
        StringJoin[
            (* display a inlined PNG image encoded in base64 *)
            "<img alt=\"Output\" src=\"data:image/png;base64,",
            (* the rasterized form of the result, converted to base64 *)
            BaseEncode[
                UsingFrontEnd[ExportByteArray[
                    If[Head[result] === Manipulate, result, Rasterize[result]],
                    "PNG"
                ]]
            ],
            (* end the element *)
            "\">"
        ];

を以下のように書き換えた環境で実行している:

(* generate HTML for the rasterized form of a result *)
    toOutImage[result_] := 
        If[Head[JupyterImageResolution] === Integer,
        StringJoin[
            "<img alt=\"Output\" src=\"data:image/png;base64,",
            BaseEncode[
                UsingFrontEnd[
                If[Head[result] === Manipulate,
                    ExportByteArray[result, "GIF", "AnimationRepetitions"->Infinity],
                    If[Head[result] === InformationDataGrid,
                        ExportByteArray[Rasterize[result, ImageFormattingWidth->800, ImageResolution->2JupyterImageResolution], "PNG"], 
                        ExportByteArray[Rasterize[result, ImageFormattingWidth->1000, ImageResolution->2JupyterImageResolution], "PNG"]
                    ]
                ]]
            ],
            If[Head[result] === Manipulate || Head[result] === InformationDataGrid, 
                "\">", 
                StringJoin["\" width=\"", 
                ToString[Quotient[Rasterize[result, "RasterSize", ImageFormattingWidth->1000, ImageResolution->2JupyterImageResolution][[1]],2]], 
                "\">"]
            ]
        ],
        StringJoin[
            (* display a inlined PNG image encoded in base64 *)
            "<img alt=\"Output\" src=\"data:image/png;base64,",
            (* the rasterized form of the result, converted to base64 *)
            BaseEncode[
                UsingFrontEnd[ExportByteArray[
                    If[Head[result] === Manipulate, result, Rasterize[result]],
                    "PNG"
                ]]
            ],
            (* end the element *)
            "\">"
        ]
        ];

このように書き換えておくと, 変数 JupyterImageResolution に72(デフォルト)などの整数を代入しておくと, その2倍の解像度で画像を作って半分の大きさで表示してくれるようになる. このようにしておくと, 特に数式を画像で表示させたときの見易さが非常に上がる.

も参照せよ.

ToOutImage改造変更履歴

  • 2019-06-01: 作成.
  • 2019-06-02: Manipulateの処理のバグを訂正し, Manipulateを無限ループGIF動画で表示するようにした.
  • 2019-06-04: InformationDataGridが非常に小さく表示される問題を改善した.
In [1]:
JupyterImageResolution = 84;
JupyterOutTextForm = "TeX";

TeX[x_] := ToString[TeXForm[x]]
TeX[x_, y__] := StringJoin[TeX[x], TeX[y]]
TeXRaw[x__, y_] := StringJoin[x, TeX[y]]

MappedBy[x_] := x
MappedBy[x_, F___, G_] := MappedBy[x, F] // G

SetAttributes[TeXEq, HoldFirst]
TeXEq[x_] := TeX[HoldForm[x] == MappedBy[x, ReleaseHold, FullSimplify]]
TeXEq[x_, F__] := TeX[HoldForm[x] == MappedBy[x, ReleaseHold, F]]
In [14]:
Clear[f,x,a,b, JupyterImageResolution]
Integrate[f[x], {x,a,b}]
Out[14]:
Output
In [16]:
JupyterImageResolution = 72;
Integrate[f[x], {x,a,b}]
Out[16]:
Output
In [18]:
JupyterImageResolution = 84;
Integrate[f[x], {x,a,b}]
Out[18]:
Output
In [20]:
Clear[JupyterOutTextForm] (* default *)
Integrate[f[x], {x,a,b}] // TeX
Out[20]:
\int_a^b f(x) \, dx
In [22]:
JupyterOutTextForm = "Plain";
Integrate[f[x], {x,a,b}] // TeX
Out[22]:
\int_a^b f(x) \, dx
In [24]:
JupyterOutTextForm = "TeX";
Integrate[f[x], {x,a,b}] // TeX
Out[24]:
$$\int_a^b f(x) \, dx$$
In [26]:
A = {{0,-1},{1,0}};
A // TeX["A=",#]&
Out[26]:
$$\text{A=}\left( \begin{array}{cc} 0 & -1 \\ 1 & 0 \\ \end{array} \right)$$
In [28]:
MatrixExp[theta A] // TeXEq
Out[28]:
$$\text{MatrixExp}[\theta A]=\left( \begin{array}{cc} \cos (\theta ) & -\sin (\theta ) \\ \sin (\theta ) & \cos (\theta ) \\ \end{array} \right)$$

テスト

In [29]:
JupyterOutTextForm = "Plain";
$Version
Out[29]:
12.0.0 for Microsoft Windows (64-bit) (May 19, 2019)
In [31]:
JupyterImageResolution = 84
JupyterOutTextForm = "TeX"
Out[31]:
Out[31]:
$$84$$
Out[32]:
$$TeX$$
In [33]:
Plot3D[Sin[x y], {x, -Pi, Pi}, {y, -Pi, Pi}]
Out[33]:
Output
In [34]:
Integrate[Log[Sin[x]], x]
Out[34]:
Output

数式の出力がテキスト形式の画像になってしまう. 本当は以下のように出力してもらいたい:

$$ \int \log(\sin x)\,dx = -x\log(1-e^{2ix} + x\log(\sin x) + \frac{i}{2}(x^2 + \operatorname{Li}_2(e^{2ix})). $$

上の方で述べた方法で以下のように表示することができる.

In [35]:
Integrate[Log[Sin[x]], x] // TeXEq
Out[35]:
$$\int \log (\sin (x)) \, dx=\frac{1}{2} i \left(x^2+\text{Li}_2\left(e^{2 i x}\right)\right)-x \log \left(1-e^{2 i x}\right)+x \log (\sin (x))$$

微分方程式

単振り子

In [36]:
pendulum = {y''[x] + Sin[y[x]] == 0, y[0] == 2, y'[0] == 1};
solpendulum = NDSolve[pendulum, y, {x, -10, 10}]
Plot[y[x] /. solpendulum, {x, -10, 10}]
Plot[Evaluate[{y[x], y'[x], y''[x]} /. solpendulum], {x, -10, 10}]
ParametricPlot[Evaluate[{y[x], y'[x]} /. solpendulum], {x, -10, 10}]
Out[36]:
Out[37]:
Output
Out[38]:
Output
Out[39]:
Output
Out[40]:
Output

単振り子のManipulate

In [41]:
manipulatependulum = Manipulate[
    Module[
        {sol = NDSolve[{y''[x] + Sin[y[x]] == 0, y[0] == p[[1]], y'[0] == p[[2]]}, y, {x, 0, T}]},
        ParametricPlot[Evaluate[{y[x], y'[x]} /. sol], {x, 0, T}, PlotRange -> {{-2Pi, 2Pi}, {-3, 3}}]
    ], {{p, {2, 1}}, Locator}, {{T, 5}, 0, 20}
]
Out[41]: