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

SRI-CSL / yices2 / 26470694967

26 May 2026 07:36PM UTC coverage: 69.0% (+0.2%) from 68.786%
26470694967

Pull #630

github

web-flow
Merge 3fb2be356 into 2137d2aaf
Pull Request #630: Add wide (truth-invariant) model generalization mode

407 of 551 new or added lines in 2 files covered. (73.87%)

28 existing lines in 1 file now uncovered.

88101 of 127682 relevant lines covered (69.0%)

1625967.0 hits per line

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

69.43
/src/model/generalization.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
 * MODEL GENERALIZATION
21
 *
22
 * Given a model mdl for a formula F(x, y), this module computes
23
 * the generalization of mdl for the variables 'x'. The result
24
 * is a formula G(x) such that 
25
 * 1) G(x) is true in mdl
26
 * 2) G(x) implies (EXISTS y: F(x, y))
27
 *
28
 * If any variable in y is an arithmetic variable then G(x) is
29
 * computed using model-based projection. Otherwise, G(x) is
30
 * obtained by substitution: we replace every variable y_i
31
 * by its value in the model.
32
 *
33
 * NOTE: we could use model-based projection in both cases, but
34
 * experiments with the exists/forall solver seem to show that
35
 * substitution works better for Boolean and bitvector variables.
36
 *
37
 *
38
 * Two projection variants are implemented:
39
 *
40
 * - "local" (gen_model_by_proj_local): the legacy pipeline.
41
 *   Builds a single literal implicant of f[] at the model
42
 *   (one literal per disjunction) and projects that flat
43
 *   conjunction. Cheaper per call but commits to one disjunct,
44
 *   so the resulting cell is sign-invariant for the chosen
45
 *   implicant and is strictly narrower than the wide cell
46
 *   whenever F has Boolean structure the model satisfies in
47
 *   more than one way.
48
 *
49
 * - "wide" (gen_model_by_proj_sat_guided): the public default.
50
 *   Builds a model-pruned Boolean abstraction of f[], enumerates
51
 *   model-true Boolean implicants with a SAT solver and blocker
52
 *   clauses, projects each implicant as a cube through the legacy
53
 *   implicant-then-project pipeline, and unions the results at the
54
 *   term level.
55
 *
56
 *   If SAT-guided enumeration hits the cube budget or a projection
57
 *   error after collecting some cubes, the result is OR(collected,
58
 *   local). If no SAT-guided cube has been collected, the wide path
59
 *   returns the local cell.
60
 */
61

62
#include <assert.h>
63

64
#include "model/generalization.h"
65
#include "model/model_eval.h"
66
#include "model/model_queries.h"
67
#include "model/projection.h"
68
#include "model/val_to_term.h"
69
#include "solvers/cdcl/new_sat_solver.h"
70
#include "terms/term_manager.h"
71
#include "terms/term_substitution.h"
72
#include "terms/terms.h"
73
#include "utils/int_hash_map.h"
74
#include "utils/int_vectors.h"
75
#include "utils/memalloc.h"
76

77

78
/*
79
 * Split the array of variables v into discrete and real variables
80
 * - n = number of variables in v
81
 * - all variables of type REAL are added to vector reals
82
 * - all other variables are added to discretes
83
 */
UNCOV
84
static void split_elim_array(term_table_t *terms, uint32_t n, const term_t v[], ivector_t *reals, ivector_t *discretes) {
×
85
  uint32_t i;
86
  term_t t;
87

UNCOV
88
  for (i=0; i<n; i++) {
×
UNCOV
89
    t = v[i];
×
NEW
90
    if (is_real_term(terms, t)) {
×
NEW
91
      ivector_push(reals, t);
×
92
    } else {
NEW
93
      ivector_push(discretes, t);
×
94
    }
95
  }
NEW
96
}
×
97

98

99
/*
100
 * Conversion of error codes to GEN.. codes
101
 * - eval_errors are in the range [-7 ... -2]
102
 *   MDL_EVAL_FAILED = -7 and MDL_EVAL_INTERNAL_ERROR = -2
103
 *   they are kept unchanged
104
 * - conversion errors are in the range [-6 .. -2]
105
 *   CONVERT_FAILED = -6 and CONVERT_INTERNAL_ERROR = -2
106
 *   we renumber them to [-13 .. -9]
107
 * - implicant construction errors are in the range [-8 ... -2]
108
 *   MDL_EVAL_FORMULA_FALSE = -8 and MDL_EVAL_INTERNAL_ERROR = -2
109
 * - projection errors are in the range [-7 .. - 1]
110
 *   we renumber to [-20 .. -14]
111
 */
112
static inline int32_t gen_eval_error(int32_t error) {
×
113
  assert(MDL_EVAL_FAILED <= error && error <= MDL_EVAL_INTERNAL_ERROR);
114
  return error;
×
115
}
116

117
static inline int32_t gen_convert_error(int32_t error) {
×
118
  assert(CONVERT_FAILED <= error && error <= CONVERT_INTERNAL_ERROR);
UNCOV
119
  return error + (GEN_CONV_INTERNAL_ERROR - CONVERT_INTERNAL_ERROR);
×
120
}
121

UNCOV
122
static inline int32_t gen_implicant_error(int32_t error) {
×
123
  assert(MDL_EVAL_FORMULA_FALSE <= error && error <= MDL_EVAL_INTERNAL_ERROR);
UNCOV
124
  return error;
×
125
}
126

UNCOV
127
static inline int32_t gen_projection_error(proj_flag_t error) {
×
128
  assert(PROJ_ERROR_UNSUPPORTED_ARITH_TERM <= error && error <= PROJ_ERROR_NON_LINEAR);
UNCOV
129
  return error + (GEN_PROJ_ERROR_NON_LINEAR - PROJ_ERROR_NON_LINEAR);
×
130
}
131

132

133
/*
134
 * Generalization by substitution: core procedure
135
 * - mdl = model
136
 * - mngr = relevant term manager
137
 * - elim[0 ... nelim -1] = variables to eliminate
138
 * - on entry to the function, v must contain the formulas to project
139
 * - the result is returned in place (in vector v)
140
 *
141
 * - returned code: 0 if no error, an error code otherwise
142
 * - error codes are listed in generalization.h
143
 */
