4 bit の head と n bit の transition table からなる 4 + n bit 長の遺伝子
ある遺伝子の head と別の遺伝子の任意の連続した 4 bit の bit が全て異なれば再生産可能
head にマッチした直後の bit から transition table の XOR をとった値が次代の遺伝子となる
transition table は遺伝子長と比べて head の分 bit が足りないので、transition table の先頭 4 bit は使いまわされる
例
01011111 という遺伝子Aと 11010100 という遺伝子Bが子孫を残す場合を例にとると、
遺伝子 A の head Ah は 0101, transition table At は 1111Ah が遺伝子 B に対してマッチする部分を探索すると、1bit目と3bit目が見つかる(現在の実装では、探索開始インデックスはランダム)
Ah が1bit目にヒットした場合、 At が読み込むのは B の1bit目から head 長分後ろの5bit目からとなる。すなわち5bitシフトして B’10011010
これを At が読み込むのだが、 transition table 長は遺伝子長より短いので transition table の前半を使い回して At’11111111 を用いる
At’ と B’ のXORをとった 11111111 ^ 10011010 = 01100101 が次代の遺伝子となる
# python
DEBUG = True
header = 4
def log(s):
if DEBUG == True:
print(s)
def create(l):
return [format(i, '0%sb' % l) for i in xrange(pow(2, l))]
def check_header(b, i):
l = len(b)
for j in xrange(header):
if int(b[j]) ^ int(b[(i - 4 + j) % l]) != 1:
return False
return True
def check_all(l):
result = {}
a = create(l)
for b in a:
r = []
for i in xrange(l):
if check_header(b, i) == True:
r.append(i)
if len(r) > 0:
result[b] = r
return result
子孫全探索
「子孫を残せる相手の種類」が4パターンしかない
# 結果
>>> z = check_all(10)
>>> len(z)
1024
>>> a = create(10)
>>> r = []
>>> for b in a:
... zz = z[b]
... if len(zz) not in r:
... r.append(len(zz))
...
>>> r
[316, 580, 510, 462]
# python
DEBUG = True
header = 4
def log(s):
if DEBUG == True:
print(s)
def create(l):
return [format(i, '0%sb' % l) for i in xrange(pow(2, l))]
def check_header(h, other, i):
l = len(other)
for j in xrange(header):
if int(h[j]) ^ int(other[(i - 4 + j) % l]) != 1:
return False
return True
def child(t, other, i):
l = len(other)
m = pow(2, l) - 1
n = int(other, 2)
shifted = (((n << l) + n) >> (l - i)) & m
binary = int(t, 2) ^ shifted
return format(binary, '0%sb' % l)
def check_all(l):
transition = l - header
result = {}
a = create(l)
ii = 0
for b in a:
if (ii % 10000) == 0:
log("{} genes".format(ii))
ii += 1
r = {}
t = b[header:l] + b[header:(header * 2)]
for other in a:
rr = []
for i in xrange(l):
if check_header(b, other, i) == True:
rr.append(child(t, other, i))
if len(rr) > 0:
r[other] = rr
if len(r) > 0:
result[b] = r
return result
課題
アイデア
はじめに
ALife Core
前身の ALife Core では、収束しない進化を実現するため、自己と他者の関係性を淘汰圧として実装した。 具体的には、遺伝子を自己を示す部分(prey-part)と餌を示す部分(pred-part)のふたつに分け、接触した他者のprey-partが自己のpred-partと一致すれば捕食する実装を行なった。
これによりとりうる自己-他者の関係性は以下の4パターンである。
ある時点でどのような関係の他者が周囲に存在するかは異なるためその時々で有利となる遺伝形質も異なり、これにより収束しない進化を実現した。
ALife Core の課題
前述のように ALife Core ではprey-partとpred-partのふたつの部分からなる遺伝子を実装したが、これは標識となる情報であるだけで、それ以外に何の働きももたない。 遺伝子の情報はそれをもつ生命個体にとって何の意味もなく、他者との関係性が存在して初めて意味を持つようになるものである。
今回の施策では遺伝子が「次代をコードする情報」と「どの相手と番うか」の両方の情報を重ねて保持する実装を行う。
補足
遺伝子を可変長にするなどの変更を実装することもできるが、自己複製機能に遊びをもたせると複製効率の良い遺伝子に収束する恐れがある(マッチングしやすい巨大な遺伝子になるなど) 今回も環境に対する適応性ではなく他者との関係性に注目するためそのような仕様にはしない。
構造
4 bit の head と n bit の transition table からなる 4 + n bit 長の遺伝子 ある遺伝子の head と別の遺伝子の任意の連続した 4 bit の bit が全て異なれば再生産可能 head にマッチした直後の bit から transition table の XOR をとった値が次代の遺伝子となる transition table は遺伝子長と比べて head の分 bit が足りないので、transition table の先頭 4 bit は使いまわされる
例
01011111
という遺伝子Aと11010100
という遺伝子Bが子孫を残す場合を例にとると、遺伝子 A の head Ah は
0101
, transition table At は1111
Ah が遺伝子 B に対してマッチする部分を探索すると、1bit目と3bit目が見つかる(現在の実装では、探索開始インデックスはランダム) Ah が1bit目にヒットした場合、 At が読み込むのは B の1bit目から head 長分後ろの5bit目からとなる。すなわち5bitシフトして B’10011010
これを At が読み込むのだが、 transition table 長は遺伝子長より短いので transition table の前半を使い回して At’11111111
を用いる At’ と B’ のXORをとった11111111
^10011010
=01100101
が次代の遺伝子となる実装仕様
個体の死について
自己複製遺伝子の探索
head:
1111
, transition table:0
* n の遺伝子は自己複製可能自分とマッチできる遺伝子の探索
子孫全探索
「子孫を残せる相手の種類」が4パターンしかない
refs
Active Mutation in Self-Reproducing Networks of Machines and Tapes
パラメータ
遺伝子
227,05e,1b9
391,2d4,285
11b,276,0ad
14a, 362, 1e8
g=20e,169
mode=equidistant
では螺旋を描く?d=1&m=equidistant&a=0&t=0&si=500&f=0.93&g=20e,169&ig=2&p=1000&ls=12&mr=0&l=100&bl=50&mi=200&ri=100&af=0.6
Gallery
https://twitter.com/vespid/status/1279120617237655552?s=20
mode=attracted
mode=scroll 世代の波が見える
Gene Length を 12 に 差異は感じられない
mode=family, art_mode=1
その他