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

SRI-CSL / yices2 / 25368527405

05 May 2026 09:28AM UTC coverage: 66.87% (+0.2%) from 66.687%
25368527405

Pull #611

github

disteph
Merge branch 'master' into mcsat-supplement-cdclt

Conflicts resolved with a hybrid of both sides:

- tests/regress/run_test.sh: keep this branch's explicit --dpllt for
  the non-mcsat side of /both/ tests (symmetric with --mcsat), because
  yices' default solver path is heuristically chosen and is not
  guaranteed to be DPLL(T) for every logic. Also adopt master's new
  per-mode .mcsat.gold / .dpllt.gold override mechanism so tests that
  intentionally differ between the two solvers can supply separate
  gold files.

- tests/regress/both/README.md: document the symmetric --mcsat /
  --dpllt pair and the per-mode gold-override convention in one place.
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.

84342 of 126128 relevant lines covered (66.87%)

1634859.8 hits per line

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

70.42
/src/solvers/mcsat_satellite.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 <assert.h>
20
#include "context/context.h"
21
#include "model/models.h"
22
#include "solvers/egraph/composites.h"
23
#include "solvers/egraph/egraph.h"
24
#include "solvers/mcsat_satellite.h"
25
#include "terms/term_explorer.h"
26
#include "terms/term_manager.h"
27
#include "terms/term_substitution.h"
28
#include "utils/int_hash_sets.h"
29
#include "utils/memalloc.h"
30
#include "utils/pair_hash_map2.h"
31

32

33
typedef struct mcsat_atom_object_s {
34
  uint32_t id;
35
} mcsat_atom_object_t;
36

37
typedef struct mcsat_atom_entry_s {
38
  term_t atom;
39
  literal_t lit;
40
  term_t pos_label;
41
  term_t neg_label;
42
  mcsat_atom_object_t *obj;
43
  bool active;
44
} mcsat_atom_entry_t;
45

46
typedef struct mcsat_eq_entry_s {
47
  term_t label;
48
  literal_t source_lit;
49
} mcsat_eq_entry_t;
50

51
struct mcsat_satellite_s {
52
  context_t *ctx;
53
  smt_core_t *core;
54
  egraph_t *egraph;
55

56
  context_t mctx;
57
  term_manager_t tm;
58

59
  param_t params;
60
  bool check_in_propagate;
61

62
  int32_t internal_error;
63

64
  bool cache_valid;
65
  uint64_t cache_signature;
66

67
  mcsat_atom_entry_t *atoms;
68
  uint32_t num_atoms;
69
  uint32_t atom_size;
70
  uint32_t push_depth;
71
  ivector_t atom_push_mark;
72

73
  int_hmap_t arith_var_to_term;
74

75
  mcsat_eq_entry_t *eq;
76
  uint32_t num_eq;
77
  uint32_t eq_size;
78
  uint32_t dlevel;
79
  pmap2_t eq_active;
80
  ivector_t eq_level_mark;
81

82
  ivector_t assumptions;
83
  ivector_t assumption_lits;
84
  int_hmap_t label_to_lit;
85
  ivector_t conflict;
86
};
87

88

89
/*
90
 * Atom/generic vector growth.
91
 */
92
static void mcsat_satellite_extend_atoms(mcsat_satellite_t *sat) {
16✔
93
  uint32_t n;
94
  assert(sat->num_atoms == sat->atom_size);
95
  n = sat->atom_size + 1;
16✔
96
  n += n >> 1;
16✔
97
  sat->atoms = (mcsat_atom_entry_t *) safe_realloc(sat->atoms, n * sizeof(mcsat_atom_entry_t));
16✔
98
  sat->atom_size = n;
16✔
99
}
16✔
100

NEW
101
static void mcsat_satellite_extend_eq(mcsat_satellite_t *sat) {
×
102
  uint32_t n;
103
  assert(sat->num_eq == sat->eq_size);
NEW
104
  n = sat->eq_size + 1;
×
NEW
105
  n += n >> 1;
×
NEW
106
  sat->eq = (mcsat_eq_entry_t *) safe_realloc(sat->eq, n * sizeof(mcsat_eq_entry_t));
×
NEW
107
  sat->eq_size = n;
×
NEW
108
}
×
109

110
/*
111
 * Hash helper for assignment signatures.
112
 */
113
static inline uint64_t sig_mix(uint64_t h, uint32_t x) {
99✔
114
  h ^= x;
99✔
115
  h *= UINT64_C(1099511628211);
99✔
116
  return h;
99✔
117
}
118

119
static uint64_t mcsat_satellite_signature(const mcsat_satellite_t *sat) {
43✔
120
  uint64_t h;
121
  uint32_t i, n;
122

123
  h = UINT64_C(1469598103934665603);
43✔
124
  n = sat->assumptions.size;
43✔
125
  for (i = 0; i < n; i++) {
99✔
126
    h = sig_mix(h, (uint32_t) sat->assumptions.data[i]);
56✔
127
  }
128
  return sig_mix(h, n);
43✔
129
}
130

131
/*
132
 * Ensure mctx is ready for assertion.
133
 */
134
static void mcsat_satellite_prepare_assertion_state(mcsat_satellite_t *sat) {
62✔
135
  smt_status_t status;
136
  status = mcsat_status(sat->mctx.mcsat);
62✔
137
  if (status != YICES_STATUS_IDLE) {
62✔
138
    mcsat_clear(sat->mctx.mcsat);
7✔
139
  }
140
}
62✔
141

142
/*
143
 * Assert a formula in the internal MCSAT context.
144
 */
145
static int32_t mcsat_satellite_assert_formula(mcsat_satellite_t *sat, term_t f) {
62✔
146
  int32_t code;
147

148
  mcsat_satellite_prepare_assertion_state(sat);
62✔
149
  code = mcsat_assert_formulas(sat->mctx.mcsat, 1, &f);
62✔
150
  if (code < 0) {
62✔
NEW
151
    sat->internal_error = code;
×
152
  }
153
  return code;
62✔
154
}
155

156
/*
157
 * Track one assumption.
158
 */
