Tuesday, May 20, 2014

A Java Slider/Text Combo

A few years back I was coding (in Java, of course) the <shudder>GUI</shudder> for a research program. I needed to provide controls that would let a user specify priorities (0-100) scale for various things. Two possibilities occurred to me, with pretty much diametrically opposed strengths and weaknesses.

Sliders have a few virtues.
  • Grabbing and yanking the handle is usually faster than typing.
  • When you have more than one, they provide a sort of quasi-horizontal bar graph of the inputs -- it's easy to tell from their relative positions which inputs are larger than which others.
  • It's immediately obvious if you are the absolute maximum or minimum value for the input.
When your input range is something like {1, 2, 3, 4, 5}, it's pretty easy to look at a slider and see what the exact value is. When your input range is {0, 1, ..., 100}, that's a lot harder, which led me to consider text fields. Text fields have their own virtues.
  • It's easy to be precise, regardless of the legal range of inputs. (Try hitting exactly 58, rather than 57, on a 0-100 slider.)
  • You can immediately see exactly what value is being set.
On the other hand, the sort of approximate ratio information you get just by looking at two sliders requires a bit of mental arithmetic with two text fields. Also, if the range of legal values is not obvious to the user, it can be hard to know whether a particular input is at (or near) the maximum or minimum legal value.

Hoping for the best of both worlds, I went looking for Java Swing control that combined a slider with an editable text entry field, and did not find one I liked (which may say more about how picky I am and how good my search skills are than about their availability). So I rolled my own.

Fast-forward to this month, where I'm working on a program for an entirely different research project and once again wanted a slider/text combo control. I dusted off the old code and decided to pretty it up a (very) little, add some documentation and release it into the wild. The project name is ComboSlider, and you can find its repository on Bitbucket. I released it under the EPL 1.0 open source license. Source code, a compiled jar file and documentation are all in the repository. The jar file includes a small demonstration program.

Here is a screen shot of the demo program:


The ComboSlider control is in the orange rectangle. Pull the slider and the text field updates. Type a legal value in the text field and the slider automatically repositions.

The top two text fields demonstrate how to redefine the domain of the ComboSlider on the fly. The bottom text label updates automatically when you change the input value, demonstrating how to use a change listener to monitor the ComboSlider's value. (In an ordinary input dialog, though, you would not need the change listener; you would just wait for the dialog to complete and then use ComboSlider.getValue() to find out what the input value is.)

There are two limitations I probably should mention: it only accepts integer inputs (positive or negative); and, while you can set the size of the entire ComboSlider using the usual setters for minimum/maximum/preferred size, the separate dimensions of the slider and text field are hard coded in the constructor.

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.