diff --git a/.gitignore b/.gitignore index 6400ab3d7..0c3f3de6d 100644 --- a/.gitignore +++ b/.gitignore @@ -4,7 +4,7 @@ cache.txt test.csv summary.csv basic_tournament.csv -test_outputs/*csv +test_outputs/*csv.summary test_outputs/*svg test_outputs/*cache @@ -118,3 +118,12 @@ docker-compose.yml # Mypy files .mypy_cache/ + +test_outputs/stochastic_tournament_0.csv +test_outputs/stochastic_tournament_1.csv +test_outputs/test_fingerprint.csv +test_outputs/test_fingerprint_tmp.csv +test_outputs/test_results_from_file.csv +test_outputs/test_results_from_file_tmp.csv +test_outputs/test_tournament.csv +test_outputs/tran_fin.csv diff --git a/axelrod/__init__.py b/axelrod/__init__.py index 009d09c55..599e2397c 100644 --- a/axelrod/__init__.py +++ b/axelrod/__init__.py @@ -18,7 +18,7 @@ from .deterministic_cache import DeterministicCache from .match_generator import * from .tournament import Tournament -from .result_set import ResultSet, ResultSetFromFile +from .result_set import ResultSet from .ecosystem import Ecosystem from .fingerprint import AshlockFingerprint, TransitiveFingerprint diff --git a/axelrod/ecosystem.py b/axelrod/ecosystem.py index f75cff485..61f4d81cb 100644 --- a/axelrod/ecosystem.py +++ b/axelrod/ecosystem.py @@ -12,7 +12,7 @@ def __init__(self, results: ResultSet, population: List[int] = None) -> None: self.results = results - self.nplayers = self.results.nplayers + self.num_players = self.results.num_players self.payoff_matrix = self.results.payoff_matrix self.payoff_stddevs = self.results.payoff_stddevs @@ -27,7 +27,7 @@ def __init__(self, results: ResultSet, if min(population) < 0: raise TypeError( "Minimum value of population vector must be non-negative") - elif len(population) != self.nplayers: + elif len(population) != self.num_players: raise TypeError( "Population vector must be same size as number of players") else: @@ -35,7 +35,7 @@ def __init__(self, results: ResultSet, self.population_sizes = [[p / norm for p in population]] else: self.population_sizes = [ - [1 / self.nplayers for _ in range(self.nplayers)]] + [1 / self.num_players for _ in range(self.num_players)]] # This function is quite arbitrary and probably only influences the # kinetics for the current code. @@ -47,7 +47,7 @@ def __init__(self, results: ResultSet, def reproduce(self, turns: int): for iturn in range(turns): - plist = list(range(self.nplayers)) + plist = list(range(self.num_players)) pops = self.population_sizes[-1] # The unit payoff for each player in this turn is the sum of the diff --git a/axelrod/fingerprint.py b/axelrod/fingerprint.py index 9cc9c6f54..1b1a289a1 100644 --- a/axelrod/fingerprint.py +++ b/axelrod/fingerprint.py @@ -6,6 +6,8 @@ import matplotlib.pyplot as plt import numpy as np import tqdm +import dask.dataframe as dd +import dask as da from mpl_toolkits.axes_grid1 import make_axes_locatable import axelrod as axl @@ -266,7 +268,7 @@ def construct_tournament_elements(self, step: float, def fingerprint( self, turns: int = 50, repetitions: int = 10, step: float = 0.01, - processes: int=None, filename: str = None, in_memory: bool = False, + processes: int=None, filename: str = None, progress_bar: bool = True ) -> dict: """Build and play the spatial tournament. @@ -290,10 +292,7 @@ def fingerprint( The number of processes to be used for parallel processing filename: str, optional The name of the file for self.spatial_tournament's interactions. - if None and in_memory=False, will auto-generate a filename. - in_memory: bool - Whether self.spatial_tournament keeps interactions_dict in memory or - in a file. + if None, will auto-generate a filename. progress_bar : bool Whether or not to create a progress bar which will be updated @@ -305,7 +304,7 @@ def fingerprint( """ temp_file_descriptor = None - if not in_memory and filename is None: + if filename is None: temp_file_descriptor, filename = mkstemp() edges, tourn_players = self.construct_tournament_elements( @@ -318,13 +317,10 @@ def fingerprint( self.spatial_tournament.play(build_results=False, filename=filename, processes=processes, - in_memory=in_memory, progress_bar=progress_bar) - if in_memory: - self.interactions = self.spatial_tournament.interactions_dict - else: - self.interactions = read_interactions_from_file( - filename, progress_bar=progress_bar) + + self.interactions = read_interactions_from_file( + filename, progress_bar=progress_bar) if temp_file_descriptor is not None: os.close(temp_file_descriptor) @@ -483,17 +479,21 @@ def analyse_cooperation_ratio(filename): opponent in each turn. The ith row corresponds to the ith opponent and the jth column the jth turn. """ - did_c = np.vectorize(lambda action: int(action == 'C')) + did_c = np.vectorize(lambda actions: [int(action == 'C') + for action in actions]) cooperation_rates = {} - with open(filename, "r") as f: - reader = csv.reader(f) - for row in reader: - opponent_index, player_history = int(row[1]), list(row[4]) - if opponent_index in cooperation_rates: - cooperation_rates[opponent_index].append(did_c(player_history)) - else: - cooperation_rates[opponent_index] = [did_c(player_history)] + df = dd.read_csv(filename) + # We ignore the actions of all opponents. So we filter the dataframe to + # only include the results of the player with index `0`. + df = df[df["Player index"] == 0][["Opponent index", "Actions"]] + + for _, row in df.iterrows(): + opponent_index, player_history = row["Opponent index"], row["Actions"] + if opponent_index in cooperation_rates: + cooperation_rates[opponent_index].append(did_c(player_history)) + else: + cooperation_rates[opponent_index] = [did_c(player_history)] for index, rates in cooperation_rates.items(): cooperation_rates[index] = np.mean(rates, axis=0) diff --git a/axelrod/interaction_utils.py b/axelrod/interaction_utils.py index 4338a162a..7488e9ca4 100644 --- a/axelrod/interaction_utils.py +++ b/axelrod/interaction_utils.py @@ -7,9 +7,10 @@ This is used by both the Match class and the ResultSet class which analyse interactions. """ -from collections import Counter +from collections import Counter, defaultdict import csv import tqdm +import pandas as pd from axelrod.action import Action, str_to_actions from .game import Game @@ -239,36 +240,22 @@ def compute_sparklines(interactions, c_symbol='█', d_symbol=' '): sparkline(histories[1], c_symbol, d_symbol)) -def read_interactions_from_file(filename, progress_bar=True, - num_interactions=False): +def read_interactions_from_file(filename, progress_bar=True): """ Reads a file and returns a dictionary mapping tuples of player pairs to lists of interactions """ + df = pd.read_csv(filename)[["Interaction index", "Player index", + "Opponent index", "Actions"]] + groupby = df.groupby("Interaction index") if progress_bar: - if not num_interactions: - with open(filename) as f: - num_interactions = sum(1 for line in f) - progress_bar = tqdm.tqdm(total=num_interactions, desc="Loading") - - pairs_to_interactions = {} - with open(filename, 'r') as f: - for row in csv.reader(f): - index_pair = (int(row[0]), int(row[1])) - p1_actions = str_to_actions(row[4]) - p2_actions = str_to_actions(row[5]) - interaction = list(zip(p1_actions, p2_actions)) - - try: - pairs_to_interactions[index_pair].append(interaction) - except KeyError: - pairs_to_interactions[index_pair] = [interaction] - - if progress_bar: - progress_bar.update() + groupby = tqdm.tqdm(groupby) - if progress_bar: - progress_bar.close() + pairs_to_interactions = defaultdict(list) + for _, d in tqdm.tqdm(groupby): + key = tuple(d[["Player index", "Opponent index"]].iloc[0]) + value = list(map(str_to_actions, zip(*d["Actions"]))) + pairs_to_interactions[key].append(value) return pairs_to_interactions diff --git a/axelrod/plot.py b/axelrod/plot.py index a80381311..9f170bd02 100644 --- a/axelrod/plot.py +++ b/axelrod/plot.py @@ -25,7 +25,7 @@ def default_cmap(version: str = "2.0") -> str: class Plot(object): def __init__(self, result_set: ResultSet) -> None: self.result_set = result_set - self.nplayers = self.result_set.nplayers + self.num_players = self.result_set.num_players self.players = self.result_set.players def _violinplot( @@ -40,16 +40,16 @@ def _violinplot( ax = ax figure = ax.get_figure() - width = max(self.nplayers / 3, 12) + width = max(self.num_players / 3, 12) height = width / 2 spacing = 4 - positions = spacing * arange(1, self.nplayers + 1, 1) + positions = spacing * arange(1, self.num_players + 1, 1) figure.set_size_inches(width, height) ax.violinplot(data, positions=positions, widths=spacing / 2, showmedians=True, showextrema=False) ax.set_xticks(positions) ax.set_xticklabels(names, rotation=90) - ax.set_xlim([0, spacing * (self.nplayers + 1)]) + ax.set_xlim([0, spacing * (self.num_players + 1)]) ax.tick_params(axis='both', which='both', labelsize=8) if title: ax.set_title(title) @@ -175,14 +175,14 @@ def _payoff_heatmap( ax = ax figure = ax.get_figure() - width = max(self.nplayers / 4, 12) + width = max(self.num_players / 4, 12) height = width figure.set_size_inches(width, height) matplotlib_version = matplotlib.__version__ cmap = default_cmap(matplotlib_version) mat = ax.matshow(data, cmap=cmap) - ax.set_xticks(range(self.result_set.nplayers)) - ax.set_yticks(range(self.result_set.nplayers)) + ax.set_xticks(range(self.result_set.num_players)) + ax.set_yticks(range(self.result_set.num_players)) ax.set_xticklabels(names, rotation=90) ax.set_yticklabels(names) ax.tick_params(axis='both', which='both', labelsize=16) @@ -246,7 +246,7 @@ def stackplot( ticks = [] for i, n in enumerate(self.result_set.ranked_names): x = -0.01 - y = (i + 0.5) * 1 / self.result_set.nplayers + y = (i + 0.5) * 1 / self.result_set.num_players ax.annotate( n, xy=(x, y), xycoords=trans, clip_on=False, va='center', ha='right', fontsize=5) diff --git a/axelrod/result_set.py b/axelrod/result_set.py index 480af41af..035c161aa 100644 --- a/axelrod/result_set.py +++ b/axelrod/result_set.py @@ -1,9 +1,14 @@ from collections import namedtuple, Counter +from multiprocessing import cpu_count import csv +import itertools -from numpy import mean, nanmedian, std +import numpy as np import tqdm +import dask as da +import dask.dataframe as dd + from axelrod.action import Action, str_to_actions import axelrod.interaction_utils as iu from . import eigen @@ -15,9 +20,9 @@ def update_progress_bar(method): """A decorator to update a progress bar if it exists""" - def wrapper(*args): + def wrapper(*args, **kwargs): """Run the method and update the progress bar if it exists""" - output = method(*args) + output = method(*args, **kwargs) try: args[0].progress_bar.update(1) @@ -28,314 +33,250 @@ def wrapper(*args): return wrapper -class ResultSet(object): - """A class to hold the results of a tournament.""" +class ResultSet(): + """ + A class to hold the results of a tournament. Reads in a CSV file produced + by the tournament class. + """ - def __init__(self, players, interactions, repetitions=False, - progress_bar=True, game=None): + def __init__(self, filename, + players, repetitions, + processes=None, progress_bar=True): """ Parameters ---------- + filename : string + the file from which to read the interactions players : list - a list of player objects. - interactions : dict - a dictionary mapping tuples of player indices to - interactions (1 for each repetition) + A list of the names of players. If not known will be efficiently + read from file. repetitions : int - The number of repetitions - game : axlerod.game - The particular game used. - progress_bar : bool - Whether or not to create a progress bar which will be updated - """ - if game is None: - self.game = Game() - else: - self.game = game - - self.interactions = interactions - self.players = players - self.num_matches = len(interactions) - - if not players or not repetitions: - self.players, self.repetitions = self._read_players_and_repetition_numbers(progress_bar=progress_bar) - else: - self.players, self.repetitions = players, repetitions - - self.nplayers = len(self.players) - - self._build_empty_metrics() - self._build_score_related_metrics(progress_bar=progress_bar) - - def create_progress_bar(self, desc=None): - """ - Create a progress bar for a read through of the data file. - - Parameters - ---------- - desc : string - A description. - """ - return tqdm.tqdm(total=self.num_matches, desc=desc) - - def _update_players(self, index_pair, players): + The number of repetitions of each match. If not know will be + efficiently read from file. + processes : integer + The number of processes to be used for parallel processing """ - During a read of the data, update the internal players dictionary - - Parameters - ---------- + self.filename = filename + self.players, self.repetitions = players, repetitions + self.num_players = len(self.players) - index_pair : tuple - A tuple of player indices - players : tuple - A tuple of player names - """ - for index, player in zip(index_pair, players): - if index not in self.players_d: - self.players_d[index] = player + if progress_bar: + self.progress_bar = tqdm.tqdm(total=25, + desc="Analysing") - def _update_repetitions(self, index_pair, nbr=1): - """ - During a read of the data, update the internal repetitions dictionary + df = dd.read_csv(filename) + dask_tasks = self._build_tasks(df) - Parameters - ---------- + if processes == 0: + processes = cpu_count() - index_pair : tuple - A tuple of player indices - nbr : integer - The number of repetitions - """ - try: - self.repetitions_d[index_pair] += nbr - except KeyError: - self.repetitions_d[index_pair] = nbr + out = self._compute_tasks(tasks=dask_tasks, processes=processes) - def _build_repetitions(self): - """ - Count the number of repetitions + self._reshape_out(*out) - Returns - ------- + if progress_bar: + self.progress_bar.close() - repetitions : int - The number of repetitions - """ - repetitions = max(self.repetitions_d.values()) + def _reshape_out(self, + mean_per_reps_player_opponent_df, + sum_per_player_opponent_df, + sum_per_player_repetition_df, + normalised_scores_series, + initial_cooperation_count_series, + interactions_count_series): + """ + Reshape the various pandas series objects to be of the required form and + set the corresponding attributes. + """ + + self.payoffs = self._reshape_three_dim_list( + mean_per_reps_player_opponent_df["Score per turn"], + first_dimension=range(self.num_players), + second_dimension=range(self.num_players), + third_dimension=range(self.repetitions), + key_order=[2, 0, 1]) + + self.score_diffs = self._reshape_three_dim_list( + mean_per_reps_player_opponent_df["Score difference per turn"], + first_dimension=range(self.num_players), + second_dimension=range(self.num_players), + third_dimension=range(self.repetitions), + key_order=[2, 0, 1], + alternative=0) + + self.match_lengths = self._reshape_three_dim_list( + mean_per_reps_player_opponent_df["Turns"], + first_dimension=range(self.repetitions), + second_dimension=range(self.num_players), + third_dimension=range(self.num_players), + alternative=0) + + self.wins = self._reshape_two_dim_list(sum_per_player_repetition_df["Win"]) + self.scores = self._reshape_two_dim_list(sum_per_player_repetition_df["Score"]) + self.normalised_scores = self._reshape_two_dim_list(normalised_scores_series) + + self.cooperation = self._build_cooperation(sum_per_player_opponent_df["Cooperation count"]) + self.good_partner_matrix = self._build_good_partner_matrix(sum_per_player_opponent_df["Good partner"]) + + columns = ["CC count", "CD count", "DC count", "DD count"] + self.state_distribution = self._build_state_distribution(sum_per_player_opponent_df[columns]) + self.normalised_state_distribution = self._build_normalised_state_distribution() - del self.repetitions_d # Manual garbage collection - return repetitions + columns = ["CC to C count", + "CC to D count", + "CD to C count", + "CD to D count", + "DC to C count", + "DC to D count", + "DD to C count", + "DD to D count"] + self.state_to_action_distribution = self._build_state_to_action_distribution(sum_per_player_opponent_df[columns]) + self.normalised_state_to_action_distribution = self._build_normalised_state_to_action_distribution() - def _build_players(self): - """ - List the players + self.initial_cooperation_count = self._build_initial_cooperation_count(initial_cooperation_count_series) + self.initial_cooperation_rate = self._build_initial_cooperation_rate(interactions_count_series) + self.good_partner_rating = self._build_good_partner_rating(interactions_count_series) - Returns - ------- + self.normalised_cooperation = self._build_normalised_cooperation() + self.ranking = self._build_ranking() + self.ranked_names = self._build_ranked_names() - players : list - An ordered list of players - """ - players = [] - for i in range(len(self.players_d)): - players.append(self.players_d[i]) + self.payoff_matrix = self._build_summary_matrix(self.payoffs) + self.payoff_stddevs = self._build_summary_matrix(self.payoffs, + func=np.std) - del self.players_d # Manual garbage collection - return players + self.payoff_diffs_means = self._build_payoff_diffs_means() + self.cooperating_rating = self._build_cooperating_rating() + self.vengeful_cooperation = self._build_vengeful_cooperation() + self.eigenjesus_rating = self._build_eigenjesus_rating() + self.eigenmoses_rating = self._build_eigenmoses_rating() @update_progress_bar - def _build_eigenmoses_rating(self): + def _reshape_three_dim_list(self, series, + first_dimension, + second_dimension, + third_dimension, + alternative=None, + key_order=[0, 1, 2]): """ - Returns: - -------- + Parameters + ---------- - The eigenmoses rating as defined in: - http://www.scottaaronson.com/morality.pdf - """ - eigenvector, eigenvalue = eigen.principal_eigenvector( - self.vengeful_cooperation) + payoffs_series : pandas.Series + first_dimension : iterable + second_dimension : iterable + third_dimension : iterable + alternative : int + What to do if there is no entry at given position + key_order : list + Indices re-ording the dimensions to the correct keys in the + series - return eigenvector.tolist() - - @update_progress_bar - def _build_eigenjesus_rating(self): - """ Returns: -------- - - The eigenjesus rating as defined in: - http://www.scottaaronson.com/morality.pdf - """ - eigenvector, eigenvalue = eigen.principal_eigenvector( - self.normalised_cooperation) - - return eigenvector.tolist() + A three dimensional list across the three dimensions + """ + series_dict = series.to_dict() + output = [] + for first_index in first_dimension: + matrix = [] + for second_index in second_dimension: + row = [] + for third_index in third_dimension: + key = (first_index, second_index, third_index) + key = tuple([key[order] for order in key_order]) + if key in series_dict: + row.append(series_dict[key]) + elif alternative is not None: + row.append(alternative) + matrix.append(row) + output.append(matrix) + return output @update_progress_bar - def _build_cooperating_rating(self): + def _reshape_two_dim_list(self, series): """ - Returns: - -------- - - The list of cooperation ratings - List of the form: - - [ML1, ML2, ML3..., MLn] - - Where n is the number of players and MLi is a list of the form: - - [pi1, pi2, pi3, ..., pim] - - Where pij is the total number of cooperations divided by the total - number of turns over all repetitions played by player i against - player j. - """ - - plist = list(range(self.nplayers)) - total_length_v_opponent = [zip(*[rep[player_index] for - rep in self.match_lengths]) - for player_index in plist] - lengths = [[sum(e) for j, e in enumerate(row) if i != j] for i, row in - enumerate(total_length_v_opponent)] + Parameters + ---------- - # Max is to deal with edge cases of matches that have no turns - return [sum(cs) / max(1, sum(ls)) for cs, ls - in zip(self.cooperation, lengths)] + series : pandas.Series - @update_progress_bar - def _build_vengeful_cooperation(self): - """ Returns: -------- - - The vengeful cooperation matrix derived from the - normalised cooperation matrix: - - Dij = 2(Cij - 0.5) + A two dimensional list across repetitions and opponents """ - return [[2 * (element - 0.5) for element in row] - for row in self.normalised_cooperation] + series_dict = series.to_dict() + out = [[series_dict.get((player_index, repetition), 0) + for repetition in range(self.repetitions)] + for player_index in range(self.num_players)] + return out @update_progress_bar - def _build_payoff_diffs_means(self): - """ - Returns: - -------- - - The score differences between players. - List of the form: - - [ML1, ML2, ML3..., MLn] - - Where n is the number of players and MLi is a list of the form: - - [pi1, pi2, pi3, ..., pim] - - Where pij is the mean difference of the - scores per turn between player i and j in repetition m. - """ - payoff_diffs_means = [[mean(diff) for diff in player] - for player in self.score_diffs] - return payoff_diffs_means + def _build_cooperation(self, cooperation_series): + cooperation_dict = cooperation_series.to_dict() + cooperation = [] + for player_index in range(self.num_players): + row = [] + for opponent_index in range(self.num_players): + count = cooperation_dict.get((player_index, opponent_index),0) + if player_index == opponent_index: + # Address double count + count = int(count / 2) + row.append(count) + cooperation.append(row) + return cooperation @update_progress_bar - def _build_payoff_stddevs(self): - """ - Returns: - -------- - - The mean of per turn payoffs. - List of the form: - - [ML1, ML2, ML3..., MLn] - - Where n is the number of players and MLi is a list of the form: - - [pi1, pi2, pi3, ..., pim] - - Where m is the number of players and pij is a list of the form: - - [uij1, uij2, ..., uijk] - - Where k is the number of repetitions and u is the standard - deviation of the utility (over all repetitions) obtained by player - i against player j. - """ - plist = list(range(self.nplayers)) - payoff_stddevs = [[[0] for opponent in plist] for player in plist] - - for player in plist: - for opponent in plist: - utilities = self.payoffs[player][opponent] - - if utilities: - payoff_stddevs[player][opponent] = std(utilities) + def _build_good_partner_matrix(self, good_partner_series): + good_partner_dict = good_partner_series.to_dict() + good_partner_matrix = [] + for player_index in range(self.num_players): + row = [] + for opponent_index in range(self.num_players): + if player_index == opponent_index: + # The reduce operation implies a double count of self + # interactions. + row.append(0) else: - payoff_stddevs[player][opponent] = 0 - - return payoff_stddevs + row.append(good_partner_dict.get( + (player_index, opponent_index), 0)) + good_partner_matrix.append(row) + return good_partner_matrix @update_progress_bar - def _build_payoff_matrix(self): - """ - Returns: - -------- - The mean of per turn payoffs. - List of the form: - - [ML1, ML2, ML3..., MLn] - - Where n is the number of players and MLi is a list of the form: - - [pi1, pi2, pi3, ..., pim] + def _build_summary_matrix(self, attribute, func=np.mean): + matrix = [[0 for opponent_index in range(self.num_players)] + for player_index in range(self.num_players)] - Where m is the number of players and pij is a list of the form: + pairs = itertools.product(range(self.num_players), repeat=2) - [uij1, uij2, ..., uijk] + for player_index, opponent_index in pairs: + utilities = attribute[player_index][opponent_index] + if utilities: + matrix[player_index][opponent_index] = func(utilities) - Where k is the number of repetitions and u is the mean utility (over - all repetitions) obtained by player i against player j. - """ - plist = list(range(self.nplayers)) - payoff_matrix = [[[] for opponent in plist] for player in plist] - - for player in plist: - for opponent in plist: - utilities = self.payoffs[player][opponent] - - if utilities: - payoff_matrix[player][opponent] = mean(utilities) - else: - payoff_matrix[player][opponent] = 0 - - return payoff_matrix + return matrix @update_progress_bar - def _build_ranked_names(self): - """ - Returns: - -------- - Returns the ranked names. A list of names as calculated by - self.ranking. - """ + def _build_payoff_diffs_means(self): + payoff_diffs_means = [[np.mean(diff) for diff in player] + for player in self.score_diffs] - return [str(self.players[i]) for i in self.ranking] + return payoff_diffs_means @update_progress_bar - def _build_ranking(self): - """ - Returns: - -------- - - The ranking. List of the form: - - [R1, R2, R3..., Rn] - - Where n is the number of players and Rj is the rank of the jth player - (based on median normalised score). - """ - return sorted(range(self.nplayers), - key=lambda i: -nanmedian(self.normalised_scores[i])) + def _build_state_distribution(self, state_distribution_series): + state_key_map = {'CC count': (C, C), + 'CD count': (C, D), + 'DC count': (D, C), + 'DD count': (D, D)} + state_distribution = [[create_counter_dict(state_distribution_series, + player_index, + opponent_index, + state_key_map) + for opponent_index in range(self.num_players)] + for player_index in range(self.num_players)] + return state_distribution @update_progress_bar def _build_normalised_state_distribution(self): @@ -349,15 +290,35 @@ def _build_normalised_state_distribution(self): Dictionary where the keys are the states and the values are a normalized counts of the number of times that state occurs. """ - norm = [] + normalised_state_distribution = [] for player in self.state_distribution: counters = [] for counter in player: total = sum(counter.values()) counters.append(Counter({key: value / total for key, value in counter.items()})) - norm.append(counters) - return norm + normalised_state_distribution.append(counters) + return normalised_state_distribution + + @update_progress_bar + def _build_state_to_action_distribution(self, + state_to_action_distribution_series): + state_to_action_key_map = {"CC to C count": ((C, C), C), + "CC to D count": ((C, C), D), + "CD to C count": ((C, D), C), + "CD to D count": ((C, D), D), + "DC to C count": ((D, C), C), + "DC to D count": ((D, C), D), + "DD to C count": ((D, D), C), + "DD to D count": ((D, D), D)} + state_to_action_distribution = [[ + create_counter_dict(state_to_action_distribution_series, + player_index, + opponent_index, + state_to_action_key_map) + for opponent_index in range(self.num_players)] + for player_index in range(self.num_players)] + return state_to_action_distribution @update_progress_bar def _build_normalised_state_to_action_distribution(self): @@ -372,7 +333,7 @@ def _build_normalised_state_to_action_distribution(self): normalized counts of the number of times that state goes to a given action. """ - norm = [] + normalised_state_to_action_distribution = [] for player in self.state_to_action_distribution: counters = [] for counter in player: @@ -384,385 +345,194 @@ def _build_normalised_state_to_action_distribution(self): if counter[(state, action)] > 0: norm_counter[(state, action)] = counter[(state, action)] / total counters.append(norm_counter) - norm.append(counters) - return norm - - def _build_empty_metrics(self, keep_interactions=False): - """ - Creates the various empty metrics ready to be updated as the data is - read. - - Parameters - ---------- - - keep_interactions : bool - Whether or not to load the interactions in to memory - """ - plist = range(self.nplayers) - replist = range(self.repetitions) - self.match_lengths = [[[0 for opponent in plist] for player in plist] - for _ in replist] - self.wins = [[0 for _ in replist] for player in plist] - self.scores = [[0 for _ in replist] for player in plist] - self.normalised_scores = [[[] for _ in replist] for player in plist] - self.payoffs = [[[] for opponent in plist] for player in plist] - self.score_diffs = [[[0] * self.repetitions for opponent in plist] - for player in plist] - self.cooperation = [[0 for opponent in plist] for player in plist] - self.normalised_cooperation = [[[] for opponent in plist] - for player in plist] - self.initial_cooperation_count = [0 for player in plist] - self.state_distribution = [[Counter() for opponent in plist] - for player in plist] - self.state_to_action_distribution = [[Counter() for opponent in plist] - for player in plist] - self.good_partner_matrix = [[0 for opponent in plist] - for player in plist] - - self.total_interactions = [0 for player in plist] - self.good_partner_rating = [0 for player in plist] - - if keep_interactions: - self.interactions = {} - - def _update_match_lengths(self, repetition, p1, p2, interaction): - """ - During a read of the data, update the match lengths attribute - - Parameters - ---------- - - repetition : int - The index of the repetition - p1, p2 : int - The indices of the first and second player - interaction : list - A list of tuples of interactions - """ - self.match_lengths[repetition][p1][p2] = len(interaction) - - def _update_payoffs(self, p1, p2, scores_per_turn): - """ - During a read of the data, update the payoffs attribute - - Parameters - ---------- - - p1, p2 : int - The indices of the first and second player - scores_per_turn : tuples - A 2-tuple of the scores per turn for a given match - """ - self.payoffs[p1][p2].append(scores_per_turn[0]) - if p1 != p2: - self.payoffs[p2][p1].append(scores_per_turn[1]) - - def _update_score_diffs(self, repetition, p1, p2, scores_per_turn): - """ - During a read of the data, update the score diffs attribute - - Parameters - ---------- + normalised_state_to_action_distribution.append(counters) + return normalised_state_to_action_distribution - p1, p2 : int - The indices of the first and second player - scores_per_turn : tuples - A 2-tuple of the scores per turn for a given match - """ - diff = scores_per_turn[0] - scores_per_turn[1] - self.score_diffs[p1][p2][repetition] = diff - self.score_diffs[p2][p1][repetition] = -diff - - def _update_normalised_cooperation(self, p1, p2, interaction): - """ - During a read of the data, update the normalised cooperation attribute - - Parameters - ---------- - - p1, p2 : int - The indices of the first and second player - interaction : list of tuples - A list of interactions - """ - normalised_cooperations = iu.compute_normalised_cooperation(interaction) - - self.normalised_cooperation[p1][p2].append(normalised_cooperations[0]) - self.normalised_cooperation[p2][p1].append(normalised_cooperations[1]) - - def _update_wins(self, repetition, p1, p2, interaction): - """ - During a read of the data, update the wins attribute - - Parameters - ---------- - - repetition : int - The index of a repetition - p1, p2 : int - The indices of the first and second player - interaction : list of tuples - A list of interactions - """ - match_winner_index = iu.compute_winner_index(interaction, - game=self.game) - index_pair = [p1, p2] - if match_winner_index is not False: - winner_index = index_pair[match_winner_index] - self.wins[winner_index][repetition] += 1 - - def _update_scores(self, repetition, p1, p2, interaction): - """ - During a read of the data, update the scores attribute + @update_progress_bar + def _build_initial_cooperation_count(self, initial_cooperation_count_series): + initial_cooperation_count_dict = initial_cooperation_count_series.to_dict() + initial_cooperation_count = [initial_cooperation_count_dict.get(player_index, 0) + for player_index in + range(self.num_players)] + return initial_cooperation_count - Parameters - ---------- + @update_progress_bar + def _build_normalised_cooperation(self): + normalised_cooperation = [list(np.nan_to_num(row)) + for row in np.array(self.cooperation) / + sum(map(np.array, self.match_lengths))] + return normalised_cooperation - repetition : int - The index of a repetition - p1, p2 : int - The indices of the first and second player - interaction : list of tuples - A list of interactions - """ - final_scores = iu.compute_final_score(interaction, game=self.game) - for index, player in enumerate([p1, p2]): - player_score = final_scores[index] - self.scores[player][repetition] += player_score + @update_progress_bar + def _build_initial_cooperation_rate(self, interactions_series): + interactions_dict = interactions_series.to_dict() + interactions_array = np.array([interactions_series.get(player_index, 0) + for player_index in range(self.num_players)]) + initial_cooperation_rate = list( + np.nan_to_num(np.array(self.initial_cooperation_count) / + interactions_array)) + return initial_cooperation_rate - def _update_normalised_scores(self, repetition, p1, p2, scores_per_turn): - """ - During a read of the data, update the normalised scores attribute + @update_progress_bar + def _build_ranking(self): + ranking = sorted( + range(self.num_players), + key=lambda i: -np.nanmedian(self.normalised_scores[i])) + return ranking - Parameters - ---------- + @update_progress_bar + def _build_ranked_names(self): + ranked_names = [str(self.players[i]) for i in self.ranking] + return ranked_names - repetition : int - The index of a repetition - p1, p2 : int - The indices of the first and second player - scores_per_turn : tuple - A 2 tuple with the scores per turn of each player + @update_progress_bar + def _build_eigenmoses_rating(self): """ - for index, player in enumerate([p1, p2]): - score_per_turn = scores_per_turn[index] - self.normalised_scores[player][repetition].append(score_per_turn) + Returns: + -------- - def _update_cooperation(self, p1, p2, cooperations): + The eigenmoses rating as defined in: + http://www.scottaaronson.com/morality.pdf """ - During a read of the data, update the cooperation attribute + eigenvector, eigenvalue = eigen.principal_eigenvector( + self.vengeful_cooperation) - Parameters - ---------- + return eigenvector.tolist() - p1, p2 : int - The indices of the first and second player - cooperations : tuple - A 2 tuple with the count of cooperation each player + @update_progress_bar + def _build_eigenjesus_rating(self): """ - self.cooperation[p1][p2] += cooperations[0] - self.cooperation[p2][p1] += cooperations[1] + Returns: + -------- - def _update_initial_cooperation_count(self, p1, p2, initial_cooperations): + The eigenjesus rating as defined in: + http://www.scottaaronson.com/morality.pdf """ - During a read of the data, update the initial cooperation count - attribute - - Parameters - ---------- + eigenvector, eigenvalue = eigen.principal_eigenvector( + self.normalised_cooperation) - p1, p2 : int - The indices of the first and second player - initial_cooperations : tuple - A 2 tuple with a 0 or 1 indicating if the initial move of each - player was Cooperation (0) or Defection (1), e.g. (0, 1) for a - round (C, D) - """ - self.initial_cooperation_count[p1] += initial_cooperations[0] - self.initial_cooperation_count[p2] += initial_cooperations[1] + return eigenvector.tolist() - def _update_state_distribution(self, p1, p2, counter): + @update_progress_bar + def _build_cooperating_rating(self): """ - During a read of the data, update the state_distribution attribute - - Parameters - ---------- + Returns: + -------- - p1, p2 : int - The indices of the first and second player - counter : collections.Counter - A counter object for the states of a match - """ - self.state_distribution[p1][p2] += counter + The list of cooperation ratings + List of the form: - counter[(C, D)], counter[(D, C)] = counter[(D, C)], counter[(C, D)] - self.state_distribution[p2][p1] += counter + [ML1, ML2, ML3..., MLn] - def _update_state_to_action_distribution(self, p1, p2, counter_list): - """ - During a read of the data, update the state_distribution attribute + Where n is the number of players and MLi is a list of the form: - Parameters - ---------- + [pi1, pi2, pi3, ..., pim] - p1, p2 : int - The indices of the first and second player - counter_list : list of collections.Counter - A list of counter objects for the states to action of a match + Where pij is the total number of cooperations divided by the total + number of turns over all repetitions played by player i against + player j. """ - counter = counter_list[0] - self.state_to_action_distribution[p1][p2] += counter - - counter = counter_list[1] - for act in [C, D]: - counter[((C, D), act)], counter[((D, C), act)] = counter[((D, C), act)], counter[((C, D), act)] - self.state_to_action_distribution[p2][p1] += counter - def _update_good_partner_matrix(self, p1, p2, cooperations): - """ - During a read of the data, update the good partner matrix attribute + plist = list(range(self.num_players)) + total_length_v_opponent = [zip(*[rep[player_index] for + rep in self.match_lengths]) + for player_index in plist] + lengths = [[sum(e) for j, e in enumerate(row) if i != j] for i, row in + enumerate(total_length_v_opponent)] - Parameters - ---------- + cooperation = [[col for j, col in enumerate(row) if i != j] + for i, row in enumerate(self.cooperation)] + # Max is to deal with edge cases of matches that have no turns + cooperating_rating = [sum(cs) / max(1, sum(ls)) + for cs, ls in zip(cooperation, lengths)] + return cooperating_rating - p1, p2 : int - The indices of the first and second player - cooperations : tuple - A 2 tuple with the count of cooperation each player + @update_progress_bar + def _build_vengeful_cooperation(self): """ - if cooperations[0] >= cooperations[1]: - self.good_partner_matrix[p1][p2] += 1 - if cooperations[1] >= cooperations[0]: - self.good_partner_matrix[p2][p1] += 1 + Returns: + -------- - def _summarise_normalised_scores(self): - """ - At the end of a read of the data, finalise the normalised scores - """ - for i, rep in enumerate(self.normalised_scores): - for j, player_scores in enumerate(rep): - if player_scores != []: - self.normalised_scores[i][j] = mean(player_scores) - else: - self.normalised_scores[i][j] = 0 - try: - self.progress_bar.update() - except AttributeError: - pass + The vengeful cooperation matrix derived from the + normalised cooperation matrix: - def _summarise_normalised_cooperation(self): - """ - At the end of a read of the data, finalise the normalised cooperation + Dij = 2(Cij - 0.5) """ - for i, rep in enumerate(self.normalised_cooperation): - for j, cooperation in enumerate(rep): - if cooperation != []: - self.normalised_cooperation[i][j] = mean(cooperation) - else: - self.normalised_cooperation[i][j] = 0 - try: - self.progress_bar.update() - except AttributeError: - pass + vengeful_cooperation = [[2 * (element - 0.5) for element in row] + for row in self.normalised_cooperation] + return vengeful_cooperation @update_progress_bar - def _build_good_partner_rating(self): + def _build_good_partner_rating(self, interactions_series): """ At the end of a read of the data, build the good partner rating attribute """ - return [sum(self.good_partner_matrix[player]) / - max(1, self.total_interactions[player]) - for player in range(self.nplayers)] + interactions_dict = interactions_series.to_dict() + good_partner_rating = [sum(self.good_partner_matrix[player]) / + max(1, interactions_dict.get(player, 0)) + for player in range(self.num_players)] + return good_partner_rating - @update_progress_bar - def _build_initial_cooperation_rate(self): - """ - At the end of a read of the data, build the normalised initial - cooperation rate attribute + def _compute_tasks(self, tasks, processes): """ - return [self.initial_cooperation_count[player] / - max(1, self.total_interactions[player]) - for player in range(self.nplayers)] - - def _build_score_related_metrics(self, progress_bar=False, - keep_interactions=False): + Compute all dask tasks """ - Read the data and carry out all relevant calculations. - - Parameters - ---------- - progress_bar : bool - Whether or not to display a progress bar - keep_interactions : bool - Whether or not to lad the interactions in to memory - """ - match_chunks = self.read_match_chunks(progress_bar) - - for match in match_chunks: - p1, p2 = int(match[0][0]), int(match[0][1]) - - for repetition, record in enumerate(match): - interaction = record[4:] - - if keep_interactions: - try: - self.interactions[(p1, p2)].append(interaction) - except KeyError: - self.interactions[(p1, p2)] = [interaction] - - scores_per_turn = iu.compute_final_score_per_turn(interaction, - game=self.game) - cooperations = iu.compute_cooperations(interaction) - state_counter = iu.compute_state_distribution(interaction) - - self._update_match_lengths(repetition, p1, p2, interaction) - self._update_payoffs(p1, p2, scores_per_turn) - self._update_score_diffs(repetition, p1, p2, scores_per_turn) - self._update_normalised_cooperation(p1, p2, interaction) - - if p1 != p2: # Anything that ignores self interactions - state_to_actions = iu.compute_state_to_action_distribution(interaction) - - for player in [p1, p2]: - self.total_interactions[player] += 1 - - self._update_match_lengths(repetition, p2, p1, interaction) - self._update_wins(repetition, p1, p2, interaction) - self._update_scores(repetition, p1, p2, interaction) - self._update_normalised_scores(repetition, p1, p2, - scores_per_turn) - self._update_cooperation(p1, p2, cooperations) - initial_coops = iu.compute_cooperations(interaction[:1]) - self._update_initial_cooperation_count(p1, p2, - initial_coops) - self._update_state_distribution(p1, p2, state_counter) - self._update_state_to_action_distribution(p1, p2, - state_to_actions) - self._update_good_partner_matrix(p1, p2, cooperations) - - if progress_bar: - self.progress_bar = tqdm.tqdm(total=13 + 2 * self.nplayers, - desc="Finishing") - self._summarise_normalised_scores() - self._summarise_normalised_cooperation() - - self.ranking = self._build_ranking() - self.normalised_state_distribution = self._build_normalised_state_distribution() - self.normalised_state_to_action_distribution = self._build_normalised_state_to_action_distribution() - self.ranked_names = self._build_ranked_names() - self.payoff_matrix = self._build_payoff_matrix() - self.payoff_stddevs = self._build_payoff_stddevs() - self.payoff_diffs_means = self._build_payoff_diffs_means() - self.vengeful_cooperation = self._build_vengeful_cooperation() - self.cooperating_rating = self._build_cooperating_rating() - self.initial_cooperation_rate = self._build_initial_cooperation_rate() - self.good_partner_rating = self._build_good_partner_rating() - self.eigenjesus_rating = self._build_eigenjesus_rating() - self.eigenmoses_rating = self._build_eigenmoses_rating() - - if progress_bar: - self.progress_bar.close() + if processes is None: + out = da.compute(*tasks, get=da.get) + else: + out = da.compute(*tasks, num_workers=processes) + return out + + def _build_tasks(self, df): + """ + Returns a tuple of dask tasks + """ + groups = ["Repetition", "Player index", "Opponent index"] + columns = ["Turns", "Score per turn", "Score difference per turn"] + mean_per_reps_player_opponent_task = df.groupby(groups)[columns].mean() + + groups = ["Player index", "Opponent index"] + columns = ["Cooperation count", + "CC count", + "CD count", + "DC count", + "DD count", + "CC to C count", + "CC to D count", + "CD to C count", + "CD to D count", + "DC to C count", + "DC to D count", + "DD to C count", + "DD to D count", + "Good partner"] + sum_per_player_opponent_task = df.groupby(groups)[columns].sum() + + ignore_self_interactions_task = df["Player index"] != df["Opponent index"] + adf = df[ignore_self_interactions_task] + + groups = ["Player index", "Repetition"] + columns = ["Win", "Score"] + sum_per_player_repetition_task = adf.groupby(groups)[columns].sum() + + groups = ["Player index", "Repetition"] + column = "Score per turn" + normalised_scores_task = adf.groupby(groups)[column].mean() + + + groups = ["Player index"] + column = "Initial cooperation" + initial_cooperation_count_task = adf.groupby(groups)[column].sum() + interactions_count_task = adf.groupby("Player index")["Player index"].count() + + + return (mean_per_reps_player_opponent_task, + sum_per_player_opponent_task, + sum_per_player_repetition_task, + normalised_scores_task, + initial_cooperation_count_task, + interactions_count_task) def __eq__(self, other): """ @@ -821,8 +591,8 @@ def summarise(self): """ - median_scores = map(nanmedian, self.normalised_scores) - median_wins = map(nanmedian, self.wins) + median_scores = map(np.nanmedian, self.normalised_scores) + median_wins = map(np.nanmedian, self.wins) self.player = namedtuple("Player", ["Rank", "Name", "Median_score", "Cooperation_rating", "Wins", @@ -852,7 +622,7 @@ def summarise(self): if counter[(state, C)] > 0] if len(counts) > 0: - rate = mean(counts) + rate = np.mean(counts) else: rate = 0 @@ -888,204 +658,29 @@ def write_summary(self, filename): for player in summary_data: writer.writerow(player) - def read_match_chunks(self, progress_bar=False): - """ - A generator to return a given repetitions of matches - - Parameters - ---------- - - progress_bar : bool - whether or not to display a progress bar - Yields - ------ - repetitions : list - A list of lists include index pairs, player pairs and - repetitions. All repetitions for a given pair are yielded - together. - """ - - if progress_bar: - progress_bar = self.create_progress_bar(desc="Analysing") - - for match_pair, interactions in self.interactions.items(): - players_pair = [self.players[i] for i in match_pair] - repetitions = [list(match_pair) + players_pair + rep for rep in - interactions] - if progress_bar: - progress_bar.update() - yield repetitions - - if progress_bar: - progress_bar.close() - - def _read_players_and_repetition_numbers(self, progress_bar=False): - """ - Read the players and the repetitions numbers - - Parameters - ---------- - progress_bar : bool - Whether or not to display a progress bar - """ - - if progress_bar: - progress_bar = self.create_progress_bar(desc="Counting") - - self.players_d = {} - self.repetitions_d = {} - for index_pair, interactions in self.interactions.items(): - players = [self.players[i] for i in index_pair] - self._update_repetitions(index_pair, len(interactions)) - self._update_players(index_pair, players) - if progress_bar: - progress_bar.update() - - if progress_bar: - progress_bar.close() - - repetitions = self._build_repetitions() - players = self._build_players() - - return players, repetitions - - -class ResultSetFromFile(ResultSet): +def create_counter_dict(df, player_index, opponent_index, key_map): """ - A class to hold the results of a tournament. Reads in a CSV file produced - by the tournament class. + Create a Counter object mapping states (corresponding to columns of df) for + players given by player_index, opponent_index. Renaming the variables with + `key_map`. Used by `ResultSet._reshape_out` + + Parameters + ---------- + df : a multiindex pandas df + player_index: int + opponent_index: int + key_map : a dict + maps cols of df to strings + + Returns + ------- + A counter dictionary """ - - def __init__(self, filename, progress_bar=True, - num_interactions=False, players=False, repetitions=False, - game=None, keep_interactions=False): - """ - Parameters - ---------- - filename : string - the file from which to read the interactions - progress_bar : bool - Whether or not to create a progress bar which will be updated - num_interactions : int - The number of interactions in the file. Used for the progress - bar. If not known but progress_bar is true, will be efficiently - read from file. - players : list - A list of the names of players. If not known will be efficiently - read from file. - repetitions : int - The number of repetitions of each match. If not know will be - efficiently read from file. - game : axelrod.Game - The particular game that should be used to calculate the scores. - keep_interactions : bool - Whether or not to load the interactions in to memory. WARNING: - for large tournaments this drastically increases the memory - required. - """ - if game is None: - self.game = Game() - else: - self.game = game - - self.filename = filename - self.num_interactions = num_interactions - - if not players and not repetitions: - self.players, self.repetitions = self._read_players_and_repetition_numbers(progress_bar=progress_bar) - else: - self.players, self.repetitions = players, repetitions - self.nplayers = len(self.players) - - self._build_empty_metrics(keep_interactions=keep_interactions) - self._build_score_related_metrics(progress_bar=progress_bar, - keep_interactions=keep_interactions) - - def create_progress_bar(self, desc=None): - """ - Create a progress bar for a read through of the data file. - - Parameters - ---------- - desc : string - A description. - """ - if not self.num_interactions: - with open(self.filename) as f: - self.num_interactions = sum(1 for line in f) - return tqdm.tqdm(total=self.num_interactions, desc=desc) - - def _read_players_and_repetition_numbers(self, progress_bar=False): - """ - Read the players and the repetitions numbers - - Parameters - ---------- - progress_bar : bool - Whether or not to display a progress bar - """ - - if progress_bar: - progress_bar = self.create_progress_bar(desc="Counting") - - self.players_d = {} - self.repetitions_d = {} - with open(self.filename, 'r') as f: - for row in csv.reader(f): - index_pair = (int(row[0]), int(row[1])) - players = (row[2], row[3]) - self._update_repetitions(index_pair) - self._update_players(index_pair, players) - if progress_bar: - progress_bar.update() - - if progress_bar: - progress_bar.close() - - repetitions = self._build_repetitions() - players = self._build_players() - - return players, repetitions - - def read_match_chunks(self, progress_bar=False): - """ - A generator to return a given repetitions of matches - - Parameters - ---------- - - progress_bar : bool - whether or not to display a progress bar - - Yields - ------ - repetitions : list - A list of lists include index pairs, player pairs and - repetitions. All repetitions for a given pair are yielded - together. - """ - - if progress_bar: - progress_bar = self.create_progress_bar(desc="Analysing") - - with open(self.filename, 'r') as f: - csv_reader = csv.reader(f) - repetitions = [] - count = 0 - for row in csv_reader: - index_and_names = row[:4] - p1_actions = str_to_actions(row[4]) - p2_actions = str_to_actions(row[5]) - interactions = list(zip(p1_actions, p2_actions)) - repetitions.append(index_and_names + interactions) - count += 1 - if progress_bar: - progress_bar.update() - if count == self.repetitions: - yield repetitions - repetitions = [] - count = 0 - - if progress_bar: - progress_bar.close() + counter = Counter() + if player_index != opponent_index: + if (player_index, opponent_index) in df.index: + for key, value in df.loc[player_index, opponent_index].items(): + if value > 0: + counter[key_map[key]] = value + return counter diff --git a/axelrod/tests/unit/test_ecosystem.py b/axelrod/tests/unit/test_ecosystem.py index 11626ee4d..67536f830 100644 --- a/axelrod/tests/unit/test_ecosystem.py +++ b/axelrod/tests/unit/test_ecosystem.py @@ -30,7 +30,7 @@ def test_init(self): # By default create populations of equal size eco = axelrod.Ecosystem(self.res_cooperators) pops = eco.population_sizes - self.assertEqual(eco.nplayers, 4) + self.assertEqual(eco.num_players, 4) self.assertEqual(len(pops), 1) self.assertEqual(len(pops[0]), 4) self.assertAlmostEqual(sum(pops[0]), 1.0) @@ -39,7 +39,7 @@ def test_init(self): # Can pass list of initial population distributions eco = axelrod.Ecosystem(self.res_cooperators, population=[.7, .25, .03, .02]) pops = eco.population_sizes - self.assertEqual(eco.nplayers, 4) + self.assertEqual(eco.num_players, 4) self.assertEqual(len(pops), 1) self.assertEqual(len(pops[0]), 4) self.assertAlmostEqual(sum(pops[0]), 1.0) @@ -48,7 +48,7 @@ def test_init(self): # Distribution will automatically normalise eco = axelrod.Ecosystem(self.res_cooperators, population=[70, 25, 3, 2]) pops = eco.population_sizes - self.assertEqual(eco.nplayers, 4) + self.assertEqual(eco.num_players, 4) self.assertEqual(len(pops), 1) self.assertEqual(len(pops[0]), 4) self.assertAlmostEqual(sum(pops[0]), 1.0) diff --git a/axelrod/tests/unit/test_fingerprint.py b/axelrod/tests/unit/test_fingerprint.py index f925ad477..7bd31faf7 100644 --- a/axelrod/tests/unit/test_fingerprint.py +++ b/axelrod/tests/unit/test_fingerprint.py @@ -187,19 +187,11 @@ def test_temp_file_creation(self): af = AshlockFingerprint(self.strategy, self.probe) filename = "test_outputs/test_fingerprint.csv" - # No temp file is created. - af.fingerprint(turns=1, repetitions=1, step=0.5, progress_bar=False, - in_memory=True) - af.fingerprint(turns=1, repetitions=1, step=0.5, progress_bar=False, - in_memory=True, filename=filename) - af.fingerprint(turns=1, repetitions=1, step=0.5, progress_bar=False, - in_memory=False, filename=filename) - self.assertEqual(RecordedMksTemp.record, []) # Temp file is created and destroyed. af.fingerprint(turns=1, repetitions=1, step=0.5, progress_bar=False, - in_memory=False, filename=None) + filename=None) self.assertEqual(len(RecordedMksTemp.record), 1) filename = RecordedMksTemp.record[0][1] @@ -214,19 +206,7 @@ def test_fingerprint_with_filename(self): filename=filename) with open(filename, 'r') as out: data = out.read() - self.assertEqual(len(data.split("\n")), 10) - - def test_in_memory_fingerprint(self): - af = AshlockFingerprint(self.strategy, self.probe) - af.fingerprint(turns=10, repetitions=2, step=0.5, progress_bar=False, - in_memory=True) - edge_keys = sorted(list(af.interactions.keys())) - coord_keys = sorted(list(af.data.keys())) - self.assertEqual(af.step, 0.5) - self.assertEqual(af.spatial_tournament.interactions_dict, - af.interactions) - self.assertEqual(edge_keys, self.expected_edges) - self.assertEqual(coord_keys, self.expected_points) + self.assertEqual(len(data.split("\n")), 20) def test_serial_fingerprint(self): af = AshlockFingerprint(self.strategy, self.probe) @@ -431,7 +411,7 @@ def test_fingerprint_with_filename(self): filename=filename) with open(filename, 'r') as out: data = out.read() - self.assertEqual(len(data.split("\n")), 50 + 1) + self.assertEqual(len(data.split("\n")), 102) def test_serial_fingerprint(self): strategy = axl.TitForTat() @@ -452,14 +432,23 @@ def test_analyse_cooperation_ratio(self): filename = "test_outputs/test_fingerprint.csv" with open(filename, "w") as f: f.write( -"""0,1,Player0,Player1,CCC,DDD -0,1,Player0,Player1,CCC,DDD -0,2,Player0,Player2,CCD,DDD -0,2,Player0,Player2,CCC,DDD -0,3,Player0,Player3,CCD,DDD -0,3,Player0,Player3,DCC,DDD -0,4,Player0,Player3,DDD,DDD -0,4,Player0,Player3,DDD,DDD""") +"""Interaction index,Player index,Opponent index,Repetition,Player name,Opponent name,Actions +0,0,1,0,Player0,Player1,CCC +0,1,0,0,Player1,Player0,DDD +1,0,1,1,Player0,Player1,CCC +1,1,0,1,Player1,Player0,DDD +2,0,2,0,Player0,Player2,CCD +2,2,0,0,Player2,Player0,DDD +3,0,2,1,Player0,Player2,CCC +3,2,0,1,Player2,Player0,DDD +4,0,3,0,Player0,Player3,CCD +4,3,0,0,Player3,Player0,DDD +5,0,3,1,Player0,Player3,DCC +5,3,0,1,Player3,Player0,DDD +6,0,4,2,Player0,Player4,DDD +6,4,0,2,Player4,Player0,DDD +7,0,4,3,Player0,Player4,DDD +7,4,0,3,Player4,Player0,DDD""") data = tf.analyse_cooperation_ratio(filename) expected_data = np.array([[1, 1, 1], [1, 1, 1 / 2], diff --git a/axelrod/tests/unit/test_plot.py b/axelrod/tests/unit/test_plot.py index 4a31fe95f..767393fe1 100644 --- a/axelrod/tests/unit/test_plot.py +++ b/axelrod/tests/unit/test_plot.py @@ -12,33 +12,21 @@ class TestPlot(unittest.TestCase): @classmethod def setUpClass(cls): - cls.players = ( - axelrod.Alternator(), axelrod.TitForTat(), axelrod.Defector()) + cls.filename = "test_outputs/test_results.csv" + + cls.players = [axelrod.Alternator(), axelrod.TitForTat(), + axelrod.Defector()] + cls.repetitions = 3 cls.turns = 5 - cls.matches = { - (0, 1): [axelrod.Match( - (cls.players[0], cls.players[1]), turns=cls.turns) - for _ in range(3)], - (0, 2): [axelrod.Match( - (cls.players[0], cls.players[2]), turns=cls.turns) - for _ in range(3)], - (1, 2): [axelrod.Match( - (cls.players[1], cls.players[2]), turns=cls.turns) - for _ in range(3)] - } - # This would not actually be a round robin tournament - # (no cloned matches) - - cls.interactions = {} - for index_pair, matches in cls.matches.items(): - for match in matches: - match.play() - try: - cls.interactions[index_pair].append(match.result) - except KeyError: - cls.interactions[index_pair] = [match.result] - - cls.test_result_set = axelrod.ResultSet(cls.players, cls.interactions, + + cls.test_result_set = axelrod.ResultSet(cls.filename, + cls.players, + cls.repetitions, + progress_bar=False) + + cls.test_result_set = axelrod.ResultSet(cls.filename, + cls.players, + cls.repetitions, progress_bar=False) cls.expected_boxplot_dataset = [ [(17 / 5 + 9 / 5) / 2 for _ in range(3)], @@ -110,15 +98,16 @@ def test_init(self): def test_init_from_resulsetfromfile(self): tmp_file = tempfile.NamedTemporaryFile(mode='w', delete=False) + players=[axelrod.Cooperator(), + axelrod.TitForTat(), + axelrod.Defector()] tournament = axelrod.Tournament( - players=[axelrod.Cooperator(), - axelrod.TitForTat(), - axelrod.Defector()], + players=players, turns=2, repetitions=2) tournament.play(filename=tmp_file.name, progress_bar=False) tmp_file.close() - rs = axelrod.ResultSetFromFile(tmp_file.name, progress_bar=False) + rs = axelrod.ResultSet(tmp_file.name, players, 2, progress_bar=False) plot = axelrod.Plot(rs) self.assertEqual(plot.result_set, rs) diff --git a/axelrod/tests/unit/test_resultset.py b/axelrod/tests/unit/test_resultset.py index a5a93adfb..9fa8fec73 100644 --- a/axelrod/tests/unit/test_resultset.py +++ b/axelrod/tests/unit/test_resultset.py @@ -4,9 +4,12 @@ from hypothesis import given, settings from numpy import mean, std, nanmedian +from dask.dataframe.core import DataFrame +import pandas as pd import axelrod import axelrod.interaction_utils as iu +from axelrod.result_set import create_counter_dict from axelrod.tests.property import tournaments, prob_end_tournaments @@ -22,30 +25,9 @@ def setUpClass(cls): cls.players = [axelrod.Alternator(), axelrod.TitForTat(), axelrod.Defector()] + cls.repetitions = 3 cls.turns = 5 - cls.matches = { - (0, 1): [axelrod.Match((cls.players[0], cls.players[1]), - turns=cls.turns) for _ in range(3)], - (0, 2): [axelrod.Match((cls.players[0], cls.players[2]), - turns=cls.turns) for _ in range(3)], - (1, 2): [axelrod.Match((cls.players[1], cls.players[2]), - turns=cls.turns) for _ in range(3)]} - # This would not actually be a round robin tournament - # (no cloned matches) - - cls.interactions = {} - for index_pair, matches in cls.matches.items(): - for match in matches: - match.play() - try: - cls.interactions[index_pair].append(match.result) - except KeyError: - cls.interactions[index_pair] = [match.result] - - cls.expected_players_to_match_dicts = { - 0: cls.matches[(0, 1)] + cls.matches[(0, 2)], - 1: cls.matches[(0, 1)] + cls.matches[(1, 2)], - 2: cls.matches[(1, 2)] + cls.matches[(0, 2)]} + cls.edges = [(0, 1), (0, 2), (1, 2)] cls.expected_match_lengths = [ [[0, 5, 5], [5, 0, 5], [5, 5, 0]] @@ -128,7 +110,7 @@ def setUpClass(cls): cls.expected_normalised_cooperation = [ [0, mean([3 / 5 for _ in range(3)]), - mean([3 / 5 for _ in range(3)])], + mean([3 / 5 for _ in range(3)])], [mean([3 / 5 for _ in range(3)]), 0, mean([1 / 5 for _ in range(3)])], [0, 0, 0], @@ -219,52 +201,31 @@ def setUpClass(cls): ] def test_init(self): - rs = axelrod.ResultSet(self.players, self.interactions, + rs = axelrod.ResultSet(self.filename, self.players, self.repetitions, progress_bar=False) self.assertEqual(rs.players, self.players) - self.assertEqual(rs.nplayers, len(self.players)) - self.assertEqual(rs.interactions, self.interactions) - for inter in self.interactions.values(): - self.assertEqual(rs.repetitions, len(inter)) - - # Test structure of matches - # This is really a test of the test - for index_pair, repetitions in rs.interactions.items(): - self.assertIsInstance(repetitions, list) - self.assertIsInstance(index_pair, tuple) - for interaction in repetitions: - self.assertIsInstance(interaction, list) - self.assertEqual(len(interaction), self.turns) - - def test_init_with_repetitions(self): - rs = axelrod.ResultSet(self.players, self.interactions, - repetitions=3, - progress_bar=False) + self.assertEqual(rs.num_players, len(self.players)) + + def test_init_multiprocessing(self): + rs = axelrod.ResultSet(self.filename, self.players, self.repetitions, + progress_bar=False, processes=2) self.assertEqual(rs.players, self.players) - self.assertEqual(rs.nplayers, len(self.players)) - self.assertEqual(rs.interactions, self.interactions) - self.assertEqual(rs.repetitions, 3) + self.assertEqual(rs.num_players, len(self.players)) - def test_init_with_different_game(self): - game = axelrod.Game(p=-1, r=-1, s=-1, t=-1) - rs = axelrod.ResultSet(self.players, self.interactions, - progress_bar=False, game=game) - self.assertEqual(rs.game.RPST(), (-1, -1, -1, -1)) + rs = axelrod.ResultSet(self.filename, self.players, self.repetitions, + progress_bar=False, processes=0) + self.assertEqual(rs.players, self.players) + self.assertEqual(rs.num_players, len(self.players)) def test_with_progress_bar(self): - rs = axelrod.ResultSet(self.players, self.interactions) - self.assertTrue(rs.progress_bar) - self.assertEqual(rs.progress_bar.total, 13 + 2 * rs.nplayers) - self.assertEqual(rs.progress_bar.n, rs.progress_bar.total) - - rs = axelrod.ResultSet(self.players, self.interactions, + rs = axelrod.ResultSet(self.filename, self.players, self.repetitions, progress_bar=True) self.assertTrue(rs.progress_bar) - self.assertEqual(rs.progress_bar.total, 13 + 2 * rs.nplayers) + self.assertEqual(rs.progress_bar.total, 25) self.assertEqual(rs.progress_bar.n, rs.progress_bar.total) def test_match_lengths(self): - rs = axelrod.ResultSet(self.players, self.interactions, + rs = axelrod.ResultSet(self.filename, self.players, self.repetitions, progress_bar=False) self.assertIsInstance(rs.match_lengths, list) self.assertEqual(len(rs.match_lengths), rs.repetitions) @@ -284,68 +245,62 @@ def test_match_lengths(self): else: self.assertEqual(length, self.turns) + def test_scores(self): - rs = axelrod.ResultSet(self.players, self.interactions, + rs = axelrod.ResultSet(self.filename, self.players, self.repetitions, progress_bar=False) self.assertIsInstance(rs.scores, list) - self.assertEqual(len(rs.scores), rs.nplayers) + self.assertEqual(len(rs.scores), rs.num_players) self.assertEqual(rs.scores, self.expected_scores) - def test_scores_with_different_game(self): - game = axelrod.Game(p=-1, r=-1, s=-1, t=-1) - rs = axelrod.ResultSet(self.players, self.interactions, - progress_bar=False, game=game) - for player in rs.scores: - for score in player: - self.assertFalse(score > 0) def test_ranking(self): - rs = axelrod.ResultSet(self.players, self.interactions, + rs = axelrod.ResultSet(self.filename, self.players, self.repetitions, progress_bar=False) self.assertIsInstance(rs.ranking, list) - self.assertEqual(len(rs.ranking), rs.nplayers) + self.assertEqual(len(rs.ranking), rs.num_players) self.assertEqual(rs.ranking, self.expected_ranking) def test_ranked_names(self): - rs = axelrod.ResultSet(self.players, self.interactions, + rs = axelrod.ResultSet(self.filename, self.players, self.repetitions, progress_bar=False) self.assertIsInstance(rs.ranked_names, list) - self.assertEqual(len(rs.ranked_names), rs.nplayers) + self.assertEqual(len(rs.ranked_names), rs.num_players) self.assertEqual(rs.ranked_names, self.expected_ranked_names) def test_wins(self): - rs = axelrod.ResultSet(self.players, self.interactions, + rs = axelrod.ResultSet(self.filename, self.players, self.repetitions, progress_bar=False) self.assertIsInstance(rs.wins, list) - self.assertEqual(len(rs.wins), rs.nplayers) + self.assertEqual(len(rs.wins), rs.num_players) self.assertEqual(rs.wins, self.expected_wins) def test_normalised_scores(self): - rs = axelrod.ResultSet(self.players, self.interactions, + rs = axelrod.ResultSet(self.filename, self.players, self.repetitions, progress_bar=False) self.assertIsInstance(rs.normalised_scores, list) - self.assertEqual(len(rs.normalised_scores), rs.nplayers) + self.assertEqual(len(rs.normalised_scores), rs.num_players) self.assertEqual(rs.normalised_scores, self.expected_normalised_scores) def test_payoffs(self): - rs = axelrod.ResultSet(self.players, self.interactions, + rs = axelrod.ResultSet(self.filename, self.players, self.repetitions, progress_bar=False) self.assertIsInstance(rs.payoffs, list) - self.assertEqual(len(rs.payoffs), rs.nplayers) + self.assertEqual(len(rs.payoffs), rs.num_players) self.assertEqual(rs.payoffs, self.expected_payoffs) def test_payoff_matrix(self): - rs = axelrod.ResultSet(self.players, self.interactions, + rs = axelrod.ResultSet(self.filename, self.players, self.repetitions, progress_bar=False) self.assertIsInstance(rs.payoff_matrix, list) - self.assertEqual(len(rs.payoff_matrix), rs.nplayers) + self.assertEqual(len(rs.payoff_matrix), rs.num_players) 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.filename, self.players, self.repetitions, progress_bar=False) self.assertIsInstance(rs.score_diffs, list) - self.assertEqual(len(rs.score_diffs), rs.nplayers) + self.assertEqual(len(rs.score_diffs), rs.num_players) for i, row in enumerate(rs.score_diffs): for j, col in enumerate(row): for k, score in enumerate(col): @@ -353,144 +308,152 @@ 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.filename, self.players, self.repetitions, progress_bar=False) self.assertIsInstance(rs.payoff_diffs_means, list) - self.assertEqual(len(rs.payoff_diffs_means), rs.nplayers) + self.assertEqual(len(rs.payoff_diffs_means), rs.num_players) for i, row in enumerate(rs.payoff_diffs_means): for j, col in enumerate(row): self.assertAlmostEqual(col, self.expected_payoff_diffs_means[i][j]) def test_payoff_stddevs(self): - rs = axelrod.ResultSet(self.players, self.interactions, + rs = axelrod.ResultSet(self.filename, self.players, self.repetitions, progress_bar=False) self.assertIsInstance(rs.payoff_stddevs, list) - self.assertEqual(len(rs.payoff_stddevs), rs.nplayers) + self.assertEqual(len(rs.payoff_stddevs), rs.num_players) self.assertEqual(rs.payoff_stddevs, self.expected_payoff_stddevs) def test_cooperation(self): - rs = axelrod.ResultSet(self.players, self.interactions, + rs = axelrod.ResultSet(self.filename, self.players, self.repetitions, progress_bar=False) self.assertIsInstance(rs.cooperation, list) - self.assertEqual(len(rs.cooperation), rs.nplayers) + self.assertEqual(len(rs.cooperation), rs.num_players) self.assertEqual(rs.cooperation, self.expected_cooperation) def test_initial_cooperation_count(self): - rs = axelrod.ResultSet(self.players, self.interactions, + rs = axelrod.ResultSet(self.filename, self.players, self.repetitions, progress_bar=False) self.assertIsInstance(rs.initial_cooperation_count, list) - self.assertEqual(len(rs.initial_cooperation_count), rs.nplayers) + self.assertEqual(len(rs.initial_cooperation_count), rs.num_players) self.assertEqual(rs.initial_cooperation_count, self.expected_initial_cooperation_count) def test_normalised_cooperation(self): - rs = axelrod.ResultSet(self.players, self.interactions, + rs = axelrod.ResultSet(self.filename, self.players, self.repetitions, 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) + self.assertEqual(len(rs.normalised_cooperation), rs.num_players) + for i, row in enumerate(rs.normalised_cooperation): + for j, col in enumerate(row): + self.assertAlmostEqual(col, + self.expected_normalised_cooperation[i][j]) def test_initial_cooperation_rate(self): - rs = axelrod.ResultSet(self.players, self.interactions, + rs = axelrod.ResultSet(self.filename, self.players, self.repetitions, progress_bar=False) self.assertIsInstance(rs.initial_cooperation_rate, list) - self.assertEqual(len(rs.initial_cooperation_rate), rs.nplayers) + self.assertEqual(len(rs.initial_cooperation_rate), rs.num_players) self.assertEqual(rs.initial_cooperation_rate, self.expected_initial_cooperation_rate) def test_state_distribution(self): - rs = axelrod.ResultSet(self.players, self.interactions, + rs = axelrod.ResultSet(self.filename, self.players, self.repetitions, progress_bar=False) self.assertIsInstance(rs.state_distribution, list) - self.assertEqual(len(rs.state_distribution), rs.nplayers) + self.assertEqual(len(rs.state_distribution), rs.num_players) self.assertEqual(rs.state_distribution, self.expected_state_distribution) def test_state_normalised_distribution(self): - rs = axelrod.ResultSet(self.players, self.interactions, + rs = axelrod.ResultSet(self.filename, self.players, self.repetitions, progress_bar=False) self.assertIsInstance(rs.normalised_state_distribution, list) - self.assertEqual(len(rs.normalised_state_distribution), rs.nplayers) + self.assertEqual(len(rs.normalised_state_distribution), rs.num_players) self.assertEqual(rs.normalised_state_distribution, self.expected_normalised_state_distribution) def test_state_to_action_distribution(self): - rs = axelrod.ResultSet(self.players, self.interactions, + rs = axelrod.ResultSet(self.filename, self.players, self.repetitions, progress_bar=False) self.assertIsInstance(rs.state_to_action_distribution, list) - self.assertEqual(len(rs.state_to_action_distribution), rs.nplayers) - self.assertEqual(rs.state_to_action_distribution, - self.expected_state_to_action_distribution) + self.assertEqual(len(rs.state_to_action_distribution), rs.num_players) + self.assertEqual(rs.state_to_action_distribution[1], + self.expected_state_to_action_distribution[1]) def test_normalised_state_to_action_distribution(self): - rs = axelrod.ResultSet(self.players, self.interactions, + rs = axelrod.ResultSet(self.filename, self.players, self.repetitions, progress_bar=False) self.assertIsInstance(rs.normalised_state_to_action_distribution, list) self.assertEqual(len(rs.normalised_state_to_action_distribution), - rs.nplayers) + rs.num_players) self.assertEqual(rs.normalised_state_to_action_distribution, self.expected_normalised_state_to_action_distribution) def test_vengeful_cooperation(self): - rs = axelrod.ResultSet(self.players, self.interactions, + rs = axelrod.ResultSet(self.filename, self.players, self.repetitions, 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) + self.assertEqual(len(rs.vengeful_cooperation), rs.num_players) + for i, row in enumerate(rs.vengeful_cooperation): + for j, col in enumerate(row): + self.assertAlmostEqual(col, + self.expected_vengeful_cooperation[i][j]) def test_cooperating_rating(self): - rs = axelrod.ResultSet(self.players, self.interactions, + rs = axelrod.ResultSet(self.filename, self.players, self.repetitions, progress_bar=False) self.assertIsInstance(rs.cooperating_rating, list) - self.assertEqual(len(rs.cooperating_rating), rs.nplayers) + self.assertEqual(len(rs.cooperating_rating), rs.num_players) 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.filename, self.players, self.repetitions, progress_bar=False) self.assertIsInstance(rs.good_partner_matrix, list) - self.assertEqual(len(rs.good_partner_matrix), rs.nplayers) + self.assertEqual(len(rs.good_partner_matrix), rs.num_players) 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.filename, self.players, self.repetitions, progress_bar=False) self.assertIsInstance(rs.good_partner_rating, list) - self.assertEqual(len(rs.good_partner_rating), rs.nplayers) + self.assertEqual(len(rs.good_partner_rating), rs.num_players) 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.filename, self.players, self.repetitions, progress_bar=False) self.assertIsInstance(rs.eigenjesus_rating, list) - self.assertEqual(len(rs.eigenjesus_rating), rs.nplayers) + self.assertEqual(len(rs.eigenjesus_rating), rs.num_players) 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.filename, self.players, self.repetitions, progress_bar=False) self.assertIsInstance(rs.eigenmoses_rating, list) - self.assertEqual(len(rs.eigenmoses_rating), rs.nplayers) + self.assertEqual(len(rs.eigenmoses_rating), rs.num_players) for j, rate in enumerate(rs.eigenmoses_rating): self.assertAlmostEqual(rate, self.expected_eigenmoses_rating[j]) def test_self_interaction_for_random_strategies(self): # Based on /~https://github.com/Axelrod-Python/Axelrod/issues/670 + # Note that the conclusion of #670 is incorrect and only includes one of + # the copies of the strategy. axelrod.seed(0) players = [s() for s in axelrod.demo_strategies] tournament = axelrod.Tournament(players, repetitions=2, turns=5) results = tournament.play(progress_bar=False) - self.assertEqual(results.payoff_diffs_means[-1][-1], 1.0) + self.assertEqual(results.payoff_diffs_means[-1][-1], 0.0) def test_equality(self): - rs_sets = [axelrod.ResultSet(self.players, self.interactions, + rs_sets = [axelrod.ResultSet(self.filename, + self.players, + self.repetitions, progress_bar=False) for _ in range(2)] self.assertEqual(rs_sets[0], rs_sets[1]) @@ -500,7 +463,7 @@ def test_equality(self): self.assertNotEqual(results, rs_sets[0]) def test_summarise(self): - rs = axelrod.ResultSet(self.players, self.interactions, + rs = axelrod.ResultSet(self.filename, self.players, self.repetitions, progress_bar=False) sd = rs.summarise() @@ -566,10 +529,10 @@ def test_summarise_regression_test(self): places=3) def test_write_summary(self): - rs = axelrod.ResultSet(self.players, self.interactions, + rs = axelrod.ResultSet(self.filename, self.players, self.repetitions, progress_bar=False) - rs.write_summary(filename=self.filename) - with open(self.filename, "r") as csvfile: + rs.write_summary(filename=self.filename + ".summary") + with open(self.filename + ".summary", "r") as csvfile: ranked_names = [] csvreader = csv.reader(csvfile) for row in csvreader: @@ -579,222 +542,6 @@ def test_write_summary(self): self.assertEqual(ranked_names[1:], rs.ranked_names) -class TestResultSetFromFile(unittest.TestCase): - filename = "test_outputs/test_results_from_file.csv" - players = [axelrod.Cooperator(), - axelrod.TitForTat(), - axelrod.Defector()] - tournament = axelrod.Tournament(players=players, turns=2, repetitions=3) - tournament.play(filename=filename, progress_bar=False) - - interactions = iu.read_interactions_from_file(filename, progress_bar=False) - - def test_init(self): - brs = axelrod.ResultSetFromFile(self.filename, progress_bar=False) - self.assertEqual(brs.players, [str(p) for p in self.players]) - self.assertEqual(brs.nplayers, len(self.players)) - self.assertEqual(brs.repetitions, 3) - - def test_init_with_different_game(self): - game = axelrod.Game(p=-1, r=-1, s=-1, t=-1) - brs = axelrod.ResultSetFromFile(self.filename, progress_bar=False, - game=game) - self.assertEqual(brs.game.RPST(), (-1, -1, -1, -1)) - - def test_init_with_progress_bar(self): - """Just able to test that no error occurs""" - brs = axelrod.ResultSetFromFile(self.filename, progress_bar=True) - self.assertEqual(brs.nplayers, len(self.players)) - self.assertEqual(brs.repetitions, 3) - self.assertEqual(brs.num_interactions, 18) - - def test_init_with_num_interactions(self): - """Just able to test that no error occurs""" - brs = axelrod.ResultSetFromFile(self.filename, progress_bar=False, - num_interactions=18) - self.assertEqual(brs.nplayers, len(self.players)) - self.assertEqual(brs.repetitions, 3) - self.assertEqual(brs.num_interactions, 18) - - def test_init_with_players_repetitions(self): - """Just able to test that no error occurs""" - brs = axelrod.ResultSetFromFile(self.filename, progress_bar=False, - num_interactions=18, repetitions=3, - players=[str(p) for p in self.players]) - self.assertEqual(brs.nplayers, len(self.players)) - self.assertEqual(brs.repetitions, 3) - self.assertEqual(brs.num_interactions, 18) - - def test_equality(self): - """A test that checks overall equality by comparing to the base result - set class""" - brs = axelrod.ResultSetFromFile(self.filename, progress_bar=False) - rs = axelrod.ResultSet(self.players, self.interactions, progress_bar=False) - self.assertEqual(rs, brs) - - def test_interactions_equality(self): - brs = axelrod.ResultSetFromFile(self.filename, progress_bar=False, - keep_interactions=True) - rs = axelrod.ResultSet(self.players, self.interactions, progress_bar=False) - self.assertEqual(rs.interactions, brs.interactions) - - @given(tournament=tournaments(max_size=5, - max_turns=5, - max_noise=0, - max_repetitions=3)) - @settings(max_examples=5, max_iterations=20) - def test_equality_with_round_robin(self, tournament): - filename = "test_outputs/test_results.csv" - tournament.play(filename=filename, progress_bar=False, - build_results=False) - brs = axelrod.ResultSetFromFile(filename, progress_bar=False) - interactions = iu.read_interactions_from_file(filename, - progress_bar=False) - rs = axelrod.ResultSet(tournament.players, interactions, - progress_bar=False) - - # Not testing full equality because of floating point errors. - self.assertEqual(rs.scores, brs.scores) - self.assertEqual(rs.wins, brs.wins) - self.assertEqual(rs.match_lengths, brs.match_lengths) - self.assertEqual(rs.cooperation, brs.cooperation) - - # Test that players are in the results (due to floating point errors - # the order might not be the same) - self.assertEqual(set(rs.ranked_names), set(brs.ranked_names)) - - @given(tournament=prob_end_tournaments(max_size=5, - min_prob_end=.7, - max_repetitions=3)) - @settings(max_examples=5, max_iterations=20) - def test_equality_with_prob_end(self, tournament): - filename = "test_outputs/test_results.csv" - tournament.play(filename=filename, progress_bar=False, - build_results=False) - brs = axelrod.ResultSetFromFile(filename, progress_bar=False) - interactions = iu.read_interactions_from_file(filename, - progress_bar=False) - rs = axelrod.ResultSet(tournament.players, interactions, - progress_bar=False) - - # Not testing full equality because of floating point errors. - self.assertEqual(rs.ranked_names, brs.ranked_names) - self.assertEqual(rs.scores, brs.scores) - self.assertEqual(rs.match_lengths, brs.match_lengths) - self.assertEqual(rs.cooperation, brs.cooperation) - - def test_read_players_and_repetitions(self): - brs = axelrod.ResultSetFromFile(self.filename, progress_bar=False) - players, repetitions = brs._read_players_and_repetition_numbers() - expected_players = ['Cooperator', 'Tit For Tat', 'Defector'] - self.assertEqual(brs.players, expected_players) - self.assertEqual(repetitions, 3) - - def test_update_repetitions(self): - brs = axelrod.ResultSetFromFile(filename=self.filename, progress_bar=False) - brs.repetitions_d = {} - brs._update_repetitions((0, 0)) - self.assertEqual(brs.repetitions_d, {(0, 0): 1}) - brs._update_repetitions((0, 0)) - self.assertEqual(brs.repetitions_d, {(0, 0): 2}) - brs._update_repetitions((0, 1)) - self.assertEqual(brs.repetitions_d, {(0, 0): 2, (0, 1): 1}) - - def test_build_repetitions(self): - brs = axelrod.ResultSetFromFile(self.filename, progress_bar=False) - brs.repetitions_d = {} - brs._update_repetitions((0, 0)) - brs._update_repetitions((0, 0)) - repetitions = brs._build_repetitions() - self.assertEqual(repetitions, 2) - self.assertFalse(hasattr(brs, 'repetitions_d')) - - def test_update_players(self): - brs = axelrod.ResultSetFromFile(self.filename, progress_bar=False) - brs.players_d = {} - brs._update_players((0, 0), ('Cooperator', 'Cooperator')) - self.assertEqual(brs.players_d, {0: 'Cooperator'}) - brs._update_players((0, 0), ('Cooperator', 'Cooperator')) - self.assertEqual(brs.players_d, {0: 'Cooperator'}) - brs._update_players((0, 1), ('Cooperator', 'Defector')) - self.assertEqual(brs.players_d, {0: 'Cooperator', 1: 'Defector'}) - - def test_build_players(self): - brs = axelrod.ResultSetFromFile(self.filename, progress_bar=False) - brs.players_d = {} - brs._update_players((0, 0), ('Cooperator', 'Cooperator')) - brs._update_players((0, 1), ('Cooperator', 'Defector')) - players = brs._build_players() - self.assertEqual(players, ['Cooperator', 'Defector']) - self.assertFalse(hasattr(brs, 'players_d')) - - def test_build_read_match_chunks(self): - brs = axelrod.ResultSetFromFile(self.filename, progress_bar=False) - matches = brs.read_match_chunks() - chunk = next(matches) - self.assertEqual(chunk[0], - ['0'] * 2 + ['Cooperator'] * 2 + [(C, C)] * 2) - self.assertEqual(chunk[1], - ['0'] * 2 + ['Cooperator'] * 2 + [(C, C)] * 2) - self.assertEqual(chunk[2], - ['0'] * 2 + ['Cooperator'] * 2 + [(C, C)] * 2) - self.assertEqual(len(list(matches)), 5) - - def test_build_all(self): - brs = axelrod.ResultSetFromFile(self.filename, progress_bar=False) - rs = axelrod.ResultSet(self.players, self.interactions, - progress_bar=False) - - brs._build_empty_metrics() - self.assertNotEqual(brs, rs) - brs._build_score_related_metrics(progress_bar=False) - self.assertEqual(brs, rs) - - def test_buid_empty_metrics(self): - plist = range(3) - repetitions = 3 - replist = range(repetitions) - expected_match_lengths = [[[0 for opponent in plist] for player in plist] - for _ in replist] - expected_wins = [[0 for _ in replist] for player in plist] - expected_scores = [[0 for _ in replist] for player in plist] - expected_normalised_scores = [[[] for _ in replist] for player in plist] - expected_payoffs = [[[] for opponent in plist] for player in plist] - expected_score_diffs = [[[0] * repetitions for opponent in plist] - for player in plist] - expected_cooperation = [[0 for opponent in plist] for player in plist] - expected_normalised_cooperation = [[[] for opponent in plist] - for player in plist] - expected_good_partner_matrix = [[0 for opponent in plist] - for player in plist] - - expected_good_partner_rating = [0 for player in plist] - brs = axelrod.ResultSetFromFile(self.filename, progress_bar=False) - brs.match_lengths = [] - brs.wins = [] - brs.scores = [] - brs.normalised_scores = [] - brs.payoffs = [] - brs.score_diffs = [] - brs.cooperation = [] - brs.normalised_cooperation = [] - brs.good_partner_matrix = [] - brs.total_interactions = [] - brs.good_partner_rating = [] - brs._build_empty_metrics() - self.assertEqual(brs.match_lengths, expected_match_lengths) - self.assertEqual(brs.wins, expected_wins) - self.assertEqual(brs.scores, expected_scores) - self.assertEqual(brs.normalised_scores, expected_normalised_scores) - self.assertEqual(brs.payoffs, expected_payoffs) - self.assertEqual(brs.score_diffs, expected_score_diffs) - self.assertEqual(brs.cooperation, expected_cooperation) - self.assertEqual(brs.normalised_cooperation, - expected_normalised_cooperation) - self.assertEqual(brs.good_partner_matrix, expected_good_partner_matrix) - self.assertEqual(brs.good_partner_rating, expected_good_partner_rating) - - class TestDecorator(unittest.TestCase): def test_update_progress_bar(self): method = lambda x: None @@ -809,29 +556,11 @@ class TestResultSetSpatialStructure(TestResultSet): @classmethod def setUpClass(cls): + cls.filename = "test_outputs/test_results_spatial.csv" cls.players = [axelrod.Alternator(), axelrod.TitForTat(), axelrod.Defector()] cls.turns = 5 cls.edges = [(0, 1), (0, 2)] - cls.matches = { - (0, 1): [axelrod.Match((cls.players[0], cls.players[1]), - turns=cls.turns) for _ in range(3)], - (0, 2): [axelrod.Match((cls.players[0], cls.players[2]), - turns=cls.turns) for _ in range(3)]} - - cls.interactions = {} - for index_pair, matches in cls.matches.items(): - for match in matches: - match.play() - try: - cls.interactions[index_pair].append(match.result) - except KeyError: - cls.interactions[index_pair] = [match.result] - - cls.expected_players_to_match_dicts = { - 0: cls.matches[(0, 1)] + cls.matches[(0, 2)], - 1: cls.matches[(0, 1)], - 2: cls.matches[(0, 2)]} cls.expected_match_lengths = [ [[0, 5, 5], [5, 0, 0], [5, 0, 0]] @@ -1012,7 +741,7 @@ def test_match_lengths(self): of players-nodes that are end vertices of an edge is equal to the number of turns. Otherwise it is 0. """ - rs = axelrod.ResultSet(self.players, self.interactions, + rs = axelrod.ResultSet(self.filename, self.players, self.repetitions, progress_bar=False) self.assertIsInstance(rs.match_lengths, list) self.assertEqual(len(rs.match_lengths), rs.repetitions) @@ -1040,28 +769,11 @@ class TestResultSetSpatialStructureTwo(TestResultSetSpatialStructure): @classmethod def setUpClass(cls): + cls.filename = "test_outputs/test_results_spatial_two.csv" cls.players = [axelrod.Alternator(), axelrod.TitForTat(), axelrod.Defector(), axelrod.Cooperator()] cls.turns = 5 cls.edges = [(0, 1), (2, 3)] - cls.matches = {(0, 1): [axelrod.Match((cls.players[0], cls.players[1]), - turns=cls.turns) for _ in range(3)], - (2, 3): [axelrod.Match((cls.players[2], cls.players[3]), - turns=cls.turns) for _ in range(3)]} - - cls.interactions = {} - for index_pair, matches in cls.matches.items(): - for match in matches: - match.play() - try: - cls.interactions[index_pair].append(match.result) - except KeyError: - cls.interactions[index_pair] = [match.result] - - cls.expected_players_to_match_dicts = {0: cls.matches[(0, 1)], - 1: cls.matches[(0, 1)], - 2: cls.matches[(2, 3)], - 3: cls.matches[(2, 3)]} cls.expected_match_lengths = [ [[0, 5, 0, 0], [5, 0, 0, 0], [0, 0, 0, 5], [0, 0, 5, 0]] @@ -1260,28 +972,11 @@ class TestResultSetSpatialStructureThree(TestResultSetSpatialStructure): @classmethod def setUpClass(cls): + cls.filename = "test_outputs/test_results_spatial_three.csv" cls.players = [axelrod.Alternator(), axelrod.TitForTat(), axelrod.Defector(), axelrod.Cooperator()] cls.turns = 5 cls.edges = [(0, 0), (1, 1), (2, 2), (3, 3)] - cls.matches = {(i, i): [axelrod.Match( - (cls.players[i], cls.players[i].clone()), turns=cls.turns) - for _ in range(3)] for i in range(4)} - - cls.interactions = {} - for index_pair, matches in cls.matches.items(): - for match in matches: - match.play() - - try: - cls.interactions[index_pair].append(match.result) - except KeyError: - cls.interactions[index_pair] = [match.result] - - cls.expected_players_to_match_dicts = {0: cls.matches[(0, 0)], - 1: cls.matches[(1, 1)], - 2: cls.matches[(2, 2)], - 3: cls.matches[(3, 3)]} cls.expected_match_lengths =[ [[5, 0, 0, 0], [0, 5, 0, 0], [0, 0, 5, 0], [0, 0, 0, 5]] @@ -1342,7 +1037,10 @@ def setUpClass(cls): ] cls.expected_cooperation = [ - [0.0 for _ in range(4)] for _ in range(4) + [9.0, 0, 0, 0], + [0, 15.0, 0, 0], + [0, 0, 0, 0], + [0, 0, 0, 15.0] ] cls.expected_normalised_cooperation = [ @@ -1419,7 +1117,7 @@ def test_equality(self): def test_summarise(self): """Overwriting for this particular case""" - rs = axelrod.ResultSet(self.players, self.interactions, + rs = axelrod.ResultSet(self.filename, self.players, self.repetitions, progress_bar=False) sd = rs.summarise() @@ -1432,7 +1130,8 @@ def test_summarise(self): class TestSummary(unittest.TestCase): """Separate test to check that summary always builds without failures""" - @given(tournament=tournaments(max_size=5, + @given(tournament=tournaments(min_size=2, + max_size=5, max_turns=5, max_repetitions=3)) @settings(max_examples=5, max_iterations=20) @@ -1447,3 +1146,15 @@ def test_summarise_without_failure(self, tournament): player.DC_rate + player.DD_rate, 3) self.assertTrue(total_rate in [0, 1]) self.assertTrue(0 <= player.Initial_C_rate <= 1) + + +class TestCreateCounterDict(unittest.TestCase): + """Separate test for a helper function""" + def test_basic_use(self): + key_map = {"Col 1": "Var 1", "Col 2": "Var 2"} + df = pd.DataFrame({"Col 1": [10, 20, 30], "Col 2": [1, 2, 0]}, + index=[[5, 6, 7], [1, 2, 3]]) + self.assertEqual(create_counter_dict(df, 6, 2, key_map), + Counter({"Var 1": 20, "Var 2": 2})) + self.assertEqual(create_counter_dict(df, 7, 3, key_map), + Counter({"Var 1": 30})) diff --git a/axelrod/tests/unit/test_tournament.py b/axelrod/tests/unit/test_tournament.py index ca57906ab..d4eac0107 100644 --- a/axelrod/tests/unit/test_tournament.py +++ b/axelrod/tests/unit/test_tournament.py @@ -9,10 +9,13 @@ import unittest from unittest.mock import MagicMock, patch import warnings +import filecmp from hypothesis import given, example, settings from hypothesis.strategies import integers, floats from tqdm import tqdm +import numpy as np +import pandas as pd from axelrod.tests.property import (tournaments, prob_end_tournaments, @@ -140,26 +143,15 @@ def test_warning(self): filename=self.filename, progress_bar=False) self.assertEqual(len(w), 0) - def test_setup_output_in_memory_overrides_filename(self): - self.assertIsNone(self.test_tournament.filename) - self.assertIsNone(self.test_tournament._temp_file_descriptor) - self.assertFalse(hasattr(self.test_tournament, 'interactions_dict')) - - self.test_tournament.setup_output(self.filename, in_memory=True) - - self.assertIsNone(self.test_tournament.filename) - self.assertIsNone(self.test_tournament._temp_file_descriptor) - self.assertEqual(self.test_tournament.interactions_dict, {}) - def test_setup_output_with_filename(self): - self.test_tournament.setup_output(self.filename, in_memory=False) + self.test_tournament.setup_output(self.filename) self.assertEqual(self.test_tournament.filename, self.filename) self.assertIsNone(self.test_tournament._temp_file_descriptor) self.assertFalse(hasattr(self.test_tournament, 'interactions_dict')) - def test_setup_output_no_filename_no_in_memory(self): + def test_setup_output_no_filename(self): self.test_tournament.setup_output() self.assertIsInstance(self.test_tournament.filename, str) @@ -190,22 +182,17 @@ def test_play_changes_temp_file_descriptor(self): self.assertIsNone(self.test_tournament._temp_file_descriptor) # No file descriptor for a named file. - self.test_tournament.play(filename=self.filename, in_memory=False, - progress_bar=False) - self.assertIsNone(self.test_tournament._temp_file_descriptor) - - # No file descriptor for in_memory. - self.test_tournament.play(filename=None, in_memory=True, + self.test_tournament.play(filename=self.filename, progress_bar=False) self.assertIsNone(self.test_tournament._temp_file_descriptor) # Temp file creates file descriptor. - self.test_tournament.play(filename=None, in_memory=False, + self.test_tournament.play(filename=None, progress_bar=False) self.assertIsInstance(self.test_tournament._temp_file_descriptor, int) def test_play_tempfile_removed(self): - self.test_tournament.play(filename=None, in_memory=False, + self.test_tournament.play(filename=None, progress_bar=False) self.assertFalse(os.path.isfile(self.test_tournament.filename)) @@ -227,10 +214,6 @@ def test_play_resets_filename_and_temp_file_descriptor_each_time(self): self.assertNotEqual(old_filename, self.test_tournament.filename) self.assertNotEqual(self.test_tournament.filename, self.filename) - self.test_tournament.play(in_memory=True, progress_bar=False) - self.assertIsNone(self.test_tournament._temp_file_descriptor) - self.assertIsNone(self.test_tournament.filename) - def test_get_file_objects_no_filename(self): file, writer = self.test_tournament._get_file_objects() self.assertIsNone(file) @@ -295,7 +278,7 @@ def test_serial_play_with_different_game(self): turns=1, repetitions=1) results = tournament.play(progress_bar=False) - self.assertEqual(results.game.RPST(), (-1, -1, -1, -1)) + self.assertLessEqual(np.max(results.scores), 0) @patch('tqdm.tqdm', RecordedTQDM) def test_no_progress_bar_play(self): @@ -323,9 +306,6 @@ def test_no_progress_bar_play(self): self.assertIsNone(results) self.assertEqual(RecordedTQDM.record, []) - results = axelrod.ResultSetFromFile(self.filename, progress_bar=False) - self.assertIsInstance(results, axelrod.ResultSet) - def assert_play_pbar_correct_total_and_finished(self, pbar, total): self.assertEqual(pbar.desc, 'Playing matches') self.assertEqual(pbar.total, total) @@ -346,7 +326,7 @@ def test_progress_bar_play(self): results = tournament.play() self.assertIsInstance(results, axelrod.ResultSet) # Check that progress bar was created, updated and closed. - self.assertEqual(len(RecordedTQDM.record), 3) + self.assertEqual(len(RecordedTQDM.record), 2) play_pbar = RecordedTQDM.record[0] self.assert_play_pbar_correct_total_and_finished(play_pbar, total=15) # Check all progress bars are closed. @@ -355,7 +335,7 @@ def test_progress_bar_play(self): RecordedTQDM.reset_record() results = tournament.play(progress_bar=True) self.assertIsInstance(results, axelrod.ResultSet) - self.assertEqual(len(RecordedTQDM.record), 3) + self.assertEqual(len(RecordedTQDM.record), 2) play_pbar = RecordedTQDM.record[0] self.assert_play_pbar_correct_total_and_finished(play_pbar, total=15) @@ -368,8 +348,6 @@ def test_progress_bar_play(self): play_pbar = RecordedTQDM.record[0] self.assert_play_pbar_correct_total_and_finished(play_pbar, total=15) - results = axelrod.ResultSetFromFile(self.filename) - self.assertIsInstance(results, axelrod.ResultSet) @patch('tqdm.tqdm', RecordedTQDM) def test_progress_bar_play_parallel(self): @@ -393,7 +371,7 @@ def test_progress_bar_play_parallel(self): results = tournament.play(progress_bar=True, processes=2) self.assertIsInstance(results, axelrod.ResultSet) - self.assertEqual(len(RecordedTQDM.record), 3) + self.assertEqual(len(RecordedTQDM.record), 2) play_pbar = RecordedTQDM.record[0] self.assert_play_pbar_correct_total_and_finished(play_pbar, total=15) @@ -402,7 +380,7 @@ def test_progress_bar_play_parallel(self): results = tournament.play(processes=2) self.assertIsInstance(results, axelrod.ResultSet) - self.assertEqual(len(RecordedTQDM.record), 3) + self.assertEqual(len(RecordedTQDM.record), 2) play_pbar = RecordedTQDM.record[0] self.assert_play_pbar_correct_total_and_finished(play_pbar, total=15) @@ -432,7 +410,7 @@ def test_property_serial_play(self, tournament): # Test that we get an instance of ResultSet results = tournament.play(progress_bar=False) self.assertIsInstance(results, axelrod.ResultSet) - self.assertEqual(results.nplayers, len(tournament.players)) + self.assertEqual(results.num_players, len(tournament.players)) self.assertEqual(results.players, [str(p) for p in tournament.players]) def test_parallel_play(self): @@ -480,12 +458,12 @@ def test_run_serial(self): game=self.game, turns=axelrod.DEFAULT_TURNS, repetitions=self.test_repetitions) - tournament._write_interactions = MagicMock( - name='_write_interactions') + tournament._write_interactions_to_file = MagicMock( + name='_write_interactions_to_file') self.assertTrue(tournament._run_serial()) # Get the calls made to write_interactions - calls = tournament._write_interactions.call_args_list + calls = tournament._write_interactions_to_file.call_args_list self.assertEqual(len(calls), 15) def test_run_parallel(self): @@ -499,20 +477,20 @@ def __reduce__(self): game=self.game, turns=axelrod.DEFAULT_TURNS, repetitions=self.test_repetitions) - tournament._write_interactions = PickleableMock( - name='_write_interactions') + tournament._write_interactions_to_file = PickleableMock( + name='_write_interactions_to_file') # For test coverage purposes. This confirms PickleableMock can be # pickled exactly once. Windows multi-processing must pickle this Mock # exactly once during testing. pickled = pickle.loads(pickle.dumps(tournament)) - self.assertIsInstance(pickled._write_interactions, MagicMock) + self.assertIsInstance(pickled._write_interactions_to_file, MagicMock) self.assertRaises(pickle.PicklingError, pickle.dumps, pickled) self.assertTrue(tournament._run_parallel()) # Get the calls made to write_interactions - calls = tournament._write_interactions.call_args_list + calls = tournament._write_interactions_to_file.call_args_list self.assertEqual(len(calls), 15) def test_n_workers(self): @@ -608,10 +586,6 @@ def test_build_result_set(self): results = tournament.play(progress_bar=False) self.assertIsInstance(results, axelrod.ResultSet) - # Test in memory - results = tournament.play(progress_bar=False, in_memory=True) - self.assertIsInstance(results, axelrod.ResultSet) - def test_no_build_result_set(self): tournament = axelrod.Tournament( name=self.test_name, @@ -620,13 +594,15 @@ def test_no_build_result_set(self): turns=axelrod.DEFAULT_TURNS, repetitions=self.test_repetitions) - results = tournament.play(build_results=False, filename=self.filename, - progress_bar=False) - self.assertIsNone(results) + tournament._calculate_results = MagicMock(name='_calculate_results') + # Mocking this as it is called by play + self.assertIsNone(tournament.play(filename=self.filename, + progress_bar=False, + build_results=False)) - # Checking that results were written properly - results = axelrod.ResultSetFromFile(self.filename, progress_bar=False) - self.assertIsInstance(results, axelrod.ResultSet) + # Get the calls made to write_interactions + calls = tournament._calculate_results.call_args_list + self.assertEqual(len(calls), 0) @given(turns=integers(min_value=1, max_value=200)) @settings(max_examples=5, max_iterations=20) @@ -663,8 +639,9 @@ def make_chunk_generator(): # Check that have the expected number of repetitions self.assertEqual(len(plays), self.test_repetitions) for repetition in plays: - # Check that have the correct length for each rep - self.assertEqual(len(repetition), turns) + actions, results = repetition + self.assertEqual(len(actions), turns) + self.assertEqual(len(results), 10) # Check that matches no longer exist self.assertEqual((len(list(chunk_generator))), 0) @@ -691,26 +668,17 @@ def test_write_interactions(self): game=self.game, turns=2, repetitions=2) - tournament._write_interactions = MagicMock(name='_write_interactions') + tournament._write_interactions_to_file = MagicMock(name='_write_interactions_to_file') # Mocking this as it is called by play - tournament._build_result_set = MagicMock(name='_build_result_set') - self.assertTrue(tournament.play(filename=self.filename, - progress_bar=False)) - - # Get the calls made to write_interactions - calls = tournament._write_interactions.call_args_list - self.assertEqual(len(calls), 15) + self.assertIsNone(tournament.play(filename=self.filename, + progress_bar=False, + build_results=False)) - # Test when running in memory - tournament._write_interactions = MagicMock(name='_write_interactions') - self.assertTrue(tournament.play(filename=self.filename, - progress_bar=False, - in_memory=False)) # Get the calls made to write_interactions - calls = tournament._write_interactions.call_args_list + calls = tournament._write_interactions_to_file.call_args_list self.assertEqual(len(calls), 15) - def test_write_to_csv(self): + def test_write_to_csv_with_results(self): tournament = axelrod.Tournament( name=self.test_name, players=self.players, @@ -718,39 +686,22 @@ def test_write_to_csv(self): turns=2, repetitions=2) tournament.play(filename=self.filename, progress_bar=False) - with open(self.filename, 'r') as f: - written_data = [[int(r[0]), int(r[1])] + r[2:] for r in csv.reader(f)] - expected_data = [[0, 1, 'Cooperator', 'Tit For Tat', 'CC', 'CC'], - [0, 1, 'Cooperator', 'Tit For Tat', 'CC', 'CC'], - [1, 2, 'Tit For Tat', 'Defector', 'CD', 'DD'], - [1, 2, 'Tit For Tat', 'Defector', 'CD', 'DD'], - [0, 0, 'Cooperator', 'Cooperator', 'CC', 'CC'], - [0, 0, 'Cooperator', 'Cooperator', 'CC', 'CC'], - [3, 3, 'Grudger', 'Grudger', 'CC', 'CC'], - [3, 3, 'Grudger', 'Grudger', 'CC', 'CC'], - [2, 2, 'Defector', 'Defector', 'DD', 'DD'], - [2, 2, 'Defector', 'Defector', 'DD', 'DD'], - [4, 4, 'Soft Go By Majority', 'Soft Go By Majority', 'CC', 'CC'], - [4, 4, 'Soft Go By Majority', 'Soft Go By Majority', 'CC', 'CC'], - [1, 4, 'Tit For Tat', 'Soft Go By Majority', 'CC', 'CC'], - [1, 4, 'Tit For Tat', 'Soft Go By Majority', 'CC', 'CC'], - [1, 1, 'Tit For Tat', 'Tit For Tat', 'CC', 'CC'], - [1, 1, 'Tit For Tat', 'Tit For Tat', 'CC', 'CC'], - [1, 3, 'Tit For Tat', 'Grudger', 'CC', 'CC'], - [1, 3, 'Tit For Tat', 'Grudger', 'CC', 'CC'], - [2, 3, 'Defector', 'Grudger', 'DD', 'CD'], - [2, 3, 'Defector', 'Grudger', 'DD', 'CD'], - [0, 4, 'Cooperator', 'Soft Go By Majority', 'CC', 'CC'], - [0, 4, 'Cooperator', 'Soft Go By Majority', 'CC', 'CC'], - [2, 4, 'Defector', 'Soft Go By Majority', 'DD', 'CD'], - [2, 4, 'Defector', 'Soft Go By Majority', 'DD', 'CD'], - [0, 3, 'Cooperator', 'Grudger', 'CC', 'CC'], - [0, 3, 'Cooperator', 'Grudger', 'CC', 'CC'], - [3, 4, 'Grudger', 'Soft Go By Majority', 'CC', 'CC'], - [3, 4, 'Grudger', 'Soft Go By Majority', 'CC', 'CC'], - [0, 2, 'Cooperator', 'Defector', 'CC', 'DD'], - [0, 2, 'Cooperator', 'Defector', 'CC', 'DD']] - self.assertEqual(sorted(written_data), sorted(expected_data)) + df = pd.read_csv(self.filename) + expected_df = pd.read_csv("test_outputs/expected_test_tournament.csv") + self.assertTrue(df.equals(expected_df)) + + def test_write_to_csv_without_results(self): + tournament = axelrod.Tournament( + name=self.test_name, + players=self.players, + game=self.game, + turns=2, + repetitions=2) + tournament.play(filename=self.filename, progress_bar=False, + build_results=False) + df = pd.read_csv(self.filename) + expected_df = pd.read_csv("test_outputs/expected_test_tournament_no_results.csv") + self.assertTrue(df.equals(expected_df)) class TestProbEndTournament(unittest.TestCase): @@ -806,7 +757,7 @@ def test_property_serial_play(self, tournament): # Test that we get an instance of ResultSet results = tournament.play(progress_bar=False) self.assertIsInstance(results, axelrod.ResultSet) - self.assertEqual(results.nplayers, len(tournament.players)) + self.assertEqual(results.num_players, len(tournament.players)) self.assertEqual(results.players, [str(p) for p in tournament.players]) @@ -877,7 +828,7 @@ def test_complete_tournament(self, strategies, turns, repetitions, spatial_results = spatial_tournament.play(progress_bar=False) self.assertEqual(results.ranked_names, spatial_results.ranked_names) - self.assertEqual(results.nplayers, spatial_results.nplayers) + self.assertEqual(results.num_players, spatial_results.num_players) self.assertEqual(results.repetitions, spatial_results.repetitions) self.assertEqual(results.payoff_diffs_means, spatial_results.payoff_diffs_means) diff --git a/axelrod/tournament.py b/axelrod/tournament.py index 1c541d825..bd3c3ef5e 100644 --- a/axelrod/tournament.py +++ b/axelrod/tournament.py @@ -14,7 +14,12 @@ from .game import Game from .match import Match from .match_generator import MatchGenerator -from .result_set import ResultSetFromFile, ResultSet +from .result_set import ResultSet +from axelrod.action import Action, str_to_actions + +import axelrod.interaction_utils as iu + +C, D = Action.C, Action.D from typing import List, Tuple @@ -81,23 +86,20 @@ def __init__(self, players: List[Player], self.filename = None # type: str self._temp_file_descriptor = None # type: int - def setup_output(self, filename=None, in_memory=False): + def setup_output(self, filename=None): """assign/create `filename` to `self`. If file should be deleted once `play` is finished, assign a file descriptor. """ temp_file_descriptor = None - if in_memory: - self.interactions_dict = {} - filename = None - if not in_memory and filename is None: + if filename is None: temp_file_descriptor, filename = mkstemp() self.filename = filename self._temp_file_descriptor = temp_file_descriptor + def play(self, build_results: bool = True, filename: str = None, processes: int = None, progress_bar: bool = True, - keep_interactions: bool = False, in_memory: bool = False - ) -> ResultSetFromFile: + ) -> ResultSet: """ Plays the tournament and passes the results to the ResultSet class @@ -111,22 +113,16 @@ def play(self, build_results: bool = True, filename: str = None, The number of processes to be used for parallel processing progress_bar : bool Whether or not to create a progress bar which will be updated - keep_interactions : bool - Whether or not to load the interactions in to memory - in_memory : bool - By default interactions are written to a file. - If this is True they will be kept in memory. - This is not advised for large tournaments. Returns ------- - axelrod.ResultSetFromFile + axelrod.ResultSet """ self.num_interactions = 0 self.use_progress_bar = progress_bar - self.setup_output(filename, in_memory) + self.setup_output(filename) if not build_results and not filename: warnings.warn( @@ -134,60 +130,35 @@ def play(self, build_results: bool = True, filename: str = None, "build_results=False and no filename was supplied.") if processes is None: - self._run_serial() + self._run_serial(build_results=build_results) else: - self._run_parallel(processes=processes) + self._run_parallel(build_results=build_results, processes=processes) result_set = None if build_results: - result_set = self._build_result_set( - keep_interactions=keep_interactions, in_memory=in_memory - ) - + result_set = ResultSet(filename=self.filename, + players=[str(p) for p in self.players], + repetitions=self.repetitions, + processes=processes, + progress_bar=progress_bar) if self._temp_file_descriptor is not None: os.close(self._temp_file_descriptor) os.remove(self.filename) return result_set - def _build_result_set(self, keep_interactions: bool = False, - in_memory: bool = False): - """ - Build the result set (used by the play method) - - Returns - ------- - axelrod.BigResultSet - """ - if not in_memory: - result_set = ResultSetFromFile( - filename=self.filename, - progress_bar=self.use_progress_bar, - num_interactions=self.num_interactions, - repetitions=self.repetitions, - players=[str(p) for p in self.players], - keep_interactions=keep_interactions, - game=self.game) - else: - result_set = ResultSet( - players=[str(p) for p in self.players], - interactions=self.interactions_dict, - repetitions=self.repetitions, - progress_bar=self.use_progress_bar, - game=self.game) - return result_set - def _run_serial(self) -> bool: + def _run_serial(self, build_results: bool=True) -> bool: """Run all matches in serial.""" chunks = self.match_generator.build_match_chunks() - out_file, writer = self._get_file_objects() + out_file, writer = self._get_file_objects(build_results) progress_bar = self._get_progress_bar() for chunk in chunks: - results = self._play_matches(chunk) - self._write_interactions(results, writer=writer) + results = self._play_matches(chunk, build_results=build_results) + self._write_interactions_to_file(results, writer=writer) if self.use_progress_bar: progress_bar.update(1) @@ -196,7 +167,7 @@ def _run_serial(self) -> bool: return True - def _get_file_objects(self): + def _get_file_objects(self, build_results=True): """Returns the file object and writer for writing results or (None, None) if self.filename is None""" file_obj = None @@ -204,6 +175,38 @@ def _get_file_objects(self): if self.filename is not None: file_obj = open(self.filename, 'w') writer = csv.writer(file_obj, lineterminator='\n') + + header = ["Interaction index", + "Player index", + "Opponent index", + "Repetition", + "Player name", + "Opponent name", + "Actions"] + if build_results: + header.extend(["Score", + "Score difference", + "Turns", + "Score per turn", + "Score difference per turn", + "Win", + "Initial cooperation", + "Cooperation count", + "CC count", + "CD count", + "DC count", + "DD count", + "CC to C count", + "CC to D count", + "CD to C count", + "CD to D count", + "DC to C count", + "DC to D count", + "DD to C count", + "DD to D count", + "Good partner"]) + + writer.writerow(header) return file_obj, writer def _get_progress_bar(self): @@ -212,38 +215,57 @@ def _get_progress_bar(self): desc="Playing matches") return None - def _write_interactions(self, results, writer=None): - """Write the interactions to file or to a dictionary""" - if writer is not None: - self._write_interactions_to_file(results, writer) - elif self.interactions_dict is not None: - self._write_interactions_to_dict(results) - def _write_interactions_to_file(self, results, writer): """Write the interactions to csv.""" for index_pair, interactions in results.items(): - for interaction in interactions: - row = list(index_pair) - row.append(str(self.players[index_pair[0]])) - row.append(str(self.players[index_pair[1]])) - history1 = actions_to_str([i[0] for i in interaction]) - history2 = actions_to_str([i[1] for i in interaction]) - row.append(history1) - row.append(history2) - writer.writerow(row) - self.num_interactions += 1 - - def _write_interactions_to_dict(self, results): - """Write the interactions to memory""" - for index_pair, interactions in results.items(): - for interaction in interactions: - try: - self.interactions_dict[index_pair].append(interaction) - except KeyError: - self.interactions_dict[index_pair] = [interaction] + repetition = 0 + for interaction, results in interactions: + + if results is not None: + (scores, + score_diffs, + turns, score_per_turns, + score_diffs_per_turns, + initial_cooperation, + cooperations, + state_distribution, + state_to_action_distributions, + winner_index) = results + for index, player_index in enumerate(index_pair): + opponent_index = index_pair[index - 1] + row = [self.num_interactions, player_index, opponent_index, + repetition] + row.append(str(self.players[player_index])) + row.append(str(self.players[opponent_index])) + history = actions_to_str([i[index] for i in interaction]) + row.append(history) + + if results is not None: + row.append(scores[index]) + row.append(score_diffs[index]) + row.append(turns) + row.append(score_per_turns[index]) + row.append(score_diffs_per_turns[index]) + row.append(int(winner_index is index)) + row.append(initial_cooperation[index]) + row.append(cooperations[index]) + + states = [(C, C), (C, D), (D, C), (D, D)] + if index == 1: + states = [s[::-1] for s in states] + for state in states: + row.append(state_distribution[state]) + for state in states: + row.append(state_to_action_distributions[index][(state, C)]) + row.append(state_to_action_distributions[index][(state, D)]) + + row.append(int(cooperations[index] >= cooperations[index - 1])) + + writer.writerow(row) + repetition += 1 self.num_interactions += 1 - def _run_parallel(self, processes: int=2) -> bool: + def _run_parallel(self, processes: int=2, build_results: bool=True) -> bool: """ Run all matches in parallel @@ -263,8 +285,8 @@ def _run_parallel(self, processes: int=2) -> bool: for chunk in chunks: work_queue.put(chunk) - self._start_workers(workers, work_queue, done_queue) - self._process_done_queue(workers, done_queue) + self._start_workers(workers, work_queue, done_queue, build_results) + self._process_done_queue(workers, done_queue, build_results) return True @@ -283,7 +305,7 @@ def _n_workers(self, processes: int = 2) -> int: return n_workers def _start_workers(self, workers: int, work_queue: Queue, - done_queue: Queue) -> bool: + done_queue: Queue, build_results: bool=True) -> bool: """ Initiates the sub-processes to carry out parallel processing. @@ -298,12 +320,13 @@ def _start_workers(self, workers: int, work_queue: Queue, """ for worker in range(workers): process = Process( - target=self._worker, args=(work_queue, done_queue)) + target=self._worker, args=(work_queue, done_queue, build_results)) work_queue.put('STOP') process.start() return True - def _process_done_queue(self, workers: int, done_queue: Queue): + def _process_done_queue(self, workers: int, done_queue: Queue, + build_results: bool=True): """ Retrieves the matches from the parallel sub-processes @@ -314,7 +337,7 @@ def _process_done_queue(self, workers: int, done_queue: Queue): done_queue : multiprocessing.Queue A queue containing the output dictionaries from each round robin """ - out_file, writer = self._get_file_objects() + out_file, writer = self._get_file_objects(build_results) progress_bar = self._get_progress_bar() stops = 0 @@ -323,7 +346,7 @@ def _process_done_queue(self, workers: int, done_queue: Queue): if results == 'STOP': stops += 1 else: - self._write_interactions(results, writer) + self._write_interactions_to_file(results, writer) if self.use_progress_bar: progress_bar.update(1) @@ -331,7 +354,8 @@ def _process_done_queue(self, workers: int, done_queue: Queue): _close_objects(out_file, progress_bar) return True - def _worker(self, work_queue: Queue, done_queue: Queue): + def _worker(self, work_queue: Queue, done_queue: Queue, + build_results: bool=True): """ The work for each parallel sub-process to execute. @@ -343,12 +367,12 @@ def _worker(self, work_queue: Queue, done_queue: Queue): A queue containing the output dictionaries from each round robin """ for chunk in iter(work_queue.get, 'STOP'): - interactions = self._play_matches(chunk) + interactions = self._play_matches(chunk, build_results) done_queue.put(interactions) done_queue.put('STOP') return True - def _play_matches(self, chunk): + def _play_matches(self, chunk, build_results=True): """ Play matches in a given chunk. @@ -373,9 +397,53 @@ def _play_matches(self, chunk): match = Match(**match_params) for _ in range(repetitions): match.play() - interactions[index_pair].append(match.result) + + if build_results: + results = self._calculate_results(match.result) + else: + results = None + + interactions[index_pair].append([match.result, results]) return interactions + def _calculate_results(self, interactions): + results = [] + + scores = iu.compute_final_score(interactions, self.game) + results.append(scores) + + score_diffs = scores[0] - scores[1], scores[1] - scores[0] + results.append(score_diffs) + + turns = len(interactions) + results.append(turns) + + score_per_turns = iu.compute_final_score_per_turn(interactions, + self.game) + results.append(score_per_turns) + + score_diffs_per_turns = score_diffs[0] / turns, score_diffs[1] / turns + results.append(score_diffs_per_turns) + + initial_coops = tuple(map( + bool, + iu.compute_cooperations(interactions[:1]))) + results.append(initial_coops) + + cooperations = iu.compute_cooperations(interactions) + results.append(cooperations) + + state_distribution = iu.compute_state_distribution(interactions) + results.append(state_distribution) + + state_to_action_distributions = iu.compute_state_to_action_distribution(interactions) + results.append(state_to_action_distributions) + + winner_index = iu.compute_winner_index(interactions, self.game) + results.append(winner_index) + + return results + def _close_objects(*objs): """If the objects have a `close` method, closes them.""" diff --git a/docs/conf.py b/docs/conf.py index 3c79c2012..07a014086 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -21,7 +21,9 @@ 'scipy', 'scipy.stats','numpy', 'numpy.linalg', 'numpy.random', 'matplotlib.pyplot', 'matplotlib','matplotlib.transforms', 'tqdm', 'mpl_toolkits.axes_grid1', 'dill', 'multiprocess','prompt_toolkit', - 'prompt_toolkit.token', 'prompt_toolkit.styles','prompt_toolkit.validation'] + 'prompt_toolkit.token', 'prompt_toolkit.styles','prompt_toolkit.validation', + 'pandas', 'pandas.util', 'pandas.util.decorators', 'toolz', 'toolz.curried', + 'toolz.functoolz', 'cloudpickle', 'dask', 'dask.dataframe'] for mod_name in MOCK_MODULES: sys.modules[mod_name] = mock.Mock() diff --git a/docs/tutorials/advanced/reading_and_writing_interactions.rst b/docs/tutorials/advanced/reading_and_writing_interactions.rst index fec2813e6..f4a0de4f8 100644 --- a/docs/tutorials/advanced/reading_and_writing_interactions.rst +++ b/docs/tutorials/advanced/reading_and_writing_interactions.rst @@ -13,121 +13,98 @@ a :code:`filename` argument to the :code:`play` method of a tournament:: This will create a file `basic_tournament.csv` with data that looks something like:: - 0,0,Alternator,Alternator,CDCD,CDCD - 0,0,Alternator,Alternator,CDCD,CDCD - 0,1,Alternator,Anti Tit For Tat,CDCD,CDCD - 0,1,Alternator,Anti Tit For Tat,CDCD,CDCD - 0,2,Alternator,Bully,CDCD,DDCD - 0,2,Alternator,Bully,CDCD,DDCD - 0,3,Alternator,Cooperator,CDCD,CCCC - 0,3,Alternator,Cooperator,CDCD,CCCC - 0,4,Alternator,Defector,CDCD,DDDD - 0,4,Alternator,Defector,CDCD,DDDD - 0,5,Alternator,Suspicious Tit For Tat,CDCD,DCDC - 0,5,Alternator,Suspicious Tit For Tat,CDCD,DCDC - 0,6,Alternator,Tit For Tat,CDCD,CCDC - 0,6,Alternator,Tit For Tat,CDCD,CCDC - 0,7,Alternator,Win-Stay Lose-Shift,CDCD,CCDD - 0,7,Alternator,Win-Stay Lose-Shift,CDCD,CCDD - 1,1,Anti Tit For Tat,Anti Tit For Tat,CDCD,CDCD - 1,1,Anti Tit For Tat,Anti Tit For Tat,CDCD,CDCD - 1,2,Anti Tit For Tat,Bully,CCCC,DDDD - 1,2,Anti Tit For Tat,Bully,CCCC,DDDD - 1,3,Anti Tit For Tat,Cooperator,CDDD,CCCC - 1,3,Anti Tit For Tat,Cooperator,CDDD,CCCC - 1,4,Anti Tit For Tat,Defector,CCCC,DDDD - 1,4,Anti Tit For Tat,Defector,CCCC,DDDD - 1,5,Anti Tit For Tat,Suspicious Tit For Tat,CCDD,DCCD - 1,5,Anti Tit For Tat,Suspicious Tit For Tat,CCDD,DCCD - 1,6,Anti Tit For Tat,Tit For Tat,CDDC,CCDD - 1,6,Anti Tit For Tat,Tit For Tat,CDDC,CCDD - 1,7,Anti Tit For Tat,Win-Stay Lose-Shift,CDDC,CCDC - 1,7,Anti Tit For Tat,Win-Stay Lose-Shift,CDDC,CCDC - 2,2,Bully,Bully,DCDC,DCDC - 2,2,Bully,Bully,DCDC,DCDC - 2,3,Bully,Cooperator,DDDD,CCCC - 2,3,Bully,Cooperator,DDDD,CCCC - 2,4,Bully,Defector,DCCC,DDDD - 2,4,Bully,Defector,DCCC,DDDD - 2,5,Bully,Suspicious Tit For Tat,DCCD,DDCC - 2,5,Bully,Suspicious Tit For Tat,DCCD,DDCC - 2,6,Bully,Tit For Tat,DDCC,CDDC - 2,6,Bully,Tit For Tat,DDCC,CDDC - 2,7,Bully,Win-Stay Lose-Shift,DDCD,CDCC - 2,7,Bully,Win-Stay Lose-Shift,DDCD,CDCC - 3,3,Cooperator,Cooperator,CCCC,CCCC - 3,3,Cooperator,Cooperator,CCCC,CCCC - 3,4,Cooperator,Defector,CCCC,DDDD - 3,4,Cooperator,Defector,CCCC,DDDD - 3,5,Cooperator,Suspicious Tit For Tat,CCCC,DCCC - 3,5,Cooperator,Suspicious Tit For Tat,CCCC,DCCC - 3,6,Cooperator,Tit For Tat,CCCC,CCCC - 3,6,Cooperator,Tit For Tat,CCCC,CCCC - 3,7,Cooperator,Win-Stay Lose-Shift,CCCC,CCCC - 3,7,Cooperator,Win-Stay Lose-Shift,CCCC,CCCC - 4,4,Defector,Defector,DDDD,DDDD - 4,4,Defector,Defector,DDDD,DDDD - 4,5,Defector,Suspicious Tit For Tat,DDDD,DDDD - 4,5,Defector,Suspicious Tit For Tat,DDDD,DDDD - 4,6,Defector,Tit For Tat,DDDD,CDDD - 4,6,Defector,Tit For Tat,DDDD,CDDD - 4,7,Defector,Win-Stay Lose-Shift,DDDD,CDCD - 4,7,Defector,Win-Stay Lose-Shift,DDDD,CDCD - 5,5,Suspicious Tit For Tat,Suspicious Tit For Tat,DDDD,DDDD - 5,5,Suspicious Tit For Tat,Suspicious Tit For Tat,DDDD,DDDD - 5,6,Suspicious Tit For Tat,Tit For Tat,DCDC,CDCD - 5,6,Suspicious Tit For Tat,Tit For Tat,DCDC,CDCD - 5,7,Suspicious Tit For Tat,Win-Stay Lose-Shift,DCDD,CDDC - 5,7,Suspicious Tit For Tat,Win-Stay Lose-Shift,DCDD,CDDC - 6,6,Tit For Tat,Tit For Tat,CCCC,CCCC - 6,6,Tit For Tat,Tit For Tat,CCCC,CCCC - 6,7,Tit For Tat,Win-Stay Lose-Shift,CCCC,CCCC - 6,7,Tit For Tat,Win-Stay Lose-Shift,CCCC,CCCC - 7,7,Win-Stay Lose-Shift,Win-Stay Lose-Shift,CCCC,CCCC - 7,7,Win-Stay Lose-Shift,Win-Stay Lose-Shift,CCCC,CCCC - -The columns of this file are of the form: - -1. Index of first player -2. Index of second player -3. Name of first player -4. Name of second player -5. History of play of the first player -6. History of play of the second player + Interaction index,Player index,Opponent index,Repetition,Player name,Opponent name,Actions,Score,Score difference,Turns,Score per turn,Score difference per turn,Win,Initial cooperation,Cooperation count,CC count,CD count,DC count,DD count,CC to C count,CC to D count,CD to C count,CD to D count,DC to C count,DC to D count,DD to C count,DD to D count,Good partner + 0,0,0,0,Alternator,Alternator,CDCD,8,0,4,2.0,0.0,0,True,2,2,0,0,2,0,2,0,0,0,0,1,0,1 + 0,0,0,0,Alternator,Alternator,CDCD,8,0,4,2.0,0.0,0,True,2,2,0,0,2,0,2,0,0,0,0,1,0,1 + 1,0,0,1,Alternator,Alternator,CDCD,8,0,4,2.0,0.0,0,True,2,2,0,0,2,0,2,0,0,0,0,1,0,1 + 1,0,0,1,Alternator,Alternator,CDCD,8,0,4,2.0,0.0,0,True,2,2,0,0,2,0,2,0,0,0,0,1,0,1 + 2,0,1,0,Alternator,Anti Tit For Tat,CDCD,8,0,4,2.0,0.0,0,True,2,2,0,0,2,0,2,0,0,0,0,1,0,1 + 2,1,0,0,Anti Tit For Tat,Alternator,CDCD,8,0,4,2.0,0.0,0,True,2,2,0,0,2,0,2,0,0,0,0,1,0,1 + 3,0,1,1,Alternator,Anti Tit For Tat,CDCD,8,0,4,2.0,0.0,0,True,2,2,0,0,2,0,2,0,0,0,0,1,0,1 + 3,1,0,1,Anti Tit For Tat,Alternator,CDCD,8,0,4,2.0,0.0,0,True,2,2,0,0,2,0,2,0,0,0,0,1,0,1 + 4,0,2,0,Alternator,Bully,CDCD,5,-5,4,1.25,-1.25,0,True,2,1,1,0,2,0,1,0,1,0,0,1,0,1 + 4,2,0,0,Bully,Alternator,DDCD,10,5,4,2.5,1.25,1,False,1,1,0,1,2,0,1,0,0,0,1,1,0,0 + 5,0,2,1,Alternator,Bully,CDCD,5,-5,4,1.25,-1.25,0,True,2,1,1,0,2,0,1,0,1,0,0,1,0,1 + 5,2,0,1,Bully,Alternator,DDCD,10,5,4,2.5,1.25,1,False,1,1,0,1,2,0,1,0,0,0,1,1,0,0 + 6,0,3,0,Alternator,Cooperator,CDCD,16,10,4,4.0,2.5,1,True,2,2,0,2,0,0,2,0,0,1,0,0,0,0 + 6,3,0,0,Cooperator,Alternator,CCCC,6,-10,4,1.5,-2.5,0,True,4,2,2,0,0,2,0,1,0,0,0,0,0,1 + 7,0,3,1,Alternator,Cooperator,CDCD,16,10,4,4.0,2.5,1,True,2,2,0,2,0,0,2,0,0,1,0,0,0,0 + 7,3,0,1,Cooperator,Alternator,CCCC,6,-10,4,1.5,-2.5,0,True,4,2,2,0,0,2,0,1,0,0,0,0,0,1 + 8,0,4,0,Alternator,Cycler DC,CDCD,10,0,4,2.5,0.0,0,True,2,0,2,2,0,0,0,0,2,1,0,0,0,1 + 8,4,0,0,Cycler DC,Alternator,DCDC,10,0,4,2.5,0.0,0,False,2,0,2,2,0,0,0,0,1,2,0,0,0,1 + 9,0,4,1,Alternator,Cycler DC,CDCD,10,0,4,2.5,0.0,0,True,2,0,2,2,0,0,0,0,2,1,0,0,0,1 + 9,4,0,1,Cycler DC,Alternator,DCDC,10,0,4,2.5,0.0,0,False,2,0,2,2,0,0,0,0,1,2,0,0,0,1 + 10,0,5,0,Alternator,Defector,CDCD,2,-10,4,0.5,-2.5,0,True,2,0,2,0,2,0,0,0,2,0,0,1,0,1 + 10,5,0,0,Defector,Alternator,DDDD,12,10,4,3.0,2.5,1,False,0,0,0,2,2,0,0,0,0,0,2,0,1,0 + 11,0,5,1,Alternator,Defector,CDCD,2,-10,4,0.5,-2.5,0,True,2,0,2,0,2,0,0,0,2,0,0,1,0,1 + 11,5,0,1,Defector,Alternator,DDDD,12,10,4,3.0,2.5,1,False,0,0,0,2,2,0,0,0,0,0,2,0,1,0 + 12,0,6,0,Alternator,Grudger,CDCD,9,0,4,2.25,0.0,0,True,2,1,1,1,1,0,1,0,1,1,0,0,0,1 + 12,6,0,0,Grudger,Alternator,CCDD,9,0,4,2.25,0.0,0,True,2,1,1,1,1,1,0,0,1,0,1,0,0,1 + 13,0,6,1,Alternator,Grudger,CDCD,9,0,4,2.25,0.0,0,True,2,1,1,1,1,0,1,0,1,1,0,0,0,1 + 13,6,0,1,Grudger,Alternator,CCDD,9,0,4,2.25,0.0,0,True,2,1,1,1,1,1,0,0,1,0,1,0,0,1 + 14,0,7,0,Alternator,Suspicious Tit For Tat,CDCD,10,0,4,2.5,0.0,0,True,2,0,2,2,0,0,0,0,2,1,0,0,0,1 + 14,7,0,0,Suspicious Tit For Tat,Alternator,DCDC,10,0,4,2.5,0.0,0,False,2,0,2,2,0,0,0,0,1,2,0,0,0,1 + 15,0,7,1,Alternator,Suspicious Tit For Tat,CDCD,10,0,4,2.5,0.0,0,True,2,0,2,2,0,0,0,0,2,1,0,0,0,1 + 15,7,0,1,Suspicious Tit For Tat,Alternator,DCDC,10,0,4,2.5,0.0,0,False,2,0,2,2,0,0,0,0,1,2,0,0,0,1 + 16,0,8,0,Alternator,Tit For Tat,CDCD,13,5,4,3.25,1.25,1,True,2,1,1,2,0,0,1,0,1,1,0,0,0,0 + 16,8,0,0,Tit For Tat,Alternator,CCDC,8,-5,4,2.0,-1.25,0,True,3,1,2,1,0,1,0,0,1,1,0,0,0,1 + 17,0,8,1,Alternator,Tit For Tat,CDCD,13,5,4,3.25,1.25,1,True,2,1,1,2,0,0,1,0,1,1,0,0,0,0 + 17,8,0,1,Tit For Tat,Alternator,CCDC,8,-5,4,2.0,-1.25,0,True,3,1,2,1,0,1,0,0,1,1,0,0,0,1 + 18,0,9,0,Alternator,Win-Shift Lose-Stay: D,CDCD,9,0,4,2.25,0.0,0,True,2,1,1,1,1,0,1,0,1,1,0,0,0,1 + 18,9,0,0,Win-Shift Lose-Stay: D,Alternator,DCCD,9,0,4,2.25,0.0,0,False,2,1,1,1,1,0,1,1,0,1,0,0,0,1 + 19,0,9,1,Alternator,Win-Shift Lose-Stay: D,CDCD,9,0,4,2.25,0.0,0,True,2,1,1,1,1,0,1,0,1,1,0,0,0,1 + 19,9,0,1,Win-Shift Lose-Stay: D,Alternator,DCCD,9,0,4,2.25,0.0,0,False,2,1,1,1,1,0,1,1,0,1,0,0,0,1 + 20,0,10,0,Alternator,Win-Stay Lose-Shift: C,CDCD,9,0,4,2.25,0.0,0,True,2,1,1,1,1,0,1,0,1,1,0,0,0,1 + 20,10,0,0,Win-Stay Lose-Shift: C,Alternator,CCDD,9,0,4,2.25,0.0,0,True,2,1,1,1,1,1,0,0,1,0,1,0,0,1 + 21,0,10,1,Alternator,Win-Stay Lose-Shift: C,CDCD,9,0,4,2.25,0.0,0,True,2,1,1,1,1,0,1,0,1,1,0,0,0,1 + 21,10,0,1,Win-Stay Lose-Shift: C,Alternator,CCDD,9,0,4,2.25,0.0,0,True,2,1,1,1,1,1,0,0,1,0,1,0,0,1 + 22,1,1,0,Anti Tit For Tat,Anti Tit For Tat,CDCD,8,0,4,2.0,0.0,0,True,2,2,0,0,2,0,2,0,0,0,0,1,0,1 + 22,1,1,0,Anti Tit For Tat,Anti Tit For Tat,CDCD,8,0,4,2.0,0.0,0,True,2,2,0,0,2,0,2,0,0,0,0,1,0,1 + 23,1,1,1,Anti Tit For Tat,Anti Tit For Tat,CDCD,8,0,4,2.0,0.0,0,True,2,2,0,0,2,0,2,0,0,0,0,1,0,1 + 23,1,1,1,Anti Tit For Tat,Anti Tit For Tat,CDCD,8,0,4,2.0,0.0,0,True,2,2,0,0,2,0,2,0,0,0,0,1,0,1 + 24,1,2,0,Anti Tit For Tat,Bully,CCCC,0,-20,4,0.0,-5.0,0,True,4,0,4,0,0,0,0,3,0,0,0,0,0,1 + 24,2,1,0,Bully,Anti Tit For Tat,DDDD,20,20,4,5.0,5.0,1,False,0,0,0,4,0,0,0,0,0,0,3,0,0,0 + 25,1,2,1,Anti Tit For Tat,Bully,CCCC,0,-20,4,0.0,-5.0,0,True,4,0,4,0,0,0,0,3,0,0,0,0,0,1 + 25,2,1,1,Bully,Anti Tit For Tat,DDDD,20,20,4,5.0,5.0,1,False,0,0,0,4,0,0,0,0,0,0,3,0,0,0 + 26,1,3,0,Anti Tit For Tat,Cooperator,CDDD,18,15,4,4.5,3.75,1,True,1,1,0,3,0,0,1,0,0,0,2,0,0,0 + 26,3,1,0,Cooperator,Anti Tit For Tat,CCCC,3,-15,4,0.75,-3.75,0,True,4,1,3,0,0,1,0,2,0,0,0,0,0,1 + 27,1,3,1,Anti Tit For Tat,Cooperator,CDDD,18,15,4,4.5,3.75,1,True,1,1,0,3,0,0,1,0,0,0,2,0,0,0 + 27,3,1,1,Cooperator,Anti Tit For Tat,CCCC,3,-15,4,0.75,-3.75,0,True,4,1,3,0,0,1,0,2,0,0,0,0,0,1 + 28,1,4,0,Anti Tit For Tat,Cycler DC,CCDC,7,-5,4,1.75,-1.25,0,True,3,2,1,0,1,0,1,1,0,0,0,1,0,1 + 28,4,1,0,Cycler DC,Anti Tit For Tat,DCDC,12,5,4,3.0,1.25,1,False,2,2,0,1,1,0,1,0,0,1,0,1,0,0 + 29,1,4,1,Anti Tit For Tat,Cycler DC,CCDC,7,-5,4,1.75,-1.25,0,True,3,2,1,0,1,0,1,1,0,0,0,1,0,1 + 29,4,1,1,Cycler DC,Anti Tit For Tat,DCDC,12,5,4,3.0,1.25,1,False,2,2,0,1,1,0,1,0,0,1,0,1,0,0 + 30,1,5,0,Anti Tit For Tat,Defector,CCCC,0,-20,4,0.0,-5.0,0,True,4,0,4,0,0,0,0,3,0,0,0,0,0,1 + 30,5,1,0,Defector,Anti Tit For Tat,DDDD,20,20,4,5.0,5.0,1,False,0,0,0,4,0,0,0,0,0,0,3,0,0,0 + 31,1,5,1,Anti Tit For Tat,Defector,CCCC,0,-20,4,0.0,-5.0,0,True,4,0,4,0,0,0,0,3,0,0,0,0,0,1 + 31,5,1,1,Defector,Anti Tit For Tat,DDDD,20,20,4,5.0,5.0,1,False,0,0,0,4,0,0,0,0,0,0,3,0,0,0 + 32,1,6,0,Anti Tit For Tat,Grudger,CDDC,9,0,4,2.25,0.0,0,True,2,1,1,1,1,0,1,0,0,0,1,1,0,1 + 32,6,1,0,Grudger,Anti Tit For Tat,CCDD,9,0,4,2.25,0.0,0,True,2,1,1,1,1,1,0,0,1,0,0,0,1,1 + 33,1,6,1,Anti Tit For Tat,Grudger,CDDC,9,0,4,2.25,0.0,0,True,2,1,1,1,1,0,1,0,0,0,1,1,0,1 + 33,6,1,1,Grudger,Anti Tit For Tat,CCDD,9,0,4,2.25,0.0,0,True,2,1,1,1,1,1,0,0,1,0,0,0,1,1 + 34,1,7,0,Anti Tit For Tat,Suspicious Tit For Tat,CCDD,9,0,4,2.25,0.0,0,True,2,1,1,1,1,0,1,1,0,0,1,0,0,1 + 34,7,1,0,Suspicious Tit For Tat,Anti Tit For Tat,DCCD,9,0,4,2.25,0.0,0,False,2,1,1,1,1,1,0,0,1,1,0,0,0,1 + 35,1,7,1,Anti Tit For Tat,Suspicious Tit For Tat,CCDD,9,0,4,2.25,0.0,0,True,2,1,1,1,1,0,1,1,0,0,1,0,0,1 + 35,7,1,1,Suspicious Tit For Tat,Anti Tit For Tat,DCCD,9,0,4,2.25,0.0,0,False,2,1,1,1,1,1,0,0,1,1,0,0,0,1 + ... Note that depending on the order in which the matches have been played, the rows could also be in a different order. -:code:`Alternator` versus :code:`TitForTat` has the following interactions: -:code:`CCDC, CDCD`: +It is possible to read in this data file to obtain interactions:: -- First turn: :code:`C` versus :code:`C` (the first two letters) -- Second turn: :code:`D` versus :code:`C` (the second pair of letters) -- Third turn: :code:`C` versus :code:`D` (the third pair of letters) -- Fourth turn: :code:`D` versus :code:`C` (the fourth pair of letters) + >>> interactions = axl.interaction_utils.read_interactions_from_file("basic_tournament.csv") -This can be transformed in to the usual interactions by zipping: +This gives a dictionary mapping pairs of player indices to interaction +histories:: - >>> from axelrod.action import str_to_actions - >>> list(zip(str_to_actions("CCDC"), str_to_actions("CDCD"))) - [(C, C), (C, D), (D, C), (C, D)] + >>> interactions[(0, 1)] + [[(C, C), (D, D), (C, C), (D, D)], [(C, C), (D, D), (C, C), (D, D)]] This should allow for easy manipulation of data outside of the capabilities -within the library. Note that you can supply `build_results=False` as a keyword +within the library. + +Note that you can supply `build_results=False` as a keyword argument to `tournament.play()` to prevent keeping or loading interactions in memory, since the total memory footprint can be large for various combinations -of parameters. The memory usage scales as :math:`O(\text{players}^2 * \text{turns} * \text{repetitions})`. - -It is also possible to generate a standard result set from a datafile:: - - >>> results = axl.ResultSetFromFile(filename="basic_tournament.csv") - >>> results.ranked_names # doctest: +SKIP - ['Defector', - 'Bully', - 'Suspicious Tit For Tat', - 'Alternator', - 'Tit For Tat', - 'Anti Tit For Tat', - 'Win-Stay Lose-Shift', - 'Cooperator'] - +of parameters. The memory usage scales as :math:`O(\text{players}^2 \times \text{turns} \times \text{repetitions})`. diff --git a/docs/tutorials/advanced/tournament_results.rst b/docs/tutorials/advanced/tournament_results.rst index e94156e9b..f2778ebfa 100644 --- a/docs/tutorials/advanced/tournament_results.rst +++ b/docs/tutorials/advanced/tournament_results.rst @@ -65,9 +65,19 @@ This gives the length of the matches played by each player:: >>> import pprint # Nicer formatting of output >>> pprint.pprint(results.match_lengths) - [[[10, 10, 10, 10], [10, 10, 10, 10], [10, 10, 10, 10], [10, 10, 10, 10]], - [[10, 10, 10, 10], [10, 10, 10, 10], [10, 10, 10, 10], [10, 10, 10, 10]], - [[10, 10, 10, 10], [10, 10, 10, 10], [10, 10, 10, 10], [10, 10, 10, 10]]] + [[[10.0, 10.0, 10.0, 10.0], + [10.0, 10.0, 10.0, 10.0], + [10.0, 10.0, 10.0, 10.0], + [10.0, 10.0, 10.0, 10.0]], + [[10.0, 10.0, 10.0, 10.0], + [10.0, 10.0, 10.0, 10.0], + [10.0, 10.0, 10.0, 10.0], + [10.0, 10.0, 10.0, 10.0]], + [[10.0, 10.0, 10.0, 10.0], + [10.0, 10.0, 10.0, 10.0], + [10.0, 10.0, 10.0, 10.0], + [10.0, 10.0, 10.0, 10.0]]] + Every player plays 10 turns against every other player (including themselves) for every repetition of the tournament. @@ -119,7 +129,7 @@ Payoffs This gives for each player, against each opponent every payoff received for each repetition:: - >>> pprint.pprint(results.payoffs) + >>> pprint.pprint(results.payoffs) # doctest: +SKIP [[[3.0, 3.0, 3.0], [0.0, 0.0, 0.0], [3.0, 3.0, 3.0], [3.0, 3.0, 3.0]], [[5.0, 5.0, 5.0], [1.0, 1.0, 1.0], [1.4, 1.4, 1.4], [1.4, 1.4, 1.4]], [[3.0, 3.0, 3.0], [0.9, 0.9, 0.9], [3.0, 3.0, 3.0], [3.0, 3.0, 3.0]], @@ -202,7 +212,7 @@ Cooperation counts This gives a total count of cooperation for each player against each opponent:: >>> results.cooperation - [[0, 30, 30, 30], [0, 0, 0, 0], [30, 3, 0, 30], [30, 3, 30, 0]] + [[30, 30, 30, 30], [0, 0, 0, 0], [30, 3, 30, 30], [30, 3, 30, 30]] Normalised cooperation ---------------------- @@ -262,14 +272,14 @@ the second the action of the opponent:: Counter({(C, C): 1.0})], [Counter({(D, C): 1.0}), Counter(), - Counter({(D, D): 0.9, (D, C): 0.1}), - Counter({(D, D): 0.9, (D, C): 0.1})], + Counter({(D, D): 0.9..., (D, C): 0.1...}), + Counter({(D, D): 0.9..., (D, C): 0.1...})], [Counter({(C, C): 1.0}), - Counter({(D, D): 0.9, (C, D): 0.1}), + Counter({(D, D): 0.9..., (C, D): 0.1...}), Counter(), Counter({(C, C): 1.0})], [Counter({(C, C): 1.0}), - Counter({(D, D): 0.9, (C, D): 0.1}), + Counter({(D, D): 0.9..., (C, D): 0.1...}), Counter({(C, C): 1.0}), Counter()]] @@ -333,7 +343,7 @@ This gives the count of cooperations made by each player during the first turn of every match:: >>> results.initial_cooperation_count - [9, 0, 9, 9] + [9.0, 0.0, 9.0, 9.0] Each player plays an opponent a total of 9 times (3 opponents and 3 repetitions). Apart from the :code:`Defector`, they all cooperate on the first @@ -353,7 +363,7 @@ Morality Metrics The following morality metrics are available, they are calculated as a function of the cooperation rating:: - >>> results.cooperating_rating + >>> results.cooperating_rating # doctest: +SKIP [1.0, 0.0, 0.7, 0.7] >>> pprint.pprint(results.vengeful_cooperation) # doctest: +SKIP [[1.0, 1.0, 1.0, 1.0], diff --git a/docs/tutorials/further_topics/morality_metrics.rst b/docs/tutorials/further_topics/morality_metrics.rst index 11bfcf040..090314074 100644 --- a/docs/tutorials/further_topics/morality_metrics.rst +++ b/docs/tutorials/further_topics/morality_metrics.rst @@ -38,7 +38,7 @@ least as much as its opponent:: Each of the metrics described in Tyler's paper is available as follows (here they are rounded to 2 digits):: >>> [round(ele, 2) for ele in results.cooperating_rating] - [1.0, 0.0, 0.67, 0.67] + [1.0, 0.0, 0.67..., 0.67...] >>> [round(ele, 2) for ele in results.good_partner_rating] [1.0, 0.0, 1.0, 1.0] >>> [round(ele, 2) for ele in results.eigenjesus_rating] diff --git a/docs/tutorials/further_topics/spatial_tournaments.rst b/docs/tutorials/further_topics/spatial_tournaments.rst index 8e922adf1..3bb369ab2 100644 --- a/docs/tutorials/further_topics/spatial_tournaments.rst +++ b/docs/tutorials/further_topics/spatial_tournaments.rst @@ -30,7 +30,7 @@ To create a spatial tournament you pass the :code:`edges` to the :code:`Tournament` class:: >>> spatial_tournament = axl.Tournament(players, edges=edges) - >>> results = spatial_tournament.play(keep_interactions=True) + >>> results = spatial_tournament.play() We can plot the results:: @@ -47,33 +47,23 @@ We can, like any other tournament, obtain the ranks for our players:: >>> results.ranked_names ['Cooperator', 'Tit For Tat', 'Grudger', 'Defector'] -Let's run a small tournament of 2 :code:`turns` and 5 :code:`repetitions` +Let's run a small tournament of 2 :code:`turns` and 2 :code:`repetitions` and obtain the interactions:: >>> spatial_tournament = axl.Tournament(players ,turns=2, repetitions=2, edges=edges) - >>> results = spatial_tournament.play(keep_interactions=True) - >>> for index_pair, interaction in sorted(results.interactions.items()): - ... player1 = spatial_tournament.players[index_pair[0]] - ... player2 = spatial_tournament.players[index_pair[1]] - ... print('%s vs %s: %s' % (player1, player2, interaction)) - Cooperator vs Tit For Tat: [[(C, C), (C, C)], [(C, C), (C, C)]] - Cooperator vs Grudger: [[(C, C), (C, C)], [(C, C), (C, C)]] - Defector vs Tit For Tat: [[(D, C), (D, D)], [(D, C), (D, D)]] - Defector vs Grudger: [[(D, C), (D, D)], [(D, C), (D, D)]] - -As anticipated :code:`Cooperator` does not interact with :code:`Defector` neither -:code:`TitForTat` with :code:`Grudger`. + >>> results = spatial_tournament.play() + >>> results.payoffs + [[[], [], [3.0, 3.0], [3.0, 3.0]], [[], [], [3.0, 3.0], [3.0, 3.0]], [[3.0, 3.0], [0.5, 0.5], [], []], [[3.0, 3.0], [0.5, 0.5], [], []]] + +As anticipated not all players interact with each other. It is also possible to create a probabilistic ending spatial tournament:: >>> prob_end_spatial_tournament = axl.Tournament(players, edges=edges, prob_end=.1, repetitions=1) - >>> prob_end_results = prob_end_spatial_tournament.play(keep_interactions=True) + >>> axl.seed(0) + >>> prob_end_results = prob_end_spatial_tournament.play() We see that the match lengths are no longer all equal:: - >>> axl.seed(0) - >>> lengths = [] - >>> for interaction in prob_end_results.interactions.values(): - ... lengths.append(len(interaction[0])) - >>> min(lengths) != max(lengths) - True + >>> prob_end_results.match_lengths + [[[0, 0, 18.0, 14.0], [0, 0, 6.0, 3.0], [18.0, 6.0, 0, 0], [14.0, 3.0, 0, 0]]] diff --git a/docs/tutorials/getting_started/index.rst b/docs/tutorials/getting_started/index.rst index 83843e140..8186ed97a 100644 --- a/docs/tutorials/getting_started/index.rst +++ b/docs/tutorials/getting_started/index.rst @@ -13,7 +13,6 @@ Contents: match.rst tournament.rst summarising_tournaments.rst - interactions.rst visualising_results.rst moran.rst human_interaction.rst diff --git a/docs/tutorials/getting_started/interactions.rst b/docs/tutorials/getting_started/interactions.rst deleted file mode 100644 index d820655fa..000000000 --- a/docs/tutorials/getting_started/interactions.rst +++ /dev/null @@ -1,66 +0,0 @@ -Accessing the interactions -========================== - -This tutorial will show you briefly how to access the detailed interaction -results corresponding to the tournament. - -To access the detailed interaction results we create a tournament as usual -(see :ref:`creating_tournaments`) but indicate that we want to keep track of the -interactions:: - - >>> import axelrod as axl - >>> players = [ - ... axl.Cooperator(), axl.Defector(), - ... axl.TitForTat(), axl.Grudger()] - >>> tournament = axl.Tournament(players, turns=3, repetitions=1) - >>> results = tournament.play(keep_interactions=True) - -If the play method is called with :code:`keep_interactions=True`, the result set -object will have an :code:`interactions` attribute which contains all the -interactions between the players. These can be used to -view the history of the interactions:: - - >>> for index_pair, interaction in sorted(results.interactions.items()): - ... player1 = tournament.players[index_pair[0]] - ... player2 = tournament.players[index_pair[1]] - ... print('%s vs %s: %s' % (player1, player2, interaction[0])) - Cooperator vs Cooperator: [(C, C), (C, C), (C, C)] - Cooperator vs Defector: [(C, D), (C, D), (C, D)] - Cooperator vs Tit For Tat: [(C, C), (C, C), (C, C)] - Cooperator vs Grudger: [(C, C), (C, C), (C, C)] - Defector vs Defector: [(D, D), (D, D), (D, D)] - Defector vs Tit For Tat: [(D, C), (D, D), (D, D)] - Defector vs Grudger: [(D, C), (D, D), (D, D)] - Tit For Tat vs Tit For Tat: [(C, C), (C, C), (C, C)] - Tit For Tat vs Grudger: [(C, C), (C, C), (C, C)] - Grudger vs Grudger: [(C, C), (C, C), (C, C)] - -We can use these interactions to reconstruct :code:`axelrod.Match` objects which -have a variety of available methods for analysis (more information can be found -in :ref:`creating_matches`):: - - >>> matches = [] - >>> for index_pair, interaction in sorted(results.interactions.items()): - ... player1 = tournament.players[index_pair[0]] - ... player2 = tournament.players[index_pair[1]] - ... match = axl.Match([player1, player2], turns=3) - ... match.result = interaction[0] - ... matches.append(match) - >>> len(matches) - 10 - -As an example let us view all winners of each match (:code:`False` indicates a -tie):: - - >>> for match in matches: - ... print("{} v {}, winner: {}".format(match.players[0], match.players[1], match.winner())) - Cooperator v Cooperator, winner: False - Cooperator v Defector, winner: Defector - Cooperator v Tit For Tat, winner: False - Cooperator v Grudger, winner: False - Defector v Defector, winner: False - Defector v Tit For Tat, winner: Defector - Defector v Grudger, winner: Defector - Tit For Tat v Tit For Tat, winner: False - Tit For Tat v Grudger, winner: False - Grudger v Grudger, winner: False diff --git a/docs/tutorials/getting_started/summarising_tournaments.rst b/docs/tutorials/getting_started/summarising_tournaments.rst index 0314dae88..ebfb39b08 100644 --- a/docs/tutorials/getting_started/summarising_tournaments.rst +++ b/docs/tutorials/getting_started/summarising_tournaments.rst @@ -19,8 +19,8 @@ that summarises the results of the tournament:: >>> import pprint >>> pprint.pprint(summary) [Player(Rank=0, Name='Defector', Median_score=2.6..., Cooperation_rating=0.0, Wins=3.0, Initial_C_rate=0.0, CC_rate=...), - Player(Rank=1, Name='Tit For Tat', Median_score=2.3..., Cooperation_rating=0.7, Wins=0.0, Initial_C_rate=1.0, CC_rate=...), - Player(Rank=2, Name='Grudger', Median_score=2.3..., Cooperation_rating=0.7, Wins=0.0, Initial_C_rate=1.0, CC_rate=...), + Player(Rank=1, Name='Tit For Tat', Median_score=2.3..., Cooperation_rating=0..., Wins=0.0, Initial_C_rate=1.0, CC_rate=...), + Player(Rank=2, Name='Grudger', Median_score=2.3..., Cooperation_rating=0..., Wins=0.0, Initial_C_rate=1.0, CC_rate=...), Player(Rank=3, Name='Cooperator', Median_score=2.0..., Cooperation_rating=1.0, Wins=0.0, Initial_C_rate=1.0, CC_rate=...)] It is also possible to write this data directly to a csv file using the diff --git a/requirements.txt b/requirements.txt index 1f9449d7e..b3ac362f6 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,3 +4,7 @@ tqdm>=3.4.0 prompt-toolkit>=1.0.7 scipy>=0.19.0 hypothesis==3.2 +dask>=0.11.0 +pandas>=0.18.1 +toolz>=0.8.0 +cloudpickle>=0.2.1 diff --git a/test_outputs/expected_test_tournament.csv b/test_outputs/expected_test_tournament.csv new file mode 100644 index 000000000..643161739 --- /dev/null +++ b/test_outputs/expected_test_tournament.csv @@ -0,0 +1,61 @@ +Interaction index,Player index,Opponent index,Repetition,Player name,Opponent name,Actions,Score,Score difference,Turns,Score per turn,Score difference per turn,Win,Initial cooperation,Cooperation count,CC count,CD count,DC count,DD count,CC to C count,CC to D count,CD to C count,CD to D count,DC to C count,DC to D count,DD to C count,DD to D count,Good partner +0,0,0,0,Cooperator,Cooperator,CC,6,0,2,3.0,0.0,0,True,2,2,0,0,0,1,0,0,0,0,0,0,0,1 +0,0,0,0,Cooperator,Cooperator,CC,6,0,2,3.0,0.0,0,True,2,2,0,0,0,1,0,0,0,0,0,0,0,1 +1,0,0,1,Cooperator,Cooperator,CC,6,0,2,3.0,0.0,0,True,2,2,0,0,0,1,0,0,0,0,0,0,0,1 +1,0,0,1,Cooperator,Cooperator,CC,6,0,2,3.0,0.0,0,True,2,2,0,0,0,1,0,0,0,0,0,0,0,1 +2,0,1,0,Cooperator,Tit For Tat,CC,6,0,2,3.0,0.0,0,True,2,2,0,0,0,1,0,0,0,0,0,0,0,1 +2,1,0,0,Tit For Tat,Cooperator,CC,6,0,2,3.0,0.0,0,True,2,2,0,0,0,1,0,0,0,0,0,0,0,1 +3,0,1,1,Cooperator,Tit For Tat,CC,6,0,2,3.0,0.0,0,True,2,2,0,0,0,1,0,0,0,0,0,0,0,1 +3,1,0,1,Tit For Tat,Cooperator,CC,6,0,2,3.0,0.0,0,True,2,2,0,0,0,1,0,0,0,0,0,0,0,1 +4,0,2,0,Cooperator,Defector,CC,0,-10,2,0.0,-5.0,0,True,2,0,2,0,0,0,0,1,0,0,0,0,0,1 +4,2,0,0,Defector,Cooperator,DD,10,10,2,5.0,5.0,1,False,0,0,0,2,0,0,0,0,0,0,1,0,0,0 +5,0,2,1,Cooperator,Defector,CC,0,-10,2,0.0,-5.0,0,True,2,0,2,0,0,0,0,1,0,0,0,0,0,1 +5,2,0,1,Defector,Cooperator,DD,10,10,2,5.0,5.0,1,False,0,0,0,2,0,0,0,0,0,0,1,0,0,0 +6,0,3,0,Cooperator,Grudger,CC,6,0,2,3.0,0.0,0,True,2,2,0,0,0,1,0,0,0,0,0,0,0,1 +6,3,0,0,Grudger,Cooperator,CC,6,0,2,3.0,0.0,0,True,2,2,0,0,0,1,0,0,0,0,0,0,0,1 +7,0,3,1,Cooperator,Grudger,CC,6,0,2,3.0,0.0,0,True,2,2,0,0,0,1,0,0,0,0,0,0,0,1 +7,3,0,1,Grudger,Cooperator,CC,6,0,2,3.0,0.0,0,True,2,2,0,0,0,1,0,0,0,0,0,0,0,1 +8,0,4,0,Cooperator,Soft Go By Majority,CC,6,0,2,3.0,0.0,0,True,2,2,0,0,0,1,0,0,0,0,0,0,0,1 +8,4,0,0,Soft Go By Majority,Cooperator,CC,6,0,2,3.0,0.0,0,True,2,2,0,0,0,1,0,0,0,0,0,0,0,1 +9,0,4,1,Cooperator,Soft Go By Majority,CC,6,0,2,3.0,0.0,0,True,2,2,0,0,0,1,0,0,0,0,0,0,0,1 +9,4,0,1,Soft Go By Majority,Cooperator,CC,6,0,2,3.0,0.0,0,True,2,2,0,0,0,1,0,0,0,0,0,0,0,1 +10,1,1,0,Tit For Tat,Tit For Tat,CC,6,0,2,3.0,0.0,0,True,2,2,0,0,0,1,0,0,0,0,0,0,0,1 +10,1,1,0,Tit For Tat,Tit For Tat,CC,6,0,2,3.0,0.0,0,True,2,2,0,0,0,1,0,0,0,0,0,0,0,1 +11,1,1,1,Tit For Tat,Tit For Tat,CC,6,0,2,3.0,0.0,0,True,2,2,0,0,0,1,0,0,0,0,0,0,0,1 +11,1,1,1,Tit For Tat,Tit For Tat,CC,6,0,2,3.0,0.0,0,True,2,2,0,0,0,1,0,0,0,0,0,0,0,1 +12,1,2,0,Tit For Tat,Defector,CD,1,-5,2,0.5,-2.5,0,True,1,0,1,0,1,0,0,0,1,0,0,0,0,1 +12,2,1,0,Defector,Tit For Tat,DD,6,5,2,3.0,2.5,1,False,0,0,0,1,1,0,0,0,0,0,1,0,0,0 +13,1,2,1,Tit For Tat,Defector,CD,1,-5,2,0.5,-2.5,0,True,1,0,1,0,1,0,0,0,1,0,0,0,0,1 +13,2,1,1,Defector,Tit For Tat,DD,6,5,2,3.0,2.5,1,False,0,0,0,1,1,0,0,0,0,0,1,0,0,0 +14,1,3,0,Tit For Tat,Grudger,CC,6,0,2,3.0,0.0,0,True,2,2,0,0,0,1,0,0,0,0,0,0,0,1 +14,3,1,0,Grudger,Tit For Tat,CC,6,0,2,3.0,0.0,0,True,2,2,0,0,0,1,0,0,0,0,0,0,0,1 +15,1,3,1,Tit For Tat,Grudger,CC,6,0,2,3.0,0.0,0,True,2,2,0,0,0,1,0,0,0,0,0,0,0,1 +15,3,1,1,Grudger,Tit For Tat,CC,6,0,2,3.0,0.0,0,True,2,2,0,0,0,1,0,0,0,0,0,0,0,1 +16,1,4,0,Tit For Tat,Soft Go By Majority,CC,6,0,2,3.0,0.0,0,True,2,2,0,0,0,1,0,0,0,0,0,0,0,1 +16,4,1,0,Soft Go By Majority,Tit For Tat,CC,6,0,2,3.0,0.0,0,True,2,2,0,0,0,1,0,0,0,0,0,0,0,1 +17,1,4,1,Tit For Tat,Soft Go By Majority,CC,6,0,2,3.0,0.0,0,True,2,2,0,0,0,1,0,0,0,0,0,0,0,1 +17,4,1,1,Soft Go By Majority,Tit For Tat,CC,6,0,2,3.0,0.0,0,True,2,2,0,0,0,1,0,0,0,0,0,0,0,1 +18,2,2,0,Defector,Defector,DD,2,0,2,1.0,0.0,0,False,0,0,0,0,2,0,0,0,0,0,0,0,1,1 +18,2,2,0,Defector,Defector,DD,2,0,2,1.0,0.0,0,False,0,0,0,0,2,0,0,0,0,0,0,0,1,1 +19,2,2,1,Defector,Defector,DD,2,0,2,1.0,0.0,0,False,0,0,0,0,2,0,0,0,0,0,0,0,1,1 +19,2,2,1,Defector,Defector,DD,2,0,2,1.0,0.0,0,False,0,0,0,0,2,0,0,0,0,0,0,0,1,1 +20,2,3,0,Defector,Grudger,DD,6,5,2,3.0,2.5,1,False,0,0,0,1,1,0,0,0,0,0,1,0,0,0 +20,3,2,0,Grudger,Defector,CD,1,-5,2,0.5,-2.5,0,True,1,0,1,0,1,0,0,0,1,0,0,0,0,1 +21,2,3,1,Defector,Grudger,DD,6,5,2,3.0,2.5,1,False,0,0,0,1,1,0,0,0,0,0,1,0,0,0 +21,3,2,1,Grudger,Defector,CD,1,-5,2,0.5,-2.5,0,True,1,0,1,0,1,0,0,0,1,0,0,0,0,1 +22,2,4,0,Defector,Soft Go By Majority,DD,6,5,2,3.0,2.5,1,False,0,0,0,1,1,0,0,0,0,0,1,0,0,0 +22,4,2,0,Soft Go By Majority,Defector,CD,1,-5,2,0.5,-2.5,0,True,1,0,1,0,1,0,0,0,1,0,0,0,0,1 +23,2,4,1,Defector,Soft Go By Majority,DD,6,5,2,3.0,2.5,1,False,0,0,0,1,1,0,0,0,0,0,1,0,0,0 +23,4,2,1,Soft Go By Majority,Defector,CD,1,-5,2,0.5,-2.5,0,True,1,0,1,0,1,0,0,0,1,0,0,0,0,1 +24,3,3,0,Grudger,Grudger,CC,6,0,2,3.0,0.0,0,True,2,2,0,0,0,1,0,0,0,0,0,0,0,1 +24,3,3,0,Grudger,Grudger,CC,6,0,2,3.0,0.0,0,True,2,2,0,0,0,1,0,0,0,0,0,0,0,1 +25,3,3,1,Grudger,Grudger,CC,6,0,2,3.0,0.0,0,True,2,2,0,0,0,1,0,0,0,0,0,0,0,1 +25,3,3,1,Grudger,Grudger,CC,6,0,2,3.0,0.0,0,True,2,2,0,0,0,1,0,0,0,0,0,0,0,1 +26,3,4,0,Grudger,Soft Go By Majority,CC,6,0,2,3.0,0.0,0,True,2,2,0,0,0,1,0,0,0,0,0,0,0,1 +26,4,3,0,Soft Go By Majority,Grudger,CC,6,0,2,3.0,0.0,0,True,2,2,0,0,0,1,0,0,0,0,0,0,0,1 +27,3,4,1,Grudger,Soft Go By Majority,CC,6,0,2,3.0,0.0,0,True,2,2,0,0,0,1,0,0,0,0,0,0,0,1 +27,4,3,1,Soft Go By Majority,Grudger,CC,6,0,2,3.0,0.0,0,True,2,2,0,0,0,1,0,0,0,0,0,0,0,1 +28,4,4,0,Soft Go By Majority,Soft Go By Majority,CC,6,0,2,3.0,0.0,0,True,2,2,0,0,0,1,0,0,0,0,0,0,0,1 +28,4,4,0,Soft Go By Majority,Soft Go By Majority,CC,6,0,2,3.0,0.0,0,True,2,2,0,0,0,1,0,0,0,0,0,0,0,1 +29,4,4,1,Soft Go By Majority,Soft Go By Majority,CC,6,0,2,3.0,0.0,0,True,2,2,0,0,0,1,0,0,0,0,0,0,0,1 +29,4,4,1,Soft Go By Majority,Soft Go By Majority,CC,6,0,2,3.0,0.0,0,True,2,2,0,0,0,1,0,0,0,0,0,0,0,1 diff --git a/test_outputs/expected_test_tournament_no_results.csv b/test_outputs/expected_test_tournament_no_results.csv new file mode 100644 index 000000000..b44468a7b --- /dev/null +++ b/test_outputs/expected_test_tournament_no_results.csv @@ -0,0 +1,61 @@ +Interaction index,Player index,Opponent index,Repetition,Player name,Opponent name,Actions +0,0,0,0,Cooperator,Cooperator,CC +0,0,0,0,Cooperator,Cooperator,CC +1,0,0,1,Cooperator,Cooperator,CC +1,0,0,1,Cooperator,Cooperator,CC +2,0,1,0,Cooperator,Tit For Tat,CC +2,1,0,0,Tit For Tat,Cooperator,CC +3,0,1,1,Cooperator,Tit For Tat,CC +3,1,0,1,Tit For Tat,Cooperator,CC +4,0,2,0,Cooperator,Defector,CC +4,2,0,0,Defector,Cooperator,DD +5,0,2,1,Cooperator,Defector,CC +5,2,0,1,Defector,Cooperator,DD +6,0,3,0,Cooperator,Grudger,CC +6,3,0,0,Grudger,Cooperator,CC +7,0,3,1,Cooperator,Grudger,CC +7,3,0,1,Grudger,Cooperator,CC +8,0,4,0,Cooperator,Soft Go By Majority,CC +8,4,0,0,Soft Go By Majority,Cooperator,CC +9,0,4,1,Cooperator,Soft Go By Majority,CC +9,4,0,1,Soft Go By Majority,Cooperator,CC +10,1,1,0,Tit For Tat,Tit For Tat,CC +10,1,1,0,Tit For Tat,Tit For Tat,CC +11,1,1,1,Tit For Tat,Tit For Tat,CC +11,1,1,1,Tit For Tat,Tit For Tat,CC +12,1,2,0,Tit For Tat,Defector,CD +12,2,1,0,Defector,Tit For Tat,DD +13,1,2,1,Tit For Tat,Defector,CD +13,2,1,1,Defector,Tit For Tat,DD +14,1,3,0,Tit For Tat,Grudger,CC +14,3,1,0,Grudger,Tit For Tat,CC +15,1,3,1,Tit For Tat,Grudger,CC +15,3,1,1,Grudger,Tit For Tat,CC +16,1,4,0,Tit For Tat,Soft Go By Majority,CC +16,4,1,0,Soft Go By Majority,Tit For Tat,CC +17,1,4,1,Tit For Tat,Soft Go By Majority,CC +17,4,1,1,Soft Go By Majority,Tit For Tat,CC +18,2,2,0,Defector,Defector,DD +18,2,2,0,Defector,Defector,DD +19,2,2,1,Defector,Defector,DD +19,2,2,1,Defector,Defector,DD +20,2,3,0,Defector,Grudger,DD +20,3,2,0,Grudger,Defector,CD +21,2,3,1,Defector,Grudger,DD +21,3,2,1,Grudger,Defector,CD +22,2,4,0,Defector,Soft Go By Majority,DD +22,4,2,0,Soft Go By Majority,Defector,CD +23,2,4,1,Defector,Soft Go By Majority,DD +23,4,2,1,Soft Go By Majority,Defector,CD +24,3,3,0,Grudger,Grudger,CC +24,3,3,0,Grudger,Grudger,CC +25,3,3,1,Grudger,Grudger,CC +25,3,3,1,Grudger,Grudger,CC +26,3,4,0,Grudger,Soft Go By Majority,CC +26,4,3,0,Soft Go By Majority,Grudger,CC +27,3,4,1,Grudger,Soft Go By Majority,CC +27,4,3,1,Soft Go By Majority,Grudger,CC +28,4,4,0,Soft Go By Majority,Soft Go By Majority,CC +28,4,4,0,Soft Go By Majority,Soft Go By Majority,CC +29,4,4,1,Soft Go By Majority,Soft Go By Majority,CC +29,4,4,1,Soft Go By Majority,Soft Go By Majority,CC diff --git a/test_outputs/test_results.csv b/test_outputs/test_results.csv new file mode 100644 index 000000000..6055c18d4 --- /dev/null +++ b/test_outputs/test_results.csv @@ -0,0 +1,19 @@ +Interaction index,Player index,Opponent index,Repetition,Player name,Opponent name,Actions,Score,Score difference,Turns,Score per turn,Score difference per turn,Win,Initial cooperation,Cooperation count,CC count,CD count,DC count,DD count,CC to C count,CC to D count,CD to C count,CD to D count,DC to C count,DC to D count,DD to C count,DD to D count,Good partner +0,0,1,0,Alternator,Tit For Tat,CDCDC,13,0,5,2.6,0.0,0,True,3,1,2,2,0,0,1,0,1,2,0,0,0,1 +0,1,0,0,Tit For Tat,Alternator,CCDCD,13,0,5,2.6,0.0,0,True,3,1,2,2,0,1,0,0,2,1,0,0,0,1 +1,0,1,1,Alternator,Tit For Tat,CDCDC,13,0,5,2.6,0.0,0,True,3,1,2,2,0,0,1,0,1,2,0,0,0,1 +1,1,0,1,Tit For Tat,Alternator,CCDCD,13,0,5,2.6,0.0,0,True,3,1,2,2,0,1,0,0,2,1,0,0,0,1 +2,0,1,2,Alternator,Tit For Tat,CDCDC,13,0,5,2.6,0.0,0,True,3,1,2,2,0,0,1,0,1,2,0,0,0,1 +2,1,0,2,Tit For Tat,Alternator,CCDCD,13,0,5,2.6,0.0,0,True,3,1,2,2,0,1,0,0,2,1,0,0,0,1 +3,0,2,0,Alternator,Defector,CDCDC,2,-15,5,0.4,-3.0,0,True,3,0,3,0,2,0,0,0,2,0,0,2,0,1 +3,2,0,0,Defector,Alternator,DDDDD,17,15,5,3.4,3.0,1,False,0,0,0,3,2,0,0,0,0,0,2,0,2,0 +4,0,2,1,Alternator,Defector,CDCDC,2,-15,5,0.4,-3.0,0,True,3,0,3,0,2,0,0,0,2,0,0,2,0,1 +4,2,0,1,Defector,Alternator,DDDDD,17,15,5,3.4,3.0,1,False,0,0,0,3,2,0,0,0,0,0,2,0,2,0 +5,0,2,2,Alternator,Defector,CDCDC,2,-15,5,0.4,-3.0,0,True,3,0,3,0,2,0,0,0,2,0,0,2,0,1 +5,2,0,2,Defector,Alternator,DDDDD,17,15,5,3.4,3.0,1,False,0,0,0,3,2,0,0,0,0,0,2,0,2,0 +6,1,2,0,Tit For Tat,Defector,CDDDD,4,-5,5,0.8,-1.0,0,True,1,0,1,0,4,0,0,0,1,0,0,0,3,1 +6,2,1,0,Defector,Tit For Tat,DDDDD,9,5,5,1.8,1.0,1,False,0,0,0,1,4,0,0,0,0,0,1,0,3,0 +7,1,2,1,Tit For Tat,Defector,CDDDD,4,-5,5,0.8,-1.0,0,True,1,0,1,0,4,0,0,0,1,0,0,0,3,1 +7,2,1,1,Defector,Tit For Tat,DDDDD,9,5,5,1.8,1.0,1,False,0,0,0,1,4,0,0,0,0,0,1,0,3,0 +8,1,2,2,Tit For Tat,Defector,CDDDD,4,-5,5,0.8,-1.0,0,True,1,0,1,0,4,0,0,0,1,0,0,0,3,1 +8,2,1,2,Defector,Tit For Tat,DDDDD,9,5,5,1.8,1.0,1,False,0,0,0,1,4,0,0,0,0,0,1,0,3,0 diff --git a/test_outputs/test_results_spatial.csv b/test_outputs/test_results_spatial.csv new file mode 100644 index 000000000..45c352e53 --- /dev/null +++ b/test_outputs/test_results_spatial.csv @@ -0,0 +1,13 @@ +Interaction index,Player index,Opponent index,Repetition,Player name,Opponent name,Actions,Score,Score difference,Turns,Score per turn,Score difference per turn,Win,Initial cooperation,Cooperation count,CC count,CD count,DC count,DD count,CC to C count,CC to D count,CD to C count,CD to D count,DC to C count,DC to D count,DD to C count,DD to D count,Good partner +0,0,1,0,Alternator,Tit For Tat,CDCDC,13,0,5,2.6,0.0,0,True,3,1,2,2,0,0,1,0,1,2,0,0,0,1 +0,1,0,0,Tit For Tat,Alternator,CCDCD,13,0,5,2.6,0.0,0,True,3,1,2,2,0,1,0,0,2,1,0,0,0,1 +1,0,1,1,Alternator,Tit For Tat,CDCDC,13,0,5,2.6,0.0,0,True,3,1,2,2,0,0,1,0,1,2,0,0,0,1 +1,1,0,1,Tit For Tat,Alternator,CCDCD,13,0,5,2.6,0.0,0,True,3,1,2,2,0,1,0,0,2,1,0,0,0,1 +2,0,1,2,Alternator,Tit For Tat,CDCDC,13,0,5,2.6,0.0,0,True,3,1,2,2,0,0,1,0,1,2,0,0,0,1 +2,1,0,2,Tit For Tat,Alternator,CCDCD,13,0,5,2.6,0.0,0,True,3,1,2,2,0,1,0,0,2,1,0,0,0,1 +3,0,2,0,Alternator,Defector,CDCDC,2,-15,5,0.4,-3.0,0,True,3,0,3,0,2,0,0,0,2,0,0,2,0,1 +3,2,0,0,Defector,Alternator,DDDDD,17,15,5,3.4,3.0,1,False,0,0,0,3,2,0,0,0,0,0,2,0,2,0 +4,0,2,1,Alternator,Defector,CDCDC,2,-15,5,0.4,-3.0,0,True,3,0,3,0,2,0,0,0,2,0,0,2,0,1 +4,2,0,1,Defector,Alternator,DDDDD,17,15,5,3.4,3.0,1,False,0,0,0,3,2,0,0,0,0,0,2,0,2,0 +5,0,2,2,Alternator,Defector,CDCDC,2,-15,5,0.4,-3.0,0,True,3,0,3,0,2,0,0,0,2,0,0,2,0,1 +5,2,0,2,Defector,Alternator,DDDDD,17,15,5,3.4,3.0,1,False,0,0,0,3,2,0,0,0,0,0,2,0,2,0 diff --git a/test_outputs/test_results_spatial_three.csv b/test_outputs/test_results_spatial_three.csv new file mode 100644 index 000000000..a0c1b435f --- /dev/null +++ b/test_outputs/test_results_spatial_three.csv @@ -0,0 +1,25 @@ +Interaction index,Player index,Opponent index,Repetition,Player name,Opponent name,Actions,Score,Score difference,Turns,Score per turn,Score difference per turn,Win,Initial cooperation,Cooperation count,CC count,CD count,DC count,DD count,CC to C count,CC to D count,CD to C count,CD to D count,DC to C count,DC to D count,DD to C count,DD to D count,Good partner +0,0,0,0,Alternator,Alternator,CDCDC,11,0,5,2.2,0.0,0,True,3,3,0,0,2,0,2,0,0,0,0,2,0,1 +0,0,0,0,Alternator,Alternator,CDCDC,11,0,5,2.2,0.0,0,True,3,3,0,0,2,0,2,0,0,0,0,2,0,1 +1,0,0,1,Alternator,Alternator,CDCDC,11,0,5,2.2,0.0,0,True,3,3,0,0,2,0,2,0,0,0,0,2,0,1 +1,0,0,1,Alternator,Alternator,CDCDC,11,0,5,2.2,0.0,0,True,3,3,0,0,2,0,2,0,0,0,0,2,0,1 +2,0,0,2,Alternator,Alternator,CDCDC,11,0,5,2.2,0.0,0,True,3,3,0,0,2,0,2,0,0,0,0,2,0,1 +2,0,0,2,Alternator,Alternator,CDCDC,11,0,5,2.2,0.0,0,True,3,3,0,0,2,0,2,0,0,0,0,2,0,1 +3,1,1,0,Tit For Tat,Tit For Tat,CCCCC,15,0,5,3.0,0.0,0,True,5,5,0,0,0,4,0,0,0,0,0,0,0,1 +3,1,1,0,Tit For Tat,Tit For Tat,CCCCC,15,0,5,3.0,0.0,0,True,5,5,0,0,0,4,0,0,0,0,0,0,0,1 +4,1,1,1,Tit For Tat,Tit For Tat,CCCCC,15,0,5,3.0,0.0,0,True,5,5,0,0,0,4,0,0,0,0,0,0,0,1 +4,1,1,1,Tit For Tat,Tit For Tat,CCCCC,15,0,5,3.0,0.0,0,True,5,5,0,0,0,4,0,0,0,0,0,0,0,1 +5,1,1,2,Tit For Tat,Tit For Tat,CCCCC,15,0,5,3.0,0.0,0,True,5,5,0,0,0,4,0,0,0,0,0,0,0,1 +5,1,1,2,Tit For Tat,Tit For Tat,CCCCC,15,0,5,3.0,0.0,0,True,5,5,0,0,0,4,0,0,0,0,0,0,0,1 +6,2,2,0,Defector,Defector,DDDDD,5,0,5,1.0,0.0,0,False,0,0,0,0,5,0,0,0,0,0,0,0,4,1 +6,2,2,0,Defector,Defector,DDDDD,5,0,5,1.0,0.0,0,False,0,0,0,0,5,0,0,0,0,0,0,0,4,1 +7,2,2,1,Defector,Defector,DDDDD,5,0,5,1.0,0.0,0,False,0,0,0,0,5,0,0,0,0,0,0,0,4,1 +7,2,2,1,Defector,Defector,DDDDD,5,0,5,1.0,0.0,0,False,0,0,0,0,5,0,0,0,0,0,0,0,4,1 +8,2,2,2,Defector,Defector,DDDDD,5,0,5,1.0,0.0,0,False,0,0,0,0,5,0,0,0,0,0,0,0,4,1 +8,2,2,2,Defector,Defector,DDDDD,5,0,5,1.0,0.0,0,False,0,0,0,0,5,0,0,0,0,0,0,0,4,1 +9,3,3,0,Cooperator,Cooperator,CCCCC,15,0,5,3.0,0.0,0,True,5,5,0,0,0,4,0,0,0,0,0,0,0,1 +9,3,3,0,Cooperator,Cooperator,CCCCC,15,0,5,3.0,0.0,0,True,5,5,0,0,0,4,0,0,0,0,0,0,0,1 +10,3,3,1,Cooperator,Cooperator,CCCCC,15,0,5,3.0,0.0,0,True,5,5,0,0,0,4,0,0,0,0,0,0,0,1 +10,3,3,1,Cooperator,Cooperator,CCCCC,15,0,5,3.0,0.0,0,True,5,5,0,0,0,4,0,0,0,0,0,0,0,1 +11,3,3,2,Cooperator,Cooperator,CCCCC,15,0,5,3.0,0.0,0,True,5,5,0,0,0,4,0,0,0,0,0,0,0,1 +11,3,3,2,Cooperator,Cooperator,CCCCC,15,0,5,3.0,0.0,0,True,5,5,0,0,0,4,0,0,0,0,0,0,0,1 diff --git a/test_outputs/test_results_spatial_two.csv b/test_outputs/test_results_spatial_two.csv new file mode 100644 index 000000000..e2a1cdfce --- /dev/null +++ b/test_outputs/test_results_spatial_two.csv @@ -0,0 +1,13 @@ +Interaction index,Player index,Opponent index,Repetition,Player name,Opponent name,Actions,Score,Score difference,Turns,Score per turn,Score difference per turn,Win,Initial cooperation,Cooperation count,CC count,CD count,DC count,DD count,CC to C count,CC to D count,CD to C count,CD to D count,DC to C count,DC to D count,DD to C count,DD to D count,Good partner +0,0,1,0,Alternator,Tit For Tat,CDCDC,13,0,5,2.6,0.0,0,True,3,1,2,2,0,0,1,0,1,2,0,0,0,1 +0,1,0,0,Tit For Tat,Alternator,CCDCD,13,0,5,2.6,0.0,0,True,3,1,2,2,0,1,0,0,2,1,0,0,0,1 +1,0,1,1,Alternator,Tit For Tat,CDCDC,13,0,5,2.6,0.0,0,True,3,1,2,2,0,0,1,0,1,2,0,0,0,1 +1,1,0,1,Tit For Tat,Alternator,CCDCD,13,0,5,2.6,0.0,0,True,3,1,2,2,0,1,0,0,2,1,0,0,0,1 +2,0,1,2,Alternator,Tit For Tat,CDCDC,13,0,5,2.6,0.0,0,True,3,1,2,2,0,0,1,0,1,2,0,0,0,1 +2,1,0,2,Tit For Tat,Alternator,CCDCD,13,0,5,2.6,0.0,0,True,3,1,2,2,0,1,0,0,2,1,0,0,0,1 +3,2,3,0,Defector,Cooperator,DDDDD,25,25,5,5.0,5.0,1,False,0,0,0,5,0,0,0,0,0,0,4,0,0,0 +3,3,2,0,Cooperator,Defector,CCCCC,0,-25,5,0.0,-5.0,0,True,5,0,5,0,0,0,0,4,0,0,0,0,0,1 +4,2,3,1,Defector,Cooperator,DDDDD,25,25,5,5.0,5.0,1,False,0,0,0,5,0,0,0,0,0,0,4,0,0,0 +4,3,2,1,Cooperator,Defector,CCCCC,0,-25,5,0.0,-5.0,0,True,5,0,5,0,0,0,0,4,0,0,0,0,0,1 +5,2,3,2,Defector,Cooperator,DDDDD,25,25,5,5.0,5.0,1,False,0,0,0,5,0,0,0,0,0,0,4,0,0,0 +5,3,2,2,Cooperator,Defector,CCCCC,0,-25,5,0.0,-5.0,0,True,5,0,5,0,0,0,0,4,0,0,0,0,0,1