// BDDFactory.java, created Jan 29, 2003 9:50:57 PM by jwhaley // Copyright (C) 2003 John Whaley // Licensed under the terms of the GNU LGPL; see COPYING for details. package net.sf.javabdd; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.StringTokenizer; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.math.BigInteger; import java.security.AccessControlException; /** * <p>Interface for the creation and manipulation of BDDs.</p> * * @see net.sf.javabdd.BDD * * @author John Whaley * @version $Id: BDDFactory.java,v 1.11 2005/05/04 22:31:35 joewhaley Exp $ */ public abstract class BDDFactory { public static final String getProperty(String key, String def) { try { return System.getProperty(key, def); } catch (AccessControlException _) { return def; } } /** * <p>Initializes a BDD factory with the given initial node table size * and operation cache size. Tries to use the "buddy" native library; * if it fails, it falls back to the "java" library.</p> * * @param nodenum initial node table size * @param cachesize operation cache size * @return BDD factory object */ public static BDDFactory init(int nodenum, int cachesize) { String bddpackage = getProperty("bdd", "buddy"); return init(bddpackage, nodenum, cachesize); } /** * <p>Initializes a BDD factory of the given type with the given initial * node table size and operation cache size. The type is a string that * can be "buddy", "cudd", "cal", "j", "java", "jdd", "test", "typed", or * a name of a class that has an init() method that returns a BDDFactory. * If it fails, it falls back to the "java" factory.</p> * * @param bddpackage BDD package string identifier * @param nodenum initial node table size * @param cachesize operation cache size * @return BDD factory object */ public static BDDFactory init(String bddpackage, int nodenum, int cachesize) { try { if (bddpackage.equals("buddy")) return BuDDyFactory.init(nodenum, cachesize); if (bddpackage.equals("cudd")) return CUDDFactory.init(nodenum, cachesize); if (bddpackage.equals("cal")) return CALFactory.init(nodenum, cachesize); if (bddpackage.equals("j") || bddpackage.equals("java")) return JFactory.init(nodenum, cachesize); if (bddpackage.equals("u") || bddpackage.equals("micro")) return MicroFactory.init(nodenum, cachesize); //if (bddpackage.equals("jdd")) // return JDDFactory.init(nodenum, cachesize); //if (bddpackage.equals("test")) // return TestBDDFactory.init(nodenum, cachesize); //if (bddpackage.equals("typed")) // return TypedBDDFactory.init(nodenum, cachesize); } catch (LinkageError e) { System.out.println("Could not load BDD package "+bddpackage+": "+e.getLocalizedMessage()); } try { Class c = Class.forName(bddpackage); Method m = c.getMethod("init", new Class[] { int.class, int.class }); return (BDDFactory) m.invoke(null, new Object[] { new Integer(nodenum), new Integer(cachesize) }); } catch (ClassNotFoundException _) {} catch (NoSuchMethodException _) {} catch (IllegalAccessException _) {} catch (InvocationTargetException _) {} // falling back to default java implementation. return JFactory.init(nodenum, cachesize); } /** * Logical 'and'. */ public static final BDDOp and = new BDDOp(0, "and"); /** * Logical 'xor'. */ public static final BDDOp xor = new BDDOp(1, "xor"); /** * Logical 'or'. */ public static final BDDOp or = new BDDOp(2, "or"); /** * Logical 'nand'. */ public static final BDDOp nand = new BDDOp(3, "nand"); /** * Logical 'nor'. */ public static final BDDOp nor = new BDDOp(4, "nor"); /** * Logical 'implication'. */ public static final BDDOp imp = new BDDOp(5, "imp"); /** * Logical 'bi-implication'. */ public static final BDDOp biimp = new BDDOp(6, "biimp"); /** * Set difference. */ public static final BDDOp diff = new BDDOp(7, "diff"); /** * Less than. */ public static final BDDOp less = new BDDOp(8, "less"); /** * Inverse implication. */ public static final BDDOp invimp = new BDDOp(9, "invimp"); /** * <p>Enumeration class for binary operations on BDDs. Use the static * fields in BDDFactory to access the different binary operations.</p> */ public static class BDDOp { final int id; final String name; private BDDOp(int id, String name) { this.id = id; this.name = name; } public String toString() { return name; } } /** * <p>Construct a new BDDFactory.</p> */ protected BDDFactory() { String s = this.getClass().toString(); if (false) { s = s.substring(s.lastIndexOf('.')+1); System.out.println("Using BDD package: "+s); } } /** * <p>Get the constant false BDD.</p> * * <p>Compare to bdd_false.</p> */ public abstract BDD zero(); /** * <p>Get the constant true BDD.</p> * * <p>Compare to bdd_true.</p> */ public abstract BDD one(); /** * <p>Build a cube from an array of variables.</p> * * <p>Compare to bdd_buildcube.</p> */ public BDD buildCube(int value, List/*<BDD>*/ variables) { BDD result = one(); Iterator i = variables.iterator(); int z = 0; while (i.hasNext()) { BDD var = (BDD) i.next(); if ((value & 0x1) != 0) var = var.id(); else var = var.not(); result.andWith(var); ++z; value >>= 1; } return result; } /** * <p>Build a cube from an array of variables.</p> * * <p>Compare to bdd_ibuildcube./p> */ public BDD buildCube(int value, int[] variables) { BDD result = one(); for (int z = 0; z < variables.length; z++, value >>= 1) { BDD v; if ((value & 0x1) != 0) v = ithVar(variables[variables.length - z - 1]); else v = nithVar(variables[variables.length - z - 1]); result.andWith(v); } return result; } /** * <p>Builds a BDD variable set from an integer array. The integer array * <tt>varset</tt> holds the variable numbers. The BDD variable set is * represented by a conjunction of all the variables in their positive * form.</p> * * <p>Compare to bdd_makeset.</p> */ public BDD makeSet(int[] varset) { BDD res = one(); int varnum = varset.length; for (int v = varnum-1 ; v>=0 ; v--) { res.andWith(ithVar(varset[v])); } return res; } /**** STARTUP / SHUTDOWN ****/ /** * <p>Compare to bdd_init.</p> * * @param nodenum the initial number of BDD nodes * @param cachesize the size of caches used by the BDD operators */ protected abstract void initialize(int nodenum, int cachesize); /** * <p>Returns true if this BDD factory is initialized, false otherwise.</p> * * <p>Compare to bdd_isrunning.</p> * * @return true if this BDD factory is initialized */ public abstract boolean isInitialized(); /** * <p>Reset the BDD factory to its initial state. Everything * is reallocated from scratch. This is like calling done() * followed by initialize().</p> */ public void reset() { int nodes = getNodeTableSize(); int cache = getCacheSize(); domain = null; fdvarnum = 0; firstbddvar = 0; done(); initialize(nodes, cache); } /** * <p>This function frees all memory used by the BDD * package and resets the package to its uninitialized state. * The BDD package is no longer usable after this call.</p> * * <p>Compare to bdd_done.</p> */ public abstract void done(); /** * <p>Sets the error condition. This will cause the BDD package to throw an * exception at the next garbage collection.</p> * * @param code the error code to set */ public abstract void setError(int code); /** * <p>Clears any outstanding error condition.</p> */ public abstract void clearError(); /**** CACHE/TABLE PARAMETERS ****/ /** * <p>Set the maximum available number of BDD nodes.</p> * * <p>Compare to bdd_setmaxnodenum.</p> * * @param size maximum number of nodes * @return old value */ public abstract int setMaxNodeNum(int size); /** * <p>Set minimum percentage of nodes to be reclaimed after a garbage * collection. If this percentage is not reclaimed, the node table * will be grown. The range of x is 0..1. The default is .20.</p> * * <p>Compare to bdd_setminfreenodes.</p> * * @param x number from 0 to 1 * @return old value */ public abstract double setMinFreeNodes(double x); /** * <p>Set maximum number of nodes by which to increase node table after * a garbage collection.</p> * * <p>Compare to bdd_setmaxincrease.</p> * * @param x maximum number of nodes by which to increase node table * @return old value */ public abstract int setMaxIncrease(int x); /** * <p>Set factor by which to increase node table after a garbage * collection. The amount of growth is still limited by * <tt>setMaxIncrease()</tt>.</p> * * @param x factor by which to increase node table after GC * @return old value */ public abstract double setIncreaseFactor(double x); /** * <p>Sets the cache ratio for the operator caches. When the node table * grows, operator caches will also grow to maintain the ratio.</p> * * <p>Compare to bdd_setcacheratio.</p> * * @param x cache ratio */ public abstract double setCacheRatio(double x); /** * <p>Sets the node table size.</p> * * @param n new size of table * @return old size of table */ public abstract int setNodeTableSize(int n); /** * <p>Sets cache size.</p> * * @return old cache size */ public abstract int setCacheSize(int n); /**** VARIABLE NUMBERS ****/ /** * <p>Returns the number of defined variables.</p> * * <p>Compare to bdd_varnum.</p> */ public abstract int varNum(); /** * <p>Set the number of used BDD variables. It can be called more than one * time, but only to increase the number of variables.</p> * * <p>Compare to bdd_setvarnum.</p> * * @param num new number of BDD variables * @return old number of BDD variables */ public abstract int setVarNum(int num); /** * <p>Add extra BDD variables. Extends the current number of allocated BDD * variables with num extra variables.</p> * * <p>Compare to bdd_extvarnum.</p> * * @param num number of BDD variables to add * @return old number of BDD variables */ public int extVarNum(int num) { int start = varNum(); if (num < 0 || num > 0x3FFFFFFF) throw new BDDException(); setVarNum(start+num); return start; } /** * <p>Returns a BDD representing the I'th variable. (One node with the * children true and false.) The requested variable must be in the * (zero-indexed) range defined by <tt>setVarNum</tt>.</p> * * <p>Compare to bdd_ithvar.</p> * * @param var the variable number * @return the I'th variable on success, otherwise the constant false BDD */ public abstract BDD ithVar(int var); /** * <p>Returns a BDD representing the negation of the I'th variable. (One node * with the children false and true.) The requested variable must be in the * (zero-indexed) range defined by <tt>setVarNum</tt>.</p> * * <p>Compare to bdd_nithvar.</p> * * @param var the variable number * @return the negated I'th variable on success, otherwise the constant false BDD */ public abstract BDD nithVar(int var); /**** INPUT / OUTPUT ****/ /** * <p>Prints all used entries in the node table.</p> * * <p>Compare to bdd_printall.</p> */ public abstract void printAll(); /** * <p>Prints the node table entries used by a BDD.</p> * * <p>Compare to bdd_printtable.</p> */ public abstract void printTable(BDD b); /** * <p>Loads a BDD from a file.</p> * * <p>Compare to bdd_load.</p> */ public BDD load(String filename) throws IOException { BufferedReader r = null; try { r = new BufferedReader(new FileReader(filename)); BDD result = load(r); return result; } finally { if (r != null) try { r.close(); } catch (IOException _) { } } } // TODO: error code from bdd_load (?) /** * <p>Loads a BDD from the given input.</p> * * <p>Compare to bdd_load.</p> * * @param ifile reader * @return BDD */ public BDD load(BufferedReader ifile) throws IOException { tokenizer = null; int lh_nodenum = Integer.parseInt(readNext(ifile)); int vnum = Integer.parseInt(readNext(ifile)); // Check for constant true / false if (lh_nodenum == 0 && vnum == 0) { int r = Integer.parseInt(readNext(ifile)); return r == 0 ? zero() : one(); } // Not actually used. int[] loadvar2level = new int[vnum]; for (int n = 0; n < vnum; n++) { loadvar2level[n] = Integer.parseInt(readNext(ifile)); } if (vnum > varNum()) setVarNum(vnum); LoadHash[] lh_table = new LoadHash[lh_nodenum]; for (int n = 0; n < lh_nodenum; n++) { lh_table[n] = new LoadHash(); lh_table[n].first = -1; lh_table[n].next = n + 1; } lh_table[lh_nodenum - 1].next = -1; int lh_freepos = 0; BDD root = null; for (int n = 0; n < lh_nodenum; n++) { int key = Integer.parseInt(readNext(ifile)); int var = Integer.parseInt(readNext(ifile)); int lowi = Integer.parseInt(readNext(ifile)); int highi = Integer.parseInt(readNext(ifile)); BDD low, high; low = loadhash_get(lh_table, lh_nodenum, lowi); high = loadhash_get(lh_table, lh_nodenum, highi); if (low == null || high == null || var < 0) throw new BDDException("Incorrect file format"); BDD b = ithVar(var); root = b.ite(high, low); b.free(); int hash = key % lh_nodenum; int pos = lh_freepos; lh_freepos = lh_table[pos].next; lh_table[pos].next = lh_table[hash].first; lh_table[hash].first = pos; lh_table[pos].key = key; lh_table[pos].data = root; } BDD tmproot = root.id(); for (int n = 0; n < lh_nodenum; n++) lh_table[n].data.free(); lh_table = null; loadvar2level = null; return tmproot; } /** * Used for tokenization during loading. */ protected StringTokenizer tokenizer; /** * Read the next token from the file. * * @param ifile reader * @return next string token */ protected String readNext(BufferedReader ifile) throws IOException { while (tokenizer == null || !tokenizer.hasMoreTokens()) { String s = ifile.readLine(); if (s == null) throw new BDDException("Incorrect file format"); tokenizer = new StringTokenizer(s); } return tokenizer.nextToken(); } /** * LoadHash is used to hash during loading. */ protected static class LoadHash { int key; BDD data; int first; int next; } /** * Gets a BDD from the load hash table. */ protected BDD loadhash_get(LoadHash[] lh_table, int lh_nodenum, int key) { if (key < 0) return null; if (key == 0) return zero(); if (key == 1) return one(); int hash = lh_table[key % lh_nodenum].first; while (hash != -1 && lh_table[hash].key != key) hash = lh_table[hash].next; if (hash == -1) return null; return lh_table[hash].data; } /** * <p>Saves a BDD to a file.</p> * * <p>Compare to bdd_save.</p> */ public void save(String filename, BDD var) throws IOException { BufferedWriter is = null; try { is = new BufferedWriter(new FileWriter(filename)); save(is, var); } finally { if (is != null) try { is.close(); } catch (IOException _) { } } } // TODO: error code from bdd_save (?) /** * <p>Saves a BDD to an output writer.</p> * * <p>Compare to bdd_save.</p> */ public void save(BufferedWriter out, BDD r) throws IOException { if (r.isOne() || r.isZero()) { out.write("0 0 " + (r.isOne()?1:0) + "\n"); return; } out.write(r.nodeCount() + " " + varNum() + "\n"); for (int x = 0; x < varNum(); x++) out.write(var2Level(x) + " "); out.write("\n"); Map visited = new HashMap(); save_rec(out, visited, r); for (Iterator it = visited.keySet().iterator(); it.hasNext(); ) { BDD b = (BDD) it.next(); if (b != r) b.free(); } } /** * Helper function for save(). */ protected int save_rec(BufferedWriter out, Map visited, BDD root) throws IOException { if (root.isZero()) { root.free(); return 0; } if (root.isOne()) { root.free(); return 1; } Integer i = (Integer) visited.get(root); if (i != null) { root.free(); return i.intValue(); } int v = visited.size() + 2; visited.put(root, new Integer(v)); BDD l = root.low(); int lo = save_rec(out, visited, l); BDD h = root.high(); int hi = save_rec(out, visited, h); out.write(v + " "); out.write(root.var() + " "); out.write(lo + " "); out.write(hi + "\n"); return v; } // TODO: bdd_blockfile_hook // TODO: bdd_versionnum, bdd_versionstr /**** REORDERING ****/ /** * <p>Convert from a BDD level to a BDD variable.</p> * * <p>Compare to bdd_level2var.</p> */ public abstract int level2Var(int level); /** * <p>Convert from a BDD variable to a BDD level.</p> * * <p>Compare to bdd_var2level.</p> */ public abstract int var2Level(int var); /** * No reordering. */ public static final ReorderMethod REORDER_NONE = new ReorderMethod(0, "NONE"); /** * Reordering using a sliding window of 2. */ public static final ReorderMethod REORDER_WIN2 = new ReorderMethod(1, "WIN2"); /** * Reordering using a sliding window of 2, iterating until no further * progress. */ public static final ReorderMethod REORDER_WIN2ITE = new ReorderMethod(2, "WIN2ITE"); /** * Reordering using a sliding window of 3. */ public static final ReorderMethod REORDER_WIN3 = new ReorderMethod(5, "WIN3"); /** * Reordering using a sliding window of 3, iterating until no further * progress. */ public static final ReorderMethod REORDER_WIN3ITE = new ReorderMethod(6, "WIN3ITE"); /** * Reordering where each block is moved through all possible positions. The * best of these is then used as the new position. Potentially a very slow * but good method. */ public static final ReorderMethod REORDER_SIFT = new ReorderMethod(3, "SIFT"); /** * Same as REORDER_SIFT, but the process is repeated until no further * progress is done. Can be extremely slow. */ public static final ReorderMethod REORDER_SIFTITE = new ReorderMethod(4, "SIFTITE"); /** * Selects a random position for each variable. Mostly used for debugging * purposes. */ public static final ReorderMethod REORDER_RANDOM = new ReorderMethod(7, "RANDOM"); /** * Enumeration class for method reordering techniques. Use the static fields * in BDDFactory to access the different reordering techniques. */ public static class ReorderMethod { final int id; final String name; private ReorderMethod(int id, String name) { this.id = id; this.name = name; } /* (non-Javadoc) * @see java.lang.Object#toString() */ public String toString() { return name; } } /** * <p>Reorder the BDD with the given method.</p> * * <p>Compare to bdd_reorder.</p> */ public abstract void reorder(ReorderMethod m); /** * <p>Enables automatic reordering. If method is REORDER_NONE then automatic * reordering is disabled.</p> * * <p>Compare to bdd_autoreorder.</p> */ public abstract void autoReorder(ReorderMethod method); /** * <p>Enables automatic reordering with the given (maximum) number of * reorderings. If method is REORDER_NONE then automatic reordering is * disabled.</p> * * <p>Compare to bdd_autoreorder_times.</p> */ public abstract void autoReorder(ReorderMethod method, int max); /** * <p>Returns the current reorder method as defined by autoReorder.</p> * * <p>Compare to bdd_getreorder_method.</p> * * @return ReorderMethod */ public abstract ReorderMethod getReorderMethod(); /** * <p>Returns the number of allowed reorderings left. This value can be * defined by autoReorder.</p> * * <p>Compare to bdd_getreorder_times.</p> */ public abstract int getReorderTimes(); /** * <p>Disable automatic reordering until enableReorder is called. Reordering * is enabled by default as soon as any variable blocks have been defined.</p> * * <p>Compare to bdd_disable_reorder.</p> */ public abstract void disableReorder(); /** * <p>Enable automatic reordering after a call to disableReorder.</p> * * <p>Compare to bdd_enable_reorder.</p> */ public abstract void enableReorder(); /** * <p>Enables verbose information about reordering. A value of zero means no * information, one means some information and greater than one means lots * of information.</p> * * @param v the new verbose level * @return the old verbose level */ public abstract int reorderVerbose(int v); /** * <p>This function sets the current variable order to be the one defined by * neworder. The variable parameter neworder is interpreted as a sequence * of variable indices and the new variable order is exactly this sequence. * The array must contain all the variables defined so far. If, for * instance the current number of variables is 3 and neworder contains * [1; 0; 2] then the new variable order is v1<v0<v2.</p> * * <p>Note that this operation must walk through the node table many times, * and therefore it is much more efficient to call this when the node table * is small.</p> * * @param neworder new variable order */ public abstract void setVarOrder(int[] neworder); /** * <p>Make a new BDDPairing object.</p> * * <p>Compare to bdd_newpair.</p> */ public abstract BDDPairing makePair(); /** * Make a new pairing that maps from one variable to another. * * @param oldvar old variable * @param newvar new variable * @return BDD pairing */ public BDDPairing makePair(int oldvar, int newvar) { BDDPairing p = makePair(); p.set(oldvar, newvar); return p; } /** * Make a new pairing that maps from one variable to another BDD. * * @param oldvar old variable * @param newvar new BDD * @return BDD pairing */ public BDDPairing makePair(int oldvar, BDD newvar) { BDDPairing p = makePair(); p.set(oldvar, newvar); return p; } /** * Make a new pairing that maps from one BDD domain to another. * * @param oldvar old BDD domain * @param newvar new BDD domain * @return BDD pairing */ public BDDPairing makePair(BDDDomain oldvar, BDDDomain newvar) { BDDPairing p = makePair(); p.set(oldvar, newvar); return p; } /** * <p>Swap two variables.</p> * * <p>Compare to bdd_swapvar.</p> */ public abstract void swapVar(int v1, int v2); /** * Duplicate a BDD variable. * * @param var var to duplicate * @return index of new variable */ public abstract int duplicateVar(int var); /**** VARIABLE BLOCKS ****/ /** * <p>Adds a new variable block for reordering.</p> * * <p>Creates a new variable block with the variables in the variable set var. * The variables in var must be contiguous.</p> * * <p>The fixed parameter sets the block to be fixed (no reordering of its * child blocks is allowed) or free.</p> * * <p>Compare to bdd_addvarblock.</p> */ public abstract void addVarBlock(BDD var, boolean fixed); // TODO: handle error code for addVarBlock. /** * <p>Adds a new variable block for reordering.</p> * * <p>Creates a new variable block with the variables numbered first through * last, inclusive.</p> * * <p>The fixed parameter sets the block to be fixed (no reordering of its * child blocks is allowed) or free.</p> * * <p>Compare to bdd_intaddvarblock.</p> */ public abstract void addVarBlock(int first, int last, boolean fixed); // TODO: handle error code for addVarBlock. // TODO: fdd_intaddvarblock (?) /** * <p>Add a variable block for all variables.</p> * * <p>Adds a variable block for all BDD variables declared so far. Each block * contains one variable only. More variable blocks can be added later with * the use of addVarBlock -- in this case the tree of variable blocks will * have the blocks of single variables as the leafs.</p> * * <p>Compare to bdd_varblockall.</p> */ public abstract void varBlockAll(); /** * <p>Clears all the variable blocks that have been defined by calls to * addVarBlock.</p> * * <p>Compare to bdd_clrvarblocks.</p> */ public abstract void clearVarBlocks(); /** * <p>Prints an indented list of the variable blocks.</p> * * <p>Compare to bdd_printorder.</p> */ public abstract void printOrder(); /**** BDD STATS ****/ /** * Get the BDD library version. * * @return version string */ public abstract String getVersion(); /** * <p>Counts the number of shared nodes in a collection of BDDs. Counts all * distinct nodes that are used in the BDDs -- if a node is used in more * than one BDD then it only counts once.</p> * * <p>Compare to bdd_anodecount.</p> */ public abstract int nodeCount(Collection/*BDD*/ r); /** * <p>Get the number of allocated nodes. This includes both dead and active * nodes.</p> * * <p>Compare to bdd_getallocnum.</p> */ public abstract int getNodeTableSize(); /** * <p>Get the number of active nodes in use. Note that dead nodes that have * not been reclaimed yet by a garbage collection are counted as active.</p> * * <p>Compare to bdd_getnodenum.</p> */ public abstract int getNodeNum(); /** * <p>Get the current size of the cache, in entries.</p> * * @return size of cache */ public abstract int getCacheSize(); /** * <p>Calculate the gain in size after a reordering. The value returned is * (100*(A-B))/A, where A is previous number of used nodes and B is current * number of used nodes.</p> * * <p>Compare to bdd_reorder_gain.</p> */ public abstract int reorderGain(); /** * <p>Print cache statistics.</p> * * <p>Compare to bdd_printstat.</p> */ public abstract void printStat(); /** * Stores statistics about garbage collections. * * @author jwhaley * @version $Id: BDDFactory.java,v 1.11 2005/05/04 22:31:35 joewhaley Exp $ */ public static class GCStats { public int nodes; public int freenodes; public long time; public long sumtime; public int num; protected GCStats() { } /* (non-Javadoc) * @see java.lang.Object#toString() */ public String toString() { StringBuffer sb = new StringBuffer(); sb.append("Garbage collection #"); sb.append(num); sb.append(": "); sb.append(nodes); sb.append(" nodes / "); sb.append(freenodes); sb.append(" free"); sb.append(" / "); sb.append((float) time / (float) 1000); sb.append("s / "); sb.append((float) sumtime / (float) 1000); sb.append("s total"); return sb.toString(); } } /** * Singleton object for GC statistics. */ protected GCStats gcstats = new GCStats(); /** * <p>Return the current GC statistics for this BDD factory.</p> * * @return GC statistics */ public GCStats getGCStats() { return gcstats; } /** * Stores statistics about reordering. * * @author jwhaley * @version $Id: BDDFactory.java,v 1.11 2005/05/04 22:31:35 joewhaley Exp $ */ public static class ReorderStats { public long time; public int usednum_before, usednum_after; protected ReorderStats() { } public int gain() { if (usednum_before == 0) return 0; return (100 * (usednum_before - usednum_after)) / usednum_before; } public String toString() { StringBuffer sb = new StringBuffer(); sb.append("Went from "); sb.append(usednum_before); sb.append(" to "); sb.append(usednum_after); sb.append(" nodes, gain = "); sb.append(gain()); sb.append("% ("); sb.append((float) time / 1000f); sb.append(" sec)"); return sb.toString(); } } /** * Singleton object for reorder statistics. */ protected ReorderStats reorderstats = new ReorderStats(); /** * <p>Return the current reordering statistics for this BDD factory.</p> * * @return reorder statistics */ public ReorderStats getReorderStats() { return reorderstats; } /** * Stores statistics about the operator cache. * * @author jwhaley * @version $Id: BDDFactory.java,v 1.11 2005/05/04 22:31:35 joewhaley Exp $ */ public static class CacheStats { public int uniqueAccess; public int uniqueChain; public int uniqueHit; public int uniqueMiss; public int opHit; public int opMiss; public int swapCount; protected CacheStats() { } void copyFrom(CacheStats that) { this.uniqueAccess = that.uniqueAccess; this.uniqueChain = that.uniqueChain; this.uniqueHit = that.uniqueHit; this.uniqueMiss = that.uniqueMiss; this.opHit = that.opHit; this.opMiss = that.opMiss; this.swapCount = that.swapCount; } /* (non-Javadoc) * @see java.lang.Object#toString() */ public String toString() { StringBuffer sb = new StringBuffer(); String newLine = getProperty("line.separator", "\n"); sb.append(newLine); sb.append("Cache statistics"); sb.append(newLine); sb.append("----------------"); sb.append(newLine); sb.append("Unique Access: "); sb.append(uniqueAccess); sb.append(newLine); sb.append("Unique Chain: "); sb.append(uniqueChain); sb.append(newLine); sb.append("Unique Hit: "); sb.append(uniqueHit); sb.append(newLine); sb.append("Unique Miss: "); sb.append(uniqueMiss); sb.append(newLine); sb.append("=> Hit rate = "); if (uniqueHit + uniqueMiss > 0) sb.append(((float) uniqueHit) / ((float) uniqueHit + uniqueMiss)); else sb.append((float)0); sb.append(newLine); sb.append("Operator Hits: "); sb.append(opHit); sb.append(newLine); sb.append("Operator Miss: "); sb.append(opMiss); sb.append(newLine); sb.append("=> Hit rate = "); if (opHit + opMiss > 0) sb.append(((float) opHit) / ((float) opHit + opMiss)); else sb.append((float)0); sb.append(newLine); sb.append("Swap count = "); sb.append(swapCount); sb.append(newLine); return sb.toString(); } } /** * Singleton object for cache statistics. */ protected CacheStats cachestats = new CacheStats(); /** * <p>Return the current cache statistics for this BDD factory.</p> * * @return cache statistics */ public CacheStats getCacheStats() { return cachestats; } // TODO: bdd_sizeprobe_hook // TODO: bdd_reorder_probe /**** FINITE DOMAINS ****/ protected BDDDomain[] domain; protected int fdvarnum; protected int firstbddvar; /** * <p>Implementors must implement this factory method to create BDDDomain * objects of the correct type.</p> */ protected abstract BDDDomain createDomain(int a, BigInteger b); /** * <p>Creates a new finite domain block of the given size. Allocates * log 2 (|domainSize|) BDD variables for the domain.</p> */ public BDDDomain extDomain(long domainSize) { return extDomain(BigInteger.valueOf(domainSize)); } public BDDDomain extDomain(BigInteger domainSize) { return extDomain(new BigInteger[] { domainSize })[0]; } /** * <p>Extends the set of finite domain blocks with domains of the given sizes. * Each entry in domainSizes is the size of a new finite domain which later * on can be used for finite state machine traversal and other operations on * finite domains. Each domain allocates log 2 (|domainSizes[i]|) BDD * variables to be used later. The ordering is interleaved for the domains * defined in each call to extDomain. This means that assuming domain D0 * needs 2 BDD variables x1 and x2 , and another domain D1 needs 4 BDD * variables y1, y2, y3 and y4, then the order then will be x1, y1, x2, y2, * y3, y4. The new domains are returned in order. The BDD variables needed * to encode the domain are created for the purpose and do not interfere * with the BDD variables already in use.</p> * * <p>Compare to fdd_extdomain.</p> */ public BDDDomain[] extDomain(int[] dom) { BigInteger[] a = new BigInteger[dom.length]; for (int i = 0; i < a.length; ++i) { a[i] = BigInteger.valueOf(dom[i]); } return extDomain(a); } public BDDDomain[] extDomain(long[] dom) { BigInteger[] a = new BigInteger[dom.length]; for (int i = 0; i < a.length; ++i) { a[i] = BigInteger.valueOf(dom[i]); } return extDomain(a); } public BDDDomain[] extDomain(BigInteger[] domainSizes) { int offset = fdvarnum; int binoffset; int extravars = 0; int n, bn; boolean more; int num = domainSizes.length; /* Build domain table */ if (domain == null) /* First time */ { domain = new BDDDomain[num]; } else /* Allocated before */ { if (fdvarnum + num > domain.length) { int fdvaralloc = domain.length + Math.max(num, domain.length); BDDDomain[] d2 = new BDDDomain[fdvaralloc]; System.arraycopy(domain, 0, d2, 0, domain.length); domain = d2; } } /* Create bdd variable tables */ for (n = 0; n < num; n++) { domain[n + fdvarnum] = createDomain(n + fdvarnum, domainSizes[n]); extravars += domain[n + fdvarnum].varNum(); } binoffset = firstbddvar; int bddvarnum = varNum(); if (firstbddvar + extravars > bddvarnum) { setVarNum(firstbddvar + extravars); } /* Set correct variable sequence (interleaved) */ for (bn = 0, more = true; more; bn++) { more = false; for (n = 0; n < num; n++) { if (bn < domain[n + fdvarnum].varNum()) { more = true; domain[n + fdvarnum].ivar[bn] = binoffset++; } } } for (n = 0; n < num; n++) { domain[n + fdvarnum].var = makeSet(domain[n + fdvarnum].ivar); } fdvarnum += num; firstbddvar += extravars; BDDDomain[] r = new BDDDomain[num]; System.arraycopy(domain, offset, r, 0, num); return r; } /** * <p>This function takes two finite domain blocks and merges them * into a new one, such that the new one is encoded using both sets * of BDD variables.</p> * * <p>Compare to fdd_overlapdomain.</p> */ public BDDDomain overlapDomain(BDDDomain d1, BDDDomain d2) { BDDDomain d; int n; int fdvaralloc = domain.length; if (fdvarnum + 1 > fdvaralloc) { fdvaralloc += fdvaralloc; BDDDomain[] domain2 = new BDDDomain[fdvaralloc]; System.arraycopy(domain, 0, domain2, 0, domain.length); domain = domain2; } d = domain[fdvarnum]; d.realsize = d1.realsize.multiply(d2.realsize); d.ivar = new int[d1.varNum() + d2.varNum()]; for (n = 0; n < d1.varNum(); n++) d.ivar[n] = d1.ivar[n]; for (n = 0; n < d2.varNum(); n++) d.ivar[d1.varNum() + n] = d2.ivar[n]; d.var = makeSet(d.ivar); //bdd_addref(d.var); fdvarnum++; return d; } /** * <p>Returns a BDD defining all the variable sets used to define the variable * blocks in the given array.</p> * * <p>Compare to fdd_makeset.</p> */ public BDD makeSet(BDDDomain[] v) { BDD res = one(); int n; for (n = 0; n < v.length; n++) { res.andWith(v[n].set()); } return res; } /** * <p>Clear all allocated finite domain blocks that were defined by extDomain() * or overlapDomain().</p> * * <p>Compare to fdd_clearall.</p> */ public void clearAllDomains() { domain = null; fdvarnum = 0; firstbddvar = 0; } /** * <p>Returns the number of finite domain blocks defined by calls to * extDomain().</p> * * <p>Compare to fdd_domainnum.</p> */ public int numberOfDomains() { return fdvarnum; } /** * <p>Returns the ith finite domain block, as defined by calls to * extDomain().</p> */ public BDDDomain getDomain(int i) { if (i < 0 || i >= fdvarnum) throw new IndexOutOfBoundsException(); return domain[i]; } // TODO: fdd_file_hook, fdd_strm_hook /** * <p>Creates a variable ordering from a string. The resulting order * can be passed into <tt>setVarOrder()</tt>. Example: in the order * "A_BxC_DxExF", the bits for A are first, followed by the bits for * B and C interleaved, followed by the bits for D, E, and F * interleaved.</p> * * <p>Obviously, domain names cannot contain the 'x' or '_' * characters.</p> * * @param reverseLocal whether to reverse the bits of each domain * @param ordering string representation of ordering * @return int[] of ordering * @see net.sf.javabdd.BDDFactory#setVarOrder(int[]) */ public int[] makeVarOrdering(boolean reverseLocal, String ordering) { int varnum = varNum(); int nDomains = numberOfDomains(); int[][] localOrders = new int[nDomains][]; for (int i=0; i<localOrders.length; ++i) { localOrders[i] = new int[getDomain(i).varNum()]; } for (int i=0; i<nDomains; ++i) { BDDDomain d = getDomain(i); int nVars = d.varNum(); for (int j=0; j<nVars; ++j) { if (reverseLocal) { localOrders[i][j] = nVars - j - 1; } else { localOrders[i][j] = j; } } } BDDDomain[] doms = new BDDDomain[nDomains]; int[] varorder = new int[varnum]; //System.out.println("Ordering: "+ordering); StringTokenizer st = new StringTokenizer(ordering, "x_", true); int numberOfDomains = 0, bitIndex = 0; boolean[] done = new boolean[nDomains]; for (int i=0; ; ++i) { String s = st.nextToken(); BDDDomain d; for (int j=0; ; ++j) { if (j == numberOfDomains()) throw new BDDException("bad domain: "+s); d = getDomain(j); if (s.equals(d.getName())) break; } if (done[d.getIndex()]) throw new BDDException("duplicate domain: "+s); done[d.getIndex()] = true; doms[i] = d; if (st.hasMoreTokens()) { s = st.nextToken(); if (s.equals("x")) { ++numberOfDomains; continue; } } bitIndex = fillInVarIndices(doms, i-numberOfDomains, numberOfDomains+1, localOrders, bitIndex, varorder); if (!st.hasMoreTokens()) { break; } if (s.equals("_")) numberOfDomains = 0; else throw new BDDException("bad token: "+s); } for (int i=0; i<doms.length; ++i) { if (!done[i]) { throw new BDDException("missing domain #"+i+": "+getDomain(i)); } doms[i] = getDomain(i); } int[] test = new int[varorder.length]; System.arraycopy(varorder, 0, test, 0, varorder.length); Arrays.sort(test); for (int i=0; i<test.length; ++i) { if (test[i] != i) throw new BDDException(test[i]+" != "+i); } return varorder; } /** * Helper function for makeVarOrder(). */ static int fillInVarIndices( BDDDomain[] doms, int domainIndex, int numDomains, int[][] localOrders, int bitIndex, int[] varorder) { // calculate size of largest domain to interleave int maxBits = 0; for (int i=0; i<numDomains; ++i) { BDDDomain d = doms[domainIndex+i]; maxBits = Math.max(maxBits, d.varNum()); } // interleave the domains for (int bitNumber=0; bitNumber<maxBits; ++bitNumber) { for (int i=0; i<numDomains; ++i) { BDDDomain d = doms[domainIndex+i]; if (bitNumber < d.varNum()) { int di = d.getIndex(); int local = localOrders[di][bitNumber]; if (local >= d.vars().length) { System.out.println("bug!"); } if (bitIndex >= varorder.length) { System.out.println("bug2!"); } varorder[bitIndex++] = d.vars()[local]; } } } return bitIndex; } /**** BIT VECTORS ****/ /** * <p>Implementors must implement this factory method to create BDDBitVector * objects of the correct type.</p> */ protected abstract BDDBitVector createBitVector(int a); /** * <p>Build a bit vector that is constant true or constant false.</p> * * <p>Compare to bvec_true, bvec_false.</p> */ public BDDBitVector buildVector(int bitnum, boolean b) { BDDBitVector v = createBitVector(bitnum); v.initialize(b); return v; } /** * <p>Build a bit vector that corresponds to a constant value.</p> * * <p>Compare to bvec_con.</p> */ public BDDBitVector constantVector(int bitnum, long val) { BDDBitVector v = createBitVector(bitnum); v.initialize(val); return v; } public BDDBitVector constantVector(int bitnum, BigInteger val) { BDDBitVector v = createBitVector(bitnum); v.initialize(val); return v; } /** * <p>Build a bit vector using variables offset, offset+step, * offset+2*step, ... , offset+(bitnum-1)*step.</p> * * <p>Compare to bvec_var.</p> */ public BDDBitVector buildVector(int bitnum, int offset, int step) { BDDBitVector v = createBitVector(bitnum); v.initialize(offset, step); return v; } /** * <p>Build a bit vector using variables from the given BDD domain.</p> * * <p>Compare to bvec_varfdd.</p> */ public BDDBitVector buildVector(BDDDomain d) { BDDBitVector v = createBitVector(d.varNum()); v.initialize(d); return v; } /** * <p>Build a bit vector using the given variables.</p> * * <p>compare to bvec_varvec.</p> */ public BDDBitVector buildVector(int[] var) { BDDBitVector v = createBitVector(var.length); v.initialize(var); return v; } /**** CALLBACKS ****/ protected List gc_callbacks, reorder_callbacks, resize_callbacks; /** * <p>Register a callback that is called when garbage collection is about * to occur.</p> * * @param o base object * @param m method */ public void registerGCCallback(Object o, Method m) { if (gc_callbacks == null) gc_callbacks = new LinkedList(); registerCallback(gc_callbacks, o, m); } /** * <p>Unregister a garbage collection callback that was previously * registered.</p> * * @param o base object * @param m method */ public void unregisterGCCallback(Object o, Method m) { if (gc_callbacks == null) throw new BDDException(); if (!unregisterCallback(gc_callbacks, o, m)) throw new BDDException(); } /** * <p>Register a callback that is called when reordering is about * to occur.</p> * * @param o base object * @param m method */ public void registerReorderCallback(Object o, Method m) { if (reorder_callbacks == null) reorder_callbacks = new LinkedList(); registerCallback(reorder_callbacks, o, m); } /** * <p>Unregister a reorder callback that was previously * registered.</p> * * @param o base object * @param m method */ public void unregisterReorderCallback(Object o, Method m) { if (reorder_callbacks == null) throw new BDDException(); if (!unregisterCallback(reorder_callbacks, o, m)) throw new BDDException(); } /** * <p>Register a callback that is called when node table resizing is about * to occur.</p> * * @param o base object * @param m method */ public void registerResizeCallback(Object o, Method m) { if (resize_callbacks == null) resize_callbacks = new LinkedList(); registerCallback(resize_callbacks, o, m); } /** * <p>Unregister a reorder callback that was previously * registered.</p> * * @param o base object * @param m method */ public void unregisterResizeCallback(Object o, Method m) { if (resize_callbacks == null) throw new BDDException(); if (!unregisterCallback(resize_callbacks, o, m)) throw new BDDException(); } protected void gbc_handler(boolean pre, GCStats s) { if (gc_callbacks == null) { bdd_default_gbchandler(pre, s); } else { doCallbacks(gc_callbacks, new Integer(pre?1:0), s); } } protected static void bdd_default_gbchandler(boolean pre, GCStats s) { if (!pre) { System.err.println(s.toString()); } } void reorder_handler(boolean b, ReorderStats s) { if (b) { s.usednum_before = getNodeNum(); s.time = System.currentTimeMillis(); } else { s.time = System.currentTimeMillis() - s.time; s.usednum_after = getNodeNum(); } if (reorder_callbacks == null) { bdd_default_reohandler(b, s); } else { doCallbacks(reorder_callbacks, new Integer(b?1:0), s); } } protected void bdd_default_reohandler(boolean prestate, ReorderStats s) { int verbose = 1; if (verbose > 0) { if (prestate) { System.out.println("Start reordering"); s.usednum_before = getNodeNum(); s.time = System.currentTimeMillis(); } else { s.time = System.currentTimeMillis() - s.time; s.usednum_after = getNodeNum(); System.out.println("End reordering. "+s); } } } protected void resize_handler(int oldsize, int newsize) { if (resize_callbacks == null) { bdd_default_reshandler(oldsize, newsize); } else { doCallbacks(resize_callbacks, new Integer(oldsize), new Integer(newsize)); } } protected static void bdd_default_reshandler(int oldsize, int newsize) { int verbose = 1; if (verbose > 0) { System.out.println("Resizing node table from "+oldsize+" to "+newsize); } } protected void registerCallback(List callbacks, Object o, Method m) { if (!Modifier.isPublic(m.getModifiers()) && !m.isAccessible()) { throw new BDDException("Callback method not accessible"); } if (!Modifier.isStatic(m.getModifiers())) { if (o == null) { throw new BDDException("Base object for callback method is null"); } if (!m.getDeclaringClass().isAssignableFrom(o.getClass())) { throw new BDDException("Base object for callback method is the wrong type"); } } if (false) { Class[] params = m.getParameterTypes(); if (params.length != 1 || params[0] != int.class) { throw new BDDException("Wrong signature for callback"); } } callbacks.add(new Object[] { o, m }); } protected boolean unregisterCallback(List callbacks, Object o, Method m) { if (callbacks != null) { for (Iterator i = callbacks.iterator(); i.hasNext(); ) { Object[] cb = (Object[]) i.next(); if (o == cb[0] && m.equals(cb[1])) { i.remove(); return true; } } } return false; } protected void doCallbacks(List callbacks, Object arg1, Object arg2) { if (callbacks != null) { for (Iterator i = callbacks.iterator(); i.hasNext(); ) { Object[] cb = (Object[]) i.next(); Object o = cb[0]; Method m = (Method) cb[1]; try { switch (m.getParameterTypes().length) { case 0: m.invoke(o, new Object[] { } ); break; case 1: m.invoke(o, new Object[] { arg1 } ); break; case 2: m.invoke(o, new Object[] { arg1, arg2 } ); break; default: throw new BDDException("Wrong number of arguments for "+m); } } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { if (e.getTargetException() instanceof RuntimeException) throw (RuntimeException) e.getTargetException(); if (e.getTargetException() instanceof Error) throw (Error) e.getTargetException(); e.printStackTrace(); } } } } }