summaryrefslogtreecommitdiff
path: root/src/theory/arrays/theory_arrays.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/theory/arrays/theory_arrays.cpp')
-rw-r--r--src/theory/arrays/theory_arrays.cpp1577
1 files changed, 787 insertions, 790 deletions
diff --git a/src/theory/arrays/theory_arrays.cpp b/src/theory/arrays/theory_arrays.cpp
index 03a9d7a4c..718483143 100644
--- a/src/theory/arrays/theory_arrays.cpp
+++ b/src/theory/arrays/theory_arrays.cpp
@@ -23,45 +23,87 @@
#include <map>
#include "theory/rewriter.h"
#include "expr/command.h"
+#include "theory/uf/equality_engine_impl.h"
+
using namespace std;
-using namespace CVC4;
-using namespace CVC4::kind;
-using namespace CVC4::context;
-using namespace CVC4::theory;
-using namespace CVC4::theory::arrays;
-TheoryArrays::TheoryArrays(Context* c, UserContext* u, OutputChannel& out, Valuation valuation) :
+namespace CVC4 {
+namespace theory {
+namespace arrays {
+
+
+// These are the options that produce the best empirical results on QF_AX benchmarks.
+// Use static configuration of options for now
+const bool d_ccStore = false;
+const bool d_useArrTable = false;
+const bool d_eagerLemmas = true;
+const bool d_propagateLemmas = false;
+const bool d_preprocess = true;
+const bool d_solveWrite = true;
+const bool d_useNonLinearOpt = true;
+
+
+TheoryArrays::TheoryArrays(context::Context* c, context::UserContext* u, OutputChannel& out, Valuation valuation) :
Theory(THEORY_ARRAY, c, u, out, valuation),
- d_ccChannel(this),
- d_cc(c, &d_ccChannel),
- d_unionFind(c),
- d_backtracker(c),
- d_infoMap(c,&d_backtracker),
- d_disequalities(c),
- d_equalities(c),
- d_prop_queue(c),
- d_atoms(),
- d_explanations(c),
- d_conflict(),
d_numRow("theory::arrays::number of Row lemmas", 0),
d_numExt("theory::arrays::number of Ext lemmas", 0),
d_numProp("theory::arrays::number of propagations", 0),
d_numExplain("theory::arrays::number of explanations", 0),
+ d_numNonLinear("theory::arrays::number of calls to setNonLinear", 0),
+ d_numSharedArrayVarSplits("theory::arrays::number of shared array var splits", 0),
d_checkTimer("theory::arrays::checkTime"),
- d_donePreregister(false)
+ d_ppNotify(),
+ d_ppEqualityEngine(d_ppNotify, u, "theory::arrays::TheoryArraysPP"),
+ d_ppFacts(u),
+ // d_ppCache(u),
+ d_literalsToPropagate(c),
+ d_literalsToPropagateIndex(c, 0),
+ d_mayEqualNotify(),
+ d_mayEqualEqualityEngine(d_mayEqualNotify, c, "theory::arrays::TheoryArraysMayEqual"),
+ d_notify(*this),
+ d_equalityEngine(d_notify, c, "theory::arrays::TheoryArrays"),
+ d_conflict(c, false),
+ d_backtracker(c),
+ d_infoMap(c, &d_backtracker),
+ d_mergeQueue(c),
+ d_mergeInProgress(false),
+ d_RowQueue(u),
+ d_RowAlreadyAdded(u),
+ d_sharedArrays(c),
+ d_sharedTerms(c, false),
+ d_reads(c),
+ d_permRef(c)
{
- //d_backtracker = new Backtracker<TNode>(c);
- //d_infoMap = ArrayInfo(c, d_backtracker);
-
StatisticsRegistry::registerStat(&d_numRow);
StatisticsRegistry::registerStat(&d_numExt);
StatisticsRegistry::registerStat(&d_numProp);
StatisticsRegistry::registerStat(&d_numExplain);
+ StatisticsRegistry::registerStat(&d_numNonLinear);
+ StatisticsRegistry::registerStat(&d_numSharedArrayVarSplits);
StatisticsRegistry::registerStat(&d_checkTimer);
+ d_true = NodeManager::currentNM()->mkConst<bool>(true);
+ d_false = NodeManager::currentNM()->mkConst<bool>(false);
+
+ d_ppEqualityEngine.addTerm(d_true);
+ d_ppEqualityEngine.addTerm(d_false);
+ d_ppEqualityEngine.addTriggerEquality(d_true, d_false, d_false);
+
+ d_equalityEngine.addTerm(d_true);
+ d_equalityEngine.addTerm(d_false);
+ d_equalityEngine.addTriggerEquality(d_true, d_false, d_false);
+ // The kinds we are treating as function application in congruence
+ d_equalityEngine.addFunctionKind(kind::SELECT);
+ if (d_ccStore) {
+ d_equalityEngine.addFunctionKind(kind::STORE);
+ }
+ d_equalityEngine.addFunctionKind(kind::EQUAL);
+ if (d_useArrTable) {
+ d_equalityEngine.addFunctionKind(kind::ARR_TABLE_FUN);
+ }
}
@@ -71,159 +113,27 @@ TheoryArrays::~TheoryArrays() {
StatisticsRegistry::unregisterStat(&d_numExt);
StatisticsRegistry::unregisterStat(&d_numProp);
StatisticsRegistry::unregisterStat(&d_numExplain);
+ StatisticsRegistry::unregisterStat(&d_numNonLinear);
+ StatisticsRegistry::unregisterStat(&d_numSharedArrayVarSplits);
StatisticsRegistry::unregisterStat(&d_checkTimer);
}
-void TheoryArrays::addSharedTerm(TNode t) {
- Trace("arrays") << "Arrays::addSharedTerm(): "
- << t << endl;
-}
-
-
-void TheoryArrays::notifyEq(TNode lhs, TNode rhs) {
-}
-
-void TheoryArrays::notifyCongruent(TNode a, TNode b) {
- Trace("arrays") << "Arrays::notifyCongruent(): "
- << a << " = " << b << endl;
- if(!d_conflict.isNull()) {
- return;
- }
- merge(a,b);
-}
-
-
-void TheoryArrays::check(Effort e) {
- TimerStat::CodeTimer codeTimer(d_checkTimer);
-
- if(!d_donePreregister ) {
- // only start looking for lemmas after preregister on all input terms
- // has been called
- return;
- }
-
- Trace("arrays") <<"Arrays::start check ";
- while(!done()) {
- Node assertion = get();
- Trace("arrays") << "Arrays::check(): " << assertion << endl;
-
- switch(assertion.getKind()) {
- case kind::EQUAL:
- case kind::IFF:
- d_cc.addEquality(assertion);
- if(!d_conflict.isNull()) {
- // addEquality can cause a notify congruent which calls merge
- // which can lead to a conflict
- Node conflict = constructConflict(d_conflict);
- d_conflict = Node::null();
- d_out->conflict(conflict);
- return;
- }
- merge(assertion[0], assertion[1]);
- break;
- case kind::NOT:
- {
- Assert(assertion[0].getKind() == kind::EQUAL ||
- assertion[0].getKind() == kind::IFF );
- Node a = assertion[0][0];
- Node b = assertion[0][1];
- addDiseq(assertion[0]);
-
- d_cc.addTerm(a);
- d_cc.addTerm(b);
-
- if(!d_conflict.isNull()) {
- // we got notified through notifyCongruent which called merge
- // after addTerm since we weren't watching a or b before
- Node conflict = constructConflict(d_conflict);
- d_conflict = Node::null();
- d_out->conflict(conflict);
- return;
- }
- else if(find(a) == find(b)) {
- Node conflict = constructConflict(assertion[0]);
- d_conflict = Node::null();
- d_out->conflict(conflict);
- return;
- }
- Assert(!d_cc.areCongruent(a,b));
- if(a.getType().isArray()) {
- queueExtLemma(a, b);
- }
- break;
- }
- default:
- Unhandled(assertion.getKind());
- }
-
- }
-
- if(fullEffort(e)) {
- // generate the lemmas on the worklist
- //while(!d_RowQueue.empty() || ! d_extQueue.empty()) {
- dischargeLemmas();
- Trace("arrays-lem")<<"Arrays::discharged lemmas "<<d_RowQueue.size()<<"\n";
- //}
- }
- Trace("arrays") << "Arrays::check(): done" << endl;
-}
-
-Node TheoryArrays::getValue(TNode n) {
- NodeManager* nodeManager = NodeManager::currentNM();
-
- switch(n.getKind()) {
+/////////////////////////////////////////////////////////////////////////////
+// PREPROCESSING
+/////////////////////////////////////////////////////////////////////////////
- case kind::VARIABLE:
- Unhandled(kind::VARIABLE);
-
- case kind::EQUAL: // 2 args
- return nodeManager->
- mkConst( d_valuation.getValue(n[0]) == d_valuation.getValue(n[1]) );
-
- default:
- Unhandled(n.getKind());
- }
-}
-
-Theory::PPAssertStatus TheoryArrays::ppAssert(TNode in, SubstitutionMap& outSubstitutions) {
- switch(in.getKind()) {
- case kind::EQUAL:
- {
- d_staticFactManager.addEq(in);
- if (in[0].getMetaKind() == kind::metakind::VARIABLE && !in[1].hasSubterm(in[0])) {
- outSubstitutions.addSubstitution(in[0], in[1]);
- return PP_ASSERT_STATUS_SOLVED;
- }
- if (in[1].getMetaKind() == kind::metakind::VARIABLE && !in[0].hasSubterm(in[1])) {
- outSubstitutions.addSubstitution(in[1], in[0]);
- return PP_ASSERT_STATUS_SOLVED;
- }
- break;
- }
- case kind::NOT:
- {
- Assert(in[0].getKind() == kind::EQUAL ||
- in[0].getKind() == kind::IFF );
- Node a = in[0][0];
- Node b = in[0][1];
- d_staticFactManager.addDiseq(in[0]);
- break;
- }
- default:
- break;
- }
- return PP_ASSERT_STATUS_UNSOLVED;
-}
-Node TheoryArrays::preprocessTerm(TNode term) {
+Node TheoryArrays::ppRewrite(TNode term) {
+ if (!d_preprocess) return term;
switch (term.getKind()) {
case kind::SELECT: {
// select(store(a,i,v),j) = select(a,j)
// IF i != j
if (term[0].getKind() == kind::STORE &&
- d_staticFactManager.areDiseq(term[0][1], term[1])) {
+ (d_ppEqualityEngine.areDisequal(term[0][1], term[1]) ||
+ (term[0][1].isConst() && term[1].isConst() && term[0][1] != term[1]))) {
return NodeBuilder<2>(kind::SELECT) << term[0][0] << term[1];
}
break;
@@ -233,7 +143,8 @@ Node TheoryArrays::preprocessTerm(TNode term) {
// IF i != j and j comes before i in the ordering
if (term[0].getKind() == kind::STORE &&
(term[1] < term[0][1]) &&
- d_staticFactManager.areDiseq(term[1], term[0][1])) {
+ (d_ppEqualityEngine.areDisequal(term[1], term[0][1]) ||
+ (term[0][1].isConst() && term[1].isConst() && term[0][1] != term[1]))) {
Node inner = NodeBuilder<3>(kind::STORE) << term[0][0] << term[1] << term[2];
Node outer = NodeBuilder<3>(kind::STORE) << inner << term[0][1] << term[0][2];
return outer;
@@ -241,6 +152,7 @@ Node TheoryArrays::preprocessTerm(TNode term) {
break;
}
case kind::EQUAL: {
+ if (!d_solveWrite) break;
if (term[0].getKind() == kind::STORE ||
term[1].getKind() == kind::STORE) {
TNode left = term[0];
@@ -295,7 +207,8 @@ Node TheoryArrays::preprocessTerm(TNode term) {
NodeBuilder<> hyp(kind::AND);
for (j = leftWrites - 1; j > i; --j) {
index_j = write_j[1];
- if (d_staticFactManager.areDiseq(index_i, index_j)) {
+ if (d_ppEqualityEngine.areDisequal(index_i, index_j) ||
+ (index_i.isConst() && index_j.isConst() && index_i != index_j)) {
continue;
}
Node hyp2(index_i.getType() == nm->booleanType()?
@@ -338,7 +251,7 @@ Node TheoryArrays::preprocessTerm(TNode term) {
Node l = left;
Node tmp;
NodeBuilder<> nb(kind::AND);
- while (right.getKind() == STORE) {
+ while (right.getKind() == kind::STORE) {
tmp = nm->mkNode(kind::SELECT, l, right[1]);
nb << tmp.eqNode(right[2]);
tmp = nm->mkNode(kind::SELECT, right[0], right[1]);
@@ -357,699 +270,683 @@ Node TheoryArrays::preprocessTerm(TNode term) {
return term;
}
-Node TheoryArrays::recursivePreprocessTerm(TNode term) {
- unsigned nc = term.getNumChildren();
- if (nc == 0 ||
- (theoryOf(term) != theory::THEORY_ARRAY &&
- term.getType() != NodeManager::currentNM()->booleanType())) {
- return term;
- }
- NodeMap::iterator find = d_ppCache.find(term);
- if (find != d_ppCache.end()) {
- return (*find).second;
- }
- NodeBuilder<> newNode(term.getKind());
- unsigned i;
- for (i = 0; i < nc; ++i) {
- newNode << recursivePreprocessTerm(term[i]);
- }
- Node newTerm = Rewriter::rewrite(newNode);
- Node newTerm2 = preprocessTerm(newTerm);
- if (newTerm != newTerm2) {
- newTerm = recursivePreprocessTerm(Rewriter::rewrite(newTerm2));
- }
- d_ppCache[term] = newTerm;
- return newTerm;
-}
-Node TheoryArrays::ppRewrite(TNode atom) {
- if (d_donePreregister) return atom;
- Assert(atom.getKind() == kind::EQUAL, "expected EQUAL, got %s", atom.toString().c_str());
- return recursivePreprocessTerm(atom);
+Theory::PPAssertStatus TheoryArrays::ppAssert(TNode in, SubstitutionMap& outSubstitutions) {
+ switch(in.getKind()) {
+ case kind::EQUAL:
+ {
+ d_ppFacts.push_back(in);
+ d_ppEqualityEngine.addEquality(in[0], in[1], in);
+ if (in[0].getMetaKind() == kind::metakind::VARIABLE && !in[1].hasSubterm(in[0])) {
+ outSubstitutions.addSubstitution(in[0], in[1]);
+ return PP_ASSERT_STATUS_SOLVED;
+ }
+ if (in[1].getMetaKind() == kind::metakind::VARIABLE && !in[0].hasSubterm(in[1])) {
+ outSubstitutions.addSubstitution(in[1], in[0]);
+ return PP_ASSERT_STATUS_SOLVED;
+ }
+ break;
+ }
+ case kind::NOT:
+ {
+ d_ppFacts.push_back(in);
+ Assert(in[0].getKind() == kind::EQUAL ||
+ in[0].getKind() == kind::IFF );
+ Node a = in[0][0];
+ Node b = in[0][1];
+ d_ppEqualityEngine.addDisequality(a, b, in);
+ break;
+ }
+ default:
+ break;
+ }
+ return PP_ASSERT_STATUS_UNSOLVED;
}
-void TheoryArrays::merge(TNode a, TNode b) {
- Assert(d_conflict.isNull());
+/////////////////////////////////////////////////////////////////////////////
+// T-PROPAGATION / REGISTRATION
+/////////////////////////////////////////////////////////////////////////////
- Trace("arrays-merge")<<"Arrays::merge() " << a <<" and " <<b <<endl;
- /*
- * take care of the congruence closure part
- */
+bool TheoryArrays::propagate(TNode literal)
+{
+ Debug("arrays") << spaces(getContext()->getLevel()) << "TheoryArrays::propagate(" << literal << ")" << std::endl;
- // make "a" the one with shorter diseqList
- CNodeTNodesMap::iterator deq_ia = d_disequalities.find(a);
- CNodeTNodesMap::iterator deq_ib = d_disequalities.find(b);
+ // If already in conflict, no more propagation
+ if (d_conflict) {
+ Debug("arrays") << spaces(getContext()->getLevel()) << "TheoryArrays::propagate(" << literal << "): already in conflict" << std::endl;
+ return false;
+ }
- if(deq_ia != d_disequalities.end()) {
- if(deq_ib == d_disequalities.end() ||
- (*deq_ia).second->size() > (*deq_ib).second->size()) {
- TNode tmp = a;
- a = b;
- b = tmp;
+ // See if the literal has been asserted already
+ Node normalized = Rewriter::rewrite(literal);
+ bool satValue = false;
+ bool isAsserted = normalized == d_false || d_valuation.hasSatValue(normalized, satValue);
+
+ // If asserted, we're done or in conflict
+ if (isAsserted) {
+ if (!satValue) {
+ Debug("arrays") << spaces(getContext()->getLevel()) << "TheoryArrays::propagate(" << literal << ", normalized = " << normalized << ") => conflict" << std::endl;
+ std::vector<TNode> assumptions;
+ Node negatedLiteral;
+ if (normalized != d_false) {
+ negatedLiteral = normalized.getKind() == kind::NOT ? (Node) normalized[0] : normalized.notNode();
+ assumptions.push_back(negatedLiteral);
+ }
+ explain(literal, assumptions);
+ d_conflictNode = mkAnd(assumptions);
+ d_conflict = true;
+ return false;
}
+ // Propagate even if already known in SAT - could be a new equation between shared terms
+ // (terms that weren't shared when the literal was asserted!)
}
- a = find(a);
- b = find(b);
- if( a == b) {
- return;
- }
+ // Nothing, just enqueue it for propagation and mark it as asserted already
+ Debug("arrays") << spaces(getContext()->getLevel()) << "TheoryArrays::propagate(" << literal << ") => enqueuing for propagation" << std::endl;
+ d_literalsToPropagate.push_back(literal);
+ return true;
+}/* TheoryArrays::propagate(TNode) */
- // b becomes the canon of a
- setCanon(a, b);
-
- deq_ia = d_disequalities.find(a);
- map<TNode, TNode> alreadyDiseqs;
- if(deq_ia != d_disequalities.end()) {
- /*
- * Collecting the disequalities of b, no need to check for conflicts
- * since the representative of b does not change and we check all the things
- * in a's class when we look at the diseq list of find(a)
- */
-
- CNodeTNodesMap::iterator deq_ib = d_disequalities.find(b);
- if(deq_ib != d_disequalities.end()) {
- CTNodeListAlloc* deq = (*deq_ib).second;
- for(CTNodeListAlloc::const_iterator j = deq->begin(); j!=deq->end(); j++) {
- TNode deqn = *j;
- TNode s = deqn[0];
- TNode t = deqn[1];
- TNode sp = find(s);
- TNode tp = find(t);
- Assert(sp == b || tp == b);
- if(sp == b) {
- alreadyDiseqs[tp] = deqn;
- } else {
- alreadyDiseqs[sp] = deqn;
- }
- }
- }
- /*
- * Looking for conflicts in the a disequality list. Note
- * that at this point a and b are already merged. Also has
- * the side effect that it adds them to the list of b (which
- * became the canonical representative)
- */
-
- CTNodeListAlloc* deqa = (*deq_ia).second;
- for(CTNodeListAlloc::const_iterator i = deqa->begin(); i!= deqa->end(); i++) {
- TNode deqn = (*i);
- Assert(deqn.getKind() == kind::EQUAL || deqn.getKind() == kind::IFF);
- TNode s = deqn[0];
- TNode t = deqn[1];
- TNode sp = find(s);
- TNode tp = find(t);
-
- if(find(s) == find(t)) {
- d_conflict = deqn;
+void TheoryArrays::explain(TNode literal, std::vector<TNode>& assumptions) {
+ TNode lhs, rhs;
+ switch (literal.getKind()) {
+ case kind::EQUAL:
+ lhs = literal[0];
+ rhs = literal[1];
+ break;
+ case kind::SELECT:
+ lhs = literal;
+ rhs = d_true;
+ break;
+ case kind::NOT:
+ if (literal[0].getKind() == kind::EQUAL) {
+ // Disequalities
+ d_equalityEngine.explainDisequality(literal[0][0], literal[0][1], assumptions);
return;
- }
- Assert( sp == b || tp == b);
-
- // make sure not to add duplicates
-
- if(sp == b) {
- if(alreadyDiseqs.find(tp) == alreadyDiseqs.end()) {
- appendToDiseqList(b, deqn);
- alreadyDiseqs[tp] = deqn;
- }
} else {
- if(alreadyDiseqs.find(sp) == alreadyDiseqs.end()) {
- appendToDiseqList(b, deqn);
- alreadyDiseqs[sp] = deqn;
- }
+ // Predicates
+ lhs = literal[0];
+ rhs = d_false;
+ break;
}
+ case kind::CONST_BOOLEAN:
+ // we get to explain true = false, since we set false to be the trigger of this
+ lhs = d_true;
+ rhs = d_false;
+ break;
+ default:
+ Unreachable();
+ }
+ d_equalityEngine.explainEquality(lhs, rhs, assumptions);
+}
+
+ /**
+ * Stores in d_infoMap the following information for each term a of type array:
+ *
+ * - all i, such that there exists a term a[i] or a = store(b i v)
+ * (i.e. all indices it is being read atl; store(b i v) is implicitly read at
+ * position i due to the implicit axiom store(b i v)[i] = v )
+ *
+ * - all the stores a is congruent to (this information is context dependent)
+ *
+ * - all store terms of the form store (a i v) (i.e. in which a appears
+ * directly; this is invariant because no new store terms are created)
+ *
+ * Note: completeness depends on having pre-register called on all the input
+ * terms before starting to instantiate lemmas.
+ */
+void TheoryArrays::preRegisterTerm(TNode node)
+{
+ Debug("arrays") << spaces(getContext()->getLevel()) << "TheoryArrays::preRegisterTerm(" << node << ")" << std::endl;
+
+ switch (node.getKind()) {
+ case kind::EQUAL:
+ // Add the terms
+ // d_equalityEngine.addTerm(node[0]);
+ // d_equalityEngine.addTerm(node[1]);
+ d_equalityEngine.addTerm(node);
+ // Add the trigger for equality
+ d_equalityEngine.addTriggerEquality(node[0], node[1], node);
+ d_equalityEngine.addTriggerDisequality(node[0], node[1], node.notNode());
+ break;
+ case kind::SELECT:
+ // Reads
+ d_equalityEngine.addTerm(node);
+ // Maybe it's a predicate
+ // TODO: remove this or keep it if we allow Boolean elements in arrays.
+ if (node.getType().isBoolean()) {
+ // Get triggered for both equal and dis-equal
+ d_equalityEngine.addTriggerEquality(node, d_true, node);
+ d_equalityEngine.addTriggerEquality(node, d_false, node.notNode());
}
- }
- // TODO: check for equality propagations
- // a and b are find(a) and find(b) here
- checkPropagations(a,b);
+ d_infoMap.addIndex(node[0], node[1]);
+ checkRowForIndex(node[1], d_equalityEngine.getRepresentative(node[0]));
+ d_reads.push_back(node);
+ break;
- if(a.getType().isArray()) {
- checkRowLemmas(a,b);
- checkRowLemmas(b,a);
- // note the change in order, merge info adds the list of
- // the 2nd argument to the first
- d_infoMap.mergeInfo(b, a);
- }
+ case kind::STORE: {
+ d_equalityEngine.addTriggerTerm(node);
+ TNode a = node[0];
+ TNode i = node[1];
+ TNode v = node[2];
-}
+ d_mayEqualEqualityEngine.addEquality(node, node[0], d_true);
+ NodeManager* nm = NodeManager::currentNM();
+ Node ni = nm->mkNode(kind::SELECT, node, i);
+ if (!d_equalityEngine.hasTerm(ni)) {
+ preRegisterTerm(ni);
+ }
-void TheoryArrays::checkPropagations(TNode a, TNode b) {
- EqLists::const_iterator ita = d_equalities.find(a);
- EqLists::const_iterator itb = d_equalities.find(b);
+ // Apply RIntro1 Rule
+ d_equalityEngine.addEquality(ni, v, d_true);
- if(ita != d_equalities.end()) {
- if(itb!= d_equalities.end()) {
- // because b is the canonical representative
- List<TNode>* eqsa = (*ita).second;
- List<TNode>* eqsb = (*itb).second;
+ d_infoMap.addStore(node, node);
+ d_infoMap.addInStore(a, node);
- for(List<TNode>::const_iterator it = eqsa->begin(); it!= eqsa->end(); ++it) {
- Node eq = *it;
- Assert(eq.getKind() == kind::EQUAL || eq.getKind() == kind::IFF);
- if(find(eq[0])== find(eq[1])) {
- d_prop_queue.push_back(eq);
- }
- }
- eqsb->concat(eqsa);
+ checkStore(node);
+ break;
+ }
+ default:
+ // Variables etc
+ if (node.getType().isArray()) {
+ d_equalityEngine.addTriggerTerm(node);
}
else {
- List<TNode>* eqsb = new List<TNode>(&d_backtracker);
- List<TNode>* eqsa = (*ita).second;
- d_equalities.insert(b, eqsb);
- eqsb->concat(eqsa);
+ d_equalityEngine.addTerm(node);
}
- } else {
- List<TNode>* eqsb = new List<TNode>(&d_backtracker);
- d_equalities.insert(b, eqsb);
- List<TNode>* eqsa = new List<TNode>(&d_backtracker);
- d_equalities.insert(a, eqsa);
- eqsb->concat(eqsa);
+ break;
}
}
-bool TheoryArrays::isNonLinear(TNode a) {
- Assert(a.getType().isArray());
- const CTNodeList* inst = d_infoMap.getInStores(find(a));
- if(inst->size()<=1) {
- return false;
- }
- return true;
-}
-
-bool TheoryArrays::isAxiom(TNode t1, TNode t2) {
- Trace("arrays-axiom")<<"Arrays::isAxiom start "<<t1<<" = "<<t2<<"\n";
- if(t1.getKind() == kind::SELECT) {
- TNode a = t1[0];
- TNode i = t1[1];
-
- if(a.getKind() == kind::STORE) {
- TNode b = a[0];
- TNode j = a[1];
- TNode v = a[2];
- if(i == j && v == t2) {
- Trace("arrays-axiom")<<"Arrays::isAxiom true\n";
- return true;
+void TheoryArrays::propagate(Effort e)
+{
+ Debug("arrays") << spaces(getContext()->getLevel()) << "TheoryArrays::propagate()" << std::endl;
+ if (!d_conflict) {
+ for (; d_literalsToPropagateIndex < d_literalsToPropagate.size(); d_literalsToPropagateIndex = d_literalsToPropagateIndex + 1) {
+ TNode literal = d_literalsToPropagate[d_literalsToPropagateIndex];
+ Debug("arrays") << spaces(getContext()->getLevel()) << "TheoryArrays::propagate(): propagating " << literal << std::endl;
+ bool satValue;
+ Node normalized = Rewriter::rewrite(literal);
+ if (!d_valuation.hasSatValue(normalized, satValue) || satValue) {
+ d_out->propagate(literal);
+ } else {
+ Debug("arrays") << spaces(getContext()->getLevel()) << "TheoryArrays::propagate(): in conflict, normalized = " << normalized << std::endl;
+ Node negatedLiteral;
+ std::vector<TNode> assumptions;
+ if (normalized != d_false) {
+ negatedLiteral = normalized.getKind() == kind::NOT ? (Node) normalized[0] : normalized.notNode();
+ assumptions.push_back(negatedLiteral);
+ }
+ explain(literal, assumptions);
+ d_conflictNode = mkAnd(assumptions);
+ d_conflict = true;
+ break;
}
}
}
- return false;
+
}
-Node TheoryArrays::constructConflict(TNode diseq) {
- Trace("arrays") << "arrays: begin constructConflict()" << endl;
- Trace("arrays") << "arrays: using diseq == " << diseq << endl;
+Node TheoryArrays::explain(TNode literal)
+{
+ ++d_numExplain;
+ Debug("arrays") << spaces(getContext()->getLevel()) << "TheoryArrays::explain(" << literal << ")" << std::endl;
+ std::vector<TNode> assumptions;
+ explain(literal, assumptions);
+ return mkAnd(assumptions);
+}
+
- // returns the reason the two terms are equal
- Node explanation = d_cc.explain(diseq[0], diseq[1]);
+/////////////////////////////////////////////////////////////////////////////
+// SHARING
+/////////////////////////////////////////////////////////////////////////////
- NodeBuilder<> nb(kind::AND);
- if(explanation.getKind() == kind::EQUAL ||
- explanation.getKind() == kind::IFF) {
- // if the explanation is only one literal
- if(!isAxiom(explanation[0], explanation[1]) &&
- !isAxiom(explanation[1], explanation[0])) {
- nb<<explanation;
- }
+void TheoryArrays::addSharedTerm(TNode t) {
+ Debug("arrays::sharing") << spaces(getContext()->getLevel()) << "TheoryArrays::addSharedTerm(" << t << ")" << std::endl;
+ d_equalityEngine.addTriggerTerm(t);
+ if (t.getType().isArray()) {
+ d_sharedArrays.insert(t,true);
}
else {
- Assert(explanation.getKind() == kind::AND);
- for(TNode::iterator i = TNode(explanation).begin();
- i != TNode(explanation).end(); i++) {
- if(!isAxiom((*i)[0], (*i)[1]) && !isAxiom((*i)[1], (*i)[0])) {
- nb<<*i;
- }
- }
+ d_sharedTerms = true;
}
-
- nb<<diseq.notNode();
- Node conflict = nb;
- Trace("arrays") << "conflict constructed : " << conflict << endl;
- return conflict;
}
-/*
-void TheoryArrays::addAtom(TNode eq) {
- Assert(eq.getKind() == kind::EQUAL);
-
- TNode lhs = eq[0];
- TNode rhs = eq[1];
-
- appendToAtomList(find(lhs), rhs);
- appendToAtomList(find(rhs), lhs);
-}
-void TheoryArrays::appendToAtomList(TNode a, TNode b) {
- Assert(find(a) == a);
-
- NodeTNodesMap::const_iterator it = d_atoms.find(a);
- if(it != d_atoms.end()) {
- (*it).second->push_back(b);
+EqualityStatus TheoryArrays::getEqualityStatus(TNode a, TNode b) {
+ Assert(d_equalityEngine.hasTerm(a) && d_equalityEngine.hasTerm(b));
+ if (d_equalityEngine.areEqual(a, b)) {
+ // The terms are implied to be equal
+ return EQUALITY_TRUE;
}
- else {
- CTNodeList* list = new (true)CTNodeList(getContext());
- list->push_back(b);
- d_atoms[a] = list;
+ if (d_equalityEngine.areDisequal(a, b)) {
+ // The terms are implied to be dis-equal
+ return EQUALITY_FALSE;
}
-
+ //TODO: can we be more precise sometimes?
+ return EQUALITY_UNKNOWN;
}
-*/
-void TheoryArrays::addEq(TNode eq) {
- Assert(eq.getKind() == kind::EQUAL ||
- eq.getKind() == kind::IFF);
- TNode a = eq[0];
- TNode b = eq[1];
-
- // don't need to say find(a) because due to the structure of the lists it gets
- // automatically added
- appendToEqList(a, eq);
- appendToEqList(b, eq);
-
-}
-void TheoryArrays::appendToEqList(TNode n, TNode eq) {
- Assert(eq.getKind() == kind::EQUAL ||
- eq.getKind() == kind::IFF);
-
- EqLists::const_iterator it = d_equalities.find(n);
- if(it == d_equalities.end()) {
- List<TNode>* eqs = new List<TNode>(&d_backtracker);
- eqs->append(eq);
- d_equalities.insert(n, eqs);
- } else {
- List<TNode>* eqs = (*it).second;
- eqs->append(eq);
+void TheoryArrays::computeCareGraph()
+{
+ if (d_sharedArrays.size() > 0) {
+ context::CDHashMap<TNode, bool, TNodeHashFunction>::iterator it1 = d_sharedArrays.begin(), it2, iend = d_sharedArrays.end();
+ for (; it1 != iend; ++it1) {
+ for (it2 = it1, ++it2; it2 != iend; ++it2) {
+ EqualityStatus eqStatusArr = getEqualityStatus((*it1).first, (*it2).first);
+ if (eqStatusArr != EQUALITY_UNKNOWN) {
+ continue;
+ }
+ Assert(d_valuation.getEqualityStatus((*it1).first, (*it2).first) == EQUALITY_UNKNOWN);
+ addCarePair((*it1).first, (*it2).first);
+ ++d_numSharedArrayVarSplits;
+ return;
+ }
+ }
}
-}
+ if (d_sharedTerms) {
-void TheoryArrays::addDiseq(TNode diseq) {
- Assert(diseq.getKind() == kind::EQUAL ||
- diseq.getKind() == kind::IFF);
- TNode a = diseq[0];
- TNode b = diseq[1];
+ vector< pair<TNode, TNode> > currentPairs;
- appendToDiseqList(find(a), diseq);
- appendToDiseqList(find(b), diseq);
+ // Go through the read terms and see if there are any to split on
+ unsigned size = d_reads.size();
+ for (unsigned i = 0; i < size; ++ i) {
+ TNode r1 = d_reads[i];
-}
+ for (unsigned j = i + 1; j < size; ++ j) {
+ TNode r2 = d_reads[j];
-void TheoryArrays::appendToDiseqList(TNode of, TNode eq) {
- Trace("arrays") << "appending " << eq << endl
- << " to diseq list of " << of << endl;
- Assert(eq.getKind() == kind::EQUAL ||
- eq.getKind() == kind::IFF);
-
- CNodeTNodesMap::iterator deq_i = d_disequalities.find(of);
- CTNodeListAlloc* deq;
- if(deq_i == d_disequalities.end()) {
- deq = new(getContext()->getCMM()) CTNodeListAlloc(true, getContext(), false,
- ContextMemoryAllocator<TNode>(getContext()->getCMM()));
- d_disequalities.insertDataFromContextMemory(of, deq);
- } else {
- deq = (*deq_i).second;
- }
+ Debug("arrays::sharing") << "TheoryArrays::computeCareGraph(): checking reads " << r1 << " and " << r2 << std::endl;
- deq->push_back(eq);
-}
+ // If the terms are already known to be equal, we are also in good shape
+ if (d_equalityEngine.areEqual(r1, r2)) {
+ Debug("arrays::sharing") << "TheoryArrays::computeCareGraph(): equal, skipping" << std::endl;
+ continue;
+ }
+ if (r1[0] != r2[0]) {
+ // If arrays are known to be disequal, or cannot become equal, we can continue
+ if (d_equalityEngine.areDisequal(r1[0], r2[0]) ||
+ (!d_mayEqualEqualityEngine.areEqual(r1[0], r2[0]))) {
+ Debug("arrays::sharing") << "TheoryArrays::computeCareGraph(): arrays can't be equal, skipping" << std::endl;
+ continue;
+ }
+ }
-/**
- * Iterates through the indices of a and stores of b and checks if any new
- * Row lemmas need to be instantiated.
- */
-bool TheoryArrays::isRedundantRowLemma(TNode a, TNode b, TNode i, TNode j) {
- Assert(a.getType().isArray());
- Assert(b.getType().isArray());
+ TNode x = r1[1];
+ TNode y = r2[1];
- if(d_RowAlreadyAdded.count(make_quad(a, b, i, j)) != 0 ||
- d_RowAlreadyAdded.count(make_quad(b, a, i, j)) != 0 ) {
- return true;
- }
+ EqualityStatus eqStatus = getEqualityStatus(x, y);
- return false;
-}
+ if (eqStatus != EQUALITY_UNKNOWN) {
+ Debug("arrays::sharing") << "TheoryArrays::computeCareGraph(): equality status known, skipping" << std::endl;
+ continue;
+ }
-bool TheoryArrays::isRedundantInContext(TNode a, TNode b, TNode i, TNode j) {
- NodeManager* nm = NodeManager::currentNM();
- Node aj = nm->mkNode(kind::SELECT, a, j);
- Node bj = nm->mkNode(kind::SELECT, b, j);
+ if (!d_equalityEngine.isTriggerTerm(x) || !d_equalityEngine.isTriggerTerm(y)) {
+ Debug("arrays::sharing") << "TheoryArrays::computeCareGraph(): not connected to shared terms, skipping" << std::endl;
+ continue;
+ }
- if(find(i) == find(j) || find(aj) == find(bj)) {
- Trace("arrays") << "isRedundantInContext valid "
- << a << " " << b << " " << i << " " << j << endl;
- checkRowForIndex(j, b); // why am i doing this?
- checkRowForIndex(i, a);
- return true;
- }
- Trace("arrays") << "isRedundantInContext " << a << endl
- << "isRedundantInContext " << b << endl
- << "isRedundantInContext " << i << endl
- << "isRedundantInContext " << j << endl;
- Node ieqj = i.eqNode(j);
- Node literal1 = Rewriter::rewrite(ieqj);
- bool hasValue1, satValue1;
- Node ff = nm->mkConst<bool>(false);
- Node tt = nm->mkConst<bool>(true);
- if (literal1 == ff) {
- hasValue1 = true;
- satValue1 = false;
- } else if (literal1 == tt) {
- hasValue1 = true;
- satValue1 = true;
- } else {
- hasValue1 = (d_valuation.isSatLiteral(literal1) && d_valuation.hasSatValue(literal1, satValue1));
- }
- if (hasValue1) {
- if (satValue1) {
- Trace("arrays") << "isRedundantInContext returning, hasValue1 && satValue1" << endl;
- return true;
- }
- Node ajeqbj = aj.eqNode(bj);
- Node literal2 = Rewriter::rewrite(ajeqbj);
- bool hasValue2, satValue2;
- if (literal2 == ff) {
- hasValue2 = true;
- satValue2 = false;
- } else if (literal2 == tt) {
- hasValue2 = true;
- satValue2 = true;
- } else {
- hasValue2 = (d_valuation.isSatLiteral(literal2) && d_valuation.hasSatValue(literal2, satValue2));
- }
- if (hasValue2) {
- if (satValue2) {
- Trace("arrays") << "isRedundantInContext returning, hasValue2 && satValue2" << endl;
- return true;
+ // Get representative trigger terms
+ TNode x_shared = d_equalityEngine.getTriggerTermRepresentative(x);
+ TNode y_shared = d_equalityEngine.getTriggerTermRepresentative(y);
+
+ EqualityStatus eqStatusDomain = d_valuation.getEqualityStatus(x_shared, y_shared);
+ switch (eqStatusDomain) {
+ case EQUALITY_FALSE_AND_PROPAGATED:
+ case EQUALITY_FALSE:
+ continue;
+ break;
+ case EQUALITY_TRUE_AND_PROPAGATED:
+ case EQUALITY_TRUE:
+ // Should have been propagated to us
+ Assert(false);
+ continue;
+ break;
+ case EQUALITY_FALSE_IN_MODEL:
+ Debug("arrays::sharing") << "TheoryArrays::computeCareGraph(): false in model, skipping" << std::endl;
+ continue;
+ break;
+ default:
+ break;
+ }
+
+ // Otherwise, add this pair
+ Debug("arrays::sharing") << "TheoryArrays::computeCareGraph(): adding to care-graph" << std::endl;
+ addCarePair(x_shared, y_shared);
}
- // conflict
- Assert(!satValue1 && !satValue2);
- Assert(literal1.getKind() == kind::EQUAL && literal2.getKind() == kind::EQUAL);
- NodeBuilder<2> nb(kind::AND);
- //literal1 = areDisequal(literal1[0], literal1[1]);
- //literal2 = areDisequal(literal2[0], literal2[1]);
- Assert(!literal1.isNull() && !literal2.isNull());
- nb << literal1.notNode() << literal2.notNode();
- literal1 = nb;
- d_conflict = Node::null();
- d_out->conflict(literal1);
- Trace("arrays") << "TheoryArrays::isRedundantInContext() "
- << "conflict via lemma: " << literal1 << endl;
- return true;
}
}
- if(alreadyAddedRow(a, b, i, j)) {
- Trace("arrays") << "isRedundantInContext already added "
- << a << " " << b << " " << i << " " << j << endl;
- return true;
- }
- return false;
}
-TNode TheoryArrays::areDisequal(TNode a, TNode b) {
- Trace("arrays-prop") << "Arrays::areDisequal " << a << " " << b << "\n";
-
- a = find(a);
- b = find(b);
-
- CNodeTNodesMap::const_iterator it = d_disequalities.find(a);
- if(it!= d_disequalities.end()) {
- CTNodeListAlloc::const_iterator i = (*it).second->begin();
- for( ; i!= (*it).second->end(); i++) {
- Trace("arrays-prop") << " " << *i << "\n";
- TNode s = (*i)[0];
- TNode t = (*i)[1];
-
- Assert(find(s) == a || find(t) == a);
- TNode sp, tp;
- if(find(t) == a) {
- sp = find(t);
- tp = find(s);
- }
- else {
- sp = find(s);
- tp = find(t);
- }
- if(tp == b) {
- return *i;
- }
+/////////////////////////////////////////////////////////////////////////////
+// MODEL GENERATION
+/////////////////////////////////////////////////////////////////////////////
- }
- }
- Trace("arrays-prop") << " not disequal \n";
- return TNode::null();
-}
-bool TheoryArrays::propagateFromRow(TNode a, TNode b, TNode i, TNode j) {
- Trace("arrays-prop")<<"Arrays::propagateFromRow "<<a<<" "<<b<<" "<<i<<" "<<j<<"\n";
+Node TheoryArrays::getValue(TNode n)
+{
+ // TODO: Implement this
+ NodeManager* nodeManager = NodeManager::currentNM();
- NodeManager* nm = NodeManager::currentNM();
- Node aj = nm->mkNode(kind::SELECT, a, j);
- Node bj = nm->mkNode(kind::SELECT, b, j);
+ switch(n.getKind()) {
- Node eq_aj_bj = nm->mkNode(kind::EQUAL,aj, bj);
- Node eq_ij = nm->mkNode(kind::EQUAL, i, j);
-
- // first check if the current context implies NOT (i = j)
- // in which case we can propagate a[j] = b[j]
- // FIXME: does i = j need to be a SAT literal or can we propagate anyway?
-
- // if it does not have an atom we must add the lemma (do we?!)
- if(d_atoms.count(eq_aj_bj) != 0 ||
- d_atoms.count(nm->mkNode(kind::EQUAL, bj, aj))!=0) {
- Node diseq = areDisequal(i, j);
- // check if the current context implies that (NOT i = j)
- if(diseq != TNode::null()) {
- // if it's unassigned
- Trace("arrays-prop")<<"satValue "<<d_valuation.getSatValue(eq_aj_bj).isNull();
- if(d_valuation.getSatValue(eq_aj_bj).isNull()) {
- d_out->propagate(eq_aj_bj);
- ++d_numProp;
- // save explanation
- d_explanations.insert(eq_aj_bj,std::make_pair(eq_ij, diseq));
- return true;
- }
+ case kind::VARIABLE:
+ Unhandled(kind::VARIABLE);
- }
- }
+ case kind::EQUAL: // 2 args
+ return nodeManager->
+ mkConst( d_valuation.getValue(n[0]) == d_valuation.getValue(n[1]) );
- // now check if the current context implies NOT a[j] = b[j]
- // in which case we propagate i = j
+ default:
+ Unhandled(n.getKind());
+ }
+}
- // we can't propagate if it does not have an atom
- if(d_atoms.count(eq_ij) != 0 || d_atoms.count(nm->mkNode(kind::EQUAL, j, i))!= 0) {
- Node diseq = areDisequal(aj, bj);
- Assert(TNode::null() == Node::null());
+/////////////////////////////////////////////////////////////////////////////
+// NOTIFICATIONS
+/////////////////////////////////////////////////////////////////////////////
- if(diseq != TNode::null()) {
- if(d_valuation.getSatValue(eq_ij) == Node::null()) {
- d_out->propagate(eq_ij);
- ++d_numProp;
- // save explanation
- d_explanations.insert(eq_ij, std::make_pair(eq_aj_bj,diseq));
- return true;
- }
- }
- }
- Trace("arrays-prop")<<"Arrays::propagateFromRow no prop \n";
- return false;
+void TheoryArrays::presolve()
+{
+ Trace("arrays")<<"Presolving \n";
}
-Node TheoryArrays::explain(TNode n) {
+/////////////////////////////////////////////////////////////////////////////
+// MAIN SOLVER
+/////////////////////////////////////////////////////////////////////////////
- Trace("arrays")<<"Arrays::explain start for "<<n<<"\n";
- ++d_numExplain;
-
- Assert(n.getKind()==kind::EQUAL);
- Node explanation = d_cc.explain(n[0], n[1]);
+void TheoryArrays::check(Effort e) {
+ TimerStat::CodeTimer codeTimer(d_checkTimer);
- NodeBuilder<> nb(kind::AND);
+ while (!done() && !d_conflict)
+ {
+ // Get all the assertions
+ Assertion assertion = get();
+ TNode fact = assertion.assertion;
+
+ Debug("arrays") << spaces(getContext()->getLevel()) << "TheoryArrays::check(): processing " << fact << std::endl;
+
+ // If the assertion doesn't have a literal, it's a shared equality
+ Assert(assertion.isPreregistered ||
+ ((fact.getKind() == kind::EQUAL && d_equalityEngine.hasTerm(fact[0]) && d_equalityEngine.hasTerm(fact[1])) ||
+ (fact.getKind() == kind::NOT && fact[0].getKind() == kind::EQUAL &&
+ d_equalityEngine.hasTerm(fact[0][0]) && d_equalityEngine.hasTerm(fact[0][1]))));
+
+ // Do the work
+ switch (fact.getKind()) {
+ case kind::EQUAL:
+ d_equalityEngine.addEquality(fact[0], fact[1], fact);
+ break;
+ case kind::SELECT:
+ d_equalityEngine.addPredicate(fact, true, fact);
+ break;
+ case kind::NOT:
+ if (fact[0].getKind() == kind::SELECT) {
+ d_equalityEngine.addPredicate(fact[0], false, fact);
+ } else if (!d_equalityEngine.areDisequal(fact[0][0], fact[0][1])) {
+ // Assert the dis-equality
+ d_equalityEngine.addDisequality(fact[0][0], fact[0][1], fact);
+
+ // Apply ArrDiseq Rule if diseq is between arrays
+ if(fact[0][0].getType().isArray()) {
+ NodeManager* nm = NodeManager::currentNM();
+ TypeNode indexType = fact[0][0].getType()[0];
+ TNode k;
+ std::hash_map<TNode, Node, TNodeHashFunction>::iterator it = d_diseqCache.find(fact);
+ if (it == d_diseqCache.end()) {
+ Node newk = nm->mkVar(indexType);
+ Dump.declareVar(newk.toExpr(),
+ "an extensional lemma index variable from the theory of arrays");
+ d_diseqCache[fact] = newk;
+ k = newk;
+ }
+ else {
+ k = (*it).second;
+ }
- if(explanation.getKind() == kind::EQUAL ||
- explanation.getKind() == kind::IFF) {
- // if the explanation is only one literal
- if(!isAxiom(explanation[0], explanation[1]) &&
- !isAxiom(explanation[1], explanation[0])) {
- nb<<explanation;
+ Node ak = nm->mkNode(kind::SELECT, fact[0][0], k);
+ Node bk = nm->mkNode(kind::SELECT, fact[0][1], k);
+ if (!d_equalityEngine.hasTerm(ak)) {
+ preRegisterTerm(ak);
+ }
+ if (!d_equalityEngine.hasTerm(bk)) {
+ preRegisterTerm(bk);
+ }
+ d_equalityEngine.addDisequality(ak, bk, fact);
+ Trace("arrays-lem")<<"Arrays::addExtLemma "<< ak << " /= " << bk <<"\n";
+ ++d_numExt;
+ }
+ }
+ break;
+ default:
+ Unreachable();
}
}
+
+ // If in conflict, output the conflict
+ if (d_conflict) {
+ Debug("arrays") << spaces(getContext()->getLevel()) << "TheoryArrays::check(): conflict " << d_conflictNode << std::endl;
+ d_out->conflict(d_conflictNode);
+ }
else {
- Assert(explanation.getKind() == kind::AND);
- for(TNode::iterator i = TNode(explanation).begin();
- i != TNode(explanation).end(); i++) {
- if(!isAxiom((*i)[0], (*i)[1]) && !isAxiom((*i)[1], (*i)[0])) {
- nb<<*i;
- }
+ // Otherwise we propagate
+ propagate(e);
+
+ if(!d_eagerLemmas && fullEffort(e)) {
+ // generate the lemmas on the worklist
+ Trace("arrays-lem")<<"Arrays::discharging lemmas: "<<d_RowQueue.size()<<"\n";
+ dischargeLemmas();
}
}
- Node reason = nb;
-
- return reason;
-
- /*
- context::CDMap<TNode, std::pair<TNode, TNode>, TNodeHashFunction>::const_iterator
- it = d_explanations.find(n);
- Assert(it!= d_explanations.end());
- TNode diseq = (*it).second.second;
- TNode s = diseq[0];
- TNode t = diseq[1];
- TNode eq = (*it).second.first;
- TNode a = eq[0];
- TNode b = eq[1];
+ Trace("arrays") << spaces(getContext()->getLevel()) << "Arrays::check(): done" << endl;
+}
- Assert ((find(a) == find(s) && find(b) == find(t)) ||
- (find(a) == find(t) && find(b) == find(s)));
+Node TheoryArrays::mkAnd(std::vector<TNode>& conjunctions)
+{
+ Assert(conjunctions.size() > 0);
- if(find(a) == find(t)) {
- TNode temp = t;
- t = s;
- s = temp;
- }
+ std::set<TNode> all;
+ std::set<TNode> explained;
- // construct the explanation
+ unsigned i = 0;
+ TNode t;
+ for (; i < conjunctions.size(); ++i) {
+ t = conjunctions[i];
- NodeBuilder<> nb(kind::AND);
- Node explanation1 = d_cc.explain(a, s);
- Node explanation2 = d_cc.explain(b, t);
+ // Remove true node - represents axiomatically true assertion
+ if (t == d_true) continue;
- if(explanation1.getKind() == kind::AND) {
- for(TNode::iterator i= TNode(explanation1).begin();
- i!=TNode(explanation1).end(); ++i) {
- nb << *i;
+ // Expand explanation resulting from propagating a ROW lemma
+ if (t.getKind() == kind::OR) {
+ if ((explained.find(t) == explained.end())) {
+ Assert(t[1].getKind() == kind::EQUAL);
+ d_equalityEngine.explainDisequality(t[1][0], t[1][1], conjunctions);
+ explained.insert(t);
+ }
+ continue;
}
- } else {
- Assert(explanation1.getKind() == kind::EQUAL ||
- explanation1.getKind() == kind::IFF);
- nb << explanation1;
+ all.insert(t);
}
- if(explanation2.getKind() == kind::AND) {
- for(TNode::iterator i= TNode(explanation2).begin();
- i!=TNode(explanation2).end(); ++i) {
- nb << *i;
- }
- } else {
- Assert(explanation2.getKind() == kind::EQUAL ||
- explanation2.getKind() == kind::IFF);
- nb << explanation2;
+ Assert(all.size() > 0);
+ if (all.size() == 1) {
+ // All the same, or just one
+ return *(all.begin());
+ }
+
+ NodeBuilder<> conjunction(kind::AND);
+ std::set<TNode>::const_iterator it = all.begin();
+ std::set<TNode>::const_iterator it_end = all.end();
+ while (it != it_end) {
+ conjunction << *it;
+ ++ it;
}
- nb << diseq;
- Node reason = nb;
- d_out->explanation(reason);
- Trace("arrays")<<"explanation "<< reason<<" done \n";
- */
+ return conjunction;
}
-void TheoryArrays::checkRowLemmas(TNode a, TNode b) {
- Trace("arrays-crl")<<"Arrays::checkLemmas begin \n"<<a<<"\n";
- if(Trace.isOn("arrays-crl"))
- d_infoMap.getInfo(a)->print();
- Trace("arrays-crl")<<" ------------ and "<<b<<"\n";
- if(Trace.isOn("arrays-crl"))
- d_infoMap.getInfo(b)->print();
+void TheoryArrays::setNonLinear(TNode a)
+{
+ if (d_infoMap.isNonLinear(a)) return;
+
+ Trace("arrays") << spaces(getContext()->getLevel()) << "Arrays::setNonLinear (" << a << ")\n";
+ d_infoMap.setNonLinear(a);
+ ++d_numNonLinear;
List<TNode>* i_a = d_infoMap.getIndices(a);
- const CTNodeList* st_b = d_infoMap.getStores(b);
- const CTNodeList* inst_b = d_infoMap.getInStores(b);
+ const CTNodeList* st_a = d_infoMap.getStores(a);
+ const CTNodeList* inst_a = d_infoMap.getInStores(a);
- List<TNode>::const_iterator it = i_a->begin();
- CTNodeList::const_iterator its;
+ CTNodeList::const_iterator it = st_a->begin();
- for( ; it != i_a->end(); it++ ) {
- TNode i = *it;
- its = st_b->begin();
- for ( ; its != st_b->end(); its++) {
- TNode store = *its;
+ // Propagate non-linearity down chain of stores
+ for(; it!= st_a->end(); ++it) {
+ TNode store = *it;
+ Assert(store.getKind()==kind::STORE);
+ setNonLinear(store[0]);
+ }
+
+ // Instantiate ROW lemmas that were ignored before
+ List<TNode>::const_iterator itl = i_a->begin();
+ RowLemmaType lem;
+ for(; itl != i_a->end(); ++itl ) {
+ TNode i = *itl;
+ it = inst_a->begin();
+ for ( ; it !=inst_a->end(); ++it) {
+ TNode store = *it;
Assert(store.getKind() == kind::STORE);
TNode j = store[1];
TNode c = store[0];
-
- if( !isRedundantRowLemma(store, c, j, i)){
- //&&!propagateFromRow(store, c, j, i)) {
- queueRowLemma(store, c, j, i);
- }
+ lem = make_quad(store, c, j, i);
+ Trace("arrays-lem") << spaces(getContext()->getLevel()) <<"Arrays::setNonLinear ("<<store<<", "<<c<<", "<<j<<", "<<i<<")\n";
+ queueRowLemma(lem);
}
-
}
- it = i_a->begin();
+}
- for( ; it != i_a->end(); it++ ) {
- TNode i = *it;
- its = inst_b->begin();
- for ( ; its !=inst_b->end(); its++) {
- TNode store = *its;
- Assert(store.getKind() == kind::STORE);
- TNode j = store[1];
- TNode c = store[0];
- if ( isNonLinear(c)
- &&!isRedundantRowLemma(store, c, j, i)){
- //&&!propagateFromRow(store, c, j, i)) {
- queueRowLemma(store, c, j, i);
+void TheoryArrays::mergeArrays(TNode a, TNode b)
+{
+ // Note: a is the new representative
+ Assert(a.getType().isArray() && b.getType().isArray());
+
+ if (d_mergeInProgress) {
+ // Nested call to mergeArrays, just push on the queue and return
+ d_mergeQueue.push(a.eqNode(b));
+ return;
+ }
+
+ d_mergeInProgress = true;
+
+ Node n;
+ while (true) {
+ if (d_useNonLinearOpt) {
+ bool aNL = d_infoMap.isNonLinear(a);
+ bool bNL = d_infoMap.isNonLinear(b);
+ if (aNL) {
+ if (bNL) {
+ // already both marked as non-linear - no need to do anything
+ }
+ else {
+ // Set b to be non-linear
+ setNonLinear(b);
+ }
}
+ else {
+ if (bNL) {
+ // Set a to be non-linear
+ setNonLinear(a);
+ }
+ else {
+ // Check for new non-linear arrays
+ const CTNodeList* astores = d_infoMap.getStores(a);
+ const CTNodeList* bstores = d_infoMap.getStores(a);
+ Assert(astores->size() <= 1 && bstores->size() <= 1);
+ if (astores->size() > 0 && bstores->size() > 0) {
+ setNonLinear(a);
+ setNonLinear(b);
+ }
+ }
+ }
+ }
+
+ d_mayEqualEqualityEngine.addEquality(a, b, d_true);
+ checkRowLemmas(a,b);
+ checkRowLemmas(b,a);
+
+ // merge info adds the list of the 2nd argument to the first
+ d_infoMap.mergeInfo(a, b);
+
+ // If no more to do, break
+ if (d_conflict || d_mergeQueue.empty()) {
+ break;
}
- }
- Trace("arrays-crl")<<"Arrays::checkLemmas done.\n";
+ // Otherwise, prepare for next iteration
+ n = d_mergeQueue.front();
+ a = n[0];
+ b = n[1];
+ d_mergeQueue.pop();
+ }
+ d_mergeInProgress = false;
}
-/**
- * Adds a new Row lemma of the form i = j OR a[j] = b[j] if i and j are not the
- * same and a and b are also not identical.
- *
- */
-
-inline void TheoryArrays::addRowLemma(TNode a, TNode b, TNode i, TNode j) {
- Assert(a.getType().isArray());
- Assert(b.getType().isArray());
- // construct lemma
- NodeManager* nm = NodeManager::currentNM();
- Node aj = nm->mkNode(kind::SELECT, a, j);
- Node bj = nm->mkNode(kind::SELECT, b, j);
- Node eq1 = nm->mkNode(kind::EQUAL, aj, bj);
- Node eq2 = nm->mkNode(kind::EQUAL, i, j);
- Node lem = nm->mkNode(kind::OR, eq2, eq1);
+void TheoryArrays::checkStore(TNode a) {
+ Trace("arrays-cri")<<"Arrays::checkStore "<<a<<"\n";
+ if(Trace.isOn("arrays-cri")) {
+ d_infoMap.getInfo(a)->print();
+ }
+ Assert(a.getType().isArray());
+ Assert(a.getKind()==kind::STORE);
+ TNode b = a[0];
+ TNode i = a[1];
+ TNode brep = d_equalityEngine.getRepresentative(b);
- Trace("arrays-lem")<<"Arrays::addRowLemma adding "<<lem<<"\n";
- d_RowAlreadyAdded.insert(make_quad(a,b,i,j));
- d_out->lemma(lem);
- ++d_numRow;
+ if (!d_useNonLinearOpt || d_infoMap.isNonLinear(brep)) {
+ List<TNode>* js = d_infoMap.getIndices(brep);
+ List<TNode>::const_iterator it = js->begin();
+ RowLemmaType lem;
+ for(; it!= js->end(); ++it) {
+ TNode j = *it;
+ if (i == j) continue;
+ lem = make_quad(a,b,i,j);
+ Trace("arrays-lem") << spaces(getContext()->getLevel()) <<"Arrays::checkStore ("<<a<<", "<<b<<", "<<i<<", "<<j<<")\n";
+ queueRowLemma(lem);
+ }
+ }
}
-/**
- * Because a is now being read at position i check if new lemmas can be
- * instantiated for all store terms equal to a and all store terms of the form
- * store(a _ _ )
- */
-void TheoryArrays::checkRowForIndex(TNode i, TNode a) {
+void TheoryArrays::checkRowForIndex(TNode i, TNode a)
+{
Trace("arrays-cri")<<"Arrays::checkRowForIndex "<<a<<"\n";
Trace("arrays-cri")<<" index "<<i<<"\n";
@@ -1057,107 +954,207 @@ void TheoryArrays::checkRowForIndex(TNode i, TNode a) {
d_infoMap.getInfo(a)->print();
}
Assert(a.getType().isArray());
+ Assert(d_equalityEngine.getRepresentative(a) == a);
const CTNodeList* stores = d_infoMap.getStores(a);
const CTNodeList* instores = d_infoMap.getInStores(a);
CTNodeList::const_iterator it = stores->begin();
+ RowLemmaType lem;
- for(; it!= stores->end(); it++) {
+ for(; it!= stores->end(); ++it) {
TNode store = *it;
Assert(store.getKind()==kind::STORE);
TNode j = store[1];
- //Trace("arrays-lem")<<"Arrays::checkRowForIndex ("<<store<<", "<<store[0]<<", "<<j<<", "<<i<<")\n";
- if(!isRedundantRowLemma(store, store[0], j, i)) {
- //Trace("arrays-lem")<<"Arrays::checkRowForIndex ("<<store<<", "<<store[0]<<", "<<j<<", "<<i<<")\n";
- queueRowLemma(store, store[0], j, i);
- }
+ if (i == j) continue;
+ lem = make_quad(store, store[0], j, i);
+ Trace("arrays-lem") << spaces(getContext()->getLevel()) <<"Arrays::checkRowForIndex ("<<store<<", "<<store[0]<<", "<<j<<", "<<i<<")\n";
+ queueRowLemma(lem);
}
- it = instores->begin();
- for(; it!= instores->end(); it++) {
- TNode instore = *it;
- Assert(instore.getKind()==kind::STORE);
- TNode j = instore[1];
- //Trace("arrays-lem")<<"Arrays::checkRowForIndex ("<<instore<<", "<<instore[0]<<", "<<j<<", "<<i<<")\n";
- if(!isRedundantRowLemma(instore, instore[0], j, i)) {
- //Trace("arrays-lem")<<"Arrays::checkRowForIndex ("<<instore<<", "<<instore[0]<<", "<<j<<", "<<i<<")\n";
- queueRowLemma(instore, instore[0], j, i);
+ if (!d_useNonLinearOpt || d_infoMap.isNonLinear(a)) {
+ it = instores->begin();
+ for(; it!= instores->end(); ++it) {
+ TNode instore = *it;
+ Assert(instore.getKind()==kind::STORE);
+ TNode j = instore[1];
+ if (i == j) continue;
+ lem = make_quad(instore, instore[0], j, i);
+ Trace("arrays-lem") << spaces(getContext()->getLevel()) <<"Arrays::checkRowForIndex ("<<instore<<", "<<instore[0]<<", "<<j<<", "<<i<<")\n";
+ queueRowLemma(lem);
}
}
-
}
-void TheoryArrays::checkStore(TNode a) {
- Trace("arrays-cri")<<"Arrays::checkStore "<<a<<"\n";
-
- if(Trace.isOn("arrays-cri")) {
+// a just became equal to b
+// look for new ROW lemmas
+void TheoryArrays::checkRowLemmas(TNode a, TNode b)
+{
+ Trace("arrays-crl")<<"Arrays::checkLemmas begin \n"<<a<<"\n";
+ if(Trace.isOn("arrays-crl"))
d_infoMap.getInfo(a)->print();
- }
- Assert(a.getType().isArray());
- Assert(a.getKind()==kind::STORE);
- TNode b = a[0];
- TNode i = a[1];
+ Trace("arrays-crl")<<" ------------ and "<<b<<"\n";
+ if(Trace.isOn("arrays-crl"))
+ d_infoMap.getInfo(b)->print();
- List<TNode>* js = d_infoMap.getIndices(b);
- List<TNode>::const_iterator it = js->begin();
+ List<TNode>* i_a = d_infoMap.getIndices(a);
+ const CTNodeList* st_b = d_infoMap.getStores(b);
+ const CTNodeList* inst_b = d_infoMap.getInStores(b);
+
+ List<TNode>::const_iterator it = i_a->begin();
+ CTNodeList::const_iterator its;
- for(; it!= js->end(); it++) {
- TNode j = *it;
+ RowLemmaType lem;
- if(!isRedundantRowLemma(a, b, i, j)) {
- //Trace("arrays-lem")<<"Arrays::checkRowStore ("<<a<<", "<<b<<", "<<i<<", "<<j<<")\n";
- queueRowLemma(a,b,i,j);
+ for( ; it != i_a->end(); ++it ) {
+ TNode i = *it;
+ its = st_b->begin();
+ for ( ; its != st_b->end(); ++its) {
+ TNode store = *its;
+ Assert(store.getKind() == kind::STORE);
+ TNode j = store[1];
+ TNode c = store[0];
+ lem = make_quad(store, c, j, i);
+ Trace("arrays-lem") << spaces(getContext()->getLevel()) <<"Arrays::checkRowLemmas ("<<store<<", "<<c<<", "<<j<<", "<<i<<")\n";
+ queueRowLemma(lem);
}
}
+ if (!d_useNonLinearOpt || d_infoMap.isNonLinear(b)) {
+ for(it = i_a->begin() ; it != i_a->end(); ++it ) {
+ TNode i = *it;
+ its = inst_b->begin();
+ for ( ; its !=inst_b->end(); ++its) {
+ TNode store = *its;
+ Assert(store.getKind() == kind::STORE);
+ TNode j = store[1];
+ TNode c = store[0];
+ lem = make_quad(store, c, j, i);
+ Trace("arrays-lem") << spaces(getContext()->getLevel()) <<"Arrays::checkRowLemmas ("<<store<<", "<<c<<", "<<j<<", "<<i<<")\n";
+ queueRowLemma(lem);
+ }
+ }
+ }
+ Trace("arrays-crl")<<"Arrays::checkLemmas done.\n";
}
-inline void TheoryArrays::queueExtLemma(TNode a, TNode b) {
+
+void TheoryArrays::queueRowLemma(RowLemmaType lem)
+{
+ if (d_conflict || d_RowAlreadyAdded.count(lem) != 0) {
+ return;
+ }
+ TNode a = lem.first;
+ TNode b = lem.second;
+ TNode i = lem.third;
+ TNode j = lem.fourth;
Assert(a.getType().isArray() && b.getType().isArray());
+ if (d_equalityEngine.areEqual(a,b) ||
+ d_equalityEngine.areEqual(i,j)) {
+ return;
+ }
- d_extQueue.insert(make_pair(a,b));
-}
+ NodeManager* nm = NodeManager::currentNM();
-void TheoryArrays::queueRowLemma(TNode a, TNode b, TNode i, TNode j) {
- Assert(a.getType().isArray() && b.getType().isArray());
-//if(!isRedundantInContext(a,b,i,j)) {
- d_RowQueue.insert(make_quad(a, b, i, j));
-}
+ Node aj = nm->mkNode(kind::SELECT, a, j);
+ Node bj = nm->mkNode(kind::SELECT, b, j);
+ if (!d_equalityEngine.hasTerm(aj)) {
+ preRegisterTerm(aj);
+ }
+ if (!d_equalityEngine.hasTerm(bj)) {
+ preRegisterTerm(bj);
+ }
+
+ if (d_equalityEngine.areEqual(aj,bj)) {
+ return;
+ }
+
+ if (d_useArrTable) {
+ Node tableEntry = nm->mkNode(kind::ARR_TABLE_FUN, a, b, i, j);
+ if (d_equalityEngine.getSize(tableEntry) != 1) {
+ return;
+ }
+ }
-/**
-* Adds a new Ext lemma of the form
-* a = b OR a[k]!=b[k], for a new variable k
-*/
+ //Propagation
+ if (d_propagateLemmas) {
+ if (d_equalityEngine.areDisequal(i,j)) {
+ Trace("arrays-lem") << spaces(getContext()->getLevel()) <<"Arrays::queueRowLemma: propagating aj = bj ("<<aj<<", "<<bj<<")\n";
+ Node reason = nm->mkNode(kind::OR, aj.eqNode(bj), i.eqNode(j));
+ d_permRef.push_back(reason);
+ d_equalityEngine.addEquality(aj, bj, reason);
+ ++d_numProp;
+ return;
+ }
+ if (d_equalityEngine.areDisequal(aj,bj)) {
+ Trace("arrays-lem") << spaces(getContext()->getLevel()) <<"Arrays::queueRowLemma: propagating i = j ("<<i<<", "<<j<<")\n";
+ Node reason = nm->mkNode(kind::OR, i.eqNode(j), aj.eqNode(bj));
+ d_permRef.push_back(reason);
+ d_equalityEngine.addEquality(i, j, reason);
+ ++d_numProp;
+ return;
+ }
+ }
-inline void TheoryArrays::addExtLemma(TNode a, TNode b) {
- Assert(a.getType().isArray());
- Assert(b.getType().isArray());
+ // TODO: maybe add triggers here
- Trace("arrays-cle")<<"Arrays::checkExtLemmas "<<a<<" \n";
- Trace("arrays-cle")<<" and "<<b<<" \n";
+ if (d_eagerLemmas) {
+ Node eq1 = nm->mkNode(kind::EQUAL, aj, bj);
+ Node eq2 = nm->mkNode(kind::EQUAL, i, j);
+ Node lemma = nm->mkNode(kind::OR, eq2, eq1);
+ Trace("arrays-lem")<<"Arrays::addRowLemma adding "<<lemma<<"\n";
+ d_RowAlreadyAdded.insert(lem);
+ d_out->lemma(lemma);
+ ++d_numRow;
+ }
+ else {
+ d_RowQueue.push(lem);
+ }
+}
- if( d_extAlreadyAdded.count(make_pair(a, b)) == 0
- && d_extAlreadyAdded.count(make_pair(b, a)) == 0) {
+void TheoryArrays::dischargeLemmas()
+{
+ size_t sz = d_RowQueue.size();
+ for (unsigned count = 0; count < sz; ++count) {
+ RowLemmaType l = d_RowQueue.front();
+ d_RowQueue.pop();
+ if (d_RowAlreadyAdded.count(l) != 0) {
+ continue;
+ }
- NodeManager* nm = NodeManager::currentNM();
- TypeNode ixType = a.getType()[0];
- Node k = nm->mkVar(ixType);
- Dump.declareVar(k.toExpr(), "an extensional lemma index variable from the theory of arrays");
- Node eq = nm->mkNode(kind::EQUAL, a, b);
- Node ak = nm->mkNode(kind::SELECT, a, k);
- Node bk = nm->mkNode(kind::SELECT, b, k);
- Node neq = nm->mkNode(kind::NOT, nm->mkNode(kind::EQUAL, ak, bk));
- Node lem = nm->mkNode(kind::OR, eq, neq);
+ TNode a = l.first;
+ TNode b = l.second;
+ TNode i = l.third;
+ TNode j = l.fourth;
+ Assert(a.getType().isArray() && b.getType().isArray());
+
+ NodeManager* nm = NodeManager::currentNM();
+ Node aj = nm->mkNode(kind::SELECT, a, j);
+ Node bj = nm->mkNode(kind::SELECT, b, j);
+
+ // Check for redundant lemma
+ // TODO: more checks possible (i.e. check d_RowAlreadyAdded in context)
+ if (d_equalityEngine.areEqual(i,j) ||
+ d_equalityEngine.areEqual(a,b) ||
+ d_equalityEngine.areEqual(aj,bj)) {
+ d_RowQueue.push(l);
+ continue;
+ }
- Trace("arrays-lem")<<"Arrays::addExtLemma "<<lem<<"\n";
- d_extAlreadyAdded.insert(make_pair(a,b));
- d_out->lemma(lem);
- ++d_numExt;
- return;
- }
+ // construct lemma
+ Node eq1 = nm->mkNode(kind::EQUAL, aj, bj);
+ Node eq2 = nm->mkNode(kind::EQUAL, i, j);
+ Node lem = nm->mkNode(kind::OR, eq2, eq1);
- Trace("arrays-cle")<<"Arrays::checkExtLemmas lemma already generated. \n";
+ Trace("arrays-lem")<<"Arrays::addRowLemma adding "<<lem<<"\n";
+ d_RowAlreadyAdded.insert(l);
+ d_out->lemma(lem);
+ ++d_numRow;
+ }
}
+
+}/* CVC4::theory::arrays namespace */
+}/* CVC4::theory namespace */
+}/* CVC4 namespace */
generated by cgit on debian on lair
contact matthew@masot.net with questions or feedback