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

SRI-CSL / yices2 / 23882467517

02 Apr 2026 03:34AM UTC coverage: 66.728% (+0.2%) from 66.539%
23882467517

Pull #611

github

web-flow
Merge branch 'master' into mcsat-supplement-cdclt
Pull Request #611: Wrap MCSAT as a Nelson-Oppen theory solver in CDCL(T) architecture

690 of 1006 new or added lines in 15 files covered. (68.59%)

2 existing lines in 2 files now uncovered.

84087 of 126014 relevant lines covered (66.73%)

1691640.28 hits per line

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

72.46
/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/mcsat_satellite.h"
39
#include "solvers/funs/fun_solver.h"
40
#include "solvers/simplex/simplex.h"
41
#include "terms/term_explorer.h"
42
#include "terms/term_manager.h"
43
#include "terms/term_substitution.h"
44
#include "utils/int_hash_map.h"
45
#include "utils/int_hash_sets.h"
46

47
#include "api/yices_globals.h"
48
#include "api/yices_api_lock_free.h"
49
#include "mt/thread_macros.h"
50

51

52

53
/*
54
 * TRACE FUNCTIONS
55
 */
56

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

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

82

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

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

94

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

103

104

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

113

114

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

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

133
  case VAL_TRUE:
×
134
    break;
×
135

136
  case VAL_FALSE:
2,576✔
137
    save_conflicting_assumption(core, l);
2,576✔
138
    break;
2,576✔
139
  }
140
}
90,168✔
141

142

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

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

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

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

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

175
    // assumption
176
    if (core->has_assumptions) {
6,282,571✔
177
      l = get_next_assumption(core);
4✔
178
      if (l != null_literal) {
4✔
179
        process_assumption(core, l);
3✔
180
        continue;
3✔
181
      }
182
    }
183

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

195
  *reduce_threshold = r_threshold;
13,428✔
196
}
13,428✔
197

198

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

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

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

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

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

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

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

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

258

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

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

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

278
  smt_process(core);
20,855✔
279
  while (smt_status(core) == YICES_STATUS_SEARCHING && num_conflicts(core) <= max_conflicts) {
633,779✔
280
    // reduce heuristic
281
    if (num_learned_clauses(core) >= r_threshold) {
612,924✔
282
      deletions = core->stats.learned_clauses_deleted;
61✔
283
      reduce_clause_database(core);
61✔
284
      r_threshold = (uint32_t) (r_threshold * r_factor);
61✔
285
      trace_reduce(core, core->stats.learned_clauses_deleted - deletions);
61✔
286
    }
287

288
    // assumption
289
    if (core->has_assumptions) {
612,924✔
290
      l = get_next_assumption(core);
5✔
291
      if (l != null_literal) {
5✔
292
        process_assumption(core, l);
5✔
293
        continue;
5✔
294
      }
295
    }
296

297
    // decision
298
    l = select_unassigned_literal(core);
612,919✔
299
    if (l == null_literal) {
612,919✔
300
      // all variables assigned: call final check
301
      smt_final_check(core);
22,626✔
302
    } else {
303
      // apply the branching heuristic
304
      l = branch(core, l);
590,293✔
305
      // propagation
306
      decide_literal(core, l);
590,293✔
307
      smt_process(core);
590,293✔
308
    }
309
  }
310

311
  *reduce_threshold = r_threshold;
20,855✔
312
}
20,855✔
313

314

315

316

317

318
/*
319
 * SUPPORTED BRANCHING
320
 */
321

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

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

335

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

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

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

363

364

365

366

367
/*
368
 * CORE SOLVER
369
 */
370

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

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

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

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

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

433
      if (smt_status(core) != YICES_STATUS_SEARCHING) break;
47,704✔
434

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

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

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

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

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

471
  trace_done(core);
45,398✔
472
}
45,398✔
473

474

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

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

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

529
  if (ctx->mcsat_supplement_active && egraph != NULL && egraph->th[ETYPE_MCSAT] != NULL) {
45,398✔
530
    mcsat_satellite_set_search_parameters((mcsat_satellite_t *) egraph->th[ETYPE_MCSAT], params);
6✔
531
  }
532

533
  /*
534
   * Set simplex parameters
535
   */
536
  if (context_has_simplex_solver(ctx)) {
45,398✔
537
    simplex = ctx->arith_solver;
33,232✔
538
    if (params->use_simplex_prop) {
33,232✔
539
      simplex_enable_propagation(simplex);
12,856✔
540
      simplex_set_prop_threshold(simplex, params->max_prop_row_size);
12,856✔
541
    }
542
    if (params->adjust_simplex_model) {
33,232✔
543
      simplex_enable_adjust_model(simplex);
12,810✔
544
    }
545
    simplex_set_bland_threshold(simplex, params->bland_threshold);
33,232✔
546
    if (params->integer_check) {
33,232✔
547
      simplex_enable_periodic_icheck(simplex);
×
548
      simplex_set_integer_check_period(simplex, params->integer_check_period);
×
549
    }
550
  }
551

552
  /*
553
   * Set array solver parameters
554
   */
555
  if (context_has_fun_solver(ctx)) {
45,398✔
556
    fsolver = ctx->fun_solver;
12,867✔
557
    fun_solver_set_max_update_conflicts(fsolver, params->max_update_conflicts);
12,867✔
558
    fun_solver_set_max_extensionality(fsolver, params->max_extensionality);
12,867✔
559
  }
560
}
45,398✔
561

562
static smt_status_t _o_call_mcsat_solver(context_t *ctx, const param_t *params) {
1,082✔
563
  smt_status_t stat;
564

565
  mcsat_solve(ctx->mcsat, params, NULL, 0, NULL);
1,082✔
566
  stat = mcsat_status(ctx->mcsat);
1,082✔
567

568
  /*
569
   * For plain UNSAT results (without explicit model assumptions), keep the
570
   * interpolant API usable by providing the canonical false interpolant.
571
   */
572
  if (stat == YICES_STATUS_UNSAT &&
1,384✔
573
      context_supports_model_interpolation(ctx) &&
311✔
574
      mcsat_get_unsat_model_interpolant(ctx->mcsat) == NULL_TERM) {
9✔
NEW
575
    mcsat_set_unsat_result(ctx->mcsat, false_term);
×
576
  }
577

578
  return stat;
1,082✔
579
}
580

581
static smt_status_t call_mcsat_solver(context_t *ctx, const param_t *params) {
1,082✔
582
  MT_PROTECT(smt_status_t, __yices_globals.lock, _o_call_mcsat_solver(ctx, params));
1,082✔
583
}
584

585
/*
586
 * Initialize search parameters then call solve
587
 * - if ctx->status is not IDLE, return the status.
588
 * - if params is NULL, we use default values.
589
 */
590
smt_status_t check_context(context_t *ctx, const param_t *params) {
60,627✔
591
  smt_core_t *core;
592
  smt_status_t stat;
593
  mcsat_satellite_t *sat;
594

595
  if (params == NULL) {
60,627✔
596
    params = get_default_params();
×
597
  }
598

599
  if (ctx->mcsat != NULL) {
60,627✔
600
    return call_mcsat_solver(ctx, params);
1,082✔
601
  }
602

603
  core = ctx->core;
59,545✔
604
  stat = smt_status(core);
59,545✔
605
  if (stat == YICES_STATUS_IDLE) {
59,545✔
606
    // clean state: the search can proceed
607
    context_set_search_parameters(ctx, params);
42,721✔
608
    solve(core, params, 0, NULL);
42,721✔
609
    stat = smt_status(core);
42,721✔
610
  }
611

612
  sat = NULL;
59,545✔
613
  if (ctx->mcsat_supplement_active &&
59,545✔
614
      ctx->egraph != NULL &&
4✔
615
      ctx->egraph->th[ETYPE_MCSAT] != NULL) {
4✔
616
    sat = (mcsat_satellite_t *) ctx->egraph->th[ETYPE_MCSAT];
4✔
617
  }
618
  if (stat == YICES_STATUS_UNSAT &&
59,545✔
NEW
619
      sat != NULL &&
×
NEW
620
      context_supports_model_interpolation(ctx) &&
×
NEW
621
      mcsat_satellite_get_unsat_model_interpolant(sat) == NULL_TERM) {
×
NEW
622
    mcsat_satellite_set_unsat_model_interpolant(sat, false_term);
×
623
  }
624

625
  return stat;
59,545✔
626
}
627

628

629
/*
630
 * Check with assumptions a[0] ... a[n-1]
631
 * - if ctx->status is not IDLE, return the status.
632
 */
633
smt_status_t check_context_with_assumptions(context_t *ctx, const param_t *params, uint32_t n, const literal_t *a) {
2,677✔
634
  smt_core_t *core;
635
  smt_status_t stat;
636
  mcsat_satellite_t *sat;
637

638
  assert(ctx->mcsat == NULL);
639

640
  core = ctx->core;
2,677✔
641
  stat = smt_status(core);
2,677✔
642
  if (stat == YICES_STATUS_IDLE) {
2,677✔
643
    // clean state
644
    if (params == NULL) {
2,677✔
645
      params = get_default_params();
×
646
    }
647
    context_set_search_parameters(ctx, params);
2,677✔
648
    solve(core, params, n, a);
2,677✔
649
    stat = smt_status(core);
2,677✔
650
  }
651

652
  sat = NULL;
2,677✔
653
  if (ctx->mcsat_supplement_active &&
2,677✔
654
      ctx->egraph != NULL &&
2✔
655
      ctx->egraph->th[ETYPE_MCSAT] != NULL) {
2✔
656
    sat = (mcsat_satellite_t *) ctx->egraph->th[ETYPE_MCSAT];
2✔
657
  }
658
  if (stat == YICES_STATUS_UNSAT &&
2,677✔
659
      sat != NULL &&
2✔
660
      context_supports_model_interpolation(ctx) &&
2✔
NEW
661
      mcsat_satellite_get_unsat_model_interpolant(sat) == NULL_TERM) {
×
NEW
662
    mcsat_satellite_set_unsat_model_interpolant(sat, false_term);
×
663
  }
664

665
  return stat;
2,677✔
666
}
667

668
/*
669
 * Explore term t and collect all Boolean atoms into atoms.
670
 */
671
static void collect_boolean_atoms(context_t *ctx, term_t t, int_hset_t *atoms, int_hset_t *visited) {
56✔
672
  term_table_t *terms;
673
  uint32_t i, nchildren;
674

675
  if (t < 0) {
56✔
676
    t = not(t);
×
677
  }
678

679
  if (int_hset_member(visited, t)) {
56✔
680
    return;
×
681
  }
682
  int_hset_add(visited, t);
56✔
683

684
  terms = ctx->terms;
56✔
685
  if (term_type(terms, t) == bool_type(terms->types)) {
56✔
686
    int_hset_add(atoms, t);
56✔
687
  }
688

689
  if (term_is_projection(terms, t)) {
56✔
690
    collect_boolean_atoms(ctx, proj_term_arg(terms, t), atoms, visited);
×
691
  } else if (term_is_sum(terms, t)) {
56✔
692
    nchildren = term_num_children(terms, t);
×
693
    for (i=0; i<nchildren; i++) {
×
694
      term_t child;
695
      mpq_t q;
696
      mpq_init(q);
×
697
      sum_term_component(terms, t, i, q, &child);
×
698
      collect_boolean_atoms(ctx, child, atoms, visited);
×
699
      mpq_clear(q);
×
700
    }
701
  } else if (term_is_bvsum(terms, t)) {
56✔
702
    uint32_t nbits = term_bitsize(terms, t);
×
703
    int32_t *aux = (int32_t*) safe_malloc(nbits * sizeof(int32_t));
×
704
    nchildren = term_num_children(terms, t);
×
705
    for (i=0; i<nchildren; i++) {
×
706
      term_t child;
707
      bvsum_term_component(terms, t, i, aux, &child);
×
708
      collect_boolean_atoms(ctx, child, atoms, visited);
×
709
    }
710
    safe_free(aux);
×
711
  } else if (term_is_product(terms, t)) {
56✔
712
    nchildren = term_num_children(terms, t);
×
713
    for (i=0; i<nchildren; i++) {
×
714
      term_t child;
715
      uint32_t exp;
716
      product_term_component(terms, t, i, &child, &exp);
×
717
      collect_boolean_atoms(ctx, child, atoms, visited);
×
718
    }
719
  } else if (term_is_composite(terms, t)) {
56✔
720
    nchildren = term_num_children(terms, t);
30✔
721
    for (i=0; i<nchildren; i++) {
81✔
722
      collect_boolean_atoms(ctx, term_child(terms, t, i), atoms, visited);
51✔
723
    }
724
  }
725
}
726

