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

SRI-CSL / yices2 / 27070969443

06 Jun 2026 06:55PM UTC coverage: 69.017% (+0.01%) from 69.005%
27070969443

Pull #639

github

web-flow
Merge bdd2a844e into e9b6610a2
Pull Request #639: New Mcsat reduce

34 of 38 new or added lines in 4 files covered. (89.47%)

1 existing line in 1 file now uncovered.

88156 of 127731 relevant lines covered (69.02%)

1879341.47 hits per line

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

91.56
/src/mcsat/bool/bool_plugin.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
#include "mcsat/bool/bool_plugin.h"
20

21
#include "mcsat/bool/clause_db.h"
22
#include "mcsat/bool/cnf.h"
23
#include "mcsat/bool/bcp_watch_manager.h"
24

25
#include "mcsat/tracing.h"
26

27
#include "utils/int_array_sort2.h"
28
#include "mcsat/utils/scope_holder.h"
29

30
#include <math.h>
31

32
typedef struct {
33

34
  /** The plugin interface */
35
  plugin_t plugin_interface;
36

37
  /** The plugin context */
38
  plugin_context_t* ctx;
39

40
  /** The clause database */
41
  clause_db_t clause_db;
42

43
  /** The CNF converter */
44
  cnf_t cnf;
45

46
  /** Clauses that have been added and need to be processed */
47
  ivector_t clauses_to_add;
48

49
  /** Clauses, these we keep forever */
50
  ivector_t clauses;
51

52
  /** Lemmas, so that we can potentially remove them */
53
  ivector_t lemmas;
54

55
  /** Cumulative number of conflicts (drives the conflict-based reduce trigger) */
56
  uint64_t conflicts;
57

58
  /** Next conflict count at which to reduce the lemma database */
59
  uint64_t reduce_threshold;
60

61
  /**
62
   * Whether the next GC was requested by this plugin's reduce schedule.
63
   * The 'used' protection counters decay only on such GCs, not on GCs
64
   * triggered for other reasons (e.g. the GC on every pop).
65
   */
66
  bool reduce_requested;
67

68
  /** Clauses to re-check for propagations. */
69
  ivector_t clauses_to_repropagate;
70

71
  /** The watch manager for BCP */
72
  bcp_watch_manager_t wlm;
73

74
  /** Map from variables to clauses that we used to propagate the value */
75
  ivector_t reason;
76

77
  /** List of propagated variables */
78
  ivector_t propagated;
79

80
  /** Size of the propagated vector */
81
  uint32_t propagated_size;
82

83
  /** Next index of the trail to process */
84
  uint32_t trail_i;
85

86
  /** The clause of the latest conflict */
87
  clause_ref_t conflict;
88

89
  /** Scope holder for the int variables */
90
  scope_holder_t scope;
91

92
  /** GC info for clause removal */
93
  gc_info_t gc_clauses;
94

95
  struct {
96

97
    /** Score increase per bump (multiplicative) */
98
    float clause_score_bump_factor;
99
    /** Decay all scores */
100
    float clause_score_decay_factor;
101
    /** Limit for when to scale down */
102
    float clause_score_limit;
103

104
    /** Initial conflict count at which to reduce the lemma database */
105
    uint32_t reduce_init_threshold;
106
    /** Reduce interval multiplier (next threshold = conflicts + this * sqrt(conflicts)) */
107
    uint32_t reduce_interval;
108

109
    /** bump factor for bool vars -- geq 1. Higher number means more weightage **/
110
    uint32_t bool_var_bump_factor;
111

112
  } heuristic_params;
113

114
  struct {
115
    statistic_int_t* propagations;
116
    statistic_int_t* conflicts;
117
    statistic_int_t* clauses_attached;
118
    statistic_int_t* clauses_attached_binary;
119
  } stats;
120

121
  /** Exception handler */
122
  jmp_buf* exception;
123

124
} bool_plugin_t;
125

126
static
127
void bool_plugin_stats_init(bool_plugin_t* bp) {
740✔
128
  bp->stats.propagations = statistics_new_int(bp->ctx->stats, "mcsat::bool::propagations");
740✔
129
  bp->stats.conflicts = statistics_new_int(bp->ctx->stats, "mcsat::bool::conflicts");
740✔
130
  bp->stats.clauses_attached = statistics_new_int(bp->ctx->stats, "mcsat::bool::clauses_attached");
740✔
131
  bp->stats.clauses_attached_binary = statistics_new_int(bp->ctx->stats, "mcsat::bool::clauses_attached_binary");
740✔
132
}
740✔
133

134
static
135
void bool_plugin_heuristics_init(bool_plugin_t* bp) {
740✔
136
  // Clause scoring
137
  bp->heuristic_params.clause_score_bump_factor = 1;
740✔
138
  bp->heuristic_params.clause_score_decay_factor = 0.999;
740✔
139
  bp->heuristic_params.clause_score_limit = 1e20;
740✔
140

141
  // Clause database reduction (conflict-based schedule, cf. smt_core)
142
  bp->heuristic_params.reduce_init_threshold = 300;
740✔
143
  bp->heuristic_params.reduce_interval = 25;
740✔
144

145
  // Bool var scoring
146
  bp->heuristic_params.bool_var_bump_factor = 20;
740✔
147
}
740✔
148

149
static
150
void bool_plugin_construct(plugin_t* plugin, plugin_context_t* ctx) {
740✔
151
  bool_plugin_t* bp = (bool_plugin_t*) plugin;
740✔
152

153
  bp->ctx = ctx;
740✔
154
  clause_db_construct(&bp->clause_db, ctx->var_db);
740✔
155
  cnf_construct(&bp->cnf, ctx, &bp->clause_db);
740✔
156
  init_ivector(&bp->clauses_to_add, 0);
740✔
157
  init_ivector(&bp->clauses, 0);
740✔
158
  init_ivector(&bp->lemmas, 0);
740✔
159
  init_ivector(&bp->clauses_to_repropagate, 0);
740✔
160
  bcp_watch_manager_construct(&bp->wlm);
740✔
161
  init_ivector(&bp->reason, 0);
740✔
162
  init_ivector(&bp->propagated, 0);
740✔
163

164
  bp->trail_i = 0;
740✔
165
  bp->propagated_size = 0;
740✔
166

167
  ctx->request_term_notification_by_kind(ctx, OR_TERM, false);
740✔
168
  ctx->request_term_notification_by_kind(ctx, XOR_TERM, false);
740✔
169
  ctx->request_term_notification_by_kind(ctx, EQ_TERM, false);
740✔
170
  ctx->request_term_notification_by_kind(ctx, ITE_TERM, false);
740✔
171
  ctx->request_term_notification_by_kind(ctx, ITE_SPECIAL, false);
740✔
172

173
  ctx->request_term_notification_by_type(ctx, BOOL_TYPE);
740✔
174

175
  ctx->request_decision_calls(ctx, BOOL_TYPE);
740✔
176

177
  scope_holder_construct(&bp->scope);
740✔
178
  // CONSTRUCTED ON DEMAND: gc_info_construct(&bp->gc_clauses, clause_ref_null);
179

180
  bool_plugin_stats_init(bp);
740✔
181
  bool_plugin_heuristics_init(bp);
740✔
182

183
  // Conflict-based reduce schedule. Conflicts are counted cumulatively here so
184
  // bp->conflicts matches the core solver's conflict count (mcsat::conflicts);
185
  // only the schedule (threshold) is reset per search, not the count.
186
  bp->conflicts = 0;
740✔
187
  bp->reduce_threshold = bp->heuristic_params.reduce_init_threshold;
740✔
188
  bp->reduce_requested = false;
740✔
189
}
740✔
190