UNCOV
144
static int32_t gen_model_by_subst(model_t *mdl, term_manager_t *mngr, uint32_t nelims, const term_t elim[], ivector_t *v) {
×
145
  term_subst_t subst;
146
  ivector_t aux;
147
  term_table_t *terms;
148
  int32_t code;
149
  uint32_t k, n;
150
  term_t t;
151

152
  // get the value of elim[i] in aux.data[i]
UNCOV
153
  init_ivector(&aux, nelims);
×
UNCOV
154
  code = evaluate_term_array(mdl, nelims, elim, aux.data);
×
UNCOV
155
  if (code < 0) {
×
156
    // error in evaluator
UNCOV
157
    code = gen_eval_error(code);
×
158
    assert(GEN_EVAL_FAILED <= code  && code <= GEN_EVAL_INTERNAL_ERROR);
UNCOV
159
    goto done;
×
160
  }
161

162
  // convert every aux.data[i] to a constant term
UNCOV
163
  terms = term_manager_get_terms(mngr);
×
UNCOV
164
  k = convert_value_array(mngr, terms, model_get_vtbl(mdl), nelims, aux.data);
×
165
  if (k < nelims) {
×
166
    // aux.data[k] couldn't be converted to a term
167
    // the error code is in aux.data[k]
UNCOV
168
    code = gen_convert_error(aux.data[k]);
×
169
    assert(GEN_CONV_FAILED <= code && code <= GEN_CONV_INTERNAL_ERROR);
UNCOV
170
    goto done;
×
171
  }
172

173
  // build the substitution: elim[i] := aux.data[i]
174
  // then apply it to every term in vector v
175
  code = 0;
×
176
  init_term_subst(&subst, mngr, nelims, elim, aux.data);
×
UNCOV
177
  n = v->size;
×
178
  for (k=0; k<n; k++) {
×
UNCOV
179
    t = apply_term_subst(&subst, v->data[k]);
×
180
    v->data[k] = t;
×
UNCOV
181
    if (t < 0) {
×
UNCOV
182
      code = GEN_PROJ_ERROR_IN_SUBST;
×
UNCOV
183
      break;
×
184
    }
185
  }
186
  delete_term_subst(&subst);
×
187

UNCOV
188
 done:
×
189
  delete_ivector(&aux);
×
190

191
  return code;
×
192
}
193

194

195
/*
196
 * Generalization by local projection (legacy pipeline):
197
 *   - compute an implicant of v then project the implicant
198
 * - mdl = model
199
 * - mngr = relevant term manager
200
 * - elim[0 ... nelims-1] = variables to eliminate
201
 * - on entry to the function, v must contain the formulas to project
202
 *   the result is returned in place (in vector v)
203
 * - extra_error = to store another error code for diagnosis (see projection.h).
204
 *
205
 * Return code: 0 if no error, an error code otherwise
206
 *
207
 * The output cell is sign-invariant for the chosen implicant. If v[]
208
 * has Boolean structure (disjunctions, Boolean ITEs), only one branch
209
 * is captured: the one selected by get_implicant from the model.
210
 */
211
static int32_t gen_model_by_proj_local(model_t *mdl, term_manager_t *mngr, uint32_t nelims, const term_t elim[], ivector_t *v, int32_t *extra_error) {
10✔
212
  ivector_t implicant;
213
  int32_t code;
214
  proj_flag_t pflag;
215

216
  init_ivector(&implicant, 10);
10✔
217
  code = get_implicant(mdl, mngr, LIT_COLLECTOR_ALL_OPTIONS, v->size, v->data, &implicant);
10✔
218
  if (code < 0) {
10✔
219
    // implicant construction failed
UNCOV
220
    code = gen_implicant_error(code);
×
UNCOV
221
    goto done;
×
222
  }
223
  
224
  ivector_reset(v); // reset v to collect the projection result
10✔
225
  code = 0;
10✔
226
  pflag = project_literals(mdl, mngr, implicant.size, implicant.data, nelims, elim, v, extra_error);
10✔
227
  if (pflag != PROJ_NO_ERROR) {
10✔
NEW
228
    code = gen_projection_error(pflag);
×
229
  }
230

231
 done:
10✔
232
  delete_ivector(&implicant);
10✔
233

234
  return code;
10✔
235
  
236
}
237

238

239

240
/*
241
 * Default cube budget for the SAT-guided wide pipeline. If SAT-guided
242
 * implicant enumeration would emit more than this many cubes, the
243
 * loop stops and the wide result is OR(collected, local fallback).
244
 */
245
#define WIDE_CUBE_BUDGET 1024
246

247
/*
248
 * Project a single cube via the legacy implicant+project pipeline.
249
 * The cube's literals are normalized through get_implicant (which
250
 * expands ITE-inside-arith, etc.) and then projected.
251
 *
252
 * The projected literals are appended to *out (a list of literals
253
 * whose AND is the projected cube). out is not reset.
254
 */
255
static int32_t project_one_cube_into(model_t *mdl, term_manager_t *mngr,
1,036✔
256
                                     const term_t *cube_lits, uint32_t cube_size,
257
                                     uint32_t nelims, const term_t elim[],
258
                                     ivector_t *out, int32_t *extra_error) {
259
  ivector_t implicant;
260
  proj_flag_t pflag;
261
  int32_t code;
262

263
  init_ivector(&implicant, cube_size);
1,036✔
264

265
  code = get_implicant(mdl, mngr, LIT_COLLECTOR_ALL_OPTIONS, cube_size, cube_lits, &implicant);
1,036✔
266
  if (code < 0) {
1,036✔
NEW
267
    code = gen_implicant_error(code);
×
NEW
268
    goto cleanup;
×
269
  }
270

271
  pflag = project_literals(mdl, mngr, implicant.size, implicant.data,
1,036✔
272
                           nelims, elim, out, extra_error);
273
  if (pflag != PROJ_NO_ERROR) {
1,036✔
NEW
274
    code = gen_projection_error(pflag);
×
NEW
275
    goto cleanup;
×
276
  }
277

278
  code = 0;
1,036✔
279

280
 cleanup:
1,036✔
281
  delete_ivector(&implicant);
1,036✔
282
  return code;
1,036✔
283
}
284

285

286
/*
287
 * SAT-GUIDED WIDE PROJECTION
288
 *
289
 * Front end built on a model-pruned Boolean abstraction and SAT-guided
290
 * implicant enumeration. Each emitted Boolean implicant is mapped back
291
 * to a theory cube and projected via project_one_cube_into above. The
292
 * union of the projected cubes is returned as the wide cell.
293
 */
294

295
typedef int32_t bool_node_id_t;
296

297
typedef enum {
298
  BOOL_NODE_TRUE = 0,
299
  BOOL_NODE_FALSE = 1,
300
  BOOL_NODE_LIT = 2,
301
  BOOL_NODE_AND = 3,
302
  BOOL_NODE_OR = 4,
303
} bool_node_kind_t;
304

305
typedef struct bool_node_s {
306
  uint8_t kind;
307
  literal_t lit;
308
  uint32_t start;
309
  uint32_t count;
310
  literal_t tseitin;
311
} bool_node_t;
312

313
typedef struct bool_dag_s {
314
  bool_node_t *data;
315
  uint32_t size;
316
  uint32_t capacity;
317
  ivector_t children;
318
} bool_dag_t;
319

320
enum {
321
  BOOL_DAG_TRUE = 0,
322
  BOOL_DAG_FALSE = 1,
323
};
324

325
typedef enum {
326
  ABS_OK = 0,
327
  ABS_ERROR = 1,
328
} abs_status_t;
329

330
typedef enum {
331
  ABS_EVAL_FALSE = 0,
332
  ABS_EVAL_TRUE = 1,
333
  ABS_EVAL_ERROR = 2,
334
} abs_eval_t;
335

336
typedef struct abs_builder_s {
337
  term_manager_t *mngr;
338
  term_table_t *terms;
339
  evaluator_t *eval;
340
  int_hmap_t atom_to_bvar;
341
  ivector_t bvar_to_atom;
342
  int_hmap_t cache_signed;
343
  bool_dag_t dag;
344
  bool decomposed;
345
} abs_builder_t;
346