727
/*
728
 * Extract assumptions whose labels appear in term t.
729
 */
730
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✔
731
  int_hset_t atoms, visited;
732
  uint32_t i;
733

734
  init_int_hset(&atoms, 0);
5✔
735
  init_int_hset(&visited, 0);
5✔
736
  collect_boolean_atoms(ctx, t, &atoms, &visited);
5✔
737

738
  ivector_reset(core);
5✔
739
  for (i=0; i<labels->size; i++) {
75✔
740
    term_t label = labels->data[i];
70✔
741
    if (int_hset_member(&atoms, label)) {
70✔
742
      int_hmap_pair_t *p = int_hmap_find((int_hmap_t *) label_map, label);
26✔
743
      if (p != NULL) {
26✔
744
        ivector_push(core, p->val);
26✔
745
      }
746
    }
747
  }
748

749
  delete_int_hset(&visited);
5✔
750
  delete_int_hset(&atoms);
5✔
751
}
5✔
752

753
/*
754
 * Cache a core vector in the context.
755
 */
756
static void cache_unsat_core(context_t *ctx, const ivector_t *core) {
2,739✔
757
  if (ctx->unsat_core_cache == NULL) {
2,739✔
758
    ctx->unsat_core_cache = (ivector_t *) safe_malloc(sizeof(ivector_t));
2,739✔
759
    init_ivector(ctx->unsat_core_cache, core->size);
2,739✔
760
  } else {
761
    ivector_reset(ctx->unsat_core_cache);
×
762
  }
763
  ivector_copy(ctx->unsat_core_cache, core->data, core->size);
2,739✔
764
}
2,739✔
765

766
/*
767
 * MCSAT variant of check_context_with_term_assumptions.
768
 * Caller must hold __yices_globals.lock.
769
 */
770
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✔
771
  smt_status_t stat;
772
  ivector_t assumptions;
773
  uint32_t i;
774

775
  /*
776
   * MCSAT: create fresh labels b_i, assert (b_i => a_i), then solve with model b_i=true.
777
   * We extract interpolant/core before cleanup, then restore sticky UNSAT artifacts.
778
   */
779
  if (!context_supports_model_interpolation(ctx)) {
6✔
780
    if (error != NULL) {
×
781
      *error = CTX_OPERATION_NOT_SUPPORTED;
×
782
    }
783
    return YICES_STATUS_ERROR;
×
784
  }
785

786
  {
787
    model_t mdl;               // temporary model: sets all label terms b_i to true
788
    int_hmap_t label_map;      // map label b_i -> original assumption a_i
789
    ivector_t mapped_core;     // translated core over original assumptions
790
    term_t interpolant = NULL_TERM; // raw/substituted interpolant for sticky UNSAT result
6✔
791
    int32_t code;              // return code from assert_formula (negative on internalization error)
792
    bool pushed;               // whether we pushed a temporary scope and must pop it
793
    term_manager_t tm;
794

795
    init_model(&mdl, ctx->terms, true);
6✔
796
    init_int_hmap(&label_map, 0);
6✔
797
    init_ivector(&assumptions, n);
6✔
798
    init_ivector(&mapped_core, 0);
6✔
799
    init_term_manager(&tm, ctx->terms);
6✔
800
    stat = YICES_STATUS_IDLE;
6✔
801

802
    pushed = false;
6✔
803
    if (context_supports_pushpop(ctx)) {
6✔
804
      context_push(ctx);
5✔
805
      pushed = true;
5✔
806
    }
807

808
    for (i=0; i<n; i++) {
77✔
809
      term_t b = new_uninterpreted_term(ctx->terms, bool_id);
71✔
810
      term_t implication = mk_implies(&tm, b, a[i]);
71✔
811

812
      int_hmap_add(&label_map, b, a[i]);
71✔
813
      code = _o_assert_formula(ctx, implication);
71✔
814
      if (code < 0) {
71✔
815
        if (error != NULL) {
×
816
          *error = code;
×
817
        }
818
        stat = YICES_STATUS_ERROR;
×
819
        break;
×
820
      }
821
      model_map_term(&mdl, b, vtbl_mk_bool(&mdl.vtbl, true));
71✔
822
      ivector_push(&assumptions, b);
71✔
823
    }
824

825
    if (stat != YICES_STATUS_ERROR) {
6✔
826
      stat = check_context_with_model(ctx, params, &mdl, n, assumptions.data);
6✔
827
      if (stat == YICES_STATUS_UNSAT) {
6✔
828
        term_subst_t subst;
829

830
        interpolant = context_get_unsat_model_interpolant(ctx);
5✔
831
        assert(interpolant != NULL_TERM);
832
        core_from_labeled_interpolant(ctx, interpolant, &assumptions, &label_map, &mapped_core);
5✔
833

834
        init_term_subst(&subst, &tm, n, assumptions.data, a);
5✔
835
        interpolant = apply_term_subst(&subst, interpolant);
5✔
836
        delete_term_subst(&subst);
5✔
837
      }
838
    }
839

840
    if (pushed) {
6✔
841
      mcsat_cleanup_assumptions(ctx->mcsat);
5✔
842
      context_pop(ctx);
5✔
843
    }
844
    if (stat == YICES_STATUS_UNSAT) {
6✔
845
      mcsat_set_unsat_result(ctx->mcsat, interpolant);
5✔
846
      cache_unsat_core(ctx, &mapped_core);
5✔
847
    }
848

849
    delete_term_manager(&tm);
6✔
850
    delete_ivector(&mapped_core);
6✔
851
    delete_ivector(&assumptions);
6✔
852
    delete_int_hmap(&label_map);
6✔
853
    delete_model(&mdl);
6✔
854

855
    return stat;
6✔
856
  }
857
}
858

859
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✔
860
  MT_PROTECT(smt_status_t, __yices_globals.lock, _o_check_context_with_term_assumptions_mcsat(ctx, params, n, a, error));
6✔
861
}
862

