/**
*
*/
package vroom.common.utilities;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import java.util.Properties;
import vroom.common.utilities.logging.LoggerHelper;
import vroom.common.utilities.optimization.OptimizationSense;
/**
* <code>BestKnownSolutions</code> is a utility class to read a property file and get best known solutions.
* <p>
* Creation date: Jul 6, 2010 - 6:00:37 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 BestKnownSolutions {
/**
* Getter for this class logger
*
* @return the logger associated with this class
*/
public static LoggerHelper getLogger() {
return LoggerHelper.getLogger(BestKnownSolutions.class);
}
/**
* A suffix added to instance name to state whether the best known solution is optimal or not
*/
public static final String OPT_SUFFIX = "-Opt";
/**
* A suffix added to instance name to specify the detailed solution
*/
public static final String SOLUTION_SUFFIX = "-S";
/**
* A suffix added to instance name to specify the authors for a BKS
*/
public static final String AUTHOR_SUFFIX = "-A";
/**
* A suffix added to instance name to specify the number of vehicles
*/
public static final String FLEET_SIZE_SUFFIX = "-K";
/** A flag that can be set to <code>true</code> if all bks are optimal */
public static final String ALL_OPTIMAL = "AllOptimal";
/** String representing that no BKS is konwn */
public static final String NA = "na";
private final Properties mProperties;
private boolean mAllOpt;
private final File mFile;
private boolean mChanged = false;
private String mComments;
/**
* Creates a new <code>BestKnownSolutions</code>
*
* @param file
* the property file containing the best known solutions
* @throws FileNotFoundException
* @throws IOException
*/
public BestKnownSolutions(String file) {
mProperties = new SortedProperties();
mFile = new File(file);
FileReader reader = null;
try {
if (!mFile.exists())
mFile.createNewFile();
reader = new FileReader(mFile);
mProperties.load(reader);
// Read and store the comment lines
BufferedReader r = new BufferedReader(reader);
String line = r.readLine();
StringBuffer coms = new StringBuffer(250);
while (line != null && line.startsWith("#")) {
if (coms.length() > 0) {
coms.append("\n");
}
coms.append(line.substring(1));
line = r.readLine();
}
mComments = coms.toString();
} catch (FileNotFoundException e) {
getLogger().exception("BestKnownSolution constructor", e);
} catch (IOException e) {
getLogger().exception("BestKnownSolution constructor", e);
} finally {
if (reader != null)
try {
reader.close();
} catch (IOException e) {
getLogger().exception("BestKnownSolutions.BestKownSolutions", e);
}
}
if (mProperties.containsKey(ALL_OPTIMAL)) {
mAllOpt = Boolean.valueOf(mProperties.getProperty(ALL_OPTIMAL));
} else {
mAllOpt = false;
}
}
/**
* Return a list of all the instances' name contained in this instance
*
* @return a list of all the instances' name contained in this instance
*/
public List<String> getInstanceNames() {
List<String> names = new ArrayList<>();
for (Object key : mProperties.keySet()) {
if (key instanceof String) {
String s = (String) key;
if (!s.endsWith(AUTHOR_SUFFIX) && !s.endsWith(FLEET_SIZE_SUFFIX)
&& !s.endsWith(OPT_SUFFIX) && !s.endsWith(SOLUTION_SUFFIX))
names.add(s);
}
}
Collections.sort(names);
return names;
}
/**
* Getter for the best known solution for a given instance
*
* @param instanceName
* the name of the considered instance
* @return the corresponding BKS, or <code>null</code> if no value is associated with the given instance.
*/
public Double getBKS(String instanceName) {
if (mProperties.containsKey(instanceName)) {
String value = mProperties.getProperty(instanceName);
if (NA.equalsIgnoreCase(value)) {
return null;
} else {
return Double.valueOf(value);
}
} else {
return null;
}
}
/**
* Returns the gap to the best known solution, or 0 if the BKS is not defined
*
* @param instanceName
* @param sol
* @param sense
* @return the gap to the best known solution, or 0 if the BKS is not defined
*/
public double getGapToBKS(String instanceName, double sol, OptimizationSense sense) {
Double bks = getBKS(instanceName);
if (bks != null)
if (bks != 0)
return sense.getImprovement(sol, bks) / bks;
else
return Double.NaN;
else
return Double.NaN;
}
/**
* Getter for the a value associated with a given instance
*
* @param instanceName
* the name of the considered instance
* @param suffix
* the suffix to the instance name
* @return the String associated with key <code>instanceName+"-"+suffix</code>
*/
public String getValue(String instanceName, String suffix) {
String key = instanceName + suffix;
if (mProperties.containsKey(key)) {
return mProperties.getProperty(key);
} else {
return null;
}
}
/**
* Getter for the a value associated with a given instance
*
* @param instanceName
* the name of the considered instance
* @param suffix
* the suffix to the instance name
* @return the String associated with key <code>instanceName+"-"+suffix</code>
*/
public Integer getIntValue(String instanceName, String suffix) {
String val = getValue(instanceName, suffix);
return val != null ? Integer.valueOf(val) : null;
}
/**
* Getter for the a value associated with a given instance
*
* @param instanceName
* the name of the considered instance
* @param suffix
* the suffix to the instance name
* @return the String associated with key <code>instanceName+"-"+suffix</code>
*/
public double getDoubleValue(String instanceName, String suffix) {
return Double.valueOf(getValue(instanceName, suffix));
}
/**
* Check if a best known solution is an optimal solution
*
* @param instanceName
* the name of the considered instance
* @return <code>true</code> if the BKS is optimal, or <code>false</code> if it is not or no value is associated
* with the given instance
*/
public boolean isOptimal(String instanceName) {
if (mAllOpt && mProperties.containsKey(instanceName)) {
return true;
} else {
String b = getValue(instanceName, OPT_SUFFIX);
return b != null ? Boolean.valueOf(b) : false;
}
}
/**
* Returns the authors associated with a BKS
*
* @param instancename
* the name of the considered instance
* @return the authors associated with a BKS
*/
public String getAuthors(String instancename) {
return getValue(instancename, AUTHOR_SUFFIX);
}
/**
* Returns the number of vehicles used in a BKS
*
* @param instancename
* the name of the considered instance
* @return the number of vehicles used in a BKS
*/
public String getFleetSize(String instancename) {
return getValue(instancename, FLEET_SIZE_SUFFIX);
}
/**
* Returns the detailed BKS as a string
*
* @param instancename
* the name of the considered instance
* @return the detailed BKS as a string
*/
public String getSolution(String instancename) {
return getValue(instancename, SOLUTION_SUFFIX);
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return mProperties.toString();
}
/**
* Returns a string with the BKS in a CSV format
*
* @param linesPerInstance
* the number of times a row has to be repeated
* @return a string with the BKS in a CSV format
*/
public String toCSVString(int linesPerInstance) {
StringBuffer sb = new StringBuffer();
sb.append("name;bks;bks_opt;bks_K;bks_authors;bks_sol\n");
for (Enumeration<Object> e = mProperties.keys(); e.hasMoreElements();) {
String key = (String) e.nextElement();
if (!key.endsWith(OPT_SUFFIX) && !key.endsWith(AUTHOR_SUFFIX)
&& !key.endsWith(FLEET_SIZE_SUFFIX) && !key.endsWith(SOLUTION_SUFFIX)) {
for (int i = 0; i < linesPerInstance; i++) {
sb.append(String.format("%s;%s;%s;%s;%s;%s\n", key, getBKS(key),
isOptimal(key), getFleetSize(key), getAuthors(key), getSolution(key)));
}
}
}
return sb.toString();
}
/**
* Set the all optimal flag
*
* @param b
* <code>true</code> if all solutions are optimal
*/
public void setAllOptimal(boolean b) {
mProperties.setProperty(ALL_OPTIMAL, "" + b);
mAllOpt = b;
mChanged = true;
}
/**
* Set the best known solution
*
* @param instance
* the instance name
* @param obj
* the objective value
* @param optimal
* <code>true</code> if <code>obj</code> is the optimal value for the given <code>instance</code>
* @param fleetSize
* the number of vehicles used, {@code null} if ignored
* @param authors
* the name of the authors, {@code null} if ignored
* @param solution
* the detailed solution, {@code null} if ignored
*/
public void setBestKnownSolution(String instance, double obj, boolean optimal,
Integer fleetSize, String authors, String solution) {
mProperties.setProperty(instance, "" + obj);
if (!mAllOpt) {
mProperties.setProperty(instance + OPT_SUFFIX, "" + optimal);
}
if (fleetSize != null)
mProperties.setProperty(instance + FLEET_SIZE_SUFFIX, fleetSize.toString());
if (authors != null)
mProperties.setProperty(instance + AUTHOR_SUFFIX, authors);
if (solution != null)
mProperties.setProperty(instance + SOLUTION_SUFFIX, solution);
mChanged = true;
}
/**
* Getter for <code>file</code>
*
* @return the file
*/
public File getFile() {
return mFile;
}
/**
* Save the current values to the best known solution file
*
* @throws FileNotFoundException
* @throws IOException
*/
public void save(String comment) throws FileNotFoundException, IOException {
if (mChanged) {
FileOutputStream output = new FileOutputStream(mFile);
mProperties.store(output, mComments.isEmpty() ? comment : mComments + "\n" + comment);
output.close();
mChanged = false;
}
}
}