GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/theory/ext_theory.cpp Lines: 163 251 64.9 %
Date: 2021-11-07 Branches: 283 725 39.0 %

Line Exec Source
1
/******************************************************************************
2
 * Top contributors (to current version):
3
 *   Andrew Reynolds, Tim King
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
 * Extended theory interface.
14
 *
15
 * This implements a generic module, used by theory solvers, for performing
16
 * "context-dependent simplification", as described in Reynolds et al
17
 * "Designing Theory Solvers with Extensions", FroCoS 2017.
18
 */
19
20
#include "theory/ext_theory.h"
21
22
#include "base/check.h"
23
#include "smt/smt_statistics_registry.h"
24
#include "theory/output_channel.h"
25
#include "theory/quantifiers_engine.h"
26
#include "theory/rewriter.h"
27
#include "theory/substitutions.h"
28
29
using namespace std;
30
31
namespace cvc5 {
32
namespace theory {
33
34
const char* toString(ExtReducedId id)
35
{
36
  switch (id)
37
  {
38
    case ExtReducedId::SR_CONST: return "SR_CONST";
39
    case ExtReducedId::REDUCTION: return "REDUCTION";
40
    case ExtReducedId::ARITH_SR_ZERO: return "ARITH_SR_ZERO";
41
    case ExtReducedId::ARITH_SR_LINEAR: return "ARITH_SR_LINEAR";
42
    case ExtReducedId::STRINGS_SR_CONST: return "STRINGS_SR_CONST";
43
    case ExtReducedId::STRINGS_NEG_CTN_DEQ: return "STRINGS_NEG_CTN_DEQ";
44
    case ExtReducedId::STRINGS_POS_CTN: return "STRINGS_POS_CTN";
45
    case ExtReducedId::STRINGS_CTN_DECOMPOSE: return "STRINGS_CTN_DECOMPOSE";
46
    case ExtReducedId::STRINGS_REGEXP_INTER: return "STRINGS_REGEXP_INTER";
47
    case ExtReducedId::STRINGS_REGEXP_INTER_SUBSUME:
48
      return "STRINGS_REGEXP_INTER_SUBSUME";
49
    case ExtReducedId::STRINGS_REGEXP_INCLUDE: return "STRINGS_REGEXP_INCLUDE";
50
    case ExtReducedId::STRINGS_REGEXP_INCLUDE_NEG:
51
      return "STRINGS_REGEXP_INCLUDE_NEG";
52
    default: return "?ExtReducedId?";
53
  }
54
}
55
56
std::ostream& operator<<(std::ostream& out, ExtReducedId id)
57
{
58
  out << toString(id);
59
  return out;
60
}
61
62
bool ExtTheoryCallback::getCurrentSubstitution(
63
    int effort,
64
    const std::vector<Node>& vars,
65
    std::vector<Node>& subs,
66
    std::map<Node, std::vector<Node> >& exp)
67
{
68
  return false;
69
}
70
bool ExtTheoryCallback::isExtfReduced(
71
    int effort, Node n, Node on, std::vector<Node>& exp, ExtReducedId& id)
72
{
73
  id = ExtReducedId::SR_CONST;
74
  return n.isConst();
75
}
76
bool ExtTheoryCallback::getReduction(int effort,
77
                                    Node n,
78
                                    Node& nr,
79
                                    bool& isSatDep)
80
{
81
  return false;
82
}
83
84
24969
ExtTheory::ExtTheory(Env& env, ExtTheoryCallback& p, TheoryInferenceManager& im)
85
    : EnvObj(env),
86
      d_parent(p),
87
      d_im(im),
88
      d_ext_func_terms(context()),
89
      d_extfExtReducedIdMap(context()),
90
24969
      d_ci_inactive(userContext()),
91
      d_has_extf(context()),
92
24969
      d_lemmas(userContext()),
93
74907
      d_pp_lemmas(userContext())
94
{
95
24969
  d_true = NodeManager::currentNM()->mkConst(true);
96
24969
}
97
98
// Gets all leaf terms in n.
99
18679
std::vector<Node> ExtTheory::collectVars(Node n)
100
{
101
18679
  std::vector<Node> vars;
102
37358
  std::set<Node> visited;
103
37358
  std::vector<Node> worklist;
104
18679
  worklist.push_back(n);
105
248689
  while (!worklist.empty())
106
  {
107
192300
    Node current = worklist.back();
108
115005
    worklist.pop_back();
109
115005
    if (current.isConst() || visited.count(current) > 0)
110
    {
111
37710
      continue;
112
    }
113
77295
    visited.insert(current);
114
    // Treat terms not belonging to this theory as leaf
115
    // note : chould include terms not belonging to this theory
116
    // (commented below)
117
77295
    if (current.getNumChildren() > 0)
118
    {
119
49301
      worklist.insert(worklist.end(), current.begin(), current.end());
120
    }
121
    else
122
    {
123
27994
      vars.push_back(current);
124
    }
125
  }
126
37358
  return vars;
127
}
128
129
Node ExtTheory::getSubstitutedTerm(int effort,
130
                                   Node term,
131
                                   std::vector<Node>& exp)
132
{
133
  std::vector<Node> terms;
134
  terms.push_back(term);
135
  std::vector<Node> sterms;
136
  std::vector<std::vector<Node> > exps;
137
  getSubstitutedTerms(effort, terms, sterms, exps);
138
  Assert(sterms.size() == 1);
139
  Assert(exps.size() == 1);
140
  exp.insert(exp.end(), exps[0].begin(), exps[0].end());
141
  return sterms[0];
142
}
143
144
// do inferences
145
41956
void ExtTheory::getSubstitutedTerms(int effort,
146
                                    const std::vector<Node>& terms,
147
                                    std::vector<Node>& sterms,
148
                                    std::vector<std::vector<Node> >& exp)
149
{
150
83912
  Trace("extt-debug") << "getSubstitutedTerms for " << terms.size() << " / "
151
83912
                      << d_ext_func_terms.size() << " extended functions."
152
41956
                      << std::endl;
153
41956
  if (!terms.empty())
154
  {
155
    // all variables we need to find a substitution for
156
9892
    std::vector<Node> vars;
157
9892
    std::vector<Node> sub;
158
9892
    std::map<Node, std::vector<Node> > expc;
159
41542
    for (const Node& n : terms)
160
    {
161
      // do substitution, rewrite
162
36596
      std::map<Node, ExtfInfo>::iterator iti = d_extf_info.find(n);
163
36596
      Assert(iti != d_extf_info.end());
164
97714
      for (const Node& v : iti->second.d_vars)
165
      {
166
61118
        if (std::find(vars.begin(), vars.end(), v) == vars.end())
167
        {
168
26874
          vars.push_back(v);
169
        }
170
      }
171
    }
172
4946
    bool useSubs = d_parent.getCurrentSubstitution(effort, vars, sub, expc);
173
    // get the current substitution for all variables
174
4946
    Assert(!useSubs || vars.size() == sub.size());
175
41542
    for (const Node& n : terms)
176
    {
177
73192
      Node ns = n;
178
73192
      std::vector<Node> expn;
179
36596
      if (useSubs)
180
      {
181
        // do substitution
182
23173
        ns = n.substitute(vars.begin(), vars.end(), sub.begin(), sub.end());
183
23173
        if (ns != n)
184
        {
185
          // build explanation: explanation vars = sub for each vars in FV(n)
186
6691
          std::map<Node, ExtfInfo>::iterator iti = d_extf_info.find(n);
187
6691
          Assert(iti != d_extf_info.end());
188
19168
          for (const Node& v : iti->second.d_vars)
189
          {
190
12477
            std::map<Node, std::vector<Node> >::iterator itx = expc.find(v);
191
12477
            if (itx != expc.end())
192
            {
193
14812
              for (const Node& e : itx->second)
194
              {
195
7406
                if (std::find(expn.begin(), expn.end(), e) == expn.end())
196
                {
197
7406
                  expn.push_back(e);
198
                }
199
              }
200
            }
201
          }
202
        }
203
46346
        Trace("extt-debug") << "  have " << n << " == " << ns
204
23173
                            << ", exp size=" << expn.size() << "." << std::endl;
205
      }
206
      // add to vector
207
36596
      sterms.push_back(ns);
208
36596
      exp.push_back(expn);
209
    }
210
  }
211
41956
}
212
213
41956
bool ExtTheory::doInferencesInternal(int effort,
214
                                     const std::vector<Node>& terms,
215
                                     std::vector<Node>& nred,
216
                                     bool batch,
217
                                     bool isRed)
218
{
219
41956
  if (batch)
220
  {
221
41956
    bool addedLemma = false;
222
41956
    if (isRed)
223
    {
224
      for (const Node& n : terms)
225
      {
226
        Node nr;
227
        // note: could do reduction with substitution here
228
        bool satDep = false;
229
        if (!d_parent.getReduction(effort, n, nr, satDep))
230
        {
231
          nred.push_back(n);
232
        }
233
        else
234
        {
235
          if (!nr.isNull() && n != nr)
236
          {
237
            Node lem = NodeManager::currentNM()->mkNode(kind::EQUAL, n, nr);
238
            if (sendLemma(lem, InferenceId::EXTT_SIMPLIFY, true))
239
            {
240
              Trace("extt-lemma")
241
                  << "ExtTheory : reduction lemma : " << lem << std::endl;
242
              addedLemma = true;
243
            }
244
          }
245
          markReduced(n, ExtReducedId::REDUCTION, satDep);
246
        }
247
      }
248
    }
249
    else
250
    {
251
83912
      std::vector<Node> sterms;
252
83912
      std::vector<std::vector<Node> > exp;
253
41956
      getSubstitutedTerms(effort, terms, sterms, exp);
254
83912
      std::map<Node, unsigned> sterm_index;
255
41956
      NodeManager* nm = NodeManager::currentNM();
256
78552
      for (unsigned i = 0, size = terms.size(); i < size; i++)
257
      {
258
36596
        bool processed = false;
259
36596
        if (sterms[i] != terms[i])
260
        {
261
13382
          Node sr = rewrite(sterms[i]);
262
          // ask the theory if this term is reduced, e.g. is it constant or it
263
          // is a non-extf term.
264
          ExtReducedId id;
265
6691
          if (d_parent.isExtfReduced(effort, sr, terms[i], exp[i], id))
266
          {
267
5665
            processed = true;
268
5665
            markReduced(terms[i], id);
269
            // We have exp[i] => terms[i] = sr, convert this to a clause.
270
            // This ensures the proof infrastructure can process this as a
271
            // normal theory lemma.
272
11330
            Node eq = terms[i].eqNode(sr);
273
11330
            Node lem = eq;
274
5665
            if (!exp[i].empty())
275
            {
276
11330
              std::vector<Node> eei;
277
11719
              for (const Node& e : exp[i])
278
              {
279
6054
                eei.push_back(e.negate());
280
              }
281
5665
              eei.push_back(eq);
282
5665
              lem = nm->mkNode(kind::OR, eei);
283
            }
284
285
11330
            Trace("extt-debug") << "ExtTheory::doInferences : infer : " << eq
286
5665
                                << " by " << exp[i] << std::endl;
287
5665
            Trace("extt-debug") << "...send lemma " << lem << std::endl;
288
5665
            if (sendLemma(lem, InferenceId::EXTT_SIMPLIFY))
289
            {
290
3188
              Trace("extt-lemma")
291
1594
                  << "ExtTheory : substitution + rewrite lemma : " << lem
292
1594
                  << std::endl;
293
1594
              addedLemma = true;
294
            }
295
          }
296
          else
297
          {
298
            // check if we have already reduced this
299
1026
            std::map<Node, unsigned>::iterator itsi = sterm_index.find(sr);
300
1026
            if (itsi == sterm_index.end())
301
            {
302
782
              sterm_index[sr] = i;
303
            }
304
            else
305
            {
306
              // unsigned j = itsi->second;
307
              // note : can add (non-reducing) lemma :
308
              //   exp[j] ^ exp[i] => sterms[i] = sterms[j]
309
            }
310
311
1026
            Trace("extt-nred") << "Non-reduced term : " << sr << std::endl;
312
          }
313
        }
314
        else
315
        {
316
29905
          Trace("extt-nred") << "Non-reduced term : " << sterms[i] << std::endl;
317
        }
318
36596
        if (!processed)
319
        {
320
30931
          nred.push_back(terms[i]);
321
        }
322
      }
323
    }
324
41956
    return addedLemma;
325
  }
326
  // non-batch
327
  std::vector<Node> nnred;
328
  if (terms.empty())
329
  {
330
    for (NodeBoolMap::iterator it = d_ext_func_terms.begin();
331
         it != d_ext_func_terms.end();
332
         ++it)
333
    {
334
      if ((*it).second && !isContextIndependentInactive((*it).first))
335
      {
336
        std::vector<Node> nterms;
337
        nterms.push_back((*it).first);
338
        if (doInferencesInternal(effort, nterms, nnred, true, isRed))
339
        {
340
          return true;
341
        }
342
      }
343
    }
344
  }
345
  else
346
  {
347
    for (const Node& n : terms)
348
    {
349
      std::vector<Node> nterms;
350
      nterms.push_back(n);
351
      if (doInferencesInternal(effort, nterms, nnred, true, isRed))
352
      {
353
        return true;
354
      }
355
    }
356
  }
357
  return false;
358
}
359
360
5665
bool ExtTheory::sendLemma(Node lem, InferenceId id, bool preprocess)
361
{
362
5665
  if (preprocess)
363
  {
364
    if (d_pp_lemmas.find(lem) == d_pp_lemmas.end())
365
    {
366
      d_pp_lemmas.insert(lem);
367
      d_im.lemma(lem, id);
368
      return true;
369
    }
370
  }
371
  else
372
  {
373
5665
    if (d_lemmas.find(lem) == d_lemmas.end())
374
    {
375
1594
      d_lemmas.insert(lem);
376
1594
      d_im.lemma(lem, id);
377
1594
      return true;
378
    }
379
  }
380
4071
  return false;
381
}
382
383
bool ExtTheory::doInferences(int effort,
384
                             const std::vector<Node>& terms,
385
                             std::vector<Node>& nred,
386
                             bool batch)
387
{
388
  if (!terms.empty())
389
  {
390
    return doInferencesInternal(effort, terms, nred, batch, false);
391
  }
392
  return false;
393
}
394
395
41956
bool ExtTheory::doInferences(int effort, std::vector<Node>& nred, bool batch)
396
{
397
83912
  std::vector<Node> terms = getActive();
398
83912
  return doInferencesInternal(effort, terms, nred, batch, false);
399
}
400
401
bool ExtTheory::doReductions(int effort,
402
                             const std::vector<Node>& terms,
403
                             std::vector<Node>& nred,
404
                             bool batch)
405
{
406
  if (!terms.empty())
407
  {
408
    return doInferencesInternal(effort, terms, nred, batch, true);
409
  }
410
  return false;
411
}
412
413
bool ExtTheory::doReductions(int effort, std::vector<Node>& nred, bool batch)
414
{
415
  const std::vector<Node> terms = getActive();
416
  return doInferencesInternal(effort, terms, nred, batch, true);
417
}
418
419
// Register term.
420
784674
void ExtTheory::registerTerm(Node n)
421
{
422
784674
  if (d_extf_kind.find(n.getKind()) != d_extf_kind.end())
423
  {
424
171203
    if (d_ext_func_terms.find(n) == d_ext_func_terms.end())
425
    {
426
18679
      Trace("extt-debug") << "Found extended function : " << n << std::endl;
427
18679
      d_ext_func_terms[n] = true;
428
18679
      d_has_extf = n;
429
18679
      d_extf_info[n].d_vars = collectVars(n);
430
    }
431
  }
432
784674
}
433
434
// mark reduced
435
152524
void ExtTheory::markReduced(Node n, ExtReducedId rid, bool satDep)
436
{
437
152524
  Trace("extt-debug") << "Mark reduced " << n << std::endl;
438
152524
  registerTerm(n);
439
152524
  Assert(d_ext_func_terms.find(n) != d_ext_func_terms.end());
440
152524
  d_ext_func_terms[n] = false;
441
152524
  d_extfExtReducedIdMap[n] = rid;
442
152524
  if (!satDep)
443
  {
444
    d_ci_inactive[n] = rid;
445
  }
446
447
  // update has_extf
448
152524
  if (d_has_extf.get() == n)
449
  {
450
219241
    for (NodeBoolMap::iterator it = d_ext_func_terms.begin();
451
219241
         it != d_ext_func_terms.end();
452
         ++it)
453
    {
454
      // if not already reduced
455
210564
      if ((*it).second && !isContextIndependentInactive((*it).first))
456
      {
457
74838
        d_has_extf = (*it).first;
458
      }
459
    }
460
  }
461
152524
}
462
463
892411
bool ExtTheory::isContextIndependentInactive(Node n) const
464
{
465
892411
  ExtReducedId rid = ExtReducedId::UNKNOWN;
466
892411
  return isContextIndependentInactive(n, rid);
467
}
468
469
1276966
bool ExtTheory::isContextIndependentInactive(Node n, ExtReducedId& rid) const
470
{
471
1276966
  NodeExtReducedIdMap::iterator it = d_ci_inactive.find(n);
472
1276966
  if (it != d_ci_inactive.end())
473
  {
474
    rid = it->second;
475
    return true;
476
  }
477
1276966
  return false;
478
}
479
480
4881
void ExtTheory::getTerms(std::vector<Node>& terms)
481
{
482
42325
  for (NodeBoolMap::iterator it = d_ext_func_terms.begin();
483
42325
       it != d_ext_func_terms.end();
484
       ++it)
485
  {
486
37444
    terms.push_back((*it).first);
487
  }
488
4881
}
489
490
bool ExtTheory::hasActiveTerm() const { return !d_has_extf.get().isNull(); }
491
492
384555
bool ExtTheory::isActive(Node n) const
493
{
494
384555
  ExtReducedId rid = ExtReducedId::UNKNOWN;
495
384555
  return isActive(n, rid);
496
}
497
498
384555
bool ExtTheory::isActive(Node n, ExtReducedId& rid) const
499
{
500
384555
  NodeBoolMap::const_iterator it = d_ext_func_terms.find(n);
501
384555
  if (it != d_ext_func_terms.end())
502
  {
503
384555
    if ((*it).second)
504
    {
505
384555
      return !isContextIndependentInactive(n, rid);
506
    }
507
    NodeExtReducedIdMap::const_iterator itr = d_extfExtReducedIdMap.find(n);
508
    Assert(itr != d_extfExtReducedIdMap.end());
509
    rid = itr->second;
510
    return false;
511
  }
512
  return false;
513
}
514
515
// get active
516
125807
std::vector<Node> ExtTheory::getActive() const
517
{
518
125807
  std::vector<Node> active;
519
1378325
  for (NodeBoolMap::iterator it = d_ext_func_terms.begin();
520
1378325
       it != d_ext_func_terms.end();
521
       ++it)
522
  {
523
    // if not already reduced
524
1252518
    if ((*it).second && !isContextIndependentInactive((*it).first))
525
    {
526
816040
      active.push_back((*it).first);
527
    }
528
  }
529
125807
  return active;
530
}
531
532
10637
std::vector<Node> ExtTheory::getActive(Kind k) const
533
{
534
10637
  std::vector<Node> active;
535
38092
  for (NodeBoolMap::iterator it = d_ext_func_terms.begin();
536
38092
       it != d_ext_func_terms.end();
537
       ++it)
538
  {
539
    // if not already reduced
540
62484
    if ((*it).first.getKind() == k && (*it).second
541
56443
        && !isContextIndependentInactive((*it).first))
542
    {
543
1533
      active.push_back((*it).first);
544
    }
545
  }
546
10637
  return active;
547
}
548
549
}  // namespace theory
550
31137
}  // namespace cvc5