Refactor operator elimination in arithmetic (#4519)
authorAndrew Reynolds <andrew.j.reynolds@gmail.com>
Sat, 23 May 2020 03:39:16 +0000 (22:39 -0500)
committerGitHub <noreply@github.com>
Sat, 23 May 2020 03:39:16 +0000 (22:39 -0500)
This is a major refactor of how operators are eliminated in arithmetic. Currently there were (at least) two things wrong:
(1) ppRewriteTerm sent lemmas on the output channel. This behavior is incompatible with how preprocessing works. In particular, this caused unconstrained simplification to be unaware of terms from such lemmas, leading to incorrect "sat" answers.
(2) Lemmas used to eliminate certain "div-like" terms were processed in a context-independent way. However, lemmas should be cached in a user-context-dependent way. This was leading to incorrect "sat" answers in incremental.

The solution to these issues is to eliminate operators via the construction of witness terms. No lemmas are sent out, and instead these lemmas are the consequence of term formula removal in the standard way.

As a result of the refactor, 2 quantifiers regressions time out due to infinite branch and bound issues (one only during --check-unsat-cores). These appear to be random and I've changed the options to avoid these issues. 3 others now have check-model warnings, which I've added --quiet to. Improving check-model will be addressed on a future PR.

This PR is not required for SMT COMP since we have workarounds that avoid both the incorrect behaviors in our scripts.

Also notice that --rewrite-divk is effectively now enabled by default always.

Fixes #4484, fixes #4486, fixes #4481.

28 files changed:
src/options/arith_options.toml
src/smt/set_defaults.cpp
src/theory/arith/theory_arith_private.cpp
src/theory/arith/theory_arith_private.h
test/regress/CMakeLists.txt
test/regress/regress0/arith/bug547.2.smt2
test/regress/regress0/arith/div-chainable.smt2
test/regress/regress0/arith/mod-simp.smt2
test/regress/regress0/bug548a.smt2
test/regress/regress0/quantifiers/mix-match.smt2
test/regress/regress0/unconstrained/4481.smt2 [new file with mode: 0644]
test/regress/regress0/unconstrained/4484.smt2 [new file with mode: 0644]
test/regress/regress0/unconstrained/4486.smt2 [new file with mode: 0644]
test/regress/regress1/arith/bug547.1.smt2
test/regress/regress1/bv/bv-int-collapse2-sat.smt2
test/regress/regress1/bv/cmu-rdk-3.smt2
test/regress/regress1/bv/issue3776.smt2
test/regress/regress1/fmf/fmf-fun-no-elim-ext-arith.smt2
test/regress/regress1/fmf/fmf-fun-no-elim-ext-arith2.smt2
test/regress/regress1/fmf/issue3626.smt2
test/regress/regress1/nl/issue3441.smt2
test/regress/regress1/nl/issue3955-ee-double-notify.smt2
test/regress/regress1/push-pop/model-unsound-ania.smt2 [new file with mode: 0644]
test/regress/regress1/strings/chapman150408.smt2
test/regress/regress1/strings/cmu-substr-rw.smt2
test/regress/regress1/strings/crash-1019.smt2
test/regress/regress2/bug674.smt2
test/regress/regress2/strings/cmi-split-cm-fail.smt2

index 4865cceadbd5c148ba403db87ef86b76e23dba25..d16513cfbc5786934072d6d75d1df17c35af70e9 100644 (file)
@@ -275,14 +275,6 @@ header = "options/arith_options.h"
   default    = "false"
   help       = "use quick explain to minimize the sum of infeasibility conflicts"
 
-[[option]]
-  name       = "rewriteDivk"
-  category   = "regular"
-  long       = "rewrite-divk"
-  type       = "bool"
-  default    = "false"
-  help       = "rewrite division and mod when by a constant into linear terms"
-
 [[option]]
   name       = "trySolveIntStandardEffort"
   category   = "regular"
index 899da4d4a5e88d87edbe258246aed760cd7b4af7..e06363883f52c4789d6fb5f43dec97e23ca104a4 100644 (file)
@@ -1097,11 +1097,6 @@ void setDefaults(SmtEngine& smte, LogicInfo& logic)
     {
       options::quantSplit.set(false);
     }
-    // rewrite divk
-    if (!options::rewriteDivk.wasSetByUser())
-    {
-      options::rewriteDivk.set(true);
-    }
     // do not do macros
     if (!options::macrosQuant.wasSetByUser())
     {
@@ -1136,11 +1131,6 @@ void setDefaults(SmtEngine& smte, LogicInfo& logic)
   }
   if (options::cegqi())
   {
-    // must rewrite divk
-    if (!options::rewriteDivk.wasSetByUser())
-    {
-      options::rewriteDivk.set(true);
-    }
     if (options::incrementalSolving())
     {
       // cannot do nested quantifier elimination in incremental mode
index 7fdab203401cfac16bdad996ef61f7642e2131db..09b6d742a36faa817d45b1296c29913142077c9b 100644 (file)
@@ -34,6 +34,7 @@
 #include "expr/node.h"
 #include "expr/node_algorithm.h"
 #include "expr/node_builder.h"
+#include "expr/proof_skolem_cache.h"
 #include "options/arith_options.h"
 #include "options/smt_options.h"  // for incrementalSolving()
 #include "preprocessing/util/ite_utilities.h"
@@ -226,100 +227,6 @@ static void resolve(ConstraintCPVec& buf, ConstraintP c, const ConstraintCPVec&
   // return safeConstructNary(nb);
 }
 
-// Integer division axioms:
-// These concenrate the high level constructs needed to constrain the functions:
-// div, mod, div_total and mod_total.
-//
-// The high level constraint.
-// (for all ((m Int) (n Int))
-//   (=> (distinct n 0)
-//       (let ((q (div m n)) (r (mod m n)))
-//         (and (= m (+ (* n q) r))
-//              (<= 0 r (- (abs n) 1))))))
-//
-// We now add division by 0 functions.
-// (for all ((m Int) (n Int))
-//   (let ((q (div m n)) (r (mod m n)))
-//     (ite (= n 0)
-//          (and (= q (div_0_func m)) (= r (mod_0_func m)))
-//          (and (= m (+ (* n q) r))
-//               (<= 0 r (- (abs n) 1)))))))
-//
-// We now express div and mod in terms of div_total and mod_total.
-// (for all ((m Int) (n Int))
-//   (let ((q (div m n)) (r (mod m n)))
-//     (ite (= n 0)
-//          (and (= q (div_0_func m)) (= r (mod_0_func m)))
-//          (and (= q (div_total m n)) (= r (mod_total m n))))))
-//
-// Alternative div_total and mod_total without the abs function:
-// (for all ((m Int) (n Int))
-//   (let ((q (div_total m n)) (r (mod_total m n)))
-//     (ite (= n 0)
-//          (and (= q 0) (= r 0))
-//          (and (r = m - n * q)
-//               (ite (> n 0)
-//                    (n*q <= m < n*q + n)
-//                    (n*q <= m < n*q - n))))))
-
-
-// Returns a formula that entails that q equals the total integer division of m
-// by n.
-// (for all ((m Int) (n Int))
-//   (let ((q (div_total m n)))
-//     (ite (= n 0)
-//          (= q 0)
-//          (ite (> n 0)
-//               (n*q <= m < n*q + n)
-//               (n*q <= m < n*q - n)))))
-Node mkAxiomForTotalIntDivision(Node m, Node n, Node q) {
-  NodeManager* nm = NodeManager::currentNM();
-  Node zero = mkRationalNode(0);
-  // (n*q <= m < n*q + n) is equivalent to (0 <= m - n*q < n).
-  Node diff = nm->mkNode(kind::MINUS, m, mkMult(n, q));
-  Node when_n_is_positive = mkInRange(diff, zero, n);
-  Node when_n_is_negative = mkInRange(diff, zero, nm->mkNode(kind::UMINUS, n));
-  return n.eqNode(zero).iteNode(
-      q.eqNode(zero), nm->mkNode(kind::GT, n, zero)
-                          .iteNode(when_n_is_positive, when_n_is_negative));
-}
-
-// Returns a formula that entails that r equals the integer division total of m
-// by n assuming q is equal to (div_total m n).
-// (for all ((m Int) (n Int))
-//   (let ((q (div_total m n)) (r (mod_total m n)))
-//     (ite (= n 0)
-//          (= r 0)
-//          (= r (- m (n * q))))))
-Node mkAxiomForTotalIntMod(Node m, Node n, Node q, Node r) {
-  NodeManager* nm = NodeManager::currentNM();
-  Node diff = nm->mkNode(kind::MINUS, m, mkMult(n, q));
-  return mkOnZeroIte(n, r, mkRationalNode(0), diff);
-}
-
-// Returns an expression that constrains a term to be the result of the
-// [total] integer division of num by den. Equivalently:
-// (and (=> (den > 0) (den*term <= num < den*term + den))
-//      (=> (den < 0) (den*term <= num < den*term - den))
-//      (=> (den = 0) (term = 0)))
-// static Node mkIntegerDivisionConstraint(Node term, Node num, Node den) {
-//   // We actually encode:
-//   // (and (=> (den > 0) (0 <= num - den*term < den))
-//   //      (=> (den < 0) (0 <= num - den*term < -den))
-//   //      (=> (den = 0) (term = 0)))
-//   NodeManager* nm = NodeManager::currentNM();
-//   Node zero = nm->mkConst(Rational(0));
-//   Node den_is_positive = nm->mkNode(kind::GT, den, zero);
-//   Node den_is_negative = nm->mkNode(kind::LT, den, zero);
-//   Node diff = nm->mkNode(kind::MINUS, num, mkMult(den, term));
-//   Node when_den_positive = den_positive.impNode(mkInRange(diff, zero, den));
-//   Node when_den_negative = den_negative.impNode(
-//       mkInRange(diff, zero, nm->mkNode(kind::UMINUS, den)));
-//   Node when_den_is_zero = (den.eqNode(zero)).impNode(term.eqNode(zero));
-//   return mk->mkNode(kind::AND, when_den_positive, when_den_negative,
-//                     when_den_is_zero);
-// }
-
 void TheoryArithPrivate::setMasterEqualityEngine(eq::EqualityEngine* eq) {
   d_congruenceManager.setMasterEqualityEngine(eq);
 }
@@ -1176,126 +1083,443 @@ Node TheoryArithPrivate::ppRewriteTerms(TNode n) {
   if(Theory::theoryOf(n) != THEORY_ARITH) {
     return n;
   }
+  // Eliminate operators recursively. Notice we must do this here since other
+  // theories may generate lemmas that involve non-standard operators. For
+  // example, quantifier instantiation may use TO_INTEGER terms; SyGuS may
+  // introduce non-standard arithmetic terms appearing in grammars.
+  // call eliminate operators
+  Node nn = eliminateOperators(n);
+  if (nn != n)
+  {
+    // since elimination may introduce new operators to eliminate, we must
+    // recursively eliminate result
+    return eliminateOperatorsRec(nn);
+  }
+  return n;
+}
+
+void TheoryArithPrivate::checkNonLinearLogic(Node term)
+{
+  if (getLogicInfo().isLinear())
+  {
+    Trace("arith-logic") << "ERROR: Non-linear term in linear logic: " << term
+                         << std::endl;
+    std::stringstream serr;
+    serr << "A non-linear fact was asserted to arithmetic in a linear logic."
+         << std::endl;
+    serr << "The fact in question: " << term << endl;
+    throw LogicException(serr.str());
+  }
+}
+
+struct ArithElimOpAttributeId
+{
+};
+typedef expr::Attribute<ArithElimOpAttributeId, Node> ArithElimOpAttribute;
+
+Node TheoryArithPrivate::eliminateOperatorsRec(Node n)
+{
+  ArithElimOpAttribute aeoa;
+  Trace("arith-elim") << "Begin elim: " << n << std::endl;
+  NodeManager* nm = NodeManager::currentNM();
+  std::unordered_map<Node, Node, TNodeHashFunction> visited;
+  std::unordered_map<Node, Node, TNodeHashFunction>::iterator it;
+  std::vector<Node> visit;
+  Node cur;
+  visit.push_back(n);
+  do
+  {
+    cur = visit.back();
+    visit.pop_back();
+    it = visited.find(cur);
+    if (Theory::theoryOf(cur) != THEORY_ARITH)
+    {
+      visited[cur] = cur;
+    }
+    else if (it == visited.end())
+    {
+      if (cur.hasAttribute(aeoa))
+      {
+        visited[cur] = cur.getAttribute(aeoa);
+      }
+      else
+      {
+        visited[cur] = Node::null();
+        visit.push_back(cur);
+        for (const Node& cn : cur)
+        {
+          visit.push_back(cn);
+        }
+      }
+    }
+    else if (it->second.isNull())
+    {
+      Node ret = cur;
+      bool childChanged = false;
+      std::vector<Node> children;
+      if (cur.getMetaKind() == metakind::PARAMETERIZED)
+      {
+        children.push_back(cur.getOperator());
+      }
+      for (const Node& cn : cur)
+      {
+        it = visited.find(cn);
+        Assert(it != visited.end());
+        Assert(!it->second.isNull());
+        childChanged = childChanged || cn != it->second;
+        children.push_back(it->second);
+      }
+      if (childChanged)
+      {
+        ret = nm->mkNode(cur.getKind(), children);
+      }
+      Node retElim = eliminateOperators(ret);
+      if (retElim != ret)
+      {
+        // recursively eliminate operators in result, since some eliminations
+        // are defined in terms of other non-standard operators.
+        ret = eliminateOperatorsRec(retElim);
+      }
+      cur.setAttribute(aeoa, ret);
+      visited[cur] = ret;
+    }
+  } while (!visit.empty());
+  Assert(visited.find(n) != visited.end());
+  Assert(!visited.find(n)->second.isNull());
+  return visited[n];
+}
 
+Node TheoryArithPrivate::eliminateOperators(Node node)
+{
   NodeManager* nm = NodeManager::currentNM();
 
-  switch(Kind k = n.getKind()) {
-
-  case kind::TO_INTEGER:
-  case kind::IS_INTEGER: {
-    Node toIntSkolem;
-    NodeMap::const_iterator it = d_to_int_skolem.find( n[0] );
-    if( it==d_to_int_skolem.end() ){
-      toIntSkolem = nm->mkSkolem("toInt", nm->integerType(),
-                            "a conversion of a Real term to its Integer part");
-      d_to_int_skolem[n[0]] = toIntSkolem;
-      // n[0] - 1 < toIntSkolem <= n[0]
-      // -1 < toIntSkolem - n[0] <= 0
-      // 0 <= n[0] - toIntSkolem < 1
-      Node one = mkRationalNode(1);
-      Node lem = mkAxiomForTotalIntDivision(n[0], one, toIntSkolem);
-      outputLemma(lem);
-    }else{
-      toIntSkolem = (*it).second;
+  Kind k = node.getKind();
+  switch (k)
+  {
+    case kind::TANGENT:
+    case kind::COSECANT:
+    case kind::SECANT:
+    case kind::COTANGENT:
+    {
+      // these are eliminated by rewriting
+      return Rewriter::rewrite(node);
+      break;
     }
-    if(k == kind::IS_INTEGER) {
-      return nm->mkNode(kind::EQUAL, n[0], toIntSkolem);
+    case kind::TO_INTEGER:
+    case kind::IS_INTEGER:
+    {
+      Node toIntSkolem;
+      NodeMap::const_iterator it = d_to_int_skolem.find(node[0]);
+      if (it == d_to_int_skolem.end())
+      {
+        // node[0] - 1 < toIntSkolem <= node[0]
+        // -1 < toIntSkolem - node[0] <= 0
+        // 0 <= node[0] - toIntSkolem < 1
+        Node v = nm->mkBoundVar(nm->integerType());
+        Node one = mkRationalNode(1);
+        Node zero = mkRationalNode(0);
+        Node diff = nm->mkNode(kind::MINUS, node[0], v);
+        Node lem = mkInRange(diff, zero, one);
+        toIntSkolem = ProofSkolemCache::mkSkolem(
+            v, lem, "toInt", "a conversion of a Real term to its Integer part");
+        toIntSkolem = ProofSkolemCache::getWitnessForm(toIntSkolem);
+        d_to_int_skolem[node[0]] = toIntSkolem;
+      }
+      else
+      {
+        toIntSkolem = (*it).second;
+      }
+      if (k == kind::IS_INTEGER)
+      {
+        return nm->mkNode(kind::EQUAL, node[0], toIntSkolem);
+      }
+      Assert(k == kind::TO_INTEGER);
+      return toIntSkolem;
     }
-    Assert(k == kind::TO_INTEGER);
-    return toIntSkolem;
-  }
-  case kind::INTS_DIVISION:
-  case kind::INTS_MODULUS:
-  case kind::DIVISION:
-    // these should be removed during expand definitions
-    Assert(false);
-    break;
-  
-  case kind::INTS_DIVISION_TOTAL: 
-  case kind::INTS_MODULUS_TOTAL: {
-    Node den = Rewriter::rewrite(n[1]);
-    if(!options::rewriteDivk() && den.isConst()) {
-      return n;
-    }
-    Node num = Rewriter::rewrite(n[0]);
-    Node intVar;
-    Node rw = nm->mkNode(k, num, den);
-    NodeMap::const_iterator it = d_int_div_skolem.find( rw );
-    if( it==d_int_div_skolem.end() ){
-      intVar = nm->mkSkolem("linearIntDiv", nm->integerType(), "the result of an intdiv-by-k term");
-      d_int_div_skolem[rw] = intVar;
-      Node lem;
-      if (den.isConst()) {
-        const Rational& rat = den.getConst<Rational>();
-        Assert(!num.isConst());
-        if(rat != 0) {
-          if(rat > 0) {
-            lem = nm->mkNode(kind::AND, nm->mkNode(kind::LEQ, nm->mkNode(kind::MULT, den, intVar), num), 
-                                        nm->mkNode(kind::LT, num, nm->mkNode(kind::MULT, den, nm->mkNode(kind::PLUS, intVar, nm->mkConst(Rational(1))))));
-          } else {
-            lem = nm->mkNode(kind::AND, nm->mkNode(kind::LEQ, nm->mkNode(kind::MULT, den, intVar), num), 
-                                        nm->mkNode(kind::LT, num, nm->mkNode(kind::MULT, den, nm->mkNode(kind::PLUS, intVar, nm->mkConst(Rational(-1))))));
+
+    case kind::INTS_DIVISION_TOTAL:
+    case kind::INTS_MODULUS_TOTAL:
+    {
+      Node den = Rewriter::rewrite(node[1]);
+      Node num = Rewriter::rewrite(node[0]);
+      Node intVar;
+      Node rw = nm->mkNode(k, num, den);
+      NodeMap::const_iterator it = d_int_div_skolem.find(rw);
+      if (it == d_int_div_skolem.end())
+      {
+        Node v = nm->mkBoundVar(nm->integerType());
+        Node lem;
+        Node leqNum = nm->mkNode(LEQ, nm->mkNode(MULT, den, v), num);
+        if (den.isConst())
+        {
+          const Rational& rat = den.getConst<Rational>();
+          if (num.isConst() || rat == 0)
+          {
+            // just rewrite
+            return Rewriter::rewrite(node);
+          }
+          if (rat > 0)
+          {
+            lem = nm->mkNode(
+                AND,
+                leqNum,
+                nm->mkNode(
+                    LT,
+                    num,
+                    nm->mkNode(MULT,
+                               den,
+                               nm->mkNode(PLUS, v, nm->mkConst(Rational(1))))));
+          }
+          else
+          {
+            lem = nm->mkNode(
+                AND,
+                leqNum,
+                nm->mkNode(
+                    LT,
+                    num,
+                    nm->mkNode(
+                        MULT,
+                        den,
+                        nm->mkNode(PLUS, v, nm->mkConst(Rational(-1))))));
           }
         }
-      }else{
-        lem = nm->mkNode(kind::AND,
-                nm->mkNode(kind::IMPLIES, NodeManager::currentNM()->mkNode( kind::GT, den, nm->mkConst(Rational(0)) ),
-                  nm->mkNode(kind::AND, nm->mkNode(kind::LEQ, nm->mkNode(kind::MULT, den, intVar), num), 
-                                        nm->mkNode(kind::LT, num, nm->mkNode(kind::MULT, den, nm->mkNode(kind::PLUS, intVar, nm->mkConst(Rational(1))))))),
-                nm->mkNode(kind::IMPLIES, NodeManager::currentNM()->mkNode( kind::LT, den, nm->mkConst(Rational(0)) ),
-                  nm->mkNode(kind::AND, nm->mkNode(kind::LEQ, nm->mkNode(kind::MULT, den, intVar), num), 
-                                        nm->mkNode(kind::LT, num, nm->mkNode(kind::MULT, den, nm->mkNode(kind::PLUS, intVar, nm->mkConst(Rational(-1))))))));                
-      }    
-      if( !lem.isNull() ){
-        outputLemma(lem);
+        else
+        {
+          checkNonLinearLogic(node);
+          lem = nm->mkNode(
+              AND,
+              nm->mkNode(
+                  IMPLIES,
+                  nm->mkNode(GT, den, nm->mkConst(Rational(0))),
+                  nm->mkNode(
+                      AND,
+                      leqNum,
+                      nm->mkNode(
+                          LT,
+                          num,
+                          nm->mkNode(
+                              MULT,
+                              den,
+                              nm->mkNode(PLUS, v, nm->mkConst(Rational(1))))))),
+              nm->mkNode(
+                  IMPLIES,
+                  nm->mkNode(LT, den, nm->mkConst(Rational(0))),
+                  nm->mkNode(
+                      AND,
+                      leqNum,
+                      nm->mkNode(
+                          LT,
+                          num,
+                          nm->mkNode(
+                              MULT,
+                              den,
+                              nm->mkNode(
+                                  PLUS, v, nm->mkConst(Rational(-1))))))));
+        }
+        intVar = ProofSkolemCache::mkSkolem(
+            v, lem, "linearIntDiv", "the result of an intdiv-by-k term");
+        intVar = ProofSkolemCache::getWitnessForm(intVar);
+        d_int_div_skolem[rw] = intVar;
       }
-    }else{
-      intVar = (*it).second;
+      else
+      {
+        intVar = (*it).second;
+      }
+      if (k == kind::INTS_MODULUS_TOTAL)
+      {
+        Node nn =
+            nm->mkNode(kind::MINUS, num, nm->mkNode(kind::MULT, den, intVar));
+        return nn;
+      }
+      else
+      {
+        return intVar;
+      }
+      break;
     }
-    if( k==kind::INTS_MODULUS_TOTAL ){
-      Node node = nm->mkNode(kind::MINUS, num, nm->mkNode(kind::MULT, den, intVar));
-      return node;
-    }else{
-      return intVar;
+    case kind::DIVISION_TOTAL:
+    {
+      Node num = Rewriter::rewrite(node[0]);
+      Node den = Rewriter::rewrite(node[1]);
+      if (den.isConst())
+      {
+        // No need to eliminate here, can eliminate via rewriting later.
+        // Moreover, rewriting may change the type of this node from real to
+        // int, which impacts certain issues with subtyping.
+        return node;
+      }
+      checkNonLinearLogic(node);
+      Node var;
+      Node rw = nm->mkNode(k, num, den);
+      NodeMap::const_iterator it = d_div_skolem.find(rw);
+      if (it == d_div_skolem.end())
+      {
+        Node v = nm->mkBoundVar(nm->realType());
+        Node lem = nm->mkNode(IMPLIES,
+                              den.eqNode(nm->mkConst(Rational(0))).negate(),
+                              nm->mkNode(MULT, den, v).eqNode(num));
+        var = ProofSkolemCache::mkSkolem(
+            v, lem, "nonlinearDiv", "the result of a non-linear div term");
+        var = ProofSkolemCache::getWitnessForm(var);
+        d_div_skolem[rw] = var;
+      }
+      else
+      {
+        var = (*it).second;
+      }
+      return var;
+      break;
     }
-    break;
-  }
-  case kind::DIVISION_TOTAL: {
-    Node num = Rewriter::rewrite(n[0]);
-    Node den = Rewriter::rewrite(n[1]);
-    Assert(!den.isConst());
-    Node var;
-    Node rw = nm->mkNode(k, num, den);
-    NodeMap::const_iterator it = d_div_skolem.find( rw );
-    if( it==d_div_skolem.end() ){
-      var = nm->mkSkolem("nonlinearDiv", nm->realType(), "the result of a non-linear div term");
-      d_div_skolem[rw] = var;
-      outputLemma(nm->mkNode(kind::IMPLIES, den.eqNode(nm->mkConst(Rational(0))).negate(), nm->mkNode(kind::MULT, den, var).eqNode(num)));
-    }else{
-      var = (*it).second;
+    case kind::DIVISION:
+    {
+      Node num = Rewriter::rewrite(node[0]);
+      Node den = Rewriter::rewrite(node[1]);
+      Node ret = nm->mkNode(kind::DIVISION_TOTAL, num, den);
+      if (!den.isConst() || den.getConst<Rational>().sgn() == 0)
+      {
+        checkNonLinearLogic(node);
+        Node divByZeroNum = getArithSkolemApp(num, ArithSkolemId::DIV_BY_ZERO);
+        Node denEq0 = nm->mkNode(kind::EQUAL, den, nm->mkConst(Rational(0)));
+        ret = nm->mkNode(kind::ITE, denEq0, divByZeroNum, ret);
+      }
+      return ret;
+      break;
     }
-    return var;
-    break;
-  }
-  default:
-    break;
-  }
 
-  for(TNode::const_iterator i = n.begin(); i != n.end(); ++i) {
-    Node rewritten = ppRewriteTerms(*i);
-    if(rewritten != *i) {
-      NodeBuilder<> b(n.getKind());
-      b.append(n.begin(), i);
-      b << rewritten;
-      for(++i; i != n.end(); ++i) {
-        b << ppRewriteTerms(*i);
+    case kind::INTS_DIVISION:
+    {
+      // partial function: integer div
+      Node num = Rewriter::rewrite(node[0]);
+      Node den = Rewriter::rewrite(node[1]);
+      Node ret = nm->mkNode(kind::INTS_DIVISION_TOTAL, num, den);
+      if (!den.isConst() || den.getConst<Rational>().sgn() == 0)
+      {
+        checkNonLinearLogic(node);
+        Node intDivByZeroNum =
+            getArithSkolemApp(num, ArithSkolemId::INT_DIV_BY_ZERO);
+        Node denEq0 = nm->mkNode(kind::EQUAL, den, nm->mkConst(Rational(0)));
+        ret = nm->mkNode(kind::ITE, denEq0, intDivByZeroNum, ret);
       }
-      rewritten = b;
-      return rewritten;
+      return ret;
+      break;
     }
-  }
 
-  return n;
+    case kind::INTS_MODULUS:
+    {
+      // partial function: mod
+      Node num = Rewriter::rewrite(node[0]);
+      Node den = Rewriter::rewrite(node[1]);
+      Node ret = nm->mkNode(kind::INTS_MODULUS_TOTAL, num, den);
+      if (!den.isConst() || den.getConst<Rational>().sgn() == 0)
+      {
+        checkNonLinearLogic(node);
+        Node modZeroNum = getArithSkolemApp(num, ArithSkolemId::MOD_BY_ZERO);
+        Node denEq0 = nm->mkNode(kind::EQUAL, den, nm->mkConst(Rational(0)));
+        ret = nm->mkNode(kind::ITE, denEq0, modZeroNum, ret);
+      }
+      return ret;
+      break;
+    }
+
+    case kind::ABS:
+    {
+      return nm->mkNode(kind::ITE,
+                        nm->mkNode(kind::LT, node[0], nm->mkConst(Rational(0))),
+                        nm->mkNode(kind::UMINUS, node[0]),
+                        node[0]);
+      break;
+    }
+    case kind::SQRT:
+    case kind::ARCSINE:
+    case kind::ARCCOSINE:
+    case kind::ARCTANGENT:
+    case kind::ARCCOSECANT:
+    case kind::ARCSECANT:
+    case kind::ARCCOTANGENT:
+    {
+      checkNonLinearLogic(node);
+      // eliminate inverse functions here
+      NodeMap::const_iterator it = d_nlin_inverse_skolem.find(node);
+      if (it == d_nlin_inverse_skolem.end())
+      {
+        Node var = nm->mkBoundVar(nm->realType());
+        Node lem;
+        if (k == kind::SQRT)
+        {
+          Node skolemApp = getArithSkolemApp(node[0], ArithSkolemId::SQRT);
+          Node uf = skolemApp.eqNode(var);
+          Node nonNeg = nm->mkNode(
+              kind::AND, nm->mkNode(kind::MULT, var, var).eqNode(node[0]), uf);
+
+          // sqrt(x) reduces to:
+          // witness y. ite(x >= 0.0, y * y = x ^ y = Uf(x), y = Uf(x))
+          //
+          // Uf(x) makes sure that the reduction still behaves like a function,
+          // otherwise the reduction of (x = 1) ^ (sqrt(x) != sqrt(1)) would be
+          // satisfiable. On the original formula, this would require that we
+          // simultaneously interpret sqrt(1) as 1 and -1, which is not a valid
+          // model.
+          lem = nm->mkNode(
+              kind::ITE,
+              nm->mkNode(kind::GEQ, node[0], nm->mkConst(Rational(0))),
+              nonNeg,
+              uf);
+        }
+        else
+        {
+          Node pi = mkPi();
+
+          // range of the skolem
+          Node rlem;
+          if (k == kind::ARCSINE || k == ARCTANGENT || k == ARCCOSECANT)
+          {
+            Node half = nm->mkConst(Rational(1) / Rational(2));
+            Node pi2 = nm->mkNode(kind::MULT, half, pi);
+            Node npi2 = nm->mkNode(kind::MULT, nm->mkConst(Rational(-1)), pi2);
+            // -pi/2 < var <= pi/2
+            rlem = nm->mkNode(
+                AND, nm->mkNode(LT, npi2, var), nm->mkNode(LEQ, var, pi2));
+          }
+          else
+          {
+            // 0 <= var < pi
+            rlem = nm->mkNode(AND,
+                              nm->mkNode(LEQ, nm->mkConst(Rational(0)), var),
+                              nm->mkNode(LT, var, pi));
+          }
+
+          Kind rk = k == kind::ARCSINE
+                        ? kind::SINE
+                        : (k == kind::ARCCOSINE
+                               ? kind::COSINE
+                               : (k == kind::ARCTANGENT
+                                      ? kind::TANGENT
+                                      : (k == kind::ARCCOSECANT
+                                             ? kind::COSECANT
+                                             : (k == kind::ARCSECANT
+                                                    ? kind::SECANT
+                                                    : kind::COTANGENT))));
+          Node invTerm = nm->mkNode(rk, var);
+          lem = nm->mkNode(AND, rlem, invTerm.eqNode(node[0]));
+        }
+        Assert(!lem.isNull());
+        Node ret = ProofSkolemCache::mkSkolem(
+            var,
+            lem,
+            "tfk",
+            "Skolem to eliminate a non-standard transcendental function");
+        ret = ProofSkolemCache::getWitnessForm(ret);
+        d_nlin_inverse_skolem[node] = ret;
+        return ret;
+      }
+      return (*it).second;
+      break;
+    }
+
+    default: break;
+  }
+  return node;
 }
 
 Node TheoryArithPrivate::ppRewrite(TNode atom) {
@@ -1312,6 +1536,7 @@ Node TheoryArithPrivate::ppRewrite(TNode atom) {
       Node rewritten = Rewriter::rewrite(leq.andNode(geq));
       Debug("arith::preprocess")
           << "arith::preprocess() : returning " << rewritten << endl;
+      // don't need to rewrite terms since rewritten is not a non-standard op
       return rewritten;
     }
   }
@@ -1441,11 +1666,6 @@ void TheoryArithPrivate::setupVariable(const Variable& x){
   //setupInitialValue(varN);
 
   markSetup(n);
-
-  if(x.isDivLike()){
-    setupDivLike(x);
-  }
-
 }
 
 void TheoryArithPrivate::setupVariableList(const VarList& vl){
@@ -1508,52 +1728,6 @@ void TheoryArithPrivate::cautiousSetupPolynomial(const Polynomial& p){
   }
 }
 
-void TheoryArithPrivate::setupDivLike(const Variable& v){
-  Assert(v.isDivLike());
-
-  if(getLogicInfo().isLinear()){
-    throw LogicException(
-        "A non-linear fact (involving div/mod/divisibility) was asserted to "
-        "arithmetic in a linear logic;\nif you only use division (or modulus) "
-        "by a constant value, or if you only use the divisibility-by-k "
-        "predicate, try using the --rewrite-divk option.");
-  }
-
-  Node vnode = v.getNode();
-  Assert(
-      isSetup(vnode));  // Otherwise there is some invariant breaking recursion
-  Polynomial m = Polynomial::parsePolynomial(vnode[0]);
-  Polynomial n = Polynomial::parsePolynomial(vnode[1]);
-
-  cautiousSetupPolynomial(m);
-  cautiousSetupPolynomial(n);
-
-  Node lem;
-  switch(vnode.getKind()){
-  case DIVISION:
-  case INTS_DIVISION:
-  case INTS_MODULUS:
-    // these should be removed during expand definitions
-    Assert(false);
-    break;
-  case DIVISION_TOTAL:
-    lem = axiomIteForTotalDivision(vnode);
-    break;
-  case INTS_DIVISION_TOTAL:
-  case INTS_MODULUS_TOTAL:
-    lem = axiomIteForTotalIntDivision(vnode);
-    break;
-  default:
-    /* intentionally blank */
-    break;
-  }
-
-  if(!lem.isNull()){
-    Debug("arith::div") << lem << endl;
-    outputLemma(lem);
-  }
-}
-
 Node TheoryArithPrivate::axiomIteForTotalDivision(Node div_tot){
   Assert(div_tot.getKind() == DIVISION_TOTAL);
 
@@ -5056,157 +5230,15 @@ const BoundsInfo& TheoryArithPrivate::boundsInfo(ArithVar basic) const{
 
 Node TheoryArithPrivate::expandDefinition(Node node)
 {
-  NodeManager* nm = NodeManager::currentNM();
-
-  // eliminate here since the rewritten form of these may introduce division
-  Kind k = node.getKind();
-  if (k == kind::TANGENT || k == kind::COSECANT || k == kind::SECANT
-      || k == kind::COTANGENT)
+  // call eliminate operators
+  Node nn = eliminateOperators(node);
+  if (nn != node)
   {
-    node = Rewriter::rewrite(node);
-    k = node.getKind();
+    // since elimination may introduce new operators to eliminate, we must
+    // recursively eliminate result
+    return eliminateOperatorsRec(nn);
   }
-
-  switch (k)
-  {
-    case kind::DIVISION:
-    {
-      TNode num = node[0], den = node[1];
-      Node ret = nm->mkNode(kind::DIVISION_TOTAL, num, den);
-      if (!den.isConst() || den.getConst<Rational>().sgn() == 0)
-      {
-        Node divByZeroNum = getArithSkolemApp(num, ArithSkolemId::DIV_BY_ZERO);
-        Node denEq0 = nm->mkNode(kind::EQUAL, den, nm->mkConst(Rational(0)));
-        ret = nm->mkNode(kind::ITE, denEq0, divByZeroNum, ret);
-      }
-      return ret;
-      break;
-    }
-
-    case kind::INTS_DIVISION:
-    {
-      // partial function: integer div
-      TNode num = node[0], den = node[1];
-      Node ret = nm->mkNode(kind::INTS_DIVISION_TOTAL, num, den);
-      if (!den.isConst() || den.getConst<Rational>().sgn() == 0)
-      {
-        Node intDivByZeroNum =
-            getArithSkolemApp(num, ArithSkolemId::INT_DIV_BY_ZERO);
-        Node denEq0 = nm->mkNode(kind::EQUAL, den, nm->mkConst(Rational(0)));
-        ret = nm->mkNode(kind::ITE, denEq0, intDivByZeroNum, ret);
-      }
-      return ret;
-      break;
-    }
-
-    case kind::INTS_MODULUS:
-    {
-      // partial function: mod
-      TNode num = node[0], den = node[1];
-      Node ret = nm->mkNode(kind::INTS_MODULUS_TOTAL, num, den);
-      if (!den.isConst() || den.getConst<Rational>().sgn() == 0)
-      {
-        Node modZeroNum = getArithSkolemApp(num, ArithSkolemId::MOD_BY_ZERO);
-        Node denEq0 = nm->mkNode(kind::EQUAL, den, nm->mkConst(Rational(0)));
-        ret = nm->mkNode(kind::ITE, denEq0, modZeroNum, ret);
-      }
-      return ret;
-      break;
-    }
-
-    case kind::ABS:
-    {
-      return nm->mkNode(kind::ITE,
-                        nm->mkNode(kind::LT, node[0], nm->mkConst(Rational(0))),
-                        nm->mkNode(kind::UMINUS, node[0]),
-                        node[0]);
-      break;
-    }
-    case kind::SQRT:
-    case kind::ARCSINE:
-    case kind::ARCCOSINE:
-    case kind::ARCTANGENT:
-    case kind::ARCCOSECANT:
-    case kind::ARCSECANT:
-    case kind::ARCCOTANGENT:
-    {
-      // eliminate inverse functions here
-      NodeMap::const_iterator it = d_nlin_inverse_skolem.find(node);
-      if (it == d_nlin_inverse_skolem.end())
-      {
-        Node var = nm->mkBoundVar(nm->realType());
-        Node lem;
-        if (k == kind::SQRT)
-        {
-          Node skolemApp = getArithSkolemApp(node[0], ArithSkolemId::SQRT);
-          Node uf = skolemApp.eqNode(var);
-          Node nonNeg = nm->mkNode(
-              kind::AND, nm->mkNode(kind::MULT, var, var).eqNode(node[0]), uf);
-
-          // sqrt(x) reduces to:
-          // witness y. ite(x >= 0.0, y * y = x ^ Uf(x), Uf(x))
-          //
-          // Uf(x) makes sure that the reduction still behaves like a function,
-          // otherwise the reduction of (x = 1) ^ (sqrt(x) != sqrt(1)) would be
-          // satisfiable. On the original formula, this would require that we
-          // simultaneously interpret sqrt(1) as 1 and -1, which is not a valid
-          // model.
-          lem = nm->mkNode(
-              kind::ITE,
-              nm->mkNode(kind::GEQ, node[0], nm->mkConst(Rational(0))),
-              nonNeg,
-              uf);
-        }
-        else
-        {
-          Node pi = mkPi();
-
-          // range of the skolem
-          Node rlem;
-          if (k == kind::ARCSINE || k == ARCTANGENT || k == ARCCOSECANT)
-          {
-            Node half = nm->mkConst(Rational(1) / Rational(2));
-            Node pi2 = nm->mkNode(kind::MULT, half, pi);
-            Node npi2 = nm->mkNode(kind::MULT, nm->mkConst(Rational(-1)), pi2);
-            // -pi/2 < var <= pi/2
-            rlem = nm->mkNode(
-                AND, nm->mkNode(LT, npi2, var), nm->mkNode(LEQ, var, pi2));
-          }
-          else
-          {
-            // 0 <= var < pi
-            rlem = nm->mkNode(AND,
-                              nm->mkNode(LEQ, nm->mkConst(Rational(0)), var),
-                              nm->mkNode(LT, var, pi));
-          }
-
-          Kind rk = k == kind::ARCSINE
-                        ? kind::SINE
-                        : (k == kind::ARCCOSINE
-                               ? kind::COSINE
-                               : (k == kind::ARCTANGENT
-                                      ? kind::TANGENT
-                                      : (k == kind::ARCCOSECANT
-                                             ? kind::COSECANT
-                                             : (k == kind::ARCSECANT
-                                                    ? kind::SECANT
-                                                    : kind::COTANGENT))));
-          Node invTerm = nm->mkNode(rk, var);
-          lem = nm->mkNode(AND, rlem, invTerm.eqNode(node[0]));
-        }
-        Assert(!lem.isNull());
-        Node ret = nm->mkNode(WITNESS, nm->mkNode(BOUND_VAR_LIST, var), lem);
-        d_nlin_inverse_skolem[node] = ret;
-        return ret;
-      }
-      return (*it).second;
-      break;
-    }
-
-    default: return node; break;
-  }
-
-  Unreachable();
+  return node;
 }
 
 Node TheoryArithPrivate::getArithSkolem(ArithSkolemId asi)
index b5debe76d5d215753df04e0aad9a303473a67385..8198dbcf17087c3dfc6ee4afc8182c42dc982ada 100644 (file)
@@ -189,9 +189,6 @@ public:
     d_setupNodes.insert(n);
   }
 private:
-
-  void setupDivLike(const Variable& x);
-
   void setupVariable(const Variable& x);
   void setupVariableList(const VarList& vl);
   void setupPolynomial(const Polynomial& poly);
@@ -422,7 +419,39 @@ private:
   // handle linear /, div, mod, and also is_int, to_int
   Node ppRewriteTerms(TNode atom);
 
-public:
+  /**
+   * Called when a non-linear term n is given to this class. Throw an exception
+   * if the logic is linear.
+   */
+  void checkNonLinearLogic(Node term);
+  /**
+   * Eliminate operators in term n. If n has top symbol that is not a core
+   * one (including division, int division, mod, to_int, is_int, syntactic sugar
+   * transcendental functions), then we replace it by a form that eliminates
+   * that operator. This may involve the introduction of witness terms.
+   *
+   * One exception to the above rule is that we may leave certain applications
+   * like (/ 4 1) unchanged, since replacing this by 4 changes its type from
+   * real to int. This is important for some subtyping issues during
+   * expandDefinition. Moreover, applications like this can be eliminated
+   * trivially later by rewriting.
+   *
+   * This method is called both during expandDefinition and during ppRewrite.
+   *
+   * @param n The node to eliminate operators from.
+   * @return The (single step) eliminated form of n.
+   */
+  Node eliminateOperators(Node n);
+  /**
+   * Recursively ensure that n has no non-standard operators. This applies
+   * the above method on all subterms of n.
+   *
+   * @param n The node to eliminate operators from.
+   * @return The eliminated form of n.
+   */
+  Node eliminateOperatorsRec(Node n);
+
+ public:
   TheoryArithPrivate(TheoryArith& containing, context::Context* c, context::UserContext* u, OutputChannel& out, Valuation valuation, const LogicInfo& logicInfo);
   ~TheoryArithPrivate();
 
index 2df07db5a896928af6f9d3addba3d74383b2cfa4..5a59f2f54d9f3f532d16aeb831b8003a90df46dd 100644 (file)
@@ -1121,6 +1121,9 @@ set(regress_0_tests
   regress0/uflra/simple.02.cvc
   regress0/uflra/simple.03.cvc
   regress0/uflra/simple.04.cvc
+  regress0/unconstrained/4481.smt2
+  regress0/unconstrained/4484.smt2
+  regress0/unconstrained/4486.smt2
   regress0/unconstrained/arith.smt2
   regress0/unconstrained/arith3.smt2
   regress0/unconstrained/arith4.smt2
@@ -1472,6 +1475,7 @@ set(regress_1_tests
   regress1/push-pop/fuzz_7.smt2
   regress1/push-pop/fuzz_8.smt2
   regress1/push-pop/fuzz_9.smt2
+  regress1/push-pop/model-unsound-ania.smt2
   regress1/push-pop/quant-fun-proc-unmacro.smt2
   regress1/push-pop/quant-fun-proc.smt2
   regress1/quantifiers/006-cbqi-ite.smt2
index b7d114eb3336409131cb706265273a62a1e25012..7ff2a538a64492ee7467527412bf481ed0bf844e 100644 (file)
@@ -1,6 +1,5 @@
-; COMMAND-LINE: --rewrite-divk
-; EXPECT: sat
 (set-logic QF_NIA)
+(set-info :status sat)
 (declare-fun x () Int)
 (declare-fun y () Int)
 (declare-fun z () Int)
index 9ddc80e988fbb599801e9b1f6e80916a98b3f64c..76bc4095f8b461cd691350a2da846d1da6fad26b 100644 (file)
@@ -1,5 +1,3 @@
-; COMMAND-LINE: --rewrite-divk
-; EXPECT: sat
 (set-logic QF_LIA)
 (set-info :status sat)
 
index 7294cd8638d92bdc9527fc5a60be951918bf18e0..1a9c50590d49a694b1e2131c34e81a5d0b1eec5c 100644 (file)
@@ -1,5 +1,3 @@
-; COMMAND-LINE: --rewrite-divk
-; EXPECT: unsat
 (set-logic QF_LIA)
 (set-info :status unsat)
 
index 75d82d98f45785c4e060ec5460ecac1ece7f7109..581c34f2dfc44de5ec64069bc7c313c87926d37b 100644 (file)
@@ -1,4 +1,4 @@
-; COMMAND-LINE: --rewrite-divk --tlimit 1000
+; COMMAND-LINE: --tlimit 1000
 ; EXPECT: unknown
 (set-logic AUFLIA)
 (declare-fun f (Int) Int)
index c6ac3b56f87ee7a9d7ea5133335b775d3602d7ed..fbf996a0a3892f68fff7818c50c530778340552a 100644 (file)
@@ -1,3 +1,5 @@
+; COMMAND-LINE: --no-check-unsat-cores
+; EXPECT: unsat
 (set-logic ALL_SUPPORTED)
 (set-info :status unsat)
 (declare-fun P (Real) Bool)
@@ -7,4 +9,4 @@
 (assert (is_int a))
 (assert (not (P a)))
 
-(check-sat)
\ No newline at end of file
+(check-sat)
diff --git a/test/regress/regress0/unconstrained/4481.smt2 b/test/regress/regress0/unconstrained/4481.smt2
new file mode 100644 (file)
index 0000000..0286070
--- /dev/null
@@ -0,0 +1,8 @@
+; COMMAND-LINE: --unconstrained-simp
+; EXPECT: unsat
+(set-logic ALL)
+(set-info :status unsat)
+(declare-fun a () Int)
+(assert (= (div a 2) 2))
+(assert (= (div a 3) 0))
+(check-sat)
diff --git a/test/regress/regress0/unconstrained/4484.smt2 b/test/regress/regress0/unconstrained/4484.smt2
new file mode 100644 (file)
index 0000000..f2f1129
--- /dev/null
@@ -0,0 +1,8 @@
+; COMMAND-LINE: --unconstrained-simp
+; EXPECT: unsat
+(set-logic QF_NIRA)
+(set-info :status unsat)
+(declare-fun a () Real)
+(assert (= (to_int a) 2))
+(assert (= (to_int (/ a 2.0)) 2))
+(check-sat)
diff --git a/test/regress/regress0/unconstrained/4486.smt2 b/test/regress/regress0/unconstrained/4486.smt2
new file mode 100644 (file)
index 0000000..01771ce
--- /dev/null
@@ -0,0 +1,8 @@
+; COMMAND-LINE: --unconstrained-simp
+; EXPECT: sat
+(set-logic ALL)
+(set-info :status sat)
+(declare-fun x () Real)
+(assert (is_int x))
+(assert (is_int (+ x 1)))
+(check-sat)
index 38d1dfcb132fcc79b5197d984576feaf07a5c37c..75894eb60f9e1efaf5493dadd56bdb5342954007 100644 (file)
@@ -1,4 +1,4 @@
-; COMMAND-LINE: --rewrite-divk --nl-ext-tplanes
+; COMMAND-LINE: --nl-ext-tplanes --quiet
 ; EXPECT: sat
 (set-logic QF_NIA)
 (declare-fun x () Int)
index 3d932a0767fe9790212696656443cfe9d45a52c1..07f2dae6a15ae450866b9633cbcc69e864009ab9 100644 (file)
@@ -1,5 +1,3 @@
-; COMMAND-LINE: --rewrite-divk
-; EXPECT: sat
 (set-logic ALL_SUPPORTED)
 (set-info :status sat)
 (declare-fun t () Int)
index 9e773388963b6f3f6c02f56c4bc92b10b2a4c5ef..742dd593badfb3f9d2b081880443f18d334e97a5 100644 (file)
@@ -1,5 +1,3 @@
-; COMMAND-LINE: --rewrite-divk
-; EXPECT: sat
 (set-logic ALL_SUPPORTED)
 (set-info :status sat)
 
index 30c034c7044670c4dc1b614f2652a4b99b990a0c..3e86a497409145a6a874e9049d190b294a2500e1 100644 (file)
@@ -1,6 +1,5 @@
-; COMMAND-LINE: --rewrite-divk
-; EXPECT: sat
 (set-logic QF_BVLIA)
+(set-info :status sat)
 (declare-fun t () Int)
 (assert (= t (bv2nat ((_ int2bv 1) t))))
 (check-sat)
index 0618e28cb94c0a3eb8bea4527576d311c1aee031..dd9cb68863a4dd40d97edace39cc0d001207dfdf 100644 (file)
@@ -1,4 +1,4 @@
-; COMMAND-LINE: --fmf-fun --no-check-models --rewrite-divk
+; COMMAND-LINE: --fmf-fun --no-check-models
 ; EXPECT: sat
 (set-logic UFLIA)
 (set-info :status sat)
index 07f1e66740c4966080aff0684247b2063f5c721f..ea5a5e4b74807bc4ad0568272e3b5e01a29a393c 100644 (file)
@@ -1,4 +1,4 @@
-; COMMAND-LINE: --fmf-fun --no-check-models --rewrite-divk
+; COMMAND-LINE: --fmf-fun --no-check-models
 ; EXPECT: sat
 (set-logic UFLIA)
 (set-info :status sat)
index 9f27dee0103c8229f8553034ad4010b2aa64bb33..25dc80223156535656ed6a6dbe67989f0e0588be 100644 (file)
@@ -1,4 +1,4 @@
-; COMMAND-LINE: --fmf-bound
+; COMMAND-LINE: --fmf-bound --no-cegqi
 ; EXPECT: sat
 (set-logic ALL)
 (assert (forall ((a Int)) (or (distinct (/ 0 0) a) (= (/ 0 a) 0))))
index 19fe98bc5141e048ac70f8fdd35407cfed98b43b..6be4e7d7ebd90969cc65d0a5973a0f35e364efaa 100644 (file)
@@ -1,3 +1,5 @@
+; COMMAND-LINE: --quiet
+; EXPECT: sat
 (set-logic QF_NIA)   
 (set-info :status sat)                                                           
 (declare-fun a () Int)                                                          
index 8aa8793df786a93746af9bae14f5594b4236d971..21f26df2dc23439c9a3deff23e7fbefc5b543124 100644 (file)
@@ -1,3 +1,5 @@
+; COMMAND-LINE: --quiet\r
+; EXPECT: sat\r
 (set-logic QF_UFNRA)\r
 (set-option :snorm-infer-eq true)\r
 (set-info :status sat)\r
diff --git a/test/regress/regress1/push-pop/model-unsound-ania.smt2 b/test/regress/regress1/push-pop/model-unsound-ania.smt2
new file mode 100644 (file)
index 0000000..6f7f275
--- /dev/null
@@ -0,0 +1,11 @@
+; COMMAND-LINE: --incremental
+; EXPECT: sat
+; EXPECT: unsat
+(set-logic QF_ANIA)                       
+(declare-fun _substvar_16_ () Int)        
+(push 1)                                  
+(assert (not (= (mod _substvar_16_ 2) 0)))
+(check-sat)                               
+(pop 1)                                   
+(assert (= (- (mod _substvar_16_ 2) 2) 0))
+(check-sat)     
index f037185565f806e5afa3999a19a1d9676d998f7d..e6047498ef5fced063bb4db869367b6f52ff6544 100644 (file)
@@ -1,7 +1,6 @@
 (set-logic SLIA)
 (set-info :status sat)
 (set-option :strings-exp true)
-(set-option :rewrite-divk true)
 (declare-fun string () String)
 (assert (and
      (and (not (not (not (= (ite (> (str.indexof string ";" 0) 0) 1 0)
index 20bf979ddfd4d8890e6bbb2f92a13b11d2ea429f..a4d649d7fd9c9f341d686797dad7a7f0dcd308e5 100644 (file)
@@ -1,8 +1,6 @@
 (set-logic ALL_SUPPORTED)
 (set-info :status sat)
 (set-option :strings-exp true)
-;(set-option :produce-models true)
-;(set-option :rewrite-divk true)
 
 (declare-fun uri () String)
 
index 9f2e99b02ae8f988c2616395d55abdf32d9e0002..317840ddb0fc0cb27607aa45bf1e1efd18254ba1 100644 (file)
@@ -1,6 +1,5 @@
 (set-logic ALL_SUPPORTED)
 (set-option :strings-exp true)
-(set-option :rewrite-divk true)
 (set-info :status unsat)
  
 (declare-fun s () String)
index 31eaa5aba26c7c556de21de2ed92a50e603cc036..cce0c963a7b966e0d273e10a5ab04dd4d8da3e48 100644 (file)
@@ -1,4 +1,4 @@
-; COMMAND-LINE: --quant-ind --incremental --rewrite-divk
+; COMMAND-LINE: --quant-ind --incremental
 (set-logic ALL_SUPPORTED)
 (declare-datatypes ((Lst 0)) (((cons (head Int) (tail Lst)) (nil))))
 (define-fun-rec app ((l1 Lst) (l2 Lst)) Lst (ite ((_ is nil) l1) l2 (cons (head l1) (app (tail l1) l2))))
index e2ae94a278b902404ed10acb1cc61ceeb65551be..4782fa7f88d8766b2a0ae0424e3a74066cb13b2c 100644 (file)
@@ -1,4 +1,4 @@
-; COMMAND-LINE: --strings-exp --rewrite-divk
+; COMMAND-LINE: --strings-exp
 ; EXPECT: sat
 (set-info :smt-lib-version 2.6)
 (set-logic QF_SLIA)