863
/*
864
 * Supplemental-MCSAT variant for term assumptions in CDCL(T) mode.
865
 * We encode assumptions via fresh labels b_i with implications (b_i => a_i),
866
 * then solve under assumptions b_i = true.
867
 *
868
 * Labels are kept in the context (no push/pop) so the regular context status
869
 * and multicheck protocol remain unchanged.
870
 */
871
static smt_status_t _o_check_context_with_term_assumptions_supplement(context_t *ctx, const param_t *params, uint32_t n, const term_t *a, int32_t *error) {
2✔
872
  smt_status_t stat;
873
  ivector_t assumptions;
874
  term_t interpolant;
875
  uint32_t i;
876
  literal_t l;
877
  mcsat_satellite_t *sat;
878

879
  init_ivector(&assumptions, n);
2✔
880

881
  stat = YICES_STATUS_IDLE;
2✔
882
  interpolant = NULL_TERM;
2✔
883
  sat = NULL;
2✔
884

885
  if (ctx->mcsat_supplement_active &&
2✔
886
      ctx->egraph != NULL &&
2✔
887
      ctx->egraph->th[ETYPE_MCSAT] != NULL) {
2✔
888
    sat = (mcsat_satellite_t *) ctx->egraph->th[ETYPE_MCSAT];
2✔
889
  }
890

891
  for (i=0; i<n; i++) {
6✔
892
    l = context_add_assumption(ctx, a[i]);
4✔
893
    if (l < 0) {
4✔
NEW
894
      if (error != NULL) {
×
NEW
895
        *error = l;
×
896
      }
NEW
897
      stat = YICES_STATUS_ERROR;
×
NEW
898
      break;
×
899
    }
900

901
    ivector_push(&assumptions, l);
4✔
902
  }
903

904
  if (stat != YICES_STATUS_ERROR) {
2✔
905
    stat = check_context_with_assumptions(ctx, params, assumptions.size, (const literal_t *) assumptions.data);
2✔
906
    if (stat == YICES_STATUS_UNSAT) {
2✔
907
      if (sat != NULL) {
2✔
908
        interpolant = mcsat_satellite_compute_unsat_model_interpolant(sat, params, n, a);
2✔
909
      }
910
      if (interpolant == NULL_TERM && context_supports_model_interpolation(ctx)) {
2✔
NEW
911
        interpolant = context_get_unsat_model_interpolant(ctx);
×
912
      }
913
      if (interpolant == NULL_TERM && context_supports_model_interpolation(ctx)) {
2✔
NEW
914
        interpolant = false_term;
×
915
      }
916
    }
917
  }
918

919
  if (stat == YICES_STATUS_UNSAT && interpolant != NULL_TERM && sat != NULL) {
2✔
920
    mcsat_satellite_set_unsat_model_interpolant(sat, interpolant);
2✔
921
  }
922

923
  delete_ivector(&assumptions);
2✔
924

925
  return stat;
2✔
926
}
927

928
static smt_status_t check_context_with_term_assumptions_supplement(context_t *ctx, const param_t *params, uint32_t n, const term_t *a, int32_t *error) {
2✔
929
  MT_PROTECT(smt_status_t, __yices_globals.lock, _o_check_context_with_term_assumptions_supplement(ctx, params, n, a, error));
2✔
930
}
931

932
/*
933
 * Check under assumptions given as terms.
934
 * - if MCSAT is enabled, this uses temporary labels + model interpolation.
935
 * - otherwise terms are converted to literals and handled by the CDCL(T) path.
936
 *
937
 * Preconditions:
938
 * - context status must be IDLE.
939
 */
940
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,683✔
941
  if (error != NULL) {
2,683✔
942
    *error = CTX_NO_ERROR;
2,683✔
943
  }
944

945
  context_invalidate_unsat_core_cache(ctx);
2,683✔
946

947
  if (ctx->mcsat == NULL) {
2,683✔
948
    if (ctx->mcsat_supplement_active) {
2,677✔
949
      return check_context_with_term_assumptions_supplement(ctx, params, n, a, error);
2✔
950
    } else {
951
      smt_status_t stat;
952
      ivector_t assumptions;
953
      uint32_t i;
954
      literal_t l;
955

956
      init_ivector(&assumptions, n);
2,675✔
957
      for (i=0; i<n; i++) {
94,334✔
958
        l = context_add_assumption(ctx, a[i]);
91,659✔
959
        if (l < 0) {
91,659✔
NEW
960
          if (error != NULL) {
×
NEW
961
            *error = l;
×
962
          }
NEW
963
          delete_ivector(&assumptions);
×
NEW
964
          return YICES_STATUS_ERROR;
×
965
        }
966
        ivector_push(&assumptions, l);
91,659✔
967
      }
968

969
      stat = check_context_with_assumptions(ctx, params, n, assumptions.data);
2,675✔
970
      delete_ivector(&assumptions);
2,675✔
971
      return stat;
2,675✔
972
    }
973
  }
974

975
  return check_context_with_term_assumptions_mcsat(ctx, params, n, a, error);
6✔
976
}
977

978
/*
979
 * Check with given model
980
 * - if mcsat status is not IDLE, return the status
981
 */
982
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✔
983
  smt_status_t stat;
984

985
  assert(ctx->mcsat != NULL);
986

987
  stat = mcsat_status(ctx->mcsat);
144✔
988
  if (stat == YICES_STATUS_IDLE) {
144✔
989
    mcsat_solve(ctx->mcsat, params, mdl, n, t);
144✔
990
    stat = mcsat_status(ctx->mcsat);
144✔
991

992
    // BD: this looks wrong. We shouldn't call clear yet.
993
    // we want the status to remain STATUS_UNSAT until the next call to check or assert.
994
    //    if (n > 0 && stat == STATUS_UNSAT && context_supports_multichecks(ctx)) {
995
    //      context_clear(ctx);
996
    //    }
997
  }
998

999
  return stat;
144✔
1000
}
1001

1002
/*
1003
 * Check with given model and hints
1004
 * - set the model hint and call check_context_with_model
1005
 */
1006
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✔
1007
  assert(m <= n);
1008

1009
  mcsat_set_model_hint(ctx->mcsat, mdl, n, t);
12✔
1010

1011
  return check_context_with_model(ctx, params, mdl, m, t);
