示例#1
0
    def simulate_many(self, num_iterations=DEFAULT_ITERATIONS, num_gens=DEFAULT_GENERATIONS, return_labeled=True, parallelize=True):
        """
        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
        """
        frequencies = numpy.zeros(self.game_cls.num_equilibria())
        output = par_for(parallelize)(delayed(wrapper_simulate)(self, num_gens=num_gens) for iteration in range(num_iterations))

        for x in output:
            frequencies += x

        frequencies /= frequencies.sum()
        if return_labeled:
            return self._convert_equilibria_frequencies(frequencies)
        else:
            return frequencies
示例#2
0
    def simulate_many(self,
                      num_iterations=DEFAULT_ITERATIONS,
                      num_gens=DEFAULT_GENERATIONS,
                      return_labeled=True,
                      parallelize=True):
        """
        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
        """
        frequencies = numpy.zeros(self.game_cls.num_equilibria())
        output = par_for(parallelize)(
            delayed(wrapper_simulate)(self, num_gens=num_gens)
            for iteration in range(num_iterations))

        for x in output:
            frequencies += x

        frequencies /= frequencies.sum()
        if return_labeled:
            return self._convert_equilibria_frequencies(frequencies)
        else:
            return frequencies
示例#3
0
    def _vary_for_kwargs(self, ips, idx, dependent_params, sim_wrapper, chosen_vals, parallelize=False, **kwargs):
        """
        A recursively defined function to iterate over all possible permutations of the variables defined in the list
        of independent variables that returns the simulation results of the cross product of these variable variations.

        @param ips: a list of all the VerboseIndependentParameters that will be varied
        @type ips: list(L{VerboseIndependentParameter})
        @param idx: the index of the independent parameter about to be iterated upon
        @type idx: int
        @param dependent_params: the tuple of dictionaries representing the DependentParameters for the game_kwargs
            and the dynamics_kwargs, respectively.
        @type dependent_params: tuple({string: DependentParameter})
        @param sim_wrapper: the pre-initialized sim-wrapper on which we will call simulate_many
        @type sim_wrapper: L{GameDynamicsWrapper}
        @param chosen_vals: a tuple of all the indices of the chosen values for each already-decided independent param
        @type chosen_vals: tuple(int)
        @param parallelize: whether or not to parallelize the subloops of this function. We set to true on the parent call
            and then false for all recursive calls.
        @type parallelize: bool
        @param kwargs: These are the rest of the keyword arguments that should be passed directly to the simulate_many function call
        @rtype: list(list(...))
        @return: a recursive list of lists representing the simulation results for having assigned each independent parameter
            the value corresponding to the index at which the simulation results are present in the list of lists.

            i.e. Two independent parameters, the return type will be a list of lists of simulation results

            The simulation result present at the address [4][17] represents the value of the simulation when the first
            independent parameter was set to its value at index 4 (@see IndependentParameter.__getitem__), and the second
            independent parameter was set to its value at index 17.
        """

        if idx == len(ips):
            # the list is divided as follows:
            # [[direct_game_kwargs, indirect_game_kwargs], [direct_dynamics_kwargs, indirect_dynamics_kwargs]]
            varied_kwargs = [[{}, {}], [{}, {}]]

            # helper function to return the correct keywords give the desired params
            def kws(ip):
                return varied_kwargs[int(not ip.is_direct)][int(not ip.is_game_kwarg)]

            for chosen_idx, ip in zip(chosen_vals, ips):
                kws(ip)[ip.key] = ip[chosen_idx]

            # the list is organized as follows:
            # [game_kwargs, dynamics_kwargs]
            sim_kwargs = [{}, {}]

            for i in (0, 1):
                # set all the direct ones
                for k, v in varied_kwargs[i][0].items():
                    sim_kwargs[i][k] = v

                # now calculate all of the dependent parameters, as a function of both direct
                # and indirect independent parameters
                for k, dp in dependent_params[i].items():
                    # get the inputs to the dependent param calculation
                    if i == 0:
                        dependent_kw_params = sim_wrapper.game_kwargs.copy()
                    else:
                        dependent_kw_params = sim_wrapper.dynamics_kwargs.copy()
                    dependent_kw_params.update(varied_kwargs[i][0])
                    dependent_kw_params.update(varied_kwargs[i][1])

                    sim_kwargs[i][k] = dp.get_val(**dependent_kw_params)

            sim_wrapper.update_dynamics_kwargs(sim_kwargs[1])
            sim_wrapper.update_game_kwargs(sim_kwargs[0])
            # don't paralellize the simulate_many requests, we are parallelizing higher up in the call chain
            return sim_wrapper.simulate_many(return_labeled=False, parallelize=False, **kwargs)

        var_indices = xrange(len(ips[idx]))
        #dependent_params = [{}, {}]
        return par_for(parallelize)(delayed(wrapper_vary_for_kwargs)(self, ips, idx + 1, dependent_params, sim_wrapper, chosen_vals + (i, ), **kwargs) for i in var_indices)
