Instruction:
Write a Python 3 function for
returning an array of payoff values one for each own action given the profile of the opponents actions.

Response:
def payoff_vector(self, opponents_actions):
        """
        Return an array of payoff values, one for each own action, given
        a profile of the opponents' actions.

        Parameters
        ----------
        opponents_actions : see `best_response`.

        Returns
        -------
        payoff_vector : ndarray(float, ndim=1)
            An array representing the player's payoff vector given the
            profile of the opponents' actions.

        """
        def reduce_last_player(payoff_array, action):
            """
            Given `payoff_array` with ndim=M, return the payoff array
            with ndim=M-1 fixing the last player's action to be `action`.

            """
            if isinstance(action, numbers.Integral):  # pure action
                return payoff_array.take(action, axis=-1)
            else:  # mixed action
                return payoff_array.dot(action)

        if self.num_opponents == 1:
            payoff_vector = \
                reduce_last_player(self.payoff_array, opponents_actions)
        elif self.num_opponents >= 2:
            payoff_vector = self.payoff_array
            for i in reversed(range(self.num_opponents)):
                payoff_vector = \
                    reduce_last_player(payoff_vector, opponents_actions[i])
        else:  # Trivial case with self.num_opponents == 0
            payoff_vector = self.payoff_array

        return payoff_vector


Instruction:
Explain what the following Python 3 code does
def is_best_response(self, own_action, opponents_actions, tol=None):
        if tol is None:
            tol = self.tol

        payoff_vector = self.payoff_vector(opponents_actions)
        payoff_max = payoff_vector.max()

        if isinstance(own_action, numbers.Integral):
            return payoff_vector[own_action] >= payoff_max - tol
        else:
            return np.dot(own_action, payoff_vector) >= payoff_max - tol

Response: Return True if own_action is a best response to opponents_actions.


Instruction:
How would you code a function in Python 3 to
return the best response action to opponents_actions.

Response:
def best_response(self, opponents_actions, tie_breaking='smallest',
                      payoff_perturbation=None, tol=None, random_state=None):
        """
        Return the best response action(s) to `opponents_actions`.

        Parameters
        ----------
        opponents_actions : scalar(int) or array_like
            A profile of N-1 opponents' actions, represented by either
            scalar(int), array_like(float), array_like(int), or
            array_like(array_like(float)). If N=2, then it must be a
            scalar of integer (in which case it is treated as the
            opponent's pure action) or a 1-dimensional array of floats
            (in which case it is treated as the opponent's mixed
            action). If N>2, then it must be an array of N-1 objects,
            where each object must be an integer (pure action) or an
            array of floats (mixed action).

        tie_breaking : str, optional(default='smallest')
            str in {'smallest', 'random', False}. Control how, or
            whether, to break a tie (see Returns for details).

        payoff_perturbation : array_like(float), optional(default=None)
            Array of length equal to the number of actions of the player
            containing the values ("noises") to be added to the payoffs
            in determining the best response.

        tol : scalar(float), optional(default=None)
            Tolerance level used in determining best responses. If None,
            default to the value of the `tol` attribute.

        random_state : int or np.random.RandomState, optional
            Random seed (integer) or np.random.RandomState instance to
            set the initial state of the random number generator for
            reproducibility. If None, a randomly initialized RandomState
            is used. Relevant only when tie_breaking='random'.

        Returns
        -------
        scalar(int) or ndarray(int, ndim=1)
            If tie_breaking=False, returns an array containing all the
            best response pure actions. If tie_breaking='smallest',
            returns the best response action with the smallest index; if
            tie_breaking='random', returns an action randomly chosen
            from the best response actions.

        """
        if tol is None:
            tol = self.tol

        payoff_vector = self.payoff_vector(opponents_actions)
        if payoff_perturbation is not None:
            try:
                payoff_vector += payoff_perturbation
            except TypeError:  # type mismatch
                payoff_vector = payoff_vector + payoff_perturbation

        best_responses = \
            np.where(payoff_vector >= payoff_vector.max() - tol)[0]

        if tie_breaking == 'smallest':
            return best_responses[0]
        elif tie_breaking == 'random':
            return self.random_choice(best_responses,
                                      random_state=random_state)
        elif tie_breaking is False:
            return best_responses
        else:
            msg = "tie_breaking must be one of 'smallest', 'random', or False"
            raise ValueError(msg)


Instruction:
Write a Python 3 script for
returning a random action from the actions array.

Response:
def random_choice(self, actions=None, random_state=None):
        """
        Return a pure action chosen randomly from `actions`.

        Parameters
        ----------
        actions : array_like(int), optional(default=None)
            An array of integers representing pure actions.

        random_state : int or np.random.RandomState, optional
            Random seed (integer) or np.random.RandomState instance to
            set the initial state of the random number generator for
            reproducibility. If None, a randomly initialized RandomState
            is used.

        Returns
        -------
        scalar(int)
            If `actions` is given, returns an integer representing a
            pure action chosen randomly from `actions`; if not, an
            action is chosen randomly from the player's all actions.

        """
        random_state = check_random_state(random_state)

        if actions is not None:
            n = len(actions)
        else:
            n = self.num_actions

        if n == 1:
            idx = 0
        else:
            idx = random_state.randint(n)

        if actions is not None:
            return actions[idx]
        else:
            return idx


Instruction:
How would you code a function in Python 3 to
determine if an action is strictly dominated by some mixed action.

Response:
def is_dominated(self, action, tol=None, method=None):
        """
        Determine whether `action` is strictly dominated by some mixed
        action.

        Parameters
        ----------
        action : scalar(int)
            Integer representing a pure action.

        tol : scalar(float), optional(default=None)
            Tolerance level used in determining domination. If None,
            default to the value of the `tol` attribute.

        method : str, optional(default=None)
            If None, `lemke_howson` from `quantecon.game_theory` is used
            to solve for a Nash equilibrium of an auxiliary zero-sum
            game. If `method` is set to `'simplex'` or
            `'interior-point'`, `scipy.optimize.linprog` is used with
            the method as specified by `method`.

        Returns
        -------
        bool
            True if `action` is strictly dominated by some mixed action;
            False otherwise.

        """
        if tol is None:
            tol = self.tol

        payoff_array = self.payoff_array

        if self.num_opponents == 0:
            return payoff_array.max() > payoff_array[action] + tol

        ind = np.ones(self.num_actions, dtype=bool)
        ind[action] = False
        D = payoff_array[ind]
        D -= payoff_array[action]
        if D.shape[0] == 0:  # num_actions == 1
            return False
        if self.num_opponents >= 2:
            D.shape = (D.shape[0], np.prod(D.shape[1:]))

        if method is None:
            from .lemke_howson import lemke_howson
            g_zero_sum = NormalFormGame([Player(D), Player(-D.T)])
            NE = lemke_howson(g_zero_sum)
            return NE[0] @ D @ NE[1] > tol
        elif method in ['simplex', 'interior-point']:
            from scipy.optimize import linprog
            m, n = D.shape
            A = np.empty((n+2, m+1))
            A[:n, :m] = -D.T
            A[:n, -1] = 1  # Slack variable
            A[n, :m], A[n+1, :m] = 1, -1  # Equality constraint
            A[n:, -1] = 0
            b = np.empty(n+2)
            b[:n] = 0
            b[n], b[n+1] = 1, -1
            c = np.zeros(m+1)
            c[-1] = -1
            res = linprog(c, A_ub=A, b_ub=b, method=method)
            if res.success:
                return res.x[-1] > tol
            elif res.status == 2:  # infeasible
                return False
            else:  # pragma: no cover
                msg = 'scipy.optimize.linprog returned {0}'.format(res.status)
                raise RuntimeError(msg)
        else:
            raise ValueError('Unknown method {0}'.format(method))


Instruction:
How would you explain what the following Python 3 function does
def dominated_actions(self, tol=None, method=None):
        out = []
        for action in range(self.num_actions):
            if self.is_dominated(action, tol=tol, method=method):
                out.append(action)
        return out

Response: Returns a list of actions that are strictly dominated by some mixed action.


Instruction:
Make a summary of the following Python 3 code
def delete_action(self, player_idx, action):
        # Allow negative indexing
        if -self.N <= player_idx < 0:
            player_idx = player_idx + self.N

        players_new = tuple(
            player.delete_action(action, player_idx-i)
            for i, player in enumerate(self.players)
        )
        return NormalFormGame(players_new)

Response: Delete the action set of the player with the specified index.


Instruction:
Can you generate the documentation for the following Python 3 function
def is_nash(self, action_profile, tol=None):
        if self.N == 2:
            for i, player in enumerate(self.players):
                own_action, opponent_action = \
                    action_profile[i], action_profile[1-i]
                if not player.is_best_response(own_action, opponent_action,
                                               tol):
                    return False

        elif self.N >= 3:
            for i, player in enumerate(self.players):
                own_action = action_profile[i]
                opponents_actions = \
                    tuple(action_profile[i+1:]) + tuple(action_profile[:i])

                if not player.is_best_response(own_action, opponents_actions,
                                               tol):
                    return False

        else:  # Trivial case with self.N == 1
            if not self.players[0].is_best_response(action_profile[0], None,
                                                    tol):
                return False

        return True

Response: Return True if the action_profile is a Nash equilibrium.


Instruction:
Can you tell what is the following Python 3 function doing
def mclennan_tourky(g, init=None, epsilon=1e-3, max_iter=200,
                    full_output=False):
    r"""
    Find one mixed-action epsilon-Nash equilibrium of an N-player normal
    form game by the fixed point computation algorithm by McLennan and
    Tourky [1]_.

    Parameters
    ----------
    g : NormalFormGame
        NormalFormGame instance.

    init : array_like(int or array_like(float, ndim=1)), optional
        Initial action profile, an array of N objects, where each object
        must be an iteger (pure action) or an array of floats (mixed
        action). If None, default to an array of zeros (the zero-th
        action for each player).

    epsilon : scalar(float), optional(default=1e-3)
        Value of epsilon-optimality.

    max_iter : scalar(int), optional(default=100)
        Maximum number of iterations.

    full_output : bool, optional(default=False)
        If False, only the computed Nash equilibrium is returned. If
        True, the return value is `(NE, res)`, where `NE` is the Nash
        equilibrium and `res` is a `NashResult` object.

    Returns
    -------
    NE : tuple(ndarray(float, ndim=1))
        Tuple of computed Nash equilibrium mixed actions.

    res : NashResult
        Object containing information about the computation. Returned
        only when `full_output` is True. See `NashResult` for details.

    Examples
    --------
    Consider the following version of 3-player "anti-coordination" game,
    where action 0 is a safe action which yields payoff 1, while action
    1 yields payoff :math:`v` if no other player plays 1 and payoff 0
    otherwise:

    >>> N = 3
    >>> v = 2
    >>> payoff_array = np.empty((2,)*n)
    >>> payoff_array[0, :] = 1
    >>> payoff_array[1, :] = 0
    >>> payoff_array[1].flat[0] = v
    >>> g = NormalFormGame((Player(payoff_array),)*N)
    >>> print(g)
    3-player NormalFormGame with payoff profile array:
    [[[[ 1.,  1.,  1.],   [ 1.,  1.,  2.]],
      [[ 1.,  2.,  1.],   [ 1.,  0.,  0.]]],
     [[[ 2.,  1.,  1.],   [ 0.,  1.,  0.]],
      [[ 0.,  0.,  1.],   [ 0.,  0.,  0.]]]]

    This game has a unique symmetric Nash equilibrium, where the
    equilibrium action is given by :math:`(p^*, 1-p^*)` with :math:`p^*
    = 1/v^{1/(N-1)}`:

    >>> p_star = 1/(v**(1/(N-1)))
    >>> [p_star, 1 - p_star]
    [0.7071067811865475, 0.29289321881345254]

    Obtain an approximate Nash equilibrium of this game by
    `mclennan_tourky`:

    >>> epsilon = 1e-5  # Value of epsilon-optimality
    >>> NE = mclennan_tourky(g, epsilon=epsilon)
    >>> print(NE[0], NE[1], NE[2], sep='\n')
    [ 0.70710754  0.29289246]
    [ 0.70710754  0.29289246]
    [ 0.70710754  0.29289246]
    >>> g.is_nash(NE, tol=epsilon)
    True

    Additional information is returned if `full_output` is set True:

    >>> NE, res = mclennan_tourky(g, epsilon=epsilon, full_output=True)
    >>> res.converged
    True
    >>> res.num_iter
    18

    References
    ----------
    .. [1] A. McLennan and R. Tourky, "From Imitation Games to
       Kakutani," 2006.

    """
    try:
        N = g.N
    except:
        raise TypeError('g must be a NormalFormGame')
    if N < 2:
        raise NotImplementedError('Not implemented for 1-player games')

    if init is None:
        init = (0,) * N
    try:
        l = len(init)
    except TypeError:
        raise TypeError('init must be array_like')
    if l != N:
        raise ValueError(
            'init must be of length {N}'.format(N=N)
        )

    indptr = np.empty(N+1, dtype=int)
    indptr[0] = 0
    indptr[1:] = np.cumsum(g.nums_actions)
    x_init = _flatten_action_profile(init, indptr)

    is_approx_fp = lambda x: _is_epsilon_nash(x, g, epsilon, indptr)
    x_star, converged, num_iter = \
        _compute_fixed_point_ig(_best_response_selection, x_init, max_iter,
                                verbose=0, print_skip=1,
                                is_approx_fp=is_approx_fp,
                                g=g, indptr=indptr)
    NE = _get_action_profile(x_star, indptr)

    if not full_output:
        return NE

    res = NashResult(NE=NE,
                     converged=converged,
                     num_iter=num_iter,
                     max_iter=max_iter,
                     init=init,
                     epsilon=epsilon)

    return NE, res

