Skip to content

Commit a518d09

Browse files
committed
Fix many tests and errors for seed setting and propagation
1 parent e31b00d commit a518d09

File tree

18 files changed

+114
-92
lines changed

18 files changed

+114
-92
lines changed

axelrod/__init__.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,17 @@
22

33
# The order of imports matters!
44
from axelrod.version import __version__
5+
from axelrod.action import Action
6+
from axelrod.random_ import Pdf, RandomGenerator, BulkRandomGenerator
7+
8+
# Initialize module level Random
9+
# This is seeded by the clock / OS entropy pool
10+
# It is not used if user specifies seeds everywhere and should only be
11+
# used internally by the library
12+
_module_random = RandomGenerator()
13+
514
from axelrod.load_data_ import load_pso_tables, load_weights
615
from axelrod import graph
7-
from axelrod.action import Action
816
from axelrod.random_ import Pdf, RandomGenerator, BulkRandomGenerator
917
from axelrod.plot import Plot
1018
from axelrod.game import DefaultGame, Game

axelrod/match.py

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,6 @@ def __init__(
6969
self.noise = noise
7070

7171
self.seed = seed
72-
self._random = RandomGenerator(seed=self.seed)
7372

7473
if game is None:
7574
self.game = Game()
@@ -142,8 +141,8 @@ def simultaneous_play(self, player, coplayer, noise=0):
142141
# Note this uses the Match classes random generator, not either
143142
# player's random generator. A player shouldn't be able to
144143
# predict the outcome of this noise flip.
145-
s1 = self.random_flip(s1, noise)
146-
s2 = self.random_flip(s2, noise)
144+
s1 = self._random.random_flip(s1, noise)
145+
s2 = self._random.random_flip(s2, noise)
147146
player.update_history(s1, s2)
148147
coplayer.update_history(s2, s1)
149148
return s1, s2
@@ -166,18 +165,23 @@ def play(self):
166165
167166
i.e. One entry per turn containing a pair of actions.
168167
"""
168+
# if self._stochastic:
169169
self._random = RandomGenerator(seed=self.seed)
170-
r = self._random.random()
171-
turns = min(sample_length(self.prob_end, r), self.turns)
170+
if self.prob_end:
171+
r = self._random.random()
172+
turns = min(sample_length(self.prob_end, r), self.turns)
173+
else:
174+
turns = self.turns
172175
cache_key = (self.players[0], self.players[1])
173176

174177
if self._stochastic or not self._cached_enough_turns(cache_key, turns):
175178
for p in self.players:
176179
if self.reset:
177180
p.reset()
178181
p.set_match_attributes(**self.match_attributes)
179-
# Generate a random seed for each player
180-
p.set_seed(self._random.randint(0, 100000000))
182+
# if p.classifier["stochastic"]:
183+
# Generate a random seed for the player
184+
p.set_seed(self._random.random_seed_int())
181185
result = []
182186
for _ in range(turns):
183187
plays = self.simultaneous_play(

axelrod/moran.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ def __init__(
2929
fitness_transformation: Callable = None,
3030
mutation_method="transition",
3131
stop_on_fixation=True,
32-
seed = None
32+
seed=None
3333
) -> None:
3434
"""
3535
An agent based Moran process class. In each round, each player plays a
@@ -362,6 +362,7 @@ def score_all(self) -> List:
362362
noise=self.noise,
363363
game=self.game,
364364
deterministic_cache=self.deterministic_cache,
365+
seed=self._random.random_seed_int()
365366
)
366367
match.play()
367368
match_scores = match.final_score_per_turn()

axelrod/player.py

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
import numpy as np
88

9+
from axelrod import _module_random
910
from axelrod.action import Action
1011
from axelrod.game import DefaultGame
1112
from axelrod.history import History
@@ -27,14 +28,26 @@ def __new__(cls, *args, **kwargs):
2728
"""Caches arguments for Player cloning."""
2829
obj = super().__new__(cls)
2930
obj.init_kwargs = cls.init_params(*args, **kwargs)
31+
# # Set up random seed from the module level random seed
32+
# # in case the user doesn't specific one later.
33+
# need_seed = False
34+
# try:
35+
# seed = kwargs["seed"]
36+
# if seed is None:
37+
# need_seed = True
38+
# except KeyError:
39+
# need_seed = True
40+
# if need_seed:
41+
# seed = _module_random.randint(0, 2**32-1)
42+
# obj._seed = seed
3043
return obj
3144

3245
@classmethod
3346
def init_params(cls, *args, **kwargs):
3447
"""
3548
Return a dictionary containing the init parameters of a strategy
3649
(without 'self').
37-
Use *args and *kwargs as value if specified
50+
Use *args and **kwargs as value if specified
3851
and complete the rest with the default values.
3952
"""
4053
sig = inspect.signature(cls.__init__)
@@ -53,7 +66,7 @@ def __init__(self):
5366
self._history = History()
5467
self.classifier = copy.deepcopy(self.classifier)
5568
self.set_match_attributes()
56-
self.set_seed()
69+
# self.set_seed(seed=self._seed)
5770

5871
def __eq__(self, other):
5972
"""
@@ -67,8 +80,8 @@ def __eq__(self, other):
6780
value = getattr(self, attribute, None)
6881
other_value = getattr(other, attribute, None)
6982

70-
if attribute == "_random":
71-
# Don't compare the random seeds.
83+
if attribute in ["_random", "_seed"]:
84+
# Don't compare the random generators.
7285
continue
7386

7487
if isinstance(value, np.ndarray):
@@ -118,9 +131,13 @@ def set_match_attributes(self, length=-1, game=None, noise=0):
118131
self.receive_match_attributes()
119132

120133
def set_seed(self, seed=None):
121-
"""Set a random seed for the player's random number
122-
generator."""
123-
self._random = RandomGenerator(seed=seed)
134+
"""Set a random seed for the player's random number generator."""
135+
if seed is None:
136+
# Warning: using global seed
137+
self._seed = _module_random.random_seed_int()
138+
else:
139+
self._seed = seed
140+
self._random = RandomGenerator(seed=self._seed)
124141

125142
def __repr__(self):
126143
"""The string method for the strategy.
@@ -161,6 +178,7 @@ def clone(self):
161178
cls = self.__class__
162179
new_player = cls(**self.init_kwargs)
163180
new_player.match_attributes = copy.copy(self.match_attributes)
181+
# new_player.set_seed(self._seed)
164182
return new_player
165183

166184
def reset(self):
@@ -172,6 +190,7 @@ def reset(self):
172190
"""
173191
# This also resets the history.
174192
self.__init__(**self.init_kwargs)
193+
# self.set_seed(self._seed)
175194

176195
def update_history(self, play, coplay):
177196
self.history.append(play, coplay)

axelrod/random_.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ def random(self, *args, **kwargs):
2727
def randint(self, *args, **kwargs):
2828
return self._random.randint(*args, **kwargs)
2929

30+
def random_seed_int(self):
31+
return self.randint(0, 2**32-1)
32+
3033
def choice(self, *args, **kwargs):
3134
return self._random.choice(*args, **kwargs)
3235

axelrod/strategies/hmm.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,7 @@ class SimpleHMM(object):
5858
"""
5959

6060
def __init__(
61-
self, transitions_C, transitions_D, emission_probabilities, initial_state,
62-
seed=None
61+
self, transitions_C, transitions_D, emission_probabilities, initial_state
6362
) -> None:
6463
"""
6564
Params
@@ -73,7 +72,6 @@ def __init__(
7372
self.transitions_D = transitions_D
7473
self.emission_probabilities = emission_probabilities
7574
self.state = initial_state
76-
self._random = RandomGenerator(seed=seed)
7775

7876
def is_well_formed(self) -> bool:
7977
"""
@@ -161,8 +159,7 @@ def __init__(
161159
self.initial_state = initial_state
162160
self.initial_action = initial_action
163161
self.hmm = SimpleHMM(
164-
copy_lists(transitions_C), copy_lists(transitions_D), list(emission_probabilities), initial_state,
165-
seed=self._random.randint(0, 10000000)
162+
copy_lists(transitions_C), copy_lists(transitions_D), list(emission_probabilities), initial_state
166163
)
167164
assert self.hmm.is_well_formed()
168165
self.state = self.hmm.state
@@ -190,6 +187,11 @@ def strategy(self, opponent: Player) -> Action:
190187
self.state = self.hmm.state
191188
return action
192189

190+
def set_seed(self, seed=None):
191+
super().set_seed(seed=seed)
192+
# Share RNG with HMM
193+
self.hmm._random = self._random
194+
193195

194196
class EvolvableHMMPlayer(HMMPlayer, EvolvablePlayer):
195197
"""Evolvable version of HMMPlayer."""

axelrod/strategies/memoryone.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,10 @@ def strategy(self, opponent: Player) -> Action:
9191
# Determine which probability to use
9292
p = self._four_vector[(self.history[-1], opponent.history[-1])]
9393
# Draw a random number in [0, 1] to decide
94-
return self._random.random_choice(p)
94+
try:
95+
return self._random.random_choice(p)
96+
except AttributeError:
97+
return D if p == 0 else C
9598

9699

97100
class WinStayLoseShift(MemoryOnePlayer):

axelrod/strategies/memorytwo.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,10 @@ def strategy(self, opponent: Player) -> Action:
102102
(tuple(self.history[-2:]), tuple(opponent.history[-2:]))
103103
]
104104
# Draw a random number in [0, 1] to decide
105-
return self._random.random_choice(p)
105+
try:
106+
return self._random.random_choice(p)
107+
except AttributeError:
108+
return D if p == 0 else C
106109

107110

108111
class AON2(MemoryTwoPlayer):

axelrod/strategies/meta.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,6 @@ def __init__(self, team=None):
5858
self.team = [t() for t in self.team]
5959

6060
super().__init__()
61-
self.set_seed()
6261

6362
# This player inherits the classifiers of its team.
6463
# Note that memory_depth is not simply the max memory_depth of the team.
@@ -76,9 +75,10 @@ def __init__(self, team=None):
7675
self._last_results = None
7776

7877
def set_seed(self, seed=None):
79-
super().set_seed(seed)
78+
super().set_seed(seed=seed)
79+
# Seed the team as well
8080
for t in self.team:
81-
t.set_seed(self._random.randint(0, 100000000))
81+
t.set_seed(self._random.random_seed_int())
8282

8383
def receive_match_attributes(self):
8484
for t in self.team:

axelrod/strategies/oncebitten.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import random
2-
31
from axelrod.action import Action
42
from axelrod.player import Player
53

@@ -118,7 +116,7 @@ def __init__(self, forget_probability: float = 0.05) -> None:
118116
self.forget_probability = forget_probability
119117

120118
def strategy(self, opponent: Player) -> Action:
121-
r = random.random()
119+
r = self._random.random()
122120
if not opponent.history:
123121
return self._initial
124122
if opponent.history[-1] == D:

0 commit comments

Comments
 (0)