Skip to content
Snippets Groups Projects
Commit f4ca1eec authored by Christos Christodoulopoulos's avatar Christos Christodoulopoulos
Browse files

Merge branch 'daniel_mergeWithLBjavaIlpSolver' into 'master'

merging with lbjava's ilp-solver interface

Changes: 
- merges the two `ILPSolver` interfaces (in this package and the one in LBJava)
- merging the two gurobi solvers:  for most of the syntax I have used that of LBJava's. 

If this get's merged, we can remove `IlpSolver.java`, `GurobiHook.java` and `oJAlgoHook.java` from lbjava and add this `illinios-inference` as LBJava's dependency.  

FYI @mssammon 

See merge request !1
parents ceb601b9 16dac70b
No related branches found
No related tags found
1 merge request!1merging with lbjava's ilp-solver interface
Pipeline #
# 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"
```
...@@ -14,4 +14,7 @@ Made Gurobi an optional dependency ...@@ -14,4 +14,7 @@ Made Gurobi an optional dependency
Version 0.5.0 Version 0.5.0
Added the oj! Algorithm (ojAlgo) solver Added the oj! Algorithm (ojAlgo) solver
Updated dependencies Updated dependencies
\ No newline at end of file
Version 0.6.0
Moving augmenting the current ilp-solvers with lbjava's ilp-solver
\ No newline at end of file
...@@ -4,17 +4,15 @@ ...@@ -4,17 +4,15 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>edu.illinois.cs.cogcomp</groupId> <groupId>edu.illinois.cs.cogcomp</groupId>
<artifactId>inference</artifactId> <artifactId>illinois-inference</artifactId>
<packaging>jar</packaging> <packaging>jar</packaging>
<version>0.5.0</version> <version>0.6.0</version>
<properties> <properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
</properties> </properties>
<repositories> <repositories>
<repository> <repository>
<id>CogcompSoftware</id> <id>CogcompSoftware</id>
...@@ -23,39 +21,12 @@ ...@@ -23,39 +21,12 @@
</repository> </repository>
</repositories> </repositories>
<dependencies> <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> <dependency>
<groupId>edu.illinois.cs.cogcomp</groupId> <groupId>edu.illinois.cs.cogcomp</groupId>
<artifactId>illinois-core-utilities</artifactId> <artifactId>illinois-core-utilities</artifactId>
<version>3.0.27</version> <version>3.0.51</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>edu.illinois.cs.cogcomp</groupId> <groupId>edu.illinois.cs.cogcomp</groupId>
<artifactId>illinois-sl-core</artifactId> <artifactId>illinois-sl-core</artifactId>
...@@ -67,12 +38,27 @@ ...@@ -67,12 +38,27 @@
</exclusion> </exclusion>
</exclusions> </exclusions>
</dependency> </dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency> <dependency>
<groupId>org.ojalgo</groupId> <groupId>org.ojalgo</groupId>
<artifactId>ojalgo</artifactId> <artifactId>ojalgo</artifactId>
<version>37.1.1</version> <version>37.1.1</version>
</dependency> </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>
</dependencies> </dependencies>
<build> <build>
...@@ -122,5 +108,4 @@ ...@@ -122,5 +108,4 @@
</repository> </repository>
</distributionManagement> </distributionManagement>
</project> </project>
package edu.illinois.cs.cogcomp.infer.ilp; package edu.illinois.cs.cogcomp.infer.ilp;
import gurobi.GRB; import edu.illinois.cs.cogcomp.core.datastructures.vectors.DVector;
import gurobi.GRBEnv; import edu.illinois.cs.cogcomp.core.datastructures.vectors.OVector;
import gurobi.GRBException; import gurobi.*;
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 org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; 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 { 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;
}
} }
package edu.illinois.cs.cogcomp.infer.ilp; 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 { public interface ILPSolver {
/** A possible setting for {@link #verbosity}. */
void setMaximize(boolean arg0); public static final int VERBOSITY_NONE = 0;
/** A possible setting for {@link #verbosity}. */
int addBooleanVariable(double arg0); public static final int VERBOSITY_LOW = 1;
/** A possible setting for {@link #verbosity}. */
int addRealVariable(double arg0); public static final int VERBOSITY_HIGH = 2;
int addIntegerVariable(double arg0); /**
* Sets the direction of the objective function.
int[] addDiscreteVariable(double[] arg0); *
* @param d <code>true</code> if the objective function is to be maximized.
void addEqualityConstraint(int[] arg0, double[] arg1, double arg2); **/
public void setMaximize(boolean d);
void addGreaterThanConstraint(int[] arg0, double[] arg1, double arg2);
void addLessThanConstraint(int[] arg0, double[] arg1, double arg2); /**
* Adds a new Boolean variable (an integer variable constrained to take either the value 0 or
boolean solve() throws Exception; * the value 1) with the specified coefficient in the objective function to the problem.
*
boolean isSolved(); * @param c The objective function coefficient for the new Boolean variable.
* @return The indexes of the created variable.
boolean getBooleanValue(int arg0); **/
public int addBooleanVariable(double c);
int getIntegerValue(int arg0);
/**
double getRealValue(int arg0); * Adds a new Real variable with the specified coefficient in the objective function to the
* problem.
double objectiveValue(); *
* @param c The objective function coefficient for the new Real variable.
void reset(); * @return The indexes of the created variable.
**/
void write(java.lang.StringBuffer arg0); 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);
} }
...@@ -15,278 +15,279 @@ import java.util.List; ...@@ -15,278 +15,279 @@ import java.util.List;
*/ */
public class JLISCuttingPlaneILPSolverGurobi implements ILPSolver { 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 static final int MAX_ITER = 100;
private final ILPSolver baseSolver; private final ILPSolver baseSolver;
boolean isSolved = false; boolean isSolved = false;
boolean hasUpdate = false; boolean hasUpdate = false;
boolean timedOut = 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) { public JLISCuttingPlaneILPSolverGurobi(ILPSolver baseSolver) {
this.baseSolver = baseSolver; this.baseSolver = baseSolver;
cuttingPlaneConstraints = new ArrayList<ILPConstraintGenerator>(); cuttingPlaneConstraints = new ArrayList<ILPConstraintGenerator>();
hasUpdate = false; hasUpdate = false;
isSolved = false; isSolved = false;
} }
public void setOutputGenerator(ILPOutputGenerator outputGenerator) { public void setOutputGenerator(ILPOutputGenerator outputGenerator) {
this.outputGenerator = outputGenerator; this.outputGenerator = outputGenerator;
} }
public void setInput(IInstance x) { public void setInput(IInstance x) {
this.x = x; this.x = x;
} }
public void setVariableManager(InferenceVariableLexManager variables) { public void setVariableManager(InferenceVariableLexManager variables) {
this.variables = variables; this.variables = variables;
} }
@Override @Override
public int addBooleanVariable(double arg0) { public int addBooleanVariable(double arg0) {
isSolved = false; isSolved = false;
hasUpdate = true; hasUpdate = true;
numVariables++; numVariables++;
return baseSolver.addBooleanVariable(arg0); return baseSolver.addBooleanVariable(arg0);
} }
@Override @Override
public int addRealVariable(double arg0) { public int addRealVariable(double arg0) {
isSolved = false; isSolved = false;
hasUpdate = true; hasUpdate = true;
numVariables++; numVariables++;
return baseSolver.addRealVariable(arg0); return baseSolver.addRealVariable(arg0);
} }
@Override @Override
public int addIntegerVariable(double arg0) { public int addIntegerVariable(double arg0) {
isSolved = false; isSolved = false;
hasUpdate = true; hasUpdate = true;
numVariables++; numVariables++;
return baseSolver.addIntegerVariable(arg0); return baseSolver.addIntegerVariable(arg0);
} }
@Override @Override
public int[] addDiscreteVariable(double[] arg0) { public int[] addDiscreteVariable(double[] arg0) {
isSolved = false; isSolved = false;
hasUpdate = true; 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) { public void addEqualityConstraint(int[] arg0, double[] arg1, double arg2) {
this.baseSolver.addEqualityConstraint(arg0, arg1, arg2); this.baseSolver.addEqualityConstraint(arg0, arg1, arg2);
isSolved = false; isSolved = false;
} }
public void addGreaterThanConstraint(int[] arg0, double[] arg1, double arg2) { public void addGreaterThanConstraint(int[] arg0, double[] arg1, double arg2) {
this.baseSolver.addGreaterThanConstraint(arg0, arg1, arg2); this.baseSolver.addGreaterThanConstraint(arg0, arg1, arg2);
isSolved = false; isSolved = false;
} }
public void addLessThanConstraint(int[] arg0, double[] arg1, double arg2) { public void addLessThanConstraint(int[] arg0, double[] arg1, double arg2) {
this.baseSolver.addLessThanConstraint(arg0, arg1, arg2); this.baseSolver.addLessThanConstraint(arg0, arg1, arg2);
isSolved = false; isSolved = false;
} }
public void addCuttingPlaneConstraintGenerator(ILPConstraintGenerator c) { public void addCuttingPlaneConstraintGenerator(ILPConstraintGenerator c) {
this.cuttingPlaneConstraints.add(c); this.cuttingPlaneConstraints.add(c);
} }
public boolean getBooleanValue(int arg0) { public boolean getBooleanValue(int arg0) {
return baseSolver.getBooleanValue(arg0); return baseSolver.getBooleanValue(arg0);
} }
public double getRealValue(int arg0) { public double getRealValue(int arg0) {
return baseSolver.getRealValue(arg0); return baseSolver.getRealValue(arg0);
} }
public int getIntegerValue(int arg0) { public int getIntegerValue(int arg0) {
return baseSolver.getIntegerValue(arg0); return baseSolver.getIntegerValue(arg0);
} }
public boolean isSolved() { public boolean isSolved() {
return isSolved; return isSolved;
} }
public double objectiveValue() { public double objectiveValue() {
return baseSolver.objectiveValue(); return baseSolver.objectiveValue();
} }
public void reset() { public void reset() {
baseSolver.reset(); baseSolver.reset();
this.cuttingPlaneConstraints.clear(); this.cuttingPlaneConstraints.clear();
this.isSolved = false; this.isSolved = false;
this.hasUpdate = false; this.hasUpdate = false;
this.timedOut = false; this.timedOut = false;
} }
public void setMaximize(boolean arg0) { public void setMaximize(boolean arg0) {
baseSolver.setMaximize(arg0); baseSolver.setMaximize(arg0);
} }
public boolean isTimedOut() { public boolean isTimedOut() {
return timedOut; return timedOut;
} }
@Override @Override
public boolean solve() throws Exception { public boolean solve() throws Exception {
assert numVariables > 0 : "No variables added!"; assert numVariables > 0 : "No variables added!";
long start = System.currentTimeMillis(); long start = System.currentTimeMillis();
int iteration = 0; int iteration = 0;
int numConstraints = 0; int numConstraints = 0;
boolean solved = baseSolver.solve(); boolean solved = baseSolver.solve();
isSolved = false; isSolved = false;
if (baseSolver instanceof GurobiHook) { if (baseSolver instanceof GurobiHook) {
GurobiHook s = (GurobiHook) baseSolver; GurobiHook s = (GurobiHook) baseSolver;
timedOut = s.isTimedOut(); timedOut = s.isTimedOut();
// s.noPresolveTimelimit = 0; // s.noPresolveTimelimit = 0;
} }
if (!solved) { if (!solved) {
isSolved = false; isSolved = false;
} else { } else {
List<List<ILPConstraint>> violated = new ArrayList<List<ILPConstraint>>(); List<List<ILPConstraint>> violated = new ArrayList<List<ILPConstraint>>();
populateViolatedConstraints(violated); 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);
while (!isSolved && !constraintsSatisfied && iteration < MAX_ITER) { // XXX This is the wrong way to check if there are any violated constraints
for (List<ILPConstraint> cList : violated) { // boolean constraintsSatisfied = violated.size() == 0;
for (ILPConstraint c : cList) { boolean constraintsSatisfied = areConstraintsSatisfied(violated);
addConstraint(c);
numConstraints++;
}
}
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 populateViolatedConstraints(violated);
//constraintsSatisfied = violated.size() == 0;
constraintsSatisfied = areConstraintsSatisfied(violated);
if (!solved) {
isSolved = false;
if (baseSolver instanceof GurobiHook) {
GurobiHook s = (GurobiHook) baseSolver;
timedOut = s.isTimedOut();
System.out.println("Status from gurobi model: "); // XXX This is the wrong way to check if there are any violated constraints
s.printModelStatus(); // constraintsSatisfied = violated.size() == 0;
constraintsSatisfied = areConstraintsSatisfied(violated);
if (timedOut) { if (!solved) {
log.error("Timed out!"); isSolved = false;
} if (baseSolver instanceof GurobiHook) {
GurobiHook s = (GurobiHook) baseSolver;
timedOut = s.isTimedOut();
if (s.unsat()) { System.out.println("Status from gurobi model: ");
log.error("Unsat!"); s.printModelStatus();
throw new RuntimeException("Unsat");
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) return isSolved;
isSolved = false; }
else
isSolved = true; 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 private void populateViolatedConstraints(List<List<ILPConstraint>> violated) {
+ " constraints", (end - start), iteration); violated.clear();
return isSolved; IStructure y = outputGenerator.getOutput(baseSolver, variables, x);
}
private boolean areConstraintsSatisfied(List<List<ILPConstraint>> constraints) {
for (List<ILPConstraint> list : constraints) {
if (!list.isEmpty()) return false;
}
return true;
}
private void populateViolatedConstraints(List<List<ILPConstraint>> violated) { for (ILPConstraintGenerator c : this.cuttingPlaneConstraints) {
violated.clear();
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 List<ILPConstraint> cs = c.getViolatedILPConstraints(input, output, variables);
// upon which it operates. If not, then use the input to the inference, violated.add(cs);
// namely x }
IInstance input = c.getConstraintInput(); }
if (input == null)
input = x;
IStructure output = c.getConstraintOutput(y); public void printModelStatus() throws GRBException {
if (baseSolver instanceof GurobiHook) {
((GurobiHook) baseSolver).printModelStatus();
}
}
List<ILPConstraint> cs = c.getViolatedILPConstraints(input, output, private void addConstraint(ILPConstraint c) {
variables); if (c.sense == GRB.EQUAL)
violated.add(cs); 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 { public void write(StringBuffer arg0) {
if (baseSolver instanceof GurobiHook) { baseSolver.write(arg0);
((GurobiHook) baseSolver).printModelStatus();
} }
}
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);
}
} }
...@@ -3,45 +3,48 @@ package edu.illinois.cs.cogcomp.infer.ilp; ...@@ -3,45 +3,48 @@ package edu.illinois.cs.cogcomp.infer.ilp;
import gurobi.*; import gurobi.*;
import org.junit.Test; import org.junit.Test;
import static org.junit.Assert.*; import static org.junit.Assert.assertEquals;
public class GurobiHookTest { public class GurobiHookTest {
@Test @Test
public void testGurobi() throws GRBException { public void testGurobi() throws GRBException {
GRBEnv env = new GRBEnv(); try {
GRBModel model = new GRBModel(env); GRBEnv env = new GRBEnv();
GRBModel model = new GRBModel(env);
// Create variables
GRBVar x = model.addVar(0.0, 1.0, -1.0, GRB.BINARY, "x"); // Create variables
GRBVar y = model.addVar(0.0, 1.0, -1.0, GRB.BINARY, "y"); GRBVar x = model.addVar(0.0, 1.0, -1.0, GRB.BINARY, "x");
GRBVar z = model.addVar(0.0, 1.0, -2.0, GRB.BINARY, "z"); 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
// Integrate new variables
model.update(); model.update();
// Add constraint: x + 2 y + 3 z <= 4 // Add constraint: x + 2 y + 3 z <= 4
GRBLinExpr expr = new GRBLinExpr(); GRBLinExpr expr = new GRBLinExpr();
expr.addTerm(1.0, x); expr.addTerm(1.0, x);
expr.addTerm(2.0, y); expr.addTerm(2.0, y);
expr.addTerm(3, z); expr.addTerm(3, z);
model.addConstr(expr, GRB.LESS_EQUAL, 4.0, "c0"); model.addConstr(expr, GRB.LESS_EQUAL, 4.0, "c0");
// Add constraint: x + y >= 1 // Add constraint: x + y >= 1
expr = new GRBLinExpr(); expr = new GRBLinExpr();
expr.addTerm(1.0, x); expr.addTerm(1.0, x);
expr.addTerm(1.0, y); expr.addTerm(1.0, y);
model.addConstr(expr, GRB.GREATER_EQUAL, 1.0, "c1"); model.addConstr(expr, GRB.GREATER_EQUAL, 1.0, "c1");
// Optimize model // Optimize model
model.optimize(); model.optimize();
assertEquals("x", x.get(GRB.StringAttr.VarName)); assertEquals("x", x.get(GRB.StringAttr.VarName));
assertEquals(1.0, x.get(GRB.DoubleAttr.X), 0.0); assertEquals(1.0, x.get(GRB.DoubleAttr.X), 0.0);
assertEquals(0.0, y.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(1.0, z.get(GRB.DoubleAttr.X), 0.0);
assertEquals(-3.0, model.get(GRB.DoubleAttr.ObjVal), 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
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment