1
- from collections import namedtuple
1
+ import csv
2
2
import os
3
+ from collections import namedtuple
3
4
from tempfile import mkstemp
5
+
4
6
import matplotlib .pyplot as plt
5
7
import numpy as np
6
8
import tqdm
9
+ from mpl_toolkits .axes_grid1 import make_axes_locatable
7
10
8
11
import axelrod as axl
9
12
from axelrod import Player
@@ -154,7 +157,7 @@ def generate_data(interactions: dict, points: list, edges: list) -> dict:
154
157
155
158
Parameters
156
159
----------
157
- interactions : dictionary
160
+ interactions : dict
158
161
A dictionary mapping edges to the corresponding interactions of
159
162
those players.
160
163
points : list
@@ -166,7 +169,7 @@ def generate_data(interactions: dict, points: list, edges: list) -> dict:
166
169
167
170
Returns
168
171
----------
169
- point_scores : dictionary
172
+ point_scores : dict
170
173
A dictionary where the keys are Points of the form (x, y) and
171
174
the values are the mean score for the corresponding interactions.
172
175
"""
@@ -265,7 +268,7 @@ def fingerprint(
265
268
self , turns : int = 50 , repetitions : int = 10 , step : float = 0.01 ,
266
269
processes : int = None , filename : str = None , in_memory : bool = False ,
267
270
progress_bar : bool = True
268
- ) -> dict :
271
+ ) -> dict :
269
272
"""Build and play the spatial tournament.
270
273
271
274
Creates the probes and their edges then builds a spatial tournament.
@@ -276,16 +279,16 @@ def fingerprint(
276
279
277
280
Parameters
278
281
----------
279
- turns : integer , optional
282
+ turns : int , optional
280
283
The number of turns per match
281
- repetitions : integer , optional
284
+ repetitions : int , optional
282
285
The number of times the round robin should be repeated
283
286
step : float, optional
284
287
The separation between each Point. Smaller steps will
285
288
produce more Points that will be closer together.
286
- processes : integer , optional
289
+ processes : int , optional
287
290
The number of processes to be used for parallel processing
288
- filename: string , optional
291
+ filename: str , optional
289
292
The name of the file for self.spatial_tournament's interactions.
290
293
if None and in_memory=False, will auto-generate a filename.
291
294
in_memory: bool
@@ -296,7 +299,7 @@ def fingerprint(
296
299
297
300
Returns
298
301
----------
299
- self.data : dictionary
302
+ self.data : dict
300
303
A dictionary where the keys are coordinates of the form (x, y) and
301
304
the values are the mean score for the corresponding interactions.
302
305
"""
@@ -379,3 +382,190 @@ def plot(self, cmap: str = 'seismic', interpolation: str = 'none',
379
382
if title is not None :
380
383
plt .title (title )
381
384
return fig
385
+
386
+
387
+ class TransitiveFingerprint (object ):
388
+ def __init__ (self , strategy , opponents = None , number_of_opponents = 50 ):
389
+ """
390
+ Parameters
391
+ ----------
392
+ strategy : class or instance
393
+ A class that must be descended from axelrod.Player or an instance of
394
+ axelrod.Player.
395
+ opponents : list of instances
396
+ A list that contains a list of opponents
397
+ Default: A spectrum of Random players
398
+ number_of_opponents: int
399
+ The number of Random opponents
400
+ Default: 50
401
+ """
402
+ self .strategy = strategy
403
+
404
+ if opponents is None :
405
+ self .opponents = [axl .Random (p ) for p in
406
+ np .linspace (0 , 1 , number_of_opponents )]
407
+ else :
408
+ self .opponents = opponents
409
+
410
+ def fingerprint (self , turns : int = 50 , repetitions : int = 1000 ,
411
+ noise : float = None , processes : int = None ,
412
+ filename : str = None ,
413
+ progress_bar : bool = True ) -> np .array :
414
+ """Creates a spatial tournament to run the necessary matches to obtain
415
+ fingerprint data.
416
+
417
+ Creates the opponents and their edges then builds a spatial tournament.
418
+
419
+ Parameters
420
+ ----------
421
+ turns : int, optional
422
+ The number of turns per match
423
+ repetitions : int, optional
424
+ The number of times the round robin should be repeated
425
+ noise : float, optional
426
+ The probability that a player's intended action should be flipped
427
+ processes : int, optional
428
+ The number of processes to be used for parallel processing
429
+ filename: str, optional
430
+ The name of the file for spatial tournament's interactions.
431
+ if None, a filename will be generated.
432
+ progress_bar : bool
433
+ Whether or not to create a progress bar which will be updated
434
+
435
+ Returns
436
+ ----------
437
+ self.data : np.array
438
+ A numpy array containing the mean cooperation rate against each
439
+ opponent in each turn. The ith row corresponds to the ith opponent
440
+ and the jth column the jth turn.
441
+ """
442
+
443
+ if isinstance (self .strategy , axl .Player ):
444
+ players = [self .strategy ] + self .opponents
445
+ else :
446
+ players = [self .strategy ()] + self .opponents
447
+
448
+ temp_file_descriptor = None
449
+ if filename is None :
450
+ temp_file_descriptor , filename = mkstemp ()
451
+
452
+ edges = [(0 , k + 1 ) for k in range (len (self .opponents ))]
453
+ tournament = axl .Tournament (players = players ,
454
+ edges = edges , turns = turns , noise = noise ,
455
+ repetitions = repetitions )
456
+ tournament .play (filename = filename , build_results = False ,
457
+ progress_bar = progress_bar , processes = processes )
458
+
459
+ self .data = self .analyse_cooperation_ratio (filename )
460
+
461
+ if temp_file_descriptor is not None :
462
+ os .close (temp_file_descriptor )
463
+ os .remove (filename )
464
+
465
+ return self .data
466
+
467
+ @staticmethod
468
+ def analyse_cooperation_ratio (filename ):
469
+ """Generates the data used from the tournament
470
+
471
+ Return an M by N array where M is the number of opponents and N is the
472
+ number of turns.
473
+
474
+ Parameters
475
+ ----------
476
+ filename : str
477
+ The filename of the interactions
478
+
479
+ Returns
480
+ ----------
481
+ self.data : np.array
482
+ A numpy array containing the mean cooperation rate against each
483
+ opponent in each turn. The ith row corresponds to the ith opponent
484
+ and the jth column the jth turn.
485
+ """
486
+ did_c = np .vectorize (lambda action : int (action == 'C' ))
487
+
488
+ cooperation_rates = {}
489
+ with open (filename , "r" ) as f :
490
+ reader = csv .reader (f )
491
+ for row in reader :
492
+ opponent_index , player_history = int (row [1 ]), list (row [4 ])
493
+ if opponent_index in cooperation_rates :
494
+ cooperation_rates [opponent_index ].append (did_c (player_history ))
495
+ else :
496
+ cooperation_rates [opponent_index ] = [did_c (player_history )]
497
+
498
+ for index , rates in cooperation_rates .items ():
499
+ cooperation_rates [index ] = np .mean (rates , axis = 0 )
500
+
501
+ return np .array ([cooperation_rates [index ]
502
+ for index in cooperation_rates ])
503
+
504
+ def plot (self , cmap : str = 'viridis' , interpolation : str = 'none' ,
505
+ title : str = None , colorbar : bool = True , labels : bool = True ,
506
+ display_names : bool = False ,
507
+ ax : plt .Figure = None ) -> plt .Figure :
508
+ """Plot the results of the spatial tournament.
509
+ Parameters
510
+ ----------
511
+ cmap : str, optional
512
+ A matplotlib colour map, full list can be found at
513
+ http://matplotlib.org/examples/color/colormaps_reference.html
514
+ interpolation : str, optional
515
+ A matplotlib interpolation, full list can be found at
516
+ http://matplotlib.org/examples/images_contours_and_fields/interpolation_methods.html
517
+ title : str, optional
518
+ A title for the plot
519
+ colorbar : bool, optional
520
+ Choose whether the colorbar should be included or not
521
+ labels : bool, optional
522
+ Choose whether the axis labels and ticks should be included
523
+ display_names : bool, optional
524
+ Choose whether to display the names of the strategies
525
+ ax: matplotlib axis
526
+ Allows the plot to be written to a given matplotlib axis.
527
+ Default is None.
528
+ Returns
529
+ ----------
530
+ figure : matplotlib figure
531
+ A heat plot of the results of the spatial tournament
532
+ """
533
+ if ax is None :
534
+ fig , ax = plt .subplots ()
535
+ else :
536
+ ax = ax
537
+
538
+ fig = ax .get_figure ()
539
+ mat = ax .imshow (self .data , cmap = cmap , interpolation = interpolation )
540
+
541
+ width = len (self .data ) / 2
542
+ height = width
543
+ fig .set_size_inches (width , height )
544
+
545
+ plt .xlabel ('turns' )
546
+ ax .tick_params (axis = 'both' , which = 'both' , length = 0 )
547
+
548
+ if display_names :
549
+ plt .yticks (range (len (self .opponents )), [str (player ) for player in
550
+ self .opponents ])
551
+ else :
552
+ plt .yticks ([0 , len (self .opponents ) - 1 ], [0 , 1 ])
553
+ plt .ylabel ("Probability of cooperation" )
554
+
555
+ if not labels :
556
+ plt .axis ('off' )
557
+
558
+ if title is not None :
559
+ plt .title (title )
560
+
561
+ if colorbar :
562
+ max_score = 0
563
+ min_score = 1
564
+ ticks = [min_score , 1 / 2 , max_score ]
565
+
566
+ divider = make_axes_locatable (ax )
567
+ cax = divider .append_axes ("right" , size = "5%" , pad = 0.2 )
568
+ cbar = fig .colorbar (mat , cax = cax , ticks = ticks )
569
+
570
+ plt .tight_layout ()
571
+ return fig
0 commit comments