Gnofract 4Dの内部

このセクションではGnofract 4Dがどのように構成されているかを説明します。プログラムを使うにあたって知っている必要はありませんが、プログラムに変更を加えたい、あるいは開発に貢献したいとしたら、このセクションの情報は便利でしょう。(開発への貢献を心からお奨めいたします。)

Gnofract 4Dは主にPythonで、一部はC++拡張で実装されています。あらゆる点できちんと動くように、Pythonのユニットテスト・フレームワークが広範囲で使われています。ひとつひとつのPythonファイル foo.py には、その機能の単体テストをするための test_foo.py が付随しています。各フォルダの 'test.py' はそのフォルダ内のすべてのテストを実行します。


ソースコードのレイアウト

ソースで重要なディレクトリは以下の通りです:

ディレクトリコンテンツ
fract4dこのディレクトリにはGUIに関係のないすべての部分が収録されています。プラットフォームから独立したコード群です。これは別の環境に移植する時のために必要なことです。(例えばサーバ上でクラスタの一部として、GUIを実装せず実行する場合です。)ほとんどのファイルはコンパイラ(下記を参照してください)の部品です。フラクタルを表現するメインクラスは fractal.py の中にあります。このクラスはコンパイル済みのコードへの参照を保持します。コンパイル済みのコードには数式と色彩関数の定義、パラメータ、カラーマップがあります。このクラスはまた、.fct ファイルのデータ読込みおよび保存、より洗練されたC++拡張のためのラッパーを扱います。
fract4d/cここには fract4dc.so の生成用にコンパイルされたC++拡張コードが含まれます。インターフェースを介してうまく通信するクラス群に分けられます。このコードの主な役割は、画像の各々のピクセルを計算する 'pointFunc' 関数を呼ぶことです。このコードはまた、'4D' 操作の大半を受け持ちます。 - vectors.h には4つのベクトルと 4x4 の行列演算が含まれます。さらにこのライブラリはマルチ・スレッド計算を扱います。タスクを threadpool.h 内のキューを介して、複数の MTFractWorkers に小分けします。
fract4dguiこれにはGUIを実装するPythonコードが含まれます。GUIツールキットとしてPyGTKを使っています。使用可能なPyGTKのバージョンで最も古いのは1.99ですが、2.4のようなより新しいPyGTKとは互換性がない部分があってちょっと不都合です。古いライブラリのサポートをやめるか、新旧の違いを隠すラッパーを作るかを決めなくてはいけません。基本的には各ダイアログやカスタムコントロール毎にひとつのクラスがあります。あとはユーティリティ目的の2、3のクラスがあります。中心的なクラスは gtkfractal です。これは計算されたフラクタルを包み、ウィンドウ内に表示するクラスです。
fract4dgui/cこれは、fract4dguic.so 拡張機能を実装するCコードを含みます。このコードは gconf の設定を取得するための最小限の機能を持っています。

コンパイラ

Gnofract 4Dの最も複雑な部分がコンパイラです。入力として UltraFractalまたはFractintの数式ファイルを用います。そしてC言語のコードを処理します。次に、Cコンパイラ(gcc)を起動して、動的に読み込めるフラクタルのコードを含む共有ライブラリを生成します。

数式ファイルのフォーマットを述べたものでは、UltraFractalのマニュアルが最良です。ただ、Gnofract 4Dがまだサポートしていない機能に関する記述もいくつかあります。マニュアルのPDFはここからダウンロードできます。

実装はModern Compiler Implementation in ML: Basic Techniques (Appel 1997, Cambridge)に述べられた概説に基づいています。現時点では最適化はしていません。Cコンパイラがバックエンドで使われるままにしてあります。たぶんシンプルな最適化は実装する価値があるでしょう。(たとえば定数畳み込み、1.0倍の乗算を排除することです。)C言語は浮動小数点数にはこれを行わないようになっていますから。

全体的な構造: 字句解析とSLRアルゴリズムによる構文解析のために PLY パッケージが使われています。lex.py と yacc.py が解析を行ないます。fractlexer.py と fractparser.py はそれぞれの解析を定義します。これらのスクリプトはAbsynモジュールと定義される抽象構文ツリーを出力します。翻訳モジュールがコードをタイプチェックして、シンボルテーブルを保ち(symbol.py)中間フォームに変換します(ir.py)。規範によってirの段階でいくつかのパスが簡素化されます。そして codegen がC言語の単純な命令を直鎖状に配列します。stdlib.py には cosh(z) のような数学関数の「標準ライブラリ」が含まれています。現時点では複素数と超複素数の変数は浮動小数点数のペアに展開されます。Cコードでは複素数であることが忘れられます。最後に、ネイティブコードの共有ライブラリに変換するためCコンパイラが実行されます。

実行時には、さまざまなフェーズが異なるタイミングで起こります。まず、.frm 全体の字句と構文が解析されます。その後、特定の数式が選択されると、その式は翻訳され構文がチェックされます。実際のコードはフラクタルが描画される直前に生成されます。このフェーズは関数のパラメータが変更される度に繰り返されます。(たとえば @fn1 に 'cosh' が指定されるなど。)

たぶん、コードの一番醜い部分はパラメータの処理でしょう。浮動小数などの数値は配列に渡されます。このためC++コードとPythonコードは、どのパラメータとどのインデックスが対応しているか解決するために、互いに協力する必要があります。これはアルファベット順にソートすることでなされます。概してこの部分は多少混乱しています。


スレッディング

コードの中でヘンな部分といえば、スレッドをどう扱うかという部分でしょう。基本的にフラクタルの計算は、メインGUIとは違うスレッドで(あるいはSMP上のマルチスレッドで)行なわれてほしいと思います。これが、Pythonがグローバル・インタープリタ・ロックで単一のスレッドしか扱えないこと、それとPyGTKがしばしばLinuxディストリビューションでスレッドサポートを欠いてコンパイルされることのために、複雑になります。意味するのは、GTKのmainループが走っている時はインタープリタ・ロックがリリースされないということです。

この状況での解決法は、追加のスレッドは、PythonとGTKからは見えないC++コード内だけで走らせることです。async=True の条件下で pycalc が呼び出された時、pycalcは計算をするためのスレッドを発生させます。そのスレッドは、複数のスレッドが必要な場合はさらに担い手を発生させます。これらはすべてイメージバッファに書き込まれ、稼働状況のメッセージをパイプに書き込むことで報告を返します。このパイプはGTKのmainループが監視しているリストに追加され、新しいメッセージが現れる度に gtkfractal コードへのコールバックを得ることになります。スレッドのメッセージと通常のGTKイベントは交互に配置されます。計算スレッドをチェックする頻度を変数で指定しておけば、進行中の計算を中断することができます。中断された計算はそこで破棄されスレッドは終了します。

注意
マルチスレッドとC++拡張はうまく共存できません。少なくとも、Gnofract 4Dが実行する libstdc++ の拡張コードのいくつかは、そのケースに該当します。そのため、当該C++コードは例外を返すことができなかったり、クラッシュを含むかなりヘンなことが起こる可能性があります。


< 数式言語リファレンス バグと既知の問題 >