|
| 1 | +.. _create-heterogeneous-moran-processes: |
| 2 | + |
| 3 | +Create Heterogeneous Moran Processes |
| 4 | +==================================== |
| 5 | + |
| 6 | +Axelrod Matches are homogeneous by nature but can be extended to utilize |
| 7 | +additional attributes of heterogeneous players. This tutorial indicates how the |
| 8 | +Axelrod :code:`Match` class can be manipulated in order to play heterogeneous |
| 9 | +tournaments and Moran processes using mass as a score modifier similarly to the |
| 10 | +work of [Krapohl2020]_. |
| 11 | + |
| 12 | +The following lines of code creates a list of players from the available demo |
| 13 | +strategies along with an ascending list of masses we will use for the players. |
| 14 | +This is equivalent in principle to the country masses discussed in |
| 15 | +[Krapohl2020]_:: |
| 16 | + |
| 17 | + >>> import axelrod as axl |
| 18 | + >>> players = [player() for player in axl.demo_strategies] |
| 19 | + >>> masses = [i for i in range(len(players))] |
| 20 | + >>> players |
| 21 | + [Cooperator, Defector, Tit For Tat, Grudger, Random: 0.5] |
| 22 | + |
| 23 | +Using the :code:`setattr()` function, additional attributes can be passed to |
| 24 | +players to enable access during matches and tournaments without manual |
| 25 | +modification of individual strategies:: |
| 26 | + |
| 27 | + >>> def set_player_mass(players, masses): |
| 28 | + ... """Add mass attribute to player strategy classes to be accessable via self.mass""" |
| 29 | + ... for player, mass in zip(players, masses): |
| 30 | + ... setattr(player, "mass", mass) |
| 31 | + ... |
| 32 | + >>> set_player_mass(players, masses) |
| 33 | + |
| 34 | +The :code:`Match` class can be partially altered to enable different behaviour |
| 35 | +(see :ref:`use-custom-matches`). |
| 36 | +Here we extend :code:`axl.Match` and overwrite its |
| 37 | +:code:`final_score_per_turn()` function to utilize the player mass attribute as |
| 38 | +a multiplier for the final score:: |
| 39 | + |
| 40 | + >>> class MassBaseMatch(axl.Match): |
| 41 | + ... """Axelrod Match object with a modified final score function to enable mass to influence the final score as a multiplier""" |
| 42 | + ... def final_score_per_turn(self): |
| 43 | + ... base_scores = axl.Match.final_score_per_turn(self) |
| 44 | + ... return [player.mass * score for player, score in zip(self.players, base_scores)] |
| 45 | + |
| 46 | +In [Krapohl2020]_ a non standard Moran process is used where the mass of |
| 47 | +individuals is not reproduced so we will use inheritance to create a new Moran |
| 48 | +process that keeps the mass of the individuals constant:: |
| 49 | + |
| 50 | + >>> class MassBasedMoranProcess(axl.MoranProcess): |
| 51 | + ... """Axelrod MoranProcess class """ |
| 52 | + ... def __next__(self): |
| 53 | + ... set_player_mass(self.players, masses) |
| 54 | + ... super().__next__() |
| 55 | + ... return self |
| 56 | + |
| 57 | + >>> mp = MassBasedMoranProcess(players, match_class=MassBaseMatch, seed=0) |
| 58 | + >>> populations = mp.play() |
| 59 | + >>> print(mp.winning_strategy_name) |
| 60 | + Random: 0.5 |
| 61 | + |
| 62 | +Note that the snippets here only influence the final score of matches. The |
| 63 | +behavior of matches, and Moran processes can be more heavily influenced by |
| 64 | +partially overwriting other :code:`match` functions or :code:`birth` and |
| 65 | +:code:`death` functions within :code:`MoranProcess`. |
0 commit comments