Response: r This function is used to compute mixed - action epsilon - Nash equilibriums of a normal - form game by McLennan and McTourky.


Instruction:
Can you generate a brief explanation for the following Python 3 code
def _is_epsilon_nash(x, g, epsilon, indptr=None):
    if indptr is None:
        indptr = np.empty(g.N+1, dtype=int)
        indptr[0] = 0
        indptr[1:] = np.cumsum(g.nums_actions)

    action_profile = _get_action_profile(x, indptr)
    return g.is_nash(action_profile, tol=epsilon)

Response: Determines whether x is an epsilon - Nash equilibrium of g.


Instruction:
Create a Python 3 function to
return a tuple of mixed actions from a flattened action profile.

Response:
def _get_action_profile(x, indptr):
    """
    Obtain a tuple of mixed actions from a flattened action profile.

    Parameters
    ----------
    x : array_like(float, ndim=1)
        Array of flattened mixed action profile of length equal to n_0 +
        ... + n_N-1, where `out[indptr[i]:indptr[i+1]]` contains player
        i's mixed action.

    indptr : array_like(int, ndim=1)
        Array of index pointers of length N+1, where `indptr[0] = 0` and
        `indptr[i+1] = indptr[i] + n_i`.

    Returns
    -------
    action_profile : tuple(ndarray(float, ndim=1))
        Tuple of N mixed actions, each of length n_i.

    """
    N = len(indptr) - 1
    action_profile = tuple(x[indptr[i]:indptr[i+1]] for i in range(N))
    return action_profile


Instruction:
Can you implement a function in Python 3 that
flattens the given action profile into a single array of mixed actions.

Response:
def _flatten_action_profile(action_profile, indptr):
    """
    Flatten the given action profile.

    Parameters
    ----------
    action_profile : array_like(int or array_like(float, ndim=1))
        Profile of actions of the N players, where each player i' action
        is a pure action (int) or a mixed action (array_like of floats
        of length n_i).

    indptr : array_like(int, ndim=1)
        Array of index pointers of length N+1, where `indptr[0] = 0` and
        `indptr[i+1] = indptr[i] + n_i`.

    Returns
    -------
    out : ndarray(float, ndim=1)
        Array of flattened mixed action profile of length equal to n_0 +
        ... + n_N-1, where `out[indptr[i]:indptr[i+1]]` contains player
        i's mixed action.

    """
    N = len(indptr) - 1
    out = np.empty(indptr[-1])

    for i in range(N):
        if isinstance(action_profile[i], numbers.Integral):  # pure action
            num_actions = indptr[i+1] - indptr[i]
            mixed_action = pure2mixed(num_actions, action_profile[i])
        else:  # mixed action
            mixed_action = action_profile[i]
        out[indptr[i]:indptr[i+1]] = mixed_action

    return out


Instruction:
How would you code a function in Python 3 to
return a NormalFormGame instance of a 2 - player non - zero sum Colonel

Response:
def blotto_game(h, t, rho, mu=0, random_state=None):
    """
    Return a NormalFormGame instance of a 2-player non-zero sum Colonel
    Blotto game (Hortala-Vallve and Llorente-Saguer, 2012), where the
    players have an equal number `t` of troops to assign to `h` hills
    (so that the number of actions for each player is equal to
    (t+h-1) choose (h-1) = (t+h-1)!/(t!*(h-1)!)). Each player has a
    value for each hill that he receives if he assigns strictly more
    troops to the hill than his opponent (ties are broken uniformly at
    random), where the values are drawn from a multivariate normal
    distribution with covariance `rho`. Each player’s payoff is the sum
    of the values of the hills won by that player.

    Parameters
    ----------
    h : scalar(int)
        Number of hills.
    t : scalar(int)
        Number of troops.
    rho : scalar(float)
        Covariance of the players' values of each hill. Must be in
        [-1, 1].
    mu : scalar(float), optional(default=0)
        Mean of the players' values of each hill.
    random_state : int or np.random.RandomState, optional
        Random seed (integer) or np.random.RandomState instance to set
        the initial state of the random number generator for
        reproducibility. If None, a randomly initialized RandomState is
        used.

    Returns
    -------
    g : NormalFormGame

    Examples
    --------
    >>> g = blotto_game(2, 3, 0.5, random_state=1234)
    >>> g.players[0]
    Player([[-0.44861083, -1.08443468, -1.08443468, -1.08443468],
            [ 0.18721302, -0.44861083, -1.08443468, -1.08443468],
            [ 0.18721302,  0.18721302, -0.44861083, -1.08443468],
            [ 0.18721302,  0.18721302,  0.18721302, -0.44861083]])
    >>> g.players[1]
    Player([[-1.20042463, -1.39708658, -1.39708658, -1.39708658],
            [-1.00376268, -1.20042463, -1.39708658, -1.39708658],
            [-1.00376268, -1.00376268, -1.20042463, -1.39708658],
            [-1.00376268, -1.00376268, -1.00376268, -1.20042463]])

    """
    actions = simplex_grid(h, t)
    n = actions.shape[0]
    payoff_arrays = tuple(np.empty((n, n)) for i in range(2))
    mean = np.array([mu, mu])
    cov = np.array([[1, rho], [rho, 1]])
    random_state = check_random_state(random_state)
    values = random_state.multivariate_normal(mean, cov, h)
    _populate_blotto_payoff_arrays(payoff_arrays, actions, values)
    g = NormalFormGame(
        [Player(payoff_array) for payoff_array in payoff_arrays]
    )
    return g


Instruction:
Given the following Python 3 function, write the documentation
def _populate_blotto_payoff_arrays(payoff_arrays, actions, values):
    n, h = actions.shape
    payoffs = np.empty(2)
    for i in range(n):
        for j in range(n):
            payoffs[:] = 0
            for k in range(h):
                if actions[i, k] == actions[j, k]:
                    for p in range(2):
                        payoffs[p] += values[k, p] / 2
                else:
                    winner = np.int(actions[i, k] < actions[j, k])
                    payoffs[winner] += values[k, winner]
            payoff_arrays[0][i, j], payoff_arrays[1][j, i] = payoffs

Response: Populate the ndarrays in the payoff_arrays with the payoff values of the current node.


Instruction:
How would you explain what the following Python 3 function does
def ranking_game(n, steps=10, random_state=None):
    payoff_arrays = tuple(np.empty((n, n)) for i in range(2))
    random_state = check_random_state(random_state)

    scores = random_state.randint(1, steps+1, size=(2, n))
    scores.cumsum(axis=1, out=scores)

    costs = np.empty((2, n-1))
    costs[:] = random_state.randint(1, steps+1, size=(2, n-1))
    costs.cumsum(axis=1, out=costs)
    costs[:] /= (n * steps)

    _populate_ranking_payoff_arrays(payoff_arrays, scores, costs)
    g = NormalFormGame(
        [Player(payoff_array) for payoff_array in payoff_arrays]
    )
    return g

Response: Returns a ranking game for the given number of steps and random number generator.


Instruction:
Can you generate a brief explanation for the following Python 3 code
def _populate_ranking_payoff_arrays(payoff_arrays, scores, costs):
    n = payoff_arrays[0].shape[0]
    for p, payoff_array in enumerate(payoff_arrays):
        payoff_array[0, :] = 0
        for i in range(1, n):
            for j in range(n):
                payoff_array[i, j] = -costs[p, i-1]

    prize = 1.
    for i in range(n):
        for j in range(n):
            if scores[0, i] > scores[1, j]:
                payoff_arrays[0][i, j] += prize
            elif scores[0, i] < scores[1, j]:
                payoff_arrays[1][j, i] += prize
            else:
                payoff_arrays[0][i, j] += prize / 2
                payoff_arrays[1][j, i] += prize / 2

Response:
Populate the ndarrays in the ranking_payoff_arrays with the payoff values of
Apps that are given by the scores and costs.


Instruction:
Can you create a Python 3 function that
returns a NormalFormGame instance of the 2 - player game introduced by the given nash .

Response:
def sgc_game(k):
    """
    Return a NormalFormGame instance of the 2-player game introduced by
    Sandholm, Gilpin, and Conitzer (2005), which has a unique Nash
    equilibrium, where each player plays half of the actions with
    positive probabilities. Payoffs are normalized so that the minimum
    and the maximum payoffs are 0 and 1, respectively.

    Parameters
    ----------
    k : scalar(int)
        Positive integer determining the number of actions. The returned
        game will have `4*k-1` actions for each player.

    Returns
    -------
    g : NormalFormGame

    Examples
    --------
    >>> g = sgc_game(2)
    >>> g.players[0]
    Player([[ 0.75,  0.5 ,  1.  ,  0.5 ,  0.5 ,  0.5 ,  0.5 ],
            [ 1.  ,  0.75,  0.5 ,  0.5 ,  0.5 ,  0.5 ,  0.5 ],
            [ 0.5 ,  1.  ,  0.75,  0.5 ,  0.5 ,  0.5 ,  0.5 ],
            [ 0.  ,  0.  ,  0.  ,  0.75,  0.  ,  0.  ,  0.  ],
            [ 0.  ,  0.  ,  0.  ,  0.  ,  0.75,  0.  ,  0.  ],
            [ 0.  ,  0.  ,  0.  ,  0.  ,  0.  ,  0.75,  0.  ],
            [ 0.  ,  0.  ,  0.  ,  0.  ,  0.  ,  0.  ,  0.75]])
    >>> g.players[1]
    Player([[ 0.75,  0.5 ,  1.  ,  0.5 ,  0.5 ,  0.5 ,  0.5 ],
            [ 1.  ,  0.75,  0.5 ,  0.5 ,  0.5 ,  0.5 ,  0.5 ],
            [ 0.5 ,  1.  ,  0.75,  0.5 ,  0.5 ,  0.5 ,  0.5 ],
            [ 0.  ,  0.  ,  0.  ,  0.  ,  0.75,  0.  ,  0.  ],
            [ 0.  ,  0.  ,  0.  ,  0.75,  0.  ,  0.  ,  0.  ],
            [ 0.  ,  0.  ,  0.  ,  0.  ,  0.  ,  0.  ,  0.75],
            [ 0.  ,  0.  ,  0.  ,  0.  ,  0.  ,  0.75,  0.  ]])

    """
    payoff_arrays = tuple(np.empty((4*k-1, 4*k-1)) for i in range(2))
    _populate_sgc_payoff_arrays(payoff_arrays)
    g = NormalFormGame(
        [Player(payoff_array) for payoff_array in payoff_arrays]
    )
    return g


