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

org.ggp.base.util.gdl.model.DependencyGraphs Maven / Gradle / Ivy

The newest version!
package org.ggp.base.util.gdl.model;

import java.util.Collection;
import java.util.Deque;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;

import org.ggp.base.util.concurrency.ConcurrencyUtils;

import com.google.common.base.Predicate;
import com.google.common.collect.Collections2;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Multimap;
import com.google.common.collect.Multimaps;
import com.google.common.collect.Queues;
import com.google.common.collect.SetMultimap;
import com.google.common.collect.Sets;

/**
 * When dealing with GDL, dependency graphs are often useful. DependencyGraphs
 * offers a variety of functionality for dealing with dependency graphs expressed
 * in the form of SetMultimaps.
 *
 * These multimaps are paired with sets of all nodes, to account for the
 * possibility of nodes not included in the multimap representation.
 *
 * All methods assume that keys in multimaps depend on their associated values,
 * or in other words are downstream of or are children of those values.
 */
public class DependencyGraphs {
    private DependencyGraphs() {}

    /**
     * Returns all elements of the dependency graph that match the
     * given predicate, and any elements upstream of those matching
     * elements.
     *
     * The graph may contain cycles.
     *
     * Each key in the dependency graph depends on/is downstream of
     * its associated values.
     */
    public static  ImmutableSet getMatchingAndUpstream(
            Set allNodes,
            SetMultimap dependencyGraph,
            Predicate matcher) {
        Set results = Sets.newHashSet();

        Deque toTry = Queues.newArrayDeque();
        toTry.addAll(Collections2.filter(allNodes, matcher));

        while (!toTry.isEmpty()) {
            T curElem = toTry.remove();
            if (!results.contains(curElem)) {
                results.add(curElem);
                toTry.addAll(dependencyGraph.get(curElem));
            }
        }
        return ImmutableSet.copyOf(results);
    }

    /**
     * Returns all elements of the dependency graph that match the
     * given predicate, and any elements downstream of those matching
     * elements.
     *
     * The graph may contain cycles.
     *
     * Each key in the dependency graph depends on/is downstream of
     * its associated values.
     */
    public static  ImmutableSet getMatchingAndDownstream(
            Set allNodes,
            SetMultimap dependencyGraph,
            Predicate matcher) {
        return getMatchingAndUpstream(allNodes, reverseGraph(dependencyGraph), matcher);
    }

    public static  SetMultimap reverseGraph(SetMultimap graph) {
        return Multimaps.invertFrom(graph, HashMultimap.create());
    }

    /**
     * Given a dependency graph, return a topologically sorted
     * ordering of its components, stratified in a way that allows
     * for recursion and cycles. (Each set in the list is one
     * unordered "stratum" of elements. Elements may depend on elements
     * in earlier strata or the same stratum, but not in later
     * strata.)
     *
     * If there are no cycles, the result will be a list of singleton
     * sets, topologically sorted.
     *
     * Each key in the given dependency graph depends on/is downstream of
     * its associated values.
     */
    public static  List> toposortSafe(
            Set allElements,
            Multimap dependencyGraph) throws InterruptedException {
        Set> strataToAdd = createAllStrata(allElements);
        SetMultimap, Set> strataDependencyGraph = createStrataDependencyGraph(dependencyGraph);
        List> ordering = Lists.newArrayList();

        while (!strataToAdd.isEmpty()) {
            Set curStratum = strataToAdd.iterator().next();
            addOrMergeStratumAndAncestors(curStratum, ordering,
                    strataToAdd, strataDependencyGraph,
                    Lists.>newArrayList());
        }
        return ordering;
    }

    private static  void addOrMergeStratumAndAncestors(Set curStratum,
            List> ordering, Set> toAdd,
            SetMultimap, Set> strataDependencyGraph,
            List> downstreamStrata) throws InterruptedException {
        if (downstreamStrata.contains(curStratum)) {
            int mergeStartIndex = downstreamStrata.indexOf(curStratum);
            List> toMerge = downstreamStrata.subList(mergeStartIndex, downstreamStrata.size());
            mergeStrata(Sets.newHashSet(toMerge), toAdd, strataDependencyGraph);
            return;
        }
        downstreamStrata.add(curStratum);
        for (Set parent : ImmutableList.copyOf(strataDependencyGraph.get(curStratum))) {
            ConcurrencyUtils.checkForInterruption();
            //We could merge away the parent here, so we protect against CMEs and
            //make sure the parent is still in toAdd before recursing.
            if (toAdd.contains(parent)) {
                addOrMergeStratumAndAncestors(parent, ordering, toAdd, strataDependencyGraph, downstreamStrata);
            }
        }
        downstreamStrata.remove(curStratum);
        // - If we've added all our parents, we will still be in toAdd
        //   and none of our dependencies will be in toAdd. Add to the ordering.
        // - If there was a merge upstream that we weren't involved in,
        //   we will still be in toAdd, but we will have (possibly new)
        //   dependencies that are still in toAdd. Do nothing.
        // - If there was a merge upstream that we were involved in,
        //   we won't be in toAdd anymore. Do nothing.
        if (!toAdd.contains(curStratum)) {
            return;
        }
        for (Set parent : strataDependencyGraph.get(curStratum)) {
            ConcurrencyUtils.checkForInterruption();
            if (toAdd.contains(parent)) {
                return;
            }
        }
        ordering.add(curStratum);
        toAdd.remove(curStratum);
    }

    //Replace the old strata with the new stratum in toAdd and strataDependencyGraph.
    private static  void mergeStrata(Set> toMerge,
            Set> toAdd,
            SetMultimap, Set> strataDependencyGraph) throws InterruptedException {
        Set newStratum = ImmutableSet.copyOf(Iterables.concat(toMerge));
        for (Set oldStratum : toMerge) {
            ConcurrencyUtils.checkForInterruption();
            toAdd.remove(oldStratum);
        }
        toAdd.add(newStratum);
        //Change the keys
        for (Set oldStratum : toMerge) {
            ConcurrencyUtils.checkForInterruption();
            Collection> parents = strataDependencyGraph.get(oldStratum);
            strataDependencyGraph.putAll(newStratum, parents);
            strataDependencyGraph.removeAll(oldStratum);
        }
        //Change the values
        for (Entry, Set> entry : ImmutableList.copyOf(strataDependencyGraph.entries())) {
            ConcurrencyUtils.checkForInterruption();
            if (toMerge.contains(entry.getValue())) {
                strataDependencyGraph.remove(entry.getKey(), entry.getValue());
                strataDependencyGraph.put(entry.getKey(), newStratum);
            }
        }
    }

    private static  Set> createAllStrata(Set allElements) {
        Set> result = Sets.newHashSet();
        for (T element : allElements) {
            result.add(ImmutableSet.of(element));
        }
        return result;
    }

    private static  SetMultimap, Set> createStrataDependencyGraph(
            Multimap dependencyGraph) throws InterruptedException {
        SetMultimap, Set> strataDependencyGraph = HashMultimap.create();
        for (Entry entry : dependencyGraph.entries()) {
            ConcurrencyUtils.checkForInterruption();
            strataDependencyGraph.put(ImmutableSet.of(entry.getKey()), ImmutableSet.of(entry.getValue()));
        }
        return strataDependencyGraph;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy