Exemple #1
0
/*
 * Merge the exists and forall variables of constraint i
 * - store the variables in solver->all_vars
 * - store their values in solver->all_values
 */
static void ef_build_full_map(ef_solver_t *solver, uint32_t i) {
  ef_cnstr_t *cnstr;
  ivector_t *v;
  uint32_t n;

  assert(i < ef_prob_num_constraints(solver->prob));
  cnstr = solver->prob->cnstr + i;

  // collect all variables of cnstr in solver->all_vars
  v = &solver->all_vars;
  ivector_reset(v);
  n = ef_constraint_num_evars(cnstr);
  ivector_add(v, cnstr->evars, n);
  n = ef_constraint_num_uvars(cnstr);
  ivector_add(v, cnstr->uvars, n);

  // project the evalues onto the exists variable of constraint i
  // store the results in all_values
  v = &solver->all_values;
  n = ef_constraint_num_evars(cnstr);
  resize_ivector(v, n);
  v->size = n;
  ef_project_exists_model(solver->prob, solver->evalue, cnstr->evars, v->data, n);

  // copy the uvalues for constraint i (from uvalue_aux to v)
  n = ef_constraint_num_uvars(cnstr);
  assert(n == solver->uvalue_aux.size);
  ivector_add(v, solver->uvalue_aux.data, n);

#if EF_VERBOSE
  printf("Full map\n");
  print_full_map(stdout, solver);
  fflush(stdout);
#endif
}
Exemple #2
0
/*
 * Initialize the exists context
 * - asserts all conditions from solver->prob
 * - if max_samples is positive, also apply pre-sampling to all the universal constraints
 *
 * - solver->status is set as follows
 *   EF_STATUS_SEARCHING: means not solved yet
 *   EF_STATUS_UNSAT: if the exists context is trivially UNSAT
 *   EF_STATUS_..._ERROR: if something goes wrong
 *   EF_STATUS_INTERRUPTED if the pre-sampling is interrupted
 */
static void ef_solver_start(ef_solver_t *solver) {
  int32_t code;
  uint32_t i, n;

  assert(solver->status == EF_STATUS_IDLE);

  solver->status = EF_STATUS_SEARCHING;

  init_exists_context(solver);
  code = exists_context_assert_conditions(solver); // add all conditions
  if (code < 0) {
    // assertion error
    solver->status = EF_STATUS_ASSERT_ERROR;
    solver->error_code = code;
  } else if (code == TRIVIALLY_UNSAT) {
    solver->status = EF_STATUS_UNSAT;
  } else if (solver->max_samples > 0) {
    /*
     * run the pre-sampling stuff
     */
    n = ef_prob_num_constraints(solver->prob);
    for (i=0; i<n; i++) {
      ef_sample_constraint(solver, i);
      if (solver->status != EF_STATUS_SEARCHING) break;
    }
  }
}
Exemple #3
0
/*
 * Option 2: generalize by substitution
 * - return (prob->cnstr[i].guarantee with y := value)
 */
static term_t ef_generalize2(ef_prob_t *prob, uint32_t i, term_t *value) {
  ef_cnstr_t *cnstr;
  uint32_t n;
  term_t g;

  assert(i < ef_prob_num_constraints(prob));
  cnstr = prob->cnstr + i;
  n = ef_constraint_num_uvars(cnstr);
  g = ef_substitution(prob, cnstr->uvars, value, n, cnstr->guarantee);
  return g;
}
Exemple #4
0
/*
 * Learn a constraint for the exists variable based on the
 * existing forall witness for constraint i:
 * - the witness is defined by the uvars of constraint i
 *   and the values stored in uvalue_aux.
 *
 * This function adds a constraint to the exists_context that will remove the
 * current exists model:
 * - if solver->option is EF_NOGEN_OPTION, the new constraint is
 *   of the form (or (/= var[0] eval[k0]) ... (/= var[k-1] eval[k-1]))
 *   where var[0 ... k-1] are the exist variables of constraint i
 * - if solver->option is EF_GEN_BY_SUBST_OPTION, we build a new
 *   constraint by substitution (option 2)
 *
 * If something goes wrong, then solver->status is updated to EF_STATUS_ERROR.
 * If the new learned assertion makes the exist context trivially unsat
 * then context->status is set to EF_STATUS_UNSAT.
 * Otherwise context->status is kept as is.
 */
