標高+1m

Don't be rational.

We Need More Space!

f:id:ympbyc:20151202230659p:plain

  • 「この宇宙」と書くとき、これは観測可能な宇宙を表す。
  • 「宇宙」と書くときは、数学的に可能な宇宙を表す。

モノの抽象間のコミュニケーションを扱うプログラミングパラダイムについて研究している。既存の例としてはオブジェクト指向が有名であるが、オブジェクトはモデリングの失敗例である。オブジェクト指向は間違ったモデルの上に組み立てられた宇宙であり、僕らの住むこの宇宙のアナログであるとは言い難い。*1 そこで、今回はモノの抽象とそれらのコミュニケーションとして、より適切なモデルを模索してみよう。

まずは既存のモデルを物理的に解釈してみる。モノの抽象を扱う仮想のパラダイムxを使って記述する宇宙は以下の特性をもつとしよう。

  • 時間発展が実時間に依存している。
  • (スレッド内では)同期プログラミング。つまりモノAからモノBに、メッセージはゼロタイムで伝わる。
  • メッセージは宛先を知っている。

上記の3点は、この宇宙が超光速リンクが縦横無尽に走るSF的宇宙か、さもなければグラフの全ノードが1点に縮退し、エッジがくしゃくしゃに絡まったみじめな宇宙かであることを示唆する。

f:id:ympbyc:20151202213019p:plainf:id:ympbyc:20151202213307p:plain

どちらにしても、僕らがモデリングの対象とするこの宇宙とは似ても似つかないことは確かだ。注意して欲しいのは、このような宇宙は存在し得るということだ。ただし、こんな宇宙にあるモノを、僕らの宇宙のモノの記述として扱うのはちょっと大雑把過ぎる。

(普通の意味での)超光速コミュニケーションを禁止すると、グチャッと一点に凝固したグラフ宇宙になる*2。こんなに自由度が低い宇宙ではろくに歩き回ることもできず、まともに思考できるわけがない。 Hence the title "We Need More Space!" ここから出してくれ!

と、いうわけで特殊相対論を初めてちゃんと勉強した。サスカインド教授のレクチャーシリーズがオススメ。これを見てあとはグラフを描きまくれば理解できる。

www.youtube.com

特殊相対論が、時間と空間の関係を与えてくれるので、これを使えば今までのようにグローバルな時計を動かして直径1プランク光秒の閉所に自分たちを閉じ込めなくて済む。言い方を変えると、空間が欲しかったら時間のことも気にかけてやる必要があるってことだ。

ローレンツ変換や、光円錐とトラジェクトリの交点の判定などのテクニックを駆使して、特殊相対論的に正しいと思われるコミュニケーションモデルのシミュレーションを作成できた。clojureで、計算にはexpresso,可視化にはquilを使った。expressoは開発が止まっているようで、SNAPSHOT版は動かないので注意。とても便利。

View post on imgur.com

光速まで(0 ≦ v ≦ c)の速度を持つ慣性系どうしが、0 ≦ u ≦ cの速度の情報を交換する現象を記述できるようになったわけだ。

今のところ簡単のため空間次元はxしか扱っていないが、少なくとも2次元の自由度は得られた。やっと歩き回れる空間が手に入ったんだ。

実を言うと上記uがc未満のときの可視化にバグがあるため、コードの公開はしばらくお待ち下さい。核心部分は大体こんな具合です。

f:id:ympbyc:20151202230833p:plain

また、基本ではあるが、核心的部分については再帰的に定義し、内在するロジックだけで時間発展できるよう留意しました。

引き続きワッチしてください。

*1:オブジェクトをモノの抽象であるかのように宣伝せずに、抽象的な仮想機械であると説明するならば、その利便性については太鼓判を押そう

*2:ちょっと言い過ぎで、この近似でも1秒以内の誤差で直径30万kmの球は手に入る

場を使った相対論的オブジェクト間コミュニケーション

アクターやオブジェクトにおいて採用されているメッセージング機構(つまり宛先アドレスを持ったメッセージ)は、自然界のシミュレーションやIoTデバイス間コミュニケーションに適さないのではないか、という仮説のもと、メッセージングに依らないオブジェクト間コミュニケーションについて考えています。ここまでの経緯は、以下2つの記事を参照してください。

