Skip to content

Commit 87b33b9

Browse files
committed
Additional behavior tests for Evolvable players
1 parent 9d0d294 commit 87b33b9

16 files changed

+166
-73
lines changed

axelrod/evolvable_player.py

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
from pickle import dumps, loads
2-
import random
2+
from random import randrange
33
from .player import Player
44

55

66
class InsufficientParametersError(Exception):
77
"""Error indicating that insufficient parameters were specified to initialize an Evolvable Player."""
8-
98
def __init__(self, *args):
109
super().__init__(*args)
1110

@@ -17,6 +16,8 @@ class EvolvablePlayer(Player):
1716
"""
1817

1918
name = "EvolvablePlayer"
19+
parent_class = Player
20+
parent_kwargs = []
2021

2122
def overwrite_init_kwargs(self, **kwargs):
2223
"""Use to overwrite parameters for proper cloning and testing."""
@@ -69,20 +70,14 @@ def copy_lists(lists):
6970

7071

7172
def crossover_lists(list1, list2):
72-
cross_point = random.randrange(len(list1))
73+
cross_point = randrange(len(list1))
7374
new_list = list(list1[:cross_point]) + list(list2[cross_point:])
7475
return new_list
7576

7677

77-
def crossover_lists_of_lists(lists1, lists2):
78-
cross_point = random.randrange(len(lists1))
79-
new_lists = copy_lists(lists1[:cross_point]) + copy_lists(lists2[cross_point:])
80-
return new_lists
81-
82-
8378
def crossover_dictionaries(table1, table2):
8479
keys = list(table1.keys())
85-
cross_point = random.randrange(len(keys))
80+
cross_point = randrange(len(keys))
8681
new_items = [(k, table1[k]) for k in keys[:cross_point]]
8782
new_items += [(k, table2[k]) for k in keys[cross_point:]]
8883
new_table = dict(new_items)

axelrod/moran.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
"""Implementation of the Moran process on Graphs."""
22

3-
import random
43
from collections import Counter
4+
import random
5+
from random import randrange
56
from typing import Callable, List, Optional, Set, Tuple
67

78
import matplotlib.pyplot as plt
@@ -11,7 +12,6 @@
1112
from .deterministic_cache import DeterministicCache
1213
from .graph import Graph, complete_graph
1314
from .match import Match
14-
from .random_ import randrange
1515

1616

1717
def fitness_proportionate_selection(

axelrod/random_.py

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,19 @@
11
import random
22

3-
import numpy
3+
import numpy as np
4+
from numpy.random import choice
5+
46
from axelrod.action import Action
57

68
C, D = Action.C, Action.D
79

810

11+
def seed(seed_):
12+
"""Sets a seed"""
13+
random.seed(seed_)
14+
np.random.seed(seed_)
15+
16+
917
def random_choice(p: float = 0.5) -> Action:
1018
"""
1119
Return C with probability `p`, else return D
@@ -55,18 +63,24 @@ def random_flip(action: Action, threshold: float) -> Action:
5563
return action
5664

5765

58-
def randrange(a: int, b: int) -> int:
59-
"""Python 2 / 3 compatible randrange. Returns a random integer uniformly
60-
between a and b (inclusive)"""
61-
c = b - a
62-
r = c * random.random()
63-
return a + int(r)
66+
# def randrange(a: int, b: int) -> int:
67+
# """Python 2 / 3 compatible randrange. Returns a random integer uniformly
68+
# between a and b (inclusive)"""
69+
# c = b - a
70+
# r = c * random.random()
71+
# return a + int(r)
6472

6573

66-
def seed(seed_):
67-
"""Sets a seed"""
68-
random.seed(seed_)
69-
numpy.random.seed(seed_)
74+
def random_vector(size):
75+
"""Create a random vector of values in [0, 1] that sums to 1."""
76+
vector = []
77+
s = 1
78+
for _ in range(size - 1):
79+
r = s * random.random()
80+
vector.append(r)
81+
s -= r
82+
vector.append(s)
83+
return vector
7084

7185

7286
class Pdf(object):
@@ -81,7 +95,7 @@ def __init__(self, counter):
8195

8296
def sample(self):
8397
"""Sample from the pdf"""
84-
index = numpy.random.choice(a=range(self.size), p=self.probability)
98+
index = choice(a=range(self.size), p=self.probability)
8599
# Numpy cannot sample from a list of n dimensional objects for n > 1,
86100
# need to sample an index.
87101
return self.sample_space[index]

