Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

1次元セル・オートマトン

1次元セル・オートマトン

ここでは2状態1次元1近傍セル・オートマトンを考える. 以下の仮定を置く.

仮定

ある時間ステップ tt でのセルの配列を横1列に並べ,t+1t+1 の状態をその下に記録していくと,横軸が空間(セルの位置),縦軸が時間(ステップ数)の図を描写できる. シンプルなルールから複雑なパターンが生み出されるセル・オートマトンの振る舞いが,この図により可視化される.

<Figure size 1920x1440 with 0 Axes>
<Figure size 941.176x480 with 1 Axes>

2状態1次元1近傍セルオートマトンのルールの命名規則

よって,遷移ルールの総数は28通り.

以下の命名規則を採用する.

たとえば,8通りの出力が 1,0,1,1,0,0,1,01, 0, 1, 1, 0, 0, 1, 0 であれば, 2進数で 10110010 となり,

1×27+0×26+1×25+1×24+0×23+0×22+1×21+0×20=1781 \times 2^7 + 0 \times 2^6 + 1 \times 2^5 + 1 \times 2^4 + 0 \times 2^3 + 0 \times 2^2 + 1 \times 2^1 + 0 \times 2^0 = 178

このルールは「ルール178」と呼ばれる.

ルール178の実装

まずはシンプルにifで条件分岐させることで,ルール178を実装してみる.

# ルール178
def rule178(cell1, cell2, cell3):
    if cell1 == 0:
        if cell2 == 0:
            if cell3 == 0:
                return 0
            elif cell3 == 1:
                return 1
        elif cell2 == 1:
            if cell3 == 0:
                return 0
            elif cell3 == 1:
                return 0
    elif cell1 == 1:
        if cell2 == 0:
            if cell3 == 0:
                return 1
            elif cell3 == 1:
                return 1
        elif cell2 == 1:
            if cell3 == 0:
                return 0
            elif cell3 == 1:
                return 1

cell1(左),cell2(中央),cell1(右)のセルの状態を引数で渡し,中央の次のステップの状態が返ってくる.

# 確認
for cell1 in range(2):
    for cell2 in range(2):
        for cell3 in range(2):
            print(cell1, cell2, cell3, ": ", rule178(cell1, cell2, cell3))
0 0 0 :  0
0 0 1 :  1
0 1 0 :  0
0 1 1 :  0
1 0 0 :  1
1 0 1 :  1
1 1 0 :  0
1 1 1 :  1

これを使ってセル・オートマトンを実行しよう.

セルを100個ならべ,真ん中に一つだけ1,それ以外は0を初期値として,50ステップ計算してみる.

# セルオートマトンの実行
# パッケージの読み込み等
import numpy as np
import matplotlib.pyplot as plt

num_cell = 100  # セルの数
steps = 50  # 何ステップまで計算するか

cell_list = []  # 各世代のセルの情報を記録するリスト
cell = np.zeros(num_cell, dtype=int)  # セルの初期化

cell[int(num_cell / 2)] = 1  # 一つのセルだけに1を代入
cell_list.append(cell)
for t in range(steps):
    # -- 状態遷移 --
    cell_tmp = np.ones(num_cell, dtype=int)
    # 境界条件処理その1
    cell_tmp[0] = rule178(cell[num_cell - 1], cell[0], cell[1])
    # メイン
    for i in range(1, num_cell - 1, 1):
        cell_tmp[i] = rule178(cell[i - 1], cell[i], cell[i + 1])
    # 境界条件処理その2
    cell_tmp[num_cell - 1] = rule178(cell[num_cell - 2], cell[num_cell - 1], cell[0])

    # 情報の更新
    cell = np.copy(cell_tmp)
    cell_list.append(cell)

cell_listに各世代のセルの状態が記録されていく.

# セルオートマトンの可視化
plt.figure(dpi=300)
plt.matshow(cell_list[:], cmap="binary")
<Figure size 1920x1440 with 0 Axes>
<Figure size 941.176x480 with 1 Axes>

最後はmatshowを使って可視化しよう.二値(0か1)なのでcmap="binaryとする.

境界条件

空間構造を扱う場合は,「端」の処理に注意が必要. 1次元セル・オートマトンでは,最も左のセルには「左近傍」が,最も右のセルには「右近傍」が存在しないため, 何らかの仮定を置く必要がある.これを境界条件という.

周期境界条件と固定端境界条件が良く利用される. 周期境界条件では,左端と右端が繋がっていると考える(左端のセルの左近傍は右端のセル,右端のセルの右近傍は左端のセル). 1次元の場合,これはセルが円環状に並んでいるとみなすことに相当する.

固定端境界条件では,端のセルの値をあらかじめ固定して変動しない. 両端を常に一定の状態(例えば0)に保つ.

上のシミュレーションではでは周期境界条件を採用している. 左端(インデックス 0)の左近傍としてインデックス N1N-1(最後のセル)を, 右端(インデックス N1N-1)の右近傍としてインデックス 0(最初のセル)を参照している.

別のルールの実装

その他のルールも実装して,シミュレーションしてみよう.

ルール0(クラス1)

