読者です 読者をやめる 読者になる 読者になる

WPF の binding に驚いた人をさらに唸らせるかもしれない Mathematica の Dynamic

.NET

XAML のデータバインディングって「プログラミング言語」っぽいよね? とか一瞬思ったので今日は Mathematica 6.0 の Dynamic の話.
Mathematica は学生を除いてお値段がアレ過ぎるので,ほとんどの人はこの機能をご存じないかと思いますが,かなり面白い試みなので機能の名前と概要ぐらいは憶えておくと良いかも,みたいな.学生ならお値段そこそこなので是非試すべし.大学でサイトライセンス持っていることも多いですしね.そういう人は使えるうちに遊んでおくと吉.

Mathematica 6.0 の Dynamic って何?

動的インタラクティブ機能言語

Mathematica の動的インタラクティブ機能の基本は,新しい形式の記号動的言語である.他の言語構造と自由に交わることのできる非常に強力な少数のプリミティブを使って,直接記号的なスタイルでプログラムを書くことができる.Mathematica は自動的に依存関係を追跡し,あらゆる種類のコントロールと出力を動的に更新する.

Mathematica ではカーネルと呼ばれる計算プロセスと,フロントエンドと呼ばれる UI プロセスが分離しています.計算やシンボル管理は基本的にカーネルで行われ,フロントエンドは文字通り表示と入力のみしか行いません.Web サーバとブラウザの関係みたいなものです.
このアナロジーで言えば,Dynamic という仕組みは MathematicaAJAX です.もっとも,Mathematica通信プロトコルは従来型の計算に非同期通信を使い,Dynamic 型の動的更新に同期通信を使うので,その点は多少語弊があるかもしれませんけど.

メインリンクはShift+Enterの評価に使われる.フロントエンドは保留中の評価リクエストのキューを維持し,このリンクに送る.1つ以上の入力セルでShift+Enterを使うと,それらはすべてキューに加えられ,1つずつ処理される.どのときもカーネルは1つのメインリンク評価だけ(現在使用中のものがあればそれ)を扱う.その間,フロントエンドは完全に機能している.タイプしたり,ファイルを開いて保存したりすること等もできる.メインリンクの評価にどのくらいかかるかについての制限はない.終了するのに何日もかかるような評価を日常的に行う人もいる.

割込みリンクはフロントエンドが評価を送り答を得るという意味ではメインリンクと同様に動作するが,両側で全く異なる方法で管理される.フロントエンド側では割込みリンクは通常のDynamic更新を扱うために使われる.キューはない.その代り,フロントエンドは1度に1つの評価を送り,その他のタスクを続ける前に結果を待つ.従って,割込みリンクの評価を最大で数秒に制限することが大切である.割込みリンクの評価中,フロントエンドは完全に使われているため,タイプや他のアクションはできない.

変更の検出戦略

Dynamic とラベリングされたオブジェクトの変更はフロントエンドによって常に監視されていて,値が変更されると割り込みリンクを通じてカーネルに再計算を行わせます.と口で言うのは簡単ですが,実装の話は結構深くて面白いです.

動的出力の指定は簡単である.Dynamic[expr]はexprを今評価した場合に得られる値を常に表示する.変数値,あるいはシステムのその他の状態が変更されると,動的出力も直ちに更新される.もちろん効率的な理由で,任意の変数が変更されるたびにすべての動的出力が再評価されるというわけではない.動的出力は,必要なときにだけ評価されるよう,依存性を追跡することが必要である.

次の2つの式を考える.

In[9]:= Dynamic[a+b+c]
Out[9]= a+b+c
In[10]:= Dynamic[If[a, b, c]]
Out[10]= If[a, b, c]
はじめの式では,a,b,c の値が変更されるたび,あるいはa,b,c に関連付けられたパターンが変更されるたびに値が変化するかもしれない.2つ目の式は,a がTrueならばc ではなくa とb に依存し,a がFalseならばb ではなくa とc に依存する.a がTrueでもFalseでもなければ,出力はa のみに依存する(If文が評価されずに戻るからである). 先験的にこのような依存性を見付けることは不可能(この効果の定理がある)なので,与えられた式を評価する過程で,実際にどの変数あるいは追跡可能な実体に遭遇したかを,システムが追跡する.その後,与えられた変数が新しい値を受け取ったときに,それを告知されなければならない動的式はいずれかを特定する変数とデータが関連付けられる. システム設計上の大切な目標は,特に変数の値が素早く変更されている場合にでも,システムに絶対必要量以上の負荷をかけることなく,変数値を参照する動的出力により,変数値の監視を許可することである. 次の簡単な例題を考える.
In[11]:= Dynamic[x]
Out[11]= x
In[13]:= Do[x, {x, 1, 5000000}]

動的出力が生成されると,それは評価され,シンボルx は値が変化したときに更新する必要のある出力を見分ける情報でタグが付く. ループが開始されx に初めて新しい値が与えられると,それに関連付けられたデータが問い合され,動的出力を更新する必要があることがフロントエンドに知らされる.その後x に関連付けられたデータは削除される.必然的に,システムは動的出力のことはすべて忘れ,ループにおける後続の割当てはx の値を監視する動的出力があることによるスピードの遅延は全く起らない.

そのずっと後(コンピュータの時間スケール上の話であって,人間の時間スケールだと1秒足らずの間に),スクリーンが再描画されx への参照を含む動的出力が再評価されると,動的出力と変数x との間の接続が再び「認識」され,関連付けが再構築される. その間ループはずっと実行し続けている.次回スクリーンが更新された後に割当てが終了すると,フロントエンドに別の知らせが送られ,プロセスが繰り返される.

言語との融合

XAML での Binding 構文の使い方は言語内言語という感じで割と制限されていて,C# と INotifyPropertyChanged の組み合わせはかなりの暗黒面.では Mathematica はどうかというと,無茶しやがって勇敢にも従来文法との融合を目指していてかなり面白いことになっています.
Dynamic は「画面更新により Force される遅延評価」と見ても面白いです.

デフォルトで,変数値の変化により引き起される動的出力は,最高で毎秒20回更新される(この割合はSystemOptionの"DynamicUpdateInterval"で変更できる).前の例では,各更新で値が数万,数十万倍跳ね上がるのが見られる(コンピュータのスピードが速ければ速いほど,この割合も大きくなる)が,計算の全体的なスピードはほんの1〜2%しか遅くならず,マルチプロセッサシステムではほぼゼロに近い. きついループで急速に変化しているシンボルの値を監視する動的出力があると,ループが格段に遅くなるように思うかもしれない.しかし,実際にはオーバーヘッドは,変数が変化する割合では0次であり,事実上通常最小限である.

動的出力はスクリーン上で可視なときにのみ更新される.この最適化により,プロセッサに多量の負荷を与えなくても絶え間なく変化する無数の動的出力を使うことができる.現在の文書の位置の上か下にある,スクリーンから見えない部分の出力は次回スクリーン上で見えるようになるまで評価されないままになる.スクリーンに現れたときに,表示される前に更新される.(従って,出力が更新されなくなるということは,副作用でもない限り通常はっきり分からないが,一般に副作用はない方がよい.)

動的出力は,変数以外のものに依存することもある.その場合,追跡も注意深く選択的に行われる.

以下は,スクリーン座標における現在のマウス位置を即座に更新して表示する.

In[14]:= Dynamic[MousePosition[]]
Out[14]= {0,0}

これが画面上に見えている限り,この動的出力はマウスの動きにつれて即座に再描画されるため,マウスが動くたびにある量のCPU動作がある.しかし,スクリーンから見えなくなると,CPU使用量はなくなる.

例えば (画面に表示されている限り) 毎秒 20 回実行されるコルーチンのように使ってみる例.しかも不動点に入った時点で自動的に停止.

Dynamicと無限ループ

注意を払わないと,Dynamicを無限ループに投げ込んでしまうようなことが簡単に起る.

これはスクリーン上に見えている限り,できる限り速く昇順で数を数える.

In[54]:= DynamicModule[{x = 1}, Dynamic[x = x + 1]]

これはバグではない(しかし,いやならば上の出力を削除しても構わない).

無限ループの各サイクルの後で出力が更新されスクリーンが再描画されるので,これは実際非常に便利なものである.一般に,無限のDynamic更新が進んでいても,システムはタイプ,評価等に反応する.

また,ある点で変更を中止するこのような自己制動のDynamicを作ると便利である.

これは垂れたスライダーであり,どこにドラッグしても常にゼロに戻る.

In[55]:= DynamicModule[{x = 1}, {VerticalSlider[Dynamic[x]], 
             Dynamic[x = Max[0, x - 0.01]]}]

CPU監視装置を起動していると,スライダーがドロップしている間にCPUにわずかの負荷がかかるのが分かる(主にスクリーン再描画のため).しかし一旦ゼロに達すると,負荷はなくなる.動的追跡システムにより,x の値が変化していないので,別の誰かがx の値を再び変更する(自分でスライダーをクリックしたとき等)まで,アップデートの必要がないということが分かる.「Dynamicの高度な機能」では動的追跡システムの仕組みをさらに詳しく説明する.

このあたりの計算モデルは,常に即時評価を行う WPF のデータバインディングと異なっていて面白いですね.

Dynamic のサンプルから WPF のアイディアを得る

http://demonstrations.wolfram.com:title= で公開されているサンプルは,ちゃんとテーマがあって見ていて飽きません.WPF 使いの人がインスピレーションを得るために見てみると,案外役立つものがあったりして.
無償で公開されている Mathematica Player を使えば,サンプルをローカル環境で実際に動かしてみることができます.
例えば『http://demonstrations.wolfram.com/DesigningOpticalSystems/:title=』などは,頑張れば WPF でも再現できそうですよね?*1
スライダーや制御点のドラッグをハンドリングする低レベルのコードは一切無く,全て Dynamic で GUI オブジェクトと変数をバインドすることで実現できるというのがポイントです.

おまけ

なぜかゲームパッドもサポートしている Mathematica

ゲームパッドとデバイスのインターフェース

Mathematica では,ほぼすべての種類のコントローラや入力デバイスをコンピュータに繋ぐとすぐに,それらを使ってManipulateや3Dグラフィックス等が制御できるようになる.Mathematica には,任意のプログラムにおける高度なインターフェースデバイスのサポートの組込みを非常に簡単にする記号表現もある.MathematicaWolfram Researchの2+12自由度ゲームパッドの特別なサポートを提供する.

あれだけ需要のありそうな Flash や Silverlight で未対応のゲームパッドが,なぜか「そこは Mathematica が半年前に通過した場所だ!」という驚愕の事実.この斜め上を行く感がたまりません.

謝辞

iframe 形式の Silverlight Streaming メディアを埋め込むのに,『はてなダイアリーに任意のiframeを貼り付ける - daily gimite』を利用させていただきました.感謝です.

*1:まあこのサンプル,ソースが激しすぎて普通は挫折するでしょうけど.自前でレイトレーサ書いちゃってますし.