# Gurobi LP solver's cut-off behavior---an example

**URL:** <https://discourse.julialang.org/t/gurobi-lp-solvers-cut-off-behavior-an-example/127454>\
**Category:** Optimization (Mathematical)\
**Tags:** examples, gurobi\
**Created:** [March 28, 2025, 1:03pm UTC](https://discourse.julialang.org/t/gurobi-lp-solvers-cut-off-behavior-an-example/127454 "2025-03-28T13:03:44Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![WalterMadelim](https://avatars.discourse-cdn.com/v4/letter/w/3e96dc/32.png) [@WalterMadelim](https://discourse.julialang.org/u/WalterMadelim)\
**Post date:** [March 28, 2025, 1:03pm UTC](https://discourse.julialang.org/t/gurobi-lp-solvers-cut-off-behavior-an-example/127454/1 "2025-03-28T13:03:44Z")

</div>

I find an interesting example about Gurobi’s LP solver.  
There are 2 linear constraints in my **LP** , named `fixed` and `cut`.

The outcomes for the following 2 schemes are different:

1. Add Both constraints, and solve only once
2. Add `fixed`, solve; Then add `cut`, solve again

In the 1st scheme, the solution `y = -1.192e-6`.  
In the 2nd scheme, the solution upon the first solve is `y = -2.384e-6`. Theoretically, upon adding the `cut`, the updated solution `y` should be the same as the 1st scheme (because the final `model` are identical). But the `cut` cannot cut off the first solution `y = -2.384e-6`.

Here is the runnable code to produce the above results

```julia
import JuMP, Gurobi
function optimise(model)
    JuMP.optimize!(model)
    JuMP.assert_is_solved_and_feasible(model; allow_local = false, dual = true)
end
c = -4.76837158203125e-6
model = JuMP.Model(Gurobi.Optimizer) 
JuMP.@variable(model, y)
JuMP.@objective(model, Min, 2 * y)
JuMP.@constraint(model, fixed, -9.5367431640625e-6 * y <= 2.2737367544323206e-11)
optimise(model);
yt = JuMP.value(y)
JuMP.@constraint(model, cut, c * y <= 5.6843418860808015e-12)
optimise(model);
yt = JuMP.value(y)

```

Then I proceed to explore the can-cut-off behavior.  
I modify the RHS constant of `cut`, and find the 2 values, with one can cut off while the other cannot.

```julia
b = -1.1e-9 # cannot cut off
b = -2.9e-9 # can cut off
JuMP.@constraint(model, cut, c * y <= b)
optimise(model);
yt = JuMP.value(y)

```

I guessed Gurobi decide whether the current trial point `y = -2.384e-6` is cut off according to the violation of the new `cut`. But with this line of reasoning, the critical violation is a value which is not very rational (It’s somewhere between `1.1e-9` and `2.9e-9`).

I don’t quite understand this.

---

<div class="post-metadata">

**Author:** ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)\
**Post date:** [March 28, 2025, 1:16pm UTC](https://discourse.julialang.org/t/gurobi-lp-solvers-cut-off-behavior-an-example/127454/2 "2025-03-28T13:16:57Z")

</div>

Have you tried setting a tolerance? [Setting tolerances in JuMP - is there an Optimizer-independent way? - #2 by odow](https://discourse.julialang.org/t/setting-tolerances-in-jump-is-there-an-optimizer-independent-way/95030/2)

---

<div class="post-metadata">

**Author:** ![WalterMadelim](https://avatars.discourse-cdn.com/v4/letter/w/3e96dc/32.png) [@WalterMadelim](https://discourse.julialang.org/u/WalterMadelim)\
**Post date:** [March 28, 2025, 3:13pm UTC](https://discourse.julialang.org/t/gurobi-lp-solvers-cut-off-behavior-an-example/127454/4 "2025-03-28T15:13:43Z")

</div>

The updated description should be more clearer.  
I guess it’s not a straightforward tolerance problem.  
I’m sleeping now. I’ll check the other issues tomorrow 🙂.

---

<div class="post-metadata">

**Author:** ![odow](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/odow/32/28685_2.png) [@odow](https://discourse.julialang.org/u/odow)\
**Post date:** [March 28, 2025, 6:04pm UTC](https://discourse.julialang.org/t/gurobi-lp-solvers-cut-off-behavior-an-example/127454/5 "2025-03-28T18:04:30Z")

</div>

Read [Tolerances and numerical issues · JuMP](https://jump.dev/JuMP.jl/stable/tutorials/getting_started/tolerances/)

---

<div class="post-metadata">

**Author:** ![WalterMadelim](https://avatars.discourse-cdn.com/v4/letter/w/3e96dc/32.png) [@WalterMadelim](https://discourse.julialang.org/u/WalterMadelim)\
**Post date:** [March 28, 2025, 10:18pm UTC](https://discourse.julialang.org/t/gurobi-lp-solvers-cut-off-behavior-an-example/127454/6 "2025-03-28T22:18:26Z")

</div>

I read them. Although it is ideal to have the [recommended](https://jump.dev/JuMP.jl/stable/tutorials/getting_started/tolerances/#Recommended-values) setting, in real cases numerical problematic cuts are generated via algorithms, e.g. my case.

And to update, I didn’t quite understand how Gurobi judges (the last line below)

```julia
import JuMP, Gurobi
function optimise(model)
    JuMP.optimize!(model)
    JuMP.assert_is_solved_and_feasible(model; allow_local = false, dual = true)
end
function test_can_cut_off(b)
    model = JuMP.Model(Gurobi.Optimizer)
    JuMP.set_silent(model) 
    JuMP.@variable(model, y)
    JuMP.@objective(model, Min, 2 * y)
    JuMP.@constraint(model, fixed, -9.5367431640625e-6 * y <= 2.2737367544323206e-11)
    optimise(model);
    yt = JuMP.value(y)
    JuMP.@constraint(model, cut, c * y <= b)
    optimise(model);
    println("y was $yt, y is $(JuMP.value(y)). Vio = $(c * yt - b)")
end
c = -4.76837158203125e-6
base = -1.95e-9
d = 1e-11

b = base + d/5 # can cut off
test_can_cut_off(b) # y was -2.384185791015625e-6, y is 0.0004085252096. Vio = 1.9593686837721615e-9
b = base + d # cannot cut off
test_can_cut_off(b) # y was -2.384185791015625e-6, y is -2.384185791015625e-6. Vio = 1.9513686837721615e-9
# Remark: if Gurobi adopts a Vio parameter, it should be some number between 1.951e-9 and 1.959e-9, which is not very rational

```

---

<div class="post-metadata">

**Author:** ![odow](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/odow/32/28685_2.png) [@odow](https://discourse.julialang.org/u/odow)\
**Post date:** [March 28, 2025, 10:40pm UTC](https://discourse.julialang.org/t/gurobi-lp-solvers-cut-off-behavior-an-example/127454/7 "2025-03-28T22:40:06Z")

</div>

Gurobi’s default feasibility tolerance is 1e-6: [Parameter Reference - Gurobi Optimizer Reference Manual](https://docs.gurobi.com/projects/optimizer/en/current/reference/parameters.html#parameterfeasibilitytol)

You cannot rely on specific behavior below this limit. It may find a solution that has a violation of 1e-10 or it may find a solution that has a tolerance of 1e-6. Gurobi doesn’t care, so long as the violation is less than the tolerance.

---

<div class="post-metadata">

**Author:** ![WalterMadelim](https://avatars.discourse-cdn.com/v4/letter/w/3e96dc/32.png) [@WalterMadelim](https://discourse.julialang.org/u/WalterMadelim)\
**Post date:** [March 28, 2025, 11:05pm UTC](https://discourse.julialang.org/t/gurobi-lp-solvers-cut-off-behavior-an-example/127454/8 "2025-03-28T23:05:03Z")

</div>

> [@odow](#):
>
> You cannot rely on specific behavior below this limit.

Therefore I set a tolerance myself, in my real-world application.  
e.g. this [example](https://discourse.julialang.org/t/slow-progress-or-numerical-error-on-a-very-simple-sdp-example/127399/7).
