Sunday, May 11, 2014

Setting CPLEX Parameters in Java Revisited

A bit more than a year and a half ago, I wrote some Java code to facilitate setting parameters for the CPLEX optimizer using their Concert API. Since then, I've added support for their CP Optimizer, and IBM has refactored the handling of parameters in CPLEX, necessitating an update to my code. This post (which supersedes the previous one) describes how to get and use my code, which is licensed under the Eclipse Public License.

Purpose


When using any of the programming APIs to CPLEX or CP Optimizer, the conventional ways to set parameters for the solver are to hard-code them, or to hard-code methods of setting specific parameters (for instance, write a method that sets the solver time limit using an argument fetched from somewhere). Unfortunately, picking parameter values for either solver is an NP-annoying task, with lots of possible combinations to consider. I therefore prefer to set them either from command line arguments or possibly from user inputs obtained from a GUI for my program. My utility library provides the necessary infrastructure for doing that.

Requirements


Other than Java 7 or later (once a "later" becomes available), all you need to use the code are the same jar and binary files for CPLEX and/or CP Optimizer that your program needs to use those solvers. If you are only using one of the solvers, you will not need to link the other library in order to use my code.

Versions


Due to the aforementioned refactoring, I was forced to create two versions of the utility, with the second version not backward-compatible. I'll provide usage example for both below. As best I can tell, they work identically with CPOptimizer, other than needing (version 2) or not needing (version 1) to have a class instance created. CPOptimizer does not have hierarchical parameter names, and none of its parameter names are ambiguous.

Both versions use strings for both the parameter name and the parameter value (since a string is how the value would be obtained from the command line); the utility takes care of interpreting strings representing numerical or logical values.

Version 1.0


Version 1 of my utility works with CPLEX 12.5.1 or earlier. It uses the (mercifully short) parameter names in force prior to the IBM refactoring, so for instance you can specify the time limit parameter as "TiLim". Parameter names in this version are case-sensitive (so "TiLim" works but neither "tilim" nor "Tilim" will). Version 1.0 also works with CPLEX 12.6 using the old parameter names, but it will not recognize the new names. For instance, when setting the branching direction in CPLEX 12.6, version 1.0 will recognize the old name "BrDir" but not the new name "MIP.Strategy.Branch". According to the CPLEX documentation, the old parameter names are deprecated, so version 1 of my utility will work with CPLEX 12.6 and probably with any minor upgrades (a hypothetical 12.6.1, say), but may stop working with some future version of CPLEX.

Version 1 uses a static method to set parameters (see the example below) and does not require that an instance of the parameter setter class be created.

Version 1 does not contain a main program; it is purely a library.

Version 2.0


Version 2 of my utility works with CPLEX 12.6 but not with earlier versions of CPLEX, as it only knows the new parameter names. Thus, reversing the previous example, version 2 will recognize "MIP.Strategy.Branch" but not "BrDir". Unlike version 1, parameter names in version 2 are not case-sensitive; "mip.strategy.branch" and "MIP.strategy.branch" will work just as well as "MIP.Strategy.Branch".

The new parameter names, which reflect a class hierarchy, are a PITA to type, so version 2 will accept shortened versions when no ambiguity exists. In the case of the branch direction, "Strategy.Branch" and just "Branch" will work equally well. The name "Display", however, maps to seven (!) distinct parameters, so you need to provide enough of the hierarchy to eliminate the ambiguity. If you want to adjust the simplex display, you can use "IloCplex.Param.Simplex.Display" (have fun typing that!) or "Param.Simplex.Display" or "Simplex.Display" (which gets my vote), but not just "Display". Failure to use enough of the hierarchy to uniquely identify the target parameter will result in an AmbiguousParameterException being thrown.

The inevitable one exception to this is the time limit. Three parameters answer to the name "TimeLimit", but for somewhat arcane reasons I coded the utility to recognize "TimeLimit" as meaning "IloCplex.Param.TimeLimit". (For the other two parameters, you'll need "Tune.TimeLimit" and either "DistMIP.Rampup.TimeLimit" or just "Rampup.TimeLimit".)

Version 2, in contrast to version 1, does not employ a static parameter setter, and so you need to create an instance of the parameter setter in order to use it. Again, see the example below.

Version 2 contains a main program. If you run it, it will list all the parameters it can find for both CPLEX and CPOptimizer. If you have one but not the other linked, you'll want to comment out the portion of the code that lists parameters for the solver you are not using.

Examples


In the following examples, I will assume that you want to set the time limit (double) for both CPLEX and CPOptimizer, the log verbosity (integer) for CPOptimizer, the solution limit (long) for CPLEX, and the presolve switch (logical) for CPLEX. For the CPLEX 12.6 parameters, I'll use the shortest legal name. Left to the reader as an exercising: import statements and wrapping portions of the code in try-catch blocks as needed.

Version 1.0


// create solver instances
IloCplex cplex = new IloCplex();
IloCP cp = new IloCP();

// parameter names (would normally come from command line or GUI)
String[] cplexNames = new String[] {"TiLim", "IntSolLim", "PreInd"};
String[]  cpNames = new String[] {"TimeLimit", LogVerbosity"};

// parameter values  (would normally come from command line or GUI)
String[] cplexValues = new String[] {"23.5", "1000", "false"};
String[] cpValues = new String[] {"23.5", 1};

// set the CPLEX parameters
for (int i = 0; i < cplexNames.length; i++) {
   cplexutils.CplexParamSetter.set(cplex, cplexNames[i], cplexValues[i]);
}

// set the CPOptimizer parameters
for (int i = 0; i < cpNames.length; i++) {
  cplexutils.CPOptimizerParamSetter.set(cp, cpNames[i], cpValues[i]);
}

Version 2.0


// create solver instances
IloCplex cplex = new IloCplex();
IloCP cp = new IloCP();

// create parameter setter instances
CplexParameterSetter csetter = new CplexParameterSetter();
CPOptimizerParameterSetter cpsetter = new CPOptimizerParameterSetter();

// parameter names (would normally come from command line or GUI)
String[] cplexNames = new String[] {"TimeLimit", "Limits.Solutions", "Presolve"};
String[]  cpNames = new String[] {"TimeLimit", LogVerbosity"};

// parameter values  (would normally come from command line or GUI)
String[] cplexValues = new String[] {"23.5", "1000", "false"};
String[] cpValues = new String[] {"23.5", 1};

// set the CPLEX parameters
for (int i = 0; i < cplexNames.length; i++) {
   csetter.set(cplex, cplexNames[i], cplexValues[i]);
}

// set the CPOptimizer parameters
for (int i = 0; i < cpNames.length; i++) {
  cpsetter.set(cp, cpNames[i], cpValues[i]);
}


Obtaining the code


You can download either version from the project's downloads page on Bitbucket. Binaries are available from the "Downloads" tab, source code from the "Tags" tab. If you run into a bug (unlikely, but you never know), please let me know. There's an issue tracker under the "Issues" link.
Please see the more recent post Updated Java Utilities for CPLEX and CP Optimizer for a description of the contents of the library and links to both source code and a binary distribution. Please note that version 1.0 no longer exists; the links in the updated post will take you to the most recent version.

No comments:

Post a Comment

If this is your first time commenting on the blog, please read the Ground Rules for Comments. In particular, if you want to ask an operations research-related question not relevant to this post, consider asking it on OR-Exchange.