159
static inline void mcsat_satellite_add_assumption(mcsat_satellite_t *sat, term_t label, literal_t lit) {
61✔
160
  int_hmap_pair_t *p;
161

162
  ivector_push(&sat->assumptions, label);
61✔
163
  ivector_push(&sat->assumption_lits, lit);
61✔
164
  if (lit != null_literal) {
61✔
165
    p = int_hmap_get(&sat->label_to_lit, label);
61✔
166
    p->val = lit;
61✔
167
  }
168
}
61✔
169

170
/*
171
 * Build assumption lists from current assignment + active eq/diseq labels.
172
 */
173
static void mcsat_satellite_build_assumptions(mcsat_satellite_t *sat, bool complete_with_phase) {
46✔
174
  bval_t v;
175
  uint32_t i;
176

177
  ivector_reset(&sat->assumptions);
46✔
178
  ivector_reset(&sat->assumption_lits);
46✔
179
  int_hmap_reset(&sat->label_to_lit);
46✔
180

181
  for (i = 0; i < sat->num_atoms; i++) {
127✔
182
    if (!sat->atoms[i].active) {
81✔
183
      continue;
6✔
184
    }
185

186
    v = literal_value(sat->core, sat->atoms[i].lit);
75✔
187
    if (v == VAL_TRUE || (complete_with_phase && v == VAL_UNDEF_TRUE)) {
75✔
188
      mcsat_satellite_add_assumption(sat, sat->atoms[i].pos_label, sat->atoms[i].lit);
60✔
189
    } else if (v == VAL_FALSE || (complete_with_phase && v == VAL_UNDEF_FALSE)) {
15✔
190
      mcsat_satellite_add_assumption(sat, sat->atoms[i].neg_label, not(sat->atoms[i].lit));
1✔
191
    }
192
  }
193

194
  for (i = 0; i < sat->num_eq; i++) {
46✔
NEW
195
    mcsat_satellite_add_assumption(sat, sat->eq[i].label, sat->eq[i].source_lit);
×
196
  }
197

198
}
46✔
199

200
/*
201
 * Explore a term and collect all Boolean atoms in atoms.
202
 */
203
static void collect_boolean_atoms(mcsat_satellite_t *sat, term_t t, int_hset_t *atoms, int_hset_t *visited) {
13✔
204
  term_table_t *terms;
205
  uint32_t i, nchildren;
206

207
  if (t < 0) {
13✔
NEW
208
    t = not(t);
×
209
  }
210
  if (int_hset_member(visited, t)) {
13✔
NEW
211
    return;
×
212
  }
213
  int_hset_add(visited, t);
13✔
214

215
  terms = sat->mctx.terms;
13✔
216
  if (term_type(terms, t) == bool_type(terms->types)) {
13✔
217
    int_hset_add(atoms, t);
13✔
218
  }
219

220
  if (term_is_projection(terms, t)) {
13✔
NEW
221
    collect_boolean_atoms(sat, proj_term_arg(terms, t), atoms, visited);
×
222

223
  } else if (term_is_sum(terms, t)) {
13✔
NEW
224
    nchildren = term_num_children(terms, t);
×
NEW
225
    for (i=0; i<nchildren; i++) {
×
226
      term_t child;
227
      mpq_t q;
NEW
228
      mpq_init(q);
×
NEW
229
      sum_term_component(terms, t, i, q, &child);
×
NEW
230
      collect_boolean_atoms(sat, child, atoms, visited);
×
NEW
231
      mpq_clear(q);
×
232
    }
233

234
  } else if (term_is_bvsum(terms, t)) {
13✔
235
    int32_t *aux;
236
    uint32_t nbits;
237
    term_t child;
NEW
238
    nbits = term_bitsize(terms, t);
×
NEW
239
    aux = (int32_t *) safe_malloc(nbits * sizeof(int32_t));
×
NEW
240
    nchildren = term_num_children(terms, t);
×
NEW
241
    for (i=0; i<nchildren; i++) {
×
NEW
242
      bvsum_term_component(terms, t, i, aux, &child);
×
NEW
243
      collect_boolean_atoms(sat, child, atoms, visited);
×
244
    }
NEW
245
    safe_free(aux);
×
246

247
  } else if (term_is_product(terms, t)) {
13✔
248
    term_t child;
249
    uint32_t exp;
NEW
250
    nchildren = term_num_children(terms, t);
×
NEW
251
    for (i=0; i<nchildren; i++) {
×
NEW
252
      product_term_component(terms, t, i, &child, &exp);
×
NEW
253
      collect_boolean_atoms(sat, child, atoms, visited);
×
254
    }
255

256
  } else if (term_is_composite(terms, t)) {
13✔
257
    nchildren = term_num_children(terms, t);
6✔
258
    for (i=0; i<nchildren; i++) {
14✔
259
      collect_boolean_atoms(sat, term_child(terms, t, i), atoms, visited);
8✔
260
    }
261
  }
262
}
263

264
/*
265
 * Build a conflict clause from mcsat interpolant labels.
266
 * Fallback: use all current tracked assumptions.
267
 */
268
static void mcsat_satellite_record_conflict(mcsat_satellite_t *sat) {
5✔
269
  term_t interpolant;
270
  int_hset_t labels;
271
  int_hset_t visited;
272
  int_hset_t seen_lits;
273
  int_hmap_pair_t *p;
274
  literal_t l;
275
  uint32_t i;
276

277
  ivector_reset(&sat->conflict);
5✔
278
  init_int_hset(&labels, 0);
5✔
279
  init_int_hset(&visited, 0);
5✔
280
  init_int_hset(&seen_lits, 0);
5✔
281

282
  interpolant = mcsat_get_unsat_model_interpolant(sat->mctx.mcsat);
5✔
283
  if (interpolant != NULL_TERM) {
5✔
284
    collect_boolean_atoms(sat, interpolant, &labels, &visited);
5✔
285
    p = int_hmap_first_record(&sat->label_to_lit);
5✔
286
    while (p != NULL) {
16✔
287
      if (int_hset_member(&labels, p->key) && p->val != null_literal &&
11✔
288
          !int_hset_member(&seen_lits, p->val)) {
4✔
289
        int_hset_add(&seen_lits, p->val);
3✔
290
        ivector_push(&sat->conflict, not(p->val));
3✔
291
      }
292
      p = int_hmap_next_record(&sat->label_to_lit, p);
11✔
293
    }
294
  }
295

296
  if (sat->conflict.size == 0) {
5✔
297
    for (i = 0; i < sat->assumption_lits.size; i++) {
9✔
298
      l = sat->assumption_lits.data[i];
6✔
299
      if (l != null_literal && !int_hset_member(&seen_lits, l)) {
6✔
300
        int_hset_add(&seen_lits, l);
6✔
301
        ivector_push(&sat->conflict, not(l));
6✔
302
      }
303
    }
304
  }
305

306
  ivector_push(&sat->conflict, null_literal);
5✔
307
  record_theory_conflict(sat->core, sat->conflict.data);
5✔
308

309
  delete_int_hset(&seen_lits);
5✔
310
  delete_int_hset(&visited);
5✔
311
  delete_int_hset(&labels);
5✔
312
}
5✔
313

