package splar.core.fm.clustering; import java.util.ArrayList; import java.util.Collection; import java.util.Comparator; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.Vector; import splar.core.constraints.BooleanVariable; import splar.core.constraints.BooleanVariableInterface; import splar.core.constraints.CNFClause; import splar.core.constraints.CNFFormula; import splar.core.constraints.CNFLiteral; import splar.core.constraints.PropositionalFormula; import splar.core.fm.FeatureGroup; import splar.core.fm.FeatureModel; import splar.core.fm.FeatureTreeNode; import splar.core.heuristics.FORCEVariableOrderingHeuristic; import splar.core.heuristics.FTPreOrderTraversalHeuristic; import splar.core.util.hypergraphs.Hyperedge; import splar.core.util.hypergraphs.Hypergraph; import splar.core.util.hypergraphs.Vertex; /* * For each node N and its child nodes A, B, C, D, E, F, G, and clauses (A,D), (A,F), (B,G) * * >> Creates clusters for N based on the their dependencies as in the extra constraints * 1. (A,D,F) * 2. (B,G) * 3. (C) * 4. (E) * (*) Note that variables not in the clauses form a cluster each * */ public class FeatureModelClustersManager { private FeatureModel featureModel; private Map<FeatureTreeNode,List<NodeCluster>> clusters; public FeatureModelClustersManager( FeatureModel featureModel ) { this.featureModel = featureModel; clusters = new LinkedHashMap<FeatureTreeNode,List<NodeCluster>>(); } public FeatureModel getFeatureModel() { return featureModel; } public List<NodeCluster> getNodeClusters(FeatureTreeNode node) { return clusters.get(node); } public boolean clustersReady() { return clusters.size() > 0; } public void createClusters() { Map<FeatureTreeNode,Hypergraph> clustersHypergraphs = createHypergraphsForNodesInEC(); createClustersForAllNodes(clustersHypergraphs); } private Map<FeatureTreeNode,Hypergraph> createHypergraphsForNodesInEC() { Map<FeatureTreeNode,Hypergraph> nodesClusters = new LinkedHashMap<FeatureTreeNode,Hypergraph>(); // Traverse EC CNF clauses and create clusters (hypergraphs) for related nodes (lowest common anscestors) for( PropositionalFormula pf : featureModel.getConstraints() ) { for( CNFClause cnfClause : pf.toCNFClauses() ) { CNFClause clusterClause = new CNFClause(); FeatureTreeNode node = createClusterClause(cnfClause, clusterClause); // create a Hypergraph for each node // - the hyperedges represent the clusters // - hyperedges also store the original CNF clause from EC // - and the clause connecting the 'node's child nodes // - feature groups have a single cluster including all its grouped nodes Hypergraph hyperGraph = nodesClusters.get(node); if ( hyperGraph == null ) { hyperGraph = new ClusterHypergraph(); Hyperedge hyperedge = null; nodesClusters.put(node, hyperGraph); for( int i = 0 ; i < node.getChildCount() ; i++ ) { FeatureTreeNode childNode = (FeatureTreeNode)node.getChildAt(i); Vertex vertex = new Vertex(childNode.getID()); hyperGraph.addVertex(vertex); // feature groups have a single cluster with all grouped nodes if ( node instanceof FeatureGroup ) { if ( hyperedge == null ) { hyperedge = new Hyperedge(); hyperGraph.addHyperEdge(hyperedge); } } else { hyperedge = new Hyperedge(); hyperGraph.addHyperEdge(hyperedge); } hyperedge.addVertex(vertex); } } // merge hypergraph edges based on 'clusterClause' if ( clusterClause.countVariables() > 1 ) { Hypergraph clauseHyperGraph = new ClusterHypergraph(); Hyperedge clauseHyperEdge = new Hyperedge(); for( CNFLiteral literal : clusterClause.getLiterals() ) { Vertex vertex= hyperGraph.getVertexByName(literal.getVariable().getID()); clauseHyperGraph.addVertex(vertex); clauseHyperEdge.addVertex(vertex); } List<CNFClause> ECClauses = (List<CNFClause>)clauseHyperEdge.getProperty("EC_clauses"); List<CNFClause> clusterClauses = (List<CNFClause>)clauseHyperEdge.getProperty("cluster_clauses"); if ( ECClauses == null ) { ECClauses = new ArrayList<CNFClause>(); clauseHyperEdge.setProperty("EC_clauses", ECClauses); } if ( clusterClauses == null ) { clusterClauses = new ArrayList<CNFClause>(); clauseHyperEdge.setProperty("cluster_clauses", clusterClauses); } ECClauses.add( cnfClause ); clusterClauses.add( clusterClause ); clauseHyperGraph.addHyperEdge(clauseHyperEdge); hyperGraph.merge(clauseHyperGraph); } } } return nodesClusters; } public NodeCluster[] sortClusterAscendingOrder(FeatureTreeNode clusterNodeRoot) { List<NodeCluster> nodeClusters = clusters.get(clusterNodeRoot); NodeCluster sortedClusters[] = null; if ( nodeClusters != null ) { sortedClusters = nodeClusters.toArray(new NodeCluster[0]); Comparator<NodeCluster> c = new Comparator<NodeCluster>() { public int compare(NodeCluster cluster1, NodeCluster cluster2) { int size1 = cluster1.size(); int size2 = cluster2.size(); return ( size1 > size2 ? 1 : ((size1 < size2) ? -1 : 0) ); } }; java.util.Arrays.sort(sortedClusters, c); } return sortedClusters; } public NodeCluster[] sortClusterDescendingOrder(FeatureTreeNode clusterNodeRoot) { List<NodeCluster> nodeClusters = clusters.get(clusterNodeRoot); NodeCluster sortedClusters[] = null; if ( nodeClusters != null ) { sortedClusters = nodeClusters.toArray(new NodeCluster[0]); Comparator<NodeCluster> c = new Comparator<NodeCluster>() { public int compare(NodeCluster cluster1, NodeCluster cluster2) { int size1 = cluster1.size(); int size2 = cluster2.size(); return ( size1 > size2 ? -1 : ((size1 < size2) ? 1 : 0) ); } }; java.util.Arrays.sort(sortedClusters, c); } return sortedClusters; } protected FeatureTreeNode createClusterClause(CNFClause clause, CNFClause returnClause) { // for each variable in "clause" creates a list with its ancestors List<List<FeatureTreeNode>> ancestorsList = new Vector<List<FeatureTreeNode>>(); for( BooleanVariableInterface var : clause.getVariables() ) { List<FeatureTreeNode> ancestors = new ArrayList<FeatureTreeNode>(); FeatureTreeNode node = featureModel.getNodeByID(var.getID()); if ( node != null ) { ancestors.add(0,node); FeatureTreeNode ancestorNode = (FeatureTreeNode)node.getParent(); // ancestorNode = ( ancestorNode instanceof FeatureGroup )? (FeatureTreeNode)ancestorNode.getParent() : ancestorNode; while( ancestorNode != null ) { ancestors.add(0,ancestorNode); ancestorNode = (FeatureTreeNode)ancestorNode.getParent(); // ancestorNode = ( ancestorNode instanceof FeatureGroup )? (FeatureTreeNode)ancestorNode.getParent() : ancestorNode; } ancestorsList.add(ancestors); } } // find the list with the lowest number of variables int minSize = Integer.MAX_VALUE; for( List<FeatureTreeNode> list : ancestorsList) { int listSize = list.size(); if ( listSize < minSize ) { minSize = listSize; } } // find the lowest-level common ancestor and its corresponding children that are // roots on the subtrees containing the variables in 'clause' FeatureTreeNode commonAncestor = null; boolean finalCommonAncestorFound = false; int index = 0; while( index < minSize && !finalCommonAncestorFound ) { FeatureTreeNode ancestorNode = null; for( List<FeatureTreeNode> ancestors : ancestorsList ) { FeatureTreeNode tempNode = ancestors.get(index); if ( ancestorNode == null ) { ancestorNode = tempNode; } else if ( tempNode != ancestorNode ) { finalCommonAncestorFound = true; } } if ( !finalCommonAncestorFound ) { commonAncestor = ancestorNode; } index++; } Set<BooleanVariableInterface> variables = new LinkedHashSet<BooleanVariableInterface>(); for( List<FeatureTreeNode> ancestors : ancestorsList ) { if ( ancestors.size() >= index ) { variables.add(new BooleanVariable(ancestors.get(index-1).getID())); } } if ( variables.size() > 1 ) { for( BooleanVariableInterface var : variables ) { returnClause.addLiteral(new CNFLiteral(var,true)); } } return commonAncestor; } public void createClustersForAllNodes(Map<FeatureTreeNode,Hypergraph> clustersHypergraphs) { clusters.clear(); // first, all nodes in the EC Set<FeatureTreeNode> nodesWithClusters = clustersHypergraphs.keySet(); for( FeatureTreeNode node : nodesWithClusters ) { Hypergraph h = clustersHypergraphs.get(node); List<NodeCluster> nodeClusters = new ArrayList<NodeCluster>(); // create clusters, one for each hyperedge for( Hyperedge hyperedge : h.getHyperedges() ) { NodeCluster cluster = new NodeCluster(featureModel, node); for( Vertex vertex : hyperedge.getVertices() ) { cluster.addNode(featureModel.getNodeByID(vertex.getName())); } cluster.addECClauses((List<CNFClause>)hyperedge.getProperty("EC_clauses")); cluster.addClusterClauses((List<CNFClause>)hyperedge.getProperty("cluster_clauses")); nodeClusters.add(cluster); } clusters.put(node,nodeClusters); } // second, all remaining nodes (not in EC) for( FeatureTreeNode node : featureModel.getNodes() ) { if ( !nodesWithClusters.contains(node) ) { List<NodeCluster> nodeClusters = new ArrayList<NodeCluster>(); if ( node instanceof FeatureGroup ) { NodeCluster cluster = new NodeCluster(featureModel, node); for( int i = 0 ; i < node.getChildCount() ; i++ ) { FeatureTreeNode groupedNode = (FeatureTreeNode)node.getChildAt(i); cluster.addNode(groupedNode); } nodeClusters.add(cluster); } else { for( int i = 0 ; i < node.getChildCount() ; i++ ) { FeatureTreeNode childNode = (FeatureTreeNode)node.getChildAt(i); NodeCluster cluster = new NodeCluster(featureModel, node); cluster.addNode(childNode); nodeClusters.add(cluster); } } clusters.put(node,nodeClusters); } } } public void dump() { for( FeatureTreeNode node : clusters.keySet() ) { List<NodeCluster> nodeClusters = clusters.get(node); for( NodeCluster cluster : nodeClusters ) { cluster.dump(); } } } } class ClusterHypergraph extends Hypergraph { public Hyperedge mergeHyperedges(Collection<Hyperedge> hyperEdgesSet ) { Collection<Hyperedge> oldHyperEdges = hyperEdgesSet; Hyperedge mergedHyperedge = super.mergeHyperedges(hyperEdgesSet); List<CNFClause> mergedECClauses = null; List<CNFClause> mergedClusterClauses = null; for( Hyperedge hyperedge : hyperEdgesSet ) { List<CNFClause> ECClauses = (List<CNFClause>)hyperedge.getProperty("EC_clauses"); if ( ECClauses != null ) { if ( mergedECClauses == null ) { mergedECClauses = ECClauses; } else { mergedECClauses.addAll(ECClauses); } } List<CNFClause> clusterClauses = (List<CNFClause>)hyperedge.getProperty("cluster_clauses"); if ( clusterClauses != null ) { if ( mergedClusterClauses == null ) { mergedClusterClauses = clusterClauses; } else { mergedClusterClauses.addAll(clusterClauses); } } } mergedHyperedge.setProperty("EC_clauses", mergedECClauses); mergedHyperedge.setProperty("cluster_clauses", mergedClusterClauses); return mergedHyperedge; } }