VOC (Vision Oriented Communication)の中心にあるコミュニケーション機構は、以下の2点に集約できる。

  • 情報は自身を中心に波状に拡散する
  • だれでも視野の中にある情報は読める

f:id:ympbyc:20151113195651p:plain

前回は、場(場 - Wikipedia)を使った機構を紹介し、同時にこの方法のパフォーマンス上の問題点について示唆した。現状の問題点は以下の3点。

  • 全てのオブジェクトが絶対座標を持っていてかっこ悪い。
  • 場を愚直にグリッドとして実装すると、大量の無駄な計算が生じる。
  • 情報が1フレームで場全体に拡散されるのは、光速を超えた情報伝達であり、相対論に違反している。

今回はこの3点を解決する実装を紹介する。

座標を廃止し、各オブジェクト間の距離を表明するグラフを導入する。

(make-graph
  (list sun cat1 100)
  (list sun cat2 200)
  (list cat1 cat2 150))

'(オブジェクト オブジェクト 距離) でエッジを作っていって、逆引きできるデータ構造を組み立てておく。

2点の絶対座標の差から距離を出すのはコストがかかる上に、絶対座標を持つモノのネットワークは回転について余分な情報を持っている。グラフを使った地図表現に変更することで、計算コストと、不要な情報を削減できた。

真空(オブジェクトがないところ)では場は値をもたないものとし、オブジェクトの周りの小さな領域だけ計算する。

オブジェクトとオブジェクトのコミュニケーションのために、巨大なグリッド(マトリクス)を計算するのは効果対計算コストが高すぎる。物理シミュレーションをしているわけではないので、伝播方法の詳細は無視する。後述の参考実装では、field−value(場の値)として各オブジェクトにスロットを持たせ、物理が情報をそこに書き込むようになっている。

距離と伝播速度から遅れを計算し、正しい順序で情報を伝播させる。

;;例 - 光速の1/3の速度の犬の鳴き声:
`(sound "bow-wow" ,inverse-square ,(/ 1 3))
;;-> 1m以内にネズミ、3m以内にネコがいた場合
`((,cat  :sound "bow-wow" 9)
  (,mice :sound "bow-wow" 3))
;;猫には9フレーム後、ネズミには3フレーム後に鳴き声が届く.

オブジェクトが場を振動させると、前述のグラフから近くのオブジェクトをそれぞれ取り出し、しかるべき時間の遅れののちにターゲットオブジェクトが監視しているローカルな場に、波の減衰曲線を距離に適用して得た値を書き込む。

[場の振動命令]は、減衰曲線(典型的な例は距離の逆2乗)と、光速(=1)に対する比率の形の伝播速度を付与されている。

参考実装

静止系のVOCネットワークを実装してみた。プロトタイピングでリストを乱用していて読みづらいのが恐縮ですが、コンセプトは大体表現できたと思う。

場を使った相対論的オブジェクト間コミュニケーション · GitHub

前回の記事と同じく、一つの太陽と2匹のネコの静止系で実験してみる。

f:id:ympbyc:20151113211141p:plain

(define (main . args)
  (let ([cat1 (make <cat> :fields '(sound temperature)
                    :body-temperature 34)]
        [cat2 (make <cat> :fields '(sound temperature)
                    :body-temperature 34)]
        [sun  (make <sun> :fields '(temperature electromagnetic)
                    :temperature 1000000)])
    (run-space (list cat1 cat2 sun)
               (make-graph
                (list sun cat1 100)
                (list sun cat2 200)
                (list cat1 cat2 150))
               800)))

結果:

f:id:ympbyc:20151114134200p:plain

時刻100: まず、太陽からの電磁ノイズがピンクの猫に到達する。ここでは光速を1として計算しているため、距離がすなわち時間の遅れとなる。同時刻に1000000℃あった太陽熱が、距離の逆2乗(100℃)まで下がってピンクの猫に到達し、猫は "too hot meow!" と鳴く。

