/** * */ package vroom.common.utilities.gurobi; import gurobi.GRB; import gurobi.GRB.CharAttr; import gurobi.GRB.DoubleAttr; import gurobi.GRB.IntAttr; import gurobi.GRB.StringAttr; import gurobi.GRBConstr; import gurobi.GRBException; import gurobi.GRBModel; import gurobi.GRBVar; import java.util.Collections; import java.util.HashSet; import java.util.Set; import vroom.common.utilities.lp.SolverStatus; /** * <code>GRBUtilities</code> is a class providing convenience methods when working with gurobi models. * <p> * Creation date: Jul 6, 2010 - 4:58:26 PM * * @author Victor Pillac, <a href="http://uniandes.edu.co">Universidad de Los Andes</a>-<a * href="http://copa.uniandes.edu.co">Copa</a> <a href="http://www.emn.fr">Ecole des Mines de Nantes</a>-<a * href="http://www.irccyn.ec-nantes.fr/irccyn/d/en/equipes/Slp">SLP</a> * @version 1.0 */ public class GRBUtilities { /** * Returns a string describing the given variable * * @param var * @return a string describing the given variable */ public static String varToString(GRBVar var) { try { return String.format("%s [%s,%s] c=%.3f: %s", var.get(StringAttr.VarName), var.get(DoubleAttr.LB), var.get(DoubleAttr.UB), var.get(DoubleAttr.Obj), var.get(CharAttr.VType)); } catch (GRBException e) { return String.format("%s:%s", var, e.getMessage()); } } /** * Returns a string describing the given constraint * * @param ctr * @param model * @return a string describing the given constraint */ public static String constrToString(GRBConstr ctr, GRBModel model) { StringBuilder row = new StringBuilder(); for (GRBVar v : model.getVars()) { try { double coef = model.getCoeff(ctr, v); if (coef != 0) { row.append(String.format("%s*%s ", coefToString(coef), v.get(StringAttr.VarName))); } } catch (GRBException e) { row.append(e.getMessage()); } } try { return String.format("%s\t: %s %s %s", ctr.get(StringAttr.ConstrName), row, ctr.get(CharAttr.Sense), ctr.get(DoubleAttr.RHS)); } catch (GRBException e) { return String.format("%s:%s", ctr, e.getMessage()); } } /** * Returns a string describing the given coefficient * * @param coef * @return a string describing the given coefficient */ public static String coefToString(double coef) { String sign = ""; if (coef > 0) { sign = "+"; } return String.format("%s%s", sign, coef); } /** * <ul> * <li>LOADED Model is loaded, but no solution information is available.</li> * <li>OPTIMAL Model was solved to optimality (subject to tolerances), and an optimal solution is available.</li> * <li>INFEASIBLE Model was proven to be infeasible.</li> * <li>INF_OR_UNBD Model was proven to be either infeasible or unbounded.</li> * <li>UNBOUNDED Model was proven to be unbounded.</li> * <li>CUTOFF Optimal objective for model was proven to be worse than the value specified in the Cutoff parameter. * No solution information is available.</li> * <li>ITERATION_LIMIT Optimization terminated because the total number of simplex iterations performed exceeded the * value specified in the IterationLimit parameter.</li> * <li>NODE_LIMIT Optimization terminated because the total number of branch-and-cut nodes explored exceeded the * value specified in the NodeLimit parameter.</li> * <li>TIME_LIMIT Optimization terminated because the time expended exceeded the value specified in the TimeLimit * parameter.</li> * <li>SOLUTION_LIMIT Optimization terminated because the number of solutions found reached the value specified in * the SolutionLimit parameter.</li> * <li>INTERRUPTED Optimization was terminated by the user.</li> * <li>NUMERIC Optimization was terminated due to unrecoverable numerical difficulties.</li> * <ul/> * * @param status * @return a string describing the given status code */ public static String statusToString(int status) { switch (status) { case GRB.LOADED: return "LOADED"; case GRB.OPTIMAL: return "OPTIMAL"; case GRB.INFEASIBLE: return "INFEASIBLE"; case GRB.INF_OR_UNBD: return "INF_OR_UNBD"; case GRB.UNBOUNDED: return "UNBOUNDED"; case GRB.CUTOFF: return "CUTOFF"; case GRB.ITERATION_LIMIT: return "ITERATION_LIMIT"; case GRB.NODE_LIMIT: return "NODE_LIMIT"; case GRB.TIME_LIMIT: return "TIME_LIMIT"; case GRB.SOLUTION_LIMIT: return "SOLUTION_LIMIT"; case GRB.INTERRUPTED: return "INTERRUPTED"; case GRB.NUMERIC: return "NUMERIC"; default: return "UNKNOWN_STATUS"; } } /** * <ul> * <li>LOADED Model is loaded, but no solution information is available.</li> * <li>OPTIMAL Model was solved to optimality (subject to tolerances), and an optimal solution is available.</li> * <li>INFEASIBLE Model was proven to be infeasible.</li> * <li>INF_OR_UNBD Model was proven to be either infeasible or unbounded.</li> * <li>UNBOUNDED Model was proven to be unbounded.</li> * <li>CUTOFF Optimal objective for model was proven to be worse than the value specified in the Cutoff parameter. * No solution information is available.</li> * <li>ITERATION_LIMIT Optimization terminated because the total number of simplex iterations performed exceeded the * value specified in the IterationLimit parameter.</li> * <li>NODE_LIMIT Optimization terminated because the total number of branch-and-cut nodes explored exceeded the * value specified in the NodeLimit parameter.</li> * <li>TIME_LIMIT Optimization terminated because the time expended exceeded the value specified in the TimeLimit * parameter.</li> * <li>SOLUTION_LIMIT Optimization terminated because the number of solutions found reached the value specified in * the SolutionLimit parameter.</li> * <li>INTERRUPTED Optimization was terminated by the user.</li> * <li>NUMERIC Optimization was terminated due to unrecoverable numerical difficulties.</li> * <ul/> * * @param status * @return a string describing the given status code */ public static SolverStatus convertStatus(int status) { switch (status) { case GRB.LOADED: return SolverStatus.LOADED; case GRB.OPTIMAL: return SolverStatus.OPTIMAL; case GRB.INFEASIBLE: return SolverStatus.INFEASIBLE; case GRB.INF_OR_UNBD: return SolverStatus.INF_OR_UNBD; case GRB.UNBOUNDED: return SolverStatus.UNBOUNDED; case GRB.CUTOFF: return SolverStatus.CUTOFF; case GRB.ITERATION_LIMIT: return SolverStatus.ITERATION_LIMIT; case GRB.NODE_LIMIT: return SolverStatus.NODE_LIMIT; case GRB.TIME_LIMIT: return SolverStatus.TIME_LIMIT; case GRB.SOLUTION_LIMIT: return SolverStatus.SOLUTION_LIMIT; case GRB.INTERRUPTED: return SolverStatus.INTERRUPTED; case GRB.NUMERIC: return SolverStatus.NUMERIC; default: return SolverStatus.UNKNOWN_STATUS; } } /** * Returns a string describing the current solver status * * @param model * @return a string describing the current solver status */ public static String solverStatusString(GRBModel model) { int status = -1; int solCount = -1; double obj = Double.NaN; try { status = model.get(IntAttr.Status); } catch (GRBException e) { } try { solCount = model.get(IntAttr.SolCount); } catch (GRBException e) { } try { obj = model.get(DoubleAttr.ObjVal); } catch (GRBException e) { } return String.format("Status:%s SolCount:%s Objective:%s", statusToString(status), solCount, obj); } /** * Human friendly description of the where value in a callback * * @param where * the value of the <code>where</code> field in the callback * @return a description of the where value */ public static String callbackWhereDescription(int where) { switch (where) { case GRB.Callback.POLLING: return "Periodic polling callback"; case GRB.Callback.PRESOLVE: return "Currently performing presolve"; case GRB.Callback.SIMPLEX: return "Currently in simplex"; case GRB.Callback.MIP: return "Currently in MIP"; case GRB.Callback.MIPSOL: return "Found a new MIP incumbent"; case GRB.Callback.MIPNODE: return "Currently exploring a MIP node"; case GRB.Callback.BARRIER: return "Currently in barrier"; case GRB.Callback.MESSAGE: return "Printing a log message"; default: return "Not a valid value"; } } /** * Name of the where value in a callback * * @param where * the value of the <code>where</code> field in the callback * @return the name of the GRB.Callback constant corresponding to the where value */ public static String callbackWhereToString(int where) { switch (where) { case GRB.Callback.POLLING: return "POLLING"; case GRB.Callback.PRESOLVE: return "PRESOLVE"; case GRB.Callback.SIMPLEX: return "SIMPLEX"; case GRB.Callback.MIP: return "MIP"; case GRB.Callback.MIPSOL: return "MIPSOL"; case GRB.Callback.MIPNODE: return "MIPNODE"; case GRB.Callback.BARRIER: return "BARRIER"; case GRB.Callback.MESSAGE: return "MESSAGE"; default: return "UNKNOWN"; } } /** * Returns a set containing the name of the columns removed by the presolve * * @param model * the original model * @param presolved * the presolved model * @return a set containing the name of the columns removed by the presolve */ public static Set<String> getRemovedColumns(GRBModel model, GRBModel presolved) { String[] colNames; GRBVar[] preCols; try { colNames = model.get(StringAttr.VarName, model.getVars()); preCols = presolved.getVars(); } catch (GRBException e) { return Collections.singleton(e.getMessage()); } Set<String> remCols = new HashSet<String>(colNames.length); for (int r = 0; r < colNames.length; r++) { try { preCols[r].get(StringAttr.VarName); } catch (GRBException e) { remCols.add(colNames[r]); } } return remCols; } /** * Returns a set containing the name of the rows removed by the presolve * * @param model * the original model * @param presolved * the presolved model * @return a set containing the name of the rows removed by the presolve */ public static Set<String> getRemovedRows(GRBModel model, GRBModel presolved) { String[] rowNames; GRBConstr[] preRows; try { rowNames = model.get(StringAttr.ConstrName, model.getConstrs()); preRows = presolved.getConstrs(); } catch (GRBException e) { return Collections.singleton(e.getMessage()); } Set<String> remRows = new HashSet<String>(rowNames.length); for (int r = 0; r < rowNames.length; r++) { try { preRows[r].get(StringAttr.ConstrName); } catch (GRBException e) { remRows.add(rowNames[r]); } } return remRows; } }