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

SRI-CSL / yices2 / 22793527819

07 Mar 2026 06:08AM UTC coverage: 66.485% (+0.07%) from 66.414%
22793527819

Pull #609

github

disteph
ci(windows): add mirrored/retried gmp download fallback
Pull Request #609: Fix #608 bug: MCSAT + Thread-Safe Lock Recursion and Add API Regressions

23 of 42 new or added lines in 5 files covered. (54.76%)

2 existing lines in 1 file now uncovered.

82974 of 124802 relevant lines covered (66.48%)

1706376.43 hits per line

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

72.54
/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 "api/yices_api_lock_free.h"
48
#include "mt/thread_macros.h"
49

50

51

52
/*
53
 * TRACE FUNCTIONS
54
 */
55

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

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

81

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

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

93

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

102

103

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

112

113

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

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

132
  case VAL_TRUE:
×
133
    break;
×
134

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

141

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

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

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

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

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

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

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

194
  *reduce_threshold = r_threshold;
13,426✔
195
}
13,426✔
196

197

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

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

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

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

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

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

248
  *reduce_threshold = r_threshold;
13,421✔
249
}
13,421✔
250

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

257

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

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

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

277
  smt_process(core);
20,850✔
278
  while (smt_status(core) == YICES_STATUS_SEARCHING && num_conflicts(core) <= max_conflicts) {
636,270✔
279
    // reduce heuristic
280
    if (num_learned_clauses(core) >= r_threshold) {
615,420✔
281
      deletions = core->stats.learned_clauses_deleted;
61✔
282
      reduce_clause_database(core);
61✔
283
      r_threshold = (uint32_t) (r_threshold * r_factor);
61✔
284
      trace_reduce(core, core->stats.learned_clauses_deleted - deletions);
61✔
285
    }
286

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

296
    // decision
297
    l = select_unassigned_literal(core);
615,418✔
298
    if (l == null_literal) {
615,418✔
299
      // all variables assigned: call final check
300
      smt_final_check(core);
22,608✔
301
    } else {
302
      // apply the branching heuristic
303
      l = branch(core, l);
592,810✔
304
      // propagation
305
      decide_literal(core, l);
592,810✔
306
      smt_process(core);
592,810✔
307
    }
308
  }
309

310
  *reduce_threshold = r_threshold;
20,850✔
311
}
20,850✔
312

313

314

315

316

317
/*
318
 * SUPPORTED BRANCHING
319
 */
320

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

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

334

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

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

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

362

363

364

365

366
/*
367
 * CORE SOLVER
368
 */
369

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

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

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

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

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

432
      if (smt_status(core) != YICES_STATUS_SEARCHING) break;
47,697✔
433

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

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

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

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

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

470
  trace_done(core);
45,391✔
471
}
45,391✔
472

473

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

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

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

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

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

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

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

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

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

579
  if (ctx->mcsat != NULL) {
60,619✔
580
    return call_mcsat_solver(ctx, params);
1,080✔
581
  }
582

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

592
  return stat;
59,539✔
593
}
594

595

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

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

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

618
  return stat;
2,675✔
619
}
620

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

628
  if (t < 0) {
56✔
629
    t = not(t);
×
630
  }
631

632
  if (int_hset_member(visited, t)) {
56✔
633
    return;
×
634
  }
635
  int_hset_add(visited, t);
56✔
636

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

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

680
/*
681
 * Extract assumptions whose labels appear in term t.
682
 */
683
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) {
5✔
684
  int_hset_t atoms, visited;
685
  uint32_t i;
686

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

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

702
  delete_int_hset(&visited);
5✔
703
  delete_int_hset(&atoms);
5✔
704
}
5✔
705

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

719
/*
720
 * MCSAT variant of check_context_with_term_assumptions.
721
 * Caller must hold __yices_globals.lock.
722
 */