時刻200: 100フレーム後には、太陽からの電磁ノイズと熱が緑の猫に伝わる。熱は減衰して25℃になっていて、気持ちよいポカポカ陽気なので、猫は "nice and warm meow!" と鳴く。

時刻400: 時刻100に発信され、光速の1/3で伝わる音波"too hot meow!"は、300フレーム後に太陽に到達し、太陽の周りの音場を揺らす。ここでは音については厳密に減衰を考慮していない。

時刻550: ピンクの猫が発した鳴き声が緑の猫の耳に届く。

時刻650: 緑の猫の鳴き声がピンクの猫の耳に届く。

まとめ

場を使ったオブジェクト間コミュニケーションというややこしそうな仕組みの割に、簡単に宣言的にオブジェクトを記述できるフレームワークを作れそうだ。

VOCの拡張 - 場の導入

昨日の記事でちらっと出てきた、VOCについて。1年半前にこんな記事を書いた。

ympbyc.hatenablog.com

VOC(Vision Oriented Communication)を初めて紹介した記事だが、それ以来VOCについてたまに考えていて、よりわかりやすい説明を思いついたことと、IoTへの応用を見据えた再定義を行いたいので、この記事をもってVOCの定義を改訂する。

メッセージは波であり、宛先は持たない

f:id:ympbyc:20151111224650p:plain

電話やメールと言ったテクノロジーの助けがないプリミティブな世界では、真に1対1のコミュニケーションは(接触以外に)あり得ない。

  • 眼はおおまかに400nm~800nmの波長の電磁波を受信して検波する。電磁波は波であり、ある程度の指向性こそ持たせられても特定の宛先を持つものではない。
  • 耳も音波について同様である。
  • 口はAM+FM変調した音波を送信する。
  • 身振りは可視光線を反射するパターンを変えることで、情報を光にエンコードする。

前にも言った喩えだと、信号が車のbrakeメソッドを呼ぶわけじゃなくて、ドライバーが信号を見てブレーキを踏むか否か判断する。

宛先を持ったメッセージが電話なら、宛先を持たないメッセージはアマチュア無線だ。

空間は場だ

波は場を伝わる。場は全ての地点になんらかの値を割り振られたグリッドと考える。場は、音場、電磁場等自然由来の場を使ってもいいし、シミュレーションでもいい。例えば文字列が値となる場があったって良いし、音場に方向を追加してベクトルにしたっていい。場をコミュニケーションの媒体として使う。シミュレートする方法は問わない。

受信 -> 計算 -> ブロードキャスト -> 再帰

1年半前の記事ではVOCのコンセプトと、その実装の一例を一つの記事にまとめてしまっていた。ここでは改めてコンセプトだけを説明する。

AI

VOCを実装するモノは、REPLの亜種REBLを実装する。REPLはRead Eval Print Loopで、通常Rは標準入力、Pは標準出力や標準エラーと、限定されたストリームに対する読み書きを指すが、VOCではReadとPrintを拡張し、多対多のコミュニケーションのため、それぞれReceiveとBroadcastとする。

Receive Eval Broadcast LoopでREBLだ。

Receive (受信)

自分が持つあらゆるセンサーそれぞれについて値を解析する。

シミュレーションの場合は自分の周りの場の値を読む。

Eval (評価)

受信した情報と、自分の状態から、自分の次の状態を計算する。

Broadcast (ブロードキャスト)

Evalされた自分の次の状態に応じて、影響を与える場を振動させる。「場を振動させる」っていってもそれほど難しく考えないこと。ようするに近くの誰にでも受け取れるメッセージを送り出すってことだ。本文に宛名は入ってもいいが、他のモノが見るのを妨げることはない。

Loop (再帰)

また受信待機に戻る。

アクターとの違い

VOCは実際Erlangのアクターによく似ている。違いはメールボックスの代わりに場を読み、メッセージを特定のプロセスに送る代わりに特定の場を振動させるという2点だ。

場のシミュレーションについて

実際のところ、場のシミュレーションは愚直にやると無駄が多いと思うので、実装方法は処理系に一任する。モノがある場所の値だけ計算すればいい。時間(光速度や音速)についてもシミュレートするのが理想だがなくて困らないならいらない。

