From 8334542bad118577b1dfefaea13893f709ffd5a4 Mon Sep 17 00:00:00 2001 From: Vince Knight Date: Mon, 2 May 2016 22:03:26 +0100 Subject: [PATCH 1/5] Adding new hypothesis decorators - Lists of strategies - Matches - Tournaments - Prob End Tournaments - Game (RPST) --- axelrod/tests/property.py | 201 +++++++++++++++++++++++++++ axelrod/tests/unit/test_property.py | 202 ++++++++++++++++++++++++++++ 2 files changed, 403 insertions(+) create mode 100644 axelrod/tests/property.py create mode 100644 axelrod/tests/unit/test_property.py diff --git a/axelrod/tests/property.py b/axelrod/tests/property.py new file mode 100644 index 000000000..f40623270 --- /dev/null +++ b/axelrod/tests/property.py @@ -0,0 +1,201 @@ +""" +A module for creating hypothesis based strategies for property based testing +""" +import axelrod +from hypothesis.strategies import composite, tuples, sampled_from, integers, floats, random_module, lists + + +@composite +def strategy_lists(draw, strategies=axelrod.strategies, min_size=1, + max_size=len(axelrod.strategies)): + """ + A hypothesis decorator to return a list of strategies + + Parameters + ---------- + min_size : integer + The minimum number of strategies to include + max_size : integer + The maximum number of strategies to include + """ + strategies = draw(lists(sampled_from(strategies), min_size=min_size, + max_size=max_size)) + return strategies + +@composite +def matches(draw, strategies=axelrod.strategies, + min_turns=1, max_turns=200, + min_noise=0, max_noise=1): + """ + A hypothesis decorator to return a random match as well as a random seed (to + ensure reproducibility when instance of class need the random library). + + Parameters + ---------- + strategies : list + The strategies from which to sample the two the players + min_turns : integer + The minimum number of turns + max_turns : integer + The maximum number of turns + min_noise : float + The minimum noise + max_noise : float + The maximum noise + + Returns + ------- + tuple : a random match as well as a random seed + """ + seed = draw(random_module()) + strategies = draw(strategy_lists(min_size=2, max_size=2)) + players = [s() for s in strategies] + turns = draw(integers(min_value=min_turns, max_value=max_turns)) + noise = draw(floats(min_value=min_noise, max_value=max_noise)) + match = axelrod.Match(players, turns=turns, noise=noise) + return match, seed + +@composite +def tournaments(draw, strategies=axelrod.strategies, + min_size=1, max_size=10, + min_turns=1, max_turns=200, + min_noise=0, max_noise=1, + min_repetitions=1, max_repetitions=20, + max_processes=None): + """ + A hypothesis decorator to return a tournament and a random seed (to ensure + reproducibility for strategies that make use of the random module when + initiating). + + Parameters + ---------- + min_size : integer + The minimum number of strategies to include + max_size : integer + The maximum number of strategies to include + min_turns : integer + The minimum number of turns + max_turns : integer + The maximum number of turns + min_noise : float + The minimum noise value + min_noise : float + The maximum noise value + min_repetitions : integer + The minimum number of repetitions + max_repetitions : integer + The maximum number of repetitions + max_processes : bool + Maximum number of processes to use + """ + seed = draw(random_module()) + strategies = draw(strategy_lists(strategies=strategies, + min_size=min_size, + max_size=max_size)) + players = [s() for s in strategies] + turns = draw(integers(min_value=min_turns, max_value=max_turns)) + repetitions = draw(integers(min_value=min_repetitions, + max_value=max_repetitions)) + noise = draw(floats(min_value=min_noise, max_value=max_noise)) + + if max_processes is not None: + processes = draw(integers(min_value=1, max_value=max_processes)) + else: + processes = None + + tournament = axelrod.Tournament(players, turns=turns, + repetitions=repetitions, noise=noise, + processes=processes) + return tournament, seed + + +@composite +def prob_end_tournaments(draw, strategies=axelrod.strategies, + min_size=1, max_size=10, + min_prob_end=0, max_prob_end=1, + min_noise=0, max_noise=1, + min_repetitions=1, max_repetitions=20, + max_processes=None): + """ + A hypothesis decorator to return a tournament and a random seed (to ensure + reproducibility for strategies that make use of the random module when + initiating). + + Parameters + ---------- + min_size : integer + The minimum number of strategies to include + max_size : integer + The maximum number of strategies to include + min_prob_end : float + The minimum probability of a match ending + max_prob_end : float + The maximum probability of a match ending + min_noise : float + The minimum noise value + min_noise : float + The maximum noise value + min_repetitions : integer + The minimum number of repetitions + max_repetitions : integer + The maximum number of repetitions + max_processes : bool + Maximum number of processes to use + """ + seed = draw(random_module()) + strategies = draw(strategy_lists(strategies=strategies, + min_size=min_size, + max_size=max_size)) + players = [s() for s in strategies] + prob_end = draw(floats(min_value=min_prob_end, max_value=max_prob_end)) + repetitions = draw(integers(min_value=min_repetitions, + max_value=max_repetitions)) + noise = draw(floats(min_value=min_noise, max_value=max_noise)) + + if max_processes is not None: + processes = draw(integers(min_value=1, max_value=max_processes)) + else: + processes = None + + tournament = axelrod.ProbEndTournament(players, prob_end=prob_end, + repetitions=repetitions, noise=noise, + processes=processes) + return tournament, seed + + +@composite +def games(draw, prisoners_dilemma=True, max_value=100): + """ + A hypothesis decorator to return a random game. + + Parameters + ---------- + prisoners_dilemma : bool + If set not True the R,P,S,T values will be uniformly random. True by + default which ensures T > R > P > S and 2R > T + S. + max_value : the maximal payoff value + """ + + if prisoners_dilemma: + s_upper_bound = max_value - 4 # Ensures there is enough room + s = draw(integers(max_value=s_upper_bound)) + + t_lower_bound = s + 3 # Ensures there is enough room + t = draw(integers(min_value=t_lower_bound, max_value=max_value)) + + r_upper_bound = t - 1 + r_lower_bound = min(max(int((t + s) / 2), s) + 2, r_upper_bound) + r = draw(integers(min_value=r_lower_bound, max_value=r_upper_bound)) + + p_lower_bound = s + 1 + p_upper_bound = r - 1 + p = draw(integers(min_value=p_lower_bound, max_value=p_upper_bound)) + + else: + s = draw(integers(max_value=max_value)) + t = draw(integers(max_value=max_value)) + r = draw(integers(max_value=max_value)) + p = draw(integers(max_value=max_value)) + + game = axelrod.Game(r=r, s=s, t=t, p=p) + return game diff --git a/axelrod/tests/unit/test_property.py b/axelrod/tests/unit/test_property.py new file mode 100644 index 000000000..e15b54418 --- /dev/null +++ b/axelrod/tests/unit/test_property.py @@ -0,0 +1,202 @@ +from __future__ import absolute_import +import unittest +import axelrod +from axelrod.tests.property import (strategy_lists, + matches, tournaments, + prob_end_tournaments, games) + +from hypothesis import given +from hypothesis.strategies import random_module + +stochastic_strategies = [s for s in axelrod.strategies if + s().classifier['stochastic']] + + +class TestStrategyList(unittest.TestCase): + + def test_call(self): + strategies = strategy_lists().example() + self.assertIsInstance(strategies, list) + for p in strategies: + self.assertIsInstance(p(), axelrod.Player) + + @given(strategies=strategy_lists(min_size=1, max_size=50), + rm=random_module()) + def test_decorator(self, strategies, rm): + self.assertIsInstance(strategies, list) + self.assertGreaterEqual(len(strategies), 1) + self.assertLessEqual(len(strategies), 50) + for strategy in strategies: + self.assertIsInstance(strategy(), axelrod.Player) + + @given(strategies=strategy_lists(strategies=axelrod.basic_strategies), + rm=random_module()) + def test_decorator_with_given_strategies(self, strategies, rm): + self.assertIsInstance(strategies, list) + basic_player_names = [str(s()) for s in axelrod.basic_strategies] + for strategy in strategies: + player = strategy() + self.assertIsInstance(player, axelrod.Player) + self.assertIn(str(player), basic_player_names) + + @given(strategies=strategy_lists(strategies=stochastic_strategies), + rm=random_module()) + def test_decorator_with_stochastic_strategies(self, strategies, rm): + self.assertIsInstance(strategies, list) + stochastic_player_names = [str(s()) for s in stochastic_strategies] + for strategy in strategies: + player = strategy() + self.assertIsInstance(player, axelrod.Player) + self.assertIn(str(player), stochastic_player_names) + + +class TestMatch(unittest.TestCase): + """ + Test that the composite method works + """ + + def test_call(self): + match, seed = matches().example() + self.assertTrue(str(seed).startswith('random.seed')) + self.assertIsInstance(match, axelrod.Match) + + @given(match_and_seed=matches(min_turns=10, max_turns=50, + min_noise=0, max_noise=1)) + def test_decorator(self, match_and_seed): + match, seed = match_and_seed + self.assertTrue(str(seed).startswith('random.seed')) + + self.assertIsInstance(match, axelrod.Match) + self.assertGreaterEqual(len(match), 10) + self.assertLessEqual(len(match), 50) + self.assertGreaterEqual(match.noise, 0) + self.assertLessEqual(match.noise, 1) + + @given(match_and_seed=matches(min_turns=10, max_turns=50, + min_noise=0, max_noise=0)) + def test_decorator_with_no_noise(self, match_and_seed): + match, seed = match_and_seed + self.assertTrue(str(seed).startswith('random.seed')) + + self.assertIsInstance(match, axelrod.Match) + self.assertGreaterEqual(len(match), 10) + self.assertLessEqual(len(match), 50) + self.assertEqual(match.noise, 0) + + +class TestTournament(unittest.TestCase): + + def test_call(self): + tournament, seed = tournaments().example() + self.assertTrue(str(seed).startswith('random.seed')) + self.assertIsInstance(tournament, axelrod.Tournament) + + @given(tournament_and_seed=tournaments(min_turns=2, max_turns=50, min_noise=0, + max_noise=1, min_repetitions=2, + max_repetitions=50)) + def test_decorator(self, tournament_and_seed): + tournament, seed = tournament_and_seed + self.assertTrue(str(seed).startswith('random.seed')) + + self.assertIsInstance(tournament, axelrod.Tournament) + self.assertLessEqual(tournament.turns, 50) + self.assertGreaterEqual(tournament.turns, 2) + self.assertLessEqual(tournament.noise, 1) + self.assertGreaterEqual(tournament.noise, 0) + self.assertLessEqual(tournament.repetitions, 50) + self.assertGreaterEqual(tournament.repetitions, 2) + self.assertIsNone(tournament._processes) + + @given(tournament_and_seed=tournaments(strategies=axelrod.basic_strategies)) + def test_decorator_with_given_strategies(self, tournament_and_seed): + tournament, seed = tournament_and_seed + self.assertTrue(str(seed).startswith('random.seed')) + + self.assertIsInstance(tournament, axelrod.Tournament) + basic_player_names = [str(s()) for s in axelrod.basic_strategies] + for p in tournament.players: + self.assertIn(str(p), basic_player_names) + + @given(tournament_and_seed=tournaments(strategies=stochastic_strategies)) + def test_decorator_with_stochastic_strategies(self, tournament_and_seed): + tournament, seed = tournament_and_seed + self.assertTrue(str(seed).startswith('random.seed')) + + self.assertIsInstance(tournament, axelrod.Tournament) + stochastic_player_names = [str(s()) for s in stochastic_strategies] + for p in tournament.players: + self.assertIn(str(p), stochastic_player_names) + + @given(tournament_and_seed=tournaments(max_processes=2)) + def test_decorator_with_stochastic_strategies(self, tournament_and_seed): + tournament, seed = tournament_and_seed + self.assertGreaterEqual(tournament._processes, 1) + self.assertLessEqual(tournament._processes, 2) + + +class TestProbEndTournament(unittest.TestCase): + + def test_call(self): + tournament, seed = prob_end_tournaments().example() + self.assertTrue(str(seed).startswith('random.seed')) + self.assertIsInstance(tournament, axelrod.Tournament) + + @given(tournament_and_seed=prob_end_tournaments(min_prob_end=0, + max_prob_end=1, + min_noise=0, max_noise=1, + min_repetitions=2, + max_repetitions=50)) + def test_decorator(self, tournament_and_seed): + tournament, seed = tournament_and_seed + self.assertTrue(str(seed).startswith('random.seed')) + + self.assertIsInstance(tournament, axelrod.ProbEndTournament) + self.assertLessEqual(tournament.prob_end, 1) + self.assertGreaterEqual(tournament.prob_end, 0) + self.assertLessEqual(tournament.noise, 1) + self.assertGreaterEqual(tournament.noise, 0) + self.assertLessEqual(tournament.repetitions, 50) + self.assertGreaterEqual(tournament.repetitions, 2) + self.assertIsNone(tournament._processes) + + @given(tournament_and_seed=prob_end_tournaments(strategies=axelrod.basic_strategies)) + def test_decorator_with_given_strategies(self, tournament_and_seed): + tournament, seed = tournament_and_seed + self.assertTrue(str(seed).startswith('random.seed')) + + self.assertIsInstance(tournament, axelrod.ProbEndTournament) + basic_player_names = [str(s()) for s in axelrod.basic_strategies] + for p in tournament.players: + self.assertIn(str(p), basic_player_names) + + @given(tournament_and_seed=prob_end_tournaments(strategies=stochastic_strategies)) + def test_decorator_with_stochastic_strategies(self, tournament_and_seed): + tournament, seed = tournament_and_seed + self.assertTrue(str(seed).startswith('random.seed')) + + self.assertIsInstance(tournament, axelrod.ProbEndTournament) + stochastic_player_names = [str(s()) for s in stochastic_strategies] + for p in tournament.players: + self.assertIn(str(p), stochastic_player_names) + + @given(tournament_and_seed=prob_end_tournaments(max_processes=2)) + def test_decorator_with_stochastic_strategies(self, tournament_and_seed): + tournament, seed = tournament_and_seed + self.assertGreaterEqual(tournament._processes, 1) + self.assertLessEqual(tournament._processes, 2) + +class TestGame(unittest.TestCase): + + def test_call(self): + game = games().example() + self.assertIsInstance(game, axelrod.Game) + + @given(game=games()) + def test_decorator(self, game): + self.assertIsInstance(game, axelrod.Game) + r, p, s, t = game.RPST() + self.assertTrue((2 * r) > (t + s) and (t > r > p > s)) + + @given(game=games(prisoners_dilemma=False)) + def test_decorator_unconstrained(self, game): + self.assertIsInstance(game, axelrod.Game) From ca0ce7ca0debde88f4df388e85ed293c81530c04 Mon Sep 17 00:00:00 2001 From: Vince Knight Date: Mon, 9 May 2016 17:08:59 +0100 Subject: [PATCH 2/5] Adding repr method to game. --- axelrod/game.py | 3 +++ axelrod/tests/unit/test_game.py | 7 +++++++ 2 files changed, 10 insertions(+) diff --git a/axelrod/game.py b/axelrod/game.py index 62a062c86..fc5ffe184 100644 --- a/axelrod/game.py +++ b/axelrod/game.py @@ -30,4 +30,7 @@ def score(self, pair): """ return self.scores[pair] + def __repr__(self): + return "Axelrod game: (R,P,S,T) = {}".format(self.RPST()) + DefaultGame = Game() diff --git a/axelrod/tests/unit/test_game.py b/axelrod/tests/unit/test_game.py index 66247d68f..9da7f9ce4 100644 --- a/axelrod/tests/unit/test_game.py +++ b/axelrod/tests/unit/test_game.py @@ -2,6 +2,8 @@ from hypothesis import given from hypothesis.strategies import integers, tuples +from axelrod.tests.property import * + import axelrod C, D = axelrod.Actions.C, axelrod.Actions.D @@ -50,3 +52,8 @@ def test_property_score(self, r, p, s, t): self.assertEqual(game.score((D, D)), (p, p)) self.assertEqual(game.score((C, D)), (s, t)) self.assertEqual(game.score((D, C)), (t, s)) + + @given(game=games()) + def test_repr(self, game): + expected_repr = "Axelrod game: (R,P,S,T) = {}".format(game.RPST()) + self.assertEqual(expected_repr, str(game)) From d07a5bdc33e0bdfd83b18fc775a3adec283826de Mon Sep 17 00:00:00 2001 From: Vince Knight Date: Mon, 9 May 2016 17:29:57 +0100 Subject: [PATCH 3/5] Clearing up some tests with new hypothesis tools --- axelrod/tests/unit/test_match.py | 11 ++-- axelrod/tests/unit/test_moran.py | 8 +-- axelrod/tests/unit/test_tournament.py | 95 +++++++++++++-------------- 3 files changed, 57 insertions(+), 57 deletions(-) diff --git a/axelrod/tests/unit/test_match.py b/axelrod/tests/unit/test_match.py index 325d652f8..f4db108ba 100644 --- a/axelrod/tests/unit/test_match.py +++ b/axelrod/tests/unit/test_match.py @@ -7,16 +7,18 @@ from hypothesis import given, example from hypothesis.strategies import integers, floats, random_module, assume +from axelrod.tests.property import games + C, D = Actions.C, Actions.D class TestMatch(unittest.TestCase): - @given(turns=integers(min_value=1, max_value=200)) - @example(turns=5) - def test_init(self, turns): + @given(turns=integers(min_value=1, max_value=200), game=games()) + @example(turns=5, game=axelrod.DefaultGame) + def test_init(self, turns, game): p1, p2 = axelrod.Cooperator(), axelrod.Cooperator() - match = axelrod.Match((p1, p2), turns) + match = axelrod.Match((p1, p2), turns, game=game) self.assertEqual(match.result, []) self.assertEqual(match.players, [p1, p2]) self.assertEqual( @@ -28,6 +30,7 @@ def test_init(self, turns): self.assertEqual(match.turns, turns) self.assertEqual(match._cache, {}) self.assertEqual(match.noise, 0) + self.assertEqual(match.game.RPST(), game.RPST()) @given(turns=integers(min_value=1, max_value=200)) @example(turns=5) diff --git a/axelrod/tests/unit/test_moran.py b/axelrod/tests/unit/test_moran.py index c5d596a4d..4b00d8bf2 100644 --- a/axelrod/tests/unit/test_moran.py +++ b/axelrod/tests/unit/test_moran.py @@ -7,7 +7,9 @@ from axelrod.moran import fitness_proportionate_selection from hypothesis import given, example, settings -from hypothesis.strategies import integers, lists, sampled_from, random_module, floats +from hypothesis.strategies import random_module + +from axelrod.tests.property import strategy_lists class TestMoranProcess(unittest.TestCase): @@ -67,9 +69,7 @@ def test_four_players(self): self.assertEqual(populations, mp.populations) self.assertEqual(mp.winning_strategy_name, str(axelrod.Defector())) - @given(strategies=lists(sampled_from(axelrod.strategies), - min_size=2, # Errors are returned if less than 2 strategies - max_size=5, unique=True), + @given(strategies=strategy_lists(min_size=2, max_size=5), rm=random_module()) @settings(max_examples=5, timeout=0) # Very low number of examples diff --git a/axelrod/tests/unit/test_tournament.py b/axelrod/tests/unit/test_tournament.py index 79c962f58..11e105e74 100644 --- a/axelrod/tests/unit/test_tournament.py +++ b/axelrod/tests/unit/test_tournament.py @@ -10,6 +10,7 @@ from hypothesis import given, example, settings from hypothesis.strategies import integers, lists, sampled_from, random_module, floats +from axelrod.tests.property import tournaments, prob_end_tournaments import axelrod @@ -191,38 +192,35 @@ def test_progress_bar_play_parallel(self): results = tournament.play(progress_bar=True) self.assertIsInstance(results, axelrod.ResultSet) - @given(s=lists(sampled_from(axelrod.strategies), - min_size=2, # Errors are returned if less than 2 strategies - max_size=5, unique=True), - turns=integers(min_value=2, max_value=50), - repetitions=integers(min_value=2, max_value=4), - rm=random_module()) + @given(tournament_and_seed=tournaments(min_size=2, max_size=5, min_turns=2, + max_turns=50, min_repetitions=2, + max_repetitions=4)) @settings(max_examples=50, timeout=0) - @example(s=test_strategies, turns=test_turns, repetitions=test_repetitions, - rm=random.seed(0)) + @example(tournament_and_seed=(axelrod.Tournament(players=[s() for s in + test_strategies], turns=test_turns, repetitions=test_repetitions), + random.seed(0))) + # These two examples are to make sure #465 is fixed. # As explained there: https://github.com/Axelrod-Python/Axelrod/issues/465, # these two examples were identified by hypothesis. - @example(s=[axelrod.BackStabber, axelrod.MindReader], turns=2, repetitions=1, - rm=random.seed(0)) - @example(s=[axelrod.ThueMorse, axelrod.MindReader], turns=2, repetitions=1, - rm=random.seed(0)) - def test_property_serial_play(self, s, turns, repetitions, rm): + @example(tournament_and_seed=( + axelrod.Tournament(players=[axelrod.BackStabber(), + axelrod.MindReader()], + turns=2, repetitions=1), + random.seed(0))) + @example(tournament_and_seed=( + axelrod.Tournament(players=[axelrod.BackStabber(), + axelrod.ThueMorse()], + turns=2, repetitions=1), + random.seed(0))) + def test_property_serial_play(self, tournament_and_seed): """Test serial play using hypothesis""" # Test that we get an instance of ResultSet - - players = [strat() for strat in s] - - tournament = axelrod.Tournament( - name=self.test_name, - players=players, - game=self.game, - turns=turns, - repetitions=repetitions) + tournament, _ = tournament_and_seed # Discarding the seed results = tournament.play(progress_bar=False) self.assertIsInstance(results, axelrod.ResultSet) - self.assertEqual(results.nplayers, len(players)) - self.assertEqual(results.players, [str(p) for p in players]) + self.assertEqual(results.nplayers, len(tournament.players)) + self.assertEqual(results.players, [str(p) for p in tournament.players]) def test_parallel_play(self): # Test that we get an instance of ResultSet @@ -530,38 +528,37 @@ def test_init(self): anonymous_tournament = axelrod.Tournament(players=self.players) self.assertEqual(anonymous_tournament.name, 'axelrod') - @given(s=lists(sampled_from(axelrod.strategies), - min_size=2, # Errors are returned if less than 2 strategies - max_size=5, unique=True), - prob_end=floats(min_value=.1, max_value=.9), - repetitions=integers(min_value=2, max_value=4), - rm=random_module()) + @given(tournament_and_seed=prob_end_tournaments(min_size=2, max_size=5, + min_prob_end=.1, + max_prob_end=.9, + min_repetitions=2, + max_repetitions=4)) @settings(max_examples=50, timeout=0) - @example(s=test_strategies, prob_end=.2, repetitions=test_repetitions, - rm=random.seed(0)) + @example(tournament_and_seed=( + axelrod.ProbEndTournament(players=[s() for s in test_strategies], + prob_end=.2, repetitions=test_repetitions), + random.seed(0))) # These two examples are to make sure #465 is fixed. # As explained there: https://github.com/Axelrod-Python/Axelrod/issues/465, # these two examples were identified by hypothesis. - @example(s=[axelrod.BackStabber, axelrod.MindReader], prob_end=.2, repetitions=1, - rm=random.seed(0)) - @example(s=[axelrod.ThueMorse, axelrod.MindReader], prob_end=.2, repetitions=1, - rm=random.seed(0)) - def test_property_serial_play(self, s, prob_end, repetitions, rm): + @example(tournament_and_seed=( + axelrod.ProbEndTournament(players=[axelrod.BackStabber(), + axelrod.MindReader()], + prob_end=.2, repetitions=1), + random.seed(0))) + @example(tournament_and_seed=( + axelrod.ProbEndTournament(players=[axelrod.ThueMorse(), + axelrod.MindReader()], + prob_end=.2, repetitions=1), + random.seed(0))) + def test_property_serial_play(self, tournament_and_seed): """Test serial play using hypothesis""" # Test that we get an instance of ResultSet - - players = [strat() for strat in s] - - tournament = axelrod.ProbEndTournament( - name=self.test_name, - players=players, - game=self.game, - prob_end=prob_end, - repetitions=repetitions) + tournament, _ = tournament_and_seed results = tournament.play(progress_bar=False) self.assertIsInstance(results, axelrod.ResultSet) - self.assertEqual(results.nplayers, len(players)) - self.assertEqual(results.players, [str(p) for p in players]) + self.assertEqual(results.nplayers, len(tournament.players)) + self.assertEqual(results.players, [str(p) for p in tournament.players]) for rep in results.interactions.values(): - self.assertEqual(len(rep), repetitions) + self.assertEqual(len(rep), tournament.repetitions) From 60f84993ba12f9b387049f1652428a7db27224fb Mon Sep 17 00:00:00 2001 From: Marc Harper Date: Tue, 10 May 2016 20:35:43 -0700 Subject: [PATCH 4/5] Move processes argument from Tournament.__init__ to tournament.play --- axelrod/tests/integration/test_tournament.py | 5 +-- axelrod/tests/unit/test_tournament.py | 40 ++++++++------------ axelrod/tournament.py | 23 +++++------ 3 files changed, 27 insertions(+), 41 deletions(-) diff --git a/axelrod/tests/integration/test_tournament.py b/axelrod/tests/integration/test_tournament.py index 7e1305eac..1580de852 100644 --- a/axelrod/tests/integration/test_tournament.py +++ b/axelrod/tests/integration/test_tournament.py @@ -50,8 +50,7 @@ def test_parallel_play(self): players=self.players, game=self.game, turns=20, - repetitions=self.test_repetitions, - processes=2) - scores = tournament.play(progress_bar=False).scores + repetitions=self.test_repetitions) + scores = tournament.play(processes=2, progress_bar=False).scores actual_outcome = sorted(zip(self.player_names, scores)) self.assertEqual(actual_outcome, self.expected_outcome) diff --git a/axelrod/tests/unit/test_tournament.py b/axelrod/tests/unit/test_tournament.py index 11e105e74..c66b17e6a 100644 --- a/axelrod/tests/unit/test_tournament.py +++ b/axelrod/tests/unit/test_tournament.py @@ -63,7 +63,6 @@ def test_init(self): players=self.players, game=self.game, turns=self.test_turns, - processes=4, noise=0.2) self.assertEqual(len(tournament.players), len(test_strategies)) self.assertIsInstance( @@ -73,7 +72,6 @@ def test_init(self): self.assertEqual(tournament.turns, self.test_turns) self.assertEqual(tournament.repetitions, 10) self.assertEqual(tournament.name, 'test') - self.assertEqual(tournament._processes, 4) self.assertTrue(tournament._with_morality) self.assertIsInstance(tournament._logger, logging.Logger) self.assertEqual(tournament.noise, 0.2) @@ -183,10 +181,9 @@ def test_progress_bar_play_parallel(self): players=self.players, game=self.game, turns=200, - repetitions=self.test_repetitions, - processes=2) + repetitions=self.test_repetitions) - results = tournament.play() + results = tournament.play(processes=2) self.assertIsInstance(results, axelrod.ResultSet) results = tournament.play(progress_bar=True) @@ -229,9 +226,8 @@ def test_parallel_play(self): players=self.players, game=self.game, turns=200, - repetitions=self.test_repetitions, - processes=2) - results = tournament.play(progress_bar=False) + repetitions=self.test_repetitions) + results = tournament.play(processes=2, progress_bar=False) self.assertIsInstance(results, axelrod.ResultSet) # The following relates to #516 @@ -243,9 +239,8 @@ def test_parallel_play(self): players=players, game=self.game, turns=20, - repetitions=self.test_repetitions, - processes=2) - scores = tournament.play(progress_bar=False).scores + repetitions=self.test_repetitions) + scores = tournament.play(processes=2, progress_bar=False).scores self.assertEqual(len(scores), len(players)) def test_run_serial(self): @@ -254,8 +249,7 @@ def test_run_serial(self): players=self.players, game=self.game, turns=200, - repetitions=self.test_repetitions, - processes=2) + repetitions=self.test_repetitions) tournament._write_interactions = MagicMock( name='_write_interactions') self.assertTrue(tournament._run_serial()) @@ -270,8 +264,7 @@ def test_run_parallel(self): players=self.players, game=self.game, turns=200, - repetitions=self.test_repetitions, - processes=2) + repetitions=self.test_repetitions) tournament._write_interactions = MagicMock( name='_write_interactions') self.assertTrue(tournament._run_parallel()) @@ -288,18 +281,17 @@ def test_n_workers(self): players=self.players, game=self.game, turns=200, - repetitions=self.test_repetitions, - processes=1) - self.assertEqual(tournament._n_workers(), max_processes) + repetitions=self.test_repetitions) + self.assertEqual(tournament._n_workers(processes=1), max_processes) tournament = axelrod.Tournament( name=self.test_name, players=self.players, game=self.game, turns=200, - repetitions=self.test_repetitions, - processes=max_processes + 2) - self.assertEqual(tournament._n_workers(), max_processes) + repetitions=self.test_repetitions) + self.assertEqual(tournament._n_workers(processes=max_processes+2), + max_processes) @unittest.skipIf( cpu_count() < 2, @@ -313,9 +305,8 @@ def test_2_workers(self): players=self.players, game=self.game, turns=200, - repetitions=self.test_repetitions, - processes=2) - self.assertEqual(tournament._n_workers(), 2) + repetitions=self.test_repetitions,) + self.assertEqual(tournament._n_workers(processes=2), 2) def test_start_workers(self): workers = 2 @@ -521,7 +512,6 @@ def test_init(self): self.assertEqual(tournament.turns, float("inf")) self.assertEqual(tournament.repetitions, 10) self.assertEqual(tournament.name, 'test') - self.assertEqual(tournament._processes, None) self.assertTrue(tournament._with_morality) self.assertIsInstance(tournament._logger, logging.Logger) self.assertEqual(tournament.noise, 0.2) diff --git a/axelrod/tournament.py b/axelrod/tournament.py index 4a973d6ff..b463a94ef 100644 --- a/axelrod/tournament.py +++ b/axelrod/tournament.py @@ -20,7 +20,7 @@ class Tournament(object): def __init__(self, players, match_generator=RoundRobinMatches, name='axelrod', game=None, turns=200, repetitions=10, - processes=None, noise=0, with_morality=True): + noise=0, with_morality=True): """ Parameters ---------- @@ -53,7 +53,6 @@ def __init__(self, players, match_generator=RoundRobinMatches, self.match_generator = match_generator(players, turns, self.game, self.repetitions) self._with_morality = with_morality - self._processes = processes self._logger = logging.getLogger(__name__) def setup_output_file(self, filename=None): @@ -68,7 +67,7 @@ def setup_output_file(self, filename=None): # Save filename for loading ResultSet later self.filename = filename - def play(self, build_results=True, filename=None, progress_bar=True): + def play(self, build_results=True, filename=None, processes=None, progress_bar=True): """ Plays the tournament and passes the results to the ResultSet class @@ -92,10 +91,10 @@ def play(self, build_results=True, filename=None, progress_bar=True): if not build_results and not filename: warnings.warn("Tournament results will not be accessible since build_results=False and no filename was supplied.") - if self._processes is None: + if processes is None: self._run_serial(progress_bar=progress_bar) else: - self._run_parallel(progress_bar=progress_bar) + self._run_parallel(processes=processes, progress_bar=progress_bar) # Make sure that python has finished writing to disk self.outputfile.flush() @@ -151,7 +150,7 @@ def _write_interactions(self, results): row.append(history2) self.writer.writerow(row) - def _run_parallel(self, progress_bar=False): + def _run_parallel(self, processes=2, progress_bar=False): """ Run all matches in parallel @@ -166,7 +165,7 @@ def _run_parallel(self, progress_bar=False): # target functions which can be pickled and instance methods cannot. work_queue = Queue() done_queue = Queue() - workers = self._n_workers() + workers = self._n_workers(processes=processes) chunks = self.match_generator.build_match_chunks() for chunk in chunks: @@ -177,7 +176,7 @@ def _run_parallel(self, progress_bar=False): return True - def _n_workers(self): + def _n_workers(self, processes=2): """ Determines the number of parallel processes to use. @@ -185,8 +184,8 @@ def _n_workers(self): ------- integer """ - if (2 <= self._processes <= cpu_count()): - n_workers = self._processes + if (2 <= processes <= cpu_count()): + n_workers = processes else: n_workers = cpu_count() return n_workers @@ -297,7 +296,6 @@ class ProbEndTournament(Tournament): def __init__(self, players, match_generator=ProbEndRoundRobinMatches, name='axelrod', game=None, prob_end=.5, repetitions=10, - processes=None, noise=0, with_morality=True): """ @@ -324,8 +322,7 @@ def __init__(self, players, match_generator=ProbEndRoundRobinMatches, """ super(ProbEndTournament, self).__init__( players, name=name, game=game, turns=float("inf"), - repetitions=repetitions, processes=processes, - noise=noise, with_morality=with_morality) + repetitions=repetitions, noise=noise, with_morality=with_morality) self.prob_end = prob_end self.match_generator = ProbEndRoundRobinMatches( From b9178c9931ad9c63c39867f2c94cb8af5d460ff8 Mon Sep 17 00:00:00 2001 From: Vince Knight Date: Wed, 11 May 2016 11:50:57 +0100 Subject: [PATCH 5/5] Fixing hypothesis decorators. --- axelrod/tests/property.py | 26 ++++---------------------- axelrod/tests/unit/test_property.py | 14 -------------- 2 files changed, 4 insertions(+), 36 deletions(-) diff --git a/axelrod/tests/property.py b/axelrod/tests/property.py index f40623270..b5154759d 100644 --- a/axelrod/tests/property.py +++ b/axelrod/tests/property.py @@ -60,8 +60,7 @@ def tournaments(draw, strategies=axelrod.strategies, min_size=1, max_size=10, min_turns=1, max_turns=200, min_noise=0, max_noise=1, - min_repetitions=1, max_repetitions=20, - max_processes=None): + min_repetitions=1, max_repetitions=20): """ A hypothesis decorator to return a tournament and a random seed (to ensure reproducibility for strategies that make use of the random module when @@ -85,8 +84,6 @@ def tournaments(draw, strategies=axelrod.strategies, The minimum number of repetitions max_repetitions : integer The maximum number of repetitions - max_processes : bool - Maximum number of processes to use """ seed = draw(random_module()) strategies = draw(strategy_lists(strategies=strategies, @@ -98,14 +95,8 @@ def tournaments(draw, strategies=axelrod.strategies, max_value=max_repetitions)) noise = draw(floats(min_value=min_noise, max_value=max_noise)) - if max_processes is not None: - processes = draw(integers(min_value=1, max_value=max_processes)) - else: - processes = None - tournament = axelrod.Tournament(players, turns=turns, - repetitions=repetitions, noise=noise, - processes=processes) + repetitions=repetitions, noise=noise) return tournament, seed @@ -114,8 +105,7 @@ def prob_end_tournaments(draw, strategies=axelrod.strategies, min_size=1, max_size=10, min_prob_end=0, max_prob_end=1, min_noise=0, max_noise=1, - min_repetitions=1, max_repetitions=20, - max_processes=None): + min_repetitions=1, max_repetitions=20): """ A hypothesis decorator to return a tournament and a random seed (to ensure reproducibility for strategies that make use of the random module when @@ -139,8 +129,6 @@ def prob_end_tournaments(draw, strategies=axelrod.strategies, The minimum number of repetitions max_repetitions : integer The maximum number of repetitions - max_processes : bool - Maximum number of processes to use """ seed = draw(random_module()) strategies = draw(strategy_lists(strategies=strategies, @@ -152,14 +140,8 @@ def prob_end_tournaments(draw, strategies=axelrod.strategies, max_value=max_repetitions)) noise = draw(floats(min_value=min_noise, max_value=max_noise)) - if max_processes is not None: - processes = draw(integers(min_value=1, max_value=max_processes)) - else: - processes = None - tournament = axelrod.ProbEndTournament(players, prob_end=prob_end, - repetitions=repetitions, noise=noise, - processes=processes) + repetitions=repetitions, noise=noise) return tournament, seed diff --git a/axelrod/tests/unit/test_property.py b/axelrod/tests/unit/test_property.py index e15b54418..79d36d5dc 100644 --- a/axelrod/tests/unit/test_property.py +++ b/axelrod/tests/unit/test_property.py @@ -105,7 +105,6 @@ def test_decorator(self, tournament_and_seed): self.assertGreaterEqual(tournament.noise, 0) self.assertLessEqual(tournament.repetitions, 50) self.assertGreaterEqual(tournament.repetitions, 2) - self.assertIsNone(tournament._processes) @given(tournament_and_seed=tournaments(strategies=axelrod.basic_strategies)) def test_decorator_with_given_strategies(self, tournament_and_seed): @@ -127,12 +126,6 @@ def test_decorator_with_stochastic_strategies(self, tournament_and_seed): for p in tournament.players: self.assertIn(str(p), stochastic_player_names) - @given(tournament_and_seed=tournaments(max_processes=2)) - def test_decorator_with_stochastic_strategies(self, tournament_and_seed): - tournament, seed = tournament_and_seed - self.assertGreaterEqual(tournament._processes, 1) - self.assertLessEqual(tournament._processes, 2) - class TestProbEndTournament(unittest.TestCase): @@ -157,7 +150,6 @@ def test_decorator(self, tournament_and_seed): self.assertGreaterEqual(tournament.noise, 0) self.assertLessEqual(tournament.repetitions, 50) self.assertGreaterEqual(tournament.repetitions, 2) - self.assertIsNone(tournament._processes) @given(tournament_and_seed=prob_end_tournaments(strategies=axelrod.basic_strategies)) def test_decorator_with_given_strategies(self, tournament_and_seed): @@ -179,12 +171,6 @@ def test_decorator_with_stochastic_strategies(self, tournament_and_seed): for p in tournament.players: self.assertIn(str(p), stochastic_player_names) - @given(tournament_and_seed=prob_end_tournaments(max_processes=2)) - def test_decorator_with_stochastic_strategies(self, tournament_and_seed): - tournament, seed = tournament_and_seed - self.assertGreaterEqual(tournament._processes, 1) - self.assertLessEqual(tournament._processes, 2) - class TestGame(unittest.TestCase): def test_call(self):