6
6
7
7
import matplotlib .pyplot as plt
8
8
import numpy as np
9
- from axelrod import DEFAULT_TURNS , Game , Player
9
+ from axelrod import EvolvablePlayer , DEFAULT_TURNS , Game , Player
10
10
11
11
from .deterministic_cache import DeterministicCache
12
12
from .graph import Graph , complete_graph
@@ -56,7 +56,8 @@ def __init__(
56
56
interaction_graph : Graph = None ,
57
57
reproduction_graph : Graph = None ,
58
58
fitness_transformation : Callable = None ,
59
- mutation_method = "transition"
59
+ mutation_method = "transition" ,
60
+ stop_on_fixation = True
60
61
) -> None :
61
62
"""
62
63
An agent based Moran process class. In each round, each player plays a
@@ -109,6 +110,8 @@ def __init__(
109
110
given
110
111
fitness_transformation:
111
112
A function mapping a score to a (non-negative) float
113
+ stop_on_fixation:
114
+ A bool indicating if the process should stop on fixation.
112
115
"""
113
116
self .turns = turns
114
117
self .prob_end = prob_end
@@ -121,6 +124,7 @@ def __init__(
121
124
self .score_history = [] # type: List
122
125
self .winning_strategy_name = None # type: Optional[str]
123
126
self .mutation_rate = mutation_rate
127
+ self .stop_on_fixation = stop_on_fixation
124
128
m = mutation_method .lower ()
125
129
if m in ["atomic" , "transition" ]:
126
130
self .mutation_method = m
@@ -165,6 +169,7 @@ def __init__(
165
169
# Map players to graph vertices
166
170
self .locations = sorted (interaction_graph .vertices )
167
171
self .index = dict (zip (sorted (interaction_graph .vertices ), range (len (players ))))
172
+ self .fixated = self .fixation_check ()
168
173
169
174
def set_players (self ) -> None :
170
175
"""Copy the initial players into the first population."""
@@ -184,21 +189,21 @@ def mutate(self, index: int) -> Player:
184
189
"""
185
190
186
191
if self .mutation_method == "atomic" :
187
- mutant = self .players [index ].clone ()
188
- mutant . mutate ( )
189
- return mutant
190
-
191
- # Choose another strategy at random from the initial population
192
- r = random . random ()
193
- if r < self . mutation_rate :
194
- s = str ( self . players [ index ] )
195
- j = randrange ( 0 , len ( self .mutation_targets [ s ]))
196
- p = self .mutation_targets [ s ][ j ]
197
- new_player = p . clone ( )
198
- else :
199
- # Just clone the player
200
- new_player = self . players [ index ]. clone ()
201
- return new_player
192
+ if not issubclass ( self .players [index ].__class__ , EvolvablePlayer ):
193
+ raise TypeError ( "Player is not evolvable. Use a subclass of EvolvablePlayer." )
194
+ return self . players [ index ]. mutate ()
195
+
196
+ # Assuming mutation_method == "transition"
197
+ if self . mutation_rate > 0 :
198
+ # Choose another strategy at random from the initial population
199
+ r = random . random ( )
200
+ if r < self .mutation_rate :
201
+ s = str ( self .players [ index ])
202
+ j = randrange ( 0 , len ( self . mutation_targets [ s ]) )
203
+ p = self . mutation_targets [ s ][ j ]
204
+ return p . clone ()
205
+ # Just clone the player
206
+ return self . players [ index ]. clone ()
202
207
203
208
def death (self , index : int = None ) -> int :
204
209
"""
@@ -262,14 +267,13 @@ def fixation_check(self) -> bool:
262
267
Boolean:
263
268
True if fixation has occurred (population all of a single type)
264
269
"""
265
- if self .mutation_rate > 0 :
266
- return False
267
270
classes = set (str (p ) for p in self .players )
271
+ self .fixated = False
268
272
if len (classes ) == 1 :
269
273
# Set the winning strategy name variable
270
274
self .winning_strategy_name = str (self .players [0 ])
271
- return True
272
- return False
275
+ self . fixated = True
276
+ return self . fixated
273
277
274
278
def __next__ (self ) -> object :
275
279
"""
@@ -287,7 +291,7 @@ def __next__(self) -> object:
287
291
Returns itself with a new population
288
292
"""
289
293
# Check the exit condition, that all players are of the same type.
290
- if self .fixation_check ():
294
+ if self .stop_on_fixation and self . fixation_check ():
291
295
raise StopIteration
292
296
if self .mode == "bd" :
293
297
# Birth then death
@@ -298,16 +302,10 @@ def __next__(self) -> object:
298
302
i = self .death ()
299
303
self .players [i ] = None
300
304
j = self .birth (i )
301
- # Mutate
302
- if self .mutation_rate :
303
- new_player = self .mutate (j )
304
- else :
305
- new_player = self .players [j ].clone ()
306
- # Replace player i with clone of player j
307
- self .players [i ] = new_player
305
+ # Mutate and/or replace player i with clone of player j
306
+ self .players [i ] = self .mutate (j )
307
+ # Record population.
308
308
self .populations .append (self .population_distribution ())
309
- # Check again for fixation
310
- self .fixation_check ()
311
309
return self
312
310
313
311
def _matchup_indices (self ) -> Set [Tuple [int , int ]]:
@@ -407,10 +405,10 @@ def play(self) -> List[Counter]:
407
405
populations:
408
406
Returns a list of all the populations
409
407
"""
410
- if self .mutation_rate != 0 :
408
+ if not self . stop_on_fixation or self .mutation_rate != 0 :
411
409
raise ValueError (
412
410
"MoranProcess.play() will never exit if mutation_rate is"
413
- "nonzero. Use iteration instead."
411
+ "nonzero or stop_on_fixation is False . Use iteration instead."
414
412
)
415
413
while True :
416
414
try :
0 commit comments