This has been asked more than once on help forums, so I list below a Java function that I believe will extract all variables from an instance of IloCplex. Something quite similar should work in the C++ API, and hopefully if you use the C API you can make the leap from the Java code. The Python API seems to provide a VariablesInterface class that I think provides a mechanism (if it's even needed in Python -- I don't really know). I'm blissfully ignorant about the Matlab API.
I've tried to stress-test the Java code, but nonetheless you use it at your own peril.
private IloNumVar[] parse(IloCplex cplex) throws IloException { HashSet<IloNumVar> vars = new HashSet<IloNumVar>(); Iterator it = cplex.iterator(); IloLinearNumExpr expr; IloLinearNumExprIterator it2; while (it.hasNext()) { IloAddable thing = (IloAddable) it.next(); if (thing instanceof IloRange) { expr = (IloLinearNumExpr) ((IloRange) thing).getExpr(); it2 = expr.linearIterator(); while (it2.hasNext()) { vars.add(it2.nextNumVar()); } } else if (thing instanceof IloObjective) { expr = (IloLinearNumExpr) ((IloObjective) thing).getExpr(); it2 = expr.linearIterator(); while (it2.hasNext()) { vars.add(it2.nextNumVar()); } } else if (thing instanceof IloSOS1) { vars.addAll(Arrays.asList(((IloSOS1) thing).getNumVars())); } else if (thing instanceof IloSOS2) { vars.addAll(Arrays.asList(((IloSOS2) thing).getNumVars())); } else if (thing instanceof IloLPMatrix) { vars.addAll(Arrays.asList(((IloLPMatrix) thing).getNumVars())); } } IloNumVar[] varray = vars.toArray(new IloNumVar[1]); return varray; }
Update: I have updated the code, incorporating some suggestions I received on a CPLEX forum. The parser is now a static method in a class of its own. You can download the source code (one file, plain text) from Google Docs. To use it, you will likely want to change the package name. Also feel free to delete the various print statements, which are there only for demonstration purposes.
O' Java, the horror! [No "switch"ing on object types? That's _so_ behind the times.]
ReplyDeleteUnfortunately, Concert Technology makes things even worse: No use of generics, thus: no type-safe for-each-loop for iterators? Multiple getExpr() and getNumVars() methods, but no common interface? ...That's not just "behind the times", that's really bad software design.
I'd reserver "horror" for C++ (and "terror" for Perl).
ReplyDeleteI don't have a problem with multiple getters. The Java API does have some rough edges (I find IloNumVar being an interface but not a class a bit funky). Part of it is that the C and C++ interfaces, the latter using separate models and problem classes and requiring an IloEnvironment class to sort things out, came first. The Java API merges the model and problem (IloModel and IloCplex in C++) into one class (IloCplex), and jettisons the environment class (thank you!). My guess is that if the Java API had been built from scratch, without regard to the C and C++ APIs, things would be different.
As far as generics go, I wouldn't rule out the possibility of using them. I'm inexperienced using Java's introspection features, so I took the easy way out writing my code. It may not be possible to streamline the parsing with generics, but it also may just be beyond my coding skills. That's also true of switching on object type; I didn't try (if-else was easier), so I don't know if it could be done.
I agree, though, that Java is behind the times ... in the sense that there's a new programming language popping up every 30 minutes or so, and Java has been around for a while.
Thanks for this!!
ReplyDeleteGreat! cplex doesn't allow to access to vars values when you import a model (using the importModel method for a simple lp file).. your solution bypass this problem
ReplyDelete