Skip to content

Invalid MOI TerminationStatus in v26.02 #31

@mtanneau

Description

@mtanneau

In a nutshell

While trying to wrap the latest cuOpt release (v26.02), I encountered an issue with the test_air05 test.
This test passes with cuopt 25.12, but errors with v26.02 (see details below).

[EDIT]: this is likely coming from an upstream bug being triggered in this specific instance. Upstream issue opened.

Root cause

cuOpt --> MOI status code conversion

I tracked the issue to the MOI-level retrieval of the TerminationStatusCode here:

cuOpt.jl/src/MOI_wrapper.jl

Lines 288 to 290 in aa06a3a

function MOI.get(model::Optimizer, ::MOI.TerminationStatus)
return _TerminationStatusMap[model.termination_status][1]
end

and more specifically here:

cuOpt.jl/src/MOI_wrapper.jl

Lines 284 to 285 in aa06a3a

CUOPT_TERIMINATION_STATUS_FEASIBLE_FOUND =>
(MOI.OTHER_LIMIT, "cuOptModelStatusFeasible"),

Note that:

  • cuOpt's return status FEASIBLE_FOUND gets mapped to MOI.OtherLimit
  • (see solver log below) cuOpt returns a lower bound (2.6383013888888891e+04) which is higher than the optimal value (2.6373999999999985e+04)

cuOpt termination status

I also noted the following difference at the cuOpt level:

  • in v25.12, after the call to MOI.optimize!(model), model.termination_status evaluates to 1
  • in v26.02, after the call to MOI.optimize!(model), model.termination_status evaluates to 8

Steps to reproduce (v26.02)

  1. Make sure you have cuOpt v26.02 installed
  2. Run test_air05 test from the test suite
  3. You should obtain a log and stracktrace similar to the ones reproduced below:

