GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/theory/strings/inference_manager.cpp Lines: 155 179 86.6 %
Date: 2021-09-15 Branches: 343 772 44.4 %

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
9942
InferenceManager::InferenceManager(Env& env,
34
                                   Theory& t,
35
                                   SolverState& s,
36
                                   TermRegistry& tr,
37
                                   ExtTheory& e,
38
                                   SequencesStatistics& statistics,
39
9942
                                   ProofNodeManager* pnm)
40
    : InferenceManagerBuffered(env, t, s, pnm, "theory::strings::", false),
41
      d_state(s),
42
      d_termReg(tr),
43
      d_extt(e),
44
      d_statistics(statistics),
45
9942
      d_ipc(pnm ? new InferProofCons(context(), pnm, d_statistics) : nullptr)
46
{
47
9942
  NodeManager* nm = NodeManager::currentNM();
48
9942
  d_zero = nm->mkConst(Rational(0));
49
9942
  d_one = nm->mkConst(Rational(1));
50
9942
  d_true = nm->mkConst(true);
51
9942
  d_false = nm->mkConst(false);
52
9942
}
53
54
41660
void InferenceManager::doPending()
55
{
56
41660
  doPendingFacts();
57
41660
  if (d_state.isInConflict())
58
  {
59
    // just clear the pending vectors, nothing else to do
60
2906
    clearPendingLemmas();
61
2906
    clearPendingPhaseRequirements();
62
2906
    return;
63
  }
64
38754
  doPendingLemmas();
65
38754
  doPendingPhaseRequirements();
66
}
67
68
39957
bool InferenceManager::sendInternalInference(std::vector<Node>& exp,
69
                                             Node conc,
70
                                             InferenceId infer)
71
{
72
79914
  if (conc.getKind() == AND
73
79914
      || (conc.getKind() == NOT && conc[0].getKind() == OR))
74
  {
75
5446
    Node conj = conc.getKind() == AND ? conc : conc[0];
76
2723
    bool pol = conc.getKind() == AND;
77
2723
    bool ret = true;
78
10039
    for (const Node& cc : conj)
79
    {
80
7316
      bool retc = sendInternalInference(exp, pol ? cc : cc.negate(), infer);
81
7316
      ret = ret && retc;
82
    }
83
2723
    return ret;
84
  }
85
37234
  bool pol = conc.getKind() != NOT;
86
74468
  Node lit = pol ? conc : conc[0];
87
37234
  if (lit.getKind() == EQUAL)
88
  {
89
17212
    for (unsigned i = 0; i < 2; i++)
90
    {
91
11572
      if (!lit[i].isConst() && !d_state.hasTerm(lit[i]))
92
      {
93
        // introduces a new non-constant term, do not infer
94
146
        return false;
95
      }
96
    }
97
    // does it already hold?
98
19820
    if (pol ? d_state.areEqual(lit[0], lit[1])
99
14180
            : d_state.areDisequal(lit[0], lit[1]))
100
    {
101
5124
      return true;
102
    }
103
  }
104
31448
  else if (lit.isConst())
105
  {
106
142
    if (lit.getConst<bool>())
107
    {
108
      Assert(pol);
109
      // trivially holds
110
      return true;
111
    }
112
  }
113
31306
  else if (!d_state.hasTerm(lit))
114
  {
115
    // introduces a new non-constant term, do not infer
116
28624
    return false;
117
  }
118
2682
  else if (d_state.areEqual(lit, pol ? d_true : d_false))
119
  {
120
    // already holds
121
2638
    return true;
122
  }
123
702
  sendInference(exp, conc, infer);
124
702
  return true;
125
}
126
127
61936
bool InferenceManager::sendInference(const std::vector<Node>& exp,
128
                                     const std::vector<Node>& noExplain,
129
                                     Node eq,
130
                                     InferenceId infer,
131
                                     bool isRev,
132
                                     bool asLemma)
133
{
134
61936
  if (eq.isNull())
135
  {
136
78
    eq = d_false;
137
  }
138
61858
  else if (Rewriter::rewrite(eq) == d_true)
139
  {
140
    // if trivial, return
141
6
    return false;
142
  }
143
  // wrap in infer info and send below
144
123860
  InferInfo ii(infer);
145
61930
  ii.d_idRev = isRev;
146
61930
  ii.d_conc = eq;
147
61930
  ii.d_premises = exp;
148
61930
  ii.d_noExplain = noExplain;
149
61930
  sendInference(ii, asLemma);
150
61930
  return true;
151
}
152
153
57201
bool InferenceManager::sendInference(const std::vector<Node>& exp,
154
                                     Node eq,
155
                                     InferenceId infer,
156
                                     bool isRev,
157
                                     bool asLemma)
