Gurobi reports OPTIMAL on a QP having a huge MaxVio

julia> JuMP.unset_silent(outer)
Set parameter OutputFlag to value 1

julia> JuMP.optimize!(outer)
Gurobi Optimizer version 12.0.2 build v12.0.2rc0 (win64 - Windows 11.0 (26100.2))

CPU model: 11th Gen Intel(R) Core(TM) i5-1135G7 @ 2.40GHz, instruction set [SSE2|AVX|AVX2|AVX512]
Thread count: 4 physical cores, 8 logical processors, using up to 8 threads

Optimize a model with 39 rows, 41 columns and 127 nonzeros
Model fingerprint: 0x6abf7598
Model has 38 quadratic objective terms
Coefficient statistics:
  Matrix range     [1e+00, 3e+02]
  Objective range  [9e-18, 5e+00]
  QObjective range [2e+00, 2e+00]
  Bounds range     [1e+03, 1e+03]
  RHS range        [4e+02, 2e+03]
Presolve removed 11 rows and 2 columns
Presolve time: 0.00s
Presolved: 28 rows, 39 columns, 80 nonzeros
Presolved model has 38 quadratic objective terms
Ordering time: 0.00s

Barrier statistics:
 Free vars  : 39
 AA' NZ     : 6.900e+01
 Factor NZ  : 1.060e+02
 Factor Ops : 8.340e+02 (less than 1 second per iteration)
 Threads    : 1

                  Objective                Residual
Iter       Primal          Dual         Primal    Dual     Compl     Time
   0   1.29618707e+01  1.29610499e+01  4.29e+02 1.02e+01  1.08e+06     0s
   1   7.79452936e+03 -9.29391938e+03  4.59e+01 4.59e-01  8.38e+04     0s
   2   2.34735483e+03 -3.67336088e+03  3.99e+00 4.00e-02  1.54e+04     0s
   3   9.28074635e+01 -8.89553051e+02  5.28e-01 5.42e-03  3.32e+03     0s
   4   2.67970847e+00 -2.69217523e+02  6.93e-02 1.81e-04  4.56e+02     0s
   5   4.93346415e+00 -2.33632458e+02  6.85e-02 1.78e-04  4.42e+02     0s
   6   2.61337406e+00 -2.70695461e+02  3.15e-01 1.50e-04  4.63e+02     0s
   7   1.09725171e+01 -1.34247786e+02  5.05e-01 1.37e-04  3.67e+02     0s
   8   6.15979734e+00 -7.90459574e+01  2.02e-01 6.79e-05  1.87e+02     0s
   9   2.03422156e-02 -8.54163247e+01  1.06e-01 4.22e-05  1.57e+02     0s
  10   6.28528487e-01 -3.31827999e+01  2.99e-02 4.33e-11  3.35e+01     0s
  11   1.33921978e-03 -2.70800877e+01  4.09e-13 2.12e-12  1.23e+00     0s
  12   2.55952604e-04 -3.06308511e+01  1.99e-13 2.79e-12  1.10e-02     0s
  13   3.33498799e-04 -3.42217263e+01  3.09e-13 2.70e-12  1.19e-04     0s
  14   5.57483325e-04 -3.51843270e+01  9.52e-14 1.82e-12  4.34e-07     0s
  15   2.39292335e-04 -3.74834526e+01  2.37e-13 1.82e-12  2.28e-09     0s
  16   2.43578332e-04 -3.89336401e+01  1.87e-13 2.41e-12  1.43e-10     0s
  17   2.11498031e-03 -5.43399078e+01  4.19e-02 3.42e-12  7.46e-11     0s
  18   2.96317192e-04 -5.53941734e+01  1.40e-13 5.61e-12  2.39e-12     0s
  19   4.12094691e-04 -5.67513066e+01  5.10e-14 4.84e-12  3.94e-13     0s
  20   6.75682559e-04 -5.83702637e+01  1.42e-13 1.61e-12  4.12e-16     0s
  21   8.90546712e-04 -5.95038795e+01  2.26e-13 3.58e-12  1.14e-16     0s
  22   9.87057970e-04 -6.26382723e+01  1.59e-13 3.55e-12  2.54e-19     0s

Barrier solved model in 22 iterations and 0.01 seconds (0.00 work units)
Optimal objective 9.87057970e-04


User-callback calls 113, time in user-callback 0.00 sec

julia> JuMP.solution_summary(outer)
solution_summary(; result = 1, verbose = false)
├ solver_name          : Gurobi
├ Termination
│ ├ termination_status : OPTIMAL
│ ├ result_count       : 1
│ ├ raw_status         : Model was solved to optimality (subject to tolerances), and an optimal solution is available.
│ └ objective_bound    : -2.70801e+01
├ Solution (result = 1)
│ ├ primal_status        : FEASIBLE_POINT
│ ├ dual_status          : FEASIBLE_POINT
│ ├ objective_value      : 9.87058e-04
│ └ dual_objective_value : -4.97810e+01
└ Work counters
  ├ solve_time (sec)   : 7.99990e-03
  ├ simplex_iterations : 0
  ├ barrier_iterations : 22
  └ node_count         : 0

julia> vio = JuMP.MOI.get(JuMP.backend(outer), Gurobi.ModelAttribute("MaxVio"))
43.97206084166428

julia> Gurobi.GRBwrite(JuMP.unsafe_backend(outer), "outer.mps")
0

