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

SRI-CSL / yices2 / 12367760548

17 Dec 2024 06:41AM UTC coverage: 65.285% (-0.6%) from 65.92%
12367760548

push

github

web-flow
Fixes GitHub coverage issue (#544)

* Update action.yml

* Update action.yml

81020 of 124102 relevant lines covered (65.29%)

1509382.01 hits per line

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

87.85
/src/mcsat/uf/uf_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 "uf_plugin.h"
20

21
#include "mcsat/trail.h"
22
#include "mcsat/tracing.h"
23
#include "mcsat/watch_list_manager.h"
24
#include "mcsat/utils/scope_holder.h"
25
#include "mcsat/value.h"
26

27
#include "mcsat/eq/equality_graph.h"
28
#include "mcsat/weq/weak_eq_graph.h"
29

30
#include "utils/int_array_sort2.h"
31
#include "utils/ptr_array_sort2.h"
32
#include "utils/int_hash_sets.h"
33

34
#include "model/models.h"
35

36
#include "terms/terms.h"
37
#include "inttypes.h"
38

39

40
#define DECIDE_FUNCTION_VALUE_START UINT32_MAX/64
41

42

43
typedef struct {
44

45
  /** The plugin interface */
46
  plugin_t plugin_interface;
47

48
  /** The plugin context */
49
  plugin_context_t* ctx;
50

51
  /** Scope holder for the int variables */
52
  scope_holder_t scope;
53

54
  /** Conflict  */
55
  ivector_t conflict;
56

57
  /** The term manager (no ITE simplification) */
58
  term_manager_t* tm;
59

60
  /** Equality graph */
61
  eq_graph_t eq_graph;
62

63
  /** Stuff added to eq_graph */
64
  ivector_t eq_graph_addition_trail;
65

66
  /** Weak Equality graph for array reasoning */
67
  weq_graph_t weq_graph;
68

69
  /** Function Values that have been used */
70
  int_hset_t fun_used_values;
71

72
  /** Tmp vector */
73
  int_mset_t tmp;
74

75
  struct {
76
    statistic_int_t* egraph_terms;
77
    statistic_int_t* propagations;
78
    statistic_int_t* conflicts;
79
    statistic_avg_t* avg_conflict_size;
80
    statistic_avg_t* avg_explanation_size;
81
  } stats;
82

83
  /** Exception handler */
84
  jmp_buf* exception;
85

86
} uf_plugin_t;
87

88

89
static
90
void uf_plugin_stats_init(uf_plugin_t* uf) {
634✔
91
  // Add statistics
92
  uf->stats.propagations = statistics_new_int(uf->ctx->stats, "mcsat::uf::propagations");
634✔
93
  uf->stats.conflicts = statistics_new_int(uf->ctx->stats, "mcsat::uf::conflicts");
634✔
94
  uf->stats.egraph_terms = statistics_new_int(uf->ctx->stats, "mcsat::uf::egraph_terms");
634✔
95
  uf->stats.avg_conflict_size = statistics_new_avg(uf->ctx->stats, "mcsat::uf::avg_conflict_size");
634✔
96
  uf->stats.avg_explanation_size = statistics_new_avg(uf->ctx->stats, "mcsat::uf::avg_explanation_size");
634✔
97
}
634✔
98

99
static
100
void uf_plugin_bump_terms_and_reset(uf_plugin_t* uf, int_mset_t* to_bump) {
63,496✔
101
  uint32_t i;
102
  for (i = 0; i < to_bump->element_list.size; ++ i) {
951,409✔
103
    term_t t = to_bump->element_list.data[i];
887,913✔
104
    variable_t t_var = variable_db_get_variable_if_exists(uf->ctx->var_db, t);
887,913✔
105
    if (t_var != variable_null) {
887,913✔
106
      int_hmap_pair_t* find = int_hmap_find(&to_bump->count_map, t);
887,913✔
107
      uf->ctx->bump_variable_n(uf->ctx, t_var, find->val);
887,913✔
108
    }
109
  }
110
  int_mset_clear(to_bump);
63,496✔
111
}
63,496✔
112

113
static
114
void uf_plugin_construct(plugin_t* plugin, plugin_context_t* ctx) {
634✔
115
  uf_plugin_t* uf = (uf_plugin_t*) plugin;
634✔
116

117
  uf->ctx = ctx;
634✔
118

119
  scope_holder_construct(&uf->scope);
634✔
120
  init_ivector(&uf->conflict, 0);
634✔
121
  init_int_hset(&uf->fun_used_values, 0);
634✔
122
  int_mset_construct(&uf->tmp, NULL_TERM);
634✔
123

124
  // Terms
125
  ctx->request_term_notification_by_kind(ctx, APP_TERM, false);
634✔
126
  ctx->request_term_notification_by_kind(ctx, ARITH_RDIV, false);
634✔
127
  ctx->request_term_notification_by_kind(ctx, ARITH_IDIV, false);
634✔
128
  ctx->request_term_notification_by_kind(ctx, ARITH_MOD, false);
634✔
129
  ctx->request_term_notification_by_kind(ctx, EQ_TERM, false);
634✔
130
  ctx->request_term_notification_by_kind(ctx, UPDATE_TERM, false);
634✔
131

132
  // Types
133
  ctx->request_term_notification_by_type(ctx, UNINTERPRETED_TYPE);
634✔
134
  ctx->request_term_notification_by_type(ctx, FUNCTION_TYPE);
634✔
135

136
  // Decisions
137
  ctx->request_decision_calls(ctx, UNINTERPRETED_TYPE);
634✔
138
  ctx->request_decision_calls(ctx, FUNCTION_TYPE);
634✔
139

140
  // Equality graph
141
  eq_graph_construct(&uf->eq_graph, ctx, "uf");
634✔
142
  init_ivector(&uf->eq_graph_addition_trail, 0);
634✔
143

144
  // Weak Equality graph
145
  weq_graph_construct(&uf->weq_graph, ctx, &uf->eq_graph);
634✔
146

147
  // stats
148
  uf_plugin_stats_init(uf);
634✔
149
}
634✔
150

151
static
152
void uf_plugin_destruct(plugin_t* plugin) {
634✔
153
  uf_plugin_t* uf = (uf_plugin_t*) plugin;
634✔
154
  scope_holder_destruct(&uf->scope);
634✔
155
  delete_ivector(&uf->conflict);
634✔
156
  delete_int_hset(&uf->fun_used_values);
634✔
157
  int_mset_destruct(&uf->tmp);
634✔
158

159
  eq_graph_destruct(&uf->eq_graph);
634✔
160
  delete_ivector(&uf->eq_graph_addition_trail);
634✔
161

162
  weq_graph_destruct(&uf->weq_graph);
634✔
163
}
634✔
164

165
static
166
bool uf_plugin_process_eq_graph_propagations(uf_plugin_t* uf, trail_token_t* prop) {
697,477✔
167
  bool propagated = false;
697,477✔
168
  // Process any propagated terms
169
  if (eq_graph_has_propagated_terms(&uf->eq_graph)) {
697,477✔
170
    uint32_t i = 0;
451,240✔
171
    ivector_t eq_propagations;
172
    init_ivector(&eq_propagations, 0);
451,240✔
173
    eq_graph_get_propagated_terms(&uf->eq_graph, &eq_propagations);
451,240✔
174
    for (; i < eq_propagations.size; ++ i) {
2,133,108✔
175
      // Term to propagate
176
      term_t t = eq_propagations.data[i];
1,681,868✔
177
      // Variable to propagate
178
      variable_t t_var = variable_db_get_variable_if_exists(uf->ctx->var_db, t);
1,681,868✔
179
      if (t_var != variable_null) {
1,681,868✔
180
        // Only set values of uninterpreted, function and boolean type
181
        type_kind_t t_type_kind = term_type_kind(uf->ctx->terms, t);
1,681,868✔
182
        if (t_type_kind == UNINTERPRETED_TYPE ||
1,681,868✔
183
            t_type_kind == FUNCTION_TYPE ||
1,267,607✔
184
            t_type_kind == BOOL_TYPE) {
185
          const mcsat_value_t* v = eq_graph_get_propagated_term_value(&uf->eq_graph, t);
1,481,793✔
186
          if (!trail_has_value(uf->ctx->trail, t_var)) {
1,481,793✔
187
            if (ctx_trace_enabled(uf->ctx, "mcsat::eq::propagate")) {
849,477✔
188
              FILE* out = ctx_trace_out(uf->ctx);
×
189
              ctx_trace_term(uf->ctx, t);
×
190
              fprintf(out, " -> ");
×
191
              mcsat_value_print(v, out);
×
192
              fprintf(out, "\n");
×
193
            }
194
            
195
            prop->add(prop, t_var, v);
849,477✔
196
            (*uf->stats.propagations) ++;
849,477✔
197

198
            propagated = true;
849,477✔
199
          } else {
200
            // Ignore, we will report conflict
201
          }
202
        }
203
      }
204
    }
205
    delete_ivector(&eq_propagations);
451,240✔
206
  }
207

208
  return propagated;
697,477✔
209
}
210

211
static
212
void uf_plugin_add_to_eq_graph(uf_plugin_t* uf, term_t t, bool record) {
146,210✔
213

214
  term_table_t* terms = uf->ctx->terms;
146,210✔
215

216
  // The kind
217
  term_kind_t t_kind = term_kind(terms, t);
146,210✔
218

219
  // Add to equality graph
220
  composite_term_t* t_desc = NULL;
146,210✔
221
  switch (t_kind) {
146,210✔
222
  case APP_TERM:
5,148✔
223
    t_desc = app_term_desc(terms, t);
5,148✔
224
    eq_graph_add_ufun_term(&uf->eq_graph, t, t_desc->arg[0], t_desc->arity - 1, t_desc->arg + 1);
5,148✔
225
    weq_graph_add_select_term(&uf->weq_graph, t);
5,148✔
226
    break;
5,148✔
227
  case UPDATE_TERM:
1,248✔
228
    t_desc = update_term_desc(terms, t);
1,248✔
229
    eq_graph_add_ufun_term(&uf->eq_graph, t, t_desc->arg[0], t_desc->arity - 1, t_desc->arg + 1);
1,248✔
230
    // remember array term
231
    weq_graph_add_array_term(&uf->weq_graph, t);
1,248✔
232
    weq_graph_add_array_term(&uf->weq_graph, t_desc->arg[0]);
1,248✔
233
    // remember select terms
234
    term_t r1 = app_term(terms, t, t_desc->arity - 2, t_desc->arg + 1);
1,248✔
235
    variable_db_get_variable(uf->ctx->var_db, r1);
1,248✔
236
    weq_graph_add_select_term(&uf->weq_graph, r1);
1,248✔
237
    // if the element domain is finite then we add this extra read term
238
    if (is_finite_type(terms->types, term_type(terms, t_desc->arg[2]))) {
1,248✔
239
      term_t r2 = app_term(terms, t_desc->arg[0], t_desc->arity - 2, t_desc->arg + 1);
76✔
240
      variable_db_get_variable(uf->ctx->var_db, r2);
76✔
241
      weq_graph_add_select_term(&uf->weq_graph, r2);
76✔
242
    }
243
    break;
1,248✔
244
  case ARITH_RDIV:
27✔
245
    t_desc = arith_rdiv_term_desc(terms, t);
27✔
246
    eq_graph_add_ifun_term(&uf->eq_graph, t, ARITH_RDIV, 2, t_desc->arg);
27✔
247
    break;
27✔
248
  case ARITH_IDIV:
112✔
249
    t_desc = arith_idiv_term_desc(terms, t);
112✔
250
    eq_graph_add_ifun_term(&uf->eq_graph, t, ARITH_IDIV, 2, t_desc->arg);
112✔
251
    break;
112✔
252
  case ARITH_MOD:
112✔
253
    t_desc = arith_mod_term_desc(terms, t);
112✔
254
    eq_graph_add_ifun_term(&uf->eq_graph, t, ARITH_MOD, 2, t_desc->arg);
112✔
255
    break;
112✔
256
  case EQ_TERM:
139,563✔
257
    t_desc = eq_term_desc(terms, t);
139,563✔
258
    eq_graph_add_ifun_term(&uf->eq_graph, t, EQ_TERM, 2, t_desc->arg);
139,563✔
259
    // remember array terms
260
    uint32_t i;
261
    for (i = 0; i < 2; ++ i) {
418,689✔
262
      if (is_function_term(terms, t_desc->arg[i]) &&
521,664✔
263
          (term_kind(terms, t_desc->arg[i]) == UNINTERPRETED_TERM ||
473,149✔
264
           term_kind(terms, t_desc->arg[i]) == UPDATE_TERM)) {
230,611✔
265
        weq_graph_add_array_term(&uf->weq_graph, t_desc->arg[i]);
122,128✔
266
      }
267
    }
268
    break;
139,563✔
269
  default:
146,210✔
270
    assert(false);
271
  }
272

273
  // Make sure the subterms are registered
274
  uint32_t i;
275
  for (i = 0; i < t_desc->arity; ++ i) {
440,866✔
276
    term_t c = t_desc->arg[i];
294,656✔
277
    variable_t c_var = variable_db_get_variable(uf->ctx->var_db, c);
294,656✔
278
    if (trail_has_value(uf->ctx->trail, c_var)) {
294,656✔
279
      // we need to process it if we ignored it
280
      if (eq_graph_term_is_rep(&uf->eq_graph, c)) {
120,655✔
281
        eq_graph_propagate_trail_assertion(&uf->eq_graph, c);
28✔
282
      }
283
    }
284
  }
285

286
  // Record addition so we can re-add on backtracks
287
  if (record) {
146,210✔
288
    (*uf->stats.egraph_terms) ++;
10,962✔
289
    ivector_push(&uf->eq_graph_addition_trail, t);
10,962✔
290
  }
291
}
146,210✔
292

293

294
static
295
void uf_plugin_new_term_notify(plugin_t* plugin, term_t t, trail_token_t* prop) {
12,326✔
296
  uf_plugin_t* uf = (uf_plugin_t*) plugin;
12,326✔
297
  term_table_t* terms = uf->ctx->terms;
12,326✔
298

299
  if (ctx_trace_enabled(uf->ctx, "mcsat::new_term")) {
12,326✔
300
    ctx_trace_printf(uf->ctx, "uf_plugin_new_term_notify: ");
×
301
    ctx_trace_term(uf->ctx, t);
×
302
  }
303

304
  term_kind_t t_kind = term_kind(terms, t);
12,326✔
305

306
  switch (t_kind) {
12,326✔
307
  case ARITH_MOD:
10,962✔
308
  case ARITH_IDIV:
309
  case ARITH_RDIV:
310
  case APP_TERM:
311
  case UPDATE_TERM:
312
  case EQ_TERM:
313
    // Application terms (or other terms we treat as uninterpreted)
314
    uf_plugin_add_to_eq_graph(uf, t, true);
10,962✔
315
    break;
10,962✔
316
  default:
1,364✔
317
    // All other is of uninterpreted sort, and we treat as variables
318
    break;
1,364✔
319
  }
320

321
  // eagerly add array idx lemma
322
  if (t_kind == UPDATE_TERM) {
12,326✔
323
    term_t r_lemma = weq_graph_get_array_update_idx_lemma(&uf->weq_graph, t);
737✔
324
    prop->definition_lemma(prop, r_lemma, t);
737✔
325
  }
326
}
12,326✔
327

328
static
329
void uf_plugin_learn(plugin_t* plugin, trail_token_t* prop) {
994✔
330
  uf_plugin_t* uf = (uf_plugin_t*) plugin;
994✔
331
  assert(uf->conflict.size == 0);
332

333
  // check array conflicts
334
  weq_graph_check_array_conflict(&uf->weq_graph, &uf->conflict);
994✔
335

336
  if (uf->conflict.size > 0) {
994✔
337
    // Report conflict
338
    prop->conflict(prop);
×
339
    (*uf->stats.conflicts) ++;
×
340
    statistic_avg_add(uf->stats.avg_conflict_size, uf->conflict.size);
×
341
  }
342
}
994✔
343

344
static
345
void uf_plugin_propagate(plugin_t* plugin, trail_token_t* prop) {
1,223,726✔
346

347
  uf_plugin_t* uf = (uf_plugin_t*) plugin;
1,223,726✔
348

349
  if (ctx_trace_enabled(uf->ctx, "uf_plugin")) {
1,223,726✔
350
    ctx_trace_printf(uf->ctx, "uf_plugin_propagate()\n");
×
351
  }
352

353
  // If we're not watching anything, we just ignore
354
  if (eq_graph_term_size(&uf->eq_graph) == 0) {
1,223,726✔
355
    return;
526,249✔
356
  }
357

358
  // Propagate known terms
359
  eq_graph_propagate_trail(&uf->eq_graph);
697,477✔
360
  uf_plugin_process_eq_graph_propagations(uf, prop);
697,477✔
361

362
  // Check for conflicts
363
  if (uf->eq_graph.in_conflict) {
697,477✔
364
    // Report conflict
365
    prop->conflict(prop);
12,787✔
366
    (*uf->stats.conflicts) ++;
12,787✔
367
    // Construct the conflict
368
    eq_graph_get_conflict(&uf->eq_graph, &uf->conflict, NULL, &uf->tmp);
12,787✔
369
    uf_plugin_bump_terms_and_reset(uf, &uf->tmp);
12,787✔
370
    statistic_avg_add(uf->stats.avg_conflict_size, uf->conflict.size);
12,787✔
371
    return;
12,787✔
372
  }
373

374
  // optimization: skip array checks if some terms, that are present
375
  // in the eq_graph, don't have an assigned value.
376
  if (weq_graph_is_all_assigned(&uf->weq_graph)) {
684,690✔
377
    assert(uf->conflict.size == 0);
378
    weq_graph_check_array_conflict(&uf->weq_graph, &uf->conflict);
31,264✔
379
    if (uf->conflict.size > 0) {
31,264✔
380
      // Report conflict
381
      prop->conflict(prop);
372✔
382
      (*uf->stats.conflicts) ++;
372✔
383
      statistic_avg_add(uf->stats.avg_conflict_size, uf->conflict.size);
372✔
384
    }
385
  }
386
}
387

388
static
389
void uf_plugin_push(plugin_t* plugin) {
682,198✔
390
  uf_plugin_t* uf = (uf_plugin_t*) plugin;
682,198✔
391

392
  // Push the int variable values
393
  scope_holder_push(&uf->scope,
682,198✔
394
                    &uf->eq_graph_addition_trail.size,
395
                    NULL);
396

397
  weq_graph_push(&uf->weq_graph);
682,198✔
398
  eq_graph_push(&uf->eq_graph);
682,198✔
399
}
682,198✔
400

401
static
402
void uf_plugin_pop(plugin_t* plugin) {
675,143✔
403
  uf_plugin_t* uf = (uf_plugin_t*) plugin;
675,143✔
404

405
  uint32_t old_eq_graph_addition_trail_size;
406

407
  // Pop the int variable values
408
  scope_holder_pop(&uf->scope,
675,143✔
409
                   &old_eq_graph_addition_trail_size,
410
                   NULL);
411

412
  weq_graph_pop(&uf->weq_graph);
675,143✔
413
  eq_graph_pop(&uf->eq_graph);
675,143✔
414

415
  // Re-add all the terms to eq graph
416
  uint32_t i;
417
  for (i = old_eq_graph_addition_trail_size; i < uf->eq_graph_addition_trail.size; ++ i) {
810,391✔
418
    term_t t = uf->eq_graph_addition_trail.data[i];
135,248✔
419
    uf_plugin_add_to_eq_graph(uf, t, false);
135,248✔
420
  }
421
  // We've already processed all the propagations, so we just reset it
422
  eq_graph_get_propagated_terms(&uf->eq_graph, NULL);
675,143✔
423

424
  // Clear the conflict
425
  ivector_reset(&uf->conflict);
675,143✔
426
}
675,143✔
427

428
bool value_cmp(void* data, void* v1_void, void* v2_void) {
56,083✔
429

430
  const mcsat_value_t* v1 = (mcsat_value_t*) v1_void;
56,083✔
431
  const mcsat_value_t* v2 = (mcsat_value_t*) v2_void;
56,083✔
432

433
  assert(v1->type == VALUE_RATIONAL);
434
  assert(v2->type == VALUE_RATIONAL);
435

436
  return q_cmp(&v1->q, &v2->q) < 0;
56,083✔
437
}
438

439
static
440
void uf_plugin_decide(plugin_t* plugin, variable_t x, trail_token_t* decide, bool must) {
160,953✔
441
  uf_plugin_t* uf = (uf_plugin_t*) plugin;
160,953✔
442

443
  if (ctx_trace_enabled(uf->ctx, "uf_plugin")) {
160,953✔
444
    ctx_trace_printf(uf->ctx, "uf_plugin_decide: ");
×
445
    ctx_trace_term(uf->ctx, variable_db_get_term(uf->ctx->var_db, x));
×
446
  }
447

448
  assert(eq_graph_is_trail_propagated(&uf->eq_graph));
449

450
  // Get the cached value
451
  const mcsat_value_t* x_cached_value = NULL;
160,953✔
452
  if (trail_has_cached_value(uf->ctx->trail, x)) {
160,953✔
453
    x_cached_value = trail_get_cached_value(uf->ctx->trail, x);
159,306✔
454
  }
455

456
  // Pick a value not in the forbidden set
457
  term_t x_term = variable_db_get_term(uf->ctx->var_db, x);
160,953✔
458
  pvector_t forbidden;
459
  init_pvector(&forbidden, 0);
160,953✔
460
  bool cache_ok = eq_graph_get_forbidden(&uf->eq_graph, x_term, &forbidden, x_cached_value);
160,953✔
461
  if (ctx_trace_enabled(uf->ctx, "uf_plugin::decide")) {
160,953✔
462
    ctx_trace_printf(uf->ctx, "picking !=");
×
463
    uint32_t i;
464
    for (i = 0; i < forbidden.size; ++ i) {
×
465
      const mcsat_value_t* v = forbidden.data[i];
×
466
      ctx_trace_printf(uf->ctx, " ");
×
467
      mcsat_value_print(v, ctx_trace_out(uf->ctx));
×
468
    }
469
    ctx_trace_printf(uf->ctx, "\n");
×
470
  }
471

472
  term_table_t *terms = uf->ctx->terms;
160,953✔
473
  int32_t picked_value = 0;
160,953✔
474
  if (!cache_ok) {
160,953✔
475
    // Pick smallest value not in forbidden list
476
    ptr_array_sort2(forbidden.data, forbidden.size, NULL, value_cmp);
5,827✔
477
    uint32_t i;
478
    // function types have different value picking strategy
479
    if (term_type_kind(terms, x_term) != FUNCTION_TYPE) {
5,827✔
480
      for (i = 0; i < forbidden.size; ++ i) {
24,271✔
481
        const mcsat_value_t* v = forbidden.data[i];
21,546✔
482
        assert(v->type == VALUE_RATIONAL);
483
        int32_t v_int = 0;
21,546✔
484
        bool ok = q_get32((rational_t*)&v->q, &v_int);
21,546✔
485
        (void) ok;
486
        assert(ok);
487
        if (picked_value < v_int) {
21,546✔
488
          // found a gap
489
          break;
1,759✔
490
        } else {
491
          picked_value = v_int + 1;
19,787✔
492
        }
493
      }
494
      // DECIDE_FUNCTION_VALUE_START is the starting point for function values
495
      assert(picked_value < DECIDE_FUNCTION_VALUE_START);
496
    } else {
497
      /* we pick different values for different functions. Equal
498
         functions get equal values via equality propagation. */
499
      if (forbidden.size > 0) {
1,343✔
500
        int32_t max_forbidden_val = 0;
293✔
501
        const mcsat_value_t* v = forbidden.data[forbidden.size - 1];
293✔
502
        assert(v->type == VALUE_RATIONAL);
503
        bool ok = q_get32((rational_t*)&v->q, &max_forbidden_val);
293✔
504
        (void) ok;
505
        assert(ok);
506
        picked_value = max_forbidden_val + 1;
293✔
507
      }
508
      if (picked_value < DECIDE_FUNCTION_VALUE_START) {
1,343✔
509
        // starting point for function values
510
        picked_value = DECIDE_FUNCTION_VALUE_START;
1,050✔
511
      }
512

513
      while (int_hset_member(&uf->fun_used_values, picked_value)) {
143,724✔
514
        picked_value += 1;
142,381✔
515
      }
516
      // save the used value
517
      int_hset_add(&uf->fun_used_values, picked_value);
1,343✔
518
    }
519
  } else {
520
    assert(x_cached_value->type == VALUE_RATIONAL);
521
    bool ok = q_get32((rational_t*)&x_cached_value->q, &picked_value);
155,126✔
522
    (void) ok;
523
    assert(ok);
524
  }
525

526
  if (ctx_trace_enabled(uf->ctx, "uf_plugin::decide")) {
160,953✔
527
    ctx_trace_printf(uf->ctx, "picked %"PRIi32"\n", picked_value);
×
528
  }
529

530
  // Remove temp
531
  delete_pvector(&forbidden);
160,953✔
532

533
  // Make the yices rational
534
  rational_t q;
535
  q_init(&q);
160,953✔
536
  q_set32(&q, picked_value);
160,953✔
537

538
  // Make the mcsat value
539
  mcsat_value_t value;
540
  mcsat_value_construct_rational(&value, &q);
160,953✔
541

542
  // Set the value
543
  decide->add(decide, x, &value);
160,953✔
544

545
  // Remove temps
546
  mcsat_value_destruct(&value);
160,953✔
547
  q_clear(&q);
160,953✔
548
}
160,953✔
549

550
static
551
void uf_plugin_gc_mark(plugin_t* plugin, gc_info_t* gc_vars) {
1,381✔
552
  uf_plugin_t* uf = (uf_plugin_t*) plugin;
1,381✔
553

554
  // Mark all asserted stuff, and all the children of marked stuff
555
  eq_graph_gc_mark(&uf->eq_graph, gc_vars, EQ_GRAPH_MARK_ALL);
1,381✔
556
}
1,381✔
557

558
static
559
void uf_plugin_gc_sweep(plugin_t* plugin, const gc_info_t* gc_vars) {
581✔
560
  // Should be nothing!
561
}
581✔
562

563
static
564
void uf_plugin_get_conflict(plugin_t* plugin, ivector_t* conflict) {
13,150✔
565
  uf_plugin_t* uf = (uf_plugin_t*) plugin;
13,150✔
566
  ivector_swap(conflict, &uf->conflict);
13,150✔
567
  ivector_reset(&uf->conflict);
13,150✔
568
}
13,150✔
569

570
static
571
term_t uf_plugin_explain_propagation(plugin_t* plugin, variable_t var, ivector_t* reasons) {
50,709✔
572
  uf_plugin_t* uf = (uf_plugin_t*) plugin;
50,709✔
573

574
  term_t var_term = variable_db_get_term(uf->ctx->var_db, var);
50,709✔
575

576
  if (ctx_trace_enabled(uf->ctx, "uf_plugin")) {
50,709✔
577
    ctx_trace_printf(uf->ctx, "uf_plugin_explain_propagation():\n");
×
578
    ctx_trace_term(uf->ctx, var_term);
×
579
    ctx_trace_printf(uf->ctx, "var = %"PRIu32"\n", var);
×
580
  }
581

582
  term_t subst = eq_graph_explain_term_propagation(&uf->eq_graph, var_term, reasons, NULL, &uf->tmp);
50,709✔
583
  uf_plugin_bump_terms_and_reset(uf, &uf->tmp);
50,709✔
584
  statistic_avg_add(uf->stats.avg_explanation_size, reasons->size);
50,709✔
585
  return subst;
50,709✔
586
}
587

588
static
589
bool uf_plugin_explain_evaluation(plugin_t* plugin, term_t t, int_mset_t* vars, mcsat_value_t* value) {
133,189✔
590
  uf_plugin_t* uf = (uf_plugin_t*) plugin;
133,189✔
591

592
  if (ctx_trace_enabled(uf->ctx, "uf_plugin")) {
133,189✔
593
    ctx_trace_printf(uf->ctx, "uf_plugin_explain_evaluation():\n");
×
594
    ctx_trace_term(uf->ctx, t);
×
595
  }
596

597
  term_table_t* terms = uf->ctx->terms;
133,189✔
598
  variable_db_t* var_db = uf->ctx->var_db;
133,189✔
599
  const mcsat_trail_t* trail = uf->ctx->trail;
133,189✔
600

601
  // Get all the variables and make sure they are all assigned.
602
  term_kind_t t_kind = term_kind(terms, t);
133,189✔
603
  if (t_kind == EQ_TERM) {
133,189✔
604
    composite_term_t* eq_desc = eq_term_desc(terms, t);
102,722✔
605
    term_t lhs_term = eq_desc->arg[0];
102,722✔
606
    term_t rhs_term = eq_desc->arg[1];
102,722✔
607
    variable_t lhs_var = variable_db_get_variable_if_exists(var_db, lhs_term);
102,722✔
608
    variable_t rhs_var = variable_db_get_variable_if_exists(var_db, rhs_term);
102,722✔
609

610
    // Add to variables
611
    if (lhs_var != variable_null) {
102,722✔
612
      int_mset_add(vars, lhs_var);
102,722✔
613
    }
614
    if (rhs_var != variable_null) {
102,722✔
615
      int_mset_add(vars, rhs_var);
102,722✔
616
    }
617

618
    // Check if both are assigned
619
    if (lhs_var == variable_null) {
102,722✔
620
      return false;
×
621
    }
622
    if (rhs_var == variable_null) {
102,722✔
623
      return false;
×
624
    }
625
    if (!trail_has_value(trail, lhs_var)) {
102,722✔
626
      return false;
66,768✔
627
    }
628
    if (!trail_has_value(trail, rhs_var)) {
35,954✔
629
      return false;
11,889✔
630
    }
631

632
    const mcsat_value_t* lhs_value = trail_get_value(trail, lhs_var);
24,065✔
633
    const mcsat_value_t* rhs_value = trail_get_value(trail, rhs_var);
24,065✔
634
    bool lhs_eq_rhs = mcsat_value_eq(lhs_value, rhs_value);
24,065✔
635
    bool negated = is_neg_term(t);
24,065✔
636
    if ((negated && (value->b != lhs_eq_rhs)) ||
24,065✔
637
        (!negated && (value->b == lhs_eq_rhs))) {
22,652✔
638
      return true;
24,059✔
639
    }
640

641
  }
642

643
  // Default: return false for cases like f(x) -> false, this is done in the
644
  // core
645
  return false;
30,473✔
646
}
647

648
static
649
void uf_plugin_set_exception_handler(plugin_t* plugin, jmp_buf* handler) {
×
650
  uf_plugin_t* uf = (uf_plugin_t*) plugin;
×
651
  uf->exception = handler;
×
652
}
×
653

654
typedef struct {
655
  term_table_t* terms;
656
  variable_db_t* var_db;
657
} model_sort_data_t;
658

659

660
// Compare two terms by function application: group same functions together
661
static
662
bool uf_plugin_build_model_compare(void *data, term_t t1, term_t t2) {
64✔
663
  model_sort_data_t* ctx = (model_sort_data_t*) data;
64✔
664

665
  int32_t t1_app = term_kind(ctx->terms, t1);
64✔
666
  int32_t t2_app = term_kind(ctx->terms, t2);
64✔
667

668
  if (t1_app != t2_app) {
64✔
669
    return t1_app < t2_app;
4✔
670
  }
671

672
  if (t1_app == APP_TERM) {
60✔
673
    t1_app = app_term_desc(ctx->terms, t1)->arg[0];
59✔
674
    t2_app = app_term_desc(ctx->terms, t2)->arg[0];
59✔
675
    if (t1_app != t2_app) {
59✔
676
      return t1_app < t2_app;
44✔
677
    }
678
  }
679
  return t1 < t2;
16✔
680
}
681

682
static inline
683
type_t get_function_application_type(term_table_t* terms, term_kind_t app_kind, term_t f) {
9✔
684

685
  type_table_t* types = terms->types;
9✔
686
  type_t reals = real_type(types);
9✔
687
  type_t ints = int_type(types);
9✔
688

689
  switch (app_kind) {
9✔
690
  case UPDATE_TERM:
8✔
691
  case APP_TERM:
692
    return term_type(terms, f);
8✔
693
  case ARITH_RDIV:
×
694
    return function_type(types, reals, 1, &reals);
×
695
  case ARITH_IDIV:
1✔
696
  case ARITH_MOD:
697
    return function_type(types, ints, 1, &ints);
1✔
698
  default:
×
699
    assert(false);
700
  }
701

702
  return NULL_TYPE;
×
703
}
704

705
static inline
706
value_t uf_plugin_get_term_value(uf_plugin_t* uf, value_table_t* vtbl, term_t t) {
41✔
707
  type_t t_type = term_type(uf->ctx->terms, t);
41✔
708
  variable_t t_var = variable_db_get_variable_if_exists(uf->ctx->var_db, t);
41✔
709
  if (t_var != variable_null) {
41✔
710
    const mcsat_value_t* t_value = trail_get_value(uf->ctx->trail, t_var);
41✔
711
    return mcsat_value_to_value(t_value, uf->ctx->types, t_type, vtbl);
41✔
712
  } else {
713
    assert(false);
714
    return null_value;
×
715
  }
716
}
717

718
static inline
719
value_t vtbl_mk_default(type_table_t* types, value_table_t *vtbl, type_t tau) {
9✔
720
  type_kind_t tau_kind = type_kind(types, tau);
9✔
721
  switch(tau_kind) {
9✔
722
  case BOOL_TYPE:
5✔
723
    return vtbl_mk_false(vtbl);
5✔
724
    break;
725
  case REAL_TYPE:
2✔
726
  case INT_TYPE:
727
    return vtbl_mk_int32(vtbl, 0);
2✔
728
    break;
729
  case BITVECTOR_TYPE:
×
730
    return vtbl_mk_bv_zero(vtbl, bv_type_size(types, tau));
×
731
    break;
732
  case UNINTERPRETED_TYPE:
2✔
733
    return vtbl_mk_const(vtbl, tau, 0, "");
2✔
734
    break;
735
  default:
×
736
    assert(false);
737
  }
738

739
  return null_value;
×
740
}
741

742
static
743
void uf_plugin_build_model(plugin_t* plugin, model_t* model) {
23✔
744
  uf_plugin_t* uf = (uf_plugin_t*) plugin;
23✔
745

746
  term_table_t* terms = uf->ctx->terms;
23✔
747
  value_table_t* vtbl = &model->vtbl;
23✔
748
  variable_db_t* var_db = uf->ctx->var_db;
23✔
749
  const mcsat_trail_t* trail = uf->ctx->trail;
23✔
750

751
  // Data for sorting
752
  model_sort_data_t sort_ctx;
753
  sort_ctx.terms = terms;
23✔
754
  sort_ctx.var_db = var_db;
23✔
755

756
  // Sort the terms from the equality graph by function used: we get a list of function
757
  // applications where we can easily collect them by linear scan
758
  ivector_t app_terms;
759
  init_ivector(&app_terms, uf->eq_graph_addition_trail.size);
23✔
760
  ivector_copy(&app_terms, uf->eq_graph_addition_trail.data, uf->eq_graph_addition_trail.size);
23✔
761
  int_array_sort2(app_terms.data, app_terms.size, &sort_ctx, uf_plugin_build_model_compare);
23✔
762

763
  // Mappings that we collect for a single function
764
  ivector_t mappings;
765
  init_ivector(&mappings, 0);
23✔
766

767
  // Temp for arguments of a one concrete mapping
768
  ivector_t arguments;
769
  init_ivector(&arguments, 0);
23✔
770

771
  // Got through all the representatives that have a set value and
772
  // - while same function, collect the concrete mappings
773
  // - if different function, construct the function and add to model
774
  uint32_t i;
775
  term_t app_f = NULL_TERM, prev_app_f = NULL_TERM;  // Current and previous function symbol
23✔
776
  term_t app_term, prev_app_term = NULL_TERM; // Current and previous function application term
23✔
777
  term_kind_t app_kind = UNUSED_TERM, prev_app_kind = UNUSED_TERM; // Kind of the current and previous function
23✔
778
  bool app_construct = true; // Whether to construct it
23✔
779
  for (i = 0; i < app_terms.size; ++ i) {
50✔
780

781
    // Current representative application
782
    app_term = app_terms.data[i];
27✔
783

784
    // Only need to do functions and uninterpreted
785
    app_kind = term_kind(terms, app_term);
27✔
786
    switch (app_kind) {
27✔
787
    case APP_TERM:
26✔
788
    case ARITH_RDIV:
789
    case ARITH_IDIV:
790
    case ARITH_MOD:
791
      app_construct = true;
26✔
792
      break;
26✔
793
    default:
1✔
794
      app_construct = false;
1✔
795
      break;
1✔
796
    }
797

798
    composite_term_t* app_comp = composite_term_desc(terms, app_term);
27✔
799

800
    if (ctx_trace_enabled(uf->ctx, "uf_plugin::model")) {
27✔
801
      ctx_trace_printf(uf->ctx, "processing app rep:");
×
802
      ctx_trace_term(uf->ctx, app_term);
×
803
    }
804

805
    if (app_kind == APP_TERM) {
27✔
806
      app_f = app_comp->arg[0];
18✔
807
    } else {
808
      app_f = NULL_TERM;
9✔
809
      // Division only if division by 0
810
      assert(app_comp->arity == 2);
811
      term_t divisor_term = app_comp->arg[1];
9✔
812
      variable_t divisor_var = variable_db_get_variable(var_db, divisor_term);
9✔
813
      assert(trail_has_value(trail, divisor_var));
814
      const mcsat_value_t* divisor_value = trail_get_value(trail, divisor_var);
9✔
815
      if (!mcsat_value_is_zero(divisor_value)) {
9✔
816
        continue;
8✔
817
      }
818
    }
819

820
    // If we changed the function, construct the previous one
821
    if (prev_app_term != NULL_TERM) {
19✔
822
      if (app_f != prev_app_f || app_kind != prev_app_kind) {
16✔
823
        type_t tau = get_function_application_type(terms, prev_app_kind, prev_app_f);
7✔
824
        type_t range_tau = function_type_range(terms->types, tau);
7✔
825
        value_t f_value = vtbl_mk_function(vtbl, tau, mappings.size, mappings.data, vtbl_mk_default(terms->types, vtbl, range_tau));
7✔
826
        switch (prev_app_kind) {
7✔
827
        case ARITH_RDIV:
×
828
          vtbl_set_zero_rdiv(vtbl, f_value);
×
829
          break;
×
830
        case ARITH_IDIV:
×
831
          vtbl_set_zero_idiv(vtbl, f_value);
×
832
          break;
×
833
        case ARITH_MOD:
×
834
          vtbl_set_zero_mod(vtbl, f_value);
×
835
          break;
×
836
        case APP_TERM:
7✔
837
          model_map_term(model, prev_app_f, f_value);
7✔
838
          break;
7✔
839
        default:
7✔
840
          assert(false);
841
        }
842
        // Reset the mapping
843
        ivector_reset(&mappings);
7✔
844
      }
845
    }
846

847
    // Next concrete mapping f : (x1, x2, ..., xn) -> v
848
    // a) Get the v value
849
    value_t v = uf_plugin_get_term_value(uf, vtbl, app_term);
19✔
850
    // b) Get the argument values
851
    uint32_t arg_i;
852
    uint32_t arg_start = app_kind == APP_TERM ? 1 : 0;
19✔
853
    uint32_t arg_end = app_kind == APP_TERM ? app_comp->arity : app_comp->arity - 1;
19✔
854
    ivector_reset(&arguments);
19✔
855
    for (arg_i = arg_start; arg_i < arg_end; ++ arg_i) {
41✔
856
      term_t arg_term = app_comp->arg[arg_i];
22✔
857
      value_t arg_v = uf_plugin_get_term_value(uf, vtbl, arg_term);
22✔
858
      ivector_push(&arguments, arg_v);
22✔
859
    }
860
    // c) Construct the concrete mapping, and save in the list for f
861
    value_t map_value = vtbl_mk_map(vtbl, arguments.size, arguments.data, v);
19✔
862
    ivector_push(&mappings, map_value);
19✔
863

864
    // Remember the previous one
865
    prev_app_f = app_f;
19✔
866
    prev_app_term = app_term;
19✔
867
    prev_app_kind = app_kind;
19✔
868
  }
869

870
  // Since we make functions when we see a new one, we also construct the last function
871
  if (app_terms.size > 0 && mappings.size > 0 && app_construct) {
23✔
872
    type_t tau = get_function_application_type(terms, app_kind, app_f);
2✔
873
    type_t range_tau = function_type_range(terms->types, tau);
2✔
874
    value_t f_value = vtbl_mk_function(vtbl, tau, mappings.size, mappings.data, vtbl_mk_default(terms->types, vtbl, range_tau));
2✔
875
    switch (app_kind) {
2✔
876
    case ARITH_RDIV:
×
877
      vtbl_set_zero_rdiv(vtbl, f_value);
×
878
      break;
×
879
    case ARITH_IDIV:
×
880
      vtbl_set_zero_idiv(vtbl, f_value);
×
881
      break;
×
882
    case ARITH_MOD:
1✔
883
      vtbl_set_zero_mod(vtbl, f_value);
1✔
884
      break;
1✔
885
    case APP_TERM:
1✔
886
      model_map_term(model, app_f, f_value);
1✔
887
      break;
1✔
888
    default:
21✔
889
      assert(false);
890
    }
891
  }
892

893
  // Remove temps
894
  delete_ivector(&arguments);
23✔
895
  delete_ivector(&mappings);
23✔
896
  delete_ivector(&app_terms);
23✔
897
}
23✔
898

899
plugin_t* uf_plugin_allocator(void) {
634✔
900
  uf_plugin_t* plugin = safe_malloc(sizeof(uf_plugin_t));
634✔
901
  plugin_construct((plugin_t*) plugin);
634✔
902
  plugin->plugin_interface.construct             = uf_plugin_construct;
634✔
903
  plugin->plugin_interface.destruct              = uf_plugin_destruct;
634✔
904
  plugin->plugin_interface.new_term_notify       = uf_plugin_new_term_notify;
634✔
905
  plugin->plugin_interface.new_lemma_notify      = NULL;
634✔
906
  plugin->plugin_interface.event_notify          = NULL;
634✔
907
  plugin->plugin_interface.propagate             = uf_plugin_propagate;
634✔
908
  plugin->plugin_interface.decide                = uf_plugin_decide;
634✔
909
  plugin->plugin_interface.decide_assignment     = NULL;
634✔
910
  plugin->plugin_interface.learn                 = uf_plugin_learn;
634✔
911
  plugin->plugin_interface.get_conflict          = uf_plugin_get_conflict;
634✔
912
  plugin->plugin_interface.explain_propagation   = uf_plugin_explain_propagation;
634✔
913
  plugin->plugin_interface.explain_evaluation    = uf_plugin_explain_evaluation;
634✔
914
  plugin->plugin_interface.push                  = uf_plugin_push;
634✔
915
  plugin->plugin_interface.pop                   = uf_plugin_pop;
634✔
916
  plugin->plugin_interface.build_model           = uf_plugin_build_model;
634✔
917
  plugin->plugin_interface.gc_mark               = uf_plugin_gc_mark;
634✔
918
  plugin->plugin_interface.gc_sweep              = uf_plugin_gc_sweep;
634✔
919
  plugin->plugin_interface.set_exception_handler = uf_plugin_set_exception_handler;
634✔
920

921
  return (plugin_t*) plugin;
634✔
922
}
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

© 2025 Coveralls, Inc