314
/*
315
 * Run one consistency check in the internal MCSAT context.
316
 */
317
static smt_status_t mcsat_satellite_check(mcsat_satellite_t *sat, bool force, bool emit_conflict) {
43✔
318
  model_t mdl;
319
  smt_status_t status;
320
  uint64_t sig;
321
  uint32_t i;
322
  value_t vtrue;
323

324
  if (sat->internal_error < 0) {
43✔
NEW
325
    if (emit_conflict) {
×
326
      literal_t c[1];
NEW
327
      c[0] = null_literal;
×
NEW
328
      record_theory_conflict(sat->core, c);
×
329
    }
NEW
330
    return YICES_STATUS_UNSAT;
×
331
  }
332

333
  mcsat_satellite_build_assumptions(sat, false);
43✔
334
  sig = mcsat_satellite_signature(sat);
43✔
335
  if (!force && sat->cache_valid && sat->cache_signature == sig) {
43✔
336
    return YICES_STATUS_SAT;
16✔
337
  }
338

339
  init_model(&mdl, sat->mctx.terms, true);
27✔
340
  vtrue = vtbl_mk_bool(&mdl.vtbl, true);
27✔
341
  for (i=0; i<sat->assumptions.size; i++) {
64✔
342
    if (model_find_term_value(&mdl, sat->assumptions.data[i]) == null_value) {
37✔
343
      model_map_term(&mdl, sat->assumptions.data[i], vtrue);
37✔
344
    }
345
  }
346

347
  mcsat_clear(sat->mctx.mcsat);
27✔
348
  mcsat_solve(sat->mctx.mcsat, &sat->params, &mdl, sat->assumptions.size, (const term_t *) sat->assumptions.data);
27✔
349
  status = mcsat_status(sat->mctx.mcsat);
27✔
350

351
  delete_model(&mdl);
27✔
352

353
  sat->cache_valid = false;
27✔
354
  if (status == YICES_STATUS_SAT) {
27✔
355
    sat->cache_valid = true;
22✔
356
    sat->cache_signature = sig;
22✔
357
  } else if (status == YICES_STATUS_UNSAT && emit_conflict) {
5✔
358
    mcsat_satellite_record_conflict(sat);
5✔
359
  }
360

361
  return status;
27✔
362
}
363

364
/*
365
 * Open one decision level in the eq/diseq activation map.
366
 */
367
static void mcsat_satellite_open_level(mcsat_satellite_t *sat) {
5✔
368
  pmap2_push(&sat->eq_active);
5✔
369
  sat->dlevel ++;
5✔
370
  if (sat->eq_level_mark.size <= sat->dlevel) {
5✔
371
    ivector_push(&sat->eq_level_mark, sat->num_eq);
4✔
372
  } else {
373
    sat->eq_level_mark.data[sat->dlevel] = sat->num_eq;
1✔
374
  }
375
  sat->cache_valid = false;
5✔
376
}
5✔
377

378
/*
379
 * Backtrack eq/diseq activation to target.
380
 */
381
static void mcsat_satellite_backtrack_to(mcsat_satellite_t *sat, uint32_t target) {
3✔
382
  while (sat->dlevel > target) {
6✔
383
    sat->num_eq = sat->eq_level_mark.data[sat->dlevel];
3✔
384
    pmap2_pop(&sat->eq_active);
3✔
385
    sat->dlevel --;
3✔
386
  }
387
  sat->cache_valid = false;
3✔
388
}
3✔
389

390
/*
391
 * Align the internal MCSAT scope stack with the outer context base level.
392
 * This is needed when the satellite is attached after one or more pushes.
393
 */
394
static void mcsat_satellite_sync_base_level(mcsat_satellite_t *sat, uint32_t base_level) {
10✔
395
  uint32_t i;
396

397
  for (i = 0; i < base_level; i++) {
11✔
398
    context_push(&sat->mctx);
1✔
399
    ivector_push(&sat->atom_push_mark, sat->num_atoms);
1✔
400
    sat->push_depth ++;
1✔
401
    mcsat_satellite_open_level(sat);
1✔
402
  }
403
}
10✔
404

405
/*
406
 * Derive a source literal from a disequality hint if possible.
407
 */
NEW
408
static literal_t source_lit_from_hint(mcsat_satellite_t *sat, composite_t *hint) {
×
409
  literal_t l;
410

NEW
411
  if (hint == NULL || sat->egraph == NULL) {
×
NEW
412
    return null_literal;
×
413
  }
414

NEW
415
  l = egraph_occ2literal(sat->egraph, pos_occ(hint->id));
×
NEW
416
  if (l == null_literal || l == false_literal) {
×
NEW
417
    return null_literal;
×
418
  }
419

NEW
420
  if (composite_kind(hint) == COMPOSITE_EQ) {
×
NEW
421
    return not(l);
×
422
  }
423

NEW
424
  return l;
×
425
}
426

427
/*
428
 * Add one eq/diseq notification as a labeled internal assumption.
429
 */
NEW
430
static void mcsat_satellite_add_eq_notification(mcsat_satellite_t *sat, term_t t1, term_t t2, bool eq, literal_t src) {
×
431
  int32_t k0, k1;
432
  pmap2_rec_t *r;
433
  term_t atom;
434
  term_t label;
435
  term_t implication;
436

NEW
437
  if (t1 > t2) {
×
NEW
438
    term_t aux = t1;
×
NEW
439
    t1 = t2;
×
NEW
440
    t2 = aux;
×
441
  }
442

NEW
443
  k0 = eq ? (int32_t) t1 : -((int32_t) t1) - 1;
×
NEW
444
  k1 = (int32_t) t2;
×
445

NEW
446
  r = pmap2_get(&sat->eq_active, k0, k1);
×
NEW
447
  if (r->val >= 0) {
×
NEW
448
    return;
×
449
  }
450

NEW
451
  if (sat->num_eq == sat->eq_size) {
×
NEW
452
    mcsat_satellite_extend_eq(sat);
×
453
  }
454

NEW
455
  label = mk_uterm(&sat->tm, bool_type(sat->mctx.types));
×
NEW
456
  atom = eq ? mk_eq(&sat->tm, t1, t2) : mk_neq(&sat->tm, t1, t2);
×
NEW
457
  implication = mk_implies(&sat->tm, label, atom);
×
NEW
458
  if (mcsat_satellite_assert_formula(sat, implication) < 0) {
×
NEW
459
    return;
×
460
  }
461

NEW
462
  sat->eq[sat->num_eq].label = label;
×
NEW
463
  sat->eq[sat->num_eq].source_lit = src;
×
NEW
464
  sat->num_eq ++;
×
465

NEW
466
  r->val = 1;
×
NEW
467
  sat->cache_valid = false;
×
468
}
469

470
/*
471
 * Control-interface callbacks.
472
 */
473
static void mcsat_satellite_start_internalization(void *solver) {
22✔
474
  mcsat_satellite_t *sat = solver;
22✔
475
  sat->cache_valid = false;
22✔
476
}
22✔
477

478
static void mcsat_satellite_start_search(void *solver) {
6✔
479
  mcsat_satellite_t *sat = solver;
6✔
480
  sat->cache_valid = false;
6✔
481
  if (mcsat_status(sat->mctx.mcsat) != YICES_STATUS_IDLE) {
6✔
482
    mcsat_clear(sat->mctx.mcsat);
6✔
483
  }
484
}
6✔
485

486
static bool mcsat_satellite_propagate(void *solver) {
40✔
487
  mcsat_satellite_t *sat = solver;
40✔
488
  smt_status_t status;
489

490
  if (!sat->check_in_propagate) {
40✔
491
    return true;
1✔
492
  }
493

494
  status = mcsat_satellite_check(sat, false, true);
39✔
495
  return status != YICES_STATUS_UNSAT;
39✔
496
}
497

498
static fcheck_code_t mcsat_satellite_final_check(void *solver) {
4✔
499
  mcsat_satellite_t *sat = solver;
4✔
500
  smt_status_t status;
501

502
  status = mcsat_satellite_check(sat, false, true);
4✔
503
  switch (status) {
4✔
NEW
504
  case YICES_STATUS_UNSAT:
×
NEW
505
    return FCHECK_CONTINUE;
×
NEW
506
  case YICES_STATUS_UNKNOWN:
×
NEW
507
    return FCHECK_UNKNOWN;
×
508
  default:
4✔
509
    return FCHECK_SAT;
4✔
510
  }
511
}
512

513
static void mcsat_satellite_increase_decision_level(void *solver) {
3✔
514
  mcsat_satellite_open_level(solver);
3✔
515
}
3✔
516

517
static void mcsat_satellite_backtrack(void *solver, uint32_t back_level) {
1✔
518
  mcsat_satellite_backtrack_to(solver, back_level);
1✔
519
}
1✔
520

521
static void mcsat_satellite_push(void *solver) {
1✔
522
  mcsat_satellite_t *sat = solver;
1✔
523
  context_push(&sat->mctx);
1✔
524
  ivector_push(&sat->atom_push_mark, sat->num_atoms);
1✔
525
  sat->push_depth ++;
1✔
526
  mcsat_satellite_open_level(sat);
1✔
527
}
1✔
528

529
static void mcsat_satellite_pop(void *solver) {
2✔
530
  mcsat_satellite_t *sat = solver;
2✔
531
  uint32_t i;
532
  int32_t mark;
533

534
  assert(sat->push_depth > 0);
535
  assert(sat->atom_push_mark.size > 0);
536

537
  context_pop(&sat->mctx);
2✔
538
  assert(sat->dlevel > 0);
539

540
  mark = ivector_pop2(&sat->atom_push_mark);
2✔
541
  for (i=mark; i<sat->num_atoms; i++) {
5✔
542
    sat->atoms[i].active = false;
3✔
543
  }
544
  sat->push_depth --;
2✔
545
  mcsat_satellite_backtrack_to(sat, sat->dlevel - 1);
2✔
546
}
2✔
547

548
static void mcsat_satellite_reset(void *solver) {
5✔
549
  mcsat_satellite_t *sat = solver;
5✔
550
  uint32_t i;
551

552
  reset_context(&sat->mctx);
5✔
553

554
  for (i=0; i<sat->num_atoms; i++) {
15✔
555
    if (sat->atoms[i].obj != NULL) {
10✔
556
      safe_free(sat->atoms[i].obj);
8✔
557
      sat->atoms[i].obj = NULL;
8✔
558
    }
559
  }
560

561
  sat->num_atoms = 0;
5✔
562
  sat->num_eq = 0;
5✔
563
  sat->push_depth = 0;
5✔
564
  sat->dlevel = 0;
5✔
565
  sat->cache_valid = false;
5✔
566
  sat->internal_error = 0;
5✔
567

568
  ivector_reset(&sat->atom_push_mark);
5✔
569
  reset_pmap2(&sat->eq_active);
5✔
570
  ivector_reset(&sat->eq_level_mark);
5✔
571
  ivector_push(&sat->eq_level_mark, 0);
5✔
572

573
  int_hmap_reset(&sat->arith_var_to_term);
5✔
574
  int_hmap_reset(&sat->label_to_lit);
5✔
575
  ivector_reset(&sat->assumptions);
5✔
576
  ivector_reset(&sat->assumption_lits);
5✔
577
  ivector_reset(&sat->conflict);
5✔
578
}
5✔
579

580
static void mcsat_satellite_clear(void *solver) {
1✔
581
  mcsat_satellite_t *sat = solver;
1✔
582
  context_clear(&sat->mctx);
1✔
583
  sat->cache_valid = false;
1✔
584
}
1✔
585

