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

com.ibm.wala.util.graph.Acyclic Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2007 IBM Corporation.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 */
package com.ibm.wala.util.graph;

import com.ibm.wala.util.collections.HashSetFactory;
import com.ibm.wala.util.collections.Iterator2Iterable;
import com.ibm.wala.util.intset.BasicNaturalRelation;
import com.ibm.wala.util.intset.IBinaryNaturalRelation;
import com.ibm.wala.util.intset.IntIterator;
import com.ibm.wala.util.intset.IntPair;
import java.util.ArrayDeque;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

/** Utilities for dealing with acyclic subgraphs */
public class Acyclic {

  /*
   * prevent instantiation
   */
  private Acyclic() {}

  /** This is slow. Fix it. */
  public static  boolean isAcyclic(NumberedGraph G, T root) {
    IBinaryNaturalRelation r = computeBackEdges(G, root);
    Iterator it = r.iterator();
    return !it.hasNext();
  }

  public static final int THRESHOLD_FOR_NONRECURSIVE_DFS = 1000;

  /**
   * Compute a relation R s.t. (i,j) \in R iff (i,j) is a backedge according to a DFS of a numbered
   * graph starting from some root.
   *
   * 

Not efficient. Recursive and uses hash sets. */ public static IBinaryNaturalRelation computeBackEdges(NumberedGraph G, T root) { if (G == null) { throw new IllegalArgumentException("G is null"); } final BasicNaturalRelation result = new BasicNaturalRelation(); // for large methods (e.g. obfuscated library code as found in android libraries // 'com.google.ads.ad.a([B[B)V') // the recursive dfs can lead to a stack overflow error. // for smaller methods the recursive solution seems to be faster, so we keep it. if (G.getNumberOfNodes() <= THRESHOLD_FOR_NONRECURSIVE_DFS) { final Set visited = HashSetFactory.make(); final Set onstack = HashSetFactory.make(); dfs(result, root, G, visited, onstack); } else { dfsNonRecursive(result, root, G); } return result; } private static void dfs( BasicNaturalRelation result, T root, NumberedGraph G, Set visited, Set onstack) { visited.add(root); onstack.add(root); for (T dstNode : Iterator2Iterable.make(G.getSuccNodes(root))) { if (onstack.contains(dstNode)) { int src = G.getNumber(root); int dst = G.getNumber(dstNode); result.add(src, dst); } if (!visited.contains(dstNode)) { dfs(result, dstNode, G, visited, onstack); } } onstack.remove(root); } private static void dfsNonRecursive( final BasicNaturalRelation result, final T root, final NumberedGraph G) { final ArrayDeque stack = new ArrayDeque<>(); final Set stackSet = new HashSet<>(); final ArrayDeque> stackIt = new ArrayDeque<>(); final Set finished = new HashSet<>(); stack.push(root); stackSet.add(root); stackIt.push(G.getSuccNodes(root)); while (!stack.isEmpty()) { final T current = stack.pop(); stackSet.remove(current); final Iterator currentIt = stackIt.pop(); if (finished.contains(current)) { continue; } boolean isFinished = true; while (isFinished && currentIt.hasNext()) { final T succ = currentIt.next(); if (!finished.contains(succ)) { if (succ == current || !stackSet.add(succ)) { // found a backedge final int src = G.getNumber(current); final int dst = G.getNumber(succ); result.add(src, dst); } else { stack.push(current); stackSet.add(current); stackIt.push(currentIt); stack.push(succ); stackIt.push(G.getSuccNodes(succ)); isFinished = false; } } } if (isFinished) { finished.add(current); } } } public static boolean hasIncomingBackEdges(Path p, NumberedGraph G, T root) { /* * TODO: pull out computeBackEdges, and pass in the backedge relation as a parameter to this call */ IBinaryNaturalRelation backedges = computeBackEdges(G, root); for (int index = 0; index < p.size(); index++) { int gn = p.get(index); Iterator predIter = G.getPredNodes(G.getNode(gn)); while (predIter.hasNext()) { if (backedges.contains(G.getNumber(predIter.next()), gn)) return true; } } return false; } /** * Compute a set of acyclic paths through a graph G from a node src to a node sink. * *

This is not terribly efficient. * * @param max the max number of paths to return. */ public static Collection computeAcyclicPaths( NumberedGraph G, T root, T src, T sink, int max) { Collection result = HashSetFactory.make(); EdgeFilteredNumberedGraph acyclic = new EdgeFilteredNumberedGraph<>(G, computeBackEdges(G, root)); Collection worklist = HashSetFactory.make(); Path sinkPath = Path.make(G.getNumber(sink)); worklist.add(sinkPath); while (!worklist.isEmpty() && result.size() <= max) { Path p = worklist.iterator().next(); worklist.remove(p); int first = p.get(0); if (first == G.getNumber(src)) { result.add(p); } else { for (IntIterator it = acyclic.getPredNodeNumbers(acyclic.getNode(first)).intIterator(); it.hasNext(); ) { worklist.add(Path.prepend(it.next(), p)); } } } return result; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy