# A toy example triggering Gurobi's Warning: Model contains variables with very large bounds participating in product terms

**URL:** <https://discourse.julialang.org/t/a-toy-example-triggering-gurobis-warning-model-contains-variables-with-very-large-bounds-participating-in-product-terms/127295>\
**Category:** Optimization (Mathematical)\
**Tags:** jump, examples, gurobi\
**Created:** [March 24, 2025, 2:35am UTC](https://discourse.julialang.org/t/a-toy-example-triggering-gurobis-warning-model-contains-variables-with-very-large-bounds-participating-in-product-terms/127295 "2025-03-24T02:35:41Z")\
**Posts on this page:** 14\
**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 24, 2025, 2:35am UTC](https://discourse.julialang.org/t/a-toy-example-triggering-gurobis-warning-model-contains-variables-with-very-large-bounds-participating-in-product-terms/127295/1 "2025-03-24T02:35:41Z")

</div>

It’s common to find Gurobi logging the following Warning in QCP

```julia
Warning: Model contains variables with very large bounds participating 
in product terms.
Presolve was not able to compute smaller bounds for these variables.
Consider bounding these variables or reformulating the model.

```

In this post I give a simple example which triggers it

```julia
julia> import JuMP, Gurobi

julia> begin
           model = JuMP.Model(Gurobi.Optimizer)
           JuMP.@variable(model, x)
           JuMP.@variable(model, xx)
           JuMP.@constraint(model, xx >= x * x)
           JuMP.@objective(model, Min, xx)
           JuMP.optimize!(model)
       end
Gurobi Optimizer version 12.0.1 build v12.0.1rc0 (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 0 rows, 2 columns and 0 nonzeros
Model fingerprint: 0xf65faed0
Model has 1 quadratic constraint
Coefficient statistics:
  Matrix range [0e+00, 0e+00]
  QMatrix range [1e+00, 1e+00]
  QLMatrix range [1e+00, 1e+00]
  Objective range [1e+00, 1e+00]
  Bounds range [0e+00, 0e+00]
  RHS range [0e+00, 0e+00]
Presolve removed 0 rows and 2 columns
Presolve time: 0.00s
Presolve: All rows and columns removed

Barrier solved model in 0 iterations and 0.00 seconds (0.00 work units)
Optimal objective 0.00000000e+00

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

julia> JuMP.solution_summary(model)
Warning: to get QCP duals, please set parameter QCPDual to 1 # 🍅
* Solver : Gurobi

* Status
  Result count : 1
  Termination status : OPTIMAL
  Message from the solver:
  "Model was solved to optimality (subject to tolerances), and an optimal solution is available."

* Candidate solution (result #1)
  Primal status : FEASIBLE_POINT
  Dual status : NO_SOLUTION
  Objective value : 0.00000e+00
  Objective bound : 0.00000e+00

* Work counters
  Solve time (sec) : 0.00000e+00
  Simplex iterations : 0
  Barrier iterations : 0
  Node count : 0

julia> 

julia> JuMP.@constraint(model, xx <= x * x);

julia> JuMP.optimize!(model)
Gurobi Optimizer version 12.0.1 build v12.0.1rc0 (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 0 rows, 2 columns and 0 nonzeros
Model fingerprint: 0xfe761c6f
Model has 2 quadratic constraints
Coefficient statistics:
  Matrix range [0e+00, 0e+00]
  QMatrix range [1e+00, 1e+00]
  QLMatrix range [1e+00, 1e+00]
  Objective range [1e+00, 1e+00]
  Bounds range [0e+00, 0e+00]
  RHS range [0e+00, 0e+00]

Continuous model is non-convex -- solving as a MIP

Found heuristic solution: objective 0.0000000
Presolve time: 0.00s
Presolved: 1 rows, 3 columns, 2 nonzeros
Presolved model has 1 quadratic constraint(s)
Presolved model has 1 bilinear constraint(s)
Warning: Model contains variables with very large bounds participating
         in product terms.
         Presolve was not able to compute smaller bounds for these variables.
         Consider bounding these variables or reformulating the model.

Variable types: 3 continuous, 0 integer (0 binary)

Explored 0 nodes (0 simplex iterations) in 0.02 seconds (0.00 work units)
Thread count was 8 (of 8 available processors)

Solution count 1: 0

Optimal solution found (tolerance 1.00e-04)
Best objective 0.000000000000e+00, best bound 0.000000000000e+00, gap 0.0000%

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

julia> JuMP.solution_summary(model)
* Solver : Gurobi

* Status
  Result count : 1
  Termination status : OPTIMAL
  Message from the solver:
  "Model was solved to optimality (subject to tolerances), and an optimal solution is available."

* Candidate solution (result #1)
  Primal status : FEASIBLE_POINT
  Dual status : NO_SOLUTION
  Objective value : 0.00000e+00
  Objective bound : 0.00000e+00
  Relative gap : 0.00000e+00

* Work counters
  Solve time (sec) : 2.09999e-02
  Simplex iterations : 0
  Barrier iterations : 0
  Node count : 0

```

---

<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 24, 2025, 2:36am UTC](https://discourse.julialang.org/t/a-toy-example-triggering-gurobis-warning-model-contains-variables-with-very-large-bounds-participating-in-product-terms/127295/2 "2025-03-24T02:36:12Z")

</div>

Would it be a good practice to add some Warning info of solvers also to `JuMP.solution_summary`’s ? Like the 🍅 does. @odow

---

<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 24, 2025, 2:39am UTC](https://discourse.julialang.org/t/a-toy-example-triggering-gurobis-warning-model-contains-variables-with-very-large-bounds-participating-in-product-terms/127295/3 "2025-03-24T02:39:51Z")

</div>

This is expected behavior, and we won’t be making any changes because we cannot programmatically access the warning print outs from Gurobi.

It is _always_ best practice to add variable bounds if you know they exist.

---

<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 24, 2025, 2:44am UTC](https://discourse.julialang.org/t/a-toy-example-triggering-gurobis-warning-model-contains-variables-with-very-large-bounds-participating-in-product-terms/127295/4 "2025-03-24T02:44:11Z")

</div>

> [@odow](#):
>
> This is expected behavior

Yes, this post is not a question post, it’s an example sharing experience.

> [@odow](#):
>
> cannot programmatically access the warning print

I see.

> [@odow](#):
>
> add variable bounds if you know they exist

For primal variables which has physical meaning, this might be easier. But if it is a dual one, it’s typically harder.

---

<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 25, 2025, 1:45am UTC](https://discourse.julialang.org/t/a-toy-example-triggering-gurobis-warning-model-contains-variables-with-very-large-bounds-participating-in-product-terms/127295/5 "2025-03-25T01:45:11Z")

</div>

Following the discussion under this topic, if we want to eliminate this Warning.  
We can do `JuMP.set_upper_bound(xx, 1)`.

If we want to enforce bounds on `x` instead of `xx`, then we **have to** enforce **both** lower and upper bounds, like `JuMP.@variable(model, -1 <= x <= 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 25, 2025, 2:12am UTC](https://discourse.julialang.org/t/a-toy-example-triggering-gurobis-warning-model-contains-variables-with-very-large-bounds-participating-in-product-terms/127295/6 "2025-03-25T02:12:06Z")

</div>

Evidence indicates that:  
whether Gurobi opts to print this Warning doesn’t depend on the `objective_sence`.  
But `objective_function` will certainly affect this.  
Look at this example, at `Time 1`, Gurobi reports no Warning (🟢);  
But at `Time 2`, the Warning is triggered (🔴).

```julia
import JuMP, Gurobi
model = JuMP.Model(Gurobi.Optimizer)
JuMP.@variable(model, x); JuMP.@variable(model, xx)
JuMP.@constraint(model, xx >= x * x)
JuMP.@constraint(model, xx <= x * x)
print(model) # Time 1
JuMP.optimize!(model) # 🟢 Time 1
JuMP.@objective(model, JuMP.objective_sense(model), xx)
print(model) # Time 2
JuMP.optimize!(model) # 🔴 Time 2

```

Therefore the `model` at `Time 2` is **different from** the `model` at `Time 1`.

But this difference cannot be observed via Julia’s `print(::JuMP.Model)`.  
@odow I guess this can be improved?

---

<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 25, 2025, 2:22am UTC](https://discourse.julialang.org/t/a-toy-example-triggering-gurobis-warning-model-contains-variables-with-very-large-bounds-participating-in-product-terms/127295/7 "2025-03-25T02:22:14Z")

</div>

```julia
julia> JuMP.objective_sense(model)
FEASIBILITY_SENSE::OptimizationSense = 2

```

You’ve set the objective to `FEASIBILITY_SENSE`, but you’ve also set an objective function. This doesn’t make sense. This is defined behavior. Solvers can choose to ignore the objective function, or, like Gurobi, they may not support a true objective sense and default to minimization.

The `Time 2` print is correct to ignore the objective function.

---

<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 25, 2025, 4:46am UTC](https://discourse.julialang.org/t/a-toy-example-triggering-gurobis-warning-model-contains-variables-with-very-large-bounds-participating-in-product-terms/127295/9 "2025-03-25T04:46:17Z")

</div>

I reorganize my expression to make it clearer.  
Since in my case I use `JuMP + Gurobi`  
My setting at Time 2 is actually (1️⃣)

```julia
(JuMP's default sense) xx
subject to
 -x² + xx >= 0
 -x² + xx <= 0

```

But behind the scenes, Gurobi is going to solve (2️⃣)

```julia
Minimize xx
subject to
 -x² + xx >= 0
 -x² + xx <= 0

```

Therefore, I think the existing Julia’s printing (JuMP’s realization)

```julia
Feasibility
Subject to
 -x² + xx >= 0
 -x² + xx <= 0

```

does not reflect the true situation, either in view of 1️⃣, or 2️⃣.

---

<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 25, 2025, 6:29am UTC](https://discourse.julialang.org/t/a-toy-example-triggering-gurobis-warning-model-contains-variables-with-very-large-bounds-participating-in-product-terms/127295/10 "2025-03-25T06:29:06Z")

</div>

It’s not JuMP’s “default” sense. It is `MOI.FEASIBILITY_SENSE`, which says that the objective does not exist. You cannot have an objective function and `FEASIBILITY_SENSE`. But this is a soft restriction in the sense that it is enforced by convention, not code.

We allow setting the objective function after setting the objective sense because we never strictly enforced that the objective sense must be set before the objective function. We probably should have enforced this in MOI v1.0.0, but we didn’t so we can’t change now.

This means we allow:

```julia
model = Model()
@variable(model, x)
set_objective_function(model, x) # Even though the sense is current FEASIBILITY_SENSE
set_objective_sense(model, MIN_SENSE)

```

the lack of a strict order between the sense and the function means that this works:

```julia
model = Model()
@variable(model, x)
set_objective(model, FEASIBILITY_SENSE, x)
set_objective_sense(model, MIN_SENSE)
optimize!(model)

```

As I said above, setting an objective function and keeping `FEASIBILITY_SENSE` is undefined behavior. You should not do this.

Now Gurobi.jl could choose delete the objective in `MOI.optimize!(::Gurobi.Optimizer)` if the sense is still `MOI.FEASIBILITY_SENSE`. But this could also be a breaking change that I don’t have a strong desire to make.

We could also error in `JuMP.set_objective` if you provide `FEASIBILITY_SENSE`, but this could also be breaking.

I’ll admit that the current behavior is a bit muddled, but I think that it’s too late to make large clarifying changes to it.

> Therefore, I think the existing Julia’s printing (JuMP’s realization)  
> does not reflect the true situation

It represents the view that we want people to have of the model. You have set `FEASIBILITY_SENSE`.

---

<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 25, 2025, 6:53am UTC](https://discourse.julialang.org/t/a-toy-example-triggering-gurobis-warning-model-contains-variables-with-very-large-bounds-participating-in-product-terms/127295/11 "2025-03-25T06:53:26Z")

</div>

> [@odow](#):
>
> setting an objective function and keeping `FEASIBILITY_SENSE` is undefined behavior.

Okay, I learn it.

Therefore we have to distinguish these 2 needs:

1. Do an optimization. In this case **we have to** specify `Max` or `Min`, and set a proper `objective_function`.
2. Merely get a feasible solution (or, testify the infeasibility). In this case **we have to** either **(i)** never call `JuMP.@objective`, or **(ii)** set the `objective_function` to `0`.

Typically the second need can be solved faster.

---

<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:** [April 9, 2025, 6:55am UTC](https://discourse.julialang.org/t/a-toy-example-triggering-gurobis-warning-model-contains-variables-with-very-large-bounds-participating-in-product-terms/127295/12 "2025-04-09T06:55:11Z")

</div>

> [@odow](#):
>
> It is _always_ best practice to add variable bounds if you know they exist.

I then have a question.

- Is the keyword `Bin` not that indispensable, in `@variable`?
- Is it always a better practice to use `Int` instead of `Bin`?

You see, it is always viable to rewrite `@variable(model, x, Bin)`  
as `@variable(model, 0 <= x <= 1, Int)`.  
In the second fashion we add variable bounds, which is deemed beneficial.

And the second fashion is more convenient—I can use `unset_integer(x)` to relax the integral constraints.

**Update** : Gurobi won’t readily recognize `0 <= x <= 1, Int` as `x, Bin`. At least from its logging. For the sake of safety, it might still be better to use `Bin`.

---

<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:** [April 9, 2025, 7:50am UTC](https://discourse.julialang.org/t/a-toy-example-triggering-gurobis-warning-model-contains-variables-with-very-large-bounds-participating-in-product-terms/127295/13 "2025-04-09T07:50:17Z")

</div>

> - Is it always a better practice to use `Int` instead of `Bin`?

There’s effectively no difference. `Bin` is less keystrokes and matches what some people write in their mathematical model.

> Gurobi won’t readily recognize `0 <= x <= 1, Int` as `x, Bin` . At least from its logging

It won’t make a different to the solve time

---

<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:** [June 11, 2025, 10:18am UTC](https://discourse.julialang.org/t/a-toy-example-triggering-gurobis-warning-model-contains-variables-with-very-large-bounds-participating-in-product-terms/127295/14 "2025-06-11T10:18:13Z")

</div>

> [@WalterMadelim](#):
>
> rewrite `@variable(model, x, Bin)`  
> as `@variable(model, 0 <= x <= 1, Int)`.

The former is better (the latter will error) when we need to fix `x` at `1`

```julia
julia> @variable(model, x, Bin);

julia> fix(x, 1) # no Error

```

---

<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:** [June 11, 2025, 7:32pm UTC](https://discourse.julialang.org/t/a-toy-example-triggering-gurobis-warning-model-contains-variables-with-very-large-bounds-participating-in-product-terms/127295/15 "2025-06-11T19:32:30Z")

</div>

See the `force` keyword: [JuMP · JuMP](https://jump.dev/JuMP.jl/stable/api/JuMP/#fix)
