GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/theory/bv/bv_inequality_graph.cpp Lines: 284 300 94.7 %
Date: 2021-05-22 Branches: 510 1247 40.9 %

Line Exec Source
1
/******************************************************************************
2
 * Top contributors (to current version):
3
 *   Liana Hadarean, Aina Niemetz, Mathias Preiner
4
 *
5
 * This file is part of the cvc5 project.
6
 *
7
 * Copyright (c) 2009-2021 by the authors listed in the file AUTHORS
8
 * in the top-level source directory and their institutional affiliations.
9
 * All rights reserved.  See the file COPYING in the top-level source
10
 * directory for licensing information.
11
 * ****************************************************************************
12
 *
13
 * A graph representation of the currently asserted bv inequalities.
14
 */
15
16
#include "theory/bv/bv_inequality_graph.h"
17
#include "theory/bv/theory_bv_utils.h"
18
19
using namespace std;
20
using namespace cvc5;
21
using namespace cvc5::context;
22
using namespace cvc5::theory;
23
using namespace cvc5::theory::bv;
24
using namespace cvc5::theory::bv::utils;
25
26
const TermId cvc5::theory::bv::UndefinedTermId = -1;
27
const ReasonId cvc5::theory::bv::UndefinedReasonId = -1;
28
const ReasonId cvc5::theory::bv::AxiomReasonId = -2;
29
30
1158735
bool InequalityGraph::addInequality(TNode a, TNode b, bool strict, TNode reason) {
31
1158735
  Debug("bv-inequality") << "InequalityGraph::addInequality " << a << " " << b << " strict: " << strict << "\n";
32
33
1158735
  TermId id_a = registerTerm(a);
34
1158735
  TermId id_b = registerTerm(b);
35
1158735
  ReasonId id_reason = registerReason(reason);
36
37
1158735
  Assert(!(isConst(id_a) && isConst(id_b)));
38
2317470
  BitVector a_val = getValue(id_a);
39
2317470
  BitVector b_val = getValue(id_b);
40
41
1158735
  unsigned bitwidth = utils::getSize(a);
42
2317470
  BitVector diff = strict ? BitVector(bitwidth, 1u) : BitVector(bitwidth, 0u);
43
44
1158735
  if (a_val + diff < a_val) {
45
    // we have an overflow
46
86
    std::vector<ReasonId> conflict;
47
43
    conflict.push_back(id_reason);
48
43
    computeExplanation(UndefinedTermId, id_a, conflict);
49
43
    setConflict(conflict);
50
43
    return false;
51
  }
52
53
1158692
  if (a_val + diff <= b_val) {
54
    // the inequality is true in the current partial model
55
    // we still add the edge because it may not be true later (cardinality)
56
943029
    addEdge(id_a, id_b, strict, id_reason);
57
943029
    return true;
58
  }
59
60
215663
  if (isConst(id_b) && a_val + diff > b_val) {
61
    // we must be in a conflict since a has the minimum value that
62
    // satisifes the constraints
63
366
    std::vector<ReasonId> conflict;
64
183
    conflict.push_back(id_reason);
65
183
    computeExplanation(UndefinedTermId, id_a, conflict);
66
183
    Debug("bv-inequality") << "InequalityGraph::addInequality conflict: constant UB \n";
67
183
    setConflict(conflict);
68
183
    return false;
69
  }
70
71
  // add the inequality edge
72
215480
  addEdge(id_a, id_b, strict, id_reason);
73
430960
  BFSQueue queue(&d_modelValues);
74
215480
  Assert(hasModelValue(id_a));
75
215480
  queue.push(id_a);
76
215480
  return processQueue(queue, id_a);
77
}
78
79
4310493
bool InequalityGraph::updateValue(TermId id, ModelValue new_mv, TermId start, bool& changed) {
80
8620986
  BitVector lower_bound = new_mv.value;
81
82
4310493
  if (isConst(id)) {
83
112798
    if (getValue(id) < lower_bound) {
84
209
      Debug("bv-inequality") << "Conflict: constant " << getValue(id) << "\n";
85
418
      std::vector<ReasonId> conflict;
86
209
      TermId parent = new_mv.parent;
87
209
      ReasonId reason = new_mv.reason;
88
209
      conflict.push_back(reason);
89
209
      computeExplanation(UndefinedTermId, parent, conflict);
90
209
      Debug("bv-inequality") << "InequalityGraph::addInequality conflict: constant\n";
91
209
      setConflict(conflict);
92
209
      return false;
93
    }
94
  } else {
95
    // if not constant we can try to update the value
96
4197695
    if (getValue(id) < lower_bound) {
97
      // if we are updating the term we started with we must be in a cycle
98
229996
      if (id == start) {
99
153
        TermId parent = new_mv.parent;
100
153
        ReasonId reason = new_mv.reason;
101
306
        std::vector<TermId> conflict;
102
153
        conflict.push_back(reason);
103
153
        computeExplanation(id, parent, conflict);
104
153
        Debug("bv-inequality") << "InequalityGraph::addInequality conflict: cycle \n";
105
153
        setConflict(conflict);
106
153
        return false;
107
      }
108
459686
      Debug("bv-inequality-internal") << "Updating " << getTermNode(id)
109
459686
                                      << "  from " << getValue(id) << "\n"
110
229843
                                      << "  to " << lower_bound << "\n";
111
229843
      changed = true;
112
229843
      setModelValue(id, new_mv);
113
    }
114
  }
115
4310131
  return true;
116
}
117
118
660315
bool InequalityGraph::processQueue(BFSQueue& queue, TermId start) {
119
1105150
  while (!queue.empty()) {
120
445227
    TermId current = queue.top();
121
445227
    queue.pop();
122
445227
    Debug("bv-inequality-internal") << "InequalityGraph::processQueue processing " << getTermNode(current) << "\n";
123
124
890062
    BitVector current_value = getValue(current);
125
126
445227
    unsigned size = getBitwidth(current);
127
890062
    const BitVector zero(size, 0u);
128
890062
    const BitVector one(size, 1u);
129
130
445227
    const Edges& edges = getEdges(current);
131
4755358
    for (Edges::const_iterator it = edges.begin(); it!= edges.end(); ++it) {
132
4310523
      TermId next = it->next;
133
4310523
      ReasonId reason = it->reason;
134
135
4540366
      const BitVector increment = it->strict ? one : zero;
136
4540366
      const BitVector next_lower_bound = current_value + increment;
137
138
4310523
      if (next_lower_bound < current_value) {
139
        // it means we have an overflow and hence a conflict
140
60
        std::vector<TermId> conflict;
141
30
        conflict.push_back(it->reason);
142
30
        Assert(hasModelValue(start));
143
30
        ReasonId start_reason = getModelValue(start).reason;
144
30
        if (start_reason != UndefinedReasonId) {
145
4
          conflict.push_back(start_reason);
146
        }
147
30
        computeExplanation(UndefinedTermId, current, conflict);
148
30
        Debug("bv-inequality") << "InequalityGraph::addInequality conflict: cycle \n";
149
30
        setConflict(conflict);
150
30
        return false;
151
      }
152
153
4540336
      ModelValue new_mv(next_lower_bound, current, reason);
154
4310493
      bool updated = false;
155
4310493
      if (!updateValue(next, new_mv, start, updated)) {
156
362
        return false;
157
      }
158
159
4421039
      if (next == start) {
160
        // we know what we didn't update start or we would have had a conflict
161
        // this means we are in a cycle where all the values are forced to be equal
162
110908
        Debug("bv-inequality-internal") << "InequalityGraph::processQueue equal cycle.";
163
110908
        continue;
164
      }
165
166
8168603
      if (!updated) {
167
        // if we didn't update current we don't need to add to the queue it's children
168
3969380
        Debug("bv-inequality-internal") << "  unchanged " << getTermNode(next) << "\n";
169
3969380
        continue;
170
      }
171
172
229843
      queue.push(next);
173
229843
      Debug("bv-inequality-internal") << "   enqueue " << getTermNode(next) << "\n";
174
    }
175
  }
176
215088
  return true;
177
}
178
179
15701
void InequalityGraph::computeExplanation(TermId from, TermId to, std::vector<ReasonId>& explanation) {
180
15701
  if(Debug.isOn("bv-inequality")) {
181
    if (from == UndefinedTermId) {
182
      Debug("bv-inequality") << "InequalityGraph::computeExplanation " << getTermNode(to) << "\n";
183
    } else {
184
      Debug("bv-inequality") << "InequalityGraph::computeExplanation " << getTermNode(from) <<" => "
185
                             << getTermNode(to) << "\n";
186
    }
187
  }
188
189
31402
  TermIdSet seen;
190
191
17339
  while(hasReason(to) && from != to && !seen.count(to)) {
192
819
    seen.insert(to);
193
1638
    const ModelValue& exp = getModelValue(to);
194
819
    Assert(exp.reason != UndefinedReasonId);
195
819
    explanation.push_back(exp.reason);
196
819
    Assert(exp.parent != UndefinedTermId);
197
819
    to = exp.parent;
198
1638
    Debug("bv-inequality-internal") << "  parent: " << getTermNode(to) << "\n"
199
819
                                    << "  reason: " << getReasonNode(exp.reason) << "\n";
200
  }
201
15701
}
202
203
1158509
void InequalityGraph::addEdge(TermId a, TermId b, bool strict, TermId reason) {
204
2317018
  Debug("bv-inequality-internal") << "InequalityGraph::addEdge " << getTermNode(a) << " => " << getTermNode(b) << "\n"
205
1158509
                                  << " strict ? " << strict << "\n";
206
1158509
  Edges& edges = getEdges(a);
207
1158509
  InequalityEdge new_edge(b, strict, reason);
208
1158509
  edges.push_back(new_edge);
209
1158509
  d_undoStack.push_back(std::make_pair(a, new_edge));
210
1158509
  d_undoStackIndex = d_undoStackIndex + 1;
211
1158509
}
212
213
927954
void InequalityGraph::initializeModelValue(TNode node) {
214
927954
  TermId id = getTermId(node);
215
927954
  Assert(!hasModelValue(id));
216
927954
  bool isConst = node.getKind() == kind::CONST_BITVECTOR;
217
927954
  unsigned size = utils::getSize(node);
218
1855908
  BitVector value = isConst? node.getConst<BitVector>() : BitVector(size, 0u);
219
927954
  setModelValue(id, ModelValue(value, UndefinedTermId, UndefinedReasonId));
220
927954
}
221
222
678577
bool InequalityGraph::isRegistered(TNode term) const {
223
678577
  return d_termNodeToIdMap.find(term) != d_termNodeToIdMap.end();
224
}
225
226
2317504
TermId InequalityGraph::registerTerm(TNode term) {
227
2317504
  if (d_termNodeToIdMap.find(term) != d_termNodeToIdMap.end()) {
228
2287872
    TermId id = d_termNodeToIdMap[term];
229
2287872
    if (!hasModelValue(id)) {
230
      // we could have backtracked and
231
732862
      initializeModelValue(term);
232
    }
233
2287872
    return id;
234
  }
235
236
  // store in node mapping
237
29632
  TermId id = d_termNodes.size();
238
29632
  Debug("bv-inequality-internal") << "InequalityGraph::registerTerm " << term << " => id"<< id << "\n";
239
240
29632
  d_termNodes.push_back(term);
241
29632
  d_termNodeToIdMap[term] = id;
242
243
  // create InequalityNode
244
29632
  unsigned size = utils::getSize(term);
245
246
29632
  bool isConst = term.getKind() == kind::CONST_BITVECTOR;
247
29632
  InequalityNode ineq = InequalityNode(id, size, isConst);
248
249
29632
  Assert(d_ineqNodes.size() == id);
250
29632
  d_ineqNodes.push_back(ineq);
251
252
29632
  Assert(d_ineqEdges.size() == id);
253
29632
  d_ineqEdges.push_back(Edges());
254
255
29632
  initializeModelValue(term);
256
257
29632
  return id;
258
}
259
260
1158735
ReasonId InequalityGraph::registerReason(TNode reason) {
261
1158735
  if (d_reasonToIdMap.find(reason) != d_reasonToIdMap.end()) {
262
1115230
    return d_reasonToIdMap[reason];
263
  }
264
43505
  d_reasonSet.insert(reason);
265
43505
  ReasonId id = d_reasonNodes.size();
266
43505
  d_reasonNodes.push_back(reason);
267
43505
  d_reasonToIdMap[reason] = id;
268
43505
  Debug("bv-inequality-internal") << "InequalityGraph::registerReason " << reason << " => id"<< id << "\n";
269
43505
  return id;
270
}
271
272
2260
TNode InequalityGraph::getReasonNode(ReasonId id) const {
273
2260
  Assert(d_reasonNodes.size() > id);
274
2260
  return d_reasonNodes[id];
275
}
276
277
19597581
TNode InequalityGraph::getTermNode(TermId id) const {
278
19597581
  Assert(d_termNodes.size() > id);
279
19597581
  return d_termNodes[id];
280
}
281
282
1545660
TermId InequalityGraph::getTermId(TNode node) const {
283
1545660
  Assert(d_termNodeToIdMap.find(node) != d_termNodeToIdMap.end());
284
1545660
  return d_termNodeToIdMap.find(node)->second;
285
}
286
287
618
void InequalityGraph::setConflict(const std::vector<ReasonId>& conflict) {
288
618
  Assert(!d_inConflict);
289
618
  d_inConflict = true;
290
618
  d_conflict.clear();
291
1862
  for (unsigned i = 0; i < conflict.size(); ++i) {
292
1244
    if (conflict[i] != AxiomReasonId) {
293
1244
      d_conflict.push_back(getReasonNode(conflict[i]));
294
    }
295
  }
296
618
  if (Debug.isOn("bv-inequality")) {
297
    Debug("bv-inequality") << "InequalityGraph::setConflict \n";
298
    for (unsigned i = 0; i < d_conflict.size(); ++i) {
299
      Debug("bv-inequality") << "   " << d_conflict[i] <<"\n";
300
    }
301
  }
302
618
}
303
304
618
void InequalityGraph::getConflict(std::vector<TNode>& conflict) {
305
1862
  for (unsigned i = 0; i < d_conflict.size(); ++i) {
306
1244
    conflict.push_back(d_conflict[i]);
307
  }
308
618
}
309
310
1157797
void InequalityGraph::setModelValue(TermId term, const ModelValue& mv) {
311
1157797
  d_modelValues[term] = mv;
312
1157797
}
313
314
17369
InequalityGraph::ModelValue InequalityGraph::getModelValue(TermId term) const {
315
17369
  Assert(d_modelValues.find(term) != d_modelValues.end());
316
17369
  return (*(d_modelValues.find(term))).second;
317
}
318
319
11970024
bool InequalityGraph::hasModelValue(TermId id) const {
320
11970024
  return d_modelValues.find(id) != d_modelValues.end();
321
}
322
323
7920982
BitVector InequalityGraph::getValue(TermId id) const {
324
7920982
  Assert(hasModelValue(id));
325
7920982
  return (*(d_modelValues.find(id))).second.value;
326
}
327
328
16520
bool InequalityGraph::hasReason(TermId id) const {
329
33040
  const ModelValue& mv = getModelValue(id);
330
33040
  return mv.reason != UndefinedReasonId;
331
}
332
333
361684
bool InequalityGraph::addDisequality(TNode a, TNode b, TNode reason) {
334
361684
  Debug("bv-inequality") << "InequalityGraph::addDisequality " << reason << "\n";
335
361684
  d_disequalities.push_back(reason);
336
337
361684
  if (!isRegistered(a) || !isRegistered(b)) {
338
    //splitDisequality(reason);
339
52831
    return true;
340
  }
341
308853
  TermId id_a = getTermId(a);
342
308853
  TermId id_b = getTermId(b);
343
308853
  if (!hasModelValue(id_a)) {
344
94985
    initializeModelValue(a);
345
  }
346
308853
  if (!hasModelValue(id_b)) {
347
70475
    initializeModelValue(b);
348
  }
349
617706
  const BitVector val_a = getValue(id_a);
350
617706
  const BitVector val_b = getValue(id_b);
351
308853
  if (val_a == val_b) {
352
106066
    if (a.getKind() == kind::CONST_BITVECTOR) {
353
      // then we know b cannot be smaller  than the assigned value so we try to make it larger
354
1042
      std::vector<ReasonId> explanation_ids;
355
521
      computeExplanation(UndefinedTermId, id_b, explanation_ids);
356
1042
      std::vector<TNode> explanation_nodes;
357
521
      explanation_nodes.push_back(reason);
358
575
      for (unsigned i = 0; i < explanation_ids.size(); ++i) {
359
54
        explanation_nodes.push_back(getReasonNode(explanation_ids[i]));
360
      }
361
1042
      Node explanation = utils::mkAnd(explanation_nodes);
362
521
      d_reasonSet.insert(explanation);
363
521
      return addInequality(a, b, true, explanation);
364
    }
365
105545
    if (b.getKind() == kind::CONST_BITVECTOR) {
366
      // then we know b cannot be smaller  than the assigned value so we try to make it larger
367
29124
      std::vector<ReasonId> explanation_ids;
368
14562
      computeExplanation(UndefinedTermId, id_a, explanation_ids);
369
29124
      std::vector<TNode> explanation_nodes;
370
14562
      explanation_nodes.push_back(reason);
371
14705
      for (unsigned i = 0; i < explanation_ids.size(); ++i) {
372
143
        explanation_nodes.push_back(getReasonNode(explanation_ids[i]));
373
      }
374
29124
      Node explanation = utils::mkAnd(explanation_nodes);
375
14562
      d_reasonSet.insert(explanation);
376
14562
      return addInequality(b, a, true, explanation);
377
    }
378
    // if none of the terms are constants just add the lemma
379
    //splitDisequality(reason);
380
  } else {
381
405574
    Debug("bv-inequality-internal") << "Disequal: " << a << " => " << val_a.toString(10) << "\n"
382
202787
                                    << "          " << b << " => " << val_b.toString(10) << "\n";
383
  }
384
293770
  return true;
385
}
386
387
// void InequalityGraph::splitDisequality(TNode diseq) {
388
//   Debug("bv-inequality-internal")<<"InequalityGraph::splitDisequality " <<
389
//   diseq <<"\n"; Assert (diseq.getKind() == kind::NOT &&
390
//   diseq[0].getKind() == kind::EQUAL); if
391
//   (d_disequalitiesAlreadySplit.find(diseq) ==
392
//   d_disequalitiesAlreadySplit.end()) {
393
//     d_disequalitiesToSplit.push_back(diseq);
394
//   }
395
// }
396
397
2239992
void InequalityGraph::backtrack() {
398
2239992
  Debug("bv-inequality-internal") << "InequalityGraph::backtrack()\n";
399
2239992
  int size = d_undoStack.size();
400
3398501
  for (int i = size - 1; i >= (int)d_undoStackIndex.get(); --i) {
401
1158509
    Assert(!d_undoStack.empty());
402
1158509
    TermId id = d_undoStack.back().first;
403
1158509
    InequalityEdge edge = d_undoStack.back().second;
404
1158509
    d_undoStack.pop_back();
405
406
2317018
    Debug("bv-inequality-internal") << " remove edge " << getTermNode(id) << " => "
407
1158509
                                                       << getTermNode(edge.next) <<"\n";
408
1158509
    Edges& edges = getEdges(id);
409
11246787
    for (Edges::const_iterator it = edges.begin(); it!= edges.end(); ++it) {
410
10088278
      Debug("bv-inequality-internal") << getTermNode(it->next) <<" " << it->strict << "\n";
411
    }
412
1158509
    Assert(!edges.empty());
413
1158509
    Assert(edges.back() == edge);
414
1158509
    edges.pop_back();
415
  }
416
2239992
}
417
418
7
Node InequalityGraph::makeDiseqSplitLemma(TNode diseq)
419
{
420
7
  Assert(diseq.getKind() == kind::NOT && diseq[0].getKind() == kind::EQUAL);
421
7
  NodeManager* nm = NodeManager::currentNM();
422
14
  TNode a = diseq[0][0];
423
14
  TNode b = diseq[0][1];
424
14
  Node a_lt_b = nm->mkNode(kind::BITVECTOR_ULT, a, b);
425
14
  Node b_lt_a = nm->mkNode(kind::BITVECTOR_ULT, b, a);
426
14
  Node eq = diseq[0];
427
7
  Node lemma = nm->mkNode(kind::OR, a_lt_b, b_lt_a, eq);
428
14
  return lemma;
429
}
430
431
44
void InequalityGraph::checkDisequalities(std::vector<Node>& lemmas) {
432
66
  for (CDQueue<TNode>::const_iterator it = d_disequalities.begin(); it != d_disequalities.end(); ++it) {
433
22
    if (d_disequalitiesAlreadySplit.find(*it) == d_disequalitiesAlreadySplit.end()) {
434
      // if we haven't already split on this disequality
435
34
      TNode diseq = *it;
436
17
      TermId a_id = registerTerm(diseq[0][0]);
437
17
      TermId b_id = registerTerm(diseq[0][1]);
438
17
      if (getValue(a_id) == getValue(b_id)) {
439
7
        lemmas.push_back(makeDiseqSplitLemma(diseq));
440
7
        d_disequalitiesAlreadySplit.insert(diseq);
441
      }
442
    }
443
  }
444
44
}
445
446
bool InequalityGraph::isLessThan(TNode a, TNode b) {
447
  Assert(isRegistered(a) && isRegistered(b));
448
  Unimplemented();
449
}
450
451
74
bool InequalityGraph::hasValueInModel(TNode node) const {
452
74
  if (isRegistered(node)) {
453
    TermId id = getTermId(node);
454
    return hasModelValue(id);
455
  }
456
74
  return false;
457
}
458
459
BitVector InequalityGraph::getValueInModel(TNode node) const {
460
  TermId id = getTermId(node);
461
  Assert(hasModelValue(id));
462
  return getValue(id);
463
}
464
465
31
void InequalityGraph::getAllValuesInModel(std::vector<Node>& assignments)
466
{
467
31
  NodeManager* nm = NodeManager::currentNM();
468
186
  for (ModelValues::const_iterator it = d_modelValues.begin();
469
186
       it != d_modelValues.end();
470
       ++it)
471
  {
472
155
    TermId id = (*it).first;
473
310
    BitVector value = (*it).second.value;
474
310
    TNode var = getTermNode(id);
475
310
    Node constant = utils::mkConst(value);
476
310
    Node assignment = nm->mkNode(kind::EQUAL, var, constant);
477
155
    assignments.push_back(assignment);
478
155
    Debug("bitvector-model") << "   " << var << " => " << constant << "\n";
479
  }
480
28222
}