/* * 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.LinkedList; import java.util.List; import com.rapidminer.operator.IOContainer; import com.rapidminer.operator.IOObject; import com.rapidminer.operator.MissingIOObjectException; import com.rapidminer.operator.Operator; import com.rapidminer.operator.OperatorDescription; import com.rapidminer.operator.OperatorException; import com.rapidminer.operator.UserError; import com.rapidminer.operator.ValueDouble; import com.rapidminer.operator.condition.InnerOperatorCondition; import com.rapidminer.operator.condition.SimpleChainInnerOperatorCondition; import com.rapidminer.operator.performance.PerformanceVector; import com.rapidminer.parameter.ParameterType; import com.rapidminer.parameter.ParameterTypeBoolean; import com.rapidminer.parameter.value.ParameterValueRange; import com.rapidminer.parameter.value.ParameterValues; /** * <p>In contrast to the {@link GridSearchParameterOptimizationOperator} operator this * operators simply uses the defined parameters and perform the inner operators * for all possible combinations. This can be especially usefull for plotting * or logging purposes and sometimes also for simply configuring the parameters for * the inner operators as a sort of meta step (e.g. learning curve generation).</p> * * <p>This operator iterates through a set of parameters by using all possible * parameter combinations. The parameter <var>parameters</var> is a list of key value pairs * where the keys are of the form <code>operator_name.parameter_name</code> and * the value is either a comma separated list of values (e.g. 10,15,20,25) or an * interval definition in the format [start;end;stepsize] (e.g. [10;25;5]). Additionally, * the format [start;end;steps;scale] is allowed.</p> * * <p>Please note that this operator has two modes: synchronized and non-synchronized. * In the latter, all parameter combinations are generated and the inner operators * are applied for each combination. In the synchronized mode, no combinations are * generated but the set of all pairs of the increasing number of parameters * are used. For the iteration over a single parameter there is no difference * between both modes. Please note that the number of parameter possibilities must be * the same for all parameters in the synchronized mode.</p> * * @author Ingo Mierswa, Tobias Malbrecht * @version $Id: ParameterIteration.java,v 1.11 2006/04/05 08:57:26 ingomierswa * Exp $ */ public class ParameterIteration extends ParameterIteratingOperatorChain { /** The parameter name for "A list of parameters to optimize" */ public static final String PARAMETER_PARAMETERS = "parameters"; /** The parameter name for "Synchronize parameter iteration" */ public static final String PARAMETER_SYNCHRONIZE = "synchronize"; /** The parameter name for "Keep the output of the last operator in the operator chain" */ public static final String PARAMETER_KEEP_OUTPUT = "keep_output"; private PerformanceVector performance; private int iteration = 0; public ParameterIteration(OperatorDescription description) { super(description); addValue(new ValueDouble("performance", "The last performance.") { public double getDoubleValue() { if (performance != null) return performance.getMainCriterion().getAverage(); else return Double.NaN; } }); addValue(new ValueDouble("iteration", "The current iteration.") { public double getDoubleValue() { return iteration; } }); } public int getParameterValueMode() { return VALUE_MODE_DISCRETE; } public IOObject[] apply() throws OperatorException { IOContainer input = getInput(); LinkedList<IOObject> output = new LinkedList<IOObject>(); boolean isSynchronized = getParameterAsBoolean(PARAMETER_SYNCHRONIZE); boolean keepOutput = getParameterAsBoolean(PARAMETER_KEEP_OUTPUT); List parameterList = getParameterList(PARAMETER_PARAMETERS); Operator[] operators = new Operator[parameterList.size()]; String[] parameters = new String[parameterList.size()]; String[][] values = new String[parameterList.size()][]; int[] currentIndex = new int[parameterList.size()]; // check parameter values List<ParameterValues> parameterValuesList = parseParameterValues(getParameterList("parameters")); int numberOfCombinations = 1; int lastNumberOfValues = -1; for (Iterator<ParameterValues> iterator = parameterValuesList.iterator(); iterator.hasNext(); ) { ParameterValues parameterValues = iterator.next(); if (parameterValues instanceof ParameterValueRange) { logWarning("found (and deleted) parameter values range (" + parameterValues.getKey() + ") which makes no sense in grid parameter optimization"); iterator.remove(); } numberOfCombinations *= parameterValues.getNumberOfValues(); } // initialize data structures operators = new Operator[parameterValuesList.size()]; parameters = new String[parameterValuesList.size()]; values = new String[parameterValuesList.size()][]; currentIndex = new int[parameterValuesList.size()]; // get parameter values and fill data structures int index = 0; for (Iterator<ParameterValues> iterator = parameterValuesList.iterator(); iterator.hasNext(); ) { ParameterValues parameterValues = iterator.next(); operators[index] = parameterValues.getOperator(); parameters[index] = parameterValues.getParameterType().getKey(); values[index] = parameterValues.getValuesArray(); if (!isSynchronized) { numberOfCombinations *= values[index].length; } else { numberOfCombinations = values[index].length; if (lastNumberOfValues < 0) { lastNumberOfValues = values[index].length; } else { if (lastNumberOfValues != values[index].length) throw new UserError(this, 926); } } index++; } this.iteration = 0; while (true) { log("Using parameter set"); // set all parameter values for (int j = 0; j < operators.length; j++) { operators[j].setParameter(parameters[j], values[j][currentIndex[j]].trim()); //operators[j].getParameters().setParameter(parameters[j], values[j][currentIndex[j]].trim()); log(operators[j] + "." + parameters[j] + " = " + values[j][currentIndex[j]].trim()); } setInput(input.copy()); IOObject[] evalout = super.apply(); IOContainer evalCont = new IOContainer(evalout); try { this.performance = evalCont.remove(PerformanceVector.class); } catch (MissingIOObjectException e) { log("Inner operators of ParameterIteration do not provide performance vectors: performance cannot be plotted!"); } if (keepOutput) { // get the output of the operator chain for (int j = 0; j < evalout.length; j++) { evalout[j].setSource(evalout[j].getSource() + " (" + this.getName() + ", iter.: " + (this.iteration + 1) + ")"); output.add(evalout[j]); } } this.iteration++; boolean ok = true; if (!isSynchronized) { // next parameter values int k = 0; while (!(++currentIndex[k] < values[k].length)) { currentIndex[k] = 0; k++; if (k >= currentIndex.length) { ok = false; break; } } } else { for (int k = 0; k < currentIndex.length; k++) { currentIndex[k]++; } if (!(currentIndex[0] < values[0].length)) { ok = false; break; } } if (!ok) { break; } inApplyLoop(); } // return IOObjects of last operator in chain if (keepOutput) { getInput().removeAll(); } IOObject [] temp = new IOObject[output.size()]; output.toArray(temp); return temp; } public Class<?>[] getInputClasses() { return new Class[0]; } public Class<?>[] getOutputClasses() { return new Class[0]; } /** Returns a simple chain condition. */ public InnerOperatorCondition getInnerOperatorCondition() { return new SimpleChainInnerOperatorCondition(); } public List<ParameterType> getParameterTypes() { List<ParameterType> types = super.getParameterTypes(); types.add(new ParameterTypeBoolean(PARAMETER_SYNCHRONIZE, "Synchronize parameter iteration", false)); types.add(new ParameterTypeBoolean(PARAMETER_KEEP_OUTPUT, "Delivers the merged output of the last operator of all the iterations, delivers the original input otherwise.", false)); return types; } }