https://github.com/ohno/runningexternalprograms
Running External Programs from The Julia Programming Language
https://github.com/ohno/runningexternalprograms
Last synced: 4 months ago
JSON representation
Running External Programs from The Julia Programming Language
- Host: GitHub
- URL: https://github.com/ohno/runningexternalprograms
- Owner: ohno
- License: mit
- Created: 2021-08-31T22:24:49.000Z (almost 5 years ago)
- Default Branch: main
- Last Pushed: 2021-09-02T22:30:59.000Z (almost 5 years ago)
- Last Synced: 2025-06-21T22:02:41.891Z (12 months ago)
- Language: Fortran
- Size: 47.9 KB
- Stars: 0
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.ipynb
- License: LICENSE
Awesome Lists containing this project
README
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Juliaから外部プログラムを実行する\n",
"\n",
"© 2021 Shuhei Ohno\n",
"
License: https://opensource.org/licenses/MIT\n",
"
Repository: https://github.com/ohno/RunningExternalPrograms\n",
"\n",
"[Calling C and Fortran Code](https://docs.julialang.org/en/v1/manual/calling-c-and-fortran-code/)によると, JuliaではCやFortranのルーチンを`ccall`で呼び出せます. しかし, 共有ライブラリとしてコンパイルしないとけないので, 標準入力と標準出力でデータをやり取りするメインプログラムを呼び出すことは難しいように思われます. **元のプログラムには手を加えない**で, Julaiからプログラムを呼び出せるようにすることがこのノートのテーマです. CやFortranに限らず, 外部プログラムを呼び出す方法は[Running External Programs](https://docs.julialang.org/en/v1/manual/running-external-programs/)に概ね書いてありますが, サンプルが少なく, Windows環境だといろいろ引っかかるポイントがあるので補足説明していきます. 環境は以下の通りです."
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Julia Version 1.6.2\n",
"Commit 1b93d53fc4 (2021-07-14 15:36 UTC)\n",
"Platform Info:\n",
" OS: Windows (x86_64-w64-mingw32)\n",
" CPU: Intel(R) Core(TM) i7-4650U CPU @ 1.70GHz\n",
" WORD_SIZE: 64\n",
" LIBM: libopenlibm\n",
" LLVM: libLLVM-11.0.1 (ORCJIT, haswell)\n"
]
}
],
"source": [
"versioninfo()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Hello World!\n",
"\n",
"さっそくですがHello World!していきましょう. 戻り値が不要な(標準出力で良い)場合は`run()`, 結果を受け取りたい場合は`read()`か`readchomp()`です. なお, コマンドを囲むのはバッククォートです. \n",
"\n",
"これはWindowsでは動きません"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"run(`echo hello`)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"read(`echo hello`, String)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"readchomp(`echo hello`)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Windowsでのechoはこっち!"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"hello\r\n"
]
},
{
"data": {
"text/plain": [
"Process(`\u001b[4mcmd\u001b[24m \u001b[4m/C\u001b[24m \u001b[4mecho\u001b[24m \u001b[4mhello\u001b[24m`, ProcessExited(0))"
]
},
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"run(`cmd /C echo hello`)"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"\"hello\\r\\n\""
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"read(`cmd /C echo hello`, String)"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"\"hello\""
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"readchomp(`cmd /C echo hello`)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"型はそれぞれ以下のようになります. 出力に`hello`が挟まりますが気にしないでください."
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"1. Cmd\n",
"hello\n",
"2. Base.Process\n",
"3. String\n",
"4. SubString{String}\n"
]
}
],
"source": [
"println(\"1. \", typeof(`cmd /C echo hello`))\n",
"println(\"2. \", typeof(run(`cmd /C echo hello`)))\n",
"println(\"3. \", typeof(read(`cmd /C echo hello`, String)))\n",
"println(\"4. \", typeof(readchomp(`cmd /C echo hello`)))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"メモ帳`notepad.exe`などのプログラムを呼び出すこともできます."
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"Process(`\u001b[4mnotepad\u001b[24m`, ProcessExited(0))"
]
},
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"run(`notepad`)"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"Process(`\u001b[4mnotepad.exe\u001b[24m`, ProcessExited(0))"
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"run(`notepad.exe`)"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"Process(`\u001b[4mnotepad.exe\u001b[24m \u001b[4mprogram1.f90\u001b[24m`, ProcessExited(0))"
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"run(`notepad.exe program1.f90`)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 標準出力のみ\n",
"\n",
"Hello World!と同じですが, 一応確認しましょう. 次の`program1.f90`は4という数値を標準出力に返すだけのプログラムです. コンパイルして生成された`program1.exe`を呼び出して標準出力の動作を確認していきます. (ファイルは[リポジトリ](https://github.com/ohno/RunningExternalPrograms)にあります. `gfortran`がインストールされてパスが通っていれば`compile.bat`をクリックすると勝手にコンパイルされます.)\n",
"\n",
"`program1.f90`\n",
"```fortran\n",
"program main\n",
" write(6,*) 4\n",
"end program main\n",
"```"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
" 4\r\n"
]
}
],
"source": [
"run(`program1`);"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
" 4\r\n"
]
}
],
"source": [
"run(`program1.exe`);"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
" 4\r\n"
]
},
{
"data": {
"text/plain": [
"Process(`\u001b[4mcmd\u001b[24m \u001b[4m/C\u001b[24m \u001b[4mprogram1.exe\u001b[24m`, ProcessExited(0))"
]
},
"execution_count": 11,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"run(`cmd /C program1.exe`)"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"\" 4\\r\\n\""
]
},
"execution_count": 12,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"read(`program1.exe`, String)"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"\" 4\""
]
},
"execution_count": 13,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"readchomp(`program1.exe`)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 標準入力から変数を1つ渡す\n",
"\n",
"次の`program2.f90`は標準入力をread文で読み取り, 数値を2乗して標準出力に返すプログラムです. コンパイルして生成された`program2.exe`を呼び出して動作を確認していきます. (ファイルは[リポジトリ](https://github.com/ohno/RunningExternalPrograms)にあります. `gfortran`がインストールされてパスが通っていれば`compile.bat`をクリックすると勝手にコンパイルされます.)\n",
"\n",
"`program2.f90`\n",
"```fortran\n",
"program main\n",
" implicit none\n",
" integer x\n",
" read(5,*) x\n",
" write(6,*) x**2\n",
"end program main\n",
"```\n",
"`input2.txt`\n",
"```\n",
"5\n",
"```\n",
"\n",
"まず, コマンドラインから`<`によって標準入力を渡せますが, `<`は`'<'`のようにシングルクォーテーションで囲います."
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
" 25\r\n"
]
},
{
"data": {
"text/plain": [
"Process(`\u001b[4mcmd\u001b[24m \u001b[4m/C\u001b[24m \u001b[4mprogram2.exe\u001b[24m \u001b[4m'<'\u001b[24m \u001b[4minput2.txt\u001b[24m`, ProcessExited(0))"
]
},
"execution_count": 14,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"run(`cmd /C program2.exe '<' input2.txt`)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"コマンドプロンプトを経由せずに実行するには`pipeline()`を使います. 引数`stdin`にファイル名やコマンドを与えることができます. ファイル名の時はダブルクォーテーション, コマンドはバッククォートなので気を付けてください."
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
" 25\r\n"
]
},
{
"data": {
"text/plain": [
"Process(`\u001b[4mprogram2.exe\u001b[24m`, ProcessExited(0))"
]
},
"execution_count": 15,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"run(pipeline(`program2.exe`, stdin=\"input2.txt\"))"
]
},
{
"cell_type": "code",
"execution_count": 16,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
" 36\r\n"
]
},
{
"data": {
"text/plain": [
"Base.ProcessChain(Base.Process[Process(`\u001b[4mcmd\u001b[24m \u001b[4m/C\u001b[24m \u001b[4mecho\u001b[24m \u001b[4m6\u001b[24m`, ProcessExited(0)), Process(`\u001b[4mprogram2.exe\u001b[24m`, ProcessExited(0))], Base.DevNull(), Base.DevNull(), Base.DevNull())"
]
},
"execution_count": 16,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"run(pipeline(`program2.exe`, stdin=`cmd /C echo 6`))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"以下のように`open`文で標準入力を渡すこともできます. 恐らく, 結果を受け取れないようなので`run`でよいと思います. 強いて言うなら, `open`と`end`の間で`for`文を回したり, `write`, `print`,`println`などが使い分けられるなどのメリットがあります."
]
},
{
"cell_type": "code",
"execution_count": 17,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
" 49\r\n"
]
}
],
"source": [
"open(`program2.exe`, \"w\", stdout) do io\n",
" println(io, 7)\n",
"end"
]
},
{
"cell_type": "code",
"execution_count": 18,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
" 49\r\n"
]
}
],
"source": [
"io = open(`program2.exe`, \"w\", stdout)\n",
"println(io, 7)\n",
"close(io)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 標準入力から変数を2つ渡す\n",
"\n",
"次の`program3.f90`は標準入力をread文で読み取り, 2つの数値の和を取って標準出力に返すプログラムです. 基本的には先ほどの例と同じですが, Fortran側のプログラムが2つの数値を読み取れるようになっています. コンパイルして生成された`program3.exe`を呼び出して動作を確認していきます. (ファイルは[リポジトリ](https://github.com/ohno/RunningExternalPrograms)にあります. `gfortran`がインストールされてパスが通っていれば`compile.bat`をクリックすると勝手にコンパイルされます.)\n",
"\n",
"`program3.f90`\n",
"```fortran\n",
"program main\n",
" implicit none\n",
" double precision x, y\n",
" read(5,*) x, y\n",
" write(6,*) x + y\n",
"end program main\n",
"```\n",
"`input2.txt`\n",
"```\n",
"5\n",
"6\n",
"```"
]
},
{
"cell_type": "code",
"execution_count": 19,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
" 11.000000000000000 \r\n"
]
},
{
"data": {
"text/plain": [
"Process(`\u001b[4mprogram3.exe\u001b[24m`, ProcessExited(0))"
]
},
"execution_count": 19,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"run(pipeline(`program3.exe`, stdin=\"input3.txt\"))"
]
},
{
"cell_type": "code",
"execution_count": 20,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
" 12.000000000000000 \r\n"
]
},
{
"data": {
"text/plain": [
"Base.ProcessChain(Base.Process[Process(`\u001b[4mcmd\u001b[24m \u001b[4m/C\u001b[24m \u001b[4mecho\u001b[24m \u001b[4m5\u001b[24m \u001b[4m7\u001b[24m`, ProcessExited(0)), Process(`\u001b[4mprogram3.exe\u001b[24m`, ProcessExited(0))], Base.DevNull(), Base.DevNull(), Base.DevNull())"
]
},
"execution_count": 20,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"run(pipeline(`program3.exe`, stdin=`cmd /C echo 5 7`))"
]
},
{
"cell_type": "code",
"execution_count": 21,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
" 13.000000000000000 \r\n"
]
}
],
"source": [
"open(`program3.exe`, \"w\", stdout) do io\n",
" println(io, \"5 8\")\n",
"end"
]
},
{
"cell_type": "code",
"execution_count": 22,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
" 14.000000000000000 \r\n"
]
}
],
"source": [
"input = \"5\n",
"9\"\n",
"\n",
"open(`program3.exe`, \"w\", stdout) do io\n",
" println(io, input)\n",
"end"
]
},
{
"cell_type": "code",
"execution_count": 23,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
" 15.000000000000000 \r\n"
]
}
],
"source": [
"open(`program3.exe`, \"w\", stdout) do io\n",
" println(io, 5)\n",
" println(io, 10)\n",
"end"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"配列として渡したい場合には以下のように対処します."
]
},
{
"cell_type": "code",
"execution_count": 24,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
" 16.000000000000000 \r\n"
]
},
{
"data": {
"text/plain": [
"Base.ProcessChain(Base.Process[Process(`\u001b[4mcmd\u001b[24m \u001b[4m/C\u001b[24m \u001b[4mecho\u001b[24m \u001b[4m5\u001b[24m \u001b[4m11\u001b[24m`, ProcessExited(0)), Process(`\u001b[4mprogram3.exe\u001b[24m`, ProcessExited(0))], Base.DevNull(), Base.DevNull(), Base.DevNull())"
]
},
"execution_count": 24,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"input = [5,11]\n",
"run(pipeline(`program3.exe`, stdin=`cmd /C echo $input`))"
]
},
{
"cell_type": "code",
"execution_count": 25,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
" 17.000000000000000 \r\n"
]
}
],
"source": [
"input = [5,12]\n",
"open(`program3.exe`, \"w\", stdout) do io\n",
" for x in input\n",
" println(io, x)\n",
" end\n",
"end"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 外部プログラムを関数として扱う\n",
"\n",
"先ほどの`program3.exe`を例に解説します. このプログラムを関数として扱うには, 戻り値が必要なので`read()`か`readchomp()`を利用します. これらの結果は文字列なので, さらに`parse()`を使って数値に型変換します."
]
},
{
"cell_type": "code",
"execution_count": 26,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"f (generic function with 1 method)"
]
},
"execution_count": 26,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"f(X) = parse(Float64,readchomp(pipeline(`program3.exe`, stdin=`cmd /C echo $X`)))"
]
},
{
"cell_type": "code",
"execution_count": 27,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"18.0"
]
},
"execution_count": 27,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"y = f([5,13])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 外部プログラムのパラメータ最適化\n",
"\n",
"ここでは[Optim.jl](https://github.com/JuliaNLSolvers/Optim.jl)を利用します. 事前に[パッケージモード](https://qiita.com/skiing_LAL10/items/0c0132a34629fbc8a91f)で`add Optim`を実行してインストールし, ノート上では`using Optim`を宣言しておく必要があります. まず, [Rosenbrock関数](https://en.wikipedia.org/wiki/Rosenbrock_function)を最小化する例を見てみましょう."
]
},
{
"cell_type": "code",
"execution_count": 28,
"metadata": {},
"outputs": [],
"source": [
"# using Pkg\n",
"# Pkg.add(\"Optim\")\n",
"using Optim"
]
},
{
"cell_type": "code",
"execution_count": 29,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[0.9999634355313174, 0.9999315506115275]\n",
"3.5255270584829996e-9\n"
]
}
],
"source": [
"rosenbrock_julia(x) = (1.0 - x[1])^2 + 100.0 * (x[2] - x[1]^2)^2\n",
"x0 = [0.0, 0.0]\n",
"opt = optimize(rosenbrock_julia, x0)\n",
"\n",
"println(Optim.minimizer(opt))\n",
"println(Optim.minimum(opt))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"次の`program4.f90`は標準入力から変数$x,y$を受け取ってRosenbrock関数の値を標準出力に返す例です. このプログラムをコンパイルした`program4.exe`をJuliaの関数としての扱い, $x,y$を最適化します.\n",
"\n",
"`program4.f90`\n",
"```fortran\n",
"program main\n",
" implicit none\n",
" double precision x, y\n",
" read(5,*) x, y\n",
" write(6,*) 100.*(x*x-y)**2+(1.-x)**2\n",
"end program main\n",
"```"
]
},
{
"cell_type": "code",
"execution_count": 30,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[0.9999634355313174, 0.9999315506115275]\n",
"3.5255270584829996e-9\n"
]
}
],
"source": [
"rosenbrock_fortran(x) = parse(Float64,readchomp(pipeline(`program4.exe`, stdin=`cmd /C echo $x`)))\n",
"x0 = [0.0, 0.0]\n",
"opt = optimize(rosenbrock_fortran, x0)\n",
"\n",
"println(Optim.minimizer(opt))\n",
"println(Optim.minimum(opt))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Juliaだけの例と, Fortranと連携した例で全く同じ結果が得られました."
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Julia 1.6.2",
"language": "julia",
"name": "julia-1.6"
},
"language_info": {
"file_extension": ".jl",
"mimetype": "application/julia",
"name": "julia",
"version": "1.6.2"
}
},
"nbformat": 4,
"nbformat_minor": 4
}