158
{
159
114402
  std::vector<Node> noExplain;
160
114402
  return sendInference(exp, noExplain, eq, infer, isRev, asLemma);
161
}
162
163
64984
void InferenceManager::sendInference(InferInfo& ii, bool asLemma)
164
{
165
64984
  Assert(!ii.isTrivial());
166
  // set that this inference manager will be processing this inference
167
64984
  ii.d_sim = this;
168
129968
  Trace("strings-infer-debug")
169
64984
      << "sendInference: " << ii << ", asLemma = " << asLemma << std::endl;
170
  // check if we should send a conflict, lemma or a fact
171
64984
  if (ii.isConflict())
172
  {
173
1394
    Trace("strings-infer-debug") << "...as conflict" << std::endl;
174
2788
    Trace("strings-lemma") << "Strings::Conflict: " << ii.d_premises << " by "
175
1394
                           << ii.getId() << std::endl;
176
1394
    Trace("strings-conflict") << "CONFLICT: inference conflict " << ii.d_premises << " by " << ii.getId() << std::endl;
177
1394
    ++(d_statistics.d_conflictsInfer);
178
    // process the conflict immediately
179
1394
    processConflict(ii);
180
1394
    return;
181
  }
182
63590
  else if (asLemma || options::stringInferAsLemmas() || !ii.isFact())
183
  {
184
24991
    Trace("strings-infer-debug") << "...as lemma" << std::endl;
185
24991
    addPendingLemma(std::unique_ptr<InferInfo>(new InferInfo(ii)));
186
24991
    return;
187
  }
188
38599
  if (options::stringInferSym())
189
  {
190
77198
    std::vector<Node> unproc;
191
126349
    for (const Node& ac : ii.d_premises)
192
    {
193
87750
      d_termReg.removeProxyEqs(ac, unproc);
194
    }
195
38599
    if (unproc.empty())
196
    {
197
      Node eqs = ii.d_conc;
198
      // keep the same id for now, since we are transforming the form of the
199
      // inference, not the root reason.
200
      InferInfo iiSubsLem(ii.getId());
201
      iiSubsLem.d_sim = this;
202
      iiSubsLem.d_conc = eqs;
203
      if (Trace.isOn("strings-lemma-debug"))
204
      {
205
        Trace("strings-lemma-debug")
206
            << "Strings::Infer " << iiSubsLem << std::endl;
207
        Trace("strings-lemma-debug")
208
            << "Strings::Infer Alternate : " << eqs << std::endl;
209
      }
210
      Trace("strings-infer-debug") << "...as symbolic lemma" << std::endl;
211
      addPendingLemma(std::unique_ptr<InferInfo>(new InferInfo(iiSubsLem)));
212
      return;
213
    }
214
38599
    if (Trace.isOn("strings-lemma-debug"))
215
    {
216
      for (const Node& u : unproc)
217
      {
218
        Trace("strings-lemma-debug")
219
            << "  non-trivial explanation : " << u << std::endl;
220
      }
221
    }
222
  }
223
38599
  Trace("strings-infer-debug") << "...as fact" << std::endl;
224
  // add to pending to be processed as a fact
225
38599
  addPendingFact(std::unique_ptr<InferInfo>(new InferInfo(ii)));
226
}
227
228
585
bool InferenceManager::sendSplit(Node a, Node b, InferenceId infer, bool preq)
229
{
230
1170
  Node eq = a.eqNode(b);
231
585
  eq = Rewriter::rewrite(eq);
232
585
  if (eq.isConst())
233
  {
234
    return false;
235
  }
236
585
  NodeManager* nm = NodeManager::currentNM();
237
1170
  InferInfo iiSplit(infer);
238
585
  iiSplit.d_sim = this;
239
585
  iiSplit.d_conc = nm->mkNode(OR, eq, nm->mkNode(NOT, eq));
240
585
  eq = Rewriter::rewrite(eq);
241
585
  addPendingPhaseRequirement(eq, preq);
242
585
  addPendingLemma(std::unique_ptr<InferInfo>(new InferInfo(iiSplit)));
243
585
  return true;
244
}
245
246
944405
void InferenceManager::addToExplanation(Node a,
247
                                        Node b,
248
                                        std::vector<Node>& exp) const