191
static
192
void bool_plugin_destruct(plugin_t* plugin) {
740✔
193
  bool_plugin_t* bp = (bool_plugin_t*) plugin;
740✔
194
  clause_db_destruct(&bp->clause_db);
740✔
195
  cnf_destruct(&bp->cnf);
740✔
196
  delete_ivector(&bp->clauses_to_add);
740✔
197
  delete_ivector(&bp->clauses);
740✔
198
  delete_ivector(&bp->lemmas);
740✔
199
  bcp_watch_manager_destruct(&bp->wlm);
740✔
200
  delete_ivector(&bp->clauses_to_repropagate); // BD: fixed memory leak
740✔
201
  delete_ivector(&bp->reason);
740✔
202
  delete_ivector(&bp->propagated);
740✔
203
  scope_holder_destruct(&bp->scope);
740✔
204
  // DESTRUCTED ON DEMAND: gc_info_destruct(&bp->gc_clauses);
205
}
740✔
206

207
static
208
void bool_plugin_new_term_notify(plugin_t* plugin, term_t term, trail_token_t* prop) {
164,275✔
209
  bool_plugin_t* bp = (bool_plugin_t*) plugin;
164,275✔
210

211
  if (ctx_trace_enabled(bp->ctx, "mcsat::new_term")) {
164,275✔
212
    ctx_trace_printf(bp->ctx, "bool_plugin_new_term_notify: ");
×
213
    ctx_trace_term(bp->ctx, term);
×
214
  }
215

216
  // Ignore non-Boolean terms
217
  if (term_type_kind(bp->ctx->terms, term) != BOOL_TYPE) {
164,275✔
218
    assert(is_ite_term(bp->ctx->terms, term));
219
    return;
12,803✔
220
  }
221

222
  // Convert to CNF
223
  cnf_convert(&bp->cnf, term, &bp->clauses_to_add);
151,472✔
224

225
  // Variable to the watch list manager
226
  assert(variable_db_has_variable(bp->ctx->var_db, term));
227
  variable_t term_var = variable_db_get_variable(bp->ctx->var_db, term);
151,472✔
228
  bcp_watch_manager_new_variable_notify(&bp->wlm, term_var);
151,472✔
229

230
  // If constant true, then propagate it's true
231
  if (term == true_term) {
151,472✔
232
    prop->add_at_level(prop, term_var, &mcsat_value_true, bp->ctx->trail->decision_level_base);
269✔
233
  }
234

235
}
236

237
static
238
void bool_plugin_new_lemma_notify(plugin_t* plugin, ivector_t* lemma, trail_token_t* prop) {
85,832✔
239
  bool_plugin_t* bp = (bool_plugin_t*) plugin;
85,832✔
240

241
  uint32_t i;
242
  clause_ref_t clause_ref;
243

244
  // Convert to CNF
245
  i = bp->clauses_to_add.size;
85,832✔
246
  cnf_convert_lemma(&bp->cnf, lemma, &bp->clauses_to_add);
85,832✔
247

248
  // Remember the lemma clauses
249
  for (; i < bp->clauses_to_add.size; ++ i) {
171,664✔
250
    clause_ref = bp->clauses_to_add.data[i];
85,832✔
251
    assert(clause_db_is_clause(&bp->clause_db, clause_ref, true));
252
    ivector_push(&bp->lemmas, clause_ref);
85,832✔
253
  }
254
}
85,832✔
255

256
/** Comparison based on trail */
257
static
258
bool bool_plugin_trail_literal_compare(void *data, mcsat_literal_t l1, mcsat_literal_t l2) {
4,366,915✔
259
  const mcsat_trail_t* trail;
260
  bool l1_has_value, l2_has_value;
261
  uint32_t l1_level, l2_level;
262
  bool l1_value, l2_value;
263

264
  trail = data;
4,366,915✔
265

266
  //
267
  // We compare based literals so that true < undef < false, while sorting
268
  // literals with the same value based on the trail level
269

270
  // Literals with no value
271
  l1_has_value = literal_has_value(l1, trail);
4,366,915✔
272
  l2_has_value = literal_has_value(l2, trail);
4,366,915✔
273
  if (!l1_has_value && !l2_has_value) {
4,366,915✔
274
    // Both have no value, just order by variable
275
    return literal_get_variable(l1) < literal_get_variable(l2);
881,488✔
276
  }
277

278
  // At least one has a value
279
  if (!l1_has_value) {
3,485,427✔
280
    l2_value = literal_get_value(l2, trail);
248,417✔
281
    if (l2_value) {
248,417✔
282
      return false;
307✔
283
    } else {
284
      return true;
248,110✔
285
    }
286
  }
287
  if (!l2_has_value) {
3,237,010✔
288
    l1_value = literal_get_value(l1, trail);
316,441✔
289
    if (l1_value) {
316,441✔
290
      return true;
97,016✔
291
    } else {
292
      return false;
219,425✔
293
    }
294
  }
295

296
  // Both literals have a value
297

298
  // True literals go up front
299
  l1_value = literal_get_value(l1, trail);
2,920,569✔
300
  l2_value = literal_get_value(l2, trail);
2,920,569✔
301
  if (l1_value && !l2_value) {
2,920,569✔
302
    return true;
1,011✔
303
  }
304
  if (l2_value && !l1_value) {
2,919,558✔
305
    return false;
46✔
306
  }
307

308
  // Same value, sort by decreasing level for false literals and by
309
  // increasing level for true literals
310
  l1_level = literal_get_level(l1, trail);
2,919,512✔
311
  l2_level = literal_get_level(l2, trail);
2,919,512✔
312
  if (l1_level != l2_level) {
2,919,512✔
313
    if (l1_value) {
1,749,891✔
314
      return l1_level < l2_level;
×
315
    } else {
316
      return l1_level > l2_level;
1,749,891✔
317
    }
318
  } else {
319
    return literal_get_variable(l1) < literal_get_variable(l2);
1,169,621✔
320
  }
321
}
322

