Esempio n. 1
0
    def graph(self, equilibria, graph_options):
        setupGraph(graph_options)
        graph_options[GraphOptions.LEGEND_LABELS_KEY] = lambda i: equilibria[i]
        x = self.independent_parameters[0]
        y = self.independent_parameters[1]
        x_values = list(x)
        y_values = list(y)
        nx = len(x_values)
        ny = len(y_values)
        n_equilibria = len(equilibria)
        data = numpy.zeros((nx, ny, n_equilibria))
        for i in range(nx):
            for j in range(ny):
                data[i, j, :] = self.data[i][j]

        if graph_options['type'] == '3d':
            plot_3d_data_set(
                data, x.key, x_values, y.key, y_values,
                "Equilibrium Proportion",
                "%s:(%.3f ... %.3f), %s:(%.3f ... %.3f) " %
                (x.key, x.lb, x.ub, y.key, y.lb, y.ub), n_equilibria,
                graph_options)
        elif graph_options['type'] == 'contour':
            plot_contour_data_set(
                data, x.key, x_values, y.key, y_values,
                "Equilibrium Proportion",
                "%s:(%.3f ... %.3f), %s:(%.3f ... %.3f) " %
                (x.key, x.lb, x.ub, y.key, y.lb, y.ub), n_equilibria,
                graph_options)
        else:
            print(graph_type)
Esempio n. 2
0
 def graph(self, equilibria, graph_options):
     setupGraph(graph_options)
     graph_options[GraphOptions.LEGEND_LABELS_KEY] = lambda i: equilibria[i]
     x = self.independent_parameters[0]
     x_values = list(x)
     n_equilibria = len(equilibria)
     data = numpy.array(self.data)
     plot_single_data_set(data, x.key, x_values, "Equilibrium Proportion",
                          "%s:(%.3f ... %.3f)" % (x.key, x.lb, x.ub),
                          n_equilibria, graph_options)
Esempio n. 3
0
    def graph(self, equilibria, graph_options):
        setupGraph(graph_options)
        graph_options[GraphOptions.LEGEND_LABELS_KEY] = lambda i: equilibria[i]
        x = self.independent_parameters[0]
        y = self.independent_parameters[1]
        x_values = list(x)
        y_values = list(y)
        nx = len(x_values)
        ny = len(y_values)
        n_equilibria = len(equilibria)
        data = numpy.zeros((nx, ny, n_equilibria))
        for i in range(nx):
            for j in range(ny):
                data[i, j, :] = self.data[i][j]

        # Don’t include the unclassified equilibrium if it’s proportion is always zero (i.e. unclassified takes the value False)
        unclassified = False
        for i in range(len(data)):
            for j in range(len(data[i])):
                if data[i][j][-1] == 0.0:
                    unclassified = unclassified or False
                else:
                    unclassified = unclassified or True

        if not unclassified:
            n_equilibria = n_equilibria - 1
            final_data = numpy.zeros((nx, ny, n_equilibria))
            for i in range(nx):
                for j in range(ny):
                    final_data[i, j, :] = data[i][j][:-1]
        else:
            final_data = data

        if graph_options['type'] == '3d':
            plot_3d_data_set(
                final_data, x.key, x_values, y.key, y_values,
                "Equilibrium Proportion",
                "%s:(%.3f ... %.3f), %s:(%.3f ... %.3f) " %
                (x.key, x.lb, x.ub, y.key, y.lb, y.ub), n_equilibria,
                graph_options)
        elif graph_options['type'] == 'contour':
            plot_contour_data_set(
                final_data, x.key, x_values, y.key, y_values,
                "Equilibrium Proportion",
                "%s:(%.3f ... %.3f), %s:(%.3f ... %.3f) " %
                (x.key, x.lb, x.ub, y.key, y.lb, y.ub), n_equilibria,
                graph_options)
        else:
            print(graph_type)