723
static smt_status_t _o_check_context_with_term_assumptions_mcsat(context_t *ctx, const param_t *params, uint32_t n, const term_t *a, int32_t *error) {
6✔
724
  smt_status_t stat;
725
  ivector_t assumptions;
726
  uint32_t i;
727

728
  /*
729
   * MCSAT: create fresh labels b_i, assert (b_i => a_i), then solve with model b_i=true.
730
   * We extract interpolant/core before cleanup, then restore sticky UNSAT artifacts.
731
   */
732
  if (!context_supports_model_interpolation(ctx)) {
6✔
733
    if (error != NULL) {
×
734
      *error = CTX_OPERATION_NOT_SUPPORTED;
×
735
    }
736
    return YICES_STATUS_ERROR;
×
737
  }
738

739
  {
740
    model_t mdl;               // temporary model: sets all label terms b_i to true
741
    int_hmap_t label_map;      // map label b_i -> original assumption a_i
742
    ivector_t mapped_core;     // translated core over original assumptions
743
    term_t interpolant = NULL_TERM; // raw/substituted interpolant for sticky UNSAT result
6✔
744
    int32_t code;              // return code from assert_formula (negative on internalization error)
745
    bool pushed;               // whether we pushed a temporary scope and must pop it
746
    term_manager_t tm;
747

748
    init_model(&mdl, ctx->terms, true);
6✔
749
    init_int_hmap(&label_map, 0);
6✔
750
    init_ivector(&assumptions, n);
6✔
751
    init_ivector(&mapped_core, 0);
6✔
752
    init_term_manager(&tm, ctx->terms);
6✔
753
    stat = YICES_STATUS_IDLE;
6✔
754

755
    pushed = false;
6✔
756
    if (context_supports_pushpop(ctx)) {
6✔
757
      context_push(ctx);
5✔
758
      pushed = true;
5✔
759
    }
760

761
    for (i=0; i<n; i++) {
77✔
762
      term_t b = new_uninterpreted_term(ctx->terms, bool_id);
71✔
763
      term_t implication = mk_implies(&tm, b, a[i]);
71✔
764

765
      int_hmap_add(&label_map, b, a[i]);
71✔
766
      code = _o_assert_formula(ctx, implication);
71✔
767
      if (code < 0) {
71✔
768
        if (error != NULL) {
×
769
          *error = code;
×
770
        }
771
        stat = YICES_STATUS_ERROR;
×
772
        break;
×
773
      }
774
      model_map_term(&mdl, b, vtbl_mk_bool(&mdl.vtbl, true));
71✔
775
      ivector_push(&assumptions, b);
71✔
776
    }
777

778
    if (stat != YICES_STATUS_ERROR) {
6✔
779
      stat = check_context_with_model(ctx, params, &mdl, n, assumptions.data);
6✔
780
      if (stat == YICES_STATUS_UNSAT) {
6✔
781
        term_subst_t subst;
782

783
        interpolant = context_get_unsat_model_interpolant(ctx);
5✔
784
        assert(interpolant != NULL_TERM);
785
        core_from_labeled_interpolant(ctx, interpolant, &assumptions, &label_map, &mapped_core);
5✔
786

787
        init_term_subst(&subst, &tm, n, assumptions.data, a);
5✔
788
        interpolant = apply_term_subst(&subst, interpolant);
5✔
789
        delete_term_subst(&subst);
5✔
790
      }
791
    }
792

793
    if (pushed) {
6✔
794
      mcsat_cleanup_assumptions(ctx->mcsat);
5✔
795
      context_pop(ctx);
5✔
796
    }
797
    if (stat == YICES_STATUS_UNSAT) {
6✔
798
      mcsat_set_unsat_result(ctx->mcsat, interpolant);
5✔
799
      cache_unsat_core(ctx, &mapped_core);
5✔
800
    }
801

802
    delete_term_manager(&tm);
6✔
803
    delete_ivector(&mapped_core);
6✔
804
    delete_ivector(&assumptions);
6✔
805
    delete_int_hmap(&label_map);
6✔
806
    delete_model(&mdl);
6✔
807

808
    return stat;
6✔
809
  }
810
}
811

812
static smt_status_t check_context_with_term_assumptions_mcsat(context_t *ctx, const param_t *params, uint32_t n, const term_t *a, int32_t *error) {
6✔
813
  MT_PROTECT(smt_status_t, __yices_globals.lock, _o_check_context_with_term_assumptions_mcsat(ctx, params, n, a, error));
6✔
814
}
815

816
/*
817
 * Check under assumptions given as terms.
818
 * - if MCSAT is enabled, this uses temporary labels + model interpolation.
819
 * - otherwise terms are converted to literals and handled by the CDCL(T) path.
820
 *
821
 * Preconditions:
822
 * - context status must be IDLE.
823
 */
824
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,681✔
825
  if (error != NULL) {
2,681✔
826
    *error = CTX_NO_ERROR;
2,681✔
827
  }
