Evolife
Evolife has been developed to study Genetic algorithms, Natural evolution and behavioural ecology.
Learner.py
Go to the documentation of this file.
1#!/usr/bin/env python3
2""" @brief Simple trial-and-error learning mechanism.
3"""
4
5#============================================================================#
6# EVOLIFE http://evolife.telecom-paris.fr Jean-Louis Dessalles #
7# Telecom Paris 2022-04-11 www.dessalles.fr #
8# -------------------------------------------------------------------------- #
9# License: Creative Commons BY-NC-SA #
10#============================================================================#
11# Documentation: https://evolife.telecom-paris.fr/Classes #
12#============================================================================#
13
14
15
18
19
20if __name__ == '__main__': # for tests
21 import sys
22 sys.path.append('../..')
23 # from Evolife.Scenarii.MyScenario import InstantiateScenario
24 # InstantiateScenario('SexRatio')
25
26
27from random import random, randint
28from Evolife.Tools.Tools import boost, LimitedMemory, error
29
30
31# Global elements
32class Global:
33 """ General functions: Closer, Perturbate, Limitate, Decrease
34 """
35 def __init__(self):
36 """ Definitions of Closer, Perturbate, Limitate, Decrease
37 """
38 # Closer pushes x towards Target
39 self.Closer = lambda x, Target, Attractiveness: ((100.0 - Attractiveness) * x + Attractiveness * Target) / 100
40 # Perturbate is a mutation function
41 self.Perturbate = lambda x, Amplitude: x + (2 * random() - 1) * Amplitude
42 # Limitate keeps x within limits
43 self.Limitate = lambda x, Min, Max: min(max(x,Min), Max)
44 # Decrease is a linear decreasing function between 100 and MinY
45 self.Decrease = lambda x, MaxX, MinY: max(MinY, (100 - x * ((100.0 - MinY)/ MaxX)))
46
47
48Gbl = Global()
49
51 """ memory buffer with limited length
52 """
53 def __str__(self):
54 if self.past: features = list(self.past[0][0].keys())
55 Res = ''
56 for b in self.past:
57 Res += ','.join([f"{f[0]}:{b[0][f]:0.1f}" for f in features if f == 'Signal'])
58 Res += f": {b[1]:0.1f} -- "
59 return Res
60
61class Learner:
62 """ defines learning capabilities
63 """
64 def __init__(self, Features, MemorySpan=5, AgeMax=100, Infancy=0, Imitation=0, Speed=3,
65 JumpProbability=0, Conservatism=0, LearningSimilarity=10, toric=False, Start=-1):
66 """ Features : Dictionary or list of features that will be learned
67 MemorySpan: size of memory
68 Scores : memory of past benefits
69 AgeMax: Max age before resetting
70 Performance : stores current performances
71 Infancy : percentage of lifetime when the learner is considered a child
72 Imitation : forced similarity wiht neighbouring values when learning continuous function
73 Speed : learning speed
74 JumpProbability: Probability of jumping far from last value
75 Conservatism: Importance in % of immediate past solutions
76 LearningSimilarity = LearningSimilarity
77 Influence of neighbouring feature values when retrieving best past feature value.
78 Between 0.1 (or so) and 100.
79 Influence of NeighbVal on Val is LearningSimilarity / abs(Val - NeighbVal)
80 10 means that a feature that differs by 30 contributes up to 1/3 of its value.
81 0.1 or so would cancel the effect of neighbouring feature values.
82 Toric = toric
83 If True, learning space is circular (toric): maximal feature values are next to smallest values.
84 Start: Features are created random (-1) or all-zero (0) or all-100 (1)
85 """
86 self.Features = Features # Dictionary or list of features that will be learned
87 self.MemorySpan = MemorySpan
88 self.Scores = LimitedMemory_(self.MemorySpan) # memory of past benefits
89 self.AgeMax = AgeMax # Max age before resetting
90 self.Performance = [] # stores current performances
91 self.Infancy = Infancy # percentage of lifetime when the learner is considered a child
92 self.Imitation = Imitation # forced similarity wiht neighbouring values when learning continuous function
93 self.Speed = Speed # learning speed
94 self.JumpProbability = JumpProbability # Probability of jumping far from last value
95 self.Conservatism = Conservatism # Importance in % of immediate past solutions
96 self.LearningSimilarity = LearningSimilarity
97 self.Toric = toric
98 self.Start = Start
99 self.Reset(Newborn=False) # individuals are created at various ages
100
101 def Reset(self, Newborn=True):
102 """ Initializes Feature values to random values (if Start == -1)
103 Age set to random value if Newborn is False (useful at start)
104 """
105 self.Age = 0 if Newborn else randint(0, self.AgeMax) # age is random at initialization
106 Features = dict() # so that self.Features may be created as a list
107 for F in self.Features:
108 if self.Start == -1 or Newborn: Features[F] = randint(0,100)
109 else: Features[F] = 100 * self.Start
110 self.Features = Features
111 self.Scores.reset()
112
113 def adult(self):
114 """ adult if age larger than AgeMax*Infancy/100
115 """
116 return self.Age > self.AgeMax * self.Infancy / 100.0
117
118 def feature(self, F, Value=None):
119 """ reads or sets feature value
120 """
121 if F is None: F = list(self.Features.keys())[0]
122 if Value is not None: self.Features[F] = Value
123 return self.Features[F]
124
125 def Limitate(self, x, Min, Max):
126 if self.Toric: return (x % Max)
127 else: return Gbl.Limitate(x, Min, Max)
128
129
130 def imitate(self, models, Feature):
131 """ The individual moves its own feature closer to its models' features
132 """
133 if models:
134 TrueModels = [m for m in models if m.adult()]
135 if TrueModels:
136 ModelValues = list(map(lambda x: x.feature(Feature), TrueModels))
137 Avg = float(sum(ModelValues)) / len(ModelValues)
138 return Gbl.Closer(self.feature(Feature), Avg, self.Imitation)
139 return self.feature(Feature)
140
141
142 def bestRecord(self, second=False):
143 """ Retrieves the best (or the second best) solution so far
144 """
145 if len(self.Scores) == 0: Best = None
146 elif len(self.Scores) == 1: Best = self.Scores.last()
147 else:
148 # ------ retrieve the best solution so far
149 past = self.Scores.retrieve()
150 Best = max(past, key = lambda x: x[1])
151 if second:
152 # ------ retrieve the SECOND best solution so far
153 past = past[:]
154 past.remove(Best)
155 Best = max(past, key = lambda x: x[1])
156 return Best
157
158 def bestFeatureRecord(self, Feature):
159 """ Alternative to bestRecord that aggregates similar feature values
160 """
161 if len(self.Scores) == 0: return None
162 Best = dict()
163 for i, (B1, Perf1) in enumerate(self.Scores.retrieve()):
164 for j, (B2, Perf2) in enumerate(self.Scores.retrieve()):
165 # if j >= i: break # symmetry
166 dist= abs(B1[Feature] - B2[Feature]) / (0.001 +self.LearningSimilarity)
167 # ------ updating Perf1 through weighted sum depending on distance
168 Perf1 = (Perf1 + Perf2 / (1 + dist)) * (1 + dist)/(2 + dist)
169 Best[B1[Feature]] = Perf1
170 return max(Best, key=Best.get) # return key with max value
171
172 def avgRecord(self):
173 """ Averaging past scores
174 """
175 if len(self.Scores) > 0:
176 return sum([p[1] for p in self.Scores.retrieve()]) / len(self.Scores)
177 else: return 0
178
179 def loser(self):
180 """ A looser has full experience and bad results
181 """
182 return self.Scores.complete() and self.bestRecord()[1] <= 0
183
184 def explore(self, Feature, Speed, Bottom=0, Top=100):
185 """ the individual changes its feature values
186 """
187 # try: Best = self.bestRecord(second=False)[0][Feature]
188 try: Best = self.bestFeatureRecord(Feature)
189 except (TypeError, IndexError): Best = self.Features[Feature]
190 Target = self.Limitate(Gbl.Perturbate(Best, Speed), Bottom, Top)
191 return round(Gbl.Closer(Target, self.feature(Feature), self.Conservatism), 2) # Target closer to old value if conservative
192
193
194 def Learns(self, neighbours=None, Speed=None, hot=False, BottomValue=0, TopValue=100):
195 """ Learns by randomly changing current value.
196 Starting point depends on previous success and on neighbours.
197 If 'hot' is true, perturbation is larger for children
198 """
199 if self.Age > self.AgeMax: self.Reset(Newborn=True)
200 self.Age += 1
201 # Averaging performances obtained for current feature values
202 Performance = 0
203 if len(self.Performance): Performance = float(sum(self.Performance)) / len(self.Performance)
204 self.Performance = [] # resetting performance
205 self.Scores.push((self.Features.copy(), Performance)) # storing current performance
206 if self.Age == 1: return False # Newborn, no learning
207
208 # (1) imitation
209 FeatureNames = list(self.Features.keys()) # safer to put 'list'
210 # get features closer to neighbours' values
211 if self.Imitation:
212 for F in FeatureNames: self.feature(F, self.imitate(neighbours, F))
213
214 # (2) exploration
215 if Speed is None: Speed = self.Speed
216 if hot and not self.adult(): # still a kid
217 LearningSpeed = Gbl.Decrease(self.Age, self.Infancy, Speed)
218 else: LearningSpeed = Speed
219 if randint(0,100) < self.JumpProbability: LearningSpeed = TopValue # max exploration from time to time
220 # compromise between current value and a perturbation of past best value
221 for F in FeatureNames:
222 # Pr = (F == 'Signal' and self.feature(F) == 0) ################################
223 self.feature(F, self.explore(F, LearningSpeed, Bottom=BottomValue, Top=TopValue))
224 # if Pr: #################################
225 # print(self.outrages, self.Scores, '\t//\t', self.feature(F))
226 # print(self.bestFeatureRecord('Signal'))
227 return True
228
229 def wins(self, Points):
230 """ stores a benefit
231 """
232 self.Performance.append(Points)
233
234 def __str__(self): return str(self.Features)
235
236
237
238
239
242
243if __name__ == "__main__":
244 print(__doc__)
245 print(Learner.__doc__ + '\n\n')
246 John_Doe = Learner({'F':0})
247 print("John_Doe:\n")
248 print(John_Doe)
249 raw_input('[Return]')
250
251
252__author__ = 'Dessalles'
General functions: Closer, Perturbate, Limitate, Decrease.
Definition: Learner.py:32
def __init__(self)
Definitions of Closer, Perturbate, Limitate, Decrease.
Definition: Learner.py:35
defines learning capabilities
Definition: Learner.py:61
def loser(self)
A looser has full experience and bad results.
Definition: Learner.py:179
def feature(self, F, Value=None)
reads or sets feature value
Definition: Learner.py:118
def bestFeatureRecord(self, Feature)
Alternative to bestRecord that aggregates similar feature values.
Definition: Learner.py:158
def wins(self, Points)
stores a benefit
Definition: Learner.py:229
def imitate(self, models, Feature)
The individual moves its own feature closer to its models' features.
Definition: Learner.py:130
def avgRecord(self)
Averaging past scores.
Definition: Learner.py:172
def Reset(self, Newborn=True)
Initializes Feature values to random values (if Start == -1) Age set to random value if Newborn is Fa...
Definition: Learner.py:101
def Learns(self, neighbours=None, Speed=None, hot=False, BottomValue=0, TopValue=100)
Learns by randomly changing current value.
Definition: Learner.py:194
def Limitate(self, x, Min, Max)
Definition: Learner.py:125
def bestRecord(self, second=False)
Retrieves the best (or the second best) solution so far.
Definition: Learner.py:142
def __init__(self, Features, MemorySpan=5, AgeMax=100, Infancy=0, Imitation=0, Speed=3, JumpProbability=0, Conservatism=0, LearningSimilarity=10, toric=False, Start=-1)
Features : Dictionary or list of features that will be learned MemorySpan: size of memory Scores : me...
Definition: Learner.py:65
def explore(self, Feature, Speed, Bottom=0, Top=100)
the individual changes its feature values
Definition: Learner.py:184
def adult(self)
adult if age larger than AgeMax*Infancy/100
Definition: Learner.py:113
memory buffer with limited length
Definition: Learner.py:50
memory buffer with limited length
Definition: Tools.py:201
Various functions.
Definition: Tools.py:1