Esempio n. 4
0
    def graph(self, equilibria, graph_options):
        setupGraph(graph_options)
        graph_options[GraphOptions.LEGEND_LABELS_KEY] = lambda i: equilibria[i]
        x = self.independent_parameters[0]
        x_values = list(x)
        n_equilibria = len(equilibria)
        data = numpy.array(self.data)

        # Don’t include the unclassified equilibrium if it’s proportion is zero
        if not data[:, -1].all():
            n_equilibria = n_equilibria - 1
            data = data[:, :-1]

        # Plot the 2D data
        plot_single_data_set(data, x.key, x_values, "Equilibrium Proportion",
                             "%s:(%.3f ... %.3f)" % (x.key, x.lb, x.ub),
                             n_equilibria, graph_options)
Esempio n. 5
0
    def simulate(self,
                 num_gens=DEFAULT_GENERATIONS,
                 pop_size=100,
                 start_state=None,
                 graph=True,
                 return_labeled=True,
                 burn=0,
                 class_end=False,
                 frac_invasions=False,
                 strategy_indx=0):
        """
        Simulate a game in the presence of group selection for a specific number of generations optionally
        graphing the results

        @param num_gens: the number of iterations of the simulation.
        @type num_gens: int
        @param group_size: Fixed population in each group.
        @type group_size: int
        @param rate: Rate of group selection vs individual selection
        @type rate: float
        @param start_state: whether the starting state is to be predetermined
        @type start_state: list
        @param graph: the type of graph (false if no graph is wished)
        @type graph: dict, bool
        @param return_labeled: whether the distribution of classified equilibria that are returned should be labelled
            or simply listed with their keys inferred by their order
        @type return_labeled: bool
        @param class_end: if False the equilibria are classified at all generations and if True only the last generation is classified.
        @type class_end: bool
        @param frac_invasions: Whether the given simulation is to compute the fraction of invasions for a strategy.
        @type: bool
        @param strategy_indx: The index of the strategy whose fraction of invasions is to be computed
        @type strategy_indx: int
        @return: the frequency of time spent in each equilibria, defined by the game
        @rtype: numpy.ndarray or dict
        TO DO: Explain what 'burn' does
        """

        game = self.game_cls(**self.game_kwargs)
        dyn = self.dynamics_cls(payoff_matrix=game.pm,
                                player_frequencies=game.player_frequencies,
                                pop_size=pop_size,
                                **self.dynamics_kwargs)

        # Group Selection simulation for a given number of generations.
        results_total, payoffs_total = dyn.simulate(num_gens, start_state,
                                                    frac_invasions,
                                                    strategy_indx)

        # Classify the equilibria and plot the results
        params = Obj(**self.game_kwargs)

        frequencies = np.zeros(self.game_cls.num_equilibria()
                               )  # one extra for the Unclassified key
        if dyn.stochastic:
            classifications = []

            if class_end:  # Only classify the final generation
                lastGenerationState = [
                    np.zeros(len(player[0])) for player in results_total
                ]
                for playerIdx, player in enumerate(results_total):
                    for stratIdx, strat in enumerate(player[-1]):
                        lastGenerationState[playerIdx][stratIdx] = strat
                    lastGenerationState[playerIdx] /= lastGenerationState[
                        playerIdx].sum()
                equi = game.classify(params, lastGenerationState,
                                     game.equilibrium_tolerance)
                frequencies = np.zeros(self.game_cls.num_equilibria())
                frequencies[equi] = 1

            else:  # Classify every generation

                for state in zip(*results_total):
                    state = [x / x.sum() for x in state]
                    equi = game.classify(params, state,
                                         game.equilibrium_tolerance)
                    # note, if equi returns -1, then the -1 index gets the last entry in the array
                    classifications.append(equi)
                    frequencies[equi] += 1
        else:
            last_generation_state = [results_total[-1][-1]]
            classification = game.classify(params, last_generation_state,
                                           game.equilibrium_tolerance)
            frequencies[classification] = 1

        if graph:
            setupGraph(graph, game, dyn, burn, num_gens, results_total,
                       payoffs_total)
        else:
            if return_labeled:
                return self._convert_equilibria_frequencies(frequencies)
            else:
                return frequencies, results_total, payoffs_total
