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