/********************* */ /*! \file trigger.cpp ** \verbatim ** Top contributors (to current version): ** Andrew Reynolds, Morgan Deters, Tim King ** This file is part of the CVC4 project. ** Copyright (c) 2009-2016 by the authors listed in the file AUTHORS ** in the top-level source directory) and their institutional affiliations. ** All rights reserved. See the file COPYING in the top-level source ** directory for licensing information.\endverbatim ** ** \brief Implementation of trigger class **/ #include "theory/quantifiers/trigger.h" #include "theory/quantifiers/candidate_generator.h" #include "theory/quantifiers/inst_match_generator.h" #include "theory/quantifiers/term_database.h" #include "theory/quantifiers_engine.h" #include "theory/theory_engine.h" #include "theory/uf/equality_engine.h" #include "util/hash.h" using namespace std; using namespace CVC4::kind; using namespace CVC4::context; namespace CVC4 { namespace theory { namespace inst { void TriggerTermInfo::init( Node q, Node n, int reqPol, Node reqPolEq ){ if( d_fv.empty() ){ quantifiers::TermDb::getVarContainsNode( q, n, d_fv ); } if( d_reqPol==0 ){ d_reqPol = reqPol; d_reqPolEq = reqPolEq; }else{ //determined a ground (dis)equality must hold or else q is a tautology? } } /** trigger class constructor */ Trigger::Trigger( QuantifiersEngine* qe, Node f, std::vector< Node >& nodes, int matchOption ) : d_quantEngine( qe ), d_f( f ) { d_nodes.insert( d_nodes.begin(), nodes.begin(), nodes.end() ); Trace("trigger") << "Trigger for " << f << ": " << std::endl; for( unsigned i=0; isetActiveAdd(true); } }else{ d_mg = new InstMatchGeneratorMulti( f, d_nodes, qe ); //d_mg = InstMatchGenerator::mkInstMatchGenerator( d_nodes, qe ); //d_mg->setActiveAdd(); } if( d_nodes.size()==1 ){ if( isSimpleTrigger( d_nodes[0] ) ){ ++(qe->d_statistics.d_triggers); }else{ ++(qe->d_statistics.d_simple_triggers); } }else{ Trace("multi-trigger") << "Trigger for " << f << ": " << std::endl; for( unsigned i=0; id_statistics.d_multi_triggers); } //Notice() << "Trigger : " << (*this) << " for " << f << std::endl; if( options::eagerInstQuant() ){ for( int i=0; i<(int)d_nodes.size(); i++ ){ Node op = qe->getTermDatabase()->getMatchOperator( d_nodes[i] ); qe->getTermDatabase()->registerTrigger( this, op ); } } Trace("trigger-debug") << "Finished making trigger." << std::endl; } Trigger::~Trigger() { if(d_mg != NULL) { delete d_mg; } } void Trigger::resetInstantiationRound(){ d_mg->resetInstantiationRound( d_quantEngine ); } void Trigger::reset( Node eqc ){ d_mg->reset( eqc, d_quantEngine ); } bool Trigger::getNextMatch( Node f, InstMatch& m ){ bool retVal = d_mg->getNextMatch( f, m, d_quantEngine ); return retVal; } bool Trigger::getMatch( Node f, Node t, InstMatch& m ){ //FIXME: this assumes d_mg is an inst match generator return ((InstMatchGenerator*)d_mg)->getMatch( f, t, m, d_quantEngine ); } int Trigger::addTerm( Node t ){ return d_mg->addTerm( d_f, t, d_quantEngine ); } int Trigger::addInstantiations( InstMatch& baseMatch ){ int addedLemmas = d_mg->addInstantiations( d_f, baseMatch, d_quantEngine ); if( addedLemmas>0 ){ Debug("inst-trigger") << "Added " << addedLemmas << " lemmas, trigger was "; for( int i=0; i<(int)d_nodes.size(); i++ ){ Debug("inst-trigger") << d_nodes[i] << " "; } Debug("inst-trigger") << std::endl; } return addedLemmas; } Trigger* Trigger::mkTrigger( QuantifiersEngine* qe, Node f, std::vector< Node >& nodes, int matchOption, bool keepAll, int trOption ){ std::vector< Node > trNodes; if( !keepAll ){ //only take nodes that contribute variables to the trigger when added std::vector< Node > temp; temp.insert( temp.begin(), nodes.begin(), nodes.end() ); std::map< Node, bool > vars; std::map< Node, std::vector< Node > > patterns; size_t varCount = 0; std::map< Node, std::vector< Node > > varContains; quantifiers::TermDb::getVarContains( f, temp, varContains ); for( unsigned i=0; igetTriggerDatabase()->getTrigger( trNodes ); if( t ){ if( trOption==TR_GET_OLD ){ //just return old trigger return t; }else{ return NULL; } } } Trigger* t = new Trigger( qe, f, trNodes, matchOption ); qe->getTriggerDatabase()->addTrigger( trNodes, t ); return t; } Trigger* Trigger::mkTrigger( QuantifiersEngine* qe, Node f, Node n, int matchOption, bool keepAll, int trOption ){ std::vector< Node > nodes; nodes.push_back( n ); return mkTrigger( qe, f, nodes, matchOption, keepAll, trOption ); } bool Trigger::isUsable( Node n, Node q ){ if( quantifiers::TermDb::getInstConstAttr(n)==q ){ if( isAtomicTrigger( n ) ){ for( unsigned i=0; i coeffs; if( isBooleanTermTrigger( n ) ){ return true; }else if( options::purifyTriggers() ){ Node x = getInversionVariable( n ); if( !x.isNull() ){ return true; } } } return false; }else{ return true; } } Node Trigger::getIsUsableEq( Node q, Node n ) { Assert( isRelationalTrigger( n ) ); for( unsigned i=0; i<2; i++) { if( isUsableEqTerms( q, n[i], n[1-i] ) ){ if( i==1 && ( n.getKind()==EQUAL || n.getKind()==IFF ) && !quantifiers::TermDb::hasInstConstAttr(n[0]) ){ return NodeManager::currentNM()->mkNode( n.getKind(), n[1], n[0] ); }else{ return n; } } } return Node::null(); } bool Trigger::isUsableEqTerms( Node q, Node n1, Node n2 ) { if( n1.getKind()==INST_CONSTANT ){ if( options::relationalTriggers() ){ if( !quantifiers::TermDb::hasInstConstAttr(n2) ){ return true; }else if( n2.getKind()==INST_CONSTANT ){ return true; } } }else if( isAtomicTrigger( n1 ) && isUsable( n1, q ) ){ if( options::relationalTriggers() && n2.getKind()==INST_CONSTANT && !quantifiers::TermDb::containsTerm( n1, n2 ) ){ return true; }else if( !quantifiers::TermDb::hasInstConstAttr(n2) ){ return true; } } return false; } Node Trigger::getIsUsableTrigger( Node n, Node q ) { bool pol = true; Trace("trigger-debug") << "Is " << n << " a usable trigger?" << std::endl; if( n.getKind()==NOT ){ pol = !pol; n = n[0]; } if( n.getKind()==INST_CONSTANT ){ return pol ? n : NodeManager::currentNM()->mkNode( IFF, n, NodeManager::currentNM()->mkConst( true ) ).notNode(); }else if( isRelationalTrigger( n ) ){ Node rtr = getIsUsableEq( q, n ); if( rtr.isNull() && n[0].getType().isReal() ){ //try to solve relation std::map< Node, Node > m; if( QuantArith::getMonomialSumLit(n, m) ){ for( std::map< Node, Node >::iterator it = m.begin(); it!=m.end(); ++it ){ bool trySolve = false; if( !it->first.isNull() ){ if( it->first.getKind()==INST_CONSTANT ){ trySolve = options::relationalTriggers(); }else if( isUsableTrigger( it->first, q ) ){ trySolve = true; } } if( trySolve ){ Trace("trigger-debug") << "Try to solve for " << it->first << std::endl; Node veq; if( QuantArith::isolate( it->first, m, veq, n.getKind() )!=0 ){ rtr = getIsUsableEq( q, veq ); } //either all solves will succeed or all solves will fail break; } } } } if( !rtr.isNull() ){ Trace("relational-trigger") << "Relational trigger : " << std::endl; Trace("relational-trigger") << " " << rtr << " (from " << n << ")" << std::endl; Trace("relational-trigger") << " in quantifier " << q << std::endl; Node rtr2 = pol ? rtr : rtr.negate(); Trace("relational-trigger") << " return : " << rtr2 << std::endl; return rtr2; } }else{ bool usable = quantifiers::TermDb::getInstConstAttr(n)==q && isAtomicTrigger( n ) && isUsable( n, q ); Trace("trigger-debug") << n << " usable : " << (quantifiers::TermDb::getInstConstAttr(n)==q) << " " << isAtomicTrigger( n ) << " " << isUsable( n, q ) << std::endl; if( usable ){ return pol ? n : NodeManager::currentNM()->mkNode( IFF, n, NodeManager::currentNM()->mkConst( true ) ).notNode(); } } return Node::null(); } bool Trigger::isUsableTrigger( Node n, Node q ){ Node nu = getIsUsableTrigger( n, q ); return !nu.isNull(); } bool Trigger::isAtomicTrigger( Node n ){ Kind k = n.getKind(); return ( k==APPLY_UF && !n.getOperator().getAttribute(NoMatchAttribute()) ) || ( k!=APPLY_UF && isAtomicTriggerKind( k ) ); } bool Trigger::isAtomicTriggerKind( Kind k ) { return k==APPLY_UF || k==SELECT || k==STORE || k==APPLY_CONSTRUCTOR || k==APPLY_SELECTOR_TOTAL || k==APPLY_TESTER || k==UNION || k==INTERSECTION || k==SUBSET || k==SETMINUS || k==MEMBER || k==SINGLETON; } bool Trigger::isRelationalTrigger( Node n ) { return isRelationalTriggerKind( n.getKind() ); } bool Trigger::isRelationalTriggerKind( Kind k ) { return k==EQUAL || k==IFF || k==GEQ; } bool Trigger::isCbqiKind( Kind k ) { return quantifiers::TermDb::isBoolConnective( k ) || k==PLUS || k==GEQ || k==EQUAL || k==MULT || k==APPLY_CONSTRUCTOR || k==APPLY_SELECTOR_TOTAL || k==APPLY_TESTER; } bool Trigger::isSimpleTrigger( Node n ){ Node t = n.getKind()==NOT ? n[0] : n; if( n.getKind()==IFF || n.getKind()==EQUAL ){ if( !quantifiers::TermDb::hasInstConstAttr( n[1] ) ){ t = n[0]; } } if( isAtomicTrigger( t ) ){ for( unsigned i=0; i& visited, std::map< Node, TriggerTermInfo >& tinfo, quantifiers::TriggerSelMode tstrt, std::vector< Node >& exclude, std::vector< Node >& added, bool pol, bool hasPol, bool epol, bool hasEPol ){ std::map< Node, Node >::iterator itv = visited.find( n ); if( itv==visited.end() ){ visited[ n ] = Node::null(); Trace("auto-gen-trigger-debug2") << "Collect pat terms " << n << " " << pol << " " << hasPol << " " << epol << " " << hasEPol << std::endl; bool retVal = false; if( n.getKind()!=FORALL && n.getKind()!=INST_CONSTANT ){ bool rec = true; Node nu; bool nu_single = false; if( n.getKind()!=NOT && std::find( exclude.begin(), exclude.end(), n )==exclude.end() ){ nu = getIsUsableTrigger( n, q ); if( !nu.isNull() ){ Assert( nu.getKind()!=NOT ); Trace("auto-gen-trigger-debug2") << "...found usable trigger : " << nu << std::endl; Node reqEq; if( nu.getKind()==IFF || nu.getKind()==EQUAL ){ if( isAtomicTrigger( nu[0] ) && !quantifiers::TermDb::hasInstConstAttr(nu[1]) ){ if( hasPol ){ reqEq = nu[1]; } nu = nu[0]; } } Assert( reqEq.isNull() || !quantifiers::TermDb::hasInstConstAttr( reqEq ) ); Assert( isUsableTrigger( nu, q ) ); //do not add if already excluded bool add = true; if( n!=nu ){ std::map< Node, Node >::iterator itvu = visited.find( nu ); if( itvu!=visited.end() && itvu->second.isNull() ){ add = false; } } if( add ){ Trace("auto-gen-trigger-debug2") << "...add usable trigger : " << nu << std::endl; visited[ nu ] = nu; tinfo[ nu ].init( q, nu, hasEPol ? ( epol ? 1 : -1 ) : 0, reqEq ); nu_single = tinfo[ nu ].d_fv.size()==q[0].getNumChildren(); retVal = true; if( tstrt==quantifiers::TRIGGER_SEL_MAX || ( tstrt==quantifiers::TRIGGER_SEL_MIN_SINGLE_MAX && !nu_single ) ){ rec = false; } } } } if( rec ){ Node nrec = nu.isNull() ? n : nu; std::vector< Node > added2; for( unsigned i=0; isecond.isNull() ){ return false; }else{ added.push_back( itv->second ); return true; } } } bool Trigger::isBooleanTermTrigger( Node n ) { if( n.getKind()==ITE ){ //check for boolean term converted to ITE if( n[0].getKind()==INST_CONSTANT && n[1].getKind()==CONST_BITVECTOR && n[2].getKind()==CONST_BITVECTOR ){ if( ((BitVectorType)n[1].getType().toType()).getSize()==1 && n[1].getConst().toInteger()==1 && n[2].getConst().toInteger()==0 ){ return true; } } } return false; } bool Trigger::isPureTheoryTrigger( Node n ) { if( n.getKind()==APPLY_UF || n.getKind()==VARIABLE || n.getKind()==SKOLEM ){ //|| !quantifiers::TermDb::hasInstConstAttr( n ) ){ return false; }else{ for( unsigned i=0; i& vars, std::vector< Node >& patTerms ) { if( !n.getType().isBoolean() && n.getKind()==APPLY_UF ){ if( std::find( patTerms.begin(), patTerms.end(), n )==patTerms.end() ){ bool hasVar = false; for( unsigned i=0; i& patTerms, quantifiers::TriggerSelMode tstrt, std::vector< Node >& exclude, std::map< Node, TriggerTermInfo >& tinfo, bool filterInst ){ std::map< Node, Node > visited; if( filterInst ){ //immediately do not consider any term t for which another term is an instance of t std::vector< Node > patTerms2; std::map< Node, TriggerTermInfo > tinfo2; collectPatTerms( q, n, patTerms2, quantifiers::TRIGGER_SEL_ALL, exclude, tinfo2, false ); std::vector< Node > temp; temp.insert( temp.begin(), patTerms2.begin(), patTerms2.end() ); quantifiers::TermDb::filterInstances( temp ); if( temp.size()!=patTerms2.size() ){ Trace("trigger-filter-instance") << "Filtered an instance: " << std::endl; Trace("trigger-filter-instance") << "Old: "; for( unsigned i=0; i added; collectPatTerms2( q, n, visited, tinfo, tstrt, exclude, added, true, true, false, true ); for( std::map< Node, TriggerTermInfo >::iterator it = tinfo.begin(); it != tinfo.end(); ++it ){ if( !visited[it->first].isNull() ){ patTerms.push_back( it->first ); } } } Node Trigger::getInversionVariable( Node n ) { if( n.getKind()==INST_CONSTANT ){ return n; }else if( n.getKind()==PLUS || n.getKind()==MULT ){ Node ret; for( unsigned i=0; i(); if( r!=Rational(-1) && r!=Rational(1) ){ Trace("var-trigger-debug") << "No : not integer coefficient " << n << std::endl; return Node::null(); } } */ } } return ret; }else{ Trace("var-trigger-debug") << "No : unsupported operator " << n << "." << std::endl; } return Node::null(); } Node Trigger::getInversion( Node n, Node x ) { if( n.getKind()==INST_CONSTANT ){ return x; }else if( n.getKind()==PLUS || n.getKind()==MULT ){ int cindex = -1; for( unsigned i=0; imkNode( MINUS, x, n[i] ); }else if( n.getKind()==MULT ){ Assert( n[i].isConst() ); if( x.getType().isInteger() ){ Node coeff = NodeManager::currentNM()->mkConst( n[i].getConst().abs() ); if( !n[i].getConst().abs().isOne() ){ x = NodeManager::currentNM()->mkNode( INTS_DIVISION_TOTAL, x, coeff ); } if( n[i].getConst().sgn()<0 ){ x = NodeManager::currentNM()->mkNode( UMINUS, x ); } }else{ Node coeff = NodeManager::currentNM()->mkConst( Rational(1) / n[i].getConst() ); x = NodeManager::currentNM()->mkNode( MULT, x, coeff ); } } x = Rewriter::rewrite( x ); }else{ Assert( cindex==-1 ); cindex = i; } } Assert( cindex!=-1 ); return getInversion( n[cindex], x ); } return Node::null(); } void Trigger::getTriggerVariables( Node icn, Node q, std::vector< Node >& t_vars ) { std::vector< Node > patTerms; std::map< Node, TriggerTermInfo > tinfo; //collect all patterns from icn std::vector< Node > exclude; collectPatTerms( q, icn, patTerms, quantifiers::TRIGGER_SEL_ALL, exclude, tinfo ); //collect all variables from all patterns in patTerms, add to t_vars for( unsigned i=0; i