586
/*
587
 * SMT interface callbacks.
588
 */
589
static bool mcsat_satellite_assert_atom(void *solver, void *atom, literal_t l) {
17✔
590
  (void) solver;
591
  (void) atom;
592
  (void) l;
593
  return true;
17✔
594
}
595

NEW
596
static void mcsat_satellite_expand_explanation(void *solver, literal_t l, void *expl, ivector_t *v) {
×
597
  (void) solver;
598
  (void) l;
599
  (void) expl;
600
  (void) v;
NEW
601
}
×
602

NEW
603
static literal_t mcsat_satellite_select_polarity(void *solver, void *atom, literal_t l) {
×
604
  (void) solver;
605
  (void) atom;
NEW
606
  return l;
×
607
}
608

609
/*
610
 * Egraph interface callbacks.
611
 */
NEW
612
static void mcsat_satellite_assert_equality(void *solver, thvar_t x1, thvar_t x2, int32_t id) {
×
NEW
613
  mcsat_satellite_t *sat = solver;
×
614
  int_hmap_pair_t *p1, *p2;
615
  (void) id;
616

NEW
617
  p1 = int_hmap_find(&sat->arith_var_to_term, x1);
×
NEW
618
  p2 = int_hmap_find(&sat->arith_var_to_term, x2);
×
NEW
619
  if (p1 != NULL && p2 != NULL) {
×
NEW
620
    mcsat_satellite_add_eq_notification(sat, p1->val, p2->val, true, null_literal);
×
621
  }
NEW
622
}
×
623

NEW
624
static void mcsat_satellite_assert_disequality(void *solver, thvar_t x1, thvar_t x2, composite_t *hint) {
×
NEW
625
  mcsat_satellite_t *sat = solver;
×
626
  int_hmap_pair_t *p1, *p2;
627
  literal_t src;
628

NEW
629
  p1 = int_hmap_find(&sat->arith_var_to_term, x1);
×
NEW
630
  p2 = int_hmap_find(&sat->arith_var_to_term, x2);
×
NEW
631
  if (p1 != NULL && p2 != NULL) {
×
NEW
632
    src = source_lit_from_hint(sat, hint);
×
NEW
633
    mcsat_satellite_add_eq_notification(sat, p1->val, p2->val, false, src);
×
634
  }
NEW
635
}
×
636

NEW
637
static bool mcsat_satellite_assert_distinct(void *solver, uint32_t n, thvar_t *a, composite_t *hint) {
×
NEW
638
  mcsat_satellite_t *sat = solver;
×
639
  int_hmap_pair_t *p1, *p2;
640
  literal_t src;
641
  uint32_t i, j;
642

NEW
643
  src = source_lit_from_hint(sat, hint);
×
NEW
644
  for (i=0; i<n; i++) {
×
NEW
645
    p1 = int_hmap_find(&sat->arith_var_to_term, a[i]);
×
NEW
646
    if (p1 == NULL) continue;
×
NEW
647
    for (j=i+1; j<n; j++) {
×
NEW
648
      p2 = int_hmap_find(&sat->arith_var_to_term, a[j]);
×
NEW
649
      if (p2 != NULL) {
×
NEW
650
        mcsat_satellite_add_eq_notification(sat, p1->val, p2->val, false, src);
×
651
      }
652
    }
653
  }
NEW
654
  return true;
×
655
}
656

NEW
657
static bool mcsat_satellite_check_diseq(void *solver, thvar_t x1, thvar_t x2) {
×
658
  (void) solver;
659
  (void) x1;
660
  (void) x2;
NEW
661
  return false;
×
662
}
663

NEW
664
static bool mcsat_satellite_is_constant(void *solver, thvar_t x) {
×
665
  (void) solver;
666
  (void) x;
NEW
667
  return false;
×
668
}
669

NEW
670
static void mcsat_satellite_expand_th_expl(void *solver, thvar_t x1, thvar_t x2, void *expl, th_explanation_t *result) {
×
671
  (void) solver;
672
  (void) x1;
673
  (void) x2;
674
  (void) expl;
675
  (void) result;
NEW
676
}
×
677

NEW
678
static uint32_t mcsat_satellite_reconcile_model(void *solver, uint32_t max_eq) {
×
679
  (void) solver;
680
  (void) max_eq;
NEW
681
  return 0;
×
682
}
683

NEW
684
static void mcsat_satellite_prepare_model(void *solver) {
×
685
  (void) solver;
NEW
686
}
×
687

NEW
688
static bool mcsat_satellite_equal_in_model(void *solver, thvar_t x1, thvar_t x2) {
×
689
  (void) solver;
690
  (void) x1;
691
  (void) x2;
NEW
692
  return false;
×
693
}
694

NEW
695
static void mcsat_satellite_gen_interface_lemma(void *solver, literal_t l, thvar_t x1, thvar_t x2, bool equiv) {
×
696
  (void) solver;
697
  (void) l;
698
  (void) x1;
699
  (void) x2;
700
  (void) equiv;
NEW
701
}
×
702

NEW
703
static void mcsat_satellite_release_model(void *solver) {
×
704
  (void) solver;
NEW
705
}
×
706

NEW
707
static ipart_t *mcsat_satellite_build_partition(void *solver) {
×
708
  (void) solver;
NEW
709
  return NULL;
×
710
}
711

NEW
712
static void mcsat_satellite_release_partition(void *solver, ipart_t *partition) {
×
713
  (void) solver;
714
  (void) partition;
NEW
715
}
×
716

NEW
717
static void mcsat_satellite_attach_eterm(void *solver, thvar_t x, eterm_t t) {
×
718
  (void) solver;
719
  (void) x;
720
  (void) t;
NEW
721
}
×
722

NEW
723
static eterm_t mcsat_satellite_eterm_of_var(void *solver, thvar_t x) {
×
724
  (void) solver;
725
  (void) x;
NEW
726
  return null_eterm;
×
727
}
728

NEW
729
static literal_t mcsat_satellite_select_eq_polarity(void *solver, thvar_t x, thvar_t y, literal_t l) {
×
730
  (void) solver;
731
  (void) x;
732
  (void) y;
NEW
733
  return l;
×
734
}
735

736
/*
737
 * Static interface records.
738
 */