示例#4
0
    def _vary_for_kwargs(self,
                         ips,
                         idx,
                         dependent_params,
                         sim_wrapper,
                         chosen_vals,
                         parallelize=False,
                         **kwargs):
        """
        A recursively defined function to iterate over all possible permutations of the variables defined in the list
        of independent variables that returns the simulation results of the cross product of these variable variations.

        @param ips: a list of all the VerboseIndependentParameters that will be varied
        @type ips: list(L{VerboseIndependentParameter})
        @param idx: the index of the independent parameter about to be iterated upon
        @type idx: int
        @param dependent_params: the tuple of dictionaries representing the DependentParameters for the game_kwargs
            and the dynamics_kwargs, respectively.
        @type dependent_params: tuple({string: DependentParameter})
        @param sim_wrapper: the pre-initialized sim-wrapper on which we will call simulate_many
        @type sim_wrapper: L{GameDynamicsWrapper}
        @param chosen_vals: a tuple of all the indices of the chosen values for each already-decided independent param
        @type chosen_vals: tuple(int)
        @param parallelize: whether or not to parallelize the subloops of this function. We set to true on the parent call
            and then false for all recursive calls.
        @type parallelize: bool
        @param kwargs: These are the rest of the keyword arguments that should be passed directly to the simulate_many function call
        @rtype: list(list(...))
        @return: a recursive list of lists representing the simulation results for having assigned each independent parameter
            the value corresponding to the index at which the simulation results are present in the list of lists.

            i.e. Two independent parameters, the return type will be a list of lists of simulation results

            The simulation result present at the address [4][17] represents the value of the simulation when the first
            independent parameter was set to its value at index 4 (@see IndependentParameter.__getitem__), and the second
            independent parameter was set to its value at index 17.
        """
        if idx == len(ips):
            # the list is divided as follows:
            # [[direct_game_kwargs, indirect_game_kwargs], [direct_dynamics_kwargs, indirect_dynamics_kwargs]]
            varied_kwargs = [[{}, {}], [{}, {}]]

            # helper function to return the correct keywords give the desired params
            def kws(ip):
                return varied_kwargs[int(not ip.is_game_kwarg)][int(
                    not ip.is_direct)]

            for chosen_idx, ip in zip(chosen_vals, ips):
                kws(ip)[ip.key] = ip[chosen_idx]

            # the list is organized as follows:
            # [game_kwargs, dynamics_kwargs]
            sim_kwargs = [{}, {}]

            for i in (0, 1):
                # set all the direct ones
                for k, v in varied_kwargs[i][0].items():
                    sim_kwargs[i][k] = v

                # now calculate all of the dependent parameters, as a function of both direct
                # and indirect independent parameters
                for k, dp in dependent_params[i].items():
                    # get the inputs to the dependent param calculation
                    if i == 0:

                        dependent_kw_params = sim_wrapper.game_kwargs.copy()
                    else:
                        dependent_kw_params = sim_wrapper.dynamics_kwargs.copy(
                        )
                    dependent_kw_params.update(varied_kwargs[i][0])
                    dependent_kw_params.update(varied_kwargs[i][1])

                    sim_kwargs[i][k] = dp.get_val(**dependent_kw_params)

            sim_wrapper.update_dynamics_kwargs(sim_kwargs[1])
            sim_wrapper.update_game_kwargs(sim_kwargs[0])
            # don't parallellize the simulate_many requests, we are parallelizing higher up in the call chain
            return sim_wrapper.simulate_many(return_labeled=False,
                                             parallelize=False,
                                             **kwargs)

        var_indices = range(len(ips[idx]))
        #dependent_params = [{}, {}]
        return par_for(parallelize)(delayed(wrapper_vary_for_kwargs)(
            self, ips, idx + 1, dependent_params, sim_wrapper, chosen_vals +
            (i, ), **kwargs) for i in var_indices)
示例#5
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
示例#6
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