static void ef_solver_learn(ef_solver_t *solver, uint32_t i) {
  ef_cnstr_t *cnstr;
  term_t *val;
  term_t new_constraint;
  uint32_t n;
  int32_t code;

  assert(i < ef_prob_num_constraints(solver->prob));
  cnstr = solver->prob->cnstr + i;

  switch (solver->option) {
  case EF_NOGEN_OPTION:
    /*
     * project solver->evalues on the existential
     * variables that occur in constraint i.
     * then build (or (/= evar[0] val[0]) ... (/= evar[n-1] val[n-1]))
     */
    n = ef_constraint_num_evars(cnstr);
    resize_ivector(&solver->evalue_aux, n);
    solver->evalue_aux.size = n;
    val = solver->evalue_aux.data;
    ef_project_exists_model(solver->prob, solver->evalue, cnstr->evars, val, n);
    new_constraint = ef_generalize1(solver->prob, cnstr->evars, val, n);
    break;

  case EF_GEN_BY_SUBST_OPTION:
    val = solver->uvalue_aux.data;
    new_constraint = ef_generalize2(solver->prob, i, val);
    if (new_constraint < 0) {
      // error in substitution
      solver->status = EF_STATUS_SUBST_ERROR;
      solver->error_code = new_constraint;
      return;
    }
    break;

  case EF_GEN_BY_PROJ_OPTION:
  default: // added this to prevent bogus GCC warning
    new_constraint = ef_generalize3(solver, i);
    if (new_constraint < 0) {     
      return;
    }
    break;
  }

  // add the new constraint to the exists context
  code = update_exists_context(solver, new_constraint);
  if (code == TRIVIALLY_UNSAT) {
    solver->status = EF_STATUS_UNSAT;
  } else if (code < 0) {
    solver->status = EF_STATUS_ASSERT_ERROR;
    solver->error_code = code;
  }
}
Exemple #5
0
/*
 * Show witness found for constraint i
 */
void print_forall_witness(FILE *f, ef_solver_t *solver, uint32_t i) {
  ef_prob_t *prob;
  ef_cnstr_t *cnstr;
  uint32_t j, n;

  prob = solver->prob;
  assert(i < ef_prob_num_constraints(prob));
  cnstr = prob->cnstr + i;

  n = ef_constraint_num_uvars(cnstr);
  for (j=0; j<n; j++) {
    fprintf(f, "%s := ", yices_get_term_name(cnstr->uvars[j]));
    yices_pp_term(f, solver->uvalue_aux.data[j], 100, 1, 10);
  }
}
Exemple #6
0
/*
 * Check whether the current exists_model can be falsified by one
 * of the universal constraints.
 * - scan the universal constraints starting from solver->scan_idx
 * - the current exists model is defined by
 *   the mapping from solver->prob->evars to solver->evalues
 *
 * Update the solver->status as follows:
 * - if no constraint falsifies the model, solver->status = EF_STATUS_SAT
 * - if some constraint falsifies the model, then solver->status may be
 *   updated to EF_STATUS_UNSAT (trivially unsat after learning)
 * - if something goes wrong, solver->status = EF_STATUS_ERROR
 *
 * If constraint i falsifies the model then solver->scan_idx is
 * set to (i+1) modulo num_constraints.
 */
static void  ef_solver_check_exists_model(ef_solver_t *solver) {
  smt_status_t status;
  uint32_t i, n;

  n = ef_prob_num_constraints(solver->prob);

  /*
   * Special case: if there are no universal constraints
   * we return SAT immediately.
   */
  if (n == 0) {
    solver->status = EF_STATUS_SAT;
    return;
  }

  i = solver->scan_idx;
  do {
    tprintf(solver->trace, 4, "(EF: testing candidate against constraint %"PRIu32")\n", i);
    status = ef_solver_test_exists_model(solver, i);
    trace_candidate_check(solver, i, status);

    switch (status) {
    case STATUS_SAT:
    case STATUS_UNKNOWN:
#if EF_VERBOSE
      printf("Counterexample for constraint[%"PRIu32"]\n", i);
      print_forall_witness(stdout, solver, i);
      printf("\n");
      fflush(stdout);
#endif
      ef_solver_learn(solver, i);

    default:
      i ++;
      if (i == n) {
	i = 0;
      }
      break;
    }

  } while (status == STATUS_UNSAT && i != solver->scan_idx);

  solver->scan_idx = i; // prepare for the next call
  if (status == STATUS_UNSAT) {
    // done a full scan
    solver->status = EF_STATUS_SAT;
  }
}
Exemple #7
0
/*
 * 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

}
Exemple #8
0
/*
 * Sampling for the i-th constraint in solver->prob
 * - search for at most max_samples y's that satisfy the assumption of constraint i in prob
 * - for each such y, add a new constraint to the exist context ctx
 * - max_samples = bound on the number of samples to take (must be positive)
 *
 * Constraint i is of the form
 *    (FORALL Y_i: B_i(Y_i) => C(X_i, Y_i))
 * - every sample is a model y_i that satisfies B_i.
 * - for each sample, we learn that any good candidate X_i must satisfy C(X_i, y_i).
 *   So we add the constraint  C(X_i, y_i) to ctx.
 *
 * Update the solver->status to
 * - EF_STATUS_UNSAT if the exits context is trivially unsat
 * - EF_STATUS_..._ERRORif something goes wrong
 * - EF_STATUS_INTERRUPTED if a call to check/recheck context is interrupted
 * keep it unchanged otherwise (should be EF_STATUS_SEARCHING).
 */
