/*
* RapidMiner
*
* Copyright (C) 2001-2008 by Rapid-I and the contributors
*
* Complete list of developers available at our web site:
*
* http://rapid-i.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see http://www.gnu.org/licenses/.
*/
package com.rapidminer.operator.meta;
import java.util.Iterator;
import java.util.List;
import java.util.LinkedList;
import com.rapidminer.gui.properties.ConfigureParameterOptimizationDialogCreator;
import com.rapidminer.operator.Operator;
import com.rapidminer.operator.OperatorChain;
import com.rapidminer.operator.OperatorDescription;
import com.rapidminer.operator.OperatorException;
import com.rapidminer.operator.UserError;
import com.rapidminer.parameter.ParameterType;
import com.rapidminer.parameter.ParameterTypeConfiguration;
import com.rapidminer.parameter.ParameterTypeList;
import com.rapidminer.parameter.ParameterTypeParameterValue;
import com.rapidminer.parameter.value.ParameterValues;
import com.rapidminer.parameter.value.ParameterValueGrid;
import com.rapidminer.parameter.value.ParameterValueList;
import com.rapidminer.parameter.value.ParameterValueRange;
/**
* Provides an operator chain which operates on given parameters
* depending on specified values for these parameters.
*
* @author Tobias Malbrecht
* @version $Id: ParameterIteratingOperatorChain.java,v 1.6 2008/07/04 10:27:17 stiefelolm Exp $
*/
public abstract class ParameterIteratingOperatorChain extends OperatorChain {
/** The parameter name for "Parameters to optimize in the format OPERATORNAME.PARAMETERNAME." */
public static final String PARAMETER_PARAMETERS = "parameters";
/** A specification of the parameter values for a parameter." */
public static final String PARAMETER_VALUES = "values";
/** Means that the parameter iteration scheme can only handle discrete parameter values (i.e. lists or numerical grids). */
public static final int VALUE_MODE_DISCRETE = 0;
/** Means that the parameter iteration scheme can only handle intervals of numerical values. */
public static final int VALUE_MODE_CONTINUOUS = 1;
private static final int PARAMETER_VALUES_ARRAY_LENGTH_RANGE = 2;
private static final int PARAMETER_VALUES_ARRAY_LENGTH_GRID = 3;
private static final int PARAMETER_VALUES_ARRAY_LENGTH_SCALED_GRID = 4;
public ParameterIteratingOperatorChain(OperatorDescription description) {
super(description);
}
/**
* Has to return one of the predefined modes which indicate whether the
* operator takes discrete values or intervals as basis for optimization.
* The first option is to be taken for all strategies that iterate over the
* given parameters. The latter option is to be taken for strategies such
* as an evolutionary one in which allowed ranges of parameters have to be
* specified.
*/
public abstract int getParameterValueMode();
/**
* Parses a parameter list and creates the corresponding data structures.
*/
public List<ParameterValues> parseParameterValues(List parameterList) throws OperatorException {
List<ParameterValues> parameterValuesList = new LinkedList<ParameterValues>();
Iterator iterator = parameterList.iterator();
while (iterator.hasNext()) {
Object[] parameterListEntry = (Object[]) iterator.next();
String[] parameter = ((String) parameterListEntry[0]).split("\\.");
if (parameter.length != 2)
throw new UserError(this, 907, parameterListEntry[0]);
Operator operator = getProcess().getOperator(parameter[0]);
if (operator == null)
throw new UserError(this, 109, parameter[0]);
ParameterType parameterType = operator.getParameters().getParameterType(parameter[1]);
if (parameterType == null) {
throw new UserError(this, 906, parameter[0] + "." + parameter[1]);
}
String parameterValuesString = (String)parameterListEntry[1];
ParameterValues parameterValues = null;
try {
int startIndex = parameterValuesString.indexOf("[");
if (startIndex >= 0) {
int endIndex = parameterValuesString.indexOf("]");
if (endIndex > startIndex) {
String[] parameterValuesArray = parameterValuesString.substring(startIndex + 1, endIndex).trim().split("[;:,]");
switch (parameterValuesArray.length) {
case PARAMETER_VALUES_ARRAY_LENGTH_RANGE: { // value range: [minValue;maxValue]
double min = Double.parseDouble(parameterValuesArray[0]);
double max = Double.parseDouble(parameterValuesArray[1]);
parameterValues = new ParameterValueRange(operator, parameterType, min, max);
}
break;
case PARAMETER_VALUES_ARRAY_LENGTH_GRID: { // value grid: [minValue;maxValue;stepSize]
double min = Double.parseDouble(parameterValuesArray[0]);
double max = Double.parseDouble(parameterValuesArray[1]);
double stepSize = Double.parseDouble(parameterValuesArray[2]);
if (stepSize == 0) {
throw new Exception("step size of 0 is not allowed");
}
if (min <= max + stepSize) {
throw new Exception("end value must at least be as large as start value plus step size");
}
parameterValues = new ParameterValueGrid(operator, parameterType, min, max, stepSize);
}
break;
case PARAMETER_VALUES_ARRAY_LENGTH_SCALED_GRID: { // value grid: [minValue;maxValue;noOfSteps;scale]
double min = Double.parseDouble(parameterValuesArray[0]);
double max = Double.parseDouble(parameterValuesArray[1]);
int steps = Integer.parseInt(parameterValuesArray[2]);
if (steps == 0) {
throw new Exception("step size of 0 is not allowed");
}
String scaleName = parameterValuesArray[3];
parameterValues = new ParameterValueGrid(operator, parameterType, min, max, steps, scaleName);
}
break;
default:
throw new Exception("parameter values string could not be parsed (too many arguments)");
}
} else {
throw new Exception("']' was missing");
}
} else {
int colonIndex = parameterValuesString.indexOf(":");
if (colonIndex >= 0) {
// maintain compatibility for evolutionary parameter optimization (old format: startValue:endValue without parantheses)
String[] parameterValuesArray = parameterValuesString.trim().split(":");
if (parameterValuesArray.length != 2) {
throw new Exception("wrong parameter range format");
} else {
double min = Double.parseDouble(parameterValuesArray[0]);
double max = Double.parseDouble(parameterValuesArray[1]);
parameterValues = new ParameterValueRange(operator, parameterType, min, max);
}
} else {
// usual parameter value list: value1,value2,value3,...
if (parameterValuesString.length() != 0) {
String[] values = parameterValuesString.split(",");
parameterValues = new ParameterValueList(operator, parameterType, values);
}
}
}
} catch (Throwable e) {
throw new UserError(this, 116, parameterListEntry[0], "Unknown parameter value specification format: '" + parameterListEntry[1] + "'. Error: " + e.getMessage());
}
if (parameterValues != null) {
parameterValuesList.add(parameterValues);
}
}
return parameterValuesList;
}
/**
* Returns the highest possible value for the maximum number of inner
* operators.
*/
public int getMaxNumberOfInnerOperators() {
return Integer.MAX_VALUE;
}
/** Returns 1 for the minimum number of inner operators. */
public int getMinNumberOfInnerOperators() {
return 1;
}
public List<ParameterType> getParameterTypes() {
List<ParameterType> types = super.getParameterTypes();
ParameterType type = new ParameterTypeConfiguration(ConfigureParameterOptimizationDialogCreator.class, this);
type.setExpert(false);
types.add(type);
type = new ParameterTypeList(PARAMETER_PARAMETERS, "The parameters.", new ParameterTypeParameterValue(PARAMETER_VALUES, "The value specifications for the parameters."));
type.setHidden(true);
types.add(type);
return types;
}
}