Chapter Seventy five

第75話


進化は偶然の産物がフルイにかけられた結果なのだという。では、偶然は単なるデタラメなのだろうか? カウフマンが30年研究し続けたというランダムネットワークはこのような問いかけを考えるためにモデル化したものらしい。凡人にはこのモデル化は飛躍が大きすぎてついていけない。カウフマンがランダムネットワークを考え始めたきっかけは、生物学者のジャック モノーの研究だと書いてあったので、モノーの「偶然と必然」(みすず書房ISBN4-622-00428-3)を読んでみることにした。書店の棚を探していて、科学書のコーナーではなく哲学書のコーナーに置いてあるのを見つけたときにもっと覚悟しておくべきだったが、読み通すのに苦労してしまった。カバーのデザインがあまりにも懐かしいので思い返してみると、実は学生時代にタイトルに引かれて買って、ほうっておいてどこかに行ってしまった本だった。ヘーゲル、カント、弁証法といった活字に混じって、「複雑なサイバネティックなネットワーク」や「1個の電子リレー」などのランダムネットワークにつながりそうな文章も確かに書かれている。、、、が、あまりよく分からないので作って見てみよう、というのがこのシリーズのテーマだ。

ON状態、OFF状態という2つの状態を持つ素子が、例えば100個あるときに、その取りうる状態の数は2^100という大きな数字になる。素子同士の入力、出力が密接にからみ合うと初期値が少し異なるだけで全く異なる状態が現れるというのは不思議でも何でもないような気がするが、カオスという解説がつくと、バタフライ効果かと、妙に納得をしてみたりする。

前回は、ランダムネットワークを作るために整数をランダムに並べる関数を作った。今回はこれを使ってランダムネットワークを作ってみよう。ネットワークといっても実際にLabVIEWのダイアグラムのように配線をするわけではない。入力として使う素子番号をランダムに割り付けるだけだ。入力が全て決まれば自動的に出力がつながっている素子も分かるはずだ。

素子に与える情報としてはそれぞれ2個の入出力の他に、素子の入出力対応表(アンド素子、オア素子など)と現在の状態(オンになっているのかオフになっているのか)を与える必要がある。素子1個の状態をクラスターにまとめて、クラスターのアレイをランダムネットワークそのものと考えることにした。右の図は使用しているクラスター定数で、in1, in2, out1, out2, functionと名付けた数値定数(I32), faunc_rawという名前のブール配列定数, stateという名前のブール定数がひとまとめになっている。

まず、素子の入出力の対応付けを行おう。

前回作ったrandum_order.viを使って0から(net size-1)の数値をランダムに並べ直した配列を2個作って2次元配列にしよう。これが入力になる。素子の出力がどこの入力になっているのかは、計算上必要ないのだが、知っておいたほうが便利なこともあるかもしれないので、洗い出しておこう。ある素子の出力がどの素子の入力になっているのかは、逆にたどれば分かる。

-1で初期化した2*(net size)の出力2次元配列を作り、2重のフォーループの中で出力を記録していくことにする。外側のループで素子番号順に入力1, 入力2からなる配列を取りだし、内側のループに接続する。この入力素子番号をもとに、出力2次元配列の要素に出力がつながっている素子の番号を記録する。2次元配列の0行がまだ記録されていない状態(-1)のときには第0行に記録し、第0行が記録されているとき(0以上)には第1行に記録することにした。3入力以上の場合はこの手は使いにくいし、分かりにくいのであまり良い方法ではないと反省。

入力配列と出力配列を並べて一つの2次元配列にしてから転置して、行ごとに各素子の入出力が並んでいる状態にして出力する。

次に入力に対する素子の反応を考えてみよう。2入力の素子では入力の状態は(FF,FT,TF,TT)の4通りある。このような入力に対して、例えば、なじみ深いAND素子では(F,F,F,T)という反応を行う。AND素子は考えられる素子の中の一例に過ぎなくて、入力パターンに対して出力の取りうる状態は16(=2^4)個ある。取りうる状態の一つずつの状態をfunctionと名付けて、functionを指定すると対応を示すブール配列を出力するようにしたのが右のnode_function.viだ。

入力パターン(FF,FT,TF,TT)に対応してnode_functionの配列が並んでいると考えると、function=3のときには、(F,F,T,T)という出力する素子ということになる。この素子の場合は入力の一方の状態にはまったく左右されない素子ということになる。

16種類関数からランダムに選んでネットワークの素子に割り付けていこう。0から15までの数値をランダムに選べばよいのだから、net size分だけランダム関数で乱数を発生させfunctionという配列を作ろう。素子を動作させるときにはブール配列もあったほうが楽なので素子毎のブール配列を集めて2次元配列にしておこう。
素子の初期状態もランダムに選んで、stateというブール配列で出力する。
ここまで作ってきたサブVIを使って、クラスター配列でネットワークの初期状態を作りだすVIを作った。

ネットワークの全体像は全く分からないが、このサブVIの出力のnet_settingを見ればそれぞれの素子がどの素子とつながっているか、入力に対してどういう反応をするか、現在の状態はどうなっているかなどが分かる。

ネットワークの動作ステップは逐次動作で行う。ある時点の状態をもとに次の状態を計算し、全ての素子の計算が終わったときに一括して状態の更新を行う。計算は単純で、素子の入力を調べてin1をビット0、in2をビット1として数値に変換し、その素子のファンクションのブール配列func_rawからin1,in2から得られた数値の要素を取りだし素子の出力とする。

あとはこのVIを連続で動かすだけなのだが、画面表示はdisp_state.viというサブVIで二次元のブール配列に変形してから表示している。どのくらいのサイクルで点滅しているかを調べるため累積値の平均値を強度グラフに表示することにした。開始直後は変化が激しく、徐々に定常サイクルには行っていくので200サイクルでリセットをかけるようにした。(これは次回説明することにしよう。)

これは見ているだけで面白い。どのようなクラスターになっているのかが分かるようにできれば、もっと楽しむことができるだろう。また、初期値によってパターンがどんなふうに変化するかを調べたいときに、記憶力というのはあてにならないので、なんらかの表現方法を考えないといけない。1次元のままでは400個も並べるのは見にくいから2次元で表示したが、ひょっとしたら400ビットの数値として扱ったほうが良いのかもしれない。

まあ、ゆっくり考えてみよう。

 

See you!

Nigel Yamaguchi


戻る