828

829
  context_invalidate_unsat_core_cache(ctx);
2,681✔
830

831
  if (ctx->mcsat == NULL) {
2,681✔
832
    smt_status_t stat;
833
    ivector_t assumptions;
834
    uint32_t i;
835
    literal_t l;
836

837
    init_ivector(&assumptions, n);
2,675✔
838
    for (i=0; i<n; i++) {
94,334✔
839
      l = context_add_assumption(ctx, a[i]);
91,659✔
840
      if (l < 0) {
91,659✔
NEW
841
        if (error != NULL) {
×
NEW
842
          *error = l;
×
843
        }
NEW
844
        delete_ivector(&assumptions);
×
NEW
845
        return YICES_STATUS_ERROR;
×
846
      }
847
      ivector_push(&assumptions, l);
91,659✔
848
    }
849

850
    stat = check_context_with_assumptions(ctx, params, n, assumptions.data);
2,675✔
851
    delete_ivector(&assumptions);
2,675✔
852
    return stat;
2,675✔
853
  }
854

855
  return check_context_with_term_assumptions_mcsat(ctx, params, n, a, error);
6✔
856
}
857

858
/*
859
 * Check with given model
860
 * - if mcsat status is not IDLE, return the status
861
 */
862
smt_status_t check_context_with_model(context_t *ctx, const param_t *params, model_t* mdl, uint32_t n, const term_t t[]) {
144✔
863
  smt_status_t stat;
864

865
  assert(ctx->mcsat != NULL);
866

867
  stat = mcsat_status(ctx->mcsat);
144✔
868
  if (stat == YICES_STATUS_IDLE) {
144✔
869
    mcsat_solve(ctx->mcsat, params, mdl, n, t);
144✔
870
    stat = mcsat_status(ctx->mcsat);
144✔
871

872
    // BD: this looks wrong. We shouldn't call clear yet.
873
    // we want the status to remain STATUS_UNSAT until the next call to check or assert.
874
    //    if (n > 0 && stat == STATUS_UNSAT && context_supports_multichecks(ctx)) {
875
    //      context_clear(ctx);
876
    //    }
877
  }
878

879
  return stat;
144✔
880
}
881

882
/*
883
 * Check with given model and hints
884
 * - set the model hint and call check_context_with_model
885
 */
886
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) {
12✔
887
  assert(m <= n);
888

889
  mcsat_set_model_hint(ctx->mcsat, mdl, n, t);
12✔
890

891
  return check_context_with_model(ctx, params, mdl, m, t);
12✔
892
}
893

894

895
/*
896
 * Precheck: force generation of clauses and other stuff that's
897
 * constructed lazily by the solvers. For example, this
898
 * can be used to convert all the constraints asserted in the
899
 * bitvector to clauses so that we can export the result to DIMACS.
900
 *
901
 * If ctx status is IDLE:
902
 * - the function calls 'start_search' and does one round of propagation.
903
 * - if this results in UNSAT, the function returns UNSAT
904
 * - if the precheck is interrupted, the function returns INTERRUPTED
905
 * - otherwise the function returns UNKNOWN and sets the status to
906
 *   UNKNOWN.
907
 *
908
 * IMPORTANT: call smt_clear or smt_cleanup to restore the context to
909
 * IDLE before doing anything else with this context.
910
 *
911
 * If ctx status is not IDLE, the function returns it and does nothing
912
 * else.
913
 */
914
smt_status_t precheck_context(context_t *ctx) {
×
915
  smt_status_t stat;
916
  smt_core_t *core;
917

918
  core = ctx->core;
×
919

920
  stat = smt_status(core);
×
921
  if (stat == YICES_STATUS_IDLE) {
×
922
    start_search(core, 0, NULL);
×
923
    smt_process(core);
×
924
    stat = smt_status(core);
×
925

926
    assert(stat == YICES_STATUS_UNSAT || stat == YICES_STATUS_SEARCHING ||
927
           stat == YICES_STATUS_INTERRUPTED);
928

929
    if (stat == YICES_STATUS_SEARCHING) {
×
930
      end_search_unknown(core);
×
931
      stat = YICES_STATUS_UNKNOWN;
×
932
    }
933
  }
934

935
  return stat;
×
936
}
937

938

939

