double nlopt_eval_enode(const double* x, void * extra) { auto extra_info = static_cast<tuple<Enode *, box const &, bool> *>(extra); Enode * e = get<0>(*extra_info); box const & b = get<1>(*extra_info); bool const polarity = get<2>(*extra_info); unordered_map<Enode *, double> var_map; unsigned i = 0; for (Enode * e : b.get_vars()) { if (e->isForallVar()) { var_map.emplace(e, x[i]); i++; } else { var_map.emplace(e, b[e].mid()); } } try { double const ret1 = eval_enode(e->get1st(), var_map); double const ret2 = eval_enode(e->get2nd(), var_map); double ret = 0; if (e->isLt() || e->isLeq() || e->isEq()) { ret = ret1 - ret2; } else if (e->isGt() || e->isGeq()) { ret = ret2 - ret1; } else if (e->isEq()) { throw runtime_error("nlopt_obj: something is wrong."); } if (!polarity) { ret = - ret; } return ret; } catch (exception & e) { DREAL_LOG_FATAL << "Exception in nlopt_eval_enode: " << e.what() << endl; throw e; } }
void nlopt_fill_gradient(const double * x, double * grad, void * extra) { auto extra_info = static_cast<tuple<Enode *, box const &, bool> *>(extra); Enode * e = get<0>(*extra_info); box const & b = get<1>(*extra_info); bool const polarity = get<2>(*extra_info); unordered_map<Enode *, double> var_map; unsigned i = 0; vector<Enode*> forall_var_vec; for (Enode * e : b.get_vars()) { if (e->isForallVar()) { var_map.emplace(e, x[i]); i++; forall_var_vec.push_back(e); } else { var_map.emplace(e, b[e].mid()); } } i = 0; for (Enode * var : forall_var_vec) { double deriv_i = deriv_enode(e->get1st(), var, var_map) - deriv_enode(e->get2nd(), var, var_map); if (e->isGt() || e->isGeq()) { deriv_i = - deriv_i; } if (!polarity) { deriv_i = - deriv_i; } grad[i] = deriv_i; i++; } }
// ∀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 ); }
void SimpSMTSolver::getDLVars( Enode * e, bool negate, Enode ** x, Enode ** y ) { assert( config.sat_preprocess_theory != 0 ); assert( e->isLeq( ) ); Enode * lhs = e->get1st( ); Enode * rhs = e->get2nd( ); (void)rhs; assert( lhs->isMinus( ) ); assert( rhs->isConstant( ) || ( rhs->isUminus( ) && rhs->get1st( )->isConstant( ) ) ); *x = lhs->get1st( ); *y = lhs->get2nd( ); if ( negate ) { Enode *tmp = *x; *x = *y; *y = tmp; } }
//∀a, i, e, j, f i != j → W (W (a, i, e), j, f ) = W (W (a, j, f ), i, e) void Egraph::WoWNeqAxiom( 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( ) ); // Case i, j not coincident if( i != j ) { // create term W(W(a,j,f),i,e) Enode * store1 = mkStore(a,j,f); Enode * store2 = mkStore(store1,i,e); // add clause IF i!=j THEN W(W(a,i,e),j,f)=W(W(a,j,f),i,e) // that is (i=j OR W(W(a,i,e),j,f)=W(W(a,j,f),i,e)) vector< Enode * > v; Enode * lit1 = mkEq(cons(i,cons(j))); Enode * lit2 = mkEq(cons(wow,cons(store2))); v.push_back( lit1 ); v.push_back( lit2 ); #ifdef ARR_VERB cout << "Axiom WoW!= -> " << "(or " << lit1 << " " << lit2 << " )" << endl; #endif splitOnDemand( v, id ); handleArrayAssertedAtomTerm(store2); } }
map<Enode *, bool> CoreSMTSolver::getBoolModel() { map<Enode *, bool> ret; for (int i = 0; i < trail.size(); i++) { Lit const & l = trail[i]; Var const v = var(l); if (v >= 2) { Enode * e = theory_handler->varToEnode(v); bool p = value(l) == l_True; if (e->isNot()) { e = e->get1st(); p = !p; } if (e->isVar()) { if (sign(l)) { p = !p; } ret.emplace(e, p); } } } return ret; }
box refine_CE_with_nlopt_core(box counterexample, vector<Enode*> const & opt_ctrs, vector<Enode*> const & side_ctrs) { // Plug-in `a` into the constraint and optimize `b` in the counterexample `M` by solving: // // ∃ y_opt ∈ I_y. ∀ y ∈ I_y. f(a, y_opt) >= f(a, y) — (2) // // using local optimizer (i.e. nlopt). // Let `M’ = (a, b_opt)` be a model for (2). DREAL_LOG_DEBUG << "================================" << endl; DREAL_LOG_DEBUG << " Before Refinement " << endl; DREAL_LOG_DEBUG << "================================" << endl; DREAL_LOG_DEBUG << counterexample << endl; DREAL_LOG_DEBUG << "================================" << endl; static bool initialized = false; static vector<double> lb, ub, init; init.clear(); for (Enode * e : counterexample.get_vars()) { if (e->isForallVar()) { if (!initialized) { lb.push_back(e->getDomainLowerBound()); ub.push_back(e->getDomainUpperBound()); } init.push_back(counterexample[e].mid()); DREAL_LOG_DEBUG << lb.back() << " <= " << init.back() << " <= " << ub.back() << endl; } } auto const n = init.size(); static nlopt::opt opt(nlopt::LD_SLSQP, n); if (!initialized) { opt.set_lower_bounds(lb); opt.set_upper_bounds(ub); // set tollerance // TODO(soonhok): set precision // opt.set_xtol_rel(0.0001); opt.set_xtol_abs(0.001); opt.set_maxtime(0.01); initialized = true; } opt.remove_equality_constraints(); opt.remove_inequality_constraints(); // set objective function vector<tuple<Enode *, box const &, bool> *> extra_vec; Enode * e = opt_ctrs[0]; bool polarity = false; while (e->isNot()) { e = e->get1st(); polarity = !polarity; } auto extra = new tuple<Enode *, box const &, bool>(e, counterexample, polarity); extra_vec.push_back(extra); opt.set_min_objective(nlopt_obj, extra); opt.add_inequality_constraint(nlopt_side_condition, extra); DREAL_LOG_DEBUG << "objective function is added: " << e << endl; // set side conditions for (Enode * e : side_ctrs) { bool polarity = false; while (e->isNot()) { e = e->get1st(); polarity = !polarity; } auto extra = new tuple<Enode *, box const &, bool>(e, counterexample, polarity); extra_vec.push_back(extra); DREAL_LOG_DEBUG << "refine_counterexample_with_nlopt: Side condition is added: " << e << endl; if (e->isEq()) { opt.add_equality_constraint(nlopt_side_condition, extra); } else if (e->isLt() || e->isLeq() || e->isGt() || e->isGeq()) { opt.add_inequality_constraint(nlopt_side_condition, extra); } } try { vector<double> output = opt.optimize(init); unsigned i = 0; for (Enode * e : counterexample.get_vars()) { if (e->isForallVar()) { counterexample[e] = output[i]; i++; } } } catch (nlopt::roundoff_limited & e) { } catch (std::runtime_error & e) { DREAL_LOG_DEBUG << e.what() << endl; } for (auto extra : extra_vec) { delete extra; } DREAL_LOG_DEBUG << "================================" << endl; DREAL_LOG_DEBUG << " After Refinement " << endl; DREAL_LOG_DEBUG << "================================" << endl; DREAL_LOG_DEBUG << counterexample << endl; DREAL_LOG_DEBUG << "================================" << endl; return counterexample; }
// ∀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 ); } }
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 ) ); }
double eval_enode(Enode * const e, unordered_map<Enode*, double> const & var_map) { if (e->isVar()) { auto const it = var_map.find(e); if (it == var_map.cend()) { throw runtime_error("variable not found"); } else { // Variable is found in var_map return it->second; } } else if (e->isConstant()) { double const v = e->getValue(); return v; } else if (e->isSymb()) { throw runtime_error("eval_enode: Symb"); } else if (e->isNumb()) { throw runtime_error("eval_enode: Numb"); } else if (e->isTerm()) { assert(e->getArity() >= 1); enodeid_t id = e->getCar()->getId(); double ret = 0.0; Enode * tmp = e; switch (id) { case ENODE_ID_PLUS: ret = eval_enode(tmp->get1st(), var_map); tmp = tmp->getCdr()->getCdr(); // e is pointing to the 2nd arg while (!tmp->isEnil()) { ret = ret + eval_enode(tmp->getCar(), var_map); tmp = tmp->getCdr(); } return ret; case ENODE_ID_MINUS: ret = eval_enode(tmp->get1st(), var_map); tmp = tmp->getCdr()->getCdr(); // e is pointing to the 2nd arg while (!tmp->isEnil()) { ret = ret - eval_enode(tmp->getCar(), var_map); tmp = tmp->getCdr(); } return ret; case ENODE_ID_UMINUS: ret = eval_enode(tmp->get1st(), var_map); assert(tmp->getArity() == 1); return (- ret); case ENODE_ID_TIMES: ret = eval_enode(tmp->get1st(), var_map); tmp = tmp->getCdr()->getCdr(); // e is pointing to the 2nd arg while (!tmp->isEnil()) { ret = ret * eval_enode(tmp->getCar(), var_map); tmp = tmp->getCdr(); } return ret; case ENODE_ID_DIV: ret = eval_enode(tmp->get1st(), var_map); tmp = tmp->getCdr()->getCdr(); // e is pointing to the 2nd arg while (!tmp->isEnil()) { ret = ret / eval_enode(tmp->getCar(), var_map); tmp = tmp->getCdr(); } return ret; case ENODE_ID_ACOS: assert(e->getArity() == 1); return acos(eval_enode(e->get1st(), var_map)); case ENODE_ID_ASIN: assert(e->getArity() == 1); return asin(eval_enode(e->get1st(), var_map)); case ENODE_ID_ATAN: assert(e->getArity() == 1); return atan(eval_enode(e->get1st(), var_map)); case ENODE_ID_ATAN2: assert(e->getArity() == 2); return atan2(eval_enode(e->get1st(), var_map), eval_enode(e->get2nd(), var_map)); case ENODE_ID_MIN: assert(e->getArity() == 2); return fmin(eval_enode(e->get1st(), var_map), eval_enode(e->get2nd(), var_map)); case ENODE_ID_MAX: assert(e->getArity() == 2); return fmax(eval_enode(e->get1st(), var_map), eval_enode(e->get2nd(), var_map)); case ENODE_ID_MATAN: assert(e->getArity() == 1); throw runtime_error("eval_enode: MATAN"); case ENODE_ID_SAFESQRT: assert(e->getArity() == 1); throw runtime_error("eval_enode: SAFESQRT"); case ENODE_ID_SQRT: assert(e->getArity() == 1); return sqrt(eval_enode(e->get1st(), var_map)); case ENODE_ID_EXP: assert(e->getArity() == 1); return exp(eval_enode(e->get1st(), var_map)); case ENODE_ID_LOG: assert(e->getArity() == 1); return log(eval_enode(e->get1st(), var_map)); case ENODE_ID_POW: assert(e->getArity() == 2); return pow(eval_enode(e->get1st(), var_map), eval_enode(e->get2nd(), var_map)); case ENODE_ID_ABS: assert(e->getArity() == 1); return fabs(eval_enode(e->get1st(), var_map)); case ENODE_ID_SIN: assert(e->getArity() == 1); return sin(eval_enode(e->get1st(), var_map)); case ENODE_ID_COS: assert(e->getArity() == 1); return cos(eval_enode(e->get1st(), var_map)); case ENODE_ID_TAN: assert(e->getArity() == 1); return tan(eval_enode(e->get1st(), var_map)); case ENODE_ID_SINH: assert(e->getArity() == 1); return sinh(eval_enode(e->get1st(), var_map)); case ENODE_ID_COSH: assert(e->getArity() == 1); return cosh(eval_enode(e->get1st(), var_map)); case ENODE_ID_TANH: assert(e->getArity() == 1); return tanh(eval_enode(e->get1st(), var_map)); default: throw runtime_error("eval_enode: Unknown Term"); } } else if (e->isList()) { throw runtime_error("eval_enode: List"); } else if (e->isDef()) { throw runtime_error("eval_enode: Def"); } else if (e->isEnil()) { throw runtime_error("eval_enode: Nil"); } else { throw runtime_error("eval_enode: unknown case"); } throw runtime_error("Not implemented yet: eval_enode"); }
double deriv_enode(Enode * const e, Enode * const v, unordered_map<Enode*, double> const & var_map) { if (e == v) { return 1.0; } if (e->isVar()) { auto const it = var_map.find(e); if (it == var_map.cend()) { throw runtime_error("variable not found"); } else { // Variable is found in var_map return 0.0; } } else if (e->isConstant()) { return 0.0; } else if (e->isSymb()) { throw runtime_error("eval_enode: Symb"); } else if (e->isNumb()) { throw runtime_error("eval_enode: Numb"); } else if (e->isTerm()) { assert(e->getArity() >= 1); enodeid_t id = e->getCar()->getId(); double ret = 0.0; Enode * tmp = e; switch (id) { case ENODE_ID_PLUS: ret = deriv_enode(tmp->get1st(), v, var_map); tmp = tmp->getCdr()->getCdr(); // e is pointing to the 2nd arg while (!tmp->isEnil()) { ret = ret + deriv_enode(tmp->getCar(), v, var_map); tmp = tmp->getCdr(); } return ret; case ENODE_ID_MINUS: ret = deriv_enode(tmp->get1st(), v, var_map); tmp = tmp->getCdr()->getCdr(); // e is pointing to the 2nd arg while (!tmp->isEnil()) { ret = ret - deriv_enode(tmp->getCar(), v, var_map); tmp = tmp->getCdr(); } return ret; case ENODE_ID_UMINUS: ret = deriv_enode(tmp->get1st(), v, var_map); assert(tmp->getArity() == 1); return (- ret); case ENODE_ID_TIMES: { // (f * g)' = f' * g + f * g' if (tmp->getArity() != 2) { throw runtime_error("deriv_enode: only support arity = 2 case for multiplication"); } double const f = eval_enode(e->get1st(), var_map); double const f_ = deriv_enode(e->get1st(), v, var_map); double const g = eval_enode(e->get2nd(), var_map); double const g_ = deriv_enode(e->get2nd(), v, var_map); return f_ * g + f * g_; } case ENODE_ID_DIV: { // (f / g)' = (f' * g - f * g') / g^2 if (tmp->getArity() != 2) { throw runtime_error("deriv_enode: only support arity = 2 case for division"); } double const f = eval_enode(e->get1st(), var_map); double const f_ = deriv_enode(e->get1st(), v, var_map); double const g = eval_enode(e->get2nd(), var_map); double const g_ = deriv_enode(e->get2nd(), v, var_map); return (f_ * g - f * g_) / (g * g); } case ENODE_ID_ACOS: { // (acos f)' = -(1 / sqrt(1 - f^2)) f' assert(e->getArity() == 1); double const f = eval_enode(e->get1st(), var_map); double const f_ = deriv_enode(e->get1st(), v, var_map); return - (1 / sqrt(1 - f * f)) * f_; } case ENODE_ID_ASIN: { // (asin f)' = (1 / sqrt(1 - f^2)) f' assert(e->getArity() == 1); double const f = eval_enode(e->get1st(), var_map); double const f_ = deriv_enode(e->get1st(), v, var_map); return 1 / sqrt(1 - f * f) * f_; } case ENODE_ID_ATAN: { // (atan f)' = (1 / (1 + f^2)) * f' assert(e->getArity() == 1); double const f = eval_enode(e->get1st(), var_map); double const f_ = deriv_enode(e->get1st(), v, var_map); return 1 / (1 + f * f) * f_; } case ENODE_ID_ATAN2: { // atan2(x,y)' = -y / (x^2 + y^2) dx + x / (x^2 + y^2) dy // = (-y dx + x dy) / (x^2 + y^2) assert(e->getArity() == 2); double const f = eval_enode(e->get1st(), var_map); double const f_ = deriv_enode(e->get1st(), v, var_map); double const g = eval_enode(e->get2nd(), var_map); double const g_ = deriv_enode(e->get2nd(), v, var_map); return (-g * f_ + f * g_) / (f * f + g * g); } case ENODE_ID_MIN: assert(e->getArity() == 2); throw runtime_error("deriv_enode: no support for min"); case ENODE_ID_MAX: assert(e->getArity() == 2); throw runtime_error("deriv_enode: no support for max"); case ENODE_ID_MATAN: assert(e->getArity() == 1); throw runtime_error("deriv_enode: no support for matan"); case ENODE_ID_SAFESQRT: assert(e->getArity() == 1); throw runtime_error("deriv_enode: no support for safesqrt"); case ENODE_ID_SQRT: { // (sqrt(f))' = 1/2 * 1/(sqrt(f)) * f' assert(e->getArity() == 1); double const f = eval_enode(e->get1st(), var_map); double const f_ = deriv_enode(e->get1st(), v, var_map); return 0.5 * 1 / sqrt(f) * f_; } case ENODE_ID_EXP: { // (exp f)' = (exp f) * f' assert(e->getArity() == 1); double const f = eval_enode(e->get1st(), var_map); double const f_ = deriv_enode(e->get1st(), v, var_map); return exp(f) * f_; } case ENODE_ID_LOG: { // (log f)' = f' / f assert(e->getArity() == 1); double const f = eval_enode(e->get1st(), var_map); double const f_ = deriv_enode(e->get1st(), v, var_map); return f_ / f; } case ENODE_ID_POW: { // (f^g)' = f^g (f' * g / f + g' * ln g) assert(e->getArity() == 2); double const f = eval_enode(e->get1st(), var_map); double const f_ = deriv_enode(e->get1st(), v, var_map); double const g = eval_enode(e->get2nd(), var_map); double const g_ = deriv_enode(e->get2nd(), v, var_map); return pow(f, g) * (f_ * g / f + g_ * log(g)); } case ENODE_ID_ABS: { assert(e->getArity() == 1); double const f = eval_enode(e->get1st(), var_map); double const f_ = deriv_enode(e->get1st(), v, var_map); if (f > 0) { return f_; } else { return - f_; } } case ENODE_ID_SIN: { // (sin f)' = (cos f) * f' assert(e->getArity() == 1); double const f = eval_enode(e->get1st(), var_map); double const f_ = deriv_enode(e->get1st(), v, var_map); return cos(f) * f_; } case ENODE_ID_COS: { // (cos f)' = - (sin f) * f' assert(e->getArity() == 1); double const f = eval_enode(e->get1st(), var_map); double const f_ = deriv_enode(e->get1st(), v, var_map); return - sin(f) * f_; } case ENODE_ID_TAN: { // (tan f)' = (1 + tan^2 f) * f' assert(e->getArity() == 1); double const f = eval_enode(e->get1st(), var_map); double const f_ = deriv_enode(e->get1st(), v, var_map); return (1 + tan(f) * tan(f)) * f_; } case ENODE_ID_SINH: { // (sinh f)' = (e^f + e^(-f))/2 * f' // = cosh(f) * f' assert(e->getArity() == 1); double const f = eval_enode(e->get1st(), var_map); double const f_ = deriv_enode(e->get1st(), v, var_map); return cosh(f) * f_; } case ENODE_ID_COSH: { // (cosh f)' = (e^f - e^(-f))/2 * f' // = sinh(f) * f' assert(e->getArity() == 1); double const f = eval_enode(e->get1st(), var_map); double const f_ = deriv_enode(e->get1st(), v, var_map); return sinh(f) * f_; } case ENODE_ID_TANH: { // (tanh f)' = (sech^2 f) * f' // = (1 - tanh(f) ^ 2) * f' assert(e->getArity() == 1); double const f = eval_enode(e->get1st(), var_map); double const f_ = deriv_enode(e->get1st(), v, var_map); return (1 - tanh(f) * tanh(f)) * f_; } default: throw runtime_error("deriv_enode: Unknown Term"); } } else if (e->isList()) { throw runtime_error("deriv_enode: List"); } else if (e->isDef()) { throw runtime_error("deriv_enode: Def"); } else if (e->isEnil()) { throw runtime_error("deriv_enode: Nil"); } else { throw runtime_error("deriv_enode: unknown case"); } throw runtime_error("Not implemented yet: deriv_enode"); }
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; }
// // 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; }
// Translate an Enode e into ibex::ExprNode. // Note: As a side-effect, update var_map : string -> ibex::Variable // Note: Use subst map (Enode ->ibex::Interval) ExprNode const * translate_enode_to_exprnode(map<string, Variable const> & var_map, Enode * const e, unordered_map<Enode*, ibex::Interval> const & subst) { // TODO(soonhok): for the simple case such as 0 <= x or x <= 10. // Handle it as a domain specification instead of constraints. if (e->isVar()) { auto const subst_it = subst.find(e); if (subst_it != subst.cend()) { auto const i = subst_it->second; return &ExprConstant::new_scalar(i); } string const & var_name = e->getCar()->getNameFull(); auto const it = var_map.find(var_name); if (it == var_map.cend()) { // The variable is new, we need to make one. Variable v(var_name.c_str()); // double const lb = e->getLowerBound(); // double const ub = e->getUpperBound(); var_map.emplace(var_name, v); return v.symbol; } else { // Variable is found in var_map Variable const & v = it->second; return v.symbol; } } else if (e->isConstant()) { double const lb = e->getValueLowerBound(); double const ub = e->getValueUpperBound(); return &ExprConstant::new_scalar(ibex::Interval(lb, ub)); } else if (e->isSymb()) { throw logic_error("translateEnodeExprNode: Symb"); } else if (e->isNumb()) { throw logic_error("translateEnodeExprNode: Numb"); } else if (e->isTerm()) { assert(e->getArity() >= 1); enodeid_t id = e->getCar()->getId(); ExprNode const * ret = nullptr; Enode * tmp = e; switch (id) { case ENODE_ID_PLUS: ret = translate_enode_to_exprnode(var_map, tmp->get1st(), subst); tmp = tmp->getCdr()->getCdr(); // e is pointing to the 2nd arg while (!tmp->isEnil()) { ret = &(*ret + *translate_enode_to_exprnode(var_map, tmp->getCar(), subst)); tmp = tmp->getCdr(); } return ret; case ENODE_ID_MINUS: ret = translate_enode_to_exprnode(var_map, tmp->get1st(), subst); tmp = tmp->getCdr()->getCdr(); // e is pointing to the 2nd arg while (!tmp->isEnil()) { ret = &(*ret - *translate_enode_to_exprnode(var_map, tmp->getCar(), subst)); tmp = tmp->getCdr(); } return ret; case ENODE_ID_UMINUS: ret = translate_enode_to_exprnode(var_map, tmp->get1st(), subst); assert(tmp->getArity() == 1); return &(- *ret); case ENODE_ID_TIMES: ret = translate_enode_to_exprnode(var_map, tmp->get1st(), subst); tmp = tmp->getCdr()->getCdr(); // e is pointing to the 2nd arg while (!tmp->isEnil()) { ret = &(*ret * *translate_enode_to_exprnode(var_map, tmp->getCar(), subst)); tmp = tmp->getCdr(); } return ret; case ENODE_ID_DIV: ret = translate_enode_to_exprnode(var_map, tmp->get1st(), subst); tmp = tmp->getCdr()->getCdr(); // e is pointing to the 2nd arg while (!tmp->isEnil()) { ret = &(*ret / *translate_enode_to_exprnode(var_map, tmp->getCar(), subst)); tmp = tmp->getCdr(); } return ret; case ENODE_ID_ACOS: assert(e->getArity() == 1); return &acos(*translate_enode_to_exprnode(var_map, e->get1st(), subst)); case ENODE_ID_ASIN: assert(e->getArity() == 1); return &asin(*translate_enode_to_exprnode(var_map, e->get1st(), subst)); case ENODE_ID_ATAN: assert(e->getArity() == 1); return &atan(*translate_enode_to_exprnode(var_map, e->get1st(), subst)); case ENODE_ID_ATAN2: assert(e->getArity() == 2); return &atan2(*translate_enode_to_exprnode(var_map, e->get1st(), subst), *translate_enode_to_exprnode(var_map, e->get2nd(), subst)); case ENODE_ID_MIN: assert(e->getArity() == 2); return &min(*translate_enode_to_exprnode(var_map, e->get1st(), subst), *translate_enode_to_exprnode(var_map, e->get2nd(), subst)); case ENODE_ID_MAX: assert(e->getArity() == 2); return &max(*translate_enode_to_exprnode(var_map, e->get1st(), subst), *translate_enode_to_exprnode(var_map, e->get2nd(), subst)); case ENODE_ID_MATAN: // TODO(soonhok): MATAN throw logic_error("translateEnodeExprNode: MATAN"); case ENODE_ID_SAFESQRT: // TODO(soonhok): SAFESQRT throw logic_error("translateEnodeExprNode: SAFESQRT"); case ENODE_ID_SQRT: assert(e->getArity() == 1); return &sqrt(*translate_enode_to_exprnode(var_map, e->get1st(), subst)); case ENODE_ID_EXP: assert(e->getArity() == 1); return &exp(*translate_enode_to_exprnode(var_map, e->get1st(), subst)); case ENODE_ID_LOG: assert(e->getArity() == 1); return &log(*translate_enode_to_exprnode(var_map, e->get1st(), subst)); case ENODE_ID_POW: { assert(e->getArity() == 2); bool is_1st_constant = false; bool is_1st_int = false; bool is_2nd_constant = false; bool is_2nd_int = false; double dbl_1st = 0.0; int int_1st = 0; double dbl_2nd = 0.0; int int_2nd = 0; if (e->get1st()->isConstant()) { dbl_1st = e->get1st()->getValue(); is_1st_constant = true; double tmp; if (modf(dbl_1st, &tmp) == 0.0) { is_1st_int = true; int_1st = static_cast<int>(tmp); } } if (e->get2nd()->isConstant()) { dbl_2nd = e->get2nd()->getValue(); is_2nd_constant = true; double tmp; if (modf(dbl_2nd, &tmp) == 0.0) { is_2nd_int = true; int_2nd = static_cast<int>(tmp); } } if (is_1st_constant && is_2nd_constant) { // Both of them are constant, just compute and return a number return &ExprConstant::new_scalar(pow(dbl_1st, dbl_2nd)); } // Now, either of them is non-constant. if (is_1st_int) { return &pow(int_1st, *translate_enode_to_exprnode(var_map, e->get2nd(), subst)); } if (is_1st_constant) { return &pow(dbl_1st, *translate_enode_to_exprnode(var_map, e->get2nd(), subst)); } if (is_2nd_int) { return &pow(*translate_enode_to_exprnode(var_map, e->get1st(), subst), int_2nd); } if (is_2nd_constant) { return &pow(*translate_enode_to_exprnode(var_map, e->get1st(), subst), dbl_2nd); } return &pow(*translate_enode_to_exprnode(var_map, e->get1st(), subst), *translate_enode_to_exprnode(var_map, e->get2nd(), subst)); } case ENODE_ID_ABS: assert(e->getArity() == 1); return &abs(*translate_enode_to_exprnode(var_map, e->get1st(), subst)); case ENODE_ID_SIN: assert(e->getArity() == 1); return &sin(*translate_enode_to_exprnode(var_map, e->get1st(), subst)); case ENODE_ID_COS: assert(e->getArity() == 1); return &cos(*translate_enode_to_exprnode(var_map, e->get1st(), subst)); case ENODE_ID_TAN: assert(e->getArity() == 1); return &tan(*translate_enode_to_exprnode(var_map, e->get1st(), subst)); case ENODE_ID_SINH: assert(e->getArity() == 1); return &sinh(*translate_enode_to_exprnode(var_map, e->get1st(), subst)); case ENODE_ID_COSH: assert(e->getArity() == 1); return &cosh(*translate_enode_to_exprnode(var_map, e->get1st(), subst)); case ENODE_ID_TANH: assert(e->getArity() == 1); return &tanh(*translate_enode_to_exprnode(var_map, e->get1st(), subst)); default: throw logic_error("translateEnodeExprNode: Unknown Term"); } } else if (e->isList()) { throw logic_error("translateEnodeExprNode: List"); } else if (e->isDef()) { throw logic_error("translateEnodeExprNode: Def"); } else if (e->isEnil()) { throw logic_error("translateEnodeExprNode: Nil"); } else { throw logic_error("translateEnodeExprNode: unknown case"); } throw logic_error("Not implemented yet: translateEnodeExprNode"); }