File size: 13,067 Bytes
76c9bfe
 
 
3cc63f3
 
 
76c9bfe
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3cc63f3
 
 
76c9bfe
 
 
 
 
 
 
 
 
 
3cc63f3
76c9bfe
3cc63f3
76c9bfe
 
 
 
 
3cc63f3
76c9bfe
3cc63f3
76c9bfe
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3cc63f3
76c9bfe
 
 
 
 
 
 
 
 
 
 
 
 
3cc63f3
76c9bfe
 
 
 
 
3cc63f3
 
76c9bfe
 
 
 
 
 
 
 
 
 
 
 
 
3cc63f3
 
 
 
76c9bfe
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3cc63f3
76c9bfe
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3cc63f3
76c9bfe
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3cc63f3
76c9bfe
 
3cc63f3
76c9bfe
 
3cc63f3
76c9bfe
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3cc63f3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
import random
from typing import Tuple, Dict, List

import matplotlib.pyplot as plt
import numpy as np


class Individual:
    '''各個体のクラス
        args: 個体の持つ遺伝子情報(np.array)'''
    def __init__(self, genom_size):
        self.body_hair = np.random.randint(0, 2, genom_size).tolist()
        self.body_size = np.random.randint(0, 2, genom_size).tolist()
        self.herd_num = np.random.randint(0, 2, genom_size).tolist()
        self.eating = np.random.randint(0, 2, genom_size).tolist()
        self.body_color = np.random.randint(0, 2, genom_size).tolist()
        self.ferocity = np.random.randint(0, 2, genom_size).tolist()

        self.all_genoms = {
            "body_hair": self.body_hair,
            "body_size": self.body_size,
            "herd_num": self.herd_num,
            "eating": self.eating,
            "body_color": self.body_color,
            "ferocity": self.ferocity,
        }
        self.fitness = {
            "body_hair": 0,
            "body_size": 0,
            "herd_num": 0,
            "eating": 0,
            "body_color": 0,
            "ferocity": 0,
        }  # 個体の適応度(set_fitness関数で設定)
        self.set_fitness()
        self.set_all_genoms()

    def set_fitness(self):
        '''個体に対する目的関数(OneMax)の値をself.fitnessに代入'''
        self.fitness = {key: sum(value) for key, value in self.all_genoms.items()}

    def get_fitness(self):
        '''self.fitnessを出力'''
        return self.fitness

    def set_all_genoms(self):
        '''self.all_parameterの中身をself.body_hair以下に代入する'''
        self.body_hair = self.all_genoms["body_hair"]
        self.body_size = self.all_genoms["body_size"]
        self.herd_num = self.all_genoms["herd_num"]
        self.eating = self.all_genoms["eating"]
        self.body_color = self.all_genoms["body_color"]
        self.ferocity = self.all_genoms["ferocity"]

    def mutate(self):
        '''遺伝子の突然変異'''
        for i, (parameter, genom) in enumerate(self.all_genoms.items()):

            tmp = genom.copy()
            i = np.random.randint(0, len(genom) - 1)
            tmp[i] = float(not genom[i])
            self.all_genoms[parameter] = tmp

        self.set_all_genoms()
        self.set_fitness()

def random_temperature() -> float:
    """
        火星の気温20℃〜-140℃の範囲でrandomにfloat値を返す

        args
            times (int): 試行回数
        return
            float: ランダムに作成した 火星の気温
    """

    temperature = random.uniform(-140, 30)

    return temperature

def random_food_volume(food_volume):
    """
        餌の量

        args
            times (int): 試行回数
        return
            float: ランダムに作成した 火星の気温
    """
    
    food_volume += random.randint(-100, 100)
    if food_volume < 0:
        food_volume = 0
    
    return food_volume

def create_generation(POPURATIONS, GENOMS_SIZE):
    '''初期世代の作成
        return: 個体クラスのリスト'''
    generation = {}
    for i in range(POPURATIONS):
        individual = Individual(GENOMS_SIZE)
        generation[individual] = 0
    return generation

def select_tournament(
    generation_: List[Tuple[Individual, int]], TOUNAMENT_NUM
    ) -> List[Tuple[Individual, int]]:

    """
        選択の関数(トーナメント方式)。すべてのgenerationから3つ選び、強い(scoreが最も高い)genomを1つ選ぶ。これをgenerationのサイズだけ繰り返す

        args
            generation List[Tuple[Individual, int]]: Individual で作成したゲノム情報 [["body_hair"], ["body_size"], ["herd_num"]] , 評価score
        return
            List[Tuple[Individual, int]] : トーナメント戦で生き残ったゲノム1つ
    """

    selected_genoms = []

    for i in range(len(generation_)):

        # 最もスコアのよいgeneration を採用
        tournament = random.sample(generation_, TOUNAMENT_NUM)
        max_genom = max(tournament, key=lambda x: x[1])

        selected_genoms.append(max_genom)

    return selected_genoms