323
/**
324
 * Add a new clause, normalize and attach to any internal structures. Returns
325
 * -1 if the clause does not propagate, otherwise returns the level at which
326
 * the clause propagates.
327
 */
328
static
329
int bool_plugin_attach_clause(bool_plugin_t* bp, clause_ref_t c_ref, trail_token_t* prop) {
344,171✔
330
  int i, propagation_level;
331
  mcsat_clause_t* c;
332

333
  // Get the clause
334
  c = clause_db_get_clause(&bp->clause_db, c_ref);
344,171✔
335

336
  if (ctx_trace_enabled(bp->ctx, "mcsat::bool::attach")) {
344,171✔
337
    ctx_trace_printf(bp->ctx, "bool_plugin_attach_clause: ");
×
338
    clause_db_print_clause(&bp->clause_db, c_ref, ctx_trace_out(bp->ctx));
×
339
    ctx_trace_printf(bp->ctx, "\n");
×
340
  }
341

342
  // Sort the clause based on trail
343
  int_array_sort2(c->literals, c->size, (void*) bp->ctx->trail, bool_plugin_trail_literal_compare);
344,171✔
344

345
  if (ctx_trace_enabled(bp->ctx, "mcsat::bool::attach")) {
344,171✔
346
    ctx_trace_printf(bp->ctx, "sorted: ");
×
347
    clause_db_print_clause(&bp->clause_db, c_ref, ctx_trace_out(bp->ctx));
×
348
    ctx_trace_printf(bp->ctx, "\n");
×
349
  }
350

351
  // Reduce the size of the clause by removing base level false literals.
352
  // These literals are at the end (see trail_compare in the sort)
353
  i = c->size - 1;
344,171✔
354
  while (i >= 0) {
495,916✔
355
    if (literal_has_value_at_base(c->literals[i], bp->ctx->trail) && literal_is_false(c->literals[i], bp->ctx->trail)) {
495,915✔
356
      c->size --;
151,745✔
357
      i --;
151,745✔
358
    } else {
359
      break;
360
    }
361
  }
362

363
  if (c->size == 0) {
344,171✔
364
    // Empty clause derived, we have a conflict at base level
365
    prop->conflict(prop);
1✔
366
    return -1;
1✔
367
  }
368

369
  // If the first literal at base, it must be true at base making the clause
370
  // irellevant
371
  if (literal_has_value_at_base(c->literals[0], bp->ctx->trail)) {
344,170✔
372
    assert(literal_is_true(c->literals[0], bp->ctx->trail));
373
    return -1;
93,421✔
374
  }
375

376
  // If it propagates, add it to the delayed propagation list (even empty clauses)
377
  if (c->size == 1) {
250,749✔
378
    propagation_level = bp->ctx->trail->decision_level_base;
21,031✔
379
  } else if (literal_is_false(c->literals[1], bp->ctx->trail)) {
229,718✔
380
    propagation_level = trail_get_level(bp->ctx->trail, literal_get_variable(c->literals[1]));
62,231✔
381
  } else {
382
    propagation_level = -1;
167,487✔
383
  }
384

385
  // Attach the two first literals
386
  // ~c[0], ~c[1], i.e. when c[0] or c[1] become false we do something
387
  if (c->size == 2) {
250,749✔
388
    bcp_watch_manager_add_to_watch(&bp->wlm, literal_negate(c->literals[0]), c_ref, true, c->literals[1]);
119,139✔
389
    bcp_watch_manager_add_to_watch(&bp->wlm, literal_negate(c->literals[1]), c_ref, true, c->literals[0]);
119,139✔
390
    (*bp->stats.clauses_attached_binary) ++;
119,139✔
391
  } else if (c->size > 2) {
131,610✔
392
    bcp_watch_manager_add_to_watch(&bp->wlm, literal_negate(c->literals[0]), c_ref, false, c->literals[1]);
110,579✔
393
    bcp_watch_manager_add_to_watch(&bp->wlm, literal_negate(c->literals[1]), c_ref, false, c->literals[0]);
110,579✔
394
    (*bp->stats.clauses_attached) ++;
110,579✔
395
  }
396

397
  if (ctx_trace_enabled(bp->ctx, "mcsat::bool::attach")) {
250,749✔
398
    ctx_trace_printf(bp->ctx, "propagates at level %d\n", propagation_level);
×
399
  }
400

401
  // Return the level at which the clause propagates
402
  return propagation_level;
250,749✔
403
}
404

405
static
406
void bool_plugin_decay_clause_scores(bool_plugin_t* bp) {
86,036✔
407
  bp->heuristic_params.clause_score_bump_factor *= (1 / bp->heuristic_params.clause_score_decay_factor);
86,036✔
408
}
86,036✔
409

410
static
411
void bool_plugin_rescale_clause_scores(bool_plugin_t* bp) {
×
412
  uint32_t i;
413
  clause_ref_t clause;
414
  mcsat_clause_tag_t* tag;
415

416
  for (i = 0; i < bp->lemmas.size; ++ i) {
×
417
    clause = bp->lemmas.data[i];
×
418
    tag = clause_db_get_tag(&bp->clause_db, clause);
×
419
    assert(tag->type == CLAUSE_LEMMA);
420
    tag->score /= bp->heuristic_params.clause_score_limit;
×
421
  }
422

423
  bp->heuristic_params.clause_score_bump_factor /= bp->heuristic_params.clause_score_limit;
×
424
}
×
425

426
static
427
void bool_plugin_bump_clause(bool_plugin_t* bp, const mcsat_clause_t* clause) {
682,997✔
428
  mcsat_clause_tag_t* tag;
429

430
  tag = clause_get_tag(clause);
682,997✔
431
  if (tag->type == CLAUSE_LEMMA) {
682,997✔
432
    // Bump
433
    tag->score += bp->heuristic_params.clause_score_bump_factor;
169,662✔
434
    // Used in conflict analysis: protect from the next GC round(s)
435
    tag->used = clause_used_init(clause->size);
169,662✔
436
    // If over the limit, normalize
437
    if (tag->score > bp->heuristic_params.clause_score_limit) {
169,662✔
438
      bool_plugin_rescale_clause_scores(bp);
×
439
    }
440
  }
441
}
682,997✔
442

443
static inline
444
void bool_plugin_report_conflict(bool_plugin_t* bp, trail_token_t* prop, clause_ref_t c) {
60,794✔
445
  mcsat_tagged_clause_t* clause;
446

447
  // Bump the conflict clause
448
  clause = clause_db_get_tagged_clause(&bp->clause_db, c);
60,794✔
449
  if (clause->tag.type == CLAUSE_LEMMA) {
60,794✔
450
    bool_plugin_bump_clause(bp, &clause->clause);
26,857✔
451
  }
452

453
  prop->conflict(prop);
60,794✔
454
  bp->conflict = c;
60,794✔
455

456
  (*bp->stats.conflicts) ++;
60,794✔
457
}
60,794✔
458