static void ef_sample_constraint(ef_solver_t *solver, uint32_t i) {
  context_t *sampling_ctx;
  ef_cnstr_t *cnstr;
  term_t *value;
  uint32_t nvars, samples;
  int32_t ucode, ecode;
  smt_status_t status;
  term_t cnd;

  assert(i < ef_prob_num_constraints(solver->prob) && solver->max_samples > 0);

  cnstr = solver->prob->cnstr + i;

  /*
   * make uvalue_aux large enough
   * its size = number of universal variables in constraint i
   */
  nvars = ef_constraint_num_uvars(cnstr);
  resize_ivector(&solver->uvalue_aux, nvars);
  solver->uvalue_aux.size = nvars;
  value = solver->uvalue_aux.data;

  samples = solver->max_samples;

  /*
   * assert the assumption in the forall context
   */
  sampling_ctx = get_forall_context(solver);
  ucode = assert_formula(sampling_ctx, cnstr->assumption);
  while (ucode == CTX_NO_ERROR) {
    status = satisfy_context(sampling_ctx, solver->parameters, cnstr->uvars, nvars, value, NULL);
    switch (status) {
    case STATUS_SAT:
    case STATUS_UNKNOWN:
      // learned condition on X:
      cnd = ef_substitution(solver->prob, cnstr->uvars, value, nvars, cnstr->guarantee);
      if (cnd < 0) {
	solver->status = EF_STATUS_SUBST_ERROR;
	solver->error_code = cnd;
	goto done;
      }
      ecode = update_exists_context(solver, cnd);
      if (ecode < 0) {
	solver->status = EF_STATUS_ASSERT_ERROR;
	solver->error_code = ecode;
	goto done;
      }
      if (ecode == TRIVIALLY_UNSAT) {
	solver->status = EF_STATUS_UNSAT;
	goto done;
      }
      break;

    case STATUS_UNSAT:
      // no more samples for this constraints
      goto done;

    case STATUS_INTERRUPTED:
      solver->status = EF_STATUS_INTERRUPTED;
      goto done;

    default:
      solver->status = EF_STATUS_CHECK_ERROR;
      solver->error_code = status;
      goto done;
    }

    samples --;
    if (samples == 0) goto done;

    ucode = assert_blocking_clause(sampling_ctx);
  }

  /*
   * if ucode < 0, something went wrong in assert_formula
   * or in assert_blocking_clause
   */
  if (ucode < 0) {
    solver->status = EF_STATUS_ASSERT_ERROR;
    solver->error_code = ucode;
  }

 done:
  clear_forall_context(solver, true);
}
Exemple #9
0
/*
 * Test the current exists model using universal constraint i
 * - i must be a valid index (i.e., 0 <= i < solver->prob->num_cnstr)
 * - this checks the assertion B_i and not C_i after replacing existential
 *   variables by their values (stored in evalue)
 * - return code:
 *   if STATUS_SAT (or STATUS_UNKNOWN): a model of (B_i and not C_i)
 *   is found and stored in uvalue_aux
 *   if STATUS_UNSAT: no model found (current exists model is good as
 *   far as constraint i is concerned)
 *   anything else: an error or interruption
 *
 * - if we get an error or interruption, solver->status is updated
 *   otherwise, it is kept as is (should be EF_STATUS_SEARCHING)
 */
