예제 #1
0
    def test_cache_update_required(self, p):
        p1, p2 = axl.Cooperator(), axl.Cooperator()
        match = axl.Match((p1, p2), 5, noise=p)
        self.assertFalse(match._cache_update_required)

        cache = DeterministicCache()
        cache.mutable = False
        match = axl.Match((p1, p2), 5, deterministic_cache=cache)
        self.assertFalse(match._cache_update_required)

        match = axl.Match((p1, p2), 5)
        self.assertTrue(match._cache_update_required)

        p1 = axl.Random()
        match = axl.Match((p1, p2), 5)
        self.assertFalse(match._cache_update_required)
예제 #2
0
    def test_cache_update_required(self, p):

        assume(0 < p < 1)

        p1, p2 = axelrod.Cooperator(), axelrod.Cooperator()
        match = axelrod.Match((p1, p2), 5, noise=p)
        self.assertFalse(match._cache_update_required)

        cache = DeterministicCache()
        cache.mutable = False
        match = axelrod.Match((p1, p2), 5, deterministic_cache=cache)
        self.assertFalse(match._cache_update_required)

        match = axelrod.Match((p1, p2), 5)
        self.assertTrue(match._cache_update_required)

        p1 = axelrod.Random()
        match = axelrod.Match((p1, p2), 5)
        self.assertFalse(match._cache_update_required)
예제 #3
0
    def test_play(self):
        cache = DeterministicCache()
        players = (axelrod.Cooperator(), axelrod.Defector())
        match = axelrod.Match(players, 3, deterministic_cache=cache)
        expected_result = [(C, D), (C, D), (C, D)]
        self.assertEqual(match.play(), expected_result)
        self.assertEqual(cache[(axelrod.Cooperator(), axelrod.Defector(), 3)],
                         expected_result)

        # a deliberately incorrect result so we can tell it came from the cache
        expected_result = [(C, C), (D, D), (D, C)]
        cache[(axelrod.Cooperator(), axelrod.Defector(), 3)] = expected_result
        match = axelrod.Match(players, 3, deterministic_cache=cache)
        self.assertEqual(match.play(), expected_result)
예제 #4
0
 def test_cache_doesnt_shrink(self):
     """
     We want to make sure that when we access the cache looking for fewer
     turns than what is stored, then it will not overwrite the cache with the
     shorter result.
     """
     cache = DeterministicCache()
     players = (axl.Cooperator(), axl.Defector())
     match = axl.Match(players, 5, deterministic_cache=cache)
     expected_result_5_turn = [(C, D), (C, D), (C, D), (C, D), (C, D)]
     expected_result_3_turn = [(C, D), (C, D), (C, D)]
     self.assertEqual(match.play(), expected_result_5_turn)
     match.turns = 3
     self.assertEqual(match.play(), expected_result_3_turn)
     # The cache should still hold the 5.
     self.assertEqual(cache[(axl.Cooperator(), axl.Defector())],
                      expected_result_5_turn)
예제 #5
0
 def test_cache_grows(self):
     """
     We want to make sure that if we try to use the cache for more turns than
     what is stored, then it will instead regenerate the result and overwrite
     the cache.
     """
     cache = DeterministicCache()
     players = (axl.Cooperator(), axl.Defector())
     match = axl.Match(players, 3, deterministic_cache=cache)
     expected_result_5_turn = [(C, D), (C, D), (C, D), (C, D), (C, D)]
     expected_result_3_turn = [(C, D), (C, D), (C, D)]
     self.assertEqual(match.play(), expected_result_3_turn)
     match.turns = 5
     self.assertEqual(match.play(), expected_result_5_turn)
     # The cache should now hold the 5-turn result..
     self.assertEqual(cache[(axl.Cooperator(), axl.Defector())],
                      expected_result_5_turn)
예제 #6
0
    def __init__(self,
                 players,
                 turns=None,
                 prob_end=None,
                 game=None,
                 deterministic_cache=None,
                 noise=0,
                 match_attributes=None,
                 reset=True,
                 seed=None):
        """
        Parameters
        ----------
        players : tuple
            A pair of axelrod.Player objects
        turns : integer
            The number of turns per match
        prob_end : float
            The probability of a given turn ending a match
        game : axelrod.Game
            The game object used to score the match
        deterministic_cache : axelrod.DeterministicCache
            A cache of resulting actions for deterministic matches
        noise : float
            The probability that a player's intended action should be flipped
        match_attributes : dict
            Mapping attribute names to values which should be passed to players.
            The default is to use the correct values for turns, game and noise
            but these can be overridden if desired.
        reset : bool
            Whether to reset players or not
        seed : int
            Random seed for reproducibility
        """

        defaults = {
            (True, True): (DEFAULT_TURNS, 0),
            (True, False): (float("inf"), prob_end),
            (False, True): (turns, 0),
            (False, False): (turns, prob_end),
        }
        self.turns, self.prob_end = defaults[(turns is None, prob_end is None)]

        self.result = []
        self.noise = noise

        self.set_seed(seed)

        if game is None:
            self.game = Game()
        else:
            self.game = game

        if deterministic_cache is None:
            self._cache = DeterministicCache()
        else:
            self._cache = deterministic_cache

        if match_attributes is None:
            known_turns = self.turns if prob_end is None else float("inf")
            self.match_attributes = {
                "length": known_turns,
                "game": self.game,
                "noise": self.noise,
            }
        else:
            self.match_attributes = match_attributes

        self.players = list(players)
        self.reset = reset