def cross_two_point_copy(child1, child2):
    '''二点交叉'''

    new_child1 = {}
    new_child2 = {}

    for parameter_genom_1, parameter_genom_2 in zip(child1[0].all_genoms.items(), child2[0].all_genoms.items()):

        size = len(parameter_genom_1[1])

        tmp_child_parameter1 = parameter_genom_1[0]
        tmp_child_parameter2 = parameter_genom_2[0]

        tmp_child_genom1 = parameter_genom_1[1].copy()
        tmp_child_genom2 = parameter_genom_2[1].copy()

        cxpoint1 = np.random.randint(1, size)
        cxpoint2 = np.random.randint(1, size - 1)

        if cxpoint2 >= cxpoint1:
            cxpoint2 += 1
        else:
            cxpoint1, cxpoint2 = cxpoint2, cxpoint1

        tmp_child_genom1[cxpoint1:cxpoint2], tmp_child_genom2[cxpoint1:cxpoint2] = tmp_child_genom2[cxpoint1:cxpoint2].copy(), tmp_child_genom1[cxpoint1:cxpoint2].copy()

        child1[0].all_genoms[tmp_child_parameter1] = tmp_child_genom1
        child2[0].all_genoms[tmp_child_parameter2] = tmp_child_genom2

        child1[0].set_all_genoms()
        child1[0].set_fitness()
        child2[0].set_all_genoms()
        child2[0].set_fitness()

    return child1, child2

def crossover(selected, POPURATIONS, CROSSOVER_PB):
    '''交叉の関数'''
    children = []
    if POPURATIONS % 2:
        selected.append(selected[0])
    for child1, child2 in zip(selected[::2], selected[1::2]):
        if np.random.rand() < CROSSOVER_PB:
            child1, child2 = cross_two_point_copy(child1, child2)
        children.append(child1)
        children.append(child2)
    children = children[:POPURATIONS]
    return children


def mutate(children, MUTATION_PB):

    tmp_children = []

    for child in children:
        individual, score = child[0], child[1]
        if np.random.rand() < MUTATION_PB:
            individual.mutate()

        tmp_children.append((individual, score))

    return tmp_children

def reset_generation_score(generation_):

    for i, (individual, score) in enumerate(generation_):
        generation_[i] = (individual, 0)

    return generation_

def scoring(generation_, temperature, food_volume):
    
    MAX_NUM = 4 # fitness の最大値
    THREASHOLD_TEMPRETURE = 10 # score の判定に用いる気温のしきい値
    THREASHOLD_FOOD_VOLUME = 3000 # score の判定に用いる食料のしきい値

    generation_ = reset_generation_score(generation_)

    # scoring を実施
    for i, (individual, score) in enumerate(generation_):

        # 各パラメーターの特性値を探索
        for parameter, fitness in individual.get_fitness().items():

            # 気温が高い 
            if temperature > THREASHOLD_TEMPRETURE:
                if parameter == "body_hair": # body_hair が小さいほうが有利、大きいほうが不利
                    score += MAX_NUM - fitness
                elif parameter == "body_size": # body_size が小さいほうが有利、大きいほうが不利
                    score += MAX_NUM - fitness
                elif parameter == "body_color": # body_color が暗い方が有利、明るいほうが不利
                    score += fitness
            
            # 気温が低い
            else:
                if parameter == "body_hair": # body_hair が大きいほうが有利、小さいほうが不利
                    score += fitness
                elif parameter == "body_size": # body_size が大きいほうが有利、小さいほうが不利
                    score += fitness
                elif parameter == "body_color": # body_color が明るい方が有利、暗いほうが不利
                    score += MAX_NUM - fitness

            # エサが多い
            if food_volume > THREASHOLD_FOOD_VOLUME:
                if parameter == "body_size": # body_size が大きいほうが有利、小さいほうが不利
                    score += fitness
                elif parameter == "herd_num": # herd_num が大きいほうが有利、小さいほうが不利
                    score += fitness
                elif parameter == "eating": # eating が大きい(肉食)ほうが有利、小さい(草食)ほうが不利
                    score += fitness

            # エサが少ない
            else:
                if parameter == "body_size": # body_size が小さいほうが有利、大きいほうが不利
                    score += MAX_NUM - fitness
                elif parameter == "herd_num": # herd_num が小さいほうが有利、大きいほうが不利
                    score += MAX_NUM - fitness
                elif parameter == "eating": # eating が小さい(草食)ほうが有利、大さい(肉食)ほうが不利
                    score += MAX_NUM - fitness


            # 強さ
            if parameter == "body_size": # body_size が大きいほうが有利、小さい方が不利
                score += fitness
            elif parameter == "herd_num":
                score += fitness
            elif parameter == "ferocity": # ferocity が大きい(凶暴)ほうが有利、小さい(おとなしい)ほうが不利
                score += fitness
            

        # score を更新
        generation_[i] = (individual, int(score))

    return generation_

