# A numerical study of fixation probabilities for strategies in the Prisoners Dilemma

This notebook contains all the Python code used to generate the plots and figures for the paper.

Here are the versions of the various libraries used:



In :

%matplotlib inline

import axelrod as axl
import pandas as pd
import matplotlib
import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np
import sympy as sym
import itertools
import csv
import os
import glob

from mpl_toolkits.axes_grid1 import make_axes_locatable
from pandas.util.testing import assert_frame_equal

import math
import imp

assert axl.__version__ == '2.9.0'
assert pd.__version__ == '0.19.2'
assert matplotlib.__version__ == '2.0.0'
assert sns.__version__ == '0.7.1'
assert np.__version__ == '1.12.1'
assert sym.__version__ == '1.0'



## Description of the used strategies

Here are all the strategies used in this experiment:



In :

from abbreviations import abbreviations

def abbreviate(player_name, abbreviations=abbreviations):
"""
Return the abbreviated name of a play if one has been given
"""
if isinstance(player_name, axl.Player):
player_name = str(player_name)
return abbreviations.get(player_name, player_name)

assert abbreviate("Tit For Tat") == "TfT"
assert abbreviate("Random: 0.5") == "Random"
assert abbreviate(axl.TitForTat()) == "TfT"




In :

from players import selected_players
players_of_interest = set([p for p in selected_players()
if ("length" not in p.classifier["makes_use_of"]) and
("Incorrect" not in abbreviate(p))])

assert len(players_of_interest) == 164



These players have been omitted from the analysis (even though their data was collected). This is because they use the match length or were entered in to the original data sweep by error (the FSM strategies do not use the correct initial state).



In :

set(map(str, selected_players())) - set(map(str, players_of_interest))




Out:

{"BackStabber: ('D', 'D')",
'Champion',
"DoubleCrosser: ('D', 'D')",
"FSM Player: [(0, 'C', 0, 'C'), (0, 'D', 3, 'C'), (1, 'C', 5, 'D'), (1, 'D', 0, 'C'), (2, 'C', 3, 'C'), (2, 'D', 2, 'D'), (3, 'C', 4, 'D'), (3, 'D', 6, 'D'), (4, 'C', 3, 'C'), (4, 'D', 1, 'D'), (5, 'C', 6, 'C'), (5, 'D', 3, 'D'), (6, 'C', 6, 'D'), (6, 'D', 6, 'D'), (7, 'C', 7, 'D'), (7, 'D', 5, 'C')], 1, C",
"FSM Player: [(0, 'C', 13, 'D'), (0, 'D', 12, 'D'), (1, 'C', 3, 'D'), (1, 'D', 4, 'D'), (2, 'C', 14, 'D'), (2, 'D', 9, 'D'), (3, 'C', 0, 'C'), (3, 'D', 1, 'D'), (4, 'C', 1, 'D'), (4, 'D', 2, 'D'), (5, 'C', 12, 'C'), (5, 'D', 6, 'C'), (6, 'C', 1, 'C'), (6, 'D', 14, 'D'), (7, 'C', 12, 'D'), (7, 'D', 2, 'D'), (8, 'C', 7, 'D'), (8, 'D', 9, 'D'), (9, 'C', 8, 'D'), (9, 'D', 0, 'D'), (10, 'C', 2, 'C'), (10, 'D', 15, 'C'), (11, 'C', 7, 'D'), (11, 'D', 13, 'D'), (12, 'C', 3, 'C'), (12, 'D', 8, 'D'), (13, 'C', 7, 'C'), (13, 'D', 10, 'D'), (14, 'C', 10, 'D'), (14, 'D', 7, 'D'), (15, 'C', 15, 'C'), (15, 'D', 11, 'D')], 1, C",
"FSM Player: [(0, 'C', 7, 'C'), (0, 'D', 1, 'C'), (1, 'C', 11, 'D'), (1, 'D', 11, 'D'), (2, 'C', 8, 'D'), (2, 'D', 8, 'C'), (3, 'C', 3, 'C'), (3, 'D', 12, 'D'), (4, 'C', 6, 'C'), (4, 'D', 3, 'C'), (5, 'C', 11, 'C'), (5, 'D', 8, 'D'), (6, 'C', 13, 'D'), (6, 'D', 14, 'C'), (7, 'C', 4, 'D'), (7, 'D', 2, 'D'), (8, 'C', 14, 'D'), (8, 'D', 8, 'D'), (9, 'C', 0, 'C'), (9, 'D', 10, 'D'), (10, 'C', 8, 'C'), (10, 'D', 15, 'C'), (11, 'C', 6, 'D'), (11, 'D', 5, 'D'), (12, 'C', 6, 'D'), (12, 'D', 9, 'D'), (13, 'C', 9, 'D'), (13, 'D', 8, 'D'), (14, 'C', 8, 'D'), (14, 'D', 13, 'D'), (15, 'C', 4, 'C'), (15, 'D', 5, 'C')], 1, C",
'Knowledgeable Worse and Worse',
'Meta Majority Memory One: 32 players',
'Meta Winner Memory One: 32 players',
'NMWE Memory One: 32 players',
'Stalker: D'}