Esempio n. 6
0
    def simulate_many(self,
                      num_iterations=DEFAULT_ITERATIONS,
                      num_gens=DEFAULT_GENERATIONS,
                      pop_size=100,
                      start_state=None,
                      graph=False,
                      histogram=False,
                      return_labeled=True,
                      burn=0,
                      parallelize=True,
                      class_end=True,
                      frac_invasions=False,
                      strategy_indx=0):
        """
        A helper method to call the simulate methods num_iterations times simulating num_gens generations each time,
        and then averaging the frequency of the resulting equilibria. Method calls are parallelized and attempt to
        use all available cores on the machine.

        @param num_iterations: the number of times to iterate the simulation
        @type num_iterations: int
        @param num_gens: the number of generations to run each simulation with
        @type num_gens: int
        @param pop_size: total population size
        @type pop_size: int
        @param start_state: An optional list of distributions of strategies for each player
        @type start_state: list or None
        @param graph: the type of graph (false if no graph is wished)
        @type graph: dict, bool
        @param histogram: if True plots the histogram of final population sizes over iterations. Both graph and histogram can't be True at the same time.
        @type histogram: bool
        @param return_labeled: whether the distribution of classified equilibria that are returned should be labelled
            or simply listed with their keys inferred by their order
        @type return_labeled: bool
        @param parallelize: whether or not to parallelize the computation, defaults to true, but an override when
            varying the parameters, as seen in the L{VariedGame} class to achieve coarser parallelization
        @type parallelize: bool
        @param class_end: if False the equilibria are classified at all generations and if True only the last generation is classified.
        @type class_end: bool
        @param frac_invasions: Whether the given simulation is to compute the fraction of invasions for a strategy.
        @type: bool
        @param strategy_indx: The index of the strategy whose fraction of invasions is to be computed
        @type strategy_indx: int
        @return: the frequency of time spent in each equilibria, defined by the game
        @rtype: np.ndarray or dict
        """
        # TODO move this graphing into graphSetup and link it to extra options

        game = self.game_cls(**self.game_kwargs)
        dyn = self.dynamics_cls(payoff_matrix=game.pm,
                                player_frequencies=game.player_frequencies,
                                pop_size=pop_size,
                                **self.dynamics_kwargs)
        frequencies = np.zeros(self.game_cls.num_equilibria())

        output = par_for(parallelize)(
            delayed(wrapper_simulate)(self,
                                      num_gens=num_gens,
                                      pop_size=pop_size,
                                      frac_invasions=frac_invasions,
                                      strategy_indx=strategy_indx,
                                      start_state=start_state,
                                      class_end=class_end)
            for iteration in range(num_iterations))

        equilibria = []
        strategies = [0] * num_iterations
        payoffs = [0] * num_iterations
        for idx, sim in enumerate(output):
            equilibria.append(sim[0])
            strategies[idx] = sim[1]
            payoffs[idx] = sim[2]

        #TODO move these averages or only compile them if appropriate simulation type
        stratAvg = [
            np.zeros(shape=(num_gens, dyn.pm.num_strats[playerIdx]))
            for playerIdx in range(dyn.pm.num_player_types)
        ]
        # Storing the final strategy populations per iteration
        strat_final = [
            np.zeros(shape=(num_iterations, dyn.pm.num_strats[playerIdx]))
            for playerIdx in range(dyn.pm.num_player_types)
        ]

        for iteration in range(num_iterations):
            for player in range(dyn.pm.num_player_types):
                for gen in range(num_gens - burn):
                    for strat in range(dyn.pm.num_strats[player]):
                        stratAvg[player][gen][strat] += strategies[iteration][
                            player][gen][strat]
                        if histogram and gen == num_gens - burn - 1:
                            strat_final[player][iteration][
                                strat] += strategies[iteration][player][gen][
                                    strat]

        for playerIdx, player in enumerate(stratAvg):
            for genIdx, gen in enumerate(player):
                if gen.sum() != 0:
                    gen /= gen.sum()
                    gen *= dyn.num_players[playerIdx]

        payoffsAvg = [
            np.zeros(shape=(num_gens - 1, dyn.pm.num_strats[playerIdx]))
            for playerIdx in range(dyn.pm.num_player_types)
        ]
        for iteration in range(num_iterations):
            for player in range(dyn.pm.num_player_types):
                for gen in range(num_gens - 1 - burn):
                    for strat in range(dyn.pm.num_strats[player]):
                        payoffsAvg[player][gen][strat] += payoffs[iteration][
                            player][gen][strat]

        for playerIdx, player in enumerate(payoffsAvg):
            for genIdx, gen in enumerate(player):
                if gen.sum() != 0:
                    gen /= gen.sum()
                    gen *= dyn.num_players[playerIdx]

        if graph:
            assert histogram == False, (
                "Can't plot graph and histogram at the same time")
            setupGraph(graph, game, dyn, burn, num_gens, stratAvg, payoffs[0])
        elif histogram:
            setupHistogram(histogram, game, dyn, num_iterations,
                           dyn.num_players, strat_final)

        for x in equilibria:
            frequencies += x

        frequencies /= frequencies.sum()

        if return_labeled:
            return self._convert_equilibria_frequencies(frequencies)
        else:
            return frequencies
