/**
*
*/
package vroom.common.utilities.gurobi;
import static gurobi.GRB.Callback.RUNTIME;
import gurobi.GRB;
import gurobi.GRBCallback;
import gurobi.GRBException;
import java.io.File;
import java.text.DecimalFormat;
import java.util.Arrays;
import vroom.common.utilities.StatCollector;
import vroom.common.utilities.StatCollector.Label;
/**
* <code>GRBStatCollectot</code>
* <p>
* Creation date: Aug 25, 2011 - 9:42:34 AM
*
* @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 GRBStatCollector extends GRBCallback {
private double mIncumbent;
private double mBestBound;
private double mNodLft;
public final static Label<?>[] LABELS = new Label<?>[] {
new Label<Long>("Time", Long.class), //
new Label<Long>("ExplNodes", Long.class),//
new Label<Long>("UnexplNodes", Long.class),//
new Label<Integer>("SolCount", Integer.class),//
new Label<Double>("Incumbent", Double.class, new DecimalFormat("###0.0000")),//
new Label<Double>("BestBd", Double.class, new DecimalFormat("###0.0000")), //
new Label<Double>("Gap", Double.class, new DecimalFormat("###0.000000")),
new Label<String>("Where", String.class)
};
private final StatCollector mCollector;
/**
* Creates a new <code>GRBStatCollectot</code>
*
* @param file
* the file in which stats will be collected
* @param comment
* a comment for the file
*/
public GRBStatCollector(String file, String comment) {
this(file, comment, false, true);
}
/**
* Creates a new <code>GRBStatCollectot</code>
*
* @param detailStatFile
* the file in which stats will be collected
* @param comment
* a comment for the file
* @param append
* <code>true</code> if the new stats should be appended to the end of an existing file
* @param autoFlush
* <code>true</code> if the new stats should be written immediately to the file
* @see StatCollector#StatCollector(File, boolean, boolean, String, Label...)
*/
public GRBStatCollector(String detailStatFile, String comment, boolean append, boolean autoFlush) {
Label<?>[] labels = LABELS;
Label<?>[] add = getAdditionalLabels();
if (add.length > 0) {
labels = Arrays.copyOf(labels, labels.length + add.length);
for (int i = 1; i <= add.length; i++) {
labels[labels.length - i] = add[add.length - i];
}
}
mCollector = new StatCollector(new File(detailStatFile), autoFlush, append, comment, labels);
}
/**
* Returns the current gap
*
* @return the current gap
*/
public double getGap() {
return Math.abs((mIncumbent - mBestBound) / mIncumbent);
}
/**
* Returns the current incumbent value
*
* @return the current incumbent value
*/
public double getIncumbent() {
return mIncumbent;
}
/**
* Returns the current best bound
*
* @return the current best bound
*/
public double getBestBound() {
return mBestBound;
}
/**
* Returns additional labels for the {@link StatCollector}
*
* @return additional labels for the {@link StatCollector}
*/
protected Label<?>[] getAdditionalLabels() {
return new Label<?>[0];
}
/* (non-Javadoc)
* @see gurobi.GRBCallback#callback()
*/
@Override
protected void callback() {
switch (where) {
case GRB.Callback.MIPNODE:
case GRB.Callback.MIPSOL:
try {
if (updateStats())
collectStats();
} catch (GRBException e) {
GRBLogging.CALLBACK.exception("GRBStatCollectot.callback", e);
}
break;
}
}
/**
* Update the stored statistics
*
* @return <code>true</code> if one of the statistics has changed
* @throws GRBException
*/
private boolean updateStats() throws GRBException {
boolean changed = false;
int objbnd = 0, objbst = 0;
switch (where) {
case GRB.Callback.MIPNODE:
objbnd = GRB.Callback.MIPNODE_OBJBND;
objbst = GRB.Callback.MIPNODE_OBJBST;
mNodLft = getDoubleInfo(GRB.Callback.MIP_NODLFT);
break;
case GRB.Callback.MIPSOL:
objbnd = GRB.Callback.MIPSOL_OBJBND;
objbst = GRB.Callback.MIPSOL_OBJBST;
}
double bnd = getDoubleInfo(objbnd);
double inc = getDoubleInfo(objbst);
if (bnd != mBestBound) {
mBestBound = bnd;
changed = true;
}
if (inc != mIncumbent) {
mIncumbent = inc;
changed = true;
}
return changed;
}
/**
* Returns an array of objects containing the stats to be collected
*
* @return an array of objects containing the stats to be collected
* @throws GRBException
*/
private final Object[] getStatArray() throws GRBException {
int nodcnt = 0, solcnt = 0;
switch (where) {
case GRB.Callback.MIPNODE:
nodcnt = GRB.Callback.MIPNODE_NODCNT;
solcnt = GRB.Callback.MIPNODE_SOLCNT;
break;
case GRB.Callback.MIPSOL:
nodcnt = GRB.Callback.MIPSOL_NODCNT;
solcnt = GRB.Callback.MIPSOL_SOLCNT;
}
return new Object[] { (long) getDoubleInfo(RUNTIME),//
(long) getDoubleInfo(nodcnt), //
(long) mNodLft, //
getIntInfo(solcnt), //
mIncumbent, //
mBestBound, //
getGap(), GRBUtilities.callbackWhereToString(where) };
}
/**
* Returns an array of objects containing the additional stats to be collected
*
* @return an array of objects containing the additional stats to be collected
* @throws GRBException
*/
protected Object[] getAdditionalStatArray() throws GRBException {
return new Object[0];
}
/**
* Collect the statistics in the stat file
*
* @throws GRBException
*/
private final void collectStats() throws GRBException {
Object[] stats = getStatArray();
Object[] add = getAdditionalStatArray();
if (add.length > 0) {
stats = Arrays.copyOf(stats, stats.length + add.length);
for (int i = 1; i <= add.length; i++) {
stats[stats.length - i] = add[add.length - i];
}
}
mCollector.collect(stats);
}
/**
* Flush the collected stats to the destination stat file
*/
public void flush() {
mCollector.flush();
}
/**
* Close the underlying file writer
*/
public void closeCollector() {
mCollector.close();
}
}