459
static inline
460
void bool_plugin_set_reason_ref(bool_plugin_t* bp, variable_t x, clause_ref_t reason_ref) {
5,655,851✔
461
  assert(x < bp->reason.size);
462
  assert(variable_db_is_variable(bp->ctx->var_db, x, true));
463
  assert(reason_ref == clause_ref_null || clause_db_is_clause(&bp->clause_db, reason_ref, true));
464

465
  bp->reason.data[x] = reason_ref;
5,655,851✔
466
}
5,655,851✔
467

468
/**
469
 * Propagate the literal and remember any needed information.
470
 */
471
static inline
472
void bool_plugin_propagate_literal(bool_plugin_t* bp, mcsat_literal_t l, trail_token_t* prop, clause_ref_t cref) {
2,823,849✔
473
  variable_t x;
474

475
  assert(cref != clause_ref_null);
476
  assert(!literal_has_value(l, bp->ctx->trail));
477

478
  (*bp->stats.propagations) ++;
2,823,849✔
479

480
  literal_set_value(l, prop);
2,823,849✔
481
  ivector_push(&bp->propagated, literal_get_variable(l));
2,823,849✔
482

483
  x = literal_get_variable(l);
2,823,849✔
484
  while (x >= bp->reason.size) {
2,983,889✔
485
    ivector_push(&bp->reason, clause_ref_null);
160,040✔
486
  }
487
  assert(bp->reason.data[x] == clause_ref_null);
488
  bool_plugin_set_reason_ref(bp, x, cref);
2,823,849✔
489
}
2,823,849✔
490

491
/**
492
 * Get the reason of why x is set to this value.
493
 */
494
static inline
495
clause_ref_t bool_plugin_get_reason_ref(bool_plugin_t* bp, variable_t x) {
713,021✔
496
  clause_ref_t reason_ref;
497

498
  assert(x < bp->reason.size);
499
  assert(variable_db_is_variable(bp->ctx->var_db, x, true));
500

501
  reason_ref = bp->reason.data[x];
713,021✔
502
  assert(clause_db_is_clause(&bp->clause_db, reason_ref, true));
503

504
  return reason_ref;
713,021✔
505
}
506

507
/**
508
 * Get the reason of why x is set to this value.
509
 */
510
static inline
511
const mcsat_clause_t* bool_plugin_get_reason(bool_plugin_t* bp, variable_t x) {
656,140✔
512
  return clause_db_get_clause(&bp->clause_db, bool_plugin_get_reason_ref(bp, x));
656,140✔
513
}
514

515
/**
516
 * Add any new clauses that were added in the meantime.
517
 */
518
static
519
void bool_plugin_add_new_clauses(bool_plugin_t* bp, trail_token_t* prop) {
1,248,356✔
520
  uint32_t i;
521
  int propagation_level;
522
  clause_ref_t c_ref;
523
  mcsat_clause_t* c;
524

525
  // Attach all the clauses
526
  for (i = 0; i < bp->clauses_to_add.size; ++ i) {
1,592,527✔
527
    // Get the clause and attach it
528
    c_ref = bp->clauses_to_add.data[i];
344,171✔
529
    propagation_level = bool_plugin_attach_clause(bp, c_ref, prop);
344,171✔
530

531
    if (propagation_level >= 0) {
344,171✔
532
      c = clause_db_get_clause(&bp->clause_db, c_ref);
83,262✔
533
      // If the clause propagates at current level, just propagate it
534
      assert(propagation_level <= bp->ctx->trail->decision_level);
535
      if (propagation_level == bp->ctx->trail->decision_level) {
83,262✔
536
        bool_plugin_propagate_literal(bp, c->literals[0], prop, c_ref);
83,212✔
537
      } else {
538
        // Propagates at lower level (this happens with assumptions)
539
        // we don't currently repropagate since we don't need to
540
        ivector_push(&bp->clauses_to_repropagate, c_ref);
50✔
541
        bool_plugin_propagate_literal(bp, c->literals[0], prop, c_ref);
50✔
542
      }
543
    }
544
  }
545

546
  // Done with the clause, reset
547
  ivector_reset(&bp->clauses_to_add);
1,248,356✔
548
}
1,248,356✔
549

550
/**
551
 * Propagates the trail with BCP. When done, either the trail is fully
552
 * propagated, or the trail is in an inconsistent state.
553
 */
554
static
555
void bool_plugin_propagate(plugin_t* plugin, trail_token_t* prop) {
1,248,356✔
556
  uint32_t k;
557
  bool_plugin_t* bp;
558
  const mcsat_trail_t* trail;
559
  variable_t var;
560
  bool var_value;
561
  mcsat_literal_t var_lit, var_lit_neg, lit, lit_neg;
562
  bcp_remove_iterator_t it;
563
  bcp_watcher_t* it_w;
564
  mcsat_clause_t* clause;
565
  bool watch_found;
566

567
  bp = (bool_plugin_t*) plugin;
1,248,356✔
568
  trail = bp->ctx->trail;
1,248,356✔
569

570
  // Add any new clauses
571
  bool_plugin_add_new_clauses(bp, prop);
1,248,356✔
572

573
  if (ctx_trace_enabled(bp->ctx, "bool::propagate")) {
1,248,356✔
574
    ctx_trace_printf(bp->ctx, "trail:\n");
×
575
    trail_print(bp->ctx->trail, bp->ctx->tracer->file);
×
576
  }
577

578
  // Propagate
579
  for(; trail_is_consistent(trail) && bp->trail_i < trail_size(trail); ++ bp->trail_i) {
6,060,805✔
580

581
    // Current trail element
582
    var = trail_at(bp->ctx->trail, bp->trail_i);;
4,812,449✔
583

584
    // Only for Boolean variables
585
    if (variable_db_is_boolean(bp->ctx->var_db, var)) {
4,812,449✔
586
      assert(trail_has_value(trail, var));
587
      var_value = trail_get_value(trail, var)->b;
3,995,062✔
588

589
      if (ctx_trace_enabled(bp->ctx, "bool::propagate")) {
3,995,062✔
590
        ctx_trace_printf(bp->ctx, "checking propagation due to ");
×
591
        variable_db_print_variable(bp->ctx->var_db, var, bp->ctx->tracer->file);
×
592
        ctx_trace_printf(bp->ctx, "\n");
×
593
      }
594

595
      // The literal we're propagating
596
      var_lit = literal_construct(var, !var_value);
3,995,062✔
597
      var_lit_neg = literal_negate(var_lit);
3,995,062✔
598

599
      // Get the watch-list
600
      bcp_remove_iterator_construct(&it, &bp->wlm, var_lit);
3,995,062✔
601

602
      while (trail_is_consistent(trail) && !bcp_remove_iterator_done(&it)) {
39,375,291✔
603
        it_w = bcp_remove_iterator_get_watcher(&it);
35,380,229✔
604

605
        // Check the blocker
606
        if(literal_is_true(it_w->blocker, trail)) {
35,380,229✔
607
          bcp_remove_iterator_next_and_keep(&it);
24,648,503✔
608
          continue;
24,648,503✔
609
        }
610

611
        // The binary clause case
612
        if (it_w->is_binary) {
10,731,726✔
613
          // Check the blocker
614
          if (literal_is_false(it_w->blocker, trail)) {
571,107✔
615
            bool_plugin_report_conflict(bp, prop, it_w->cref);
3,710✔
616
          } else {
617
            bool_plugin_propagate_literal(bp, it_w->blocker, prop, it_w->cref);
567,397✔
618
          }
619
          bcp_remove_iterator_next_and_keep(&it);
571,107✔
620
          continue;
571,107✔
621
        }
622

623
        // Get the clause
624
        clause = clause_db_get_clause(&bp->clause_db, it_w->cref);
10,160,619✔
625

626
        if (ctx_trace_enabled(bp->ctx, "bool::propagate")) {
10,160,619✔
627
          ctx_trace_printf(bp->ctx, "bool propagate on: %d ", it_w->cref);
×
628
          clause_print(clause, bp->ctx->var_db, bp->ctx->tracer->file);
×
629
          ctx_trace_printf(bp->ctx, "\n");
×
630
        }
631

632
        // Put the literal to [1] so that [0] is the propagation one
633
        if (clause->literals[0] == var_lit_neg) {
10,160,619✔
634
          clause_swap_literals(clause, 0, 1);
3,618,372✔
635
        }
636
        assert(literal_get_variable(clause->literals[1]) == var);
637

638
        // If [0] is true, the clause is already satisfied
639
        if (literal_is_true(clause->literals[0], trail)) {
10,160,619✔
640
          it_w->blocker = clause->literals[0];
732,896✔
641
          bcp_remove_iterator_next_and_keep(&it);
732,896✔
642

643
          if (ctx_trace_enabled(bp->ctx, "bool::propagate")) {
732,896✔
644
            ctx_trace_printf(bp->ctx, "clause true due to blocker\n");
×
645
          }
646
          continue;
732,896✔
647
        }
648

649
        // Find a new watch
650
        watch_found = false;
9,427,723✔
651
        for (k = 2; k < clause->size; ++ k) {
18,409,117✔
652
          if (!literal_is_false(clause->literals[k], trail)) {
16,178,843✔
653
            // Put it in place and add to watch list if not true at base level
654
            clause_swap_literals(clause, 1, k);
7,197,449✔
655
            lit = clause->literals[1];
7,197,449✔
656
            lit_neg = literal_negate(lit);
7,197,449✔
657
            bcp_watch_manager_add_to_watch(&bp->wlm, lit_neg, it_w->cref, false, clause->literals[0]);
7,197,449✔
658
            // Found the watch, done
659
            watch_found = true;
7,197,449✔
660
            break;
7,197,449✔
661
          } else {
662
            // Literal is false, see if at level 0, to push to back
663
            // TODO: We can check == clause level, but it's not clear
664
            // this optimization has any merit
665
            if (literal_get_level(clause->literals[k], trail) == 0) {
8,981,394✔
666
              clause->size --;
29,233✔
667
              clause_swap_literals(clause, k, clause->size);
29,233✔
668
              -- k;
29,233✔
669
            }
670
          }
671
        }
672

673
        if (!watch_found) {
9,427,723✔
674
          if (ctx_trace_enabled(bp->ctx, "bool::propagate")) {
2,230,274✔
675
            ctx_trace_printf(bp->ctx, "no watch found\n");
×
676
          }
677
          // No watch, we're ready to propagate
678
          lit = clause->literals[0];
2,230,274✔
679
          if (literal_has_value(lit, trail)) {
2,230,274✔
680
            // We've checked that it's not true, so it must be false
681
            assert(literal_is_false(lit, trail));
682
            bool_plugin_report_conflict(bp, prop, it_w->cref);
57,084✔
683
          } else {
684
            bool_plugin_propagate_literal(bp, lit, prop, it_w->cref);
2,173,190✔
685
          }
686
          // Keep the watch
687
          bcp_remove_iterator_next_and_keep(&it);
2,230,274✔
688
        } else {
689
          if (ctx_trace_enabled(bp->ctx, "bool::propagate")) {
7,197,449✔
690
            ctx_trace_printf(bp->ctx, "new watch found: %d ", it_w->cref);
×
691
            clause_print(clause, bp->ctx->var_db, bp->ctx->tracer->file);
×
692
            ctx_trace_printf(bp->ctx, "\n");
×
693
          }
694
          bcp_remove_iterator_next_and_remove(&it);
7,197,449✔
695
        }
696
      }
697

698
      // Done, destruct the iterator
699
      bcp_remove_iterator_destruct(&it);
3,995,062✔
700
    }
701
  }
702
}
1,248,356✔
703

704
static
705
void bool_plugin_decide(plugin_t* plugin, variable_t x, trail_token_t* decide, bool must) {
214,853✔
706
  bool_plugin_t* bp = (bool_plugin_t*) plugin;
214,853✔
707
  mcsat_literal_t literal;
708

709
  assert(!trail_has_value(bp->ctx->trail, x));
710

711
  if (trail_has_cached_value(bp->ctx->trail, x)) {
214,853✔
712
    // Use the cached value if exists
713
    literal = literal_construct(x, !trail_get_cached_value(bp->ctx->trail, x)->b);
213,160✔
714
  } else {
715
    // Go negative
716
    literal = literal_construct(x, true);
1,693✔
717
  }
718

719
  literal_set_value(literal, decide);
214,853✔
720
}
214,853✔
721

722
void bool_plugin_get_conflict(plugin_t* plugin, ivector_t* conflict) {
60,765✔
723
  bool_plugin_t* bp = (bool_plugin_t*) plugin;
60,765✔
724

725
  uint32_t i;
726
  mcsat_literal_t l_i;
727
  variable_t var_i;
728
  term_t term_i;
729
  mcsat_clause_t* conflict_clause;
730

731
  assert(bp->conflict != clause_ref_null);
732

733
  // Get the clause in conflict
734
  conflict_clause = clause_db_get_clause(&bp->clause_db, bp->conflict);
60,765✔
735

736
  // Add the negated literals to the conflict
737
  // (or l1 ... ln) is the same as
738
  // (and ~l1 ... ~ln) => false
739
  for (i = 0; i < conflict_clause->size; ++ i) {
391,840✔
740
    l_i = conflict_clause->literals[i];
331,075✔
741
    var_i = literal_get_variable(l_i);
331,075✔
742
    term_i = variable_db_get_term(bp->ctx->var_db, var_i);
331,075✔
743
    if (literal_is_negated(l_i)) {
331,075✔
744
      term_i = opposite_term(term_i);
182,630✔
745
    }
746
    ivector_push(conflict, opposite_term(term_i));
331,075✔
747
  }
748
}
60,765✔
749

