package splar.core.fm.randomization;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;
import splar.core.constraints.BooleanVariable;
import splar.core.constraints.BooleanVariableInterface;
import splar.core.constraints.CNFClause;
import splar.core.constraints.CNFFormula;
import splar.core.constraints.CNFGenerator;
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 RandomFeatureModel2 extends FeatureModel {
/**
*
*/
private static final long serialVersionUID = 637877082231222395L;
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
private static CNFGenerator cnfGenerator;
public RandomFeatureModel2(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-1;
this.maxChildrenPerNode = maxChildrenPerNode;
this.minChildrenPerNode = minChildrenPerNode;
this.mandatoryOdds = (2 + (maxGroupCardinality - 2)/2) * mandatoryOdds;
this.optionalOdds = (2 + (maxGroupCardinality - 2)/2) * optionalOdds;
// this.mandatoryOdds = mandatoryOdds;
// this.optionalOdds = optionalOdds;
this.group1NOdds = group1NOdds;
this.group11Odds = group11Odds;
this.maxGroupCardinality = maxGroupCardinality;
this.balanceFactor = balanceFactor;
cnfGenerator = new CNFGenerator();
}
@Override
protected FeatureTreeNode createNodes() throws FeatureModelException {
ArrayList<FeatureTreeNode> fmNodes = new ArrayList<FeatureTreeNode>();
String featureName = "R";
RootNode root = new RootNode( featureName, featureName, TreeNodeRendererFactory.createRootRenderer());
root.attachData(new Integer(numberOfFeaturesToCreate-1));
fmNodes.add(root);
FeatureTreeNode parentNode = null;
int countCreatedFeatures = 0;
while ( countCreatedFeatures < numberOfFeaturesToCreate ) {
parentNode = fmNodes.get(0);
fmNodes.remove(0);
int numberOfDescendants = ((Integer)parentNode.getAttachedData()).intValue();
int numberOfChildNodesToCreate = Math.min(numberOfDescendants, (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;
}
if ( numberOfChildNodesToCreate > 0 ) {
List<FeatureTreeNode> createdNodes = new ArrayList<FeatureTreeNode>();
int i = 0;
while ( (createdNodes.size() < numberOfChildNodesToCreate) && (countCreatedFeatures < numberOfFeaturesToCreate) ) {
int availableNodes = numberOfFeaturesToCreate-countCreatedFeatures;
String childFeatureName = parentNode.getID().substring(1) + "_" + (i+1);
FeatureTreeNode randomNode = createRandomNode(childFeatureName, randonlyChooseNodeTypeToCreate(availableNodes), availableNodes);
parentNode.add(randomNode);
if ( randomNode instanceof FeatureGroup ) {
FeatureGroup groupRandomNode = (FeatureGroup)randomNode;
int countGroupedNodes = groupRandomNode.getChildCount();
for( int j = 0 ; j < countGroupedNodes ; j++ ) {
FeatureTreeNode groupedNode = (FeatureTreeNode)groupRandomNode.getChildAt(j);
fmNodes.add(groupedNode);
}
countCreatedFeatures += (countGroupedNodes);
}
else {
fmNodes.add(randomNode);
countCreatedFeatures++;
}
createdNodes.add(randomNode);
i++;
}
// assign the number of descendants to create to each child node according to the balance factor
distributeChildrenNodes(createdNodes, numberOfDescendants-createdNodes.size());
for( FeatureTreeNode createdNode : createdNodes ){
if ( createdNode instanceof FeatureGroup ) {
int d = ((Integer)createdNode.getAttachedData()).intValue()/createdNode.getChildCount();
for( int j = 0 ; j < createdNode.getChildCount() ; j++ ) {
FeatureTreeNode groupedNode = (FeatureTreeNode) createdNode.getChildAt(j);
groupedNode.attachData(new Integer(d));
}
}
}
}
}
return root;
}
private void distributeChildrenNodes(List<FeatureTreeNode> nodes, int numberOfDescendants) {
int numChildren = nodes.size();
if ( numberOfDescendants <= 0 ) {
for( FeatureTreeNode node : nodes ) {
node.attachData(new Integer(0));
}
}
else if ( numChildren == 1 ) {
nodes.get(0).attachData(new Integer(numberOfDescendants));
}
else if ( numberOfDescendants < numChildren ){
for( int i = 0 ; i < numChildren ; i++ ) {
nodes.get(i).attachData( (i <= numberOfDescendants) ? new Integer(1) : new Integer(0) );
}
}
else {
int children[] = new int[numChildren];
int fixedNumDescendants = Math.round(numberOfDescendants * (1-(balanceFactor/100.0f)));
int variableNumDescendants = numberOfDescendants - fixedNumDescendants;
if ( variableNumDescendants > 0 ) {
// distribute variable number of descendants among children
float total = 0;
// generate random distribution
// System.out.println("------------------");
for( int i = 0 ; i < numChildren ;i++ ) {
int range = 2*(i+1);//(int)(Math.pow(10,i+1));
// System.out.println("Range: " + range);
children[i] = range;//Math.abs(new Random().nextInt())%range;
total += children[i];
}
// distribute according to random values
for( int i = 0 ; i < numChildren ;i++ ) {
float x = (children[i]/total) * variableNumDescendants;
// System.out.println("xx: " + x);
children[i] = Math.round(x);
}
}
else {
for( int i = 0 ; i < numChildren ;i++ ) {
children[i] = 0;
}
}
// distribute fixed num descendants among children
int childIndex = 0;
for( int i = 0 ; i < fixedNumDescendants ; i++ ) {
children[childIndex]++;
childIndex = (childIndex+1)%numChildren;
}
// System.out.println("**>> : " + numberOfDescendants);
// attach integers to nodes
for( int i = 0 ; i < numChildren ;i++ ) {
// System.out.println(">> : " + children[i]);
FeatureTreeNode node = nodes.get(i);
node.attachData(new Integer(children[i]));
}
}
}
private String randonlyChooseNodeTypeToCreate(int availableNodes) {
if ( availableNodes >= 2 ) {
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";
}
int randomIndex = Math.abs(new Random().nextInt()) % (mandatoryOdds+optionalOdds);
if ( randomIndex < mandatoryOdds ) {
return "mandatory";
}
return "optional";
}
private FeatureTreeNode createRandomNode(String childFeatureName, String nodeType, int numAvailableNodes) {
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.min((Math.abs(new Random().nextInt())%(maxGroupCardinality))+1, numAvailableNodes);
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.min((Math.abs(new Random().nextInt())%(maxGroupCardinality))+1, numAvailableNodes);
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);
// }
// expand the current number of 3-cnf clauses to reach the specified clause density
// never shrinks the number of clauses though
public static int expand3CNFClauses(FeatureModel fm, float clauseDensity, String prefix) {
CNFFormula cnf = fm.EC2CNF();
List<BooleanVariableInterface> vars = new LinkedList<BooleanVariableInterface>(cnf.getVariables());
List<CNFClause> clauses = new LinkedList<CNFClause>(cnf.getClauses());
List<CNFClause> allClauses = cnfGenerator.generateCNFInstance(vars, clauses , clauseDensity, 3);
int i = 0;
for( CNFClause clause : allClauses ) {
try {
fm.addConstraint(new PropositionalFormula(prefix + (i++), clause.toPropositionalFormula()));
}
catch( Exception e ) {
e.printStackTrace();
}
}
return allClauses.size();
}
public static int createExtraConstraints(FeatureModel fm, int numVars, int numConstraints, int maxArity, int [][] modularityParameters) {
return createExtraConstraints(fm, numVars, numConstraints, maxArity, modularityParameters, 100, 100);
}
public static int createExtraConstraints(FeatureModel fm, int numVars, int numConstraints, int maxArity, int [][] modularityParameters, int percVar3cnf, int percForm3cnf) {
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;
List<PropositionalFormula> createdFormulas = new LinkedList<PropositionalFormula>();
for( int i = levelsToConsider-1 ; i >= 0 ; i-- ) {
// at least two variables to consider level
if ( modularityParameters[i][1] >= 2 ) {
int numConstraintsLevel = (numConstraints * Math.round(modularityParameters[i][1]/100f))/numVars;
LevelConstraintGenerator constraintGenerator = new LevelConstraintGenerator(fm, modularityParameters[i][0], modularityParameters[i][1], numConstraintsLevel, maxArity, modularityParameters[i][2], modularityParameters[i][3]);
List<PropositionalFormula> levelFormulas = constraintGenerator.createExtraConstraints();
countLevelsWithContraints = (levelFormulas.size() > 0) ? countLevelsWithContraints + 1 : countLevelsWithContraints;
createdFormulas.addAll( levelFormulas );
}
}
// System.out.println("> Before: ------------------------------");
// for( PropositionalFormula formula : createdFormulas ) {
// System.out.println("> " + formula.getFormula());
// }
enrichFormulas(createdFormulas, percVar3cnf, percForm3cnf);
// System.out.println("> AFTER: ------------------------------");
// for( PropositionalFormula formula : createdFormulas ) {
// System.out.println("> " + formula.getFormula());
// }
for( PropositionalFormula formula : createdFormulas) {
fm.addConstraint(formula);
}
return countLevelsWithContraints;
}
public void saveNodes() {}
private static void enrichFormulas(List<PropositionalFormula> fmConstraints, int percVars, int percFormulas) {
// first, eliminate some formulas based on percFormulas
List<PropositionalFormula> pickedFormulas = new LinkedList<PropositionalFormula>();
pickedFormulas.addAll(fmConstraints);
Collections.shuffle(pickedFormulas);
int formulasToEliminate = (int)((1-(percFormulas/100.0))*pickedFormulas.size());
for( int i = 0 ; i < formulasToEliminate ; i++ ) {
pickedFormulas.remove(0);
}
// now, eliminate some variables based on percVars
List<BooleanVariable> pickedVariables = new LinkedList<BooleanVariable>();
for( PropositionalFormula constraint : fmConstraints ) {
for( BooleanVariable var : constraint.getVariables() ) {
if ( !pickedVariables.contains(var) ) {
pickedVariables.add(var);
}
}
}
Collections.shuffle(pickedVariables);
int variablesToEliminate = (int)((1-(percVars/100.0))*pickedVariables.size());
for( int i = 0 ; i < variablesToEliminate ; i++ ) {
pickedVariables.remove(0);
}
// then, add a new variable to each of the picked formulas
int varIndex = 0;
for( PropositionalFormula pickedFormula : pickedFormulas ) {
BooleanVariable pickedVariable = pickedVariables.get(varIndex);
int breakcounter = 0;
while( pickedFormula.getVariables().contains(pickedVariable) ) {
varIndex = (varIndex+1)%pickedVariables.size();
pickedVariable = pickedVariables.get(varIndex);
if ( ++breakcounter == pickedVariables.size() ) break;
}
if ( breakcounter < pickedVariables.size() ) {
addNewVariableToFormula(pickedFormula, pickedVariable);
}
varIndex = (varIndex+1)%pickedVariables.size();
}
}
private static void addNewVariableToFormula( PropositionalFormula pickedFormula, BooleanVariable pickedVariable) {
// System.out.println("> Before: " + pickedFormula.getFormula());
String literal = (new Random().nextBoolean()) ? "~" + pickedVariable.getID() : pickedVariable.getID();
pickedFormula.appendToFormula(" OR " + literal);
pickedFormula.appendToVariables(pickedVariable);
// System.out.println("---> After: " + pickedFormula.getFormula());
}
}
class LevelConstraintGenerator {
private FeatureModel fm;
private int level;
private int numVars;
private int numConstraints;
private int maxArity;
private int clustersPercentage;
private int clusterChildNodesPercentage;
public LevelConstraintGenerator(FeatureModel fm, int level, int numVars, int numConstraints, int maxArity, int clustersPercentage, int clusterChildNodesPercentage) {
this.fm = fm;
this.level = level;
this.numVars = numVars;
this.numConstraints = numConstraints;
this.maxArity = maxArity;
this.clustersPercentage = clustersPercentage;
this.clusterChildNodesPercentage = clusterChildNodesPercentage;
}
public int getNumberOfVariables() {
return numVars;
}
public List<PropositionalFormula> createExtraConstraints() {
List<PropositionalFormula> formulas = new ArrayList<PropositionalFormula>();
// select clusters to add constraints based on the clusters percentage specified
List<FeatureTreeNode> selectedClusters = selectClusters(fm.getNodesAtLevel(level), clustersPercentage);
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/2; //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, clusterChildNodesPercentage));
}
}
return formulas;
}
private List<PropositionalFormula> createClusterConstraints(FeatureTreeNode clusterNode, int numVarsCluster, int numConstraintsCluster, int clusterChildNodesPercentage) {
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));
}
// eliminate some child nodes according to cluster child nodes percentage
int nodesToEliminate = Math.min(nodes.size(), Math.round((1-(clusterChildNodesPercentage/100f))*nodes.size()));
for( int i = 0 ; i < nodesToEliminate ; i++ ) {
nodes.remove(Math.abs(new Random().nextInt())%nodes.size());
}
// 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+1) % 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();
if ( countNodes > 0 ) {
// generate constraints
// Collections.shuffle(pickedNodesForVariables);
List<ArrayList<List<FeatureTreeNode>>> groups = createClusterGroups(pickedNodesForVariables);
if ( groups.size() >= 1 ) {
int groupsVarIndexes[][] = new int[groups.size()][];
for( int g = 0 ; g < groups.size() ; g++ ) {
groupsVarIndexes[g] = new int[groups.get(g).size()];
for( int e = 0 ; e < groups.get(g).size() ; e++ ) {
groupsVarIndexes[g][e] = 0;
}
}
int nodeGroupIndex[] = new int[groups.size()];
java.util.Arrays.fill(nodeGroupIndex, 0);
int groupIndex = 0;
for( int i = 0 ; i < numConstraintsCluster ; i++ ) {
List<FeatureTreeNode> variables = new ArrayList<FeatureTreeNode>();
int arity = 2; // + Math.abs(new Random().nextInt())%(maxArity-1);
ArrayList<List<FeatureTreeNode>> pickedGroup = groups.get(groupIndex);
int groupSize = pickedGroup.size();
for( int j = 0 ; j < arity ; j++ ) {
List<FeatureTreeNode> variablesForNode = pickedGroup.get(nodeGroupIndex[groupIndex]);
variables.add(variablesForNode.get(groupsVarIndexes[groupIndex][nodeGroupIndex[groupIndex]]));
groupsVarIndexes[groupIndex][nodeGroupIndex[groupIndex]] = (++groupsVarIndexes[groupIndex][nodeGroupIndex[groupIndex]])%pickedGroup.get(nodeGroupIndex[groupIndex]).size();
if ( groupsVarIndexes[groupIndex][nodeGroupIndex[groupIndex]] == 0) {
Collections.shuffle(pickedGroup.get(nodeGroupIndex[groupIndex]));
}
nodeGroupIndex[groupIndex] = (nodeGroupIndex[groupIndex]+1)%groupSize;
// if ( Math.abs(new Random().nextInt())%3 == 0 ) {
// }
// else if ( (++nodeIndex) == countNodes ) {
// nodeIndex = 0;
// }
}
groupIndex = (groupIndex+1)%(groups.size());
String formulaStr = "";
for( Iterator<FeatureTreeNode> it = variables.iterator() ; it.hasNext() ; ) {
String ID = it.next().getID().trim();
if ( new Random().nextBoolean() == true ) {
ID = "~" + ID;
}
formulaStr += ID;
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;
}
private List<ArrayList<List<FeatureTreeNode>>> createClusterGroups( ArrayList<List<FeatureTreeNode>> pickedNodesForVariables) {
List<ArrayList<List<FeatureTreeNode>>> groups = new ArrayList<ArrayList<List<FeatureTreeNode>>>();
int countNodes = pickedNodesForVariables.size();
if ( countNodes >= 2 ) {
// draw groups
int numGroups = 1+(Math.abs(new Random().nextInt())%(countNodes/2));
for( int i = 0 ; i < numGroups ; i++ ) {
ArrayList<List<FeatureTreeNode>> group = new ArrayList<List<FeatureTreeNode>>();
groups.add(group);
}
// fill up groups
Collections.shuffle(pickedNodesForVariables);
int groupIndex = 0;
int smallerGroupSize = 2;
for( List<FeatureTreeNode> nodeGroup : pickedNodesForVariables ) {
groups.get(groupIndex).add(nodeGroup);
// groups new a minimum of 2 nodes
if ( groups.get(groupIndex).size() <= smallerGroupSize ) {
groupIndex = (groupIndex+1)%(groups.size());
}
// allow groups to have different sizes
else {
groupIndex = Math.abs(new Random().nextInt())%numGroups;
}
}
// DEBUG
// System.out.print("\ntotal groups: " + pickedNodesForVariables.size());
// for( int i = 0 ; i < numGroups ; i++ ) {
// System.out.print("\ngroup " + i + ": ");
// for( int j = 0 ; j < groups.get(i).size() ; j++ ) {
// System.out.print("X ,");
// }
// }
}
return groups;
}
// 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 clustersPercentage) {
// if ( level == 1) {
// System.out.println("");
// }
//
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-(clustersPercentage/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;
}
}