739
static th_ctrl_interface_t mcsat_satellite_ctrl = {
740
  (start_intern_fun_t) mcsat_satellite_start_internalization,
741
  (start_fun_t) mcsat_satellite_start_search,
742
  (propagate_fun_t) mcsat_satellite_propagate,
743
  (final_check_fun_t) mcsat_satellite_final_check,
744
  (increase_level_fun_t) mcsat_satellite_increase_decision_level,
745
  (backtrack_fun_t) mcsat_satellite_backtrack,
746
  (push_fun_t) mcsat_satellite_push,
747
  (pop_fun_t) mcsat_satellite_pop,
748
  (reset_fun_t) mcsat_satellite_reset,
749
  (clear_fun_t) mcsat_satellite_clear,
750
};
751

752
static th_smt_interface_t mcsat_satellite_smt = {
753
  (assert_fun_t) mcsat_satellite_assert_atom,
754
  (expand_expl_fun_t) mcsat_satellite_expand_explanation,
755
  (select_pol_fun_t) mcsat_satellite_select_polarity,
756
  NULL,
757
  NULL,
758
};
759

760
static th_egraph_interface_t mcsat_satellite_eg = {
761
  (assert_eq_fun_t) mcsat_satellite_assert_equality,
762
  (assert_diseq_fun_t) mcsat_satellite_assert_disequality,
763
  (assert_distinct_fun_t) mcsat_satellite_assert_distinct,
764
  (check_diseq_fun_t) mcsat_satellite_check_diseq,
765
  (is_constant_fun_t) mcsat_satellite_is_constant,
766
  (expand_eq_exp_fun_t) mcsat_satellite_expand_th_expl,
767
  (reconcile_model_fun_t) mcsat_satellite_reconcile_model,
768
  (prepare_model_fun_t) mcsat_satellite_prepare_model,
769
  (equal_in_model_fun_t) mcsat_satellite_equal_in_model,
770
  (gen_inter_lemma_fun_t) mcsat_satellite_gen_interface_lemma,
771
  (release_model_fun_t) mcsat_satellite_release_model,
772
  (build_partition_fun_t) mcsat_satellite_build_partition,
773
  (free_partition_fun_t) mcsat_satellite_release_partition,
774
  (attach_to_var_fun_t) mcsat_satellite_attach_eterm,
775
  (get_eterm_fun_t) mcsat_satellite_eterm_of_var,
776
  (select_eq_polarity_fun_t) mcsat_satellite_select_eq_polarity,
777
};
778

779

780
/*
781
 * Constructor/destructor.
782
 */
783
mcsat_satellite_t *new_mcsat_satellite(context_t *ctx) {
10✔
784
  mcsat_satellite_t *sat;
785

786
  sat = (mcsat_satellite_t *) safe_malloc(sizeof(mcsat_satellite_t));
10✔
787

788
  sat->ctx = ctx;
10✔
789
  sat->core = ctx->core;
10✔
790
  sat->egraph = ctx->egraph;
10✔
791

792
  init_context(&sat->mctx, ctx->terms, ctx->logic, CTX_MODE_PUSHPOP, CTX_ARCH_MCSAT, false);
10✔
793
  sat->mctx.mcsat_options = ctx->mcsat_options;
10✔
794
  sat->mctx.mcsat_options.model_interpolation = true;
10✔
795
  ivector_copy(&sat->mctx.mcsat_var_order, ctx->mcsat_var_order.data, ctx->mcsat_var_order.size);
10✔
796
  ivector_copy(&sat->mctx.mcsat_initial_var_order, ctx->mcsat_initial_var_order.data, ctx->mcsat_initial_var_order.size);
10✔
797

798
  init_term_manager(&sat->tm, sat->mctx.terms);
10✔
799

800
  init_params_to_defaults(&sat->params);
10✔
801
  sat->check_in_propagate = true;
10✔
802

803
  sat->internal_error = 0;
10✔
804

805
  sat->cache_valid = false;
10✔
806
  sat->cache_signature = 0;
10✔
807

808
  sat->atoms = NULL;
10✔
809
  sat->num_atoms = 0;
10✔
810
  sat->atom_size = 0;
10✔
811
  sat->push_depth = 0;
10✔
812
  init_ivector(&sat->atom_push_mark, 0);
10✔
813

814
  init_int_hmap(&sat->arith_var_to_term, 0);
10✔
815

816
  sat->eq = NULL;
10✔
817
  sat->num_eq = 0;
10✔
818
  sat->eq_size = 0;
10✔
819
  sat->dlevel = 0;
10✔
820
  init_pmap2(&sat->eq_active);
10✔
821
  init_ivector(&sat->eq_level_mark, 8);
10✔
822
  ivector_push(&sat->eq_level_mark, 0);
10✔
823

824
  init_ivector(&sat->assumptions, 0);
10✔
825
  init_ivector(&sat->assumption_lits, 0);
10✔
826
  init_int_hmap(&sat->label_to_lit, 0);
10✔
827
  init_ivector(&sat->conflict, 0);
10✔
828

829
  if (ctx->trace != NULL) {
10✔
NEW
830
    mcsat_satellite_set_trace(sat, ctx->trace);
×
831
  }
832

833
  mcsat_satellite_sync_base_level(sat, ctx->base_level);
10✔
834

835
  return sat;
10✔
836
}
837

838
void delete_mcsat_satellite(mcsat_satellite_t *sat) {
10✔
839
  uint32_t i;
840

841
  if (sat == NULL) {
10✔
NEW
842
    return;
×
843
  }
844

845
  for (i = 0; i < sat->num_atoms; i++) {
20✔
846
    if (sat->atoms[i].obj != NULL) {
10✔
847
      safe_free(sat->atoms[i].obj);
8✔
848
      sat->atoms[i].obj = NULL;
8✔
849
    }
850
  }
851
  safe_free(sat->atoms);
10✔
852
  sat->atoms = NULL;
10✔
853

854
  safe_free(sat->eq);
10✔
855
  sat->eq = NULL;
10✔
856

857
  delete_ivector(&sat->conflict);
10✔
858
  delete_int_hmap(&sat->label_to_lit);
10✔
859
  delete_ivector(&sat->assumption_lits);
10✔
860
  delete_ivector(&sat->assumptions);
10✔
861

862
  delete_ivector(&sat->eq_level_mark);
10✔
863
  delete_pmap2(&sat->eq_active);
10✔
864

865
  delete_int_hmap(&sat->arith_var_to_term);
10✔
866
  delete_ivector(&sat->atom_push_mark);
10✔
867

868
  delete_term_manager(&sat->tm);
10✔
869
  delete_context(&sat->mctx);
10✔
870
  safe_free(sat);
10✔
871
}
872