Pseudo Code

改訂版VOCの実装はまだないので、ナイーブな疑似コード。

100℃の太陽と猫2匹の世界

;;気温がちょうど良いとニャーと鳴く(音場を揺らす)猫
(defn cat-with-no-mass [fields cat]
  (let [temp (read-field :temperature (:address @cat) fields)]
    (vibrate-field fields :sound (:address @cat) inverse-square
      (cond
        (> temp 30)
        "too hot meow!"
        (< temp 15)
        "too cold meow!"
        :else "warm and nice meow")))

;;温度場を照らす太陽
(defn sun [fields sun]
  (vibrate-field fields 
    :temperature 
    (:address @sun) 
    inverse-square 
    (:intensity @sun)))

;;減衰曲線を使って場を計算する
(defn vibrate-field [fields layer address decay val]
  ;; ...omitted...
  fields)

;;メインループ  -- とりあえず相対論は無視して絶対時間
(defn run-space [things]
  (loop [fields]
    (recur (merge-fields (pmap #((:ai %) fields (:property %)) things))))

;;初期設定
(run-space
 #{{:ai cat-with-no-mass
    :property (atom {:address [0 0 0]})
   {:ai cat-with-no-mass
    :property (atom {:address [100 100 100]})
   {:ai sun
    :property (atom {:address [10 10 10] :intensity 100})}}}
 #{:temperature [[[...]]]
   :sound       [[[...]]]})

シミュレーションと、IoTデバイスのネットワーク両方に同じコードが使えるとアツい。

本当はアドレスと、グローバルな場をなくしたい。場のバケツリレー(相互作用するローカルな場)とか、いくつか案はある。

とりあえず以上です。

IoTの意味がわかった

f:id:ympbyc:20151016161744j:plain

注) IoTの定義は知らないので、この記事で言うIoTは、「僕が考えるIoT」です。

最近まで気にしてこなかったけど、電子工作を始めてから興味が湧いてきたIoTについて。

現状はビーコンとかセンサーとかRFIDとかの弱いコンピュータ群とインターネットが作るネットワークを指してIoTと言っているように見える。

これでも十分面白いんだけど、今は過渡期の初期段階で、実はもっと面白いことが起ころうとしてるはずだ。

僕らにはどうしてもコンピュータは高価なものっていう固定観念があって、やっとビーコンやArduinoを惜しげも無くばらまけるようになってきたかどうかといったところだ。

本当は全てのノードが強いコンピュータになって初めて面白くなってくる。IoTは、「安いマイコンをばら撒こう」ってことじゃなくて、「汎用チップやモジュールがどんどん安くなるよ」って話なんだ。

強いコンピュータ、弱いコンピュータと言っているのはLinux搭載か否かとかではなくて、おおまかに

の全てを備えたものを強い、それ以外を弱いと表現している。BLEでデータを一方的に送信するビーコンやセンサは弱いし、wifiシールド等を付ければArduinoも強くなり得るということ。

強いコンピュータのネットワークは、OOP(メッセージングのOOP)やアクターモデルVOCの計算基盤になる。

つまり、大雑把に言うと実世界がコンピュータになるということ。

以前僕は、「OOPでは机に高さを訪ねると答えが返ってくるが、現実では僕らが自分で測らないといけない。OOPは実世界のシミュレーションには適していない。」という旨の主張をしていたが、どうやらOOPが現実に即するのではなく、現実がOOPに即するようになるようだ。じきに僕らは机と会話するようになる。

インターネットにつながる一つのデバイスを作ってIoTとか言ってちゃだめで、大量のボードを惜しげも無く使ってネットワークを構築して、ネットワーク単位でプログラミングするのがIoTなんじゃないだろうか。

というわけで、これからは

なんかが流行ると思います。みなさんも研究してみてはいかがでしょうか。

それから、実世界ではアドレスを持ったメッセージというのは稀なので、OOPアクターモデルよりも、VOCに近いコミュニケーションプロトコルを採用する方が賢いと予想します。ようするに1対1のメッセージではなく、多対多の 受信->計算->ブロードキャスティング のループを各ノードが実装するってことです。

VOCについてはこちらを参照してください。

ympbyc.hatenablog.com

カッコの少ないSchemeを作る

f:id:ympbyc:20150908160824p:plain

昨日の記事

ympbyc.hatenablog.com

の内容をScheme (gauche)で実験できるようにしてみます。

今回の記事は、上から順にGaucheのリスナに貼り付けていけば、動作が確認できるようにしてあります。

準備

まず、固定長引数とそれ以降の引数を取って、余った引数を結果にapplyする関数を作るマクロを作ります。define-syntax面倒臭いのでdefine-macroしちゃいます。

(define-macro defix
  (lambda (defclause . body)
    `(define (,@defclause . next)
       (if (pair? next) 
           (apply (begin ,@body) next)
           (begin ,@body)))))

(macroexpand '(defix (-car x xs) x))
;;=> (define (-car x xs . next) 
;;=>   (if (pair? next) 
;;=>     (apply (begin x) next)
;;=>     (begin x)))

(define-macro lamfix
  (lambda (params . body)
    `(lambda (,@params . next)
       (if (pair? next) 
           (apply (begin ,@body) next)
           (begin ,@body)))))

(macroexpand '(lamfix (x xs) xs))
;;=> (lambda (x xs . next) (if (pair? next) (apply (begin xs) next) (begin xs)))

ちょっと汚いけどあんまり気にしないで。

以降、すべての definedefix, lambdalamfix に置き換えて定義していきます。

リスト関数

(defix (cons x xs)
  (lamfix (f) (f x xs)))

(defix (car xs)
  (xs (lamfix (y ys) y)))

(defix (cdr xs)
  (xs (lamfix (y ys) ys)))

(defix (-car x xs) x)
(defix (-cdr x xs) xs)

(defix (<: x xs)
  (lamfix (y) (cons y (cons x xs))))

(defix (list x)
  (cons x '()))

(list 4 <: 3 <: 2 <: 1 -car)           ;;=> 1
(list 4 <: 3 <: 2 <: 1 -cdr -cdr -car) ;;=> 3

mapfilter の例も同様に、 defixlamfix をうまいこと使って作っていきます。

(defix (map xs f)
  (if (null? xs) '()
    (cons (f (car xs)) (map (cdr xs) f))))

(defix (filter xs f)
  (if (null? xs) '()
    (if (f (car xs))
      (cons (car xs) (filter (cdr xs) f))
      (filter (cdr xs) f))))

(defix (prefix f)
  (lamfix (x xs) (pa$ f (cons x xs))))

(define -map (prefix map))
(define -filter (prefix filter))

(list 5 <: 4 <: 3 <: 2 <: 1 
 -filter odd? 
 -map    (pa$ * 2) 
 -map    print)
;;=> 2
;;=> 6
;;=> 10
;;=> #<closure (cons cons)>

パイプライン演算子

-> も簡単です。

(defix (-> x)
  (lamfix (f)
    (lamfix (g)
      (f (g x)))))

(defix (<- x) x)

(-> 2
 -> (pa$ + 3)
 -> (pa$ * 4)
 <- number->string)   ;;=> "20"

できました!

混ぜて使う

固定長引数の関数は defix で定義して、可変長引数の関数は define で定義すれば、混ぜて使うこともできます。

(define (list . xs)
  (fold-right (lambda (x acc) (cons x acc)) '() xs)) ;;再定義したconsを使うため

(-> (list 1 2 3 4)
 -> (cut map <> (pa$ * 2))
 <- (cut map <> print))
;;=> 2 4 6 8 #<closure (cons cons)>

まとめ

Scheme処理系の上に、マクロを使って評価規則をちょっとだけ変えたLispを実装しました。Lisp式のマクロがあると言語の(見かけ上の)評価規則すら変えられるという例です。

リストの例(consの改造)は影響が大き過ぎるので実用は難しいと思いますが、固定長引数の関数を新しく作る時にとりあえず defix を使ってみるっていうのはやってみると意外と実用的かもしれません。


オススメ