In :

with open("../data/reference_keys.csv", "r") as f:
reference_keys = {player: eval(keys) for player, keys in reader}

assert reference_keys['ALLCorALLD'] == ['axelrodproject']
assert reference_keys['Cooperator'] == ['Axelrod1984', 'Mittal2009', 'Press2012']




In :

with open("../tex/list_of_players.tex", "w") as f:
for player in sorted(players_of_interest, key=str):
latex_name = "{}".format(player).replace("_", "\_")
f.write("\item {}".format(latex_name))
abbreviation = abbreviate(player)
if abbreviation != player.name:
f.write("(\\textbf{{{}}})".format(abbreviation))
if player.classifier["stochastic"]:
f.write(" - \\textit{Stochastic}")
else:
f.write(" - \\textit{Deterministic}")
try:
mem = int(player.classifier["memory_depth"])
except OverflowError:
mem = "$$\infty$$"
if player.name == "Grudger":
mem = 1  # Overwrite incorrect classification
f.write(" - \\textit{{Memory depth}}: {}".format(mem))
try:
f.write(". \cite{{{}}}\n".format(", ".join(sorted(reference_keys[str(player)]))))
except KeyError:
f.write(".\n")



Here are some summary information about the strategies



In :

def clean_mem(n):
try:
return int(n)
except OverflowError:
return -1

player_info = pd.DataFrame([[abbreviate(p), p.classifier["stochastic"], clean_mem(p.classifier['memory_depth'])]
for p in players_of_interest],
columns=["Player", "Stochastic", "Memory Depth"])




In :

temp_df = pd.DataFrame(player_info.groupby("Stochastic")["Player"].count()).reset_index().rename(columns={"Player": "Count"})
for bool in [True, False]:
filename = "../tex/num_stochastic.tex" if bool else "../tex/num_deterministic.tex"
with open(filename, "w") as f:
num = temp_df[temp_df["Stochastic"] == bool]["Count"].iloc
print(num, bool)
f.write(str(num))

with open("../tex/num_strategies.tex", "w") as f:
num = len(players_of_interest)
f.write(str(num))
print(num)

with open("../tex/num_strategies_axelrod.tex", "w") as f:
num = len(axl.strategies)
f.write(str(num))
print(num)




43 True
121 False
164
186




In :

mem_df = pd.DataFrame(player_info.groupby("Memory Depth")["Player"].count()).transpose()
mem_df.rename(index={"Player": "Count"}, inplace=True)
cols = mem_df.columns.tolist()
mem_df = mem_df[cols[1:] + [cols]]
mem_df




Out:

Memory Depth
0
1
2
3
4
5
6
9
10
11
12
16
20
40
200
-1

Count
3
29
12
8
2
6
1
1
5
1
1
2
2
2
1
88




In :

def clean_latex(string):
"""Replace some special carachters"""
string = string.replace("textbackslashpi", "pi")
string = string.replace("textbackslashphi", "phi")
string = string.replace("\\$", "$")
string = string.replace("\\$", "$")
string = string.replace("\\textasciicircum", "^")
string = string.replace("\_", "_")
string = string.replace("2_2_2", "2\_2\_2")
string = string.replace("1_1_1", "1\_1\_1")
for i in range(1, 4):
string = string.replace("TF{}".format(i), "\\textbf{{TF{}}}".format(i))
return string

with open("../tex/memory_depth_count.tex", "w") as f:
string = clean_latex(mem_df.to_latex()).replace("-1", "$$\infty$$")
f.write(string)



## Illustrating the Approximate moran process



In :

names=["Player 1", "Player 2", "Score 1", "Score 2", "Iteration"])

