package edu.brown.designer.partitioners; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.SortedMap; import java.util.TreeMap; import java.util.TreeSet; import org.apache.commons.collections15.map.ListOrderedMap; import org.apache.commons.collections15.set.ListOrderedSet; import org.apache.log4j.Logger; import org.voltdb.catalog.CatalogType; import org.voltdb.catalog.Column; import org.voltdb.catalog.Database; import org.voltdb.catalog.ProcParameter; import org.voltdb.catalog.Procedure; import org.voltdb.catalog.Statement; import org.voltdb.catalog.Table; import org.voltdb.types.QueryType; import edu.brown.catalog.CatalogKey; import edu.brown.catalog.CatalogPair; import edu.brown.catalog.CatalogUtil; import edu.brown.catalog.DependencyUtil; import edu.brown.catalog.special.MultiColumn; import edu.brown.catalog.special.MultiProcParameter; import edu.brown.catalog.special.ReplicatedColumn; import edu.brown.designer.AccessGraph; import edu.brown.designer.DependencyGraph; import edu.brown.designer.DesignerEdge; import edu.brown.designer.DesignerHints; import edu.brown.designer.DesignerInfo; import edu.brown.designer.DesignerVertex; import edu.brown.graphs.IGraph; import edu.brown.graphs.VertexTreeWalker; import edu.brown.logging.LoggerUtil; import edu.brown.logging.LoggerUtil.LoggerBoolean; import edu.brown.mappings.ParameterMapping; import edu.brown.mappings.ParameterMappingsSet; import edu.brown.statistics.Histogram; import edu.brown.statistics.ObjectHistogram; import edu.brown.statistics.TableStatistics; import edu.brown.utils.CollectionUtil; import edu.brown.utils.MathUtil; import edu.brown.utils.PredicatePairs; public abstract class PartitionerUtil { private static final Logger LOG = Logger.getLogger(PartitionerUtil.class); private static final LoggerBoolean debug = new LoggerBoolean(); private static final LoggerBoolean trace = new LoggerBoolean(); static { LoggerUtil.attachObserver(LOG, debug, trace); } /** * @param <T> */ private static class CatalogWeightComparator<T extends CatalogType> implements Comparator<T> { private final Map<T, Double> weights; public CatalogWeightComparator(Map<T, Double> weights) { this.weights = weights; } public int compare(T t0, T t1) { Double w0 = this.weights.get(t0); assert (w0 != null) : "Missing weight for " + t0; Double w1 = this.weights.get(t1); assert (w1 != null) : "Missing weight for " + t1; if (w0.equals(w1)) return (t0.getName().compareTo(t1.getName())); return (w1.compareTo(w0)); }; } /** * Returns true if a procedure should be ignored in any calculations or * decision making * * @param hints * @param catalog_proc * @return */ public static boolean shouldIgnoreProcedure(final DesignerHints hints, final Procedure catalog_proc) { assert (catalog_proc != null); // Ignore criteria: // (1) The procedure is a sysproc // (2) The procedure doesn't have any input parameters (meaning it just // have to be randomly assigned) // (3) The procedure is set to be ignored in the given DesignerHints boolean ignore = PartitionerUtil.isPartitionable(catalog_proc) == false; if (hints != null && ignore == false) { ignore = hints.shouldIgnoreProcedure(catalog_proc); } return (ignore); } /** * Returns true if this Proce * * @param catalog_proc * @return */ public static boolean isPartitionable(Procedure catalog_proc) { assert (catalog_proc != null); return (!catalog_proc.getSystemproc() && catalog_proc.getParameters().size() > 0); } /** * Generate the ordered list of Procedures that we need to visit for * partitioning * * @param catalog_db * @param hints * @return * @throws Exception */ public LinkedList<String> generateProcedureOrder(final DesignerInfo info, final Database catalog_db, final DesignerHints hints) throws Exception { if (debug.val) LOG.debug("Generating Procedure visit order"); final Map<Procedure, Double> proc_weights = new HashMap<Procedure, Double>(); Histogram<String> hist = info.workload.getProcedureHistogram(); TreeSet<Procedure> proc_visit_order = new TreeSet<Procedure>(new PartitionerUtil.CatalogWeightComparator<Procedure>(proc_weights)); for (Procedure catalog_proc : catalog_db.getProcedures()) { if (catalog_proc.getSystemproc()) continue; String proc_key = CatalogKey.createKey(catalog_proc); Long weight = hist.get(proc_key); if (weight != null && weight.longValue() > 0) { proc_weights.put(catalog_proc, weight.doubleValue()); proc_visit_order.add(catalog_proc); } } // FOR // Convert to CatalogKeys LinkedList<String> ret = new LinkedList<String>(); for (Procedure catalog_proc : proc_visit_order) ret.add(CatalogKey.createKey(catalog_proc)); return (ret); } /** * @param catalog_db * @param hints * @return * @throws Exception */ public static ListOrderedSet<String> generateProcParameterOrder(final DesignerInfo info, final Database catalog_db, final Procedure catalog_proc, final DesignerHints hints) throws Exception { // HACK: Reload the correlations file so that we can get the proper // catalog objects ParameterMappingsSet mappings = info.getMappings(); assert (mappings != null); // ParameterCorrelations correlations = new ParameterCorrelations(); // assert(info.getCorrelationsFile() != null) : // "The correlations file path was not set"; // correlations.load(info.getCorrelationsFile(), catalog_db); // For each procedure, we need to generate a list of potential // partitioning parameters String proc_key = CatalogKey.createKey(catalog_proc); assert (proc_key != null); // For each ProcParameter, get a list of the correlations that can be // mapped to the partitioning columns // of tables. We will generate a total weight for each ProcParameter final Map<ProcParameter, List<Double>> param_correlations = new HashMap<ProcParameter, List<Double>>(); // Get the list of tables accessed by this procedure for (Table catalog_tbl : CatalogUtil.getReferencedTables(catalog_proc)) { if (catalog_tbl.getIsreplicated()) continue; Column catalog_col = catalog_tbl.getPartitioncolumn(); for (ProcParameter catalog_proc_param : catalog_proc.getParameters()) { // Skip if this is an array if (hints.enable_array_procparameter_candidates == false && catalog_proc_param.getIsarray()) continue; if (!param_correlations.containsKey(catalog_proc_param)) { param_correlations.put(catalog_proc_param, new ArrayList<Double>()); } // Special Case: MultiProcParameter if (catalog_proc_param instanceof MultiProcParameter) { if (hints.enable_multi_partitioning) { MultiProcParameter mpp = (MultiProcParameter) catalog_proc_param; for (ProcParameter inner : mpp) { // Divide the values by the number of attributes in // mpp so that we take the average Collection<ParameterMapping> pms = mappings.get(inner, catalog_col); if (pms != null) { for (ParameterMapping c : pms) { param_correlations.get(catalog_proc_param).add(c.getCoefficient() / (double) mpp.size()); } // FOR (ParameterMapping) } } // FOR } } else { Collection<ParameterMapping> pms = mappings.get(catalog_proc_param, catalog_col); if (pms != null) { for (ParameterMapping c : pms) { param_correlations.get(catalog_proc_param).add(c.getCoefficient()); } // FOR (Correlation) } } } // FOR (ProcParameter) } // FOR (Table) // The weights for each ProcParameter will be the geometric mean of the // correlation coefficients Map<ProcParameter, Double> param_weights = new HashMap<ProcParameter, Double>(); for (Entry<ProcParameter, List<Double>> e : param_correlations.entrySet()) { List<Double> weights_list = e.getValue(); if (!weights_list.isEmpty()) { double weights[] = new double[weights_list.size()]; for (int i = 0; i < weights.length; i++) weights[i] = weights_list.get(i); double mean = MathUtil.geometricMean(weights, MathUtil.GEOMETRIC_MEAN_ZERO); param_weights.put(e.getKey(), mean); } } // FOR // Convert to CatalogKeys ListOrderedSet<String> ret = new ListOrderedSet<String>(); // If there were no matches, then we'll just include all of the // attributes if (param_weights.isEmpty()) { if (debug.val) LOG.warn("No parameter correlations found for " + catalog_proc.getName() + ". Returning all candidates!"); for (ProcParameter catalog_proc_param : catalog_proc.getParameters()) { if (hints.enable_multi_partitioning || !(catalog_proc_param instanceof MultiProcParameter)) { ret.add(CatalogKey.createKey(catalog_proc_param)); } } // FOR } else { List<ProcParameter> param_visit_order = new ArrayList<ProcParameter>(param_weights.keySet()); Collections.sort(param_visit_order, new PartitionerUtil.CatalogWeightComparator<ProcParameter>(param_weights)); for (ProcParameter catalog_proc_param : param_visit_order) ret.add(CatalogKey.createKey(catalog_proc_param)); } // FOR return (ret); } /** * Generate an ordered list of the tables that define how we should traverse * the search tree * * @param agraph * @param hints * @return * @throws Exception */ public static List<Table> generateTableOrder(final DesignerInfo info, final AccessGraph agraph, final DesignerHints hints) throws Exception { final List<Table> table_visit_order = new ArrayList<Table>(); // Put small read-only tables at the top of the list so that we can try // everything with replicating them first if (hints.force_replication_size_limit != null) { final Map<Table, Double> replication_weights = new HashMap<Table, Double>(); final TreeSet<Table> temp_list = new TreeSet<Table>(new PartitionerUtil.CatalogWeightComparator<Table>(replication_weights)); for (Table catalog_tbl : info.catalogContext.database.getTables()) { TableStatistics ts = info.stats.getTableStatistics(catalog_tbl); assert (ts != null); double size_ratio = ts.tuple_size_total / (double) hints.max_memory_per_partition; if (ts.readonly && size_ratio <= hints.force_replication_size_limit) { if (debug.val) LOG.debug(CatalogUtil.getDisplayName(catalog_tbl) + " is read-only and only " + String.format("%.02f", (size_ratio * 100)) + "% of total memory. Forcing replication..."); replication_weights.put(catalog_tbl, size_ratio); temp_list.add(catalog_tbl); } } // FOR for (Table catalog_tbl : temp_list) { table_visit_order.add(catalog_tbl); } Collections.reverse(table_visit_order); if (debug.val) LOG.debug("Forced Replication: " + table_visit_order); } for (DesignerVertex root : PartitionerUtil.createCandidateRoots(info, hints, agraph)) { if (debug.val) LOG.debug("Examining edges for candidate root '" + root.getCatalogItem().getName() + "'"); // From each candidate root, traverse the graph in breadth first // order based on // the edge weights in the AccessGraph new VertexTreeWalker<DesignerVertex, DesignerEdge>(info.dgraph, VertexTreeWalker.TraverseOrder.BREADTH) { @Override protected void populate_children(VertexTreeWalker.Children<DesignerVertex> children, DesignerVertex element) { // For the current element, look at all of its children and // count up the total // weight of all the edges to each child final Map<Table, Double> vertex_weights = new HashMap<Table, Double>(); DependencyGraph dgraph = (DependencyGraph) this.getGraph(); if (agraph.containsVertex(element)) { for (DesignerVertex child : dgraph.getSuccessors(element)) { Table child_tbl = child.getCatalogItem(); DesignerVertex child_vertex = this.getGraph().getVertex(child_tbl); if (agraph.containsVertex(child_vertex)) { for (DesignerEdge edge : agraph.findEdgeSet(element, child_vertex)) { Double orig_weight = vertex_weights.get(child_tbl); if (orig_weight == null) orig_weight = 0.0d; vertex_weights.put(child_tbl, orig_weight + edge.getTotalWeight()); } // FOR } } // FOR } // Now sort the children them by weights and throw them at // the walker List<Table> sorted = new ArrayList<Table>(vertex_weights.keySet()); Collections.sort(sorted, new PartitionerUtil.CatalogWeightComparator<Table>(vertex_weights)); for (Table child_tbl : sorted) { children.addAfter(this.getGraph().getVertex(child_tbl)); } // FOR if (debug.val) { LOG.debug(element); LOG.debug(" sorted=" + sorted); LOG.debug(" weights=" + vertex_weights); LOG.debug(" children=" + children); } }; @Override protected void callback(DesignerVertex element) { Table catalog_tbl = element.getCatalogItem(); if (!table_visit_order.contains(catalog_tbl)) { table_visit_order.add(catalog_tbl); } } }.traverse(root); } // FOR // Remove ignored tables. if (hints.ignore_tables.isEmpty() == false) { Set<Table> toRemove = new HashSet<Table>(); for (Table tbl : table_visit_order) { if (hints.ignore_tables.contains(tbl.getName())) { toRemove.add(tbl); } } // FOR table_visit_order.removeAll(toRemove); } // Add in any missing tables to the end of the list // This can occur if there are tables that do not appear in the // AccessGraph for whatever reason // Note that we have to traverse the graph so that we don't try to plan // a parent before a child // for (DesignerVertex root : info.dgraph.getRoots()) { // if (trace.val) // LOG.trace("Creating table visit order starting from root " + root); // // new VertexTreeWalker<DesignerVertex, DesignerEdge>(info.dgraph, // VertexTreeWalker.TraverseOrder.BREADTH) { // protected void callback(DesignerVertex element) { // Table catalog_tbl = element.getCatalogItem(); // assert(catalog_tbl != null); // String table_key = CatalogKey.createKey(catalog_tbl); // if (!table_visit_order.contains(table_key)) { // if (debug.val) LOG.warn("Added " + catalog_tbl + // " because it does not appear in the AccessGraph"); // table_visit_order.add(table_key); // } // }; // }.traverse(root); // if (table_visit_order.size() == info.catalog_db.getTables().size()) // break; // } // FOR return (table_visit_order); } /** * For a given table, generate the search order of its columns * * @param agraph * @param catalog_tbl * @param hints * @return * @throws Exception */ public static List<Column> generateColumnOrder(final DesignerInfo info, final AccessGraph agraph, final Table catalog_tbl, final DesignerHints hints) throws Exception { return (PartitionerUtil.generateColumnOrder(info, agraph, catalog_tbl, hints, false, false)); } /** * @param info * @param agraph * @param catalog_tbl * @param hints * @param no_replication * @param force_replication_last * @return * @throws Exception */ public static List<Column> generateColumnOrder(final DesignerInfo info, final AccessGraph agraph, final Table catalog_tbl, final DesignerHints hints, boolean no_replication, boolean force_replication_last) throws Exception { assert (agraph != null); final List<Column> ret = new ArrayList<Column>(); final String table_key = CatalogKey.createKey(catalog_tbl); // Force columns in hints Collection<Column> force_columns = hints.getForcedTablePartitionCandidates(catalog_tbl); if (!force_columns.isEmpty()) { if (debug.val) LOG.debug("Force " + catalog_tbl + " candidates: " + force_columns); for (Column catalog_col : force_columns) { ret.add(catalog_col); } return (ret); } // Get a list of this table's attributes that were used in the workload. // Luckily, the AccessGraph // already has that for us. We'll sort them by their weights so that we // traverse the likely // best candidate first, thereby pruning other branches more quickly final Map<Column, Double> column_weights = new HashMap<Column, Double>(); // SortedMap<Double, Collection<Column>> weighted_columnsets = new // TreeMap<Double, Collection<Column>>(Collections.reverseOrder()); DesignerVertex vertex = agraph.getVertex(catalog_tbl); assert (vertex != null) : "No vertex exists in AccesGraph for " + catalog_tbl; if (debug.val) LOG.debug("Retreiving edges for " + vertex + " from AccessGraph"); Collection<DesignerEdge> edges = agraph.getIncidentEdges(vertex); if (edges == null) { if (debug.val) LOG.warn("No edges were found for " + vertex + " in AccessGraph"); } else { for (DesignerEdge edge : agraph.getIncidentEdges(vertex)) { PredicatePairs orig_cset = (PredicatePairs) edge.getAttribute(AccessGraph.EdgeAttributes.COLUMNSET.name()); assert (orig_cset != null); // Skip any ColumnSets that were used only for INSERTs PredicatePairs cset = new PredicatePairs(); for (CatalogPair entry : orig_cset) { if (!(entry.containsQueryType(QueryType.INSERT) && entry.getQueryTypeCount() == 1)) { cset.add(entry); } } // FOR double edge_weight = edge.getTotalWeight(); for (Column catalog_col : cset.findAllForParent(Column.class, catalog_tbl)) { Double column_weight = column_weights.get(catalog_col); if (column_weight == null) column_weight = 0.0d; column_weights.put(catalog_col, column_weight + edge_weight); } // FOR } // FOR } // Increase the weight of the columns based on the number foreign key // descendants they have DependencyUtil dependencies = DependencyUtil.singleton(CatalogUtil.getDatabase(catalog_tbl)); if (debug.val) LOG.debug("Calculating descendants for columns"); for (Entry<Column, Double> entry : column_weights.entrySet()) { Column catalog_col = entry.getKey(); Double weight = entry.getValue(); int descendants = dependencies.getDescendants(catalog_col).size(); column_weights.put(catalog_col, weight * (descendants + 1)); if (descendants > 0) LOG.debug(" " + catalog_col + ": " + descendants); } // FOR // Now sort them by the weights // TODO: Do we want to consider ancestory paths? Like what if there is a // foreign key that is // used down in a bunch of children tables? Well, then if they are // really important relationships, // they will show up with greater weights in the List<Column> sorted = new ArrayList<Column>(column_weights.keySet()); Collections.sort(sorted, new PartitionerUtil.CatalogWeightComparator<Column>(column_weights)); if (debug.val) { LOG.debug(catalog_tbl); LOG.debug(" sorted=" + sorted); LOG.debug(" weights=" + column_weights); LOG.debug(" children=" + agraph.getIncidentEdges(vertex)); } // Always add replicated column placeholder // Simple Optimization: Put the replication placeholder as the last // attribute in the // list if the table is not read-only if (!no_replication) { ReplicatedColumn replicated_col = ReplicatedColumn.get(catalog_tbl); if (info.stats.getTableStatistics(table_key).readonly && !force_replication_last) { sorted.add(0, replicated_col); } else { sorted.add(replicated_col); } } return (sorted); } /** * @param info * @param hints * @param catalog_proc * @return * @throws Exception */ public static ObjectHistogram<Column> generateProcedureColumnAccessHistogram(final DesignerInfo info, final DesignerHints hints, final AccessGraph agraph, final Procedure catalog_proc) throws Exception { if (debug.val) LOG.debug("Constructing column access histogram for " + catalog_proc.getName()); ObjectHistogram<Column> column_histogram = new ObjectHistogram<Column>(); for (Table catalog_tbl : CatalogUtil.getReferencedTables(catalog_proc)) { DesignerVertex v = agraph.getVertex(catalog_tbl); for (DesignerEdge e : agraph.getIncidentEdges(v)) { Collection<DesignerVertex> vertices = agraph.getVertices(); if (vertices.size() != 1) { DesignerVertex v0 = CollectionUtil.get(vertices, 0); DesignerVertex v1 = CollectionUtil.get(vertices, 1); if (v0.equals(v) && v1.equals(v)) continue; } double edge_weight = e.getTotalWeight(); PredicatePairs predicates = e.getAttribute(AccessGraph.EdgeAttributes.COLUMNSET.name()); Histogram<Column> cset_histogram = predicates.buildHistogramForType(Column.class); for (Column catalog_col : cset_histogram.values()) { if (!catalog_col.getParent().equals(catalog_tbl)) continue; long cnt = cset_histogram.get(catalog_col); column_histogram.put(catalog_col, Math.round(cnt * edge_weight)); } // FOR } // FOR (EDGE) } // FOR (TABLE) return (column_histogram); } /** * Generate a cross-product of * * @param info * @param hints * @param catalog_proc */ public static Map<ProcParameter, Set<MultiProcParameter>> generateMultiProcParameters(final DesignerInfo info, final DesignerHints hints, final Procedure catalog_proc) { List<ProcParameter> params = new ArrayList<ProcParameter>(); Map<ProcParameter, Set<MultiProcParameter>> param_map = new ListOrderedMap<ProcParameter, Set<MultiProcParameter>>(); CollectionUtil.addAll(params, catalog_proc.getParameters()); // Why do I need to make a map like this? for (ProcParameter catalog_param : params) { param_map.put(catalog_param, new HashSet<MultiProcParameter>()); } // FOR for (int i = 0, cnt = params.size(); i < cnt; i++) { ProcParameter param0 = params.get(i); assert (param0 != null); if (param0 instanceof MultiProcParameter || param0.getIsarray()) continue; for (int ii = i + 1; ii < cnt; ii++) { ProcParameter param1 = params.get(ii); assert (param1 != null); if (param1 instanceof MultiProcParameter || param1.getIsarray()) continue; // This will automatically update the Procedure, so there isn't // anything more // we need to do here... MultiProcParameter mpp = MultiProcParameter.get(param0, param1); assert (mpp != null); param_map.get(param0).add(mpp); param_map.get(param1).add(mpp); } // FOR } // FOR return (param_map); } /** * @param info * @param hints * @param catalog_proc * @param agraph * @return */ protected static Map<Table, Collection<MultiColumn>> generateMultiColumns(final DesignerInfo info, final DesignerHints hints, final Procedure catalog_proc) { Map<Table, Collection<MultiColumn>> multicolumns = new HashMap<Table, Collection<MultiColumn>>(); // For each Statement, find the columns that are accessed together for (Statement catalog_stmt : catalog_proc.getStatements()) { // Skip inserts for now... if (catalog_stmt.getQuerytype() == QueryType.INSERT.getValue()) continue; // Collection<Column> columns = // CatalogUtil.getReferencedColumns(catalog_stmt); // assert(columns != null) : catalog_stmt.fullName(); // Collection<Column> modified = // CatalogUtil.getModifiedColumns(catalog_stmt); // assert(modified != null) : catalog_stmt.fullName(); // We only want to consider those columns that read-only Collection<Column> columns = CatalogUtil.getReadOnlyColumns(catalog_stmt); assert (columns != null) : catalog_stmt.fullName(); // For now we only bother with two-column pairs for (Column catalog_col0 : columns) { Table catalog_tbl = catalog_col0.getParent(); if (!multicolumns.containsKey(catalog_tbl)) { multicolumns.put(catalog_tbl, new ArrayList<MultiColumn>()); } if (trace.val) LOG.trace(String.format("%s - MultiColumn Candidate: %s", catalog_stmt.fullName(), catalog_col0.fullName())); for (Column catalog_col1 : columns) { if (catalog_col0.equals(catalog_col1) || !catalog_tbl.equals(catalog_col1.getParent())) continue; MultiColumn mc = MultiColumn.get(catalog_col0, catalog_col1); assert (mc != null); multicolumns.get(catalog_tbl).add(mc); } // FOR } // FOR } // FOR return (multicolumns); } /** * @param graph * @param agraph * @return * @throws Exception */ public static List<DesignerVertex> createCandidateRoots(final DesignerInfo info, final DesignerHints hints, final IGraph<DesignerVertex, DesignerEdge> agraph) throws Exception { LOG.debug("Searching for candidate roots..."); if (agraph == null) throw new NullPointerException("AccessGraph is Null"); // // For each vertex, count the number of edges that point to it // List<DesignerVertex> roots = new ArrayList<DesignerVertex>(); SortedMap<Double, Set<DesignerVertex>> candidates = new TreeMap<Double, Set<DesignerVertex>>(Collections.reverseOrder()); for (DesignerVertex vertex : info.dgraph.getVertices()) { if (!agraph.containsVertex(vertex)) continue; if (hints.force_replication.contains(vertex.getCatalogItem().getName())) continue; // // We only can only use this vertex as a candidate root if // none of its parents (if it even has any) are used in the // AccessGraph or are // not marked for replication // boolean valid = true; Collection<DesignerVertex> parents = info.dgraph.getPredecessors(vertex); for (DesignerVertex other : agraph.getNeighbors(vertex)) { if (parents.contains(other) && !hints.force_replication.contains(other.getCatalogItem().getName())) { LOG.debug("SKIP " + vertex + " [" + other + "]"); valid = false; break; } } // FOR if (!valid) continue; // System.out.println("CANDIDATE: " + vertex); // // We now need to set the weight of the candidate. // The first way I did this was to count the number of outgoing // edges from the candidate // Now I'm going to use the weights of the outgoing edges in the // AccessGraph. // final List<Double> weights = new ArrayList<Double>(); new VertexTreeWalker<DesignerVertex, DesignerEdge>(info.dgraph) { @Override protected void callback(DesignerVertex element) { // Get the total weights from this vertex to all of its // descendants if (agraph.containsVertex(element)) { double total_weight = 0d; Collection<DesignerVertex> descedents = info.dgraph.getDescendants(element); // System.out.println(element + ": " + descedents); for (DesignerVertex descendent : descedents) { if (descendent != element && agraph.containsVertex(descendent)) { for (DesignerEdge edge : agraph.findEdgeSet(element, descendent)) { Double weight = edge.getTotalWeight(); if (weight != null) total_weight += weight; // System.out.println(element + "->" + // descendent); } } } // FOR weights.add(total_weight); } // Vertex parent = this.getPrevious(); // if (agraph.containsVertex(element) && parent != null) { // for (Edge edge : agraph.findEdgeSet(parent, element)) { // Double weight = // (Double)edge.getAttribute(AccessGraph.EdgeAttributes.WEIGHT.name()); // weights.add(weight); // System.out.println(parent + "->" + element); // } // } } }.traverse(vertex); double weight = 0d; for (Double _weight : weights) weight += _weight; if (!candidates.containsKey(weight)) candidates.put(weight, new HashSet<DesignerVertex>()); candidates.get(weight).add(vertex); /* * int count = info.dgraph.getOutEdges(vertex).size(); if (count > 0 * || agraph.getVertexCount() == 1) { if * (!candidates.containsKey(count)) candidates.put(count, new * HashSet<Vertex>()); candidates.get(count).add(vertex); * LOG.debug("Found candidate root '" + vertex + "' [" + count + * "]"); } */ } // FOR StringBuilder buffer = new StringBuilder(); buffer.append("Found ").append(candidates.size()).append(" candidate roots and ranked them as follows:\n"); int ctr = 0; for (Double weight : candidates.keySet()) { for (DesignerVertex vertex : candidates.get(weight)) { buffer.append("\t[").append(ctr++).append("] ").append(vertex).append(" Weight=").append(weight).append("\n"); roots.add(vertex); } // FOR } // FOR LOG.debug(buffer.toString()); // LOG.info(buffer.toString()); return (roots); } }