What is a solver friendly way of encoding integer division in MILP programs? Currently I'm using the following encoding (in Gurobi python), which may not be totally correct and I hope not that optimal.
# res is an integer variable in the solver
# val is an integer variable in the solver
# divVal is just a python variable, a constant for solver
offset = 0.999
divRes = val / divVal
model.addConstr(divRes - offset <= res)
model.addConstr(res <= divRes)
Above encoding essentially says that res
should be assigned a value between divRes - offset
and divRes
, since offset
is 0.999, there should be only 1 integer in the range and solver is forced to assign that to res
. Is there a better (faster) way of encoding this?
EDIT: By integer division I mean that the result of division is an integer. If there is any fractional part after the division, I want to discard that and round down the result which will be stored in res
. What I essentially want to do is shift a number by some x
bits. In MILP solver, that boils down to dividing a number by (1 << x)
, but there is some fractional part after the division which I want to get rid of.
model.addRange(val - divVal*res, 0, 0.99999, name="Range")
I would prefer to use this above mentioned Range Constraint only. Incorporating tighter bounds (there is only integer in the given range, which we require) directly into the model can not only improve the numerical behavior, but it also speed up the optimization process (because gurobi use branch and bound algorithm to get solutions) https://www.gurobi.com/documentation/9.1/refman/improving_ranges_for_varia.html
Optimality - A small change in model can easily calculate an optimal result, adding res in a Minimisation type objective function or negative of it in the maximisation function would shrink its value at lower side if divVal*res will become integer. Gurobi does not provide Less than constraint. Moreover, An integrality restriction on a variable is considered satisfied in Gurobi when the variable's value is less than IntFeasTol from the nearest integer value. Default value of IntFeasTol tolerance is 1e-5, and it could be further reduced up to 1e-9 for a better result. However, making multi-objective model, add extra complexity to the model. I would not like to recommend it.
model.addRange(val - divVal*res, 0, 1, name="Range")
model.setObjective(res, GRB.MINIMIZE)