249
{
250
944405
  if (a != b)
251
  {
252
953388
    Debug("strings-explain")
253
476694
        << "Add to explanation : " << a << " == " << b << std::endl;
254
476694
    Assert(d_state.areEqual(a, b));
255
476694
    exp.push_back(a.eqNode(b));
256
  }
257
944405
}
258
259
void InferenceManager::addToExplanation(Node lit, std::vector<Node>& exp) const
260
{
261
  if (!lit.isNull())
262
  {
263
    Assert(!lit.isConst());
264
    exp.push_back(lit);
265
  }
266
}
267
268
3841216
bool InferenceManager::hasProcessed() const
269
{
270
3841216
  return d_state.isInConflict() || hasPending();
271
}
272
273
197
void InferenceManager::markReduced(Node n, ExtReducedId id, bool contextDepend)
274
{
275
197
  d_extt.markReduced(n, id, contextDepend);
276
197
}
277
278
1837
void InferenceManager::processConflict(const InferInfo& ii)
279
{
280
1837
  Assert(!d_state.isInConflict());
281
  // setup the fact to reproduce the proof in the call below
282
1837
  if (d_ipc != nullptr)
283
  {
284
260
    d_ipc->notifyFact(ii);
285
  }
286
  // make the trust node
287
3674
  TrustNode tconf = mkConflictExp(ii.d_premises, d_ipc.get());
288
1837
  Assert(tconf.getKind() == TrustNodeKind::CONFLICT);
289
3674
  Trace("strings-assert") << "(assert (not " << tconf.getNode()
290
1837
                          << ")) ; conflict " << ii.getId() << std::endl;
291
  // send the trusted conflict
292
1837
  trustedConflict(tconf, ii.getId());
293
1837
}
294
295
37631
void InferenceManager::processFact(InferInfo& ii, ProofGenerator*& pg)
296
{
297
75262
  Trace("strings-assert") << "(assert (=> " << ii.getPremises() << " "
298
37631
                          << ii.d_conc << ")) ; fact " << ii.getId() << std::endl;
299
75262
  Trace("strings-lemma") << "Strings::Fact: " << ii.d_conc << " from "
300
75262
                         << ii.getPremises() << " by " << ii.getId()
301
37631
                         << std::endl;
302
37631
  if (d_ipc != nullptr)
303
  {
304
    // ensure the proof generator is ready to explain this fact in the
305
    // current SAT context
306
9287
    d_ipc->notifyFact(ii);
307
9287
    pg = d_ipc.get();
308
  }
309
37631
}
310
311
25297
TrustNode InferenceManager::processLemma(InferInfo& ii, LemmaProperty& p)
312
{
313
25297
  Assert(!ii.isTrivial());
314
25297
  Assert(!ii.isConflict());
315
  // set up the explanation and no-explanation
316
50594
  std::vector<Node> exp;
317
80457
  for (const Node& ec : ii.d_premises)
318
  {
319
55160
    utils::flattenOp(AND, ec, exp);
320
  }
321
50594
  std::vector<Node> noExplain;
322
25297
  if (!options::stringRExplainLemmas())
323
  {
324
    // if we aren't regressing the explanation, we add all literals to
325
    // noExplain and ignore ii.d_ant.
326
    noExplain.insert(noExplain.end(), exp.begin(), exp.end());
327
  }
328
  else
329
  {
330
    // otherwise, the no-explain literals are those provided
331
30607
    for (const Node& ecn : ii.d_noExplain)
332
    {
333
5310
      utils::flattenOp(AND, ecn, noExplain);
334
    }
335
  }
336
  // ensure that the proof generator is ready to explain the final conclusion
337
  // of the lemma (ii.d_conc).
338
25297
  if (d_ipc != nullptr)
339
  {
340
4933
    d_ipc->notifyFact(ii);
341
  }
342
25297
  TrustNode tlem = mkLemmaExp(ii.d_conc, exp, noExplain, d_ipc.get());
343
50594
  Trace("strings-pending") << "Process pending lemma : " << tlem.getNode()
344
25297
                           << std::endl;
345
346
  // Process the side effects of the inference info.
347
  // Register the new skolems from this inference. We register them here
348
  // (lazily), since this is the moment when we have decided to process the
349
  // inference.
350
1407
  for (const std::pair<const LengthStatus, std::vector<Node> >& sks :
351
25297
       ii.d_skolems)
352
  {
353
2814
    for (const Node& n : sks.second)
354
    {
355
1407
      d_termReg.registerTermAtomic(n, sks.first);
356
    }
357
  }
358
25297
  if (ii.getId() == InferenceId::STRINGS_REDUCTION)
359
  {
360
2110
    p |= LemmaProperty::NEEDS_JUSTIFY;
361
  }
362
50594
  Trace("strings-assert") << "(assert " << tlem.getNode() << ") ; lemma "
363
25297
                          << ii.getId() << std::endl;
364
50594
  Trace("strings-lemma") << "Strings::Lemma: " << tlem.getNode() << " by "
365
25297
                         << ii.getId() << std::endl;
366
50594
  return tlem;
367
}
368
369
}  // namespace strings
370
}  // namespace theory
371
29577
}  // namespace cvc5