/** * 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.learner.tree; import java.util.ArrayDeque; import java.util.Deque; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import com.rapidminer.operator.Operator; import com.rapidminer.operator.OperatorException; import com.rapidminer.operator.learner.tree.criterions.ColumnCriterion; import com.rapidminer.studio.internal.Resources; /** * Build a tree from an example set in parallel. The benefit calculation for the attributes is * parallelized until the nodes are too small. The remaining nodes are split in parallel. * * @author Gisa Schaefer */ public class ConcurrentTreeBuilder extends AbstractParallelTreeBuilder { private static final int MINIMAL_EXAMPLES_FOR_GROWING_PARALLEL = 5000; private static final int MINIMAL_EXAMPLES_FOR_SORTING_PARALLEL = 10000; /** * Pipes the arguments to the super constructor and sets an additional parameter allowing * parallel table creation. */ public ConcurrentTreeBuilder(Operator operator, ColumnCriterion criterion, List<ColumnTerminator> terminationCriteria, Pruner pruner, AttributePreprocessing preprocessing, boolean noPrePruning, int numberOfPrepruningAlternatives, int minSizeForSplit, int minLeafSize) { super(operator, criterion, terminationCriteria, pruner, preprocessing, noPrePruning, numberOfPrepruningAlternatives, minSizeForSplit, minLeafSize, true); } @Override void startTree(Tree root, Map<Integer, int[]> allSelectedExamples, int[] selectedAttributes, int depth) throws OperatorException { // start the tree building attribute parallel NodeData rootNode = new NodeData(root, allSelectedExamples, selectedAttributes, depth); Deque<NodeData> queue = new ArrayDeque<>(); queue.push(rootNode); List<NodeData> tooSmallList = new LinkedList<>(); while (!queue.isEmpty()) { NodeData nextNode = queue.pop(); if (nodeIsTooSmall(nextNode)) { tooSmallList.add(nextNode); } else { queue.addAll(splitNode(nextNode, true)); } } // only small nodes are left, split them in parallel List<Callable<Void>> todo = new LinkedList<>(); for (final NodeData node : tooSmallList) { Callable<Void> task = new Callable<Void>() { @Override public Void call() throws OperatorException { Deque<NodeData> queue = new ArrayDeque<>(); queue.push(node); while (!queue.isEmpty()) { queue.addAll(splitNode(queue.pop(), false)); } return null; } }; todo.add(task); } if (operator != null && Resources.getConcurrencyContext(operator).getParallelism() > 1) { try { Resources.getConcurrencyContext(operator).call(todo); } catch (ExecutionException e) { Throwable cause = e.getCause(); if (cause instanceof OperatorException) { throw (OperatorException) cause; } else if (cause instanceof RuntimeException) { throw (RuntimeException) cause; } else if (cause instanceof Error) { throw (Error) cause; } else { throw new OperatorException(cause.getMessage(), cause); } } } else { for (Callable<Void> task : todo) { try { task.call(); } catch (Exception e) { if (e instanceof OperatorException) { throw (OperatorException) e; } else if (e instanceof RuntimeException) { throw (RuntimeException) e; } // nothing else possible } } } } /** * Decides whether the node is to small to do the splitting attribute parallel. * * @param nodeData * @return */ boolean nodeIsTooSmall(NodeData nodeData) { return nodeData.getSelectedAttributes().length < 2 || SelectionCreator.getArbitraryValue(nodeData.getAllSelectedExamples()).length < MINIMAL_EXAMPLES_FOR_GROWING_PARALLEL; } @Override boolean doStartSelectionInParallel() { return columnTable.getNumberOfExamples() > MINIMAL_EXAMPLES_FOR_SORTING_PARALLEL && columnTable.getNumberOfRegularNumericalAttributes() > 1; } }