def opt_cut(model, where): if where == GRB.Callback.MIPSOL: # for a given integer first stage solution startTime = time.time() y0_param_preprocessed = model.cbGetSolution(y0) #{y[i, j, k]} y0_param = {} y0_key = "" # cant hash with a dict type (y0_param), so transform it to a str for i, j in A: for k in K: y0_param[i, j, k] = round(y0_param_preprocessed[i, j, k]) y0_key += str(y0_param[i, j, k]) theta_tongo = model.cbGetSolution(theta) # calculate FSC(y^) FSC = sum(lc[(arco, k)] * y0_param[arco[0], arco[1], k] for arco in AF for k in K) # print("FSC: {}, theta tongo {}".format(FSC, theta_tongo)) if partial: if y0_key not in next_gap_to_solve.keys(): next_gap_to_solve[y0_key] = gaps_to_assign[0] # if for this solution Q hasnt been solved if (not partial and y0_key not in int_Q_hash.keys()) or ( partial and next_gap_to_solve[y0_key] != None): startTime = time.time() Qs_int_calculated = {} Qs_bound_int = {} Qs_gaps_int = {} for s in S: #update if not continuous_cargo: ss6, ss7, ss10, ss11 = update_model_Qs( const_ss, int_sat, y_sat, x_sat, w_sat, q_sat, zplus_sat, r_sat, days, s, airports, Nint, Nf, Nl, N, AF, AG, A, K, nav, n, av, air_cap, air_vol, Cargo, OD, size, vol, ex, mand, cv, cf, ch, inc, lc, sc, delta, V, gap, tv) const_ss[6], const_ss[7], const_ss[10], const_ss[ 11] = ss6, ss7, ss10, ss11 else: ss5_list, ss7 = update_model_Qs_ContFlow( const_ss, int_sat, y_sat, f_sat, zplus_sat, r_sat, days, s, airports, Nint, Nf, Nl, N, AF, AG, A, K, nav, n, av, air_cap, air_vol, Cargo, OD, size, vol, ex, mand, cv, cf, ch, inc, lc, sc, delta, V, gap, tv, volperkg, incperkg) const_ss[5], const_ss[7] = ss5_list, ss7 #solve if partial: Qs_int_calculated[s], Qs_bound_int[s], Qs_gaps_int[ s] = solve_integer_Qs_SingleSat( y0_param, s, A, K, int_r1, int_sat, partial_gap=next_gap_to_solve[y0_key]) else: Qs_int_calculated[s], Qs_bound_int[ s] = solve_integer_Qs_SingleSat( y0_param, s, A, K, int_r1, int_sat) if partial: Q_av_gaps_int = sum(Qs_gaps_int[s] for s in S) / len(S) if Q_av_gaps_int <= gaps_to_assign[-1]: # av <= 1% next_gap_to_solve[y0_key] = None # finish else: next_gap_to_solve[y0_key] = max( gap for gap in gaps_to_assign if gap < Q_av_gaps_int) # the next smaller gap Q_int_calculated = sum(Qs_int_calculated[s] for s in S) / len(S) Q_int_bound = sum(Qs_bound_int[s] for s in S) / len(S) # print("Q_int_calculated: ", Q_int_calculated) theta_Q.append((theta_tongo, Q_int_calculated)) int_Q_hash[y0_key] = Q_int_calculated int_sol_hash[y0_key] = y0_param comment = "QTbound didnt cut; solved Q" # adding feasible solution (y tongo, Q(y tongo)) this_value = Q_int_calculated - FSC if this_value > model._current_incumbent: # print("*** solution better than incumbent ***") model._sol_to_add = model.cbGetSolution(model.getVars()) model._theta_to_add = Q_int_calculated model._current_incumbent = this_value if Q_int_calculated > L: print( "\n########## ERROR: Q(y tongo)={:.0f} > {:.0f}=L ##########\n" .format(Q_int_calculated, L)) if theta_tongo > Q_int_calculated + epsilon: # print(" ##Theta tongo {:.0f} > Q(y tongo) {:.0f}, adding INTEGER optimality cut##".format(theta_tongo, Q_int_calculated)) SUP_y = [(i, j, k) for i, j in AF for k in K if y0_param[i, j, k] > 0.5] not_SUP_y = [(i, j, k) for i, j in AF for k in K if y0_param[i, j, k] < 0.5] delta_callback = quicksum(1 - y[0][i, j, k] for i, j, k in SUP_y) + quicksum( y[0][i, j, k] for i, j, k in not_SUP_y) if partial: model.cbLazy(theta <= (L - Q_int_bound) * delta_callback + Q_int_bound) else: model.cbLazy( theta <= (L - Q_int_calculated) * delta_callback + Q_int_calculated) model._Q_cblazy_added += 1 else: comment = comment + ", Q didnt cut; not added" # print("**** Theta tongo {:.0f} <= Q(y tongo) {:.0f}, NO NEW INTEGER CUT TO ADD ****".format(theta_tongo, Q_int_calculated)) theta_int_values.append(theta_tongo) Q_int_values.append(Q_int_calculated) runTime = time.time() - startTime int_cut_times.append(runTime) if write_cuts: wrt.write_cuts(logs_dir, model.ModelName, FSC, theta_tongo, Q_int_calculated, runTime, integer=True, QT_bound=False, comment=comment) elif (not partial and y0_key in int_Q_hash.keys()) or ( partial and next_gap_to_solve[y0_key] == None): Q_int_calculated = int_Q_hash[y0_key] # for plotting if theta_tongo > Q_int_calculated + epsilon: # add the cut if the first theta wasnt the optimal for the schedule SUP_y = [(i, j, k) for i, j in AF for k in K if y0_param[i, j, k] > 0.5] not_SUP_y = [(i, j, k) for i, j in AF for k in K if y0_param[i, j, k] < 0.5] delta_callback = quicksum(1 - y[0][i, j, k] for i, j, k in SUP_y) + quicksum( y[0][i, j, k] for i, j, k in not_SUP_y) model.cbLazy(theta <= (L - Q_int_calculated) * delta_callback + Q_int_calculated) model._Q_cblazy_added += 1 theta_int_values.append(theta_tongo) Q_int_values.append(Q_int_calculated) # print("***** VISITED SOLUTION TWICE, NOTHING TO SOLVE. Q(y) = {:.0f} *****".format(Q_int_calculated)) # adding the feasible solution as the incumbent for the node elif where == GRB.Callback.MIPNODE: if len(model._sol_to_add) > 0: # print("*** ADDING FEASIBLE SOLUTION ***") model.cbSetSolution(model.getVars(), model._sol_to_add) model.cbSetSolution(theta, model._theta_to_add) model._sol_to_add.clear() model._theta_to_add = 0
#solve Qs_bound_int = {} Qs_int_calculated = {} times_list = [] Q_list = [] for s in S_OUTSAMPLE: this_StarTime = time.time() ss6, ss7, ss10, ss11 = update_model_Qs( const_ss, int_sat, y_sat, x_sat, w_sat, q_sat, zplus_sat, r_sat, days, s, airports, Nint, Nf, Nl, N, AF, AG, A, K, nav, n, av, air_cap, air_vol, Cargo_base, OD_base, size_OUTSAMPLE, vol_OUTSAMPLE, ex_OUTSAMPLE, mand_OUTSAMPLE, cv, cf, ch, inc_OUTSAMPLE, lc, sc_OUTSAMPLE, delta, V, gap, tv) const_ss[6], const_ss[7], const_ss[10], const_ss[ 11] = ss6, ss7, ss10, ss11 Qs_int_calculated[s], Qs_bound_int[s], _ = solve_integer_Qs_SingleSat( y0_param, s, A, K, int_r1, int_sat, partial_gap=gap_outsample) this_RunTime = round(time.time() - this_StarTime, 2) times_list.append(this_RunTime) Q_list.append(round(Qs_int_calculated[s], 2)) print(" Scenario OUTSAMPLE {}: Q = {:.2f}, time = {}".format( s, Qs_int_calculated[s], this_RunTime)) Q_int_calculated = sum(Qs_int_calculated[s] for s in S_OUTSAMPLE) / len(S_OUTSAMPLE) performance_RunTime = round(time.time() - performance_StartTime, 2) sd = round(st.stdev(Q_list), 2) print( f"Q mean : {st.mean(Q_list)}, min: {min(Q_list)}, max: {max(Q_list)}, SD: {sd}, Percentage Variation: {round(sd/st.mean(Q_list)*100, 2)}%" ) print( f"Real second stage value for the schedule: {round(Q_int_calculated, 2)}" )
def opt_cut(model, where): if where == GRB.Callback.MIPSOL: # for a given integer first stage solution startTime = time.time() y0_param_preprocessed = model.cbGetSolution(y0) #{y[i, j, k]} y0_param = {} y0_key = "" # cant hash with a dict type (y0_param), so transform it to a str for i,j in A: for k in K: y0_param[i,j,k] = round(y0_param_preprocessed[i,j,k]) y0_key += str(y0_param[i,j,k]) theta_tongo = model.cbGetSolution(theta) # calculate FSC(y^) FSC = sum(lc[(arco, k)]*y0_param[arco[0], arco[1], k] for arco in AF for k in K) # print("FSC: {}, theta tongo {}".format(FSC, theta_tongo)) if partial: if y0_key not in next_gap_to_solve.keys(): next_gap_to_solve[y0_key] = gaps_to_assign[0] # if for this solution Q hasnt been solved if (not partial and y0_key not in int_Q_hash.keys()) or (partial and next_gap_to_solve[y0_key] != None): # print("New solution, solving QTbound(y)") # predict a bound from a previous solution QT_bound = check_best_bound(y0_param, int_sol_hash, int_Q_bound_hash, sc, V, gap, tv, AF, K, S) # print("QT_bound: ", QT_bound) # hash the QTbound if its for a new sol or is better than the previous if y0_key not in QTbound_hash.keys(): QTbound_hash[y0_key] = QT_bound elif QT_bound < QTbound_hash[y0_key]: QTbound_hash[y0_key] = QT_bound comment = "" # if doesnt cut current solution (y, theta), solve Q if not theta_tongo > QT_bound + epsilon: # print("QTbound didnt cut, solving Q") Qs_int_calculated = {} Qs_bound_int = {} Qs_gaps_int = {} for s in S: if not continuous_cargo: ss6, ss7, ss10, ss11 = update_model_Qs(const_ss, int_sat, y_sat, x_sat, w_sat, q_sat, zplus_sat, r_sat, days, s, airports, Nint, Nf, Nl, N, AF, AG, A, K, nav, n, av, air_cap, air_vol, Cargo, OD, size, vol, ex, mand, cv, cf, ch, inc, lc, sc, delta, V, gap, tv) const_ss[6], const_ss[7], const_ss[10], const_ss[11] = ss6, ss7, ss10, ss11 else: ss5_list, ss7 = update_model_Qs_ContFlow(const_ss, int_sat, y_sat, f_sat, zplus_sat, r_sat, days, s, airports, Nint, Nf, Nl, N, AF, AG, A, K, nav, n, av, air_cap, air_vol, Cargo, OD, size, vol, ex, mand, cv, cf, ch, inc, lc, sc, delta, V, gap, tv, volperkg, incperkg) const_ss[5], const_ss[7] = ss5_list, ss7 if partial: Qs_int_calculated[s], Qs_bound_int[s], Qs_gaps_int[s] = solve_integer_Qs_SingleSat(y0_param, s, A, K, int_r1, int_sat, partial_gap=next_gap_to_solve[y0_key]) else: Qs_int_calculated[s], Qs_bound_int[s] = solve_integer_Qs_SingleSat(y0_param, s, A, K, int_r1, int_sat) if partial: Q_av_gaps_int = sum(Qs_gaps_int[s] for s in S)/len(S) if Q_av_gaps_int <= gaps_to_assign[-1]: # av <= 1% next_gap_to_solve[y0_key] = None # finish else: next_gap_to_solve[y0_key] = max(gap for gap in gaps_to_assign if gap < Q_av_gaps_int) # the next smaller gap Q_int_calculated = sum(Qs_int_calculated[s] for s in S)/len(S) Q_int_bound = sum(Qs_bound_int[s] for s in S)/len(S) # print("Q_int_calculated: ", Q_int_calculated) theta_Q.append( (theta_tongo, Q_int_calculated) ) int_Q_hash[y0_key] = Q_int_calculated int_Q_bound_hash[y0_key] = Q_int_bound int_sol_hash[y0_key] = y0_param # to calculate T(y02,y01) comment = "QTbound didnt cut; solved Q" # adding feasible solution (y tongo, Q(y tongo)) this_value = Q_int_calculated - FSC if this_value > model._current_incumbent: # print("*** solution better than incumbent ***") model._sol_to_add = model.cbGetSolution(model.getVars()) model._theta_to_add = Q_int_calculated model._current_incumbent = this_value if Q_int_calculated > L: print("\n########## ERROR: Q(y tongo)={:.0f} > {:.0f}=L ##########\n".format(Q_int_calculated, L)) if theta_tongo > Q_int_calculated + epsilon: # print(" ##Theta tongo {:.0f} > Q(y tongo) {:.0f}, adding INTEGER optimality cut##".format(theta_tongo, Q_int_calculated)) SUP_y = [(i,j,k) for i,j in AF for k in K if y0_param[i, j, k] > 0.5] not_SUP_y = [(i,j,k) for i,j in AF for k in K if y0_param[i, j, k] < 0.5] delta_callback = quicksum(1 - y[0][i, j, k] for i,j,k in SUP_y) + quicksum(y[0][i, j, k] for i,j,k in not_SUP_y) if partial: model.cbLazy(theta <= (L - Q_int_bound)*delta_callback + Q_int_bound) else: model.cbLazy(theta <= (L - Q_int_calculated)*delta_callback + Q_int_calculated) model._Q_cblazy_added += 1 else: comment = comment + ", Q didnt cut; not added" # print("**** Theta tongo {:.0f} <= Q(y tongo) {:.0f}, NO NEW INTEGER CUT TO ADD ****".format(theta_tongo, Q_int_calculated)) theta_int_values.append(theta_tongo) Q_int_values.append(Q_int_calculated) runTime = time.time() - startTime int_cut_times.append(runTime) if write_cuts: wrt.write_cuts(logs_dir, model.ModelName, FSC, theta_tongo, Q_int_calculated, runTime, integer=True, QT_bound=False, comment=comment) # GLOBAL CUTS proactive_startTime = time.time() # for every operated flight for i_bar, j_bar in AF: for k_bar in K: if y0_param[i_bar, j_bar, k_bar] == 1: # similar flights to the observed one Similar = generate_similar_flights(y0_param, i_bar, j_bar, k_bar, AF, airports, K) if Similar != []: # QTb in this vicinity Q_y2_p = Q_int_calculated T_y2_y1_p = sum(sc[s][(i_bar, j_bar), k_bar] for s in S)/len(S) QT_bound_p = Q_y2_p + T_y2_y1_p #add global cut Omega_1 = [(i,j,k) for i,j in AF for k in K if (y0_param[i, j, k] > 0.5 and i,j not in Similar)] Omega_2 = [(i,j,k) for i,j in AF for k in K if (y0_param[i, j, k] < 0.5 and i,j not in Similar)] Delta_1 = quicksum(1 - y[0][i, j, k] for i,j,k in Omega_1) + quicksum(y[0][i, j, k] for i,j,k in Omega_2) Delta_2 = quicksum(y[0][i, j, k] for i,j in Similar for k in K if k != k_bar) + quicksum(y[0][i_bar, j_bar, k] for k in K) a_hat, b_hat = bounds_for_similar(Similar) Delta_3 = 1 - nu_two[a_hat][b_hat, k_bar] # GC constraints for i,j in Similar: #GC1 model.cbLazy(y[0][i, j, k_bar] <= nu_one[a_hat][b_hat, k_bar]) #GC2 model.cbLazy(nu_one[a_hat][b_hat, k_bar] <= quicksum(y[0][i, j, k_bar] for i,j in Similar)) #GC3 model.cbLazy(2*nu_one[a_hat][b_hat, k_bar] - quicksum(y[0][i, j, k_bar] for i,j in Similar) <= nu_two[a_hat][b_hat, k_bar]) #global cut model.cbLazy(theta <= QT_bound_p + (Delta_1 + Delta_2 + Delta_3)*L) model._QTbound_global_cuts += 1 proactive_runTime = time.time() - proactive_startTime proactive_bound_times.append(proactive_runTime) # if it cuts the current solution, add the QTbound else: SUP_y = [(i,j,k) for i,j in AF for k in K if y0_param[i, j, k] > 0.5] not_SUP_y = [(i,j,k) for i,j in AF for k in K if y0_param[i, j, k] < 0.5] delta_callback = quicksum(1 - y[0][i, j, k] for i,j,k in SUP_y) + quicksum(y[0][i, j, k] for i,j,k in not_SUP_y) model.cbLazy(theta <= (L - QT_bound)*delta_callback + QT_bound) model._QTbound_cblazy_added += 1 runTime_bound = time.time() - startTime bound_times.append(runTime_bound) if write_cuts: wrt.write_cuts(logs_dir, model.ModelName, FSC, theta_tongo, QT_bound, runTime_bound, integer=True, QT_bound=True, comment=comment) elif (not partial and y0_key in int_Q_hash.keys()) or (partial and next_gap_to_solve[y0_key] == None): Q_int_calculated = int_Q_hash[y0_key] # for plotting if theta_tongo > Q_int_calculated + epsilon: # add the cut if the first theta wasnt the optimal for the schedule SUP_y = [(i,j,k) for i,j in AF for k in K if y0_param[i, j, k] > 0.5] not_SUP_y = [(i,j,k) for i,j in AF for k in K if y0_param[i, j, k] < 0.5] delta_callback = quicksum(1 - y[0][i, j, k] for i,j,k in SUP_y) + quicksum(y[0][i, j, k] for i,j,k in not_SUP_y) model.cbLazy(theta <= (L - Q_int_calculated)*delta_callback + Q_int_calculated) model._Q_cblazy_added += 1 theta_int_values.append(theta_tongo) Q_int_values.append(Q_int_calculated) # print("***** VISITED SOLUTION TWICE, NOTHING TO SOLVE. Q(y) = {:.0f} *****".format(Q_int_calculated)) # adding the feasible solution as the incumbent for the node elif where == GRB.Callback.MIPNODE: if len(model._sol_to_add) > 0: # print("*** ADDING FEASIBLE SOLUTION ***") model.cbSetSolution(model.getVars(), model._sol_to_add) model.cbSetSolution(theta, model._theta_to_add) model._sol_to_add.clear() model._theta_to_add = 0