diff --git a/axelrod/result_set.py b/axelrod/result_set.py index b2bc1b94c..b0a673009 100644 --- a/axelrod/result_set.py +++ b/axelrod/result_set.py @@ -1,5 +1,6 @@ from collections import defaultdict import csv +import tqdm from numpy import mean, nanmedian, std @@ -14,10 +15,25 @@ from io import StringIO +def update_progress_bar(method): + """A decorator to update a progress bar if it exists""" + def wrapper(*args): + """Run the method and update the progress bar if it exists""" + output = method(*args) + + try: + args[0].progress_bar.update(1) + except AttributeError: + pass + + return output + return wrapper + + class ResultSet(object): """A class to hold the results of a tournament.""" - def __init__(self, players, interactions, with_morality=True): + def __init__(self, players, interactions, progress_bar=True): """ Parameters ---------- @@ -26,19 +42,24 @@ def __init__(self, players, interactions, with_morality=True): interactions : list a list of dictionaries mapping tuples of player indices to interactions (1 for each repetition) - with_morality : bool - a flag to determine whether morality metrics should be - calculated. + progress_bar : bool + Whether or not to create a progress bar which will be updated """ self.players = players self.nplayers = len(players) self.interactions = interactions self.nrepetitions = max([len(rep) for rep in list(interactions.values())]) + if progress_bar: + self.progress_bar = tqdm.tqdm(total=19, desc="Analysing results") + else: + self.progress_bar = False + # Calculate all attributes: - self.build_all(with_morality) + self.build_all() + - def build_all(self, with_morality): + def build_all(self): """Build all the results. In a seperate method to make inheritance more straightforward""" self.wins = self.build_wins() @@ -54,15 +75,19 @@ def build_all(self, with_morality): self.score_diffs = self.build_score_diffs() self.payoff_diffs_means = self.build_payoff_diffs_means() - if with_morality: - self.cooperation = self.build_cooperation() - self.normalised_cooperation = self.build_normalised_cooperation() - self.vengeful_cooperation = self.build_vengeful_cooperation() - self.cooperating_rating = self.build_cooperating_rating() - self.good_partner_matrix = self.build_good_partner_matrix() - self.good_partner_rating = self.build_good_partner_rating() - self.eigenmoses_rating = self.build_eigenmoses_rating() - self.eigenjesus_rating = self.build_eigenjesus_rating() + self.cooperation = self.build_cooperation() + self.normalised_cooperation = self.build_normalised_cooperation() + self.vengeful_cooperation = self.build_vengeful_cooperation() + self.cooperating_rating = self.build_cooperating_rating() + self.good_partner_matrix = self.build_good_partner_matrix() + self.good_partner_rating = self.build_good_partner_rating() + self.eigenmoses_rating = self.build_eigenmoses_rating() + self.eigenjesus_rating = self.build_eigenjesus_rating() + + try: + self.progress_bar.close() + except AttributeError: + pass @property def _null_results_matrix(self): @@ -79,6 +104,7 @@ def _null_results_matrix(self): replist = list(range(self.nrepetitions)) return [[[0 for j in plist] for i in plist] for r in replist] + @update_progress_bar def build_match_lengths(self): """ Returns: @@ -110,6 +136,7 @@ def build_match_lengths(self): return match_lengths + @update_progress_bar def build_scores(self): """ Returns: @@ -143,6 +170,7 @@ def build_scores(self): return scores + @update_progress_bar def build_ranked_names(self): """ Returns: @@ -150,8 +178,10 @@ def build_ranked_names(self): Returns the ranked names. A list of names as calculated by self.ranking. """ + return [str(self.players[i]) for i in self.ranking] + @update_progress_bar def build_wins(self): """ Returns: @@ -187,6 +217,7 @@ def build_wins(self): return wins + @update_progress_bar def build_normalised_scores(self): """ Returns: @@ -229,6 +260,7 @@ def build_normalised_scores(self): return normalised_scores + @update_progress_bar def build_ranking(self): """ Returns: @@ -244,6 +276,7 @@ def build_ranking(self): return sorted(range(self.nplayers), key=lambda i: -nanmedian(self.normalised_scores[i])) + @update_progress_bar def build_payoffs(self): """ Returns: @@ -281,8 +314,10 @@ def build_payoffs(self): utilities.append(iu.compute_final_score_per_turn(interaction)[1]) payoffs[player][opponent] = utilities + return payoffs + @update_progress_bar def build_payoff_matrix(self): """ Returns: @@ -317,6 +352,7 @@ def build_payoff_matrix(self): return payoff_matrix + @update_progress_bar def build_payoff_stddevs(self): """ Returns: @@ -353,6 +389,7 @@ def build_payoff_stddevs(self): return payoff_stddevs + @update_progress_bar def build_score_diffs(self): """ Returns: @@ -391,8 +428,10 @@ def build_score_diffs(self): scores = iu.compute_final_score_per_turn(interaction) diff = (scores[1] - scores[0]) score_diffs[player][opponent][repetition] = diff + return score_diffs + @update_progress_bar def build_payoff_diffs_means(self): """ Returns: @@ -429,8 +468,10 @@ def build_payoff_diffs_means(self): payoff_diffs_means[player][opponent] = mean(diffs) else: payoff_diffs_means[player][opponent] = 0 + return payoff_diffs_means + @update_progress_bar def build_cooperation(self): """ Returns: @@ -465,8 +506,10 @@ def build_cooperation(self): coop_count += iu.compute_cooperations(interaction)[1] cooperations[player][opponent] += coop_count + return cooperations + @update_progress_bar def build_normalised_cooperation(self): """ Returns: @@ -507,8 +550,10 @@ def build_normalised_cooperation(self): # Mean over all reps: normalised_cooperations[player][opponent] = mean(coop_counts) + return normalised_cooperations + @update_progress_bar def build_vengeful_cooperation(self): """ Returns: @@ -522,6 +567,7 @@ def build_vengeful_cooperation(self): return [[2 * (element - 0.5) for element in row] for row in self.normalised_cooperation] + @update_progress_bar def build_cooperating_rating(self): """ Returns: @@ -552,6 +598,7 @@ def build_cooperating_rating(self): return [sum(cs) / max(1, float(sum(ls))) for cs, ls in zip(self.cooperation, lengths)] + @update_progress_bar def build_good_partner_matrix(self): """ Returns: @@ -586,6 +633,7 @@ def build_good_partner_matrix(self): return good_partner_matrix + @update_progress_bar def build_good_partner_rating(self): """ Returns: @@ -607,6 +655,7 @@ def build_good_partner_rating(self): return good_partner_rating + @update_progress_bar def build_eigenjesus_rating(self): """ Returns: @@ -617,8 +666,10 @@ def build_eigenjesus_rating(self): """ eigenvector, eigenvalue = eigen.principal_eigenvector( self.normalised_cooperation) + return eigenvector.tolist() + @update_progress_bar def build_eigenmoses_rating(self): """ Returns: @@ -629,6 +680,7 @@ def build_eigenmoses_rating(self): """ eigenvector, eigenvalue = eigen.principal_eigenvector( self.vengeful_cooperation) + return eigenvector.tolist() def csv(self): @@ -655,22 +707,26 @@ class ResultSetFromFile(ResultSet): by the tournament class. """ - def __init__(self, filename, with_morality=True): + def __init__(self, filename, progress_bar=True): """ Parameters ---------- filename : string name of a file of the correct file. - with_morality : bool - a flag to determine whether morality metrics should be - calculated. + progress_bar : bool + Whether or not to create a progress bar which will be updated """ self.players, self.interactions = self._read_csv(filename) self.nplayers = len(self.players) self.nrepetitions = len(list(self.interactions.values())[0]) + if progress_bar: + self.progress_bar = tqdm.tqdm(total=19, desc="Analysing results") + else: + self.progress_bar = False + # Calculate all attributes: - self.build_all(with_morality) + self.build_all() def _read_csv(self, filename): """ diff --git a/axelrod/tests/unit/test_resultset.py b/axelrod/tests/unit/test_resultset.py index 2df8666ac..c5a084bb1 100644 --- a/axelrod/tests/unit/test_resultset.py +++ b/axelrod/tests/unit/test_resultset.py @@ -161,7 +161,9 @@ def setUpClass(cls): 'Defector,Tit For Tat,Alternator\n2.6,1.7,1.5\n2.6,1.7,1.5\n2.6,1.7,1.5\n') def test_init(self): - rs = axelrod.ResultSet(self.players, self.interactions) + rs = axelrod.ResultSet(self.players, self.interactions, + progress_bar=False) + self.assertFalse(rs.progress_bar) self.assertEqual(rs.players, self.players) self.assertEqual(rs.nplayers, len(self.players)) self.assertEqual(rs.interactions, self.interactions) @@ -176,13 +178,25 @@ def test_init(self): self.assertIsInstance(interaction, list) self.assertEqual(len(interaction), self.turns) - def test_null_results_matrix(self): + def test_with_progress_bar(self): rs = axelrod.ResultSet(self.players, self.interactions) + self.assertTrue(rs.progress_bar) + self.assertEqual(rs.progress_bar.total, 19) + + rs = axelrod.ResultSet(self.players, self.interactions, + progress_bar=True) + self.assertTrue(rs.progress_bar) + self.assertEqual(rs.progress_bar.total, 19) + + def test_null_results_matrix(self): + rs = axelrod.ResultSet(self.players, self.interactions, + progress_bar=False) self.assertEqual( rs._null_results_matrix, self.expected_null_results_matrix) def test_match_lengths(self): - rs = axelrod.ResultSet(self.players, self.interactions) + rs = axelrod.ResultSet(self.players, self.interactions, + progress_bar=False) self.assertIsInstance(rs.match_lengths, list) self.assertEqual(len(rs.match_lengths), rs.nrepetitions) self.assertEqual(rs.match_lengths, self.expected_match_lengths) @@ -202,49 +216,57 @@ def test_match_lengths(self): self.assertEqual(length, self.turns) def test_scores(self): - rs = axelrod.ResultSet(self.players, self.interactions) + rs = axelrod.ResultSet(self.players, self.interactions, + progress_bar=False) self.assertIsInstance(rs.scores, list) self.assertEqual(len(rs.scores), rs.nplayers) self.assertEqual(rs.scores, self.expected_scores) def test_ranking(self): - rs = axelrod.ResultSet(self.players, self.interactions) + rs = axelrod.ResultSet(self.players, self.interactions, + progress_bar=False) self.assertIsInstance(rs.ranking, list) self.assertEqual(len(rs.ranking), rs.nplayers) self.assertEqual(rs.ranking, self.expected_ranking) def test_ranked_names(self): - rs = axelrod.ResultSet(self.players, self.interactions) + rs = axelrod.ResultSet(self.players, self.interactions, + progress_bar=False) self.assertIsInstance(rs.ranked_names, list) self.assertEqual(len(rs.ranked_names), rs.nplayers) self.assertEqual(rs.ranked_names, self.expected_ranked_names) def test_wins(self): - rs = axelrod.ResultSet(self.players, self.interactions) + rs = axelrod.ResultSet(self.players, self.interactions, + progress_bar=False) self.assertIsInstance(rs.wins, list) self.assertEqual(len(rs.wins), rs.nplayers) self.assertEqual(rs.wins, self.expected_wins) def test_normalised_scores(self): - rs = axelrod.ResultSet(self.players, self.interactions) + rs = axelrod.ResultSet(self.players, self.interactions, + progress_bar=False) self.assertIsInstance(rs.normalised_scores, list) self.assertEqual(len(rs.normalised_scores), rs.nplayers) self.assertEqual(rs.normalised_scores, self.expected_normalised_scores) def test_payoffs(self): - rs = axelrod.ResultSet(self.players, self.interactions) + rs = axelrod.ResultSet(self.players, self.interactions, + progress_bar=False) self.assertIsInstance(rs.payoffs, list) self.assertEqual(len(rs.payoffs), rs.nplayers) self.assertEqual(rs.payoffs, self.expected_payoffs) def test_payoff_matrix(self): - rs = axelrod.ResultSet(self.players, self.interactions) + rs = axelrod.ResultSet(self.players, self.interactions, + progress_bar=False) self.assertIsInstance(rs.payoff_matrix, list) self.assertEqual(len(rs.payoff_matrix), rs.nplayers) self.assertEqual(rs.payoff_matrix, self.expected_payoff_matrix) def test_score_diffs(self): - rs = axelrod.ResultSet(self.players, self.interactions) + rs = axelrod.ResultSet(self.players, self.interactions, + progress_bar=False) self.assertIsInstance(rs.score_diffs, list) self.assertEqual(len(rs.score_diffs), rs.nplayers) for i, row in enumerate(rs.score_diffs): @@ -254,7 +276,8 @@ def test_score_diffs(self): self.expected_score_diffs[i][j][k]) def test_payoff_diffs_means(self): - rs = axelrod.ResultSet(self.players, self.interactions) + rs = axelrod.ResultSet(self.players, self.interactions, + progress_bar=False) self.assertIsInstance(rs.payoff_diffs_means, list) self.assertEqual(len(rs.payoff_diffs_means), rs.nplayers) for i, row in enumerate(rs.payoff_diffs_means): @@ -263,68 +286,78 @@ def test_payoff_diffs_means(self): self.expected_payoff_diffs_means[i][j]) def test_payoff_stddevs(self): - rs = axelrod.ResultSet(self.players, self.interactions) + rs = axelrod.ResultSet(self.players, self.interactions, + progress_bar=False) self.assertIsInstance(rs.payoff_stddevs, list) self.assertEqual(len(rs.payoff_stddevs), rs.nplayers) self.assertEqual(rs.payoff_stddevs, self.expected_payoff_stddevs) def test_cooperation(self): - rs = axelrod.ResultSet(self.players, self.interactions) + rs = axelrod.ResultSet(self.players, self.interactions, + progress_bar=False) self.assertIsInstance(rs.cooperation, list) self.assertEqual(len(rs.cooperation), rs.nplayers) self.assertEqual(rs.cooperation, self.expected_cooperation) def test_normalised_cooperation(self): - rs = axelrod.ResultSet(self.players, self.interactions) + rs = axelrod.ResultSet(self.players, self.interactions, + progress_bar=False) self.assertIsInstance(rs.normalised_cooperation, list) self.assertEqual(len(rs.normalised_cooperation), rs.nplayers) self.assertEqual(rs.normalised_cooperation, self.expected_normalised_cooperation) def test_vengeful_cooperation(self): - rs = axelrod.ResultSet(self.players, self.interactions) + rs = axelrod.ResultSet(self.players, self.interactions, + progress_bar=False) self.assertIsInstance(rs.vengeful_cooperation, list) self.assertEqual(len(rs.vengeful_cooperation), rs.nplayers) self.assertEqual(rs.vengeful_cooperation, self.expected_vengeful_cooperation) def test_cooperating_rating(self): - rs = axelrod.ResultSet(self.players, self.interactions) + rs = axelrod.ResultSet(self.players, self.interactions, + progress_bar=False) self.assertIsInstance(rs.cooperating_rating, list) self.assertEqual(len(rs.cooperating_rating), rs.nplayers) self.assertEqual(rs.cooperating_rating, self.expected_cooperating_rating) def test_good_partner_matrix(self): - rs = axelrod.ResultSet(self.players, self.interactions) + rs = axelrod.ResultSet(self.players, self.interactions, + progress_bar=False) self.assertIsInstance(rs.good_partner_matrix, list) self.assertEqual(len(rs.good_partner_matrix), rs.nplayers) self.assertEqual(rs.good_partner_matrix, self.expected_good_partner_matrix) def test_good_partner_rating(self): - rs = axelrod.ResultSet(self.players, self.interactions) + rs = axelrod.ResultSet(self.players, self.interactions, + progress_bar=False) self.assertIsInstance(rs.good_partner_rating, list) self.assertEqual(len(rs.good_partner_rating), rs.nplayers) self.assertEqual(rs.good_partner_rating, self.expected_good_partner_rating) def test_eigenjesus_rating(self): - rs = axelrod.ResultSet(self.players, self.interactions) + rs = axelrod.ResultSet(self.players, self.interactions, + progress_bar=False) self.assertIsInstance(rs.eigenjesus_rating, list) self.assertEqual(len(rs.eigenjesus_rating), rs.nplayers) for j, rate in enumerate(rs.eigenjesus_rating): self.assertAlmostEqual(rate, self.expected_eigenjesus_rating[j]) def test_eigenmoses_rating(self): - rs = axelrod.ResultSet(self.players, self.interactions) + rs = axelrod.ResultSet(self.players, self.interactions, + progress_bar=False) self.assertIsInstance(rs.eigenmoses_rating, list) self.assertEqual(len(rs.eigenmoses_rating), rs.nplayers) for j, rate in enumerate(rs.eigenmoses_rating): self.assertAlmostEqual(rate, self.expected_eigenmoses_rating[j]) def test_csv(self): - rs = axelrod.ResultSet(self.players, self.interactions) + rs = axelrod.ResultSet(self.players, self.interactions, + progress_bar=False) self.assertEqual(rs.csv(), self.expected_csv) @@ -341,7 +374,7 @@ class TestResultSetFromFile(unittest.TestCase): def test_init(self): - rs = axelrod.ResultSetFromFile(self.tmp_file.name) + rs = axelrod.ResultSetFromFile(self.tmp_file.name, progress_bar=False) players = ['Cooperator', 'Tit For Tat', 'Defector'] self.assertEqual(rs.players, players) self.assertEqual(rs.nplayers, len(players)) @@ -354,3 +387,9 @@ def test_init(self): (0, 2): [[('C', 'D'), ('C', 'D')]], (1, 1): [[('C', 'C'), ('C', 'C')]]} self.assertEqual(rs.interactions, expected_interactions) + + +class TestDecorator(unittest.TestCase): + def test_update_progress_bar(self): + method = lambda x: None + self.assertEqual(axelrod.result_set.update_progress_bar(method)(1), None) diff --git a/axelrod/tournament.py b/axelrod/tournament.py index 6b638aa1f..326846433 100644 --- a/axelrod/tournament.py +++ b/axelrod/tournament.py @@ -85,7 +85,8 @@ def play(self, build_results=True, filename=None, processes=None, progress_bar=T axelrod.ResultSet """ if progress_bar: - self.progress_bar = tqdm.tqdm(total=len(self.match_generator)) + self.progress_bar = tqdm.tqdm(total=len(self.match_generator), + desc="Playing matches") self.setup_output_file(filename) if not build_results and not filename: @@ -96,13 +97,16 @@ def play(self, build_results=True, filename=None, processes=None, progress_bar=T else: self._run_parallel(processes=processes, progress_bar=progress_bar) + if progress_bar: + self.progress_bar.close() + # Make sure that python has finished writing to disk self.outputfile.flush() if build_results: - return self._build_result_set() + return self._build_result_set(progress_bar=progress_bar) - def _build_result_set(self): + def _build_result_set(self, progress_bar=True): """ Build the result set (used by the play method) @@ -112,7 +116,7 @@ def _build_result_set(self): """ result_set = ResultSetFromFile( filename=self.filename, - with_morality=self._with_morality) + progress_bar=progress_bar) self.outputfile.close() return result_set