# Multiple user cut

**URL:** <https://discourse.julialang.org/t/multiple-user-cut/109080>\
**Category:** Optimization (Mathematical)\
**Tags:** gurobi\
**Created:** [January 22, 2024, 12:11am UTC](https://discourse.julialang.org/t/multiple-user-cut/109080 "2024-01-22T00:11:20Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![Fabrizio](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/fabrizio/32/30587_2.png) [@Fabrizio](https://discourse.julialang.org/u/Fabrizio)\
**Post date:** [January 22, 2024, 12:11am UTC](https://discourse.julialang.org/t/multiple-user-cut/109080/1 "2024-01-22T00:11:20Z")

</div>

Hi,  
I am using a solver indipendent callback to add user cut withing a for loop, in a bilevel opt model. They are of the form:

```julia
function MyUsercutRelaxedSD(cb_data)
#Get var
...
for t in T
				SDrelcon = 
 				@build_constraint(LL_Primal[t]<=LL_Dual[t])
 				MOI.submit(MyMod, MOI.UserCut(cb_data), SDrelcon)
end
end#callback func

```

then calling as usual with

`set_attribute(MyMod, MOI.UserCutCallback(), MyUsercutRelaxedSD)`

Question is, am I actually adding **all** the t \in T different contraints to the call back? And if not, should i create a vector of cns with `@build_constraint` and how (?) before calling `MOI.submit(.)`?

Regards,  
Fabrizio

---

<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:** [January 22, 2024, 2:47am UTC](https://discourse.julialang.org/t/multiple-user-cut/109080/2 "2024-01-22T02:47:11Z")

</div>

> [@Fabrizio](#):
>
> am I actually adding **all** the t \in T different contraints to the call back

Yes, assuming the solver supports multiple user cuts in a single call. If you didnt get an error and this callback made the solve faster then i guess it worked. You can also check the return of `MOI.submit`.

This sort of thing is hard/impossible to debug.

If you need more control, youll need to write a solver-specific callback for the particular solver that you are using.

---

<div class="post-metadata">

**Author:** ![Fabrizio](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/fabrizio/32/30587_2.png) [@Fabrizio](https://discourse.julialang.org/u/Fabrizio)\
**Post date:** [January 22, 2024, 12:14pm UTC](https://discourse.julialang.org/t/multiple-user-cut/109080/3 "2024-01-22T12:14:08Z")

</div>

Thanks for your reply @odow, I’am using Gurobi and it should be able to receive multiple cuts. I gave a try with COPT and it also not complaining. But the issue is that:

- passing a single, aggregated, u cut that sum over t \in T Primal[t] and Dual[t] above, the result is correct, even if actually the cut is not beneficial, but this is another issue
- using T disaggregate u cuts as explained above, the results is wrong in terms of OF, it is higher indicating i am actually cutting optimal solution(s)
- but the Lower Level is separable in t, therefore this theoretically should not be the case, that brings me to think i am actually not inserting all T user Cuts separately, but maybe only the last one, the Tth.
- I have tryes to get a return value from .submit call,

> Resp=MOI.submit(MyMod, MOI.UserCut(cb\_data), SDrelcon)  
> @printf(“resp=%s\n”,Resp)

but i get a laconic “nothing”

- I have also tried to indexing a vector of user cuts and submit with

> SDrelcon[t] = @build\_constraint(LL\_Primal[t]\<=LL\_Dual[t])  
> MOI.submit(MyMod, MOI.UserCut(cb\_data), SDrelcon[t])

but i get an error, “_SDrelcon not defined_”. Does @build\_constraint() support the costruction of a vector of cns(?)

any suggestion welcome, thanks again,  
Fabrizio

---

<div class="post-metadata">

**Author:** ![mike\_k](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mike_k/32/211864_2.png) [@mike\_k](https://discourse.julialang.org/u/mike_k)\
**Post date:** [January 22, 2024, 1:43pm UTC](https://discourse.julialang.org/t/multiple-user-cut/109080/4 "2024-01-22T13:43:41Z")

</div>

Did you consider potential numerical issues? The results returned by a solver are typically subject to +/- epsilon precision. It might be that the epsilon-terms cancel out in the sum over all `t`, but this might not be the case if you create a cut for each `t`. In the latter case, you might cut more away than you want.  
You could try to manually add epsilon-terms in the cut (in the direction you need), to avoid potential numerical issues if it applies to your problem.

---

<div class="post-metadata">

**Author:** ![Fabrizio](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/fabrizio/32/30587_2.png) [@Fabrizio](https://discourse.julialang.org/u/Fabrizio)\
**Post date:** [January 22, 2024, 4:24pm UTC](https://discourse.julialang.org/t/multiple-user-cut/109080/5 "2024-01-22T16:24:50Z")

</div>

Thanks for your reply @mike_k but i do add an \eps in the cut violation check, i don’t think its this is the problem. Besides for @odow, the error above was my mistake, in fact i initialize a vector

> SDrelcon=Vector{ScalarConstraint}(undef,length(T))

and now this works fine:

> SDrelcon[t] = @build\_constraint(LL\_Primal[t]\<=LL\_Dual[t])  
> MOI.submit(MyMod, MOI.UserCut(cb\_data), SDrelcon[t])

and it works exactly as in the non indexed version, as you envisioned, so there should be some conceptual issue i am try to face

thanks for the moment,  
Fabrizio

---

<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:** [January 22, 2024, 7:26pm UTC](https://discourse.julialang.org/t/multiple-user-cut/109080/6 "2024-01-22T19:26:58Z")

</div>

Take a read of [https://www.gurobi.com/documentation/11.0/refman/c\_cbcut.html](https://www.gurobi.com/documentation/11.0/refman/c_cbcut.html). In particular:

> Note that cuts should be added sparingly, since they increase the size of the relaxation model that is solved at each node and can significantly degrade node processing speed.

> You should consider setting parameter [PreCrush](https://www.gurobi.com/documentation/11.0/refman/precrush.html#parameter:PreCrush) to value 1 when adding your own cuts. This setting shuts off a few presolve reductions that can sometimes prevent your cut from being applied to the presolved model (which would result in your cut being silently ignored).

User cuts are really hard to debug because there are a hint to the solver, they don’t change the solution, only the solution speed.

Your original code is how I would write the loop. The question is whether this improves the solution speed of Gurobi. That’s an open question. It may also be that Gurobi is just ignoring your cuts because it already found them (or something better).

> but i get a laconic “nothing”

Yes, this is my fault. `UserCut` doesn’t report acceptance, only `HeuristicSolution`.

---

<div class="post-metadata">

**Author:** ![Fabrizio](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/fabrizio/32/30587_2.png) [@Fabrizio](https://discourse.julialang.org/u/Fabrizio)\
**Post date:** [January 26, 2024, 9:26am UTC](https://discourse.julialang.org/t/multiple-user-cut/109080/7 "2024-01-26T09:26:39Z")

</div>

Thanks @odow for this added reccomandations, however i know/considered all, the Precrush is set to 1, i also switched off the DualReductions (which alone is able to speed up the complete dual increase in the problem, closing the MipGap much faster…but i don’t know why).  
As for the size, the user cut added are cheap and sparse, the thing is that they are ineffective, so it must be a conceptual issue related to my problem. From the JuMP side, i can confirm that to the first question, the anwer is yes, the t .in T cuts are actually all added, there is no need to add a vector and then add one by one

Regards,  
Fabrizio