347
static void init_bool_dag(bool_dag_t *dag) {
8✔
348
  dag->capacity = 32;
8✔
349
  dag->size = 0;
8✔
350
  dag->data = (bool_node_t *) safe_malloc(dag->capacity * sizeof(bool_node_t));
8✔
351
  init_ivector(&dag->children, 64);
8✔
352

353
  // Constant TRUE node.
354
  dag->data[dag->size].kind = BOOL_NODE_TRUE;
8✔
355
  dag->data[dag->size].lit = null_literal;
8✔
356
  dag->data[dag->size].start = 0;
8✔
357
  dag->data[dag->size].count = 0;
8✔
358
  dag->data[dag->size].tseitin = null_literal;
8✔
359
  dag->size ++;
8✔
360

361
  // Constant FALSE node.
362
  dag->data[dag->size].kind = BOOL_NODE_FALSE;
8✔
363
  dag->data[dag->size].lit = null_literal;
8✔
364
  dag->data[dag->size].start = 0;
8✔
365
  dag->data[dag->size].count = 0;
8✔
366
  dag->data[dag->size].tseitin = null_literal;
8✔
367
  dag->size ++;
8✔
368
}
8✔
369

370
static void delete_bool_dag(bool_dag_t *dag) {
8✔
371
  safe_free(dag->data);
8✔
372
  dag->data = NULL;
8✔
373
  dag->size = 0;
8✔
374
  dag->capacity = 0;
8✔
375
  delete_ivector(&dag->children);
8✔
376
}
8✔
377

NEW
378
static void bool_dag_rollback(bool_dag_t *dag, uint32_t size, uint32_t children_size) {
×
379
  assert(BOOL_DAG_FALSE < size && size <= dag->size);
380
  assert(children_size <= dag->children.size);
381

NEW
382
  dag->size = size;
×
NEW
383
  dag->children.size = children_size;
×
NEW
384
}
×
385

386
static bool_node_id_t bool_dag_add_node(bool_dag_t *dag, bool_node_kind_t kind,
74✔
387
                                        literal_t lit, uint32_t n, const int32_t child[]) {
388
  bool_node_id_t id;
389
  uint32_t i;
390

391
  if (dag->size == dag->capacity) {
74✔
392
    dag->capacity <<= 1;
1✔
393
    dag->data = (bool_node_t *) safe_realloc(dag->data, dag->capacity * sizeof(bool_node_t));
1✔
394
  }
395

396
  id = (bool_node_id_t) dag->size;
74✔
397
  dag->data[id].kind = kind;
74✔
398
  dag->data[id].lit = lit;
74✔
399
  dag->data[id].start = dag->children.size;
74✔
400
  dag->data[id].count = n;
74✔
401
  dag->data[id].tseitin = null_literal;
74✔
402
  for (i = 0; i < n; i++) {
156✔
403
    ivector_push(&dag->children, child[i]);
82✔
404
  }
405
  dag->size ++;
74✔
406
  return id;
74✔
407
}
408

409
static bool_node_id_t bool_dag_add_lit(bool_dag_t *dag, literal_t lit) {
42✔
410
  return bool_dag_add_node(dag, BOOL_NODE_LIT, lit, 0, NULL);
42✔
411
}
412

413
static bool_node_id_t bool_dag_add_and(bool_dag_t *dag, uint32_t n, const int32_t child[]) {
15✔
414
  return bool_dag_add_node(dag, BOOL_NODE_AND, null_literal, n, child);
15✔
415
}
416

417
static bool_node_id_t bool_dag_add_or(bool_dag_t *dag, uint32_t n, const int32_t child[]) {
17✔
418
  return bool_dag_add_node(dag, BOOL_NODE_OR, null_literal, n, child);
17✔
419
}
420

421
static inline bool bool_node_is_true(bool_node_id_t id) {
80✔
422
  return id == BOOL_DAG_TRUE;
80✔
423
}
424

425
static inline bool bool_node_is_false(bool_node_id_t id) {
114✔
426
  return id == BOOL_DAG_FALSE;
114✔
427
}
428

429
static void init_abs_builder(abs_builder_t *b, model_t *mdl, term_manager_t *mngr, evaluator_t *eval) {
8✔
430
  b->mngr = mngr;
8✔
431
  b->terms = term_manager_get_terms(mngr);
8✔
432
  b->eval = eval;
8✔
433
  init_int_hmap(&b->atom_to_bvar, 0);
8✔
434
  init_ivector(&b->bvar_to_atom, 16);
8✔
435
  ivector_push(&b->bvar_to_atom, NULL_TERM); // var 0 is reserved by new_sat_solver
8✔
436
  init_int_hmap(&b->cache_signed, 0);
8✔
437
  init_bool_dag(&b->dag);
8✔
438
  b->decomposed = false;
8✔
439
  (void) mdl;
440
}
8✔
441

442
static void delete_abs_builder(abs_builder_t *b) {
8✔
443
  // After ABS_ERROR, discard the whole builder: rollback does not undo caches.
444
  delete_bool_dag(&b->dag);
8✔
445
  delete_int_hmap(&b->cache_signed);
8✔
446
  delete_ivector(&b->bvar_to_atom);
8✔
447
  delete_int_hmap(&b->atom_to_bvar);
8✔
448
}
8✔
449

450
static bvar_t abs_builder_get_atom_var(abs_builder_t *b, term_t atom) {
42✔
451
  int_hmap_pair_t *r;
452
  bvar_t v;
453

454
  assert(atom == unsigned_term(atom));
455
  r = int_hmap_get(&b->atom_to_bvar, atom);
42✔
456
  if (r->val < 0) {
42✔
457
    v = (bvar_t) b->bvar_to_atom.size;
42✔
458
    r->val = v;
42✔
459
    ivector_push(&b->bvar_to_atom, atom);
42✔
460
  } else {
NEW
461
    v = (bvar_t) r->val;
×
462
  }
463
  return v;
42✔
464
}
465

466
static abs_status_t abstract_signed(abs_builder_t *b, term_t t, bool_node_id_t *out);
467

468
static void ivector_push_unique(ivector_t *v, int32_t x) {
11,373✔
469
  uint32_t i, n;
470

471
  n = v->size;
11,373✔
472
  for (i = 0; i < n; i++) {
67,844✔
473
    if (v->data[i] == x) {
56,472✔
474
      return;
1✔
475
    }
476
  }
477
  ivector_push(v, x);
11,372✔
478
}
479

