Written by: Paul Rubin

Primary Source: Or in an OB World, 6/22/2019

There’s a feature in the Java API for CPLEX (and in the C++ and C APIs; I’m not sure about the others) that I don’t see mentioned very often, possibly because use cases may not arise all that frequently. It became relevant in a recent email exchange, though, so I thought I’d highlight it. As usual, I describe it in the context of Java and let users of other APIs figure out the corresponding syntax.

Anyone who uses CPLEX knows how to use `IloCplex.getValue()`

or `IloCplex.getValues()`

to retrieve the values of the model variables in the final solution. What might fly under the radar is that there is an overload that takes as an argument an expression involving the variables (an instance of `IloNumExpr`

), evaluates that expression, and returns the value. This is more a convenience than an essential feature: armed with the variable values, you could presumably compute the expression value yourself. The convenience aspects are not trivial though. Using `getValue()`

to evaluate an expression saves you having to retrieve coefficients from someplace and likely run through a loop each time you evaluate the expression. (You do have to create the expression and store a pointer to it, but you do that once.) Also, if the only reason you need the variable values is to evaluate the expression, it saves you having to use `getValue()`

to get the variable values.

Where might you use this? It’s possible to use it to compute slacks for cuts you generate somewhere. (Use it to evaluate the cut expression, then subtract the relevant bound on the cut inequality and you have the slack.) It’s also possible to use it to track secondary criteria or objectives that you did not build into the model. I cobbled together some code to demonstrate the latter use, which is now available from my campus GitLab repository. The underlying problem comes from an old post by Erwin Kalvelagen (“Minimizing Standard Deviation“). It involves loading pallets and minimizing the standard deviation of the pallet loads. Let \(p\) be the vector of pallet loads and \(\mu\) the mean load per pallet. My code creates expressions for both \(\parallel p-(\mu,\dots,\mu)\parallel_{1}\) and \(\parallel p-(\mu,\dots,\mu)\parallel_{2}^{2}\), minimizes the \(L_1\) norm (which is computationally easier than minimizing the \(L_2\) norm), and evaluates for each newly found solution (in an incumbent callback) and for the final solution.

The use in a callback brings up an important point. There are `getValue()`

methods in several classes, and you need to be a bit careful about which one you are using. `IloCplex.getValue()`

evaluates the expression using the final solution, and will generate error 1217 (no solution exists) if you try to use before the solver is done. That in particular means you cannot use it in a callback. Fortunately, the relevant callbacks have their own versions. `IloCplex.LazyConstraintCallback.getValue()`

and `IloCplex.IncumbentCallback.getValue()`

evaluate expressions using the new candidate and accepted integer-feasible solution, respectively. Other callbacks (solve, branch, user cut) evaluate using the solution to the LP relaxation. To use any of them, call `this.getValue(...)`

or just `getValue(...)`

. Do *not* call `mip.getValue(...)`

inside a callback, where `mip`

is the problem you are solving: that will invoke `IloCplex.getValue()`

and trigger the aforementioned error.

Note that there are also `getSlack()`

and `getSlacks()`

methods for computing the slack in a constraint. The former takes a single instance of `IloRange`

as argument and the latter takes a vector of `IloRange`

instances. Like `getValue()`

, there are separate versions in `IloCplex`

and in the callback classes. I assume they behave the same way that `getValue`

does, but I have not actually checked that.

Finally, if you have switched to generic callbacks, there is good news. The `IloCplex.Callback.Context`

class (the class used by the argument to your callback function) has `getCandidateValue()`

, `getIncumbentValue()`

and `getRelaxationValue()`

for evaluating expressions based on a proposed new integer-feasible solution, the current best integer-feasible solution and the current node LP solution, respectively. (`IloCplex.getValue()`

remains as it was.) Not only can you do the same evaluations, but the method names are utterly unambiguous. Gotta love that!

#### Paul Rubin

#### Latest posts by Paul Rubin (see all)

- Reversing Differences - February 19, 2020
- Collections of CPLEX Variables - February 19, 2020
- Generic Callback Changes in CPLEX 12.10 - February 3, 2020