Skip to content

Commit c47fab4

Browse files
committed
Sprice up deterministic_cache, ecosystem and eigen
* Minor improvements to docstrings and unit tests for ecosystem * Simplified unit tests for deterministic_cache a little bit more * Minor edits to eigen module, and made several function internal
1 parent 4ac5147 commit c47fab4

File tree

5 files changed

+89
-63
lines changed

5 files changed

+89
-63
lines changed

axelrod/ecosystem.py

+42-4
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,47 @@
1+
"""Tools for simulating population dynamics of immutable players.
2+
3+
An ecosystem runs in the context of a previous tournament, and takes the
4+
results as input. That means no matches are run by the ecosystem, and a
5+
tournament needs to happen before it is create. For example:
6+
7+
players = [axelrod.Cooperator(), axlerod.Defector()]
8+
tournament = axelrod.Tournament(players=players)
9+
results = tournament.play()
10+
ecosystem = axelrod.Ecosystem(results)
11+
acosystem.reproduce(100)
12+
"""
13+
114
import random
2-
from axelrod.result_set import ResultSet
15+
316
from typing import List, Callable
417

18+
from axelrod.result_set import ResultSet
19+
520

621
class Ecosystem(object):
7-
"""Create an ecosystem based on the payoff matrix from an Axelrod
8-
tournament."""
22+
"""An ecosystem based on the payoff matrix from a tournament.
23+
24+
Attributes
25+
----------
26+
num_players: int
27+
The number of players
28+
"""
929

1030
def __init__(self, results: ResultSet,
1131
fitness: Callable[[float], float] = None,
1232
population: List[int] = None) -> None:
33+
"""Create a new ecosystem.
34+
35+
Paramters
36+
---------
37+
results: ResultSet
38+
The results of the tournament run beforehand to use.
39+
fitness: List of callables
40+
The reproduction rate at which populations reproduce.
41+
population: List of ints.
42+
The initial populations of the players, corresponding to the
43+
payoff matrix in results.
44+
"""
1345

