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 |