GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/theory/strings/inference_manager.cpp Lines: 164 180 91.1 %
Date: 2021-05-22 Branches: 359 776 46.3 %

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