480
static abs_status_t bool_dag_mk_and(abs_builder_t *b, ivector_t *child, bool_node_id_t *out) {
16✔
481
  bool_dag_t *dag;
482
  ivector_t flat;
483
  uint32_t i, j, n;
484
  bool_node_id_t id;
485
  bool_node_t *node;
486

487
  dag = &b->dag;
16✔
488
  init_ivector(&flat, child->size);
16✔
489
  n = child->size;
16✔
490
  for (i = 0; i < n; i++) {
50✔
491
    id = child->data[i];
34✔
492
    if (bool_node_is_false(id)) {
34✔
NEW
493
      *out = BOOL_DAG_FALSE;
×
NEW
494
      delete_ivector(&flat);
×
NEW
495
      return ABS_OK;
×
496
    }
497
    if (bool_node_is_true(id)) {
34✔
NEW
498
      continue;
×
499
    }
500
    node = &dag->data[id];
34✔
501
    if (node->kind == BOOL_NODE_AND) {
34✔
502
      for (j = 0; j < node->count; j++) {
27✔
503
        ivector_push_unique(&flat, dag->children.data[node->start + j]);
21✔
504
      }
505
    } else {
506
      ivector_push_unique(&flat, id);
28✔
507
    }
508
  }
509

510
  if (flat.size == 0) {
16✔
NEW
511
    *out = BOOL_DAG_TRUE;
×
512
  } else if (flat.size == 1) {
16✔
513
    *out = flat.data[0];
1✔
514
  } else {
515
    *out = bool_dag_add_and(dag, flat.size, flat.data);
15✔
516
  }
517
  delete_ivector(&flat);
16✔
518
  return ABS_OK;
16✔
519
}
520

521
static abs_status_t bool_dag_mk_or(abs_builder_t *b, ivector_t *child, bool_node_id_t *out) {
19✔
522
  bool_dag_t *dag;
523
  ivector_t flat;
524
  uint32_t i, j, n;
525
  bool_node_id_t id;
526
  bool_node_t *node;
527

528
  dag = &b->dag;
19✔
529
  init_ivector(&flat, child->size);
19✔
530
  n = child->size;
19✔
531
  for (i = 0; i < n; i++) {
57✔
532
    id = child->data[i];
38✔
533
    if (bool_node_is_true(id)) {
38✔
NEW
534
      *out = BOOL_DAG_TRUE;
×
NEW
535
      delete_ivector(&flat);
×
NEW
536
      return ABS_OK;
×
537
    }
538
    if (bool_node_is_false(id)) {
38✔
539
      continue;
2✔
540
    }
541
    node = &dag->data[id];
36✔
542
    if (node->kind == BOOL_NODE_OR) {
36✔
NEW
543
      for (j = 0; j < node->count; j++) {
×
NEW
544
        ivector_push_unique(&flat, dag->children.data[node->start + j]);
×
545
      }
546
    } else {
547
      ivector_push_unique(&flat, id);
36✔
548
    }
549
  }
550

551
  if (flat.size == 0) {
19✔
NEW
552
    *out = BOOL_DAG_FALSE;
×
553
  } else if (flat.size == 1) {
19✔
554
    *out = flat.data[0];
2✔
555
  } else {
556
    *out = bool_dag_add_or(dag, flat.size, flat.data);
17✔
557
  }
558
  delete_ivector(&flat);
19✔
559
  return ABS_OK;
19✔
560
}
561

562
static abs_status_t abstract_or_term(abs_builder_t *b, composite_term_t *desc, bool_node_id_t *out) {
19✔
563
  ivector_t child;
564
  uint32_t i, n;
565
  bool_node_id_t c;
566
  abs_status_t st;
567

568
  n = desc->arity;
19✔
569
  init_ivector(&child, n);
19✔
570
  for (i = 0; i < n; i++) {
57✔
571
    st = abstract_signed(b, desc->arg[i], &c);
38✔
572
    if (st != ABS_OK) {
38✔
NEW
573
      delete_ivector(&child);
×
NEW
574
      return st;
×
575
    }
576
    ivector_push(&child, c);
38✔
577
  }
578
  st = bool_dag_mk_or(b, &child, out);
19✔
579
  delete_ivector(&child);
19✔
580
  return st;
19✔
581
}
582

583
static abs_status_t abstract_and_of_opposites(abs_builder_t *b, composite_term_t *desc, bool_node_id_t *out) {
8✔
584
  ivector_t child;
585
  uint32_t i, n;
586
  bool_node_id_t c;
587
  abs_status_t st;
588

589
  n = desc->arity;
8✔
590
  init_ivector(&child, n);
8✔
591
  for (i = 0; i < n; i++) {
33✔
592
    st = abstract_signed(b, opposite_term(desc->arg[i]), &c);
25✔
593
    if (st != ABS_OK) {
25✔
NEW
594
      delete_ivector(&child);
×
NEW
595
      return st;
×
596
    }
597
    ivector_push(&child, c);
25✔
598
    if (bool_node_is_false(c)) {
25✔
NEW
599
      break;
×
600
    }
601
  }
602
  st = bool_dag_mk_and(b, &child, out);
8✔
603
  delete_ivector(&child);
8✔
604
  return st;
8✔
605
}
606

NEW
607
static abs_status_t abstract_and2(abs_builder_t *b, term_t a, term_t c, bool_node_id_t *out) {
×
608
  ivector_t child;
609
  bool_node_id_t x;
610
  abs_status_t st;
611

NEW
612
  init_ivector(&child, 2);
×
NEW
613
  st = abstract_signed(b, a, &x);
×
NEW
614
  if (st != ABS_OK) goto done;
×
NEW
615
  ivector_push(&child, x);
×
NEW
616
  if (! bool_node_is_false(x)) {
×
NEW
617
    st = abstract_signed(b, c, &x);
×
NEW
618
    if (st != ABS_OK) goto done;
×
NEW
619
    ivector_push(&child, x);
×
620
  }
NEW
621
  st = bool_dag_mk_and(b, &child, out);
×
622

NEW
623
 done:
×
NEW
624
  delete_ivector(&child);
×
NEW
625
  return st;
×
626
}
627

NEW
628
static abs_status_t abstract_boolean_ite(abs_builder_t *b, term_t base, bool neg, bool_node_id_t *out) {
×
629
  composite_term_t *idesc;
630
  term_t cond, then_b, else_b;
631
  bool_node_id_t left, right;
632
  ivector_t child;
633
  abs_status_t st;
634

NEW
635
  idesc = ite_term_desc(b->terms, base);
×
NEW
636
  cond = idesc->arg[0];
×
NEW
637
  then_b = idesc->arg[1];
×
NEW
638
  else_b = idesc->arg[2];
×
639

NEW
640
  st = abstract_and2(b, cond, neg ? opposite_term(then_b) : then_b, &left);
×
NEW
641
  if (st != ABS_OK) return st;
×
642

NEW
643
  st = abstract_and2(b, opposite_term(cond), neg ? opposite_term(else_b) : else_b, &right);
×
NEW
644
  if (st != ABS_OK) return st;
×
645

NEW
646
  init_ivector(&child, 2);
×
NEW
647
  ivector_push(&child, left);
×
NEW
648
  ivector_push(&child, right);
×
NEW
649
  st = bool_dag_mk_or(b, &child, out);
×
NEW
650
  delete_ivector(&child);
×
NEW
651
  return st;
×
652
}
653

654
static abs_status_t abstract_leaf(abs_builder_t *b, term_t t, bool_node_id_t *out) {
42✔
655
  term_t atom;
656
  bvar_t v;
657
  literal_t lit;
658

659
  atom = unsigned_term(t);
42✔
660
  v = abs_builder_get_atom_var(b, atom);
42✔
661
  lit = is_neg_term(t) ? neg_lit(v) : pos_lit(v);
42✔
662
  *out = bool_dag_add_lit(&b->dag, lit);
42✔
663
  return ABS_OK;
42✔
664
}
665

