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 |
9928 |
InferenceManager::InferenceManager(Env& env, |
34 |
|
Theory& t, |
35 |
|
SolverState& s, |
36 |
|
TermRegistry& tr, |
37 |
|
ExtTheory& e, |
38 |
|
SequencesStatistics& statistics, |
39 |
9928 |
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 |
9928 |
d_ipc(pnm ? new InferProofCons(context(), pnm, d_statistics) : nullptr) |
46 |
|
{ |
47 |
9928 |
NodeManager* nm = NodeManager::currentNM(); |
48 |
9928 |
d_zero = nm->mkConst(Rational(0)); |
49 |
9928 |
d_one = nm->mkConst(Rational(1)); |
50 |
9928 |
d_true = nm->mkConst(true); |
51 |
9928 |
d_false = nm->mkConst(false); |
52 |
9928 |
} |
53 |
|
|
54 |
41932 |
void InferenceManager::doPending() |
55 |
|
{ |
56 |
41932 |
doPendingFacts(); |
57 |
41932 |
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 |
39026 |
doPendingLemmas(); |
65 |
39026 |
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 |
3845296 |
bool InferenceManager::hasProcessed() const |
269 |
|
{ |
270 |
3845296 |
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 |
29505 |
} // namespace cvc5 |