940
/*
941
 * Solve using another SAT solver
942
 * - sat_solver = name of the solver to use
943
 * - verbosity = verbosity level (0 means quiet)
944
 * - this may be used only for BV or pure SAT problems
945
 * - we perform one round of propagation to convert the problem to CNF
946
 * - then we call an external SAT solver on the CNF problem
947
 */
948
smt_status_t check_with_delegate(context_t *ctx, const char *sat_solver, uint32_t verbosity) {
×
949
  smt_status_t stat;
950
  smt_core_t *core;
951
  delegate_t delegate;
952
  bvar_t x;
953
  bval_t v;
954

955
  core = ctx->core;
×
956

957
  stat = smt_status(core);
×
958
  if (stat == YICES_STATUS_IDLE) {
×
959
    start_search(core, 0, NULL);
×
960
    smt_process(core);
×
961
    stat = smt_status(core);
×
962

963
    assert(stat == YICES_STATUS_UNSAT || stat == YICES_STATUS_SEARCHING ||
964
           stat == YICES_STATUS_INTERRUPTED);
965

966
    if (stat == YICES_STATUS_SEARCHING) {
×
967
      if (smt_easy_sat(core)) {
×
968
        stat = YICES_STATUS_SAT;
×
969
      } else {
970
        // call the delegate
971
        init_delegate(&delegate, sat_solver, num_vars(core));
×
972
        delegate_set_verbosity(&delegate, verbosity);
×
973

974
        stat = solve_with_delegate(&delegate, core);
×
975
        set_smt_status(core, stat);
×
976
        if (stat == YICES_STATUS_SAT) {
×
977
          for (x=0; x<num_vars(core); x++) {
×
978
            v = delegate_get_value(&delegate, x);
×
979
            set_bvar_value(core, x, v);
×
980
          }
981
        }
982
        delete_delegate(&delegate);
×
983
      }
984
    }
985
  }
986

987
  return stat;
×
988
}
989

990

991
/*
992
 * Bit-blast then export to DIMACS
993
 * - filename = name of the output file
994
 * - status = status of the context after bit-blasting
995
 *
996
 * If ctx status is IDLE
997
 * - perform one round of propagation to conver the problem to CNF
998
 * - export the CNF to DIMACS
999
 *
1000
 * If ctx status is not IDLE,
1001
 * - store the stauts in *status and do nothing else
1002
 *
1003
 * Return code:
1004
 *  1 if the DIMACS file was created
1005
 *  0 if the problem was solved by the propagation round
1006
 * -1 if there was an error in creating or writing to the file.
1007
 */
1008
int32_t bitblast_then_export_to_dimacs(context_t *ctx, const char *filename, smt_status_t *status) {
×
1009
  smt_core_t *core;
1010
  FILE *f;
1011
  smt_status_t stat;
1012
  int32_t code;
1013

1014
  core = ctx->core;
×
1015

1016
  code = 0;
×
1017
  stat = smt_status(core);
×
1018
  if (stat == YICES_STATUS_IDLE) {
×
1019
    start_search(core, 0, NULL);
×
1020
    smt_process(core);
×
1021
    stat = smt_status(core);
×
1022

1023
    assert(stat == YICES_STATUS_UNSAT || stat == YICES_STATUS_SEARCHING ||
1024
           stat == YICES_STATUS_INTERRUPTED);
1025

1026
    if (stat == YICES_STATUS_SEARCHING) {
×
1027
      code = 1;
×
1028
      f = fopen(filename, "w");
×
1029
      if (f == NULL) {
×
1030
        code = -1;
×
1031
      } else {
1032
        dimacs_print_bvcontext(f, ctx);
×
1033
        if (ferror(f)) code = -1;
×
1034
        fclose(f);
×
1035
      }
1036
    }
1037
  }
1038

1039
  *status = stat;
×
1040

1041
  return code;
×
1042
}
1043

1044

1045
/*
1046
 * Simplify then export to Dimacs:
1047
 * - filename = name of the output file
1048
 * - status = status of the context after CNF conversion + preprocessing
1049
 *
1050
 * If ctx status is IDLE
1051
 * - perform one round of propagation to convert the problem to CNF
1052
 * - export the CNF to y2sat for extra preprocessing then export that to DIMACS
1053
 *
1054
 * If ctx status is not IDLE, the function stores that in *status
1055
 * If y2sat preprocessing solves the formula, return the status also in *status
1056
 *
1057
 * Return code:
1058
 *  1 if the DIMACS file was created
1059
 *  0 if the problems was solved by preprocessing (or if ctx status is not IDLE)
1060
 * -1 if there was an error creating or writing to the file.
1061
 */