750
term_t bool_plugin_explain_propagation(plugin_t* plugin, variable_t var, ivector_t* reasons) {
656,140✔
751
  bool_plugin_t* bp = (bool_plugin_t*) plugin;
656,140✔
752

753
  uint32_t i;
754
  mcsat_literal_t l_i;
755
  variable_t x_i;
756
  term_t t_i;
757
  bool var_value;
758
  const mcsat_clause_t* clause;
759

760
  // Add the other literals from the clause as explanations
761
  assert(trail_has_value(bp->ctx->trail, var));
762
  var_value = trail_get_value(bp->ctx->trail, var)->b;
656,140✔
763
  clause = bool_plugin_get_reason(bp, var);
656,140✔
764
  assert(clause->size == 2 || literal_get_variable(clause->literals[0]) == var);
765
  // Start from 0 to cover the binary clause case
766
  for (i = 0; i < clause->size; ++ i) {
3,285,800✔
767
    l_i = clause->literals[i];
2,629,660✔
768
    x_i = literal_get_variable(l_i);
2,629,660✔
769
    if (x_i == var) {
2,629,660✔
770
      continue;
656,140✔
771
    }
772

773
    t_i = variable_db_get_term(bp->ctx->var_db, x_i);
1,973,520✔
774
    if (literal_is_negated(l_i)) {
1,973,520✔
775
      t_i = opposite_term(t_i);
1,011,162✔
776
    }
777
    ivector_push(reasons, opposite_term(t_i));
1,973,520✔
778

779
    // Bump the reason variable -- give more weightage to boolean reasons
780
    bp->ctx->bump_variable_n(bp->ctx, x_i,
1,973,520✔
781
                             bp->heuristic_params.bool_var_bump_factor);
782
  }
783

784
  // Bump the clause as useful
785
  bool_plugin_bump_clause(bp, clause);
656,140✔
786

787
  return bool2term(var_value);
656,140✔
788
}
789

790
bool bool_plugin_explain_evaluation(plugin_t* plugin, term_t t, int_mset_t* vars, mcsat_value_t* value) {
16,499✔
791

792
  bool_plugin_t* bp = (bool_plugin_t*) plugin;
16,499✔
793
  const variable_db_t* var_db = bp->ctx->var_db;
16,499✔
794
  const mcsat_trail_t* trail = bp->ctx->trail;
16,499✔
795

796
  // Boolean plugin only explains evaluation of assigned false literals
797
  term_t t_unsigned = unsigned_term(t);
16,499✔
798
  variable_t t_var = variable_db_get_variable_if_exists(var_db, t_unsigned);
16,499✔
799
  if (t_var == variable_null) {
16,499✔
800
    // trying one step further to evaluate equality terms
801
    if (term_kind(bp->ctx->terms, t_unsigned) == EQ_TERM) {
40✔
802
      composite_term_t* t_desc = eq_term_desc(bp->ctx->terms, t_unsigned);
40✔
803
      term_t t1 = t_desc->arg[0];
40✔
804
      term_t t2 = t_desc->arg[1];
40✔
805
      assert(t1 != NULL_TERM);
806
      assert(t2 != NULL_TERM);
807
      variable_t t1_var = variable_db_get_variable_if_exists(var_db, t1);
40✔
808
      variable_t t2_var = variable_db_get_variable_if_exists(var_db, t2);
40✔
809
      if (t1_var != variable_null && t2_var != variable_null) {
40✔
810
        if (trail_has_value(trail, t1_var) && trail_has_value(trail, t2_var)) {
40✔
811
          int_mset_add(vars, t1_var);
40✔
812
          int_mset_add(vars, t2_var);
40✔
813
          bool negated = is_neg_term(t);
40✔
814
          const mcsat_value_t* t1_var_value = trail_get_value(trail, t1_var);
40✔
815
          const mcsat_value_t* t2_var_value = trail_get_value(trail, t2_var);
40✔
816
          if (negated) {
40✔
817
            return (t1_var_value->b == t2_var_value->b) != value->b;
×
818
          } else {
819
            return (t1_var_value->b == t2_var_value->b) == value->b;
40✔
820
          }
821
        }
822
      }
823
    }
824
    // couldn't evaluate
825
    return false;
×
826
  }
827

828
  int_mset_add(vars, t_var);
16,459✔
829
  if (trail_has_value(trail, t_var)) {
16,459✔
830
    bool negated = is_neg_term(t);
16,459✔
831
    const mcsat_value_t* t_var_value = trail_get_value(trail, t_var);
16,459✔
832
    if (negated) {
16,459✔
833
      return t_var_value->b != value->b;
8,933✔
834
    } else {
835
      return t_var_value->b == value->b;
7,526✔
836
    }
837
  }
838

839
  return false;
×
840
}
841

842
void bool_plugin_push(plugin_t* plugin) {
655,658✔
843
  bool_plugin_t* bp = (bool_plugin_t*) plugin;
655,658✔
844

845
  bp->propagated_size = bp->propagated.size;
655,658✔
846

847
  scope_holder_push(&bp->scope,
655,658✔
848
      &bp->trail_i,
849
      &bp->propagated_size,
850
      NULL);
851
}
655,658✔
852

853
void bool_plugin_pop(plugin_t* plugin) {
647,726✔
854
  bool_plugin_t* bp = (bool_plugin_t*) plugin;
647,726✔
855

856
  variable_t propagated_var;
857

858
  scope_holder_pop(&bp->scope,
647,726✔
859
      &bp->trail_i,
860
      &bp->propagated_size,
861
      NULL);
862

863
  assert(bp->propagated.size >= bp->propagated_size);
864
  while (bp->propagated.size > bp->propagated_size) {
3,422,847✔
865
    propagated_var = ivector_pop2(&bp->propagated);
2,775,121✔
866
    bool_plugin_set_reason_ref(bp, propagated_var, clause_ref_null);
2,775,121✔
867
  }
868
}
647,726✔
869

870
/**
871
 * Comparison based on clause value. Better go front.
872
 */