666
static abs_eval_t eval_boolean_at_model(evaluator_t *eval, term_t t) {
71✔
667
  value_t v;
668

669
  v = eval_in_model(eval, t);
71✔
670
  if (! good_object(eval->vtbl, v) || ! object_is_boolean(eval->vtbl, v)) {
71✔
NEW
671
    return ABS_EVAL_ERROR;
×
672
  }
673
  return boolobj_value(eval->vtbl, v) ? ABS_EVAL_TRUE : ABS_EVAL_FALSE;
71✔
674
}
675

676
static abs_status_t abstract_signed(abs_builder_t *b, term_t t, bool_node_id_t *out) {
72✔
677
  int_hmap_pair_t *r;
678
  term_t base;
679
  bool neg;
680
  term_kind_t kind;
681
  uint32_t saved_dag_size, saved_children_size;
682
  abs_eval_t eval;
683
  abs_status_t st;
684

685
  r = int_hmap_find(&b->cache_signed, t);
72✔
686
  if (r != NULL) {
72✔
687
    *out = r->val;
1✔
688
    return ABS_OK;
1✔
689
  }
690

691
  if (t == true_term) {
71✔
NEW
692
    *out = BOOL_DAG_TRUE;
×
NEW
693
    goto cache_result;
×
694
  }
695
  if (t == false_term) {
71✔
NEW
696
    *out = BOOL_DAG_FALSE;
×
NEW
697
    goto cache_result;
×
698
  }
699

700
  eval = eval_boolean_at_model(b->eval, t);
71✔
701
  if (eval == ABS_EVAL_ERROR) {
71✔
NEW
702
    return ABS_ERROR;
×
703
  }
704
  if (eval == ABS_EVAL_FALSE) {
71✔
705
    *out = BOOL_DAG_FALSE;
2✔
706
  } else {
707
    saved_dag_size = b->dag.size;
69✔
708
    saved_children_size = b->dag.children.size;
69✔
709

710
    base = unsigned_term(t);
69✔
711
    neg = is_neg_term(t);
69✔
712
    kind = term_kind(b->terms, base);
69✔
713

714
    if (kind == OR_TERM) {
69✔
715
      b->decomposed = true;
27✔
716
      if (neg) {
27✔
717
        st = abstract_and_of_opposites(b, or_term_desc(b->terms, base), out);
8✔
718
      } else {
719
        st = abstract_or_term(b, or_term_desc(b->terms, base), out);
19✔
720
      }
721
      if (st != ABS_OK) goto error;
27✔
722
      goto cache_result;
27✔
723
    }
724

725
    if ((kind == ITE_TERM || kind == ITE_SPECIAL) && is_boolean_term(b->terms, base)) {
42✔
NEW
726
      b->decomposed = true;
×
NEW
727
      st = abstract_boolean_ite(b, base, neg, out);
×
NEW
728
      if (st != ABS_OK) goto error;
×
NEW
729
      goto cache_result;
×
730
    }
731

732
    (void) abstract_leaf(b, t, out);
42✔
733
  }
734

735
 cache_result:
71✔
736
  int_hmap_add(&b->cache_signed, t, *out);
71✔
737
  return ABS_OK;
71✔
738

NEW
739
 error:
×
740
  // Roll back only the DAG; successful subcalls may have populated caches.
741
  // The caller must discard this builder after ABS_ERROR.
NEW
742
  bool_dag_rollback(&b->dag, saved_dag_size, saved_children_size);
×
NEW
743
  return st;
×
744
}
745

746
static abs_status_t abstract_formula_array(abs_builder_t *b, uint32_t n, const term_t f[], bool_node_id_t *out) {
8✔
747
  ivector_t child;
748
  uint32_t i;
749
  bool_node_id_t c;
750
  abs_status_t st;
751

752
  init_ivector(&child, n);
8✔
753
  for (i = 0; i < n; i++) {
17✔
754
    st = abstract_signed(b, f[i], &c);
9✔
755
    if (st != ABS_OK) {
9✔
NEW
756
      delete_ivector(&child);
×
NEW
757
      return st;
×
758
    }
759
    ivector_push(&child, c);
9✔
760
    if (bool_node_is_false(c)) {
9✔
NEW
761
      break;
×
762
    }
763
  }
764
  st = bool_dag_mk_and(b, &child, out);
8✔
765
  delete_ivector(&child);
8✔
766
  return st;
8✔
767
}
768

769
static void sat_add_clause(sat_solver_t *sat, uint32_t n, literal_t *lit) {
1,127✔
770
  nsat_solver_simplify_and_add_clause(sat, n, lit);
1,127✔
771
}
1,127✔
772

773
static void sat_add_unit_clause(sat_solver_t *sat, literal_t l) {
7✔
774
  literal_t clause[1];
775

776
  clause[0] = l;
7✔
777
  sat_add_clause(sat, 1, clause);
7✔
778
}
7✔
779

780
static void sat_add_binary_clause(sat_solver_t *sat, literal_t l1, literal_t l2) {
59✔
781
  literal_t clause[2];
782

783
  clause[0] = l1;
59✔
784
  clause[1] = l2;
59✔
785
  sat_add_clause(sat, 2, clause);
59✔
786
}
59✔
787

788
static literal_t clausify_node(bool_dag_t *dag, sat_solver_t *sat, bool_node_id_t id) {
66✔
789
  bool_node_t *node;
790
  literal_t result, child_lit;
791
  ivector_t clause;
792
  uint32_t i;
793

794
  assert(0 <= id && (uint32_t) id < dag->size);
795
  node = &dag->data[id];
66✔
796
  if (node->tseitin != null_literal) {
66✔
797
    return node->tseitin;
1✔
798
  }
799

800
  switch (node->kind) {
65✔
801
  case BOOL_NODE_LIT:
40✔
802
    result = node->lit;
40✔
803
    break;
40✔
804

805
  case BOOL_NODE_AND:
8✔
806
    result = pos_lit(nsat_solver_new_var(sat));
8✔
807
    init_ivector(&clause, node->count + 1);
8✔
808
    ivector_push(&clause, result);
8✔
809
    for (i = 0; i < node->count; i++) {
33✔
810
      child_lit = clausify_node(dag, sat, dag->children.data[node->start + i]);
25✔
811
      sat_add_binary_clause(sat, not(result), child_lit);
25✔
812
      ivector_push(&clause, not(child_lit));
25✔
813
    }
814
    sat_add_clause(sat, clause.size, clause.data);
8✔
815
    delete_ivector(&clause);
8✔
816
    break;
8✔
817

818
  case BOOL_NODE_OR:
17✔
819
    result = pos_lit(nsat_solver_new_var(sat));
17✔
820
    init_ivector(&clause, node->count + 1);
17✔
821
    ivector_push(&clause, not(result));
17✔
822
    for (i = 0; i < node->count; i++) {
51✔
823
      child_lit = clausify_node(dag, sat, dag->children.data[node->start + i]);
34✔
824
      sat_add_binary_clause(sat, not(child_lit), result);
34✔
825
      ivector_push(&clause, child_lit);
34✔
826
    }
827
    sat_add_clause(sat, clause.size, clause.data);
17✔
828
    delete_ivector(&clause);
17✔
829
    break;
17✔
830

NEW
831
  default:
×
832
    assert(false);
NEW
833
    result = null_literal;
×
NEW
834
    break;
×
835
  }
836

837
  node->tseitin = result;
65✔
838
  return result;
65✔
839
}
840

