Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

651 - Tests for random seeding and a random seeding function #653

Merged
merged 6 commits into from
Jul 18, 2016
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion axelrod/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

# The order of imports matters!
from .actions import Actions, flip_action
from .random_ import random_choice
from .random_ import random_choice, set_seed
from .plot import Plot
from .game import DefaultGame, Game
from .player import init_args, is_basic, obey_axelrod, update_history, Player
Expand Down
7 changes: 7 additions & 0 deletions axelrod/random_.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import random
import numpy
from axelrod import Actions


Expand All @@ -21,3 +22,9 @@ def randrange(a, b):
c = b - a
r = c * random.random()
return a + int(r)


def set_seed(seed):
"""Sets a seed"""
random.seed(seed)
numpy.random.seed(seed)
20 changes: 20 additions & 0 deletions axelrod/tests/integration/test_matches.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@
deterministic_strategies = [s for s in axelrod.ordinary_strategies
if not s().classifier['stochastic']] # Well behaved strategies

stochastic_strategies = [s for s in axelrod.ordinary_strategies
if s().classifier['stochastic']]


class TestMatchOutcomes(unittest.TestCase):

@given(strategies=strategy_lists(strategies=deterministic_strategies,
Expand All @@ -23,3 +27,19 @@ def test_outcome_repeats(self, strategies, turns):
matches = [axelrod.Match(players, turns) for _ in range(3)]
self.assertEqual(matches[0].play(), matches[1].play())
self.assertEqual(matches[1].play(), matches[2].play())

@given(strategies=strategy_lists(strategies=stochastic_strategies,
min_size=2, max_size=2),
turns=integers(min_value=1, max_value=20),
seed=integers(min_value=0, max_value=4294967295))
def test_outcome_repeats_stochastic(self, strategies, turns, seed):
"""a test to check that if a seed is set stochastic strategies give the
same result"""
results = []
for _ in range(3):
axelrod.set_seed(seed)
players = [s() for s in strategies]
results.append(axelrod.Match(players, turns).play())

self.assertEqual(results[0], results[1])
self.assertEqual(results[1], results[2])
34 changes: 34 additions & 0 deletions axelrod/tests/integration/test_tournament.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import unittest
import axelrod
import tempfile
import filecmp

from axelrod.strategy_transformers import FinalTransformer

Expand Down Expand Up @@ -60,6 +61,39 @@ def test_parallel_play(self):
actual_outcome = sorted(zip(self.player_names, scores))
self.assertEqual(actual_outcome, self.expected_outcome)

def test_repeat_tournament_deterministic(self):
"""A test to check that tournament gives same results."""
deterministic_players = [s() for s in axelrod.ordinary_strategies
if not s().classifier['stochastic']]
files = []
for _ in range(2):
tournament = axelrod.Tournament(name='test',
players=deterministic_players,
game=self.game, turns=2,
repetitions=2)
files.append(tempfile.NamedTemporaryFile())
tournament.play(progress_bar=False, filename=files[-1].name,
build_results=False)
self.assertTrue(filecmp.cmp(files[0].name, files[1].name))

def test_repeat_tournament_stochastic(self):
"""
A test to check that tournament gives same results when setting seed.
"""
files = []
for _ in range(2):
axelrod.set_seed(0)
stochastic_players = [s() for s in axelrod.ordinary_strategies
if s().classifier['stochastic']]
tournament = axelrod.Tournament(name='test',
players=stochastic_players,
game=self.game, turns=2,
repetitions=2)
files.append(tempfile.NamedTemporaryFile())
tournament.play(progress_bar=False, filename=files[-1].name,
build_results=False)
self.assertTrue(filecmp.cmp(files[0].name, files[1].name))


class TestNoisyTournament(unittest.TestCase):
def test_noisy_tournament(self):
Expand Down
18 changes: 17 additions & 1 deletion axelrod/tests/unit/test_random_.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
"""Test for the random strategy."""

import numpy
import random
import unittest

from axelrod import random_choice, Actions
from axelrod import random_choice, set_seed, Actions

C, D = Actions.C, Actions.D

Expand All @@ -16,3 +17,18 @@ def test_return_values(self):
self.assertEqual(random_choice(), C)
random.seed(2)
self.assertEqual(random_choice(), D)

def test_set_seed(self):
"""Test that numpy and stdlib random seed is set by set seed helper
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

set_seed would make this sentence less confusing.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah good call.

Before I change that: what do you think about the name of the function: is set_seed the right way to go? Should it actually just be seed?

So there's random.seed, numpy.random.seed and then equivalently axelrod.seed?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah that's a good idea :)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done!

function"""

numpy_random_numbers = []
stdlib_random_numbers = []
for _ in range(2):
set_seed(0)
numpy_random_numbers.append(numpy.random.random())
stdlib_random_numbers.append(random.random())

self.assertEqual(numpy_random_numbers[0], numpy_random_numbers[1])
self.assertEqual(stdlib_random_numbers[0], stdlib_random_numbers[1])

1 change: 1 addition & 0 deletions docs/tutorials/advanced/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@ Contents:
making_tournaments.rst
reading_and_writing_interactions.rst
using_the_cache.rst
setting_a_seed.rst
35 changes: 35 additions & 0 deletions docs/tutorials/advanced/setting_a_seed.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
.. _setting_a_seed:

Setting a random seed
=====================

The library has a variety of strategies whose behaviour is stochastic. To ensure
reproducible results a random seed should be set. As both Numpy and the standard
library are used for random number generation, both seeds need to be
set. To do this we can use the `set_seed` function::

>>> import axelrod as axl
>>> players = (axl.Random(), axl.MetaMixer()) # Two stochastic strategies
>>> axl.set_seed(0)
>>> axl.Match(players, turns=3).play()
[('D', 'C'), ('D', 'D'), ('C', 'D')]

We obtain the same results is it is played with the same seed::

>>> axl.set_seed(0)
>>> axl.Match(players, turns=3).play()
[('D', 'C'), ('D', 'D'), ('C', 'D')]

Note that this is equivalent to::

>>> import numpy
>>> import random
>>> players = (axl.Random(), axl.MetaMixer())
>>> random.seed(0)
>>> numpy.random.seed(0)
>>> axl.Match(players, turns=3).play()
[('D', 'C'), ('D', 'D'), ('C', 'D')]
>>> numpy.random.seed(0)
>>> random.seed(0)
>>> axl.Match(players, turns=3).play()
[('D', 'C'), ('D', 'D'), ('C', 'D')]