873
static
874
bool bool_plugin_clause_compare_for_removal(void *data, clause_ref_t c1, clause_ref_t c2) {
1,334,199✔
875

876
  clause_db_t* clause_db = (clause_db_t*) data;
1,334,199✔
877
  mcsat_clause_tag_t *c1_tag, *c2_tag;
878

879
  c1_tag = clause_db_get_tag(clause_db, c1);
1,334,199✔
880
  c2_tag = clause_db_get_tag(clause_db, c2);
1,334,199✔
881

882
  assert(c1_tag->type == CLAUSE_LEMMA);
883
  assert(c2_tag->type == CLAUSE_LEMMA);
884

885
  return c1_tag->score > c2_tag->score;
1,334,199✔
886
}
887

888
void bool_plugin_gc_mark(plugin_t* plugin, gc_info_t* gc_vars) {
1,380✔
889

890
  bool_plugin_t* bp = (bool_plugin_t*) plugin;
1,380✔
891
  clause_db_t* db = &bp->clause_db;
1,380✔
892
  const mcsat_trail_t* trail = bp->ctx->trail;
1,380✔
893

894
  uint32_t i;
895
  float act_threshold;
896
  variable_t var;
897
  clause_ref_t clause_ref;
898
  mcsat_clause_t* c;
899
  mcsat_clause_tag_t *c_tag;
900

901
  if (gc_vars->level == 0) {
1,380✔
902

903
    // Construct the gc info (destructed in collect())
904
    gc_info_construct(&bp->gc_clauses, clause_ref_null, false);
576✔
905

906
    // Sort the lemmas based on scores
907
    int_array_sort2(bp->lemmas.data, bp->lemmas.size, (void*) db, bool_plugin_clause_compare_for_removal);
576✔
908

909
    // avg activity score
910
    act_threshold = bp->heuristic_params.clause_score_bump_factor / bp->lemmas.size;
576✔
911

912
    // Mark all the variables in half of lemmas as used
913
    for (i = 0; i < bp->lemmas.size / 2; ++ i) {
53,395✔
914
      clause_ref = bp->lemmas.data[i];
52,840✔
915
      assert(clause_db_is_clause(db, clause_ref, true));
916
      c_tag = clause_db_get_tag(db, clause_ref);
52,840✔
917
      if (c_tag->score <= act_threshold) {
52,840✔
918
        // consider clauses with score higher than the avg activity score
919
        // since the clauses are sorted according to their scores, we break here
920
        break;
21✔
921
      }
922
      gc_info_mark(&bp->gc_clauses, clause_ref);
52,819✔
923
    }
924

925
    // We also keep the clauses of any propagated literals
926
    for (i = 0; i < bp->propagated.size; ++ i) {
57,457✔
927
      var = bp->propagated.data[i];
56,881✔
928
      clause_ref = bool_plugin_get_reason_ref(bp, var);
56,881✔
929
      gc_info_mark(&bp->gc_clauses, clause_ref);
56,881✔
930
    }
931

932
    // Keep clauses recently created or resolved in conflict analysis
933
    // (cf. smt_core). The protection decays only on reduce GCs (the ones
934
    // this plugin scheduled), not on GCs triggered for other reasons.
935
    // Binary clauses start higher, so they get an extra reduce round.
936
    for (i = 0; i < bp->lemmas.size; ++ i) {
112,382✔
937
      clause_ref = bp->lemmas.data[i];
111,806✔
938
      assert(clause_db_is_clause(db, clause_ref, true));
939
      c_tag = clause_db_get_tag(db, clause_ref);
111,806✔
940
      if (c_tag->used > 0) {
111,806✔
941
        if (bp->reduce_requested) {
69,359✔
942
          c_tag->used --;
69,132✔
943
        }
944
        // no protection for binary clauses with a satisfied literal: GC
945
        // runs at base level, so they are permanently satisfied
946
        c = clause_db_get_clause(db, clause_ref);
69,359✔
947
        if (c->size == 2 &&
73,733✔
948
            (literal_is_true(c->literals[0], trail) ||
8,493✔
949
             literal_is_true(c->literals[1], trail))) {
4,119✔
950
          continue;
1,031✔
951
        }
952
        gc_info_mark(&bp->gc_clauses, clause_ref);
68,328✔
953
      }
954
    }
955
    bp->reduce_requested = false;
576✔
956
  }
957

958
  // Mark all the CNF definitions
959
  cnf_gc_mark(&bp->cnf, &bp->gc_clauses, gc_vars);
1,380✔
960

961
  // Mark all variables through the clause database
962
  clause_db_gc_mark(db, &bp->gc_clauses, gc_vars);
1,380✔
963
}
1,380✔
964

965
void bool_plugin_gc_sweep(plugin_t* plugin, const gc_info_t* gc_vars) {
576✔
966

967
  bool_plugin_t* bp = (bool_plugin_t*) plugin;
576✔
968

969
  uint32_t i;
970
  variable_t var;
971
  int_mset_t vars_undefined;
972
  clause_ref_t clause, clause_reloc;
973

974
  // Clauses
975
  int_mset_construct(&vars_undefined, variable_null);
576✔
976
  clause_db_gc_sweep(&bp->clause_db, &bp->gc_clauses, &vars_undefined);
576✔
977
  cnf_gc_sweep(&bp->cnf, &bp->gc_clauses, &vars_undefined);
576✔
978
  int_mset_destruct(&vars_undefined);
576✔
979

980
  // Vectors of clauses
981
  gc_info_sweep_ivector(&bp->gc_clauses, &bp->clauses_to_add);
576✔
982
  gc_info_sweep_ivector(&bp->gc_clauses, &bp->clauses);
576✔
983
  gc_info_sweep_ivector(&bp->gc_clauses, &bp->lemmas);
576✔
984
  gc_info_sweep_ivector(&bp->gc_clauses, &bp->clauses_to_repropagate);
576✔
985

986
  assert(clause_db_is_clause_vector(&bp->clause_db, &bp->clauses_to_add, true));
987
  assert(clause_db_is_clause_vector(&bp->clause_db, &bp->clauses, true));
988
  assert(clause_db_is_clause_vector(&bp->clause_db, &bp->lemmas, true));
989
  assert(clause_db_is_clause_vector(&bp->clause_db, &bp->clauses_to_repropagate, true));
990

991
  // Watch manager
992
  bcp_watch_manager_sweep(&bp->wlm, &bp->gc_clauses, gc_vars);
576✔
993

994
  // Reasons
995
  assert(gc_vars->is_id);
996
  for (i = 0; i < bp->propagated.size; ++ i) {
57,457✔
997

998
    // The variable itself
999
    var = bp->propagated.data[i];
56,881✔
1000
    assert(bp->reason.data[var] != clause_ref_null);
1001

1002
    // Variable might be gone, just remove the reason
1003
    if (gc_info_get_reloc(gc_vars, var) == variable_null) {
56,881✔
UNCOV
1004
      bool_plugin_set_reason_ref(bp, var, clause_ref_null);
×
1005
    } else {
1006
      // The clausal reason for var propagation
1007
      clause = bp->reason.data[var]; // Getting directly, not a valud reason anymore
56,881✔
1008
      clause_reloc = gc_info_get_reloc(&bp->gc_clauses, clause);
56,881✔
1009
      assert(clause_reloc != clause_ref_null);
1010
      bool_plugin_set_reason_ref(bp, var, clause_reloc);
56,881✔
1011
    }
1012
  }
1013

1014
  // Propagated vector
1015
  gc_info_sweep_ivector(gc_vars, &bp->propagated);
576✔
1016

1017
  // Destroy the gc info (constructed in mark())
1018
  gc_info_destruct(&bp->gc_clauses);
576✔
1019
}
576✔
1020