Esempio n. 7
0
    def simulate(self,
                 num_gens=DEFAULT_GENERATIONS,
                 graph=True,
                 burn=0,
                 return_labeled=True,
                 start_state=None,
                 class_end=False):
        """
        Simulate the game for the given number of generations with the specified dynamics class and optionally graph the results

        @param num_gens: the number of iterations of the simulation.
        @type num_gens: int
        @param graph: the type of graph (false if no graph is wished)
        @type graph: dict, bool
        @param return_labeled: whether the distribution of classified equilibria that are returned should be labelled
            or simply listed with their keys inferred by their order
        @type return_labeled: bool
        @param start_state: whether the starting state is to be predeterined
        @type start_state: list
        @param graph_payoffs: to graph the payoffs for each generation
        @type graph_payoffs: bool
        @param graph_payoff_line: to show the dominate strategy for each generation of the game underneath the graph
        @type graph_payoff_line: bool
        @return: the frequency of time spent in each equilibria, defined by the game
        @rtype: numpy.ndarray or dict
        """
        game = self.game_cls(**self.game_kwargs)
        dyn = self.dynamics_cls(payoff_matrix=game.pm,
                                player_frequencies=game.player_frequencies,
                                **self.dynamics_kwargs)
        results, payoffs = dyn.simulate(num_gens=num_gens,
                                        debug_state=start_state)

        # results_obj = SingleSimulationOutcome(self.dynamics_cls, self.dynamics_kwargs, self.game_cls, self.game_kwargs, results)
        # TODO: serialize results to file
        params = Obj(**self.game_kwargs)
        frequencies = numpy.zeros(self.game_cls.num_equilibria()
                                  )  # one extra for the Unclassified key

        for playerIdx, player in enumerate(payoffs):
            payoffs[playerIdx] = numpy.delete(player, (0), axis=0)

        if burn:
            for index, array in enumerate(results):
                results[index] = array[burn:]

        if dyn.stochastic:
            classifications = []
            if class_end:  # Only classify the final generation
                lastGenerationState = [
                    numpy.zeros(len(player[0])) for player in results
                ]
                for playerIdx, player in enumerate(results):
                    for stratIdx, strat in enumerate(player[-1]):
                        lastGenerationState[playerIdx][stratIdx] = strat
                    lastGenerationState[playerIdx] /= lastGenerationState[
                        playerIdx].sum()
                equi = game.classify(params, lastGenerationState,
                                     game.equilibrium_tolerance)
                frequencies = numpy.zeros(self.game_cls.num_equilibria())
                frequencies[equi] = 1
            else:  # Classify every generation
                for state in zip(*results):
                    state = [x / x.sum() for x in state]
                    equi = game.classify(params, state,
                                         game.equilibrium_tolerance)
                    # note, if equi returns -1, then the -1 index gets the last entry in the array
                    classifications.append(equi)
                    frequencies[equi] += 1
        else:
            last_generation_state = results[-1]
            classification = game.classify(params, last_generation_state,
                                           game.equilibrium_tolerance)
            frequencies[classification] = 1

        if graph:
            setupGraph(graph, game, dyn, burn, num_gens, results, payoffs)
        else:
            if return_labeled:
                return self._convert_equilibria_frequencies(frequencies)
            else:
                return frequencies, results, payoffs
