HT_extensive.solve(outputFlag=0, TimeLimit=time_limit) print('lower bound', HT_extensive.objBound) print('upper bound', HT_extensive.objVal) print('total time', HT_extensive.total_time) print('gap', HT_extensive.MIPGap) if solver == 'SDDiP': HydroThermal.binarize(bin_stage=T) HT_sddp = SDDiP(HydroThermal) HT_sddp.solve( logFile=0, logToConsole=0, cuts=[cut], n_processes=1, n_steps=1, max_iterations=n_iterations, ) result = Evaluation(HydroThermal) if T in [2, 3]: result.run(random_state=666, n_simulations=-1) else: result.run(random_state=666, n_simulations=3000) resultTrue = EvaluationTrue(HydroThermal) resultTrue.run(random_state=666, n_simulations=3000) print('lower bound', result.db) if T in [2, 3]: print('upper bound', result.epv) else: print('CI appr.', result.CI) print('gap', result.gap) print('CI true', resultTrue.CI)
sharex=True) fig_mca.text(0.5, 0.01, 'stage', ha='center') fig_mca.text(0, 0.5, 'demand', va='center', rotation='vertical') for j_idx, j in enumerate(J): fig_mca = fan_plot(true[:, :, j_idx], ax=ax_mca[j_idx][0]) fig_mca = fan_plot(simulation[:, :, j_idx], ax=ax_mca[j_idx][1]) fig_mca.tight_layout() fig_mca.savefig("./result/airline_MCA.png", dpi=1200) fig_bound, ax_bound = plt.subplots(2, 1, figsize=(10, 5), sharey=True) for cut_index, cut in enumerate(['B', 'SB']): airline_sddp = SDDiP(airline) airline_sddp.solve(cuts=[cut], n_processes=1, n_steps=1, max_iterations=100) result = Evaluation(airline) result.run(random_state=666, n_simulations=3000) resultTrue = EvaluationTrue(airline) resultTrue.run(random_state=666, n_simulations=3000) summary['model'].append(idx) summary['cut'].append(cut) summary['time'].append(airline_sddp.total_time) summary['gap'].append(result.gap) summary['bound'].append(result.db) summary['CI_approx_lower'].append(result.CI[0]) summary['CI_approx_upper'].append(result.CI[1]) summary['CI_true_lower'].append(resultTrue.CI[0]) summary['CI_true_upper'].append(resultTrue.CI[1]) if idx == 2: fig_bound = airline_sddp.plot_bounds(window=1, smooth=1,
AssetMgt.add_Markovian_uncertainty(g) for t in range(T): m = AssetMgt[t] now, past = m.addStateVars(2, lb=0, obj=0) if t == 0: m.addConstr(now[0] + now[1] == 55) if t in [1, 2]: m.addConstr(past[0] + past[1] == now[0] + now[1], uncertainty_dependent={ past[0]: 0, past[1]: 1 }) if t == 3: y = m.addVar(obj=1) w = m.addVar(obj=-4) m.addConstr(past[0] + past[1] - y + w == 80, uncertainty_dependent={ past[0]: 0, past[1]: 1 }) AssetMgt.discretize( n_Markov_states=25, n_sample_paths=10000, ) # Extensive(AssetMgt).solve() SDDP(AssetMgt).solve(max_iterations=400) result = Evaluation(AssetMgt) result.run(n_simulations=1000) resultTrue = EvaluationTrue(AssetMgt) resultTrue.run(n_simulations=1000)
def evaluate_policy(self, n_simulations): evaluate = [False] # When evaluating the obtained policy from TS approach we sometimes get computational issues if self.markov: evaluate.append(True) for true_distribution in evaluate: #[True,False] if true_distribution: result = EvaluationTrue(self.model_ip) string = '_true' else: result = Evaluation(self.model_ip) string = '' result.run(n_simulations=n_simulations, query_stage_cost=True, query=self.data.dat['query']) #I could add this to the dataframe self.data.evaluation['db_eval' + string] = result.db self.data.evaluation['CI_eval' + string] = result.CI self.data.evaluation['gap_eval' + string] = result.gap self.data.evaluation['pv2_eval' + string] = result.pv self.data.evaluation['epv_eval' + string] = result.epv decision_matrix = pd.DataFrame(index=list(range(n_simulations)), columns=self.data.dat['T_STAGES']) decision_matrix = decision_matrix.fillna('-') for i in range(n_simulations): for (t, a) in self.data.dat['x_act_combinations']: if result.solution["x_act[{},{}]".format(t, a)].at[i, t] == 1: decision_matrix.at[i, t] = a operating = True for t in self.data.dat['T_STAGES']: if result.solution['y_sd'].at[i, t] == 1 and operating: decision_matrix.at[i, t] = 'y_sd' operating = False self.data.evaluation['decision_matrix' + string] = decision_matrix self.data.evaluation['prices' + string] = result.solution['price'] if self.two_factor: self.data.evaluation['eq_fact' + string] = result.solution['eq_fact'] self.data.evaluation['dev_fact' + string] = result.solution['dev_fact'] ####################### ### DECISION MATRIX ### ####################### if self.print_decision_matrix: if self.markov: decision_matrix = self.data.evaluation['decision_matrix_true'] prices = self.data.evaluation['prices_true'] else: decision_matrix = self.data.evaluation['decision_matrix'] prices = self.data.evaluation['prices'] summary_decision_matrix = decision_matrix.groupby( decision_matrix.columns.tolist(), as_index=False).size() unique_solutions = decision_matrix.drop_duplicates() unique_solutions = unique_solutions.reset_index(drop=True) rows_to_instances = {i: [] for i in range(len(unique_solutions)) } #rows are the unique solutions for index, row in decision_matrix.iterrows(): for index2, row2 in unique_solutions.iterrows(): if list(row) == list(row2): rows_to_instances[index2].append(index) unique_solutions['count'] = 0 rows_dec_mat = list(summary_decision_matrix.index) for i in range(len(rows_dec_mat)): for j in range(len(unique_solutions)): if list(summary_decision_matrix.iloc[i, 0:6]) == list( unique_solutions.iloc[j, 0:6]): unique_solutions['count'][j] = summary_decision_matrix[ 'size'][i] ordering = { 'new_wells8': 0, 'new_wells4': 1, 'sidetrack2': 2, 'sidetrack1': 3, '-': 4, 'y_sd': 5 } unique_solutions['order'] = 0 for i in [5, 4, 3, 2, 1, 0]: unique_solutions['order'] = [ ordering[j] for j in unique_solutions[i] ] unique_solutions = unique_solutions.sort_values(by='order', ascending=True) order = list(unique_solutions.index) def manual_sorting(col): output = [] for i in col: for j in range(len(order)): if i == order[j]: output.append(j) return output vars = ['prices'] statistics = [ i + '_' + j for i in vars for j in ['mean', 'min', 'max'] ] stat_df = { s: pd.DataFrame(index=range(len(unique_solutions))) for s in statistics } num_stages = len(decision_matrix.columns) for t in range(num_stages): # initialize for stat in statistics: stat_df[stat][str(t)] = 0.0 for i in range(len(unique_solutions)): subset = prices.iloc[rows_to_instances[i], :] for t in range(num_stages): var = 'prices' stat_df[var + '_min'][str(t)][i] = subset.iloc[:, t].min() stat_df[var + '_max'][str(t)][i] = subset.iloc[:, t].max() stat_df[var + '_mean'][str(t)][i] = subset.iloc[:, t].mean() for stat in statistics: print(stat) stat_df[stat]['row_number'] = list(stat_df[stat].index) print((stat_df[stat].sort_values(by='row_number', key=manual_sorting)))
def solve(self, n_processes=1, n_steps=1, max_iterations=10000, max_stable_iterations=10000, max_time=1000000.0, tol=0.001, freq_evaluations=None, percentile=95, tol_diff=float("-inf"), random_state=None, freq_evaluations_true=None, freq_comparisons=None, n_simulations=3000, n_simulations_true=3000, freq_clean=None, logFile=1, logToConsole=1): """Solve approximation model. Parameters ---------- n_processes: int, optional (default=1) The number of processes to run in parallel. Run serial SDDP if 1. If n_steps is 1, n_processes is coerced to be 1. n_steps: int, optional (default=1) The number of forward/backward steps to run in each cut iteration. It is coerced to be 1 if n_processes is 1. max_iterations: int, optional (default=10000) The maximum number of iterations to run SDDP. max_stable_iterations: int, optional (default=10000) The maximum number of iterations to have same deterministic bound tol: float, optional (default=1e-3) tolerance for convergence of bounds freq_evaluations: int, optional (default=None) The frequency of evaluating gap on approximation model. It will be ignored if risk averse percentile: float, optional (default=95) The percentile used to compute confidence interval diff: float, optional (default=-inf) The stablization threshold freq_comparisons: int, optional (default=None) The frequency of comparisons of policies n_simulations: int, optional (default=10000) The number of simluations to run when evaluating a policy on approximation model freq_clean: int/list, optional (default=None) The frequency of removing redundant cuts. If int, perform cleaning at the same frequency for all stages. If list, perform cleaning at different frequency for each stage; must be of length T-1 (the last stage does not have any cuts). random_state: int, RandomState instance or None, optional (default=None) Used in evaluations and comparisons. (In the forward step, there is an internal random_state which is not supposed to be changed.) If int, random_state is the seed used by the random number generator; If RandomState instance, random_state is the random number generator; If None, the random number generator is the RandomState instance used by numpy.random. logFile: binary, optional (default=1) Switch of logging to log file logToConsole: binary, optional (default=1) Switch of logging to console """ MSP = self.MSP if freq_clean is not None: if isinstance(freq_clean, (numbers.Integral, numpy.integer)): freq_clean = [freq_clean] * (MSP.T - 1) if isinstance(freq_clean, ((abc.Sequence, numpy.ndarray))): if len(freq_clean) != MSP.T - 1: raise ValueError("freq_clean list must be of length T-1!") else: raise TypeError( "freq_clean must be int/list instead of {}!".format( type(freq_clean))) if not MSP._flag_update: MSP._update() stable_iterations = 0 total_time = 0 a = time.time() gap = 1.0 right_end_of_CI = float("inf") db_past = MSP.bound self.percentile = percentile if MSP.measure != "risk neutral": freq_evaluations = None # distinguish pv_sim from pv pv_sim_past = None if n_processes != 1: self.n_steps = n_steps self.n_processes = min(n_steps, n_processes) self.jobs = allocate_jobs(self.n_steps, self.n_processes) logger_sddp = LoggerSDDP( logFile=logFile, logToConsole=logToConsole, n_processes=self.n_processes, percentile=self.percentile, ) logger_sddp.header() if freq_evaluations is not None or freq_comparisons is not None: logger_evaluation = LoggerEvaluation( n_simulations=n_simulations, percentile=percentile, logFile=logFile, logToConsole=logToConsole, ) logger_evaluation.header() if freq_comparisons is not None: logger_comparison = LoggerComparison( n_simulations=n_simulations, percentile=percentile, logFile=logFile, logToConsole=logToConsole, ) logger_comparison.header() try: while (self.iteration < max_iterations and total_time < max_time and stable_iterations < max_stable_iterations and tol < gap and tol_diff < right_end_of_CI): start = time.time() self._compute_cut_type() if self.n_processes == 1: pv = self._SDDP_single() else: pv = self._SDDP_multiprocessesing() m = (MSP.models[0] if MSP.n_Markov_states == 1 else MSP.models[0][0]) m.optimize() if m.status not in [2, 11]: m.write_infeasible_model("backward_" + str(m._model.modelName) + ".lp") db = m.objBound self.db.append(db) MSP.db = db if self.n_processes != 1: CI = compute_CI(pv, percentile) self.pv.append(pv) if self.iteration >= 1: if db_past == db: stable_iterations += 1 else: stable_iterations = 0 self.iteration += 1 db_past = db end = time.time() elapsed_time = end - start total_time += elapsed_time if self.n_processes == 1: logger_sddp.text( iteration=self.iteration, db=db, pv=pv[0], time=elapsed_time, ) else: logger_sddp.text( iteration=self.iteration, db=db, CI=CI, time=elapsed_time, ) if (freq_evaluations is not None and self.iteration % freq_evaluations == 0 or freq_comparisons is not None and self.iteration % freq_comparisons == 0): start = time.time() evaluation = Evaluation(MSP) evaluation.run( n_simulations=n_simulations, random_state=random_state, query_stage_cost=False, percentile=percentile, ) pandas.DataFrame({ 'pv': evaluation.pv }).to_csv("evaluation.csv") elapsed_time = time.time() - start gap = evaluation.gap if n_simulations == -1: logger_evaluation.text( iteration=self.iteration, db=db, pv=evaluation.epv, gap=gap, time=elapsed_time, ) elif n_simulations == 1: logger_evaluation.text( iteration=self.iteration, db=db, pv=evaluation.pv, gap=gap, time=elapsed_time, ) else: logger_evaluation.text( iteration=self.iteration, db=db, CI=evaluation.CI, gap=gap, time=elapsed_time, ) if (freq_comparisons is not None and self.iteration % freq_comparisons == 0): start = time.time() pv_sim = evaluation.pv if self.iteration / freq_comparisons >= 2: diff = MSP.sense * (numpy.array(pv_sim_past) - numpy.array(pv_sim)) if n_simulations == -1: diff_mean = numpy.mean(diff) right_end_of_CI = diff_mean else: diff_CI = compute_CI(diff, self.percentile) right_end_of_CI = diff_CI[1] elapsed_time = time.time() - start if n_simulations == -1: logger_comparison.text( iteration=self.iteration, ref_iteration=self.iteration - freq_comparisons, diff=diff_mean, time=elapsed_time, ) else: logger_comparison.text( iteration=self.iteration, ref_iteration=self.iteration - freq_comparisons, diff_CI=diff_CI, time=elapsed_time, ) pv_sim_past = pv_sim if freq_clean is not None: clean_stages = [ t for t in range(1, MSP.T - 1) if self.iteration % freq_clean[t] == 0 ] if len(clean_stages) != 0: self._remove_redundant_cut(clean_stages) # self._clean() except KeyboardInterrupt: stop_reason = "interruption by the user" # SDDP iteration stops MSP.db = self.db[-1] if self.iteration >= max_iterations: stop_reason = "iteration:{} has reached".format(max_iterations) if total_time >= max_time: stop_reason = "time:{} has reached".format(max_time) if stable_iterations >= max_stable_iterations: stop_reason = "stable iteration:{} has reached".format( max_stable_iterations) if gap <= tol: stop_reason = "convergence tolerance:{} has reached".format(tol) if right_end_of_CI <= tol_diff: stop_reason = "stablization threshold:{} has reached".format( tol_diff) b = time.time() logger_sddp.footer(reason=stop_reason) if freq_evaluations is not None or freq_comparisons is not None: logger_evaluation.footer() if freq_comparisons is not None: logger_comparison.footer() self.total_time = total_time
) for j in range(N): m.addConstr(past[j] == capm[j], uncertainty_dependent={past[j]:j}) m.addConstr(past[j] == idio[j], uncertainty={past[j]:f(alpha[j],sigma[j])}) m.addConstr(now[N] == (1+rf) * past[N]) AssetMgt.discretize( n_samples=100, method='input', Markov_states=Markov_states, transition_matrix=transition_matrix, random_state=888, ) AssetMgt.set_AVaR(lambda_=lambda_, alpha_=0.25) AssetMgt_SDDP = SDDP(AssetMgt) AssetMgt_SDDP.solve(max_iterations=50, n_steps=3, n_processes=3) evaluation = Evaluation(AssetMgt) evaluation.run(n_simulations=1000, random_state=666) evaluationTrue = EvaluationTrue(AssetMgt) evaluationTrue.run(n_simulations=1000, random_state=666) result = { 'mean':numpy.mean(evaluation.pv)-100, 'std':numpy.std(evaluation.pv), 'VAR': numpy.quantile(evaluation.pv,0.05)-100, 'skewness': stats.skew(evaluation.pv), 'kurtosis': 3+stats.kurtosis(evaluation.pv), 'Sharpe Ratio': (numpy.mean(evaluation.pv)-100-0.005513771)/numpy.std(evaluation.pv) } evaluation_tab.append(pandas.Series(result)) resultTrue = { 'mean':numpy.mean(evaluationTrue.pv)-100,