This “OPTIMAL” solution is very weird—noticing the MaxVio, and the termination gap. Is this normal? (@odow I remember that there was also a gap displayed here)

The code to reproduce this model outer is here. (A lightweight PGlib.jl data package is required.)

code
import SparseArrays, PGLib; RawDict = PGLib.pglib("pglib_opf_case118_ieee.m");
import Random
function rectify_dir!(bft)
    for r in 1:size(bft, 1)
        if bft[r, 1] > bft[r, 2]
            bft[r, 1], bft[r, 2] = bft[r, 2], bft[r, 1]
        elseif bft[r, 1] == bft[r, 2]
            error("self-branch")
        end
    end
end; function assert_is_connected(bft)
    B = size(bft, 1)
    pb = Vector(1:B) # primal bs
    sn = [1] # subnet
    for ite in 1:B # only du a full iteration as a conservative choice
        progress = false
        for (i, b) in enumerate(pb) # take out branch b
            if bft[b, 1] in sn
                on = bft[b, 2] # the other node
                on ∉ sn && push!(sn, on)
            elseif bft[b, 2] in sn
                on = bft[b, 1] # the other node
                on ∉ sn && push!(sn, on)
            else
                continue
            end
            popat!(pb, i); progress = true; break # fathom branch b
        end
        if progress == false
            error("ite = $ite. All the rest branches are not connected to the subnet being investigated. Check the current subnet")
        end
        1:maximum(bft) ⊆ sn && return  # The graph is proved to be connected
    end
    error("here shouldn't be reached")
end; function bft_2_A(bft)
    B, N = size(bft, 1), maximum(bft)
    return SparseArrays.sparse([Vector(1:B); Vector(1:B)], vec(bft), [-ones(Int, B); ones(Int, B)], B, N)
end; function adjnode(n)
    nv = Int[]
    for ci in findall(x -> x == n, bft)
        b, c = ci.I # row(= branch), col
        on = bft[b, 3 - c]
        on ∉ nv && push!(nv, on)
    end
    sort(nv)
end; function get_b(n, m) # use this after `rectify_dir!(bft)` had been done
    n > m && ((n, m) = (m, n))
    for r in 1:size(bft, 1)
        (bft[r, 1] == n && bft[r, 2] == m) && return r
    end
    error("no such branch exist")
end;
G, G2N, Gcapinit, Gptcost = let
    G = length(RawDict["gen"])
    G2N = [RawDict["gen"]["$g"]["gen_bus"] for g in 1:G]
    cost = [RawDict["gen"]["$g"]["cost"] for g in 1:G]
    pmax = [RawDict["gen"]["$g"]["pmax"] for g in 1:G]
    gi = findall(x -> x > 1e-5, pmax)
    G2N, cost, pmax, G = G2N[gi], cost[gi], pmax[gi], length(gi)
    cost = [.001i[1] for i in cost]
    f = x -> round(x; digits = 6)
    G, G2N, pmax, f.(cost)
end;
D, D2N, Dref = let
    D = length(RawDict["load"])
    D2N = [RawDict["load"]["$d"]["load_bus"] for d in 1:D]
    Dref  = [RawDict["load"]["$d"]["pd"]       for d in 1:D]
    D, D2N, Dref
end;
T, Dura, Dmat = let
    Random.seed!(1);    Dlarge = rand(1.1:7e-13:1.7, D) .* Dref
    Random.seed!(6);    Dmiddle = rand(.7:7e-13:1.7, D) .* Dref
    Dmat = [Dref Dmiddle Dlarge]
    Dura = [   6,      3,     1]
    T = size(Dmat, 2)
    T, Dura, Dmat
end;
S, prob_vec, avai_vec, g_hyd, Ginvcost = let
    g_hyd = 17; Gptcost[g_hyd] = 0 # 💧 pg[g = 17] is a hydro generator
    Ginvcost = 2.0Gptcost;
    Ginvcost[g_hyd] = maximum(Gptcost);
    avai_vec = [.3, .79] # availability factor for 💧 generator
    prob_vec = [.5, .5] # The associated probability vector
    S = length(prob_vec)
    S, prob_vec, avai_vec, g_hyd, Ginvcost
end;

# feasible cost = 959.7425433749277
# SP solution cost = 904.3816383661987
# NA-relaxed cost = 884.6469843243801

B, N = length.([RawDict["branch"], RawDict["bus"]]) # (186, 118), fixed for this network
bft = let # load a rectified bft
    bft = [RawDict["branch"]["$r"][c == 1 ? "f_bus" : "t_bus"] for r in 1:B, c in 1:2]; # raw bft
    assert_is_connected(bft);
    rectify_dir!(bft);
    bft
end; A = bft_2_A(bft);

import JuMP, Gurobi; GRB_ENV = Gurobi.Env();