841
static void bool_dag_reset_tseitin(bool_dag_t *dag) {
7✔
842
  uint32_t i;
843

844
  for (i = 0; i < dag->size; i++) {
92✔
845
    dag->data[i].tseitin = null_literal;
85✔
846
  }
847
}
7✔
848

849
static bool sat_literal_true(sat_solver_t *sat, literal_t lit) {
16,410✔
850
  return lit_is_true(sat, lit);
16,410✔
851
}
852

853
static bool sat_node_true(bool_dag_t *dag, sat_solver_t *sat, bool_node_id_t id) {
16,413✔
854
  bool_node_t *node;
855
  uint32_t i;
856

857
  node = &dag->data[id];
16,413✔
858
  switch (node->kind) {
16,413✔
NEW
859
  case BOOL_NODE_TRUE:
×
NEW
860
    return true;
×
NEW
861
  case BOOL_NODE_FALSE:
×
NEW
862
    return false;
×
863
  case BOOL_NODE_LIT:
16,410✔
864
    return sat_literal_true(sat, node->lit);
16,410✔
865
  case BOOL_NODE_AND:
3✔
866
    for (i = 0; i < node->count; i++) {
7✔
867
      if (! sat_node_true(dag, sat, dag->children.data[node->start + i])) {
5✔
868
        return false;
1✔
869
      }
870
    }
871
    return true;
2✔
NEW
872
  case BOOL_NODE_OR:
×
NEW
873
    for (i = 0; i < node->count; i++) {
×
NEW
874
      if (sat_node_true(dag, sat, dag->children.data[node->start + i])) {
×
NEW
875
        return true;
×
876
      }
877
    }
NEW
878
    return false;
×
NEW
879
  default:
×
880
    assert(false);
NEW
881
    return false;
×
882
  }
883
}
884

885
static void extract_implicant(bool_dag_t *dag, sat_solver_t *sat, bool_node_id_t id, ivector_t *implicant) {
23,604✔
886
  bool_node_t *node;
887
  uint32_t i;
888
  bool_node_id_t child;
889

890
  node = &dag->data[id];
23,604✔
891
  switch (node->kind) {
23,604✔
NEW
892
  case BOOL_NODE_TRUE:
×
NEW
893
    break;
×
894
  case BOOL_NODE_LIT:
11,288✔
895
    assert(sat_literal_true(sat, node->lit));
896
    ivector_push_unique(implicant, node->lit);
11,288✔
897
    break;
11,288✔
898
  case BOOL_NODE_AND:
1,036✔
899
    for (i = 0; i < node->count; i++) {
12,324✔
900
      extract_implicant(dag, sat, dag->children.data[node->start + i], implicant);
11,288✔
901
    }
902
    break;
1,036✔
903
  case BOOL_NODE_OR:
11,280✔
904
    for (i = 0; i < node->count; i++) {
16,408✔
905
      child = dag->children.data[node->start + i];
16,408✔
906
      if (sat_node_true(dag, sat, child)) {
16,408✔
907
        extract_implicant(dag, sat, child, implicant);
11,280✔
908
        break;
11,280✔
909
      }
910
    }
911
    break;
11,280✔
NEW
912
  default:
×
913
    assert(false);
NEW
914
    break;
×
915
  }
916
}
23,604✔
917

918
static void implicant_to_cube(abs_builder_t *b, const ivector_t *implicant, ivector_t *cube) {
1,036✔
919
  uint32_t i, n;
920
  literal_t lit;
921
  term_t atom;
922

923
  ivector_reset(cube);
1,036✔
924
  n = implicant->size;
1,036✔
925
  for (i = 0; i < n; i++) {
12,323✔
926
    lit = implicant->data[i];
11,287✔
927
    assert(var_of(lit) > const_bvar);
928
    atom = b->bvar_to_atom.data[var_of(lit)];
11,287✔
929
    ivector_push(cube, is_pos(lit) ? atom : opposite_term(atom));
11,287✔
930
  }
931
}
1,036✔
932

933
static void add_blocker_clause_to_sat(sat_solver_t *sat, const ivector_t *implicant) {
1,036✔
934
  ivector_t clause;
935
  uint32_t i, n;
936

937
  init_ivector(&clause, implicant->size);
1,036✔
938
  n = implicant->size;
1,036✔
939
  for (i = 0; i < n; i++) {
12,323✔
940
    ivector_push(&clause, not(implicant->data[i]));
11,287✔
941
  }
942
  sat_add_clause(sat, clause.size, clause.data);
1,036✔
943
  delete_ivector(&clause);
1,036✔
944
}
1,036✔
945

946
static term_t make_projected_cubes_term(term_manager_t *mngr, bool multiple,
1✔
947
                                        const ivector_t *first_projected, const ivector_t *cube_terms) {
948
  if (! multiple) {
1✔
NEW
949
    return mk_and_safe(mngr, first_projected->size, first_projected->data);
×
950
  }
951
  return mk_or_safe(mngr, cube_terms->size, cube_terms->data);
1✔
952
}
953

954
static int32_t append_projected_cube_term(model_t *mdl, term_manager_t *mngr,
1,036✔
955
                                          uint32_t nelims, const term_t elim[],
956
                                          ivector_t *cube, int32_t *extra_error,
957
                                          bool *have_first, bool *multiple,
958
                                          ivector_t *first_projected, ivector_t *cube_terms) {
959
  ivector_t projected;
960
  term_t cube_term;
961
  int32_t code;
962

963
  init_ivector(&projected, 4);
1,036✔
964
  code = project_one_cube_into(mdl, mngr, cube->data, cube->size, nelims, elim, &projected, extra_error);
1,036✔
965
  if (code != 0) {
1,036✔
NEW
966
    delete_ivector(&projected);
×
NEW
967
    return code;
×
968
  }
969

970
  if (! *have_first) {
1,036✔
971
    ivector_reset(first_projected);
7✔
972
    ivector_add(first_projected, projected.data, projected.size);
7✔
973
    *have_first = true;
7✔
974
  } else {
975
    if (! *multiple) {
1,029✔
976
      cube_term = mk_and_safe(mngr, first_projected->size, first_projected->data);
5✔
977
      if (cube_term == NULL_TERM) {
5✔
NEW
978
        delete_ivector(&projected);
×
NEW
979
        return GEN_EVAL_INTERNAL_ERROR;
×
980
      }
981
      ivector_push(cube_terms, cube_term);
5✔
982
      *multiple = true;
5✔
983
    }
984
    cube_term = mk_and_safe(mngr, projected.size, projected.data);
1,029✔
985
    if (cube_term == NULL_TERM) {
1,029✔
NEW
986
      delete_ivector(&projected);
×
NEW
987
      return GEN_EVAL_INTERNAL_ERROR;
×
988
    }
989
    ivector_push(cube_terms, cube_term);
1,029✔
990
  }
991

992
  delete_ivector(&projected);
1,036✔
993
  return 0;
1,036✔
994
}
995