1062
int32_t process_then_export_to_dimacs(context_t *ctx, const char *filename, smt_status_t *status) {
×
1063
  smt_core_t *core;
1064
  FILE *f;
1065
  smt_status_t stat;
1066
  delegate_t delegate;
1067
  bvar_t x;
1068
  bval_t v;
1069
  int32_t code;
1070

1071
  core = ctx->core;
×
1072

1073
  code = 0;
×
1074
  stat = smt_status(core);
×
1075
  if (stat == YICES_STATUS_IDLE) {
×
1076
    start_search(core, 0, NULL);
×
1077
    smt_process(core);
×
1078
    stat = smt_status(core);
×
1079

1080
    assert(stat == YICES_STATUS_UNSAT || stat == YICES_STATUS_SEARCHING ||
1081
           stat == YICES_STATUS_INTERRUPTED);
1082

1083
    if (stat == YICES_STATUS_SEARCHING) {
×
1084
      if (smt_easy_sat(core)) {
×
1085
        stat = YICES_STATUS_SAT;
×
1086
      } else {
1087
        // call the delegate
1088
        init_delegate(&delegate, "y2sat", num_vars(core));
×
1089
        delegate_set_verbosity(&delegate, 0);
×
1090

1091
        stat = preprocess_with_delegate(&delegate, core);
×
1092
        set_smt_status(core, stat);
×
1093
        if (stat == YICES_STATUS_SAT) {
×
1094
          for (x=0; x<num_vars(core); x++) {
×
1095
            v = delegate_get_value(&delegate, x);
×
1096
            set_bvar_value(core, x, v);
×
1097
          }
1098
        } else if (stat == YICES_STATUS_UNKNOWN) {
×
1099
          code = 1;
×
1100
          f = fopen(filename, "w");
×
1101
          if (f == NULL) {
×
1102
            code = -1;
×
1103
          } else {
1104
            export_to_dimacs_with_delegate(&delegate, f);
×
1105
            if (ferror(f)) code = -1;
×
1106
            fclose(f);
×
1107
          }
1108
        }
1109

1110
        delete_delegate(&delegate);
×
1111
      }
1112
    }
1113
  }
1114

1115
  *status = stat;
×
1116

1117
  return code;
×
1118
}
1119

1120

1121

1122
/*
1123
 * MODEL CONSTRUCTION
1124
 */
1125

1126
/*
1127
 * Value of literal l in ctx->core
1128
 */
1129
static value_t bool_value(context_t *ctx, value_table_t *vtbl, literal_t l) {
65,201✔
1130
  value_t v;
1131

1132
  v = null_value; // prevent GCC warning
65,201✔
1133
  switch (literal_value(ctx->core, l)) {
65,201✔
1134
  case VAL_FALSE:
38,577✔
1135
    v = vtbl_mk_false(vtbl);
38,577✔
1136
    break;
38,577✔
1137
  case VAL_UNDEF_FALSE:
×
1138
  case VAL_UNDEF_TRUE:
1139
    v = vtbl_mk_unknown(vtbl);
×
1140
    break;
×
1141
  case VAL_TRUE:
26,624✔
1142
    v = vtbl_mk_true(vtbl);
26,624✔
1143
    break;
26,624✔
1144
  }
1145
  return v;
65,201✔
1146
}
1147

1148

1149
/*
1150
 * Value of arithmetic variable x in ctx->arith_solver
1151
 */
1152
static value_t arith_value(context_t *ctx, value_table_t *vtbl, thvar_t x) {
22,590✔
1153
  rational_t *a;
1154
  value_t v;
1155

1156
  assert(context_has_arith_solver(ctx));
1157

1158
  a = &ctx->aux;
22,590✔
1159
  if (ctx->arith.value_in_model(ctx->arith_solver, x, a)) {
22,590✔
1160
    v = vtbl_mk_rational(vtbl, a);
22,590✔
1161
  } else {
1162
    v = vtbl_mk_unknown(vtbl);
×
1163
  }
1164

1165
  return v;
22,590✔
1166
}
1167

1168

1169

1170
/*
1171
 * Value of bitvector variable x in ctx->bv_solver
1172
 */
