package splar.core.fm.randomization; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.Iterator; import java.util.List; import java.util.Random; import java.util.Vector; import splar.core.constraints.PropositionalFormula; import splar.core.fm.FeatureGroup; import splar.core.fm.FeatureModel; import splar.core.fm.FeatureModelException; import splar.core.fm.FeatureTreeNode; import splar.core.fm.GroupedFeature; import splar.core.fm.RootNode; import splar.core.fm.SolitaireFeature; import splar.core.fm.TreeNodeRendererFactory; public class RandomFeatureModel extends FeatureModel { private int numberOfFeaturesToCreate; // total number of features in the feature model not counting feature group nodes private int maxChildrenPerNode; // max number of children for a given node (opt, mand, and group count as one) private int minChildrenPerNode; // min number of children for a given node (opt, mand, and group count as one) private int mandatoryOdds; // odds for mandatory features private int optionalOdds; // odds for optional features private int group1NOdds; // odds for [1,*] groups private int group11Odds; // odds for [1,1] groups private int maxGroupCardinality; // max number of grouped features in a group private int balanceFactor; // [0%-100%] indicate the % of nodes that will NOT have children public RandomFeatureModel(String name,int numberOfFeaturesToCreate, int mandatoryOdds, int optionalOdds, int group1NOdds, int group11Odds, int minChildrenPerNode, int maxChildrenPerNode, int maxGroupCardinality, int balanceFactor) { super(); setName(name); // feature tree parameters this.numberOfFeaturesToCreate = numberOfFeaturesToCreate; this.maxChildrenPerNode = maxChildrenPerNode; this.minChildrenPerNode = minChildrenPerNode; this.mandatoryOdds = mandatoryOdds; this.optionalOdds = optionalOdds; this.group1NOdds = group1NOdds; this.group11Odds = group11Odds; this.maxGroupCardinality = maxGroupCardinality; this.balanceFactor = balanceFactor; } protected FeatureTreeNode createNodes() throws FeatureModelException { int countFeatures = 1; Vector<FeatureTreeNode> fmNodes = new Vector<FeatureTreeNode>(); String featureName = "R"; countFeatures++; RootNode root = new RootNode( featureName, featureName, TreeNodeRendererFactory.createRootRenderer()); fmNodes.add(root); FeatureTreeNode parentNode = null; while ( countFeatures <= numberOfFeaturesToCreate ) { parentNode = fmNodes.firstElement(); fmNodes.removeElement(parentNode); int numberOfChildNodesToCreate = Math.min(numberOfFeaturesToCreate-countFeatures+1, (Math.abs(new Random().nextInt())%(maxChildrenPerNode-minChildrenPerNode+1))+minChildrenPerNode); // root node always has the maximum allowed number of children if ( parentNode.getParent() == null ) { numberOfChildNodesToCreate = maxChildrenPerNode; } else if ( isRoot((FeatureTreeNode)parentNode.getParent()) ) { numberOfChildNodesToCreate = maxChildrenPerNode; } // prevents an early end of the recursion when all nodes happen to have no children if ( numberOfChildNodesToCreate == 0 ) { if ( fmNodes.size() == 0 ) { numberOfChildNodesToCreate = 1; } } if ( numberOfChildNodesToCreate > 0 ) { for( int i = 0 ; i < numberOfChildNodesToCreate && countFeatures <= numberOfFeaturesToCreate ; i++ ) { String childFeatureName = parentNode.getID().substring(1) + (i+1); FeatureTreeNode randomNode = createRandomNode(childFeatureName, randonlyChooseNodeTypeToCreate()); parentNode.add(randomNode); if ( randomNode instanceof FeatureGroup ) { FeatureGroup groupRandomNode = (FeatureGroup)randomNode; int countGroupedNodes = groupRandomNode.getChildCount(); if ( Math.abs(new Random().nextInt()) % 100 > balanceFactor || fmNodes.size() == 0 ) { for( int j = 0 ; j < countGroupedNodes ; j++ ) { fmNodes.add((FeatureTreeNode)groupRandomNode.getChildAt(j)); } } countFeatures += (countGroupedNodes); } else { if ( Math.abs(new Random().nextInt()) % 100 > balanceFactor || fmNodes.size() == 0 ) { fmNodes.add(randomNode); } countFeatures++; } } } } return root; } private String randonlyChooseNodeTypeToCreate() { int randomIndex = Math.abs(new Random().nextInt()) % (mandatoryOdds+optionalOdds+group1NOdds+group11Odds); if ( randomIndex < mandatoryOdds ) { return "mandatory"; } else if ( randomIndex < (mandatoryOdds+optionalOdds)) { return "optional"; } else if ( randomIndex < (mandatoryOdds+optionalOdds+group1NOdds)) { return "group1N"; } return "group11"; } private FeatureTreeNode createRandomNode(String childFeatureName, String nodeType) { FeatureTreeNode node = null; String featureName = ""; // should create a solitaire feature (optional/mandatory)? if ( nodeType.compareToIgnoreCase("optional") == 0 ) { featureName = "o" + childFeatureName; node = new SolitaireFeature( true, featureName, featureName, TreeNodeRendererFactory.createOptionalRenderer()); } else if ( nodeType.compareToIgnoreCase("mandatory") == 0 ) { featureName = "m" + childFeatureName; node = new SolitaireFeature( false, featureName, featureName, TreeNodeRendererFactory.createOptionalRenderer()); } // should create a feature group? else if ( nodeType.compareToIgnoreCase("group1N") == 0 ) { // number of grouped features int groupSize = (Math.abs(new Random().nextInt())%(maxGroupCardinality))+1; if (groupSize <= 1) groupSize=2; int lower = 1; int upper = -1; String groupName = "_Gi_" + childFeatureName; node = new FeatureGroup( groupName, groupName, lower,upper,TreeNodeRendererFactory.createFeatureGroupRenderer() ); // create grouped feature nodes for( int i = 0 ; i < groupSize ; i++ ) { featureName = "g" + childFeatureName + (i+1); node.add( new GroupedFeature( featureName, featureName, TreeNodeRendererFactory.createGroupedRenderer() )); } } else if ( nodeType.compareToIgnoreCase("group11") == 0 ) { // number of grouped features int groupSize = (Math.abs(new Random().nextInt())%(maxGroupCardinality))+1; if (groupSize <= 1) groupSize=2; int lower = 1; int upper = 1; String groupName = "_Ge_" + childFeatureName; node = new FeatureGroup( groupName, groupName, lower,upper,TreeNodeRendererFactory.createFeatureGroupRenderer() ); // create grouped feature nodes for( int i = 0 ; i < groupSize ; i++ ) { featureName = "g" + childFeatureName + (i+1); node.add( new GroupedFeature( featureName, featureName, TreeNodeRendererFactory.createGroupedRenderer() )); } } return node; } // returns the number of levels for which constraints were created for public int createExtraConstraints(int numVars, int numConstraints, int maxArity, int [][] modularityParameters) { return RandomFeatureModel.createExtraConstraints(this, numVars, numConstraints, maxArity, modularityParameters); } public static int createExtraConstraints(FeatureModel fm, int numVars, int numConstraints, int maxArity, int [][] modularityParameters) { int levelsToConsider = modularityParameters.length; int depth = fm.depth()-1; // last level are the leaves, ie, cannot be considered as no children nodes are available levelsToConsider = ( levelsToConsider > depth ) ? depth : levelsToConsider; // discover levels to be worked out based on percentages passed as parameters for( int i = 0 ; i < levelsToConsider; i++ ) { modularityParameters[i][0] = Math.round(modularityParameters[i][0]/100f * (depth)); modularityParameters[i][1] = Math.round(modularityParameters[i][1]/100f * numVars); } fm.resetNodesAttachedData(); // generates constraints for each level int countLevelsWithContraints = 0; for( int i = levelsToConsider-1 ; i >= 0 ; i-- ) { // at least two variables to consider level if ( modularityParameters[i][1] >= 2 ) { int numConstraintsLevel = Math.round((numConstraints * modularityParameters[i][1]) / numVars); _LevelConstraintGenerator constraintGenerator = new _LevelConstraintGenerator(fm, modularityParameters[i][0], modularityParameters[i][1], numConstraintsLevel, maxArity, modularityParameters[i][2]); List<PropositionalFormula> levelFormulas = constraintGenerator.createExtraConstraints(); countLevelsWithContraints = (levelFormulas.size() > 0) ? countLevelsWithContraints + 1 : countLevelsWithContraints; for( PropositionalFormula formula : levelFormulas) { fm.addConstraint(formula); } } } return countLevelsWithContraints; } public void saveNodes() {} } class _LevelConstraintGenerator { private FeatureModel fm; private int level; private int numVars; private int numConstraints; private int maxArity; private int percentage; public _LevelConstraintGenerator(FeatureModel fm, int level, int numVars, int numConstraints, int maxArity, int percentage) { this.fm = fm; this.level = level; this.numVars = numVars; this.numConstraints = numConstraints; this.maxArity = maxArity; this.percentage = percentage; } public int getNumberOfVariables() { return numVars; } public List<PropositionalFormula> createExtraConstraints() { List<PropositionalFormula> formulas = new ArrayList<PropositionalFormula>(); // select clusters to add constraints based on the percentage specified List<FeatureTreeNode> selectedClusters = selectClusters(fm.getNodesAtLevel(level), percentage); int numClusters = selectedClusters.size(); if ( numClusters > 0 ) { // makes number of variables/constraints proportional to each cluster size // minimum is 2 vars/1 constraint per cluster int clustersTotalSize = 0; for( FeatureTreeNode cluster : selectedClusters ) { clustersTotalSize += fm.countNodes(cluster); } // for each selected cluster, select features for constraints for( FeatureTreeNode cluster : selectedClusters ) { float percVarsConst = fm.countNodes(cluster)/(1f*clustersTotalSize); int numVarsCluster = Math.round(percVarsConst*numVars); int numConstraintsCluster = numVarsCluster/3; //Math.round(percVarsConst*numConstraints); if ( numVarsCluster < 2 || numConstraintsCluster < 1) { numVarsCluster = 2; numConstraintsCluster = 1; } // System.out.println("Cluster: " + cluster.getID()); // System.out.println("********** vars: " + numVarsCluster); // System.out.println("********** const: " + numConstraintsCluster); formulas.addAll(createClusterConstraints(cluster, numVarsCluster, numConstraintsCluster)); } } return formulas; } private List<PropositionalFormula> createClusterConstraints(FeatureTreeNode clusterNode, int numVarsCluster, int numConstraintsCluster) { List<PropositionalFormula> formulas = new ArrayList<PropositionalFormula>(); List<FeatureTreeNode> nodes = new ArrayList<FeatureTreeNode>(); for( int i = 0 ; i < clusterNode.getChildCount() ; i++ ) { nodes.add((FeatureTreeNode)clusterNode.getChildAt(i)); } // initializations ArrayList<List<FeatureTreeNode>> pickedNodesForVariables = new ArrayList<List<FeatureTreeNode>>(); int countNodes = nodes.size(); for( int i = 0 ; i < countNodes; i++ ) { pickedNodesForVariables.add(i, new ArrayList<FeatureTreeNode>()); } List<FeatureTreeNode> tempNodes = new ArrayList<FeatureTreeNode>(); tempNodes.addAll(nodes); int nodeIndex = 0; for( int i = 0 ; i < numVarsCluster && tempNodes.size() > 0 ; i++ ) { FeatureTreeNode node = tempNodes.get(nodeIndex); if ( !selectVariableFeature(node, pickedNodesForVariables.get(nodeIndex)) ) { tempNodes.remove(nodeIndex); nodeIndex = (nodeIndex == (tempNodes.size())) ? 0 : nodeIndex; } else { nodeIndex = (++nodeIndex % tempNodes.size()); } } // Remove subtrees that have no variables selected for them (eg. when numVars is very small some subtrees may never get any features picked) int indexToRemove = 0; for( int i = 0 ; i < countNodes ; i++ ) { List<FeatureTreeNode> pickedFeatures = pickedNodesForVariables.get(indexToRemove); if ( pickedFeatures.size() == 0 ) { pickedNodesForVariables.remove(indexToRemove); nodes.remove(indexToRemove); } else { indexToRemove++; } } countNodes = nodes.size(); // // remove 34% of the nodes if the number of nodes is larger than 2 // if ( countNodes > 2 ) { // countNodes = Math.round(0.60f * countNodes); // } if ( countNodes > 0 ) { // create indexes to track down variables selected for each subtree int varsIndex[] = new int[countNodes]; for( int i = 0 ; i < countNodes ; i++ ) { varsIndex[i] = 0; } // generate constraints Collections.shuffle(pickedNodesForVariables); nodeIndex = 0; for( int i = 0 ; i < numConstraintsCluster ; i++ ) { List<FeatureTreeNode> variables = new ArrayList<FeatureTreeNode>(); int arity = 2; // + Math.abs(new Random().nextInt())%(maxArity-1); for( int j = 0 ; j < arity ; j++ ) { List<FeatureTreeNode> variablesForNode = pickedNodesForVariables.get(nodeIndex); variables.add(variablesForNode.get(varsIndex[nodeIndex])); varsIndex[nodeIndex] = (++varsIndex[nodeIndex])%pickedNodesForVariables.get(nodeIndex).size(); if ( varsIndex[nodeIndex] == 0) { Collections.shuffle(pickedNodesForVariables.get(nodeIndex)); } // nodeIndex = (Math.abs(new Random().nextInt())%countNodes); // nodeIndex = (++nodeIndex)%countNodes; if ( Math.abs(new Random().nextInt())%2 == 0 ) { nodeIndex = (Math.abs(new Random().nextInt())%countNodes); } else if ( (++nodeIndex) == countNodes ) { nodeIndex = 0; } } String formulaStr = ""; for( Iterator<FeatureTreeNode> it = variables.iterator() ; it.hasNext() ; ) { formulaStr += it.next().getID(); if ( it.hasNext() ) { formulaStr += " OR "; } } try { formulas.add( new PropositionalFormula("C_"+(level+1)+"_"+clusterNode.getID()+"_"+i,formulaStr) ); } catch( Exception e ) { System.out.println("Error: " + formulaStr); // e.printStackTrace(); } } } return formulas; } // returns true if a node was found and false if no node is available, ie, they have all been previously picked private boolean selectVariableFeature(FeatureTreeNode subtreeRootNode, List<FeatureTreeNode> pickedNodesForVariables) { List<FeatureTreeNode> subtreeNodes = new ArrayList<FeatureTreeNode>(); fm.getSubtreeNodes(subtreeRootNode, subtreeNodes); if( !(subtreeRootNode instanceof FeatureGroup) ) { subtreeNodes.add(subtreeRootNode); } // remove nodes that have been already picked for( Iterator<FeatureTreeNode> it = subtreeNodes.iterator() ; it.hasNext() ; ) { FeatureTreeNode node = it.next(); if ( !(node instanceof FeatureGroup ) ) { Integer isSelected = (Integer)node.getAttachedData(); if ( (isSelected != null && isSelected.intValue() == 1) || pickedNodesForVariables.contains(node)) { it.remove(); } } } int countNodes = subtreeNodes.size(); if ( countNodes > 0 ) { FeatureTreeNode nodeSelected = subtreeNodes.get(Math.abs(new Random().nextInt())%countNodes); nodeSelected.attachData(new Integer(1)); pickedNodesForVariables.add(nodeSelected); return true; } return false; } // to be selected as a cluster a subtree must have: // - at least two subtreess // - each subtree must have at least public List<FeatureTreeNode> selectClusters(List<FeatureTreeNode> nodes, int percentage) { int totalCandidateClusters = nodes.size(); // ELIMINATION PHASE 1: Eliminate subtrees that do not qualify as clusters // find cluster candidate based on the number of subtrees (minimum 2) and features availability (minimum 1 per subtree) for( Iterator<FeatureTreeNode> it = nodes.iterator() ; it.hasNext() ; ) { FeatureTreeNode node = it.next(); // at least two subtrees are necessary for a cluster to qualify boolean qualifies = false; if ( node.getChildCount() >= 2 ) { int countCandidateSubTrees = 0; for( int i = 0 ; i < node.getChildCount() ; i++ ) { FeatureTreeNode childNode = (FeatureTreeNode)node.getChildAt(i); // each subtree must have at least one available feature if ( countAvailableFeatures(childNode, !(node instanceof GroupedFeature)) >= 1 ) { countCandidateSubTrees++; } } qualifies = (countCandidateSubTrees >= 2); } if ( !qualifies ) { it.remove(); } } // Shuffle candidate clusters for fairness java.util.Collections.shuffle(nodes); // ELIMINATION PHASE 2: Eliminate nodes based on specified percentage parameter int nodesToEliminate = Math.min(nodes.size(), Math.round((1-(percentage/100f))*totalCandidateClusters)); for( int i = 0 ; i < nodesToEliminate ; i++ ) { nodes.remove(Math.abs(new Random().nextInt())%nodes.size()); } // ELIMINATION PHASE 3: When number of clusters is larger in comparison to the number of variables for the cluster, // Eliminate clusters so that each remaining cluster has at least two variables float numClusters = nodes.size(); if ( (2*numClusters) > numVars) { int clustersToRemove = Math.round(numClusters-(numVars/2f)); for( int i = 0 ; i < clustersToRemove ; i++ ) { nodes.remove(Math.round(Math.abs(new Random().nextInt())%nodes.size())); } } return nodes; } private int countAvailableFeatures(FeatureTreeNode node, boolean includesNodeItSelf) { List<FeatureTreeNode> nodes = new ArrayList<FeatureTreeNode>(); fm.getSubtreeNodes(node, nodes); if ( includesNodeItSelf ) { nodes.add(node); } int countFeatures = 0; for( FeatureTreeNode descendantNode : nodes ) { if ( !(descendantNode instanceof FeatureGroup ) && descendantNode.getAttachedData() == null ) { countFeatures++; } } return countFeatures; } // Select Subtrees based on the percentage indicated // public List<FeatureTreeNode> selectSubtrees(List<FeatureTreeNode> nodes, int percentage, int minChildren) { // // if ( nodes.size() == 1 ) { // return nodes; // } // // java.util.Collections.shuffle(nodes); // // /*********************************/ // int countNodes = nodes.size(); // int nodesToEliminate = Math.round((1-(percentage/100f))*countNodes); // //// System.out.println("Subtrees picked: " + (countNodes-nodesToEliminate)); // // // at least two nodes must be left //// nodesToEliminate = (nodes.size()-nodesToEliminate)<=1 ? (countNodes-2) : nodesToEliminate; // for( int i = 0 ; i < nodesToEliminate ; i++ ) { // nodes.remove(Math.abs(new Random().nextInt())%nodes.size()); // } ///*********************************/ // // remove nodes with less than 2 features // for( Iterator<FeatureTreeNode> it = nodes.iterator() ; it.hasNext() ; ) { // FeatureTreeNode node = it.next(); // int minChildrenAllowed = minChildren; // if ( node instanceof FeatureGroup ) { // minChildrenAllowed += node.getChildCount(); // } //// System.out.println("Subtree size: " + fm.countNodes(node)); // if ( (fm.countNodes(node) <= minChildrenAllowed) || ( node instanceof FeatureGroup )) { // it.remove(); // } // } // // // select nodes based on the specified percentage // // return nodes; // } } /* * Produce constraints for a particular level of the Feature Tree by linking the subtrees in the level through particular selected features */ //class _LevelConstraintGenerator { // // private FeatureModel fm; // private int level; // private int numVars; // private int numConstraints; // private int maxArity; // private int percentage; // // public _LevelConstraintGenerator(FeatureModel fm, int level, int numVars, int numConstraints, int maxArity, int percentage) { // this.fm = fm; // this.level = level; // this.numVars = numVars; // this.numConstraints = numConstraints; // this.maxArity = maxArity; // this.percentage = percentage; // } // // public int getNumberOfVariables() { // return numVars; // } // // public List<PropositionalFormula> createExtraConstraints() { // // List<PropositionalFormula> formulas = new ArrayList<PropositionalFormula>(); // // // select subtrees to add constraints based on the percentage specified // List<FeatureTreeNode> selectedClusters = selectSubtrees(); // // // at least two subtrees have been selected? // int countSubTrees = selectedClusters.size(); // if ( countSubTrees > 1 ) { // // select variables and mark them so that they wont be used at other levels // ArrayList<List<FeatureTreeNode>> pickedNodesForVariables = new ArrayList<List<FeatureTreeNode>>(); // for( int i = 0 ; i < countSubTrees; i++ ) { // pickedNodesForVariables.add(i, new ArrayList<FeatureTreeNode>()); // } // int subtreeIndex = 0; // int countNotPickedVariables = 0; // for( int i = 0 ; i < numVars ; i++ ) { // FeatureTreeNode subtreeRootNode = selectedClusters.get(subtreeIndex); // if ( !selectVariableFeature(subtreeRootNode, pickedNodesForVariables.get(subtreeIndex)) ) { // countNotPickedVariables++; // } // subtreeIndex = (++subtreeIndex % countSubTrees); // } // // // update number of variables actually used // numVars -= countNotPickedVariables; // // // Remove subtrees that have no variables selected for them (eg. when numVars is very small some subtrees may never get any features picked) // int indexToRemove = 0; // for( int i = 0 ; i < countSubTrees ; i++ ) { // List<FeatureTreeNode> pickedFeatures = pickedNodesForVariables.get(indexToRemove); // if ( pickedFeatures.size() == 0 ) { // pickedNodesForVariables.remove(indexToRemove); // selectedClusters.remove(indexToRemove); //// System.out.println("Removed! + " + indexToRemove); // } // else { // indexToRemove++; // } // } // // // DEBUG // for( int i = 0 ; i < selectedClusters.size() ; i++ ) { // // System.out.print("\n" + selectedClusters.get(i).getID() + ": "); // List<FeatureTreeNode> list = pickedNodesForVariables.get(i); // for( FeatureTreeNode node : list ) { // System.out.print(node.getID() + ","); // } // System.out.print("\n"); // } // // // create indexes to track down variables selected for each subtree // int varsIndex[] = new int[countSubTrees]; // for( int i = 0 ; i < countSubTrees ; i++ ) { // varsIndex[i] = 0; // } // // // generate constraints // subtreeIndex = 0; // for( int i = 0 ; i < numConstraints ; i++ ) { // List<FeatureTreeNode> variables = new ArrayList<FeatureTreeNode>(); // for( int j = 0 ; j < maxArity ; j++ ) { // variables.add(pickedNodesForVariables.get(subtreeIndex).get(varsIndex[subtreeIndex])); // varsIndex[subtreeIndex] = (++varsIndex[subtreeIndex])%pickedNodesForVariables.get(subtreeIndex).size(); // if ( varsIndex[subtreeIndex] == 0) { // Collections.shuffle(pickedNodesForVariables.get(subtreeIndex)); // } // subtreeIndex = (++subtreeIndex)%countSubTrees; // } // // String formulaStr = ""; // for( Iterator<FeatureTreeNode> it = variables.iterator() ; it.hasNext() ; ) { // formulaStr += it.next().getID(); // if ( it.hasNext() ) { // formulaStr += " OR "; // } // } // try { // formulas.add( new PropositionalFormula("C_"+level+"_"+i,formulaStr) ); // } // catch( Exception e ) { // e.printStackTrace(); // } // // } // } // // return formulas; // } // // // returns true if a node was found and false if no node is available, ie, they have all been previously picked // private boolean selectVariableFeature(FeatureTreeNode subtreeRootNode, List<FeatureTreeNode> pickedNodesForVariables) { // // List<FeatureTreeNode> subtreeNodes = new ArrayList<FeatureTreeNode>(); // fm.getSubtreeNodes(subtreeRootNode, subtreeNodes); // // // remove nodes that have been already picked // for( Iterator<FeatureTreeNode> it = subtreeNodes.iterator() ; it.hasNext() ; ) { // FeatureTreeNode node = it.next(); // Integer isSelected = (Integer)node.getAttachedData(); // if ( (isSelected != null && isSelected.intValue() == 1) || pickedNodesForVariables.contains(node)) { // it.remove(); // } // } // // int countNodes = subtreeNodes.size(); // if ( countNodes > 0 ) { // FeatureTreeNode nodeSelected = subtreeNodes.get(Math.abs(new Random().nextInt())%countNodes); // nodeSelected.attachData(new Integer(1)); // pickedNodesForVariables.add(nodeSelected); // return true; // } // // return false; // } // // // Select Subtrees at level "level" based on the percentage indicated // public List<FeatureTreeNode> selectSubtrees() { // // // candidate nodes // List<FeatureTreeNode> nodes = fm.getNodesAtLevel(level); // // // remove nodes without children // for( Iterator<FeatureTreeNode> it = nodes.iterator() ; it.hasNext() ; ) { // FeatureTreeNode node = it.next(); //// System.out.println("Subtree size: " + fm.countNodes(node)); // if ( fm.countNodes(node) <= 1 ) { // it.remove(); // } // } // // // select nodes based on the specified percentage // int countNodes = nodes.size(); // int nodesToEliminate = Math.round((1-(percentage/100f))*countNodes); // //// System.out.println("Subtrees picked: " + (countNodes-nodesToEliminate)); // // // at least two nodes must be left //// nodesToEliminate = (nodes.size()-nodesToEliminate)<=1 ? (countNodes-2) : nodesToEliminate; // for( int i = 0 ; i < nodesToEliminate ; i++ ) { // nodes.remove(Math.abs(new Random().nextInt())%nodes.size()); // } // // return nodes; // } //} // // // // //