集団の進化をPython を用いてシミュレーションしてみる。 第3回。
孤独なミュータント問題の進化の形質の伝播をシミュレートする試みの第3回。
今回実施したコードはこちら。(汚いコードなのは分かっている・・・)
# 必要なライブラリのインポート
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
import random
# 初期設定
# Parameters
population_size = 1000
generations = 30
survival_rate = 0.5
trait_propagation_probability = 0.5
max_trials = 50 # Number of trials to simulate
# 個体群の初期化(0: 形質を持たない個体, 1: 形質を持つ個体, -1: 死んだ個体)
population = np.zeros(population_size) # 全ての個体は最初形質を持たない
# 形質を持つ個体の初期設定
# ケース1: 1個体のみ形質を持つ
population[0] = 1
# ケース2: ランダムな2個体が形質を持つ
population[random.sample(range(population_size), 2)] = 1
# 2次元空間での個体群の初期配置
# 個体群の位置をランダムに生成(0から1の範囲内)
positions = np.random.rand(population_size, 2)
# 各世代の状態を保存するリストを再初期化
generation_states = [population.copy()]
import numpy as np
import matplotlib.pyplot as plt
def is_adjacent(pos1, pos2):
"""Check if two positions are adjacent."""
dx = abs(pos1[0] - pos2[0])
dy = abs(pos1[1] - pos2[1])
return dx <= 1 and dy <= 1
def simulate_trait_spread_with_trials(population_size, generations, survival_rate, trait_propagation_probability, max_trials):
side_length = int(np.sqrt(population_size))
trial_results = []
for trial in range(1, max_trials + 1):
grid = np.zeros((side_length, side_length))
# 2個体の初期位置を選択
positions = [tuple(np.random.randint(0, side_length, size=2)) for _ in range(2)]
# Check if the two initial individuals are adjacent and set them on the grid
adjacent = is_adjacent(*positions)
for pos in positions:
grid[pos] = 1
generation_states = [grid.copy()]
if adjacent:
for gen in range(1, generations + 1):
new_grid = np.zeros_like(grid)
for x in range(side_length):
for y in range(side_length):
if grid[x, y] == 1 and np.random.rand() < survival_rate:
new_grid[x, y] = 1 # The individual survives
# Propagate the trait to adjacent individuals
for dx in [-1, 0, 1]:
for dy in [-1, 0, 1]:
nx, ny = x + dx, y + dy
if 0 <= nx < side_length and 0 <= ny < side_length and np.random.rand() < trait_propagation_probability:
new_grid[nx, ny] = 1
grid = new_grid
generation_states.append(new_grid.copy())
trial_results.append((generation_states, positions))
return trial_results
def visualize_and_save_png(trial_results, side_length, output_dir="simulation_frames"):
if not os.path.exists(output_dir):
os.makedirs(output_dir)
plt.close('all') # 既存のすべての図を閉じる
for trial, (generation_states, initial_positions) in enumerate(trial_results, start=1):
for gen, state in enumerate(generation_states):
fig, ax = plt.subplots(figsize=(8, 8)) # 新しい図と軸を生成
display_grid = np.full((side_length, side_length, 3), [0.75, 0.75, 0.75])
for pos in initial_positions:
if gen == 0:
display_grid[pos] = [1.0, 0.0, 0.0]
for x in range(side_length):
for y in range(side_length):
if state[x, y] == 1 and (x, y) not in initial_positions:
display_grid[x, y] = [1.0, 1.0, 0.0]
ax.imshow(display_grid, interpolation='nearest')
ax.set_title(f"Trial {trial} - Generation {gen}", pad=20)
ax.axis('off')
plt.draw() # 明示的に図を描画
filename = f"trial_{trial}_gen_{gen}.png"
filepath = os.path.join(output_dir, filename)
fig.savefig(filepath) # 図を保存
plt.close(fig) # 使用後の図を閉じる
# Parameters
population_size = 1000
generations = 30
survival_rate = 0.5
trait_propagation_probability = 0.5
max_trials = 150
import imageio
import os
def create_gif_from_pngs(png_directory, output_gif_path, duration=0.5):
# PNGファイル名を取得し、並び替える
png_files = [f for f in os.listdir(png_directory) if f.endswith('.png')]
png_files_sorted = sorted(png_files, key=lambda x: (int(x.split('_')[1]), int(x.split('_')[3].split('.')[0])))
# PNG画像を読み込む
images = [imageio.imread(os.path.join(png_directory, f)) for f in png_files_sorted]
# 読み込んだ画像をGIFとして保存
imageio.mimsave(output_gif_path, images, duration=duration)
# シミュレーションの実行とPNG画像の保存
trial_results = simulate_trait_spread_with_trials(population_size, generations, survival_rate, trait_propagation_probability, max_trials)
visualize_and_save_png(trial_results, int(np.sqrt(population_size)), "simulation_frames")
# GIFアニメーションの作成
create_gif_from_pngs("simulation_frames", "simulation_animation.gif", duration=0.8)
こちらで想定した内容は孤独なミュータントの設定だ。つまり、形質を持った2個体が同時に隣接した場合のみに形質が伝播するということを想定している。
そのため、形質が伝播する可能性が極端に低くなっていることが見て取れる。
実際に出来上がったものがこちら。(なぜかタイトルが途中消えてしまう・・・いずれ直したい)