1173
static value_t bv_value(context_t *ctx, value_table_t *vtbl, thvar_t x) {
18,133✔
1174
  bvconstant_t *b;
1175
  value_t v;
1176

1177
  assert(context_has_bv_solver(ctx));
1178

1179
  b = &ctx->bv_buffer;
18,133✔
1180
  if (ctx->bv.value_in_model(ctx->bv_solver, x, b)) {
18,133✔
1181
    v = vtbl_mk_bv_from_constant(vtbl, b);
18,133✔
1182
  } else {
1183
    v = vtbl_mk_unknown(vtbl);
×
1184
  }
1185

1186
  return v;
18,133✔
1187
}
1188

1189

1190
/*
1191
 * Get a value for term t in the solvers or egraph
1192
 * - attach the mapping from t to that value in model
1193
 * - if we don't have a concrete object for t but t is
1194
 *   mapped to a term u and the model->has_alias is true,
1195
 *   then we store the mapping [t --> u] in the model's
1196
 *   alias map.
1197
 */
1198
static void build_term_value(context_t *ctx, model_t *model, term_t t) {
319,774✔
1199
  value_table_t *vtbl;
1200
  term_t r;
1201
  uint32_t polarity;
1202
  int32_t x;
1203
  type_t tau;
1204
  value_t v;
1205

1206
  /*
1207
   * Get the root of t in the substitution table
1208
   */
1209
  r = intern_tbl_get_root(&ctx->intern, t);
319,774✔
1210
  if (intern_tbl_root_is_mapped(&ctx->intern, r)) {
319,774✔
1211
    /*
1212
     * r is mapped to some object x in egraph/core/or theory solvers
1213
     * - keep track of polarity then force r to positive polarity
1214
     */
1215
    vtbl = model_get_vtbl(model);
110,696✔
1216
    polarity = polarity_of(r);
110,696✔
1217
    r = unsigned_term(r);
110,696✔
1218

1219
    /*
1220
     * Convert x to a concrete value
1221
     */
1222
    x = intern_tbl_map_of_root(&ctx->intern, r);
110,696✔
1223
    if (code_is_eterm(x)) {
110,696✔
1224
      // x refers to an egraph object or true_occ/false_occ
1225
      if (x == bool2code(true)) {
4,772✔
1226
        v = vtbl_mk_true(vtbl);
475✔
1227
      } else if (x == bool2code(false)) {
4,297✔
1228
        v = vtbl_mk_false(vtbl);
1,420✔
1229
      } else {
1230
        assert(context_has_egraph(ctx));
1231
        v = egraph_get_value(ctx->egraph, vtbl, code2occ(x));
2,877✔
1232
      }
1233

1234
    } else {
1235
      // x refers to a literal or a theory variable
1236
      tau = term_type(ctx->terms, r);
105,924✔
1237
      switch (type_kind(ctx->types, tau)) {
105,924✔
1238
      case BOOL_TYPE:
65,201✔
1239
        v = bool_value(ctx, vtbl, code2literal(x));
65,201✔
1240
        break;
65,201✔
1241

1242
      case INT_TYPE:
22,590✔
1243
      case REAL_TYPE:
1244
        v = arith_value(ctx, vtbl, code2thvar(x));
22,590✔
1245
        break;
22,590✔
1246

1247
      case BITVECTOR_TYPE:
18,133✔
1248
        v = bv_value(ctx, vtbl, code2thvar(x));
18,133✔
1249
        break;
18,133✔
1250

1251
      default:
×
1252
        /*
1253
         * This should never happen:
1254
         * scalar, uninterpreted, tuple, function terms
1255
         * are mapped to egraph terms.
1256
         */
1257
        assert(false);
1258
        v = vtbl_mk_unknown(vtbl); // prevent GCC warning
×
1259
        break;
×
1260
      }
1261
    }
1262

1263
    /*
1264
     * Restore polarity then add mapping [t --> v] in the model
1265
     */
1266
    if (! object_is_unknown(vtbl, v)) {
110,696✔
1267
      if (object_is_boolean(vtbl, v)) {
110,696✔
1268
        if (polarity) {
67,096✔
1269
          // negate the value
1270
          v = vtbl_mk_not(vtbl, v);
24✔
1271
        }
1272
      }
1273
      model_map_term(model, t, v);
110,696✔
1274
    }
1275

1276
  } else {
1277
    /*
1278
     * r is not mapped to anything:
1279
     *
1280
     * 1) If t == r and t is present in the internalization table
1281
     *    then t is relevant. So we should display its value
1282
     *    when we print the model. To do this, we assign an
1283
     *    arbitrary value v to t and store [t := v] in the map.
1284
     *
1285
     * 2) If t != r then we keep the mapping [t --> r] in
1286
     *    the alias table (provided the model supports aliases).
1287
     */
1288
    if (t == r) {
209,078✔
1289
      if (intern_tbl_term_present(&ctx->intern, t)) {
208,430✔
1290
        tau = term_type(ctx->terms, t);
×
1291
        vtbl = model_get_vtbl(model);
×
1292
        v = vtbl_make_object(vtbl, tau);
×
1293
        model_map_term(model, t, v);
×
1294
      }
1295
    } else if (model->has_alias) {
648✔
1296
      // t != r: keep the substitution [t --> r] in the model
1297
      model_add_substitution(model, t, r);
648✔
1298
    }
1299
  }
1300
}
319,774✔
1301

