package LBJ2.infer; import java.util.*; import LBJ2.classify.*; import LBJ2.jni.GLPKHook; /** * Uses the GNU Linear Programming Kit library to perform Integer Linear * Programming over the variables, maximizing the sum of all * learner-object-value triples selected while respecting the constraints. * This code implements the most straight-forward algorithm for translating * FOL constraints to linear inequalities. First, the FOL constraints are * propositionalized and conjuncted together to arrive at a single * propositional expression representing all the constraints. Then, each * type of propositional subexpression recursively translates its children, * replacing each with an unnegated variable before translating itself into * two (or one, in the case of negation) linear inequalities. * * <p> This class assumes that the <code>constraint</code> variable inherited * from class <code>Inference</code> is of type * <code>FirstOrderConstraint</code>. * * @author Nick Rizzolo * @deprecated As of LBJ release 2.0.12, it is preferrable to pass a * {@link LBJ2.jni.GLPKHook} object to the * {@link LBJ2.infer.ILPInference} constructor. **/ public class NaiveGLPK extends GLPK { /** Debugging variable. */ private static final int PRINT_ILP = ILPInference.VERBOSITY_NONE; /** Default constructor. */ public NaiveGLPK() { this(false); } /** * Initializing constructor. * * @param g Whether or not to generate cuts. **/ public NaiveGLPK(boolean g) { super(g); } /** * Initializing constructor. * * @param g Whether or not to generate cuts. * @param w Whether or not to write debug files when problems arise. **/ public NaiveGLPK(boolean g, boolean w) { super(g, w); } /** * Initializing constructor. * * @param h The head object. **/ public NaiveGLPK(Object h) { this(h, false); } /** * Initializing constructor. * * @param h The head object. * @param g Whether or not to generate cuts. **/ public NaiveGLPK(Object h, boolean g) { super(h, g); } /** * Initializing constructor. * * @param h The head object. * @param g Whether or not to generate cuts. * @param w Whether or not to write debug files when problems arise. **/ public NaiveGLPK(Object h, boolean g, boolean w) { super(h, g, w); } /** * Uses the <code>lpx_intopt(LPX*)</code> C routine from the GLPK library * to solve the ILP proglem if it hasn't already been solved. **/ protected void infer() throws Exception { if (solver != null) return; constraint.consolidateVariables(variables); indexMap = new HashMap(); if (writeStatusFiles) solver = new GLPKHook("NaiveGLPKInference" + ID, generateCuts, PRINT_ILP); else solver = new GLPKHook(generateCuts, PRINT_ILP); solver.setMaximize(true); for (Iterator I = variables.values().iterator(); I.hasNext(); ) { FirstOrderVariable v = (FirstOrderVariable) I.next(); ScoreSet ss = getNormalizer(v.getClassifier()).normalize(v.getScores()); Score[] scores = null; if (ss != null) scores = ss.toArray(); if (scores == null || scores.length == 0) { System.err.println( "LBJ ERROR: Classifier " + v.getClassifier() + " did not return any scores. GLPK Inference cannot be " + "performed."); System.exit(1); } int[] indexes = new int[scores.length]; double[] coefficients = new double[scores.length]; Arrays.fill(coefficients, 1); for (int j = 0; j < scores.length; ++j) { indexes[j] = solver.addBooleanVariable(scores[j].score); indexMap.put( new PropositionalVariable(v.getClassifier(), v.getExample(), scores[j].value), new Integer(indexes[j])); if (PRINT_ILP == ILPInference.VERBOSITY_HIGH) { System.out.println( indexes[j] + "(" + scores[j].score + "): " + v.getClassifier() + "(" + v.getExample() + ") == " + scores[j].value); } } solver.addEqualityConstraint(indexes, coefficients, 1); } PropositionalConstraint propositional = ((FirstOrderConstraint) constraint).propositionalize(); propositional = propositional.simplify(); propositional.runVisit(this); solver.addEqualityConstraint( new int[]{ returnIndex }, new double[]{ 1 }, 1); if (PRINT_ILP == ILPInference.VERBOSITY_HIGH) { StringBuffer buffer = new StringBuffer(); solver.write(buffer); System.out.println(buffer); } if (!solver.solve()) throw new InferenceNotOptimalException(solver, head); int variableIndex = 0; for (Iterator I = variables.values().iterator(); I.hasNext(); ) { FirstOrderVariable v = (FirstOrderVariable) I.next(); Score[] scores = v.getScores().toArray(); for (int j = 0; j < scores.length; ++j, ++variableIndex) if (solver.getBooleanValue(variableIndex)) v.setValue(scores[j].value); } } /** * Two <code>Inference</code> objects are equal when they have the same * run-time type and store the same head object. I.e., the <code>==</code> * operator must return <code>true</code> when comparing the two head * objects for this method to return <code>true</code>. * * @param o The object to compare to this object. * @return <code>true</code> iff this object equals the argument object as * defined above. **/ public boolean equals(Object o) { if (!(o instanceof NaiveGLPK)) return false; return head == ((NaiveGLPK) o).head; } /** * Derived classes override this method to do some type of processing on * constraints of the parameter's type. * * @param c The constraint to process. **/ public void visit(PropositionalDoubleImplication c) { PropositionalConstraint[] children = (PropositionalConstraint[]) c.getChildren(); visit( new PropositionalConjunction( new PropositionalImplication(children[0], children[1]), new PropositionalImplication(children[1], children[0]))); } /** * Derived classes override this method to do some type of processing on * constraints of the parameter's type. * * @param c The constraint to process. **/ public void visit(PropositionalImplication c) { PropositionalConstraint[] children = (PropositionalConstraint[]) c.getChildren(); visit( new PropositionalDisjunction( new PropositionalNegation(children[0]), children[1])); } /** * Derived classes override this method to do some type of processing on * constraints of the parameter's type. * * @param c The constraint to process. **/ public void visit(PropositionalConjunction c) { PropositionalConstraint[] children = (PropositionalConstraint[]) c.getChildren(); int[] indexes = new int[children.length + 1]; double[] coefficients = new double[children.length + 1]; for (int i = 0; i < children.length; ++i) { children[i].runVisit(this); indexes[i] = returnIndex; coefficients[i] = 1; } indexes[children.length] = Integer.MAX_VALUE; Arrays.sort(indexes); String key = "" + indexes[0]; for (int i = 1; i < children.length; ++i) key += "&" + indexes[i]; Integer I = (Integer) indexMap.get(key); if (I == null) { I = new Integer(createNewVariable(key)); indexMap.put(key, I); indexes[children.length] = I.intValue(); coefficients[children.length] = -1; solver.addLessThanConstraint(indexes, coefficients, children.length - 1); coefficients = new double[]{ 1, -1 }; for (int i = 0; i < children.length; ++i) solver.addGreaterThanConstraint( new int[]{ indexes[i], indexes[children.length] }, coefficients, 0); } returnIndex = I.intValue(); } /** * Derived classes override this method to do some type of processing on * constraints of the parameter's type. * * @param c The constraint to process. **/ public void visit(PropositionalDisjunction c) { PropositionalConstraint[] children = (PropositionalConstraint[]) c.getChildren(); int[] indexes = new int[children.length + 1]; double[] coefficients = new double[children.length + 1]; for (int i = 0; i < children.length; ++i) { children[i].runVisit(this); indexes[i] = returnIndex; coefficients[i] = 1; } indexes[children.length] = Integer.MAX_VALUE; Arrays.sort(indexes); String key = "" + indexes[0]; for (int i = 1; i < children.length; ++i) key += "|" + indexes[i]; Integer I = (Integer) indexMap.get(key); if (I == null) { I = new Integer(createNewVariable(key)); indexMap.put(key, I); indexes[children.length] = I.intValue(); coefficients[children.length] = -1; solver.addGreaterThanConstraint(indexes, coefficients, 0); coefficients = new double[]{ 1, -1 }; for (int i = 0; i < children.length; ++i) solver.addLessThanConstraint( new int[]{ indexes[i], indexes[children.length] }, coefficients, 0); } returnIndex = I.intValue(); } /** * Derived classes override this method to do some type of processing on * constraints of the parameter's type. * * @param c The constraint to process. **/ public void visit(PropositionalAtLeast c) { PropositionalConstraint[] children = (PropositionalConstraint[]) c.getChildren(); int[] indexes = new int[children.length + 1]; double[] coefficients = new double[children.length + 1]; for (int i = 0; i < children.length; ++i) { children[i].runVisit(this); indexes[i] = returnIndex; coefficients[i] = 1; } indexes[children.length] = Integer.MAX_VALUE; Arrays.sort(indexes); String key = "atl" + c.getM() + "of" + indexes[0]; for (int i = 1; i < children.length; ++i) key += "&" + indexes[i]; Integer I = (Integer) indexMap.get(key); if (I == null) { I = new Integer(createNewVariable(key)); indexMap.put(key, I); indexes[children.length] = I.intValue(); coefficients[children.length] = -c.getM(); solver.addGreaterThanConstraint(indexes, coefficients, 0); coefficients[children.length] = -children.length; solver.addLessThanConstraint(indexes, coefficients, c.getM() - 1); } returnIndex = I.intValue(); } /** * Derived classes override this method to do some type of processing on * constraints of the parameter's type. * * @param c The constraint to process. **/ public void visit(PropositionalNegation c) { PropositionalConstraint[] children = (PropositionalConstraint[]) c.getChildren(); children[0].runVisit(this); int index = returnIndex; String key = "!" + index; Integer I = (Integer) indexMap.get(key); if (I == null) { I = new Integer(createNewVariable(key)); indexMap.put(key, I); int[] indexes = new int[]{ index, I.intValue() }; double[] coefficients = new double[]{ 1, 1 }; solver.addEqualityConstraint(indexes, coefficients, 1); } returnIndex = I.intValue(); } /** * Derived classes override this method to do some type of processing on * constraints of the parameter's type. * * @param c The constraint to process. **/ public void visit(PropositionalVariable c) { returnIndex = ((Integer) indexMap.get(c)).intValue(); } /** * Derived classes override this method to do some type of processing on * constraints of the parameter's type. * * @param c The constraint to process. **/ public void visit(PropositionalConstant c) { assert false : "NaiveGLPK: Constraint contains a constant."; } }