12✔
1012
}
1013

1014

1015
/*
1016
 * Precheck: force generation of clauses and other stuff that's
1017
 * constructed lazily by the solvers. For example, this
1018
 * can be used to convert all the constraints asserted in the
1019
 * bitvector to clauses so that we can export the result to DIMACS.
1020
 *
1021
 * If ctx status is IDLE:
1022
 * - the function calls 'start_search' and does one round of propagation.
1023
 * - if this results in UNSAT, the function returns UNSAT
1024
 * - if the precheck is interrupted, the function returns INTERRUPTED
1025
 * - otherwise the function returns UNKNOWN and sets the status to
1026
 *   UNKNOWN.
1027
 *
1028
 * IMPORTANT: call smt_clear or smt_cleanup to restore the context to
1029
 * IDLE before doing anything else with this context.
1030
 *
1031
 * If ctx status is not IDLE, the function returns it and does nothing
1032
 * else.
1033
 */
1034
smt_status_t precheck_context(context_t *ctx) {
×
1035
  smt_status_t stat;
1036
  smt_core_t *core;
1037

1038
  core = ctx->core;
×
1039

1040
  stat = smt_status(core);
×
1041
  if (stat == YICES_STATUS_IDLE) {
×
1042
    start_search(core, 0, NULL);
×
1043
    smt_process(core);
×
1044
    stat = smt_status(core);
×
1045

1046
    assert(stat == YICES_STATUS_UNSAT || stat == YICES_STATUS_SEARCHING ||
1047
           stat == YICES_STATUS_INTERRUPTED);
1048

1049
    if (stat == YICES_STATUS_SEARCHING) {
×
1050
      end_search_unknown(core);
×
1051
      stat = YICES_STATUS_UNKNOWN;
×
1052
    }
1053
  }
1054

1055
  return stat;
×
1056
}
1057

1058

1059

1060
/*
1061
 * Solve using another SAT solver
1062
 * - sat_solver = name of the solver to use
1063
 * - verbosity = verbosity level (0 means quiet)
1064
 * - this may be used only for BV or pure SAT problems
1065
 * - we perform one round of propagation to convert the problem to CNF
1066
 * - then we call an external SAT solver on the CNF problem
1067
 */
1068
smt_status_t check_with_delegate(context_t *ctx, const char *sat_solver, uint32_t verbosity) {
×
1069
  smt_status_t stat;
1070
  smt_core_t *core;
1071
  delegate_t delegate;
1072
  bvar_t x;
1073
  bval_t v;
1074

1075
  core = ctx->core;
×
1076

1077
  stat = smt_status(core);
×
1078
  if (stat == YICES_STATUS_IDLE) {
×
1079
    start_search(core, 0, NULL);
×
1080
    smt_process(core);
×
1081
    stat = smt_status(core);
×
1082

1083
    assert(stat == YICES_STATUS_UNSAT || stat == YICES_STATUS_SEARCHING ||
1084
           stat == YICES_STATUS_INTERRUPTED);
1085

1086
    if (stat == YICES_STATUS_SEARCHING) {
×
1087
      if (smt_easy_sat(core)) {
×
1088
        stat = YICES_STATUS_SAT;
×
1089
      } else {
1090
        // call the delegate
1091
        init_delegate(&delegate, sat_solver, num_vars(core));
×
1092
        delegate_set_verbosity(&delegate, verbosity);
×
1093

1094
        stat = solve_with_delegate(&delegate, core);
×
1095
        set_smt_status(core, stat);
×
1096
        if (stat == YICES_STATUS_SAT) {
×
1097
          for (x=0; x<num_vars(core); x++) {
×
1098
            v = delegate_get_value(&delegate, x);
×
1099
            set_bvar_value(core, x, v);
×
1100
          }
1101
        }
1102
        delete_delegate(&delegate);
×
1103
      }
1104
    }
1105
  }
1106

1107
  return stat;
×
1108
}
1109

1110

1111
/*
1112
 * Bit-blast then export to DIMACS
1113
 * - filename = name of the output file
1114
 * - status = status of the context after bit-blasting
1115
 *
1116
 * If ctx status is IDLE
1117
 * - perform one round of propagation to conver the problem to CNF
1118
 * - export the CNF to DIMACS
1119
 *
1120
 * If ctx status is not IDLE,
1121
 * - store the stauts in *status and do nothing else
1122
 *
1123
 * Return code:
1124
 *  1 if the DIMACS file was created
1125
 *  0 if the problem was solved by the propagation round
1126
 * -1 if there was an error in creating or writing to the file.
1127
 */
1128
int32_t bitblast_then_export_to_dimacs(context_t *ctx, const char *filename, smt_status_t *status) {
×
1129
  smt_core_t *core;
1130
  FILE *f;
1131
  smt_status_t stat;
1132
  int32_t code;
1133

1134
  core = ctx->core;
×
1135

1136
  code = 0;
×
1137
  stat = smt_status(core);
×
1138
  if (stat == YICES_STATUS_IDLE) {
×
1139
    start_search(core, 0, NULL);
×
1140
    smt_process(core);
×
1141
    stat = smt_status(core);
×
1142

1143
    assert(stat == YICES_STATUS_UNSAT || stat == YICES_STATUS_SEARCHING ||
1144
           stat == YICES_STATUS_INTERRUPTED);
1145

1146
    if (stat == YICES_STATUS_SEARCHING) {
×
1147
      code = 1;
×
1148
      f = fopen(filename, "w");
×
1149
      if (f == NULL) {
×
1150
        code = -1;
×
1151
      } else {
1152
        dimacs_print_bvcontext(f, ctx);
×
1153
        if (ferror(f)) code = -1;
×
1154
        fclose(f);
×
1155
      }
1156
    }
1157
  }
1158

1159
  *status = stat;
×
1160

1161
  return code;
×
1162
}
1163

1164

1165
/*
1166
 * Simplify then export to Dimacs:
1167
 * - filename = name of the output file
1168
 * - status = status of the context after CNF conversion + preprocessing
1169
 *
1170
 * If ctx status is IDLE
1171
 * - perform one round of propagation to convert the problem to CNF
1172
 * - export the CNF to y2sat for extra preprocessing then export that to DIMACS
1173
 *
1174
 * If ctx status is not IDLE, the function stores that in *status
1175
 * If y2sat preprocessing solves the formula, return the status also in *status
1176
 *
1177
 * Return code:
1178
 *  1 if the DIMACS file was created
1179
 *  0 if the problems was solved by preprocessing (or if ctx status is not IDLE)
1180
 * -1 if there was an error creating or writing to the file.
1181
 */
1182
int32_t process_then_export_to_dimacs(context_t *ctx, const char *filename, smt_status_t *status) {
×
1183
  smt_core_t *core;
1184
  FILE *f;
1185
  smt_status_t stat;
1186
  delegate_t delegate;
1187
  bvar_t x;
1188
  bval_t v;
1189
  int32_t code;
1190

1191
  core = ctx->core;
×
1192

1193
  code = 0;
×
1194
  stat = smt_status(core);
×
1195
  if (stat == YICES_STATUS_IDLE) {
×
1196
    start_search(core, 0, NULL);
×
1197
    smt_process(core);
×
1198
    stat = smt_status(core);
×
1199

1200
    assert(stat == YICES_STATUS_UNSAT || stat == YICES_STATUS_SEARCHING ||
1201
           stat == YICES_STATUS_INTERRUPTED);
1202

1203
    if (stat == YICES_STATUS_SEARCHING) {
×
1204
      if (smt_easy_sat(core)) {
×
1205
        stat = YICES_STATUS_SAT;
×
1206
      } else {
1207
        // call the delegate
1208
        init_delegate(&delegate, "y2sat", num_vars(core));
×
1209
        delegate_set_verbosity(&delegate, 0);
×
1210

1211
        stat = preprocess_with_delegate(&delegate, core);
×
1212
        set_smt_status(core, stat);
×
1213
        if (stat == YICES_STATUS_SAT) {
×
1214
          for (x=0; x<num_vars(core); x++) {
×
1215
            v = delegate_get_value(&delegate, x);
×
1216
            set_bvar_value(core, x, v);
×
1217
          }
1218
        } else if (stat == YICES_STATUS_UNKNOWN) {
×
1219
          code = 1;
×
1220
          f = fopen(filename, "w");
×
1221
          if (f == NULL) {
×
1222
            code = -1;
×
1223
          } else {
1224
            export_to_dimacs_with_delegate(&delegate, f);
×
1225
            if (ferror(f)) code = -1;
×
1226
            fclose(f);
×
1227
          }
1228
        }
1229

1230
        delete_delegate(&delegate);
×
1231
      }
1232
    }
1233
  }
1234

1235
  *status = stat;
×
1236

1237
  return code;
×
1238
}
1239

1240

1241

1242
/*
1243
 * MODEL CONSTRUCTION
1244
 */
1245

1246
/*
1247
 * Value of literal l in ctx->core
1248
 */
1249
static value_t bool_value(context_t *ctx, value_table_t *vtbl, literal_t l) {
65,201✔
1250
  value_t v;
1251

1252
  v = null_value; // prevent GCC warning
65,201✔
1253
  switch (literal_value(ctx->core, l)) {
65,201✔
1254
  case VAL_FALSE:
38,577✔
1255
    v = vtbl_mk_false(vtbl);
38,577✔
1256
    break;
38,577✔
1257
  case VAL_UNDEF_FALSE:
×
1258
  case VAL_UNDEF_TRUE:
1259
    v = vtbl_mk_unknown(vtbl);
×
1260
    break;
×
1261
  case VAL_TRUE:
26,624✔
1262
    v = vtbl_mk_true(vtbl);
26,624✔
1263
    break;
26,624✔
1264
  }
1265
  return v;
65,201✔
1266
}
1267

1268

1269
/*
1270
 * Value of arithmetic variable x in ctx->arith_solver
1271
 */
1272
static value_t arith_value(context_t *ctx, value_table_t *vtbl, thvar_t x) {
22,590✔
1273
  rational_t *a;
1274
  value_t v;
1275

1276
  assert(context_has_arith_solver(ctx));
1277

1278
  a = &ctx->aux;
22,590✔
1279
  if (ctx->arith.value_in_model(ctx->arith_solver, x, a)) {
22,590✔
1280
    v = vtbl_mk_rational(vtbl, a);
22,590✔
1281
  } else {
1282
    v = vtbl_mk_unknown(vtbl);
×
1283
  }
1284

1285
  return v;
22,590✔
1286
}
1287

1288

1289

1290
/*
1291
 * Value of bitvector variable x in ctx->bv_solver
1292
 */
1293
static value_t bv_value(context_t *ctx, value_table_t *vtbl, thvar_t x) {
18,133✔
1294
  bvconstant_t *b;
1295
  value_t v;
1296

1297
  assert(context_has_bv_solver(ctx));
1298

1299
  b = &ctx->bv_buffer;
18,133✔
1300
  if (ctx->bv.value_in_model(ctx->bv_solver, x, b)) {
18,133✔
1301
    v = vtbl_mk_bv_from_constant(vtbl, b);
18,133✔
1302
  } else {
1303
    v = vtbl_mk_unknown(vtbl);
×
1304
  }
1305

1306
  return v;
18,133✔
1307
}
1308

1309

1310
/*
1311
 * Get a value for term t in the solvers or egraph
1312
 * - attach the mapping from t to that value in model
1313
 * - if we don't have a concrete object for t but t is
1314
 *   mapped to a term u and the model->has_alias is true,
1315
 *   then we store the mapping [t --> u] in the model's
1316
 *   alias map.
1317
 */