def ga_solve(generation, NUM_GENERATION, POPURATIONS, TOUNAMENT_NUM, CROSSOVER_PB, MUTATION_PB):
    '''遺伝的アルゴリズムのソルバー
        return: 最終世代の最高適応値の個体、最低適応値の個体'''

    best = []
    worst = []

    temperature_transition = []
    food_volume = 500
    food_volume_transition = []

    parameter_transiton = {
        "body_size" : [],
        "body_hair" : [],
        "herd_num" : [],
        "eating" : [],
        "body_color" : [],
        "ferocity" : [],
    }

    # --- Generation loop
    print('Generation loop start.')

    # Dict[Individual, int] から List[Tuple(individual, int)]へ変換
    # Dict だと Key の重複ができないため
    generation_ = [(individual, score) for individual, score in generation.items()]

    for i in range(NUM_GENERATION):

        temperature = random_temperature()
        temperature_transition.append(temperature)
        food_volume = random_food_volume(food_volume)
        food_volume_transition.append(food_volume)

        # スコアリング
        generation_ = scoring(generation_, temperature, food_volume)


        # --- Step1. Print fitness in the generation
        best_individual_score = max(generation_, key=lambda x: x[1])

        best.append(best_individual_score[0].fitness)
        worst_individual_score = min(generation_, key=lambda x: x[1])

        worst.append(worst_individual_score[0].fitness)
        # print("Generation: " + str(i) \
        #         + ": Best fitness: " + str(best_individual_score[0].fitness) + "Best fitness score: " + str(best_individual_score[1]) \
        #         + ". Worst fitness: " + str(worst_individual_score[0].fitness) + "Worst fitness score: " + str(worst_individual_score[1]) 
        #         )

        # --- Step2. Selection (Roulette)
        selected_genoms = select_tournament(generation_, TOUNAMENT_NUM)

        # --- Step3. Crossover (two_point_copy)
        children = crossover(selected_genoms, POPURATIONS, CROSSOVER_PB)

        # --- Step4. Mutation
        generation_ = mutate(children, MUTATION_PB)

        for parameter, genom in best_individual_score[0].all_genoms.items():
            parameter_transiton[parameter].append(sum(genom))

        best_individual_score[0].set_all_genoms()
        best_individual_score[0].set_fitness()

    print("Generation loop ended. The best individual: ")
    print(best_individual_score[0].all_genoms)

    plt.figure(figsize=(20, 5))
    plt.title("temperature")
    plt.plot(temperature_transition)
    plt.ylabel("temperature Celsius")
    plt.xlabel("generation")
    plt.savefig("simlation_tempreture.png")

    plt.figure(figsize=(20, 5))
    plt.title("food volume")
    plt.plot(food_volume_transition)
    plt.ylim(0);
    plt.ylabel("food volume")
    plt.xlabel("generation")
    plt.savefig("simlation_food_volume.png")

    plt.figure(figsize=(20, 16))
    for i, (parameter, transition) in enumerate(parameter_transiton.items()):
        plt.subplot(6, 1, i+1)
        plt.title(parameter)
        plt.plot(transition, label=parameter)
        plt.legend()
        plt.ylim(0, 4)
    plt.tight_layout()
    plt.savefig("each_parameter_transition.png")

    return best, worst

def get_word_for_image_generate(word_dict, best, index):

    # アルゴリズムの結果に対応するwordを抽出
    word_list = [word_dict[parameter][int(fitness)] for parameter, fitness in best[index].items()]

    # 最終的な I/F 補足: スペース区切りの文字列 を渡す, 英語もありうる
    word = " ".join(word_list)
    return word