/**
*
*/
package vroom.common.utilities.params;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map.Entry;
import java.util.Properties;
import vroom.common.utilities.SortedProperties;
import vroom.common.utilities.Utilities;
import vroom.common.utilities.logging.Logging;
/**
* The class <code>ExperimentDesignParameter</code> provides base functionalities for experiment design. It defines a
* base set of parameters and a set of {@link ParameterValueSet}, as well as methods to generate a list of all the
* possibles parameter combinations
* <p>
* Creation date: Jun 21, 2012 - 11:24:45 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 ParameterExperimentDesign<G extends GlobalParameters> {
private final G mBaseParameters;
private final HashMap<ParameterKey<?>, ParameterValueSet<?>> mValueSets;
/**
* Creates a new <code>ExperimentDesignParameter</code>
*
* @param baseParameters
*/
public ParameterExperimentDesign(G baseParameters) {
mBaseParameters = baseParameters;
mValueSets = new HashMap<>();
}
/**
* Add a new set of values for a parameter. If a previous set was defined it will be replaced.
*
* @param key
* @param values
*/
@SuppressWarnings("unchecked")
public <T> void addParamValueSet(ParameterKey<T> key, T... values) {
mValueSets.put(key, new ParameterValueSet<>(key, values));
}
public void addParamValueSet(String a) throws ClassNotFoundException {
String[] p = a.split("=");
@SuppressWarnings("unchecked")
ParameterKey<Object> key = (ParameterKey<Object>) mBaseParameters.getRegisteredKey(p[0]);
if (key == null)
throw new IllegalArgumentException("Unknown parameter key:" + p[0]);
String[] stringValues = p[1].substring(1, p[1].length() - 1).split(",");
Object[] values = new Object[stringValues.length];
for (int i = 0; i < values.length; i++) {
values[i] = ParametersFilePersistenceDelegate.castProperty(key, stringValues[i]);
}
mValueSets.put(key, new ParameterValueSet<Object>(key, values));
}
/**
* Generate the list of experiments
*
* @return a list containing all the experiments defined in this instance
*/
public List<ExperimentParameterSetting<G>> getExperiments() {
ArrayList<ParameterKey<?>> keys = new ArrayList<>(mValueSets.keySet());
Collections.sort(keys);
Object[][] matrix = new Object[keys.size()][];
int combCount = 1;
for (int k = 0; k < keys.size(); k++) {
ParameterValueSet<?> s = mValueSets.get(keys.get(k));
matrix[k] = new Object[s.getValues().length + 1];
matrix[k][0] = keys.get(k);
for (int i = 1; i < matrix[k].length; i++) {
matrix[k][i] = s.getValues()[i - 1];
}
combCount *= s.getValues().length;
}
ArrayList<ExperimentParameterSetting<G>> experiments = new ArrayList<>(combCount);
generateCombinations(matrix, experiments, 0, null, null);
return experiments;
}
/**
* Recursive method that does a deep first exploration of the tree of combinations
*
* @param matrix
* @param experiments
* @param depth
* @param params
* @param changedValues
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
private void generateCombinations(Object[][] matrix,
ArrayList<ExperimentParameterSetting<G>> experiments, int depth, G params,
HashMap<ParameterKey, Object> changedValues) {
if (depth == 0) {
// Root child : initialize data structures
changedValues = new HashMap<ParameterKey, Object>();
params = (G) mBaseParameters.clone();
} else if (depth == matrix.length) {
// We reached a leaf, add the experiment to the list
experiments.add(new ExperimentParameterSetting<G>(params, changedValues));
return;
}
for (int i = 1; i < matrix[depth].length; i++) {
HashMap<ParameterKey, Object> localChangedValues = (HashMap<ParameterKey, Object>) changedValues
.clone();
G localParams = (G) params.clone();
localChangedValues.put((ParameterKey) matrix[depth][0], matrix[depth][i]);
localParams.setNoCheck((ParameterKey) matrix[depth][0], matrix[depth][i]);
generateCombinations(matrix, experiments, depth + 1, localParams, localChangedValues);
}
}
/**
* Returns a string containing all the parameter keys in the order they will be displayed in each
* {@link ExperimentParameterSetting}
*
* @return string containing all the parameter keys
*/
public String getParameterKeys() {
ArrayList<ParameterKey<?>> keys = new ArrayList<>(mValueSets.keySet());
Collections.sort(keys);
return Utilities.toShortString(keys);
}
/**
* Save all the settings contained in this instance into a file
*
* @param file
* @throws IOException
*/
public void save(File file) throws IOException {
SortedProperties prop = new SortedProperties();
for (Entry<ParameterKey<?>, ParameterValueSet<?>> setting : mValueSets.entrySet()) {
prop.setProperty(setting.getKey().getName(),
Utilities.toShortString(setting.getValue().getValues()));
}
FileOutputStream sf = new FileOutputStream(file);
prop.store(sf, String.format("Created by %s \nDate: %s", this.getClass().getName(),
new Date(System.currentTimeMillis())));
sf.flush();
sf.close();
}
/**
* Load the settings contained in a file in this instance
*
* @param file
* @param params
* @throws IOException
*/
@SuppressWarnings("unchecked")
public void load(File file) throws IOException {
// Loading the properties file
Properties properties = new Properties();
FileInputStream sf = new FileInputStream(file);
properties.load(sf);
for (String k : properties.stringPropertyNames()) {
ParameterKey<Object> key = (ParameterKey<Object>) mBaseParameters.getRegisteredKey(k);
if (key != null) {
Object[] values = Utilities.toArray(properties.get(k).toString());
addParamValueSet(key, values);
} else {
Logging.getSetupLogger().warn("Unknown key %s - ignoring", k);
}
}
}
/**
* Remove all the configurations from this instance
*/
public void clear() {
mValueSets.clear();
}
/**
* The class <code>ExperimentParameterSetting</code> defines a parameter setting for an experiment
* <p>
* Creation date: Jun 21, 2012 - 2:40:22 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
* @param <GG>
*/
public static class ExperimentParameterSetting<GG extends GlobalParameters> {
private final GG mParameters;
@SuppressWarnings("rawtypes")
private final HashMap<ParameterKey, Object> mChangedValues;
@SuppressWarnings("rawtypes")
private ExperimentParameterSetting(GG parameters,
HashMap<ParameterKey, Object> changedValues) {
super();
mParameters = parameters;
mChangedValues = changedValues;
}
/**
* Return the parameter setting of this experiment
*
* @return the parameter setting of this experiment
*/
public GG getParameters() {
return mParameters;
}
/**
* Return an array containing all the changed values, sorted according to the alphabetical order of the
* corresponding keys as in {@link ParameterExperimentDesign#getParameterKeys()}
*
* @return an array containing all the changed values
* @see ParameterExperimentDesign#getParameterKeys()
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
public Object[] getChangedValues() {
ArrayList<ParameterKey> keys = new ArrayList<>(mChangedValues.keySet());
Collections.sort(keys);
Object[] values = new Object[keys.size()];
int i = 0;
for (ParameterKey<?> k : keys) {
values[i++] = mChangedValues.get(k);
}
return values;
}
/**
* A comma-separated list of the changed values
*
* @return comma-separated list of the changed values
*/
public String getChangedValuesString() {
return Utilities.toShortString(getChangedValues(), ',', false);
}
@Override
public String toString() {
return mChangedValues.toString();
}
}
}