Instruction:
Can you generate the documentation for the following Python 3 function
def _populate_sgc_payoff_arrays(payoff_arrays):
    n = payoff_arrays[0].shape[0]  # 4*k-1
    m = (n+1)//2 - 1  # 2*k-1
    for payoff_array in payoff_arrays:
        for i in range(m):
            for j in range(m):
                payoff_array[i, j] = 0.75
            for j in range(m, n):
                payoff_array[i, j] = 0.5
        for i in range(m, n):
            for j in range(n):
                payoff_array[i, j] = 0

        payoff_array[0, m-1] = 1
        payoff_array[0, 1] = 0.5
        for i in range(1, m-1):
            payoff_array[i, i-1] = 1
            payoff_array[i, i+1] = 0.5
        payoff_array[m-1, m-2] = 1
        payoff_array[m-1, 0] = 0.5

    k = (m+1)//2
    for h in range(k):
        i, j = m + 2*h, m + 2*h
        payoff_arrays[0][i, j] = 0.75
        payoff_arrays[0][i+1, j+1] = 0.75
        payoff_arrays[1][j, i+1] = 0.75
        payoff_arrays[1][j+1, i] = 0.75

Response: Populate the ndarrays in the SGC game with the payoff values of the SGC game.


Instruction:
Create a Python 3 function for
returning a NormalFormGame instance of the 2 - player win - lose game with the payoff of 0 and 1 for all of the k - subset nodes.

Response:
def tournament_game(n, k, random_state=None):
    """
    Return a NormalFormGame instance of the 2-player win-lose game,
    whose payoffs are either 0 or 1, introduced by Anbalagan et al.
    (2013). Player 0 has n actions, which constitute the set of nodes
    {0, ..., n-1}, while player 1 has n choose k actions, each
    corresponding to a subset of k elements of the set of n nodes. Given
    a randomly generated tournament graph on the n nodes, the payoff for
    player 0 is 1 if, in the tournament, the node chosen by player 0
    dominates all the nodes in the k-subset chosen by player 1. The
    payoff for player 1 is 1 if player 1's k-subset contains player 0's
    chosen node.

    Parameters
    ----------
    n : scalar(int)
        Number of nodes in the tournament graph.
    k : scalar(int)
        Size of subsets of nodes in the tournament graph.
    random_state : int or np.random.RandomState, optional
        Random seed (integer) or np.random.RandomState instance to set
        the initial state of the random number generator for
        reproducibility. If None, a randomly initialized RandomState is
        used.

    Returns
    -------
    g : NormalFormGame

    Notes
    -----
    The actions of player 1 are ordered according to the combinatorial
    number system [1]_, which is different from the order used in the
    original library in C.

    Examples
    --------
    >>> g = tournament_game(5, 2, random_state=1234)
    >>> g.players[0]
    Player([[ 0.,  0.,  0.,  0.,  1.,  0.,  0.,  0.,  0.,  0.],
            [ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  1.],
            [ 1.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.],
            [ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.],
            [ 0.,  1.,  0.,  1.,  0.,  1.,  0.,  0.,  0.,  0.]])
    >>> g.players[1]
    Player([[ 1.,  1.,  0.,  0.,  0.],
            [ 1.,  0.,  1.,  0.,  0.],
            [ 0.,  1.,  1.,  0.,  0.],
            [ 1.,  0.,  0.,  1.,  0.],
            [ 0.,  1.,  0.,  1.,  0.],
            [ 0.,  0.,  1.,  1.,  0.],
            [ 1.,  0.,  0.,  0.,  1.],
            [ 0.,  1.,  0.,  0.,  1.],
            [ 0.,  0.,  1.,  0.,  1.],
            [ 0.,  0.,  0.,  1.,  1.]])

    References
    ----------
    .. [1] `Combinatorial number system
       <https://en.wikipedia.org/wiki/Combinatorial_number_system>`_,
       Wikipedia.

    """
    m = scipy.special.comb(n, k, exact=True)
    if m > np.iinfo(np.intp).max:
        raise ValueError('Maximum allowed size exceeded')

    payoff_arrays = tuple(np.zeros(shape) for shape in [(n, m), (m, n)])
    tourn = random_tournament_graph(n, random_state=random_state)
    indices, indptr = tourn.csgraph.indices, tourn.csgraph.indptr
    _populate_tournament_payoff_array0(payoff_arrays[0], k, indices, indptr)
    _populate_tournament_payoff_array1(payoff_arrays[1], k)
    g = NormalFormGame(
        [Player(payoff_array) for payoff_array in payoff_arrays]
    )
    return g


Instruction:
Can you generate the documentation for the following Python 3 function
def _populate_tournament_payoff_array0(payoff_array, k, indices, indptr):
    n = payoff_array.shape[0]
    X = np.empty(k, dtype=np.int_)
    a = np.empty(k, dtype=np.int_)
    for i in range(n):
        d = indptr[i+1] - indptr[i]
        if d >= k:
            for j in range(k):
                a[j] = j
            while a[-1] < d:
                for j in range(k):
                    X[j] = indices[indptr[i]+a[j]]
                payoff_array[i, k_array_rank_jit(X)] = 1
                a = next_k_array(a)

Response: Populate payoff_array with payoff values for player 0 in the tournament game given a random tournament graph.


Instruction:
Can you generate a brief explanation for the following Python 3 code
def _populate_tournament_payoff_array1(payoff_array, k):
    m = payoff_array.shape[0]
    X = np.arange(k)
    for j in range(m):
        for i in range(k):
            payoff_array[j, X[i]] = 1
        X = next_k_array(X)

Response: Populate payoff_array with the payoff values for player 1 in the tournament game.


Instruction:
Can you implement a function in Python 3 that
returns a NormalFormGame instance of the 2 - player game unit vector game.

Response:
def unit_vector_game(n, avoid_pure_nash=False, random_state=None):
    """
    Return a NormalFormGame instance of the 2-player game "unit vector
    game" (Savani and von Stengel, 2016). Payoffs for player 1 are
    chosen randomly from the [0, 1) range. For player 0, each column
    contains exactly one 1 payoff and the rest is 0.

    Parameters
    ----------
    n : scalar(int)
        Number of actions.
    avoid_pure_nash : bool, optional(default=False)
        If True, player 0's payoffs will be placed in order to avoid
        pure Nash equilibria. (If necessary, the payoffs for player 1
        are redrawn so as not to have a dominant action.)
    random_state : int or np.random.RandomState, optional
        Random seed (integer) or np.random.RandomState instance to set
        the initial state of the random number generator for
        reproducibility. If None, a randomly initialized RandomState is
        used.

    Returns
    -------
    g : NormalFormGame

    Examples
    --------
    >>> g = unit_vector_game(4, random_state=1234)
    >>> g.players[0]
    Player([[ 1.,  0.,  1.,  0.],
            [ 0.,  0.,  0.,  1.],
            [ 0.,  0.,  0.,  0.],
            [ 0.,  1.,  0.,  0.]])
    >>> g.players[1]
    Player([[ 0.19151945,  0.62210877,  0.43772774,  0.78535858],
            [ 0.77997581,  0.27259261,  0.27646426,  0.80187218],
            [ 0.95813935,  0.87593263,  0.35781727,  0.50099513],
            [ 0.68346294,  0.71270203,  0.37025075,  0.56119619]])

    With `avoid_pure_nash=True`:

    >>> g = unit_vector_game(4, avoid_pure_nash=True, random_state=1234)
    >>> g.players[0]
    Player([[ 1.,  1.,  0.,  0.],
            [ 0.,  0.,  0.,  0.],
            [ 0.,  0.,  1.,  1.],
            [ 0.,  0.,  0.,  0.]])
    >>> g.players[1]
    Player([[ 0.19151945,  0.62210877,  0.43772774,  0.78535858],
            [ 0.77997581,  0.27259261,  0.27646426,  0.80187218],
            [ 0.95813935,  0.87593263,  0.35781727,  0.50099513],
            [ 0.68346294,  0.71270203,  0.37025075,  0.56119619]])
    >>> pure_nash_brute(g)
    []

    """
    random_state = check_random_state(random_state)
    payoff_arrays = (np.zeros((n, n)), random_state.random_sample((n, n)))

    if not avoid_pure_nash:
        ones_ind = random_state.randint(n, size=n)
        payoff_arrays[0][ones_ind, np.arange(n)] = 1
    else:
        if n == 1:
            raise ValueError('Cannot avoid pure Nash with n=1')
        maxes = payoff_arrays[1].max(axis=0)
        is_suboptimal = payoff_arrays[1] < maxes
        nums_suboptimal = is_suboptimal.sum(axis=1)

        while (nums_suboptimal==0).any():
            payoff_arrays[1][:] = random_state.random_sample((n, n))
            payoff_arrays[1].max(axis=0, out=maxes)
            np.less(payoff_arrays[1], maxes, out=is_suboptimal)
            is_suboptimal.sum(axis=1, out=nums_suboptimal)

        for i in range(n):
            one_ind = random_state.randint(n)
            while not is_suboptimal[i, one_ind]:
                one_ind = random_state.randint(n)
            payoff_arrays[0][one_ind, i] = 1

    g = NormalFormGame(
        [Player(payoff_array) for payoff_array in payoff_arrays]
    )
    return g


Instruction:
Explain what the following Python 3 code does
def gth_solve(A, overwrite=False, use_jit=True):
    A1 = np.array(A, dtype=float, copy=not overwrite, order='C')
    # `order='C'` is for use with Numba <= 0.18.2
    # See issue github.com/numba/numba/issues/1103

    if len(A1.shape) != 2 or A1.shape[0] != A1.shape[1]:
        raise ValueError('matrix must be square')

    n = A1.shape[0]
    x = np.zeros(n)

    if use_jit:
        _gth_solve_jit(A1, x)
        return x

    # if not using jit
    # === Reduction === #
    for k in range(n-1):
        scale = np.sum(A1[k, k+1:n])
        if scale <= 0:
            # There is one (and only one) recurrent class contained in
            # {0, ..., k};
            # compute the solution associated with that recurrent class.
            n = k+1
            break
        A1[k+1:n, k] /= scale

        A1[k+1:n, k+1:n] += np.dot(A1[k+1:n, k:k+1], A1[k:k+1, k+1:n])

    # === Backward substitution === #
    x[n-1] = 1
    for k in range(n-2, -1, -1):
        x[k] = np.dot(x[k+1:n], A1[k+1:n, k])

    # === Normalization === #
    x /= np.sum(x)

    return x

Response: r Solve the Grassmann - Taksar - Heyman - GTH algorithm for a given irreducible markov transition matrix or generator matrix.


Instruction:
How would you explain what the following Python 3 function does
def _csr_matrix_indices(S):
    m, n = S.shape

    for i in range(m):
        for j in range(S.indptr[i], S.indptr[i+1]):
            row_index, col_index = i, S.indices[j]
            yield row_index, col_index

Response: Generate the indices of nonzero entries of a csr_matrix S


Instruction:
Can you generate the documentation for the following Python 3 function
def random_tournament_graph(n, random_state=None):
    random_state = check_random_state(random_state)
    num_edges = n * (n-1) // 2
    r = random_state.random_sample(num_edges)
    row = np.empty(num_edges, dtype=int)
    col = np.empty(num_edges, dtype=int)
    _populate_random_tournament_row_col(n, r, row, col)
    data = np.ones(num_edges, dtype=bool)
    adj_matrix = sparse.coo_matrix((data, (row, col)), shape=(n, n))
    return DiGraph(adj_matrix)

Response: Generate a random tournament graph with n nodes.


Instruction:
Here you have a function in Python 3, explain what it does
def _populate_random_tournament_row_col(n, r, row, col):
    k = 0
    for i in range(n):
        for j in range(i+1, n):
            if r[k] < 0.5:
                row[k], col[k] = i, j
            else:
                row[k], col[k] = j, i
            k += 1

Response: Populate row and col with random numbers in r.


Instruction:
How would you code a function in Python 3 to
find strongly connected components for this node.

Response:
def _find_scc(self):
        """
        Set ``self._num_scc`` and ``self._scc_proj``
        by calling ``scipy.sparse.csgraph.connected_components``:
        * docs.scipy.org/doc/scipy/reference/sparse.csgraph.html
        * github.com/scipy/scipy/blob/master/scipy/sparse/csgraph/_traversal.pyx

        ``self._scc_proj`` is a list of length `n` that assigns to each node
        the label of the strongly connected component to which it belongs.

        """
        # Find the strongly connected components
        self._num_scc, self._scc_proj = \
            csgraph.connected_components(self.csgraph, connection='strong')


Instruction:
Explain what the following Python 3 code does
def _condensation_lil(self):
        condensation_lil = sparse.lil_matrix(
            (self.num_strongly_connected_components,
             self.num_strongly_connected_components), dtype=bool
        )

        scc_proj = self.scc_proj
        for node_from, node_to in _csr_matrix_indices(self.csgraph):
            scc_from, scc_to = scc_proj[node_from], scc_proj[node_to]
            if scc_from != scc_to:
                condensation_lil[scc_from, scc_to] = True

        return condensation_lil

Response:
Return the sparse matrix representation of the condensation digraph
        in lil format.


Instruction:
How would you code a function in Python 3 to
find the sink SCC that is strongly connected to the other SCCs.

Response:
def _find_sink_scc(self):
        """
        Set self._sink_scc_labels, which is a list containing the labels of
        the strongly connected components.

        """
        condensation_lil = self._condensation_lil()

        # A sink SCC is a SCC such that none of its members is strongly
        # connected to nodes in other SCCs
        # Those k's such that graph_condensed_lil.rows[k] == []
        self._sink_scc_labels = \
            np.where(np.logical_not(condensation_lil.rows))[0]


Instruction:
Here you have a function in Python 3, explain what it does
def _compute_period(self):
        # Degenerate graph with a single node (which is strongly connected)
        # csgraph.reconstruct_path would raise an exception
        # github.com/scipy/scipy/issues/4018
        if self.n == 1:
            if self.csgraph[0, 0] == 0:  # No edge: "trivial graph"
                self._period = 1  # Any universally accepted definition?
                self._cyclic_components_proj = np.zeros(self.n, dtype=int)
                return None
            else:  # Self loop
                self._period = 1
                self._cyclic_components_proj = np.zeros(self.n, dtype=int)
                return None

        if not self.is_strongly_connected:
            raise NotImplementedError(
                'Not defined for a non strongly-connected digraph'
            )

        if np.any(self.csgraph.diagonal() > 0):
            self._period = 1
            self._cyclic_components_proj = np.zeros(self.n, dtype=int)
            return None

        # Construct a breadth-first search tree rooted at 0
        node_order, predecessors = \
            csgraph.breadth_first_order(self.csgraph, i_start=0)
        bfs_tree_csr = \
            csgraph.reconstruct_path(self.csgraph, predecessors)

        # Edges not belonging to tree_csr
        non_bfs_tree_csr = self.csgraph - bfs_tree_csr
        non_bfs_tree_csr.eliminate_zeros()

        # Distance to 0
        level = np.zeros(self.n, dtype=int)
        for i in range(1, self.n):
            level[node_order[i]] = level[predecessors[node_order[i]]] + 1

        # Determine the period
        d = 0
        for node_from, node_to in _csr_matrix_indices(non_bfs_tree_csr):
            value = level[node_from] - level[node_to] + 1
            d = gcd(d, value)
            if d == 1:
                self._period = 1
                self._cyclic_components_proj = np.zeros(self.n, dtype=int)
                return None

        self._period = d
        self._cyclic_components_proj = level % d

Response: Compute the period of the tree.


Instruction:
Can you generate a brief explanation for the following Python 3 code
def subgraph(self, nodes):
        adj_matrix = self.csgraph[np.ix_(nodes, nodes)]

        weighted = True  # To copy the dtype

        if self.node_labels is not None:
            node_labels = self.node_labels[nodes]
        else:
            node_labels = None

        return DiGraph(adj_matrix, weighted=weighted, node_labels=node_labels)

Response: Returns the subgraph consisting of the given nodes and edges between thses nodes.


Instruction:
Implement a Python 3 function for
estimating the rank of a matrix.

Response:
def rank_est(A, atol=1e-13, rtol=0):
    """
    Estimate the rank (i.e. the dimension of the nullspace) of a matrix.

    The algorithm used by this function is based on the singular value
    decomposition of `A`.

    Parameters
    ----------
    A : array_like(float, ndim=1 or 2)
        A should be at most 2-D.  A 1-D array with length n will be
        treated as a 2-D with shape (1, n)
    atol : scalar(float), optional(default=1e-13)
        The absolute tolerance for a zero singular value.  Singular
        values smaller than `atol` are considered to be zero.
    rtol : scalar(float), optional(default=0)
        The relative tolerance.  Singular values less than rtol*smax are
        considered to be zero, where smax is the largest singular value.

    Returns
    -------
    r : scalar(int)
        The estimated rank of the matrix.

    Note: If both `atol` and `rtol` are positive, the combined tolerance
    is the maximum of the two; that is:

        tol = max(atol, rtol * smax)

    Note: Singular values smaller than `tol` are considered to be zero.

    See also
    --------
    numpy.linalg.matrix_rank
        matrix_rank is basically the same as this function, but it does
        not provide the option of the absolute tolerance.

    """

    A = np.atleast_2d(A)
    s = svd(A, compute_uv=False)
    tol = max(atol, rtol * s[0])
    rank = int((s >= tol).sum())

    return rank


Instruction:
Implement a function in Python 3 to
generate s_indices and a_indices for the DiscreteDP case.

Response:
def sa_indices(num_states, num_actions):
    """
    Generate `s_indices` and `a_indices` for `DiscreteDP`, for the case
    where all the actions are feasible at every state.

    Parameters
    ----------
    num_states : scalar(int)
        Number of states.

    num_actions : scalar(int)
        Number of actions.

    Returns
    -------
    s_indices : ndarray(int, ndim=1)
        Array containing the state indices.

    a_indices : ndarray(int, ndim=1)
        Array containing the action indices.

    Examples
    --------
    >>> s_indices, a_indices = qe.markov.sa_indices(4, 3)
    >>> s_indices
    array([0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3])
    >>> a_indices
    array([0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2])

    """
    L = num_states * num_actions
    dtype = np.int_
    s_indices = np.empty(L, dtype=dtype)
    a_indices = np.empty(L, dtype=dtype)

    i = 0
    for s in range(num_states):
        for a in range(num_actions):
            s_indices[i] = s
            a_indices[i] = a
            i += 1

    return s_indices, a_indices


Instruction:
Can you generate a brief explanation for the following Python 3 code
def _generate_sample_paths(P_cdfs, init_states, random_values, out):
    num_reps, ts_length = out.shape

    for i in range(num_reps):
        out[i, 0] = init_states[i]
        for t in range(ts_length-1):
            out[i, t+1] = searchsorted(P_cdfs[out[i, t]], random_values[i, t])

Response: Generate sample paths for the next state transition.


Instruction:
Create a Python 3 function for
generating sample paths for sparse matrix.

Response:
def _generate_sample_paths_sparse(P_cdfs1d, indices, indptr, init_states,
                                  random_values, out):
    """
    For sparse matrix.

    Generate num_reps sample paths of length ts_length, where num_reps =
    out.shape[0] and ts_length = out.shape[1].

    Parameters
    ----------
    P_cdfs1d : ndarray(float, ndim=1)
        1D array containing the CDFs of the state transition.

    indices : ndarray(int, ndim=1)
        CSR format index array.

    indptr : ndarray(int, ndim=1)
        CSR format index pointer array.

    init_states : array_like(int, ndim=1)
        Array containing the initial states. Its length must be equal to
        num_reps.

    random_values : ndarray(float, ndim=2)
        Array containing random values from [0, 1). Its shape must be
        equal to (num_reps, ts_length-1)

    out : ndarray(int, ndim=2)
        Array to store the sample paths.

    Notes
    -----
    This routine is jit-complied by Numba.

    """
    num_reps, ts_length = out.shape

    for i in range(num_reps):
        out[i, 0] = init_states[i]
        for t in range(ts_length-1):
            k = searchsorted(P_cdfs1d[indptr[out[i, t]]:indptr[out[i, t]+1]],
                             random_values[i, t])
            out[i, t+1] = indices[indptr[out[i, t]]+k]


Instruction:
Here you have a function in Python 3, explain what it does
def mc_sample_path(P, init=0, sample_size=1000, random_state=None):
    random_state = check_random_state(random_state)

    if isinstance(init, numbers.Integral):
        X_0 = init
    else:
        cdf0 = np.cumsum(init)
        u_0 = random_state.random_sample()
        X_0 = searchsorted(cdf0, u_0)

    mc = MarkovChain(P)
    return mc.simulate(ts_length=sample_size, init=X_0,
                       random_state=random_state)

Response: Generates one sample path from the Markov chain represented by a Markov transition matrix P on state space S.


Instruction:
Given the following Python 3 function, write the documentation
def get_index(self, value):
        if self.state_values is None:
            state_values_ndim = 1
        else:
            state_values_ndim = self.state_values.ndim

        values = np.asarray(value)

        if values.ndim <= state_values_ndim - 1:
            return self._get_index(value)
        elif values.ndim == state_values_ndim:  # array of values
            k = values.shape[0]
            idx = np.empty(k, dtype=int)
            for i in range(k):
                idx[i] = self._get_index(values[i])
            return idx
        else:
            raise ValueError('invalid value')

Response: Returns the index of the given value or values in the state_values array.


Instruction:
Create a Python 3 function for
returning the index of the given value in state_values.

Response:
def _get_index(self, value):
        """
        Return the index of the given value in `state_values`.

        Parameters
        ----------
        value
            Value to get the index for.

        Returns
        -------
        idx : int
            Index of `value`.

        """
        error_msg = 'value {0} not found'.format(value)

        if self.state_values is None:
            if isinstance(value, numbers.Integral) and (0 <= value < self.n):
                return value
            else:
                raise ValueError(error_msg)

        # if self.state_values is not None:
        if self.state_values.ndim == 1:
            try:
                idx = np.where(self.state_values == value)[0][0]
                return idx
            except IndexError:
                raise ValueError(error_msg)
        else:
            idx = 0
            while idx < self.n:
                if np.array_equal(self.state_values[idx], value):
                    return idx
                idx += 1
            raise ValueError(error_msg)


Instruction:
Implement a Python 3 function for
computing the stationary distributions for the current class.

Response:
def _compute_stationary(self):
        """
        Store the stationary distributions in self._stationary_distributions.

        """
        if self.is_irreducible:
            if not self.is_sparse:  # Dense
                stationary_dists = gth_solve(self.P).reshape(1, self.n)
            else:  # Sparse
                stationary_dists = \
                    gth_solve(self.P.toarray(),
                              overwrite=True).reshape(1, self.n)
        else:
            rec_classes = self.recurrent_classes_indices
            stationary_dists = np.zeros((len(rec_classes), self.n))
            for i, rec_class in enumerate(rec_classes):
                P_rec_class = self.P[np.ix_(rec_class, rec_class)]
                if self.is_sparse:
                    P_rec_class = P_rec_class.toarray()
                stationary_dists[i, rec_class] = \
                    gth_solve(P_rec_class, overwrite=True)

        self._stationary_dists = stationary_dists


