Simplify representation of inversion Skolems for bv cegqi (#1164)
authorAndrew Reynolds <andrew.j.reynolds@gmail.com>
Fri, 29 Sep 2017 19:28:52 +0000 (14:28 -0500)
committerGitHub <noreply@github.com>
Fri, 29 Sep 2017 19:28:52 +0000 (14:28 -0500)
* Simplify intermediate representation of inversion skolems for cegqi bit-vectors. Cache bv inversion status globally in QuantifierEngine. Generalize virtual term policy for marking eligible instantiation terms. Enable regression.

* Enable other regression

src/theory/quantifiers/ceg_instantiator.cpp
src/theory/quantifiers/ceg_t_instantiator.cpp
src/theory/quantifiers/ceg_t_instantiator.h
src/theory/quantifiers/inst_strategy_cbqi.cpp
src/theory/quantifiers/term_database.cpp
src/theory/quantifiers/term_database.h
src/theory/quantifiers_engine.cpp
src/theory/quantifiers_engine.h
test/regress/regress0/quantifiers/Makefile.am
test/regress/regress0/quantifiers/qbv-simple-2vars-vo.smt2
test/regress/regress0/quantifiers/qbv-test-invert-mul.smt2

index 0386a0fdb33e7e3203fa4e26526b3c32913865b6..2fdc37134387b9c2320504cc99c32fa165d28b39 100644 (file)
@@ -55,7 +55,6 @@ void CegInstantiator::computeProgVars( Node n ){
       computeProgVars( n[i] );
       if( d_inelig.find( n[i] )!=d_inelig.end() ){
         d_inelig[n] = true;
-        return;
       }
       for( std::map< Node, bool >::iterator it = d_prog_var[n[i]].begin(); it != d_prog_var[n[i]].end(); ++it ){
         d_prog_var[n][it->first] = true;
@@ -126,7 +125,6 @@ bool CegInstantiator::doAddInstantiation( SolvedForm& sf, unsigned i, unsigned e
     if( needsPostprocess ){
       //must make copy so that backtracking reverts sf
       SolvedForm sf_tmp = sf;
-      unsigned prev_var_size = sf.d_vars.size();
       bool postProcessSuccess = true;
       for( std::map< Instantiator *, Node >::iterator itp = pp_inst_to_var.begin(); itp != pp_inst_to_var.end(); ++itp ){
         if( !itp->first->postProcessInstantiationForVariable( this, sf_tmp, itp->second, effort, lemmas ) ){
@@ -134,17 +132,6 @@ bool CegInstantiator::doAddInstantiation( SolvedForm& sf, unsigned i, unsigned e
           break;
         }
       }
-      if( sf_tmp.d_vars.size()>prev_var_size ){
-        // if we added substitutions during postprocess, process these now
-        std::vector< Node > new_vars;
-        std::vector< Node > new_subs;
-        for( unsigned i=0; i<prev_var_size; i++ ){
-          Node subs = sf_tmp.d_subs[i];
-          sf_tmp.d_subs[i] = subs.substitute( sf_tmp.d_vars.begin() + prev_var_size, sf_tmp.d_vars.end(),
-                                              sf_tmp.d_subs.begin() + prev_var_size, sf_tmp.d_subs.end() );
-        }
-      }
-
       if( postProcessSuccess ){
         return doAddInstantiation( sf_tmp.d_vars, sf_tmp.d_subs, lemmas );
       }else{
index 75118dadcf56e4353f1d67debdf4a0bd41397fd2..e2065ffd9c1c27f492aeff6a64a5eafb809d13a1 100644 (file)
@@ -26,6 +26,8 @@
 #include "theory/bv/theory_bv_utils.h"
 #include "util/bitvector.h"
 
+#include <algorithm>
+
 using namespace std;
 using namespace CVC4;
 using namespace CVC4::kind;
@@ -834,7 +836,18 @@ bool EprInstantiator::processEqualTerms( CegInstantiator * ci, SolvedForm& sf, N
 Node BvInverter::getSolveVariable( TypeNode tn ) {
   std::map< TypeNode, Node >::iterator its = d_solve_var.find( tn );
   if( its==d_solve_var.end() ){
-    Node k = NodeManager::currentNM()->mkSkolem( "s", tn );
+    std::stringstream ss;
+    if( tn.isFunction() ){
+      Assert( tn.getNumChildren()==2 );
+      Assert( tn[0].isBoolean() );
+      ss << "slv_f";
+    }else{
+      ss << "slv";
+    }
+    Node k = NodeManager::currentNM()->mkSkolem( ss.str(), tn );
+    // marked as a virtual term (it is eligible for instantiation)
+    VirtualTermSkolemAttribute vtsa;
+    k.setAttribute(vtsa,true);
     d_solve_var[tn] = k;
     return k;
   }else{
@@ -843,10 +856,31 @@ Node BvInverter::getSolveVariable( TypeNode tn ) {
 }
 
 Node BvInverter::getInversionSkolemFor( Node cond, TypeNode tn ) {
+  // condition should be rewritten
+  Assert( Rewriter::rewrite(cond)==cond );
   std::unordered_map< Node, Node, NodeHashFunction >::iterator it = d_inversion_skolem_cache.find( cond );
   if( it==d_inversion_skolem_cache.end() ){
-    Node skv = NodeManager::currentNM()->mkSkolem ("skvinv", tn, "created for BvInverter");
-    Trace("cegqi-bv-skvinv") << "SKVINV : " << skv << " is the skolem associated with conditon " << cond << std::endl;
+    Node skv;
+    if( cond.getKind()==EQUAL ){
+      // optimization : if condition is ( x = v ) should just return v and not introduce a Skolem
+      // this can happen when we ask for the multiplicative inversion with bv1
+      Node x = getSolveVariable( tn );
+      for( unsigned i=0; i<2; i++ ){
+        if( cond[i]==x ){
+          skv = cond[1-i];
+          Trace("cegqi-bv-skvinv") << "SKVINV : " << skv << " is trivially associated with conditon " << cond << std::endl;
+          break;
+        }
+      }
+    }
+    if( skv.isNull() ){
+      // TODO : compute the value if the condition is deterministic, e.g. calc multiplicative inverse of 2 constants
+      skv = NodeManager::currentNM()->mkSkolem ("skvinv", tn, "created for BvInverter");
+      Trace("cegqi-bv-skvinv") << "SKVINV : " << skv << " is the skolem associated with conditon " << cond << std::endl;
+      // marked as a virtual term (it is eligible for instantiation)
+      VirtualTermSkolemAttribute vtsa;
+      skv.setAttribute(vtsa,true);
+    }
     d_inversion_skolem_cache[cond] = skv;
     return skv;
   }else{
@@ -855,27 +889,52 @@ Node BvInverter::getInversionSkolemFor( Node cond, TypeNode tn ) {
   }
 }
 
+Node BvInverter::getInversionSkolemFunctionFor( TypeNode tn ) {
+  // function maps conditions to skolems
+  TypeNode ftn = NodeManager::currentNM()->mkFunctionType( NodeManager::currentNM()->booleanType(), tn );
+  return getSolveVariable( ftn );
+}
+
+Node BvInverter::getInversionNode( Node cond, TypeNode tn ) {
+  // condition should be rewritten
+  Node new_cond = Rewriter::rewrite(cond);
+  if( new_cond!=cond ){
+    Trace("bv-invert-debug") << "Condition " << cond << " was rewritten to " << new_cond << std::endl;
+  } 
+  Node f = getInversionSkolemFunctionFor( tn );
+  return NodeManager::currentNM()->mkNode( kind::APPLY_UF, f, new_cond );
+}
+
+bool BvInverter::isInvertible( Kind k ) {
+  // TODO : make this precise (this should correspond to all kinds that we handle in solve_bv_lit/solve_bv_constraint)
+  return k!=APPLY_UF;
+}
+
 Node BvInverter::getPathToPv( Node lit, Node pv, Node sv, std::vector< unsigned >& path, std::unordered_set< TNode, TNodeHashFunction >& visited ) {
   if( visited.find( lit )==visited.end() ){
     visited.insert( lit );
     if( lit==pv ){
       return sv;
     }else{
-      unsigned rmod = 0; // TODO : randomize?
-      for( unsigned i=0; i<lit.getNumChildren(); i++ ){
-        unsigned ii = ( i + rmod )%lit.getNumChildren();
-        Node litc = getPathToPv( lit[ii], pv, sv, path, visited );
-        if( !litc.isNull() ){
-          // path is outermost term index last
-          path.push_back( ii );
-          std::vector< Node > children;
-          if( lit.getMetaKind() == kind::metakind::PARAMETERIZED ){
-            children.push_back( lit.getOperator() );
-          }
-          for( unsigned j=0; j<lit.getNumChildren(); j++ ){
-            children.push_back( j==ii ? litc : lit[j] );
+      // only recurse if the kind is invertible 
+      // this allows us to avoid paths that go through skolem functions
+      if( isInvertible( lit.getKind() ) ){
+        unsigned rmod = 0; // TODO : randomize?
+        for( unsigned i=0; i<lit.getNumChildren(); i++ ){
+          unsigned ii = ( i + rmod )%lit.getNumChildren();
+          Node litc = getPathToPv( lit[ii], pv, sv, path, visited );
+          if( !litc.isNull() ){
+            // path is outermost term index last
+            path.push_back( ii );
+            std::vector< Node > children;
+            if( lit.getMetaKind() == kind::metakind::PARAMETERIZED ){
+              children.push_back( lit.getOperator() );
+            }
+            for( unsigned j=0; j<lit.getNumChildren(); j++ ){
+              children.push_back( j==ii ? litc : lit[j] );
+            }
+            return NodeManager::currentNM()->mkNode( lit.getKind(), children );
           }
-          return NodeManager::currentNM()->mkNode( lit.getKind(), children );
         }
       }
     }
@@ -883,9 +942,69 @@ Node BvInverter::getPathToPv( Node lit, Node pv, Node sv, std::vector< unsigned
   return Node::null();
 }
 
+Node BvInverter::eliminateSkolemFunctions( TNode n, std::vector< Node >& side_conditions, std::unordered_map< TNode, Node, TNodeHashFunction >& visited ) {
+  std::unordered_map< TNode, Node, TNodeHashFunction >::iterator it = visited.find( n );
+  if( it==visited.end() ){
+    Trace("bv-invert-debug") << "eliminateSkolemFunctions from " << n << "..." << std::endl;
+    Node ret = n;
+    bool childChanged = false;
+    std::vector< Node > children;
+    if( n.getMetaKind() == kind::metakind::PARAMETERIZED ){
+      children.push_back( n.getOperator() );
+    }
+    for( unsigned i=0; i<n.getNumChildren(); i++ ){
+      Node nc = eliminateSkolemFunctions( n[i], side_conditions, visited );
+      childChanged = childChanged || n[i]!=nc;
+      children.push_back( nc );
+    } 
+    if( childChanged ){
+      ret = NodeManager::currentNM()->mkNode( n.getKind(), children );
+    }
+    // now, check if it is a skolem function
+    if( ret.getKind()==APPLY_UF ){
+      Node op = ret.getOperator();
+      TypeNode tnp = op.getType();
+      // is this a skolem function?
+      std::map< TypeNode, Node >::iterator its = d_solve_var.find( tnp );
+      if( its!=d_solve_var.end() && its->second==op ){
+        Assert( ret.getNumChildren()==1 );
+        Assert( ret[0].getType().isBoolean() );
+        
+        Node cond = ret[0];
+        // must rewrite now to ensure we lookup the correct skolem 
+        cond = Rewriter::rewrite( cond );
+        
+        // if so, we replace by the (finalized) skolem variable
+        // Notice that since we are post-rewriting, skolem functions are already eliminated from cond
+        ret = getInversionSkolemFor( cond, ret.getType() );
+        
+        // also must add (substituted) side condition to vector
+        // substitute ( solve variable -> inversion skolem )
+        TNode solve_var = getSolveVariable( ret.getType() );
+        TNode tret = ret;
+        cond = cond.substitute( solve_var, tret );
+        if( std::find( side_conditions.begin(), side_conditions.end(), cond )==side_conditions.end() ){
+          side_conditions.push_back( cond );
+        }
+      }
+    }
+    Trace("bv-invert-debug") << "eliminateSkolemFunctions from " << n << " returned " << ret << std::endl;
+    visited[n] = ret;
+    return ret;
+  }else{
+    return it->second;
+  }
+}
+
+Node BvInverter::eliminateSkolemFunctions( TNode n, std::vector< Node >& side_conditions ) {
+  std::unordered_map< TNode, Node, TNodeHashFunction > visited;
+  return eliminateSkolemFunctions( n, side_conditions, visited );
+}
+  
 Node BvInverter::getPathToPv( Node lit, Node pv, Node sv, Node pvs, std::vector< unsigned >& path ) {
   std::unordered_set< TNode, TNodeHashFunction > visited;
   Node slit = getPathToPv( lit, pv, sv, path, visited );
+  // if we are able to find a (invertible) path to pv
   if( !slit.isNull() ){
     // substitute pvs for the other occurrences of pv
     TNode tpv = pv;
@@ -929,11 +1048,9 @@ Node BvInverter::solve_bv_constraint( Node sv, Node sv_t, Node t, Kind rk, bool
       // add side condition
       status.d_conds.push_back (sc);
       
-      // get the skolem variable for this side condition
-      Node skv = getInversionSkolemFor (sc, solve_tn);
-      // cache for substitution
-      // map the skolem to its side condition
-      status.d_sk_inv[ skv ] = sc;
+      // get the skolem node for this side condition
+      Node skv = getInversionNode (sc, solve_tn);
+      // now solving with the skolem node as the RHS
       t = skv;
 
     }else if( k==BITVECTOR_NEG || k==BITVECTOR_NOT ){
@@ -1022,12 +1139,15 @@ public:
 };
 
 BvInstantiator::BvInstantiator( QuantifiersEngine * qe, TypeNode tn ) : Instantiator( qe, tn ){
-  //FIXME : this needs to be global!!  Probably move to QuantifiersEngine
-  d_inverter = new BvInverter;
+  // get the global inverter utility
+  // this must be global since we need to:
+  // * process Skolem functions properly across multiple variables within the same quantifier
+  // * cache Skolem variables uniformly across multiple quantified formulas
+  d_inverter = qe->getBvInverter();
 }
 
 BvInstantiator::~BvInstantiator(){
-  delete d_inverter;
+
 }
 
 void BvInstantiator::reset( CegInstantiator * ci, SolvedForm& sf, Node pv, unsigned effort ) {
@@ -1040,6 +1160,7 @@ void BvInstantiator::reset( CegInstantiator * ci, SolvedForm& sf, Node pv, unsig
 
 
 void BvInstantiator::processLiteral( CegInstantiator * ci, SolvedForm& sf, Node pv, Node lit, unsigned effort ) {
+  Assert( d_inverter!=NULL );
   // find path to pv
   std::vector< unsigned > path;
   Node sv = d_inverter->getSolveVariable( pv.getType() );
@@ -1082,6 +1203,10 @@ bool BvInstantiator::processAssertions( CegInstantiator * ci, SolvedForm& sf, No
     Trace("cegqi-bv") << "  " << iti->second.size() << " candidate instantiations for " << pv << " : " << std::endl;
     // the order of instantiation ids we will try
     std::vector< unsigned > inst_ids_try;
+    // until we have a model-preserving selection function for BV, this must be heuristic (we only pick one literal)
+    // hence we randomize the list
+    // this helps robustness, since picking the same literal every time may be lead to a stream of value instantiations
+    std::random_shuffle( iti->second.begin(), iti->second.end() );
     
     for( unsigned j=0; j<iti->second.size(); j++ ){
       unsigned inst_id = iti->second[j];
@@ -1106,13 +1231,14 @@ bool BvInstantiator::processAssertions( CegInstantiator * ci, SolvedForm& sf, No
       // TODO : selection criteria across multiple literals goes here
       
       // currently, we use a naive heuristic which takes only the first solved term
-      // TODO : randomize?
       if( inst_ids_try.empty() ){
         inst_ids_try.push_back( inst_id );
       }
     }
     
     // now, try all instantiation ids we want to try
+    // typically size( inst_ids_try )=0, otherwise worst-case performance for constructing
+    // instantiations is exponential on the number of variables in this quantifier
     for( unsigned j = 0; j<inst_ids_try.size(); j++ ){
       unsigned inst_id = iti->second[j];
       Assert( d_inst_id_to_term.find(inst_id)!=d_inst_id_to_term.end() );
@@ -1120,8 +1246,6 @@ bool BvInstantiator::processAssertions( CegInstantiator * ci, SolvedForm& sf, No
       // try instantiation pv -> inst_term
       TermProperties pv_prop_bv;
       Trace("cegqi-bv") << "*** try " << pv << " -> " << inst_term << std::endl;
-      // TODO : need to track dependencies between inst_term and the free variables it depends on
-      // This should enforce that we do not introduce any circular dependencies when solving for further variables.
       d_var_to_curr_inst_id[pv] = inst_id;
       if( ci->doAddInstantiationInc( pv, inst_term, pv_prop_bv, sf, effort ) ){
         return true;
@@ -1137,56 +1261,23 @@ bool BvInstantiator::processAssertions( CegInstantiator * ci, SolvedForm& sf, No
 
 bool BvInstantiator::needsPostProcessInstantiationForVariable( CegInstantiator * ci, SolvedForm& sf, Node pv, unsigned effort ) {
   // we may need to post-process the instantiation since inversion skolems need to be finalized
-  return d_var_to_curr_inst_id.find(pv)!=d_var_to_curr_inst_id.end();
+  // TODO : technically skolem functions can appear in datatypes with bit-vector fields. We need to eliminate them there too.
+  return true;
 }
 
 bool BvInstantiator::postProcessInstantiationForVariable( CegInstantiator * ci, SolvedForm& sf, Node pv, unsigned effort, std::vector< Node >& lemmas ) {
-  Trace("cegqi-bv") << "BvInstantiator::postProcessInstantiation for " << pv << std::endl;
-  // first, eliminate bound variables from all skolem conditions
-  std::unordered_map< Node, unsigned, NodeHashFunction >::iterator iti = d_var_to_curr_inst_id.find( pv );
-  Assert( iti!=d_var_to_curr_inst_id.end() );
-  unsigned inst_id = iti->second;
-  std::unordered_map< unsigned, BvInverterStatus >::iterator its = d_inst_id_to_status.find( inst_id );
-  Assert( its!=d_inst_id_to_status.end() );
-  
-  // this maps temporary skolems (those with side conditions involving free variables) -> finalized skolems
-  std::vector< Node > sk_vars_add;
-  std::vector< Node > sk_subs_add;
-  for( std::unordered_map< Node, Node, NodeHashFunction >::iterator itk = its->second.d_sk_inv.begin(); 
-       itk != its->second.d_sk_inv.end(); ++itk ){
-    TNode curr_skv = itk->first;
-    Trace("cegqi-bv-debug") << "  Skolem " << curr_skv << " has condition " << itk->second << std::endl;
-    Node curr_cond = itk->second.substitute( sf.d_vars.begin(), sf.d_vars.end(), sf.d_subs.begin(), sf.d_subs.end() );
-    // the condition should have no bound variables, or skolems that are associated with conditions that have bound variables.
-    
-    // if the condition is updated, we need to update it
-    Node sk_for_curr_cond;
-    if( curr_cond!=itk->second ){
-      Trace("cegqi-bv-debug") << "  Skolem " << curr_skv << " has condition (after substitution) " << curr_cond << std::endl;
-      sk_for_curr_cond = d_inverter->getInversionSkolemFor( curr_cond, curr_skv.getType() );
-      // we must replace the Skolem in the condition 
-      sk_vars_add.push_back( curr_skv );
-      sk_subs_add.push_back( sk_for_curr_cond );
-      
-      // TODO : register this as a skolem that is eligible for instantiation
-    }else{
-      sk_for_curr_cond = curr_skv;
+  Trace("cegqi-bv") << "BvInstantiator::postProcessInstantiation " << pv << std::endl;
+  Assert( std::find( sf.d_vars.begin(), sf.d_vars.end(), pv )!=sf.d_vars.end() );
+  unsigned index = std::find( sf.d_vars.begin(), sf.d_vars.end(), pv )-sf.d_vars.begin();
+  Trace("cegqi-bv") << "  postprocess : " << pv << " -> " << sf.d_subs[index] << std::endl;
+  // eliminate skolem functions from the substitution
+  unsigned prev_lem_size = lemmas.size();
+  sf.d_subs[index] = d_inverter->eliminateSkolemFunctions( sf.d_subs[index], lemmas );
+  if( Trace.isOn("cegqi-bv") ){
+    Trace("cegqi-bv") << "  got : " << pv << " -> " << sf.d_subs[index] << std::endl;
+    for( unsigned i=prev_lem_size; i<lemmas.size(); i++ ){
+      Trace("cegqi-bv") << "  side condition : " << lemmas[i] << std::endl;
     }
-    
-    // we now must map the solve variable in the current condition to the final skolem
-    TNode solve_var = d_inverter->getSolveVariable( sk_for_curr_cond.getType() );
-    curr_cond = curr_cond.substitute( solve_var, sk_for_curr_cond );
-    
-    // curr_cond is now in terms of the finalized skolem
-    lemmas.push_back( curr_cond );
-    Trace("cegqi-bv") << "    side condition : " << curr_cond << std::endl;
-  }
-  
-  // add new substitutions
-  TermProperties term_prop_skvinv;
-  for( unsigned i=0; i<sk_vars_add.size(); i++ ){
-    sf.push_back( sk_vars_add[i], sk_subs_add[i], term_prop_skvinv );
-    Trace("cegqi-bv") << "  (temporary -> final) skolem substitution : " << sk_vars_add[i] << " -> " << sk_subs_add[i] << std::endl;
   }
 
   return true;
index 7328df167425a6d0af7eff95ac9f36d2870cca74..26f5fe62a73528fb7d293637b50a4c9666cf7c41 100644 (file)
@@ -94,43 +94,85 @@ public:
   BvInverterStatus() : d_status(0) {}
   ~BvInverterStatus(){}
   int d_status;
+  // TODO : may not need this (conditions are now appear explicitly in solved forms)
   // side conditions 
   std::vector< Node > d_conds;
-  // conditions regarding the skolems in d_conds
-  std::unordered_map< Node, Node, NodeHashFunction > d_sk_inv;
 };
 
 // inverter class
 // TODO : move to theory/bv/ if generally useful?
 class BvInverter {
 private:
+  /** dummy variables for each type */
   std::map< TypeNode, Node > d_solve_var;
-  Node getPathToPv( Node lit, Node pv, Node sv, std::vector< unsigned >& path, std::unordered_set< TNode, TNodeHashFunction >& visited );
-  // stores the inversion skolems
+  /** stores the inversion skolems, for each condition */
   std::unordered_map< Node, Node, NodeHashFunction > d_inversion_skolem_cache;
+  /** helper function for getPathToPv */
+  Node getPathToPv( Node lit, Node pv, Node sv, std::vector< unsigned >& path, std::unordered_set< TNode, TNodeHashFunction >& visited );
+  /** helper function for eliminateSkolemFunctions */
+  Node eliminateSkolemFunctions( TNode n, std::vector< Node >& side_conditions, std::unordered_map< TNode, Node, TNodeHashFunction >& visited ); 
+  // is operator k invertible?
+  bool isInvertible( Kind k );
+  /** get inversion skolem for condition
+  * precondition : exists x. cond( x ) is a tautology in BV,
+  *               where x is getSolveVariable( tn ).
+  * returns fresh skolem k of type tn, where we may assume cond( k ).
+  */
+  Node getInversionSkolemFor( Node cond, TypeNode tn );
+  /** get inversion skolem function for type tn.
+  *   This is a function of type ( Bool -> tn ) that is used for explicitly storing side conditions
+  *   inside terms. For all ( cond, tn ), if :
+  *
+  *   f = getInversionSkolemFunctionFor( tn )
+  *   k = getInversionSkolemFor( cond, tn )
+  *   then:
+  *   f( cond ) is a placeholder for k.
+  *
+  * In the call eliminateSkolemFunctions, we replace all f( cond ) with k and add cond{ x -> k } to side_conditions.
+  * The advantage is that f( cond ) explicitly contains the side condition so it automatically
+  * updates with substitutions.
+  */
+  Node getInversionSkolemFunctionFor( TypeNode tn );
 public:
   BvInverter(){}
   ~BvInverter(){}
-  // get dummy fresh variable of type tn, used as argument for sv 
+  /** get dummy fresh variable of type tn, used as argument for sv */
   Node getSolveVariable( TypeNode tn );
-  // get inversion skolem for condition
-  // precondition : exists x. cond( x ) is a tautology in BV,
-  //                where x is getSolveVariable( tn ).
-  // returns fresh skolem k, where we may assume cond( k ).
-  Node getInversionSkolemFor( Node cond, TypeNode tn );
-  // Get path to pv in lit, replace that occurrence by sv and all others by pvs.
-  // e.g. if return value R is non-null, then:
-  //   lit.path = pv
-  //   R.path = sv
-  //   R.path' = pvs for all lit.path' = pv, where path' != path
+  /** get inversion node, if :
+  *
+  *   f = getInversionSkolemFunctionFor( tn )
+  *
+  * This returns f( cond ).
+  */
+  Node getInversionNode( Node cond, TypeNode tn );
+  /** Get path to pv in lit, replace that occurrence by sv and all others by pvs.
+  * If return value R is non-null, then :
+  *   lit.path = pv
+  *   R.path = sv
+  *   R.path' = pvs for all lit.path' = pv, where path' != path
+  */
   Node getPathToPv( Node lit, Node pv, Node sv, Node pvs, std::vector< unsigned >& path );
-public:
-  // solve for sv in constraint ( (pol ? _ : not) sv_t <rk> t ), where sv_t.path = sv
-  // status accumulates side conditions
+  /** eliminate skolem functions in node n
+  *
+  * This eliminates all Skolem functions from Node n and replaces them with finalized 
+  * Skolem variables. 
+  * 
+  * For each skolem variable we introduce, we ensure its associated side condition is
+  * added to side_conditions.
+  *
+  * Apart from Skolem functions, all subterms of n should be eligible for instantiation.
+  */
+  Node eliminateSkolemFunctions( TNode n, std::vector< Node >& side_conditions );
+  /** solve_bv_constraint
+  * solve for sv in constraint ( (pol ? _ : not) sv_t <rk> t ), where sv_t.path = sv
+  * status accumulates side conditions
+  */
   Node solve_bv_constraint( Node sv, Node sv_t, Node t, Kind rk, bool pol, std::vector< unsigned >& path,
                             BvInverterModelQuery * m, BvInverterStatus& status );
-  // solve for sv in lit, where lit.path = sv
-  // status accumulates side conditions
+  /** solve_bv_lit
+  * solve for sv in lit, where lit.path = sv
+  * status accumulates side conditions
+  */
   Node solve_bv_lit( Node sv, Node lit, bool pol, std::vector< unsigned >& path,
                      BvInverterModelQuery * m, BvInverterStatus& status );
 };
index 34758c4316c1f09ac3e5ab734808deabc6053386..52ea7cc6241013e74528dca687b3d688d895eb1b 100644 (file)
@@ -746,7 +746,8 @@ bool InstStrategyCegqi::addLemma( Node lem ) {
 
 bool InstStrategyCegqi::isEligibleForInstantiation( Node n ) {
   if( n.getKind()==INST_CONSTANT || n.getKind()==SKOLEM ){
-    if( n.getKind()==SKOLEM && d_quantEngine->getTermDatabase()->containsVtsTerm( n ) ){
+    if( n.getAttribute(VirtualTermSkolemAttribute()) ){
+      // virtual terms are allowed
       return true;
     }else{
       TypeNode tn = n.getType();
index ec5fc633d6adadf4213ac0a9139d1289a6dde51f..18ba08ae2a6969063016aa5380652131ed4bc84e 100644 (file)
@@ -1656,6 +1656,9 @@ Node TermDb::getVtsDelta( bool isFree, bool create ) {
     }
     if( d_vts_delta.isNull() ){
       d_vts_delta = NodeManager::currentNM()->mkSkolem( "delta", NodeManager::currentNM()->realType(), "delta for virtual term substitution" );
+      //mark as a virtual term
+      VirtualTermSkolemAttribute vtsa;
+      d_vts_delta.setAttribute(vtsa,true);
     }
   }
   return isFree ? d_vts_delta_free : d_vts_delta;
@@ -1668,6 +1671,9 @@ Node TermDb::getVtsInfinity( TypeNode tn, bool isFree, bool create ) {
     }
     if( d_vts_inf[tn].isNull() ){
       d_vts_inf[tn] = NodeManager::currentNM()->mkSkolem( "inf", tn, "infinity for virtual term substitution" );
+      //mark as a virtual term
+      VirtualTermSkolemAttribute vtsa;
+      d_vts_inf[tn].setAttribute(vtsa,true);
     }
   }
   return isFree ? d_vts_inf_free[tn] : d_vts_inf[tn];
index 4650cc5d499abd0e1a15d9ffb982850a111f5d32..9df9da9574c8cf97742afa8ab27cded0042515ec 100644 (file)
@@ -123,6 +123,9 @@ typedef expr::Attribute< QuantIdNumAttributeId, uint64_t > QuantIdNumAttribute;
 struct SygusVarNumAttributeId {};
 typedef expr::Attribute<SygusVarNumAttributeId, uint64_t> SygusVarNumAttribute;
 
+/** Attribute to mark Skolems as virtual terms */
+struct VirtualTermSkolemAttributeId {};
+typedef expr::Attribute< VirtualTermSkolemAttributeId, bool > VirtualTermSkolemAttribute;
 
 
 class QuantifiersEngine;
index 8649c7bddbe59f5ebdf29c5d030bd5cc24ce9ca0..2d1ae7983e32b14b04fbc3399e93be389dc18796 100644 (file)
@@ -24,6 +24,7 @@
 #include "theory/quantifiers/ambqi_builder.h"
 #include "theory/quantifiers/bounded_integers.h"
 #include "theory/quantifiers/ce_guided_instantiation.h"
+#include "theory/quantifiers/ceg_t_instantiator.h"
 #include "theory/quantifiers/conjecture_generator.h"
 #include "theory/quantifiers/first_order_model.h"
 #include "theory/quantifiers/full_model_check.h"
@@ -130,6 +131,7 @@ QuantifiersEngine::QuantifiersEngine(context::Context* c, context::UserContext*
   d_uee = NULL;
   d_fs = NULL;
   d_rel_dom = NULL;
+  d_bv_invert = NULL;
   d_builder = NULL;
 
   d_total_inst_count_debug = 0;
@@ -160,6 +162,7 @@ QuantifiersEngine::~QuantifiersEngine(){
   delete d_qcf;
   delete d_quant_rel;
   delete d_rel_dom;
+  delete d_bv_invert;
   delete d_model;
   delete d_tr_trie;
   delete d_term_db;
@@ -217,9 +220,10 @@ void QuantifiersEngine::finishInit(){
   if( options::cbqi() ){
     d_i_cbqi = new quantifiers::InstStrategyCegqi( this );
     d_modules.push_back( d_i_cbqi );
-    //if( options::cbqiModel() ){
-    //  needsBuilder = true;
-    //}
+    if( options::cbqiBv() ){
+      // if doing instantiation for BV, need the inverter class
+      d_bv_invert = new quantifiers::BvInverter;
+    }
   }
   if( options::ceGuidedInst() ){
     d_ceg_inst = new quantifiers::CegInstantiation( this, c );
index 7d8f5354b6274d9e0b4706aad723032fc035a926..443bbd643e54f87f33fb2946381390d0d61c2bd2 100644 (file)
@@ -48,16 +48,20 @@ public:
 };
 
 namespace quantifiers {
+  //TODO: organize this more/review this, github issue #1163
+  //utilities
   class TermDb;
   class TermDbSygus;
   class FirstOrderModel;
-  //modules
+  class RelevantDomain;
+  class BvInverter;
+  class InstPropagator;
+  //modules, these are "subsolvers" of the quantifiers theory.
   class InstantiationEngine;
   class ModelEngine;
   class BoundedIntegers;
   class QuantConflictFind;
   class RewriteEngine;
-  class RelevantDomain;
   class QModelBuilder;
   class ConjectureGenerator;
   class CegInstantiation;
@@ -71,7 +75,6 @@ namespace quantifiers {
   class QuantDSplit;
   class QuantAntiSkolem;
   class EqualityInference;
-  class InstPropagator;
 }/* CVC4::theory::quantifiers */
 
 namespace inst {
@@ -108,6 +111,8 @@ private:
   QuantRelevance * d_quant_rel;
   /** relevant domain */
   quantifiers::RelevantDomain* d_rel_dom;
+  /** inversion utility for BV instantiation */
+  quantifiers::BvInverter * d_bv_invert;
   /** alpha equivalence */
   quantifiers::AlphaEquivalence * d_alpha_equiv;
   /** model builder */
@@ -225,6 +230,8 @@ public:
   Valuation& getValuation();
   /** get relevant domain */
   quantifiers::RelevantDomain* getRelevantDomain() { return d_rel_dom; }
+  /** get the BV inverter utility */
+  quantifiers::BvInverter * getBvInverter() { return d_bv_invert; }
   /** get quantifier relevance */
   QuantRelevance* getQuantifierRelevance() { return d_quant_rel; }
   /** get the model builder */
index cb6ed687e34d69e9b23c5ecd5ee3b131b4c3e007..a4bd40df5d6f7b9dff56497507d047d93716aa83 100644 (file)
@@ -89,11 +89,9 @@ TESTS =      \
        cbqi-sdlx-fixpoint-3-dd.smt2 \
        qbv-simp.smt2 \
        psyco-001-bv.smt2 \
-       bug822.smt2 
-
-# FIXME: solvable with --cbqi-bv
-#qbv-test-invert-mul.smt2 
-#qbv-simple-2vars-vo.smt2
+       bug822.smt2 \
+       qbv-test-invert-mul.smt2 \
+       qbv-simple-2vars-vo.smt2
 
 # regression can be solved with --finite-model-find --fmf-inst-engine
 # set3.smt2
index f38625fd8022a04533c7a14118616eb373a6e966..b6ae95fec475824ef146cb617054c301940661ae 100644 (file)
@@ -1,3 +1,5 @@
+; COMMAND-LINE: --cbqi-bv
+; EXPECT: sat
 (set-logic BV)
 (set-info :status sat)
 (declare-fun a () (_ BitVec 32))
@@ -6,7 +8,6 @@
 
 (assert (not (= a #x00000000)))
 
-; this is sensitive to variable ordering (try changing x and y)
 (assert (forall ((x (_ BitVec 32)) (y (_ BitVec 32))) (or 
 (not (= (bvmul x y) #x0000000A))
 (not (= (bvadd y a) #x00000010))
index bea19a0549621a60079e3a523c381a359b067044..235d353ef0b09a775508b20b64d1c128fb013376 100644 (file)
@@ -1,3 +1,5 @@
+; COMMAND-LINE: --cbqi-bv
+; EXPECT: sat
 (set-logic BV)
 (set-info :status sat)
 (declare-fun a () (_ BitVec 32))