static smt_status_t ef_solver_test_exists_model(ef_solver_t *solver, uint32_t i) {
  context_t *forall_ctx;
  ef_cnstr_t *cnstr;
  term_t *value;
  term_t g;
  uint32_t n;
  int32_t code;
  smt_status_t status;

  assert(i < ef_prob_num_constraints(solver->prob));
  cnstr = solver->prob->cnstr + i;

  n = ef_prob_num_evars(solver->prob);
  g = ef_substitution(solver->prob, solver->prob->all_evars, solver->evalue, n, cnstr->guarantee);
  if (g < 0) {
    // error in substitution
    solver->status = EF_STATUS_SUBST_ERROR;
    solver->error_code = g;
    return STATUS_ERROR;
  }

  /*
   * make uvalue_aux large enough
   */
  n = ef_constraint_num_uvars(cnstr);
  resize_ivector(&solver->uvalue_aux, n);
  solver->uvalue_aux.size = n;
  value = solver->uvalue_aux.data;

  forall_ctx = get_forall_context(solver);

  code = forall_context_assert(solver, cnstr->assumption, g); // assert B_i(Y_i) and not g(Y_i)
  if (code == CTX_NO_ERROR) {
    status = satisfy_context(forall_ctx, solver->parameters, cnstr->uvars, n, value, NULL);
    switch (status) {
    case STATUS_SAT:
    case STATUS_UNKNOWN:
    case STATUS_UNSAT:
      break;

    case STATUS_INTERRUPTED:
      solver->status = EF_STATUS_INTERRUPTED;
      break;

    default:
      solver->status = EF_STATUS_CHECK_ERROR;
      solver->error_code = status;
      break;
    }

  } else if (code == TRIVIALLY_UNSAT) {
    assert(context_status(forall_ctx) == STATUS_UNSAT);
    status = STATUS_UNSAT;

  } else {
    // error in assertion
    solver->status = EF_STATUS_ASSERT_ERROR;
    solver->error_code = code;
    status = STATUS_ERROR;
  }

  clear_forall_context(solver, true);

  return status;
}
Exemple #10
0
/*
 * Sample exists models
 * - stop when we find one that's not refuted by the forall constraints
 * - or when we reach the iteration bound
 *
 * Result:
 * - solver->status = EF_STATUS_ERROR if something goes wrong
 * - solver->status = EF_STATUS_INTERRUPTED if one of the calls to
 *   check_context is interrupted
 * - solver->status = EF_STATUS_UNSAT if all efmodels have been tried and none
 *   of them worked
 * - solver->status = EF_STATUS_UNKNOWN if the iteration limit is reached
 * - solver->status = EF_STATUS_SAT if a good model is found
 *
 * In the later case,
 * - the model is stored in solver->exists_model
 * - also it's available as a mapping form solver->prob->evars to solver->evalues
 *
 * Also solver->iters stores the number of iterations used.
 */
static void ef_solver_search(ef_solver_t *solver) {
  smt_status_t stat;
  uint32_t i, max;

  max = solver->max_iters;
  i = 0;

  assert(max > 0);

#if 0
  printf("\nEF search: %"PRIu32" constraints, %"PRIu32" exists vars, %"PRIu32" forall vars\n",
	 ef_prob_num_constraints(solver->prob),
	 ef_prob_num_evars(solver->prob),
	 ef_prob_num_uvars(solver->prob));

  //  printf("\nConditions on the exists variables:\n");
  //  yices_pp_term_array(stdout, ef_prob_num_conditions(solver->prob), solver->prob->conditions, 120, UINT32_MAX, 0, 0);
#endif

  ef_solver_start(solver);
  while (solver->status == EF_STATUS_SEARCHING && i < max) {

#if 0
    printf("\n--- Iteration %"PRIu32" (scan_idx = %"PRIu32") ---\n", i, solver->scan_idx);
#endif

    stat = ef_solver_check_exists(solver);
    switch (stat) {
    case STATUS_SAT:
    case STATUS_UNKNOWN:
      // we have a candidate exists model
      // check it and learn what we can

#if 0
      // FOR DEBUGGING
      printf("Candidate exists model:\n");
      print_ef_solution(stdout, solver);
      printf("\n");
#endif

      ef_solver_check_exists_model(solver);
      break;

    case STATUS_UNSAT:
      solver->status = EF_STATUS_UNSAT;
      break;

    case STATUS_INTERRUPTED:
      solver->status = EF_STATUS_INTERRUPTED;
      break;

    default:
      solver->status = EF_STATUS_CHECK_ERROR;
      solver->error_code = stat;
      break;
    }

    i ++;
  }

  /*
   * Cleanup and set status if i == max
   */
  if (solver->status != EF_STATUS_SAT && solver->exists_model != NULL) {
    yices_free_model(solver->exists_model);
    solver->exists_model = NULL;
    if (solver->status == EF_STATUS_SEARCHING) {
      assert(i == max);
      solver->status = EF_STATUS_UNKNOWN;
    }
  }

  solver->iters = i;
}
Exemple #11
0
/*
 * 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);
}