/** * Copyright (C) 2001-2017 by RapidMiner and the contributors * * Complete list of developers available at our web site: * * http://rapidminer.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 org.jfree.util.Log; import com.rapidminer.example.ExampleSet; import com.rapidminer.operator.IOContainer; import com.rapidminer.operator.OperatorDescription; import com.rapidminer.operator.OperatorException; import com.rapidminer.operator.ProcessSetupError.Severity; import com.rapidminer.operator.SimpleProcessSetupError; import com.rapidminer.operator.performance.PerformanceVector; import com.rapidminer.operator.ports.InputPort; import com.rapidminer.operator.ports.metadata.MetaData; import com.rapidminer.operator.ports.metadata.SimplePrecondition; 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; import com.rapidminer.parameter.conditions.BooleanParameterCondition; /** * Performs its inner operators until all given criteria are met or a timeout occurs. * * @author Stefan Rueping */ public class RepeatUntilOperatorChain extends AbstractIteratingOperatorChain { public static final String PARAMETER_CONDITION_EXAMPLES = "condition_on_data"; /** 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"; public static final String PARAMETER_CONDITION_PERFORMANCE = "condition_on_performance"; /** 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"; public static final String PARAMETER_LIMIT_TIME = "limit_time"; /** 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 long stoptime; private double fitness; private int maxit; private boolean conditionBefore; private boolean conditionPerformance; private int performanceChange; private double maxCrit; private double minCrit; private boolean conditionExamples; private int maxAttributes; private int minAttributes; private int maxEx; private int minEx; private final InputPort performanceConditionInput = getSubprocess(0).getInnerSinks().createPort("performance"); private final InputPort exampleSetConditionInput = getSubprocess(0).getInnerSinks().createPort("example set"); public RepeatUntilOperatorChain(OperatorDescription description) { super(description); performanceConditionInput .addPrecondition(new SimplePrecondition(performanceConditionInput, new MetaData(PerformanceVector.class)) { @Override protected boolean isMandatory() { return getParameterAsBoolean(PARAMETER_CONDITION_PERFORMANCE); } }); exampleSetConditionInput .addPrecondition(new SimplePrecondition(exampleSetConditionInput, new MetaData(ExampleSet.class)) { @Override protected boolean isMandatory() { return getParameterAsBoolean(PARAMETER_CONDITION_EXAMPLES); } }); } @Override public void performAdditionalChecks() { if (!getParameterAsBoolean(PARAMETER_CONDITION_PERFORMANCE) && !getParameterAsBoolean(PARAMETER_CONDITION_EXAMPLES)) { this.addError(new SimpleProcessSetupError(Severity.ERROR, getPortOwner(), "need_one_parameter", PARAMETER_CONDITION_EXAMPLES, PARAMETER_CONDITION_PERFORMANCE)); } try { if (getParameterAsBoolean(PARAMETER_CONDITION_PERFORMANCE)) { double maxCrit = getParameterAsDouble(PARAMETER_MAX_CRITERION); double minCrit = getParameterAsDouble(PARAMETER_MIN_CRITERION); if (maxCrit < Double.POSITIVE_INFINITY || minCrit > Double.NEGATIVE_INFINITY) { if (maxCrit < minCrit) { this.addError(new SimpleProcessSetupError(Severity.ERROR, getPortOwner(), "parameter_combination_forbidden_range", PARAMETER_MIN_CRITERION, PARAMETER_MAX_CRITERION)); } } } if (getParameterAsBoolean(PARAMETER_CONDITION_EXAMPLES)) { int maxAtts = getParameterAsInt(PARAMETER_MAX_ATTRIBUTES); int minAtts = getParameterAsInt(PARAMETER_MIN_ATTRIBUTES); if (maxAtts < Double.POSITIVE_INFINITY || minAtts > Double.NEGATIVE_INFINITY) { if (maxAtts < minAtts) { this.addError(new SimpleProcessSetupError(Severity.ERROR, getPortOwner(), "parameter_combination_forbidden_range", PARAMETER_MIN_ATTRIBUTES, PARAMETER_MAX_ATTRIBUTES)); } } int maxEx = getParameterAsInt(PARAMETER_MAX_EXAMPLES); int minEx = getParameterAsInt(PARAMETER_MIN_EXAMPLES); if (maxEx < Double.POSITIVE_INFINITY || minEx > Double.NEGATIVE_INFINITY) { if (maxEx < minEx) { this.addError(new SimpleProcessSetupError(Severity.ERROR, getPortOwner(), "parameter_combination_forbidden_range", PARAMETER_MIN_EXAMPLES, PARAMETER_MAX_EXAMPLES)); } } } } catch (UndefinedParameterError e) { Log.error("parameter undefined", e); } } @Override public void doWork() throws OperatorException { stoptime = Long.MAX_VALUE; maxit = getParameterAsInt(PARAMETER_MAX_ITERATIONS); conditionBefore = getParameterAsBoolean(PARAMETER_CONDITION_BEFORE); conditionPerformance = getParameterAsBoolean(PARAMETER_CONDITION_PERFORMANCE); performanceChange = getParameterAsInt(PARAMETER_PERFORMANCE_CHANGE); maxCrit = getParameterAsDouble(PARAMETER_MAX_CRITERION); minCrit = getParameterAsDouble(PARAMETER_MIN_CRITERION); conditionExamples = getParameterAsBoolean(PARAMETER_CONDITION_EXAMPLES); maxAttributes = getParameterAsInt(PARAMETER_MAX_ATTRIBUTES); minAttributes = getParameterAsInt(PARAMETER_MIN_ATTRIBUTES); maxEx = getParameterAsInt(PARAMETER_MAX_EXAMPLES); minEx = getParameterAsInt(PARAMETER_MIN_EXAMPLES); if (getParameterAsBoolean(PARAMETER_LIMIT_TIME)) { stoptime = System.currentTimeMillis() + 60L * 1000 * getParameterAsInt(PARAMETER_TIMEOUT); } fitness = Double.NEGATIVE_INFINITY; getProgress().setTotal(maxit); super.doWork(); } /** * Evaluates whether the stopping condition is met * * @throws OperatorException */ private boolean evaluateCondition() throws OperatorException { if (getIteration() == 0 && !conditionBefore) { return false; } if (getIteration() >= maxit) { getLogger().fine("Maximum number of iterations met."); return true; } ; if (java.lang.System.currentTimeMillis() > stoptime) { getLogger().fine("Runtime exceeded."); return true; } ; // NOTE: This is not optional if (conditionPerformance) { PerformanceVector performanceVector = performanceConditionInput.getData(PerformanceVector.class); int changeType = performanceChange; if (changeType != NONE) { if (getIteration() > 0) { double currentFitness = performanceVector.getMainCriterion().getFitness(); if (changeType == DECREASING && currentFitness < fitness) { return true; } else if (changeType == NONINCREASING && currentFitness <= fitness) { return true; } fitness = currentFitness; return false; } } if (maxCrit < Double.POSITIVE_INFINITY || minCrit > Double.NEGATIVE_INFINITY) { double crit = performanceVector.getMainCriterion().getAverage(); if (crit > maxCrit || crit < minCrit) { return false; } } } if (conditionExamples) { ExampleSet exampleSet = exampleSetConditionInput.getData(ExampleSet.class); int maxAtts = maxAttributes; int minAtts = minAttributes; if (maxAtts < Integer.MAX_VALUE || minAtts > 0) { int nrAtts = exampleSet.getAttributes().size(); if (nrAtts > maxAtts || nrAtts < minAtts) { return false; } } if (maxEx < Integer.MAX_VALUE || minEx > 0) { int nrEx = exampleSet.size(); if (nrEx > maxEx || nrEx < minEx) { return false; } } } getLogger().fine("All criteria met."); return true; } @Override boolean shouldStop(IOContainer iterationResults) throws OperatorException { return evaluateCondition(); } @Override public List<ParameterType> getParameterTypes() { List<ParameterType> types = super.getParameterTypes(); ParameterType type = new ParameterTypeBoolean(PARAMETER_CONDITION_EXAMPLES, "If checked, conditions for the loop abortion based on the example set can be defined.", true); // type.registerDependencyCondition(new BooleanParameterCondition(this, // PARAMETER_CONDITION_PERFORMANCE, true, false)); types.add(type); type = new ParameterTypeInt(PARAMETER_MIN_ATTRIBUTES, "Minimal number of attributes in first example set", 0, Integer.MAX_VALUE, 0); type.registerDependencyCondition(new BooleanParameterCondition(this, PARAMETER_CONDITION_EXAMPLES, false, true)); 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.registerDependencyCondition(new BooleanParameterCondition(this, PARAMETER_CONDITION_EXAMPLES, false, true)); 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.registerDependencyCondition(new BooleanParameterCondition(this, PARAMETER_CONDITION_EXAMPLES, false, true)); 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.registerDependencyCondition(new BooleanParameterCondition(this, PARAMETER_CONDITION_EXAMPLES, false, true)); type.setExpert(false); types.add(type); type = new ParameterTypeBoolean(PARAMETER_CONDITION_PERFORMANCE, "If checked, conditions for the loop abortion based on the performance can be defined.", true); 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.registerDependencyCondition(new BooleanParameterCondition(this, PARAMETER_CONDITION_PERFORMANCE, false, true)); 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.registerDependencyCondition(new BooleanParameterCondition(this, PARAMETER_CONDITION_PERFORMANCE, false, true)); type.setExpert(false); types.add(type); type = new ParameterTypeCategory(PARAMETER_PERFORMANCE_CHANGE, "Stop when performance of inner chain behaves like this. 'none' means to ignore any performance changes.", COMPARISONS, NONE); type.registerDependencyCondition(new BooleanParameterCondition(this, PARAMETER_CONDITION_PERFORMANCE, false, true)); 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 ParameterTypeBoolean(PARAMETER_LIMIT_TIME, "If checked, the loop will be aborted at last after a specified time.", false); types.add(type); type = new ParameterTypeInt(PARAMETER_TIMEOUT, "Timeout in minutes", 1, Integer.MAX_VALUE, 1); type.registerDependencyCondition(new BooleanParameterCondition(this, PARAMETER_LIMIT_TIME, true, true)); type.setExpert(true); types.add(type); type = new ParameterTypeBoolean(PARAMETER_CONDITION_BEFORE, "Evaluate condition before inner chain is applied (true) or after?", false); type.setExpert(true); types.add(type); return types; } }