1446
self.results = results
1547
self.num_players = self.results.num_players
@@ -45,7 +77,13 @@ def __init__(self, results: ResultSet,
4577
self.fitness = lambda p: p
4678

4779
def reproduce(self, turns: int):
48-
80+
"""Reproduce populations according to the payoff matrix.
81+
82+
Parameters
83+
----------
84+
turns: int
85+
The number of turns to run.
86+
"""
4987
for iturn in range(turns):
5088
plist = list(range(self.num_players))
5189
pops = self.population_sizes[-1]

axelrod/eigen.py

+13-6
Original file line numberDiff line numberDiff line change
@@ -9,21 +9,21 @@
99
from typing import Tuple
1010

1111

12-
def normalise(nvec: numpy.ndarray) -> numpy.ndarray:
12+
def _normalise(nvec: numpy.ndarray) -> numpy.ndarray:
1313
"""Normalises the given numpy array."""
1414
with numpy.errstate(invalid='ignore'):
1515
result = nvec / numpy.sqrt(numpy.dot(nvec, nvec))
1616
return result
1717

1818

19-
def squared_error(vector_1: numpy.ndarray, vector_2: numpy.ndarray) -> float:
19+
def _squared_error(vector_1: numpy.ndarray, vector_2: numpy.ndarray) -> float:
2020
"""Computes the squared error between two numpy arrays."""
2121
diff = vector_1 - vector_2
2222
s = numpy.dot(diff, diff)
2323
return numpy.sqrt(s)
2424

2525

26-
def power_iteration(mat: numpy.matrix, initial: numpy.ndarray) -> numpy.ndarray:
26+
def _power_iteration(mat: numpy.matrix, initial: numpy.ndarray) -> numpy.ndarray:
2727
"""
2828
Generator of successive approximations.
2929
@@ -41,7 +41,7 @@ def power_iteration(mat: numpy.matrix, initial: numpy.ndarray) -> numpy.ndarray:
4141

4242
vec = initial
4343
while True:
44-
vec = normalise(numpy.dot(mat, vec))
44+
vec = _normalise(numpy.dot(mat, vec))
4545
yield vec
4646

4747

@@ -60,6 +60,13 @@ def principal_eigenvector(mat: numpy.matrix, maximum_iterations=1000,
6060
The maximum number of iterations of the approximation
6161
max_error: float, 1e-8
6262
Exit criterion -- error threshold of the difference of successive steps
63+
64+
Returns
65+
-------
66+
ndarray
67+
Eigenvector estimate for the input matrix
68+
float
69+
Eigenvalue corresonding to the returned eigenvector
6370
"""
6471

6572
mat_ = numpy.matrix(mat)
@@ -70,10 +77,10 @@ def principal_eigenvector(mat: numpy.matrix, maximum_iterations=1000,
7077
if not maximum_iterations:
7178
maximum_iterations = float('inf')
7279
last = initial
73-
for i, vector in enumerate(power_iteration(mat, initial=initial)):
80+
for i, vector in enumerate(_power_iteration(mat, initial=initial)):
7481
if i > maximum_iterations:
7582
break
76-
if squared_error(vector, last) < max_error:
83+
if _squared_error(vector, last) < max_error:
7784
break
7885
last = vector
7986
# Compute the eigenvalue (Rayleigh quotient)

axelrod/tests/unit/test_deterministic_cache.py

+17-25
Original file line numberDiff line numberDiff line change
@@ -32,17 +32,15 @@ def setUp(self):
3232
self.cache = DeterministicCache()
3333

3434
def test_basic_init(self):
35-
cache = DeterministicCache()
36-
self.assertTrue(cache.mutable)
35+
self.assertTrue(self.cache.mutable)
3736

3837
def test_init_from_file(self):
39-
cache = DeterministicCache(file_name=self.test_load_file)
40-
self.assertEqual(cache[self.test_key], self.test_value)
38+
loaded_cache = DeterministicCache(file_name=self.test_load_file)
39+
self.assertEqual(loaded_cache[self.test_key], self.test_value)
4140

4241
def test_setitem(self):
43-
cache = DeterministicCache()
44-
cache[self.test_key] = self.test_value
45-
self.assertEqual(cache[self.test_key], self.test_value)
42+
self.cache[self.test_key] = self.test_value
43+
self.assertEqual(self.cache[self.test_key], self.test_value)
4644

4745
def test_setitem_invalid_key_not_tuple(self):
4846
invalid_key = 'test'
@@ -87,41 +85,35 @@ def test_setitem_invalid_key_stochastic_player(self):
8785
self.cache[invalid_key] = self.test_value
8886

8987
def test_setitem_invalid_value_not_list(self):
90-
cache = DeterministicCache()
9188
with self.assertRaises(ValueError):
92-
cache[self.test_key] = 5
89+
self.cache[self.test_key] = 5
9390

9491
def test_setitem_with_immutable_cache(self):
95-
cache = DeterministicCache()
96-
cache.mutable = False
92+
self.cache.mutable = False
9793
with self.assertRaises(ValueError):
98-
cache[self.test_key] = self.test_value
94+
self.cache[self.test_key] = self.test_value
9995

10096
def test_save(self):
101-
cache = DeterministicCache()
102-
cache[self.test_key] = self.test_value
103-
cache.save(self.test_save_file)
97+
self.cache[self.test_key] = self.test_value
98+
self.cache.save(self.test_save_file)
10499
with open(self.test_save_file, 'rb') as f:
105100
text = f.read()
106101
self.assertEqual(text, self.test_pickle)
107102

108103
def test_load(self):
109-
cache = DeterministicCache()
110-
cache.load(self.test_load_file)
111-
self.assertEqual(cache[self.test_key], self.test_value)
104+
self.cache.load(self.test_load_file)
105+
self.assertEqual(self.cache[self.test_key], self.test_value)
112106

113107
def test_load_error_for_inccorect_format(self):
114108
filename = "test_outputs/test.cache"
115109
with open(filename, 'wb') as io:
116110
pickle.dump(range(5), io)
117111

118112
with self.assertRaises(ValueError):
119-
cache = DeterministicCache()
120-
cache.load(filename)
113+
self.cache.load(filename)
121114

122115
def test_del_item(self):
123-
cache = DeterministicCache()
124-
cache[self.test_key] = self.test_value
125-
self.assertTrue(self.test_key in cache)
126-
del cache[self.test_key]
127-
self.assertFalse(self.test_key in cache)
116+
self.cache[self.test_key] = self.test_value
117+
self.assertTrue(self.test_key in self.cache)
118+
del self.cache[self.test_key]
119+
self.assertFalse(self.test_key in self.cache)

axelrod/tests/unit/test_ecosystem.py

+8-15
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,7 @@ def setUpClass(cls):
2424
cls.res_cooperators = cooperators.play()
2525
cls.res_defector_wins = defector_wins.play()
2626

27-
def test_init(self):
28-
"""Are the populations created correctly?"""
29-
30-
# By default create populations of equal size
27+
def test_default_population_sizes(self):
3128
eco = axelrod.Ecosystem(self.res_cooperators)
3229
pops = eco.population_sizes
3330
self.assertEqual(eco.num_players, 4)
@@ -36,7 +33,7 @@ def test_init(self):
3633
self.assertAlmostEqual(sum(pops[0]), 1.0)
3734
self.assertEqual(list(set(pops[0])), [0.25])
3835

39-
# Can pass list of initial population distributions
36+
def test_non_default_population_sizes(self):
4037
eco = axelrod.Ecosystem(self.res_cooperators, population=[.7, .25, .03, .02])
4138
pops = eco.population_sizes
4239
self.assertEqual(eco.num_players, 4)
@@ -45,7 +42,7 @@ def test_init(self):
4542
self.assertAlmostEqual(sum(pops[0]), 1.0)
4643
self.assertEqual(pops[0], [.7, .25, .03, .02])
4744

48-
# Distribution will automatically normalise
45+
def test_population_normalization(self):
4946
eco = axelrod.Ecosystem(self.res_cooperators, population=[70, 25, 3, 2])
5047
pops = eco.population_sizes
5148
self.assertEqual(eco.num_players, 4)
@@ -54,22 +51,20 @@ def test_init(self):
5451
self.assertAlmostEqual(sum(pops[0]), 1.0)
5552
self.assertEqual(pops[0], [.7, .25, .03, .02])
5653

57-
# If passed list is of incorrect size get error
54+
def test_results_and_population_of_different_sizes(self):
5855
self.assertRaises(TypeError, axelrod.Ecosystem, self.res_cooperators,
5956
population=[.7, .2, .03, .1, .1])
6057

61-
# If passed list has negative values
58+
def test_negative_populations(self):
6259
self.assertRaises(TypeError, axelrod.Ecosystem, self.res_cooperators,
6360
population=[.7, -.2, .03, .2])
6461

65-
def test_fitness(self):
62+
def test_fitness_function(self):
6663
fitness = lambda p: 2 * p
6764
eco = axelrod.Ecosystem(self.res_cooperators, fitness=fitness)
6865
self.assertTrue(eco.fitness(10), 20)
6966

70-
def test_cooperators(self):
71-
"""Are cooperators stable over time?"""
72-
67+
def test_cooperators_are_stable_over_time(self):
7368
eco = axelrod.Ecosystem(self.res_cooperators)
7469
eco.reproduce(100)
7570
pops = eco.population_sizes
@@ -79,9 +74,7 @@ def test_cooperators(self):
7974
self.assertEqual(sum(p), 1.0)
8075
self.assertEqual(list(set(p)), [0.25])
8176

82-
def test_defector_wins(self):
83-
"""Does one defector win over time?"""
84-
77+
def test_defector_wins_with_only_cooperators(self):
8578
eco = axelrod.Ecosystem(self.res_defector_wins)
8679
eco.reproduce(1000)
8780
pops = eco.population_sizes

axelrod/tests/unit/test_eigen.py

+9-13
Original file line numberDiff line numberDiff line change
@@ -5,41 +5,37 @@
55
import numpy
66
from numpy.testing import assert_array_almost_equal
77

8-
from axelrod.eigen import normalise, principal_eigenvector
8+
from axelrod.eigen import _normalise, principal_eigenvector
99

1010

1111
class FunctionCases(unittest.TestCase):
1212

13-
def test_eigen_1(self):
14-
# Test identity matrices
13+
def test_identity_matrices(self):
1514
for size in range(2, 6):
1615
mat = numpy.identity(size)
1716
evector, evalue = principal_eigenvector(mat)
1817
self.assertAlmostEqual(evalue, 1)
19-
assert_array_almost_equal(evector, normalise(numpy.ones(size)))
18+
assert_array_almost_equal(evector, _normalise(numpy.ones(size)))
2019

21-
def test_eigen_2(self):
22-
# Test a 2x2 matrix
20+
def test_2x2_matrix(self):
2321
mat = [[2, 1], [1, 2]]
2422
evector, evalue = principal_eigenvector(mat)
2523
self.assertAlmostEqual(evalue, 3)
2624
assert_array_almost_equal(evector, numpy.dot(mat, evector) / evalue)
27-
assert_array_almost_equal(evector, normalise([1, 1]))
25+
assert_array_almost_equal(evector, _normalise([1, 1]))
2826

29-
def test_eigen_3(self):
30-
# Test a 3x3 matrix
27+
def test_3x3_matrix(self):
3128
mat = [[1, 2, 0], [-2, 1, 2], [1, 3, 1]]
3229
evector, evalue = principal_eigenvector(mat, maximum_iterations=None,
3330
max_error=1e-10)
3431
self.assertAlmostEqual(evalue, 3)
3532
assert_array_almost_equal(evector, numpy.dot(mat, evector) / evalue)
36-
assert_array_almost_equal(evector, normalise([0.5, 0.5, 1]))
33+
assert_array_almost_equal(evector, _normalise([0.5, 0.5, 1]))
3734

38-
def test_eigen_4(self):
39-
# Test a 4x4 matrix
35+
def test_4x4_matrix(self):
4036
mat = [[2, 0, 0, 0], [1, 2, 0, 0], [0, 1, 3, 0], [0, 0, 1, 3]]
4137
evector, evalue = principal_eigenvector(mat, maximum_iterations=None,
4238
max_error=1e-10)
4339
self.assertAlmostEqual(evalue, 3, places=3)
4440
assert_array_almost_equal(evector, numpy.dot(mat, evector) / evalue)
45-
assert_array_almost_equal(evector, normalise([0, 0, 0, 1]), decimal=4)
41+
assert_array_almost_equal(evector, _normalise([0, 0, 0, 1]), decimal=4)

0 commit comments

Comments
 (0)