873

874
/*
875
 * Public interface getters.
876
 */
877
th_ctrl_interface_t *mcsat_satellite_ctrl_interface(mcsat_satellite_t *sat) {
10✔
878
  (void) sat;
879
  return &mcsat_satellite_ctrl;
10✔
880
}
881

882
th_smt_interface_t *mcsat_satellite_smt_interface(mcsat_satellite_t *sat) {
10✔
883
  (void) sat;
884
  return &mcsat_satellite_smt;
10✔
885
}
886

887
th_egraph_interface_t *mcsat_satellite_egraph_interface(mcsat_satellite_t *sat) {
10✔
888
  (void) sat;
889
  return &mcsat_satellite_eg;
10✔
890
}
891

892
int32_t mcsat_satellite_assert_formulas(mcsat_satellite_t *sat, uint32_t n, const term_t *a) {
18✔
893
  uint32_t i;
894
  int32_t code;
895

896
  for (i = 0; i < n; i++) {
36✔
897
    code = mcsat_satellite_assert_formula(sat, a[i]);
18✔
898
    if (code < 0) {
18✔
NEW
899
      return code;
×
900
    }
901
  }
902

903
  return CTX_NO_ERROR;
18✔
904
}
905

906
/*
907
 * Register one tracked unsupported atom.
908
 */
909
int32_t mcsat_satellite_register_atom(mcsat_satellite_t *sat, term_t atom, literal_t l, void **obj) {
20✔
910
  mcsat_atom_entry_t *entry;
911
  term_t plabel, nlabel;
912
  term_t implication;
913
  int32_t code;
914
  mcsat_atom_object_t *atom_obj;
915
  uint32_t i;
916

917
  assert(l >= 0);
918

919
  // Already tracked: keep the existing object/literal association.
920
  for (i = 0; i < sat->num_atoms; i++) {
36✔
921
    entry = sat->atoms + i;
16✔
922
    if (entry->atom == atom && entry->lit == l) {
16✔
NEW
923
      if (obj != NULL) {
×
NEW
924
        *obj = entry->obj;
×
925
      }
NEW
926
      return CTX_NO_ERROR;
×
927
    }
928
  }
929

930
  if (sat->num_atoms == sat->atom_size) {
20✔
931
    mcsat_satellite_extend_atoms(sat);
16✔
932
  }
933

934
  plabel = mk_uterm(&sat->tm, bool_type(sat->mctx.types));
20✔
935
  nlabel = mk_uterm(&sat->tm, bool_type(sat->mctx.types));
20✔
936

937
  implication = mk_implies(&sat->tm, plabel, atom);
20✔
938
  code = mcsat_satellite_assert_formula(sat, implication);
20✔
939
  if (code < 0) {
20✔
NEW
940
    return code;
×
941
  }
942

943
  implication = mk_implies(&sat->tm, nlabel, not(atom));
20✔
944
  code = mcsat_satellite_assert_formula(sat, implication);
20✔
945
  if (code < 0) {
20✔
NEW
946
    return code;
×
947
  }
948

949
  atom_obj = NULL;
20✔
950
  if (obj != NULL) {
20✔
951
    atom_obj = safe_malloc(sizeof(mcsat_atom_object_t));
16✔
952
    atom_obj->id = sat->num_atoms;
16✔
953
    *obj = atom_obj;
16✔
954
  }
955

956
  entry = sat->atoms + sat->num_atoms;
20✔
957
  entry->atom = atom;
20✔
958
  entry->lit = l;
20✔
959
  entry->pos_label = plabel;
20✔
960
  entry->neg_label = nlabel;
20✔
961
  entry->obj = atom_obj;
20✔
962
  entry->active = true;
20✔
963
  sat->num_atoms ++;
20✔
964

965
  sat->cache_valid = false;
20✔
966
  return CTX_NO_ERROR;
20✔
967
}
968

969
/*
970
 * Register thvar -> term for arithmetic terms.
971
 */
NEW
972
void mcsat_satellite_register_arith_term(mcsat_satellite_t *sat, thvar_t x, term_t t) {
×
973
  int_hmap_pair_t *p;
NEW
974
  p = int_hmap_get(&sat->arith_var_to_term, x);
×
NEW
975
  p->val = t;
×
NEW
976
}
×
977

978
/*
979
 * Parameters/tracing/model/GC support.
980
 */
981
void mcsat_satellite_set_search_parameters(mcsat_satellite_t *sat, const param_t *params) {
6✔
982
  sat->params = *params;
6✔
983
  sat->check_in_propagate = (params->mcsat_supplement_check == MCSAT_SUPPLEMENT_CHECK_BOTH);
6✔
984
  sat->cache_valid = false;
6✔
985
}
6✔
986

NEW
987
void mcsat_satellite_set_trace(mcsat_satellite_t *sat, tracer_t *trace) {
×
NEW
988
  mcsat_set_tracer(sat->mctx.mcsat, trace);
×
NEW
989
}
×
990

NEW
991
term_t mcsat_satellite_get_unsat_model_interpolant(mcsat_satellite_t *sat) {
×
NEW
992
  return mcsat_get_unsat_model_interpolant(sat->mctx.mcsat);
×
993
}
994

995
void mcsat_satellite_set_unsat_model_interpolant(mcsat_satellite_t *sat, term_t t) {
2✔
996
  mcsat_set_unsat_result(sat->mctx.mcsat, t);
2✔
997
}
2✔
998

