GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/theory/strings/skolem_cache.cpp Lines: 122 134 91.0 %
Date: 2021-05-22 Branches: 283 581 48.7 %

Line Exec Source
1
/******************************************************************************
2
 * Top contributors (to current version):
3
 *   Andrew Reynolds, Andres Noetzli, Yoni Zohar
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
 * Implementation of a cache of skolems for theory of strings.
14
 */
15
16
#include "theory/strings/skolem_cache.h"
17
18
#include "expr/attribute.h"
19
#include "expr/bound_var_manager.h"
20
#include "expr/skolem_manager.h"
21
#include "theory/rewriter.h"
22
#include "theory/strings/arith_entail.h"
23
#include "theory/strings/theory_strings_utils.h"
24
#include "theory/strings/word.h"
25
#include "util/rational.h"
26
27
using namespace cvc5::kind;
28
29
namespace cvc5 {
30
namespace theory {
31
namespace strings {
32
33
/**
34
 * A bound variable corresponding to the universally quantified integer
35
 * variable used to range over the valid positions in a string, used
36
 * for axiomatizing the behavior of some term.
37
 */
38
struct IndexVarAttributeId
39
{
40
};
41
typedef expr::Attribute<IndexVarAttributeId, Node> IndexVarAttribute;
42
43
10389
SkolemCache::SkolemCache(bool useOpts) : d_useOpts(useOpts)
44
{
45
10389
  NodeManager* nm = NodeManager::currentNM();
46
10389
  d_strType = nm->stringType();
47
10389
  d_zero = nm->mkConst(Rational(0));
48
10389
}
49
50
25480
Node SkolemCache::mkSkolemCached(Node a, Node b, SkolemId id, const char* c)
51
{
52
25480
  return mkTypedSkolemCached(d_strType, a, b, id, c);
53
}
54
55
12485
Node SkolemCache::mkSkolemCached(Node a, SkolemId id, const char* c)
56
{
57
12485
  return mkSkolemCached(a, Node::null(), id, c);
58
}
59
60
25707
Node SkolemCache::mkTypedSkolemCached(
61
    TypeNode tn, Node a, Node b, SkolemId id, const char* c)
62
{
63
51414
  Trace("skolem-cache") << "mkTypedSkolemCached start: (" << id << ", " << a
64
25707
                        << ", " << b << ")" << std::endl;
65
25707
  SkolemId idOrig = id;
66
  // do not rewrite beforehand if we are not using optimizations, this is so
67
  // that the proof checker does not depend on the rewriter.
68
25707
  if (d_useOpts)
69
  {
70
23754
    a = a.isNull() ? a : Rewriter::rewrite(a);
71
23754
    b = b.isNull() ? b : Rewriter::rewrite(b);
72
  }
73
25707
  std::tie(id, a, b) = normalizeStringSkolem(id, a, b);
74
75
  // optimization: if we aren't asking for the purification skolem for constant
76
  // a, and the skolem is equivalent to a, then we just return a.
77
25707
  if (d_useOpts && idOrig != SK_PURIFY && id == SK_PURIFY && a.isConst())
78
  {
79
1314
    Trace("skolem-cache") << "...optimization: return constant " << a
80
657
                          << std::endl;
81
657
    return a;
82
  }
83
84
25050
  std::map<SkolemId, Node>::iterator it = d_skolemCache[a][b].find(id);
85
25050
  if (it != d_skolemCache[a][b].end())
86
  {
87
12213
    Trace("skolem-cache") << "...return existing " << it->second << std::endl;
88
    // already cached
89
12213
    return it->second;
90
  }
91
92
12837
  NodeManager* nm = NodeManager::currentNM();
93
12837
  SkolemManager* sm = nm->getSkolemManager();
94
25674
  Node sk;
95
12837
  switch (id)
96
  {
97
    // exists k. k = a
98
12769
    case SK_PURIFY:
99
    {
100
      // for sequences of Booleans, we may purify Boolean terms, in which case
101
      // they must be Boolean term variables.
102
25538
      int flags = a.getType().isBoolean() ? NodeManager::SKOLEM_BOOL_TERM_VAR
103
12769
                                          : NodeManager::SKOLEM_DEFAULT;
104
12769
      sk = sm->mkPurifySkolem(a, c, "string purify skolem", flags);
105
    }
106
12769
    break;
107
    // these are eliminated by normalizeStringSkolem
108
    case SK_ID_V_SPT:
109
    case SK_ID_V_SPT_REV:
110
    case SK_ID_VC_SPT:
111
    case SK_ID_VC_SPT_REV:
112
    case SK_FIRST_CTN_POST:
113
    case SK_ID_C_SPT:
114
    case SK_ID_C_SPT_REV:
115
    case SK_ID_DC_SPT:
116
    case SK_ID_DC_SPT_REM:
117
    case SK_ID_DEQ_X:
118
    case SK_ID_DEQ_Y:
119
    case SK_FIRST_CTN_PRE:
120
    case SK_PREFIX:
121
    case SK_SUFFIX_REM:
122
      Unhandled() << "Expected to eliminate Skolem ID " << id << std::endl;
123
      break;
124
68
    case SK_NUM_OCCUR:
125
    case SK_OCCUR_INDEX:
126
    default:
127
    {
128
68
      Notice() << "Don't know how to handle Skolem ID " << id << std::endl;
129
136
      Node v = nm->mkBoundVar(tn);
130
136
      Node cond = nm->mkConst(true);
131
136
      sk = sm->mkSkolem(v, cond, c, "string skolem");
132
    }
133
68
    break;
134
  }
135
12837
  Trace("skolem-cache") << "...returned " << sk << std::endl;
136
12837
  d_allSkolems.insert(sk);
137
12837
  d_skolemCache[a][b][id] = sk;
138
12837
  return sk;
139
}
140
191
Node SkolemCache::mkTypedSkolemCached(TypeNode tn,
141
                                      Node a,
142
                                      SkolemId id,
143
                                      const char* c)
144
{
145
191
  return mkTypedSkolemCached(tn, a, Node::null(), id, c);
146
}
147
148
40
Node SkolemCache::mkSkolemSeqNth(TypeNode seqType, const char* c)
149
{
150
  // Note this method is static and does not rely on any local caching.
151
  // It is used by expand definitions and by (dynamic) reductions, thus
152
  // it is centrally located here.
153
40
  Assert(seqType.isSequence());
154
40
  NodeManager* nm = NodeManager::currentNM();
155
40
  SkolemManager* sm = nm->getSkolemManager();
156
80
  std::vector<TypeNode> argTypes;
157
40
  argTypes.push_back(seqType);
158
40
  argTypes.push_back(nm->integerType());
159
80
  TypeNode elemType = seqType.getSequenceElementType();
160
80
  TypeNode ufType = nm->mkFunctionType(argTypes, elemType);
161
80
  return sm->mkSkolemFunction(SkolemFunId::SEQ_NTH_OOB, ufType);
162
}
163
164
1110
Node SkolemCache::mkSkolem(const char* c)
165
{
166
  // TODO: eliminate this
167
1110
  SkolemManager* sm = NodeManager::currentNM()->getSkolemManager();
168
1110
  Node n = sm->mkDummySkolem(c, d_strType, "string skolem");
169
1110
  d_allSkolems.insert(n);
170
1110
  return n;
171
}
172
173
3860
bool SkolemCache::isSkolem(Node n) const
174
{
175
3860
  return d_allSkolems.find(n) != d_allSkolems.end();
176
}
177
178
std::tuple<SkolemCache::SkolemId, Node, Node>
179
25707
SkolemCache::normalizeStringSkolem(SkolemId id, Node a, Node b)
180
{
181
182
25707
  NodeManager* nm = NodeManager::currentNM();
183
184
  // eliminate in terms of prefix/suffix_rem
185
25707
  if (id == SK_FIRST_CTN_POST)
186
  {
187
    // SK_FIRST_CTN_POST(x, y) --->
188
    //   SK_SUFFIX_REM(x, (+ (str.len SK_FIRST_CTN_PRE(x, y)) (str.len y)))
189
2333
    id = SK_SUFFIX_REM;
190
4666
    Node pre = mkSkolemCached(a, b, SK_FIRST_CTN_PRE, "pre");
191
6999
    b = nm->mkNode(
192
9332
        PLUS, nm->mkNode(STRING_LENGTH, pre), nm->mkNode(STRING_LENGTH, b));
193
  }
194
23374
  else if (id == SK_ID_V_SPT || id == SK_ID_C_SPT)
195
  {
196
    // SK_ID_*_SPT(x, y) ---> SK_SUFFIX_REM(x, (str.len y))
197
368
    id = SK_SUFFIX_REM;
198
368
    b = nm->mkNode(STRING_LENGTH, b);
199
  }
200
23006
  else if (id == SK_ID_V_SPT_REV || id == SK_ID_C_SPT_REV)
201
  {
202
    // SK_ID_*_SPT_REV(x, y) ---> SK_PREFIX(x, (- (str.len x) (str.len y)))
203
430
    id = SK_PREFIX;
204
1720
    b = nm->mkNode(
205
1720
        MINUS, nm->mkNode(STRING_LENGTH, a), nm->mkNode(STRING_LENGTH, b));
206
  }
207
22576
  else if (id == SK_ID_VC_SPT)
208
  {
209
    // SK_ID_VC_SPT(x, y) ---> SK_SUFFIX_REM(x, 1)
210
1808
    id = SK_SUFFIX_REM;
211
1808
    b = nm->mkConst(Rational(1));
212
  }
213
20768
  else if (id == SK_ID_VC_SPT_REV)
214
  {
215
    // SK_ID_VC_SPT_REV(x, y) ---> SK_PREFIX(x, (- (str.len x) 1))
216
2253
    id = SK_PREFIX;
217
6759
    b = nm->mkNode(
218
9012
        MINUS, nm->mkNode(STRING_LENGTH, a), nm->mkConst(Rational(1)));
219
  }
220
18515
  else if (id == SK_ID_DC_SPT)
221
  {
222
    // SK_ID_DC_SPT(x, y) ---> SK_PREFIX(x, 1)
223
    id = SK_PREFIX;
224
    b = nm->mkConst(Rational(1));
225
  }
226
18515
  else if (id == SK_ID_DC_SPT_REM)
227
  {
228
    // SK_ID_DC_SPT_REM(x, y) ---> SK_SUFFIX_REM(x, 1)
229
    id = SK_SUFFIX_REM;
230
    b = nm->mkConst(Rational(1));
231
  }
232
18515
  else if (id == SK_ID_DEQ_X)
233
  {
234
    // SK_ID_DEQ_X(x, y) ---> SK_PREFIX(y, (str.len x))
235
    id = SK_PREFIX;
236
    Node aOld = a;
237
    a = b;
238
    b = nm->mkNode(STRING_LENGTH, aOld);
239
  }
240
18515
  else if (id == SK_ID_DEQ_Y)
241
  {
242
    // SK_ID_DEQ_Y(x, y) ---> SK_PREFIX(x, (str.len y))
243
    id = SK_PREFIX;
244
    b = nm->mkNode(STRING_LENGTH, b);
245
  }
246
18515
  else if (id == SK_FIRST_CTN_PRE)
247
  {
248
    // SK_FIRST_CTN_PRE(x,y) ---> SK_PREFIX(x, indexof(x,y,0))
249
4670
    id = SK_PREFIX;
250
4670
    b = nm->mkNode(STRING_STRIDOF, a, b, d_zero);
251
  }
252
253
25707
  if (id == SK_ID_V_UNIFIED_SPT || id == SK_ID_V_UNIFIED_SPT_REV)
254
  {
255
2652
    bool isRev = (id == SK_ID_V_UNIFIED_SPT_REV);
256
5304
    Node la = nm->mkNode(STRING_LENGTH, a);
257
5304
    Node lb = nm->mkNode(STRING_LENGTH, b);
258
3792
    Node ta = isRev ? utils::mkPrefix(a, nm->mkNode(MINUS, la, lb))
259
6444
                    : utils::mkSuffix(a, lb);
260
3792
    Node tb = isRev ? utils::mkPrefix(b, nm->mkNode(MINUS, lb, la))
261
6444
                    : utils::mkSuffix(b, la);
262
2652
    id = SK_PURIFY;
263
    // SK_ID_V_UNIFIED_SPT(x,y) --->
264
    //   ite(len(x) >= len(y), substr(x,0,str.len(y)), substr(y,0,str.len(x))
265
2652
    a = nm->mkNode(ITE, nm->mkNode(GEQ, la, lb), ta, tb);
266
2652
    b = Node::null();
267
  }
268
269
  // now, eliminate prefix/suffix_rem in terms of purify
270
25707
  if (id == SK_PREFIX)
271
  {
272
    // SK_PREFIX(x,y) ---> SK_PURIFY(substr(x,0,y))
273
8600
    id = SK_PURIFY;
274
8600
    a = utils::mkPrefix(a, b);
275
8600
    b = Node::null();
276
  }
277
17107
  else if (id == SK_SUFFIX_REM)
278
  {
279
    // SK_SUFFIX_REM(x,y) ---> SK_PURIFY(substr(x,y,str.len(x)-y))
280
5756
    id = SK_PURIFY;
281
5756
    a = utils::mkSuffix(a, b);
282
5756
    b = Node::null();
283
  }
284
285
25707
  if (d_useOpts)
286
  {
287
23754
    a = a.isNull() ? a : Rewriter::rewrite(a);
288
23754
    b = b.isNull() ? b : Rewriter::rewrite(b);
289
  }
290
51414
  Trace("skolem-cache") << "normalizeStringSkolem end: (" << id << ", " << a
291
25707
                        << ", " << b << ")" << std::endl;
292
25707
  return std::make_tuple(id, a, b);
293
}
294
295
275
Node SkolemCache::mkIndexVar(Node t)
296
{
297
275
  NodeManager* nm = NodeManager::currentNM();
298
550
  TypeNode intType = nm->integerType();
299
275
  BoundVarManager* bvm = nm->getBoundVarManager();
300
550
  return bvm->mkBoundVar<IndexVarAttribute>(t, intType);
301
}
302
303
}  // namespace strings
304
}  // namespace theory
305
28194
}  // namespace cvc5