Skip to main content

Inquiry on Stopping Solver After Two Non-Positive Integer Solutions in YALMIP-Gurobi

Answered

Comments

10 comments

  • Riley Clement
    • Gurobi Staff

    Hi 祎泽 刘 ,

    causing the solver to miss solutions that should have been found

    This statement concerns me.  Can you explain in detail exactly what you mean by this?

    I would like to know if YALMIP supports callback functions for this purpose

    I don't think so.  For a more certain answer you could ask in the YALMIP forum: https://groups.google.com/g/yalmip

    I was thinking of using the following approach:

    This looks ok to me,  You may wish to consider turning off dual reductions, it may make it easier to find your two solutions.

    - Riley

    0
  • 祎泽 刘
    • Gurobi-versary
    • Conversationalist
    • First Question

    Hi Riley,

    Thanks a lot for the quick reply and for the tip about dual reductions.

    About the line “causing the solver to miss solutions”: what I mean is that, on the same model, I get different behavior depending on whether I set

    solver_options.gurobi.SolutionLimit = 2; 
    solver_options.gurobi.Cutoff = 0.000001; 

    When I don’t set those parameters, Gurobi typically finds integer incumbents with objective ≤ 0 (my “target” solutions). But when I do set them, I can’t get any integer incumbents with objective ≤ 0. The solver log excerpts below illustrate the difference.

    Gurobi Solver Log – With SolutionLimit and Cutoff

    Academic license - for non-commercial use only - expires 2026-09-16
    Gurobi Optimizer version 11.0.3 build v11.0.3rc0 (win64 - Windows 11+.0 (26100.2))
    
    CPU model: Intel(R) Core(TM) i9-14900HX, instruction set [SSE2|AVX|AVX2]
    Thread count: 24 physical cores, 32 logical processors, using up to 32 threads
    
    Optimize a model with 2757 rows, 1656 columns and 6813 nonzeros
    Model fingerprint: 0x7d726681
    Variable types: 1152 continuous, 504 integer (144 binary)
    Coefficient statistics:
      Matrix range     [1e-03, 4e+01]
      Objective range  [2e-01, 2e+01]
      Bounds range     [1e+00, 1e+00]
      RHS range        [1e+00, 6e+03]
    
    User MIP start did not produce a new incumbent solution
    User MIP start violates constraint R2365 by 36.009302271
    
    Presolve removed 1953 rows and 853 columns
    Presolve time: 0.01s
    Presolved: 804 rows, 803 columns, 3335 nonzeros
    Variable types: 343 continuous, 460 integer (72 binary)
    
    Root relaxation: objective -1.301388e+01, 776 iterations, 0.00 seconds (0.01 work units)
    
        Nodes    |    Current Node    |     Objective Bounds      |     Work
     Expl Unexpl |  Obj  Depth IntInf | Incumbent    BestBd   Gap | It/Node Time
    
         0     0  -13.01388    0  122          -  -13.01388      -     -    0s
    Another try with MIP start
         0     0   -9.89006    0   93          -   -9.89006      -     -    0s
         0     0   -9.88203    0   87          -   -9.88203      -     -    0s
         0     0   -9.88203    0   86          -   -9.88203      -     -    0s
         0     0   -9.80812    0   89          -   -9.80812      -     -    0s
         0     0   -9.80305    0   91          -   -9.80305      -     -    0s
         0     0   -9.65284    0  128          -   -9.65284      -     -    0s
         0     0   -9.62204    0  127          -   -9.62204      -     -    0s
         0     0   -9.59259    0  102          -   -9.59259      -     -    0s
         0     0   -9.59259    0   97          -   -9.59259      -     -    0s
         0     0   -9.57569    0   97          -   -9.57569      -     -    0s
         0     0   -9.56602    0   98          -   -9.56602      -     -    0s
         0     0   -9.56234    0   97          -   -9.56234      -     -    0s
         0     0   -9.55768    0   97          -   -9.55768      -     -    0s
         0     0   -9.55201    0   73          -   -9.55201      -     -    0s
         0     0   -9.55201    0   72          -   -9.55201      -     -    0s
         0     0   -9.55201    0   75          -   -9.55201      -     -    0s
         0     0   -9.55201    0   71          -   -9.55201      -     -    0s
         0     0   -9.55201    0   73          -   -9.55201      -     -    0s
         0     0   -9.55201    0   73          -   -9.55201      -     -    0s
         0     2   -9.55201    0   73          -   -9.55201      -     -    0s
     22156 19153   -1.30715  155   25          -   -9.27112      -   2.3    5s
     95790 70862   -3.51157  233   33          -   -9.17509      -   2.4   10s
     209828 170158   -4.87949   82   35          -   -9.12772      -   2.5   15s
     334951 279826   -0.54469  265   24          -   -9.04094      -   2.5   20s
     444414 372982   -2.47958  282   25          -   -8.99601      -   2.5   25s
     563478 474109   -2.81135  304   24          -   -8.95991      -   2.5   30s
     670746 565069   -8.58847   53   97          -   -8.94063      -   2.5   35s
     777944 660584   -4.60607  140   34          -   -8.91740      -   2.5   40s
     883412 749364   -1.67904  333   53          -   -8.90143      -   2.4   45s
     983447 837871   -3.70693  164   55          -   -8.88795      -   2.4   50s
     1052263 896510   -5.36147   65   67          -   -8.88588      -   2.4   56s
    
    Cutting planes:
      Gomory: 38
      MIR: 40
      StrongCG: 2
      Zero half: 9
      Mod-K: 22
    
    Explored 1128643 nodes (2746176 simplex iterations) in 60.12 seconds (14.16 work units)
    Thread count was 32 (of 32 available processors)
    
    Solution count 0
    
    Time limit reached
    Best objective -, best bound -8.875937370438e+00, gap -

    Gurobi Solver Log – Baseline (No Parameters Set)

    Academic license - for non-commercial use only - expires 2026-09-16
    Gurobi Optimizer version 11.0.3 build v11.0.3rc0 (win64 - Windows 11+.0 (26100.2))
    
    CPU model: Intel(R) Core(TM) i9-14900HX, instruction set [SSE2|AVX|AVX2]
    Thread count: 24 physical cores, 32 logical processors, using up to 32 threads
    
    Optimize a model with 2757 rows, 1656 columns and 6813 nonzeros
    Model fingerprint: 0xd5a500fd
    Variable types: 1152 continuous, 504 integer (144 binary)
    Coefficient statistics:
      Matrix range     [1e-03, 4e+01]
      Objective range  [2e-01, 2e+01]
      Bounds range     [1e+00, 1e+00]
      RHS range        [1e+00, 6e+03]
    Presolve removed 1953 rows and 853 columns
    Presolve time: 0.01s
    Presolved: 804 rows, 803 columns, 3335 nonzeros
    Variable types: 343 continuous, 460 integer (72 binary)
    
    Root relaxation: objective -1.301388e+01, 776 iterations, 0.00 seconds (0.01 work units)
    
        Nodes    |    Current Node    |     Objective Bounds      |     Work
     Expl Unexpl |  Obj  Depth IntInf | Incumbent    BestBd   Gap | It/Node Time
    
         0     0  -13.01388    0  122          -  -13.01388      -     -    0s
    H    0     0                      31.1484204  -13.01388   142%     -    0s
    H    0     0                      27.2739107  -13.01388   148%     -    0s
         0     0   -9.92752    0   78   27.27391   -9.92752   136%     -    0s
    H    0     0                      12.2106143   -9.91949   181%     -    0s
         0     0   -9.65567    0  141   12.21061   -9.65567   179%     -    0s
         0     0   -9.64420    0  137   12.21061   -9.64420   179%     -    0s
         0     0   -9.64420    0  138   12.21061   -9.64420   179%     -    0s
         0     0   -9.60478    0  144   12.21061   -9.60478   179%     -    0s
         0     0   -9.59938    0  120   12.21061   -9.59938   179%     -    0s
    H    0     0                      10.8648988   -9.59938   188%     -    0s
         0     0   -9.59832    0  122   10.86490   -9.59832   188%     -    0s
    H    0     0                       7.8661867   -9.59003   222%     -    0s
         0     0   -9.56100    0   89    7.86619   -9.56100   222%     -    0s
         0     0   -9.53548    0  123    7.86619   -9.53548   221%     -    0s
         0     0   -9.53371    0  130    7.86619   -9.53371   221%     -    0s
         0     0   -9.53334    0  128    7.86619   -9.53334   221%     -    0s
         0     2   -9.52802    0  128    7.86619   -9.52802   221%     -    0s
    H   46    56                       7.7274330   -9.52217   223%   3.8    0s
    H  223   248                       7.4831752   -9.51079   227%   2.5    2s
    H  227   248                       7.4053699   -9.51079   228%   2.6    2s
    H  228   248                       2.0806142   -9.51079   557%   2.6    2s
    H  240   248                       1.7854926   -9.51079   633%   2.6    2s
    H  247   278                      -0.3552126   -9.51079  2577%   2.7    4s
       781   902   -9.25045   33   43   -0.35521   -9.51079  2577%   1.9    6s
    H 1585  1592                      -0.3552126   -9.51079  2577%   1.6    6s
    H 8110  7059                      -0.3552126   -9.50711  2576%   1.8    7s
    H 8122  6713                      -0.3552127   -9.30503  2520%   1.8    8s
    H 8130  6382                      -2.3357366   -9.30138   298%   1.8    9s
    H 8142  6071                      -2.3357366   -9.29811   298%   1.9    9s
      8145  6073   -6.61892  104   92   -2.33574   -9.28784   298%   1.9   10s
    H 8146  5770                      -2.3357366   -9.28549   298%   1.9   10s
    H 8148  5482                      -2.3357366   -9.27918   297%   1.9   10s
    H 8151  5210                      -2.4578655   -9.27461   277%   1.9   10s
    H 8153  4950                      -2.4982424   -9.27461   271%   1.9   10s
    H 8155  4704                      -2.5752122   -9.27461   260%   1.9   11s
    H 8177  4481                      -2.8242522   -9.10699   222%   1.9   12s
    H 8191  4267                      -2.9012220   -9.10385   214%   2.0   13s
      8351  4406   -9.07403   42  110   -2.90122   -9.10385   214%   2.2   17s
      8383  4428   -9.03478   43  107   -2.90122   -9.10385   214%   2.2   20s
    H 8384  4213                      -2.9012235   -9.10385   214%   2.2   20s
    H 8391  4008                      -3.0182210   -9.10385   202%   2.2   20s
    H 8399  3813                      -3.0439577   -9.10385   199%   2.2   20s
    H 8406  3627                      -3.0843346   -9.10385   195%   2.2   20s
    H22871 12349                      -3.0843346   -9.10385   195%   2.7   23s
    H22893 12349                      -3.0843349   -9.10385   195%   2.7   23s
     53049 36569   -3.92817   91   82   -3.08433   -9.02426   193%   2.6   25s
     59289 39585   -7.35043   98  162   -3.08433   -9.02002   192%   2.6   30s
    H59292 37607                      -3.0843349   -9.02002   192%   2.6   30s
    H59391 35824                      -3.0843349   -9.01468   192%   2.6   33s
     64850 38935   -6.71942  137   28   -3.08433   -9.01468   192%   2.7   35s
     84208 46462   -5.05365  136   66   -3.08433   -8.97444   191%   2.7   41s
    H84233 44667                      -3.0843349   -8.97444   191%   2.7   41s
     115569 59322   -8.78688   80  148   -3.08433   -8.86295   187%   2.8   45s
     161041 78093   -5.15601  152  105   -3.08433   -8.80173   185%   2.9   50s
     181884 91362   -5.68231  163   61   -3.08433   -8.78585   185%   2.8   55s
    H181973 91362                      -3.0843349   -8.78585   185%   2.8   55s
    H181994 91362                      -3.0843349   -8.78585   185%   2.8   55s
    
    Cutting planes:
      Gomory: 51
      MIR: 57
      Zero half: 24
      Mod-K: 26
    
    Explored 217464 nodes (607498 simplex iterations) in 60.03 seconds (12.16 work units)
    Thread count was 32 (of 32 available processors)
    
    Solution count 10: -3.08433 -3.08433 -3.08433 ... -2.90122
    
    Time limit reached
    Best objective -3.084334949119e+00, best bound -8.785850436285e+00, gap 184.8540%
    

    From the solver logs, we can see a clear difference:

    • Without parameters: an integer incumbent with objective ≤ 0 is found already at around 4 seconds.
    • With SolutionLimit and Cutoff: the run continues for 60 seconds but does not find any integer incumbent with objective ≤ 0.

    This is exactly the behavior that puzzles me. Do you have any insight into why adding these parameters would prevent the solver from finding such incumbents?

    If it helps, I can export the model from YALMIP and share it with you. Please let me know which format would be most convenient (e.g., .lp, .mps, Gurobi .mat, or the MATLAB/Python code used to generate the model).

    Thank you for your time and support.

    Best regards,
    Yize

    0
  • 祎泽 刘
    • Gurobi-versary
    • Conversationalist
    • First Question

    Hi Riley,

    I would like to know if YALMIP supports callback functions for this purpose。I don't think so. Can you explain in detail exactly what you mean by this?

    Thanks also for clarifying about callbacks in YALMIP. I’ve already posted the question on the YALMIP forum (link: https://groups.google.com/g/yalmip/c/QDDG7XUb78g), and I can confirm that YALMIP does not support callbacks.

    As a follow-up regarding Gurobi itself: does the Gurobi MATLAB API support callbacks? I reviewed the MATLAB API documentation but couldn’t find any reference to callback functionality. From what I can see, callbacks are documented for the Python, C, C++, and Java interfaces, but I’m not sure if the same is available in MATLAB.

    Best regards,
    Yize

    0
  • Riley Clement
    • Gurobi Staff

    Hi Yize,

    From the docs, Cutoff:

    Indicates that you aren’t interested in solutions whose objective values are worse than the specified value.

    If you set Cutoff=0.000001 you are instructing Gurobi to ignore solutions less than this value - if your objective is f(x) it is essentially adding a constraint f(x) ≥ 0.000001, so it's no surprise you are not finding solutions with negative value.

    The matlab API does not support callbacks unfortunately, but I also don't think you need them.

    I would also try using the NoRel heuristic, in addition to your Cutoff value.

    - Riley

    0
  • 祎泽 刘
    • Gurobi-versary
    • Conversationalist
    • First Question

    Hi Riley,

    Thanks a lot for your clarification and the suggestion of trying the NoRel heuristic.

    I just had one follow-up question regarding the Cutoff parameter. From the official documentation, the definition is:

    Default value: Infinity for minimization
    Indicates that you aren’t interested in solutions whose objective values are worse than the specified value.

    A couple of questions came up:

    • About Cutoff direction (minimization): My understanding from the docs is that for minimization, Cutoff = c discards solutions with objective greater than c (keeps only ≤ c). So Cutoff = 1e-6 should prune solutions > 1e-6. That seems opposite to the phrasing of “ignore solutions less than this value” in your note — did I understand it correctly that for minimization, Cutoff actually discards solutions greater than the specified cutoff?
    • Why can Cutoff prevent solutions that vanilla finds?
      Is it expected that a tight Cutoff (e.g., 1e-6) can block NoRel/root heuristics from producing incumbents that vanilla would otherwise reach (because any intermediate incumbents with objective slightly above the cutoff get rejected)? If so, is there a recommended way to avoid this while still enforcing a target threshold?

    Thanks again for your time and guidance!

    Best regards,
    Yize

    0
  • 祎泽 刘
    • Gurobi-versary
    • Conversationalist
    • First Question

    Hi Riley,

    Thanks again for your previous suggestions. I tested the NoRel heuristic by setting

    solver_options.gurobi.NoRelHeurTime = 60;

    However, in my runs the solver did not find a feasible solution within the 60-second NoRel window. In other words, the heuristic produced no incumbents during that time, and the subsequent solve continued as if no feasible starting point had been generated. I’ve attached the solver log below to illustrate this behavior.

    Academic license - for non-commercial use only - expires 2026-09-16
    Gurobi Optimizer version 11.0.3 build v11.0.3rc0 (win64 - Windows 11+.0 (26100.2))
    
    CPU model: Intel(R) Core(TM) i9-14900HX, instruction set [SSE2|AVX|AVX2]
    Thread count: 24 physical cores, 32 logical processors, using up to 32 threads
    
    Optimize a model with 2757 rows, 1656 columns and 6813 nonzeros
    Model fingerprint: 0x7d726681
    Variable types: 1152 continuous, 504 integer (144 binary)
    Coefficient statistics:
      Matrix range     [1e-03, 4e+01]
      Objective range  [2e-01, 2e+01]
      Bounds range     [1e+00, 1e+00]
      RHS range        [1e+00, 6e+03]
    
    User MIP start did not produce a new incumbent solution
    User MIP start violates constraint R2365 by 36.009302271
    
    Presolve removed 1953 rows and 853 columns
    Presolve time: 0.01s
    Presolved: 804 rows, 803 columns, 3335 nonzeros
    Variable types: 343 continuous, 460 integer (72 binary)
    Starting NoRel heuristic
    Found phase-1 solution: relaxation 0
    Transition to phase 2
    Elapsed time for NoRel heuristic: 5s (best bound -13.0139)
    Elapsed time for NoRel heuristic: 13s (best bound -13.0139)
    Elapsed time for NoRel heuristic: 20s (best bound -13.0139)
    Elapsed time for NoRel heuristic: 28s (best bound -13.0139)
    Elapsed time for NoRel heuristic: 34s (best bound -13.0139)
    Elapsed time for NoRel heuristic: 42s (best bound -13.0139)
    Elapsed time for NoRel heuristic: 51s (best bound -13.0139)
    Elapsed time for NoRel heuristic: 59s (best bound -13.0139)
    NoRel heuristic complete
    
    Explored 0 nodes (0 simplex iterations) in 60.01 seconds (1.37 work units)
    Thread count was 32 (of 32 available processors)
    
    Solution count 0
    
    Time limit reached
    Best objective -, best bound -1.301387906275e+01, gap -

    Given this, I wanted to ask a follow-up question: in MATLAB–YALMIP, is there any practical way to enforce the rule “stop once two solutions with non-negative objective values have been found”? Since the MATLAB API doesn’t support callbacks, I wonder if there are alternative approaches (e.g., using SolutionLimit, Cutoff, or modeling tricks) that could mimic this stopping criterion.

    Any advice on how to implement such a condition—or confirmation that exporting the model and using Python callbacks would be the only reliable route—would be very helpful.

    Best regards,
    Yize

    0
  • Riley Clement
    • Gurobi Staff

    Hi Yize,

    About Cutoff direction (minimization): My understanding from the docs…

    You're understanding is correct, I missed the detail of this being a minimization and thought you were maximizing and wanting two solutions with positive objective values, please accept my apologies!

    Once Gurobi finds an initial solution, we can start running “improvement heuristics” and based off the rapid improvement of solution in the defaults log they seem to be successful.  It might be that it is much easier to find an initial solution when the cutoff parameter is not used, and that unlocks the ability to rapidly improve to solutions which would satisfy the cutoff.  If you would like to share a MPS file with us then please do so, looking into this will be a good exercise for staff who are currently in training.

    Given this, I wanted to ask a follow-up question: in MATLAB–YALMIP, is there any practical way to enforce the rule “stop once two solutions with non-negative objective values have been found”? Since the MATLAB API doesn’t support callbacks, I wonder if there are alternative approaches (e.g., using SolutionLimit, Cutoff, or modeling tricks) that could mimic this stopping criterion.

    Since there are no callbacks available in matlab, I think the only approach there would be SolutionLimit + Cutoff, it's just a shame it is not performing well.  Exporting the model to MPS and using Python might be the only option if we can't raise the performance of SolutionLimit + Cutoff, but I have some ideas to test.

    - Riley

    0
  • 祎泽 刘
    • Gurobi-versary
    • Conversationalist
    • First Question

    Hi Riley,

    Thanks for the clarification and for the ideas around using SolutionLimit + Cutoff.

    As a follow-up: I exported two MPS variants because in some runs I re-center the objective as f(x) − C to encode my stopping rule “stop once two solutions with non-positive shifted objective have been found” (i.e., f(x) ≤ C). Due to this constant shift, the two MPS files are not identical.Params I used for the “two non-positive solutions” experiment: SolutionLimit = 2, Cutoff = 0 (minimization).

    I’ve attached both versions for your review:

    • problem1.mps
      Exported via export(Constraints, Objective, ops)gurobi_write(...).
      Uses the shifted objective f(x) − C for the “two non-positive solutions” rule.
      No OBJ RHS constant is written (the writer omits the objective constant); this does not affect the optimal solution—only the reported objective value.
    • problem.mps
      Captured via savesolverinput (diagn.solverinput.model) during optimize(...)—the exact model handed to Gurobi in that run.
      Includes an explicit RHS entry for the OBJ row (≈ 197,666.086), i.e., the objective constant is serialized here. This only shifts the reported objective; feasibility and the argmin are unchanged.
    • Note: The two files differ mainly in how the objective constant (and some zero RHS entries) are serialized; variables, constraints, and types align.

    For convenience, I’ve shared both files via Filemail:   https://app.filemail.com/d/ugkvfzxpcvzsmlc.

    If you prefer me to use another sharing service (e.g., Dropbox, Google Drive, or something else you typically use), please let me know and I can re-upload them.

    Many thanks again!

    Best regards,
    Yize

    0
  • Riley Clement
    • Gurobi Staff

    Thanks Yize,

    We'll take a look!

    - Riley

    0
  • Yuya Masumura
    • Gurobi Staff

    Hi Yize, 

    Just adding objective <= C as a constraint instead of using Cutoff  works well with default setting for other parameters than SolutionLimit. NoRel heuristic performs better too. I hope this would help your issue.

    - Yuya

    0

Please sign in to leave a comment.