1302

1303

1304

1305
/*
1306
 * Build a model for the current context (including all satellite solvers)
1307
 * - the context status must be SAT (or UNKNOWN)
1308
 * - if model->has_alias is true, we store the term substitution
1309
 *   defined by ctx->intern_tbl into the model
1310
 * - cleanup of satellite models needed using clean_solver_models()
1311
 */
1312
void build_model(model_t *model, context_t *ctx) {
42,122✔
1313
  term_table_t *terms;
1314
  uint32_t i, n;
1315
  term_t t;
1316

1317
  assert(smt_status(ctx->core) == YICES_STATUS_SAT || smt_status(ctx->core) == YICES_STATUS_UNKNOWN || mcsat_status(ctx->mcsat) == YICES_STATUS_SAT);
1318

1319
  /*
1320
   * First build assignments in the satellite solvers
1321
   * and get the val_in_model functions for the egraph
1322
   */
1323
  if (context_has_arith_solver(ctx)) {
42,122✔
1324
    ctx->arith.build_model(ctx->arith_solver);
32,907✔
1325
  }
1326
  if (context_has_bv_solver(ctx)) {
42,122✔
1327
    ctx->bv.build_model(ctx->bv_solver);
21,400✔
1328
  }
1329

1330
  /*
1331
   * Construct the egraph model
1332
   */
1333
  if (context_has_egraph(ctx)) {
42,122✔
1334
    egraph_build_model(ctx->egraph, model_get_vtbl(model));
13,033✔
1335
  }
1336

1337
  /*
1338
   * Construct the mcsat model.
1339
   */
1340
  if (context_has_mcsat(ctx)) {
42,122✔
1341
    mcsat_build_model(ctx->mcsat, model);
30✔
1342
  }
1343

1344
  // scan the internalization table
1345
  terms = ctx->terms;
42,122✔
1346
  n = intern_tbl_num_terms(&ctx->intern);
42,122✔
1347
  for (i=1; i<n; i++) { // first real term has index 1 (i.e. true_term)
1,235,095,715✔
1348
    if (good_term_idx(terms, i)) {
1,235,053,593✔
1349
      t = pos_occ(i);
1,235,053,593✔
1350
      if (term_kind(terms, t) == UNINTERPRETED_TERM) {
1,235,053,593✔
1351
        build_term_value(ctx, model, t);
319,774✔
1352
      }
1353
    }
1354
  }
1355
}
42,122✔
1356

1357

1358
/*
1359
 * Cleanup solver models
1360
 */
1361
void clean_solver_models(context_t *ctx) {
42,122✔
1362
  if (context_has_arith_solver(ctx)) {
42,122✔
1363
    ctx->arith.free_model(ctx->arith_solver);
32,907✔
1364
  }
1365
  if (context_has_bv_solver(ctx)) {
42,122✔
1366
    ctx->bv.free_model(ctx->bv_solver);
21,400✔
1367
  }
1368
  if (context_has_egraph(ctx)) {
42,122✔
1369
    egraph_free_model(ctx->egraph);
13,033✔
1370
  }
1371
}
42,122✔
1372

1373

1374

1375
/*
1376
 * Build a model for the current context
1377
 * - the context status must be SAT (or UNKNOWN)
1378
 * - if model->has_alias is true, we store the term substitution
1379
 *   defined by ctx->intern_tbl into the model
1380
 */
1381
void context_build_model(model_t *model, context_t *ctx) {
221✔
1382
  // Build solver models and term values
1383
  build_model(model, ctx);
221✔
1384

1385
  // Cleanup
1386
  clean_solver_models(ctx);
221✔
1387
}
221✔
1388

1389

1390

1391
/*
1392
 * Read the value of a Boolean term t
1393
 * - return VAL_TRUE/VAL_FALSE or VAL_UNDEF_FALSE/VAL_UNDEF_TRUE if t's value is not known
1394
 */
1395
bval_t context_bool_term_value(context_t *ctx, term_t t) {
×
1396
  term_t r;
1397
  uint32_t polarity;
1398
  int32_t x;
1399
  bval_t v;
1400

1401
  assert(is_boolean_term(ctx->terms, t));
1402

1403
  // default value if t is not in the internalization table
1404
  v = VAL_UNDEF_FALSE;
×
1405
  r = intern_tbl_get_root(&ctx->intern, t);
×
1406
  if (intern_tbl_root_is_mapped(&ctx->intern, r)) {
×
1407
    // r is mapped to some object x
1408
    polarity = polarity_of(r);
×
1409
    r = unsigned_term(r);
×
1410

1411
    x  = intern_tbl_map_of_root(&ctx->intern, r);
×
1412
    if (code_is_eterm(x)) {
×
1413
      // x must be either true_occ or false_occ
1414
      if (x == bool2code(true)) {
×
1415
        v = VAL_TRUE;
×
1416
      } else {
1417
        assert(x == bool2code(false));
1418
        v = VAL_FALSE;
×
1419
      }
1420
    } else {
1421
      // x refers to a literal in the smt core
1422
      v = literal_value(ctx->core, code2literal(x));
×
1423
    }
1424

1425
    // negate v if polarity is 1 (cf. smt_core_base_types.h)
1426
    v ^= polarity;
×
1427
  }
1428

1429
  return v;
×
1430
}
1431

1432

1433
/*
1434
 * UNSAT CORE
1435
 */
1436

1437
/*
1438
 * Build an unsat core:
1439
 * - store the result in v
1440
 * - first reuse a cached term core if available.
1441
 * - otherwise:
1442
 *   CDCL(T): build from smt_core then cache as terms
1443
 *   MCSAT: return empty unless check-with-term-assumptions populated the cache.
1444
 */
1445
void context_build_unsat_core(context_t *ctx, ivector_t *v) {
2,737✔
1446
  smt_core_t *core;
1447
  uint32_t i, n;
1448
  term_t t;
1449

1450
  if (ctx->unsat_core_cache != NULL) {
2,737✔
1451
    // Fast path: repeated get_unsat_core returns the cached term vector.
1452
    ivector_reset(v);
5✔
1453
    ivector_copy(v, ctx->unsat_core_cache->data, ctx->unsat_core_cache->size);
5✔
1454
    return;
5✔
1455
  }
1456

1457
  if (ctx->mcsat != NULL) {
2,732✔
1458
    // MCSAT core extraction is done in check-with-term-assumptions; without cache
1459
    // there is no generic context-level core structure to rebuild from here.
1460
    ivector_reset(v);
×
1461
    return;
×
1462
  }
1463

1464
  core = ctx->core;
2,732✔
1465
  assert(core != NULL && core->status == YICES_STATUS_UNSAT);
1466
  build_unsat_core(core, v);
2,732✔
1467

1468
  // convert from literals to terms
1469
  n = v->size;
2,732✔
1470
  for (i=0; i<n; i++) {
37,661✔
1471
    t = assumption_term_for_literal(&ctx->assumptions, v->data[i]);
34,929✔
1472
    assert(t >= 0);
1473
    v->data[i] = t;
34,929✔
1474
  }
1475

1476
  // Cache the converted term core for subsequent queries.
1477
  cache_unsat_core(ctx, v);
2,732✔
1478
}
1479

1480

1481
/*
1482
 * MODEL INTERPOLANT
1483
 */
1484
term_t context_get_unsat_model_interpolant(context_t *ctx) {
51✔
1485
  assert(ctx->mcsat != NULL);
1486
  return mcsat_get_unsat_model_interpolant(ctx->mcsat);
51✔
1487
}
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