Instruction:
Can you create a Python 3 function that
simulates time series of state transitions where state indices are returned.

Response:
def simulate_indices(self, ts_length, init=None, num_reps=None,
                         random_state=None):
        """
        Simulate time series of state transitions, where state indices
        are returned.

        Parameters
        ----------
        ts_length : scalar(int)
            Length of each simulation.

        init : int or array_like(int, ndim=1), optional
            Initial state(s). If None, the initial state is randomly
            drawn.

        num_reps : scalar(int), optional(default=None)
            Number of repetitions of simulation.

        random_state : int or np.random.RandomState, optional
            Random seed (integer) or np.random.RandomState instance to
            set the initial state of the random number generator for
            reproducibility. If None, a randomly initialized RandomState
            is used.

        Returns
        -------
        X : ndarray(ndim=1 or 2)
            Array containing the state values of the sample path(s). See
            the `simulate` method for more information.

        """
        random_state = check_random_state(random_state)
        dim = 1  # Dimension of the returned array: 1 or 2

        msg_out_of_range = 'index {init} is out of the state space'

        try:
            k = len(init)  # init is an array
            dim = 2
            init_states = np.asarray(init, dtype=int)
            # Check init_states are in the state space
            if (init_states >= self.n).any() or (init_states < -self.n).any():
                idx = np.where(
                    (init_states >= self.n) + (init_states < -self.n)
                )[0][0]
                raise ValueError(msg_out_of_range.format(init=idx))
            if num_reps is not None:
                k *= num_reps
                init_states = np.tile(init_states, num_reps)
        except TypeError:  # init is a scalar(int) or None
            k = 1
            if num_reps is not None:
                dim = 2
                k = num_reps
            if init is None:
                init_states = random_state.randint(self.n, size=k)
            elif isinstance(init, numbers.Integral):
                # Check init is in the state space
                if init >= self.n or init < -self.n:
                    raise ValueError(msg_out_of_range.format(init=init))
                init_states = np.ones(k, dtype=int) * init
            else:
                raise ValueError(
                    'init must be int, array_like of ints, or None'
                )

        # === set up array to store output === #
        X = np.empty((k, ts_length), dtype=int)

        # Random values, uniformly sampled from [0, 1)
        random_values = random_state.random_sample(size=(k, ts_length-1))

        # Generate sample paths and store in X
        if not self.is_sparse:  # Dense
            _generate_sample_paths(
                self.cdfs, init_states, random_values, out=X
            )
        else:  # Sparse
            _generate_sample_paths_sparse(
                self.cdfs1d, self.P.indices, self.P.indptr, init_states,
                random_values, out=X
            )

        if dim == 1:
            return X[0]
        else:
            return X


Instruction:
Create a Python 3 function to
simulate the time series of state transitions.

Response:
def simulate(self, ts_length, init=None, num_reps=None, random_state=None):
        """
        Simulate time series of state transitions, where the states are
        annotated with their values (if `state_values` is not None).

        Parameters
        ----------
        ts_length : scalar(int)
            Length of each simulation.

        init : scalar or array_like, optional(default=None)
            Initial state values(s). If None, the initial state is
            randomly drawn.

        num_reps : scalar(int), optional(default=None)
            Number of repetitions of simulation.

        random_state : int or np.random.RandomState, optional
            Random seed (integer) or np.random.RandomState instance to
            set the initial state of the random number generator for
            reproducibility. If None, a randomly initialized RandomState
            is used.

        Returns
        -------
        X : ndarray(ndim=1 or 2)
            Array containing the sample path(s), of shape (ts_length,)
            if init is a scalar (integer) or None and num_reps is None;
            of shape (k, ts_length) otherwise, where k = len(init) if
            (init, num_reps) = (array, None), k = num_reps if (init,
            num_reps) = (int or None, int), and k = len(init)*num_reps
            if (init, num_reps) = (array, int).

        """
        if init is not None:
            init_idx = self.get_index(init)
        else:
            init_idx = None
        X = self.simulate_indices(ts_length, init=init_idx, num_reps=num_reps,
                                  random_state=random_state)

        # Annotate states
        if self.state_values is not None:
            X = self.state_values[X]

        return X


Instruction:
Write a Python 3 function for
computing the stationary values of the control set entry for the current entry.

Response:
def stationary_values(self, method='doubling'):
        """
        Computes the matrix :math:`P` and scalar :math:`d` that represent
        the value function

        .. math::

             V(x) = x' P x + d

        in the infinite horizon case.  Also computes the control matrix
        :math:`F` from :math:`u = - Fx`. Computation is via the solution
        algorithm as specified by the `method` option (default to the
        doubling algorithm) (see the documentation in
        `matrix_eqn.solve_discrete_riccati`).

        Parameters
        ----------
        method : str, optional(default='doubling')
            Solution method used in solving the associated Riccati
            equation, str in {'doubling', 'qz'}.

        Returns
        -------
        P : array_like(float)
            P is part of the value function representation of
            :math:`V(x) = x'Px + d`
        F : array_like(float)
            F is the policy rule that determines the choice of control
            in each period.
        d : array_like(float)
            d is part of the value function representation of
            :math:`V(x) = x'Px + d`

        """
        # === simplify notation === #
        Q, R, A, B, N, C = self.Q, self.R, self.A, self.B, self.N, self.C

        # === solve Riccati equation, obtain P === #
        A0, B0 = np.sqrt(self.beta) * A, np.sqrt(self.beta) * B
        P = solve_discrete_riccati(A0, B0, R, Q, N, method=method)

        # == Compute F == #
        S1 = Q + self.beta * dot(B.T, dot(P, B))
        S2 = self.beta * dot(B.T, dot(P, A)) + N
        F = solve(S1, S2)

        # == Compute d == #
        d = self.beta * np.trace(dot(P, dot(C, C.T))) / (1 - self.beta)

        # == Bind states and return values == #
        self.P, self.F, self.d = P, F, d

        return P, F, d


Instruction:
Can you tell what is the following Python 3 function doing
def pure_nash_brute_gen(g, tol=None):
    for a in np.ndindex(*g.nums_actions):
        if g.is_nash(a, tol=tol):
            yield a

Response: Generator version of pure_nash_brute.


Instruction:
Here you have a function in Python 3, explain what it does
def qnwequi(n, a, b, kind="N", equidist_pp=None, random_state=None):
    random_state = check_random_state(random_state)

    if equidist_pp is None:
        import sympy as sym
        equidist_pp = np.sqrt(np.array(list(sym.primerange(0, 7920))))

    n, a, b = list(map(np.atleast_1d, list(map(np.asarray, [n, a, b]))))

    d = max(list(map(len, [n, a, b])))
    n = np.prod(n)

    if a.size == 1:
        a = np.repeat(a, d)

    if b.size == 1:
        b = np.repeat(b, d)

    i = np.arange(1, n + 1)

    if kind.upper() == "N":  # Neiderreiter
        j = 2.0 ** (np.arange(1, d+1) / (d+1))
        nodes = np.outer(i, j)
        nodes = (nodes - fix(nodes)).squeeze()
    elif kind.upper() == "W":  # Weyl
        j = equidist_pp[:d]
        nodes = np.outer(i, j)
        nodes = (nodes - fix(nodes)).squeeze()
    elif kind.upper() == "H":  # Haber
        j = equidist_pp[:d]
        nodes = np.outer(i * (i+1) / 2, j)
        nodes = (nodes - fix(nodes)).squeeze()
    elif kind.upper() == "R":  # pseudo-random
        nodes = random_state.rand(n, d).squeeze()
    else:
        raise ValueError("Unknown sequence requested")

    # compute nodes and weights
    r = b - a
    nodes = a + nodes * r
    weights = (np.prod(r) / n) * np.ones(n)

    return nodes, weights

Response: Generates equidistributed sequences with property that averages over the integral as n goes to infinity.


Instruction:
How would you implement a function in Python 3 that
computes the multivariate normal distribution of a multivariate normal distribution.

Response:
def qnwnorm(n, mu=None, sig2=None, usesqrtm=False):
    """
    Computes nodes and weights for multivariate normal distribution

    Parameters
    ----------
    n : int or array_like(float)
        A length-d iterable of the number of nodes in each dimension

    mu : scalar or array_like(float), optional(default=zeros(d))
        The means of each dimension of the random variable. If a scalar
        is given, that constant is repeated d times, where d is the
        number of dimensions

    sig2 : array_like(float), optional(default=eye(d))
        A d x d array representing the variance-covariance matrix of the
        multivariate normal distribution.

    Returns
    -------
    nodes : np.ndarray(dtype=float)
        Quadrature nodes

    weights : np.ndarray(dtype=float)
        Weights for quadrature nodes

    Notes
    -----
    Based of original function ``qnwnorm`` in CompEcon toolbox by
    Miranda and Fackler

    References
    ----------
    Miranda, Mario J, and Paul L Fackler. Applied Computational
    Economics and Finance, MIT Press, 2002.

    """
    n = np.atleast_1d(n)
    d = n.size

    if mu is None:
        mu = np.zeros(d)
    else:
        mu = np.atleast_1d(mu)

    if sig2 is None:
        sig2 = np.eye(d)
    else:
        sig2 = np.atleast_1d(sig2).reshape(d, d)

    if all([x.size == 1 for x in [n, mu, sig2]]):
        nodes, weights = _qnwnorm1(n[0])
    else:
        nodes = []
        weights = []

        for i in range(d):
            _1d = _qnwnorm1(n[i])
            nodes.append(_1d[0])
            weights.append(_1d[1])

        nodes = gridmake(*nodes)
        weights = ckron(*weights[::-1])

    if usesqrtm:
        new_sig2 = la.sqrtm(sig2)
    else:  # cholesky
        new_sig2 = la.cholesky(sig2)

    if d > 1:
        nodes = nodes.dot(new_sig2) + mu  # Broadcast ok
    else:  # nodes.dot(sig) will not be aligned in scalar case.
        nodes = nodes * new_sig2 + mu

    return nodes.squeeze(), weights


Instruction:
Write a Python 3 function for
returning the nodes and weights for a multivariate lognormal distribution.

Response:
def qnwlogn(n, mu=None, sig2=None):
    """
    Computes nodes and weights for multivariate lognormal distribution

    Parameters
    ----------
    n : int or array_like(float)
        A length-d iterable of the number of nodes in each dimension

    mu : scalar or array_like(float), optional(default=zeros(d))
        The means of each dimension of the random variable. If a scalar
        is given, that constant is repeated d times, where d is the
        number of dimensions

    sig2 : array_like(float), optional(default=eye(d))
        A d x d array representing the variance-covariance matrix of the
        multivariate normal distribution.

    Returns
    -------
    nodes : np.ndarray(dtype=float)
        Quadrature nodes

    weights : np.ndarray(dtype=float)
        Weights for quadrature nodes

    Notes
    -----
    Based of original function ``qnwlogn`` in CompEcon toolbox by
    Miranda and Fackler

    References
    ----------
    Miranda, Mario J, and Paul L Fackler. Applied Computational
    Economics and Finance, MIT Press, 2002.

    """
    nodes, weights = qnwnorm(n, mu, sig2)
    return np.exp(nodes), weights


Instruction:
Write a Python 3 script for
computing the quadrature nodes and weights for a multivariate uniform distribution.

Response:
def qnwunif(n, a, b):
    """
    Computes quadrature nodes and weights for multivariate uniform
    distribution

    Parameters
    ----------
    n : int or array_like(float)
        A length-d iterable of the number of nodes in each dimension

    a : scalar or array_like(float)
        A length-d iterable of lower endpoints. If a scalar is given,
        that constant is repeated d times, where d is the number of
        dimensions

    b : scalar or array_like(float)
        A length-d iterable of upper endpoints. If a scalar is given,
        that constant is repeated d times, where d is the number of
        dimensions

    Returns
    -------
    nodes : np.ndarray(dtype=float)
        Quadrature nodes

    weights : np.ndarray(dtype=float)
        Weights for quadrature nodes

    Notes
    -----
    Based of original function ``qnwunif`` in CompEcon toolbox by
    Miranda and Fackler

    References
    ----------
    Miranda, Mario J, and Paul L Fackler. Applied Computational
    Economics and Finance, MIT Press, 2002.

    """
    n, a, b = list(map(np.asarray, [n, a, b]))
    nodes, weights = qnwlege(n, a, b)
    weights = weights / np.prod(b - a)
    return nodes, weights


Instruction:
Write a Python 3 script to
integrate a d - dimensional function f on a rectangle with lower and upper bound for dimension a and b.

Response:
def quadrect(f, n, a, b, kind='lege', *args, **kwargs):
    """
    Integrate the d-dimensional function f on a rectangle with lower and
    upper bound for dimension i defined by a[i] and b[i], respectively;
    using n[i] points.

    Parameters
    ----------
    f : function
        The function to integrate over. This should be a function
        that accepts as its first argument a matrix representing points
        along each dimension (each dimension is a column). Other
        arguments that need to be passed to the function are caught by
        `*args` and `**kwargs`

    n : int or array_like(float)
        A length-d iterable of the number of nodes in each dimension

    a : scalar or array_like(float)
        A length-d iterable of lower endpoints. If a scalar is given,
        that constant is repeated d times, where d is the number of
        dimensions

    b : scalar or array_like(float)
        A length-d iterable of upper endpoints. If a scalar is given,
        that constant is repeated d times, where d is the number of
        dimensions

    kind : string, optional(default='lege')
        Specifies which type of integration to perform. Valid
        values are:

        lege - Gauss-Legendre
        cheb - Gauss-Chebyshev
        trap - trapezoid rule
        simp - Simpson rule
        N    - Neiderreiter equidistributed sequence
        W    - Weyl equidistributed sequence
        H    - Haber  equidistributed sequence
        R    - Monte Carlo

    *args, **kwargs :
        Other arguments passed to the function f

    Returns
    -------
    out : scalar (float)
        The value of the integral on the region [a, b]

    Notes
    -----
    Based of original function ``quadrect`` in CompEcon toolbox by
    Miranda and Fackler

    References
    ----------
    Miranda, Mario J, and Paul L Fackler. Applied Computational
    Economics and Finance, MIT Press, 2002.

    """
    if kind.lower() == "lege":
        nodes, weights = qnwlege(n, a, b)
    elif kind.lower() == "cheb":
        nodes, weights = qnwcheb(n, a, b)
    elif kind.lower() == "trap":
        nodes, weights = qnwtrap(n, a, b)
    elif kind.lower() == "simp":
        nodes, weights = qnwsimp(n, a, b)
    else:
        nodes, weights = qnwequi(n, a, b, kind)

    out = weights.dot(f(nodes, *args, **kwargs))
    return out


Instruction:
Can you implement a function in Python 3 that
returns a function that computes the nodes and weights for a gamma distribution.

Response:
def qnwgamma(n, a=1.0, b=1.0, tol=3e-14):
    """
    Computes nodes and weights for gamma distribution

    Parameters
    ----------
    n : int or array_like(float)
        A length-d iterable of the number of nodes in each dimension

    a : scalar or array_like(float) : optional(default=ones(d))
        Shape parameter of the gamma distribution parameter. Must be positive

    b : scalar or array_like(float) : optional(default=ones(d))
        Scale parameter of the gamma distribution parameter. Must be positive

    tol : scalar or array_like(float) : optional(default=ones(d) * 3e-14)
        Tolerance parameter for newton iterations for each node

    Returns
    -------
    nodes : np.ndarray(dtype=float)
        Quadrature nodes

    weights : np.ndarray(dtype=float)
        Weights for quadrature nodes

    Notes
    -----
    Based of original function ``qnwgamma`` in CompEcon toolbox by
    Miranda and Fackler

    References
    ----------
    Miranda, Mario J, and Paul L Fackler. Applied Computational
    Economics and Finance, MIT Press, 2002.

    """
    return _make_multidim_func(_qnwgamma1, n, a, b, tol)


Instruction:
Given the following Python 3 function, write the documentation
def _qnwcheb1(n, a, b):
    nodes = (b+a)/2 - (b-a)/2 * np.cos(np.pi/n * np.linspace(0.5, n-0.5, n))

    # Create temporary arrays to be used in computing weights
    t1 = np.arange(1, n+1) - 0.5
    t2 = np.arange(0.0, n, 2)
    t3 = np.concatenate((np.array([1.0]),
                        -2.0/(np.arange(1.0, n-1, 2)*np.arange(3.0, n+1, 2))))

    # compute weights and return
    weights = ((b-a)/n)*np.cos(np.pi/n*np.outer(t1, t2)) @ t3

    return nodes, weights

Response: Compute univariate Guass - Checbychev quadrature nodes and weights


Instruction:
Write a Python 3 script for
computing univariate Guass - Legendre quadrature nodes and weights for a given interval.

Response:
def _qnwlege1(n, a, b):
    """
    Compute univariate Guass-Legendre quadrature nodes and weights

    Parameters
    ----------
    n : int
        The number of nodes

    a : int
        The lower endpoint

    b : int
        The upper endpoint

    Returns
    -------
    nodes : np.ndarray(dtype=float)
        An n element array of nodes

    nodes : np.ndarray(dtype=float)
        An n element array of weights

    Notes
    -----
    Based of original function ``qnwlege1`` in CompEcon toolbox by
    Miranda and Fackler

    References
    ----------
    Miranda, Mario J, and Paul L Fackler. Applied Computational
    Economics and Finance, MIT Press, 2002.

    """
    # import ipdb; ipdb.set_trace()
    maxit = 100
    m = int(fix((n + 1) / 2.0))
    xm = 0.5 * (b + a)
    xl = 0.5 * (b - a)
    nodes = np.zeros(n)

    weights = nodes.copy()
    i = np.arange(m)

    z = np.cos(np.pi * ((i + 1.0) - 0.25) / (n + 0.5))

    for its in range(maxit):
        p1 = np.ones_like(z)
        p2 = np.zeros_like(z)
        for j in range(1, n+1):
            p3 = p2
            p2 = p1
            p1 = ((2 * j - 1) * z * p2 - (j - 1) * p3) / j

        pp = n * (z * p1 - p2)/(z * z - 1.0)
        z1 = z.copy()
        z = z1 - p1/pp
        if np.all(np.abs(z - z1) < 1e-14):
            break

    if its == maxit - 1:
        raise ValueError("Maximum iterations in _qnwlege1")

    nodes[i] = xm - xl * z
    nodes[- i - 1] = xm + xl * z

    weights[i] = 2 * xl / ((1 - z * z) * pp * pp)
    weights[- i - 1] = weights[i]

    return nodes, weights


Instruction:
Explain what the following Python 3 code does
def _qnwnorm1(n):
    maxit = 100
    pim4 = 1 / np.pi**(0.25)
    m = int(fix((n + 1) / 2))
    nodes = np.zeros(n)
    weights = np.zeros(n)

    for i in range(m):
        if i == 0:
            z = np.sqrt(2*n+1) - 1.85575 * ((2 * n + 1)**(-1 / 6.1))
        elif i == 1:
            z = z - 1.14 * (n ** 0.426) / z
        elif i == 2:
            z = 1.86 * z + 0.86 * nodes[0]
        elif i == 3:
            z = 1.91 * z + 0.91 * nodes[1]
        else:
            z = 2 * z + nodes[i-2]

        its = 0

        while its < maxit:
            its += 1
            p1 = pim4
            p2 = 0
            for j in range(1, n+1):
                p3 = p2
                p2 = p1
                p1 = z * math.sqrt(2.0/j) * p2 - math.sqrt((j - 1.0) / j) * p3

            pp = math.sqrt(2 * n) * p2
            z1 = z
            z = z1 - p1/pp
            if abs(z - z1) < 1e-14:
                break

        if its == maxit:
            raise ValueError("Failed to converge in _qnwnorm1")

        nodes[n - 1 - i] = z
        nodes[i] = -z
        weights[i] = 2 / (pp*pp)
        weights[n - 1 - i] = weights[i]

    weights /= math.sqrt(math.pi)
    nodes = nodes * math.sqrt(2.0)

    return nodes, weights

Response: Compute nodes and weights for univariate standard distribution.


Instruction:
Can you create a Python 3 function that
computes univariate Simpson quadrature nodes and weights

Response:
def _qnwsimp1(n, a, b):
    """
    Compute univariate Simpson quadrature nodes and weights

    Parameters
    ----------
    n : int
        The number of nodes

    a : int
        The lower endpoint

    b : int
        The upper endpoint

    Returns
    -------
    nodes : np.ndarray(dtype=float)
        An n element array of nodes

    nodes : np.ndarray(dtype=float)
        An n element array of weights

    Notes
    -----
    Based of original function ``qnwsimp1`` in CompEcon toolbox by
    Miranda and Fackler

    References
    ----------
    Miranda, Mario J, and Paul L Fackler. Applied Computational
    Economics and Finance, MIT Press, 2002.

    """
    if n % 2 == 0:
        print("WARNING qnwsimp: n must be an odd integer. Increasing by 1")
        n += 1

    nodes = np.linspace(a, b, n)
    dx = nodes[1] - nodes[0]
    weights = np.kron(np.ones((n+1) // 2), np.array([2.0, 4.0]))
    weights = weights[:n]
    weights[0] = weights[-1] = 1
    weights = (dx / 3.0) * weights

    return nodes, weights


Instruction:
Can you generate a brief explanation for the following Python 3 code
def _qnwtrap1(n, a, b):
    if n < 1:
        raise ValueError("n must be at least one")

    nodes = np.linspace(a, b, n)
    dx = nodes[1] - nodes[0]

    weights = dx * np.ones(n)
    weights[0] *= 0.5
    weights[-1] *= 0.5

    return nodes, weights

Response: Compute univariate trapezoid rule quadrature nodes and weights


Instruction:
Can you write a function in Python 3 where it
computes nodes and weights for quadrature on the beta distribution. Default is a=b=1 which is just a uniform distribution NOTE: For now I am just following compecon; would be much better to find a different way since I don't know what they are doing. Parameters ---------- n : scalar : int The number of quadrature points a : scalar : float, optional(default=1) First Beta distribution parameter b : scalar : float, optional(default=1) Second Beta distribution parameter Returns ------- nodes : np.ndarray(dtype=float, ndim=1) The quadrature points weights : np.ndarray(dtype=float, ndim=1) The quadrature weights that correspond to nodes Notes ----- Based of original function ``_qnwbeta1`` in CompEcon toolbox by Miranda and Fackler References ---------- Miranda, Mario J, and Paul L Fackler. Applied Computational Economics and Finance, MIT Press, 2002.

Response:
def _qnwbeta1(n, a=1.0, b=1.0):
    """
    Computes nodes and weights for quadrature on the beta distribution.
    Default is a=b=1 which is just a uniform distribution

    NOTE: For now I am just following compecon; would be much better to
    find a different way since I don't know what they are doing.

    Parameters
    ----------
    n : scalar : int
        The number of quadrature points

    a : scalar : float, optional(default=1)
        First Beta distribution parameter

    b : scalar : float, optional(default=1)
        Second Beta distribution parameter

    Returns
    -------
    nodes : np.ndarray(dtype=float, ndim=1)
        The quadrature points

    weights : np.ndarray(dtype=float, ndim=1)
        The quadrature weights that correspond to nodes

    Notes
    -----
    Based of original function ``_qnwbeta1`` in CompEcon toolbox by
    Miranda and Fackler

    References
    ----------
    Miranda, Mario J, and Paul L Fackler. Applied Computational
    Economics and Finance, MIT Press, 2002.

    """
    # We subtract one and write a + 1 where we actually want a, and a
    # where we want a - 1
    a = a - 1
    b = b - 1

    maxiter = 25

    # Allocate empty space
    nodes = np.zeros(n)
    weights = np.zeros(n)

    # Find "reasonable" starting values.  Why these numbers?
    for i in range(n):
        if i == 0:
            an = a/n
            bn = b/n
            r1 = (1+a) * (2.78/(4+n*n) + .768*an/n)
            r2 = 1 + 1.48*an + .96*bn + .452*an*an + .83*an*bn
            z = 1 - r1/r2
        elif i == 1:
            r1 = (4.1+a) / ((1+a)*(1+0.156*a))
            r2 = 1 + 0.06 * (n-8) * (1+0.12*a)/n
            r3 = 1 + 0.012*b * (1+0.25*abs(a))/n
            z = z - (1-z) * r1 * r2 * r3
        elif i == 2:
            r1 = (1.67+0.28*a)/(1+0.37*a)
            r2 = 1+0.22*(n-8)/n
            r3 = 1+8*b/((6.28+b)*n*n)
            z = z-(nodes[0]-z)*r1*r2*r3
        elif i == n - 2:
            r1 = (1+0.235*b)/(0.766+0.119*b)
            r2 = 1/(1+0.639*(n-4)/(1+0.71*(n-4)))
            r3 = 1/(1+20*a/((7.5+a)*n*n))
            z = z+(z-nodes[-4])*r1*r2*r3
        elif i == n - 1:
            r1 = (1+0.37*b) / (1.67+0.28*b)
            r2 = 1 / (1+0.22*(n-8)/n)
            r3 = 1 / (1+8*a/((6.28+a)*n*n))
            z = z+(z-nodes[-3])*r1*r2*r3
        else:
            z = 3*nodes[i-1] - 3*nodes[i-2] + nodes[i-3]

        ab = a+b

        # Root finding
        its = 0
        z1 = -100
        while abs(z - z1) > 1e-10 and its < maxiter:
            temp = 2 + ab
            p1 = (a-b + temp*z)/2
            p2 = 1

            for j in range(2, n+1):
                p3 = p2
                p2 = p1
                temp = 2*j + ab
                aa = 2*j * (j+ab)*(temp-2)
                bb = (temp-1) * (a*a - b*b + temp*(temp-2) * z)
                c = 2 * (j - 1 + a) * (j - 1 + b) * temp
                p1 = (bb*p2 - c*p3)/aa

            pp = (n*(a-b-temp*z) * p1 + 2*(n+a)*(n+b)*p2)/(temp*(1 - z*z))
            z1 = z
            z = z1 - p1/pp

            if abs(z - z1) < 1e-12:
                break

            its += 1

        if its == maxiter:
            raise ValueError("Max Iteration reached.  Failed to converge")

        nodes[i] = z
        weights[i] = temp/(pp*p2)

    nodes = (1-nodes)/2
    weights = weights * math.exp(gammaln(a+n) + gammaln(b+n)
                                 - gammaln(n+1) - gammaln(n+ab+1))
    weights = weights / (2*math.exp(gammaln(a+1) + gammaln(b+1)
                         - gammaln(ab+2)))

    return nodes, weights


Instruction:
Can you create a Python 3 function that
functions to create a newton random variable from a gamma distribution.

Response:
def _qnwgamma1(n, a=1.0, b=1.0, tol=3e-14):
    """
    1d quadrature weights and nodes for Gamma distributed random variable

    Parameters
    ----------
    n : scalar : int
        The number of quadrature points

    a : scalar : float, optional(default=1.0)
        Shape parameter of the gamma distribution parameter. Must be positive

    b : scalar : float, optional(default=1.0)
        Scale parameter of the gamma distribution parameter. Must be positive

    tol : scalar : float, optional(default=3e-14)
        Tolerance parameter for newton iterations for each node

    Returns
    -------
    nodes : np.ndarray(dtype=float, ndim=1)
        The quadrature points

    weights : np.ndarray(dtype=float, ndim=1)
        The quadrature weights that correspond to nodes

    Notes
    -----
    Based of original function ``qnwgamma1`` in CompEcon toolbox by
    Miranda and Fackler

    References
    ----------
    Miranda, Mario J, and Paul L Fackler. Applied Computational
    Economics and Finance, MIT Press, 2002.

    """
    a -= 1

    maxit = 25

    factor = -math.exp(gammaln(a+n) - gammaln(n) - gammaln(a+1))
    nodes = np.zeros(n)
    weights = np.zeros(n)

    # Create nodes
    for i in range(n):
        # Reasonable starting values
        if i == 0:
            z = (1+a) * (3+0.92*a) / (1 + 2.4*n + 1.8*a)
        elif i == 1:
            z = z + (15 + 6.25*a) / (1 + 0.9*a + 2.5*n)
        else:
            j = i-1
            z = z + ((1 + 2.55*j) / (1.9*j) + 1.26*j*a / (1 + 3.5*j)) * \
                (z - nodes[j-1]) / (1 + 0.3*a)

        # root finding iterations
        its = 0
        z1 = -10000
        while abs(z - z1) > tol and its < maxit:
            p1 = 1.0
            p2 = 0.0
            for j in range(1, n+1):
                # Recurrance relation for Laguerre polynomials
                p3 = p2
                p2 = p1
                p1 = ((2*j - 1 + a - z)*p2 - (j - 1 + a)*p3) / j

            pp = (n*p1 - (n+a)*p2) / z
            z1 = z
            z = z1 - p1/pp
            its += 1

        if its == maxit:
            raise ValueError('Failure to converge')

        nodes[i] = z
        weights[i] = factor / (pp*n*p2)

    return nodes*b, weights


Instruction:
Can you create a Python 3 function that
generates a vertex_enumeration generator for a NormalFormGame instance.

Response:
def vertex_enumeration_gen(g, qhull_options=None):
    """
    Generator version of `vertex_enumeration`.

    Parameters
    ----------
    g : NormalFormGame
        NormalFormGame instance with 2 players.

    qhull_options : str, optional(default=None)
        Options to pass to `scipy.spatial.ConvexHull`. See the `Qhull
        manual <http://www.qhull.org>`_  for details.

    Yields
    -------
    tuple(ndarray(float, ndim=1))
        Tuple of Nash equilibrium mixed actions.

    """
    try:
        N = g.N
    except AttributeError:
        raise TypeError('input must be a 2-player NormalFormGame')
    if N != 2:
        raise NotImplementedError('Implemented only for 2-player games')

    brps = [_BestResponsePolytope(
        g.players[1-i], idx=i, qhull_options=qhull_options
    ) for i in range(N)]

    labelings_bits_tup = \
        tuple(_ints_arr_to_bits(brps[i].labelings) for i in range(N))
    equations_tup = tuple(brps[i].equations for i in range(N))
    trans_recips = tuple(brps[i].trans_recip for i in range(N))

    return _vertex_enumeration_gen(labelings_bits_tup, equations_tup,
                                   trans_recips)


Instruction:
Make a summary of the following Python 3 code
def _vertex_enumeration_gen(labelings_bits_tup, equations_tup, trans_recips):
    m, n = equations_tup[0].shape[1] - 1, equations_tup[1].shape[1] - 1
    num_vertices0, num_vertices1 = \
        equations_tup[0].shape[0], equations_tup[1].shape[0]
    ZERO_LABELING0_BITS = (np.uint64(1) << np.uint64(m)) - np.uint64(1)
    COMPLETE_LABELING_BITS = (np.uint64(1) << np.uint64(m+n)) - np.uint64(1)

    for i in range(num_vertices0):
        if labelings_bits_tup[0][i] == ZERO_LABELING0_BITS:
            continue
        for j in range(num_vertices1):
            xor = labelings_bits_tup[0][i] ^ labelings_bits_tup[1][j]
            if xor == COMPLETE_LABELING_BITS:
                yield _get_mixed_actions(
                    labelings_bits_tup[0][i],
                    (equations_tup[0][i], equations_tup[1][j]),
                    trans_recips
                )
                break

Response: Generator for the vertex_enumeration_gen.


Instruction:
Implement a Python 3 function for
converting an array of integers representing the set bits into the corresponding integer.

Response:
def _ints_arr_to_bits(ints_arr, out):
    """
    Convert an array of integers representing the set bits into the
    corresponding integer.

    Compiled as a ufunc by Numba's `@guvectorize`: if the input is a
    2-dim array with shape[0]=K, the function returns a 1-dim array of
    K converted integers.

    Parameters
    ----------
    ints_arr : ndarray(int32, ndim=1)
        Array of distinct integers from 0, ..., 63.

    Returns
    -------
    np.uint64
        Integer with set bits represented by the input integers.

    Examples
    --------
    >>> ints_arr = np.array([0, 1, 2], dtype=np.int32)
    >>> _ints_arr_to_bits(ints_arr)
    7
    >>> ints_arr2d = np.array([[0, 1, 2], [3, 0, 1]], dtype=np.int32)
    >>> _ints_arr_to_bits(ints_arr2d)
    array([ 7, 11], dtype=uint64)

    """
    m = ints_arr.shape[0]
    out[0] = 0
    for i in range(m):
        out[0] |= np.uint64(1) << np.uint64(ints_arr[i])


Instruction:
Here you have a function in Python 3, explain what it does
def _get_mixed_actions(labeling_bits, equation_tup, trans_recips):
    m, n = equation_tup[0].shape[0] - 1, equation_tup[1].shape[0] - 1
    out = np.empty(m+n)

    for pl, (start, stop, skip) in enumerate([(0, m, np.uint64(1)),
                                              (m, m+n, np.uint64(0))]):
        sum_ = 0.
        for i in range(start, stop):
            if (labeling_bits & np.uint64(1)) == skip:
                out[i] = 0
            else:
                out[i] = equation_tup[pl][i-start] * trans_recips[pl] - \
                    equation_tup[pl][-1]
                sum_ += out[i]
            labeling_bits = labeling_bits >> np.uint64(1)
        if sum_ != 0:
            out[start:stop] /= sum_

    return out[:m], out[m:]

Response: This function returns a tuple of mixed actions for the current language.


Instruction:
How would you implement a function in Python 3 that
returns a matrix where rows span the cartesian product of combinations of the input arrays.

Response:
def gridmake(*arrays):
    """
    Expands one or more vectors (or matrices) into a matrix where rows span the
    cartesian product of combinations of the input arrays. Each column of the
    input arrays will correspond to one column of the output matrix.

    Parameters
    ----------
    *arrays : tuple/list of np.ndarray
              Tuple/list of vectors to be expanded.

    Returns
    -------
    out : np.ndarray
          The cartesian product of combinations of the input arrays.

    Notes
    -----
    Based of original function ``gridmake`` in CompEcon toolbox by
    Miranda and Fackler

    References
    ----------
    Miranda, Mario J, and Paul L Fackler. Applied Computational Economics
    and Finance, MIT Press, 2002.

    """
    if all([i.ndim == 1 for i in arrays]):
        d = len(arrays)
        if d == 2:
            out = _gridmake2(*arrays)
        else:
            out = _gridmake2(arrays[0], arrays[1])
            for arr in arrays[2:]:
                out = _gridmake2(out, arr)

        return out
    else:
        raise NotImplementedError("Come back here")


Instruction:
Here you have a function in Python 3, explain what it does
def _gridmake2(x1, x2):
    if x1.ndim == 1 and x2.ndim == 1:
        return np.column_stack([np.tile(x1, x2.shape[0]),
                               np.repeat(x2, x1.shape[0])])
    elif x1.ndim > 1 and x2.ndim == 1:
        first = np.tile(x1, (x2.shape[0], 1))
        second = np.repeat(x2, x1.shape[0])
        return np.column_stack([first, second])
    else:
        raise NotImplementedError("Come back here")

Response:
This function is used to expand two vectors or matrices into a matrix where rows span the
    cartesian product of combinations of x1 and x2.


Instruction:
Write a Python 3 function that can
return m randomly sampled probability vectors of dimension k.

Response:
def probvec(m, k, random_state=None, parallel=True):
    """
    Return m randomly sampled probability vectors of dimension k.

    Parameters
    ----------
    m : scalar(int)
        Number of probability vectors.

    k : scalar(int)
        Dimension of each probability vectors.

    random_state : int or np.random.RandomState, optional
        Random seed (integer) or np.random.RandomState instance to set
        the initial state of the random number generator for
        reproducibility. If None, a randomly initialized RandomState is
        used.

    parallel : bool(default=True)
        Whether to use multi-core CPU (parallel=True) or single-threaded
        CPU (parallel=False). (Internally the code is executed through
        Numba.guvectorize.)

    Returns
    -------
    x : ndarray(float, ndim=2)
        Array of shape (m, k) containing probability vectors as rows.

    Examples
    --------
    >>> qe.random.probvec(2, 3, random_state=1234)
    array([[ 0.19151945,  0.43058932,  0.37789123],
           [ 0.43772774,  0.34763084,  0.21464142]])

    """
    if k == 1:
        return np.ones((m, k))

    # if k >= 2
    random_state = check_random_state(random_state)
    r = random_state.random_sample(size=(m, k-1))
    x = np.empty((m, k))

    # Parse Parallel Option #
    if parallel:
        _probvec_parallel(r, x)
    else:
        _probvec_cpu(r, x)

    return x


Instruction:
Here you have a function in Python 3, explain what it does
def _probvec(r, out):
    n = r.shape[0]
    r.sort()
    out[0] = r[0]
    for i in range(1, n):
        out[i] = r[i] - r[i-1]
    out[n] = 1 - r[n-1]

Response: Fill out with randomly sampled probability vectors as rows.


Instruction:
Explain what the following Python 3 code does
def sample_without_replacement(n, k, num_trials=None, random_state=None):
    if n <= 0:
        raise ValueError('n must be greater than 0')
    if k > n:
        raise ValueError('k must be smaller than or equal to n')

    size = k if num_trials is None else (num_trials, k)

    random_state = check_random_state(random_state)
    r = random_state.random_sample(size=size)
    result = _sample_without_replacement(n, r)

    return result

Response: Randomly choose k integers without replacement from 0... n - 1.


Instruction:
Can you create a Python 3 function that
samples n random elements from the Numba random variates.

Response:
def _sample_without_replacement(n, r, out):
    """
    Main body of `sample_without_replacement`. To be complied as a ufunc
    by guvectorize of Numba.

    """
    k = r.shape[0]

    # Logic taken from random.sample in the standard library
    pool = np.arange(n)
    for j in range(k):
        idx = int(np.floor(r[j] * (n-j)))  # np.floor returns a float
        out[j] = pool[idx]
        pool[idx] = pool[n-j-1]


Instruction:
Write a Python 3 script for
generating a random sample according to the cumulative distribution of a node.

Response:
def draw(cdf, size=None):
    """
    Generate a random sample according to the cumulative distribution
    given by `cdf`. Jit-complied by Numba in nopython mode.

    Parameters
    ----------
    cdf : array_like(float, ndim=1)
        Array containing the cumulative distribution.

    size : scalar(int), optional(default=None)
        Size of the sample. If an integer is supplied, an ndarray of
        `size` independent draws is returned; otherwise, a single draw
        is returned as a scalar.

    Returns
    -------
    scalar(int) or ndarray(int, ndim=1)

    Examples
    --------
    >>> cdf = np.cumsum([0.4, 0.6])
    >>> qe.random.draw(cdf)
    1
    >>> qe.random.draw(cdf, 10)
    array([1, 0, 1, 0, 1, 0, 0, 0, 1, 0])

    """
    if isinstance(size, types.Integer):
        def draw_impl(cdf, size):
            rs = np.random.random_sample(size)
            out = np.empty(size, dtype=np.int_)
            for i in range(size):
                out[i] = searchsorted(cdf, rs[i])
            return out
    else:
        def draw_impl(cdf, size):
            r = np.random.random_sample()
            return searchsorted(cdf, r)
    return draw_impl


Instruction:
Can you tell what is the following Python 3 function doing
def cartesian(nodes, order='C'):
    '''
    Cartesian product of a list of arrays

    Parameters
    ----------
    nodes : list(array_like(ndim=1))

    order : str, optional(default='C')
        ('C' or 'F') order in which the product is enumerated

    Returns
    -------
    out : ndarray(ndim=2)
        each line corresponds to one point of the product space
    '''

    nodes = [np.array(e) for e in nodes]
    shapes = [e.shape[0] for e in nodes]

    dtype = nodes[0].dtype

    n = len(nodes)
    l = np.prod(shapes)
    out = np.zeros((l, n), dtype=dtype)

    if order == 'C':
        repetitions = np.cumprod([1] + shapes[:-1])
    else:
        shapes.reverse()
        sh = [1] + shapes[:-1]
        repetitions = np.cumprod(sh)
        repetitions = repetitions.tolist()
        repetitions.reverse()

    for i in range(n):
        _repeat_1d(nodes[i], repetitions[i], out[:, i])

    return out

Response: Returns the cartesian product of a list of arrays


Instruction:
Here you have a function in Python 3, explain what it does
def mlinspace(a, b, nums, order='C'):
    '''
    Constructs a regular cartesian grid

    Parameters
    ----------
    a : array_like(ndim=1)
        lower bounds in each dimension

    b : array_like(ndim=1)
        upper bounds in each dimension

    nums : array_like(ndim=1)
        number of nodes along each dimension

    order : str, optional(default='C')
        ('C' or 'F') order in which the product is enumerated

    Returns
    -------
    out : ndarray(ndim=2)
        each line corresponds to one point of the product space
    '''

    a = np.array(a, dtype='float64')
    b = np.array(b, dtype='float64')
    nums = np.array(nums, dtype='int64')
    nodes = [np.linspace(a[i], b[i], nums[i]) for i in range(len(nums))]

    return cartesian(nodes, order=order)

Response: Returns a regular cartesian grid of the given numbers


Instruction:
Can you generate the documentation for the following Python 3 function
def _repeat_1d(x, K, out):
    '''
    Repeats each element of a vector many times and repeats the whole
    result many times

    Parameters
    ----------
    x : ndarray(ndim=1)
        vector to be repeated

    K : scalar(int)
        number of times each element of x is repeated (inner iterations)

    out : ndarray(ndim=1)
        placeholder for the result

    Returns
    -------
    None
    '''

    N = x.shape[0]
    L = out.shape[0] // (K*N)  # number of outer iterations
    # K                        # number of inner iterations

    # the result out should enumerate in C-order the elements
    # of a 3-dimensional array T of dimensions (K,N,L)
    # such that for all k,n,l, we have T[k,n,l] == x[n]

    for n in range(N):
        val = x[n]
        for k in range(K):
            for l in range(L):
                ind = k*N*L + n*L + l
                out[ind] = val

Response:
Repeats each element of a vector many times and repeats the whole
    result many times


Instruction:
Explain what the following Python 3 code does
def simplex_grid(m, n):
    L = num_compositions_jit(m, n)
    if L == 0:  # Overflow occured
    	raise ValueError(_msg_max_size_exceeded)
    out = np.empty((L, m), dtype=np.int_)

    x = np.zeros(m, dtype=np.int_)
    x[m-1] = n

    for j in range(m):
        out[0, j] = x[j]

    h = m

    for i in range(1, L):
        h -= 1

        val = x[h]
        x[h] = 0
        x[m-1] = val - 1
        x[h-1] += 1

        for j in range(m):
            out[i, j] = x[j]

        if val != 1:
            h = m

    return out

Response: r Constructs a simplex grid of the n - dimensional m - part compositions of the n - dimensional simplex.


Instruction:
Here you have a function in Python 3, explain what it does
def simplex_index(x, m, n):
    if m == 1:
        return 0

    decumsum = np.cumsum(x[-1:0:-1])[::-1]
    idx = num_compositions(m, n) - 1
    for i in range(m-1):
        if decumsum[i] == 0:
            break
        idx -= num_compositions(m-i, decumsum[i]-1)
    return idx

Response: r Returns the index of the point x in the lexicographic order of the simplex.


Instruction:
Write a Python 3 script for
returning the total number of m - part compositions of n.

Response:
def num_compositions(m, n):
    """
    The total number of m-part compositions of n, which is equal to
    (n+m-1) choose (m-1).

    Parameters
    ----------
    m : scalar(int)
        Number of parts of composition.

    n : scalar(int)
        Integer to decompose.

    Returns
    -------
    scalar(int)
        Total number of m-part compositions of n.

    """
    # docs.scipy.org/doc/scipy/reference/generated/scipy.special.comb.html
    return scipy.special.comb(n+m-1, m-1, exact=True)


Instruction:
Can you generate a brief explanation for the following Python 3 code
def set_params(self):
        # === set up ma_poly === #
        ma_poly = np.asarray(self._theta)
        self.ma_poly = np.insert(ma_poly, 0, 1)  # The array (1, theta)

        # === set up ar_poly === #
        if np.isscalar(self._phi):
            ar_poly = np.array(-self._phi)
        else:
            ar_poly = -np.asarray(self._phi)
        self.ar_poly = np.insert(ar_poly, 0, 1)  # The array (1, -phi)

        # === pad ar_poly with zeros if required === #
        if len(self.ar_poly) < len(self.ma_poly):
            temp = np.zeros(len(self.ma_poly) - len(self.ar_poly))
            self.ar_poly = np.hstack((self.ar_poly, temp))

Response: r Sets the parameters of the object to match the parameters of the object.


Instruction:
Given the following Python 3 function, write the documentation
def impulse_response(self, impulse_length=30):
        from scipy.signal import dimpulse
        sys = self.ma_poly, self.ar_poly, 1
        times, psi = dimpulse(sys, n=impulse_length)
        psi = psi[0].flatten()  # Simplify return value into flat array

        return psi

Response: Get the impulse response corresponding to our model.


Instruction:
Can you tell what is the following Python 3 function doing
def spectral_density(self, two_pi=True, res=1200):
        from scipy.signal import freqz
        w, h = freqz(self.ma_poly, self.ar_poly, worN=res, whole=two_pi)
        spect = h * conj(h) * self.sigma**2

        return w, spect

Response: r Compute the spectral density of the entry in the set of unique entries.


Instruction:
Make a summary of the following Python 3 code
def autocovariance(self, num_autocov=16):
        spect = self.spectral_density()[1]
        acov = np.fft.ifft(spect).real

        # num_autocov should be <= len(acov) / 2
        return acov[:num_autocov]

Response:
Calculates the autocovariance function from the ARMA parameters
        and returns it.


Instruction:
Here you have a function in Python 3, explain what it does
def simulation(self, ts_length=90, random_state=None):
        from scipy.signal import dlsim
        random_state = check_random_state(random_state)

        sys = self.ma_poly, self.ar_poly, 1
        u = random_state.randn(ts_length, 1) * self.sigma
        vals = dlsim(sys, u)[1]

        return vals.flatten()

Response: Compute a simulated sample path assuming Gaussian shocks.


Instruction:
Here you have a function in Python 3, explain what it does
def compute_steadystate(self, nnc=2):
        zx = np.eye(self.A0.shape[0])-self.A0
        self.zz = nullspace(zx)
        self.zz /= self.zz[nnc]
        self.css = self.Sc.dot(self.zz)
        self.sss = self.Ss.dot(self.zz)
        self.iss = self.Si.dot(self.zz)
        self.dss = self.Sd.dot(self.zz)
        self.bss = self.Sb.dot(self.zz)
        self.kss = self.Sk.dot(self.zz)
        self.hss = self.Sh.dot(self.zz)

Response: Computes the non - stochastic steady - state of the economy.