def test_invariance_of_solution(model_or_seed): """Test for the invariance of the solution. We run solve two times and check whether all attributes of the state space match. """ params, options = process_model_or_seed(model_or_seed) optim_paras, options = process_params_and_options(params, options) solve = get_solve_func(params, options) state_space = solve(params) state_space_ = solve(params) for attribute in [ "core", "wages", "nonpecs", "expected_value_functions", "base_draws_sol", ]: apply_to_attributes_of_two_state_spaces( getattr(state_space, attribute), getattr(state_space_, attribute), np.testing.assert_array_equal, )
def test_create_state_space_vs_specialized_kw97(model_or_seed): params, options = process_model_or_seed(model_or_seed) # Reduce runtime options["n_periods"] = 10 if options["n_periods"] > 10 else options[ "n_periods"] optim_paras, options = process_params_and_options(params, options) # Create old state space arguments. n_periods = options["n_periods"] n_types = optim_paras["n_types"] edu_max = optim_paras["choices"]["school"]["max"] edu_starts = np.array(list(optim_paras["choices"]["school"]["start"])) # Get states and indexer from old state space. if model_or_seed == "kw_97_basic": states_old, indexer_old = _create_state_space_kw97_base( n_periods, n_types, edu_starts, edu_max) else: states_old, indexer_old = _create_state_space_kw97_extended( n_periods, n_types, edu_starts, edu_max) states_new, indexer_new = _create_state_space(optim_paras, options) # Compare the state spaces via sets as ordering changed in some cases. states_old_set = set(map(tuple, states_old)) states_new_set = set(map(tuple, states_new.to_numpy())) assert states_old_set == states_new_set # Compare indexers via masks for valid indices. for period in range(n_periods): mask_old = indexer_old[period] != -1 mask_new = indexer_new[period] != -1 assert np.array_equal(mask_old, mask_new)
def test_state_space_restrictions_by_traversing_forward(model): """Test for inadmissible states in the state space. The test is motivated by the addition of another restriction in https://github.com/OpenSourceEconomics/respy/pull/145. To ensure that similar errors do not happen again, this test takes all states of the first period and finds all their child states. Taking only the child states their children are found and so on. At last, the set of visited states is compared against the total set of states. The test can only applied to some models. Most models would need custom ``options["core_state_space_filters"]`` to remove inaccessible states from the state space. """ params, options = process_model_or_seed(model) optim_paras, options = process_params_and_options(params, options) solve = get_solve_func(params, options) state_space = solve(params) out = {} for x in state_space.child_indices.values(): array = np.concatenate(x) for state in array: if state[0] in out.keys(): if state[1] not in out[state[0]]: out[state[0]].append(state[1]) else: continue else: out[state[0]] = [state[1]] for x in out: assert len(out[x]) == len(state_space.core_key_to_core_indices[x])
def test_create_state_space_vs_specialized_kw94(model): point_constr = {"n_lagged_choices": 1, "observables": False} params, options = process_model_or_seed(model, point_constr=point_constr) optim_paras, options = process_params_and_options(params, options) # Create old state space arguments. n_periods = options["n_periods"] n_types = optim_paras["n_types"] edu_max = optim_paras["choices"]["edu"]["max"] edu_starts = np.array(list(optim_paras["choices"]["edu"]["start"])) # Get states and indexer from old state space. states_old, indexer_old = _create_state_space_kw94(n_periods, n_types, edu_starts, edu_max) if n_types == 1: states_old = states_old[:, :-1] for i, idx in enumerate(indexer_old): shape = idx.shape indexer_old[i] = idx.reshape(shape[:-2] + (-1, )) states_new, indexer_new = _create_core_and_indexer(optim_paras, options) # Compare the state spaces via sets as ordering changed in some cases. states_old_set = set(map(tuple, states_old)) states_new_set = set(map(tuple, states_new.to_numpy())) assert states_old_set == states_new_set # Compare indexers via masks for valid indices. for period in range(n_periods): mask_old = indexer_old[period] != INDEXER_INVALID_INDEX mask_new = indexer_new[period] != INDEXER_INVALID_INDEX assert np.array_equal(mask_old, mask_new)
def test_create_state_space_vs_specialized_kw94(model_or_seed): point_constr = {"n_lagged_choices": 1, "observables": False} params, options = process_model_or_seed(model_or_seed, point_constr=point_constr) optim_paras, options = process_params_and_options(params, options) # Create old state space arguments. n_periods = options["n_periods"] n_types = optim_paras["n_types"] edu_max = optim_paras["choices"]["edu"]["max"] edu_starts = np.array(list(optim_paras["choices"]["edu"]["start"])) # Get states and indexer from old state space. states_old, indexer_old = _create_state_space_kw94(n_periods, n_types, edu_starts, edu_max) states_new, indexer_new = _create_state_space(optim_paras, options) # Compare the state spaces via sets as ordering changed in some cases. states_old_set = set(map(tuple, states_old)) states_new_set = set(map(tuple, states_new.to_numpy())) assert states_old_set == states_new_set # Compare indexers via masks for valid indices. for period in range(n_periods): mask_old = indexer_old[period] != -1 mask_new = indexer_new[period] != -1 assert np.array_equal(mask_old, mask_new)
def test_choice_restrictions(): """Basic first test.""" # Load model. params, options = process_model_or_seed("robinson_crusoe_extended") # Extend with observable characteristic. params.loc[("observable_health_well", "probability"), "value"] = 0.9 params.loc[("observable_health_sick", "probability"), "value"] = 0.1 # Sick people can never work. options["negative_choice_set"] = { "fishing": ["health == 'sick' & period < 2", "health == 'sick' & period >= 2"], "friday": ["period < 2", "exp_fishing == 0"], } # Create internal specification objects. optim_paras, options = process_params_and_options(params, options) state_space = create_state_space_class(optim_paras, options) for x in state_space.dense_key_to_complex.values(): if (x[0] < 2) & (x[2] == (0, )): assert x[1] == (False, False, True) elif x[2] == (0, ): assert x[1] in [(False, False, True), (False, True, True)] elif (x[0] < 2) & (x[2] == (1, )): assert x[1] == (True, False, True) elif x[2] == (1, ): assert x[1] in [(True, False, True), (True, True, True)]
def test_create_state_space_vs_specialized_kw97(model): """State space reproduces invariant features of the kw97 state space.""" params, options = process_model_or_seed(model) optim_paras, options = process_params_and_options(params, options) # Create old state space arguments. n_periods = options["n_periods"] n_types = optim_paras["n_types"] edu_max = optim_paras["choices"]["school"]["max"] edu_starts = np.array(list(optim_paras["choices"]["school"]["start"])) # Get states and indexer from old state space. if "kw_97_basic" in model: states_old, indexer_old = _create_state_space_kw97_base( n_periods, n_types, edu_starts, edu_max) else: states_old, indexer_old = _create_state_space_kw97_extended( n_periods, n_types, edu_starts, edu_max) states_old = states_old[:, :-1] states_new = _create_core_state_space(optim_paras, options) core_period_choice = _create_core_period_choice(states_new, optim_paras, options) # I think here we can get more elegant! Or is this the only way? core_index_to_complex = {i: k for i, k in enumerate(core_period_choice)} core_index_to_indices = { i: core_period_choice[core_index_to_complex[i]] for i in core_index_to_complex } # Create sp indexer indexer = _create_indexer(states_new, core_index_to_indices, optim_paras) # Compare the state spaces via sets as ordering changed in some cases. states_old_set = set(map(tuple, states_old)) states_new_set = set(map(tuple, states_new.to_numpy())) assert states_old_set == states_new_set # Compare indexers via masks for valid indices. for period in range(n_periods): index_old_period = indexer_old[period] != INDEXER_INVALID_INDEX index_old_period = np.nonzero(index_old_period) indices_old = [ [period] + [index_old_period[x][i] for x in range(len(index_old_period) - 1)] for i in range(len(index_old_period[0])) ] for index in indexer.keys(): if index[0] == period: assert list(index) in indices_old for index in indices_old: assert tuple(index) in indexer.keys()
def test_check_solution(model_or_seed): params, options = process_model_or_seed(model_or_seed) state_space = rp.solve(params, options) optim_paras, options = process_params_and_options(params, options) check_model_solution(optim_paras, options, state_space)
def test_run_through_of_solve_with_interpolation(seed): params, options = process_model_or_seed(seed, point_constr={ "n_periods": 5, "interpolation_points": 10 }) solve = get_solve_func(params, options) solve(params)
def test_check_solution(model_or_seed): """Test solution of a random model.""" params, options = process_model_or_seed(model_or_seed) solve = get_solve_func(params, options) state_space = solve(params) optim_paras, options = process_params_and_options(params, options) check_model_solution(optim_paras, options, state_space)
def test_explicitly_nonpec_choice_rewards_of_kw_94_two(): """Test values of non-pecuniary rewards for Keane & Wolpin 1994.""" params, options = process_model_or_seed("kw_94_two") solve = get_solve_func(params, options) state_space = solve(params) for arr in state_space.nonpecs.values(): assert (arr[:, :2] == 0).all() assert (arr[:, -1] == 14_500).all() if arr.shape[1] == 4: np.isin(arr[:, 2], [5_000, 0, -10_000, -15_000]).all()
def test_return_output_dict_for_likelihood(model): params, options = process_model_or_seed(model) options["n_periods"] = 3 simulate = get_simulate_func(params, options) df = simulate(params) log_like = get_log_like_func(params, options, df, return_scalar=False) log_like = log_like(params) assert isinstance(log_like["value"], float) assert isinstance(log_like["contributions"], np.ndarray) assert isinstance(log_like["comparison_plot_data"], pd.DataFrame)
def test_state_space_restrictions_by_traversing_forward(model): """Test for inadmissible states in the state space. The test is motivated by the addition of another restriction in https://github.com/OpenSourceEconomics/respy/pull/145. To ensure that similar errors do not happen again, this test takes all states of the first period and finds all their child states. Taking only the child states their children are found and so on. At last, the set of visited states is compared against the total set of states. The test can only applied to some models. Most models would need custom ``options["core_state_space_filters"]`` to remove inaccessible states from the state space. """ params, options = process_model_or_seed(model) optim_paras, options = process_params_and_options(params, options) solve = get_solve_func(params, options) state_space = solve(params) indices = np.full((state_space.core.shape[0], len(optim_paras["choices"])), INDEXER_INVALID_INDEX) core_columns = create_core_state_space_columns(optim_paras) for period in range(options["n_periods"] - 1): if period == 0: states = state_space.core.query( "period == 0")[core_columns].to_numpy(np.int) else: indices_period = state_space.indices_of_child_states[ state_space.slices_by_periods[period - 1]] indices_period = indices_period[indices_period >= 0] states = state_space.core[core_columns].to_numpy( np.int)[indices_period] indices = _insert_indices_of_child_states( indices, states, state_space.indexer[period], state_space.indexer[period + 1], state_space.is_inadmissible, len(optim_paras["choices_w_exp"]), optim_paras["n_lagged_choices"], ) # Take all valid indices and add the indices of the first period. set_valid_indices = set(indices[indices != INDEXER_INVALID_INDEX]) | set( range(state_space.core.query("period == 0").shape[0])) assert set_valid_indices == set(range(state_space.core.shape[0]))
def test_simulated_data(model_or_seed): """Test simulated data with ``check_simulated_data``. Note that, ``check_estimation_data`` is also tested in this function as these tests focus on a subset of the data. """ params, options = process_model_or_seed(model_or_seed) simulate = rp.get_simulate_func(params, options) df = simulate(params) optim_paras, _ = process_params_and_options(params, options) check_simulated_data(optim_paras, df)
def test_return_scalar_for_likelihood(model): params, options = process_model_or_seed(model) simulate = get_simulate_func(params, options) df = simulate(params) loglike = get_crit_func(params, options, df, return_scalar=True) value = loglike(params) assert isinstance(value, float) loglike = get_crit_func(params, options, df, return_scalar=False) array = loglike(params) assert isinstance(array, np.ndarray)
def test_invariance_of_model_solution_in_solve_and_simulation(model_or_seed): params, options = process_model_or_seed(model_or_seed) options["n_periods"] = 3 state_space = rp.solve(params, options) simulate = rp.get_simulate_func(params, options) _ = simulate(params) state_space_ = simulate.keywords["state_space"] np.testing.assert_array_equal(state_space.states, state_space_.states) np.testing.assert_array_equal(state_space.wages, state_space_.wages) np.testing.assert_array_equal(state_space.nonpec, state_space_.nonpec) np.testing.assert_array_equal(state_space.emax_value_functions, state_space_.emax_value_functions) np.testing.assert_array_equal(state_space.base_draws_sol, state_space_.base_draws_sol)
def test_simulation_with_flexible_choice_sets(): params, options = process_model_or_seed("robinson_crusoe_basic") # Extend with observable characteristic. params.loc[("observable_health_well", "probability"), "value"] = 0.9 params.loc[("observable_health_sick", "probability"), "value"] = 0.1 # Sick people can never work. options["negative_choice_set"] = { "fishing": ["health == 'sick'"], "friday": ["period < 2", "exp_fishing == 0"], } # Create internal specification objects. optim_paras, options = process_params_and_options(params, options) simulate = get_simulate_func(params, options) df = simulate(params) assert isinstance(df, pd.DataFrame)
def test_randomness_msm(model_or_seed): params, options = process_model_or_seed(model_or_seed) simulate = get_simulate_func(params, options) df = simulate(params) empirical_moments = _replace_nans(_calc_choice_freq(df)) weighting_matrix = get_diag_weighting_matrix(empirical_moments) weighted_sum_squared_errors = get_moment_errors_func( params, options, _calc_choice_freq, _replace_nans, empirical_moments, weighting_matrix, ) assert weighted_sum_squared_errors(params) == 0
def test_return_scalar_for_likelihood(model): params, options = process_model_or_seed(model) options["n_periods"] = 3 simulate = get_simulate_func(params, options) df = simulate(params) log_like = get_log_like_func(params, options, df, return_scalar=True) value = log_like(params) assert isinstance(value, float) log_like_contribs = get_log_like_func(params, options, df, return_scalar=False) outputs = log_like_contribs(params) assert isinstance(outputs, dict)
def test_create_state_space_vs_specialized_kw97(model): params, options = process_model_or_seed(model) # Reduce runtime options["n_periods"] = 10 if options["n_periods"] > 10 else options[ "n_periods"] optim_paras, options = process_params_and_options(params, options) # Create old state space arguments. n_periods = options["n_periods"] n_types = optim_paras["n_types"] edu_max = optim_paras["choices"]["school"]["max"] edu_starts = np.array(list(optim_paras["choices"]["school"]["start"])) # Get states and indexer from old state space. if model == "kw_97_basic": states_old, indexer_old = _create_state_space_kw97_base( n_periods, n_types, edu_starts, edu_max) else: states_old, indexer_old = _create_state_space_kw97_extended( n_periods, n_types, edu_starts, edu_max) if n_types == 1: states_old = states_old[:, :-1] for i, idx in enumerate(indexer_old): shape = idx.shape indexer_old[i] = idx.reshape(shape[:-2] + (-1, )) states_new, indexer_new = _create_core_and_indexer(optim_paras, options) states_new = pd.concat( [states_new.copy().assign(type=i) for i in range(4)]) # Compare the state spaces via sets as ordering changed in some cases. states_old_set = set(map(tuple, states_old)) states_new_set = set(map(tuple, states_new.to_numpy())) assert states_old_set == states_new_set # Compare indexers via masks for valid indices. for period in range(n_periods): mask_old = indexer_old[period] != INDEXER_INVALID_INDEX mask_new = indexer_new[period] != INDEXER_INVALID_INDEX adj_mask_new = np.repeat(mask_new, 4).reshape(mask_old.shape) assert np.array_equal(mask_old, adj_mask_new)
def test_state_space_restrictions_by_traversing_forward(model_or_seed): """Test for inadmissible states in the state space. The test is motivated by the addition of another restriction in https://github.com/OpenSourceEconomics/respy/pull/145. To ensure that similar errors do not happen again, this test takes all states of the first period and finds all their child states. Taking only the child states their children are found and so on. At last, the set of visited states is compared against the total set of states. """ params, options = process_model_or_seed(model_or_seed) optim_paras, options = process_params_and_options(params, options) state_space = rp.solve(params, options) indices = np.full( (state_space.states.shape[0], len(optim_paras["choices"])), -1) for period in range(options["n_periods"] - 1): if period == 0: states = state_space.get_attribute_from_period("states", period) else: indices_period = state_space.get_attribute_from_period( "indices_of_child_states", period - 1) indices_period = indices_period[indices_period >= 0] states = state_space.states[indices_period] indices = _insert_indices_of_child_states( indices, states, state_space.indexer[period], state_space.indexer[period + 1], state_space.is_inadmissible, len(optim_paras["choices_w_exp"]), optim_paras["n_lagged_choices"], ) # Take all valid indices and add the indices of the first period. set_valid_indices = set(indices[indices >= 0]) | set( range(state_space.get_attribute_from_period("states", 0).shape[0])) assert set_valid_indices == set(range(state_space.states.shape[0]))
def test_invariance_of_solution(model_or_seed): """Test for the invariance of the solution. We run solve two times and check whether all attributes of the state space match. """ params, options = process_model_or_seed(model_or_seed) optim_paras, options = process_params_and_options(params, options) state_space = rp.solve(params, options) state_space_ = rp.solve(params, options) np.testing.assert_array_equal(state_space.states, state_space_.states) np.testing.assert_array_equal(state_space.wages, state_space_.wages) np.testing.assert_array_equal(state_space.nonpec, state_space_.nonpec) np.testing.assert_array_equal(state_space.emax_value_functions, state_space_.emax_value_functions) np.testing.assert_array_equal(state_space.base_draws_sol, state_space_.base_draws_sol)
def test_invariance_of_model_solution_in_solve_and_criterion_functions(model): params, options = process_model_or_seed(model) options["n_periods"] = 2 if model == "kw_2000" else 3 solve = get_solve_func(params, options) state_space = solve(params) simulate = get_simulate_func(params, options) df = simulate(params) state_space_sim = simulate.keywords["solve"].keywords["state_space"] criterion = get_crit_func(params, options, df) _ = criterion(params) state_space_crit = criterion.keywords["solve"].keywords["state_space"] for state_space_ in [state_space_sim, state_space_crit]: assert state_space.core.equals( state_space_.core.reindex_like(state_space.core)) apply_to_attributes_of_two_state_spaces( state_space.get_attribute("wages"), state_space_.get_attribute("wages"), np.testing.assert_array_equal, ) apply_to_attributes_of_two_state_spaces( state_space.get_attribute("nonpecs"), state_space_.get_attribute("nonpecs"), np.testing.assert_array_equal, ) apply_to_attributes_of_two_state_spaces( state_space.get_attribute("expected_value_functions"), state_space_.get_attribute("expected_value_functions"), np.testing.assert_array_equal, ) apply_to_attributes_of_two_state_spaces( state_space.get_attribute("base_draws_sol"), state_space_.get_attribute("base_draws_sol"), np.testing.assert_array_equal, )
def test_return_comparison_plot_data_for_likelihood(model): params, options = process_model_or_seed(model) simulate = get_simulate_func(params, options) df = simulate(params) loglike = get_crit_func(params, options, df, return_comparison_plot_data=False) loglike = loglike(params) assert isinstance(loglike, float) loglike = get_crit_func(params, options, df, return_comparison_plot_data=True) loglike, df = loglike(params) assert isinstance(loglike, float) assert isinstance(df, pd.DataFrame)
def test_invariance_of_model_solution_in_solve_and_criterion_functions(model): params, options = process_model_or_seed(model) solve = get_solve_func(params, options) state_space = solve(params) simulate = get_simulate_func(params, options) df = simulate(params) state_space_sim = simulate.keywords["solve"].keywords["state_space"] log_like = get_log_like_func(params, options, df) _ = log_like(params) state_space_crit = log_like.keywords["solve"].keywords["state_space"] for state_space_ in [state_space_sim, state_space_crit]: assert state_space.core.equals( state_space_.core.reindex_like(state_space.core)) apply_to_attributes_of_two_state_spaces( state_space.wages, state_space_.wages, np.testing.assert_array_equal, ) apply_to_attributes_of_two_state_spaces( state_space.nonpecs, state_space_.nonpecs, np.testing.assert_array_equal, ) apply_to_attributes_of_two_state_spaces( state_space.expected_value_functions, state_space_.expected_value_functions, np.testing.assert_array_equal, ) apply_to_attributes_of_two_state_spaces( state_space.base_draws_sol, state_space_.base_draws_sol, np.testing.assert_array_equal, )
def test_sorting_of_type_probability_parameters(model_or_seed): # Set configuration for random models. n_types = np.random.randint(2, 5) n_type_covariates = np.random.randint(2, 4) params, options = process_model_or_seed( model_or_seed, n_types=n_types, n_type_covariates=n_type_covariates) optim_paras, options = process_params_and_options(params, options) # Update variables if not a random model, but an example model is tested. if isinstance(model_or_seed, str): n_types = optim_paras["n_types"] n_type_covariates = (None if n_types == 1 else len( optim_paras["type_covariates"])) if optim_paras["n_types"] > 1: # Resort type probability parameters. types = [f"type_{i}" for i in range(2, optim_paras["n_types"] + 1)] params.loc[types] = params.sort_index(ascending=False).loc[types] optim_paras_, _ = process_params_and_options(params, options) assert (optim_paras["type_prob"] == optim_paras_["type_prob"]).all()
def test_create_state_space_vs_specialized_kw94(model): """ Test whether create state space reproduces invariant features of the kw94 state space! """ point_constr = {"n_lagged_choices": 1, "observables": False} params, options = process_model_or_seed(model, point_constr=point_constr) optim_paras, options = process_params_and_options(params, options) # Create old state space arguments. n_periods = options["n_periods"] n_types = optim_paras["n_types"] edu_max = optim_paras["choices"]["edu"]["max"] edu_starts = np.array(list(optim_paras["choices"]["edu"]["start"])) # Get states and indexer from old state space. states_old, indexer_old = _create_state_space_kw94(n_periods, n_types, edu_starts, edu_max) if n_types == 1: states_old = states_old[:, :-1] for i, idx in enumerate(indexer_old): shape = idx.shape indexer_old[i] = idx.reshape(shape[:-2] + (-1, )) states_new = _create_core_state_space(optim_paras, options) core_period_choice = _create_core_period_choice(states_new, optim_paras, options) # I think here we can get more elegant! Or is this the only way? core_index_to_complex = {i: k for i, k in enumerate(core_period_choice)} core_index_to_indices = { i: core_period_choice[core_index_to_complex[i]] for i in core_index_to_complex } # Create sp indexer indexer = _create_indexer(states_new, core_index_to_indices, optim_paras) # Compare the state spaces via sets as ordering changed in some cases. states_old_set = set(map(tuple, states_old)) states_new_set = set(map(tuple, states_new.to_numpy())) assert states_old_set == states_new_set # Compare indexers via masks for valid indices. for period in range(n_periods): index_old_period = indexer_old[period] index_old_period = index_old_period != INDEXER_INVALID_INDEX index_old_period = np.nonzero(index_old_period) indices_old = [ [period] + [index_old_period[x][i] for x in range(len(index_old_period))] for i in range(len(index_old_period[0])) ] for index in indices_old: assert tuple(index) in indexer.keys() for index in indexer.keys(): if index[0] == period: assert list(index) in indices_old
def test_model_options(model_or_seed): _, options = process_model_or_seed(model_or_seed) _, options = process_params_and_options(_, options) validate_options(options)