bool is_horn(expr* n) { expr* n1, *n2; while (is_forall(n)) n = to_quantifier(n)->get_expr(); if (m.is_implies(n, n1, n2) && is_predicate(n2)) { if (is_var(n1)) { return true; } if (is_quantifier(n1)) { return false; } app* a1 = to_app(n1); if (m.is_and(a1)) { for (unsigned i = 0; i < a1->get_num_args(); ++i) { if (!is_predicate(a1->get_arg(i)) && contains_predicate(a1->get_arg(i))) { return false; } } } else if (!is_predicate(a1) && contains_predicate(a1)) { return false; } return true; } return false; }
bool is_implication(expr* f) { expr* e1; while (is_forall(f)) { f = to_quantifier(f)->get_expr(); } while (m.is_implies(f, e1, f)) ; return is_predicate(f); }
bool elim_bounds_cfg::reduce_quantifier(quantifier * q, expr * n, expr * const * new_patterns, expr * const * new_no_patterns, expr_ref & result, proof_ref & result_pr) { if (!is_forall(q)) { return false; } unsigned num_vars = q->get_num_decls(); ptr_buffer<expr> atoms; if (m.is_or(n)) atoms.append(to_app(n)->get_num_args(), to_app(n)->get_args()); else atoms.push_back(n); used_vars used_vars; // collect non-candidates for (expr * a : atoms) { if (!is_bound(a)) used_vars.process(a); } if (used_vars.uses_all_vars(q->get_num_decls())) { return false; } // collect candidates obj_hashtable<var> lowers; obj_hashtable<var> uppers; obj_hashtable<var> candidate_set; ptr_buffer<var> candidates; #define ADD_CANDIDATE(V) if (!lowers.contains(V) && !uppers.contains(V)) { candidate_set.insert(V); candidates.push_back(V); } for (expr * a : atoms) { var * lower = nullptr; var * upper = nullptr; if (is_bound(a, lower, upper)) { if (lower != nullptr && !used_vars.contains(lower->get_idx()) && lower->get_idx() < num_vars) { ADD_CANDIDATE(lower); lowers.insert(lower); } if (upper != nullptr && !used_vars.contains(upper->get_idx()) && upper->get_idx() < num_vars) { ADD_CANDIDATE(upper); uppers.insert(upper); } } } TRACE("elim_bounds", tout << "candidates:\n"; for (unsigned i = 0; i < candidates.size(); i++) tout << mk_pp(candidates[i], m) << "\n";);
void normalize(expr_ref& f) { bool is_positive = true; expr* e = 0; while (true) { if (is_forall(f) && is_positive) { f = to_quantifier(f)->get_expr(); } else if (is_exists(f) && !is_positive) { f = to_quantifier(f)->get_expr(); } else if (m.is_not(f, e)) { is_positive = !is_positive; f = e; } else { break; } } if (!is_positive) { f = m.mk_not(f); } }
bool macro_finder::is_macro(expr * n, app_ref & head, expr_ref & def) { if (!is_forall(n)) return false; TRACE("macro_finder", tout << "processing: " << mk_pp(n, m) << "\n";);
/** \brief Little HACK for simplifying injectivity axioms \remark It is not covering all possible cases. */ bool simplify_inj_axiom(ast_manager & m, quantifier * q, expr_ref & result) { expr * n = q->get_expr(); expr* arg1 = nullptr, * arg2 = nullptr, *narg = nullptr; expr* app1 = nullptr, * app2 = nullptr; expr* var1 = nullptr, * var2 = nullptr; if (is_forall(q) && m.is_or(n, arg1, arg2)) { if (m.is_not(arg2)) std::swap(arg1, arg2); if (m.is_not(arg1, narg) && m.is_eq(narg, app1, app2) && m.is_eq(arg2, var1, var2)) { if (is_app(app1) && is_app(app2) && to_app(app1)->get_decl() == to_app(app2)->get_decl() && to_app(app1)->get_num_args() == to_app(app2)->get_num_args() && to_app(app1)->get_family_id() == null_family_id && to_app(app1)->get_num_args() > 0 && is_var(var1) && is_var(var2) && var1 != var2) { app * f1 = to_app(app1); app * f2 = to_app(app2); bool found_vars = false; unsigned num = f1->get_num_args(); unsigned idx = UINT_MAX; unsigned num_vars = 1; for (unsigned i = 0; i < num; i++) { expr * c1 = f1->get_arg(i); expr * c2 = f2->get_arg(i); if (!is_var(c1) && !is_uninterp_const(c1)) return false; if ((c1 == var1 && c2 == var2) || (c1 == var2 && c2 == var1)) { if (found_vars) return false; found_vars = true; idx = i; } else if (c1 == c2 && c1 != var1 && c1 != var2) { if (is_var(c1)) { ++num_vars; } } else { return false; } } if (found_vars && !has_free_vars(q)) { TRACE("inj_axiom", tout << "Cadidate for simplification:\n" << mk_ll_pp(q, m) << mk_pp(app1, m) << "\n" << mk_pp(app2, m) << "\n" << mk_pp(var1, m) << "\n" << mk_pp(var2, m) << "\nnum_vars: " << num_vars << "\n";); // Building new (optimized) axiom func_decl * decl = f1->get_decl(); unsigned var_idx = 0; ptr_buffer<expr> f_args, inv_vars; ptr_buffer<sort> decls; buffer<symbol> names; expr * var = nullptr; for (unsigned i = 0; i < num; i++) { expr * c = f1->get_arg(i); if (is_var(c)) { names.push_back(symbol(i)); sort * s = decl->get_domain(i); decls.push_back(s); expr * new_c = m.mk_var(var_idx, s); var_idx++; f_args.push_back(new_c); if (i == idx) { var = new_c; } else { inv_vars.push_back(new_c); } } else { SASSERT(is_uninterp_const(c)); f_args.push_back(c); } } SASSERT(var != 0); app * f = m.mk_app(decl, f_args.size(), f_args.c_ptr()); ptr_vector<sort> domain; inv_vars.push_back(f); for (unsigned i = 0; i < inv_vars.size(); ++i) { domain.push_back(m.get_sort(inv_vars[i])); } sort * d = decl->get_domain(idx); func_decl * inv_decl = m.mk_fresh_func_decl("inj", domain.size(), domain.c_ptr(), d); expr * proj = m.mk_app(inv_decl, inv_vars.size(), inv_vars.c_ptr()); expr * eq = m.mk_eq(proj, var); expr * p = m.mk_pattern(f); // decls are in the wrong order... // Remark: the sort of the var 0 must be in the last position. std::reverse(decls.begin(), decls.end()); result = m.mk_forall(decls.size(), decls.c_ptr(), names.c_ptr(), eq, 0, symbol(), symbol(), 1, &p); TRACE("inj_axiom", tout << "new axiom:\n" << mk_pp(result, m) << "\n";); SASSERT(is_well_sorted(m, result)); return true; }
void pull_quant::pull_quant1(func_decl * d, unsigned num_children, expr * const * children, expr_ref & result) { ptr_buffer<sort> var_sorts; buffer<symbol> var_names; symbol qid; int w = INT_MAX; // The input formula is in Skolem normal form... // So all children are forall (positive context) or exists (negative context). // Remark: (AND a1 ...) may be represented (NOT (OR (NOT a1) ...))) // So, when pulling a quantifier over a NOT, it becomes an exists. if (m_manager.is_not(d)) { SASSERT(num_children == 1); expr * child = children[0]; if (is_quantifier(child)) { quantifier * q = to_quantifier(child); expr * body = q->get_expr(); result = m_manager.update_quantifier(q, !q->is_forall(), m_manager.mk_not(body)); } else { result = m_manager.mk_not(child); } return; } bool found_quantifier = false; bool forall_children; for (unsigned i = 0; i < num_children; i++) { expr * child = children[i]; if (is_quantifier(child)) { if (!found_quantifier) { found_quantifier = true; forall_children = is_forall(child); } else { // Since the initial formula was in SNF, all children must be EXISTS or FORALL. SASSERT(forall_children == is_forall(child)); } quantifier * nested_q = to_quantifier(child); if (var_sorts.empty()) { // use the qid of one of the nested quantifiers. qid = nested_q->get_qid(); } w = std::min(w, nested_q->get_weight()); unsigned j = nested_q->get_num_decls(); while (j > 0) { --j; var_sorts.push_back(nested_q->get_decl_sort(j)); symbol s = nested_q->get_decl_name(j); if (std::find(var_names.begin(), var_names.end(), s) != var_names.end()) var_names.push_back(m_manager.mk_fresh_var_name(s.is_numerical() ? 0 : s.bare_str())); else var_names.push_back(s); } } } if (!var_sorts.empty()) { SASSERT(found_quantifier); // adjust the variable ids in formulas in new_children expr_ref_buffer new_adjusted_children(m_manager); expr_ref adjusted_child(m_manager); unsigned num_decls = var_sorts.size(); unsigned shift_amount = 0; TRACE("pull_quant", tout << "Result num decls:" << num_decls << "\n";);