cuOpt log (note that I activated logging by setting log_to_console to `true)

Setting parameter time_limit to 6.000000e+01
Setting parameter log_to_console to true
cuOpt version: 26.2.0, git hash: f73da24d, host arch: aarch64, device archs: 75-real,80-real,86-real,90a-real,100f-real,120a-real,120
CPU: Unknown, threads (physical/logical): 1/20, RAM: 105.63 GiB
CUDA 13.1, device: NVIDIA GB10 (ID 0), VRAM: 119.70 GiB
CUDA device UUID: 43bce240-d7d6-65e5-ca7a-e351bab5de7a

Solving a problem with 426 constraints, 7195 variables (7195 integers), and 52121 nonzeros
Problem scaling:
Objective coefficents range:          [4e+01, 3e+03]
Constraint matrix coefficients range: [1e+00, 1e+00]
Constraint rhs / bounds range:        [0e+00, 1e+00]
Variable bounds range:                [0e+00, 1e+00]

Original problem: 426 constraints, 7195 variables, 52121 nonzeros
Calling Papilo presolver (git hash 741a2b9c)
Presolve status: reduced the problem
Presolve removed: 90 constraints, 1116 variables, 16171 nonzeros
Presolved problem: 336 constraints, 6079 variables, 35950 nonzeros
Objective function is integral
Papilo presolve time: 0.42
Objective offset 9167.000000 scaling_factor 1.000000
Model fingerprint: 0x3257a27
Running presolve!
Unused variables detected, eliminating them! Unused var count 4
After cuOpt presolve: 335 constraints, 6075 variables, objective offset 9167.000000.
cuOpt presolve time: 4.49

Solving LP root relaxation in concurrent mode
Skipping column scaling
Dual Simplex Phase 1
Dual feasible solution found.
Dual Simplex Phase 2
 Iter     Objective           Num Inf.  Sum Inf.     Perturb  Time
    0 -8.1936000000000000e+04     277 2.31020000e+04 0.00e+00 5.26
    1 -8.0926000000000000e+04     266 1.48790000e+04 0.00e+00 5.26
 1000 -5.7784953825729302e+04     127 1.13852044e+02 0.00e+00 5.33


Root relaxation solution found in 1194 iterations and 0.10s by Dual Simplex
Root relaxation objective +2.58776093e+04


 | Explored | Unexplored |    Objective    |     Bound     | IntInf | Depth | Iter/Node |   Gap    |  Time  |
           0            0             +inf    +2.588508e+04      228      0   1.2e+03        -        5.58
Gomory    cuts : 1
MIR       cuts : 0
Knapsack  cuts : 0
Strong CG cuts : 0
Cut pool size  : 211
Size with cuts : 336 constraints, 6411 variables, 1250070204 nonzeros
Strong branching using 19 threads and 228 fractional variables
H                            +4.551700e+04    +2.588508e+04                                43.1%      5.93
Strong branching completed in 0.40s
Exploring the B&B tree using 19 threads

 | Explored | Unexplored |    Objective    |     Bound     | IntInf | Depth | Iter/Node |   Gap    |  Time  |
H                            +4.354400e+04    +2.591578e+04                                40.5%      7.76
B         31           27    +2.645600e+04    +2.591578e+04        0     19   1.3e+02       2.0%      7.76
D         49           43    +2.643900e+04    +2.591578e+04        0     21   1.2e+02       2.0%      7.86
D        166          108    +2.641400e+04    +2.604586e+04        0     33   9.8e+01       1.4%      8.14
B        226          134    +2.640200e+04    +2.612955e+04        0     15   9.4e+01       1.0%      8.37
D        322          140    +2.637500e+04    +2.622476e+04        0     15   9.5e+01       0.6%      8.60
B        380          120    +2.637400e+04    +2.628093e+04        0     18   9.2e+01       0.4%      8.71
         510           27    +2.637400e+04    +2.638301e+04      166      8   7.8e+01       0.0%      8.86
Explored 510 nodes in 8.86s.
Absolute Gap -9.013889e+00 Objective 2.6373999999999985e+04 Lower Bound 2.6383013888888891e+04
Optimal solution found.
Solution objective: 26374.000000 , relative_mip_gap 0.000342 solution_bound 26383.013889 presolve_time 4.913286 total_solve_time 9.023263 max constraint violation 0.000000 max int violation 0.000000 max var bounds violation 0.000000 nodes 510 simplex_iterations 39853

test failure stack trace:

test_air05: Test Failed at ~/github/cuOpt.jl/test/MOI_wrapper.jl:84
  Expression: MOI.get(model, MOI.TerminationStatus()) == MOI.OPTIMAL
   Evaluated: MathOptInterface.OTHER_LIMIT == MathOptInterface.OPTIMAL

Stacktrace:
 [1] macro expansion
   @ ~/.julia/juliaup/julia-1.12.4+0.aarch64.linux.gnu/share/julia/stdlib/v1.12/Test/src/Test.jl:680 [inlined]
 [2] test_air05()
   @ Main.TestMOIWrapper ~/github/cuOpt.jl/test/MOI_wrapper.jl:84
 [3] macro expansion
   @ ~/github/cuOpt.jl/test/MOI_wrapper.jl:27 [inlined]
 [4] macro expansion
   @ ~/.julia/juliaup/julia-1.12.4+0.aarch64.linux.gnu/share/julia/stdlib/v1.12/Test/src/Test.jl:1776 [inlined]
 [5] runtests()
   @ Main.TestMOIWrapper ~/github/cuOpt.jl/test/MOI_wrapper.jl:27

More specifically, model.termination_status evaluates to 8, which yields an MOI.OTHER_LIMIT termination status and the test fails.

Additional logs (v25.12)

For completeness, log from the same problem, using cuOpt 25.12

Setting parameter time_limit to 6.000000e+01
Setting parameter log_to_console to true
cuOpt version: 25.12.0, git hash: d97ff6b, host arch: aarch64, device archs: 75-real,80-real,86-real,90a-real,100f-real,120a-real,120
CPU: Unknown, threads (physical/logical): 1/20, RAM: 104.57 GiB
CUDA 13.0, device: NVIDIA GB10 (ID 0), VRAM: 119.70 GiB
CUDA device UUID: 43bce240-d7d6-65e5-ca7a-e351bab5de7a

Solving a problem with 426 constraints, 7195 variables (7195 integers), and 52121 nonzeros
Problem scaling:
Objective coefficents range:          [4e+01, 3e+03]
Constraint matrix coefficients range: [1e+00, 1e+00]
Constraint rhs / bounds range:        [0e+00, 1e+00]
Variable bounds range:                [0e+00, 1e+00]

Original problem: 426 constraints, 7195 variables, 52121 nonzeros
Calling Papilo presolver
Presolve status: reduced the problem
Presolve removed: 90 constraints, 1116 variables, 16171 nonzeros
Presolved problem: 336 constraints, 6079 variables, 35950 nonzeros
Objective function is integral
Papilo presolve time: 0.426261
Objective offset 9167.000000 scaling_factor 1.000000
Running presolve!
Unused variables detected, eliminating them! Unused var count 2
After trivial presolve: 335 constraints, 6077 variables, objective offset 9167.000000.
Using 19 CPU threads for B&B
Solving LP root relaxation
Scaling matrix. Maximum column norm 1.000000e+00, minimum column norm 8.965530e-02
Dual Simplex Phase 1
Dual feasible solution found.
Dual Simplex Phase 2
 Iter     Objective           Num Inf.  Sum Inf.     Perturb  Time
    1 -8.1100000000000000e+04     272 1.84122651e+02 0.00e+00 0.01


Root relaxation solution found in 990 iterations and 0.10s
Root relaxation objective +2.58776093e+04


Strong branching using 19 threads and 222 fractional variables
Exploring the B&B tree using 19 threads (best-first = 4, diving = 15)
 | Explored | Unexplored |    Objective    |     Bound     | Depth | Iter/Node |   Gap    |  Time  |
D        92           94    +2.637400e+04    +2.615126e+04        2   1.6e+02      0.8%      0.79
B       347          157    +2.637400e+04    +2.629885e+04       16   8.9e+01      0.3%      1.32
Explored 532 nodes in 1.52s.
Absolute Gap 2.634867e+00 Objective 2.6373999999999989e+04 Lower Bound 2.6371365133232783e+04
Optimal solution found within relative MIP gap tolerance (1.0e-04)
Post-solve status: succeeded
Solution objective: 26374.000000 , relative_mip_gap 0.000100 solution_bound 26371.365133 presolve_time 0.698856 total_solve_time 3.687329 max constraint violation 0.000000 max int violation 0.000000 max var bounds violation 0.000000 nodes 532 simplex_iterations 37434

In that version, model.termination_status is 1, which yields an MOI.OPTIMAL termination status and the test passes.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions