Monday, March 17, 2014

Printing a CP Optimizer Model in Java

Update: I've converted the code to a static method (so that you do not need to extend the IloCP class), and added to a utility package I wrote. Please see this post for details on the software and how to get it.

The IBM ILOG CP Optimizer constraint programming solver has a Java API that currently seems to provide no easy way to print out a model. Here's a hack that prints the key ingredients (objectives and constraints). It does not explicitly list variables (they're embedded in the constraints and objectives), but that would be an easy fix. It also does not print out parameter settings. The code as written overrides the .toString() method for a class containing an instance of IloCP named 'solver'.

  @Override
  public String toString() {
    if (solver == null) {
      return "No model yet!";
    } else {
      StringBuilder sb = new StringBuilder();
      Iterator it = solver.iterator();
      while (it.hasNext()) {                           // iterate over the model
        IloAddable object = (IloAddable) it.next();    // get the next object
        if (object instanceof IloObjective) {
          // For an objective, .toString() returns the body of the objective.
          sb.append(object.getName())
            .append(": ")
            .append(object)
            .append("\n");
        } else if (object instanceof IloConstraint) {
          /*
            For some constraints, .toString() returns the body; for others
            (notably inequalities), .toString() returns the name if there is
            one, the body if the name is null.
          */
          IloConstraint ct = (IloConstraint) object;
          String name = ct.getName();             // get the name
          ct.setName(null);                       // temporarily remove the name
          sb.append(name)
            .append(": ")
            .append(ct)                           // this now gets the body
            .append("\n");
          ct.setName(name);                       // restore the name
        } else {
          sb.append(object.getClass().toString()).append("\n");
        }
      }
      return sb.toString();
    }
  }

Friday, March 14, 2014

CP Optimizer, Java and NetBeans

After years of coding CPLEX applications in Java, I've just started working with CP Optimizer (the IBM/ILOG constraint programming solver) ... and it did not take me long to run into problems.

As with CPLEX, you access CP Optimizer from Java through the Concert API. As always, I am using the NetBeans IDE to do the coding. With CPLEX, this required two steps: add a library to the project, containing the cplex.jar file (for compilation); and add an option of the form -Djava.library.path=<path to CPLEX>/cplex/bin/x86-64_linux to Project Properties > Run > VM Options (for execution). Your path will differ depending on which version of CPLEX you have, where it is installed, and what operating system you are using.

So I naively expected to have to do the same two things to use CP Optimizer, albeit with different paths and files. I was half right (which, like being half alive, is not all that useful).

The good news was that for compilation, all that was required was adding a library to the project, this time pointing to ILOG.CP.jar. The bad news was that putting the corresponding path to the binary libraries in java.library.path most emphatically did not work. I got a ton of linking errors.

I won't bore you with the details of the errors, nor my fruitless pounding on Google to find me an answer. The CPLEX Optimization Studio distribution comes with Java examples and a make file for them. I ran the make file (outside NetBeans), and everything worked. That gave me an idea of what I needed to do. Apparently, the path to the binaries cannot go in java.library.path. I'm not sure why, but I think it has something to do with the way the binaries, which are C/C++ code wrapped for use by Java, are linked. Instead, on my Linux system they have to be specified in the LD_LIBRARY_PATH environment variable.

Thus, per this earlier post, I hacked the <NetBeans directory>/etc/netbeans.conf file and added the line

export LD_LIBRARY_PATH=<path to CPLEX Studio 12.6>/cplex/bin/x86-64_linux:<path to CPLEX Studio 12.6>/cpoptimizer/bin/x86-64_linux

Note that if you have other goodies already on your library path that your NetBeans programs might need, you'll need to append them to the path. Also, I'm not sure what the equivalent of LD_LIBRARY_PATH is in the Windows world, let alone on a Mac ... but I suspect that something similar will be required.

I also added the same LD_LIBRARY_PATH export to ~/.bashrc so that, should I run one of my programs from a terminal, the correct paths will be in place.

A couple of warnings:
  • Obviously this hack will need to be updated whenever you upgrade to a newer version of CPLEX Optimization Studio.
  • This will complicate the testing of different versions of CPLEX or CP Optimizer in the same program (other than by editing netbeans.conf for each version). With CPLEX and my previous configuration, I could just switch the CPLEX library version and alter the java.library.path string in the NetBeans project properties for that project. So much for the good old days.

Wednesday, March 5, 2014

Twitter via RSS: More Problems, More Solutions

I previously wrote about how the Twitter 1.1 API dropped support for Atom and RSS, making it a PITA to get feeds for Twitter timelines in a feed reader. (As an aside, I switched feed readers - again - a while back. I'm now using InoReader. I can't recall if I've mentioned that since I made the change.) Since that post, two of the solutions I came up with have run into problems. The Twitter RSS service is apparently gone: surfing to twitter-rss.com yields a page saying the domain name expired on February 6 of this year. I was also pulling some feeds from twss.55uk.net, which now produces a "service unavailable" error.

After a bit of surfing, I found a solution posted by Amit Agarwal (@labnol), titled "A Simple Way to Create RSS Feeds for Twitter". Simple is in the eye of the beholder, but it works, and there's a learning curve effect (creating the second, third, ... feed gets progressively faster). It uses a Google Script run under your account, which makes me nervous for a couple of reasons. First, it creates another failure point if Google decides to discontinue support for user scripts. (Remember the late, lamented Google Reader?) Second, the script has to be configured so that anyone can run it, but it runs under your account. The script looks pretty benign to me, and it's unlikely anyone else would stumble over the correct URL to run it, but on general principles I get a bit nervous being this permissive.

Anyway, the instructions are in Mr. Agarwal's post, and they are quite clear, so I shall not repeat them here. I've replaced four feeds so far, and things seem to be working ... for the moment ...