forked from wmayner/pyanimats
-
Notifications
You must be signed in to change notification settings - Fork 0
/
individual.py
109 lines (91 loc) · 3.28 KB
/
individual.py
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
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# individual.py
import numpy as np
from copy import deepcopy
from parameters import params
from animat import Animat
class Individual:
def __init__(self, genome, parent=None, gen=0):
self.parent = parent
self.animat = Animat(genome)
self.gen = gen
# Mark whether the animat's phenotype needs updating.
self._dirty_phenotype = True
def __eq__(self, other):
return self.genome == other.genome and self.parent == other.parent
@property
def genome(self):
"""The animat's genome."""
return self.animat.genome
@property
def gen(self):
"""The generation the animat was born."""
return self.animat.gen
@gen.setter
def gen(self, value):
self.animat.gen = value
@property
def edges(self):
"""The animat's edge list."""
self._update_phenotype()
return self.animat.edges
@property
def cm(self):
"""The animat's connectivity matrix."""
cm = np.zeros((params.NUM_NODES, params.NUM_NODES), int)
cm[list(zip(*self.edges))] = 1
return cm
@property
def tpm(self):
"""The animats's TPM."""
self._update_phenotype()
return np.array(self.animat.tpm).astype(float)
@property
def correct(self):
"""The number of correct catches/avoidances in the game."""
return self.animat.correct
@property
def incorrect(self):
"""The number of incorrect catches/avoidances in the game."""
return self.animat.incorrect
def _update_phenotype(self):
"""Update the animat's phenotype if necessary. Returns whether an
update was performed."""
if self._dirty_phenotype:
self.animat.update_phenotype()
self._dirty_phenotype = False
return True
return False
def __deepcopy__(self, memo):
# Don't copy the animat or the parent.
copy = Individual(genome=self.animat.genome, parent=self.parent)
for key, val in self.__dict__.items():
if key not in ('animat', 'parent'):
copy.__dict__[key] = deepcopy(val, memo)
return copy
def mutate(self):
"""Mutate the animat's genome in-place."""
self.animat.mutate(params.MUTATION_PROB, params.DUPLICATION_PROB,
params.DELETION_PROB, params.MIN_GENOME_LENGTH,
params.MAX_GENOME_LENGTH)
self._dirty_phenotype = True
def play_game(self):
"""Return the list of state transitions the animat goes through when
playing the game."""
self._update_phenotype()
transitions = self.animat.play_game(
params.HIT_MULTIPLIERS,
params.BLOCK_PATTERNS,
scramble_world=params.SCRAMBLE_WORLD)
# TODO remove this assertion at some point for speed
# Check that everything adds up.
assert self.animat.correct + self.animat.incorrect == params.NUM_TRIALS
return transitions
def lineage(self):
"""Return a generator for the lineage of this individual."""
yield self.animat
ancestor = self.parent
while ancestor is not None:
yield ancestor.animat
ancestor = ancestor.parent