• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

SRI-CSL / yices2 / 22552082017

01 Mar 2026 08:35PM UTC coverage: 65.387% (+0.07%) from 65.317%
22552082017

push

github

web-flow
Mcsat unsat core (#578)

* mcsat unsat core

* update test

* test

* boolean labels

* another test

* update doc

* fix mcsat unsat core issue + tests

* fix

* minor

* Refactor MCSAT assumptions-core support into context layer

* Fix MCSAT check-sat-assuming regressions and assumption cleanup

* Add no-mcsat stub for assumption cleanup

---------

Co-authored-by: Stéphane Graham-Lengrand <stephane.graham-lengrand@csl.sri.com>
Co-authored-by: Stephane Graham-Lengrand <stephane.graham-lengrand@sri.com>

155 of 202 new or added lines in 5 files covered. (76.73%)

40 existing lines in 2 files now uncovered.

81484 of 124618 relevant lines covered (65.39%)

1708166.98 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

71.13
/src/context/context_solver.c
1
/*
2
 * This file is part of the Yices SMT Solver.
3
 * Copyright (C) 2017 SRI International.
4
 *
5
 * Yices is free software: you can redistribute it and/or modify
6
 * it under the terms of the GNU General Public License as published by
7
 * the Free Software Foundation, either version 3 of the License, or
8
 * (at your option) any later version.
9
 *
10
 * Yices is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 * GNU General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU General Public License
16
 * along with Yices.  If not, see <http://www.gnu.org/licenses/>.
17
 */
18

19
/*
20
 * SEARCH AND SOLVING PROCEDURES
21
 *
22
 * This module implements the check_context function (and variants).
23
 * It also implements model construction.
24
 */
25

26
#include <stdbool.h>
27
#include <assert.h>
28
#include <stdint.h>
29
#include <inttypes.h>
30
#include <stdio.h>
31

32
#include "context/context.h"
33
#include "context/internalization_codes.h"
34
#include "model/models.h"
35
#include "mcsat/solver.h"
36
#include "solvers/bv/dimacs_printer.h"
37
#include "solvers/cdcl/delegate.h"
38
#include "solvers/funs/fun_solver.h"
39
#include "solvers/simplex/simplex.h"
40
#include "terms/term_explorer.h"
41
#include "terms/term_manager.h"
42
#include "terms/term_substitution.h"
43
#include "utils/int_hash_map.h"
44
#include "utils/int_hash_sets.h"
45

46
#include "api/yices_globals.h"
47
#include "mt/thread_macros.h"
48

49

50

51
/*
52
 * TRACE FUNCTIONS
53
 */
54

55
/*
56
 * Basic statistics
57
 */
58
static void trace_stats(smt_core_t *core, const char *when, uint32_t level) {
93,254✔
59
  trace_printf(core->trace, level,
93,254✔
60
               "(%-10s %8"PRIu64" %10"PRIu64" %8"PRIu64" %8"PRIu32" %8"PRIu32" %8"PRIu64" %8"PRIu32" %8"PRIu64" %7.1f)\n",
61
               when, core->stats.conflicts, core->stats.decisions, core->stats.random_decisions,
62
               num_binary_clauses(core), num_prob_clauses(core), num_prob_literals(core),
63
               num_learned_clauses(core), num_learned_literals(core), avg_learned_clause_size(core));
64
#if 0
65
  fprintf(stderr,
66
          "(%-10s %8"PRIu64" %10"PRIu64" %8"PRIu64" %8"PRIu32" %8"PRIu32" %8"PRIu64" %8"PRIu32" %8"PRIu64" %7.1f)\n",
67
          when, core->stats.conflicts, core->stats.decisions, core->stats.random_decisions,
68
          num_binary_clauses(core), num_prob_clauses(core), num_prob_literals(core),
69
          num_learned_clauses(core), num_learned_literals(core), avg_learned_clause_size(core));
70
#endif
71
}
93,254✔
72

73
/*
74
 * On start_search
75
 */
76
static void trace_start(smt_core_t *core) {
45,385✔
77
  trace_stats(core, "start:", 1);
45,385✔
78
}
45,385✔
79

80

81
/*
82
 * On restart
83
 */
84
static void trace_restart(smt_core_t *core) {
2,213✔
85
  trace_stats(core, "restart:", 1);
2,213✔
86
}
2,213✔
87

88
static void trace_inner_restart(smt_core_t *core) {
93✔
89
  trace_stats(core, "inner restart:", 5);
93✔
90
}
93✔
91

92

93
/*
94
 * On reduce clause database
95
 */
96
static void trace_reduce(smt_core_t *core, uint64_t deleted) {
178✔
97
  trace_stats(core, "reduce:", 3);
178✔
98
  trace_printf(core->trace, 4, "(%"PRIu64" clauses deleted)\n", deleted);
178✔
99
}
178✔
100

101

102

103
/*
104
 * End of search
105
 */
106
static void trace_done(smt_core_t *core) {
45,385✔
107
  trace_stats(core, "done:", 1);
45,385✔
108
  trace_newline(core->trace, 1);
45,385✔
109
}
45,385✔
110

111

112

113
/*
114
 * PROCESS AN ASSUMPTION
115
 */
116

117
/*
118
 * l = assumption for the current decision level
119
 * If l is unassigned, we assign it and perform one round of propagation
120
 * If l is false, we record the conflict. The context is unsat under the
121
 * current set of assumptions.
122
 */
123
static void process_assumption(smt_core_t *core, literal_t l) {
90,162✔
124
  switch (literal_value(core, l)) {
90,162✔
125
  case VAL_UNDEF_FALSE:
87,588✔
126
  case VAL_UNDEF_TRUE:
127
    decide_literal(core, l);
87,588✔
128
    smt_process(core);
87,588✔
129
    break;
87,588✔
130

131
  case VAL_TRUE:
×
132
    break;
×
133

134
  case VAL_FALSE:
2,574✔
135
    save_conflicting_assumption(core, l);
2,574✔
136
    break;
2,574✔
137
  }
138
}
90,162✔
139

140

141
/*
142
 * MAIN SEARCH FUNCTIONS
143
 */
144

145
/*
146
 * Bounded search with the default branching heuristic (picosat-like)
147
 * - search until the conflict bound is reached or until the problem is solved.
148
 * - reduce_threshold: number of learned clauses above which reduce_clause_database is called
149
 * - r_factor = increment factor for reduce_threshold
150
 * - use the default branching heuristic implemented by the core
151
 */
152
static void search(smt_core_t *core, uint32_t conflict_bound, uint32_t *reduce_threshold, double r_factor) {
13,423✔
153
  uint64_t max_conflicts;
154
  uint64_t deletions;
155
  uint32_t r_threshold;
156
  literal_t l;
157

158
  assert(smt_status(core) == YICES_STATUS_SEARCHING || smt_status(core) == YICES_STATUS_INTERRUPTED);
159

160
  max_conflicts = num_conflicts(core) + conflict_bound;
13,423✔
161
  r_threshold = *reduce_threshold;
13,423✔
162

163
  smt_process(core);
13,423✔
164
  while (smt_status(core) == YICES_STATUS_SEARCHING && num_conflicts(core) <= max_conflicts) {
6,295,988✔
165
    // reduce heuristic
166
    if (num_learned_clauses(core) >= r_threshold) {
6,282,565✔
167
      deletions = core->stats.learned_clauses_deleted;
67✔
168
      reduce_clause_database(core);
67✔
169
      r_threshold = (uint32_t) (r_threshold * r_factor);
67✔
170
      trace_reduce(core, core->stats.learned_clauses_deleted - deletions);
67✔
171
    }
172

173
    // assumption
174
    if (core->has_assumptions) {
6,282,565✔
175
      l = get_next_assumption(core);
×
176
      if (l != null_literal) {
×
177
        process_assumption(core, l);
×
178
        continue;
×
179
      }
180
    }
181

182
    // decision
183
    l = select_unassigned_literal(core);
6,282,565✔
184
    if (l == null_literal) {
6,282,565✔
185
      // all variables assigned: Call final_check
186
      smt_final_check(core);
16,505✔
187
    } else {
188
      decide_literal(core, l);
6,266,060✔
189
      smt_process(core);
6,266,060✔
190
    }
191
  }
192

193
  *reduce_threshold = r_threshold;
13,423✔
194
}
13,423✔
195

196

197
/*
198
 * HACK: Variant for Luby restart:
199
 * - search until the conflict bound is reached or until the problem is solved.
200
 * - reduce_threshold: number of learned clauses above which reduce_clause_database is called
201
 * - r_factor = increment factor for reduce_threshold
202
 * - use the default branching heuristic implemented by the core
203
 *
204
 * This uses smt_bounded_process to force more frequent restarts.
205
 */
206
static void luby_search(smt_core_t *core, uint32_t conflict_bound, uint32_t *reduce_threshold, double r_factor) {
13,420✔
207
  uint64_t max_conflicts;
208
  uint64_t deletions;
209
  uint32_t r_threshold;
210
  literal_t l;
211

212
  assert(smt_status(core) == YICES_STATUS_SEARCHING || smt_status(core) == YICES_STATUS_INTERRUPTED);
213

214
  max_conflicts = num_conflicts(core) + conflict_bound;
13,420✔
215
  r_threshold = *reduce_threshold;
13,420✔
216

217
  smt_bounded_process(core, max_conflicts);
13,420✔
218
  while (smt_status(core) == YICES_STATUS_SEARCHING && num_conflicts(core) < max_conflicts) {
682,400✔
219
    // reduce heuristic
220
    if (num_learned_clauses(core) >= r_threshold) {
668,980✔
221
      deletions = core->stats.learned_clauses_deleted;
50✔
222
      reduce_clause_database(core);
50✔
223
      r_threshold = (uint32_t) (r_threshold * r_factor);
50✔
224
      trace_reduce(core, core->stats.learned_clauses_deleted - deletions);
50✔
225
    }
226

227
    // assumption
228
    if (core->has_assumptions) {
668,980✔
229
      l = get_next_assumption(core);
100,953✔
230
      if (l != null_literal) {
100,953✔
231
        process_assumption(core, l);
90,160✔
232
        continue;
90,160✔
233
      }
234
    }
235

236
    // decision
237
    l = select_unassigned_literal(core);
578,820✔
238
    if (l == null_literal) {
578,820✔
239
      // all variables assigned: Call final_check
240
      smt_final_check(core);
8,778✔
241
    } else {
242
      decide_literal(core, l);
570,042✔
243
      smt_bounded_process(core, max_conflicts);
570,042✔
244
    }
245
  }
246

247
  *reduce_threshold = r_threshold;
13,420✔
248
}
13,420✔
249

250
/*
251
 * Polarity selection (implements branching heuristics)
252
 * - filter is given a literal l + core and must return either l or not l
253
 */
254
typedef literal_t (*branching_fun_t)(smt_core_t *core, literal_t l);
255

256

257
/*
258
 * Bounded search with a non-default branching heuristics
259
 * - search until the conflict bound is reached or until the problem is solved.
260
 * - reduce_threshold: number of learned clauses above which reduce_clause_database is called
261
 * - r_factor = increment factor for reduce_threshold
262
 * - use the branching heuristic implemented by branch
263
 */
264
static void special_search(smt_core_t *core, uint32_t conflict_bound, uint32_t *reduce_threshold,
20,848✔
265
                           double r_factor, branching_fun_t branch) {
266
  uint64_t max_conflicts;
267
  uint64_t deletions;
268
  uint32_t r_threshold;
269
  literal_t l;
270

271
  assert(smt_status(core) == YICES_STATUS_SEARCHING || smt_status(core) == YICES_STATUS_INTERRUPTED);
272

273
  max_conflicts = num_conflicts(core) + conflict_bound;
20,848✔
274
  r_threshold = *reduce_threshold;
20,848✔
275

276
  smt_process(core);
20,848✔
277
  while (smt_status(core) == YICES_STATUS_SEARCHING && num_conflicts(core) <= max_conflicts) {
634,240✔
278
    // reduce heuristic
279
    if (num_learned_clauses(core) >= r_threshold) {
613,392✔
280
      deletions = core->stats.learned_clauses_deleted;
61✔
281
      reduce_clause_database(core);
61✔
282
      r_threshold = (uint32_t) (r_threshold * r_factor);
61✔
283
      trace_reduce(core, core->stats.learned_clauses_deleted - deletions);
61✔
284
    }
285

286
    // assumption
287
    if (core->has_assumptions) {
613,392✔
288
      l = get_next_assumption(core);
2✔
289
      if (l != null_literal) {
2✔
290
        process_assumption(core, l);
2✔
291
        continue;
2✔
292
      }
293
    }
294

295
    // decision
296
    l = select_unassigned_literal(core);
613,390✔
297
    if (l == null_literal) {
613,390✔
298
      // all variables assigned: call final check
299
      smt_final_check(core);
22,618✔
300
    } else {
301
      // apply the branching heuristic
302
      l = branch(core, l);
590,772✔
303
      // propagation
304
      decide_literal(core, l);
590,772✔
305
      smt_process(core);
590,772✔
306
    }
307
  }
308

309
  *reduce_threshold = r_threshold;
20,848✔
310
}
20,848✔
311

312

313

314

315

316
/*
317
 * SUPPORTED BRANCHING
318
 */
319

320
/*
321
 * Simple branching heuristics:
322
 * - branch to the negative polarity
323
 * - branch to the positive polarity
324
 */
325
static literal_t negative_branch(smt_core_t *core, literal_t l) {
176,506✔
326
  return l | 1; // force the sign bit to 1
176,506✔
327
}
328

329
static literal_t positive_branch(smt_core_t *core, literal_t l) {
21,277✔
330
  return l & ~1; // force the sign bit to 0
21,277✔
331
}
332

333

334
/*
335
 * For literals with no atom, use the default, otherwise let the theory solver decide
336
 */
337
static literal_t theory_branch(smt_core_t *core, literal_t l) {
392,989✔
338
  if (bvar_has_atom(core, var_of(l))) {
392,989✔
339
    l =  core->th_smt.select_polarity(core->th_solver, get_bvar_atom(core, var_of(l)), l);
319,618✔
340
  }
341
  return l;
392,989✔
342
}
343

344
// variants
345
static literal_t theory_or_neg_branch(smt_core_t *core, literal_t l) {
×
346
  if (bvar_has_atom(core, var_of(l))) {
×
347
    return core->th_smt.select_polarity(core->th_solver, get_bvar_atom(core, var_of(l)), l);
×
348
  } else {
349
    return l | 1;
×
350
  }
351
}
352

353
static literal_t theory_or_pos_branch(smt_core_t *core, literal_t l) {
×
354
  if (bvar_has_atom(core, var_of(l))) {
×
355
    return core->th_smt.select_polarity(core->th_solver, get_bvar_atom(core, var_of(l)), l);
×
356
  } else {
357
    return l & ~1;
×
358
  }
359
}
360

361

362

363

364

365
/*
366
 * CORE SOLVER
367
 */
368

369
/*
370
 * Full solver:
371
 * - params: heuristic parameters.
372
 * - n = number of assumptions
373
 * - a = array of n assumptions: a[0 ... n-1] must all be literals
374
 */
375
static void solve(smt_core_t *core, const param_t *params, uint32_t n, const literal_t *a) {
45,385✔
376
  bool luby;
377
  uint32_t c_threshold, d_threshold; // Picosat-style
378
  uint32_t u, v, period;             // for Luby-style
379
  uint32_t reduce_threshold;
380

381
  c_threshold = params->c_threshold;
45,385✔
382
  d_threshold = c_threshold; // required by trace_start in slow_restart mode
45,385✔
383
  luby = false;
45,385✔
384
  u = 1;
45,385✔
385
  v = 1;
45,385✔
386
  period = c_threshold;
45,385✔
387

388
  if (params->fast_restart) {
45,385✔
389
    d_threshold = params->d_threshold;
11,707✔
390
    // HACK to activate the Luby heuristic:
391
    // c_factor must be 0.0 and fast_restart must be true
392
    luby = params->c_factor == 0.0;
11,707✔
393
  }
394

395
  reduce_threshold = (uint32_t) (num_prob_clauses(core) * params->r_fraction);
45,385✔
396
  if (reduce_threshold < params->r_threshold) {
45,385✔
397
    reduce_threshold = params->r_threshold;
45,354✔
398
  }
399

400
  // initialize then do a propagation + simplification step.
401
  start_search(core, n, a);
45,385✔
402
  trace_start(core);
45,385✔
403
  if (smt_status(core) == YICES_STATUS_SEARCHING) {
45,385✔
404
    // loop
405
    for (;;) {
406
      switch (params->branching) {
47,691✔
407
      case BRANCHING_DEFAULT:
26,843✔
408
        if (luby) {
26,843✔
409
          luby_search(core, c_threshold, &reduce_threshold, params->r_factor);
13,420✔
410
        } else {
411
          search(core, c_threshold, &reduce_threshold, params->r_factor);
13,423✔
412
        }
413
        break;
26,843✔
414
      case BRANCHING_NEGATIVE:
180✔
415
        special_search(core, c_threshold, &reduce_threshold, params->r_factor, negative_branch);
180✔
416
        break;
180✔
417
      case BRANCHING_POSITIVE:
182✔
418
        special_search(core, c_threshold, &reduce_threshold, params->r_factor, positive_branch);
182✔
419
        break;
182✔
420
      case BRANCHING_THEORY:
20,486✔
421
        special_search(core, c_threshold, &reduce_threshold, params->r_factor, theory_branch);
20,486✔
422
        break;
20,486✔
423
      case BRANCHING_TH_NEG:
×
424
        special_search(core, c_threshold, &reduce_threshold, params->r_factor, theory_or_neg_branch);
×
425
        break;
×
426
      case BRANCHING_TH_POS:
×
427
        special_search(core, c_threshold, &reduce_threshold, params->r_factor, theory_or_pos_branch);
×
428
        break;
×
429
      }
430

431
      if (smt_status(core) != YICES_STATUS_SEARCHING) break;
47,691✔
432

433
      smt_restart(core);
2,306✔
434
      //      smt_partial_restart_var(core);
435

436
      if (luby) {
2,306✔
437
        // Luby-style restart
438
        if ((u & -u) == v) {
1,929✔
439
          u ++;
1,041✔
440
          v = 1;
1,041✔
441
        } else {
442
          v <<= 1;
888✔
443
        }
444
        c_threshold = v * period;
1,929✔
445
        trace_restart(core);
1,929✔
446

447
      } else {
448
        // Either Minisat or Picosat-like restart
449

450
        // inner restart: increase c_threshold
451
        c_threshold = (uint32_t) (c_threshold * params->c_factor);
377✔
452

453
        if (c_threshold >= d_threshold) {
377✔
454
          d_threshold = c_threshold; // Minisat-style
284✔
455
          if (params->fast_restart) {
284✔
456
            // Picosat style
457
            // outer restart: reset c_threshold and increase d_threshold
458
            c_threshold = params->c_threshold;
48✔
459
            d_threshold = (uint32_t) (d_threshold * params->d_factor);
48✔
460
          }
461
          trace_restart(core);
284✔
462
        } else {
463
          trace_inner_restart(core);
93✔
464
        }
465
      }
466
    }
467
  }
468

469
  trace_done(core);
45,385✔
470
}
45,385✔
471

472

473
/*
474
 * Initialize the search parameters based on params.
475
 */
476
static void context_set_search_parameters(context_t *ctx, const param_t *params) {
45,385✔
477
  smt_core_t *core;
478
  egraph_t *egraph;
479
  simplex_solver_t *simplex;
480
  fun_solver_t *fsolver;
481
  uint32_t quota;
482

483
  /*
484
   * Set core parameters
485
   */
486
  core = ctx->core;
45,385✔
487
  set_randomness(core, params->randomness);
45,385✔
488
  set_random_seed(core, params->random_seed);
45,385✔
489
  set_var_decay_factor(core, params->var_decay);
45,385✔
490
  set_clause_decay_factor(core, params->clause_decay);
45,385✔
491
  if (params->cache_tclauses) {
45,385✔
492
    enable_theory_cache(core, params->tclause_size);
33,658✔
493
  } else {
494
    disable_theory_cache(core);
11,727✔
495
  }
496

497
  /*
498
   * Set egraph parameters
499
   */
500
  egraph = ctx->egraph;
45,385✔
501
  if (egraph != NULL) {
45,385✔
502
    if (params->use_optimistic_fcheck) {
13,447✔
503
      egraph_enable_optimistic_final_check(egraph);
13,444✔
504
    } else {
505
      egraph_disable_optimistic_final_check(egraph);
3✔
506
    }
507
    if (params->use_dyn_ack) {
13,447✔
508
      egraph_enable_dyn_ackermann(egraph, params->max_ackermann);
13,250✔
509
      egraph_set_ackermann_threshold(egraph, params->dyn_ack_threshold);
13,250✔
510
    } else {
511
      egraph_disable_dyn_ackermann(egraph);
197✔
512
    }
513
    if (params->use_bool_dyn_ack) {
13,447✔
514
      egraph_enable_dyn_boolackermann(egraph, params->max_boolackermann);
13,250✔
515
      egraph_set_boolack_threshold(egraph, params->dyn_bool_ack_threshold);
13,250✔
516
    } else {
517
      egraph_disable_dyn_boolackermann(egraph);
197✔
518
    }
519
    quota = egraph_num_terms(egraph) * params->aux_eq_ratio;
13,447✔
520
    if (quota < params->aux_eq_quota) {
13,447✔
521
      quota = params->aux_eq_quota;
13,386✔
522
    }
523
    egraph_set_aux_eq_quota(egraph, quota);
13,447✔
524
    egraph_set_max_interface_eqs(egraph, params->max_interface_eqs);
13,447✔
525
  }
526

527
  /*
528
   * Set simplex parameters
529
   */
530
  if (context_has_simplex_solver(ctx)) {
45,385✔
531
    simplex = ctx->arith_solver;
33,221✔
532
    if (params->use_simplex_prop) {
33,221✔
533
      simplex_enable_propagation(simplex);
12,845✔
534
      simplex_set_prop_threshold(simplex, params->max_prop_row_size);
12,845✔
535
    }
536
    if (params->adjust_simplex_model) {
33,221✔
537
      simplex_enable_adjust_model(simplex);
12,802✔
538
    }
539
    simplex_set_bland_threshold(simplex, params->bland_threshold);
33,221✔
540
    if (params->integer_check) {
33,221✔
541
      simplex_enable_periodic_icheck(simplex);
×
542
      simplex_set_integer_check_period(simplex, params->integer_check_period);
×
543
    }
544
  }
545

546
  /*
547
   * Set array solver parameters
548
   */
549
  if (context_has_fun_solver(ctx)) {
45,385✔
550
    fsolver = ctx->fun_solver;
12,863✔
551
    fun_solver_set_max_update_conflicts(fsolver, params->max_update_conflicts);
12,863✔
552
    fun_solver_set_max_extensionality(fsolver, params->max_extensionality);
12,863✔
553
  }
554
}
45,385✔
555

556
static smt_status_t _o_call_mcsat_solver(context_t *ctx, const param_t *params) {
1,067✔
557
  mcsat_solve(ctx->mcsat, params, NULL, 0, NULL);
1,067✔
558
  return mcsat_status(ctx->mcsat);
1,067✔
559
}
560

561
static smt_status_t call_mcsat_solver(context_t *ctx, const param_t *params) {
1,067✔
562
  MT_PROTECT(smt_status_t, __yices_globals.lock, _o_call_mcsat_solver(ctx, params));
1,067✔
563
}
564

565
/*
566
 * Initialize search parameters then call solve
567
 * - if ctx->status is not IDLE, return the status.
568
 * - if params is NULL, we use default values.
569
 */
570
smt_status_t check_context(context_t *ctx, const param_t *params) {
60,602✔
571
  smt_core_t *core;
572
  smt_status_t stat;
573

574
  if (params == NULL) {
60,602✔
575
    params = get_default_params();
×
576
  }
577

578
  if (ctx->mcsat != NULL) {
60,602✔
579
    return call_mcsat_solver(ctx, params);
1,067✔
580
  }
581

582
  core = ctx->core;
59,535✔
583
  stat = smt_status(core);
59,535✔
584
  if (stat == YICES_STATUS_IDLE) {
59,535✔
585
    // clean state: the search can proceed
586
    context_set_search_parameters(ctx, params);
42,712✔
587
    solve(core, params, 0, NULL);
42,712✔
588
    stat = smt_status(core);
42,712✔
589
  }
590

591
  return stat;
59,535✔
592
}
593

594

595
/*
596
 * Check with assumptions a[0] ... a[n-1]
597
 * - if ctx->status is not IDLE, return the status.
598
 */
599
smt_status_t check_context_with_assumptions(context_t *ctx, const param_t *params, uint32_t n, const literal_t *a) {
2,673✔
600
  smt_core_t *core;
601
  smt_status_t stat;
602

603
  assert(ctx->mcsat == NULL);
604

605
  core = ctx->core;
2,673✔
606
  stat = smt_status(core);
2,673✔
607
  if (stat == YICES_STATUS_IDLE) {
2,673✔
608
    // clean state
609
    if (params == NULL) {
2,673✔
610
      params = get_default_params();
×
611
    }
612
    context_set_search_parameters(ctx, params);
2,673✔
613
    solve(core, params, n, a);
2,673✔
614
    stat = smt_status(core);
2,673✔
615
  }
616

617
  return stat;
2,673✔
618
}
619

620
/*
621
 * Explore term t and collect all Boolean atoms into atoms.
622
 */
623
static void collect_boolean_atoms(context_t *ctx, term_t t, int_hset_t *atoms, int_hset_t *visited) {
51✔
624
  term_table_t *terms;
625
  uint32_t i, nchildren;
626

627
  if (t < 0) {
51✔
NEW
628
    t = not(t);
×
629
  }
630

631
  if (int_hset_member(visited, t)) {
51✔
NEW
632
    return;
×
633
  }
634
  int_hset_add(visited, t);
51✔
635

636
  terms = ctx->terms;
51✔
637
  if (term_type(terms, t) == bool_type(terms->types)) {
51✔
638
    int_hset_add(atoms, t);
51✔
639
  }
640

641
  if (term_is_projection(terms, t)) {
51✔
NEW
642
    collect_boolean_atoms(ctx, proj_term_arg(terms, t), atoms, visited);
×
643
  } else if (term_is_sum(terms, t)) {
51✔
NEW
644
    nchildren = term_num_children(terms, t);
×
NEW
645
    for (i=0; i<nchildren; i++) {
×
646
      term_t child;
647
      mpq_t q;
NEW
648
      mpq_init(q);
×
NEW
649
      sum_term_component(terms, t, i, q, &child);
×
NEW
650
      collect_boolean_atoms(ctx, child, atoms, visited);
×
NEW
651
      mpq_clear(q);
×
652
    }
653
  } else if (term_is_bvsum(terms, t)) {
51✔
NEW
654
    uint32_t nbits = term_bitsize(terms, t);
×
NEW
655
    int32_t *aux = (int32_t*) safe_malloc(nbits * sizeof(int32_t));
×
NEW
656
    nchildren = term_num_children(terms, t);
×
NEW
657
    for (i=0; i<nchildren; i++) {
×
658
      term_t child;
NEW
659
      bvsum_term_component(terms, t, i, aux, &child);
×
NEW
660
      collect_boolean_atoms(ctx, child, atoms, visited);
×
661
    }
NEW
662
    safe_free(aux);
×
663
  } else if (term_is_product(terms, t)) {
51✔
NEW
664
    nchildren = term_num_children(terms, t);
×
NEW
665
    for (i=0; i<nchildren; i++) {
×
666
      term_t child;
667
      uint32_t exp;
NEW
668
      product_term_component(terms, t, i, &child, &exp);
×
NEW
669
      collect_boolean_atoms(ctx, child, atoms, visited);
×
670
    }
671
  } else if (term_is_composite(terms, t)) {
51✔
672
    nchildren = term_num_children(terms, t);
27✔
673
    for (i=0; i<nchildren; i++) {
74✔
674
      collect_boolean_atoms(ctx, term_child(terms, t, i), atoms, visited);
47✔
675
    }
676
  }
677
}
678

679
/*
680
 * Extract assumptions whose labels appear in term t.
681
 */
682
static void core_from_labeled_interpolant(context_t *ctx, term_t t, const ivector_t *labels, const int_hmap_t *label_map, ivector_t *core) {
4✔
683
  int_hset_t atoms, visited;
684
  uint32_t i;
685

686
  init_int_hset(&atoms, 0);
4✔
687
  init_int_hset(&visited, 0);
4✔
688
  collect_boolean_atoms(ctx, t, &atoms, &visited);
4✔
689

690
  ivector_reset(core);
4✔
691
  for (i=0; i<labels->size; i++) {
72✔
692
    term_t label = labels->data[i];
68✔
693
    if (int_hset_member(&atoms, label)) {
68✔
694
      int_hmap_pair_t *p = int_hmap_find((int_hmap_t *) label_map, label);
24✔
695
      if (p != NULL) {
24✔
696
        ivector_push(core, p->val);
24✔
697
      }
698
    }
699
  }
700

701
  delete_int_hset(&visited);
4✔
702
  delete_int_hset(&atoms);
4✔
703
}
4✔
704

705
/*
706
 * Cache a core vector in the context.
707
 */
708
static void cache_unsat_core(context_t *ctx, const ivector_t *core) {
2,736✔
709
  if (ctx->unsat_core_cache == NULL) {
2,736✔
710
    ctx->unsat_core_cache = (ivector_t *) safe_malloc(sizeof(ivector_t));
2,736✔
711
    init_ivector(ctx->unsat_core_cache, core->size);
2,736✔
712
  } else {
NEW
713
    ivector_reset(ctx->unsat_core_cache);
×
714
  }
715
  ivector_copy(ctx->unsat_core_cache, core->data, core->size);
2,736✔
716
}
2,736✔
717

718
/*
719
 * Check under assumptions given as terms.
720
 * - if MCSAT is enabled, this uses temporary labels + model interpolation.
721
 * - otherwise terms are converted to literals and handled by the CDCL(T) path.
722
 *
723
 * Preconditions:
724
 * - context status must be IDLE.
725
 */
726
smt_status_t check_context_with_term_assumptions(context_t *ctx, const param_t *params, uint32_t n, const term_t *a, int32_t *error) {
2,678✔
727
  smt_status_t stat;
728
  ivector_t assumptions;
729
  uint32_t i;
730

731
  if (error != NULL) {
2,678✔
732
    *error = CTX_NO_ERROR;
2,678✔
733
  }
734

735
  context_invalidate_unsat_core_cache(ctx);
2,678✔
736

737
  if (ctx->mcsat == NULL) {
2,678✔
738
    literal_t l;
739

740
    init_ivector(&assumptions, n);
2,673✔
741
    for (i=0; i<n; i++) {
94,330✔
742
      l = context_add_assumption(ctx, a[i]);
91,657✔
743
      if (l < 0) {
91,657✔
NEW
744
        if (error != NULL) {
×
NEW
745
          *error = l;
×
746
        }
NEW
747
        delete_ivector(&assumptions);
×
NEW
748
        return YICES_STATUS_ERROR;
×
749
      }
750
      ivector_push(&assumptions, l);
91,657✔
751
    }
752

753
    stat = check_context_with_assumptions(ctx, params, n, assumptions.data);
2,673✔
754
    delete_ivector(&assumptions);
2,673✔
755
    return stat;
2,673✔
756
  }
757

758
  /*
759
   * MCSAT: create fresh labels b_i, assert (b_i => a_i), then solve with model b_i=true.
760
   * We extract interpolant/core before cleanup, then restore sticky UNSAT artifacts.
761
   */
762
  if (!context_supports_model_interpolation(ctx)) {
5✔
NEW
763
    if (error != NULL) {
×
NEW
764
      *error = CTX_OPERATION_NOT_SUPPORTED;
×
765
    }
NEW
766
    return YICES_STATUS_ERROR;
×
767
  }
768

769
  {
770
    model_t mdl;               // temporary model: sets all label terms b_i to true
771
    int_hmap_t label_map;      // map label b_i -> original assumption a_i
772
    ivector_t mapped_core;     // translated core over original assumptions
773
    term_t interpolant = NULL_TERM; // raw/substituted interpolant for sticky UNSAT result
5✔
774
    int32_t code;              // return code from assert_formula (negative on internalization error)
775
    bool pushed;               // whether we pushed a temporary scope and must pop it
776
    term_manager_t tm;
777

778
    init_model(&mdl, ctx->terms, true);
5✔
779
    init_int_hmap(&label_map, 0);
5✔
780
    init_ivector(&assumptions, n);
5✔
781
    init_ivector(&mapped_core, 0);
5✔
782
    init_term_manager(&tm, ctx->terms);
5✔
783
    stat = YICES_STATUS_IDLE;
5✔
784

785
    pushed = false;
5✔
786
    if (context_supports_pushpop(ctx)) {
5✔
787
      context_push(ctx);
4✔
788
      pushed = true;
4✔
789
    }
790

791
    for (i=0; i<n; i++) {
74✔
792
      term_t b = new_uninterpreted_term(ctx->terms, bool_id);
69✔
793
      term_t implication = mk_implies(&tm, b, a[i]);
69✔
794

795
      int_hmap_add(&label_map, b, a[i]);
69✔
796
      code = assert_formula(ctx, implication);
69✔
797
      if (code < 0) {
69✔
NEW
798
        if (error != NULL) {
×
NEW
799
          *error = code;
×
800
        }
NEW
801
        stat = YICES_STATUS_ERROR;
×
NEW
802
        break;
×
803
      }
804
      model_map_term(&mdl, b, vtbl_mk_bool(&mdl.vtbl, true));
69✔
805
      ivector_push(&assumptions, b);
69✔
806
    }
807

808
    if (stat != YICES_STATUS_ERROR) {
5✔
809
      stat = check_context_with_model(ctx, params, &mdl, n, assumptions.data);
5✔
810
      if (stat == YICES_STATUS_UNSAT) {
5✔
811
        term_subst_t subst;
812

813
        interpolant = context_get_unsat_model_interpolant(ctx);
4✔
814
        assert(interpolant != NULL_TERM);
815
        core_from_labeled_interpolant(ctx, interpolant, &assumptions, &label_map, &mapped_core);
4✔
816

817
        init_term_subst(&subst, &tm, n, assumptions.data, a);
4✔
818
        interpolant = apply_term_subst(&subst, interpolant);
4✔
819
        delete_term_subst(&subst);
4✔
820
      }
821
    }
822

823
    if (pushed) {
5✔
824
      mcsat_cleanup_assumptions(ctx->mcsat);
4✔
825
      context_pop(ctx);
4✔
826
    }
827
    if (stat == YICES_STATUS_UNSAT) {
5✔
828
      mcsat_set_unsat_result(ctx->mcsat, interpolant);
4✔
829
      cache_unsat_core(ctx, &mapped_core);
4✔
830
    }
831

832
    delete_term_manager(&tm);
5✔
833
    delete_ivector(&mapped_core);
5✔
834
    delete_ivector(&assumptions);
5✔
835
    delete_int_hmap(&label_map);
5✔
836
    delete_model(&mdl);
5✔
837

838
    return stat;
5✔
839
  }
840
}
841

842
/*
843
 * Check with given model
844
 * - if mcsat status is not IDLE, return the status
845
 */
846
smt_status_t check_context_with_model(context_t *ctx, const param_t *params, model_t* mdl, uint32_t n, const term_t t[]) {
131✔
847
  smt_status_t stat;
848

849
  assert(ctx->mcsat != NULL);
850

851
  stat = mcsat_status(ctx->mcsat);
131✔
852
  if (stat == YICES_STATUS_IDLE) {
131✔
853
    mcsat_solve(ctx->mcsat, params, mdl, n, t);
131✔
854
    stat = mcsat_status(ctx->mcsat);
131✔
855

856
    // BD: this looks wrong. We shouldn't call clear yet.
857
    // we want the status to remain STATUS_UNSAT until the next call to check or assert.
858
    //    if (n > 0 && stat == STATUS_UNSAT && context_supports_multichecks(ctx)) {
859
    //      context_clear(ctx);
860
    //    }
861
  }
862

863
  return stat;
131✔
864
}
865

866
/*
867
 * Check with given model and hints
868
 * - set the model hint and call check_context_with_model
869
 */
870
smt_status_t check_context_with_model_and_hint(context_t *ctx, const param_t *params, model_t* mdl, uint32_t n, const term_t t[], uint32_t m) {
×
871
  assert(m <= n);
872

873
  mcsat_set_model_hint(ctx->mcsat, mdl, n, t);
×
874

875
  return check_context_with_model(ctx, params, mdl, m, t);
×
876
}
877

878

879
/*
880
 * Precheck: force generation of clauses and other stuff that's
881
 * constructed lazily by the solvers. For example, this
882
 * can be used to convert all the constraints asserted in the
883
 * bitvector to clauses so that we can export the result to DIMACS.
884
 *
885
 * If ctx status is IDLE:
886
 * - the function calls 'start_search' and does one round of propagation.
887
 * - if this results in UNSAT, the function returns UNSAT
888
 * - if the precheck is interrupted, the function returns INTERRUPTED
889
 * - otherwise the function returns UNKNOWN and sets the status to
890
 *   UNKNOWN.
891
 *
892
 * IMPORTANT: call smt_clear or smt_cleanup to restore the context to
893
 * IDLE before doing anything else with this context.
894
 *
895
 * If ctx status is not IDLE, the function returns it and does nothing
896
 * else.
897
 */
898
smt_status_t precheck_context(context_t *ctx) {
×
899
  smt_status_t stat;
900
  smt_core_t *core;
901

902
  core = ctx->core;
×
903

904
  stat = smt_status(core);
×
905
  if (stat == YICES_STATUS_IDLE) {
×
906
    start_search(core, 0, NULL);
×
907
    smt_process(core);
×
908
    stat = smt_status(core);
×
909

910
    assert(stat == YICES_STATUS_UNSAT || stat == YICES_STATUS_SEARCHING ||
911
           stat == YICES_STATUS_INTERRUPTED);
912

913
    if (stat == YICES_STATUS_SEARCHING) {
×
914
      end_search_unknown(core);
×
915
      stat = YICES_STATUS_UNKNOWN;
×
916
    }
917
  }
918

919
  return stat;
×
920
}
921

922

923

924
/*
925
 * Solve using another SAT solver
926
 * - sat_solver = name of the solver to use
927
 * - verbosity = verbosity level (0 means quiet)
928
 * - this may be used only for BV or pure SAT problems
929
 * - we perform one round of propagation to convert the problem to CNF
930
 * - then we call an external SAT solver on the CNF problem
931
 */
932
smt_status_t check_with_delegate(context_t *ctx, const char *sat_solver, uint32_t verbosity) {
×
933
  smt_status_t stat;
934
  smt_core_t *core;
935
  delegate_t delegate;
936
  bvar_t x;
937
  bval_t v;
938

939
  core = ctx->core;
×
940

941
  stat = smt_status(core);
×
942
  if (stat == YICES_STATUS_IDLE) {
×
943
    start_search(core, 0, NULL);
×
944
    smt_process(core);
×
945
    stat = smt_status(core);
×
946

947
    assert(stat == YICES_STATUS_UNSAT || stat == YICES_STATUS_SEARCHING ||
948
           stat == YICES_STATUS_INTERRUPTED);
949

950
    if (stat == YICES_STATUS_SEARCHING) {
×
951
      if (smt_easy_sat(core)) {
×
952
        stat = YICES_STATUS_SAT;
×
953
      } else {
954
        // call the delegate
955
        init_delegate(&delegate, sat_solver, num_vars(core));
×
956
        delegate_set_verbosity(&delegate, verbosity);
×
957

958
        stat = solve_with_delegate(&delegate, core);
×
959
        set_smt_status(core, stat);
×
960
        if (stat == YICES_STATUS_SAT) {
×
961
          for (x=0; x<num_vars(core); x++) {
×
962
            v = delegate_get_value(&delegate, x);
×
963
            set_bvar_value(core, x, v);
×
964
          }
965
        }
966
        delete_delegate(&delegate);
×
967
      }
968
    }
969
  }
970

971
  return stat;
×
972
}
973

974

975
/*
976
 * Bit-blast then export to DIMACS
977
 * - filename = name of the output file
978
 * - status = status of the context after bit-blasting
979
 *
980
 * If ctx status is IDLE
981
 * - perform one round of propagation to conver the problem to CNF
982
 * - export the CNF to DIMACS
983
 *
984
 * If ctx status is not IDLE,
985
 * - store the stauts in *status and do nothing else
986
 *
987
 * Return code:
988
 *  1 if the DIMACS file was created
989
 *  0 if the problem was solved by the propagation round
990
 * -1 if there was an error in creating or writing to the file.
991
 */
992
int32_t bitblast_then_export_to_dimacs(context_t *ctx, const char *filename, smt_status_t *status) {
×
993
  smt_core_t *core;
994
  FILE *f;
995
  smt_status_t stat;
996
  int32_t code;
997

998
  core = ctx->core;
×
999

1000
  code = 0;
×
1001
  stat = smt_status(core);
×
1002
  if (stat == YICES_STATUS_IDLE) {
×
1003
    start_search(core, 0, NULL);
×
1004
    smt_process(core);
×
1005
    stat = smt_status(core);
×
1006

1007
    assert(stat == YICES_STATUS_UNSAT || stat == YICES_STATUS_SEARCHING ||
1008
           stat == YICES_STATUS_INTERRUPTED);
1009

1010
    if (stat == YICES_STATUS_SEARCHING) {
×
1011
      code = 1;
×
1012
      f = fopen(filename, "w");
×
1013
      if (f == NULL) {
×
1014
        code = -1;
×
1015
      } else {
1016
        dimacs_print_bvcontext(f, ctx);
×
1017
        if (ferror(f)) code = -1;
×
1018
        fclose(f);
×
1019
      }
1020
    }
1021
  }
1022

1023
  *status = stat;
×
1024

1025
  return code;
×
1026
}
1027

1028

1029
/*
1030
 * Simplify then export to Dimacs:
1031
 * - filename = name of the output file
1032
 * - status = status of the context after CNF conversion + preprocessing
1033
 *
1034
 * If ctx status is IDLE
1035
 * - perform one round of propagation to convert the problem to CNF
1036
 * - export the CNF to y2sat for extra preprocessing then export that to DIMACS
1037
 *
1038
 * If ctx status is not IDLE, the function stores that in *status
1039
 * If y2sat preprocessing solves the formula, return the status also in *status
1040
 *
1041
 * Return code:
1042
 *  1 if the DIMACS file was created
1043
 *  0 if the problems was solved by preprocessing (or if ctx status is not IDLE)
1044
 * -1 if there was an error creating or writing to the file.
1045
 */
1046
int32_t process_then_export_to_dimacs(context_t *ctx, const char *filename, smt_status_t *status) {
×
1047
  smt_core_t *core;
1048
  FILE *f;
1049
  smt_status_t stat;
1050
  delegate_t delegate;
1051
  bvar_t x;
1052
  bval_t v;
1053
  int32_t code;
1054

1055
  core = ctx->core;
×
1056

1057
  code = 0;
×
1058
  stat = smt_status(core);
×
1059
  if (stat == YICES_STATUS_IDLE) {
×
1060
    start_search(core, 0, NULL);
×
1061
    smt_process(core);
×
1062
    stat = smt_status(core);
×
1063

1064
    assert(stat == YICES_STATUS_UNSAT || stat == YICES_STATUS_SEARCHING ||
1065
           stat == YICES_STATUS_INTERRUPTED);
1066

1067
    if (stat == YICES_STATUS_SEARCHING) {
×
1068
      if (smt_easy_sat(core)) {
×
1069
        stat = YICES_STATUS_SAT;
×
1070
      } else {
1071
        // call the delegate
1072
        init_delegate(&delegate, "y2sat", num_vars(core));
×
1073
        delegate_set_verbosity(&delegate, 0);
×
1074

1075
        stat = preprocess_with_delegate(&delegate, core);
×
1076
        set_smt_status(core, stat);
×
1077
        if (stat == YICES_STATUS_SAT) {
×
1078
          for (x=0; x<num_vars(core); x++) {
×
1079
            v = delegate_get_value(&delegate, x);
×
1080
            set_bvar_value(core, x, v);
×
1081
          }
1082
        } else if (stat == YICES_STATUS_UNKNOWN) {
×
1083
          code = 1;
×
1084
          f = fopen(filename, "w");
×
1085
          if (f == NULL) {
×
1086
            code = -1;
×
1087
          } else {
1088
            export_to_dimacs_with_delegate(&delegate, f);
×
1089
            if (ferror(f)) code = -1;
×
1090
            fclose(f);
×
1091
          }
1092
        }
1093

1094
        delete_delegate(&delegate);
×
1095
      }
1096
    }
1097
  }
1098

1099
  *status = stat;
×
1100

1101
  return code;
×
1102
}
1103

1104

1105

1106
/*
1107
 * MODEL CONSTRUCTION
1108
 */
1109

1110
/*
1111
 * Value of literal l in ctx->core
1112
 */
1113
static value_t bool_value(context_t *ctx, value_table_t *vtbl, literal_t l) {
65,201✔
1114
  value_t v;
1115

1116
  v = null_value; // prevent GCC warning
65,201✔
1117
  switch (literal_value(ctx->core, l)) {
65,201✔
1118
  case VAL_FALSE:
38,577✔
1119
    v = vtbl_mk_false(vtbl);
38,577✔
1120
    break;
38,577✔
1121
  case VAL_UNDEF_FALSE:
×
1122
  case VAL_UNDEF_TRUE:
1123
    v = vtbl_mk_unknown(vtbl);
×
1124
    break;
×
1125
  case VAL_TRUE:
26,624✔
1126
    v = vtbl_mk_true(vtbl);
26,624✔
1127
    break;
26,624✔
1128
  }
1129
  return v;
65,201✔
1130
}
1131

1132

1133
/*
1134
 * Value of arithmetic variable x in ctx->arith_solver
1135
 */
1136
static value_t arith_value(context_t *ctx, value_table_t *vtbl, thvar_t x) {
22,589✔
1137
  rational_t *a;
1138
  value_t v;
1139

1140
  assert(context_has_arith_solver(ctx));
1141

1142
  a = &ctx->aux;
22,589✔
1143
  if (ctx->arith.value_in_model(ctx->arith_solver, x, a)) {
22,589✔
1144
    v = vtbl_mk_rational(vtbl, a);
22,589✔
1145
  } else {
1146
    v = vtbl_mk_unknown(vtbl);
×
1147
  }
1148

1149
  return v;
22,589✔
1150
}
1151

1152

1153

1154
/*
1155
 * Value of bitvector variable x in ctx->bv_solver
1156
 */
1157
static value_t bv_value(context_t *ctx, value_table_t *vtbl, thvar_t x) {
18,133✔
1158
  bvconstant_t *b;
1159
  value_t v;
1160

1161
  assert(context_has_bv_solver(ctx));
1162

1163
  b = &ctx->bv_buffer;
18,133✔
1164
  if (ctx->bv.value_in_model(ctx->bv_solver, x, b)) {
18,133✔
1165
    v = vtbl_mk_bv_from_constant(vtbl, b);
18,133✔
1166
  } else {
1167
    v = vtbl_mk_unknown(vtbl);
×
1168
  }
1169

1170
  return v;
18,133✔
1171
}
1172

1173

1174
/*
1175
 * Get a value for term t in the solvers or egraph
1176
 * - attach the mapping from t to that value in model
1177
 * - if we don't have a concrete object for t but t is
1178
 *   mapped to a term u and the model->has_alias is true,
1179
 *   then we store the mapping [t --> u] in the model's
1180
 *   alias map.
1181
 */
1182
static void build_term_value(context_t *ctx, model_t *model, term_t t) {
319,771✔
1183
  value_table_t *vtbl;
1184
  term_t r;
1185
  uint32_t polarity;
1186
  int32_t x;
1187
  type_t tau;
1188
  value_t v;
1189

1190
  /*
1191
   * Get the root of t in the substitution table
1192
   */
1193
  r = intern_tbl_get_root(&ctx->intern, t);
319,771✔
1194
  if (intern_tbl_root_is_mapped(&ctx->intern, r)) {
319,771✔
1195
    /*
1196
     * r is mapped to some object x in egraph/core/or theory solvers
1197
     * - keep track of polarity then force r to positive polarity
1198
     */
1199
    vtbl = model_get_vtbl(model);
110,693✔
1200
    polarity = polarity_of(r);
110,693✔
1201
    r = unsigned_term(r);
110,693✔
1202

1203
    /*
1204
     * Convert x to a concrete value
1205
     */
1206
    x = intern_tbl_map_of_root(&ctx->intern, r);
110,693✔
1207
    if (code_is_eterm(x)) {
110,693✔
1208
      // x refers to an egraph object or true_occ/false_occ
1209
      if (x == bool2code(true)) {
4,770✔
1210
        v = vtbl_mk_true(vtbl);
475✔
1211
      } else if (x == bool2code(false)) {
4,295✔
1212
        v = vtbl_mk_false(vtbl);
1,420✔
1213
      } else {
1214
        assert(context_has_egraph(ctx));
1215
        v = egraph_get_value(ctx->egraph, vtbl, code2occ(x));
2,875✔
1216
      }
1217

1218
    } else {
1219
      // x refers to a literal or a theory variable
1220
      tau = term_type(ctx->terms, r);
105,923✔
1221
      switch (type_kind(ctx->types, tau)) {
105,923✔
1222
      case BOOL_TYPE:
65,201✔
1223
        v = bool_value(ctx, vtbl, code2literal(x));
65,201✔
1224
        break;
65,201✔
1225

1226
      case INT_TYPE:
22,589✔
1227
      case REAL_TYPE:
1228
        v = arith_value(ctx, vtbl, code2thvar(x));
22,589✔
1229
        break;
22,589✔
1230

1231
      case BITVECTOR_TYPE:
18,133✔
1232
        v = bv_value(ctx, vtbl, code2thvar(x));
18,133✔
1233
        break;
18,133✔
1234

1235
      default:
×
1236
        /*
1237
         * This should never happen:
1238
         * scalar, uninterpreted, tuple, function terms
1239
         * are mapped to egraph terms.
1240
         */
1241
        assert(false);
1242
        v = vtbl_mk_unknown(vtbl); // prevent GCC warning
×
1243
        break;
×
1244
      }
1245
    }
1246

1247
    /*
1248
     * Restore polarity then add mapping [t --> v] in the model
1249
     */
1250
    if (! object_is_unknown(vtbl, v)) {
110,693✔
1251
      if (object_is_boolean(vtbl, v)) {
110,693✔
1252
        if (polarity) {
67,096✔
1253
          // negate the value
1254
          v = vtbl_mk_not(vtbl, v);
24✔
1255
        }
1256
      }
1257
      model_map_term(model, t, v);
110,693✔
1258
    }
1259

1260
  } else {
1261
    /*
1262
     * r is not mapped to anything:
1263
     *
1264
     * 1) If t == r and t is present in the internalization table
1265
     *    then t is relevant. So we should display its value
1266
     *    when we print the model. To do this, we assign an
1267
     *    arbitrary value v to t and store [t := v] in the map.
1268
     *
1269
     * 2) If t != r then we keep the mapping [t --> r] in
1270
     *    the alias table (provided the model supports aliases).
1271
     */
1272
    if (t == r) {
209,078✔
1273
      if (intern_tbl_term_present(&ctx->intern, t)) {
208,430✔
1274
        tau = term_type(ctx->terms, t);
×
1275
        vtbl = model_get_vtbl(model);
×
1276
        v = vtbl_make_object(vtbl, tau);
×
1277
        model_map_term(model, t, v);
×
1278
      }
1279
    } else if (model->has_alias) {
648✔
1280
      // t != r: keep the substitution [t --> r] in the model
1281
      model_add_substitution(model, t, r);
648✔
1282
    }
1283
  }
1284
}
319,771✔
1285

1286

1287

1288

1289
/*
1290
 * Build a model for the current context (including all satellite solvers)
1291
 * - the context status must be SAT (or UNKNOWN)
1292
 * - if model->has_alias is true, we store the term substitution
1293
 *   defined by ctx->intern_tbl into the model
1294
 * - cleanup of satellite models needed using clean_solver_models()
1295
 */
1296
void build_model(model_t *model, context_t *ctx) {
42,113✔
1297
  term_table_t *terms;
1298
  uint32_t i, n;
1299
  term_t t;
1300

1301
  assert(smt_status(ctx->core) == YICES_STATUS_SAT || smt_status(ctx->core) == YICES_STATUS_UNKNOWN || mcsat_status(ctx->mcsat) == YICES_STATUS_SAT);
1302

1303
  /*
1304
   * First build assignments in the satellite solvers
1305
   * and get the val_in_model functions for the egraph
1306
   */
1307
  if (context_has_arith_solver(ctx)) {
42,113✔
1308
    ctx->arith.build_model(ctx->arith_solver);
32,905✔
1309
  }
1310
  if (context_has_bv_solver(ctx)) {
42,113✔
1311
    ctx->bv.build_model(ctx->bv_solver);
21,399✔
1312
  }
1313

1314
  /*
1315
   * Construct the egraph model
1316
   */
1317
  if (context_has_egraph(ctx)) {
42,113✔
1318
    egraph_build_model(ctx->egraph, model_get_vtbl(model));
13,032✔
1319
  }
1320

1321
  /*
1322
   * Construct the mcsat model.
1323
   */
1324
  if (context_has_mcsat(ctx)) {
42,113✔
1325
    mcsat_build_model(ctx->mcsat, model);
23✔
1326
  }
1327

1328
  // scan the internalization table
1329
  terms = ctx->terms;
42,113✔
1330
  n = intern_tbl_num_terms(&ctx->intern);
42,113✔
1331
  for (i=1; i<n; i++) { // first real term has index 1 (i.e. true_term)
1,235,095,681✔
1332
    if (good_term_idx(terms, i)) {
1,235,053,568✔
1333
      t = pos_occ(i);
1,235,053,568✔
1334
      if (term_kind(terms, t) == UNINTERPRETED_TERM) {
1,235,053,568✔
1335
        build_term_value(ctx, model, t);
319,771✔
1336
      }
1337
    }
1338
  }
1339
}
42,113✔
1340

1341

1342
/*
1343
 * Cleanup solver models
1344
 */
1345
void clean_solver_models(context_t *ctx) {
42,113✔
1346
  if (context_has_arith_solver(ctx)) {
42,113✔
1347
    ctx->arith.free_model(ctx->arith_solver);
32,905✔
1348
  }
1349
  if (context_has_bv_solver(ctx)) {
42,113✔
1350
    ctx->bv.free_model(ctx->bv_solver);
21,399✔
1351
  }
1352
  if (context_has_egraph(ctx)) {
42,113✔
1353
    egraph_free_model(ctx->egraph);
13,032✔
1354
  }
1355
}
42,113✔
1356

1357

1358

1359
/*
1360
 * Build a model for the current context
1361
 * - the context status must be SAT (or UNKNOWN)
1362
 * - if model->has_alias is true, we store the term substitution
1363
 *   defined by ctx->intern_tbl into the model
1364
 */
1365
void context_build_model(model_t *model, context_t *ctx) {
212✔
1366
  // Build solver models and term values
1367
  build_model(model, ctx);
212✔
1368

1369
  // Cleanup
1370
  clean_solver_models(ctx);
212✔
1371
}
212✔
1372

1373

1374

1375
/*
1376
 * Read the value of a Boolean term t
1377
 * - return VAL_TRUE/VAL_FALSE or VAL_UNDEF_FALSE/VAL_UNDEF_TRUE if t's value is not known
1378
 */
1379
bval_t context_bool_term_value(context_t *ctx, term_t t) {
×
1380
  term_t r;
1381
  uint32_t polarity;
1382
  int32_t x;
1383
  bval_t v;
1384

1385
  assert(is_boolean_term(ctx->terms, t));
1386

1387
  // default value if t is not in the internalization table
1388
  v = VAL_UNDEF_FALSE;
×
1389
  r = intern_tbl_get_root(&ctx->intern, t);
×
1390
  if (intern_tbl_root_is_mapped(&ctx->intern, r)) {
×
1391
    // r is mapped to some object x
1392
    polarity = polarity_of(r);
×
1393
    r = unsigned_term(r);
×
1394

1395
    x  = intern_tbl_map_of_root(&ctx->intern, r);
×
1396
    if (code_is_eterm(x)) {
×
1397
      // x must be either true_occ or false_occ
1398
      if (x == bool2code(true)) {
×
1399
        v = VAL_TRUE;
×
1400
      } else {
1401
        assert(x == bool2code(false));
1402
        v = VAL_FALSE;
×
1403
      }
1404
    } else {
1405
      // x refers to a literal in the smt core
1406
      v = literal_value(ctx->core, code2literal(x));
×
1407
    }
1408

1409
    // negate v if polarity is 1 (cf. smt_core_base_types.h)
1410
    v ^= polarity;
×
1411
  }
1412

1413
  return v;
×
1414
}
1415

1416

1417
/*
1418
 * UNSAT CORE
1419
 */
1420

1421
/*
1422
 * Build an unsat core:
1423
 * - store the result in v
1424
 * - first reuse a cached term core if available.
1425
 * - otherwise:
1426
 *   CDCL(T): build from smt_core then cache as terms
1427
 *   MCSAT: return empty unless check-with-term-assumptions populated the cache.
1428
 */
1429
void context_build_unsat_core(context_t *ctx, ivector_t *v) {
2,736✔
1430
  smt_core_t *core;
1431
  uint32_t i, n;
1432
  term_t t;
1433

1434
  if (ctx->unsat_core_cache != NULL) {
2,736✔
1435
    // Fast path: repeated get_unsat_core returns the cached term vector.
1436
    ivector_reset(v);
4✔
1437
    ivector_copy(v, ctx->unsat_core_cache->data, ctx->unsat_core_cache->size);
4✔
1438
    return;
4✔
1439
  }
1440

1441
  if (ctx->mcsat != NULL) {
2,732✔
1442
    // MCSAT core extraction is done in check-with-term-assumptions; without cache
1443
    // there is no generic context-level core structure to rebuild from here.
NEW
1444
    ivector_reset(v);
×
NEW
1445
    return;
×
1446
  }
1447

1448
  core = ctx->core;
2,732✔
1449
  assert(core != NULL && core->status == YICES_STATUS_UNSAT);
1450
  build_unsat_core(core, v);
2,732✔
1451

1452
  // convert from literals to terms
1453
  n = v->size;
2,732✔
1454
  for (i=0; i<n; i++) {
37,661✔
1455
    t = assumption_term_for_literal(&ctx->assumptions, v->data[i]);
34,929✔
1456
    assert(t >= 0);
1457
    v->data[i] = t;
34,929✔
1458
  }
1459

1460
  // Cache the converted term core for subsequent queries.
1461
  cache_unsat_core(ctx, v);
2,732✔
1462
}
1463

1464

1465
/*
1466
 * MODEL INTERPOLANT
1467
 */
1468
term_t context_get_unsat_model_interpolant(context_t *ctx) {
49✔
1469
  assert(ctx->mcsat != NULL);
1470
  return mcsat_get_unsat_model_interpolant(ctx->mcsat);
49✔
1471
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc