コード生成
Wolfram言語からコード生成する場合には,Wolfram言語で書かれたプログラムを別の言語に変換してから,そのプログラムが実行できるようにサポートする必要がある.Wolframシステムのコンパイラは,C言語でのコード生成を行うシステムを提供する.
CCodeGeneratorパッケージ
CCodeGeneratorパッケージは,Wolfram言語からのコード生成の主要構成要素である.このパッケージは下に挙げるような多くの関数を提供し,これらの関数はCコードを生成するのにWolframシステムのコンパイラを利用する.
| CCodeGenerate[comp,fun,file] | 関数 fun を使って,コンパイルされた関数 comp のCコードを生成し,file 内に保存する |
| CCodeStringGenerate[comp,fun] | 関数 fun を使って,コンパイルされた関数 comp のCコードの文字列を生成する |
| SymbolicCGenerate[comp,fun] | 関数 fun を使って,コンパイルされた関数 comp の記号的なCを生成する |
| LibraryGenerate[comp,fun] | 関数 fun を使って,コンパイルされた関数 comp の共有ライブラリを生成する |
CCodeGeneratorパッケージを使うには,これをロードする必要がある.
<<CCodeGenerator`これはコード生成の例として使われるコンパイルされた関数である.
cFun = Compile[{{x}}, x ^ 2 + Sin[x ^ 2]];CCodeStringGenerate[cFun, "compute"]コードフラグメントはライブラリ内にコンパイルできるように設計され,一般的な初期化,この関数のライフサイクル管理,関数の実際のボディを含む.
アプリケーションは,ヘッダファイルをインポートし,共有ライブラリ等適切なコードモジュールにリンクさせることによって,この関数を呼び出すことができる.また,以下に示すように,"CodeTarget"オプションを"WolframRTLHeader"に設定することによって,ヘッダを生成することができる.
CCodeStringGenerate[cFun, "compute", "CodeTarget" -> "WolframRTLHeader"]一般的な初期化
生成されたCの最初の部分には,一般的なCの初期化が含まれる.以下はその例である.
#include "math.h"
#include "WolframRTL.h"
static WolframCompileLibrary_Functions funStructCompile;
static mbool initialize = 1;
WolframRTL.hはヘッダファイルで,さまざまな重要定義を設定する.
WolframCompileLibrary_Functionsはコールバック関数を保持する型であり,これはいかなる状態も保持しない不変オブジェクトである.この1例であるfunStructCompileは,ファイルの中に保存される.
initializeはmbool変数であり,特定の初期化関数によって使われる.
ライフサイクル管理
生成されたCには,コードのライフサイクルを管理するセクションが含まれる.これには,特定の関数に関係する初期化と非初期化が含まれる.以下はその例である.
# include "compute.h"
DLLEXPORT int Initialize_compute (WolframLibraryData libData)
{
if ( initialize)
{
funStructCompile = libData -> compileLibraryFunctions;
initialize = 0;
}
return 0;
}
DLLEXPORT void Uninitialize_compute (WolframLibraryData libData)
{
if ( ! initialize)
{
initialize = 1;
}
}
これには,"CodeTarget"オプションを"WolframRTLHeader"に設定することによって生成されるヘッダファイルが含まれる.また構造を初期化する関数であるInitialize_computeと任意の状態を非初期化する関数であるUninitialize_computeも含まれる.初期化関数は,主要関数が使用される前に呼び出されなくてはならない.
初期化と非初期化の関数はWolframLibraryDataの引数とともに呼び出す必要がある.これはロードする関数によって作成することができる.
関数
関数の実際のボディが下に示されている.名前「compute」はGenerateCCodeStringへのセルに設定されている.
DLLEXPORT int compute (WolframLibraryData libData, mreal A1, mreal *Res)
{
mreal R0_0;
mreal R0_1;
mreal R0_2;
mreal R0_3;
R0_0 = A1;
R0_1 = R0_0 * R0_0;
R0_2 = sin (R0_1);
R0_3 = R0_1 + R0_2;
*Res = R0_3;
funStructCompile -> WolframLibraryData_cleanUp (libData, 1);
return 0;
}
これは,関数の引数がどのように入力を取るか,そしてポインタとしての結果を示している.実際の戻り値はエラーを示すのに使われる.関数はWolframLibrary.hで定義されたマクロであるDLLEXPORTで宣言される.これによって関数をWindows DLLからエキスポートすることができる.Windowsではないプラットフォーム上では,マクロは空の定義を持つ.
関数はWolframLibraryData引数も必要とする.これはロードする関数によって作成することができる.
生成されたコードを使う
Wolfram言語を使って生成したコードは,Wolframシステムのレイアウトに含まれるさまざまなサポートファイルを必要とする.構築の宣言を含むヘッダファイル,および生成された関数をサポートするのに必要な実際のコードを含むライブラリがそれに含まれる.
生成されたコードによって使用されるヘッダファイル.
/SystemFiles/IncludeFiles/Cにある.
それぞれのプラットフォームについて,生成されたコードをサポートするさまざまなランタイムライブラリがある.
| WolframRTL.dll | ランタイムサポート用の完全共有ライブラリ |
| WolframRTL.lib | ランタイムサポート用の完全共有ライブラリのためにエキスポートする |
| WolframRTL_Minimal.dll | ランタイムサポート用の最小共有ライブラリ |
| WolframRTL_Minimal.lib | ランタイムサポート用の最小共有ライブラリのためにエキスポートする |
| WolframRTL_Static_Minimal.lib | ランタイムサポート用の最小スタティックライブラリ |
Windowsプラットフォーム上の生成されたコードをサポートするランタイムライブラリ.
/SystemFiles/Libraries/$SystemIDにある.
| libWolframRTL.so | ランタイムサポート用の完全共有ライブラリ |
| libWolframRTL_Minimal.so | ランタイムサポート用の最小共有ライブラリ |
| libWolframRTL_Static_Minimal.a | ランタイムサポート用の最小スタティックライブラリ |
Linuxプラットフォーム上の生成されたコードをサポートするランタイムライブラリ.
/SystemFiles/Libraries/$SystemIDにある.
| libWolframRTL.dylib | ランタイムサポート用の完全共有ライブラリ |
| libWolframRTL_Minimal.dylib | ランタイムサポート用の最小共有ライブラリ |
| libWolframRTL_Static_Minimal.a | ランタイムサポート用の最小スタティックライブラリ |
Macintoshプラットフォーム上の生成されたコードをサポートするランタイムライブラリ.
/SystemFiles/Libraries/$SystemIDにある.
完全ランタイムライブラリはすべてWolfram言語とセットになったIntel MKLライブラリ等の他のライブラリに依存する.これによって大変速く実行することができるが,これはつまり,Wolfram言語から外部的にライブラリを使うには,MKLライブラリを使うように設定しなければならないということである.
最小ライブラリは,他のライブラリに依存する度合が低い.例えば,Intel MKLライブラリには全く依存しない.このため,行列の積等MKLを使わなければならない計算の実行スピードは遅くなる.
何でも手持ちのツールを使って生成されたファイルをコンパイルすることができる.このドキュメントでは,どのようにWolfram言語のC Compiler Driverパッケージを使うかを示す.まず,パッケージをロードしなくてはならない.
Needs["CCodeGenerator`"];
Needs["CCompilerDriver`"];
Needs["SymbolicC`"];以下でファイルを保持する一時的なディレクトリと結果の実行ファイルを作成する.
outDir = CreateDirectory[]以下は生成されたコードを検証するCコードの例であり,SymbolicCを使って書かれている.これは関数computeが引数に渡されるように呼び出し,結果を受け取ってから出力に表示する.
code =
CProgram[
CInclude["stdio.h"],
CInclude["compute.h"],
CInclude["WolframRTL.h"],
CFunction["int", "main", {{"int", "argc"}, {"char", CDereference[CArray["arg", {}]]}},
CBlock[{
CDeclare["double", CAssign["num1", 20.4]],
CDeclare["double", "num2"],
CDeclare["WolframLibraryData", CAssign["libData", CCall["WolframLibraryData_new", {"WolframLibraryVersion"}]]],
CCall["Initialize_compute", {"libData"}],
CCall["compute", {"libData", "num1", CAddress["num2"]}],
CCall["printf", {CString["%5.2f\
"], "num2"}],
CCall["WolframLibraryData_free", {"libData"}],
CReturn[0]}]]
];
cStr = ToCCodeString[code]呼出し関数がWolframLibraryDataオブジェクトを作成し,これを使ってinitialize関数を呼び出す.またcompute関数を呼び出すのにも使われる.
mainSource = Export[FileNameJoin[{outDir, "main.c"}], cStr, "Text"]ここではコンパイルされた関数は作成されてから,Cファイルとヘッダファイルを生成するのに使われている.すべての出力は上で作成された出力ディレクトリに出力される.
c = Compile[{{x}}, x ^ 2];
CCodeGenerate[c, "compute", FileNameJoin[{outDir, "compute.c"}], "CodeTarget" -> "WolframRTL"];
CCodeGenerate[c, "compute", FileNameJoin[{outDir, "compute.h"}], "CodeTarget" -> "WolframRTLHeader"];すべてのソースファイルを実行ファイル中にコンパイルする.スタティック最小ライブラリの中でリンクされる.これはこのデモンストレーションを設定する簡単な方法である.別のプラットフォームでは,適切なライブラリを使う必要がある.
exe = CreateExecutable[{mainSource, FileNameJoin[{outDir, "compute.c"}]}, "testgenerate", "IncludeDirectories" -> outDir, "Libraries" -> "WolframRTL_Static_Minimal",
"TargetDirectory" -> outDir]生成されたコードはシェルで実行することができる.コードはコンパイルされた関数で始めた計算を実行してその結果を表示する.
また,Importを使って結果を読み取り,Wolfram言語から生成されたコードを実行することもできる.
Import["!" <> exe, "Text"]コード生成のために,自分の作成した実行ファイルを他のランタイムライブラリのいずれかにリンクさせたいという場合もあるかもしれない.
制御式
StateSpaceModel式等,制御式にはCコードを生成することができるものもある.
ss = StateSpaceModel[TransferFunctionModel[z / ((z - 0.9) * (z + 0.8)), z, SamplingPeriod -> 1]]outDir = CreateDirectory[]ここでは,状態空間モデル用のCコードが生成され,ファイルcontrol.cに保存される.
file = FileNameJoin[{outDir, "control.c"}];
Export[ file, ss, "C"];FileNames["*", outDir]このCコードはCCompilerDriverパッケージを使って,実行ファイルかライブラリの中にコンパイルすることができる.まずパッケージをロードしなければならない.
Needs["CCompilerDriver`"]CreateLibrary[{file},
"control",
"IncludeDirectories" -> outDir,
"TargetDirectory" -> outDir]これで,Wolfram言語にとって外部の計算にライブラリを使うことができる.
生成されたコードのスコープ
このセクションには,コード生成器で使えるWolfram言語の計算のスコープが記述されている.
コード生成器はWolframシステムのコンパイラに基づく.コード生成器を前進させるためには,Compileを使用する必要がある.
しかし,コンパイラで処理された入力すべてが生成されたコードに変換できるわけではない.外部の呼出しはWolfram言語を呼び戻すのでコード生成には使用できない.