axelrod/strategies/ann.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1-
import random
21
from typing import List, Tuple
3-
42
import numpy as np
3+
import numpy.random as random
54
from axelrod.action import Action
65
from axelrod.load_data_ import load_weights
76
from axelrod.evolvable_player import EvolvablePlayer, InsufficientParametersError, crossover_lists
@@ -260,7 +259,7 @@ def _normalize_parameters(cls, num_features=None, num_hidden=None, weights=None,
260259
def mutate_weights(weights, num_features, num_hidden, mutation_probability,
261260
mutation_distance):
262261
size = num_weights(num_features, num_hidden)
263-
randoms = np.random.random(size)
262+
randoms = random.random(size)
264263
for i, r in enumerate(randoms):
265264
if r < mutation_probability:
266265
p = 1 + random.uniform(-1, 1) * mutation_distance

axelrod/strategies/cycler.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,8 @@ def set_cycle(self, cycle):
102102
class EvolvableCycler(Cycler, EvolvablePlayer):
103103
"""Evolvable version of Cycler."""
104104

105+
name = "EvolvableCycler"
106+
105107
def __init__(
106108
self,
107109
cycle: str = None,

axelrod/strategies/finite_state_machines.py

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,11 @@
11
import itertools
2-
import random
3-
from random import randrange, choice
4-
5-
import numpy as np
6-
2+
from random import randrange
3+
import numpy.random as random
4+
from numpy.random import choice
75
from axelrod.action import Action, UnknownActionError
86
from axelrod.evolvable_player import EvolvablePlayer, InsufficientParametersError, copy_lists
97
from axelrod.player import Player
108

11-
129
C, D = Action.C, Action.D
1310
actions = (C, D)
1411

@@ -203,7 +200,7 @@ def random_params(cls, num_states):
203200
@staticmethod
204201
def mutate_rows(rows, mutation_probability):
205202
rows = list(rows)
206-
randoms = np.random.random(len(rows))
203+
randoms = random.random(len(rows))
207204
# Flip each value with a probability proportional to the mutation rate
208205
for i, row in enumerate(rows):
209206
if randoms[i] < mutation_probability:
@@ -298,7 +295,7 @@ def repr_rows(rows):
298295
ss = []
299296
for row in rows:
300297
ss.append("_".join(list(map(str, row))))
301-
return ":".join(ss)
298+
return "|".join(ss)
302299

303300
def serialize_parameters(self):
304301
return "{}:{}:{}:{}".format(
@@ -316,7 +313,7 @@ def deserialize_parameters(cls, serialized):
316313
initial_state = int(lines[1])
317314
initial_action = Action.from_char(lines[2])
318315

319-
for line in lines[3:]:
316+
for line in lines[3].split('|'):
320317
row = []
321318
for element in line.split('_'):
322319
try:

axelrod/strategies/gambler.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,7 @@
55
https://gist.github.com/GDKO/60c3d0fd423598f3c4e4
66
"""
77
import random
8-
from random import choice
98
from typing import Any
10-
import numpy as np
119

1210
from axelrod.action import Action, str_to_actions, actions_to_str
1311
from axelrod.load_data_ import load_pso_tables

axelrod/strategies/hmm.py

Lines changed: 9 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
1-
import random
21
from random import randrange
3-
import numpy as np
2+
import numpy.random as random
43
from numpy.random import choice
54

65
from axelrod.action import Action
7-
from axelrod.evolvable_player import EvolvablePlayer, InsufficientParametersError, crossover_lists, crossover_lists_of_lists
6+
from axelrod.evolvable_player import EvolvablePlayer, InsufficientParametersError, copy_lists, crossover_lists
87
from axelrod.player import Player
9-
10-
from axelrod.random_ import random_choice
8+
from axelrod.random_ import random_choice, random_vector
119

1210
C, D = Action.C, Action.D
1311

@@ -24,23 +22,6 @@ def is_stochastic_matrix(m, ep=1e-8) -> bool:
2422
return True
2523

2624

27-
def copy_lists(rows):
28-
new_rows = list(map(list, rows))
29-
return new_rows
30-
31-
32-
def random_vector(size):
33-
"""Create a random vector of values in [0, 1] that sums to 1."""
34-
vector = []
35-
s = 1
36-
for _ in range(size - 1):
37-
r = s * random.random()
38-
vector.append(r)
39-
s -= r
40-
vector.append(s)
41-
return vector
42-
43-
4425
def normalize_vector(vec):
4526
s = sum(vec)
4627
if s == 0.0:
@@ -51,13 +32,13 @@ def normalize_vector(vec):
5132

5233

5334
def mutate_row(row, mutation_probability):
54-
"""
35+
""", crossover_lists_of_lists
5536
Given a row of probabilities, randomly change each entry with probability
5637
`mutation_probability` (a value between 0 and 1). If changing, then change
5738
by a value randomly (uniformly) chosen from [-0.25, 0.25] bounded by 0 and
5839
100%.
5940
"""
60-
randoms = np.random.random(len(row))
41+
randoms = random.random(len(row))
6142
for i in range(len(row)):
6243
if randoms[i] < mutation_probability:
6344
ep = random.uniform(-1, 1) / 4
@@ -211,6 +192,8 @@ def strategy(self, opponent: Player) -> Action:
211192

212193
class EvolvableHMMPlayer(HMMPlayer, EvolvablePlayer):
213194
"""Evolvable version of HMMPlayer."""
195+
name = "EvolvableHMMPlayer"
196+
214197
def __init__(
215198
self,
216199
transitions_C=None,
@@ -309,8 +292,8 @@ def mutate(self):
309292
def crossover(self, other):
310293
if not isinstance(other, self.__class__):
311294
raise TypeError("Crossover must be between the same player classes.")
312-
transitions_C = crossover_lists_of_lists(self.hmm.transitions_C, other.hmm.transitions_C)
313-
transitions_D = crossover_lists_of_lists(self.hmm.transitions_D, other.hmm.transitions_D)
295+
transitions_C = crossover_lists(self.hmm.transitions_C, other.hmm.transitions_C)
296+
transitions_D = crossover_lists(self.hmm.transitions_D, other.hmm.transitions_D)
314297
emission_probabilities = crossover_lists(
315298
self.hmm.emission_probabilities, other.hmm.emission_probabilities)
316299
return self.create_new(

axelrod/strategies/lookerup.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
from collections import namedtuple
22
from itertools import product
3-
import random
4-
from random import choice
53
from typing import Any, TypeVar
64

7-
import numpy as np
5+
import numpy.random as random
6+
from numpy.random import choice
87

98
from axelrod.action import Action, actions_to_str, str_to_actions
109
from axelrod.evolvable_player import EvolvablePlayer, InsufficientParametersError, crossover_dictionaries
@@ -464,7 +463,7 @@ def _normalize_parameters(cls, lookup_dict=None, initial_actions=None, pattern=N
464463

465464
@classmethod
466465
def random_value(cls):
467-
return random.choice(actions)
466+
return choice(actions)
468467

469468
@classmethod
470469
def random_params(cls, plays, op_plays, op_start_plays):
@@ -480,7 +479,7 @@ def mutate_value(cls, value):
480479

481480
@classmethod
482481
def mutate_table(cls, table, mutation_probability):
483-
randoms = np.random.random(len(table.keys()))
482+
randoms = random.random(len(table.keys()))
484483
# Flip each value with a probability proportional to the mutation rate
485484
for i, (history, move) in enumerate(table.items()):
486485
if randoms[i] < mutation_probability:

axelrod/tests/strategies/test_ann.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,12 +43,16 @@ def test_normalized_parameters(self):
4343
class TestEvolvableANN2(TestEvolvablePlayer):
4444
name = "EvolvableANN"
4545
player_class = axelrod.EvolvableANN
46+
parent_class = axelrod.ANN
47+
parent_kwargs = ["num_features", "num_hidden", "weights"]
4648
init_parameters = {"num_features": 17, "num_hidden": 8}
4749

4850

4951
class TestEvolvableANN3(TestEvolvablePlayer):
5052
name = "EvolvableANN"
5153
player_class = axelrod.EvolvableANN
54+
parent_class = axelrod.ANN
55+
parent_kwargs = ["num_features", "num_hidden", "weights"]
5256
init_parameters = {
5357
"num_features": nn_weights["Evolved ANN 5"][0],
5458
"num_hidden": nn_weights["Evolved ANN 5"][1],

0 commit comments

Comments
 (0)