Example #1
0
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
Example #2
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