1318
static void build_term_value(context_t *ctx, model_t *model, term_t t) {
319,834✔
1319
  value_table_t *vtbl;
1320
  term_t r;
1321
  uint32_t polarity;
1322
  int32_t x;
1323
  type_t tau;
1324
  value_t v;
1325

1326
  /*
1327
   * Get the root of t in the substitution table
1328
   */
1329
  r = intern_tbl_get_root(&ctx->intern, t);
319,834✔
1330
  if (intern_tbl_root_is_mapped(&ctx->intern, r)) {
319,834✔
1331
    /*
1332
     * r is mapped to some object x in egraph/core/or theory solvers
1333
     * - keep track of polarity then force r to positive polarity
1334
     */
1335
    vtbl = model_get_vtbl(model);
110,696✔
1336
    polarity = polarity_of(r);
110,696✔
1337
    r = unsigned_term(r);
110,696✔
1338

1339
    /*
1340
     * Convert x to a concrete value
1341
     */
1342
    x = intern_tbl_map_of_root(&ctx->intern, r);
110,696✔
1343
    if (code_is_eterm(x)) {
110,696✔
1344
      // x refers to an egraph object or true_occ/false_occ
1345
      if (x == bool2code(true)) {
4,772✔
1346
        v = vtbl_mk_true(vtbl);
475✔
1347
      } else if (x == bool2code(false)) {
4,297✔
1348
        v = vtbl_mk_false(vtbl);
1,420✔
1349
      } else {
1350
        assert(context_has_egraph(ctx));
1351
        v = egraph_get_value(ctx->egraph, vtbl, code2occ(x));
2,877✔
1352
      }
1353

1354
    } else {
1355
      // x refers to a literal or a theory variable
1356
      tau = term_type(ctx->terms, r);
105,924✔
1357
      switch (type_kind(ctx->types, tau)) {
105,924✔
1358
      case BOOL_TYPE:
65,201✔
1359
        v = bool_value(ctx, vtbl, code2literal(x));
65,201✔
1360
        break;
65,201✔
1361

1362
      case INT_TYPE:
22,590✔
1363
      case REAL_TYPE:
1364
        v = arith_value(ctx, vtbl, code2thvar(x));
22,590✔
1365
        break;
22,590✔
1366

1367
      case BITVECTOR_TYPE:
18,133✔
1368
        v = bv_value(ctx, vtbl, code2thvar(x));
18,133✔
1369
        break;
18,133✔
1370

1371
      default:
×
1372
        /*
1373
         * This should never happen:
1374
         * scalar, uninterpreted, tuple, function terms
1375
         * are mapped to egraph terms.
1376
         */
1377
        assert(false);
1378
        v = vtbl_mk_unknown(vtbl); // prevent GCC warning
×
1379
        break;
×
1380
      }
1381
    }
1382

1383
    /*
1384
     * Restore polarity then add mapping [t --> v] in the model
1385
     */
1386
    if (! object_is_unknown(vtbl, v)) {
110,696✔
1387
      if (object_is_boolean(vtbl, v)) {
110,696✔
1388
        if (polarity) {
67,096✔
1389
          // negate the value
1390
          v = vtbl_mk_not(vtbl, v);
24✔
1391
        }
1392
      }
1393
      model_map_term(model, t, v);
110,696✔
1394
    }
1395

1396
  } else {
1397
    /*
1398
     * r is not mapped to anything:
1399
     *
1400
     * 1) If t == r and t is present in the internalization table
1401
     *    then t is relevant. So we should display its value
1402
     *    when we print the model. To do this, we assign an
1403
     *    arbitrary value v to t and store [t := v] in the map.
1404
     *
1405
     * 2) If t != r then we keep the mapping [t --> r] in
1406
     *    the alias table (provided the model supports aliases).
1407
     */
1408
    if (t == r) {
209,138✔
1409
      if (intern_tbl_term_present(&ctx->intern, t)) {
208,488✔
1410
        tau = term_type(ctx->terms, t);
×
1411
        vtbl = model_get_vtbl(model);
×
1412
        v = vtbl_make_object(vtbl, tau);
×
1413
        model_map_term(model, t, v);
×
1414
      }
1415
    } else if (model->has_alias) {
650✔
1416
      // t != r: keep the substitution [t --> r] in the model
1417
      model_add_substitution(model, t, r);
650✔
1418
    }
1419
  }
1420
}
319,834✔
1421

1422

1423

1424

1425
/*
1426
 * Build a model for the current context (including all satellite solvers)
1427
 * - the context status must be SAT (or UNKNOWN)
1428
 * - if model->has_alias is true, we store the term substitution
1429
 *   defined by ctx->intern_tbl into the model
1430
 * - cleanup of satellite models needed using clean_solver_models()
1431
 */
1432
void build_model(model_t *model, context_t *ctx) {
42,125✔
1433
  term_table_t *terms;
1434
  uint32_t i, n;
1435
  term_t t;
1436

1437
  assert(smt_status(ctx->core) == YICES_STATUS_SAT ||
1438
         smt_status(ctx->core) == YICES_STATUS_UNKNOWN ||
1439
         (context_has_mcsat(ctx) && mcsat_status(ctx->mcsat) == YICES_STATUS_SAT));
1440

1441
  /*
1442
   * First build assignments in the satellite solvers
1443
   * and get the val_in_model functions for the egraph
1444
   */
1445
  if (context_has_arith_solver(ctx)) {
42,125✔
1446
    ctx->arith.build_model(ctx->arith_solver);
32,910✔
1447
  }
1448
  if (context_has_bv_solver(ctx)) {
42,125✔
1449
    ctx->bv.build_model(ctx->bv_solver);
21,401✔
1450
  }
1451

1452
  /*
1453
   * Construct the egraph model
1454
   */
1455
  if (context_has_egraph(ctx)) {
42,125✔
1456
    egraph_build_model(ctx->egraph, model_get_vtbl(model));
13,036✔
1457
  }
1458

1459
  /*
1460
   * Construct the mcsat model.
1461
   */
1462
  if (context_has_mcsat(ctx)) {
42,125✔
1463
    mcsat_build_model(ctx->mcsat, model);
30✔
1464
  }
1465

1466
  // scan the internalization table
1467
  terms = ctx->terms;
42,125✔
1468
  n = intern_tbl_num_terms(&ctx->intern);
42,125✔
1469
  for (i=1; i<n; i++) { // first real term has index 1 (i.e. true_term)
1,235,095,915✔
1470
    if (good_term_idx(terms, i)) {
1,235,053,790✔
1471
      t = pos_occ(i);
1,235,053,790✔
1472
      if (term_kind(terms, t) == UNINTERPRETED_TERM) {
1,235,053,790✔
1473
        build_term_value(ctx, model, t);
319,834✔
1474
      }
1475
    }
1476
  }
1477

1478
  /*
1479
   * Supplemental MCSAT values are an overlay: apply them after the regular
1480
   * CDCL(T) model is fully built so nonlinear/FF values are not overwritten.
1481
   */
1482
  if (ctx->mcsat_supplement_active && context_has_egraph(ctx) && ctx->egraph->th[ETYPE_MCSAT] != NULL) {
42,125✔
1483
    mcsat_satellite_build_model((mcsat_satellite_t *) ctx->egraph->th[ETYPE_MCSAT], model);
3✔
1484
  }
1485
}
42,125✔
1486

