GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/theory/strings/inference_manager.cpp Lines: 159 183 86.9 %
Date: 2021-11-07 Branches: 352 788 44.7 %

Line Exec Source
1
/******************************************************************************
2
 * Top contributors (to current version):
3
 *   Andrew Reynolds, Gereon Kremer, Mudathir Mohamed
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 the inference manager for the theory of strings.
14
 */
15
16
#include "theory/strings/inference_manager.h"
17
18
#include "options/strings_options.h"
19
#include "theory/ext_theory.h"
20
#include "theory/rewriter.h"
21
#include "theory/strings/theory_strings_utils.h"
22
#include "theory/strings/word.h"
23
#include "util/rational.h"
24
25
using namespace std;
26
using namespace cvc5::context;
27
using namespace cvc5::kind;
28
29
namespace cvc5 {
30
namespace theory {
31
namespace strings {
32
33
15273
InferenceManager::InferenceManager(Env& env,
34
                                   Theory& t,
35
                                   SolverState& s,
36
                                   TermRegistry& tr,
37
                                   ExtTheory& e,
38
15273
                                   SequencesStatistics& statistics)
39
    : InferenceManagerBuffered(env, t, s, "theory::strings::", false),
40
      d_state(s),
41
      d_termReg(tr),
42
      d_extt(e),
43
      d_statistics(statistics),
44
15273
      d_ipc(isProofEnabled()
45
                ? new InferProofCons(
46
5372
                      context(), env.getProofNodeManager(), d_statistics)
47
                : nullptr),
48
15273
      d_ipcl(isProofEnabled()
49
                 ? new InferProofCons(
50
5372
                       context(), env.getProofNodeManager(), d_statistics)
51
56563
                 : nullptr)
52
{
53
15273
  NodeManager* nm = NodeManager::currentNM();
54
15273
  d_zero = nm->mkConst(Rational(0));
55
15273
  d_one = nm->mkConst(Rational(1));
56
15273
  d_true = nm->mkConst(true);
57
15273
  d_false = nm->mkConst(false);
58
15273
}
59
60
44123
void InferenceManager::doPending()
61
{
62
44123
  doPendingFacts();
63
44123
  if (d_state.isInConflict())
64
  {
65
    // just clear the pending vectors, nothing else to do
66
2824
    clearPendingLemmas();
67
2824
    clearPendingPhaseRequirements();
68
2824
    return;
69
  }
70
41299
  doPendingLemmas();
71
41299
  doPendingPhaseRequirements();
72
}
73
74
38542
bool InferenceManager::sendInternalInference(std::vector<Node>& exp,
75
                                             Node conc,
76
                                             InferenceId infer)
77
{
78
77084
  if (conc.getKind() == AND
79
77084
      || (conc.getKind() == NOT && conc[0].getKind() == OR))
80
  {
81
5452
    Node conj = conc.getKind() == AND ? conc : conc[0];
82
2726
    bool pol = conc.getKind() == AND;
83
2726
    bool ret = true;
84
10052
    for (const Node& cc : conj)
85
    {
86
7326
      bool retc = sendInternalInference(exp, pol ? cc : cc.negate(), infer);
87
7326
      ret = ret && retc;
88
    }
89
2726
    return ret;
90
  }
91
35816
  bool pol = conc.getKind() != NOT;
92
71632
  Node lit = pol ? conc : conc[0];
93
35816
  if (lit.getKind() == EQUAL)
94
  {
95
16022
    for (unsigned i = 0; i < 2; i++)
96
    {
97
10780
      if (!lit[i].isConst() && !d_state.hasTerm(lit[i]))
98
      {
99
        // introduces a new non-constant term, do not infer
100
148
        return false;
101
      }
102
    }
103
    // does it already hold?
104
18228
    if (pol ? d_state.areEqual(lit[0], lit[1])
105
12986
            : d_state.areDisequal(lit[0], lit[1]))
106
    {
107
4754
      return true;
108
    }
109
  }
110
30426
  else if (lit.isConst())
111
  {
112
142
    if (lit.getConst<bool>())
113
    {
114
      Assert(pol);
115
      // trivially holds
116
      return true;
117
    }
118
  }
119
30284
  else if (!d_state.hasTerm(lit))
120
  {
121
    // introduces a new non-constant term, do not infer
122
27612
    return false;
123
  }
124
2672
  else if (d_state.areEqual(lit, pol ? d_true : d_false))
125
  {
126
    // already holds
127
2628
    return true;
128
  }
129
674
  sendInference(exp, conc, infer);
130
674
  return true;
131
}
132
133
61675
bool InferenceManager::sendInference(const std::vector<Node>& exp,
134
                                     const std::vector<Node>& noExplain,
135
                                     Node eq,
136
                                     InferenceId infer,
137
                                     bool isRev,
138
                                     bool asLemma)
139
{
140
61675
  if (eq.isNull())
141
  {
142
86
    eq = d_false;
143
  }
144
61589
  else if (rewrite(eq) == d_true)
145
  {
146
    // if trivial, return
147
6
    return false;
148
  }
149
  // wrap in infer info and send below
150
123338
  InferInfo ii(infer);
151
61669
  ii.d_idRev = isRev;
152
61669
  ii.d_conc = eq;
153
61669
  ii.d_premises = exp;
154
61669
  ii.d_noExplain = noExplain;
155
61669
  sendInference(ii, asLemma);
156
61669
  return true;
157
}
158
159
56916
bool InferenceManager::sendInference(const std::vector<Node>& exp,
160
                                     Node eq,
161
                                     InferenceId infer,
162
                                     bool isRev,
163
                                     bool asLemma)
164
{
165
113832
  std::vector<Node> noExplain;
166
113832
  return sendInference(exp, noExplain, eq, infer, isRev, asLemma);
167
}
168
169
64734
void InferenceManager::sendInference(InferInfo& ii, bool asLemma)
170
{
171
64734
  Assert(!ii.isTrivial());
172
  // set that this inference manager will be processing this inference
173
64734
  ii.d_sim = this;
174
129468
  Trace("strings-infer-debug")
175
64734
      << "sendInference: " << ii << ", asLemma = " << asLemma << std::endl;
176
  // check if we should send a conflict, lemma or a fact
177
64734
  if (ii.isConflict())
178
  {
179
1335
    Trace("strings-infer-debug") << "...as conflict" << std::endl;
180
2670
    Trace("strings-lemma") << "Strings::Conflict: " << ii.d_premises << " by "
181
1335
                           << ii.getId() << std::endl;
182
1335
    Trace("strings-conflict") << "CONFLICT: inference conflict " << ii.d_premises << " by " << ii.getId() << std::endl;
183
1335
    ++(d_statistics.d_conflictsInfer);
184
    // process the conflict immediately
185
1335
    processConflict(ii);
186
1335
    return;
187
  }
188
63399
  else if (asLemma || options::stringInferAsLemmas() || !ii.isFact())
189
  {
190
25200
    Trace("strings-infer-debug") << "...as lemma" << std::endl;
191
25200
    addPendingLemma(std::unique_ptr<InferInfo>(new InferInfo(ii)));
192
25200
    return;
193
  }
194
38199
  if (options::stringInferSym())
195
  {
196
76398
    std::vector<Node> unproc;
197
125470
    for (const Node& ac : ii.d_premises)
198
    {
199
87271
      d_termReg.removeProxyEqs(ac, unproc);
200
    }
201
38199
    if (unproc.empty())
202
    {
203
      Node eqs = ii.d_conc;
204
      // keep the same id for now, since we are transforming the form of the
205
      // inference, not the root reason.
206
      InferInfo iiSubsLem(ii.getId());
207
      iiSubsLem.d_sim = this;
208
      iiSubsLem.d_conc = eqs;
209
      if (Trace.isOn("strings-lemma-debug"))
210
      {
211
        Trace("strings-lemma-debug")
212
            << "Strings::Infer " << iiSubsLem << std::endl;
213
        Trace("strings-lemma-debug")
214
            << "Strings::Infer Alternate : " << eqs << std::endl;
215
      }
216
      Trace("strings-infer-debug") << "...as symbolic lemma" << std::endl;
217
      addPendingLemma(std::unique_ptr<InferInfo>(new InferInfo(iiSubsLem)));
218
      return;
219
    }
220
38199
    if (Trace.isOn("strings-lemma-debug"))
221
    {
222
      for (const Node& u : unproc)
223
      {
224
        Trace("strings-lemma-debug")
225
            << "  non-trivial explanation : " << u << std::endl;
226
      }
227
    }
228
  }
229
38199
  Trace("strings-infer-debug") << "...as fact" << std::endl;
230
  // add to pending to be processed as a fact
231
38199
  addPendingFact(std::unique_ptr<InferInfo>(new InferInfo(ii)));
232
}
233
234
593
bool InferenceManager::sendSplit(Node a, Node b, InferenceId infer, bool preq)
235
{
236
1186
  Node eq = a.eqNode(b);
237
593
  eq = rewrite(eq);
238
593
  if (eq.isConst())
239
  {
240
    return false;
241
  }
242
593
  NodeManager* nm = NodeManager::currentNM();
243
1186
  InferInfo iiSplit(infer);
244
593
  iiSplit.d_sim = this;
245
593
  iiSplit.d_conc = nm->mkNode(OR, eq, nm->mkNode(NOT, eq));
246
593
  eq = rewrite(eq);
247
593
  addPendingPhaseRequirement(eq, preq);
248
593
  addPendingLemma(std::unique_ptr<InferInfo>(new InferInfo(iiSplit)));
249
593
  return true;
250
}
251
252
931780
void InferenceManager::addToExplanation(Node a,
253
                                        Node b,
254
                                        std::vector<Node>& exp) const
255
{
256
931780
  if (a != b)
257
  {
258
934030
    Debug("strings-explain")
259
467015
        << "Add to explanation : " << a << " == " << b << std::endl;
260
467015
    Assert(d_state.areEqual(a, b));
261
467015
    exp.push_back(a.eqNode(b));
262
  }
263
931780
}
264
265
void InferenceManager::addToExplanation(Node lit, std::vector<Node>& exp) const
266
{
267
  if (!lit.isNull())
268
  {
269
    Assert(!lit.isConst());
270
    exp.push_back(lit);
271
  }
272
}
273
274
3904504
bool InferenceManager::hasProcessed() const
275
{
276
3904504
  return d_state.isInConflict() || hasPending();
277
}
278
279
197
void InferenceManager::markReduced(Node n, ExtReducedId id, bool contextDepend)
280
{
281
197
  d_extt.markReduced(n, id, contextDepend);
282
197
}
283
284
1952
void InferenceManager::processConflict(const InferInfo& ii)
285
{
286
1952
  Assert(!d_state.isInConflict());
287
  // setup the fact to reproduce the proof in the call below
288
1952
  if (d_ipcl != nullptr)
289
  {
290
288
    d_ipcl->notifyLemma(ii);
291
  }
292
  // make the trust node
293
3904
  TrustNode tconf = mkConflictExp(ii.d_premises, d_ipcl.get());
294
1952
  Assert(tconf.getKind() == TrustNodeKind::CONFLICT);
295
3904
  Trace("strings-assert") << "(assert (not " << tconf.getNode()
296
1952
                          << ")) ; conflict " << ii.getId() << std::endl;
297
  // send the trusted conflict
298
1952
  trustedConflict(tconf, ii.getId());
299
1952
}
300
301
37303
void InferenceManager::processFact(InferInfo& ii, ProofGenerator*& pg)
302
{
303
74606
  Trace("strings-assert") << "(assert (=> " << ii.getPremises() << " "
304
37303
                          << ii.d_conc << ")) ; fact " << ii.getId() << std::endl;
305
74606
  Trace("strings-lemma") << "Strings::Fact: " << ii.d_conc << " from "
306
74606
                         << ii.getPremises() << " by " << ii.getId()
307
37303
                         << std::endl;
308
37303
  if (d_ipc != nullptr)
309
  {
310
    // ensure the proof generator is ready to explain this fact in the
311
    // current SAT context
312
9137
    d_ipc->notifyFact(ii);
313
9137
    pg = d_ipc.get();
314
  }
315
37303
}
316
317
25507
TrustNode InferenceManager::processLemma(InferInfo& ii, LemmaProperty& p)
318
{
319
25507
  Assert(!ii.isTrivial());
320
25507
  Assert(!ii.isConflict());
321
  // set up the explanation and no-explanation
322
51014
  std::vector<Node> exp;
323
81333
  for (const Node& ec : ii.d_premises)
324
  {
325
55826
    utils::flattenOp(AND, ec, exp);
326
  }
327
51014
  std::vector<Node> noExplain;
328
25507
  if (!options::stringRExplainLemmas())
329
  {
330
    // if we aren't regressing the explanation, we add all literals to
331
    // noExplain and ignore ii.d_ant.
332
    noExplain.insert(noExplain.end(), exp.begin(), exp.end());
333
  }
334
  else
335
  {
336
    // otherwise, the no-explain literals are those provided
337
31236
    for (const Node& ecn : ii.d_noExplain)
338
    {
339
5729
      utils::flattenOp(AND, ecn, noExplain);
340
    }
341
  }
342
  // ensure that the proof generator is ready to explain the final conclusion
343
  // of the lemma (ii.d_conc).
344
25507
  if (d_ipcl != nullptr)
345
  {
346
4982
    d_ipcl->notifyLemma(ii);
347
  }
348
25507
  TrustNode tlem = mkLemmaExp(ii.d_conc, exp, noExplain, d_ipcl.get());
349
51014
  Trace("strings-pending") << "Process pending lemma : " << tlem.getNode()
350
25507
                           << std::endl;
351
352
  // Process the side effects of the inference info.
353
  // Register the new skolems from this inference. We register them here
354
  // (lazily), since this is the moment when we have decided to process the
355
  // inference.
356
1409
  for (const std::pair<const LengthStatus, std::vector<Node> >& sks :
357
25507
       ii.d_skolems)
358
  {
359
2818
    for (const Node& n : sks.second)
360
    {
361
1409
      d_termReg.registerTermAtomic(n, sks.first);
362
    }
363
  }
364
25507
  if (ii.getId() == InferenceId::STRINGS_REDUCTION)
365
  {
366
2167
    p |= LemmaProperty::NEEDS_JUSTIFY;
367
  }
368
51014
  Trace("strings-assert") << "(assert " << tlem.getNode() << ") ; lemma "
369
25507
                          << ii.getId() << std::endl;
370
51014
  Trace("strings-lemma") << "Strings::Lemma: " << tlem.getNode() << " by "
371
25507
                         << ii.getId() << std::endl;
372
51014
  return tlem;
373
}
374
375
}  // namespace strings
376
}  // namespace theory
377
31137
}  // namespace cvc5