org.ggp.base.util.gdl.model.DependencyGraphs Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of alloy-ggp-base Show documentation
Show all versions of alloy-ggp-base Show documentation
A modified version of the GGP-Base library for Alloy.
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 extends T> 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 extends T> 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;
}
}