def _attach_datatypes(self): self.Node, self.node_values = z3.EnumSort("Node", self.node_names) self.nodes = dict(zip(self.node_names, self.node_values)) # A datatype for storing a pair of edges self.Edge = z3.Datatype('Edge') self.Edge.declare('edge', ('node1', self.Node), ('node2', self.Node)) self.Edge = self.Edge.create() self.Nodeset = z3.ArraySort(self.Node, z3.BoolSort()) self.Edgeset = z3.ArraySort(self.Edge, z3.BoolSort()) self.Labelset = z3.ArraySort(z3.IntSort(), z3.BoolSort()) self.Labelmap = z3.ArraySort(self.Node, self.Labelset) # Graph, before a rule or action has applied. Merged Pregraph and Postgraph # into a single datatype. self.Graph = z3.Datatype('Graph') self.Graph.declare('graph', ('has', self.Nodeset), ('links', self.Edgeset), ('parents', self.Edgeset), ('labelmap', self.Labelmap)) self.Graph = self.Graph.create() # This represents a set of possible <pregraph, postgraph> pairs. self.Rule = z3.Datatype('Rule') self.Rule.declare('rule', ('pregraph', self.Graph), ('postgraph', self.Graph)) self.Rule = self.Rule.create()
def z3_sort(self, typeexpr): ## Check for a base type before reduction, for types ## like gmap that we want to avoid unfolding in reduce. base_lam = self.base_types.get(typeexpr['name']) if base_lam is not None: return base_lam(typeexpr['args']) typeexpr = self.context.reduce(typeexpr) ## Check again for a base type, for types like nat, where ## we may have had an alias (like uint32) before. base_lam = self.base_types.get(typeexpr['name']) if base_lam is not None: return base_lam(typeexpr['args']) z3name = typeexpr.get('name_with_args', typeexpr['name']) datatype = z3.Datatype(str(z3name)) for c in typeexpr['constructors']: cname = str(c['name']) datatype.declare( cname, *[(cname + '_%d' % idx, self.z3_sort(argtype)) for (idx, argtype) in enumerate(c['argtypes'])]) t = datatype.create() return t
def initialize(self, ctx): # clear the flat names self.flat_names = [] flat_args = [] idx = 0 for z3_arg_name, z3_arg_type in self.members: z3_arg_type = ctx.resolve_type(z3_arg_type) if isinstance(z3_arg_type, P4ComplexType): member_cls = z3_arg_type.instantiate(f"{self.name}.{idx}") propagate_validity_bit(member_cls) for sub_member in z3_arg_type.flat_names: flat_args.append((str(idx), sub_member.p4_type)) self.flat_names.append( P4Member(z3_arg_name, sub_member.name)) idx += 1 # this is a complex datatype, create a P4ComplexType ctx.set_or_add_var(z3_arg_name, member_cls, True) else: flat_args.append((str(idx), z3_arg_type)) self.flat_names.append(z3_arg_name) idx += 1 z3_type = z3.Datatype(self.name) z3_type.declare(f"mk_{self.name}", *flat_args) self.z3_type = z3_type.create() self.const = z3.Const(self.name, self.z3_type) for type_idx, arg_name in enumerate(self.flat_names): member_constructor = self.z3_type.accessor(0, type_idx) ctx.set_or_add_var(arg_name, member_constructor(self.const), True)
def solve(grid): """ . . . .1.1. . .3. """ s = z3.Solver() # Construct atoms to represent the dots Dot = z3.Datatype("Dot") for d in grid.dots(): Dot.declare("dot_{}".format(d)) Dot = Dot.create() dot_atom = {d: getattr(Dot, "dot_{}".format(d)) for d in grid.dots()} # Booleans for each of the points dot = {d: z3.Bool("dot-{}".format(d)) for d in grid.dots()} # Booleans for each of the connectors line = {l: z3.Bool("line-{}".format(l)) for l in grid.lines()} # For each point: if it is on, it must have precisely two adjoining lines activated # If it's off, then there are zero adjoining lines activated bool_to_int = z3.Function("bool_to_int", z3.BoolSort(), z3.IntSort()) s.add(bool_to_int(True) == 1) s.add(bool_to_int(True) != 0) s.add(bool_to_int(False) == 0) s.add(bool_to_int(False) != 1) for d in grid.dots(): # Get all lines coming out of this dot ls = [line[l] for l in d.lines()] sm = z3.Sum(*(bool_to_int(l) for l in ls)) s.add(z3.Implies(dot[d], sm == 2)) s.add(z3.Implies(z3.Not(dot[d]), sm == 0)) # For each line: if it is activated, then the points at both ends are activated for l in line: d1, d2 = l.ends() s.add(z3.Implies(line[l], z3.And(dot[d1], dot[d2]))) # "Is connected to" relationship connected = z3.Function("connected", Dot, Dot, z3.BoolSort()) # For each line: # The Dot at each end is connected to the other iff the line is activated for d1 in grid.dots(): for d2 in grid.dots(): a = dot_atom[d1] b = dot_atom[d2] if (l := grid.line(d1, d2)) is None: # These dots are never connected s.add(connected(a, b) != True) else: s.add(z3.Implies(line[l], connected(a, b))) s.add(z3.Implies(z3.Not(line[l]), z3.Not(connected(a, b))))
def test_model_is_refreshed(self): """ After a push and a pop, the model should be updated. After adding a new assertion, the model should be updated. """ TestVariable = z3.Datatype('TestVariable') TestVariable.declare('test_variable', ('number', z3.IntSort())) TestVariable = TestVariable.create() v1 = z3.Const('v1', TestVariable) v2 = z3.Const('v2', TestVariable) with solver.context(): solver.add(TestVariable.number(v1) == 42) m1 = solver.model() self.assertIsNotNone(m1[v1]) self.assertIsNone(m1[v2]) solver.add(TestVariable.number(v2) == 3) m2 = solver.model() self.assertIsNotNone(m2[v1]) self.assertIsNotNone(m2[v2]) m3 = solver.model() self.assertIsNone(m3[v1]) self.assertIsNone(m3[v2])
def __init__(self, name, type_params, methods): self.name = name z3_type = z3.Datatype(name) z3_type.declare(f"mk_{name}") self.z3_type = z3_type.create() self.type_params = type_params self.type_ctx = {} # attach the methods self.locals = {} for method in methods: self.locals.setdefault(method.lval, []).append(method.rval)
def _mkTypes(self, nodes, addresses): # Nodes in a network self.node, self.node_list = z3.EnumSort('Node', nodes) self.node_list = map(DumbNode, self.node_list) nodes = zip(nodes, self.node_list) for ndn, ndv in nodes: setattr(self, ndn, ndv) # Addresses for this network self.address, self.address_list = z3.EnumSort('Address', addresses) addresses = zip(addresses, self.address_list) for adn, adv in addresses: setattr(self, adn, adv) # Type for packets, contains (some of these are currently represented as relations): # - src: Source address # - dest: Destination address # - origin: Node where the data originated. (Node) # - body: Packet contents. (Integer) # - seq: Sequence number for packets. (Integer) # - options: A representation for IP options. (Integer) # TODO: Some of these are out, some of these are in. packet = z3.Datatype('Packet') packet.declare('packet', \ ('src', self.address), \ ('dest', self.address), \ ('origin', self.node), \ ('orig_body', z3.IntSort()), \ ('body', z3.IntSort()), \ ('seq', z3.IntSort()), \ ('options', z3.IntSort())) self.packet = packet.create() # $$src\_port: packet \rightarrow \mathbb{Z}^{+}$$ self.src_port = z3.Function('sport', self.packet, z3.IntSort()) # $$dest\_port: packet \rightarrow \mathbb{Z}^{+}$$ self.dest_port = z3.Function('dport', self.packet, z3.IntSort()) # Some commonly used relations # $$nodeHasAddr: node \rightarrow address \rightarrow boolean$$ self.nodeHasAddr = z3.Function('nodeHasAddr', self.node, \ self.address, z3.BoolSort ()) # $$addrToNode: address \rightarrow node$$ self.addrToNode = z3.Function('addrToNode', self.address, self.node) # Send and receive both have the form: # $$ source\rightarrow destination\rightarrow packet\rightarrow int\rightarrow bool$$ # $$send: node \rightarrow node \rightarrow packet\rightarrow int\rightarrow bool$$ self.send = z3.Function('send', self.node, self.node, self.packet, z3.IntSort(), z3.BoolSort()) # $$recv: node \rightarrow node \rightarrow packet\rightarrow int\rightarrow bool$$ self.recv = z3.Function('recv', self.node, self.node, self.packet, z3.IntSort(), z3.BoolSort())
def init_default_data(): Data = z3.Datatype('Data') Data.declare('int', ('data', z3.IntSort())) Data.declare('others', ('data', Data)) Data.declare('tuple', ('left', Data), ('right', Data)) Data.declare('mset', ('body', Data), ('elem', Data)) Data.declare('list', ('head', Data), ('tail', Data)) Data.declare('unit') Data = Data.create() global smt_data smt_data = Data
def _z3_sort(self, json_type, typeargs=[], typeargnames={}): # print "Looking up type %s (%s, %s): %s" % (json_type['name'], typeargs, typeargnames, json_type) ## Hard-coded Z3 correspondence for some types if json_type['name'] == 'nat': return z3.IntSort() if json_type['name'] == 'string': return z3.StringSort() if json_type['name'] == 'gmap': return z3.ArraySort(self._z3_sort(json_type['args'][0]), self._z3_sort(json_type['args'][1])) if json_type['what'] == 'type:var': t = typeargnames[json_type['name']] return self._z3_sort(t) if json_type['what'] == 'type:glob': if json_type['name'] == 'buf': return z3.SeqSort(z3.BitVecSort(8)) return self.z3_sort(json_type['name'], [ self._type_var_lookup(typeargnames, x) for x in json_type['args'] ]) if str(json_type) in self.json_to_sort: return self.json_to_sort[str(json_type)] if json_type['name'] == 'list': return z3.SeqSort(self._z3_sort(typeargs[0])) datatype = z3.Datatype(str(json_type['name'])) typeargnames = {} for i in range(0, len(typeargs)): typeargname = json_type['argnames'][i] typeargnames[typeargname] = typeargs[i] for c in json_type['constructors']: cname = str(c['name']) datatype.declare( cname, *[(cname + '_%d' % idx, self._z3_sort(argtype, [], typeargnames)) for (idx, argtype) in enumerate(c['argtypes'])]) t = datatype.create() self.json_to_sort[str(json_type)] = t return t
def __init__(self, name, z3_args): self.name = name self.ref_count = 0 z3_type = z3.Datatype(name) stripped_args = [] for z3_arg in z3_args: z3_arg_name = z3_arg[0] z3_arg_type = z3_arg[1] if isinstance(z3_arg_type, P4ComplexType): stripped_args.append((z3_arg_name, z3_arg_type.z3_type)) else: stripped_args.append(z3_arg) z3_type.declare(f"mk_{name}", *stripped_args) self.z3_type = z3_type.create() self.z3_args = z3_args
def __init__(self, name, type_params=[], methods=[]): # Externs are this weird bastard child of callables and a complex type # FIXME: Unify types z3_type = z3.Datatype(name) z3_type.declare(f"mk_{name}") self.members = OrderedDict() self.z3_type = z3_type.create() self.const = z3.Const(name, self.z3_type) self.p4_attrs = {} # simple fix for now until I know how to initialize params for externs self.params = {} self.name = name self.type_params = type_params # these are method declarations, not methods for method in methods: self.p4_attrs[method.lval] = method.rval
def initialize(self, ctx): flat_names = [] for idx, (member_name, member_type) in enumerate(self.fields): try: member_type = ctx.resolve_type(member_type) except KeyError: # A generic type, just use the string for now pass if isinstance(member_type, P4ComplexType): # the member is a complex type # retrieve it is flat list of fields # append it to the member list for sub_member in member_type.flat_names: # FIXME: this is not the right way to construct a member... member = Member(P4Member(member_name, sub_member.name), sub_member.p4_type, sub_member.width) flat_names.append(member) self.width += member_type.width else: if isinstance(member_type, z3.BoolSortRef): # bools do not have a size attribute unfortunately member_width = 1 elif isinstance(member_type, z3.BitVecSortRef): member_width = member_type.size() else: # a kind of strange sub-type, unclear what its width is # example: generics member_width = 0 self.width += member_width member = Member(member_name, member_type, member_width) flat_names.append(member) self.fields[idx] = (member_name, member_type) if self.width == 0: # we are dealing with an empty struct, create a dummy datatype z3_type = z3.Datatype(self.name) z3_type.declare(f"mk_{self.name}") self.z3_type = z3_type.create() else: # use the flat bit width of the struct as datatype self.z3_type = z3.BitVecSort(self.width) self.flat_names = flat_names
def f2(): a, b, c, d = z3.BitVecs('a b c d', 3) # 4 bitvectors variable tuple = z3.Datatype('tuple') # new data type 'tuple' tuple.declare( 'tuple', ('f1', z3.BitVecSort(3)), ('f2', z3.BitVecSort(3))) # f1, f2 are for accessing element in tuples tuple = tuple.create() tuple1 = tuple.tuple(a, b) # tuple1 -> (a, b) tuple2 = tuple.tuple(b, c) # tuple2 -> (b, c) tuple1_f2 = tuple.f2(tuple1) # a #tuple1_f2 = tuple.f2(tuple1) # b tuple2_f1 = tuple.f1(tuple2) # c print(tuple1_f2, tuple2_f1) if (tuple1_f2 == tuple2_f1): print("hi") arr0 = z3.K(tuple, False) # arr0 -> arr0[tuple] = false arr1 = z3.Store(arr0, tuple1, True) # arr1 -> arr0[tuple1] = true arr2 = z3.Store(arr1, tuple2, True) # arr -> arr0[tuple2] = true print(arr0) print(arr1) print(arr2) #print(arr1[tuple1]) #print(arr2[tuple2]) #print(arr0) #print(arr1) #print(arr2) s = z3.Solver() s.add(tuple1_f1 == tuple2_f2) # a = c s.add(tuple1_f1 == tuple1_f2) # a = b
try: import z3 # type: ignore[import] HAS_Z3 = True # dynamic type dyn = z3.DeclareSort('Dyn') dyn_type = z3.Const('dyn', dyn) # dimension dim = z3.Datatype('dim') dim.declare('dim', ('0', z3.IntSort()), ('1', z3.IntSort())) dim = dim.create() # tensors tensor_type = z3.Datatype('TensorType') tensor_type.declare('Dyn', ('dyn', dyn)) tensor_type.declare('tensor1', ('0', dim)) tensor_type.declare('tensor2', ('0', dim), ('1', dim)) tensor_type.declare('tensor3', ('0', dim), ('1', dim), ('2', dim)) tensor_type.declare('tensor4', ('0', dim), ('1', dim), ('2', dim), ('3', dim)) tensor_type = tensor_type.create() # create dimension D = dim.dim z3_dyn = tensor_type.Dyn(dyn_type) except ImportError: HAS_Z3 = False
basic_simplifier = RewriteEngine() always = lambda x: True # TODO: rewriting needs to resolve references - # you cannot just rewrite anything named "isfunc" # Or can you? Because pure must be imported as * and names cannot be reassigned? for (patt, repl, condition) in [ # # ('F(reduce(R, L, I))', 'reduce(R`, map(F, L), I)', reduce_fn_check), ]: basic_simplifier.add(exprparse(patt), exprparse(repl), condition) # expr = exprparse('IsTruthy(isint(c) and isint(d))') # rewritten = basic_simplifier.rewrite(expr) # print('rewrite engine test', unparse(expr), unparse(rewritten)) PyFunc = z3.DeclareSort('PyFunc') Unk = z3.Datatype('Unk') Unk.declare('none') Unk.declare('bool', ('tobool', z3.BoolSort())) Unk.declare('int', ('toint', z3.IntSort())) Unk.declare('string', ('tostring', z3.StringSort())) Unk.declare('func', ('tofunc', PyFunc)) Unk.declare('app', ('tl', Unk), ('hd', Unk)) Unk.declare('_') # empty tuple Unk.declare('undef') # error value (Unk, ) = z3.CreateDatatypes(Unk) App = z3.Function('.', Unk, Unk, Unk) class ZHolder(): pass
def _get_datatype_from_list(values, datatype_name): datatype = z3.Datatype(datatype_name) for v in values: datatype.declare(v) return datatype.create()
rects = rects[0] cases = [] for i in range(len(rects)): assert isinstance(rects[i], Rectangle) for j in range(i+1, len(rects)): cases.append(rects[i].non_intersecting(rects[j])) return And(cases) class AssemblyMachine(Rectangle): def __init__(self, size=3, x=None, y=None): super().__init__(x=x, y=y, size=size) Dir = z3.Datatype('Dir') Dir.declare('u') Dir.declare('d') Dir.declare('l') Dir.declare('r') Dir = Dir.create() class DirVal: _IDX = 0 def __init__(self, label=''): self.v = Const(f'dirval_{label}{self._IDX}', Dir) self.__class__._IDX += 1 def eval(self): return SOL.eval(self.v)
#!/usr/bin/env python import z3 English = z3.Datatype('English') English.declare('One') English.declare('Two') English.declare('Three') English.declare('Four') English.declare('Five') English.declare('Six') English.declare('Seven') English.declare('Eight') English.declare('Nine') English.declare('Ten') English = English.create() Spanish = z3.Datatype('Spanish') Spanish.declare('Uno') Spanish.declare('Dos') Spanish.declare('Tres') Spanish.declare('Cuatro') Spanish.declare('Cinco') Spanish.declare('Seis') Spanish.declare('Siete') Spanish.declare('Ocho') Spanish.declare('Nueve') Spanish.declare('Diez') Spanish.declare('Nada') Spanish = Spanish.create()
return self._simpleCheck(event.var, event.val) def checkTrigger(self, val): if self.hold_t is not None or self.delay is not None: return False else: return self._simpleCheck(self.var, val) def __str__(self): return 'hold: %s, delay: %s, %s%s%s' % (self.hold_t, self.delay, self.var, self.comp, self.val) EventExpression = TriggerExpression # ====== the following is all for symbolic expressions ====== # # comparator for boolean and set variables Comparator = z3.Datatype('Comparator') Comparator.declare('eq') Comparator.declare('neq') Comparator = Comparator.create() # comparator for range variables ComparatorRange = z3.Datatype('ComparatorRange') ComparatorRange.declare('gt') ComparatorRange.declare('lt') ComparatorRange.declare('geq') ComparatorRange.declare('leq') ComparatorRange.declare('eq') ComparatorRange.declare('neq') ComparatorRange = ComparatorRange.create() def checkExpression(value, target_value, comp, typ='bool'):
def _eval_sep(self, state: State, st_prefix: str) -> Tuple[List[z3.ExprRef], z3.ExprRef]: ''' create new state variable add assertions enforcing its value ''' def p(st: str, must_be_new: bool = True) -> str: st = st_prefix + '_' + st assert (not must_be_new) or st not in self.seen, (st, self.seen) self.seen.add(st) return st assertions: List[z3.ExprRef] = [] # will be added to the solver state_models_sep = z3.Bool( p('models_sep')) # will become self.state_vs[i] # create z3 symbols for all relations, functions, and constants, and assert their meaning # create universe unary relations scope = max(len(elems) for elems in state.univs.values()) V = z3.Datatype(p('elements')) def e(i: int) -> str: return f'e{i}' for i in range(scope): V.declare(e(i)) V = V.create() def is_e(i: int) -> z3.FuncDeclRef: return getattr(V, 'is_' + e(i)) def ee(i: int) -> z3.ExprRef: return getattr(V, e(i)) universes = { s.name: z3.Function(p(s.name), V, z3.BoolSort()) for s in state.univs } elem_to_z3: Dict[str, z3.ExprRef] = {} for s, elems in state.univs.items(): assertions.extend(universes[s.name](ee(i)) for i in range(len(elems))) assertions.extend( z3.Not(universes[s.name](ee(i))) for i in range(len(elems), scope)) for i, elem in enumerate(elems): assert elem not in elem_to_z3 elem_to_z3[elem] = ee(i) # create relations def lit(e: z3.ExprRef, polarity: bool) -> z3.ExprRef: return e if polarity else z3.Not(e) relations: Dict[str, Union[z3.ExprRef, z3.FuncDeclRef]] = { r.name: z3.Function(p(r.name), *repeat(V, len(r.arity)), z3.BoolSort()) if len(r.arity) > 0 else z3.Bool(p(r.name)) for r in state.rel_interps } for r, ri in state.rel_interps.items(): if len(r.arity) == 0: assert len(ri) == 1 and () in ri, (r, ri) a = relations[r.name] assert isinstance(a, z3.ExprRef) assertions.append(lit(a, ri[()])) else: for tup, polarity in ri.items(): a = relations[r.name] assert isinstance(a, z3.FuncDeclRef) args = [elem_to_z3[x] for x in tup] assertions.append(lit(a(*args), polarity)) # create functions assert all(len(f.arity) > 0 for f in state.func_interps) functions: Dict[str, z3.FuncDeclRef] = { f.name: z3.Function(p(f.name), *repeat(V, len(f.arity)), V) for f in state.func_interps } for f, fi in state.func_interps.items(): for tup, img in fi.items(): args = [elem_to_z3[x] for x in tup] assertions.append(functions[f.name](*args) == elem_to_z3[img]) # create constants constants: Dict[str, z3.ExprRef] = { c.name: z3.Const(p(c.name), V) for c in state.const_interps } for c, ci in state.const_interps.items(): assertions.append(constants[c.name] == elem_to_z3[ci]) # now force state_models_sep z3_vs = [z3.Const(p(f'V{i}'), V) for i in range(len(self.vs))] def to_z3(e: Expr) -> z3.ExprRef: if isinstance(e, Id): if e.name in self.var_names: return z3_vs[self.var_names.index(e.name)] elif e.name in constants: return constants[e.name] elif e.name in relations and isinstance( r := relations[e.name], z3.ExprRef): return r else: assert False, e elif isinstance(e, AppExpr) and e.callee in functions: return functions[e.callee](*(map(to_z3, e.args)))