From fac18e92a49d13602748fa9b65819835ece60cc6 Mon Sep 17 00:00:00 2001
From: khashab2 <khashab2@illinois.edu>
Date: Tue, 28 Jun 2016 19:31:41 -0700
Subject: [PATCH] merging with lbjava's ilp-solver interface   + merging the
 two gurobi solvers

---
 README.md                                     |   19 +
 doc/RELEASE.txt                               |    5 +-
 pom.xml                                       |   16 +-
 .../cs/cogcomp/infer/ilp/GurobiHook.java      | 1032 ++++++++++-------
 .../cs/cogcomp/infer/ilp/ILPSolver.java       |  206 +++-
 .../ilp/JLISCuttingPlaneILPSolverGurobi.java  |  401 +++----
 .../cs/cogcomp/infer/ilp/GurobiHookTest.java  |   73 +-
 7 files changed, 1021 insertions(+), 731 deletions(-)
 create mode 100644 README.md

diff --git a/README.md b/README.md
new file mode 100644
index 0000000..84a7794
--- /dev/null
+++ b/README.md
@@ -0,0 +1,19 @@
+# Illinois Inference 
+
+This project is a suite of unified wrappers to a set optimization libraries, as well as some basic approximate solvers: 
+
+ - Gurobi: one of the fastest commercial toolkits. You need to have a license for using this.  
+ - ojAlgo: Open Source optimization tool. 
+ - Beam search: a heuristic search algorithm that explores a graph by expanding the most promising node in a limited set.  
+
+
+## Using Gurobi  
+
+To download and install Gurobi visit [http://www.gurobi.com/](http://www.gurobi.com/)
+
+Make sure to include Gurobi in your PATH and LD_LIBRARY variables
+```
+    export GUROBI_HOME="PATH-TO-GUROBI/linux64"
+    export PATH="${PATH}:${GUROBI_HOME}/bin"
+    export LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${GUROBI_HOME}/lib"
+```
diff --git a/doc/RELEASE.txt b/doc/RELEASE.txt
index e3078c9..2ef8636 100644
--- a/doc/RELEASE.txt
+++ b/doc/RELEASE.txt
@@ -14,4 +14,7 @@ Made Gurobi an optional dependency
 
 Version 0.5.0
 Added the oj! Algorithm (ojAlgo) solver
-Updated dependencies
\ No newline at end of file
+Updated dependencies
+
+Version 0.6.0
+Moving augmenting the current ilp-solvers with lbjava's ilp-solver
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 8f44fb6..dfdd309 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,17 +4,15 @@
 
 	<modelVersion>4.0.0</modelVersion>
 	<groupId>edu.illinois.cs.cogcomp</groupId>
-	<artifactId>inference</artifactId>
+	<artifactId>illinois-inference</artifactId>
 	<packaging>jar</packaging>
-	<version>0.5.0</version>
-
+	<version>0.6.0</version>
 
 	<properties>
 		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
 		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
 	</properties>
 
-
 	<repositories>
 		<repository>
 			<id>CogcompSoftware</id>
@@ -23,39 +21,33 @@
 		</repository>
 	</repositories>
 
-
 	<dependencies>
 		<dependency>
 			<groupId>junit</groupId>
 			<artifactId>junit</artifactId>
 			<version>4.12</version>
 		</dependency>
-
 		<dependency>
 			<groupId>org.slf4j</groupId>
 			<artifactId>slf4j-api</artifactId>
 			<version>1.7.12</version>
 		</dependency>
-
 		<dependency>
 			<groupId>com.gurobi</groupId>
 			<artifactId>gurobi</artifactId>
 			<version>6.5</version>
 			<optional>true</optional>
 		</dependency>
-
 		<dependency>
 			<groupId>net.sf.jgrapht</groupId>
 			<artifactId>jgrapht</artifactId>
 			<version>0.8.3</version>
 		</dependency>
-
 		<dependency>
 			<groupId>edu.illinois.cs.cogcomp</groupId>
 			<artifactId>illinois-core-utilities</artifactId>
-			<version>3.0.27</version>
+			<version>3.0.51</version>
 		</dependency>
-
 		<dependency>
 			<groupId>edu.illinois.cs.cogcomp</groupId>
 			<artifactId>illinois-sl-core</artifactId>
@@ -67,7 +59,6 @@
 				</exclusion>
 			</exclusions>
 		</dependency>
-
 		<dependency>
 			<groupId>org.ojalgo</groupId>
 			<artifactId>ojalgo</artifactId>
@@ -122,5 +113,4 @@
 		</repository>
 	</distributionManagement>
 
-
 </project>
diff --git a/src/main/java/edu/illinois/cs/cogcomp/infer/ilp/GurobiHook.java b/src/main/java/edu/illinois/cs/cogcomp/infer/ilp/GurobiHook.java
index df3380b..ff956fb 100644
--- a/src/main/java/edu/illinois/cs/cogcomp/infer/ilp/GurobiHook.java
+++ b/src/main/java/edu/illinois/cs/cogcomp/infer/ilp/GurobiHook.java
@@ -1,460 +1,592 @@
 package edu.illinois.cs.cogcomp.infer.ilp;
 
-import gurobi.GRB;
-import gurobi.GRBEnv;
-import gurobi.GRBException;
-import gurobi.GRBLinExpr;
-import gurobi.GRBModel;
-import gurobi.GRBVar;
-import gurobi.GRB.DoubleAttr;
-import gurobi.GRB.StringAttr;
-
-import java.util.ArrayList;
-import java.util.List;
-
+import edu.illinois.cs.cogcomp.core.datastructures.vectors.DVector;
+import edu.illinois.cs.cogcomp.core.datastructures.vectors.OVector;
+import gurobi.*;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.util.Arrays;
+
+/**
+ * This is an interface to the <a href="http://www.gurobi.com">Gurobi Optimizer</a>. Make sure the
+ * jar file is on your <code>CLASSPATH</code> and that the Gurobi libraries are installed
+ * appropriately on your system before attempting to compile and use this class.
+ *
+ * @author Nick Rizzolo
+ **/
 public class GurobiHook implements ILPSolver {
 
-    private static int timeoutLimit = 200;
+    private int timelimit = 200;
+
+    // when solving without presolve how long should we take before
+    // timeout
+    private int noPresolveTimelimit = -1;
+
+    private static final Logger logger = LoggerFactory.getLogger(GurobiHook.class);
+
+    /** Prints an error message and exits the JVM. */
+    protected static void handleException(GRBException e) {
+        System.out.println("Gurobi error " + e.getErrorCode() + ": " + e.getMessage());
+        e.printStackTrace();
+        System.exit(-1);
+    }
+
+    /** The model to be optimized will be associated with this environment. */
+    protected GRBEnv environment;
+    /** The model to be optimized. */
+    protected GRBModel model;
+    /** The model's decision variables. */
+    protected OVector variables;
+    /** Contains all of the Gurobi SOS objects created for the model. */
+    protected OVector SOSes;
+    /** whether the optimization has been timed out */
+    private boolean isTimedOut;
+    /** whether the optimization has been unsatisfactory */
+    private boolean unsat;
+    /**
+     * Whether or not the <code>GRBModel.update()</code> method needs to be called before adding
+     * more constraints.
+     **/
+    protected boolean needsUpdate;
+    /** Whether or not the model has been solved. */
+    protected boolean isSolved;
+    /**
+     * Verbosity level. {@link ILPSolver#VERBOSITY_NONE} produces no incidental output. If set to
+     * {@link ILPSolver#VERBOSITY_LOW}, only variable and constraint counts are reported on
+     * <code>STDOUT</code>. If set to {@link ILPSolver#VERBOSITY_HIGH}, a textual representation of
+     * the entire optimization problem is also generated on <code>STDOUT</code>.
+     **/
+    protected int verbosity;
+    /**
+     * The coefficients of the variables in the objective function. This is redundant memory, and
+     * it's only being stored in the event that someone wants to call {@link #write(StringBuffer)}.
+     * Once we get Gurobi 4.0, we can discard of it.
+     **/
+    protected DVector objectiveCoefficients;
+
+    public void setTimeoutLimit(int limit) {
+        timelimit = limit;
+    }
+
+    /** Create a new Gurobi hook with the default environment parameters. */
+    public GurobiHook() {
+        this(ILPSolver.VERBOSITY_NONE);
+    }
+
+    /**
+     * Create a new Gurobi hook with the default environment parameters.
+     *
+     * @param v Setting for the {@link #verbosity} level.
+     **/
+    public GurobiHook(int v) {
+        try {
+            environment = new GRBEnv();
+            environment.set(GRB.IntParam.OutputFlag, 0); // no output
+
+            // how many threads can we use?
+            //environment.set(GRB.IntParam.Threads,
+            //        Math.min(8, Runtime.getRuntime().availableProcessors()));
+
+            // dump big stuff to filespace
+            //environment.set(GRB.DoubleParam.NodefileStart, 0.5);
+
+            //environment.set(GRB.DoubleParam.MIPGap, 1e-10);
+
+            //environment.set(GRB.IntParam.Presolve, 0);
+
+        } catch (GRBException e) {
+            handleException(e);
+        }
+        verbosity = v;
+        reset();
+    }
+
+    /**
+     * Create a new Gurobi hook with the specified environment.
+     *
+     * @param env An environment containing user-specified parameters.
+     **/
+    public GurobiHook(GRBEnv env) {
+        this(env, ILPSolver.VERBOSITY_NONE);
+    }
+
+    /**
+     * Create a new Gurobi hook with the specified environment.
+     *
+     * @param env An environment containing user-specified parameters.
+     * @param v Setting for the {@link #verbosity} level.
+     **/
+    public GurobiHook(GRBEnv env, int v) {
+        environment = env;
+        verbosity = v;
+        reset();
+    }
+
+
+    public boolean isTimedOut() {
+        return isTimedOut;
+    }
+
+    public boolean unsat() {
+        return unsat;
+    }
+
+    /**
+     * This method clears the all constraints and variables out of the ILP solver's problem
+     * representation, bringing the <code>ILPSolver</code> instance back to the state it was in when
+     * first constructed.
+     **/
+    public void reset() {
+        try {
+            model = new GRBModel(environment);
+        } catch (GRBException e) {
+            handleException(e);
+        }
+        variables = new OVector();
+        SOSes = new OVector();
+        objectiveCoefficients = new DVector();
+        needsUpdate = isSolved = false;
+    }
+
+
+    /**
+     * Sets the direction of the objective function.
+     *
+     * @param d <code>true</code> if the objective function is to be maximized.
+     **/
+    public void setMaximize(boolean d) {
+        try {
+            model.set(GRB.IntAttr.ModelSense, d ? -1 : 1);
+        } catch (GRBException e) {
+            handleException(e);
+        }
+    }
+
+
+    /**
+     * Adds a new Boolean variable (an integer variable constrained to take either the value 0 or
+     * the value 1) with the specified coefficient in the objective function to the problem.
+     *
+     * @param c The objective function coefficient for the new Boolean variable.
+     * @return The index of the created variable.
+     **/
+    public int addBooleanVariable(double c) {
+        int id = variables.size();
+        try {
+            variables.add(model.addVar(0, 1, c, GRB.BINARY, "x_" + id));
+        } catch (GRBException e) {
+            handleException(e);
+        }
+        // TODO: delete the line below once we get Gurobi 4.0
+        objectiveCoefficients.add(c);
+        needsUpdate = true;
+        return id;
+    }
+
+    public int addIntegerVariable(double c) {
+        int id = variables.size();
+        try {
+            variables.add(model.addVar(-GRB.INFINITY, GRB.INFINITY, c, GRB.INTEGER, "x_" + id));
+        } catch (GRBException e) {
+            handleException(e);
+        }
+        // TODO: delete the line below once we get Gurobi 4.0
+        objectiveCoefficients.add(c);
+        needsUpdate = true;
+        return id;
+    }
+
+    public int addRealVariable(double c) {
+        int id = variables.size();
+        try {
+            variables.add(model.addVar(-GRB.INFINITY, GRB.INFINITY, c, GRB.CONTINUOUS, "x_" + id));
+        } catch (GRBException e) {
+            handleException(e);
+        }
+        // TODO: delete the line below once we get Gurobi 4.0
+        objectiveCoefficients.add(c);
+        needsUpdate = true;
+        return id;
+    }
+
+
+    /**
+     * Adds a general, multi-valued discrete variable, which is implemented as a set of Boolean
+     * variables, one per value of the discrete variable, with exactly one of those variables set
+     * <code>true</code> at any given time.
+     *
+     * @param c The objective function coefficients for the new Boolean variables.
+     * @return The indexes of the newly created variables.
+     **/
+    public int[] addDiscreteVariable(double[] c) {
+        int[] result = new int[c.length];
+        for (int i = 0; i < c.length; ++i)
+            result[i] = addBooleanVariable(c[i]);
+
+        double[] w = new double[c.length];
+        Arrays.fill(w, 1);
+        addGreaterThanConstraint(result, w, 1);
+        try {
+            SOSes.add(model.addSOS(idsToVariables(result), w, GRB.SOS_TYPE1));
+        } catch (GRBException e) {
+            handleException(e);
+        }
+        return result;
+    }
+
+    /**
+     * Adds a new constraint to the problem with the specified type. This method is called by all
+     * the other <code>add*Constraint()</code> methods.
+     *
+     * @param i The indexes of the variables with non-zero coefficients.
+     * @param a The coefficients of the variables with the given indexes.
+     * @param b The new constraint will enforce (in)equality with this constant.
+     * @param t The type of linear inequality constraint to add.
+     **/
+    protected void addConstraint(int[] i, double[] a, double b, char t) {
+        if (needsUpdate) {
+            try {
+                model.update();
+            } catch (GRBException e) {
+                handleException(e);
+            }
+            needsUpdate = false;
+        }
+
+        try {
+            int constraints = model.get(GRB.IntAttr.NumConstrs);
+            model.addConstr(makeLinearExpression(i, a), t, b, "c_" + constraints);
+        } catch (GRBException e) {
+            handleException(e);
+        }
+    }
+
+
+    /**
+     * Adds a new fixed constraint to the problem. The two array arguments must be the same length,
+     * as their elements correspond to each other. Variables whose coefficients are zero need not be
+     * mentioned. Variables that are mentioned must have previously been added via
+     * {@link #addBooleanVariable(double)} or {@link #addDiscreteVariable(double[])}. The resulting
+     * constraint has the form: <blockquote> <code>x<sub>i</sub> * a = b</code> </blockquote> where
+     * <code>x<sub>i</sub></code> represents the inference variables whose indexes are contained in
+     * the array <code>i</code> and <code>*</code> represents dot product.
+     *
+     * @param i The indexes of the variables with non-zero coefficients.
+     * @param a The coefficients of the variables with the given indexes.
+     * @param b The new constraint will enforce equality with this constant.
+     **/
+    public void addEqualityConstraint(int[] i, double[] a, double b) {
+        addConstraint(i, a, b, GRB.EQUAL);
+    }
+
+
+    /**
+     * Adds a new lower bounded constraint to the problem. The two array arguments must be the same
+     * length, as their elements correspond to each other. Variables whose coefficients are zero
+     * need not be mentioned. Variables that are mentioned must have previously been added via
+     * {@link #addBooleanVariable(double)} or {@link #addDiscreteVariable(double[])}. The resulting
+     * constraint has the form: <blockquote> <code>x<sub>i</sub> * a &gt;= b</code> </blockquote>
+     * where <code>x<sub>i</sub></code> represents the inference variables whose indexes are
+     * contained in the array <code>i</code> and <code>*</code> represents dot product.
+     *
+     * @param i The indexes of the variables with non-zero coefficients.
+     * @param a The coefficients of the variables with the given indexes.
+     * @param b The lower bound for the new constraint.
+     **/
+    public void addGreaterThanConstraint(int[] i, double[] a, double b) {
+        addConstraint(i, a, b, GRB.GREATER_EQUAL);
+    }
+
+
+    /**
+     * Adds a new upper bounded constraint to the problem. The two array arguments must be the same
+     * length, as their elements correspond to each other. Variables whose coefficients are zero
+     * need not be mentioned. Variables that are mentioned must have previously been added via
+     * {@link #addBooleanVariable(double)} or {@link #addDiscreteVariable(double[])}. The resulting
+     * constraint has the form: <blockquote> <code>x<sub>i</sub> * a &lt;= b</code> </blockquote>
+     * where <code>x<sub>i</sub></code> represents the inference variables whose indexes are
+     * contained in the array <code>i</code> and <code>*</code> represents dot product.
+     *
+     * @param i The indexes of the variables with non-zero coefficients.
+     * @param a The coefficients of the variables with the given indexes.
+     * @param b The upper bound for the new constraint.
+     **/
+    public void addLessThanConstraint(int[] i, double[] a, double b) {
+        addConstraint(i, a, b, GRB.LESS_EQUAL);
+    }
+
+
+    public void printSolution() throws GRBException {
+        GRBVar[] varsAll = model.getVars();
+        for (int i = 0; i < varsAll.length; i++) {
+            GRBVar var = varsAll[i];
+            System.out.println("x_" + i + "\t" + var.get(GRB.StringAttr.VarName) + "\t"
+                    + var.get(GRB.DoubleAttr.Obj) + "\t" + var.get(GRB.DoubleAttr.X));
+        }
+    }
+
+    /**
+     * Solves the ILP problem, saving the solution internally.
+     *
+     * @return <code>true</code> iff a solution was found successfully.
+     **/
+    public boolean solve() throws Exception {
+        if (verbosity > ILPSolver.VERBOSITY_NONE) {
+            System.out.println("  variables: " + model.get(GRB.IntAttr.NumVars));
+            System.out.println("  constraints: " + model.get(GRB.IntAttr.NumConstrs));
+        }
+
+        assert model.getVars().length > 0 : "Model has no variables!";
+
+        // here we first attempt to presolve (some models can be solved quicker
+        // directly than the time it takes to presolve them)
+
+        isTimedOut = false;
+        boolean more = false; // should we perform more solving?
+        long start = System.currentTimeMillis();
+        // XXX: if you always see your solve time > noPresolveTimelimit then you
+        // probably want to set noPresolveTimelimit to 0 as you are always
+        // getting into the second solving loop.
+        if (noPresolveTimelimit > 0) {
+            environment.set(GRB.IntParam.Presolve, 0);
+            environment.set(GRB.DoubleParam.TimeLimit, noPresolveTimelimit);
+            model.optimize();
+            more = model.get(GRB.IntAttr.Status) == GRB.TIME_LIMIT;
+        }
+
+        // we may have timed out because noPresolveTimelimit is usually small
+        // we can solve big hard models quicker with presolve
+        if (more) {
+            logger.debug("Trying presolve version of problem");
+            environment.set(GRB.IntParam.Presolve, -1); // auto presolve settings
+            environment.set(GRB.DoubleParam.TimeLimit, timelimit);
+            model.optimize();
+        }
+
+        long end = System.currentTimeMillis();
+        logger.debug("Gurobi took {} ms and reported {} s", (end - start),
+                model.get(GRB.DoubleAttr.Runtime));
+
+        if (verbosity == ILPSolver.VERBOSITY_HIGH) {
+            StringBuffer buffer = new StringBuffer();
+            write(buffer);
+            System.out.print(buffer);
+        }
+
+        model.optimize();
+        int status = model.get(GRB.IntAttr.Status);
+        isSolved = status == GRB.OPTIMAL || status == GRB.SUBOPTIMAL;
+
+        unsat = false;
+        if (!isSolved) {
+            int statusNotSolved = model.get(GRB.IntAttr.Status);
+            logger.info("Gurobi returned with status code: {}", statusNotSolved);
+
+            if (status == GRB.TIME_LIMIT) {
+                isTimedOut = true;
+            }
+
+            if (status == GRB.INFEASIBLE) {
+                logger.info("Infeasable constraint set!");
+                model.computeIIS();
+                model.write("gurobi.ilp");
+                model.write("gurobi.lp");
+                logger.info("ILP information written to gurobi.ilp. Full LP written to gurobi.lp");
+                unsat = true;
+            }
+        }
+
+        return isSolved;
+    }
+
+    public void printModelStatus() throws GRBException {
+        int status = model.get(GRB.IntAttr.Status);
+        System.out.println("Model status: " + status);
+
+        if (status == GRB.INFEASIBLE) {
+            System.out.println("INFEASIBLE");
+        }
+    }
+
+    /**
+     * Tests whether the problem represented by this <code>ILPSolver</code> instance has been solved
+     * already.
+     **/
+    public boolean isSolved() {
+        return isSolved;
+    }
+
+    /**
+     * When the problem has been solved, use this method to retrieve the value of any Boolean
+     * inference variable. The result of this method is undefined when the problem has not yet been
+     * solved.
+     *
+     * @param index The index of the variable whose value is requested.
+     * @return The value of the variable.
+     **/
+    public boolean getBooleanValue(int index) {
+        if (!isSolved)
+            return false;
+        try {
+            double x = ((GRBVar) variables.get(index)).get(GRB.DoubleAttr.X);
+            return x > 0.5;
+        } catch (GRBException e) {
+            handleException(e);
+        }
+        return false;
+    }
+
+    @Override
+    public int getIntegerValue(int index) {
+        return 0;
+    }
+
+    @Override
+    public double getRealValue(int index) {
+        return 0;
+    }
+
+
+    /**
+     * When the problem has been solved, use this method to retrieve the value of the objective
+     * function at the solution. The result of this method is undefined when the problem has not yet
+     * been solved. If the problem had no feasible solutions, negative (positive, respectively)
+     * infinity will be returned if maximizing (minimizing).
+     *
+     * @return The value of the objective function at the solution.
+     **/
+    public double objectiveValue() {
+        try {
+            if (isSolved)
+                return model.get(GRB.DoubleAttr.ObjVal);
+            int status = model.get(GRB.IntAttr.Status);
+            if (status == GRB.INFEASIBLE || status == GRB.INF_OR_UNBD || status == GRB.UNBOUNDED)
+                return model.get(GRB.IntAttr.ModelSense) == -1 ? Double.NEGATIVE_INFINITY
+                        : Double.POSITIVE_INFINITY;
+        } catch (GRBException e) {
+            handleException(e);
+        }
+        return 0;
+    }
+
+
+    /**
+     * Given an array of variable indexes, this method returns the corresponding Gurobi variable
+     * objects in an array.
+     *
+     * @param ids The array of variable indexes.
+     * @return The corresponding Gurobi variable objects.
+     **/
+    protected GRBVar[] idsToVariables(int[] ids) {
+        GRBVar[] result = new GRBVar[ids.length];
+        for (int i = 0; i < ids.length; i++)
+            result[i] = (GRBVar) variables.get(ids[i]);
+        return result;
+    }
+
+
+    /**
+     * Creates a Gurobi linear expression object representing the dot product of the variables with
+     * the specified indexes and the specified coefficients.
+     *
+     * @param ids The indexes of the variables.
+     * @param c The corresponding coefficients.
+     * @return A Gurobi linear expression representing the dot product.
+     **/
+    protected GRBLinExpr makeLinearExpression(int[] ids, double[] c) {
+        try {
+            GRBLinExpr expr = new GRBLinExpr();
+            expr.addTerms(c, idsToVariables(ids));
+            return expr;
+        } catch (GRBException e) {
+            handleException(e);
+        }
+        return null;
+    }
+
 
-    public static void setTimeoutLimit(int limit)  {
-        timeoutLimit = limit;
+    /**
+     * Creates a textual representation of the ILP problem in an algebraic notation.
+     *
+     * @param buffer The created textual representation will be appended here.
+     **/
+    public void write(StringBuffer buffer) {
+        try {
+            model.update();
+            if (model.get(GRB.IntAttr.ModelSense) == -1)
+                buffer.append("max");
+            else
+                buffer.append("min");
+
+            // Using this bit of code until we get Gurobi 4.0 or higher.
+            for (int i = 0; i < objectiveCoefficients.size(); ++i) {
+                double c = objectiveCoefficients.get(i);
+                buffer.append(" ");
+                if (c >= 0)
+                    buffer.append("+");
+                buffer.append(c);
+                buffer.append(" x_");
+                buffer.append(i);
+            }
+
+            /*
+             * This code should work once we have Gurobi 4.0 or higher. Then we don't have to
+             * redundantly store objectiveCoefficients. GRBLinExpr objective = (GRBLinExpr)
+             * model.getObjective(); int objectiveSize = objective.size(); for (int i = 0; i <
+             * objectiveSize; ++i) { double c = objective.getCoeff(i); buffer.append(" "); if (c >=
+             * 0) buffer.append("+"); buffer.append(c); buffer.append(" ");
+             * buffer.append(objective.getVar(i).get(GRB.StringAttr.VarName)); }
+             */
+
+            buffer.append("\n");
+
+            int SOSesSize = SOSes.size();
+            for (int i = 0; i < SOSesSize; ++i) {
+                GRBSOS sos = (GRBSOS) SOSes.get(i);
+                int[] type = new int[1];
+                int size = model.getSOS(sos, null, null, type);
+                GRBVar[] sosVariables = new GRBVar[size];
+                model.getSOS(sos, sosVariables, new double[size], type);
+
+                buffer.append(" atmost 1 of (x in {");
+                for (int j = 0; j < size; ++j) {
+                    buffer.append(sosVariables[j].get(GRB.StringAttr.VarName));
+                    if (j + 1 < size)
+                        buffer.append(", ");
+                }
+                buffer.append("}) (x > 0)\n");
+            }
+
+            GRBConstr[] constraints = model.getConstrs();
+            for (int i = 0; i < constraints.length; ++i) {
+                GRBLinExpr row = model.getRow(constraints[i]);
+                int rowSize = row.size();
+                buffer.append(" ");
+
+                for (int j = 0; j < rowSize; ++j) {
+                    double c = row.getCoeff(j);
+                    buffer.append(" ");
+                    if (c >= 0)
+                        buffer.append("+");
+                    buffer.append(c);
+                    buffer.append(" ");
+                    buffer.append(row.getVar(j).get(GRB.StringAttr.VarName));
+                }
+
+                char type = constraints[i].get(GRB.CharAttr.Sense);
+                if (type == GRB.LESS_EQUAL)
+                    buffer.append(" <= ");
+                else if (type == GRB.GREATER_EQUAL)
+                    buffer.append(" >= ");
+                else
+                    buffer.append(" = ");
+
+                buffer.append(constraints[i].get(GRB.DoubleAttr.RHS));
+                buffer.append("\n");
+            }
+        } catch (GRBException e) {
+            handleException(e);
+        }
     }
-                                                 
-    
-	private static final Logger logger = LoggerFactory.getLogger(GurobiHook.class);
-
-	private static GRBEnv ENV;
-	static {
-		try {
-			ENV = new GRBEnv();
-			ENV.set(GRB.IntParam.OutputFlag, 0); // no output
-
-			// how many threads can we use?
-			ENV.set(GRB.IntParam.Threads,
-					Math.min(8, Runtime.getRuntime().availableProcessors()));
-
-			// dump big stuff to filespace
-			ENV.set(GRB.DoubleParam.NodefileStart, 0.5);
-
-			ENV.set(GRB.DoubleParam.MIPGap, 1e-10);
-
-			ENV.set(GRB.IntParam.Presolve, 0);
-
-		} catch (GRBException e) {
-			e.printStackTrace();
-			System.exit(1);
-		}
-	}
-
-        private final static int INTEGER = 0;
-        private final static int BOOLEAN = 1;
-        private final static int REAL = 2;
-            
-
-	private static class VariableInfo {
-		public double coeff;
-		public int variableType;
-
-		public VariableInfo(double c, int type) {
-			this.coeff = c;
-			this.variableType = type;
-		}
-	}
-
-	private GRBModel model;
-	private GRBEnv env;
-	private List<VariableInfo> obj;
-	private List<ILPConstraint> constrs;
-	private GRBVar[] vars;
-	private boolean hasUpdate;
-	private boolean isSolved;
-	protected int noPresolveTimelimit;
-	private int timelimit;
-	private boolean isTimedOut;
-	private boolean unsat;
-
-	private boolean maximize;
-
-	public GurobiHook() {
-		try {
-			// when solving without presolve how long should we take before
-			// timeout
-			noPresolveTimelimit = 30;
-			// when solving with presolve how long should we take before timeout
-			timelimit = timeoutLimit;
-			model = new GRBModel(ENV);
-			env = model.getEnv();
-			obj = new ArrayList<VariableInfo>();
-
-			constrs = new ArrayList<ILPConstraint>();
-			hasUpdate = false;
-			isSolved = false;
-		} catch (GRBException e) {
-			e.printStackTrace();
-			System.exit(-1);
-		}
-	}
-
-	public int addBooleanVariable(double arg0) {
-		int id = obj.size();
-		obj.add(new VariableInfo(arg0, BOOLEAN));
-		hasUpdate = true;
-		isSolved = false;
-		return id;
-	}
-
-    	public int addIntegerVariable(double arg0) {
-		int id = obj.size();
-		obj.add(new VariableInfo(arg0, INTEGER));
-		hasUpdate = true;
-		isSolved = false;
-		return id;
-	}
-
-
-	public int addRealVariable(double arg0) {
-		int id = obj.size();
-		obj.add(new VariableInfo(arg0, REAL));
-		hasUpdate = true;
-		isSolved = false;
-		return id;
-	}
-
-	public int[] addDiscreteVariable(double[] arg0) {
-		int[] ids = new int[arg0.length];
-		double[] objs = new double[arg0.length];
-
-		for (int i = 0; i < ids.length; i++) {
-			ids[i] = addBooleanVariable(arg0[i]);
-			objs[i] = 1;
-		}
-		addEqualityConstraint(ids, objs, 1);
-		return ids;
-	}
-
-	private GRBVar[] idsToVars(int[] ids) {
-		GRBVar[] result = new GRBVar[ids.length];
-		for (int i = 0; i < ids.length; i++) {
-			result[i] = vars[ids[i]];
-		}
-		return result;
-	}
-
-	private GRBLinExpr makeLinExpr(int[] ids, double[] coefs) {
-		try {
-			GRBLinExpr expr = new GRBLinExpr();
-			expr.addTerms(coefs, idsToVars(ids));
-			isSolved = false;
-			return expr;
-		} catch (GRBException e) {
-			e.printStackTrace();
-			System.exit(-1);
-		}
-		return null;
-	}
-
-	private void addConstraint(int[] ids, double[] coefs, double rhs, char sense) {
-
-		constrs.add(new ILPConstraint(ids, coefs, rhs, sense));
-		isSolved = false;
-	}
-
-	public void addEqualityConstraint(int[] arg0, double[] arg1, double arg2) {
-		addConstraint(arg0, arg1, arg2, ILPConstraint.EQUAL);
-	}
-
-	public void addGreaterThanConstraint(int[] arg0, double[] arg1, double arg2) {
-		addConstraint(arg0, arg1, arg2, ILPConstraint.GREATER_THAN);
-	}
-
-	public void addLessThanConstraint(int[] arg0, double[] arg1, double arg2) {
-		addConstraint(arg0, arg1, arg2, ILPConstraint.LESS_THAN);
-	}
-
-	public void printSolution() throws GRBException {
-		for (int i = 0; i < vars.length; i++) {
-			GRBVar var = vars[i];
-			System.out.println("x_" + i + "\t" + var.get(StringAttr.VarName)
-					+ "\t" + var.get(DoubleAttr.Obj) + "\t"
-					+ var.get(DoubleAttr.X));
-		}
-	}
-
-	public boolean getBooleanValue(int arg0) {
-		try {
-			if (!isSolved)
-				return false;
-			double x = vars[arg0].get(GRB.DoubleAttr.X);
-
-			return x > 0.5;
-		} catch (GRBException e) {
-			e.printStackTrace();
-			System.exit(-1);
-		}
-		return false;
-	}
-
-	public double getRealValue(int arg0) {
-		try {
-			if (!isSolved)
-				return Double.NaN;
-			double x = vars[arg0].get(GRB.DoubleAttr.X);
-
-			return x;
-		} catch (GRBException e) {
-			e.printStackTrace();
-			System.exit(-1);
-		}
-		return Double.NaN;
-	}
-
-    
-	public int getIntegerValue(int arg0) {
-		try {
-			if (!isSolved)
-                            return Integer.MIN_VALUE;
-			int x = (int) (vars[arg0].get(GRB.DoubleAttr.X));
-
-			return x;
-		} catch (GRBException e) {
-			e.printStackTrace();
-			System.exit(-1);
-		}
-                return Integer.MIN_VALUE;
-	}
-
-
-	public boolean isSolved() {
-		return isSolved;
-	}
-
-	public void reset() {
-		try {
-			model.reset();
-			obj.clear();
-			constrs.clear();
-			hasUpdate = false;
-			isSolved = false;
-			this.noPresolveTimelimit = 100;
-		} catch (GRBException e) {
-			e.printStackTrace();
-			System.exit(-1);
-		}
-
-	}
-
-	public void setMaximize(boolean arg0) {
-		this.maximize = arg0;
-		try {
-			if (arg0) {
-				model.set(GRB.IntAttr.ModelSense, -1);
-			} else {
-				model.set(GRB.IntAttr.ModelSense, +1);
-			}
-		} catch (GRBException e) {
-			e.printStackTrace();
-			System.exit(-1);
-		}
-
-	}
-
-	public boolean solve() throws Exception {
-
-		long start = System.currentTimeMillis();
-
-		// make the arrays to create the objective function and vars
-		if (obj.size() > 0) {
-			double[] coef = new double[obj.size()];
-			double[] lb = new double[obj.size()];
-			double[] ub = new double[obj.size()];
-			char[] type = new char[obj.size()];
-			String[] names = new String[obj.size()];
-			for (int i = 0; i < obj.size(); i++) {
-				VariableInfo info = obj.get(i);
-				coef[i] = info.coeff;
-
-				if (info.variableType == BOOLEAN) {
-					type[i] = GRB.BINARY;
-                                        lb[i] = 0.0;
-                                        ub[i] = 1.0;
-                                }
-				else if(info.variableType == REAL) {
-					type[i] = GRB.CONTINUOUS;
-                                        lb[i] = -GRB.INFINITY;
-                                        ub[i] = GRB.INFINITY;
-                                }
-                                else {
-                                    lb[i] = -GRB.INFINITY;
-                                    ub[i] = GRB.INFINITY;                                    
-                                    type[i] = GRB.INTEGER;
-                                }
-				names[i] = "x" + i;
-			}
-			hasUpdate = true;
-			vars = model.addVars(lb, ub, coef, type, names);
-			obj.clear(); // clear temp objective rep
-
-		}
-
-		// force model update if new vars were added
-		if (hasUpdate) {
-			model.update();
-			hasUpdate = false;
-		}
-
-		assert model.getVars().length > 0 : "Model has no variables!";
-
-		// add constraints
-		for (ILPConstraint c : constrs) {
-			model.addConstr(makeLinExpr(c.vars, c.coeffs), c.sense, c.rhs,
-					c.name);
-		}
-		// clear temporary constraint rep
-		constrs.clear();
-
-		long end = System.currentTimeMillis();
-		logger.debug("Took {} ms to update and set the constraints",
-				(end - start));
-
-		// here we first attempt to presolve (some models can be solved quicker
-		// directly than the time it takes to presolve them)
-
-		isTimedOut = false;
-		boolean more = true; // should we perform more solving?
-		start = System.currentTimeMillis();
-		// XXX: if you always see your solve time > noPresolveTimelimit then you
-		// probably want to set noPresolveTimelimit to 0 as you are always
-		// getting into the second solving loop.
-		if (noPresolveTimelimit > 0) {
-			env.set(GRB.IntParam.Presolve, 0);
-			env.set(GRB.DoubleParam.TimeLimit, noPresolveTimelimit);
-			model.optimize();
-			more = model.get(GRB.IntAttr.Status) == GRB.TIME_LIMIT;
-		}
-
-		// we may have timed out because noPresolveTimelimit is usually small
-		// we can solve big hard models quicker with presolve
-		if (more) {
-			logger.debug("Trying presolve version of problem");
-			env.set(GRB.IntParam.Presolve, -1); // auto presolve settings
-			env.set(GRB.DoubleParam.TimeLimit, timelimit);
-			model.optimize();
-		}
-
-		boolean result = model.get(GRB.IntAttr.Status) == GRB.OPTIMAL;
-
-		end = System.currentTimeMillis();
-		logger.debug("Gurobi took {} ms and reported {} s", (end - start),
-				model.get(GRB.DoubleAttr.Runtime));
-
-		unsat = false;
-		if (!result) {
-			int status = model.get(GRB.IntAttr.Status);
-			logger.info("Gurobi returned with status code: {}", status);
-
-			if (status == GRB.TIME_LIMIT) {
-				isTimedOut = true;
-			}
-
-			if (status == GRB.INFEASIBLE) {
-				logger.info("Infeasable constraint set!");
-
-				model.computeIIS();
-				model.write("gurobi.ilp");
-
-				model.write("gurobi.lp");
-
-				logger.info("ILP information written to gurobi.ilp. Full LP written to gurobi.lp");
-				result = false;
-				unsat = true;
-
-			}
-		}
-
-		isSolved = result;
-		return result;
-	}
-
-	public void printModelStatus() throws GRBException {
-		int status = model.get(GRB.IntAttr.Status);
-		System.out.println("Model status: " + status);
-
-		if (status == GRB.INFEASIBLE) {
-			System.out.println("INFEASIBLE");
-		}
-
-	}
-
-	public boolean isTimedOut() {
-		return isTimedOut;
-	}
-
-	public void write(StringBuffer buffer) {
-
-		if (maximize)
-			buffer.append("max");
-		else
-			buffer.append("min");
-
-		int variables = this.obj.size();
-		for (int i = 0; i < variables; ++i) {
-			double c = obj.get(i).coeff;
-			buffer.append(" ");
-			if (c >= 0)
-				buffer.append("+");
-			buffer.append(c);
-			buffer.append(" x_");
-			buffer.append(i);
-		}
-		buffer.append("\n");
-		buffer.append("Binary variables");
-		for (int i = 0; i < variables; ++i) {
-			if (obj.get(i).variableType == BOOLEAN) {
-				buffer.append("x_" + i + " ");
-			}
-		}
-		buffer.append("\n");
-
-		buffer.append("Integer variables");
-		for (int i = 0; i < variables; ++i) {
-			if (obj.get(i).variableType == INTEGER) {
-				buffer.append("x_" + i + " ");
-			}
-		}
-		buffer.append("\n");
-
-		buffer.append("Real variables");
-		for (int i = 0; i < variables; ++i) {
-			if (obj.get(i).variableType == REAL) {
-				buffer.append("x_" + i + " ");
-			}
-		}
-		buffer.append("\n");
-
-
-
-
-
-		for (ILPConstraint c : this.constrs) {
-			buffer.append(c.toString() + "\n");
-		}
-	}
-
-	public double objectiveValue() {
-		if (this.isSolved()) {
-			try {
-				return model.get(GRB.DoubleAttr.ObjVal);
-			} catch (GRBException e) {
-				throw new RuntimeException(e);
-			}
-		}
-		throw new RuntimeException("ILP not solved! Cannot get objective yet.");
-	}
-
-	public boolean unsat() {
-		return this.unsat;
-	}
 }
diff --git a/src/main/java/edu/illinois/cs/cogcomp/infer/ilp/ILPSolver.java b/src/main/java/edu/illinois/cs/cogcomp/infer/ilp/ILPSolver.java
index 2424909..e0f13b1 100644
--- a/src/main/java/edu/illinois/cs/cogcomp/infer/ilp/ILPSolver.java
+++ b/src/main/java/edu/illinois/cs/cogcomp/infer/ilp/ILPSolver.java
@@ -1,36 +1,178 @@
 package edu.illinois.cs.cogcomp.infer.ilp;
 
+/**
+ * Classes that implement this interface contain implementations of algorithms that solve Integer
+ * Linear Programming problems.
+ *
+ * @author Nick Rizzolo
+ **/
 public interface ILPSolver {
-
-	void setMaximize(boolean arg0);
-
-	int addBooleanVariable(double arg0);
-
-	int addRealVariable(double arg0);
-
-	int addIntegerVariable(double arg0);
-
-	int[] addDiscreteVariable(double[] arg0);
-
-	void addEqualityConstraint(int[] arg0, double[] arg1, double arg2);
-
-	void addGreaterThanConstraint(int[] arg0, double[] arg1, double arg2);
-
-	void addLessThanConstraint(int[] arg0, double[] arg1, double arg2);
-
-	boolean solve() throws Exception;
-
-	boolean isSolved();
-
-	boolean getBooleanValue(int arg0);
-
-	int getIntegerValue(int arg0);
-
-	double getRealValue(int arg0);
-
-	double objectiveValue();
-
-	void reset();
-
-	void write(java.lang.StringBuffer arg0);
+    /** A possible setting for {@link #verbosity}. */
+    public static final int VERBOSITY_NONE = 0;
+    /** A possible setting for {@link #verbosity}. */
+    public static final int VERBOSITY_LOW = 1;
+    /** A possible setting for {@link #verbosity}. */
+    public static final int VERBOSITY_HIGH = 2;
+
+    /**
+     * Sets the direction of the objective function.
+     *
+     * @param d <code>true</code> if the objective function is to be maximized.
+     **/
+    public void setMaximize(boolean d);
+
+
+    /**
+     * Adds a new Boolean variable (an integer variable constrained to take either the value 0 or
+     * the value 1) with the specified coefficient in the objective function to the problem.
+     *
+     * @param c The objective function coefficient for the new Boolean variable.
+     * @return The indexes of the created variable.
+     **/
+    public int addBooleanVariable(double c);
+
+    /**
+     * Adds a new Real variable with the specified coefficient in the objective function to the
+     * problem.
+     *
+     * @param c The objective function coefficient for the new Real variable.
+     * @return The indexes of the created variable.
+     **/
+    int addRealVariable(double c);
+
+    /**
+     * Adds a new Integer variable with the specified coefficient in the objective function to the
+     * problem.
+     *
+     * @param c The objective function coefficient for the new Integer variable.
+     * @return The indexes of the created variable.
+     **/
+    int addIntegerVariable(double c);
+
+    /**
+     * Adds a general, multi-valued discrete variable, which is implemented as a set of Boolean
+     * variables, one per value of the discrete variable, with exactly one of those variables set
+     * <code>true</code> at any given time.
+     *
+     * @param c The objective function coefficients for the new Boolean variables.
+     * @return The indexes of the newly created variables.
+     **/
+    public int[] addDiscreteVariable(double[] c);
+
+    /**
+     * Adds a new fixed constraint to the problem. The two array arguments must be the same length,
+     * as their elements correspond to each other. Variables whose coefficients are zero need not be
+     * mentioned. Variables that are mentioned must have previously been added via
+     * {@link #addBooleanVariable(double)} or {@link #addDiscreteVariable(double[])}. The resulting
+     * constraint has the form: <blockquote> <code>x<sub>i</sub> * a = b</code> </blockquote> where
+     * <code>x<sub>i</sub></code> represents the inference variables whose indexes are contained in
+     * the array <code>i</code> and <code>*</code> represents dot product.
+     *
+     * @param i The indexes of the variables with non-zero coefficients.
+     * @param a The coefficients of the variables with the given indexes.
+     * @param b The new constraint will enforce equality with this constant.
+     **/
+    public void addEqualityConstraint(int[] i, double[] a, double b);
+
+
+    /**
+     * Adds a new lower bounded constraint to the problem. The two array arguments must be the same
+     * length, as their elements correspond to each other. Variables whose coefficients are zero
+     * need not be mentioned. Variables that are mentioned must have previously been added via
+     * {@link #addBooleanVariable(double)} or {@link #addDiscreteVariable(double[])}. The resulting
+     * constraint has the form: <blockquote> <code>x<sub>i</sub> * a &gt;= b</code> </blockquote>
+     * where <code>x<sub>i</sub></code> represents the inference variables whose indexes are
+     * contained in the array <code>i</code> and <code>*</code> represents dot product.
+     *
+     * @param i The indexes of the variables with non-zero coefficients.
+     * @param a The coefficients of the variables with the given indexes.
+     * @param b The lower bound for the new constraint.
+     **/
+    public void addGreaterThanConstraint(int[] i, double[] a, double b);
+
+
+    /**
+     * Adds a new upper bounded constraint to the problem. The two array arguments must be the same
+     * length, as their elements correspond to each other. Variables whose coefficients are zero
+     * need not be mentioned. Variables that are mentioned must have previously been added via
+     * {@link #addBooleanVariable(double)} or {@link #addDiscreteVariable(double[])}. The resulting
+     * constraint has the form: <blockquote> <code>x<sub>i</sub> * a &lt;= b</code> </blockquote>
+     * where <code>x<sub>i</sub></code> represents the inference variables whose indexes are
+     * contained in the array <code>i</code> and <code>*</code> represents dot product.
+     *
+     * @param i The indexes of the variables with non-zero coefficients.
+     * @param a The coefficients of the variables with the given indexes.
+     * @param b The upper bound for the new constraint.
+     **/
+    public void addLessThanConstraint(int[] i, double[] a, double b);
+
+
+    /**
+     * Solves the ILP problem, saving the solution internally. This method may throw an exception if
+     * something doesn't go right.
+     **/
+    public boolean solve() throws Exception;
+
+
+    /**
+     * Tests whether the problem represented by this <code>ILPSolver</code> instance has been solved
+     * already.
+     **/
+    public boolean isSolved();
+
+
+    /**
+     * When the problem has been solved, use this method to retrieve the value of any Boolean
+     * inference variable. The result of this method is undefined when the problem has not yet been
+     * solved.
+     *
+     * @param index The index of the variable whose value is requested.
+     * @return The value of the variable.
+     **/
+    public boolean getBooleanValue(int index);
+
+    /**
+     * When the problem has been solved, use this method to retrieve the value of any Integer
+     * inference variable. The result of this method is undefined when the problem has not yet been
+     * solved.
+     *
+     * @param index The index of the variable whose value is requested.
+     * @return The value of the variable.
+     **/
+    int getIntegerValue(int index);
+
+    /**
+     * When the problem has been solved, use this method to retrieve the value of any Real inference
+     * variable. The result of this method is undefined when the problem has not yet been solved.
+     *
+     * @param index The index of the variable whose value is requested.
+     * @return The value of the variable.
+     **/
+    double getRealValue(int index);
+
+    /**
+     * When the problem has been solved, use this method to retrieve the value of the objective
+     * function at the solution. The result of this method is undefined when the problem has not yet
+     * been solved. If the problem had no feasible solutions, negative (positive, respectively)
+     * infinity will be returned if maximizing (minimizing).
+     *
+     * @return The value of the objective function at the solution.
+     **/
+    public double objectiveValue();
+
+
+    /**
+     * This method clears the all constraints and variables out of the ILP solver's problem
+     * representation, bringing the <code>ILPSolver</code> instance back to the state it was in when
+     * first constructed.
+     **/
+    public void reset();
+
+
+    /**
+     * Creates a textual representation of the ILP problem in an algebraic notation.
+     *
+     * @param buffer The created textual representation will be appended here.
+     **/
+    public void write(StringBuffer buffer);
 }
diff --git a/src/main/java/edu/illinois/cs/cogcomp/infer/ilp/JLISCuttingPlaneILPSolverGurobi.java b/src/main/java/edu/illinois/cs/cogcomp/infer/ilp/JLISCuttingPlaneILPSolverGurobi.java
index 0a891e7..e13aacb 100644
--- a/src/main/java/edu/illinois/cs/cogcomp/infer/ilp/JLISCuttingPlaneILPSolverGurobi.java
+++ b/src/main/java/edu/illinois/cs/cogcomp/infer/ilp/JLISCuttingPlaneILPSolverGurobi.java
@@ -15,278 +15,279 @@ import java.util.List;
  */
 public class JLISCuttingPlaneILPSolverGurobi implements ILPSolver {
 
-  private final static Logger log = LoggerFactory.getLogger(JLISCuttingPlaneILPSolverGurobi.class);
+    private final static Logger log = LoggerFactory
+            .getLogger(JLISCuttingPlaneILPSolverGurobi.class);
 
-  private static final int MAX_ITER = 100;
-  private final ILPSolver baseSolver;
-  boolean isSolved = false;
-  boolean hasUpdate = false;
-  boolean timedOut = false;
+    private static final int MAX_ITER = 100;
+    private final ILPSolver baseSolver;
+    boolean isSolved = false;
+    boolean hasUpdate = false;
+    boolean timedOut = false;
 
-  int numVariables = 0;
+    int numVariables = 0;
 
-  private IInstance x;
+    private IInstance x;
 
-  private InferenceVariableLexManager variables;
+    private InferenceVariableLexManager variables;
 
-  private final List<ILPConstraintGenerator> cuttingPlaneConstraints;
+    private final List<ILPConstraintGenerator> cuttingPlaneConstraints;
 
-  private ILPOutputGenerator outputGenerator;
+    private ILPOutputGenerator outputGenerator;
 
-  public JLISCuttingPlaneILPSolverGurobi(ILPSolver baseSolver) {
-    this.baseSolver = baseSolver;
-    cuttingPlaneConstraints = new ArrayList<ILPConstraintGenerator>();
-    hasUpdate = false;
-    isSolved = false;
+    public JLISCuttingPlaneILPSolverGurobi(ILPSolver baseSolver) {
+        this.baseSolver = baseSolver;
+        cuttingPlaneConstraints = new ArrayList<ILPConstraintGenerator>();
+        hasUpdate = false;
+        isSolved = false;
 
-  }
+    }
 
-  public void setOutputGenerator(ILPOutputGenerator outputGenerator) {
-    this.outputGenerator = outputGenerator;
-  }
+    public void setOutputGenerator(ILPOutputGenerator outputGenerator) {
+        this.outputGenerator = outputGenerator;
+    }
 
-  public void setInput(IInstance x) {
-    this.x = x;
-  }
+    public void setInput(IInstance x) {
+        this.x = x;
+    }
 
-  public void setVariableManager(InferenceVariableLexManager variables) {
-    this.variables = variables;
-  }
+    public void setVariableManager(InferenceVariableLexManager variables) {
+        this.variables = variables;
+    }
 
-  @Override
-  public int addBooleanVariable(double arg0) {
-    isSolved = false;
-    hasUpdate = true;
+    @Override
+    public int addBooleanVariable(double arg0) {
+        isSolved = false;
+        hasUpdate = true;
 
-    numVariables++;
+        numVariables++;
 
-    return baseSolver.addBooleanVariable(arg0);
-  }
+        return baseSolver.addBooleanVariable(arg0);
+    }
 
-  @Override
-  public int addRealVariable(double arg0) {
-    isSolved = false;
-    hasUpdate = true;
+    @Override
+    public int addRealVariable(double arg0) {
+        isSolved = false;
+        hasUpdate = true;
 
-    numVariables++;
+        numVariables++;
 
-    return baseSolver.addRealVariable(arg0);
-  }
+        return baseSolver.addRealVariable(arg0);
+    }
 
 
-  @Override
-  public int addIntegerVariable(double arg0) {
-    isSolved = false;
-    hasUpdate = true;
+    @Override
+    public int addIntegerVariable(double arg0) {
+        isSolved = false;
+        hasUpdate = true;
 
-    numVariables++;
+        numVariables++;
 
-    return baseSolver.addIntegerVariable(arg0);
-  }
+        return baseSolver.addIntegerVariable(arg0);
+    }
 
 
-  @Override
-  public int[] addDiscreteVariable(double[] arg0) {
-    isSolved = false;
-    hasUpdate = true;
+    @Override
+    public int[] addDiscreteVariable(double[] arg0) {
+        isSolved = false;
+        hasUpdate = true;
 
-    numVariables += arg0.length;
+        numVariables += arg0.length;
 
-    return baseSolver.addDiscreteVariable(arg0);
-  }
+        return baseSolver.addDiscreteVariable(arg0);
+    }
 
-  public void addEqualityConstraint(int[] arg0, double[] arg1, double arg2) {
-    this.baseSolver.addEqualityConstraint(arg0, arg1, arg2);
-    isSolved = false;
-  }
+    public void addEqualityConstraint(int[] arg0, double[] arg1, double arg2) {
+        this.baseSolver.addEqualityConstraint(arg0, arg1, arg2);
+        isSolved = false;
+    }
 
-  public void addGreaterThanConstraint(int[] arg0, double[] arg1, double arg2) {
-    this.baseSolver.addGreaterThanConstraint(arg0, arg1, arg2);
-    isSolved = false;
-  }
+    public void addGreaterThanConstraint(int[] arg0, double[] arg1, double arg2) {
+        this.baseSolver.addGreaterThanConstraint(arg0, arg1, arg2);
+        isSolved = false;
+    }
 
-  public void addLessThanConstraint(int[] arg0, double[] arg1, double arg2) {
-    this.baseSolver.addLessThanConstraint(arg0, arg1, arg2);
-    isSolved = false;
-  }
+    public void addLessThanConstraint(int[] arg0, double[] arg1, double arg2) {
+        this.baseSolver.addLessThanConstraint(arg0, arg1, arg2);
+        isSolved = false;
+    }
 
-  public void addCuttingPlaneConstraintGenerator(ILPConstraintGenerator c) {
-    this.cuttingPlaneConstraints.add(c);
-  }
+    public void addCuttingPlaneConstraintGenerator(ILPConstraintGenerator c) {
+        this.cuttingPlaneConstraints.add(c);
+    }
 
-  public boolean getBooleanValue(int arg0) {
-    return baseSolver.getBooleanValue(arg0);
-  }
+    public boolean getBooleanValue(int arg0) {
+        return baseSolver.getBooleanValue(arg0);
+    }
 
-  public double getRealValue(int arg0) {
-    return baseSolver.getRealValue(arg0);
-  }
+    public double getRealValue(int arg0) {
+        return baseSolver.getRealValue(arg0);
+    }
 
 
-  public int getIntegerValue(int arg0) {
-    return baseSolver.getIntegerValue(arg0);
-  }
+    public int getIntegerValue(int arg0) {
+        return baseSolver.getIntegerValue(arg0);
+    }
 
 
-  public boolean isSolved() {
-    return isSolved;
-  }
+    public boolean isSolved() {
+        return isSolved;
+    }
 
-  public double objectiveValue() {
-    return baseSolver.objectiveValue();
-  }
+    public double objectiveValue() {
+        return baseSolver.objectiveValue();
+    }
 
-  public void reset() {
+    public void reset() {
 
-    baseSolver.reset();
-    this.cuttingPlaneConstraints.clear();
-    this.isSolved = false;
-    this.hasUpdate = false;
-    this.timedOut = false;
-  }
+        baseSolver.reset();
+        this.cuttingPlaneConstraints.clear();
+        this.isSolved = false;
+        this.hasUpdate = false;
+        this.timedOut = false;
+    }
 
-  public void setMaximize(boolean arg0) {
-    baseSolver.setMaximize(arg0);
-  }
+    public void setMaximize(boolean arg0) {
+        baseSolver.setMaximize(arg0);
+    }
 
-  public boolean isTimedOut() {
-    return timedOut;
-  }
+    public boolean isTimedOut() {
+        return timedOut;
+    }
 
-  @Override
-  public boolean solve() throws Exception {
-    assert numVariables > 0 : "No variables added!";
+    @Override
+    public boolean solve() throws Exception {
+        assert numVariables > 0 : "No variables added!";
 
-    long start = System.currentTimeMillis();
-    int iteration = 0;
-    int numConstraints = 0;
+        long start = System.currentTimeMillis();
+        int iteration = 0;
+        int numConstraints = 0;
 
-    boolean solved = baseSolver.solve();
+        boolean solved = baseSolver.solve();
 
-    isSolved = false;
+        isSolved = false;
 
-    if (baseSolver instanceof GurobiHook) {
-      GurobiHook s = (GurobiHook) baseSolver;
-      timedOut = s.isTimedOut();
-      // s.noPresolveTimelimit = 0;
-    }
+        if (baseSolver instanceof GurobiHook) {
+            GurobiHook s = (GurobiHook) baseSolver;
+            timedOut = s.isTimedOut();
+            // s.noPresolveTimelimit = 0;
+        }
 
-    if (!solved) {
-      isSolved = false;
+        if (!solved) {
+            isSolved = false;
 
-    } else {
+        } else {
 
-      List<List<ILPConstraint>> violated = new ArrayList<List<ILPConstraint>>();
+            List<List<ILPConstraint>> violated = new ArrayList<List<ILPConstraint>>();
 
-      populateViolatedConstraints(violated);
-      
-      // XXX This is the wrong way to check if there are any violated constraints
-      //boolean constraintsSatisfied = violated.size() == 0;
-      boolean constraintsSatisfied = areConstraintsSatisfied(violated);
+            populateViolatedConstraints(violated);
 
-      while (!isSolved && !constraintsSatisfied && iteration < MAX_ITER) {
-        for (List<ILPConstraint> cList : violated) {
-          for (ILPConstraint c : cList) {
-            addConstraint(c);
-            numConstraints++;
-          }
-        }
+            // XXX This is the wrong way to check if there are any violated constraints
+            // boolean constraintsSatisfied = violated.size() == 0;
+            boolean constraintsSatisfied = areConstraintsSatisfied(violated);
 
-        log.debug("Added {} constraints", numConstraints);
+            while (!isSolved && !constraintsSatisfied && iteration < MAX_ITER) {
+                for (List<ILPConstraint> cList : violated) {
+                    for (ILPConstraint c : cList) {
+                        addConstraint(c);
+                        numConstraints++;
+                    }
+                }
 
-        solved = baseSolver.solve();
+                log.debug("Added {} constraints", numConstraints);
 
-        populateViolatedConstraints(violated);
+                solved = baseSolver.solve();
 
-        // XXX This is the wrong way to check if there are any violated constraints
-        //constraintsSatisfied = violated.size() == 0;
-        constraintsSatisfied = areConstraintsSatisfied(violated);
-        
-        if (!solved) {
-          isSolved = false;
-          if (baseSolver instanceof GurobiHook) {
-            GurobiHook s = (GurobiHook) baseSolver;
-            timedOut = s.isTimedOut();
+                populateViolatedConstraints(violated);
 
-            System.out.println("Status from gurobi model: ");
-            s.printModelStatus();
+                // XXX This is the wrong way to check if there are any violated constraints
+                // constraintsSatisfied = violated.size() == 0;
+                constraintsSatisfied = areConstraintsSatisfied(violated);
 
-            if (timedOut) {
-              log.error("Timed out!");
-            }
+                if (!solved) {
+                    isSolved = false;
+                    if (baseSolver instanceof GurobiHook) {
+                        GurobiHook s = (GurobiHook) baseSolver;
+                        timedOut = s.isTimedOut();
 
-            if (s.unsat()) {
-              log.error("Unsat!");
-              throw new RuntimeException("Unsat");
+                        System.out.println("Status from gurobi model: ");
+                        s.printModelStatus();
+
+                        if (timedOut) {
+                            log.error("Timed out!");
+                        }
+
+                        if (s.unsat()) {
+                            log.error("Unsat!");
+                            throw new RuntimeException("Unsat");
+                        }
+                    }
+
+                    break;
+                }
+
+                iteration++;
             }
-          }
 
-          break;
+            if (!constraintsSatisfied)
+                isSolved = false;
+            else
+                isSolved = true;
         }
+        long end = System.currentTimeMillis();
 
-        iteration++;
-      }
+        log.debug("Took {} ms and {} iterations. Added " + numConstraints + " constraints",
+                (end - start), iteration);
 
-      if (!constraintsSatisfied)
-        isSolved = false;
-      else
-        isSolved = true;
+        return isSolved;
+    }
+
+    private boolean areConstraintsSatisfied(List<List<ILPConstraint>> constraints) {
+        for (List<ILPConstraint> list : constraints) {
+            if (!list.isEmpty())
+                return false;
+        }
+        return true;
     }
-    long end = System.currentTimeMillis();
 
-    log.debug("Took {} ms and {} iterations. Added " + numConstraints
-            + " constraints", (end - start), iteration);
+    private void populateViolatedConstraints(List<List<ILPConstraint>> violated) {
+        violated.clear();
 
-    return isSolved;
-  }
-  
-  private boolean areConstraintsSatisfied(List<List<ILPConstraint>> constraints) {
-	  for (List<ILPConstraint> list : constraints) {
-		  if (!list.isEmpty()) return false;
-	  }
-	  return true;
-  }
+        IStructure y = outputGenerator.getOutput(baseSolver, variables, x);
 
-  private void populateViolatedConstraints(List<List<ILPConstraint>> violated) {
-    violated.clear();
+        for (ILPConstraintGenerator c : this.cuttingPlaneConstraints) {
 
-    IStructure y = outputGenerator.getOutput(baseSolver, variables, x);
+            // see if the constraint generator has anything to say about the input
+            // upon which it operates. If not, then use the input to the inference,
+            // namely x
+            IInstance input = c.getConstraintInput();
+            if (input == null)
+                input = x;
 
-    for (ILPConstraintGenerator c : this.cuttingPlaneConstraints) {
+            IStructure output = c.getConstraintOutput(y);
 
-      // see if the constraint generator has anything to say about the input
-      // upon which it operates. If not, then use the input to the inference,
-      // namely x
-      IInstance input = c.getConstraintInput();
-      if (input == null)
-        input = x;
+            List<ILPConstraint> cs = c.getViolatedILPConstraints(input, output, variables);
+            violated.add(cs);
+        }
+    }
 
-      IStructure output = c.getConstraintOutput(y);
+    public void printModelStatus() throws GRBException {
+        if (baseSolver instanceof GurobiHook) {
+            ((GurobiHook) baseSolver).printModelStatus();
+        }
+    }
 
-      List<ILPConstraint> cs = c.getViolatedILPConstraints(input, output,
-              variables);
-      violated.add(cs);
+    private void addConstraint(ILPConstraint c) {
+        if (c.sense == GRB.EQUAL)
+            baseSolver.addEqualityConstraint(c.vars, c.coeffs, c.rhs);
+        else if (c.sense == GRB.GREATER_EQUAL)
+            baseSolver.addGreaterThanConstraint(c.vars, c.coeffs, c.rhs);
+        else if (c.sense == GRB.LESS_EQUAL)
+            baseSolver.addLessThanConstraint(c.vars, c.coeffs, c.rhs);
+        else
+            assert false : c.sense;
     }
-  }
 
-  public void printModelStatus() throws GRBException {
-    if (baseSolver instanceof GurobiHook) {
-      ((GurobiHook) baseSolver).printModelStatus();
+    public void write(StringBuffer arg0) {
+        baseSolver.write(arg0);
     }
-  }
-
-  private void addConstraint(ILPConstraint c) {
-    if (c.sense == GRB.EQUAL)
-      baseSolver.addEqualityConstraint(c.vars, c.coeffs, c.rhs);
-    else if (c.sense == GRB.GREATER_EQUAL)
-      baseSolver.addGreaterThanConstraint(c.vars, c.coeffs, c.rhs);
-    else if (c.sense == GRB.LESS_EQUAL)
-      baseSolver.addLessThanConstraint(c.vars, c.coeffs, c.rhs);
-    else
-      assert false : c.sense;
-  }
-
-  public void write(StringBuffer arg0) {
-    baseSolver.write(arg0);
-  }
 }
diff --git a/src/test/java/edu/illinois/cs/cogcomp/infer/ilp/GurobiHookTest.java b/src/test/java/edu/illinois/cs/cogcomp/infer/ilp/GurobiHookTest.java
index ea27ea7..4069074 100644
--- a/src/test/java/edu/illinois/cs/cogcomp/infer/ilp/GurobiHookTest.java
+++ b/src/test/java/edu/illinois/cs/cogcomp/infer/ilp/GurobiHookTest.java
@@ -3,45 +3,48 @@ package edu.illinois.cs.cogcomp.infer.ilp;
 import gurobi.*;
 import org.junit.Test;
 
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertEquals;
 
 
 public class GurobiHookTest {
 
     @Test
     public void testGurobi() throws GRBException {
-        GRBEnv env = new GRBEnv();
-        GRBModel model = new GRBModel(env);
-
-        // Create variables
-        GRBVar x = model.addVar(0.0, 1.0, -1.0, GRB.BINARY, "x");
-        GRBVar y = model.addVar(0.0, 1.0, -1.0, GRB.BINARY, "y");
-        GRBVar z = model.addVar(0.0, 1.0, -2.0, GRB.BINARY, "z");
-
-        // Integrate new variables
-
-        model.update();
-
-        // Add constraint: x + 2 y + 3 z <= 4
-        GRBLinExpr expr = new GRBLinExpr();
-        expr.addTerm(1.0, x);
-        expr.addTerm(2.0, y);
-        expr.addTerm(3, z);
-        model.addConstr(expr, GRB.LESS_EQUAL, 4.0, "c0");
-
-        // Add constraint: x + y >= 1
-        expr = new GRBLinExpr();
-        expr.addTerm(1.0, x);
-        expr.addTerm(1.0, y);
-        model.addConstr(expr, GRB.GREATER_EQUAL, 1.0, "c1");
-
-        // Optimize model
-        model.optimize();
-
-        assertEquals("x", x.get(GRB.StringAttr.VarName));
-        assertEquals(1.0, x.get(GRB.DoubleAttr.X), 0.0);
-        assertEquals(0.0, y.get(GRB.DoubleAttr.X), 0.0);
-        assertEquals(1.0, z.get(GRB.DoubleAttr.X), 0.0);
-        assertEquals(-3.0, model.get(GRB.DoubleAttr.ObjVal), 0.0);
+        try {
+            GRBEnv env = new GRBEnv();
+            GRBModel model = new GRBModel(env);
+
+            // Create variables
+            GRBVar x = model.addVar(0.0, 1.0, -1.0, GRB.BINARY, "x");
+            GRBVar y = model.addVar(0.0, 1.0, -1.0, GRB.BINARY, "y");
+            GRBVar z = model.addVar(0.0, 1.0, -2.0, GRB.BINARY, "z");
+
+            // Integrate new variables
+            model.update();
+
+            // Add constraint: x + 2 y + 3 z <= 4
+            GRBLinExpr expr = new GRBLinExpr();
+            expr.addTerm(1.0, x);
+            expr.addTerm(2.0, y);
+            expr.addTerm(3, z);
+            model.addConstr(expr, GRB.LESS_EQUAL, 4.0, "c0");
+
+            // Add constraint: x + y >= 1
+            expr = new GRBLinExpr();
+            expr.addTerm(1.0, x);
+            expr.addTerm(1.0, y);
+            model.addConstr(expr, GRB.GREATER_EQUAL, 1.0, "c1");
+
+            // Optimize model
+            model.optimize();
+
+            assertEquals("x", x.get(GRB.StringAttr.VarName));
+            assertEquals(1.0, x.get(GRB.DoubleAttr.X), 0.0);
+            assertEquals(0.0, y.get(GRB.DoubleAttr.X), 0.0);
+            assertEquals(1.0, z.get(GRB.DoubleAttr.X), 0.0);
+            assertEquals(-3.0, model.get(GRB.DoubleAttr.ObjVal), 0.0);
+        } catch (UnsatisfiedLinkError e) {
+            System.out.println("\n\n**** GUROBI LICENSE NOT FOUND! SKIPPING THE TEST ****\n\n");
+        }
     }
-}
\ No newline at end of file
+}
-- 
GitLab