1 /********************* */
2 /*! \file non_clausal_simp.cpp
3 ** \verbatim
4 ** Top contributors (to current version):
5 ** Aina Niemetz, Tim King, Andres Noetzli
6 ** This file is part of the CVC4 project.
7 ** Copyright (c) 2009-2019 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.\endverbatim
11 **
12 ** \brief Non-clausal simplification preprocessing pass.
13 **
14 ** Run the nonclausal solver and try to solve all assigned theory literals.
15 **/
16
17 #include "preprocessing/passes/non_clausal_simp.h"
18
19 #include <vector>
20
21 #include "context/cdo.h"
22 #include "options/proof_options.h"
23 #include "smt/smt_statistics_registry.h"
24 #include "theory/theory_model.h"
25
26 using namespace CVC4;
27 using namespace CVC4::theory;
28
29 namespace CVC4 {
30 namespace preprocessing {
31 namespace passes {
32
33 /* -------------------------------------------------------------------------- */
34
Statistics()35 NonClausalSimp::Statistics::Statistics()
36 : d_numConstantProps(
37 "preprocessing::passes::NonClausalSimp::NumConstantProps", 0)
38 {
39 smtStatisticsRegistry()->registerStat(&d_numConstantProps);
40 }
41
~Statistics()42 NonClausalSimp::Statistics::~Statistics()
43 {
44 smtStatisticsRegistry()->unregisterStat(&d_numConstantProps);
45 }
46
47 /* -------------------------------------------------------------------------- */
48
NonClausalSimp(PreprocessingPassContext * preprocContext)49 NonClausalSimp::NonClausalSimp(PreprocessingPassContext* preprocContext)
50 : PreprocessingPass(preprocContext, "non-clausal-simp")
51 {
52 }
53
applyInternal(AssertionPipeline * assertionsToPreprocess)54 PreprocessingPassResult NonClausalSimp::applyInternal(
55 AssertionPipeline* assertionsToPreprocess)
56 {
57 Assert(!options::unsatCores() && !options::fewerPreprocessingHoles());
58
59 d_preprocContext->spendResource(options::preprocessStep());
60
61 theory::booleans::CircuitPropagator* propagator =
62 d_preprocContext->getCircuitPropagator();
63
64 for (size_t i = 0, size = assertionsToPreprocess->size(); i < size; ++i)
65 {
66 Trace("non-clausal-simplify") << "Assertion #" << i << " : "
67 << (*assertionsToPreprocess)[i] << std::endl;
68 }
69
70 if (propagator->getNeedsFinish())
71 {
72 propagator->finish();
73 propagator->setNeedsFinish(false);
74 }
75 propagator->initialize();
76
77 // Assert all the assertions to the propagator
78 Trace("non-clausal-simplify") << "asserting to propagator" << std::endl;
79 unsigned substs_index = d_preprocContext->getSubstitutionsIndex();
80 for (size_t i = 0, size = assertionsToPreprocess->size(); i < size; ++i)
81 {
82 Assert(Rewriter::rewrite((*assertionsToPreprocess)[i])
83 == (*assertionsToPreprocess)[i]);
84 // Don't reprocess substitutions
85 if (substs_index > 0 && i == substs_index)
86 {
87 continue;
88 }
89 Trace("non-clausal-simplify")
90 << "asserting " << (*assertionsToPreprocess)[i] << std::endl;
91 Debug("cores") << "propagator->assertTrue: " << (*assertionsToPreprocess)[i]
92 << std::endl;
93 propagator->assertTrue((*assertionsToPreprocess)[i]);
94 }
95
96 Trace("non-clausal-simplify") << "propagating" << std::endl;
97 if (propagator->propagate())
98 {
99 // If in conflict, just return false
100 Trace("non-clausal-simplify")
101 << "conflict in non-clausal propagation" << std::endl;
102 Assert(!options::unsatCores() && !options::fewerPreprocessingHoles());
103 assertionsToPreprocess->clear();
104 Node n = NodeManager::currentNM()->mkConst<bool>(false);
105 assertionsToPreprocess->push_back(n);
106 PROOF(ProofManager::currentPM()->addDependence(n, Node::null()));
107 propagator->setNeedsFinish(true);
108 return PreprocessingPassResult::CONFLICT;
109 }
110
111 Trace("non-clausal-simplify")
112 << "Iterate through " << propagator->getLearnedLiterals().size()
113 << " learned literals." << std::endl;
114 // No conflict, go through the literals and solve them
115 SubstitutionMap& top_level_substs =
116 d_preprocContext->getTopLevelSubstitutions();
117 SubstitutionMap constantPropagations(d_preprocContext->getUserContext());
118 SubstitutionMap newSubstitutions(d_preprocContext->getUserContext());
119 SubstitutionMap::iterator pos;
120 size_t j = 0;
121 std::vector<Node>& learned_literals = propagator->getLearnedLiterals();
122 for (size_t i = 0, size = learned_literals.size(); i < size; ++i)
123 {
124 // Simplify the literal we learned wrt previous substitutions
125 Node learnedLiteral = learned_literals[i];
126 Assert(Rewriter::rewrite(learnedLiteral) == learnedLiteral);
127 Assert(top_level_substs.apply(learnedLiteral) == learnedLiteral);
128 Trace("non-clausal-simplify")
129 << "Process learnedLiteral : " << learnedLiteral << std::endl;
130 Node learnedLiteralNew = newSubstitutions.apply(learnedLiteral);
131 if (learnedLiteral != learnedLiteralNew)
132 {
133 learnedLiteral = Rewriter::rewrite(learnedLiteralNew);
134 }
135 Trace("non-clausal-simplify")
136 << "Process learnedLiteral, after newSubs : " << learnedLiteral
137 << std::endl;
138 for (;;)
139 {
140 learnedLiteralNew = constantPropagations.apply(learnedLiteral);
141 if (learnedLiteralNew == learnedLiteral)
142 {
143 break;
144 }
145 d_statistics.d_numConstantProps += 1;
146 learnedLiteral = Rewriter::rewrite(learnedLiteralNew);
147 }
148 Trace("non-clausal-simplify")
149 << "Process learnedLiteral, after constProp : " << learnedLiteral
150 << std::endl;
151 // It might just simplify to a constant
152 if (learnedLiteral.isConst())
153 {
154 if (learnedLiteral.getConst<bool>())
155 {
156 // If the learned literal simplifies to true, it's redundant
157 continue;
158 }
159 else
160 {
161 // If the learned literal simplifies to false, we're in conflict
162 Trace("non-clausal-simplify")
163 << "conflict with " << learned_literals[i] << std::endl;
164 Assert(!options::unsatCores());
165 assertionsToPreprocess->clear();
166 Node n = NodeManager::currentNM()->mkConst<bool>(false);
167 assertionsToPreprocess->push_back(n);
168 PROOF(ProofManager::currentPM()->addDependence(n, Node::null()));
169 propagator->setNeedsFinish(true);
170 return PreprocessingPassResult::CONFLICT;
171 }
172 }
173
174 // Solve it with the corresponding theory, possibly adding new
175 // substitutions to newSubstitutions
176 Trace("non-clausal-simplify") << "solving " << learnedLiteral << std::endl;
177
178 Theory::PPAssertStatus solveStatus =
179 d_preprocContext->getTheoryEngine()->solve(learnedLiteral,
180 newSubstitutions);
181
182 switch (solveStatus)
183 {
184 case Theory::PP_ASSERT_STATUS_SOLVED:
185 {
186 // The literal should rewrite to true
187 Trace("non-clausal-simplify")
188 << "solved " << learnedLiteral << std::endl;
189 Assert(Rewriter::rewrite(newSubstitutions.apply(learnedLiteral))
190 .isConst());
191 // vector<pair<Node, Node> > equations;
192 // constantPropagations.simplifyLHS(top_level_substs, equations,
193 // true); if (equations.empty()) {
194 // break;
195 // }
196 // Assert(equations[0].first.isConst() &&
197 // equations[0].second.isConst() && equations[0].first !=
198 // equations[0].second);
199 // else fall through
200 break;
201 }
202 case Theory::PP_ASSERT_STATUS_CONFLICT:
203 {
204 // If in conflict, we return false
205 Trace("non-clausal-simplify")
206 << "conflict while solving " << learnedLiteral << std::endl;
207 Assert(!options::unsatCores());
208 assertionsToPreprocess->clear();
209 Node n = NodeManager::currentNM()->mkConst<bool>(false);
210 assertionsToPreprocess->push_back(n);
211 PROOF(ProofManager::currentPM()->addDependence(n, Node::null()));
212 propagator->setNeedsFinish(true);
213 return PreprocessingPassResult::CONFLICT;
214 }
215 default:
216 if (learnedLiteral.getKind() == kind::EQUAL
217 && (learnedLiteral[0].isConst() || learnedLiteral[1].isConst()))
218 {
219 // constant propagation
220 TNode t;
221 TNode c;
222 if (learnedLiteral[0].isConst())
223 {
224 t = learnedLiteral[1];
225 c = learnedLiteral[0];
226 }
227 else
228 {
229 t = learnedLiteral[0];
230 c = learnedLiteral[1];
231 }
232 Assert(!t.isConst());
233 Assert(constantPropagations.apply(t) == t);
234 Assert(top_level_substs.apply(t) == t);
235 Assert(newSubstitutions.apply(t) == t);
236 constantPropagations.addSubstitution(t, c);
237 // vector<pair<Node,Node> > equations;
238 // constantPropagations.simplifyLHS(t, c, equations, true);
239 // if (!equations.empty()) {
240 // Assert(equations[0].first.isConst() &&
241 // equations[0].second.isConst() && equations[0].first !=
242 // equations[0].second); assertionsToPreprocess->clear();
243 // Node n = NodeManager::currentNM()->mkConst<bool>(false);
244 // assertionsToPreprocess->push_back(n);
245 // PROOF(ProofManager::currentPM()->addDependence(n, Node::null()));
246 // false); return;
247 // }
248 // top_level_substs.simplifyRHS(constantPropagations);
249 }
250 else
251 {
252 // Keep the literal
253 learned_literals[j++] = learned_literals[i];
254 }
255 break;
256 }
257 }
258
259 #ifdef CVC4_ASSERTIONS
260 // NOTE: When debugging this code, consider moving this check inside of the
261 // loop over propagator->getLearnedLiterals(). This check has been moved
262 // outside because it is costly for certain inputs (see bug 508).
263 //
264 // Check data structure invariants:
265 // 1. for each lhs of top_level_substs, does not appear anywhere in rhs of
266 // top_level_substs or anywhere in constantPropagations
267 // 2. each lhs of constantPropagations rewrites to itself
268 // 3. if l -> r is a constant propagation and l is a subterm of l' with l' ->
269 // r' another constant propagation, then l'[l/r] -> r' should be a
270 // constant propagation too
271 // 4. each lhs of constantPropagations is different from each rhs
272 for (pos = newSubstitutions.begin(); pos != newSubstitutions.end(); ++pos)
273 {
274 Assert((*pos).first.isVar());
275 Assert(top_level_substs.apply((*pos).first) == (*pos).first);
276 Assert(top_level_substs.apply((*pos).second) == (*pos).second);
277 Assert(newSubstitutions.apply(newSubstitutions.apply((*pos).second))
278 == newSubstitutions.apply((*pos).second));
279 }
280 for (pos = constantPropagations.begin(); pos != constantPropagations.end();
281 ++pos)
282 {
283 Assert((*pos).second.isConst());
284 Assert(Rewriter::rewrite((*pos).first) == (*pos).first);
285 // Node newLeft = top_level_substs.apply((*pos).first);
286 // if (newLeft != (*pos).first) {
287 // newLeft = Rewriter::rewrite(newLeft);
288 // Assert(newLeft == (*pos).second ||
289 // (constantPropagations.hasSubstitution(newLeft) &&
290 // constantPropagations.apply(newLeft) == (*pos).second));
291 // }
292 // newLeft = constantPropagations.apply((*pos).first);
293 // if (newLeft != (*pos).first) {
294 // newLeft = Rewriter::rewrite(newLeft);
295 // Assert(newLeft == (*pos).second ||
296 // (constantPropagations.hasSubstitution(newLeft) &&
297 // constantPropagations.apply(newLeft) == (*pos).second));
298 // }
299 Assert(constantPropagations.apply((*pos).second) == (*pos).second);
300 }
301 #endif /* CVC4_ASSERTIONS */
302
303 // Resize the learnt
304 Trace("non-clausal-simplify")
305 << "Resize non-clausal learned literals to " << j << std::endl;
306 learned_literals.resize(j);
307
308 unordered_set<TNode, TNodeHashFunction> s;
309 for (size_t i = 0, size = assertionsToPreprocess->size(); i < size; ++i)
310 {
311 Node assertion = (*assertionsToPreprocess)[i];
312 Node assertionNew = newSubstitutions.apply(assertion);
313 Trace("non-clausal-simplify") << "assertion = " << assertion << std::endl;
314 Trace("non-clausal-simplify")
315 << "assertionNew = " << assertionNew << std::endl;
316 if (assertion != assertionNew)
317 {
318 assertion = Rewriter::rewrite(assertionNew);
319 Trace("non-clausal-simplify")
320 << "rewrite(assertion) = " << assertion << std::endl;
321 }
322 Assert(Rewriter::rewrite(assertion) == assertion);
323 for (;;)
324 {
325 assertionNew = constantPropagations.apply(assertion);
326 if (assertionNew == assertion)
327 {
328 break;
329 }
330 d_statistics.d_numConstantProps += 1;
331 Trace("non-clausal-simplify")
332 << "assertionNew = " << assertionNew << std::endl;
333 assertion = Rewriter::rewrite(assertionNew);
334 Trace("non-clausal-simplify")
335 << "assertionNew = " << assertionNew << std::endl;
336 }
337 s.insert(assertion);
338 assertionsToPreprocess->replace(i, assertion);
339 Trace("non-clausal-simplify")
340 << "non-clausal preprocessed: " << assertion << std::endl;
341 }
342
343 // add substitutions to model, or as assertions if needed (when incremental)
344 TheoryModel* m = d_preprocContext->getTheoryEngine()->getModel();
345 Assert(m != nullptr);
346 NodeManager* nm = NodeManager::currentNM();
347 NodeBuilder<> substitutionsBuilder(kind::AND);
348 for (pos = newSubstitutions.begin(); pos != newSubstitutions.end(); ++pos)
349 {
350 Node lhs = (*pos).first;
351 Node rhs = newSubstitutions.apply((*pos).second);
352 // If using incremental, we must check whether this variable has occurred
353 // before now. If it hasn't we can add this as a substitution.
354 if (substs_index == 0
355 || d_preprocContext->getSymsInAssertions().find(lhs)
356 == d_preprocContext->getSymsInAssertions().end())
357 {
358 Trace("non-clausal-simplify")
359 << "substitute: " << lhs << " " << rhs << std::endl;
360 m->addSubstitution(lhs, rhs);
361 }
362 else
363 {
364 // if it has, the substitution becomes an assertion
365 Node eq = nm->mkNode(kind::EQUAL, lhs, rhs);
366 Trace("non-clausal-simplify")
367 << "substitute: will notify SAT layer of substitution: " << eq
368 << std::endl;
369 substitutionsBuilder << eq;
370 }
371 }
372 // add to the last assertion if necessary
373 if (substitutionsBuilder.getNumChildren() > 0)
374 {
375 substitutionsBuilder << (*assertionsToPreprocess)[substs_index];
376 assertionsToPreprocess->replace(
377 substs_index, Rewriter::rewrite(Node(substitutionsBuilder)));
378 }
379
380 NodeBuilder<> learnedBuilder(kind::AND);
381 Assert(assertionsToPreprocess->getRealAssertionsEnd()
382 <= assertionsToPreprocess->size());
383 learnedBuilder << (*assertionsToPreprocess)
384 [assertionsToPreprocess->getRealAssertionsEnd() - 1];
385
386 for (size_t i = 0; i < learned_literals.size(); ++i)
387 {
388 Node learned = learned_literals[i];
389 Assert(top_level_substs.apply(learned) == learned);
390 Node learnedNew = newSubstitutions.apply(learned);
391 if (learned != learnedNew)
392 {
393 learned = Rewriter::rewrite(learnedNew);
394 }
395 Assert(Rewriter::rewrite(learned) == learned);
396 for (;;)
397 {
398 learnedNew = constantPropagations.apply(learned);
399 if (learnedNew == learned)
400 {
401 break;
402 }
403 d_statistics.d_numConstantProps += 1;
404 learned = Rewriter::rewrite(learnedNew);
405 }
406 if (s.find(learned) != s.end())
407 {
408 continue;
409 }
410 s.insert(learned);
411 learnedBuilder << learned;
412 Trace("non-clausal-simplify")
413 << "non-clausal learned : " << learned << std::endl;
414 }
415 learned_literals.clear();
416
417 for (pos = constantPropagations.begin(); pos != constantPropagations.end();
418 ++pos)
419 {
420 Node cProp = (*pos).first.eqNode((*pos).second);
421 Assert(top_level_substs.apply(cProp) == cProp);
422 Node cPropNew = newSubstitutions.apply(cProp);
423 if (cProp != cPropNew)
424 {
425 cProp = Rewriter::rewrite(cPropNew);
426 Assert(Rewriter::rewrite(cProp) == cProp);
427 }
428 if (s.find(cProp) != s.end())
429 {
430 continue;
431 }
432 s.insert(cProp);
433 learnedBuilder << cProp;
434 Trace("non-clausal-simplify")
435 << "non-clausal constant propagation : " << cProp << std::endl;
436 }
437
438 // Add new substitutions to topLevelSubstitutions
439 // Note that we don't have to keep rhs's in full solved form
440 // because SubstitutionMap::apply does a fixed-point iteration when
441 // substituting
442 top_level_substs.addSubstitutions(newSubstitutions);
443
444 if (learnedBuilder.getNumChildren() > 1)
445 {
446 assertionsToPreprocess->replace(
447 assertionsToPreprocess->getRealAssertionsEnd() - 1,
448 Rewriter::rewrite(Node(learnedBuilder)));
449 }
450
451 propagator->setNeedsFinish(true);
452 return PreprocessingPassResult::NO_CONFLICT;
453 } // namespace passes
454
455
456 /* -------------------------------------------------------------------------- */
457
458 } // namespace passes
459 } // namespace preprocessing
460 } // namespace CVC4
461