// // Subroutine of explain // A step of explanation for x and y // void Egraph::expExplainAlongPath ( Enode * x, Enode * y ) { Enode * v = expHighestNode( x ); Enode * to = expHighestNode( y ); while ( v != to ) { Enode * p = v->getExpParent( ); assert( p != NULL ); Enode * r = v->getExpReason( ); // If it is not a congruence edge if ( r != NULL ) { if ( !isDup1( r ) ) { assert( r->isTerm( ) ); explanation.push_back( r ); storeDup1( r ); } } // Otherwise it is a congruence edge // This means that the edge is linking nodes // like (v)f(a1,...,an) (p)f(b1,...,bn), and that // a1,...,an = b1,...bn. For each pair ai,bi // we have therefore to compute the reasons else { assert( v->getCar( ) == p->getCar( ) ); assert( v->getArity( ) == p->getArity( ) ); expEnqueueArguments( v, p ); } #ifdef PRODUCE_PROOF if ( config.produce_inter > 0 && config.logic != QF_AX ) { cgraph.addCNode( v ); cgraph.addCNode( p ); cgraph.addCEdge( v, p, r ); } #endif expUnion( v, p ); v = expHighestNode( p ); } }
Enode * ExpandITEs::doit( Enode * formula ) { assert( formula ); list< Enode * > new_clauses; vector< Enode * > unprocessed_enodes; egraph.initDupMap1( ); unprocessed_enodes.push_back( formula ); // // Visit the DAG of the formula from the leaves to the root // while( !unprocessed_enodes.empty( ) ) { Enode * enode = unprocessed_enodes.back( ); // // Skip if the node has already been processed before // if ( egraph.valDupMap1( enode ) != NULL ) { unprocessed_enodes.pop_back( ); continue; } bool unprocessed_children = false; Enode * arg_list; for ( arg_list = enode->getCdr( ) ; arg_list != egraph.enil ; arg_list = arg_list->getCdr( ) ) { Enode * arg = arg_list->getCar( ); assert( arg->isTerm( ) ); // // Push only if it is unprocessed // if ( egraph.valDupMap1( arg ) == NULL ) { unprocessed_enodes.push_back( arg ); unprocessed_children = true; } } // // SKip if unprocessed_children // if ( unprocessed_children ) continue; unprocessed_enodes.pop_back( ); Enode * result = NULL; // // At this point, every child has been processed // char def_name[ 32 ]; if ( enode->isIte( ) ) { // // Retrieve arguments // Enode * i = egraph.valDupMap1( enode->get1st( ) ); Enode * t = egraph.valDupMap1( enode->get2nd( ) ); Enode * e = egraph.valDupMap1( enode->get3rd( ) ); Enode * not_i = egraph.mkNot( egraph.cons( i ) ); // // Generate variable symbol // sprintf( def_name, ITE_STR, enode->getId( ) ); Snode * sort = enode->getLastSort( ); egraph.newSymbol( def_name, sort ); // // Generate placeholder // result = egraph.mkVar( def_name ); // // Generate additional clauses // Enode * eq_then = egraph.mkEq( egraph.cons( result , egraph.cons( t ) ) ); Enode * eq_else = egraph.mkEq( egraph.cons( result , egraph.cons( e ) ) ); new_clauses.push_back( egraph.mkOr( egraph.cons( not_i , egraph.cons( eq_then ) ) ) ); new_clauses.push_back( egraph.mkOr( egraph.cons( i , egraph.cons( eq_else ) ) ) ); } else { result = egraph.copyEnodeEtypeTermWithCache( enode ); } assert( result ); assert( egraph.valDupMap1( enode ) == NULL ); egraph.storeDupMap1( enode, result ); } Enode * new_formula = egraph.valDupMap1( formula ); assert( new_formula ); egraph.doneDupMap1( ); new_clauses.push_back( new_formula ); return egraph.mkAnd( egraph.cons( new_clauses ) ); }
void Egraph::gatherInterfaceTerms( Enode * e ) { assert( config.sat_lazy_dtc != 0 ); assert( config.logic == QF_UFIDL || config.logic == QF_UFLRA ); assert( e ); if ( config.verbosity > 2 ) cerr << "# Egraph::Gathering interface terms" << endl; vector< Enode * > unprocessed_enodes; initDup1( ); unprocessed_enodes.push_back( e ); // // Visit the DAG of the term from the leaves to the root // while( !unprocessed_enodes.empty( ) ) { Enode * enode = unprocessed_enodes.back( ); // // Skip if the node has already been processed before // if ( isDup1( enode ) ) { unprocessed_enodes.pop_back( ); continue; } bool unprocessed_children = false; Enode * arg_list; for ( arg_list = enode->getCdr( ) ; arg_list != enil ; arg_list = arg_list->getCdr( ) ) { Enode * arg = arg_list->getCar( ); assert( arg->isTerm( ) ); // // Push only if it is unprocessed // if ( !isDup1( arg ) ) { unprocessed_enodes.push_back( arg ); unprocessed_children = true; } } // // SKip if unprocessed_children // if ( unprocessed_children ) continue; unprocessed_enodes.pop_back( ); // // At this point, every child has been processed // if ( enode->isUFOp( ) ) { // Retrieve arguments for ( Enode * arg_list = enode->getCdr( ) ; !arg_list->isEnil( ) ; arg_list = arg_list->getCdr( ) ) { Enode * arg = arg_list->getCar( ); // This is for sure an interface term if ( ( arg->isArithmeticOp( ) || arg->isConstant( ) ) && interface_terms_cache.insert( arg ).second ) { interface_terms.push_back( arg ); if ( config.verbosity > 2 ) cerr << "# Egraph::Added interface term: " << arg << endl; } // We add this variable to the potential // interface terms or to interface terms if // already seen in LA else if ( arg->isVar( ) || arg->isConstant( ) ) { if ( it_la.find( arg ) == it_la.end( ) ) it_uf.insert( arg ); else if ( interface_terms_cache.insert( arg ).second ) { interface_terms.push_back( arg ); if ( config.verbosity > 2 ) cerr << "# Egraph::Added interface term: " << arg << endl; } } } } if ( enode->isArithmeticOp( ) && !isRootUF( enode ) ) { // Retrieve arguments for ( Enode * arg_list = enode->getCdr( ) ; !arg_list->isEnil( ) ; arg_list = arg_list->getCdr( ) ) { Enode * arg = arg_list->getCar( ); // This is for sure an interface term if ( arg->isUFOp( ) && interface_terms_cache.insert( arg ).second ) { interface_terms.push_back( arg ); if ( config.verbosity > 2 ) cerr << "# Egraph::Added interface term: " << arg << endl; } // We add this variable to the potential // interface terms or to interface terms if // already seen in UF else if ( arg->isVar( ) || arg->isConstant( ) ) { if ( it_uf.find( arg ) == it_uf.end( ) ) it_la.insert( arg ); else if ( interface_terms_cache.insert( arg ).second ) { interface_terms.push_back( arg ); if ( config.verbosity > 2 ) cerr << "# Egraph::Added interface term: " << arg << endl; } } } } assert( !isDup1( enode ) ); storeDup1( enode ); } doneDup1( ); }
Enode * Egraph::canonizeDTC( Enode * formula, bool split_eqs ) { assert( config.sat_lazy_dtc != 0 ); assert( config.logic == QF_UFLRA || config.logic == QF_UFIDL ); list< Enode * > dtc_axioms; vector< Enode * > unprocessed_enodes; initDupMap1( ); unprocessed_enodes.push_back( formula ); // // Visit the DAG of the formula from the leaves to the root // while( !unprocessed_enodes.empty( ) ) { Enode * enode = unprocessed_enodes.back( ); // // Skip if the node has already been processed before // if ( valDupMap1( enode ) != NULL ) { unprocessed_enodes.pop_back( ); continue; } bool unprocessed_children = false; Enode * arg_list; for ( arg_list = enode->getCdr( ) ; arg_list != enil ; arg_list = arg_list->getCdr( ) ) { Enode * arg = arg_list->getCar( ); assert( arg->isTerm( ) ); // // Push only if it is unprocessed // if ( valDupMap1( arg ) == NULL ) { unprocessed_enodes.push_back( arg ); unprocessed_children = true; } } // // SKip if unprocessed_children // if ( unprocessed_children ) continue; unprocessed_enodes.pop_back( ); Enode * result = NULL; // // Replace arithmetic atoms with canonized version // if ( enode->isTAtom( ) && !enode->isUp( ) ) { // No need to do anything if node is purely UF if ( isRootUF( enode ) ) { if ( config.verbosity > 2 ) cerr << "# Egraph::Skipping canonization of " << enode << " as it's root is purely UF" << endl; result = enode; } else { LAExpression a( enode ); result = a.toEnode( *this ); #ifdef PRODUCE_PROOF const uint64_t partitions = getIPartitions( enode ); assert( partitions != 0 ); setIPartitions( result, partitions ); #endif if ( split_eqs && result->isEq( ) ) { #ifdef PRODUCE_PROOF if ( config.produce_inter > 0 ) opensmt_error2( "can't compute interpolant for equalities at the moment ", enode ); #endif LAExpression aa( enode ); Enode * e = aa.toEnode( *this ); #ifdef PRODUCE_PROOF assert( partitions != 0 ); setIPartitions( e, partitions ); #endif Enode * lhs = e->get1st( ); Enode * rhs = e->get2nd( ); Enode * leq = mkLeq( cons( lhs, cons( rhs ) ) ); LAExpression b( leq ); leq = b.toEnode( *this ); #ifdef PRODUCE_PROOF assert( partitions != 0 ); setIPartitions( leq, partitions ); #endif Enode * geq = mkGeq( cons( lhs, cons( rhs ) ) ); LAExpression c( geq ); geq = c.toEnode( *this ); #ifdef PRODUCE_PROOF assert( partitions != 0 ); setIPartitions( geq, partitions ); #endif Enode * not_e = mkNot( cons( enode ) ); Enode * not_l = mkNot( cons( leq ) ); Enode * not_g = mkNot( cons( geq ) ); // Add clause ( !x=y v x<=y ) Enode * c1 = mkOr( cons( not_e , cons( leq ) ) ); // Add clause ( !x=y v x>=y ) Enode * c2 = mkOr( cons( not_e , cons( geq ) ) ); // Add clause ( x=y v !x>=y v !x<=y ) Enode * c3 = mkOr( cons( enode , cons( not_l , cons( not_g ) ) ) ); // Add conjunction of clauses Enode * ax = mkAnd( cons( c1 , cons( c2 , cons( c3 ) ) ) ); dtc_axioms.push_back( ax ); result = enode; } } } // // If nothing have been done copy and simplify // if ( result == NULL ) result = copyEnodeEtypeTermWithCache( enode ); assert( valDupMap1( enode ) == NULL ); storeDupMap1( enode, result ); #ifdef PRODUCE_PROOF if ( config.produce_inter > 0 ) { // Setting partitions for result setIPartitions( result, getIPartitions( enode ) ); // Setting partitions for negation as well occ if atom if ( result->hasSortBool( ) ) { setIPartitions( mkNot( cons( result ) ) , getIPartitions( enode ) ); } } #endif } Enode * new_formula = valDupMap1( formula ); assert( new_formula ); doneDupMap1( ); if ( !dtc_axioms.empty( ) ) { dtc_axioms.push_back( new_formula ); new_formula = mkAnd( cons( dtc_axioms ) ); } return new_formula; }
bool Egraph::isPureUF( Enode * e ) { assert( config.sat_lazy_dtc != 0 ); assert( config.logic == QF_UFIDL || config.logic == QF_UFLRA ); assert( e ); vector< Enode * > unprocessed_enodes; initDup1( ); unprocessed_enodes.push_back( e ); // // Visit the DAG of the term from the leaves to the root // while( !unprocessed_enodes.empty( ) ) { Enode * enode = unprocessed_enodes.back( ); // // Skip if the node has already been processed before // if ( isDup1( enode ) ) { unprocessed_enodes.pop_back( ); continue; } bool unprocessed_children = false; Enode * arg_list; for ( arg_list = enode->getCdr( ) ; arg_list != enil ; arg_list = arg_list->getCdr( ) ) { Enode * arg = arg_list->getCar( ); assert( arg->isTerm( ) ); // // Push only if it is unprocessed // if ( !isDup1( arg ) ) { unprocessed_enodes.push_back( arg ); unprocessed_children = true; } } // // SKip if unprocessed_children // if ( unprocessed_children ) continue; unprocessed_enodes.pop_back( ); // // At this point, every child has been processed // if ( enode->isArithmeticOp( ) ) { doneDup1( ); return false; } assert( !isDup1( enode ) ); storeDup1( enode ); } doneDup1( ); return true; }
// // Performs the actual cnfization // bool Tseitin::cnfize( Enode * formula, map< enodeid_t, Enode * > & cnf_cache ) { (void)cnf_cache; assert( formula ); assert( !formula->isAnd( ) ); Enode * arg_def = egraph.valDupMap1( formula ); if ( arg_def != NULL ) { vector< Enode * > clause; clause.push_back( arg_def ); #ifdef PRODUCE_PROOF if ( config.produce_inter > 0 ) return solver.addSMTClause( clause, egraph.getIPartitions( formula ) ); #endif return solver.addSMTClause( clause ); } vector< Enode * > unprocessed_enodes; // Stack for unprocessed enodes unprocessed_enodes.push_back( formula ); // formula needs to be processed // // Visit the DAG of the formula from the leaves to the root // while( !unprocessed_enodes.empty( ) ) { Enode * enode = unprocessed_enodes.back( ); // // Skip if the node has already been processed before // if ( egraph.valDupMap1( enode ) != NULL ) { unprocessed_enodes.pop_back( ); continue; } bool unprocessed_children = false; Enode * arg_list; for ( arg_list = enode->getCdr( ) ; arg_list != egraph.enil ; arg_list = arg_list->getCdr( ) ) { Enode * arg = arg_list->getCar( ); assert( arg->isTerm( ) ); // // Push only if it is an unprocessed boolean operator // if ( enode->isBooleanOperator( ) && egraph.valDupMap1( arg ) == NULL ) { unprocessed_enodes.push_back( arg ); unprocessed_children = true; } // // If it is an atom (either boolean or theory) just // store it in the cache // else if ( arg->isAtom( ) ) { egraph.storeDupMap1( arg, arg ); } } // // SKip if unprocessed_children // if ( unprocessed_children ) continue; unprocessed_enodes.pop_back( ); Enode * result = NULL; // // At this point, every child has been processed // // // Do the actual cnfization, according to the node type // char def_name[ 32 ]; if ( enode->isLit( ) ) { result = enode; } else if ( enode->isNot( ) ) { Enode * arg_def = egraph.valDupMap1( enode->get1st( ) ); assert( arg_def ); result = egraph.mkNot( egraph.cons( arg_def ) ); // Toggle the literal } else { Enode * arg_def = NULL; Enode * new_arg_list = egraph.copyEnodeEtypeListWithCache( enode->getCdr( ) ); // // If the enode is not top-level it needs a definition // if ( formula != enode ) { sprintf( def_name, CNF_STR, formula->getId( ), enode->getId( ) ); egraph.newSymbol( def_name, sstore.mkBool( ) ); arg_def = egraph.mkVar( def_name ); #ifdef PRODUCE_PROOF if ( config.produce_inter > 0 ) { // Tag Positive and negative literals egraph.tagIFormula( arg_def , egraph.getIPartitions( enode ) ); egraph.tagIFormula( egraph.mkNot( egraph.cons( arg_def ) ) , egraph.getIPartitions( enode ) ); } #endif } #ifdef PRODUCE_PROOF uint64_t partitions = 0; if ( config.produce_inter > 0 ) { partitions = egraph.getIPartitions( enode ); assert( partitions != 0 ); } #endif // // Handle boolean operators // if ( enode->isAnd( ) ) cnfizeAnd( new_arg_list, arg_def #ifdef PRODUCE_PROOF , partitions #endif ); else if ( enode->isOr( ) ) cnfizeOr( new_arg_list, arg_def #ifdef PRODUCE_PROOF , partitions #endif ); else if ( enode->isIff( ) ) cnfizeIff( new_arg_list, arg_def #ifdef PRODUCE_PROOF , partitions #endif ); else if ( enode->isXor( ) ) cnfizeXor( new_arg_list, arg_def #ifdef PRODUCE_PROOF , partitions #endif ); else { opensmt_error2( "operator not handled ", enode->getCar( ) ); } if ( arg_def != NULL ) result = arg_def; } assert( egraph.valDupMap1( enode ) == NULL ); egraph.storeDupMap1( enode, result ); } if ( formula->isNot( ) ) { // Retrieve definition of argument Enode * arg_def = egraph.valDupMap1( formula->get1st( ) ); assert( arg_def ); vector< Enode * > clause; clause.push_back( toggleLit( arg_def ) ); #ifdef PRODUCE_PROOF if ( config.produce_inter > 0 ) return solver.addSMTClause( clause, egraph.getIPartitions( formula ) ); #endif return solver.addSMTClause( clause ); } return true; }
// // Rewrite formula with maximum arity for operators // Enode * Cnfizer::rewriteMaxArity( Enode * formula, map< enodeid_t, int > & enodeid_to_incoming_edges ) { assert( formula ); vector< Enode * > unprocessed_enodes; // Stack for unprocessed enodes unprocessed_enodes.push_back( formula ); // formula needs to be processed map< enodeid_t, Enode * > cache; // Cache of processed nodes // // Visit the DAG of the formula from the leaves to the root // while( !unprocessed_enodes.empty( ) ) { Enode * enode = unprocessed_enodes.back( ); // // Skip if the node has already been processed before // if ( cache.find( enode->getId( ) ) != cache.end( ) ) { unprocessed_enodes.pop_back( ); continue; } bool unprocessed_children = false; Enode * arg_list; for ( arg_list = enode->getCdr( ) ; arg_list != egraph.enil ; arg_list = arg_list->getCdr( ) ) { Enode * arg = arg_list->getCar( ); assert( arg->isTerm( ) ); // // Push only if it is an unprocessed boolean operator // if ( arg->isBooleanOperator( ) && cache.find( arg->getId( ) ) == cache.end( ) ) { unprocessed_enodes.push_back( arg ); unprocessed_children = true; } // // If it is an atom (either boolean or theory) just // store it in the cache // else if ( arg->isAtom( ) ) { cache.insert( make_pair( arg->getId( ), arg ) ); } } // // SKip if unprocessed_children // if ( unprocessed_children ) continue; unprocessed_enodes.pop_back( ); Enode * result = NULL; // // At this point, every child has been processed // assert ( enode->isBooleanOperator( ) ); if ( enode->isAnd( ) || enode->isOr ( ) ) { assert( enode->isAnd( ) || enode->isOr( ) ); // // Construct the new lists for the operators // result = mergeEnodeArgs( enode, cache, enodeid_to_incoming_edges ); } else { result = enode; } assert( result ); assert( cache.find( enode->getId( ) ) == cache.end( ) ); cache[ enode->getId( ) ] = result; } Enode * top_enode = cache[ formula->getId( ) ]; return top_enode; }
// // Ackermann related routines // void Egraph::retrieveFunctionApplications( Enode * formula ) { assert( formula ); vector< Enode * > unprocessed_enodes; initDup1( ); unprocessed_enodes.push_back( formula ); // // Visit the DAG of the formula from the leaves to the root // while( !unprocessed_enodes.empty( ) ) { Enode * enode = unprocessed_enodes.back( ); // // Skip if the node has already been processed before // if ( isDup1( enode ) ) { unprocessed_enodes.pop_back( ); continue; } bool unprocessed_children = false; Enode * arg_list; for ( arg_list = enode->getCdr( ) ; arg_list != enil ; arg_list = arg_list->getCdr( ) ) { Enode * arg = arg_list->getCar( ); assert( arg->isTerm( ) ); // // Push only if it is unprocessed // if ( !isDup1( arg ) ) { unprocessed_enodes.push_back( arg ); unprocessed_children = true; } } // // SKip if unprocessed_children // if ( unprocessed_children ) continue; unprocessed_enodes.pop_back( ); // // At this point, every child has been processed // if ( enode->isUf( ) || enode->isUp( ) ) { if ( uf_to_appl_cache[ enode->getCar( ) ].insert( enode ).second ) { uf_to_appl[ enode->getCar( ) ].push_back( enode ); undo_stack_oper.push_back( ACK_APPL ); undo_stack_term.push_back( enode ); } } assert( !isDup1( enode ) ); storeDup1( enode ); } doneDup1( ); }
void Egraph::getInterfaceVars( Enode * e, set< Enode * > & iv ) { assert( config.produce_inter != 0 ); assert( config.sat_lazy_dtc != 0 ); assert( config.logic == QF_UFIDL || config.logic == QF_UFLRA ); assert( e ); vector< Enode * > unprocessed_enodes; initDup1( ); unprocessed_enodes.push_back( e ); // // Visit the DAG of the term from the leaves to the root // while( !unprocessed_enodes.empty( ) ) { Enode * enode = unprocessed_enodes.back( ); // // Skip if the node has already been processed before // if ( isDup1( enode ) ) { unprocessed_enodes.pop_back( ); continue; } bool unprocessed_children = false; Enode * arg_list; for ( arg_list = enode->getCdr( ) ; arg_list != enil ; arg_list = arg_list->getCdr( ) ) { Enode * arg = arg_list->getCar( ); assert( arg->isTerm( ) ); // // Push only if it is unprocessed // if ( !isDup1( arg ) ) { unprocessed_enodes.push_back( arg ); unprocessed_children = true; } } // // SKip if unprocessed_children // if ( unprocessed_children ) continue; unprocessed_enodes.pop_back( ); if ( enode->isVar( ) && interface_terms_cache.find( enode ) != interface_terms_cache.end( ) ) iv.insert( enode ); assert( !isDup1( enode ) ); storeDup1( enode ); } doneDup1( ); }