Inquiry on Stopping Solver After Two Non-Positive Integer Solutions in YALMIP-Gurobi
AnsweredI am currently working with the Matlab-YALMIP-Gurobi setup and have encountered an issue related to stopping the solver after finding two integer solutions that make the objective function non-positive.
Initially, I tried using the following solver options:
solver_options.gurobi.SolutionLimit = 2;
solver_options.gurobi.Cutoff = 0.00001;
However, these options led to premature pruning, causing the solver to miss solutions that should have been found. Therefore, I am now considering using a callback mechanism to achieve my goal.
I would like to know if YALMIP supports callback functions for this purpose. If not, I was thinking of using the following approach:
- Define the variables, constraints, and objective function using YALMIP.
- Use the
exportcommand to convert the YALMIP model to the format required by the Gurobi native API. - Call the solver using the Gurobi native API and pass in the callback function to monitor the solver's progress and stop it once the desired condition (two non-positive integer solutions) is met.
Could you kindly confirm if this approach is feasible? Or do you have a better solution or alternative recommendation for achieving the same objective?
Thank you for your time and support.
-
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 -
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
SolutionLimitandCutoff: 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,
Yize0 -
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,
Yize0 -
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 -
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
Cutoffparameter. 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
Cutoffdirection (minimization): My understanding from the docs is that for minimization,Cutoff = cdiscards solutions with objective greater thanc(keeps only ≤ c). SoCutoff = 1e-6should 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,Cutoffactually discards solutions greater than the specified cutoff? -
Why can
Cutoffprevent solutions that vanilla finds?
Is it expected that a tightCutoff(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,
Yize0 -
About
-
Hi Riley,
Thanks again for your previous suggestions. I tested the
NoRelheuristic by settingsolver_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,
Yize0 -
Hi Yize,
About
Cutoffdirection (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 -
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) − Cto 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 viaexport(Constraints, Objective, ops)→gurobi_write(...).
Uses the shifted objectivef(x) − Cfor 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 viasavesolverinput(diagn.solverinput.model) duringoptimize(...)—the exact model handed to Gurobi in that run.
Includes an explicit RHS entry for theOBJrow (≈ 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,
Yize0 -
problem1.mps
-
Thanks Yize,
We'll take a look!
- Riley
0 -
Hi Yize,
Just adding
objective <= Cas a constraint instead of usingCutoffworks well with default setting for other parameters thanSolutionLimit. NoRel heuristic performs better too. I hope this would help your issue.- Yuya
0
Please sign in to leave a comment.
Comments
10 comments