def _simulate_log_probability_of_individuals_observed_choice( wages, nonpec, continuation_values, draws, delta, choice, tau, smoothed_log_probability, ): r"""Simulate the probability of observing the agent's choice. The probability is simulated by iterating over a distribution of unobservables. First, the utility of each choice is computed. Then, the probability of observing the choice of the agent given the maximum utility from all choices is computed. The naive implementation calculates the log probability for choice `i` with the softmax function. .. math:: \log(\text{softmax}(x)_i) = \log\left( \frac{e^{x_i}}{\sum^J e^{x_j}} \right) The following function is numerically more robust. The derivation with the two consecutive `logsumexp` functions is included in `#278 <https://github.com/OpenSourceEconomics/respy/pull/288>`_. Parameters ---------- wages : numpy.ndarray Array with shape (n_choices,). nonpec : numpy.ndarray Array with shape (n_choices,). continuation_values : numpy.ndarray Array with shape (n_choices,) draws : numpy.ndarray Array with shape (n_draws, n_choices) delta : float Discount rate. choice : int Choice of the agent. tau : float Smoothing parameter for choice probabilities. Returns ------- smoothed_log_probability : float Simulated Smoothed log probability of choice. """ n_draws, n_choices = draws.shape smoothed_log_probabilities = np.empty(n_draws) smoothed_value_functions = np.empty(n_choices) for i in range(n_draws): for j in range(n_choices): value_function, _ = aggregate_keane_wolpin_utility( wages[j], nonpec[j], continuation_values[j], draws[i, j], delta, ) smoothed_value_functions[j] = value_function / tau smoothed_log_probabilities[i] = smoothed_value_functions[choice] - _logsumexp( smoothed_value_functions ) smoothed_log_prob = _logsumexp(smoothed_log_probabilities) - np.log(n_draws) smoothed_log_probability[0] = smoothed_log_prob
def calculate_emax_value_functions( wages, nonpec, continuation_values, draws, delta, is_inadmissible, emax_value_functions, ): r"""Calculate the expected maximum of value functions for a set of unobservables. The function takes an agent and calculates the utility for each of the choices, the ex-post rewards, with multiple draws from the distribution of unobservables and adds the discounted expected maximum utility of subsequent periods resulting from choices. Averaging over all maximum utilities yields the expected maximum utility of this state. The underlying process in this function is called `Monte Carlo integration`_. The goal is to approximate an integral by evaluating the integrand at randomly chosen points. In this setting, one wants to approximate the expected maximum utility of the current state. Note that ``wages`` have the same length as ``nonpec`` despite that wages are only available in some choices. Missing choices are filled with ones. In the case of a choice with wage and without wage, flow utilities are .. math:: \text{Flow Utility} = \text{Wage} * \epsilon + \text{Non-pecuniary} \text{Flow Utility} = 1 * \epsilon + \text{Non-pecuniary} Parameters ---------- wages : numpy.ndarray Array with shape (n_choices,) containing wages. nonpec : numpy.ndarray Array with shape (n_choices,) containing non-pecuniary rewards. continuation_values : numpy.ndarray Array with shape (n_choices,) containing expected maximum utility for each choice in the subsequent period. draws : numpy.ndarray Array with shape (n_draws, n_choices). delta : float The discount factor. is_inadmissible: numpy.ndarray Array with shape (n_choices,) containing indicator for whether the following state is inadmissible. Returns ------- emax_value_functions : float Expected maximum utility of an agent. .. _Monte Carlo integration: https://en.wikipedia.org/wiki/Monte_Carlo_integration """ n_draws, n_choices = draws.shape emax_value_functions[0] = 0.0 for i in range(n_draws): max_value_functions = 0.0 for j in range(n_choices): value_function, _ = aggregate_keane_wolpin_utility( wages[j], nonpec[j], continuation_values[j], draws[i, j], delta, is_inadmissible[j], ) if value_function > max_value_functions: max_value_functions = value_function emax_value_functions[0] += max_value_functions emax_value_functions[0] /= n_draws