def to_expr(self, u): if u == 1: return u elif u == -1: return 0 t = self._succ[abs(u)] i = t[0] nodes = t[1:] var = self.var_at_level(i) # group per target node c = tuple(set(nodes)) e = {x: self.to_expr(x) for x in c} cond = {v: set() for v in c} for j, x in enumerate(nodes): cond[x].add(j) # format cond_str = dict() for k, v in items(cond): if len(v) == 1: (j, ) = v cond_str[k] = '= {j}'.format(j=j) else: cond_str[k] = 'in {v}'.format(v=v) x = c[0] s = 'if ({var} {j}): {p}, '.format(var=var, j=cond_str[x], p=e[x]) s += ', '.join( '\nelif ({var} {j}): {p}'.format(var=var, j=cond_str[x], p=e[x]) for x in c[1:]) if u < 0: s = '! {s}'.format(s=s) s = '({s})'.format(s=s) return s
def to_expr(self, u): if u == 1: return u elif u == -1: return 0 t = self._succ[abs(u)] i = t[0] nodes = t[1:] var = self.var_at_level(i) # group per target node c = tuple(set(nodes)) e = {x: self.to_expr(x) for x in c} cond = {v: set() for v in c} for j, x in enumerate(nodes): cond[x].add(j) # format cond_str = dict() for k, v in items(cond): if len(v) == 1: (j,) = v cond_str[k] = '= {j}'.format(j=j) else: cond_str[k] = 'in {v}'.format(v=v) x = c[0] s = 'if ({var} {j}): {p}, '.format( var=var, j=cond_str[x], p=e[x]) s += ', '.join( '\nelif ({var} {j}): {p}'.format( var=var, j=cond_str[x], p=e[x]) for x in c[1:]) if u < 0: s = '! {s}'.format(s=s) s = '({s})'.format(s=s) return s
def var_at_level(self, i): """Return variable with level `i`.""" if self._level_to_var is None: self._level_to_var = { d['level']: var for var, d in items(self.vars)} return self._level_to_var[i]
def var_at_level(self, i): """Return variable with level `i`.""" if self._level_to_var is None: self._level_to_var = { d['level']: var for var, d in items(self.vars) } return self._level_to_var[i]
def to_pydot(mdd): import pydot g = pydot.Dot('mdd', graph_type='digraph') skeleton = list() subgraphs = dict() n = len(mdd.vars) + 1 for i in xrange(n): h = pydot.Subgraph('', rank='same') g.add_subgraph(h) subgraphs[i] = h # add phantom node u = '-{i}'.format(i=i) skeleton.append(u) nd = pydot.Node(name=u, label=str(i), shape='none') h.add_node(nd) # auxiliary edges for ranking for i, u in enumerate(skeleton[:-1]): v = skeleton[i + 1] e = pydot.Edge(str(u), str(v), style='invis') g.add_edge(e) # add nodes for u, t in items(mdd._succ): assert u > 0, u i = t[0] nodes = t[1:] # terminal ? if nodes[0] is None: var = '1' else: var = mdd.var_at_level(i) # add node label = '{var}-{u}'.format(var=var, u=u) nd = pydot.Node(name=str(u), label=label) h = subgraphs[i] # level i h.add_node(nd) # add edges if nodes[0] is None: continue # has successors for j, v in enumerate(nodes): label = str(j) # tail_label = '-1' if v < 0 else ' ' if v < 0: style = 'dashed' else: style = 'solid' su = str(u) sv = str(abs(v)) e = pydot.Edge(su, sv, label=label, style=style) g.add_edge(e) return g
def load(fname): """Return a `BDD` loaded from DDDMP file `fname`. If no `.orderedvarnames` appear in the file, then `.suppvarnames` and `.permids` are used instead. In the second case, the variable levels contains blanks. To avoid blanks, the levels are re-indexed here. This has no effect if `.orderedvarnames` appears in the file. DDDMP files are dumped by [CUDD](http://vlsi.colorado.edu/~fabio/CUDD/). """ parser = Parser() bdd_succ, n_vars, levels, roots = parser.parse(fname) # reindex to ensure no blanks perm = {k: var for var, k in items(levels)} perm = {i: perm[k] for i, k in enumerate(sorted(perm))} new_levels = {var: k for k, var in items(perm)} old2new = {levels[var]: new_levels[var] for var in levels} # convert bdd = BDD(new_levels) umap = {-1: -1, 1: 1} for j in range(len(new_levels) - 1, -1, -1): for u, (k, v, w) in items(bdd_succ): # terminal ? if v is None: assert w is None, w continue # non-terminal i = old2new[k] if i != j: continue p, q = umap[abs(v)], umap[w] if v < 0: p = -p r = bdd.find_or_add(i, p, q) umap[abs(u)] = r bdd.roots.update(roots) return bdd
def load(fname): """Return a `BDD` loaded from DDDMP file `fname`. If no `.orderedvarnames` appear in the file, then `.suppvarnames` and `.permids` are used instead. In the second case, the variable levels contains blanks. To avoid blanks, the levels are re-indexed here. This has no effect if `.orderedvarnames` appears in the file. DDDMP files are dumped by [CUDD](http://vlsi.colorado.edu/~fabio/CUDD/). """ parser = Parser() bdd_succ, n_vars, ordering, roots = parser.parse(fname) # reindex to ensure no blanks perm = {k: var for var, k in items(ordering)} perm = {i: perm[k] for i, k in enumerate(sorted(perm))} new_ordering = {var: k for k, var in items(perm)} old2new = {ordering[var]: new_ordering[var] for var in ordering} # convert bdd = BDD(new_ordering) umap = {-1: -1, 1: 1} for j in range(len(new_ordering) - 1, -1, -1): for u, (k, v, w) in items(bdd_succ): # terminal ? if v is None: assert w is None, w continue # non-terminal i = old2new[k] if i != j: continue p, q = umap[abs(v)], umap[w] if v < 0: p = -p r = bdd.find_or_add(i, p, q) umap[abs(u)] = r bdd.roots.update(roots) return bdd
def to_pydot(mdd): g = pydot.Dot('mdd', graph_type='digraph') skeleton = list() subgraphs = dict() n = len(mdd.vars) + 1 for i in xrange(n): h = pydot.Subgraph('', rank='same') g.add_subgraph(h) subgraphs[i] = h # add phantom node u = '-{i}'.format(i=i) skeleton.append(u) nd = pydot.Node(name=u, label=str(i), shape='none') h.add_node(nd) # auxiliary edges for ranking for i, u in enumerate(skeleton[:-1]): v = skeleton[i + 1] e = pydot.Edge(str(u), str(v), style='invis') g.add_edge(e) # add nodes for u, t in items(mdd._succ): assert u > 0, u i = t[0] nodes = t[1:] # terminal ? if nodes[0] is None: var = '1' else: var = mdd.var_at_level(i) # add node label = '{var}-{u}'.format(var=var, u=u) nd = pydot.Node(name=str(u), label=label) h = subgraphs[i] # level i h.add_node(nd) # add edges if nodes[0] is None: continue # has successors for j, v in enumerate(nodes): label = str(j) # tail_label = '-1' if v < 0 else ' ' if v < 0: style = 'dashed' else: style = 'solid' su = str(u) sv = str(abs(v)) e = pydot.Edge(su, sv, label=label, style=style) g.add_edge(e) return g
class Lexer(object): """Token rules to build LTL lexer.""" reserved = { 'ver': 'VERSION', 'add': 'ADD', 'mode': 'FILEMODE', 'varinfo': 'VARINFO', 'dd': 'DD', 'nnodes': 'NNODES', 'nvars': 'NVARS', 'orderedvarnames': 'ORDEREDVARNAMES', 'nsuppvars': 'NSUPPVARS', 'suppvarnames': 'SUPPVARNAMES', 'ids': 'IDS', 'permids': 'PERMIDS', 'auxids': 'AUXIDS', 'nroots': 'NROOTS', 'rootids': 'ROOTIDS', 'rootnames': 'ROOTNAMES', # 'nodes': 'NODES', # 'end': 'END' } reserved = {'.{k}'.format(k=k): v for k, v in items(reserved)} misc = ['MINUS', 'DOT', 'NAME', 'NUMBER'] # token rules t_MINUS = r'-' t_DOT = r'\.' t_NUMBER = r'\d+' t_ignore = ' \t' def __init__(self, debug=False): self.tokens = self.misc + list(self.reserved.values()) self.build(debug=debug) def t_KEYWORD(self, t): r"\.[a-zA-Z][a-zA-Z]*" t.type = self.reserved.get(t.value, 'NAME') return t def t_NAME(self, t): r"[a-zA-Z_][a-zA-Z_@0-9\'\.]*" t.type = self.reserved.get(t.value, 'NAME') return t def t_comment(self, t): r'\#.*' return def t_newline(self, t): r'\n+' t.lexer.lineno += t.value.count("\n") def t_error(self, t): raise Exception('Illegal character "{t}"'.format(t=t.value[0])) def build(self, debug=False, debuglog=None, **kwargs): """Create a lexer. @param kwargs: Same arguments as C{{ply.lex.lex}}: - except for C{{module}} (fixed to C{{self}}) - C{{debuglog}} defaults to the logger C{{"{logger}"}}. """ if debug and debuglog is None: debuglog = logging.getLogger(LEX_LOG) self.lexer = ply.lex.lex(module=self, debug=debug, debuglog=debuglog, **kwargs)
def bdd_to_mdd(bdd, dvars): """Return MDD for given BDD. Caution: collects garbage. `dvars` must map each MDD variable to the corresponding bits in BDD. Also, it should give the order as "level" keys. """ # i = level in BDD # j = level in MDD # bit = BDD variable # var = MDD variable # # map from bits to integers bit_to_var = dict() for var, d in items(dvars): bits = d['bitnames'] b = {bit: var for bit in bits} bit_to_var.update(b) # find target bit order order = list() # target levels = {d['level']: var for var, d in items(dvars)} m = len(levels) for j in xrange(m): var = levels[j] bits = dvars[var]['bitnames'] order.extend(bits) bit_to_sort = {bit: k for k, bit in enumerate(order)} # reorder bdd.collect_garbage() _bdd.reorder(bdd, order=bit_to_sort) # BDD -> MDD mdd = MDD(dvars) # zones of bits per integer var zones = dict() for var, d in items(dvars): bits = d['bitnames'] lsb = bits[0] msb = bits[-1] min_level = bit_to_sort[lsb] max_level = bit_to_sort[msb] zones[var] = (min_level, max_level) # reverse edges pred = {u: set() for u in bdd} for u, (_, v, w) in items(bdd._succ): assert u > 0, u # terminal ? if u == 1: continue # non-terminal pred[abs(v)].add(u) pred[abs(w)].add(u) # find BDD nodes mentioned from above rm = set() for u, p in items(pred): rc = bdd.ref(u) k = len(p) # number of predecessors # has external refs ? if rc > k: continue # has refs from outside zone ? i, _, _ = bdd._succ[u] bit = bdd.var_at_level(i) var = bit_to_var[bit] min_level, _ = zones[var] pred_levels = {bdd._succ[v][0] for v in p} min_pred_level = min(pred_levels) if min_pred_level < min_level: continue # referenced only from inside zone rm.add(u) pred = {u: p for u, p in items(pred) if u not in rm} # build layer by layer # TODO: use bins, instad of iterating through all nodes bdd.assert_consistent() g = to_nx(bdd, roots=[u]) for u in pred: g.add_node(u, color='red') for u in g: if u == 1: continue i, _, _ = bdd._succ[u] var = bdd.var_at_level(i) label = '{var}-{u}'.format(var=var, u=u) g.add_node(u, label=label) # bdd.dump('bdd.pdf') umap = dict() umap[1] = 1 for u, i, v, w in bdd.levels(skip_terminals=True): # ignore function ? if u not in pred: continue # keep `u` bit = bdd.var_at_level(i) var = bit_to_var[bit] bits = dvars[var]['bitnames'] bit_succ = list() for d in _enumerate_integer(bits): x = bdd.cofactor(u, d) bit_succ.append(x) # map edges int_succ = [umap[abs(z)] if z > 0 else -umap[abs(z)] for z in bit_succ] # add new MDD node at level j j = dvars[var]['level'] r = mdd.find_or_add(j, *int_succ) # cache # signed r, because low never inverted, # opposite to canonicity chosen for BDDs umap[u] = r return mdd, umap
def bdd_to_mdd(bdd, dvars): """Return MDD for given BDD. Caution: collects garbage. `dvars` must map each MDD variable to the corresponding bits in BDD. Also, it should give the order as "level" keys. """ # i = level in BDD # j = level in MDD # bit = BDD variable # var = MDD variable # # map from bits to integers bit_to_var = dict() for var, d in items(dvars): bits = d['bitnames'] b = {bit: var for bit in bits} bit_to_var.update(b) # find target bit ordering ordering = list() # target levels = {d['level']: var for var, d in items(dvars)} m = len(levels) for j in xrange(m): var = levels[j] bits = dvars[var]['bitnames'] ordering.extend(bits) bit_to_sort = {bit: k for k, bit in enumerate(ordering)} # reorder bdd.collect_garbage() _bdd.reorder(bdd, order=bit_to_sort) # BDD -> MDD mdd = MDD(dvars) # zones of bits per integer var zones = dict() for var, d in items(dvars): bits = d['bitnames'] lsb = bits[0] msb = bits[-1] min_level = bit_to_sort[lsb] max_level = bit_to_sort[msb] zones[var] = (min_level, max_level) # reverse edges pred = {u: set() for u in bdd} for u, (_, v, w) in items(bdd._succ): assert u > 0, u # terminal ? if u == 1: continue # non-terminal pred[abs(v)].add(u) pred[abs(w)].add(u) # find BDD nodes mentioned from above rm = set() for u, p in items(pred): rc = bdd.ref(u) k = len(p) # number of predecessors # has external refs ? if rc > k: continue # has refs from outside zone ? i, _, _ = bdd._succ[u] bit = bdd.var_at_level(i) var = bit_to_var[bit] min_level, _ = zones[var] pred_levels = {bdd._succ[v][0] for v in p} min_pred_level = min(pred_levels) if min_pred_level < min_level: continue # referenced only from inside zone rm.add(u) pred = {u: p for u, p in items(pred) if u not in rm} # build layer by layer # TODO: use bins, instad of iterating through all nodes bdd.assert_consistent() g = to_nx(bdd, roots=[u]) for u in pred: g.add_node(u, color='red') for u in g: if u == 1: continue i, _, _ = bdd._succ[u] var = bdd.var_at_level(i) label = '{var}-{u}'.format(var=var, u=u) g.add_node(u, label=label) # bdd.dump('bdd.pdf') umap = dict() umap[1] = 1 for u, i, v, w in bdd.levels(skip_terminals=True): # ignore function ? if u not in pred: continue # keep `u` bit = bdd.var_at_level(i) var = bit_to_var[bit] bits = dvars[var]['bitnames'] bit_succ = list() for d in _enumerate_integer(bits): x = bdd.cofactor(u, d) bit_succ.append(x) # map edges int_succ = [umap[abs(z)] if z > 0 else -umap[abs(z)] for z in bit_succ] # add new MDD node at level j j = dvars[var]['level'] r = mdd.find_or_add(j, *int_succ) # cache # signed r, because low never inverted, # opposite to canonicity chosen for BDDs umap[u] = r return mdd, umap