// ∀a, i, b, j. ( a = b → i = j → W (a, i, R(b, j)) = a ) void Egraph::WoRAxiom( Enode * wor ) { assert( false ); Enode * a = wor->get1st( ); Enode * i = wor->get2nd( ); Enode * worElement = wor->get3rd( ); Enode * b = worElement->get1st( ); Enode * j = worElement->get2nd( ); assert( worElement->isDTypeArrayElement( ) ); assert( a->isDTypeArray( ) ); assert( i->isDTypeArrayIndex( ) ); assert( b->isDTypeArray( ) ); assert( j->isDTypeArrayIndex( ) ); // create term W(a,i,R(b,j)) Enode * select = mkSelect( b, j ); Enode * store = mkStore(a,i,select); // add clause IF a=b THEN IF i=j THEN W(a,i,R(b,j))=a // that is (NOT(a=b) OR NOT(i=j) OR W(a,i,R(b,j))=a) vector< Enode * > v; Enode * lit1 = mkNot(cons(mkEq(cons(a,cons(b))))); Enode * lit2 = mkNot(cons(mkEq(cons(i,cons(j))))); Enode * lit3 = mkEq(cons(store,cons(a))); v.push_back( lit1 ); v.push_back( lit2 ); v.push_back( lit3 ); #ifdef ARR_VERB cout << "Axiom WoR -> " << "(or " << lit1 << " " << lit2 << " " << lit3 << " )" << endl; #endif splitOnDemand( v, id ); handleArrayAssertedAtomTerm( a ); }
// ∀a, i, e, j, f. ( i = j → W ( W ( a, i, e ), j, f ) = W ( a, j, f ) ) void Egraph::WoWEqAxiom( Enode * wow ) { assert( false ); Enode * wowArray = wow->get1st( ); Enode * a = wowArray->get1st( ); Enode * i = wowArray->get2nd( ); Enode * e = wowArray->get3rd( ); Enode * j = wow->get2nd( ); Enode * f = wow->get3rd( ); assert( wowArray->isDTypeArray( ) ); assert( a->isDTypeArray( ) ); assert( i->isDTypeArrayIndex( ) ); assert( e->isDTypeArrayElement( ) ); assert( j->isDTypeArrayIndex( ) ); assert( f->isDTypeArrayElement( ) ); //i,j not coincident if( i != j ) { // create term W(a,j,f) Enode * store = mkStore( a, j, f ); #ifdef PRODUCE_PROOF if ( config.gconfig.print_inter > 0 ) { const uint64_t shared = getIPartitions( a ) & getIPartitions( j ) & getIPartitions( f ); // Mixed can't be one at this point assert( shared != 1 ); // Set AB-mixed partition if no intersection if ( shared == 0 ) setIPartitions( store, 1 ); // Otherwise they share something else setIPartitions( store, shared ); } #endif // add clause IF i=j THEN W(W(a,i,e),j,f)=W(a,j,f) // that is (NOT(i=j) OR W(W(a,i,e),j,f)=W(a,j,f)) vector< Enode * > v; Enode * lit1_pos = mkEq( cons( i, cons( j ) ) ); Enode * lit1 = mkNot( cons( lit1_pos ) ); #ifdef PRODUCE_PROOF if ( config.gconfig.print_inter > 0 ) { const uint64_t shared = getIPartitions( i ) & getIPartitions( j ); // Mixed can't be one at this point assert( shared != 1 ); // Set AB-mixed partition if no intersection if ( shared == 0 ) { setIPartitions( lit1_pos, 1 ); setIPartitions( lit1, 1 ); } // Otherwise they share something else { setIPartitions( lit1_pos, shared ); setIPartitions( lit1, shared ); } } #endif Enode * lit2 = mkEq( cons( wow, cons( store ) ) ); #ifdef PRODUCE_PROOF if ( config.gconfig.print_inter > 0 ) { const uint64_t shared = getIPartitions( wow ) & getIPartitions( store ); // Mixed can't be one at this point assert( shared != 1 ); // Set AB-mixed partition if no intersection if ( shared == 0 ) setIPartitions( lit2, 1 ); // Otherwise they share something else setIPartitions( lit2, shared ); } #endif v.push_back( lit1 ); v.push_back( lit2 ); #ifdef ARR_VERB cout << "Axiom WoW= -> " << "(or " << lit1 << " " << lit2 << " )" << endl; #endif splitOnDemand( v, id ); handleArrayAssertedAtomTerm( store ); } }
// // Sort a term if it commutes. // The following simplifications implemented // (should be refactored to separate methods?): // - `and': // - drop constant true terms // - convert an empty `and' to the constant term `true' // - convert an `and' containing `false' to a replicate `false' // - `or': // - drop constant false terms // - convert an empty `or' to a replicate `false' term // - convert an `or' containing `true' to a replicate `true' // // void Logic::simplify(SymRef& s, vec<PTRef>& args) { // First sort it #ifdef SORT_BOOLEANS if (sym_store[s].commutes()) #else if (sym_store[s].commutes() && !isAnd(s) && !isOr(s)) #endif sort(args, LessThan_PTRef()); if (!isBooleanOperator(s) && !isEquality(s)) return; int dropped_args = 0; bool replace = false; if (s == getSym_and()) { int i, j; PTRef p = PTRef_Undef; for (i = j = 0; i < args.size(); i++) { if (args[i] == getTerm_false()) { args.clear(); s = getSym_false(); #ifdef SIMPLIFY_DEBUG cerr << "and -> false" << endl; #endif return; } else if (args[i] != getTerm_true() && args[i] != p) { args[j++] = p = args[i]; } else { #ifdef SIMPLIFY_DEBUG cerr << "and -> drop" << endl; #endif } } dropped_args = i-j; if (dropped_args == args.size()) { s = getSym_true(); args.clear(); #ifdef SIMPLIFY_DEBUG cerr << "and -> true" << endl; #endif return; } else if (dropped_args == args.size() - 1) replace = true; else if (dropped_args > 0) args.shrink(dropped_args); } if (s == getSym_or()) { int i, j; PTRef p = PTRef_Undef; for (i = j = 0; i < args.size(); i++) { if (args[i] == getTerm_true()) { args.clear(); s = getSym_true(); #ifdef SIMPLIFY_DEBUG cerr << "or -> true" << endl; #endif return; } else if (args[i] != getTerm_false() && args[i] != p) { args[j++] = p = args[i]; } else { #ifdef SIMPLIFY_DEBUG cerr << "or -> drop" << endl; #endif } } dropped_args = i-j; if (dropped_args == args.size()) { s = getSym_false(); args.clear(); #ifdef SIMPLIFY_DEBUG cerr << "or -> false" << endl; #endif return; } else if (dropped_args == args.size() - 1) replace = true; else if (dropped_args > 0) args.shrink(dropped_args); } if (isEquality(s)) { assert(args.size() == 2); if (isBooleanOperator(s) && (args[0] == getTerm_true())) { Pterm& t = getPterm(args[1]); s = t.symb(); args.clear(); for (int i = 0; i < t.size(); i++) args.push(t[i]); #ifdef SIMPLIFY_DEBUG cerr << "eq -> second" << endl; #endif return; } else if (isBooleanOperator(s) && (args[0] == getTerm_false())) { PTRef old = args[1]; PTRef tr = mkNot(args[1]); Pterm& t = getPterm(tr); s = t.symb(); args.clear(); args.push(old); #ifdef SIMPLIFY_DEBUG cerr << "eq -> not second" << endl; #endif return; } else if (isBooleanOperator(s) && (args[1] == getTerm_true())) { args.clear(); Pterm& t = getPterm(args[0]); s = t.symb(); args.clear(); for (int i = 0; i < t.size(); i++) args.push(t[i]); #ifdef SIMPLIFY_DEBUG cerr << "eq -> first" << endl; #endif return; } else if (isBooleanOperator(s) && (args[1] == getTerm_false())) { PTRef old = args[0]; PTRef tr = mkNot(args[0]); Pterm& t = getPterm(tr); s = t.symb(); args.clear(); args.push(old); #ifdef SIMPLIFY_DEBUG cerr << "eq -> not first"<< endl; #endif return; } else if (args[0] == args[1]) { args.clear(); s = getSym_true(); #ifdef SIMPLIFY_DEBUG cerr << "eq -> true" << endl; #endif return; } else if (isBooleanOperator(s) && (args[0] == mkNot(args[1]))) { args.clear(); s = getSym_false(); #ifdef SIMPLIFY_DEBUG cerr << "eq -> false" << endl; #endif return; } } if (isNot(s)) { if (isTrue(args[0])) { args.clear(); s = getSym_false(); #ifdef SIMPLIFY_DEBUG cerr << "not -> false" << endl; #endif return; } if (isFalse(args[0])) { args.clear(); s = getSym_true(); #ifdef SIMPLIFY_DEBUG cerr << "not -> true" << endl; #endif return; } } // Others, to be implemented: // - distinct // - implies // - xor // - ite if (replace) { // Return whatever is the sole argument Pterm& t = getPterm(args[0]); s = t.symb(); args.clear(); for (int i = 0; i < t.size(); i++) args.push(t[i]); #ifdef SIMPLIFY_DEBUG cerr << " replace" << endl; #endif } }
// a != b → R( a, i_{a,b} ) = R( b, i_{a,b} ) void Egraph::ExtAxiom( Enode * a, Enode * b ) { assert( isDynamic( a ) ); assert( isDynamic( b ) ); Enode * as = dynamicToStatic( a ); Enode * bs = dynamicToStatic( b ); assert( isStatic( as ) ); assert( isStatic( bs ) ); assert( as->isDTypeArray( ) ); assert( bs->isDTypeArray( ) ); // create fresh index i_a,b for pair a,b char def_name[ 48 ]; sprintf( def_name, IND_STR, as->getId( ), bs->getId( ) ); const unsigned type = DTYPE_ARRAY_INDEX; if ( lookupSymbol( def_name ) == NULL ) newSymbol( def_name, type ); // Create new variable Enode * i = mkVar( def_name ); // Create two new selections Enode * select1 = mkSelect( as, i ); Enode * select2 = mkSelect( bs, i ); // Create new literals Enode * lit1 = mkEq( cons( as, cons( bs ) ) ); Enode * lit2_pos = mkEq( cons( select1, cons( select2 ) ) ); Enode * lit2 = mkNot( cons( lit2_pos ) ); #ifdef PRODUCE_PROOF if ( config.gconfig.print_inter > 0 ) { const uint64_t shared = getIPartitions( as ) & getIPartitions( bs ); // Mixed can't be one at this point assert( shared != 1 ); // Set AB-mixed partition if no intersection if ( shared == 0 ) { setIPartitions( i, 1 ); setIPartitions( select1, 1 ); setIPartitions( select2, 1 ); setIPartitions( lit1, 1 ); setIPartitions( lit2_pos, 1 ); setIPartitions( lit2, 1 ); } // Otherwise they share something else { setIPartitions( i, shared ); setIPartitions( select1, shared ); setIPartitions( select2, shared ); setIPartitions( lit1, shared ); setIPartitions( lit2_pos, shared ); setIPartitions( lit2, shared ); } } #endif vector< Enode * > v; v.push_back( lit1 ); v.push_back( lit2 ); #ifdef ARR_VERB cout << "Axiom Ext -> " << "( or " << lit1 << " " << lit2 << " )" << endl; #endif splitOnDemand( v, id ); handleArrayAssertedAtomTerm( select1 ); handleArrayAssertedAtomTerm( select2 ); // New contexts to propagate info about new index // Given R(a,new) look for all store users W(a,i,e) // and instantiate RoW over R(W(a,i,e),new) vector< Enode * > sela; vector< Enode * > stoa; vector< Enode * > selb; vector< Enode * > stob; Enode * select3 = NULL; // Act over a getUsers( a, sela, stoa ); for( size_t j = 0 ; j < stoa.size( ) ; j++ ) { assert( isDynamic( stoa[ j ] ) ); Enode * ss = dynamicToStatic( stoa[ j ] ); assert( isStatic( ss ) ); // Creation new select for each store user of a select3 = mkSelect( ss, i ); // RoW over new select handleArrayAssertedAtomTerm( select3 ); #ifdef PRODUCE_PROOF if ( config.gconfig.print_inter > 0 ) { const uint64_t shared = getIPartitions( ss ) & getIPartitions( i ); // Mixed can't be one at this point assert( shared != 1 ); // Set AB-mixed partition if no intersection if ( shared == 0 ) setIPartitions( select3, 1 ); // Otherwise they share something else setIPartitions( select3, shared ); } #endif } // Act over b getUsers( b, selb, stob ); for ( size_t j = 0 ; j < stob.size( ) ; j++ ) { assert( isDynamic( stoa[ j ] ) ); Enode * ss = dynamicToStatic( stob[ j ] ); assert( isStatic( ss ) ); // Creation new select for each store user of b select3 = mkSelect( ss, i ); #ifdef PRODUCE_PROOF if ( config.gconfig.print_inter > 0 ) { const uint64_t shared = getIPartitions( ss ) & getIPartitions( i ); // Mixed can't be one at this point assert( shared != 1 ); // Set AB-mixed partition if no intersection if ( shared == 0 ) setIPartitions( select3, 1 ); // Otherwise they share something else setIPartitions( select3, shared ); } #endif // RoW over new select handleArrayAssertedAtomTerm( select3 ); } }
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->isIff( ) && !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 ); 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 ); Enode * lhs = e->get1st( ); Enode * rhs = e->get2nd( ); Enode * leq = mkLeq( cons( lhs, cons( rhs ) ) ); LAExpression b( leq ); leq = b.toEnode( *this ); Enode * geq = mkGeq( cons( lhs, cons( rhs ) ) ); LAExpression c( geq ); geq = c.toEnode( *this ); 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 ); } 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; }