/* * RapidMiner * * Copyright (C) 2001-2011 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.logging.Level; 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.performance.PerformanceVector; import com.rapidminer.operator.ports.CollectingPortPairExtender; import com.rapidminer.operator.ports.OutputPort; import com.rapidminer.operator.ports.PortPairExtender; 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 useful 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> * * Compatibility note: This operator no longer returns all of its input. * In most applications all that can be done with such a collection of IOOBjects is * iterating over them again, and that can as well be done inside the ParameterIteration. Where this * is not possible, please group them into a collection (using a IOCollector) and store the collection * using an IOStorage operator. * * @author Ingo Mierswa, Tobias Malbrecht */ 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"; ///** Deprecated: 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.") { @Override public double getDoubleValue() { if (performance != null) return performance.getMainCriterion().getAverage(); else return Double.NaN; } }); addValue(new ValueDouble("iteration", "The current iteration.") { @Override public double getDoubleValue() { return iteration; } }); } @Override protected boolean isPerformanceRequired() { return false; } @Override public int getParameterValueMode() { return VALUE_MODE_DISCRETE; } @Override public void doWork() throws OperatorException { ((CollectingPortPairExtender)getInnerSinkExtender()).reset(); boolean isSynchronized = getParameterAsBoolean(PARAMETER_SYNCHRONIZE); // check parameter values List<String[]> parameterList = getParameterList(PARAMETER_PARAMETERS); List<ParameterValues> parameterValuesList = parseParameterValues(parameterList); int numberOfCombinations = 1; int lastNumberOfValues = -1; for (Iterator<ParameterValues> iterator = parameterValuesList.iterator(); iterator.hasNext(); ) { ParameterValues parameterValues = iterator.next(); if (parameterValues instanceof ParameterValueRange) { getLogger().warning("Found (and deleted) parameter values range (" + parameterValues.getKey() + ") which makes no sense in grid parameter optimization"); iterator.remove(); } numberOfCombinations *= parameterValues.getNumberOfValues(); } // initialize data structures Operator[] operators = new Operator[parameterList.size()]; String[] parameters = new String[parameterList.size()]; String[][] values = new String[parameterList.size()][]; int[] currentIndex = new int[parameterList.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++; } // iterate parameter combinations this.iteration = 0; while (true) { checkForStop(); String[] currentValues = new String[parameters.length]; // set all parameter values for (int j = 0; j < operators.length; j++) { currentValues[j] = values[j][currentIndex[j]].trim(); //operators[j].setParameter(parameters[j], values[j][currentIndex[j]].trim()); } ParameterSet set = new ParameterSet(operators, parameters, currentValues, null); evaluateParameterSet(set); 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(); } } protected void evaluateParameterSet(ParameterSet set) throws OperatorException { if (getLogger().isLoggable(Level.FINE)) { getLogger().fine("Evaluating parameter set: "+set.toString()); } set.applyAll(getProcess(), null); this.performance = super.getPerformance(); ((CollectingPortPairExtender)getInnerSinkExtender()).collect(); if (performance == null) { getLogger().info("Inner operators of " + getName() + " do not provide performance vectors. Performance cannot be plotted."); } } @Override protected PortPairExtender makeInnerSinkExtender() { return new CollectingPortPairExtender("result", getSubprocess(0).getInnerSinks(), getOutputPorts()); } @Override public boolean shouldAddNonConsumedInput() { return false; } @Override public boolean shouldAutoConnect(OutputPort outputPort) { if (outputPort.getName().startsWith("result")) { return getParameterAsBoolean("keep_output"); } else { return super.shouldAutoConnect(outputPort); } } @Override 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; } }