def test_trans_mat_rows_one(): rand_dict = random_init() control = np.ones(rand_dict["estimation"]["states"]) assert_array_almost_equal( create_transition_matrix( rand_dict["estimation"]["states"], np.array(rand_dict["simulation"]["known_trans"]), ).sum(axis=1), control, )
def estimate(init_dict, df, repl_4=False): """ This function calls the auxiliary functions to estimate the decision parameters. Therefore it manages the estimation process. As mentioned in the model theory chapter of the paper, the estimation of the transition probabilities and the estimation of the parameters shaping the cost function are completely separate. :param init_dict: A dictionary containing the following variables as keys: :beta: (float) : Discount factor. :states: (int) : The size of the statespace. :maint_func: (func) : The maintenance cost function. Default is the linear from the paper. :param df: A pandas dataframe, which contains for each observation the Bus ID, the current state of the bus, the current period and the decision made in this period. :param repl_4: Auxiliary variable indicating the complete setting of the replication of the paper with group 4. :return: The function returns the optimization result of the transition probabilities and of the cost parameters as separate dictionaries. """ beta = init_dict["beta"] transition_results = estimate_transitions(df, repl_4=repl_4) endog = df.loc[:, "decision"].to_numpy() states = df.loc[:, "state"].to_numpy() num_obs = df.shape[0] num_states = init_dict["states"] maint_func = lin_cost # For now just set this to a linear cost function decision_mat = np.vstack(((1 - endog), endog)) trans_mat = create_transition_matrix(num_states, np.array(transition_results["x"])) state_mat = create_state_matrix(states, num_states, num_obs) result = opt.minimize( loglike_opt_rule, args=(maint_func, num_states, trans_mat, state_mat, decision_mat, beta), x0=np.array([5, 5]), bounds=[(1e-6, None), (1e-6, None)], method="L-BFGS-B", ) return transition_results, result
def test_regression_simulation(inputs): init_dict = random_init(inputs) df, unobs, utilities, num_states = simulate(init_dict["simulation"]) num_buses = init_dict["simulation"]["buses"] num_periods = init_dict["simulation"]["periods"] beta = init_dict["simulation"]["beta"] params = np.array(init_dict["simulation"]["params"]) probs = np.array(init_dict["simulation"]["known probs"]) v_disc_ = [0.0, 0.0] v_disc = discount_utility( v_disc_, num_buses, num_periods, num_periods, utilities, beta ) trans_mat = create_transition_matrix(num_states, probs) costs = myopic_costs(num_states, lin_cost, params) v_calc = calc_fixp(num_states, trans_mat, costs, beta) un_ob_av = 0 for bus in range(num_buses): un_ob_av += unobs[bus, 0, 0] un_ob_av = un_ob_av / num_buses assert_allclose(v_disc[1] / (v_calc[0] + un_ob_av), 1, rtol=1e-02)
def params_hess(params, df, beta, maint_func, repl_4=False): """Calculates the hessian of the cost parameters.""" transition_results = estimate_transitions(df, repl_4=repl_4) ll_trans = transition_results["fun"] states = df.loc[:, "state"].to_numpy() num_obs = df.shape[0] if repl_4: num_states = 90 else: num_states = int(1.2 * np.max(states)) trans_mat = create_transition_matrix(num_states, np.array(transition_results["x"])) state_mat = create_state_matrix(states, num_states, num_obs) endog = df.loc[:, "decision"].to_numpy() decision_mat = np.vstack(((1 - endog), endog)) params_df = pd.DataFrame(index=["RC", "theta_1_1"], columns=["value"], data=params) wrap_func = create_wrap_func(maint_func, num_states, trans_mat, state_mat, decision_mat, beta, ll_trans) return hessian(wrap_func, params_df)
def plot_convergence(init_dict): beta = init_dict["simulation"]["beta"] df, unobs, utilities, num_states = simulate(init_dict["simulation"]) costs = myopic_costs(num_states, lin_cost, init_dict["simulation"]["params"]) trans_probs = np.array(init_dict["simulation"]["known probs"]) trans_mat = create_transition_matrix(num_states, trans_probs) ev = calc_fixp(num_states, trans_mat, costs, beta) num_buses = init_dict["simulation"]["buses"] num_periods = init_dict["simulation"]["periods"] gridsize = init_dict["plot"]["gridsize"] num_points = int(num_periods / gridsize) v_exp = np.full(num_points, calc_ev_0(ev, unobs, num_buses)) v_start = np.zeros(num_points) v_disc = discount_utility(v_start, num_buses, gridsize, num_periods, utilities, beta) periods = np.arange(0, num_periods, gridsize) ax = plt.figure(figsize=(14, 6)) ax1 = ax.add_subplot(111) ax1.set_ylim([0, 1.3 * v_disc[-1]]) ax1.set_ylabel(r"Value at time 0") ax1.set_xlabel(r"Periods") ax1.plot(periods, v_disc, color="blue") ax1.plot(periods, v_exp, color="orange") plt.tight_layout() os.makedirs("figures", exist_ok=True) plt.savefig("figures/figure_1.png", dpi=300)
def test_create_trans_mat(inputs, outputs): assert_array_almost_equal( create_transition_matrix(inputs["nstates"], inputs["trans_prob"]), outputs["trans_mat"], )
for roh in np.arange(0, 4, 0.1): roh_plot += [roh] init_dict["simulation"]["roh"] = roh worst_trans = get_worst_trans(init_dict["simulation"]) init_dict["simulation"]["real probs"] = worst_trans df, unobs, utilities, num_states = simulate(init_dict["simulation"]) costs = myopic_costs(num_states, lin_cost, init_dict["simulation"]["params"]) num_buses = init_dict["simulation"]["buses"] num_periods = init_dict["simulation"]["periods"] gridsize = init_dict["plot"]["gridsize"] real_trans_probs = np.array(init_dict["simulation"]["real probs"]) real_trans_mat = create_transition_matrix(num_states, real_trans_probs) ev_real = calc_fixp(num_states, real_trans_mat, costs, beta) known_trans_probs = np.array(init_dict["simulation"]["known probs"]) known_trans_mat = create_transition_matrix(num_states, known_trans_probs) ev_known = calc_fixp(num_states, known_trans_mat, costs, beta) v_calc = 0 for i in range(num_buses): v_calc = v_calc + unobs[i, 0, 0] + ev_known[0] v_calc = v_calc / num_buses v_exp_known += [v_calc] v_calc = 0 for i in range(num_buses): v_calc = v_calc + unobs[i, 0, 0] + ev_real[0]
from ruspy.plotting.value_zero import calc_ev_0 from ruspy.estimation.estimation_cost_parameters import calc_fixp from ruspy.estimation.estimation_cost_parameters import lin_cost from ruspy.estimation.estimation_cost_parameters import myopic_costs from ruspy.estimation.estimation_cost_parameters import create_transition_matrix with open("init.yml") as y: init_dict = yaml.safe_load(y) beta = init_dict["simulation"]["beta"] df, unobs, utilities, num_states = simulate(init_dict["simulation"]) costs = myopic_costs(num_states, lin_cost, init_dict["simulation"]["params"]) trans_probs = np.array(init_dict["simulation"]["probs"]) trans_mat = create_transition_matrix(num_states, trans_probs) ev = calc_fixp(num_states, trans_mat, costs, beta) num_buses = init_dict["simulation"]["buses"] num_periods = init_dict["simulation"]["periods"] gridsize = init_dict["plot"]["gridsize"] num_points = int(num_periods / gridsize) v_exp = np.full(num_points, calc_ev_0(ev, unobs, num_buses)) v_start = np.zeros(num_points) v_disc = discount_utility(v_start, num_buses, gridsize, num_periods, utilities, beta) periods = np.arange(0, num_periods, gridsize) ax = plt.figure(figsize=(14, 6))
def simulate_strategy(known_trans, increments, num_buses, num_periods, params, beta, unobs, maint_func): """ This function manages the simulation process. It initializes the auxiliary variables and calls therefore the subfuctions from estimation auxiliary. It then calls the decision loop, written for numba. As the state size of the fixed point needs to be a lot larger than the actual state, the size is doubled, if the loop hasn't run yet through all the periods. :param known_trans: A numpy array containing the transition probabilities the agent assumes. :param increments: A two dimensional numpy array containing for each bus in each period a random drawn state increase as integer. :param num_buses: The number of buses to be simulated. :type num_buses: int :param num_periods: The number of periods to be simulated. :type num_periods: int :param params: A numpy array containing the parameters shaping the cost function. :param beta: The discount factor. :type beta: float :param unobs: A three dimensional numpy array containing for each bus, for each period for the decision to maintain or replace the bus engine a random drawn utility as float. :param maint_func: The maintenance cost function. Only linear implemented so far. :return: The function returns the following objects: :states: : A two dimensional numpy array containing for each bus in each period the state as an integer. :decisions: : A two dimensional numpy array containing for each bus in each period the decision as an integer. :utilities: : A two dimensional numpy array containing for each bus in each period the utility as a float. :num_states: (int) : The size of the state space. """ num_states = int(200) start_period = int(0) states = np.zeros((num_buses, num_periods), dtype=int) decisions = np.zeros((num_buses, num_periods), dtype=int) utilities = np.zeros((num_buses, num_periods), dtype=float) while start_period < num_periods - 1: num_states = 2 * num_states known_trans_mat = create_transition_matrix(num_states, known_trans) costs = myopic_costs(num_states, maint_func, params) ev = calc_fixp(num_states, known_trans_mat, costs, beta) states, decisions, utilities, start_period = simulate_strategy_loop( num_buses, states, decisions, utilities, costs, ev, increments, num_states, start_period, num_periods, beta, unobs, ) return states, decisions, utilities, num_states
def select_fixp(trans_probs, state, num_states, costs, beta, max_it): trans_mat = create_transition_matrix(num_states, trans_probs) fixp = calc_fixp(num_states, trans_mat, costs, beta, max_it=max_it) return fixp[state]
def estimate(init_dict, df, repl_4=True): """ This function calls the auxiliary functions to estimate the decision parameters. Therefore it manages the estimation process. As mentioned in the model theory chapter of the paper, the estimation of the transition probabilities and the estimation of the parameters shaping the cost function are completely separate. :param init_dict: A dictionary containing the following variables as keys: :beta: (float) : Discount factor. :states: (int) : The size of the statespace. :maint_func: (string): The type of cost function, as string. Only linear implemented so far. :param df: A pandas dataframe, which contains for each observation the Bus ID, the current state of the bus, the current period and the decision made in this period. :param repl_4: Auxiliary variable for the convention of the replacement increase. :return: The function returns the optimization result of the transition probabilities and of the cost parameters as separate dictionaries. """ beta = init_dict["beta"] if repl_4: transition_results = estimate_transitions(df) else: transition_results, state_count = estimate_transitions(df, repl_4=repl_4) endog = df.loc[:, "decision"] states = df.loc[:, "state"] num_obs = df.shape[0] num_states = init_dict["states"] if init_dict["maint_func"] == "linear": maint_func = lin_cost else: maint_func = lin_cost decision_mat = np.vstack(((1 - endog), endog)) trans_mat = create_transition_matrix(num_states, np.array(transition_results["x"])) state_mat = create_state_matrix(states, num_states, num_obs) if "max_it" in init_dict.keys(): max_it = int(init_dict["max_it"]) result = opt.minimize( loglike_opt_rule, args=( maint_func, num_states, trans_mat, state_mat, decision_mat, beta, max_it, ), x0=np.array([10, 2]), bounds=[(1e-6, None), (1e-6, None)], method="L-BFGS-B", ) else: result = opt.minimize( loglike_opt_rule, args=(maint_func, num_states, trans_mat, state_mat, decision_mat, beta), x0=np.array([10, 2]), bounds=[(1e-6, None), (1e-6, None)], method="L-BFGS-B", ) if repl_4: return transition_results, result else: return transition_results, state_count, result