예제 #7
0
    def __init__(self,
                 players: List[Player],
                 turns: int = DEFAULT_TURNS,
                 prob_end: float = None,
                 noise: float = 0,
                 game: Game = None,
                 deterministic_cache: DeterministicCache = None,
                 mutation_rate: float = 0.0,
                 mode: str = "bd",
                 interaction_graph: Graph = None,
                 reproduction_graph: Graph = None,
                 fitness_transformation: Callable = None,
                 mutation_method="transition",
                 stop_on_fixation=True,
                 seed=None) -> None:
        """
        An agent based Moran process class. In each round, each player plays a
        Match with each other player. Players are assigned a fitness score by
        their total score from all matches in the round. A player is chosen to
        reproduce proportionally to fitness, possibly mutated, and is cloned.
        The clone replaces a randomly chosen player.

        If the mutation_rate is 0, the population will eventually fixate on
        exactly one player type. In this case a StopIteration exception is
        raised and the play stops. If the mutation_rate is not zero, then the
        process will iterate indefinitely, so mp.play() will never exit, and
        you should use the class as an iterator instead.

        When a player mutates it chooses a random player type from the initial
        population. This is not the only method yet emulates the common method
        in the literature.

        It is possible to pass interaction graphs and reproduction graphs to the
        Moran process. In this case, in each round, each player plays a
        Match with each neighboring player according to the interaction graph.
        Players are assigned a fitness score by their total score from all
        matches in the round. A player is chosen to reproduce proportionally to
        fitness, possibly mutated, and is cloned. The clone replaces a randomly
        chosen neighboring player according to the reproduction graph.

        Parameters
        ----------
        players
        turns:
            The number of turns in each pairwise interaction
        prob_end :
            The probability of a given turn ending a match
        noise:
            The background noise, if any. Randomly flips plays with probability
            `noise`.
        game: axelrod.Game
            The game object used to score matches.
        deterministic_cache:
            A optional prebuilt deterministic cache
        mutation_rate:
            The rate of mutation. Replicating players are mutated with
            probability `mutation_rate`
        mode:
            Birth-Death (bd) or Death-Birth (db)
        interaction_graph: Axelrod.graph.Graph
            The graph in which the replicators are arranged
        reproduction_graph: Axelrod.graph.Graph
            The reproduction graph, set equal to the interaction graph if not
            given
        fitness_transformation:
            A function mapping a score to a (non-negative) float
        mutation_method:
            A string indicating if the mutation method should be between original types ("transition")
            or based on the player's mutation method, if present ("atomic").
        stop_on_fixation:
            A bool indicating if the process should stop on fixation
        seed: int
            A random seed for reproducibility
        """
        m = mutation_method.lower()
        if m in ["atomic", "transition"]:
            self.mutation_method = m
        else:
            raise ValueError(
                "Invalid mutation method {}".format(mutation_method))
        assert (mutation_rate >= 0) and (mutation_rate <= 1)
        assert (noise >= 0) and (noise <= 1)
        mode = mode.lower()
        assert mode in ["bd", "db"]
        self.mode = mode
        if deterministic_cache is not None:
            self.deterministic_cache = deterministic_cache
        else:
            self.deterministic_cache = DeterministicCache()
        self.turns = turns
        self.prob_end = prob_end
        self.game = game
        self.noise = noise
        self.initial_players = players  # save initial population
        self.players = []  # type: List
        self.populations = []  # type: List
        self.score_history = []  # type: List
        self.winning_strategy_name = None  # type: Optional[str]
        self.mutation_rate = mutation_rate
        self.stop_on_fixation = stop_on_fixation
        self._random = RandomGenerator(seed=seed)
        self._bulk_random = BulkRandomGenerator(self._random.random_seed_int())
        self.set_players()
        # Build the set of mutation targets
        # Determine the number of unique types (players)
        keys = set([str(p) for p in players])
        # Create a dictionary mapping each type to a set of representatives
        # of the other types
        d = dict()
        for p in players:
            d[str(p)] = p
        mutation_targets = dict()
        for key in sorted(keys):
            mutation_targets[key] = [
                v for (k, v) in sorted(d.items()) if k != key
            ]
        self.mutation_targets = mutation_targets

        if interaction_graph is None:
            interaction_graph = complete_graph(len(players), loops=False)
        if reproduction_graph is None:
            reproduction_graph = Graph(interaction_graph.edges,
                                       directed=interaction_graph.directed)
            reproduction_graph.add_loops()
        # Check equal vertices
        v1 = interaction_graph.vertices
        v2 = reproduction_graph.vertices
        assert list(v1) == list(v2)
        self.interaction_graph = interaction_graph
        self.reproduction_graph = reproduction_graph
        self.fitness_transformation = fitness_transformation
        # Map players to graph vertices
        self.locations = sorted(interaction_graph.vertices)
        self.index = dict(
            zip(sorted(interaction_graph.vertices), range(len(players))))
        self.fixated = self.fixation_check()