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 >= 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 <= 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 >= 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 <= 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