cached_outcomes["Player 1"] = cached_outcomes["Player 1"].apply(lambda x: abbreviate(x))
cached_outcomes["Player 2"] = cached_outcomes["Player 2"].apply(lambda x: abbreviate(x))

cached_outcomes = cached_outcomes[cached_outcomes["Player 1"].isin(player_info["Player"]) &
cached_outcomes["Player 2"].isin(player_info["Player"])]

assert len(cached_outcomes.index) == 1169001




In :




Out:

Player 1
Player 2
Score 1
Score 2
Iteration

0
Alternator Hunter
Prober 2
2.985
3.010
1

1
Alternator Hunter
Prober 3
0.015
4.990
1

2
2.950
2.950
1

3
Alternator Hunter
Prober 4
0.150
4.900
1

4
2.955
2.955
1




In :

is_stochastic = dict(zip(player_info["Player"], player_info["Stochastic"]))




In :

temp_df = pd.DataFrame(cached_outcomes.groupby(["Player 1", "Player 2"]).count()).reset_index()
temp_df["Outcome count"] = temp_df["Score 1"]
temp_df.drop(["Score 1", "Score 2", "Iteration"], axis=1, inplace=True)




In :

temp_df["Stochastic"] = ((temp_df["Player 1"].map(lambda p: is_stochastic[p]) |
temp_df["Player 2"].map(lambda p: is_stochastic[p])) &
(temp_df["Outcome count"] > 1))




In :




Out:

Player 1
Player 2
Outcome count
Stochastic

0
$\phi$
$\phi$
1
False

1
$\phi$
$\pi$
1
False

2
$\phi$
$e$
1
False

3
$\phi$
2TfT
1
False

4
$\phi$
1
False



## Validating the model

Here we create a number of plots comparing the theoretic fixation probability with the observed fixation probability for a number of initial starting populations.



In :




In :

validation.tail()




Out:

Repetitions
N
i
Player 1
Player 2
Theoretic
Simulated

1059
1000
20
10
Calculator
Random: 0.5
0.085562
0.084

1060
1000
20
19
Calculator
Random: 0.5
0.700176
0.712

1061
1000
20
1
Cooperator
Tit For Tat
0.050000
0.049

1062
1000
20
10
Cooperator
Tit For Tat
0.500000
0.502

1063
1000
20
19
Cooperator
Tit For Tat
0.950000
0.952




In :

markers = itertools.cycle(('X', '+', 'o'))
marker_size = 50
fontsize=16

for names, df in validation.groupby(["Player 1", "Player 2"]):
df.sort_values("N", inplace=True)

repetitions = df["Repetitions"].iloc
assert all(df["Repetitions"] == repetitions)

# Get names instead of repr (to drop some parameters)
names = [abbreviate(name) for name in names]
title = "{} and {} over {} repetitions".format(*names, repetitions)
filename = "{}_v_{}".format(*names)

for substr in [": ", ".", ":", " "]:
filename = filename.replace(substr, "_")

plt.figure()

labels = ["1", "N / 2", "N - 1"]
index_sets = [df["i"] == 1, df["i"]  == df["N"] / 2, df["i"] == df["N"] - 1]
for label, index in zip(labels, index_sets):
d = df[index]
plt.plot(d["N"], d["Theoretic"], label="")
plt.scatter(d["N"], d["Simulated"], marker=next(markers),
label="$x_{{{}}}$".format(label), s=marker_size)
plt.xticks(d["N"])
plt.title(title, fontsize=fontsize)
plt.ylim(0, 1.1)
plt.ylabel("{} fixation probability".format(names))
plt.xlabel("$N$")
plt.legend(fontsize=15)
plt.savefig("../img/{}.pdf".format(filename))




/home/vince/anaconda3/envs/moran/lib/python3.6/site-packages/ipykernel/__main__.py:6: SettingWithCopyWarning:
A value is trying to be set on a copy of a slice from a DataFrame

See the caveats in the documentation: http://pandas.pydata.org/pandas-docs/stable/indexing.html#indexing-view-versus-copy
/home/vince/anaconda3/envs/moran/lib/python3.6/site-packages/matplotlib/pyplot.py:524: RuntimeWarning: More than 20 figures have been opened. Figures created through the pyplot interface (matplotlib.pyplot.figure) are retained until explicitly closed and may consume too much memory. (To control this warning, see the rcParam figure.max_open_warning).
max_open_warning, RuntimeWarning)