function bpf(b, Dvec, pgvec, pfvec) # dispatch function of power flow on branch b
    b == 9 && return -pgvec[findfirst(x -> x == 10, G2N)::Int] # b8[9] = -3
    b == 7 && return bpf(9, Dvec, pgvec, pfvec) # b8[7] = -1
    b == 94 && return bpf(93, Dvec, pgvec, pfvec) # b8[94] = -1
    b == 127 && return -bpf(126, Dvec, pgvec, pfvec) # b8[127] = -1
    b == 134 && return -pgvec[findfirst(x -> x == 87, G2N)::Int] # b8[134] = -3
    b == 176 && return -pgvec[findfirst(x -> x == 111, G2N)::Int] # b8[176] = -3
    b == 184 && return Dvec[findfirst(x -> x == 117, D2N)::Int] # b8[184] = -2
    dup_bvec = [67, 76, 99, 86, 124, 139, 142]
    b ∈ dup_bvec && return false # b8[dup_bvec] .= 0; b8[dup_bvec .- 1] .= 2
    return pfvec[(f = findfirst(x -> x == b, F2B);)] # [Fallback] the corresponding JuMP.@variable
end
n8 = let # elements being
    # [1]: a bus with load only, and whose degree is larger than 2
    # [2]: a bus with gen only, and whose degree is larger than 2
    # [3]: a bus with both power injection and load
    # [-1]: a bus with load only, and whose degree is 1, we won't add KCL constr here
    # [-3]: a bus with gen only, and whose degree is 1, we won't add KCL constr here
    # [-2]: a bare bus whose degree is 2, we won't add KCL constr here
    # [0]: a bare joint bus
    n8 = zeros(Int8, N); n8[D2N] .+= 1; n8[G2N] .+= 2
    n8[117] = -1
    n8[[10, 87, 111]] .= -3
    n8[[9, 63, 81]] .= -2
    n8
end; b8 = let # Note this procedure is specific to case118, therefore executing once is sufficient
    # elements being
    # [2]: this line represents a 2-parallel branch, works as a normal line
    # [1]: a normal line
    # [0]: this line was banned (pf ≡ 0) due to parallel redundancy, we won't allocate pf variable here
    # [-1]: power flow of this line hinges on one of its adjacent line, we won't allocate pf variable here
    # [-2]: power flow of this line hinges on the corresponding pure load, we won't allocate pf variable here
    # [-3]: power flow of this line hinges on the corresponding pure gen, we won't allocate pf variable here
    b8 = ones(Int8, size(bft, 1))
    b8[9] = -3
    b8[7] = -1
    b8[94] = -1
    b8[127] = -1
    b8[134] = -3
    b8[176] = -3
    b8[184] = -2
    dup_bvec = [67, 76, 99, 86, 124, 139, 142]
    b8[dup_bvec] .= 0; b8[dup_bvec .- 1] .= 2
    b8
end; 
F, F2B, fra = let
    F2B = findall(s -> s ∈ 1:2, b8); F = length(F2B);
    fra = [RawDict["branch"]["$b"]["rate_a"] for b in F2B]; # f_rate_a is a used part of b_rate_a, under Dref, if fra *= .59, then INFEASIBLE
    F, F2B, fra
end;

