/* * Convert term t to a conditional; store the result in d * - d is reset first * - t must be a valid term defined in d->terms * - if t is not an if-then-else term, the result is * d->nconds = 0 * d->defval = t * - if t is (ite c a b) then the conversion depends on whether * a or b is an if-then-else term. */ void convert_term_to_conditional(conditional_t *d, term_t t) { composite_term_t *ite; term_t a, b; if (is_ite_term(d->terms, t)) { ite = ite_term_desc(d->terms, t); assert(ite->arity == 3); a = ite->arg[1]; b = ite->arg[2]; if (is_neg_term(t)) { a = opposite_term(a); b = opposite_term(b); } convert_ite_to_conditional(d, ite->arg[0], ite->arg[1], ite->arg[2]); } else { reset_conditional(d); d->defval = t; } }
/* * Build the abstraction for an (OR ...) formula or its negation * - if polarity is true: abstraction of (OR t1 ... tn) * - if polarity is false: abstraction of not (OR t1 ... tn) * (i.e., abstraction of (AND (not t1) ... (not tn))) */ static epartition_t *eq_abstract_or(eq_learner_t *learner, composite_term_t *or, bool polarity) { uint32_t i, n; epartition_manager_t *m; epartition_t *p; assert(or->arity > 1); // abstract the arguments n = or->arity; for (i=0; i<n; i++) { (void) eq_abstract(learner, or->arg[i], polarity); } /* * for (OR t1 ... t_n): construct the join of abs(t1) ... abs(t_n) * for not (OR t1 ... t_n) <=> (and (not t1) ... (not t_n)): * construct meet(abs (not t1) ... abs(not t_n)) */ m = &learner->manager; if (polarity) { // (OR t1 ... t_n) p = get_cached_abstraction(learner, or->arg[0]); epartition_init_for_join(m, p); for (i=1; i<n; i++) { p = get_cached_abstraction(learner, or->arg[i]); epartition_join(m, p); } return epartition_get_join(m); } else { // (AND (not t1) ... (not t_n)) p = get_cached_abstraction(learner, opposite_term(or->arg[0])); epartition_init_for_meet(m, p); for (i=1; i<n; i++) { p = get_cached_abstraction(learner, opposite_term(or->arg[i])); epartition_meet(m, p); } return epartition_get_meet(m); } }
/* * Assert (B and not C) in ctx * - return the assertion code */ static int32_t forall_context_assert(ef_solver_t *solver, term_t b, term_t c) { context_t *ctx; term_t assertions[2]; ctx = solver->forall_context; assert(ctx != NULL && context_status(ctx) == STATUS_IDLE); assert(is_boolean_term(ctx->terms, b) && is_boolean_term(ctx->terms, c)); assertions[0] = b; assertions[1] = opposite_term(c); return assert_formulas(ctx, 2, assertions); }
/* * Compute an implicant for constraint i * - we must have an assignment for the exists variable in solver->evalue * and an assignment for the universal variables of constraints i in * solver->uvalue_aux. * - this builds and store the full model for constraint i in solver->full_model * then construct and store the implicant in solver->implicant * * Error codes: set solver->status to EF_STATUS_ERROR * - this may happen if yices_model_from_map or yices_implicant_for_fomuulas fail */ static void ef_build_implicant(ef_solver_t *solver, uint32_t i) { model_t *mdl; ef_cnstr_t *cnstr; term_vector_t *v; term_t a[2]; uint32_t n; int32_t code; assert(i < ef_prob_num_constraints(solver->prob)); // free the current full model if any if (solver->full_model != NULL) { yices_free_model(solver->full_model); solver->full_model = NULL; } // build the full_map and the correspongin model. ef_build_full_map(solver, i); n = solver->all_vars.size; assert(n == solver->all_values.size); mdl = yices_model_from_map(n, solver->all_vars.data, solver->all_values.data); if (mdl == NULL) { // error in the model construction solver->status = EF_STATUS_ERROR; // TODO: add another code solver->error_code = yices_error_code(); return; } solver->full_model = mdl; cnstr = solver->prob->cnstr + i; a[0] = cnstr->assumption; a[1] = opposite_term(cnstr->guarantee); v = &solver->implicant; v->size = 0; code = yices_implicant_for_formulas(mdl, 2, a, v); if (code < 0) { solver->status = EF_STATUS_ERROR; solver->error_code = yices_error_code(); } #if 1 printf("Implicant\n"); yices_pp_term_array(stdout, v->size, v->data, 120, UINT32_MAX, 0, 0); printf("(%"PRIu32" literals)\n", v->size); #endif }
/* * Main abstraction function */ static epartition_t *eq_abstract(eq_learner_t *learner, term_t f, bool polarity) { term_table_t *terms; epartition_t *a; assert(is_boolean_term(learner->terms, f)); a = find_cached_abstraction(learner, signed_term(f, polarity)); if (a == NULL) { // not in the cache // remove top-level negation if (is_neg_term(f)) { f = opposite_term(f); polarity = !polarity; } // explore f terms = learner->terms; switch (term_kind(terms, f)) { case EQ_TERM: a = eq_abstract_eq(learner, eq_term_desc(terms, f), polarity); break; case ITE_TERM: case ITE_SPECIAL: a = eq_abstract_ite(learner, ite_term_desc(terms, f), polarity); break; case OR_TERM: a = eq_abstract_or(learner, or_term_desc(terms, f), polarity); break; default: a = empty_epartition(&learner->manager); break; } cache_abstraction(learner, signed_term(f, polarity), a); } return a; }
/* * Flatten all terms in flat->queue to conjuncts * - all terms in the queue must also be in the cache * - f_ite: if true, flatten (ite c a b) * - f_iff: if true, flatten (iff a b) */ static void flattener_build_conjuncts(flattener_t *flat, bool f_ite, bool f_iff) { term_table_t *terms; int_queue_t *queue; composite_term_t *d; term_t t, u, v; uint32_t i, n; queue = &flat->queue; terms = flat->terms; while (! int_queue_is_empty(queue)) { t = int_queue_pop(queue); switch (term_kind(terms, t)) { case ITE_TERM: case ITE_SPECIAL: d = ite_term_desc(terms, t); assert(d->arity == 3); if (f_ite && is_boolean_term(terms, d->arg[1])) { assert(is_boolean_term(terms, d->arg[2])); /* * If t is (ite C A B) * u := (C => A) * v := (not C => B) * Otherwise, t is (not (ite C A B)) * u := (C => not A) * v := (not C => not B) */ u = d->arg[1]; // A v = d->arg[2]; // B if (is_neg_term(t)) { u = opposite_term(u); v = opposite_term(v); } u = mk_implies(flat->manager, d->arg[0], u); // (C => u) v = mk_implies(flat->manager, opposite_term(d->arg[0]), v); // (not C) => v flattener_push_term(flat, u); flattener_push_term(flat, v); continue; } break; case EQ_TERM: d = eq_term_desc(terms, t); assert(d->arity == 2); if (f_iff && is_boolean_term(terms, d->arg[0])) { assert(is_boolean_term(terms, d->arg[1])); /* * t is either (iff A B) or (not (iff A B)): */ u = d->arg[0]; // A v = d->arg[1]; // B if (is_neg_term(t)) { u = opposite_term(u); } // flatten to (u => v) and (v => u) t = mk_implies(flat->manager, u, v); // (u => v) u = mk_implies(flat->manager, v, u); // (v => u); flattener_push_term(flat, t); flattener_push_term(flat, u); continue; } break; case OR_TERM: if (is_neg_term(t)) { /* * t is (not (or a[0] ... a[n-1])) * it flattens to (and (not a[0]) ... (not a[n-1])) */ d = or_term_desc(terms, t); n = d->arity; for (i=0; i<n; i++) { flattener_push_term(flat, opposite_term(d->arg[i])); } continue; } break; default: break; } ivector_push(&flat->resu, t); } // clean up the cache assert(int_queue_is_empty(queue)); int_hset_reset(&flat->cache); }
/* * Process disjuncts and universal quantifiers * - input = all terms in the queue * - f_ite: if true, flatten (ite c a b) * - f_iff: if true, flatten (iff a b) */ static void flattener_forall_disjuncts(flattener_t *flat, bool f_ite, bool f_iff) { term_table_t *terms; int_queue_t *queue; composite_term_t *d; term_t t, u, v; uint32_t i, n; queue = &flat->queue; terms = flat->terms; while (! int_queue_is_empty(queue)) { t = int_queue_pop(queue); switch (term_kind(terms, t)) { case ITE_TERM: case ITE_SPECIAL: d = ite_term_desc(terms, t); assert(d->arity == 3); if (f_ite && is_boolean_term(terms, d->arg[1])) { assert(is_boolean_term(terms, d->arg[2])); /* * If t is (ite C A B) * u := (C AND A) * v := (not C AND B) * Otherwise, t is (not (ite C A B)) * u := (C AND not A) * v := (not C AND not B) */ u = d->arg[1]; // A v = d->arg[2]; // B if (is_neg_term(t)) { u = opposite_term(u); // NOT A v = opposite_term(v); // NOT B } u = mk_binary_and(flat->manager, d->arg[0], u); // (C AND u) v = mk_binary_and(flat->manager, opposite_term(d->arg[0]), v); // (not C) AND v flattener_push_term(flat, u); flattener_push_term(flat, v); continue; } break; case EQ_TERM: d = eq_term_desc(terms, t); assert(d->arity == 2); if (f_iff && is_boolean_term(terms, d->arg[0])) { assert(is_boolean_term(terms, d->arg[1])); /* * t is either (iff A B) or (not (iff A B)): */ u = d->arg[0]; // A v = d->arg[1]; // B if (is_neg_term(t)) { u = opposite_term(u); } // flatten to (u AND v) or ((not u) AND (not v)) t = mk_binary_and(flat->manager, u, v); // (u AND v) u = mk_binary_and(flat->manager, opposite_term(u), opposite_term(v)); // (not u AND not v); flattener_push_term(flat, t); flattener_push_term(flat, u); continue; } break; case OR_TERM: if (is_pos_term(t)) { /* * t is (or a[0] ... a[n-1]) */ d = or_term_desc(terms, t); n = d->arity; for (i=0; i<n; i++) { flattener_push_term(flat, d->arg[i]); } continue; } break; case FORALL_TERM: if (is_pos_term(t)) { d = forall_term_desc(terms, t); n = d->arity; assert(n >= 2); /* * t is (FORALL x_0 ... x_k : body) * body is the last argument in the term descriptor */ flattener_push_term(flat, d->arg[n-1]); continue; } break; default: break; } ivector_push(&flat->resu, t); } // clean up the cache assert(int_queue_is_empty(queue)); int_hset_reset(&flat->cache); }
/* * Convert (if c a b) to a conditional */ void convert_ite_to_conditional(conditional_t *d, term_t c, term_t a, term_t b) { composite_term_t *ite; term_t t, c1, t1, t2; reset_conditional(d); if (is_ite_term(d->terms, b)) { /* * b is either (ite c1 ...) or (not (ite c1 ...). * * we normalize to (ite c1 t1 t2): * - if b is (ite c1 t1 t2) we're done * - if b is (not (ite c1 u1 u2)) we push the negation inside: * so t1 := (not u1) and t2 := (not u2) */ ite = ite_term_desc(d->terms, b); assert(ite->arity == 3); c1 = ite->arg[0]; t1 = ite->arg[1]; t2 = ite->arg[2]; if (is_neg_term(b)) { t1 = opposite_term(t1); t2 = opposite_term(t2); } /* * we try to build the conditional * [c --> a, c1 --> t1, else --> t2] if c and c1 are disjoint * or [c --> a, not c1 --> t2, else --> t1] if c and not c1 are disjoint */ if (incompatible_boolean_terms(d->terms, c, c1)) { conditional_add_pair(d, c, a); conditional_add_pair(d, c1, t1); t = t2; goto loop; } if (incompatible_boolean_terms(d->terms, c, opposite_term(c1))) { conditional_add_pair(d, c, a); conditional_add_pair(d, opposite_term(c1), t2); t = t1; goto loop; } } if (is_ite_term(d->terms, a)) { /* * a is either (ite c1 ...) or (not (ite c1 ...)) * we normalize as above to (ite c1 t1 t2) */ ite = ite_term_desc(d->terms, a); assert(ite->arity == 3); c1 = ite->arg[0]; t1 = ite->arg[1]; t2 = ite->arg[2]; if (is_neg_term(a)) { t1 = opposite_term(t1); t2 = opposite_term(t2); } /* * we try * [not c --> b, c1 --> t1, else --> t2] * or [not c --> b, not c1 --> t2, else --> t1] */ if (incompatible_boolean_terms(d->terms, opposite_term(c), c1)) { conditional_add_pair(d, opposite_term(c), b); conditional_add_pair(d, c1, t1); t = t2; goto loop; } if (incompatible_boolean_terms(d->terms, opposite_term(c), opposite_term(c1))) { conditional_add_pair(d, opposite_term(c), b); conditional_add_pair(d, opposite_term(c1), t2); t = t1; goto loop; } } // Default: found no disjoint conditions conditional_add_pair(d, c, a); d->defval = b; return; // t is the 'else part' loop: while (is_ite_term(d->terms, t)) { // t is (ite c1 t1 t2) ite = ite_term_desc(d->terms, t); assert(ite->arity == 3); c1 = ite->arg[0]; t1 = ite->arg[1]; t2 = ite->arg[2]; if (is_neg_term(t)) { t1 = opposite_term(t1); t2 = opposite_term(t2); } if (disjoint_condition(d, c1)) { conditional_add_pair(d, c1, t1); t = t2; } else if (disjoint_condition(d, opposite_term(c1))) { conditional_add_pair(d, opposite_term(c1), t2); t = t1; } else { break; } } d->defval = t; }
static match_code_t match_term(context_t *ctx, term_t t, term_t *a, term_t *x) { composite_term_t *eq; term_table_t *terms; match_code_t code; term_t t1, t2; if (term_is_false(ctx, t)) { code = MATCH_FALSE; } else { code = MATCH_OTHER; terms = ctx->terms; switch (term_kind(terms, t)) { case OR_TERM: if (is_pos_term(t)) { code = MATCH_OR; } break; case EQ_TERM: eq = eq_term_desc(terms, t); t1 = intern_tbl_get_root(&ctx->intern, eq->arg[0]); t2 = intern_tbl_get_root(&ctx->intern, eq->arg[1]); if (is_boolean_term(terms, t1)) { assert(is_boolean_term(terms, t2)); /* * t is either (iff t1 t2) or (not (iff t1 t2)) * we rewrite (not (iff t1 t2)) to (iff t1 (not t2)) */ if (is_neg_term(t)) { t2 = opposite_term(t2); } /* * Check whether t1 or t2 is true or false */ if (term_is_true(ctx, t1)) { code = MATCH_IFF; *x = t2; } else if (term_is_false(ctx, t1)) { code = MATCH_IFF; *x = opposite_term(t2); } else if (term_is_true(ctx, t2)) { code = MATCH_IFF; *x = t1; } else if (term_is_false(ctx, t2)) { code = MATCH_IFF; *x = opposite_term(t1); } } else if (t1 != t2) { /* * t1 and t2 are not Boolean * if t1 and t2 are equal, we return MATCH_OTHER, since (eq t1 t2) is true */ assert(is_pos_term(t1) && is_pos_term(t2)); if (false_eq(terms, t1, t2)) { code = MATCH_FALSE; } else if (term_is_constant(terms, t1)) { *a = t1; *x = t2; code = MATCH_EQ; } else if (term_is_constant(terms, t2)) { *a = t2; *x = t1; code = MATCH_EQ; } } break; default: break; } } return code; }
/* * Option 3: generalize by computing an implicant then * applying projection. */ static term_t ef_generalize3(ef_solver_t *solver, uint32_t i) { model_t *mdl; ef_cnstr_t *cnstr; ivector_t *v, *w; term_t a[2]; uint32_t n; int32_t code; proj_flag_t pflag; term_t result; assert(i < ef_prob_num_constraints(solver->prob)); // free the current full model if any if (solver->full_model != NULL) { yices_free_model(solver->full_model); solver->full_model = NULL; } // build the full_map and the corresponding model. ef_build_full_map(solver, i); n = solver->all_vars.size; assert(n == solver->all_values.size); mdl = yices_model_from_map(n, solver->all_vars.data, solver->all_values.data); if (mdl == NULL) { // error in the model construction solver->status = EF_STATUS_MDL_ERROR; solver->error_code = yices_error_code(); return NULL_TERM; } solver->full_model = mdl; // Constraint cnstr = solver->prob->cnstr + i; a[0] = cnstr->assumption; // B(y) a[1] = opposite_term(cnstr->guarantee); // not C(x, y) #if EF_VERBOSE printf("Constraint:\n"); yices_pp_term_array(stdout, 2, a, 120, UINT32_MAX, 0, 0); printf("(%"PRIu32" literals)\n", 2); #endif // Compute the implicant v = &solver->implicant; ivector_reset(v); code = get_implicant(mdl, solver->prob->manager, LIT_COLLECTOR_ALL_OPTIONS, 2, a, v); if (code < 0) { solver->status = EF_STATUS_IMPLICANT_ERROR; solver->error_code = code; return NULL_TERM; } #if EF_VERBOSE printf("Implicant:\n"); yices_pp_term_array(stdout, v->size, v->data, 120, UINT32_MAX, 0, 0); printf("(%"PRIu32" literals)\n", v->size); #endif // Projection w = &solver->projection; ivector_reset(w); n = ef_constraint_num_uvars(cnstr); #if EF_VERBOSE printf("(%"PRIu32" universals)\n", n); yices_pp_term_array(stdout, n, cnstr->uvars, 120, UINT32_MAX, 0, 0); #endif pflag = project_literals(mdl, solver->prob->manager, v->size, v->data, n, cnstr->uvars, w); if (pflag != PROJ_NO_ERROR) { solver->status = EF_STATUS_PROJECTION_ERROR; solver->error_code = pflag; return NULL_TERM; } #if EF_VERBOSE printf("Projection:\n"); yices_pp_term_array(stdout, w->size, w->data, 120, UINT32_MAX, 0, 0); printf("(%"PRIu32" literals)\n", w->size); #endif switch (w->size) { case 0: result = true_term; break; case 1: result = w->data[0]; break; default: result = mk_and(solver->prob->manager, w->size, w->data); break; } return opposite_term(result); }