def __init__(self, left_space, right_space, is_affine): """ Construct to pull back the `right_space`. Bool `is_affine` states if the given space represents an affine space, in which case the returned points will always have the last coordinate 1. """ assert(len(left_space[0]) == len(right_space[0])) # Fields self.affine = is_affine self.r_space = right_space[:] self.l_space = left_space[:] self.rd = len(right_space) self.ld = len(left_space) self.n = len(right_space[0]) self.rr = min(self.rd, pb_nbasis_r) self.lr = min(self.ld, pb_nbasis_l) self.k = (factorial(self.rd) // (factorial(self.rr) * factorial(self.rd-self.rr))) * \ (factorial(self.ld) // (factorial(self.lr) * factorial(self.ld-self.lr))) # Set up solver self.solver = z3.SolverFor("LRA") self.z3_rc = [ z3.Real('rc_%d'%i) for i in range(self.rd) ] # Coefficients in right_space self.z3_lc = [ z3.Real('lc_%d'%i) for i in range(self.ld) ] # Coefficients in left # Set up combination iterator self.subb_cmb = itr.product(itr.combinations(zip(self.z3_lc, self.l_space), self.lr), itr.combinations(zip(self.z3_rc, self.r_space), self.rr)) self.sbn = 0 # Number of queries we have done. By this, we signal that we have not done any queries yet self.qn = pb_nqueries
def make_solver(self) -> z3.Solver: solver = z3.SolverFor("HORN") solver.set("engine", "spacer") # allow quantified variables in pobs solver.set("spacer.ground_pobs", False) # enable quantified generalization solver.set("spacer.q3.use_qgen", True) for p in self.get_proof_rule(): solver.add(p.as_z3()) return solver
def __init__(self, environment, logic, **options): IncrementalTrackingSolver.__init__(self, environment=environment, logic=logic, **options) self.z3 = z3.SolverFor(str(logic)) self.options(self) self.declarations = set() self.converter = Z3Converter(environment, z3_ctx=self.z3.ctx) self.mgr = environment.formula_manager self._name_cnt = 0 return
def checkSat(consts, logic="None"): z3Consts = [z3Obj(c) for c in consts] if logic != "None": solver = z3.SolverFor(logic) else: solver = z3.Solver() target_z3_simplify = z3.simplify(z3.And(*z3Consts)) solver.add(target_z3_simplify) solver.set("timeout", 9000000) #timeout : 150 min with open("thermoLinear.smt2", 'w') as fle: print(solver.to_smt2(), file=fle) return (solver.check(), sizeAst(z3.And(*z3Consts)))
def __init__(self, json_file, pipeline, test_case_writer, solve_again): self.test_case_builder = TestCaseBuilder(json_file, pipeline) self.test_case_writer = test_case_writer self.solve_again = solve_again self.solver = z3.SolverFor('QF_UFBV') # Each item in list corresponds to an added path and a solver increment. # path_data is a data structure containing whatever is needed by the # child class' build_test_case implementation. self.paths_data = [] # [ (path_id, path_data), ... ] # Constraints added with each path self.constraint_lists = [] # [ [constraint, ...], ...]
def __init__(self, environment, logic, **options): IncrementalTrackingSolver.__init__(self, environment=environment, logic=logic, **options) # LBYL to avoid a possible segmentation fault caused by z3.SolverFor # See issue #465 (https://github.com/pysmt/pysmt/issues/465) if str(logic) in Z3Solver.SOLVERFOR_LOGIC_NAMES: self.z3 = z3.SolverFor(str(logic)) else: self.z3 = z3.Solver() self.options(self) self.declarations = set() self.converter = Z3Converter(environment, z3_ctx=self.z3.ctx) self.mgr = environment.formula_manager self._name_cnt = 0 return
def is_satisfied(self, command: 'Command', state_before: State, state_after: State, environment: Environment, config: Configuration) -> bool: """ Determines whether this specification is satisfied by a given before and after state in a particular context (i.e, command arguments, configuration and environment). """ logger.debug("Checking for command: %s", command.name) # FIXME ctx = z3.Context() solver = z3.SolverFor("QF_NRA", ctx=ctx) smt, decls = self._prepare_query(ctx, command, state_before, state_after) expr = self.get_expression(decls, state_before) smt.extend(expr) logger.info("SMT: {}".format(smt)) solver.add(smt) logger.debug("Z3 result: {}".format(str(solver.check()))) return solver.check() == z3.sat
def __init__(self, environment, logic, **options): IncrementalTrackingSolver.__init__(self, environment=environment, logic=logic, **options) try: self.z3 = z3.SolverFor(str(logic)) except z3.Z3Exception: self.z3 = z3.Solver() except z3.z3types.Z3Exception: self.z3 = z3.Solver() self.options(self) self.declarations = set() self.converter = Z3Converter(environment, z3_ctx=self.z3.ctx) self.mgr = environment.formula_manager self._name_cnt = 0 self.qf = False return
def test_hardtanh(self): """ This test is a mild sanity check; don't take too seriously """ y = z3.RealVector("y", 2) x = z3.RealVector("x", 2) constraints = lantern.encode_hardtanh(x, y) s = z3.SolverFor("QF_LRA") s.add(constraints) # x_0 = -2 => y_0 = -1 s.add(x[0] == -2) s.add(x[1] == y[0]) self.assertTrue(s.check() == z3.sat) # should fail, since y_1 is constrained to -1 s.add(y[1] == 0) self.assertTrue(s.check() == z3.unsat)
def main(): base = 100 expected = [99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99] expected = base2integer(expected, 100) expected = integer2base(expected, base) size = len(expected) bits = 2 * (int(math.log(base, 2)) + 1) + 1 va = create_bit_vector('a', size, bits) vb = create_bit_vector('b', size, bits) expected = left_pad_with_zeros(expected, 2 * len(expected)) print(str(expected)) z3.set_option("parallel.enable", True) # 6 Threads ran on AMD Phenom II X6 z3.set_option("parallel.threads.max", 6) s = z3.SolverFor("QF_BV") multiply(s, base, va, vb, expected) time1 = datetime.now() if s.check() == z3.sat: print("sat") m = s.model() print(str(m)) f1 = vector_to_number(m, va, base) f2 = vector_to_number(m, vb, base) print("Factor 1 = " + str(f1)) print("Factor 2 = " + str(f2)) print("The number (Factor 1 * Factor 2) " + str(f1 * f2)) else: print("unsat") print(base2integer(expected, base)) print(str(s.unsat_core())) time2 = datetime.now() print("Base " + str(base)) print("Elapsed " + str(time2 - time1))
def is_satisfiable(self, command: 'Command', state: State, environment: Environment) -> bool: """ Determines whether this specification is satisifiable in a given context (i.e., command, state, environment, configuration). Parameters: command: the parameters supplied to the command. state: the state of the system immediately prior to executing the command. environment: the state of the environment immediately prior to executing the command. Returns: True if satisfiable, false if not. """ ctx = z3.Context() s = z3.SolverFor("QF_NRA", ctx=ctx) decls = self.get_declarations(ctx, command, state) smt = Expression.values_to_smt('_', state, decls) smt.extend(self.get_expression(decls, state)) s.add(smt) return s.check() == z3.sat
def push_forward_relu(left_space): """ Finds an linear subspace to the right of relu so that any vector in `left_space` left space goes to it. Returns a basis of the space as a list of vecors. `left_space` must be a list of linearly indpenedent vectors giving a basis of the left space. """ d = len(left_space) n = len(left_space[0]) # Set up z3. solver = z3.SolverFor("LRA") z3_coeffs = [ z3.Real("c_%d"%i) for i in range(d) ] # Calculate the tie-classes of the relus tie_class_reps = [0] tie_class_vals = list(range(n)) for i in range(1, n): print("Classifying ", i, end='\r', flush=True) # DEBUG solver.push() solver.add( 0 >= z3.Sum([ c*l[i] for c, l in zip(z3_coeffs, left_space) ]) ) cls = -1 for rep in tie_class_reps: solver.push() solver.add( 0 < z3.Sum([ c*l[rep] for c, l in zip(z3_coeffs, left_space) ]) ) if solver.check() == z3.unsat: cls = rep solver.pop() break solver.pop() if cls == -1: tie_class_reps.append(i) else: tie_class_vals[i] = cls solver.pop() print() fb_basis = [] # If there are completely positive points in the left space, then those points exist in the # right space solver.reset() solver.push() h_cp = False for i in range(n): solver.add( 0 <= z3.Sum([ c*l[i] for c, l in zip(z3_coeffs, left_space) ]) ) print("Checking completely positive points") #DEBUG if solver.check() == z3.sat: print("Completely positive points exist") #DEBUG fb_basis = deepcopy(left_space) h_cp = True solver.pop() # Now, for each tie-class, we add basis generating that tie class for rep, i in zip(tie_class_reps, range(len(tie_class_reps))): for b, j in zip(left_space, range(len(left_space))): print("Adding basis %d for tie-class %d"%(j, i), end='\r', flush=True) #DEBUG b_ = [ c if rv == rep else 0 for c, rv in zip(b, tie_class_vals) ] if np.linalg.matrix_rank(np.asarray(fb_basis + [b_])) > len(fb_basis): fb_basis.append(b_) print("\nAdding basis") print() return fb_basis, tie_class_reps, tie_class_vals, h_cp
def perm_check(weights, biases, perm, prc_eq): """ Check if DNN given by the `weights` and `biases` is invariant under the given `perm`utation. The other arguments are: prc_eq - A set of linear equalities giving a precondition on the input. Specified as a list of rows, each row [a1, a2, ... an, b] specifies the equation a1.x1 + a2.x2 + ... an.xn = b. """ # Numbers inp_dim = len(weights[0]) out_dim = len(biases[-1]) num_lyrs = len(weights) # Generate basis representing permutation constraint in the larger space in_basis = [] if(len(prc_eq) > 0): # Convert the constraints into a basis for a precondition space prc_a = np.matrix([ r[:-1] for r in prc_eq]) prc_b = np.asarray([r[-1] for r in prc_eq]) prc_eq_krn = np.transpose(sp.null_space(prc_a)).tolist() prc_eq_s0, _, _, _ = sp.lstsq(prc_a, prc_b) prc_eq_s0 = prc_eq_s0.tolist() if len(prc_eq_krn) <= 0: # The equations determine exactly one input, and so we just check if that is a cex if check_cex(weights, biases, perm, prc_eq, prc_eq_s0): return False, prc_eq_s0 return True, [] in_basis = [ r + [r[p] for p in perm] + [0] for r in prc_eq_krn] + \ [ prc_eq_s0 + [prc_eq_s0[p] for p in perm] + [1] ] else: for i in range(inp_dim): b = [0]*(inp_dim*2) b[i] = 1 b[perm[i] + inp_dim] = -1 in_basis.append(b + [0]) in_basis.append((inp_dim*2)*[0] + [1]) prc_space = [ r[:] for r in in_basis ] ## ABSTRACTION # Represent affine transforms as matrices in a larger space # Joint weight matrix jlt_mat = [ np.matrix([r + [0]*len(w[0]) for r in w] + [[0]*len(w[0]) + r for r in w]) for w,b in zip(weights,biases) ] # Joint affine transform jat_mat = [ np.matrix([r + [0]*len(w[0]) + [0] for r in w] + [[0]*len(w[0]) + r + [0] for r in w] + [b + b + [1]]) for w,b in zip(weights,biases) ] # Kernels of joint weight matrices jlt_krn = [ np.transpose(sp.null_space(np.transpose(dm))) for dm in jlt_mat] # Track interpolants pre_lin_ints = [] # Interpolants post_lin_ints = [] # Interpolants after going through linear transform tc_reps = [] # Representatives of tie classes tc_vals = [] # Values of each tie class has_cp = [] # Weather the push forward of the interpolant has the complete space # of the previous layer # Linear inclusion loop for w, b, lm, curr_lyr in zip(weights, biases, jat_mat, range(num_lyrs)): print('Kernel check for layer ', curr_lyr) l = len(w[0]) # Find image of in_basis under space. Ensure generated out_basis is linearly independent out_basis = [] for b in in_basis: ob = (np.array(b) @ lm).tolist()[0] _r = np.linalg.matrix_rank(np.asarray(out_basis + [ob])) if _r > len(out_basis): out_basis.append(ob) # Check linear inclusion by finding subbasis of input basis that does not go to 0 eq_basis = lm @ np.matrix([[0]*i + [+1] + [0]*(l-i-1) for i in range(l)] + [[0]*i + [-1] + [0]*(l-i-1) for i in range(l)] + [[0]*l]) if np.allclose(np.matrix(in_basis) @ eq_basis, 0): print('Verified at layer via linear inclusion ', curr_lyr) return True, [] else: print('Linear inclusion failed at layer ', curr_lyr) # The basis containing the space at this layer where no CEX can ever be found spr_basis = np.transpose(sp.null_space(np.transpose(eq_basis))).tolist() # Backtracking pullback for cex suc, cex = pull_back_cex_explore(weights, biases, inp_dim, perm, prc_eq, prc_space, tc_reps, tc_vals, has_cp, curr_lyr, spr_basis) if suc: return False, cex pass # Save these interpolants, and get next ones print('Looking for affine interpolant for next layer') pre_lin_ints.append(in_basis) post_lin_ints.append(out_basis) in_basis, tc_r, tc_v, h_cp = push_forward_relu(out_basis) tc_reps.append(tc_r) tc_vals.append(tc_v) has_cp.append(h_cp) ## REFINEMENT # Set up solver and vars refined_solver = z3.SolverFor("LRA") # Stores the values going into each layer's transform in reverse order. First member stores # output. lyr_vars = [[ z3.Real('lyr_%d_%d'%(num_lyrs, nn)) for nn in range(out_dim) ]] lyr_vars_p = [[ z3.Real('lyr_p_%d_%d'%(num_lyrs, nn)) for nn in range(out_dim) ]] for v, v_ in zip(lyr_vars[-1], lyr_vars_p[-1]): refined_solver.add(z3.Not(v == v_)) # Refinement Loop for itp, w, b, i in zip(reversed(pre_lin_ints), reversed(weights), reversed(biases), reversed(range(num_lyrs))): print("Refining layer %d"%i) # Encode layers lyr_vars.append([ z3.Real('lyr_%d_%d'%(i, nn)) for nn in range(len(w)) ]) lyr_vars_p.append([ z3.Real('lyr_p_%d_%d'%(i, nn)) for nn in range(len(w)) ]) lyr_out = encode_dnn.encode_network([w], [b], lyr_vars[-1]) lyr_out_p = encode_dnn.encode_network([w], [b], lyr_vars_p[-1]) for le, lep, lv, lvp in zip(lyr_out, lyr_out_p, lyr_vars[-2], lyr_vars_p[-2]): refined_solver.add(le == lv) refined_solver.add(lep == lvp) # Perform refined check refined_solver.push() # Cefficient to each basis in interpolant itp_cffs = [ z3.Real('cf_%d'%i) for i in range(len(itp)) ] # Encode that input is in inetrpolant, for j in range(len(w)): refined_solver.add( lyr_vars[-1][j] == z3.Sum([ ic * it[j] for ic, it in zip(itp_cffs, itp) ])) refined_solver.add( lyr_vars_p[-1][j] == z3.Sum([ ic * it[len(w) + j] for ic, it in zip(itp_cffs, itp) ])) refined_solver.add( 1 == z3.Sum([ ic * it[-1] for ic, it in zip(itp_cffs, itp) ])) if refined_solver.check() == z3.unsat: print("Verified via refinement at layer %d") return True, [] else: print('Found potential cex, attempting pullback....', end='') mdl = refined_solver.model() # The cex is a single point in the joint affine space at the begining of the current # layer, and that can be represented by the follwing line cex_basis = [ list(map(mdl.eval, lyr_vars[-1])) + list(map(mdl.eval, lyr_vars_p[-1])) ] cex_basis[0] = [ v.numerator_as_long()/v.denominator_as_long() for v in cex_basis[0]]\ + [1] suc, cex = refined_pull_back_cex_explore(weights, biases, inp_dim, prc_space, perm, prc_eq, jlt_mat, jlt_krn, post_lin_ints, i, cex_basis) if suc: return False, cex assert(False) #We should never reach here
def stability_search1(phi, xsys, m): """ Attempts to find a max-affine Lyapunov function that proves global stability of an AMN: Dynamical system: x(t+1) = phi(x(t)) Lyapunov function: V(x) = max(Ax+b), A (m-by-n) and b (m) A and b are recalculated again at every E-solve step x should be a ref to the input variable to phi """ n = phi.outdim assert n == xsys.outdim assert m >= 1 # 0. Initialize print 'Initializing stability_search1' MAX_ITER = 50 # init counterexample set Xc = list() #Xc.append(np.ones((n,))) # go around the Linf 1-ball for xcpoint in itertools.product([-1, 1], repeat=n): Xc.append(np.array(xcpoint)) # init SMT solver esolver = z3.SolverFor('QF_LRA') fsolver = z3.SolverFor('QF_LRA') enc = amnet.smt.SmtEncoder(phi, solver=fsolver) enc.init_tree() print enc for iter in range(MAX_ITER): # 1. E-solve esolver.push() Avar = [z3.RealVector('A' + str(i), n) for i in range(m)] bvar = z3.RealVector('b', m) print 'iter=%s: Xc=%s' % (iter, Xc) print 'Avar=%s' % Avar print 'bvar=%s' % bvar esolver.add(_maxN_z3(bvar) == 0) for k, xk in enumerate(Xc): # point value xk_next = phi.eval(xk) # Lyapunov max expressions Vk_terms = [ z3.Sum([Avar[i][j] * xk[j] for j in range(n)]) + bvar[i] for i in range(m) ] Vk_next_terms = [ z3.Sum([Avar[i][j] * xk_next[j] for j in range(n)]) + bvar[i] for i in range(m) ] Vk_expr = _maxN_z3(Vk_terms) Vk_next_expr = _maxN_z3(Vk_next_terms) # Lyapunov function constraints for counterexample xk Vk = z3.Real('v' + str(k)) Vk_next = z3.Real('v' + str(k) + '_next') esolver.add(Vk == Vk_expr) esolver.add(Vk_next == Vk_next_expr) # nonnegativity/decrement of V if all(xk == 0): esolver.add(Vk == 0) else: # CONDITIONING: impose minimum decay rate esolver.add(Vk > 0) esolver.add(Vk_next > 0) esolver.add(Vk_next < 0.99 * Vk) # CONDITIONING: impose upper bound on b esolver.add(_normL1_z3(bvar) <= 10) esolver.add(_maxN_z3(bvar) == 0) #esolver.add([bvar[i] == 0 for i in range(m)]) # CONDITIONING: impose normalization on A for i in range(m): esolver.add(_normL1_z3(Avar[i]) <= 10) if _DEBUG_SMT2: filename = 'log/esolver_%s.smt2' % iter file = open(filename, 'w') print 'Writing %s...' % filename, file.write('(set-logic QF_LRA)\n') file.write(esolver.to_smt2()) file.write('(get-model)') print 'done!' file.close() # find a candidate Lyapunov function if esolver.check() == z3.sat: print 'iter=%s: Found new Lyapunov Function' % iter #print 'esolver=%s' % esolver model = esolver.model() A_cand = np.array([[mfp(model, Avar[i][j]) for j in range(n)] for i in range(m)]) b_cand = np.array([mfp(model, bvar[i]) for i in range(m)]) print "V(x)=max(Ax+b):" print "A=" + str(A_cand) print "b=" + str(b_cand) else: print 'iter=%s: Stability unknown, exiting' % iter esolver.pop() return None esolver.pop() # 2. F-solve # find counterexample for candidate Lyapunov function fsolver.push() # z3 symbol for input to phi x = enc.get_symbol(xsys) # encode Vx Vx_terms = [ z3.Sum([A_cand[i][j] * x[j] for j in range(n)]) + b_cand[i] for i in range(m) ] Vx_expr = _maxN_z3(Vx_terms) Vx = z3.Real('vx') fsolver.add(Vx == Vx_expr) # z3 symbol for phi(x) x_next = enc.get_symbol(phi) # encode Vx_next Vx_next_terms = [ z3.Sum([A_cand[i][j] * x_next[j] for j in range(n)]) + b_cand[i] for i in range(m) ] Vx_next_expr = _maxN_z3(Vx_next_terms) Vx_next = z3.Real('vx_next') fsolver.add(Vx_next == Vx_next_expr) # encode failure to decrement fsolver.add(z3.Not(x == 0)) fsolver.add(z3.Not(z3.And(Vx > 0, Vx_next - Vx < 0))) # CONDITIONING: only care about small counterexamples fsolver.add(_normL1_z3(x) <= 5) fsolver.add(_normL1_z3(x) >= 0.5) if _DEBUG_SMT2: filename = 'log/fsolver_%s.smt2' % iter file = open(filename, 'w') print 'Writing %s...' % filename, file.write('(set-logic QF_LRA)\n') file.write(fsolver.to_smt2()) file.write('(get-model)\n') print 'done!' file.close() # look for a counterexample if fsolver.check() == z3.sat: print 'iter=%s: Found new Counterexample' % iter #print 'fsolver=%s' % fsolver fmodel = fsolver.model() xc = np.array([mfp(fmodel, x[j]) for j in range(n)]) Xc.append(xc) else: print 'iter=%s: No Counterexample found' % iter print 'Lyapunov function found' print "V(x)=max(Ax+b):" print "A=" + str(A_cand) print "b=" + str(b_cand) fsolver.pop() return (A_cand, b_cand) fsolver.pop() # max iterations reached print 'Max iterations reached' return None
def walk_block(node, prev_g=None, cond=True): g = z3.Goal() g.add(cond) if prev_g is not None: for e in prev_g: if isinstance(e, z3.Goal): g.add(e.as_expr()) else: g.add(e) if isinstance(node, pycp.c_ast.Compound): if node.block_items is not None: for e in node.block_items: g_next = walk_block(e, g) g = g_next elif isinstance(node, pycp.c_ast.Decl): if "int" in node.type.type.names: ts[node.name] = 0 vars[node.name] = z3.BitVec("%s!!%d" % (node.name, ts[node.name]), 32) elif isinstance(node, pycp.c_ast.FuncCall): if node.name.name == "__ASSUME": for e_exp in node.args.exprs: g.add(gen_smt_expr(e_exp)) elif node.name.name == "__ASSERT": assertions = z3.Goal() for e_exp in node.args.exprs: assertions.add(gen_smt_expr(e_exp)) print("solving..") print("SP:", g.as_expr()) print("assert:", assertions) fml = z3.And(g.as_expr(), z3.Not(assertions.as_expr())) s = z3.SolverFor('bv') s.add(fml) under_approx_info = {} for e in vars: under_approx_info[e] = [1, z3.Bool('%s!!switch' % e)] while (1): s.push() switches = [] for e in vars: if under_approx_info[e][0] <= 30: s.add( z3.Implies( under_approx_info[e][1], z3.Extract(30, under_approx_info[e][0], vars[e]) == 0)) switches.append(under_approx_info[e][1]) if len(switches) == 0: status = s.check() s.pop() break # print(s) status = s.check(switches) s.pop() if status == z3.unsat: u_core = s.unsat_core() # print(u_core) if len(u_core) == 0: break e_bool = random.choice(u_core) var_st = str(e_bool)[:-8] # print(var_st) under_approx_info[var_st][0] += 1 elif status == z3.sat: break else: break if status == z3.sat: # print(s) model = s.model() print("program is unsafe.\nlisting an unsafe assignments..") for e in vars: print(e, ':', model[vars[e]].as_signed_long()) elif status == z3.unsat: print("program is safe.") elif status == z3.unknown: print("unknown") else: print("found a func call") elif isinstance(node, pycp.c_ast.Assignment): rexp = gen_smt_expr(node.rvalue) ts[node.lvalue.name] += 1 old_ = vars[node.lvalue.name] if z3.is_bv(vars[node.lvalue.name]): curr_ = z3.BitVec( '%s!!%d' % (node.lvalue.name, ts[node.lvalue.name]), 32) vars[node.lvalue.name] = curr_ if node.op == "=": g.add(curr_ == rexp) elif node.op == "+=": g.add(curr_ == (old_ + rexp)) elif node.op == "-=": g.add(curr_ == (old_ - rexp)) elif node.op == "*=": g.add(curr_ == (old_ * rexp)) elif node.op == "%=": g.add(curr_ == (old_ % rexp)) g_ = z3.Goal() g_.add(z3.Exists(old_, g.as_expr())) g_ = t_qe(g_) if not z3.is_quantifier(g_.as_expr()): print("q eliminated: %s" % old_) g = g_ # g = z3.Goal() # g = g.simplify() elif isinstance(node, pycp.c_ast.If): cond_exp = gen_smt_expr(node.cond) vars_ = {} for e in vars: vars_[e] = vars[e] if node.iftrue is not None: true_expr = walk_block(node.iftrue, g, cond_exp).as_expr() else: true_expr = z3.And(cond_exp, g.as_expr()) vars_t = {} for e in vars: vars_t[e] = vars[e] vars[e] = vars_[e] if node.iffalse is not None: false_expr = walk_block(node.iffalse, g, z3.Not(cond_exp)).as_expr() else: false_expr = z3.And(z3.Not(cond_exp), g.as_expr()) g_t = z3.Goal() g_f = z3.Goal() g_t.add(true_expr) g_f.add(false_expr) for e in vars: if not vars[e].eq(vars_t[e]): ts[e] += 1 new_ = z3.BitVec('%s!!%s' % (e, ts[e]), 32) g_t.add(new_ == vars_t[e]) g_f.add(new_ == vars[e]) vars[e] = new_ g = z3.Goal() g.add(z3.Or(g_t.as_expr(), g_f.as_expr())) # print(g) g = t(g) # g.simplify() else: return prev_g # print(g.as_expr(), "\n") return g
def is_false(cond): s = z3.SolverFor("QF_ABV") s.add(cond) return s.check() == z3.unsat
def main(): """Lantern demo""" # Initialize a PyTorch network # Lantern currently supports: Linear, ReLU, Hardtanh, Dropout, Identity net = nn.Sequential(nn.Linear(2, 5), nn.ReLU(), nn.Linear(5, 1), nn.ReLU()) print("A PyTorch network:") print(net) print() # Normally, we would train this network to compute some function. However, # for this demo, we'll just use the initialized weights. print("Network parameters:") print(list(net.parameters())) print() # lantern.as_z3(model) returns a triple of z3 constraints, input variables, # and output variables that directly correspond to the behavior of the # given PyTorch network. By default, latnern assumes Real-sorted variables. constraints, in_vars, out_vars = lantern.as_z3(net) print("Z3 constraints, input variables, output variables (Real-sorted):") print(constraints) print(in_vars) print(out_vars) print() # The 'payoff' is that we can prove theorems about our network with z3. # Trivially, we can ask for a satisfying assignment of variables print("A satisfying assignment to the variables in this network:") z3.solve(constraints) print() # However, we can run the network "backwards"; e.g. what is an *input* that # causes the network to output the value 0 (if such an input exists)? constraints.append(out_vars[0] == 0) print("An assignment such that the output variable is 0:") z3.solve(constraints) print() # To more precisely represent the underlying computations, consider using # an appropriate floating-point sort; PyTorch defaults to single precision. # To speed up satisfiability computations, models can be 'rounded', which # truncates the mantissa of every PyTorch model parameter. Note that the # exponent part remains the same (11 bits) so that the result can be # returned as a Python float. Here, we truncate to 10 bits (half precision). rounded_net = lantern.round_model(net, 10) constraints, in_vars, out_vars = lantern.as_z3(rounded_net, sort=z3.FPSort(11, 10)) print("Z3 constraints, input, output (FPSort(11, 10)):") print(constraints) print(in_vars) print(out_vars) print() # We add the constraint that the output must be 0.0, and solve using a # solver for FloatingPoint theory. print("An assignment such that the output variable is 0.0:") constraints.append(out_vars[0] == 0.0) z3.solve_using(z3.SolverFor("QF_FP"), *constraints) print() # Note that the constraints, and variables are all 'ordinary' Z3Py objects. print("Happy hacking!")
def get_solver(): z3.set_param('rewriter.blast_select_store', True) return z3.SolverFor('QF_ABV')