1487

1488
/*
1489
 * Cleanup solver models
1490
 */
1491
void clean_solver_models(context_t *ctx) {
42,125✔
1492
  if (context_has_arith_solver(ctx)) {
42,125✔
1493
    ctx->arith.free_model(ctx->arith_solver);
32,910✔
1494
  }
1495
  if (context_has_bv_solver(ctx)) {
42,125✔
1496
    ctx->bv.free_model(ctx->bv_solver);
21,401✔
1497
  }
1498
  if (context_has_egraph(ctx)) {
42,125✔
1499
    egraph_free_model(ctx->egraph);
13,036✔
1500
  }
1501
}
42,125✔
1502

1503

1504

1505
/*
1506
 * Build a model for the current context
1507
 * - the context status must be SAT (or UNKNOWN)
1508
 * - if model->has_alias is true, we store the term substitution
1509
 *   defined by ctx->intern_tbl into the model
1510
 */
1511
void context_build_model(model_t *model, context_t *ctx) {
224✔
1512
  // Build solver models and term values
1513
  build_model(model, ctx);
224✔
1514

1515
  // Cleanup
1516
  clean_solver_models(ctx);
224✔
1517
}
224✔
1518

1519

1520

1521
/*
1522
 * Read the value of a Boolean term t
1523
 * - return VAL_TRUE/VAL_FALSE or VAL_UNDEF_FALSE/VAL_UNDEF_TRUE if t's value is not known
1524
 */
1525
bval_t context_bool_term_value(context_t *ctx, term_t t) {
×
1526
  term_t r;
1527
  uint32_t polarity;
1528
  int32_t x;
1529
  bval_t v;
1530

1531
  assert(is_boolean_term(ctx->terms, t));
1532

1533
  // default value if t is not in the internalization table
1534
  v = VAL_UNDEF_FALSE;
×
1535
  r = intern_tbl_get_root(&ctx->intern, t);
×
1536
  if (intern_tbl_root_is_mapped(&ctx->intern, r)) {
×
1537
    // r is mapped to some object x
1538
    polarity = polarity_of(r);
×
1539
    r = unsigned_term(r);
×
1540

1541
    x  = intern_tbl_map_of_root(&ctx->intern, r);
×
1542
    if (code_is_eterm(x)) {
×
1543
      // x must be either true_occ or false_occ
1544
      if (x == bool2code(true)) {
×
1545
        v = VAL_TRUE;
×
1546
      } else {
1547
        assert(x == bool2code(false));
1548
        v = VAL_FALSE;
×
1549
      }
1550
    } else {
1551
      // x refers to a literal in the smt core
1552
      v = literal_value(ctx->core, code2literal(x));
×
1553
    }
1554

1555
    // negate v if polarity is 1 (cf. smt_core_base_types.h)
1556
    v ^= polarity;
×
1557
  }
1558

1559
  return v;
×
1560
}
1561

1562

1563
/*
1564
 * UNSAT CORE
1565
 */
1566

1567
/*
1568
 * Build an unsat core:
1569
 * - store the result in v
1570
 * - first reuse a cached term core if available.
1571
 * - otherwise:
1572
 *   CDCL(T): build from smt_core then cache as terms
1573
 *   MCSAT: return empty unless check-with-term-assumptions populated the cache.
1574
 */
1575
void context_build_unsat_core(context_t *ctx, ivector_t *v) {
2,739✔
1576
  smt_core_t *core;
1577
  uint32_t i, n;
1578
  term_t t;
1579

1580
  if (ctx->unsat_core_cache != NULL) {
2,739✔
1581
    // Fast path: repeated get_unsat_core returns the cached term vector.
1582
    ivector_reset(v);
5✔
1583
    ivector_copy(v, ctx->unsat_core_cache->data, ctx->unsat_core_cache->size);
5✔
1584
    return;
5✔
1585
  }
1586

1587
  if (ctx->mcsat != NULL) {
2,734✔
1588
    // MCSAT core extraction is done in check-with-term-assumptions; without cache
1589
    // there is no generic context-level core structure to rebuild from here.
1590
    ivector_reset(v);
×
1591
    return;
×
1592
  }
1593

1594
  core = ctx->core;
2,734✔
1595
  assert(core != NULL && core->status == YICES_STATUS_UNSAT);
1596
  build_unsat_core(core, v);
2,734✔
1597

1598
  // convert from literals to terms
1599
  n = v->size;
2,734✔
1600
  for (i=0; i<n; i++) {
37,667✔
1601
    t = assumption_term_for_literal(&ctx->assumptions, v->data[i]);
34,933✔
1602
    assert(t >= 0);
1603
    v->data[i] = t;
34,933✔
1604
  }
1605

1606
  // Cache the converted term core for subsequent queries.
1607
  cache_unsat_core(ctx, v);
2,734✔
1608
}
1609

1610

1611
/*
1612
 * MODEL INTERPOLANT
1613
 */
1614
term_t context_get_unsat_model_interpolant(context_t *ctx) {
51✔
1615
  if (ctx->mcsat != NULL) {
51✔
1616
    return mcsat_get_unsat_model_interpolant(ctx->mcsat);
51✔
1617
  }
1618

NEW
1619
  if (ctx->mcsat_supplement_active && ctx->egraph != NULL && ctx->egraph->th[ETYPE_MCSAT] != NULL) {
×
NEW
1620
    return mcsat_satellite_get_unsat_model_interpolant((mcsat_satellite_t *) ctx->egraph->th[ETYPE_MCSAT]);
×
1621
  }
1622

NEW
1623
  if (context_supports_model_interpolation(ctx) &&
×
NEW
1624
      ctx->core != NULL &&
×
NEW
1625
      smt_status(ctx->core) == YICES_STATUS_UNSAT) {
×
NEW
1626
    return false_term;
×
1627
  }
1628

NEW
1629
  return NULL_TERM;
×
1630
}
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