min_s = JuMP.Model(() -> Gurobi.Optimizer(GRB_ENV)); JuMP.set_silent(min_s);
JuMP.@variable(min_s, 0 ≤ invg_s[1:G] ≤ 300); # ⚠️ set an artificial ub here
JuMP.@variable(min_s, 0 ≤ pg_s[1:G, 1:T]);
JuMP.@expression(min_s, power_max, invg_s + Gcapinit)
JuMP.@constraint(min_s, [g = 1:G, t = 1:T], pg_s[g, t] ≤ power_max[g]);
JuMP.@variable(min_s, -fra[f] ≤ pf_s[f = 1:F, t = 1:T] ≤ fra[f]);
for t in 1:T, nState in 0:3, n in findall(ø -> ø == nState, n8) # real-time everynode KCL
    p_gen    = (nState < 2 ? false : pg_s[findfirst(x -> x == n, G2N)::Int, t])
    p_demand = (nState == 1 || nState == 3 ? Dmat[findfirst(x -> x == n, D2N)::Int, t] : false)
    bv, sgv = SparseArrays.findnz(view(A, :, n))
    f = b -> bpf(b, view(Dmat, :, t), view(pg_s, :, t), view(pf_s, :, t))
    JuMP.@constraint(min_s, sgv'f.(bv) + p_gen == p_demand)
end;
JuMP.@expression(min_s, hyd_energy, Dura'view(pg_s, g_hyd, :))
JuMP.@expression(min_s, hyd_energy_max, power_max[g_hyd]sum(Dura))
JuMP.@expression(min_s, cost_vec_s, [Ginvcost'invg_s, (Gptcost'pg_s)Dura])
constr_s = JuMP.@constraint(min_s, 0 == 1); # initial dumb
function solve_min_s(min_s, constr_s, β_s, avai_vec_s, prob_vec_s)
    JuMP.delete(min_s, constr_s)
    constr_s = JuMP.@constraint(min_s, min_s[:hyd_energy] ≤ min_s[:hyd_energy_max]avai_vec_s)
    invg_s, cost_vec_s = min_s[:invg_s], min_s[:cost_vec_s]
    bias_s = JuMP.@expression(min_s, sum(cost_vec_s)prob_vec_s)
    JuMP.@objective(min_s, Min, β_s'invg_s + bias_s) # ✅ θs-β cut
    JuMP.optimize!(min_s); JuMP.assert_is_solved_and_feasible(min_s; allow_local = false)
    slope_s, bias_s = JuMP.value.(invg_s), JuMP.value(bias_s)
    return constr_s, JuMP.objective_value(min_s), slope_s, bias_s
end

outer = JuMP.Model(() -> Gurobi.Optimizer(GRB_ENV)); JuMP.set_silent(outer); # Linear Programming
JuMP.@variable(outer, outer_β[1:G, 1:S]); JuMP.@constraint(outer, sum(outer_β; dims = 2) .== 0);
JuMP.@variable(outer, outer_O ≤ 1e3); # ⚠️ this ub needs to be valid
JuMP.@variable(outer, outer_θ[1:S]);
JuMP.@constraint(outer, outer_O == sum(outer_θ))
good_objc = JuMP.@constraint(outer, 0 ≤ 1) # initial dumb
function solve_outer_1(outer, good_objc)
    JuMP.delete(outer, good_objc)
    JuMP.@objective(outer, Max, outer[:outer_O])
    JuMP.optimize!(outer); JuMP.assert_is_solved_and_feasible(outer; allow_local = false)
    return dual_upper_bound = JuMP.objective_bound(outer)
end;
function solve_outer_2(outer, β, ub, lb)
    good_objc = JuMP.@constraint(outer, .7ub + .3lb ≤ outer[:outer_O])
    outer_β = outer[:outer_β]
    Δ = JuMP.@expression(outer, outer_β - β)
    JuMP.@objective(outer, Min, sum(x -> (x)x, Δ))
    JuMP.optimize!(outer); JuMP.assert_is_solved_and_feasible(outer; allow_local = false)
    vio = JuMP.MOI.get(JuMP.backend(outer), Gurobi.ModelAttribute("MaxVio"))
    vio > 9e-7 && @error "outer_QP: vio = $vio"
    return good_objc, JuMP.value.(outer_β)
end;

β = zeros(G, S); # start from zero
lbv = similar(prob_vec);
for ite in 1:8
    global good_objc
    for s in 1:S
        global constr_s
        constr_s, lbv[s], slope_s, bias_s = solve_min_s(min_s, constr_s, view(β, :, s), avai_vec[s], prob_vec[s])
        JuMP.@constraint(outer, outer_θ[s] ≤ bias_s + slope_s'view(outer_β, :, s))
    end; lb = sum(lbv)
    ub = solve_outer_1(outer, good_objc)
    @info "ite = $ite, (P) lb = $lb | $ub = ub (D)"
    good_objc, β = solve_outer_2(outer, β, ub, lb)
end

global good_objc # ite == 9
for s in 1:S
    global constr_s
    constr_s, lbv[s], slope_s, bias_s = solve_min_s(min_s, constr_s, view(β, :, s), avai_vec[s], prob_vec[s])
    JuMP.@constraint(outer, outer_θ[s] ≤ bias_s + slope_s'view(outer_β, :, s))
end; lb = sum(lbv)
ub = solve_outer_1(outer, good_objc)
@info "(P) lb = $lb | $ub = ub (D)"
good_objc = JuMP.@constraint(outer, .7ub + .3lb ≤ outer[:outer_O])
outer_β = outer[:outer_β]
Δ = JuMP.@expression(outer, outer_β - β)
JuMP.@objective(outer, Min, sum(x -> (x)x, Δ))
JuMP.unset_silent(outer)
JuMP.optimize!(outer)
JuMP.solution_summary(outer)
vio = JuMP.MOI.get(JuMP.backend(outer), Gurobi.ModelAttribute("MaxVio"))
Gurobi.GRBwrite(JuMP.unsafe_backend(outer), "outer.mps")

When I run your code I get:

julia> JuMP.optimize!(outer)

julia> JuMP.solution_summary(outer)
solution_summary(; result = 1, verbose = false)
├ solver_name          : Gurobi
├ Termination
│ ├ termination_status : NUMERICAL_ERROR
│ ├ result_count       : 0
│ ├ raw_status         : Optimization was terminated due to unrecoverable numerical difficulties.
│ └ objective_bound    : -2.51388e+01
├ Solution (result = 1)
│ ├ primal_status        : NO_SOLUTION
│ └ dual_status          : NO_SOLUTION
└ Work counters
  ├ solve_time (sec)   : 1.10602e-03
  ├ simplex_iterations : 0
  ├ barrier_iterations : 48
  └ node_count         : 0

Can you turn logging on and provide the output?

Can you also provide the MPS file using these instructions: GitHub - jump-dev/Gurobi.jl: A Julia interface to the Gurobi Optimizer

I’ve revised my post.
And if the MPS file is a text one, I copy-paste its content here.

outer.mps
* Signature: 0xd23e3c025a2e18cb
NAME 
ROWS
 N  OBJ
 E  R0      
 E  R1      
 E  R2      
 E  R3      
 E  R4      
 E  R5      
 E  R6      
 E  R7      
 E  R8      
 E  R9      
 E  R10     
 E  R11     
 E  R12     
 E  R13     
 E  R14     
 E  R15     
 E  R16     
 E  R17     
 E  R18     
 E  R19     
 L  R20     
 L  R21     
 L  R23     
 L  R24     
 L  R26     
 L  R27     
 L  R29     
 L  R30     
 L  R32     
 L  R33     
 L  R35     
 L  R36     
 L  R38     
 L  R39     
 L  R41     
 L  R42     
 L  R44     
 L  R45     
 L  R46     
COLUMNS
    outer_β[1,1]  OBJ       -8.6003572601445053e-18
    outer_β[1,1]  R0        1
    outer_β[1,1]  R20       -1.2487815708109435e+00
    outer_β[1,1]  R23       -1.2487815708109435e+00
    outer_β[1,1]  R26       -1.2487815708109435e+00
    outer_β[1,1]  R29       -1.2487815708109435e+00
    outer_β[1,1]  R32       -1.2487815708109435e+00
    outer_β[1,1]  R35       -1.2487815708109435e+00
    outer_β[1,1]  R38       -1.2487815708109435e+00
    outer_β[1,1]  R41       -1.2487815708109435e+00
    outer_β[1,1]  R44       -1.2487815708109435e+00
    outer_β[2,1]  R1        1
    outer_β[3,1]  R2        1
    outer_β[4,1]  R3        1
    outer_β[5,1]  R4        1
    outer_β[6,1]  R5        1
    outer_β[7,1]  OBJ       -1.6144551946487720e-01
    outer_β[7,1]  R6        1
    outer_β[7,1]  R20       -6.2192242822179207e+00
    outer_β[7,1]  R23       -5.9600000000000009e+00
    outer_β[7,1]  R26       -5.9600000000000009e+00
    outer_β[7,1]  R29       -5.9599999999999991e+00
    outer_β[7,1]  R32       -5.9600000000000009e+00
    outer_β[7,1]  R35       -6.1800000000000015e+00
    outer_β[7,1]  R38       -6.1800000000000015e+00
    outer_β[7,1]  R41       -6.1800000000000015e+00
    outer_β[7,1]  R44       -6.1800000000000015e+00
    outer_β[8,1]  R7        1
    outer_β[9,1]  R8        1
    outer_β[10,1]  OBJ       -2.0963800747519235e-16
    outer_β[10,1]  R9        1
    outer_β[10,1]  R20       -1.4379999999999999e+01
    outer_β[10,1]  R23       -1.4379999999999999e+01
    outer_β[10,1]  R26       -1.4379999999999999e+01
    outer_β[10,1]  R29       -1.4379999999999999e+01
    outer_β[10,1]  R32       -1.4379999999999999e+01
    outer_β[10,1]  R35       -1.4379999999999999e+01
    outer_β[10,1]  R38       -1.4379999999999999e+01
    outer_β[10,1]  R41       -1.4379999999999999e+01
    outer_β[10,1]  R44       -1.4379999999999999e+01
    outer_β[11,1]  R10       1
    outer_β[12,1]  R11       1
    outer_β[13,1]  R12       1
    outer_β[14,1]  R13       1
    outer_β[15,1]  R14       1
    outer_β[16,1]  R15       1
    outer_β[17,1]  OBJ       5.0888147531855576e+00
    outer_β[17,1]  R16       1
    outer_β[17,1]  R23       -300
    outer_β[17,1]  R26       -300
    outer_β[17,1]  R29       -3.1633971389895873e+01
    outer_β[17,1]  R32       -3.1633971389895873e+01
    outer_β[17,1]  R35       -3.1193971389895875e+01
    outer_β[17,1]  R38       -3.1193971389895875e+01
    outer_β[17,1]  R41       -3.1193971389895875e+01
    outer_β[17,1]  R44       -3.1193971389895875e+01
    outer_β[18,1]  R17       1
    outer_β[19,1]  R18       1
    outer_β[1,2]  OBJ       8.6003587510138856e-18
    outer_β[1,2]  R0        1
    outer_β[1,2]  R21       -1.2487815708109435e+00
    outer_β[1,2]  R24       -1.2487815708109435e+00
    outer_β[1,2]  R27       -1.2487815708109435e+00
    outer_β[1,2]  R30       -1.2487815708109435e+00
    outer_β[1,2]  R33       -1.2487815708109435e+00
    outer_β[1,2]  R36       -1.2487815708109435e+00
    outer_β[1,2]  R39       -1.2487815708109435e+00
    outer_β[1,2]  R42       -1.2487815708109435e+00
    outer_β[1,2]  R45       -1.2487815708109435e+00
    outer_β[2,2]  R1        1
    outer_β[3,2]  R2        1
    outer_β[4,2]  R3        1
    outer_β[5,2]  R4        1
    outer_β[6,2]  R5        1
    outer_β[7,2]  OBJ       1.6144551946487720e-01
    outer_β[7,2]  R6        1
    outer_β[7,2]  R21       -5.9600000000000009e+00
    outer_β[7,2]  R24       -8.4713702725457214e+00
    outer_β[7,2]  R27       -8.4713702725457214e+00
    outer_β[7,2]  R30       -8.47137027254572
    outer_β[7,2]  R33       -8.27137027254572
    outer_β[7,2]  R36       -8.27137027254572
    outer_β[7,2]  R39       -6.1800000000000015e+00
    outer_β[7,2]  R42       -6.1800000000000015e+00
    outer_β[7,2]  R45       -6.1800000000000015e+00
    outer_β[8,2]  R7        1
    outer_β[9,2]  R8        1
    outer_β[10,2]  OBJ       2.0963797633358350e-16
    outer_β[10,2]  R9        1
    outer_β[10,2]  R21       -1.4379999999999999e+01
    outer_β[10,2]  R24       -1.4379999999999999e+01
    outer_β[10,2]  R27       -1.4379999999999999e+01
    outer_β[10,2]  R30       -1.4379999999999999e+01
    outer_β[10,2]  R33       -1.4379999999999999e+01
    outer_β[10,2]  R36       -1.4379999999999999e+01
    outer_β[10,2]  R39       -1.4379999999999999e+01
    outer_β[10,2]  R42       -1.4379999999999999e+01
    outer_β[10,2]  R45       -1.4379999999999999e+01
    outer_β[11,2]  R10       1
    outer_β[12,2]  R11       1
    outer_β[13,2]  R12       1
    outer_β[14,2]  R13       1
    outer_β[15,2]  R14       1
    outer_β[16,2]  R15       1
    outer_β[17,2]  OBJ       -5.0888147531855576e+00
    outer_β[17,2]  R16       1
    outer_β[17,2]  R21       -7.9626473632515964e+00
    outer_β[17,2]  R36       -7.7955587556566606e+00
    outer_β[17,2]  R39       -7.7955587556566606e+00
    outer_β[17,2]  R42       -7.7955587556566606e+00
    outer_β[17,2]  R45       -7.7955587556566606e+00
    outer_β[18,2]  R17       1
    outer_β[19,2]  R18       1
    outer_O   R19       1
    outer_O   R46       -1
    outer_θ[1]  R19       -1
    outer_θ[1]  R20       1
    outer_θ[1]  R23       1
    outer_θ[1]  R26       1
    outer_θ[1]  R29       1
    outer_θ[1]  R32       1
    outer_θ[1]  R35       1
    outer_θ[1]  R38       1
    outer_θ[1]  R41       1
    outer_θ[1]  R44       1
    outer_θ[2]  R19       -1
    outer_θ[2]  R21       1
    outer_θ[2]  R24       1
    outer_θ[2]  R27       1
    outer_θ[2]  R30       1
    outer_θ[2]  R33       1
    outer_θ[2]  R36       1
    outer_θ[2]  R39       1
    outer_θ[2]  R42       1
    outer_θ[2]  R45       1
RHS
    RHS1      OBJ       -1.2961050123997136e+01
    RHS1      R20       4.7570906242305813e+02
    RHS1      R21       4.0893792190132206e+02
    RHS1      R23       2.2280613703891331e+03
    RHS1      R24       4.3645454531102763e+02
    RHS1      R26       2.2280613703891331e+03
    RHS1      R27       4.3645454531102763e+02
    RHS1      R29       5.5638844562656345e+02
    RHS1      R30       4.3645454531102757e+02
    RHS1      R32       5.5638844562656345e+02
    RHS1      R33       4.3642879691102763e+02
    RHS1      R35       5.5476612987656347e+02
    RHS1      R36       4.0918669064075419e+02
    RHS1      R38       5.5476612987656347e+02
    RHS1      R39       4.0901559250170180e+02
    RHS1      R41       5.5476612987656347e+02
    RHS1      R42       4.0901559250170180e+02
    RHS1      R44       5.5476612987656347e+02
    RHS1      R45       4.0901559250170180e+02
    RHS1      R46       -9.0441271353580225e+02
BOUNDS
 FR BND1      outer_β[1,1]
 FR BND1      outer_β[2,1]
 FR BND1      outer_β[3,1]
 FR BND1      outer_β[4,1]
 FR BND1      outer_β[5,1]
 FR BND1      outer_β[6,1]
 FR BND1      outer_β[7,1]
 FR BND1      outer_β[8,1]
 FR BND1      outer_β[9,1]
 FR BND1      outer_β[10,1]
 FR BND1      outer_β[11,1]
 FR BND1      outer_β[12,1]
 FR BND1      outer_β[13,1]
 FR BND1      outer_β[14,1]
 FR BND1      outer_β[15,1]
 FR BND1      outer_β[16,1]
 FR BND1      outer_β[17,1]
 FR BND1      outer_β[18,1]
 FR BND1      outer_β[19,1]
 FR BND1      outer_β[1,2]
 FR BND1      outer_β[2,2]
 FR BND1      outer_β[3,2]
 FR BND1      outer_β[4,2]
 FR BND1      outer_β[5,2]
 FR BND1      outer_β[6,2]
 FR BND1      outer_β[7,2]
 FR BND1      outer_β[8,2]
 FR BND1      outer_β[9,2]
 FR BND1      outer_β[10,2]
 FR BND1      outer_β[11,2]
 FR BND1      outer_β[12,2]
 FR BND1      outer_β[13,2]
 FR BND1      outer_β[14,2]
 FR BND1      outer_β[15,2]
 FR BND1      outer_β[16,2]
 FR BND1      outer_β[17,2]
 FR BND1      outer_β[18,2]
 FR BND1      outer_β[19,2]
 MI BND1      outer_O 
 UP BND1      outer_O   1000
 FR BND1      outer_θ[1]
 FR BND1      outer_θ[2]
QUADOBJ
    outer_β[1,1]  outer_β[1,1]  2
    outer_β[2,1]  outer_β[2,1]  2
    outer_β[3,1]  outer_β[3,1]  2
    outer_β[4,1]  outer_β[4,1]  2
    outer_β[5,1]  outer_β[5,1]  2
    outer_β[6,1]  outer_β[6,1]  2
    outer_β[7,1]  outer_β[7,1]  2
    outer_β[8,1]  outer_β[8,1]  2
    outer_β[9,1]  outer_β[9,1]  2
    outer_β[10,1]  outer_β[10,1]  2
    outer_β[11,1]  outer_β[11,1]  2
    outer_β[12,1]  outer_β[12,1]  2
    outer_β[13,1]  outer_β[13,1]  2
    outer_β[14,1]  outer_β[14,1]  2
    outer_β[15,1]  outer_β[15,1]  2
    outer_β[16,1]  outer_β[16,1]  2
    outer_β[17,1]  outer_β[17,1]  2
    outer_β[18,1]  outer_β[18,1]  2
    outer_β[19,1]  outer_β[19,1]  2
    outer_β[1,2]  outer_β[1,2]  2
    outer_β[2,2]  outer_β[2,2]  2
    outer_β[3,2]  outer_β[3,2]  2
    outer_β[4,2]  outer_β[4,2]  2
    outer_β[5,2]  outer_β[5,2]  2
    outer_β[6,2]  outer_β[6,2]  2
    outer_β[7,2]  outer_β[7,2]  2
    outer_β[8,2]  outer_β[8,2]  2
    outer_β[9,2]  outer_β[9,2]  2
    outer_β[10,2]  outer_β[10,2]  2
    outer_β[11,2]  outer_β[11,2]  2
    outer_β[12,2]  outer_β[12,2]  2
    outer_β[13,2]  outer_β[13,2]  2
    outer_β[14,2]  outer_β[14,2]  2
    outer_β[15,2]  outer_β[15,2]  2
    outer_β[16,2]  outer_β[16,2]  2
    outer_β[17,2]  outer_β[17,2]  2
    outer_β[18,2]  outer_β[18,2]  2
    outer_β[19,2]  outer_β[19,2]  2
ENDATA

Something is really going wrong with your solve. Can you try this?

julia> import Gurobi

julia> env = Gurobi.Env()
Set parameter LicenseID to value 890341
Gurobi.Env(Ptr{Nothing} @0x00007fed1f8c9a00, false, 0)

julia> pModel = Ref{Ptr{Cvoid}}(C_NULL)
Base.RefValue{Ptr{Nothing}}(Ptr{Nothing} @0x0000000000000000)

julia> Gurobi.GRBreadmodel(env, "/tmp/bug.mps", pModel)
Read MPS format model from file /tmp/bug.mps
Reading time = 0.00 seconds
: 39 rows, 41 columns, 127 nonzeros
0

julia> Gurobi.GRBoptimize(pModel[])
Gurobi Optimizer version 12.0.2 build v12.0.2rc0 (mac64[x86] - Darwin 24.1.0 24B83)

CPU model: Intel(R) Core(TM) i5-8259U CPU @ 2.30GHz
Thread count: 4 physical cores, 8 logical processors, using up to 8 threads

Optimize a model with 39 rows, 41 columns and 127 nonzeros
Model fingerprint: 0x6abf7598
Model has 38 quadratic objective terms
Coefficient statistics:
  Matrix range     [1e+00, 3e+02]
  Objective range  [9e-18, 5e+00]
  QObjective range [2e+00, 2e+00]
  Bounds range     [1e+03, 1e+03]
  RHS range        [4e+02, 2e+03]
Presolve removed 11 rows and 2 columns
Presolve time: 0.00s
Presolved: 28 rows, 39 columns, 80 nonzeros
Presolved model has 38 quadratic objective terms
Ordering time: 0.00s

Barrier statistics:
 Free vars  : 39
 AA' NZ     : 6.900e+01
 Factor NZ  : 1.060e+02
 Factor Ops : 8.340e+02 (less than 1 second per iteration)
 Threads    : 1

                  Objective                Residual
Iter       Primal          Dual         Primal    Dual     Compl     Time
   0   1.29618707e+01  1.29610499e+01  4.29e+02 1.02e+01  1.08e+06     0s
   1   7.79452936e+03 -9.29391938e+03  4.59e+01 4.59e-01  8.38e+04     0s
   2   2.34734323e+03 -3.67334728e+03  3.99e+00 4.00e-02  1.54e+04     0s
   3   9.12137558e+01 -8.83286873e+02  5.15e-01 5.25e-03  3.29e+03     0s
   4   2.66252969e+00 -3.68221392e+02  7.01e-02 5.97e-04  6.12e+02     0s
   5   6.33755441e+00 -1.41663694e+02  2.80e-01 3.33e-04  3.77e+02     0s
   6   1.04371742e+01 -7.60311350e+01  2.47e+00 2.22e-04  1.82e+02     0s
   7   1.19817416e+00 -1.80998379e+01  4.38e-01 3.88e-05  3.69e+01     0s
   8   1.11611446e-01 -1.91537672e+00  6.22e-15 1.84e-07  3.71e+00     0s
   9   8.92532211e-03 -4.57382751e-02  1.74e-15 1.56e-09  9.74e-02     0s
  10   7.97637457e-04 -6.28498269e-03  8.50e-17 5.34e-14  1.26e-02     0s
  11   2.13483370e-04 -6.80122803e-05  5.38e-18 1.78e-15  5.00e-04     0s
  12   1.34514789e-04  8.17619151e-05  1.83e-19 1.78e-15  9.38e-05     0s
  13   1.02313735e-04  9.58889782e-05  1.01e-20 1.78e-15  1.14e-05     0s
  14   1.04812007e-04  9.91673318e-05  1.93e-21 1.78e-15  1.00e-05     0s
  15   1.00771461e-04  1.00764000e-04  3.37e-21 1.78e-15  1.33e-08     0s
  16   1.00766770e-04  1.00766763e-04  1.99e-21 1.78e-15  1.33e-11     0s

Barrier solved model in 16 iterations and 0.00 seconds (0.00 work units)
Optimal objective 1.00766770e-04

0
julia> env = Gurobi.Env()
Set parameter Username
Set parameter LicenseID to value 2663413
Academic license - for non-commercial use only - expires 2026-05-11
Gurobi.Env(Ptr{Nothing} @0x0000019a16b17ff0, false, 0)

julia> pModel = Ref{Ptr{Cvoid}}(C_NULL)
Base.RefValue{Ptr{Nothing}}(Ptr{Nothing} @0x0000000000000000)

julia> Gurobi.GRBreadmodel(env, "data/outer.mps", pModel)
Read MPS format model from file data/outer.mps
Reading time = 0.04 seconds
: 39 rows, 41 columns, 127 nonzeros
0

julia> Gurobi.GRBoptimize(pModel[])
Gurobi Optimizer version 12.0.2 build v12.0.2rc0 (win64 - Windows 11.0 (26100.2))

CPU model: 11th Gen Intel(R) Core(TM) i5-1135G7 @ 2.40GHz, instruction set [SSE2|AVX|AVX2|AVX512]
Thread count: 4 physical cores, 8 logical processors, using up to 8 threads

Optimize a model with 39 rows, 41 columns and 127 nonzeros
Model fingerprint: 0x6abf7598
Model has 38 quadratic objective terms
Coefficient statistics:
  Matrix range     [1e+00, 3e+02]
  Objective range  [9e-18, 5e+00]
  QObjective range [2e+00, 2e+00]
  Bounds range     [1e+03, 1e+03]
  RHS range        [4e+02, 2e+03]
Presolve removed 11 rows and 2 columns
Presolve time: 0.00s
Presolved: 28 rows, 39 columns, 80 nonzeros
Presolved model has 38 quadratic objective terms
Ordering time: 0.00s

Barrier statistics:
 Free vars  : 39
 AA' NZ     : 6.900e+01
 Factor NZ  : 1.060e+02
 Factor Ops : 8.340e+02 (less than 1 second per iteration)
 Threads    : 1

                  Objective                Residual
Iter       Primal          Dual         Primal    Dual     Compl     Time
   0   1.29618707e+01  1.29610499e+01  4.29e+02 1.02e+01  1.08e+06     0s
   1   7.79452936e+03 -9.29391938e+03  4.59e+01 4.59e-01  8.38e+04     0s
   2   2.34735483e+03 -3.67336088e+03  3.99e+00 4.00e-02  1.54e+04     0s
   3   9.28074635e+01 -8.89553051e+02  5.28e-01 5.42e-03  3.32e+03     0s
   4   2.67970847e+00 -2.69217523e+02  6.93e-02 1.81e-04  4.56e+02     0s
   5   4.93346415e+00 -2.33632458e+02  6.85e-02 1.78e-04  4.42e+02     0s
   6   2.61337406e+00 -2.70695461e+02  3.15e-01 1.50e-04  4.63e+02     0s
   7   1.09725171e+01 -1.34247786e+02  5.05e-01 1.37e-04  3.67e+02     0s
   8   6.15979734e+00 -7.90459574e+01  2.02e-01 6.79e-05  1.87e+02     0s
   9   2.03422156e-02 -8.54163247e+01  1.06e-01 4.22e-05  1.57e+02     0s
  10   6.28528487e-01 -3.31827999e+01  2.99e-02 4.33e-11  3.35e+01     0s
  11   1.33921978e-03 -2.70800877e+01  4.09e-13 2.12e-12  1.23e+00     0s
  12   2.55952604e-04 -3.06308511e+01  1.99e-13 2.79e-12  1.10e-02     0s
  13   3.33498799e-04 -3.42217263e+01  3.09e-13 2.70e-12  1.19e-04     0s
  14   5.57483325e-04 -3.51843270e+01  9.52e-14 1.82e-12  4.34e-07     0s
  15   2.39292335e-04 -3.74834526e+01  2.37e-13 1.82e-12  2.28e-09     0s
  16   2.43578332e-04 -3.89336401e+01  1.87e-13 2.41e-12  1.43e-10     0s
  17   2.11498031e-03 -5.43399078e+01  4.19e-02 3.42e-12  7.46e-11     0s
  18   2.96317192e-04 -5.53941734e+01  1.40e-13 5.61e-12  2.39e-12     0s
  19   4.12094691e-04 -5.67513066e+01  5.10e-14 4.84e-12  3.94e-13     0s
  20   6.75682559e-04 -5.83702637e+01  1.42e-13 1.61e-12  4.12e-16     0s
  21   8.90546712e-04 -5.95038795e+01  2.26e-13 3.58e-12  1.14e-16     0s
  22   9.87057970e-04 -6.26382723e+01  1.59e-13 3.55e-12  2.54e-19     0s

Barrier solved model in 22 iterations and 0.01 seconds (0.00 work units)
Optimal objective 9.87057970e-04

0

julia> 

This looks like a bug. I’ll raise it with their support

1 Like

Thanks.

so the conclusion is: the model is itself numeric difficult,. In mac os gurobi gives an honest termination, whereas in win11 gurobi gives a dishonest OPTIMAL?

It’s a likely bug. I don’t have Windows, so I can’t investigate more. I’ve opened a support ticket and they are investigating.

1 Like