#include "theory/quantifiers/first_order_model.h"
#include "theory/quantifiers/model_engine.h"
#include "theory/quantifiers/term_database.h"
+#include "theory/quantifiers/options.h"
using namespace CVC4;
using namespace std;
using namespace CVC4::theory::quantifiers;
using namespace CVC4::kind;
+
+BoundedIntegers::RangeModel::RangeModel(BoundedIntegers * bi, Node r, context::Context* c, bool isProxy) : d_bi(bi),
+ d_range(r), d_curr_max(-1), d_range_assertions(c), d_has_range(c,false), d_curr_range(c,-1) {
+ if( options::fmfBoundIntLazy() ){
+ d_proxy_range = isProxy ? r : NodeManager::currentNM()->mkSkolem( "pbir", r.getType() );
+ }else{
+ d_proxy_range = r;
+ }
+ if( !isProxy ){
+ Trace("bound-int") << "Introduce proxy " << d_proxy_range << " for " << d_range << std::endl;
+ }
+}
+
void BoundedIntegers::RangeModel::initialize() {
//add initial split lemma
- Node ltr = NodeManager::currentNM()->mkNode( LT, d_range, NodeManager::currentNM()->mkConst( Rational(0) ) );
+ Node ltr = NodeManager::currentNM()->mkNode( LT, d_proxy_range, NodeManager::currentNM()->mkConst( Rational(0) ) );
ltr = Rewriter::rewrite( ltr );
Trace("bound-int-lemma") << " *** bound int: initial split on " << ltr << std::endl;
d_bi->getQuantifiersEngine()->getOutputChannel().split( ltr );
bool needsRange = true;
for( std::map< Node, int >::iterator it = d_lit_to_range.begin(); it != d_lit_to_range.end(); ++it ){
if( d_range_assertions.find( it->first )==d_range_assertions.end() ){
+ Trace("bound-int-debug") << "Does not need range because of " << it->first << std::endl;
needsRange = false;
break;
}
int newBound = d_curr_max;
Trace("bound-int-proc") << "Allocate range bound " << newBound << " for " << d_range << std::endl;
//TODO: newBound should be chosen in a smarter way
- Node ltr = NodeManager::currentNM()->mkNode( LEQ, d_range, NodeManager::currentNM()->mkConst( Rational(newBound) ) );
+ Node ltr = NodeManager::currentNM()->mkNode( LEQ, d_proxy_range, NodeManager::currentNM()->mkConst( Rational(newBound) ) );
ltr = Rewriter::rewrite( ltr );
Trace("bound-int-lemma") << " *** bound int: split on " << ltr << std::endl;
d_bi->getQuantifiersEngine()->getOutputChannel().split( ltr );
return Node::null();
}
+bool BoundedIntegers::RangeModel::proxyCurrentRange() {
+ //Trace("model-engine") << "Range(" << d_range << ") currently is " << d_curr_max.get() << std::endl;
+ if( d_range!=d_proxy_range ){
+ //int curr = d_curr_range.get();
+ int curr = d_curr_max;
+ if( d_ranges_proxied.find( curr )==d_ranges_proxied.end() ){
+ d_ranges_proxied[curr] = true;
+ Assert( d_range_literal.find( curr )!=d_range_literal.end() );
+ Node lem = NodeManager::currentNM()->mkNode( IFF, d_range_literal[curr].negate(),
+ NodeManager::currentNM()->mkNode( LEQ, d_range, NodeManager::currentNM()->mkConst( Rational(curr) ) ) );
+ Trace("bound-int-lemma") << "*** bound int : proxy lemma : " << lem << std::endl;
+ d_bi->getQuantifiersEngine()->addLemma( lem );
+ return true;
+ }
+ }
+ return false;
+}
+
BoundedIntegers::BoundedIntegers(context::Context* c, QuantifiersEngine* qe) :
QuantifiersModule(qe), d_assertions(c){
}
void BoundedIntegers::check( Theory::Effort e ) {
-
+ if( e==Theory::EFFORT_LAST_CALL ){
+ bool addedLemma = false;
+ //make sure proxies are up-to-date with range
+ for( unsigned i=0; i<d_ranges.size(); i++) {
+ if( d_rms[d_ranges[i]]->proxyCurrentRange() ){
+ addedLemma = true;
+ }
+ }
+ if( addedLemma ){
+ d_quantEngine->flushLemmas( &d_quantEngine->getOutputChannel() );
+ }
+ }
}
for( unsigned i=0; i<d_set[f].size(); i++) {
Node v = d_set[f][i];
Node r = d_range[f][v];
+ bool isProxy = false;
if( r.hasBoundVar() ){
//introduce a new bound
Node new_range = NodeManager::currentNM()->mkSkolem( "bir", r.getType(), "bound for term" );
d_nground_range[f][v] = d_range[f][v];
d_range[f][v] = new_range;
r = new_range;
+ isProxy = true;
}
if( r.getKind()!=CONST_RATIONAL ){
if( std::find(d_ranges.begin(), d_ranges.end(), r)==d_ranges.end() ){
Trace("bound-int") << "For " << v << ", bounded Integer Module will try to minimize : " << r << " " << r.getKind() << std::endl;
d_ranges.push_back( r );
- d_rms[r] = new RangeModel(this, r, d_quantEngine->getSatContext() );
+ d_rms[r] = new RangeModel(this, r, d_quantEngine->getSatContext(), isProxy );
d_rms[r]->initialize();
}
}
bool BoundedIntegers::isGroundRange(Node f, Node v) {
return isBoundVar(f,v) && !getLowerBound(f,v).hasBoundVar() && !getUpperBound(f,v).hasBoundVar();
}
+
private:
BoundedIntegers * d_bi;
void allocateRange();
+ Node d_proxy_range;
public:
- RangeModel(BoundedIntegers * bi, Node r, context::Context* c) : d_bi(bi),
- d_range(r), d_curr_max(-1), d_range_assertions(c), d_has_range(c,false), d_curr_range(c,-1) {}
+ RangeModel(BoundedIntegers * bi, Node r, context::Context* c, bool isProxy);
Node d_range;
int d_curr_max;
std::map< int, Node > d_range_literal;
NodeBoolMap d_range_assertions;
context::CDO< bool > d_has_range;
context::CDO< int > d_curr_range;
+ std::map< int, bool > d_ranges_proxied;
void initialize();
void assertNode(Node n);
Node getNextDecisionRequest();
+ bool proxyCurrentRange();
};
private:
//information for minimizing ranges
--- /dev/null
+; COMMAND-LINE: --fmf-bound-int-lazy
+; EXPECT: unsat
+(set-option :incremental "false")
+(set-info :status unsat)
+(set-logic ALL_SUPPORTED)
+(declare-fun Y () String)
+(set-info :notes "ufP_1 is uf type conv P")
+(declare-fun ufP_1 (Int) Int)
+(set-info :notes "ufM_2 is uf type conv M")
+(declare-fun ufM_2 (Int) Int)
+(declare-fun z1_3 () String)
+(declare-fun z2_4 () String)
+(declare-fun z3_5 () String)
+(declare-fun V_253 () String)
+(declare-fun V_254 () String)
+
+(assert (or (= Y "1") (= Y "0")))
+(assert (>= (ufP_1 0) 32))
+(assert
+
+(forall ((V_243 Int))
+(or
+(not (and (>= V_243 0) (>= (+ (str.len Y) (* (- 1) V_243)) 1)))
+(and
+(or (not (= (str.len Y) (+ 1 V_243))) (= (ufP_1 V_243) (ufM_2 V_243)))
+(not (>= (ufM_2 V_243) 10))
+(not (or (not (= (str.len Y) (+ 1 V_243 (str.len V_253)))) (not (= Y (str.++ V_253 (ite (= (ufM_2 V_243) 0) "0" "1") V_254)))) ))) ))
+
+(check-sat)