Esempio n. 8
0
    def simulate_many(self,
                      num_iterations=DEFAULT_ITERATIONS,
                      num_gens=DEFAULT_GENERATIONS,
                      return_labeled=True,
                      burn=0,
                      parallelize=True,
                      graph=False,
                      start_state=None,
                      class_end=False):
        """
        A helper method to call the simulate methods num_iterations times simulating num_gens generations each time,
        and then averaging the frequency of the resulting equilibria. Method calls are parallelized and attempt to
        use all available cores on the machine.

        @param num_iterations: the number of times to iterate the simulation
        @type num_iterations: int
        @param num_gens: the number of generations to run each simulation witu
        @type num_gens: int
        @param return_labeled: whether the distribution of classified equilibria that are returned should be labelled
            or simply listed with their keys inferred by their order
        @type return_labeled: bool
        @param parallelize: whether or not to parallelize the computation, defaults to true, but an override when
            varying the parameters, as seen in the L{VariedGame} class to achieve coarser parallelization
        @type parallelize: bool
        @return: the frequency of time spent in each equilibria, defined by the game
        @rtype: numpy.ndarray or dict
        """
        # TODO move this graphing into graphSetup and link it to extra options
        game = self.game_cls(**self.game_kwargs)
        dyn = self.dynamics_cls(payoff_matrix=game.pm,
                                player_frequencies=game.player_frequencies,
                                **self.dynamics_kwargs)
        frequencies = numpy.zeros(self.game_cls.num_equilibria())
        output = par_for(parallelize)(
            delayed(wrapper_simulate)(self,
                                      num_gens=num_gens,
                                      burn=burn,
                                      start_state=start_state,
                                      class_end=class_end)
            for iteration in range(num_iterations))

        equilibria = []
        strategies = [0] * num_iterations
        payoffs = [0] * num_iterations
        for idx, sim in enumerate(output):
            equilibria.append(sim[0])
            strategies[idx] = sim[1]
            payoffs[idx] = sim[2]

        #TODO move these averages or only compile them if appropriate simulation type
        stratAvg = [
            numpy.zeros(shape=(num_gens, dyn.pm.num_strats[playerIdx]))
            for playerIdx in range(dyn.pm.num_player_types)
        ]
        for iteration in range(num_iterations):
            for player in range(dyn.pm.num_player_types):
                for gen in range(num_gens - burn):
                    for strat in range(dyn.pm.num_strats[player]):
                        stratAvg[player][gen][strat] += strategies[iteration][
                            player][gen][strat]

        for playerIdx, player in enumerate(stratAvg):
            for genIdx, gen in enumerate(player):
                if gen.sum() != 0:
                    gen /= gen.sum()
                    gen *= dyn.num_players

        payoffsAvg = [
            numpy.zeros(shape=(num_gens - 1, dyn.pm.num_strats[playerIdx]))
            for playerIdx in range(dyn.pm.num_player_types)
        ]
        for iteration in range(num_iterations):
            for player in range(dyn.pm.num_player_types):
                for gen in range(num_gens - 1 - burn):
                    for strat in range(dyn.pm.num_strats[player]):
                        payoffsAvg[player][gen][strat] += payoffs[iteration][
                            player][gen][strat]

        for playerIdx, player in enumerate(payoffsAvg):
            for genIdx, gen in enumerate(player):
                if gen.sum() != 0:
                    gen /= gen.sum()
                    gen *= dyn.num_players

        if graph:
            setupGraph(graph, game, dyn, burn, num_gens, stratAvg, payoffs[0])

        for x in equilibria:
            frequencies += x

        frequencies /= frequencies.sum()

        if return_labeled:
            return self._convert_equilibria_frequencies(frequencies)
        else:
            return frequencies