996
static int32_t gen_model_by_proj_sat_guided(model_t *mdl, term_manager_t *mngr,
8✔
997
                                            uint32_t nelims, const term_t elim[],
998
                                            ivector_t *v, uint32_t cube_budget,
999
                                            int32_t *extra_error) {
1000
  evaluator_t eval;
1001
  abs_builder_t builder;
1002
  sat_solver_t sat;
1003
  ivector_t input, implicant, cube;
1004
  ivector_t first_projected, cube_terms, local;
1005
  bool_node_id_t root;
1006
  literal_t root_lit;
1007
  solver_status_t sat_status;
1008
  uint32_t num_cubes;
1009
  int32_t code;
1010
  bool builder_inited;
1011
  bool sat_inited;
1012
  bool have_first, multiple;
1013
  bool needs_fallback, exhausted_sat;
1014
  term_t collected, local_term, result_terms[2];
1015

1016
  init_ivector(&input, v->size);
8✔
1017
  ivector_add(&input, v->data, v->size);
8✔
1018
  ivector_reset(v);
8✔
1019

1020
  init_evaluator(&eval, mdl);
8✔
1021
  init_abs_builder(&builder, mdl, mngr, &eval);
8✔
1022
  builder_inited = true;
8✔
1023
  sat_inited = false;
8✔
1024
  code = 0;
8✔
1025

1026
  init_ivector(&implicant, 16);
8✔
1027
  init_ivector(&cube, 16);
8✔
1028
  init_ivector(&first_projected, 8);
8✔
1029
  init_ivector(&cube_terms, 8);
8✔
1030
  init_ivector(&local, 8);
8✔
1031
  have_first = false;
8✔
1032
  multiple = false;
8✔
1033
  needs_fallback = false;
8✔
1034
  exhausted_sat = false;
8✔
1035

1036
  if (input.size == 0) {
8✔
NEW
1037
    ivector_push(v, true_term);
×
NEW
1038
    goto cleanup;
×
1039
  }
1040

1041
  if (abstract_formula_array(&builder, input.size, input.data, &root) != ABS_OK) {
8✔
1042
    // ABS_ERROR means eval_boolean_at_model could not produce a Boolean
1043
    // value for a Boolean subterm of F. That is a precondition violation
1044
    // (the API requires F to be true at the model, hence model-evaluable).
1045
    // We delegate to gen_model_by_proj_local rather than asserting: the
1046
    // legacy pipeline runs the same model evaluator inside get_implicant
1047
    // and will surface the specific GEN_EVAL_* / MDL_EVAL_* error code,
1048
    // giving the caller a precise diagnostic instead of an abort.
NEW
1049
    ivector_reset(v);
×
NEW
1050
    ivector_add(v, input.data, input.size);
×
NEW
1051
    code = gen_model_by_proj_local(mdl, mngr, nelims, elim, v, extra_error);
×
NEW
1052
    goto cleanup;
×
1053
  }
1054

1055
  if (bool_node_is_false(root)) {
8✔
NEW
1056
    code = MDL_EVAL_FORMULA_FALSE;
×
NEW
1057
    goto cleanup;
×
1058
  }
1059

1060
  if (bool_node_is_true(root)) {
8✔
NEW
1061
    ivector_push(v, true_term);
×
NEW
1062
    goto cleanup;
×
1063
  }
1064

1065
  if (! builder.decomposed) {
8✔
1066
    ivector_reset(v);
1✔
1067
    ivector_add(v, input.data, input.size);
1✔
1068
    code = gen_model_by_proj_local(mdl, mngr, nelims, elim, v, extra_error);
1✔
1069
    goto cleanup;
1✔
1070
  }
1071

1072
  num_cubes = 0;
7✔
1073
  init_nsat_solver(&sat, builder.bvar_to_atom.size + builder.dag.size + 8, false);
7✔
1074
  sat_inited = true;
7✔
1075
  nsat_solver_add_vars(&sat, builder.bvar_to_atom.size - 1);
7✔
1076
  bool_dag_reset_tseitin(&builder.dag);
7✔
1077
  root_lit = clausify_node(&builder.dag, &sat, root);
7✔
1078
  sat_add_unit_clause(&sat, root_lit);
7✔
1079
  while (num_cubes < cube_budget) {
1,043✔
1080
    sat_status = nsat_solve(&sat);
1,042✔
1081
    if (sat_status == STAT_UNSAT) {
1,042✔
1082
      exhausted_sat = true;
6✔
1083
      break;
6✔
1084
    }
1085
    // nsat_solve is documented to return only STAT_SAT or STAT_UNSAT
1086
    // (see solvers/cdcl/new_sat_solver.h); anything else is a bug.
1087
    assert(sat_status == STAT_SAT);
1088

1089
    ivector_reset(&implicant);
1,036✔
1090
    extract_implicant(&builder.dag, &sat, root, &implicant);
1,036✔
1091

1092
    implicant_to_cube(&builder, &implicant, &cube);
1,036✔
1093
    code = append_projected_cube_term(mdl, mngr, nelims, elim, &cube, extra_error,
1,036✔
1094
                                      &have_first, &multiple, &first_projected, &cube_terms);
1095
    if (code != 0) {
1,036✔
1096
      // Projection errors are reachable on legitimate inputs: the
1097
      // projector can return GEN_PROJ_ERROR_NON_LINEAR,
1098
      // GEN_PROJ_ERROR_UNSUPPORTED_ARITH_TERM, etc. when the chosen
1099
      // cube contains arithmetic outside what Loos-Weispfenning /
1100
      // Cooper can handle. Fall back to OR(collected, local) below,
1101
      // which preserves soundness and lets the legacy path produce
1102
      // a definitive error code if it also fails.
NEW
1103
      needs_fallback = true;
×
NEW
1104
      code = 0;
×
NEW
1105
      break;
×
1106
    }
1107

1108
    num_cubes ++;
1,036✔
1109
    if (implicant.size == 0) {
1,036✔
NEW
1110
      exhausted_sat = true;
×
NEW
1111
      break;
×
1112
    }
1113
    // Must backtrack away from the SAT model before adding the blocker:
1114
    // otherwise y2sat simplifies all blocker literals to false and creates
1115
    // a spurious empty clause.
1116
    nsat_solver_prepare_for_next_search(&sat);
1,036✔
1117
    add_blocker_clause_to_sat(&sat, &implicant);
1,036✔
1118
  }
1119

1120
  if (! exhausted_sat) {
7✔
1121
    needs_fallback = true;
1✔
1122
  }
1123

1124
  if (needs_fallback) {
7✔
1125
    ivector_reset(&local);
1✔
1126
    ivector_add(&local, input.data, input.size);
1✔
1127
    code = gen_model_by_proj_local(mdl, mngr, nelims, elim, &local, extra_error);
1✔
1128
    if (code != 0) {
1✔
NEW
1129
      goto cleanup;
×
1130
    }
1131
    if (! have_first) {
1✔
NEW
1132
      ivector_reset(v);
×
NEW
1133
      ivector_add(v, local.data, local.size);
×
1134
    } else {
1135
      collected = make_projected_cubes_term(mngr, multiple, &first_projected, &cube_terms);
1✔
1136
      local_term = mk_and_safe(mngr, local.size, local.data);
1✔
1137
      ivector_reset(v);
1✔
1138
      if (collected == NULL_TERM || local_term == NULL_TERM) {
1✔
NEW
1139
        ivector_add(v, local.data, local.size);
×
1140
      } else {
1141
        result_terms[0] = collected;
1✔
1142
        result_terms[1] = local_term;
1✔
1143
        collected = mk_or_safe(mngr, 2, result_terms);
1✔
1144
        if (collected == NULL_TERM) {
1✔
NEW
1145
          ivector_add(v, local.data, local.size);
×
1146
        } else {
1147
          ivector_push(v, collected);
1✔
1148
        }
1149
      }
1150
    }
1151
    goto cleanup;
1✔
1152
  }
1153

1154
  if (! have_first) {
6✔
NEW
1155
    ivector_push(v, true_term);
×
1156
  } else if (! multiple) {
6✔
1157
    ivector_add(v, first_projected.data, first_projected.size);
2✔
1158
  } else {
1159
    collected = mk_or_safe(mngr, cube_terms.size, cube_terms.data);
4✔
1160
    if (collected == NULL_TERM) {
4✔
NEW
1161
      ivector_reset(&local);
×
NEW
1162
      ivector_add(&local, input.data, input.size);
×
NEW
1163
      code = gen_model_by_proj_local(mdl, mngr, nelims, elim, &local, extra_error);
×
NEW
1164
      if (code == 0) {
×
NEW
1165
        ivector_add(v, local.data, local.size);
×
1166
      }
1167
    } else {
1168
      ivector_push(v, collected);
4✔
1169
    }
1170
  }
1171

1172
 cleanup:
8✔
1173
  delete_ivector(&local);
8✔
1174
  delete_ivector(&cube_terms);
8✔
1175
  delete_ivector(&first_projected);
8✔
1176
  delete_ivector(&cube);
8✔
1177
  delete_ivector(&implicant);
8✔
1178
  if (sat_inited) delete_nsat_solver(&sat);
8✔
1179
  if (builder_inited) delete_abs_builder(&builder);
8✔
1180
  delete_evaluator(&eval);
8✔
1181
  delete_ivector(&input);
8✔
1182
  return code;
8✔
1183
}
1184

