/*
* 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.List;
import com.rapidminer.example.ExampleSet;
import com.rapidminer.operator.IOContainer;
import com.rapidminer.operator.IOObject;
import com.rapidminer.operator.MissingIOObjectException;
import com.rapidminer.operator.OperatorChain;
import com.rapidminer.operator.OperatorDescription;
import com.rapidminer.operator.OperatorException;
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.ParameterTypeCategory;
import com.rapidminer.parameter.ParameterTypeDouble;
import com.rapidminer.parameter.ParameterTypeInt;
import com.rapidminer.parameter.UndefinedParameterError;
/**
* Performs its inner operators until all given criteria are met or a timeout
* occurs.
*
* @author Stefan Rueping
* @version $Id: RepeatUntilOperatorChain.java,v 1.11 2006/04/05 08:57:26
* ingomierswa Exp $
*/
public class RepeatUntilOperatorChain extends OperatorChain {
/** The parameter name for "Minimal number of attributes in first example set" */
public static final String PARAMETER_MIN_ATTRIBUTES = "min_attributes";
/** The parameter name for "Maximal number of attributes in first example set" */
public static final String PARAMETER_MAX_ATTRIBUTES = "max_attributes";
/** The parameter name for "Minimal number of examples in first example set" */
public static final String PARAMETER_MIN_EXAMPLES = "min_examples";
/** The parameter name for "Maximal number of examples in first example set" */
public static final String PARAMETER_MAX_EXAMPLES = "max_examples";
/** The parameter name for "Minimal main criterion in first performance vector" */
public static final String PARAMETER_MIN_CRITERION = "min_criterion";
/** The parameter name for "Maximal main criterion in first performance vector" */
public static final String PARAMETER_MAX_CRITERION = "max_criterion";
/** The parameter name for "Maximum number of iterations" */
public static final String PARAMETER_MAX_ITERATIONS = "max_iterations";
/** The parameter name for "Timeout in minutes (-1 = no timeout)" */
public static final String PARAMETER_TIMEOUT = "timeout";
/** The parameter name for "Stop when performance of inner chain behaves like this." */
public static final String PARAMETER_PERFORMANCE_CHANGE = "performance_change";
/** The parameter name for "Evaluate condition before inner chain is applied (true) or after?" */
public static final String PARAMETER_CONDITION_BEFORE = "condition_before";
public static final String[] COMPARISONS = { "none", "decreasing", "non-increasing" };
public static final int NONE = 0;
public static final int DECREASING = 1;
public static final int NONINCREASING = 2;
private int iteration;
private long stoptime;
private double fitness;
private IOContainer lastInput;
public RepeatUntilOperatorChain(OperatorDescription description) {
super(description);
}
public IOObject[] apply() throws OperatorException {
iteration = 0;
int timeout = getParameterAsInt(PARAMETER_TIMEOUT);
if (timeout == -1) {
stoptime = Long.MAX_VALUE;
} else {
stoptime = System.currentTimeMillis() + 60L * 1000 * timeout;
}
lastInput = null;
fitness = Double.NEGATIVE_INFINITY;
IOContainer input = getInput().copy();
while (!evaluateCondition(input)) {
log("Iteration " + iteration);
for (int j = 0; j < getNumberOfOperators(); j++) {
input = getOperator(j).apply(input);
}
iteration++;
inApplyLoop();
}
if (lastInput != null) {
// return last known optimal result instead of current (see
// parameter "performance_change")
input = lastInput;
}
return new IOObject[0];
}
/** Evaluates whether the stopping condition is met */
private boolean evaluateCondition(IOContainer input) throws MissingIOObjectException, UndefinedParameterError {
if ((iteration == 0) && (!getParameterAsBoolean(PARAMETER_CONDITION_BEFORE))) {
return false;
}
int changeType = getParameterAsInt(PARAMETER_PERFORMANCE_CHANGE);
if (changeType != NONE) {
if (iteration > 0) {
double currentFitness = input.get(PerformanceVector.class).getMainCriterion().getFitness();
if ((changeType == DECREASING) && (currentFitness < fitness)) {
return true;
} else if ((changeType == NONINCREASING) && (currentFitness <= fitness)) {
return true;
}
fitness = currentFitness;
}
lastInput = input.copy();
input.remove(PerformanceVector.class);
}
int maxit = getParameterAsInt(PARAMETER_MAX_ITERATIONS);
if (iteration >= maxit) {
log("Maximum number of iterations met.");
return true;
};
if (java.lang.System.currentTimeMillis() > stoptime) {
log("Runtime exceeded.");
return true;
};
int maxAtts = getParameterAsInt(PARAMETER_MAX_ATTRIBUTES);
int minAtts = getParameterAsInt(PARAMETER_MIN_ATTRIBUTES);
if ((maxAtts < Integer.MAX_VALUE) || (minAtts > 0)) {
int nrAtts = input.get(ExampleSet.class).getAttributes().size();
if ((nrAtts > maxAtts) || (nrAtts < minAtts))
return false;
}
int maxEx = getParameterAsInt(PARAMETER_MAX_EXAMPLES);
int minEx = getParameterAsInt(PARAMETER_MIN_EXAMPLES);
if ((maxEx < Integer.MAX_VALUE) || (minEx > 0)) {
int nrEx = input.get(ExampleSet.class).size();
if ((nrEx > maxEx) || (nrEx < minEx))
return false;
}
double maxCrit = getParameterAsDouble(PARAMETER_MAX_CRITERION);
double minCrit = getParameterAsDouble(PARAMETER_MIN_CRITERION);
if ((maxCrit < Double.POSITIVE_INFINITY) || (minCrit > Double.NEGATIVE_INFINITY)) {
double crit = input.get(PerformanceVector.class).getMainCriterion().getAverage();
if ((crit > maxCrit) || (crit < minCrit))
return false;
}
log("All criteria met.");
return true;
}
/** Returns a simple chain condition. */
public InnerOperatorCondition getInnerOperatorCondition() {
return new SimpleChainInnerOperatorCondition();
}
/** Returns the maximum number of innner operators. */
public int getMinNumberOfInnerOperators() {
return 0;
}
/** Returns the minimum number of innner operators. */
public int getMaxNumberOfInnerOperators() {
return Integer.MAX_VALUE;
}
public Class<?>[] getOutputClasses() {
return new Class[0];
}
public Class<?>[] getInputClasses() {
return new Class[0];
}
public List<ParameterType> getParameterTypes() {
List<ParameterType> types = super.getParameterTypes();
ParameterType type = new ParameterTypeInt(PARAMETER_MIN_ATTRIBUTES, "Minimal number of attributes in first example set", 0, Integer.MAX_VALUE, 0);
type.setExpert(false);
types.add(type);
type = new ParameterTypeInt(PARAMETER_MAX_ATTRIBUTES, "Maximal number of attributes in first example set", 0, Integer.MAX_VALUE, 0);
type.setExpert(false);
types.add(type);
type = new ParameterTypeInt(PARAMETER_MIN_EXAMPLES, "Minimal number of examples in first example set", 0, Integer.MAX_VALUE, 0);
type.setExpert(false);
types.add(type);
type = new ParameterTypeInt(PARAMETER_MAX_EXAMPLES, "Maximal number of examples in first example set", 0, Integer.MAX_VALUE, Integer.MAX_VALUE);
type.setExpert(false);
types.add(type);
type = new ParameterTypeDouble(PARAMETER_MIN_CRITERION, "Minimal main criterion in first performance vector", Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY);
type.setExpert(false);
types.add(type);
type = new ParameterTypeDouble(PARAMETER_MAX_CRITERION, "Maximal main criterion in first performance vector", Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY);
type.setExpert(false);
types.add(type);
type = new ParameterTypeInt(PARAMETER_MAX_ITERATIONS, "Maximum number of iterations", 0, Integer.MAX_VALUE, Integer.MAX_VALUE);
type.setExpert(true);
types.add(type);
type = new ParameterTypeInt(PARAMETER_TIMEOUT, "Timeout in minutes (-1 = no timeout)", 1, Integer.MAX_VALUE, -1);
type.setExpert(true);
types.add(type);
type = new ParameterTypeCategory(PARAMETER_PERFORMANCE_CHANGE, "Stop when performance of inner chain behaves like this.", COMPARISONS, NONE);
type.setExpert(false);
types.add(type);
type = new ParameterTypeBoolean(PARAMETER_CONDITION_BEFORE, "Evaluate condition before inner chain is applied (true) or after?", true);
type.setExpert(true);
types.add(type);
return types;
}
}