Solution to Exercise 1

すべての場合で0を返せば良い.

# ルール0
def rule0(cell1, cell2, cell3):
    if cell1 == 0:
        if cell2 == 0:
            if cell3 == 0:
                return 0
            elif cell3 == 1:
                return 0
        elif cell2 == 1:
            if cell3 == 0:
                return 0
            elif cell3 == 1:
                return 0
    elif cell1 == 1:
        if cell2 == 0:
            if cell3 == 0:
                return 0
            elif cell3 == 1:
                return 0
        elif cell2 == 1:
            if cell3 == 0:
                return 0
            elif cell3 == 1:
                return 0

rule0を使って同様にシミュレーションしてみよう.

num_cell = 100
steps = 50

cell_list = []
cell = np.zeros(num_cell, dtype=int)

cell[int(num_cell / 2)] = 1
cell_list.append(cell)
for t in range(steps):
    # -- 状態遷移 --
    cell_tmp = np.ones(num_cell, dtype=int)
    # 境界条件処理その1
    cell_tmp[0] = rule0(cell[num_cell - 1], cell[0], cell[1])
    # メイン
    for i in range(1, num_cell - 1, 1):
        cell_tmp[i] = rule0(cell[i - 1], cell[i], cell[i + 1])
    # 境界条件処理その2
    cell_tmp[num_cell - 1] = rule0(cell[num_cell - 2], cell[num_cell - 1], cell[0])

    # 情報の更新
    cell = np.copy(cell_tmp)
    cell_list.append(cell)

plt.figure(dpi=300)
plt.matshow(cell_list[:], cmap="binary")
<Figure size 1920x1440 with 0 Axes>
<Figure size 941.176x480 with 1 Axes>

(初期状態の一行目を除いて)常にすべて白となる.

様々なルールへ対応する

Solution to Exercise 2

まず任意のルールに対応できる関数を定義する.

2状態1次元1近傍セルオートマトンのルール総数は28=2562^8 = 256 通りあり, セルの状態が(1,1,1)(1, 1, 1)から(0,0,0)(0, 0, 0)を2進数の8桁にそれぞれ対応させ,各桁の値を遷移後の状態とする.

# 任意のルールに対応できる関数
def rule(rule_num, cell1, cell2, cell3):
    rule_list = []
    mod = rule_num
    for i in range(8):
        k = 7-i
        q, mod = divmod(mod,2**k)
        rule_list.append(q)

    idx = 4*cell1+2*cell2+cell3
    cell2_new = rule_list[7-idx]

    return cell2_new

divmode(p, q)pqで割ったときの,と余りを返す関数. これを使ってルールのセット rule_list を作成する. 各要素は(1,1,1)(1, 1, 1)から(0,0,0)(0, 0, 0)までの遷移ルールに対応する.

rule_numにルール番号を与えることで任意のルールに基づいた遷移が実行される.

rule()を使ってシミュレーションをする関数を定義する.

def run_ca(rule_num = 178, n_cells = 100, n_steps = 200):

    cell_list = []
    cell = np.ones(n_cells,dtype=int)

    cell[int(n_cells/2)] = 0
    cell_list.append(cell)
    for t in range(n_steps):
        # -- 状態遷移 --
        cell_tmp = np.ones(n_cells,dtype=int)
        # 境界条件処理その1
        cell_tmp[0] = rule(rule_num, cell[n_cells-1],cell[0],cell[1])
        # メイン
        for i in range(1,n_cells-1,1):
            cell_tmp[i] = rule(rule_num, cell[i-1],cell[i],cell[i+1]);
        # 境界条件処理その2
        cell_tmp[n_cells-1] = rule(rule_num, cell[n_cells-2],cell[n_cells-1],cell[0]);

        # 情報の更新
        cell = np.copy(cell_tmp)

        cell_list.append(cell)

    return cell_list

これを使ったルール0を実行する.

cell_list = run_ca(rule_num = 0, n_cells = 100, n_steps = 50)

plt.figure(dpi=300)
plt.matshow(cell_list[:], cmap="binary")
<Figure size 1920x1440 with 0 Axes>
<Figure size 941.176x480 with 1 Axes>
Solution to Exercise 3

ルール90(クラス2)

# ルール90
cell_list = run_ca(rule_num = 90, n_cells = 200, n_steps = 100)

plt.figure(dpi=300)
plt.matshow(cell_list[:], cmap="binary")
<Figure size 1920x1440 with 0 Axes>
<Figure size 950.495x480 with 1 Axes>

ルール30(クラス3)

# ルール30
cell_list = run_ca(rule_num = 30, n_cells = 200, n_steps = 100)

plt.figure(dpi=300)
plt.matshow(cell_list[:], cmap="binary")
<Figure size 1920x1440 with 0 Axes>
<Figure size 950.495x480 with 1 Axes>

ルール110(クラス4)

# ルール178
cell_list = run_ca(rule_num = 110, n_cells = 200, n_steps = 600)

plt.figure(dpi=300)
plt.matshow(cell_list[:], cmap="binary")
<Figure size 1920x1440 with 0 Axes>
<Figure size 400x1202 with 1 Axes>