1021
static
1022
void bool_plugin_remove_stale_clauses(bool_plugin_t* bp) {
470✔
1023
  uint32_t i, to_keep;
1024
  clause_ref_t clause_ref;
1025
  clause_db_t* db = &bp->clause_db;
470✔
1026
  uint32_t base_level = bp->ctx->trail->decision_level_base;
470✔
1027
  for (i = 0, to_keep = 0; i < bp->lemmas.size; ++ i) {
4,493✔
1028
    clause_ref = bp->lemmas.data[i];
4,023✔
1029
    assert(clause_db_is_clause(db, clause_ref, true));
1030
    // Keep the lemma if it's at the right level
1031
    if (clause_db_get_tag(db, clause_ref)->level <= base_level) {
4,023✔
1032
      bp->lemmas.data[to_keep++] = clause_ref;
224✔
1033
    }
1034
  }
1035
  ivector_shrink(&bp->lemmas, to_keep);
470✔
1036
}
470✔
1037

1038
static
1039
void bool_plugin_event_notify(plugin_t* plugin, plugin_notify_kind_t kind) {
104,579✔
1040
  bool_plugin_t* bp = (bool_plugin_t*) plugin;
104,579✔
1041

1042
  switch (kind) {
104,579✔
1043
  case MCSAT_SOLVER_START:
1,062✔
1044
    // Re-initialize the reduce schedule for this search. bp->conflicts is
1045
    // cumulative (it tracks the core solver's conflict count), so anchor the
1046
    // first threshold at the current conflict count + the initial offset
1047
    // (cf. CaDiCaL's lim.reduce = conflicts + reduceinit).
1048
    bp->reduce_threshold = bp->conflicts + bp->heuristic_params.reduce_init_threshold;
1,062✔
1049
    break;
1,062✔
1050
  case MCSAT_SOLVER_RESTART:
17,011✔
1051
    // Nothing to do: the reduce schedule is driven by MCSAT_SOLVER_CONFLICT
1052
    break;
17,011✔
1053
  case MCSAT_SOLVER_CONFLICT:
86,036✔
1054
    // Count conflicts and decay the scores each conflict
1055
    bp->conflicts ++;
86,036✔
1056
    bool_plugin_decay_clause_scores(bp);
86,036✔
1057
    // Reduce the lemma database on a conflict-based schedule (cf. smt_core):
1058
    // when the conflict count reaches the threshold, request a GC and set the
1059
    // next threshold to conflicts + reduce_interval * sqrt(conflicts). The
1060
    // solver runs the GC at base level, right after the next restart (a
1061
    // pending GC forces a full restart, even with partial restarts enabled).
1062
    if (bp->conflicts >= bp->reduce_threshold) {
86,036✔
1063
      bp->ctx->request_gc(bp->ctx);
95✔
1064
      bp->reduce_requested = true;
95✔
1065
      bp->reduce_threshold = bp->conflicts +
95✔
1066
        (uint64_t) (bp->heuristic_params.reduce_interval * sqrt((double) bp->conflicts));
95✔
1067
    }
1068
    break;
86,036✔
1069
  case MCSAT_SOLVER_POP:
470✔
1070
    // Remove all learnt clauses above base level, regular clauses will be
1071
    // removed trhough garbage collection
1072
    bool_plugin_remove_stale_clauses(bp);
470✔
1073
    break;
470✔
1074
  default:
104,579✔
1075
    assert(false);
1076
  }
1077
}
104,579✔
1078

1079
static
NEW
1080
void bool_plugin_set_exception_handler(plugin_t* plugin, jmp_buf* handler) {
×
NEW
1081
  bool_plugin_t* bp = (bool_plugin_t*) plugin;
×
NEW
1082
  bp->exception = handler;
×
NEW
1083
}
×
1084

1085
static
1086
void bool_plugin_decide_assignment(plugin_t* plugin, variable_t x, const mcsat_value_t* value, trail_token_t* decide) {
1,210✔
1087
  // Nothing to do here apart from setting the value
1088
  decide->add(decide, x, value);
1,210✔
1089
}
1,210✔
1090

1091

1092
plugin_t* bool_plugin_allocator(void) {
740✔
1093
  bool_plugin_t* plugin = safe_malloc(sizeof(bool_plugin_t));
740✔
1094
  plugin_construct((plugin_t*) plugin);
740✔
1095
  plugin->plugin_interface.construct             = bool_plugin_construct;
740✔
1096
  plugin->plugin_interface.destruct              = bool_plugin_destruct;
740✔
1097
  plugin->plugin_interface.new_term_notify       = bool_plugin_new_term_notify;
740✔
1098
  plugin->plugin_interface.new_lemma_notify      = bool_plugin_new_lemma_notify;
740✔
1099
  plugin->plugin_interface.event_notify          = bool_plugin_event_notify;
740✔
1100
  plugin->plugin_interface.propagate             = bool_plugin_propagate;
740✔
1101
  plugin->plugin_interface.decide                = bool_plugin_decide;
740✔
1102
  plugin->plugin_interface.decide_assignment     = bool_plugin_decide_assignment;
740✔
1103
  plugin->plugin_interface.get_conflict          = bool_plugin_get_conflict;
740✔
1104
  plugin->plugin_interface.explain_propagation   = bool_plugin_explain_propagation;
740✔
1105
  plugin->plugin_interface.explain_evaluation    = bool_plugin_explain_evaluation;
740✔
1106
  plugin->plugin_interface.push                  = bool_plugin_push;
740✔
1107
  plugin->plugin_interface.pop                   = bool_plugin_pop;
740✔
1108
  plugin->plugin_interface.gc_mark               = bool_plugin_gc_mark;
740✔
1109
  plugin->plugin_interface.gc_sweep              = bool_plugin_gc_sweep;
740✔
1110
  plugin->plugin_interface.set_exception_handler = bool_plugin_set_exception_handler;
740✔
1111

1112
  return (plugin_t*) plugin;
740✔
1113
}
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