def test_isinstance_str(): s = 's' r = stx.isinstance_str(s) assert r is True, r s = 0 r = stx.isinstance_str(s) assert r is False, r
def add_expr(self, e, with_ops=False): """Add first-order predicate. A predicate is a Boolean-valued formula. """ assert stx.isinstance_str(e), e # optional because current implementation is slow if with_ops: defs = self.op else: defs = None s = bv.bitblast(e, vrs=self.vars, defs=defs) assert stx.isinstance_str(s), s # was `e` a predicate ? return sym_bdd.add_expr(s, self.bdd)
def _flatten_var(v, *arg, **kw): """Return `list` of bits, for both integer and Boolean var.""" flat = v.flatten(*arg, **kw) # bool ? if stx.isinstance_str(flat): flat = [flat] bits = _filter_trailing_zeros(flat) return bits
def define(self, e): """Register operator definitions. The string `e` must contain definitions. Example: ```python e = ''' a == x + y > 3 b == z - x <= 0 c == a /\ b ''' ``` In the future, this method may merge with `add_expr`. """ assert stx.isinstance_str(e), e bv_defs = bv._parser.parse(e) defs = _parser.parse(e) for opdef, bv_opdef in zip(defs, bv_defs): assert opdef.operator == '==', opdef name_ast, expr_ast = opdef.operands _, bv_ast = bv_opdef.operands name = name_ast.value if name in self.vars: raise ValueError(( 'Attempted to define operator "{name}", ' 'but "{name}" already declared as variable: ' '{old}').format( name=name, old=self.vars[name])) if name in self.op: raise ValueError(( 'Attempted to redefine operator "{name}". ' 'Previous definition as: "{old}"').format( name=name, old=self.op[name])) s = bv_ast.flatten( t=self.vars, defs=self.op_bdd) assert stx.isinstance_str(s), s # sensitive point: # operator expressions are stored before substitutions # operator BDDs are stored after operator substitutions # operator definitions cannot change, so this should # not cause problems as currently arranged. self.op[name] = expr_ast.flatten() self.op_bdd[name] = sym_bdd.add_expr(s, self.bdd)
def define(self, e): """Register operator definitions. The string `e` must contain definitions. Example: ```python e = ''' a == x + y > 3 b == z - x <= 0 c == a /\ b ''' ``` In the future, this method may merge with `add_expr`. """ assert stx.isinstance_str(e), e bv_defs = bv._parser.parse(e) defs = _parser.parse(e) for opdef, bv_opdef in zip(defs, bv_defs): assert opdef.operator == '==', opdef name_ast, expr_ast = opdef.operands _, bv_ast = bv_opdef.operands name = name_ast.value if name in self.vars: raise ValueError(('Attempted to define operator "{name}", ' 'but "{name}" already declared as variable: ' '{old}').format(name=name, old=self.vars[name])) if name in self.op: raise ValueError(('Attempted to redefine operator "{name}". ' 'Previous definition as: "{old}"').format( name=name, old=self.op[name])) s = bv_ast.flatten(t=self.vars, defs=self.op_bdd) assert stx.isinstance_str(s), s # sensitive point: # operator expressions are stored before substitutions # operator BDDs are stored after operator substitutions # operator definitions cannot change, so this should # not cause problems as currently arranged. self.op[name] = expr_ast.flatten() self.op_bdd[name] = sym_bdd.add_expr(s, self.bdd)
def replace(self, u, vars_to_new): """Return substitution of var names by values or vars. @param vars_to_new: `dict` that maps each var name to a var (as `str`), or to a value (as `bool` or `int`). """ if len(vars_to_new) == 0: # must be mapping, not `None` return u assert vars_to_new, vars_to_new for k in vars_to_new: rename = stx.isinstance_str(vars_to_new[k]) break if rename: d = _refine_renaming(vars_to_new, self.vars) else: d = _refine_assignment(vars_to_new, self.vars) return self.bdd.let(d, u)
def split_gr1_old(f): """Earlier implementation of `split_gr1`. Does not preserve formula structure. Instead, it arranges conjunction operators as a binary tree. """ # TODO: preprocess by applying syntactic identities: [][] = [] etc if stx.isinstance_str(f): t = f else: t = parser.parse(f) g = tx.Tree.from_recursive_ast(t) # collect boundary of conjunction operators Q = [g.root] b = list() # use lists to preserve as much given syntactic order while Q: u = Q.pop() # terminal ? if not g.succ.get(u): b.append(u) continue # operator if u.operator == '/\\': # use `u.operands` instead of `g.successors` # to preserve original order Q.extend(u.operands) else: b.append(u) d = dict(init=list(), G=list(), GF=list()) for u in b: # terminal ? if not g.succ.get(u): d['init'].append(u) continue # some operator if u.operator != '[]': d['init'].append(u) continue # G (v,) = u.operands # terminal in G ? if not g.succ.get(v): d['[]'].append(v) continue # some operator in G if v.operator == '<>': (w,) = v.operands d['[]<>'].append(w) else: # not a GF d['[]'].append(v) # assert only admissible temporal operators ops = {'[]', '<>', 'U', 'V', 'R'} operators = dict(G=ops) ops = set(ops) ops.add('X') operators.update(init=ops, GF=ops) for part, f in d.items(): ops = operators[part] for u in f: op = has_operator(u, g, ops) if op is None: continue raise AssertionError(( 'found inadmissible operator "{op}" ' 'in "{f}" formula. Parts:\n {d}').format( op=op, f=part, d=d)) # conjoin (except for progress) init = conj(u.flatten() for u in reversed(d['init'])) d['init'] = [init] safe = conj(u.flatten() for u in reversed(d['[]'])) d['[]'] = [safe] # flatten individual progress formulas d['[]<>'] = [u.flatten() for u in d['[]<>']] return d
def split_gr1_old(f): """Earlier implementation of `split_gr1`. Does not preserve formula structure. Instead, it arranges conjunction operators as a binary tree. """ # TODO: preprocess by applying syntactic identities: [][] = [] etc if stx.isinstance_str(f): t = f else: t = parser.parse(f) g = tx.Tree.from_recursive_ast(t) # collect boundary of conjunction operators Q = [g.root] b = list() # use lists to preserve as much given syntactic order while Q: u = Q.pop() # terminal ? if not g.succ.get(u): b.append(u) continue # operator if u.operator == '/\\': # use `u.operands` instead of `g.successors` # to preserve original order Q.extend(u.operands) else: b.append(u) d = dict(init=list(), G=list(), GF=list()) for u in b: # terminal ? if not g.succ.get(u): d['init'].append(u) continue # some operator if u.operator != '[]': d['init'].append(u) continue # G (v, ) = u.operands # terminal in G ? if not g.succ.get(v): d['[]'].append(v) continue # some operator in G if v.operator == '<>': (w, ) = v.operands d['[]<>'].append(w) else: # not a GF d['[]'].append(v) # assert only admissible temporal operators ops = {'[]', '<>', 'U', 'V', 'R'} operators = dict(G=ops) ops = set(ops) ops.add('X') operators.update(init=ops, GF=ops) for part, f in d.items(): ops = operators[part] for u in f: op = has_operator(u, g, ops) if op is None: continue raise AssertionError( ('found inadmissible operator "{op}" ' 'in "{f}" formula. Parts:\n {d}').format(op=op, f=part, d=d)) # conjoin (except for progress) init = conj(u.flatten() for u in reversed(d['init'])) d['init'] = [init] safe = conj(u.flatten() for u in reversed(d['[]'])) d['[]'] = [safe] # flatten individual progress formulas d['[]<>'] = [u.flatten() for u in d['[]<>']] return d