並列GAのプログラムは約1100行の中規模プログラムであるが, 並列化に要したのはコン ストラクタ, 世代実行, 移住のわずか3箇所である. 実質的にはサブ集団を並列オブジェ クトに割り付け, サブ集団のメソッドをfuture呼び出しで実行しているだけであ る. Island型並列GAは,そもそも並列化が容易であるという面もあるが, ABCL/f が並列 アルゴリズム記述性が優れていることを示している. 以下, 並列GAの ABCL/f に よる実装方法の中で並列化に関わる部分をソースコードで示す.
;;; 集団
(defclass population ()
(fixnum number) ;サブ集団数
((list subpopulation) member)) ;サブ集団
;;; サブ集団
(defclass subpopulation ()
(fixnum number) ;個体数
((list individual) member)) ;個体集合
;;; 個体クラス
(defclass individual ()
((vector real) x) ; 変数ベクトルx
((vector real) objf) ; 目的関数ベクトルf(x)
(real rank) ; ランク
(real sharing)) ; シェアリング関数
下位クラスから見てゆくと, 個体クラス(individual)には変数ベクトル
,
目的関数ベクトル
, ランク, シェアリング関数の4つのメンバを持つ. サブ集団
クラス(subpopulation)は個体をリストとして保持している. さらに集団クラス
(population)はサブ集団をリストとして保持する. 集団クラスはプログラム実行
中はただ一つのインスタンスを持つ.
(dotimes (i n_popu)
(let ((popu (now (make-empty-subpopulation) :on (mod i *npe*))))
(init-subpopulation! popu size)
(setq newmember (cons popu newmember))))
1プロセッサのみの場合にはnow呼び出しが必要ないので以下のようになる.
(dotimes (i n_popu)
(let ((popu (make-empty-subpopulation))
(init-subpopulation! popu size)
(setq newmember (cons popu newmember))))
(let ((boxes '()))
(declare ((list (future unit)) boxes))
(dolist (popu member)
(push (future (run-generations popu パラメータ)) boxes))
(dolist (r boxes) (touch r)))
のように, 並列に起動する. boxes は reply-box のリストであり,すべての
future 呼び出しが終ったら, 順に touch することで同期をとっている.
1プロセッサのみの場合には, 単純に各サブ集団のメソッドを順に呼び出すだけである.
(dolist (popu member)
(run-generations popu パラメータ))
(let ((boxes '()))
(declare ((list (future unit)) boxes))
(dolist (popu member)
(let ((from-popu (nth (random-int number) member)))
(push (future (migration-from popu from-popu)) boxes)))
(dolist (r boxes) (touch r)))
1プロセッサのみの場合には, 世代実行と同様にして以下のようになる.
(dolist (popu member)
(let ((from-popu (nth (random-int number) member)))
(migration-from popu from-popu)))