def _touches_address(self, op_name, address): "Return a boolean indicating whether `op_name` touches `address`" # Check for overflow, which is defined to wrap around upper_bound = self.base_address[op_name] + self.width_bytes[op_name] overflow = z3.ULT(upper_bound, self.base_address[op_name]) return z3.If( overflow, # If overflow, account for wraparound z3.Or( z3.UGE(address, self.base_address[op_name]), z3.ULT(address, upper_bound), ), # If no overflow, the address should be in [base, base + offset) z3.And( z3.UGE(address, self.base_address[op_name]), z3.ULT(address, upper_bound), ), )
def transform(self, domain, input_abstract_state): """Compute the most precise output abstract state. """ def add_primes(unprimed): return unprimed + (self.prime_depth * "'") output_domain = domain.translate( dict({ unprimed: add_primes(unprimed) for unprimed in domain.variables })) phi = z3.And(self.z3_formula, domain.gamma_hat(input_abstract_state)) output_state = bilateral(output_domain, phi) return output_state.translate( dict({ add_primes(unprimed): unprimed for unprimed in domain.variables }))
def visit_BExp(self, node, *args, **kwargs): kids = [self.visit(a, *args, **kwargs) for a in node.args] if node.op == 'not': assert node.is_unary() assert len(kids) == 1 return z3.Not(kids[0]) # fn = None # base = None assert len(kids) > 1 if node.op == 'and': return z3.And(*kids) # fn = lambda x, y : z3.And (x, y) # base = z3.BoolVal (True) elif node.op == 'or': return z3.Or(*kids) # fn = lambda x, y : z3.Or (x, y) # base = z3.BoolVal (False) assert False
def neighbor_heuristic(x, num_queens, neighbor_fn): """Deterministic neighbor heuristic (in-and version). Args: x: List[List[z3.Int]], symbols. num_queens: int, the number of queens. neighbor_fn: Callable, neighbor generator. Returns: List[z3.BoolRef], constraints. """ const = [] for i in range(num_queens): for j in range(num_queens): const.append( z3.Implies( x[i][j] == 1, z3.And([ x[nx][ny] == 0 for nx, ny in neighbor_fn(i, j, num_queens) ]))) return const
def sys_reparent(old, pid): cond = z3.And( is_pid_valid(pid), is_pid_valid(old.procs[pid].ppid), old.procs[old.procs[pid].ppid].state == dt.proc_state.PROC_ZOMBIE, z3.Or( old.procs[dt.INITPID].state == dt.proc_state.PROC_RUNNABLE, old.procs[dt.INITPID].state == dt.proc_state.PROC_RUNNING, ), ) new = old.copy() new.procs[dt.INITPID].nr_children[pid] += 1 new.procs[old.procs[pid].ppid].nr_children[pid] -= 1 new.procs[pid].ppid = dt.INITPID return cond, util.If(cond, new, old)
def check_sat(node): """ Produce the function report containing information about satisfiability and validity. """ print("Function report:") if global_solver.check() == sat: print('\tname: {0}\n\tline {1}\n\t**Satisfiable**.'.format( node.name, node.lineno)) else: print('\tname: {0}\n\tline {1}\n\t**Unsatisfiable**. No instantiation \ of variables can satisfy all assertions'.format(node.name, node.lineno)) # Checking the post condition global post_conditions if post_conditions: # substitute current incremented vars for each var in post-condition for key in local_vars: for elem in post_conditions: elem = re.sub(key, local_vars[key], elem) # Build string representation of all relevant information var_list = ", ".join(z3_vars) assertions = str(global_solver.assertions()) assertions = re.sub("Or", "z3.Or", assertions) assertions = re.sub("And", "z3.And", assertions) conditions = ", ".join(post_conditions) post_cond_str = "ForAll([" + var_list + "], Implies(z3.And(" + assertions + "), \ z3.And(" + conditions + ")))" # Assert post-condition exec("global_solver.add(" + post_cond_str + ")") if global_solver.check() == sat: print('\t**Valid**.') else: print('\t**Invalid**. Post-condition(s) falsifiable. \ Fails on this assertion: \n\t"{0}"'.format(post_cond_str)) print()
def enumerateModels(self,timeout): solver = z3.Solver() solver.set("complete",True) props = [] with open(self._cnfFile, 'r') as f: for line in f: if line.startswith('c') or line.startswith('p'): continue letters = [] for conj in line.split(' '): if conj == '0\n': break letters.append(z3.Bool(conj)) props.extend(letters) solver.add(z3.Or(letters)) models = [] count = 0 start_time = time.time() while True: #solver.check() tmp_time = time.time() if tmp_time - start_time > timeout: return Z3Manager.INDICATOR_TIMEOUT, tmp_time - start_time t = solver.check().r ==z3.Z3_L_TRUE #print(i,t,type(t), t.r == z3.Z3_L_TRUE) if not t: break model = solver.model() conj = [] for p in props: if model[p]: conj.append(p) else: conj.append(z3.Not(p)) solver.add(z3.Not(z3.And(conj))) count += 1 #print("Model Count: {}".format(len(models))) end_time = time.time() compileTime = end_time - start_time return count,compileTime
def _atomicity_axiom(self, m): "Calculate the RVWMO Atomicity Axiom" if self._allow_misaligned_atomics: raise Exception("allow_misaligned_atomics not implemented") conjuncts = [] for (sc,) in m["StoreConditional"]: w = Singleton(sc) r = w.join(m["~pair"]) for k, v in r.items(): for a in self._addresses(k[0]): # If r and w are paired load and store operations generated # by aligned LR and SC instructions in a hart h, s is a # store to byte x, and r returns a value written by s, s = self._latest_write(m, k[0], a) # then s must precede w in the global memory order, conjuncts.append(z3.Implies(v, s.in_(w.join(m["~gmo"])))) # and there can be no store from a hart other than h to # byte x writes = self._filter_by_address(m["Write"], a) writes -= w.join(m["~hart_ops.hart_ops"]) # following s following_init = writes.if_(s.no()) following_s = s.join(m["gmo"]).union(following_init) # and preceding w in the global memory order preceding_w = w.join(m["~gmo"]) conjuncts.append( z3.Implies( v, writes.intersect(following_s) .intersect(preceding_w) .no(), ) ) return z3.And(conjuncts)
def solve_board(B, max_sols): """Finds all solutions for a given sudoku board.""" sz = int(len(B)**.5) box = int(sz**.5) # initialize z3 objects X = [z3.Int('x%s%s' % (1 + (i % sz), 1 + (i // sz))) for i in range(sz**2)] s = z3.Solver() # load in the unsolved board to z3 for i in range(len(B)): if B[i] == 0: s.add(z3.And(0 < X[i], X[i] <= sz)) else: s.add(X[i] == B[i]) # horizontal and vertical constraints s.add([z3.Distinct([X[j + sz * i] for i in range(sz)]) for j in range(sz)]) s.add([z3.Distinct([X[i + sz * j] for i in range(sz)]) for j in range(sz)]) # box constraints for b in range(sz): box_ind = [] for i in range(box): ind = box * (1 + (b % box)) + box * sz * (b // box) box_ind += [x + i * sz for x in range(ind - box, ind)] s.add(z3.Distinct([X[i] for i in box_ind])) num_sols = 0 Sols = [] # build that board while s.check() == z3.sat: num_sols += 1 m = s.model() s.add(z3.Or([i != m[i] for i in X])) Sols.append([m[x].as_long() for x in X]) if max_sols and num_sols >= max_sols: break return Sols, num_sols
def visit_distinct(self, e): assert e.num_args() >= 2 # Construct a new expr that does an O(n^2) comparisons exprs_to_and = [] for index_arg0 in range(0, e.num_args()): for index_arg1 in range(0, e.num_args()): if index_arg0 >= index_arg1: # Skip unnecessary comparisons continue new_expr = z3.Not(e.arg(index_arg0) == e.arg(index_arg1)) exprs_to_and.append(new_expr) # Special case a single comparision if len(exprs_to_and) == 1: self.visit(exprs_to_and[0]) return # N > 1 comparisons final_expr = z3.BoolVal(True) while len(exprs_to_and) > 0: e = exprs_to_and.pop() final_expr = z3.And(final_expr, e) self.visit(final_expr)
def _enc_verifier_bool_expr(self, c): if isinstance(c, VerifierAndExpr): cs = list(map(self._enc_bool_expr, c.conjuncts)) return z3.And(*cs, self._ctx) if len(cs) > 0 else True elif isinstance(c, VerifierOrExpr): cs = list(map(self._enc_bool_expr, c.disjuncts)) return z3.Or(*cs, self._ctx) if len(cs) > 0 else False elif isinstance(c, VerifierNotExpr): return z3.Not(self._enc_bool_expr(c.expr)) elif type(c) in Z3Backend.ORDER_CONSTRAINTS_MAP.keys(): real1 = self._enc_real_expr(c.lhs) real2 = self._enc_real_expr(c.rhs) method = Z3Backend.ORDER_CONSTRAINTS_MAP[type(c)] method = getattr(real1, method) return method( real2) # call float's or Z3's ArithRef's __lt__, __gt__, ... elif isinstance(c, VerifierVar): return c.get() else: raise RuntimeError("unsupported VerifierBoolExpr of type " + type(c).__qualname__)
def _overlap(self, a, b): "Return a boolean indicating that `a` and `b` overlap" base_a = self.base_address[a] base_b = self.base_address[b] max_a = base_a + self.width_bytes[a] max_b = base_b + self.width_bytes[b] overflow_a = z3.ULT(max_a, base_a) overflow_b = z3.ULT(max_b, base_b) # The non-overflow case is: # z3.And(z3.ULT(base_a, max_b), z3.ULT(base_b, max_a)) # # If max_b overflows, then base_a < max_b is effectively true, and vice # versa. Therefore, logical-or the overflow conditions in too. return z3.And( z3.Or(overflow_b, z3.ULT(base_a, max_b)), z3.Or(overflow_a, z3.ULT(base_b, max_a)), )
def random_neighbor_heuristic(x, num_queens, neighbor_fn): """Stochastic neighbor heuristic (in-and version). Args: x: List[List[z3.Int]], symbols. num_queens: int, the number of queens. neighbor_fn: Callable, neighbor generator. Returns: List[z3.BoolRef], constraints. """ const = [] for i in range(num_queens): px = random.randint(0, num_queens - 1) py = random.randint(0, num_queens - 1) const.append( z3.Implies( x[px][py] == 1, z3.And([ x[nx][ny] == 0 for nx, ny in neighbor_fn(px, py, num_queens) ]))) return const
def get_preposts1(self, loc, postcond): assert isinstance(loc, str), loc assert postcond.operator() == operator.eq, postcond import infer.opt solver = infer.opt.Ieq(self.symstates, self.prog) postcond_expr = Eqt(postcond).expr(self.use_reals) preconds = solver.gen([loc], postcond_expr) preconds = list(preconds[loc]) if loc in preconds else [] #conj_preconds = self.get_conj_preconds(loc, preconds, postcond) if preconds: precond_expr = z3.And([pc.expr(self.use_reals) for pc in preconds]) inv = z3.Implies(precond_expr, postcond_expr) cexs, isSucc = self.symstates.mcheck_d(loc, inv, None, 1) if not cexs and isSucc: prepost = PrePost(Invs(preconds), postcond, stat=Inv.PROVED) prepost.is_conj = True return prepost else: return None return None
def get_models(F, M=10): s = z3.Solver() s.add(F) i = 0 while i < M and s.check() == z3.sat: m = s.model() yield i, m i += 1 block = [] for d in m: # d is a declaration # ignore room differences in schedules if d.arity() <= 0 and not d.name().endswith('room'): # create a constant from declaration c = d() if not z3.is_array( c) and c.sort().kind() != z3.Z3_UNINTERPRETED_SORT: block.append(c == m[d]) s.add(z3.simplify(z3.Not(z3.And(*block)))) else: # failed or limit -- print stats of last check print(s.statistics())
def test_main(): import OoO as soc #import soc NoThread = (2,2,2); localStates = ( soc.ProcState, soc.DevState, soc.CEState ) TemplateProgram = ( soc.ProcInst, soc.DevInst, soc.CEInst ) SysSharedState = soc.SysSharedState runProperty = lambda state: z3.And( state('IMImageAddr') == soc.BAD_IMAGE , state('DevPC') == soc.IMImageAddr ) frameCond = lambda ts: ts.entity == 'device:' unroll = unroller(NoThread = NoThread, localStates = localStates, TemplateProgram = TemplateProgram, SysSharedState = SysSharedState) unroll.unroll() unroll.addPropertyOnSingleFrames( runProperty = runProperty, frameCond = frameCond) unroll.finalizePiFun() unroll.buildRunProp(func = z3.Or) unroll.pushInstConstraintAndCheck() #unroll.printModel() unroll.solve()
def test_RSY_alpha_hat_add_subtract(): """Attempts to analyze computation of the form: x' := x - 5 x'' := x + 5 """ domain = SignDomain(["x", "x'", "x''"]) x = domain.z3_variable("x") xp = domain.z3_variable("x'") xpp = domain.z3_variable("x''") phi = z3.And(xp == x - 5, xpp == xp + 5) # Just from the statements themselves, we can't say anything about the sign # of x/x'/x'' alpha_hat = RSY(domain, phi) assert alpha_hat.sign_of("x") == Sign.Top assert alpha_hat.sign_of("x'") == Sign.Top assert alpha_hat.sign_of("x''") == Sign.Top
def sys_reclaim_intremap(old, index): pid = old.pci[old.intremaps[index].devid].owner cond = z3.And( # active index is_intremap_valid(index), old.intremaps[index].state == dt.intremap_state.IR_ACTIVE, is_pid_valid(pid), old.procs[pid].state == dt.proc_state.PROC_ZOMBIE ) new = old.copy() new.intremaps[index].state = dt.intremap_state.IR_FREE new.intremaps[index].devid = z3.BitVecVal(0, dt.devid_t) new.intremaps[index].vector = z3.BitVecVal(0, dt.uint8_t) new.procs[pid].nr_intremaps[index] -= 1 return cond, util.If(cond, new, old)
def LAnd(ls): """Smart conjunction over a sequence of :class:`z3.ExprRef`. Only introduces an `And` for sequences of at least two expressions. >>> x, y = z3.Ints("x y") >>> LAnd([x == y]) x == y >>> LAnd([x == y, x!= y, x > y]) And(x == y, x != y, x > y) >>> LAnd([]) True """ l = len(ls) if l == 1: return ls[0] elif l > 1: return z3.And(ls) else: return Z3True
def do_if(self, state): val, = esilops.pop_values(state.stack, state) val = z3.simplify(val) if self.debug: print("condition val: %s" % val) zero = 0 if z3.is_bv_value(val): val = val.as_long() elif z3.is_bv(val): zero = z3.BitVecVal(0, val.size()) if state.condition == None: if type(val) == int: return val != zero else: return z3.simplify(val != zero) else: return z3.simplify(z3.And(val != zero, state.condition))
def exclude_assignment(self, assignment, conflict): ''' Exclude assignment from the design space using provided conflict. :param assignment hole assignment that yielded unsatisfiable DTMC :param conflict indices of relevant holes in the corresponding counterexample :return estimate of pruned assignments ''' if not self.encoded: self.encode() pruning_estimate = 1 counterexample_clauses = [] for hole_index, var in enumerate(DesignSpace.solver_vars): if hole_index in conflict: option = assignment[hole_index].options[0] counterexample_clauses.append( DesignSpace.solver_clauses[hole_index][option]) else: if not self[hole_index].is_unrefined: counterexample_clauses.append( self.hole_clauses[hole_index]) pruning_estimate *= self[hole_index].size if DesignSpace.use_python_z3: assert len(counterexample_clauses) > 0 # TODO handle this counterexample_encoding = z3.Not(z3.And(counterexample_clauses)) DesignSpace.solver.add(counterexample_encoding) elif DesignSpace.use_cvc: if len(counterexample_clauses) == 0: counterexample_encoding = DesignSpace.solver.mkFalse() elif len(counterexample_clauses) == 1: counterexample_encoding = counterexample_clauses[0].notTerm() else: counterexample_encoding = DesignSpace.solver.mkTerm( pycvc5.Kind.And, counterexample_clauses).notTerm() DesignSpace.solver.assertFormula(counterexample_encoding) else: pass return pruning_estimate
def sys_protect_frame(old, pt, index, frame, perm): cond = z3.And( is_pn_valid(pt), old.pages[pt].type == dt.page_type.PAGE_TYPE_X86_PT, old.pages[pt].owner == old.current, # Index is a valid page index z3.ULT(index, 512), is_pn_valid(frame), old.pages[frame].type == dt.page_type.PAGE_TYPE_FRAME, old.pages[frame].owner == old.current, # index must be preset old.pages[pt].data(index) & dt.PTE_P != 0, # the entry in the pt must be the frame z3.Extract(63, 40, z3.UDiv(old.pages_ptr_to_int, util.i64(dt.PAGE_SIZE)) + frame) == z3.BitVecVal(0, 24), z3.Extract(39, 0, z3.UDiv(old.pages_ptr_to_int, util.i64( dt.PAGE_SIZE)) + frame) == z3.Extract(51, 12, old.pages[pt].data(index)), # no unsafe bits in perm is set perm & (dt.MAX_INT64 ^ dt.PTE_PERM_MASK) == 0, # P bit is set in perm perm & dt.PTE_P != 0 ) new = old.copy() new.pages[pt].data[index] = ( (z3.UDiv(new.pages_ptr_to_int, util.i64(dt.PAGE_SIZE)) + frame) << dt.PTE_PFN_SHIFT) | perm # The only thing that changed is the permission. new.pages[pt].pgtable_perm[index] = perm new.flush_tlb(old.current) return cond, util.If(cond, new, old)
def checkExpression(value, target_value, comp, typ='bool'): """ check if the expression is satisfied (without considering the variable) :param value: :param target_value: :param comp: :param typ: :return: """ if typ != 'numeric': eq_clause = z3.And(comp == Comparator.eq, value == target_value) neq_clause = z3.And(comp == Comparator.neq, value != target_value) sat_clause = z3.Or(eq_clause, neq_clause) else: eq_clause = z3.And(comp == ComparatorRange.eq, value == target_value) neq_clause = z3.And(comp == ComparatorRange.neq, value != target_value) gt_clause = z3.And(comp == ComparatorRange.gt, value > target_value) lt_clause = z3.And(comp == ComparatorRange.lt, value < target_value) geq_clause = z3.And(comp == ComparatorRange.geq, value >= target_value) leq_clause = z3.And(comp == ComparatorRange.leq, value <= target_value) sat_clause = z3.Or(eq_clause, neq_clause, gt_clause, lt_clause, geq_clause, leq_clause) return sat_clause
def check_automaton_edge_covering(s: Solver, a: AutomatonDecl) -> None: utils.logger.always_print('checking automaton edge covering:') prog = syntax.the_program t = s.get_translator(KEY_NEW, KEY_OLD) for phase in a.phases(): utils.logger.always_print(' checking phase %s:' % phase.name) with s: for inv in phase.invs(): s.add(t.translate_expr(inv.expr, old=True)) for trans in prog.transitions(): if any(delta.transition == trans.name and delta.precond is None for delta in phase.transitions()): utils.logger.always_print( ' transition %s is covered trivially.' % trans.name) continue utils.logger.always_print( ' checking transition %s is covered... ' % trans.name, end='') with s: s.add(t.translate_transition(trans)) s.add( z3.And(*(z3.Not( t.translate_precond_of_transition( delta.precond, trans)) for delta in phase.transitions() if trans.name == delta.transition))) logic.check_unsat( [(phase.tok, 'transition %s is not covered by this phase' % (trans.name, )), (trans.tok, 'this transition misses transitions from phase %s' % (phase.name, ))], s, [KEY_OLD, KEY_NEW])
def sys_alloc_io_bitmap(old, pn1, pn2, pn3): cond = z3.And( pn1 + 1 == pn2, pn2 + 1 == pn3, z3.Not(old.procs[old.current].use_io_bitmap), is_pn_valid(pn1), old.pages[pn1].type == dt.page_type.PAGE_TYPE_FREE, is_pn_valid(pn2), old.pages[pn2].type == dt.page_type.PAGE_TYPE_FREE, is_pn_valid(pn3), old.pages[pn3].type == dt.page_type.PAGE_TYPE_FREE, ) new = old.copy() new.pages[pn1].owner = old.current new.pages[pn1].type = dt.page_type.PAGE_TYPE_PROC_DATA new.pages[pn1].data = util.i64(0xffffffffffffffff) new.procs[old.current].nr_pages[pn1] += 1 new.pages[pn2].owner = old.current new.pages[pn2].type = dt.page_type.PAGE_TYPE_PROC_DATA new.pages[pn2].data = util.i64(0xffffffffffffffff) new.procs[old.current].nr_pages[pn2] += 1 new.pages[pn3].owner = old.current new.pages[pn3].type = dt.page_type.PAGE_TYPE_PROC_DATA new.pages[pn3].data = util.i64(0xffffffffffffffff) new.procs[old.current].nr_pages[pn3] += 1 new.procs[old.current].io_bitmap_a = pn1 new.procs[old.current].io_bitmap_b = pn2 new.procs[old.current].use_io_bitmap = z3.BoolVal(True) return cond, util.If(cond, new, old)
def sys_alloc_intremap(old, index, devid, vector): cond = z3.And( # valid and free index is_intremap_valid(index), old.intremaps[index].state == dt.intremap_state.IR_FREE, # current owns this devid old.pci[devid].owner == old.current, # current owns this vector old.vectors[vector].owner == old.current, ) new = old.copy() new.intremaps[index].state = dt.intremap_state.IR_ACTIVE new.intremaps[index].devid = devid new.intremaps[index].vector = vector new.procs[new.current].nr_intremaps[index] += 1 return cond, util.If(cond, new, old)
def conn_cons(lInput: List, lPR: List[Tuple], lOutput, vInput: List, vPR: List[Tuple], vOutput): cons = [] lList = lInput + [lOutput] for lParams, lRet in lPR: lList += lParams lList.append(lRet) vList = vInput + [vOutput] for vParams, vRet in vPR: vList += vParams vList.append(vRet) n = len(lList) assert n == len(vList) for i in range(n): for j in range(i): cons.append(z3.Implies(lList[i] == lList[j], vList[i] == vList[j])) return z3.And(*cons)
def meet(range, range_const: Range): if not check_range_const(range_const): return False assert range_const.const_type is not None if range_const.const_type == 0: if isinstance(range, Range): if range_const.left is not None and range_const.right is not None: return z3.Not( z3.Or(range_const.right < range.left, range.right < range_const.left)) if range_const.right is not None: return z3.Or(range.left <= range_const.right, range.right <= range_const.right) if range_const.left is not None: return z3.Or(range_const.left <= range.left, range_const.left <= range.right) else: return True else: if range_const.left is not None and range_const.right is not None: return bool( np.all(range_const.left <= range) and np.all(range <= range_const.right)) if range_const.right is not None: return bool(np.all(range <= range_const.right)) if range_const.left is not None: return bool(np.all(range_const.left <= range)) else: return True else: if isinstance(range, Range): if range_const.left is not None and range_const.right is not None: return z3.And(range_const.left >= range.left, range.right >= range_const.right) else: return False else: return range_const.left == range and range == range_const.right
def check_dsop_pred_equiv(thread_ctx, table, ds_exprs, target_pred): tup = thread_ctx.get_symbs().symbolic_tables[get_main_table( table)].symbols[0] target_expr = generate_condition_for_pred(thread_ctx, tup, target_pred) new_ds_exprs = [] for pair in ds_exprs: rest_pred = pair[1] ds_expr = pair[0] rest_expr = generate_condition_for_pred( thread_ctx, tup, rest_pred) if rest_pred else True new_ds_exprs.append(z3.And(ds_expr, rest_expr)) ds_expr = or_exprs(new_ds_exprs) #print 'target_expr = {}; ds_expr = {}'.format(z3.simplify(target_expr), z3.simplify(ds_expr)) thread_ctx.get_symbs().solver.push() thread_ctx.get_symbs().solver.add(z3.Not(target_expr == ds_expr)) r = (thread_ctx.get_symbs().solver.check() == z3.unsat) # if r == False: # print print_table_in_model(thread_ctx, thread_ctx.get_symbs().solver.model()) # print_all_debug_expr(thread_ctx.get_symbs().solver.model()) thread_ctx.get_symbs().solver.pop() return r
def _relational_cardinality_constraint(self, relation: z3.FuncDeclRef, n: int) -> z3.ExprRef: if relation.arity() == 0: return z3.BoolVal(True) consts = [[ z3.Const(f'card$_{relation}_{i}_{j}', relation.domain(j)) for j in range(relation.arity()) ] for i in range(n)] vs = [ z3.Const(f'x$_{relation}_{j}', relation.domain(j)) for j in range(relation.arity()) ] result = z3.ForAll( vs, z3.Implies( relation(*vs), z3.Or(*(z3.And(*(c == v for c, v in zip(cs, vs))) for cs in consts)))) return result