1185

1186

1187
/*
1188
 * Generalization by substitution
1189
 * - mdl = model
1190
 * - mngr = relevant term manager
1191
 * - f[0 ... n-1] = formula true in the model
1192
 * - elim[0 ... nelim -1] = variables to eliminate
1193
 * - v = result vector
1194
 *
1195
 * - returned code: 0 if no error, an error code otherwise
1196
 * - error codes are listed in generalization.h
1197
 */
NEW
1198
int32_t gen_model_by_substitution(model_t *mdl, term_manager_t *mngr, uint32_t n, const term_t f[], 
×
1199
                                  uint32_t nelims, const term_t elim[], ivector_t *v) {
1200

NEW
1201
  ivector_copy(v, f, n);
×
1202
  assert(v->size == n);
NEW
1203
  return gen_model_by_subst(mdl, mngr, nelims, elim, v);
×
1204
}
1205

1206

1207
/*
1208
 * Generalize by projection (wide variant, the public default).
1209
 *
1210
 * Walks the Boolean structure of f[], enumerates model-true Boolean
1211
 * implicants via a SAT-guided loop, projects each implicant as a cube
1212
 * through the legacy implicant+project pipeline, and unions the results
1213
 * at the term level. Wider output than gen_model_by_projection_local;
1214
 * recommended for CEGAR-style outer loops over quantifier prefixes.
1215
 *
1216
 * cube_budget caps the number of SAT-guided cubes the enumeration is
1217
 * allowed to emit before falling back to OR(collected, local). Pass 0
1218
 * to use the built-in default (WIDE_CUBE_BUDGET).
1219
 */
1220
int32_t gen_model_by_projection(model_t *mdl, term_manager_t *mngr, uint32_t n, const term_t f[],
8✔
1221
                                uint32_t nelims, const term_t elim[], ivector_t *v,
1222
                                uint32_t cube_budget, int32_t *extra_error) {
1223
  ivector_copy(v, f, n);
8✔
1224
  assert(v->size == n);
1225
  if (cube_budget == 0) cube_budget = WIDE_CUBE_BUDGET;
8✔
1226
  return gen_model_by_proj_sat_guided(mdl, mngr, nelims, elim, v, cube_budget, extra_error);
8✔
1227
}
1228

1229

1230
/*
1231
 * Generalize by projection (legacy local pipeline).
1232
 *
1233
 * Builds a single literal implicant of f[] at the model and projects
1234
 * that flat conjunction. The output is sign-invariant for the chosen
1235
 * implicant. Cheaper per call than the wide variant but commits to one
1236
 * disjunct when f[] has Boolean structure.
1237
 */
1238
int32_t gen_model_by_projection_local(model_t *mdl, term_manager_t *mngr, uint32_t n, const term_t f[],
8✔
1239
                                      uint32_t nelims, const term_t elim[], ivector_t *v, int32_t *extra_error) {
1240
  ivector_copy(v, f, n);
8✔
1241
  assert(v->size == n);
1242
  return gen_model_by_proj_local(mdl, mngr, nelims, elim, v, extra_error);
8✔
1243
}
1244

1245

1246

1247
/*
1248
 * Generalize mdl: two passes:
1249
 * - 1) eliminate the discrete variables by substitution
1250
 * - 2) use projection (wide variant) to eliminate the real variables
1251
 *
1252
 * cube_budget is the SAT-guided cube cap for pass 2 (pass 0 to use
1253
 * the built-in default).
1254
 */
NEW
1255
int32_t generalize_model(model_t *mdl, term_manager_t *mngr, uint32_t n, const term_t f[],
×
1256
                         uint32_t nelims, const term_t elim[], ivector_t *v,
1257
                         uint32_t cube_budget, int32_t *extra_error) {
1258
  term_table_t *terms;
1259
  ivector_t discretes;
1260
  ivector_t reals;
1261
  int32_t code;
1262

NEW
1263
  if (cube_budget == 0) cube_budget = WIDE_CUBE_BUDGET;
×
1264

1265
  // if n == 0, there's nothing to do
NEW
1266
  code = 0;
×
NEW
1267
  if (n > 0) {
×
NEW
1268
    terms = term_manager_get_terms(mngr);
×
NEW
1269
    init_ivector(&reals, 10);
×
NEW
1270
    init_ivector(&discretes, 10);
×
NEW
1271
    split_elim_array(terms, nelims, elim, &reals, &discretes);
×
1272
   
NEW
1273
    ivector_copy(v, f, n);
×
NEW
1274
    if (discretes.size > 0) {
×
NEW
1275
      code = gen_model_by_subst(mdl, mngr, discretes.size, discretes.data, v);
×
1276
    }
NEW
1277
    if (code == 0 && reals.size > 0) {
×
NEW
1278
      code = gen_model_by_proj_sat_guided(mdl, mngr, reals.size, reals.data, v, cube_budget, extra_error);
×
1279
    }
1280

NEW
1281
    delete_ivector(&reals);
×
UNCOV
1282
    delete_ivector(&discretes);
×
1283
  }
1284

UNCOV
1285
  return code;
×
1286
}
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