All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.javascript.jscomp.graph.GraphColoring Maven / Gradle / Ivy

/*
 * Copyright 2008 The Closure Compiler Authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.javascript.jscomp.graph;

import static com.google.common.base.Preconditions.checkNotNull;
import static java.util.Comparator.comparing;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;

/**
 * Annotates the graph with a color in a way that no connected node will have
 * the same color. Nodes of the same color can then be partitioned together and
 * be represented by a super node. This class will merely annotate the nodes
 * with a color using {@link GraphNode#setAnnotation(Annotation)} and provide
 * a node to super node mapping with {@link #getPartitionSuperNode(Object)}. The
 * given graph itself will not be modified.
 *
 * 

This algorithm is NOT deterministic by default. Passes that * requires deterministic output should provide a {@code Comparator} in the * constructor as a tie-breaker. This tie-break will be used when deciding * which node should be colored first when multiple nodes have the same degree. * * @param Value type that the graph node stores. * @param Value type that the graph edge stores. */ public abstract class GraphColoring { // Maps a color (represented by an integer) to a variable. If, for example, // the color 5 is mapped to "foo". Then any other variables colored with the // color 5 will now use the name "foo". protected N[] colorToNodeMap; protected final AdjacencyGraph graph; public GraphColoring(AdjacencyGraph graph) { this.graph = graph; } /** * Annotates the graph with {@link Color} objects using * {@link GraphNode#setAnnotation(Annotation)}. * * @return The number of unique colors need. */ public abstract int color(); /** * Using the coloring as partitions, finds the node that represents that * partition as the super node. The first to retrieve its partition will * become the super node. */ public N getPartitionSuperNode(N node) { checkNotNull(colorToNodeMap, "No coloring founded. color() should be called first."); Color color = graph.getNode(node).getAnnotation(); N headNode = colorToNodeMap[color.value]; if (headNode == null) { colorToNodeMap[color.value] = node; return node; } else { return headNode; } } public AdjacencyGraph getGraph() { return graph; } /** The color of a node */ public static class Color implements Annotation { int value = 0; Color(int value) { this.value = value; } @Override public boolean equals(Object other) { if (!(other instanceof Color)) { return false; } else { return value == ((Color) other).value; } } @Override public int hashCode() { return value; } } /** * Greedily assign nodes with high degree unique colors. */ public static class GreedyGraphColoring extends GraphColoring { private final Comparator tieBreaker; public GreedyGraphColoring(AdjacencyGraph graph) { this(graph, null); } /** * @param tieBreaker In case of a tie between two nodes of the same degree, * this comparator will determine which node should be colored first. */ public GreedyGraphColoring( AdjacencyGraph graph, Comparator tieBreaker) { super(graph); this.tieBreaker = tieBreaker; } @Override public int color() { List> worklist = new ArrayList<>(graph.getNodes()); // Sort nodes by degree. Collections.sort( worklist, comparing( GraphNode::getValue, // TODO(b/28382956): Take better advantage of Java8 comparing() to simplify this (leftProperty, rightProperty) -> { int result = graph.getWeight(rightProperty) - graph.getWeight(leftProperty); return result == 0 && tieBreaker != null ? tieBreaker.compare(leftProperty, rightProperty) : result; })); // Idea: From the highest to lowest degree, assign any uncolored node with // a unique color if none of its neighbors has been assigned that color. int count = 0; do { Color color = new Color(count); SubGraph subgraph = graph.newSubGraph(); for (Iterator> i = worklist.iterator(); i.hasNext();) { GraphNode node = i.next(); if (subgraph.isIndependentOf(node.getValue())) { subgraph.addNode(node.getValue()); node.setAnnotation(color); i.remove(); } } count++; } while (!worklist.isEmpty()); @SuppressWarnings("unchecked") N[] map = (N[]) new Object[count]; colorToNodeMap = map; return count; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy