Skip to content

Commit f91811e

Browse files
authored
Merge pull request #1301 from gaffney2010/fix-rs
Fix ResultSet __eq__ to handle nans
2 parents 4e351f1 + 7adee95 commit f91811e

File tree

3 files changed

+101
-18
lines changed

3 files changed

+101
-18
lines changed

axelrod/result_set.py

Lines changed: 47 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import csv
33
import itertools
44
from multiprocessing import cpu_count
5+
from typing import List
56
import warnings
67

78
import numpy as np
@@ -117,9 +118,12 @@ def _reshape_out(
117118
alternative=0,
118119
)
119120

120-
self.wins = self._reshape_two_dim_list(sum_per_player_repetition_df["Win"])
121-
self.scores = self._reshape_two_dim_list(sum_per_player_repetition_df["Score"])
122-
self.normalised_scores = self._reshape_two_dim_list(normalised_scores_series)
121+
self.wins = self._reshape_two_dim_list(
122+
sum_per_player_repetition_df["Win"])
123+
self.scores = self._reshape_two_dim_list(
124+
sum_per_player_repetition_df["Score"])
125+
self.normalised_scores = self._reshape_two_dim_list(
126+
normalised_scores_series)
123127

124128
self.cooperation = self._build_cooperation(
125129
sum_per_player_opponent_df["Cooperation count"]
@@ -166,7 +170,8 @@ def _reshape_out(
166170
self.ranked_names = self._build_ranked_names()
167171

168172
self.payoff_matrix = self._build_summary_matrix(self.payoffs)
169-
self.payoff_stddevs = self._build_summary_matrix(self.payoffs, func=np.std)
173+
self.payoff_stddevs = self._build_summary_matrix(self.payoffs,
174+
func=np.std)
170175

171176
self.payoff_diffs_means = self._build_payoff_diffs_means()
172177
self.cooperating_rating = self._build_cooperating_rating()
@@ -266,7 +271,9 @@ def _build_good_partner_matrix(self, good_partner_series):
266271
# interactions.
267272
row.append(0)
268273
else:
269-
row.append(good_partner_dict.get((player_index, opponent_index), 0))
274+
row.append(
275+
good_partner_dict.get((player_index, opponent_index),
276+
0))
270277
good_partner_matrix.append(row)
271278
return good_partner_matrix
272279

@@ -334,13 +341,15 @@ def _build_normalised_state_distribution(self):
334341
for counter in player:
335342
total = sum(counter.values())
336343
counters.append(
337-
Counter({key: value / total for key, value in counter.items()})
344+
Counter(
345+
{key: value / total for key, value in counter.items()})
338346
)
339347
normalised_state_distribution.append(counters)
340348
return normalised_state_distribution
341349

342350
@update_progress_bar
343-
def _build_state_to_action_distribution(self, state_to_action_distribution_series):
351+
def _build_state_to_action_distribution(self,
352+
state_to_action_distribution_series):
344353
state_to_action_key_map = {
345354
"CC to C count": ((C, C), C),
346355
"CC to D count": ((C, C), D),
@@ -396,7 +405,8 @@ def _build_normalised_state_to_action_distribution(self):
396405
return normalised_state_to_action_distribution
397406

398407
@update_progress_bar
399-
def _build_initial_cooperation_count(self, initial_cooperation_count_series):
408+
def _build_initial_cooperation_count(self,
409+
initial_cooperation_count_series):
400410
initial_cooperation_count_dict = initial_cooperation_count_series.to_dict()
401411
initial_cooperation_count = [
402412
initial_cooperation_count_dict.get(player_index, 0)
@@ -411,7 +421,7 @@ def _build_normalised_cooperation(self):
411421
normalised_cooperation = [
412422
list(np.nan_to_num(row))
413423
for row in np.array(self.cooperation)
414-
/ sum(map(np.array, self.match_lengths))
424+
/ sum(map(np.array, self.match_lengths))
415425
]
416426
return normalised_cooperation
417427

@@ -426,7 +436,8 @@ def _build_initial_cooperation_rate(self, interactions_series):
426436
with warnings.catch_warnings():
427437
warnings.simplefilter("ignore")
428438
initial_cooperation_rate = list(
429-
np.nan_to_num(np.array(self.initial_cooperation_count) / interactions_array)
439+
np.nan_to_num(np.array(
440+
self.initial_cooperation_count) / interactions_array)
430441
)
431442
return initial_cooperation_rate
432443

@@ -451,7 +462,8 @@ def _build_eigenmoses_rating(self):
451462
The eigenmoses rating as defined in:
452463
http://www.scottaaronson.com/morality.pdf
453464
"""
454-
eigenvector, eigenvalue = eigen.principal_eigenvector(self.vengeful_cooperation)
465+
eigenvector, eigenvalue = eigen.principal_eigenvector(
466+
self.vengeful_cooperation)
455467

456468
return eigenvector.tolist()
457469

@@ -575,7 +587,8 @@ def _build_tasks(self, df):
575587
]
576588
sum_per_player_opponent_task = df.groupby(groups)[columns].sum()
577589

578-
ignore_self_interactions_task = df["Player index"] != df["Opponent index"]
590+
ignore_self_interactions_task = df["Player index"] != df[
591+
"Opponent index"]
579592
adf = df[ignore_self_interactions_task]
580593

581594
groups = ["Player index", "Repetition"]
@@ -589,7 +602,8 @@ def _build_tasks(self, df):
589602
groups = ["Player index"]
590603
column = "Initial cooperation"
591604
initial_cooperation_count_task = adf.groupby(groups)[column].sum()
592-
interactions_count_task = adf.groupby("Player index")["Player index"].count()
605+
interactions_count_task = adf.groupby("Player index")[
606+
"Player index"].count()
593607

594608
return (
595609
mean_per_reps_player_opponent_task,
@@ -609,6 +623,18 @@ def __eq__(self, other):
609623
other : axelrod.ResultSet
610624
Another results set against which to check equality
611625
"""
626+
627+
def list_equal_with_nans(v1: List[float], v2: List[float]) -> bool:
628+
"""Matches lists, accounting for NaNs."""
629+
if len(v1) != len(v2):
630+
return False
631+
for i1, i2 in zip(v1, v2):
632+
if np.isnan(i1) and np.isnan(i2):
633+
continue
634+
if i1 != i2:
635+
return False
636+
return True
637+
612638
return all(
613639
[
614640
self.wins == other.wins,
@@ -628,8 +654,10 @@ def __eq__(self, other):
628654
self.cooperating_rating == other.cooperating_rating,
629655
self.good_partner_matrix == other.good_partner_matrix,
630656
self.good_partner_rating == other.good_partner_rating,
631-
self.eigenmoses_rating == other.eigenmoses_rating,
632-
self.eigenjesus_rating == other.eigenjesus_rating,
657+
list_equal_with_nans(self.eigenmoses_rating,
658+
other.eigenmoses_rating),
659+
list_equal_with_nans(self.eigenjesus_rating,
660+
other.eigenjesus_rating),
633661
]
634662
)
635663

@@ -699,7 +727,8 @@ def summarise(self):
699727
rates = []
700728
for state in states:
701729
counts = [
702-
counter[(state, C)] for counter in player if counter[(state, C)] > 0
730+
counter[(state, C)] for counter in player if
731+
counter[(state, C)] > 0
703732
]
704733

705734
if len(counts) > 0:
@@ -722,7 +751,8 @@ def summarise(self):
722751

723752
summary_data = []
724753
for rank, i in enumerate(self.ranking):
725-
data = list(summary_measures[i]) + state_prob[i] + state_to_C_prob[i]
754+
data = list(summary_measures[i]) + state_prob[i] + state_to_C_prob[
755+
i]
726756
summary_data.append(self.player(rank, *data))
727757

728758
return summary_data

axelrod/tests/unit/test_eigen.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,13 @@ def test_identity_matrices(self):
1515
self.assertAlmostEqual(evalue, 1)
1616
assert_array_almost_equal(evector, _normalise(numpy.ones(size)))
1717

18+
def test_zero_matrix(self):
19+
mat = numpy.array([[0, 0], [0, 0]])
20+
evector, evalue = principal_eigenvector(mat)
21+
self.assertTrue(numpy.isnan(evalue))
22+
self.assertTrue(numpy.isnan(evector[0]))
23+
self.assertTrue(numpy.isnan(evector[1]))
24+
1825
def test_2x2_matrix(self):
1926
mat = numpy.array([[2, 1], [1, 2]])
2027
evector, evalue = principal_eigenvector(mat)

axelrod/tests/unit/test_resultset.py

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
import pandas as pd
88
from axelrod.result_set import create_counter_dict
99
from axelrod.tests.property import prob_end_tournaments, tournaments
10-
from numpy import mean, nanmedian, std
10+
from numpy import mean, nan, nanmedian, std
1111

1212
from dask.dataframe.core import DataFrame
1313
from hypothesis import given, settings
@@ -190,6 +190,52 @@ def test_init(self):
190190
self.assertEqual(rs.players, self.players)
191191
self.assertEqual(rs.num_players, len(self.players))
192192

193+
def _clear_matrix(self, matrix):
194+
for i, row in enumerate(matrix):
195+
for j, _ in enumerate(row):
196+
matrix[i, j] = 0
197+
198+
def test_ne_vectors(self):
199+
rs_1 = axelrod.ResultSet(
200+
self.filename,
201+
self.players,
202+
self.repetitions
203+
)
204+
205+
rs_2 = axelrod.ResultSet(
206+
self.filename,
207+
self.players,
208+
self.repetitions
209+
)
210+
211+
# A different vector
212+
rs_2.eigenmoses_rating = (-1, -1, -1)
213+
214+
self.assertNotEqual(rs_1, rs_2)
215+
216+
def test_nan_vectors(self):
217+
rs_1 = axelrod.ResultSet(
218+
self.filename,
219+
self.players,
220+
self.repetitions
221+
)
222+
# Force a broken eigenmoses, by replacing vengeful_cooperation with
223+
# zeroes.
224+
self._clear_matrix(rs_1.vengeful_cooperation)
225+
rs_1.eigenmoses_rating = rs_1._build_eigenmoses_rating()
226+
227+
rs_2 = axelrod.ResultSet(
228+
self.filename,
229+
self.players,
230+
self.repetitions
231+
)
232+
# Force a broken eigenmoses, by replacing vengeful_cooperation with
233+
# zeroes.
234+
self._clear_matrix(rs_2.vengeful_cooperation)
235+
rs_2.eigenmoses_rating = rs_2._build_eigenmoses_rating()
236+
237+
self.assertEqual(rs_1, rs_2)
238+
193239
def test_init_multiprocessing(self):
194240
rs = axelrod.ResultSet(
195241
self.filename,

0 commit comments

Comments
 (0)