999
term_t mcsat_satellite_compute_unsat_model_interpolant(mcsat_satellite_t *sat, const param_t *params, uint32_t n, const term_t *a) {
2✔
1000
  ivector_t labels;
1001
  ivector_t assumptions;
1002
  term_manager_t tm;
1003
  model_t mdl;
1004
  value_t vtrue;
1005
  term_t result;
1006
  smt_status_t status;
1007
  uint32_t i;
1008
  bool pushed;
1009

1010
  if (sat->internal_error < 0) {
2✔
NEW
1011
    return NULL_TERM;
×
1012
  }
1013

1014
  init_ivector(&labels, n);
2✔
1015
  init_ivector(&assumptions, n);
2✔
1016
  init_term_manager(&tm, sat->mctx.terms);
2✔
1017
  init_model(&mdl, sat->mctx.terms, true);
2✔
1018

1019
  pushed = false;
2✔
1020
  result = NULL_TERM;
2✔
1021

1022
  /*
1023
   * Internal push requires an idle MCSAT state in debug builds.
1024
   * This path may be called after a previous UNSAT check.
1025
   */
1026
  if (mcsat_status(sat->mctx.mcsat) != YICES_STATUS_IDLE) {
2✔
1027
    mcsat_clear(sat->mctx.mcsat);
2✔
1028
  }
1029

1030
  if (context_supports_pushpop(&sat->mctx)) {
2✔
1031
    context_push(&sat->mctx);
2✔
1032
    pushed = true;
2✔
1033
  }
1034

1035
  vtrue = vtbl_mk_bool(&mdl.vtbl, true);
2✔
1036

1037
  for (i = 0; i < n; i++) {
6✔
1038
    term_t b, implication;
1039
    int32_t code;
1040

1041
    b = new_uninterpreted_term(sat->mctx.terms, bool_id);
4✔
1042
    implication = mk_implies(&tm, b, a[i]);
4✔
1043
    code = mcsat_satellite_assert_formula(sat, implication);
4✔
1044
    if (code < 0) {
4✔
NEW
1045
      goto done;
×
1046
    }
1047

1048
    ivector_push(&labels, b);
4✔
1049
    ivector_push(&assumptions, b);
4✔
1050
    model_map_term(&mdl, b, vtrue);
4✔
1051
  }
1052

1053
  mcsat_clear(sat->mctx.mcsat);
2✔
1054
  mcsat_solve(sat->mctx.mcsat, params != NULL ? params : &sat->params, &mdl,
2✔
1055
              assumptions.size, (const term_t *) assumptions.data);
2✔
1056
  status = mcsat_status(sat->mctx.mcsat);
2✔
1057

1058
  if (status == YICES_STATUS_UNSAT) {
2✔
1059
    term_t raw = mcsat_get_unsat_model_interpolant(sat->mctx.mcsat);
2✔
1060
    if (raw == NULL_TERM) {
2✔
NEW
1061
      raw = false_term;
×
1062
    }
1063
    if (labels.size > 0) {
2✔
1064
      term_subst_t subst;
1065
      init_term_subst(&subst, &tm, labels.size, labels.data, a);
2✔
1066
      result = apply_term_subst(&subst, raw);
2✔
1067
      delete_term_subst(&subst);
2✔
1068
    } else {
NEW
1069
      result = raw;
×
1070
    }
1071
  }
1072

NEW
1073
done:
×
1074
  delete_model(&mdl);
2✔
1075
  delete_term_manager(&tm);
2✔
1076
  delete_ivector(&assumptions);
2✔
1077
  delete_ivector(&labels);
2✔
1078

1079
  if (pushed) {
2✔
1080
    mcsat_cleanup_assumptions(sat->mctx.mcsat);
2✔
1081
    context_pop(&sat->mctx);
2✔
1082
  }
1083

1084
  if (result != NULL_TERM) {
2✔
1085
    mcsat_set_unsat_result(sat->mctx.mcsat, result);
2✔
1086
  }
1087

1088
  return result;
2✔
1089
}
1090

1091
void mcsat_satellite_build_model(mcsat_satellite_t *sat, model_t *model) {
3✔
1092
  model_t mdl;
1093
  smt_status_t status;
1094
  uint32_t i;
1095
  value_t vtrue;
1096

1097
  if (sat->internal_error < 0) {
3✔
NEW
1098
    return;
×
1099
  }
1100

1101
  /*
1102
   * For model construction, complete unassigned tracked literals with their
1103
   * current polarity hint so MCSAT can compute a concrete witness model.
1104
   */
1105
  mcsat_satellite_build_assumptions(sat, true);
3✔
1106

1107
  init_model(&mdl, sat->mctx.terms, true);
3✔
1108
  vtrue = vtbl_mk_bool(&mdl.vtbl, true);
3✔
1109
  for (i = 0; i < sat->assumptions.size; i++) {
8✔
1110
    if (model_find_term_value(&mdl, sat->assumptions.data[i]) == null_value) {
5✔
1111
      model_map_term(&mdl, sat->assumptions.data[i], vtrue);
5✔
1112
    }
1113
  }
1114

1115
  mcsat_clear(sat->mctx.mcsat);
3✔
1116
  mcsat_solve(sat->mctx.mcsat, &sat->params, &mdl, sat->assumptions.size, (const term_t *) sat->assumptions.data);
3✔
1117
  status = mcsat_status(sat->mctx.mcsat);
3✔
1118

1119
  delete_model(&mdl);
3✔
1120

1121
  if (status == YICES_STATUS_SAT) {
3✔
1122
    mcsat_build_model(sat->mctx.mcsat, model);
3✔
1123
  }
1124
}
1125

NEW
1126
void mcsat_satellite_gc_mark(mcsat_satellite_t *sat) {
×
1127
  uint32_t i;
1128

NEW
1129
  for (i=0; i<sat->num_atoms; i++) {
×
NEW
1130
    term_table_set_gc_mark(sat->mctx.terms, index_of(sat->atoms[i].atom));
×
NEW
1131
    term_table_set_gc_mark(sat->mctx.terms, index_of(sat->atoms[i].pos_label));
×
NEW
1132
    term_table_set_gc_mark(sat->mctx.terms, index_of(sat->atoms[i].neg_label));
×
1133
  }
1134

NEW
1135
  for (i=0; i<sat->num_eq; i++) {
×
NEW
1136
    term_table_set_gc_mark(sat->mctx.terms, index_of(sat->eq[i].label));
×
1137
  }
1138

NEW
1139
  mcsat_gc_mark(sat->mctx.mcsat);
×
NEW
1140
}
×
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