/**
*
*/
package soottocfg.cfg.util;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Queue;
import java.util.Set;
import org.jgrapht.DirectedGraph;
import org.jgrapht.Graphs;
import org.jgrapht.ext.DOTExporter;
import org.jgrapht.ext.StringNameProvider;
import org.jgrapht.graph.DefaultDirectedGraph;
import org.jgrapht.graph.DefaultEdge;
import com.google.common.base.Verify;
/**
* @author schaef
*
* Given a directed graph with unique source and unique sink,
* perform the following steps: 1) Compute the set of 'inevitable'
* vertices for each vertex. Here, a vertex A is inevitable for a vertex
* B if any complete path through B must contain A as well. I.e., the
* set of 'inevitable' vertices is the union of the dominators and
* post-dominators of a vertex.
*
* 2) Group the vertices into equivalence classes, s.t. two vertices are
* equivalent iff they have the same set of 'inevitable' vertices, I. We
* define a partial order over these equivalence classes: given two
* equivalence classes, E1 and E2, with the sets in inevitable vertices
* I1 and I2, then E1 <= E2 iff I1 \subseteq I2. From this partial
* order, we generate a lattice.
*
* 3) Using the lattice, we compute an 'effectual' set. Which is a
* minimal set of nodes that need to be covered to obtain a complete
* cover of the original graph. The effectual set is obtained by
* selecting one element (non-deterministically) from each equivalence
* class E that is maximal wrt our partial order.
*
* Example:
*
* TODO
*
*
*/
public class EffectualSet<A> {
private final Map<A, Set<A>> inevitable;
private final DirectedGraph<Set<A>, DefaultEdge> lattice;
private final Set<A> effectualSet;
private final Dominators<A> dominators;
private final PostDominators<A> postDominators;
/**
*
*/
public EffectualSet(Dominators<A> dominators, PostDominators<A> postDominators) {
this.dominators = dominators;
this.postDominators = postDominators;
Map<A, Set<A>> dom = dominators.getDominators();
Map<A, Set<A>> pdom = postDominators.getDominators();
inevitable = new HashMap<A, Set<A>>(dom);
for (Entry<A, Set<A>> entry : pdom.entrySet()) {
inevitable.get(entry.getKey()).addAll(entry.getValue());
}
lattice = buildLattice();
effectualSet = new HashSet<A>();
for (Set<A> v : lattice.vertexSet()) {
if (lattice.outDegreeOf(v) == 0) {
Verify.verify(!v.isEmpty(), "Lattice construction failed.");
effectualSet.add(v.iterator().next());
}
}
}
/**
* Returns the lattice of the partial order defined by subset relation
* over the set of complete paths that go through a vertex.
* That is, two vertices are in the same set in the lattice if they share
* the exact same set of complete paths. For example, if a graph has a
* unique source and a unique sink, both vertices share the same set of
* complete paths. If there is a diamond in the graph, the vertices before
* and after the diamond share the same set of complete paths while the
* vertices on either side of the diamond share a subset of paths with the
* vertices before and after the diamond (i.e., all but those that go through
* the other side of the diamond).
*
* @return
*/
public DirectedGraph<Set<A>, DefaultEdge> getLattice() {
return lattice;
}
/**
* Get the dominators that were used to build the effectual set.
* @return
*/
public Dominators<A> getDominators() {
return dominators;
}
/**
* Get the post dominators that were used to build the effectual set.
* @return
*/
public PostDominators<A> getPostDominators() {
return postDominators;
}
/**
* Compute an effectual set for the graph. I.e., a minimal set of vertices
* that need to be covered to obtain a path cover of the graph.
*
* @see http://rd.springer.com/chapter/10.1007%2F978-3-642-27705-4_24#page-1
* @return
*/
public Set<A> getEffectualSet() {
return effectualSet;
}
/**
* Finds the vertex 'b' in the lattice and returns the set of blocks
* that share the same set of traces.
* @param b
* @return
*/
public Set<A> findInLattice(A b) {
BfsIterator<Set<A>> iter = new BfsIterator<Set<A>>(lattice);
while (iter.hasNext()) {
Set<A> current = iter.next();
if (current.contains(b)) {
return current;
}
}
throw new RuntimeException("Not found!");
}
/**
* Check if 'b' is in a lattice element below 'latticeElement'.
* @param b
* @param latticeElement
* @return
*/
public boolean isBelowInLattice(A b, Set<A> latticeElement) {
if (latticeElement.contains(b))
return false;
Queue<Set<A>> todo = new LinkedList<Set<A>>(Graphs.successorListOf(lattice, latticeElement));
Set<Set<A>> done = new HashSet<Set<A>>();
while (!todo.isEmpty()) {
Set<A> current = todo.poll();
if (current.contains(b)) {
return true;
}
done.add(current);
for (Set<A> next : Graphs.successorListOf(lattice, current)) {
if (!todo.contains(next) && !done.contains(next)) {
todo.add(next);
}
}
}
return false;
}
/**
* Check if 'b' is in a lattice element above 'latticeElement'.
* @param b
* @param latticeElement
* @return
*/
public boolean isAboveInLattice(A b, Set<A> latticeElement) {
if (latticeElement.contains(b))
return false;
Queue<Set<A>> todo = new LinkedList<Set<A>>(Graphs.predecessorListOf(lattice, latticeElement));
Set<Set<A>> done = new HashSet<Set<A>>();
while (!todo.isEmpty()) {
Set<A> current = todo.poll();
if (current.contains(b)) {
return true;
}
done.add(current);
for (Set<A> next : Graphs.predecessorListOf(lattice, current)) {
if (!todo.contains(next) && !done.contains(next)) {
todo.add(next);
}
}
}
return false;
}
private DirectedGraph<Set<A>, DefaultEdge> buildLattice() {
DirectedGraph<Set<A>, DefaultEdge> lattice = new DefaultDirectedGraph<Set<A>, DefaultEdge>(DefaultEdge.class);
Set<EquivalentVertices<A>> poset = new HashSet<EquivalentVertices<A>>();
/*
* Group vertices into equivalence classes using the equivalence
* relation that two vertices are equivalent iff they have the same set
* of inevitable vertices.
*/
for (Entry<A, Set<A>> entry : inevitable.entrySet()) {
/*
* Check if another POElement exists that has the same inevitable
* vertices.
*/
EquivalentVertices<A> found = null;
for (EquivalentVertices<A> poe : poset) {
if (poe.inevitableVertices.equals(entry.getValue())) {
found = poe;
break;
}
}
/*
* If a POElement with the same inevitable vertices exists, add the
* current vertex the this POElement (which is the equivalence class
* of all vertices that have the same set of inevitable vertices.
*/
if (found != null) {
found.equivalentVertices.add(entry.getKey());
} else {
poset.add(new EquivalentVertices<A>(entry.getKey(), entry.getValue()));
}
}
Map<EquivalentVertices<A>, Set<EquivalentVertices<A>>> successors = new HashMap<EquivalentVertices<A>, Set<EquivalentVertices<A>>>();
for (EquivalentVertices<A> x : poset) {
successors.put(x, findImmediateSuccessorsOf(x, poset));
}
// Add all vertices to the lattice first
for (EquivalentVertices<A> x : poset) {
lattice.addVertex(x.equivalentVertices);
}
// Add all edges for the lattice.
for (EquivalentVertices<A> x : poset) {
for (EquivalentVertices<A> succ : successors.get(x)) {
lattice.addEdge(x.equivalentVertices, succ.equivalentVertices);
}
}
return lattice;
}
/**
* Find all immediate successors of el in elements using the partial order
* defined by el.lessThan. Probably not the most efficient way of doing
* this, but it works.
*
* @param el
* @param elements
* @return
*/
private Set<EquivalentVertices<A>> findImmediateSuccessorsOf(EquivalentVertices<A> el,
Set<EquivalentVertices<A>> elements) {
Set<EquivalentVertices<A>> res = new HashSet<EquivalentVertices<A>>();
for (EquivalentVertices<A> x : elements) {
if (x == el)
continue;
if (el.lessThan(x)) {
// check if we have something better already
// or if this entry replaces others that are worse.
boolean subsumed = false;
Set<EquivalentVertices<A>> subsumes = new HashSet<EquivalentVertices<A>>();
for (EquivalentVertices<A> y : res) {
if (y.lessThan(x)) {
subsumed = true;
break;
}
if (x.lessThan(y)) {
subsumes.add(y);
}
}
if (!subsumed) {
res.removeAll(subsumes);
res.add(x);
}
}
}
return res;
}
public static class EquivalentVertices<A> {
public final Set<A> equivalentVertices;
public final Set<A> inevitableVertices;
public EquivalentVertices(A eq, Set<A> vertices) {
this.equivalentVertices = new HashSet<A>();
this.equivalentVertices.add(eq);
this.inevitableVertices = vertices;
}
public EquivalentVertices(Set<A> equiv, Set<A> vertices) {
this.equivalentVertices = equiv;
this.inevitableVertices = vertices;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("\"");
sb.append("Equivalent blocks " + this.equivalentVertices.size());
sb.append(", inevitable blocks " + this.inevitableVertices.size());
sb.append("\"");
return sb.toString();
}
public boolean lessThan(EquivalentVertices<A> other) {
if (other == null) {
throw new NullPointerException();
}
return other.inevitableVertices.containsAll(this.inevitableVertices);
}
}
public void latticToDot(File dotFile) {
try (FileOutputStream fileStream = new FileOutputStream(dotFile);
OutputStreamWriter writer = new OutputStreamWriter(fileStream, "UTF-8");) {
DOTExporter<Set<A>, DefaultEdge> dot = new DOTExporter<Set<A>, DefaultEdge>(
new StringNameProvider<Set<A>>() {
@Override
public String getVertexName(Set<A> vertex) {
StringBuilder sb = new StringBuilder();
sb.append("\"{");
String comma = "";
for (A s : vertex) {
sb.append(comma);
sb.append(s.toString());
comma = ", ";
}
sb.append("}\"");
return sb.toString();
}
}, null, null);
dot.export(writer, lattice);
} catch (IOException e) {
e.printStackTrace();
}
}
}