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 |
9459 |
InferenceManager::InferenceManager(Theory& t, |
33 |
|
SolverState& s, |
34 |
|
TermRegistry& tr, |
35 |
|
ExtTheory& e, |
36 |
|
SequencesStatistics& statistics, |
37 |
9459 |
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 |
10651 |
: nullptr) |
45 |
|
{ |
46 |
9459 |
NodeManager* nm = NodeManager::currentNM(); |
47 |
9459 |
d_zero = nm->mkConst(Rational(0)); |
48 |
9459 |
d_one = nm->mkConst(Rational(1)); |
49 |
9459 |
d_true = nm->mkConst(true); |
50 |
9459 |
d_false = nm->mkConst(false); |
51 |
9459 |
} |
52 |
|
|
53 |
24200 |
void InferenceManager::doPending() |
54 |
|
{ |
55 |
24200 |
doPendingFacts(); |
56 |
24200 |
if (d_state.isInConflict()) |
57 |
|
{ |
58 |
|
// just clear the pending vectors, nothing else to do |
59 |
1492 |
clearPendingLemmas(); |
60 |
1492 |
clearPendingPhaseRequirements(); |
61 |
1492 |
return; |
62 |
|
} |
63 |
22708 |
doPendingLemmas(); |
64 |
22708 |
doPendingPhaseRequirements(); |
65 |
|
} |
66 |
|
|
67 |
16196 |
bool InferenceManager::sendInternalInference(std::vector<Node>& exp, |
68 |
|
Node conc, |
69 |
|
InferenceId infer) |
70 |
|
{ |
71 |
32392 |
if (conc.getKind() == AND |
72 |
32392 |
|| (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 |
14692 |
bool pol = conc.getKind() != NOT; |
85 |
29384 |
Node lit = pol ? conc : conc[0]; |
86 |
14692 |
if (lit.getKind() == EQUAL) |
87 |
|
{ |
88 |
9199 |
for (unsigned i = 0; i < 2; i++) |
89 |
|
{ |
90 |
6226 |
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 |
10410 |
if (pol ? d_state.areEqual(lit[0], lit[1]) |
98 |
7437 |
: d_state.areDisequal(lit[0], lit[1])) |
99 |
|
{ |
100 |
2715 |
return true; |
101 |
|
} |
102 |
|
} |
103 |
11579 |
else if (lit.isConst()) |
104 |
|
{ |
105 |
4 |
if (lit.getConst<bool>()) |
106 |
|
{ |
107 |
|
Assert(pol); |
108 |
|
// trivially holds |
109 |
|
return true; |
110 |
|
} |
111 |
|
} |
112 |
11575 |
else if (!d_state.hasTerm(lit)) |
113 |
|
{ |
114 |
|
// introduces a new non-constant term, do not infer |
115 |
9441 |
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 |
268 |
sendInference(exp, conc, infer); |
123 |
268 |
return true; |
124 |
|
} |
125 |
|
|
126 |
25293 |
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 |
25293 |
if (eq.isNull()) |
134 |
|
{ |
135 |
76 |
eq = d_false; |
136 |
|
} |
137 |
25217 |
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 |
50582 |
InferInfo ii(infer); |
144 |
25291 |
ii.d_idRev = isRev; |
145 |
25291 |
ii.d_conc = eq; |
146 |
25291 |
ii.d_premises = exp; |
147 |
25291 |
ii.d_noExplain = noExplain; |
148 |
25291 |
sendInference(ii, asLemma); |
149 |
25291 |
return true; |
150 |
|
} |
151 |
|
|
152 |
23045 |
bool InferenceManager::sendInference(const std::vector<Node>& exp, |
153 |
|
Node eq, |
154 |
|
InferenceId infer, |
155 |
|
bool isRev, |
156 |
|
bool asLemma) |
157 |
|
{ |
158 |
46090 |
std::vector<Node> noExplain; |
159 |
46090 |
return sendInference(exp, noExplain, eq, infer, isRev, asLemma); |
160 |
|
} |
161 |
|
|
162 |
27073 |
void InferenceManager::sendInference(InferInfo& ii, bool asLemma) |
163 |
|
{ |
164 |
27073 |
Assert(!ii.isTrivial()); |
165 |
|
// set that this inference manager will be processing this inference |
166 |
27073 |
ii.d_sim = this; |
167 |
54146 |
Trace("strings-infer-debug") |
168 |
27073 |
<< "sendInference: " << ii << ", asLemma = " << asLemma << std::endl; |
169 |
|
// check if we should send a conflict, lemma or a fact |
170 |
27073 |
if (ii.isConflict()) |
171 |
|
{ |
172 |
680 |
Trace("strings-infer-debug") << "...as conflict" << std::endl; |
173 |
1360 |
Trace("strings-lemma") << "Strings::Conflict: " << ii.d_premises << " by " |
174 |
680 |
<< ii.getId() << std::endl; |
175 |
680 |
Trace("strings-conflict") << "CONFLICT: inference conflict " << ii.d_premises << " by " << ii.getId() << std::endl; |
176 |
680 |
++(d_statistics.d_conflictsInfer); |
177 |
|
// process the conflict immediately |
178 |
680 |
processConflict(ii); |
179 |
680 |
return; |
180 |
|
} |
181 |
40320 |
else if (asLemma || options::stringInferAsLemmas() || !ii.isFact()) |
182 |
|
{ |
183 |
13889 |
Trace("strings-infer-debug") << "...as lemma" << std::endl; |
184 |
13889 |
addPendingLemma(std::unique_ptr<InferInfo>(new InferInfo(ii))); |
185 |
13889 |
return; |
186 |
|
} |
187 |
12504 |
if (options::stringInferSym()) |
188 |
|
{ |
189 |
24580 |
std::vector<Node> unproc; |
190 |
51413 |
for (const Node& ac : ii.d_premises) |
191 |
|
{ |
192 |
38909 |
d_termReg.removeProxyEqs(ac, unproc); |
193 |
|
} |
194 |
12504 |
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 |
12076 |
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 |
12076 |
Trace("strings-infer-debug") << "...as fact" << std::endl; |
223 |
|
// add to pending to be processed as a fact |
224 |
12076 |
addPendingFact(std::unique_ptr<InferInfo>(new InferInfo(ii))); |
225 |
|
} |
226 |
|
|
227 |
422 |
bool InferenceManager::sendSplit(Node a, Node b, InferenceId infer, bool preq) |
228 |
|
{ |
229 |
844 |
Node eq = a.eqNode(b); |
230 |
422 |
eq = Rewriter::rewrite(eq); |
231 |
422 |
if (eq.isConst()) |
232 |
|
{ |
233 |
|
return false; |
234 |
|
} |
235 |
422 |
NodeManager* nm = NodeManager::currentNM(); |
236 |
844 |
InferInfo iiSplit(infer); |
237 |
422 |
iiSplit.d_sim = this; |
238 |
422 |
iiSplit.d_conc = nm->mkNode(OR, eq, nm->mkNode(NOT, eq)); |
239 |
422 |
eq = Rewriter::rewrite(eq); |
240 |
422 |
addPendingPhaseRequirement(eq, preq); |
241 |
422 |
addPendingLemma(std::unique_ptr<InferInfo>(new InferInfo(iiSplit))); |
242 |
422 |
return true; |
243 |
|
} |
244 |
|
|
245 |
423299 |
void InferenceManager::addToExplanation(Node a, |
246 |
|
Node b, |
247 |
|
std::vector<Node>& exp) const |
248 |
|
{ |
249 |
423299 |
if (a != b) |
250 |
|
{ |
251 |
391808 |
Debug("strings-explain") |
252 |
195904 |
<< "Add to explanation : " << a << " == " << b << std::endl; |
253 |
195904 |
Assert(d_state.areEqual(a, b)); |
254 |
195904 |
exp.push_back(a.eqNode(b)); |
255 |
|
} |
256 |
423299 |
} |
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 |
2075915 |
bool InferenceManager::hasProcessed() const |
268 |
|
{ |
269 |
2075915 |
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 |
872 |
void InferenceManager::processConflict(const InferInfo& ii) |
278 |
|
{ |
279 |
872 |
Assert(!d_state.isInConflict()); |
280 |
|
// setup the fact to reproduce the proof in the call below |
281 |
872 |
if (d_ipc != nullptr) |
282 |
|
{ |
283 |
158 |
d_ipc->notifyFact(ii); |
284 |
|
} |
285 |
|
// make the trust node |
286 |
1744 |
TrustNode tconf = mkConflictExp(ii.d_premises, d_ipc.get()); |
287 |
872 |
Assert(tconf.getKind() == TrustNodeKind::CONFLICT); |
288 |
1744 |
Trace("strings-assert") << "(assert (not " << tconf.getNode() |
289 |
872 |
<< ")) ; conflict " << ii.getId() << std::endl; |
290 |
|
// send the trusted conflict |
291 |
872 |
trustedConflict(tconf, ii.getId()); |
292 |
872 |
} |
293 |
|
|
294 |
11742 |
void InferenceManager::processFact(InferInfo& ii, ProofGenerator*& pg) |
295 |
|
{ |
296 |
23484 |
Trace("strings-assert") << "(assert (=> " << ii.getPremises() << " " |
297 |
11742 |
<< ii.d_conc << ")) ; fact " << ii.getId() << std::endl; |
298 |
23484 |
Trace("strings-lemma") << "Strings::Fact: " << ii.d_conc << " from " |
299 |
23484 |
<< ii.getPremises() << " by " << ii.getId() |
300 |
11742 |
<< std::endl; |
301 |
11742 |
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 |
11742 |
} |
309 |
|
|
310 |
14588 |
TrustNode InferenceManager::processLemma(InferInfo& ii, LemmaProperty& p) |
311 |
|
{ |
312 |
14588 |
Assert(!ii.isTrivial()); |
313 |
14588 |
Assert(!ii.isConflict()); |
314 |
|
// set up the explanation and no-explanation |
315 |
29176 |
std::vector<Node> exp; |
316 |
42573 |
for (const Node& ec : ii.d_premises) |
317 |
|
{ |
318 |
27985 |
utils::flattenOp(AND, ec, exp); |
319 |
|
} |
320 |
29176 |
std::vector<Node> noExplain; |
321 |
29176 |
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 |
17155 |
for (const Node& ecn : ii.d_noExplain) |
331 |
|
{ |
332 |
2567 |
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 |
14588 |
if (d_ipc != nullptr) |
338 |
|
{ |
339 |
947 |
d_ipc->notifyFact(ii); |
340 |
|
} |
341 |
14588 |
TrustNode tlem = mkLemmaExp(ii.d_conc, exp, noExplain, d_ipc.get()); |
342 |
29176 |
Trace("strings-pending") << "Process pending lemma : " << tlem.getNode() |
343 |
14588 |
<< 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 |
822 |
for (const std::pair<const LengthStatus, std::vector<Node> >& sks : |
350 |
14588 |
ii.d_skolems) |
351 |
|
{ |
352 |
1644 |
for (const Node& n : sks.second) |
353 |
|
{ |
354 |
822 |
d_termReg.registerTermAtomic(n, sks.first); |
355 |
|
} |
356 |
|
} |
357 |
14588 |
if (ii.getId() == InferenceId::STRINGS_REDUCTION) |
358 |
|
{ |
359 |
1406 |
p |= LemmaProperty::NEEDS_JUSTIFY; |
360 |
|
} |
361 |
29176 |
Trace("strings-assert") << "(assert " << tlem.getNode() << ") ; lemma " |
362 |
14588 |
<< ii.getId() << std::endl; |
363 |
29176 |
Trace("strings-lemma") << "Strings::Lemma: " << tlem.getNode() << " by " |
364 |
14588 |
<< ii.getId() << std::endl; |
365 |
29176 |
return tlem; |
366 |
|
} |
367 |
|
|
368 |
|
} // namespace strings |
369 |
|
} // namespace theory |
370 |
28191 |
} // namespace cvc5 |