Always split on constructor types for datatypes involving uninterpreted sorts when...
authorajreynol <andrew.j.reynolds@gmail.com>
Tue, 22 Dec 2015 12:44:05 +0000 (13:44 +0100)
committerajreynol <andrew.j.reynolds@gmail.com>
Tue, 22 Dec 2015 12:44:05 +0000 (13:44 +0100)
src/expr/datatype.cpp
src/expr/datatype.h
src/theory/datatypes/theory_datatypes.cpp
src/theory/quantifiers/first_order_model.cpp
src/theory/quantifiers/term_database.cpp
test/regress/regress0/fmf/Makefile.am
test/regress/regress0/fmf/forall_unit_data.smt2
test/regress/regress0/fmf/forall_unit_data2.smt2 [new file with mode: 0755]
test/regress/regress0/fmf/sc_bad_model_1221.smt2 [new file with mode: 0755]

index 09fe2cdc3606bd4bb140910347512bf8ac966b89..4ebc5071f9cefef090b37df8a765cccab2faf506 100644 (file)
@@ -39,6 +39,8 @@ namespace expr {
     struct DatatypeConsIndexTag {};
     struct DatatypeFiniteTag {};
     struct DatatypeFiniteComputedTag {};
+    struct DatatypeUFiniteTag {};
+    struct DatatypeUFiniteComputedTag {};
   }/* CVC4::expr::attr namespace */
 }/* CVC4::expr namespace */
 
@@ -46,6 +48,8 @@ typedef expr::Attribute<expr::attr::DatatypeIndexTag, uint64_t> DatatypeIndexAtt
 typedef expr::Attribute<expr::attr::DatatypeConsIndexTag, uint64_t> DatatypeConsIndexAttr;
 typedef expr::Attribute<expr::attr::DatatypeFiniteTag, bool> DatatypeFiniteAttr;
 typedef expr::Attribute<expr::attr::DatatypeFiniteComputedTag, bool> DatatypeFiniteComputedAttr;
+typedef expr::Attribute<expr::attr::DatatypeUFiniteTag, bool> DatatypeUFiniteAttr;
+typedef expr::Attribute<expr::attr::DatatypeUFiniteComputedTag, bool> DatatypeUFiniteComputedAttr;
 
 const Datatype& Datatype::datatypeOf(Expr item) {
   ExprManagerScope ems(item);
@@ -120,10 +124,13 @@ void Datatype::resolve(ExprManager* em,
   d_self = self;
 
   d_involvesExt =  false;
+  d_involvesUt =  false;
   for(const_iterator i = begin(); i != end(); ++i) {
     if( (*i).involvesExternalType() ){
       d_involvesExt =  true;
-      break;
+    }
+    if( (*i).involvesUninterpretedType() ){
+      d_involvesUt =  true;
     }
   }
 }
@@ -245,18 +252,13 @@ bool Datatype::computeCardinalityRecSingleton( std::vector< Type >& processing,
 
 bool Datatype::isFinite() const throw(IllegalArgumentException) {
   CheckArgument(isResolved(), this, "this datatype is not yet resolved");
-
   // we're using some internals, so we have to set up this library context
   ExprManagerScope ems(d_self);
-
   TypeNode self = TypeNode::fromType(d_self);
-
   // is this already in the cache ?
   if(self.getAttribute(DatatypeFiniteComputedAttr())) {
     return self.getAttribute(DatatypeFiniteAttr());
   }
-
-  Cardinality c = 0;
   for(const_iterator i = begin(), i_end = end(); i != i_end; ++i) {
     if(! (*i).isFinite()) {
       self.setAttribute(DatatypeFiniteComputedAttr(), true);
@@ -269,6 +271,27 @@ bool Datatype::isFinite() const throw(IllegalArgumentException) {
   return true;
 }
 
+bool Datatype::isUFinite() const throw(IllegalArgumentException) {
+  CheckArgument(isResolved(), this, "this datatype is not yet resolved");
+  // we're using some internals, so we have to set up this library context
+  ExprManagerScope ems(d_self);
+  TypeNode self = TypeNode::fromType(d_self);
+  // is this already in the cache ?
+  if(self.getAttribute(DatatypeUFiniteComputedAttr())) {
+    return self.getAttribute(DatatypeUFiniteAttr());
+  }
+  for(const_iterator i = begin(), i_end = end(); i != i_end; ++i) {
+    if(! (*i).isUFinite()) {
+      self.setAttribute(DatatypeUFiniteComputedAttr(), true);
+      self.setAttribute(DatatypeUFiniteAttr(), false);
+      return false;
+    }
+  }
+  self.setAttribute(DatatypeUFiniteComputedAttr(), true);
+  self.setAttribute(DatatypeUFiniteAttr(), true);
+  return true;
+}
+
 bool Datatype::isWellFounded() const throw(IllegalArgumentException) {
   CheckArgument(isResolved(), this, "this datatype is not yet resolved");
   if( d_well_founded==0 ){
@@ -505,6 +528,10 @@ bool Datatype::involvesExternalType() const{
   return d_involvesExt;
 }
 
+bool Datatype::involvesUninterpretedType() const{
+  return d_involvesUt;
+}
+
 void DatatypeConstructor::resolve(ExprManager* em, DatatypeType self,
                                   const std::map<std::string, DatatypeType>& resolutions,
                                   const std::vector<Type>& placeholders,
@@ -722,7 +749,7 @@ unsigned DatatypeConstructor::getNumSygusLetArgs() const {
 Expr DatatypeConstructor::getSygusLetArg( unsigned i ) const {
   CheckArgument(isResolved(), this, "this datatype constructor is not yet resolved");
   return d_sygus_let_args[i];
-} 
+}
 
 unsigned DatatypeConstructor::getNumSygusLetInputArgs() const {
   CheckArgument(isResolved(), this, "this datatype constructor is not yet resolved");
@@ -733,7 +760,7 @@ bool DatatypeConstructor::isSygusIdFunc() const {
   CheckArgument(isResolved(), this, "this datatype constructor is not yet resolved");
   return d_sygus_let_args.size()==1 && d_sygus_let_args[0]==d_sygus_let_body;
 }
-  
+
 Cardinality DatatypeConstructor::getCardinality() const throw(IllegalArgumentException) {
   CheckArgument(isResolved(), this, "this datatype constructor is not yet resolved");
 
@@ -777,17 +804,13 @@ bool DatatypeConstructor::computeWellFounded( std::vector< Type >& processing )
 
 bool DatatypeConstructor::isFinite() const throw(IllegalArgumentException) {
   CheckArgument(isResolved(), this, "this datatype constructor is not yet resolved");
-
   // we're using some internals, so we have to set up this library context
   ExprManagerScope ems(d_constructor);
-
   TNode self = Node::fromExpr(d_constructor);
-
   // is this already in the cache ?
   if(self.getAttribute(DatatypeFiniteComputedAttr())) {
     return self.getAttribute(DatatypeFiniteAttr());
   }
-
   for(const_iterator i = begin(), i_end = end(); i != i_end; ++i) {
     if(! SelectorType((*i).getSelector().getType()).getRangeType().getCardinality().isFinite()) {
       self.setAttribute(DatatypeFiniteComputedAttr(), true);
@@ -795,11 +818,31 @@ bool DatatypeConstructor::isFinite() const throw(IllegalArgumentException) {
       return false;
     }
   }
-
   self.setAttribute(DatatypeFiniteComputedAttr(), true);
   self.setAttribute(DatatypeFiniteAttr(), true);
   return true;
 }
+bool DatatypeConstructor::isUFinite() const throw(IllegalArgumentException) {
+  CheckArgument(isResolved(), this, "this datatype constructor is not yet resolved");
+  // we're using some internals, so we have to set up this library context
+  ExprManagerScope ems(d_constructor);
+  TNode self = Node::fromExpr(d_constructor);
+  // is this already in the cache ?
+  if(self.getAttribute(DatatypeUFiniteComputedAttr())) {
+    return self.getAttribute(DatatypeUFiniteAttr());
+  }
+  for(const_iterator i = begin(), i_end = end(); i != i_end; ++i) {
+    Type t = SelectorType((*i).getSelector().getType()).getRangeType();
+    if(!t.isSort() && !t.getCardinality().isFinite()) {
+      self.setAttribute(DatatypeUFiniteComputedAttr(), true);
+      self.setAttribute(DatatypeUFiniteAttr(), false);
+      return false;
+    }
+  }
+  self.setAttribute(DatatypeUFiniteComputedAttr(), true);
+  self.setAttribute(DatatypeUFiniteAttr(), true);
+  return true;
+}
 
 Expr DatatypeConstructor::computeGroundTerm( Type t, std::vector< Type >& processing, std::map< Type, Expr >& gt ) const throw(IllegalArgumentException) {
 // we're using some internals, so we have to set up this library context
@@ -882,6 +925,15 @@ bool DatatypeConstructor::involvesExternalType() const{
   return false;
 }
 
+bool DatatypeConstructor::involvesUninterpretedType() const{
+  for(const_iterator i = begin(); i != end(); ++i) {
+    if(SelectorType((*i).getSelector().getType()).getRangeType().isSort()) {
+      return true;
+    }
+  }
+  return false;
+}
+
 DatatypeConstructorArg::DatatypeConstructorArg(std::string name, Expr selector) :
   d_name(name),
   d_selector(selector),
index c1ec475e5064c2171bfe99a830c1b1bd3ed2dee1..1ea9fd6be87d5f2db456e4c05ca13ea73d685984 100644 (file)
@@ -213,7 +213,7 @@ private:
   Type doParametricSubstitution(Type range,
                                 const std::vector< SortConstructorType >& paramTypes,
                                 const std::vector< DatatypeType >& paramReplacements);
-  
+
   /** compute the cardinality of this datatype */
   Cardinality computeCardinality( std::vector< Type >& processing ) const throw(IllegalArgumentException);
   /** compute whether this datatype is well-founded */
@@ -236,7 +236,7 @@ public:
    * constructor and tester aren't created until resolution time.
    */
   DatatypeConstructor(std::string name, std::string tester);
-  
+
   /** set sygus */
   void setSygus( Expr op, Expr let_body, std::vector< Expr >& let_args, unsigned num_let_input_argus );
 
@@ -284,7 +284,7 @@ public:
    * Datatype must be resolved.
    */
   Expr getTester() const;
-  
+
   /** get sygus op */
   Expr getSygusOp() const;
   /** get sygus let body */
@@ -297,7 +297,7 @@ public:
   unsigned getNumSygusLetInputArgs() const;
   /** is this a sygus identity function */
   bool isSygusIdFunc() const;
-  
+
   /**
    * Get the tester name for this Datatype constructor.
    */
@@ -334,6 +334,13 @@ public:
    * only be called for resolved constructors.
    */
   bool isFinite() const throw(IllegalArgumentException);
+  /**
+   * Return true iff this constructor is finite (it is nullary or
+   * each of its argument types are finite) under assumption
+   * uninterpreted sorts are finite.  This function can
+   * only be called for resolved constructors.
+   */
+  bool isUFinite() const throw(IllegalArgumentException);
 
   /**
    * Returns true iff this Datatype constructor has already been
@@ -373,6 +380,7 @@ public:
    * then we will pose additional requirements for sharing.
    */
   bool involvesExternalType() const;
+  bool involvesUninterpretedType() const;
 
 };/* class DatatypeConstructor */
 
@@ -469,16 +477,17 @@ private:
   bool d_resolved;
   Type d_self;
   bool d_involvesExt;
+  bool d_involvesUt;
   /** information for sygus */
   Type d_sygus_type;
-  Expr d_sygus_bvl;  
+  Expr d_sygus_bvl;
   bool d_sygus_allow_const;
   bool d_sygus_allow_all;
 
   // "mutable" because computing the cardinality can be expensive,
   // and so it's computed just once, on demand---this is the cache
   mutable Cardinality d_card;
-  
+
   // is this type a recursive singleton type
   mutable int d_card_rec_singleton;
   // if d_card_rec_singleton is true,
@@ -555,7 +564,7 @@ public:
    *    allow_const : whether all constants are (implicitly) included in the grammar
    */
   void setSygus( Type st, Expr bvl, bool allow_const, bool allow_all );
-  
+
   /** Get the name of this Datatype. */
   inline std::string getName() const throw();
 
@@ -576,7 +585,7 @@ public:
 
   /** is this a co-datatype? */
   inline bool isCodatatype() const;
-  
+
   /** is this a sygus datatype? */
   inline bool isSygus() const;
 
@@ -594,6 +603,14 @@ public:
    * Datatype must be resolved or an exception is thrown.
    */
   bool isFinite() const throw(IllegalArgumentException);
+  /**
+   * Return  true iff this  Datatype is  finite (all  constructors are
+   * finite,  i.e., there  are finitely  many ground  terms) under the
+   * assumption unintepreted sorts are finite.   If the
+   * datatype is  not well-founded, this function  returns false.  The
+   * Datatype must be resolved or an exception is thrown.
+   */
+  bool isUFinite() const throw(IllegalArgumentException);
 
   /**
    * Return true iff this datatype is well-founded (there exist ground
@@ -601,16 +618,16 @@ public:
    */
   bool isWellFounded() const throw(IllegalArgumentException);
 
-  /** 
+  /**
    * Return true iff this datatype is a recursive singleton
    */
   bool isRecursiveSingleton() const throw(IllegalArgumentException);
-  
-  
+
+
   /** get number of recursive singleton argument types */
   unsigned getNumRecursiveSingletonArgTypes() const throw(IllegalArgumentException);
   Type getRecursiveSingletonArgType( unsigned i ) const throw(IllegalArgumentException);
-  
+
   /**
    * Construct and return a ground term of this Datatype.  The
    * Datatype must be both resolved and well-founded, or else an
@@ -678,7 +695,7 @@ public:
    * This Datatype must be resolved.
    */
   Expr getConstructor(std::string name) const;
-  
+
   /** get sygus type */
   Type getSygusType() const;
   /** get sygus var list */
@@ -693,6 +710,7 @@ public:
    * then we will pose additional requirements for sharing.
    */
   bool involvesExternalType() const;
+  bool involvesUninterpretedType() const;
 
 };/* class Datatype */
 
@@ -743,6 +761,7 @@ inline Datatype::Datatype(std::string name, bool isCo) :
   d_resolved(false),
   d_self(),
   d_involvesExt(false),
+  d_involvesUt(false),
   d_card(CardinalityUnknown()),
   d_card_rec_singleton(0),
   d_well_founded(0) {
@@ -756,6 +775,7 @@ inline Datatype::Datatype(std::string name, const std::vector<Type>& params, boo
   d_resolved(false),
   d_self(),
   d_involvesExt(false),
+  d_involvesUt(false),
   d_card(CardinalityUnknown()),
   d_card_rec_singleton(0),
   d_well_founded(0) {
index 1962d2e319edffc58ea9a8b7f9beb702ac1325ec..77733904d492b34cac68b2e5c7d04354f23975b2 100644 (file)
@@ -244,7 +244,7 @@ void TheoryDatatypes::check(Effort e) {
                   if( consIndex==-1 ){
                     consIndex = j;
                   }
-                  if( !dt[ j ].isFinite() ) {
+                  if( !dt[ j ].isFinite() && ( !options::finiteModelFind() || !dt.involvesUninterpretedType() ) ) {
                     if( !eqc || !eqc->d_selectors ){
                       needSplit = false;
                     }
index 095e7868eeec4784ddff9e1e4e6d461d5cf6cae5..027a4573b928e5da9b3de08085088038cd76faa7 100644 (file)
@@ -688,6 +688,7 @@ Node FirstOrderModelFmc::getFunctionValue(Node op, const char* argPrefix ) {
     }else{
       //make the condition
       Node cond = d_models[op]->d_cond[i];
+      Trace("fmc-model-func") << "...cond : " << cond << std::endl;
       std::vector< Node > children;
       for( unsigned j=0; j<cond.getNumChildren(); j++) {
         TypeNode tn = vars[j].getType();
index 5ccb794f7b2d5cd25520db1ba92e265737cd9298..36b24c51bcd734d759078e47b1a545e78bb69ae2 100644 (file)
@@ -1029,7 +1029,7 @@ Node TermDb::getEnumerateTerm( TypeNode tn, unsigned index ) {
 bool TermDb::isClosedEnumerableType( TypeNode tn ) {
   std::map< TypeNode, bool >::iterator it = d_typ_closed_enum.find( tn );
   if( it==d_typ_closed_enum.end() ){
-    d_typ_closed_enum[tn] = false;
+    d_typ_closed_enum[tn] = true;
     bool ret = true;
     if( tn.isArray() || tn.isSort() || tn.isCodatatype() ){
       ret = false;
index bb85e62b307f103d4e35329ffaba20e9e81865de..bca9f2e4f4a4704e743d4f094bbc95b1be9d793b 100644 (file)
@@ -41,7 +41,9 @@ TESTS =       \
        fore19-exp2-core.smt2 \
        with-ind-104-core.smt2 \
        syn002-si-real-int.smt2 \
-       krs-sat.smt2
+       krs-sat.smt2 \
+       forall_unit_data2.smt2 \
+       sc_bad_model_1221.smt2
 
 
 EXTRA_DIST = $(TESTS)
index 7e0897d148bca030f5d6e63c444fce41c44d7be7..26ef66522b0e512e43fcbbe6066cc24f763b4dbb 100755 (executable)
@@ -1,13 +1,10 @@
-; cvc4 --lang smt\r
-\r
-; This problem returns "unsat", but it is in fact "sat", by interpreting "a" with a domain of\r
-; cardinality 1. The issue arises irrespective of the "--finite-model-find" option.\r
-\r
-(set-option :produce-models true)\r
-(set-option :interactive-mode true)\r
-(set-logic ALL_SUPPORTED)\r
-(declare-sort a 0)\r
-(declare-datatypes () ((w (Wrap (unw a)))))\r
-(declare-fun x () w)\r
-(assert (forall ((y w)) (= x y)))\r
-(check-sat)\r
+; COMMAND-LINE: --finite-model-find
+; EXPECT: sat
+(set-option :produce-models true)
+(set-option :interactive-mode true)
+(set-logic ALL_SUPPORTED)
+(declare-sort a 0)
+(declare-datatypes () ((w (Wrap (unw a)))))
+(declare-fun x () w)
+(assert (forall ((y w)) (= x y)))
+(check-sat)
diff --git a/test/regress/regress0/fmf/forall_unit_data2.smt2 b/test/regress/regress0/fmf/forall_unit_data2.smt2
new file mode 100755 (executable)
index 0000000..64847d6
--- /dev/null
@@ -0,0 +1,8 @@
+; COMMAND-LINE: --finite-model-find
+; EXPECT: unsat
+(set-logic ALL_SUPPORTED)
+(declare-sort a 0)
+(declare-datatypes () ((prod (Pair (gx a) (gy a)))))
+(declare-fun p () prod)
+(assert (forall ((x a) (y a)) (not (= p (Pair x y)))))
+(check-sat)
diff --git a/test/regress/regress0/fmf/sc_bad_model_1221.smt2 b/test/regress/regress0/fmf/sc_bad_model_1221.smt2
new file mode 100755 (executable)
index 0000000..a083e41
--- /dev/null
@@ -0,0 +1,33 @@
+; COMMAND-LINE: --finite-model-find
+; EXPECT: sat
+  (set-logic ALL_SUPPORTED)
+  (set-info :status sat)
+  (declare-sort a 0)
+  (declare-fun __nun_card_witness_0 () a)
+  (declare-datatypes () ((prod (Pair (_select_Pair_0 a) (_select_Pair_1 a)))))
+  (declare-sort G_snd 0)
+  (declare-fun __nun_card_witness_1 () G_snd)
+  (declare-fun snd (prod) a)
+  (declare-fun proj_G_snd_0 (G_snd) prod)
+  (assert
+   (forall ((a/32 G_snd))
+      (and
+       (= (snd (proj_G_snd_0 a/32)) (_select_Pair_1 (proj_G_snd_0 a/32))) 
+       true)))
+  (declare-fun p () prod)
+  (declare-datatypes () ((pd (Pd (_select_Pd_0 prod)))))
+  (declare-sort G_fs 0)
+  (declare-fun __nun_card_witness_2 () G_fs)
+  (declare-fun fs (pd) a)
+  (declare-fun proj_G_fs_0 (G_fs) pd)
+  (assert
+   (forall ((a/33 G_fs))
+      (and
+       (= (fs (proj_G_fs_0 a/33))
+        (_select_Pair_0 (_select_Pd_0 (proj_G_fs_0 a/33)))) 
+       true)))
+  (assert
+   (and (not (= (fs (Pd p)) (snd p))) 
+    (exists ((a/34 G_fs)) (= (Pd p) (proj_G_fs_0 a/34))) 
+    (exists ((a/35 G_snd)) (= p (proj_G_snd_0 a/35)))))
+  (check-sat)