void Egraph::expEnqueueArguments( Enode * x, Enode * y ) { assert( x->isTerm( ) ); assert( y->isTerm( ) ); assert( x->getArity( ) == y->getArity( ) ); // No explanation needed if they are the same if ( x == y ) return; // Simple explanation if they are arity 0 terms if ( x->getArity( ) == 0 ) { exp_pending.push_back( x ); exp_pending.push_back( y ); return; } // Otherwise they are the same function symbol // Recursively enqueue the explanations for the args assert( x->getCar( ) == y->getCar( ) ); Enode * xptr = x->getCdr( ); Enode * yptr = y->getCdr( ); while ( !xptr->isEnil( ) ) { exp_pending.push_back( xptr->getCar( ) ); exp_pending.push_back( yptr->getCar( ) ); xptr = xptr->getCdr( ); yptr = yptr->getCdr( ); } // Check both lists have the same length assert( yptr->isEnil( ) ); }
IVector ode_solver::extract_invariants() { map<Enode*, pair<double, double>> inv_map; for (auto inv : m_invs) { Enode * p = inv->getCdr()->getCdr()->getCdr()->getCdr()->getCar(); Enode * op = p->getCar(); bool pos = true; // Handle Negation if (op->getId() == ENODE_ID_NOT) { p = p->getCdr()->getCar(); op = p->getCar(); pos = false; } switch (op->getId()) { case ENODE_ID_GEQ: case ENODE_ID_GT: // Handle >= & > pos = !pos; case ENODE_ID_LEQ: case ENODE_ID_LT: { // Handle <= & < Enode * lhs = pos ? p->getCdr()->getCar() : p->getCdr()->getCdr()->getCar(); Enode * rhs = pos ? p->getCdr()->getCdr()->getCar() : p->getCdr()->getCar(); if (lhs->isVar() && rhs->isConstant()) { if (inv_map.find(lhs) != inv_map.end()) { inv_map[lhs].second = rhs->getValue(); } else { inv_map.emplace(lhs, make_pair(lhs->getLowerBound(), rhs->getValue())); } } else if (lhs->isConstant() && rhs->isVar()) { if (inv_map.find(rhs) != inv_map.end()) { inv_map[rhs].first = lhs->getValue(); } else { inv_map.emplace(rhs, make_pair(lhs->getValue(), rhs->getUpperBound())); } } else { cerr << "ode_solver::extract_invariant: error:" << p << endl; } } break; default: cerr << "ode_solver::extract_invariant: error" << p << endl; } } IVector ret (m_t_vars.size()); unsigned i = 0; for (auto const & m_t_var : m_t_vars) { if (inv_map.find(m_t_var) != inv_map.end()) { auto inv = interval(inv_map[m_t_var].first, inv_map[m_t_var].second); DREAL_LOG_INFO << "Invariant extracted from " << m_t_var << " = " << inv; ret[i++] = inv; } else { auto inv = interval(m_t_var->getLowerBound(), m_t_var->getUpperBound()); DREAL_LOG_INFO << "Default Invariant set for " << m_t_var << " = " << inv; ret[i++] = inv; } } return ret; }
// // Check if a formula is a clause // bool Cnfizer::checkClause( Enode * e, set< enodeid_t > & check_cache ) { assert( e ); if ( e->isLit( ) ) { check_cache.insert( e->getId( ) ); // Don't check again return true; } if ( !e->isOr( ) ) return false; if ( check_cache.find( e->getId( ) ) != check_cache.end( ) ) // Already visited term return true; bool is_clause = true; for ( Enode * list = e->getCdr( ) ; list != egraph.enil && is_clause ; list = list->getCdr( ) ) is_clause = checkClause( list->getCar( ), check_cache ); if ( !is_clause ) return false; check_cache.insert( e->getId( ) ); // Don't check again return true; }
// // Check if its a pure conjunction of literals // bool Cnfizer::checkPureConj( Enode * e, set< enodeid_t > & check_cache ) { if ( check_cache.find( e->getId( ) ) != check_cache.end( ) ) return true; if ( e->isLit( ) ) { check_cache.insert( e->getId( ) ); return true; } if ( !e->isAnd( ) ) return false; bool is_pure_conj = true; for ( Enode * list = e->getCdr( ) ; list != egraph.enil && is_pure_conj ; list = list->getCdr( ) ) is_pure_conj = checkPureConj( list->getCar( ), check_cache ); if ( !is_pure_conj ) return false; check_cache.insert( e->getId( ) ); return true; }
vector<shared_ptr<nonlinear_constraint>> make_nlctrs(Enode * const e, unordered_set<Enode *> const & var_set, lbool const p) { vector<shared_ptr<nonlinear_constraint>> ret; if (e->isTrue()) { return ret; } if (e->isFalse()) { DREAL_LOG_FATAL << "false is not a valid invariant (forall_t constraint)"; throw logic_error("false is not a valid invariant (forall_t constraint)"); } if (e->isNot()) { return make_nlctrs(e->get1st(), var_set, !p); } if (e->isAnd()) { Enode * tmp = e->getCdr(); while (!tmp->isEnil()) { auto const nlctrs = make_nlctrs(e->get1st(), var_set, p); ret.insert(ret.end(), nlctrs.begin(), nlctrs.end()); tmp = tmp->getCdr(); } return ret; } if (e->isOr()) { DREAL_LOG_FATAL << "or is not a valid invariant for now, (forall_t constraint)"; throw logic_error("false is not a valid invariant for now, (forall_t constraint)"); } ret.push_back(make_shared<nonlinear_constraint>(e, var_set, p)); return ret; }
// // Retrieve the formulae at the top-level // void Cnfizer::retrieveTopLevelFormulae( Enode * f, vector< Enode * > & top_level_formulae ) { if ( f->isAnd( ) ) for ( Enode * list = f->getCdr( ) ; list != egraph.enil ; list = list->getCdr( ) ) retrieveTopLevelFormulae( list->getCar( ), top_level_formulae ); else top_level_formulae.push_back( f ); }
ostream & print_infix_op(ostream & out, Enode * const e, string const & op, std::function<ostream &(ostream &, Enode * const)> const & f) { assert(e->getArity() >= 2); out << "("; f(out, e->get1st()); Enode * tmp = e->getCdr()->getCdr(); while (!tmp->isEnil()) { out << " " << op << " "; f(out, tmp->getCar()); tmp = tmp->getCdr(); } out << ")"; return out; }
// // Merge collected arguments for nodes // Enode * Cnfizer::mergeEnodeArgs( Enode * e , map< enodeid_t, Enode * > & cache , map< enodeid_t, int > & enodeid_to_incoming_edges ) { assert( e->isAnd( ) || e->isOr( ) ); Enode * e_symb = e->getCar( ); vector< Enode * > new_args; for ( Enode * list = e->getCdr( ) ; !list->isEnil( ) ; list = list->getCdr( ) ) { Enode * arg = list->getCar( ); Enode * sub_arg = cache[ arg->getId( ) ]; Enode * sym = arg->getCar( ); if ( sym->getId( ) != e_symb->getId( ) ) { new_args.push_back( sub_arg ); continue; } assert( enodeid_to_incoming_edges.find( arg->getId( ) ) != enodeid_to_incoming_edges.end( ) ); assert( enodeid_to_incoming_edges[ arg->getId( ) ] >= 1 ); if ( enodeid_to_incoming_edges[ arg->getId( ) ] > 1 ) { new_args.push_back( sub_arg ); continue; } for ( Enode * sub_arg_list = sub_arg->getCdr( ) ; !sub_arg_list->isEnil( ) ; sub_arg_list = sub_arg_list->getCdr( ) ) new_args.push_back( sub_arg_list->getCar( ) ); } Enode * new_list = const_cast< Enode * >(egraph.enil); while ( !new_args.empty( ) ) { new_list = egraph.cons( new_args.back( ), new_list ); new_args.pop_back( ); } return egraph.cons( e_symb, new_list ); }
ostream & print_call(ostream & out, Enode * const e, string const & fname, std::function<ostream &(ostream &, Enode * const)> const & f, string const & lp, string const & rp) { assert(e->getArity() >= 1); out << fname; out << lp; f(out, e->get1st()); Enode * tmp = e->getCdr()->getCdr(); while (!tmp->isEnil()) { out << ", "; f(out, tmp->getCar()); tmp = tmp->getCdr(); } out << rp; return out; }
// // Retrieve a clause // void Cnfizer::retrieveClause( Enode * f, vector< Enode * > & clause ) { assert( f->isLit( ) || f->isOr( ) ); if ( f->isLit( ) ) { clause.push_back( f ); } else if ( f->isOr( ) ) { for ( Enode * list = f->getCdr( ) ; list != egraph.enil ; list = list->getCdr( ) ) retrieveClause( list->getCar( ), clause ); } }
// // Retrieve conjuncts // void Cnfizer::retrieveConjuncts( Enode * f, vector< Enode * > & conjuncts ) { assert( f->isLit( ) || f->isAnd( ) ); if ( f->isLit( ) ) { conjuncts.push_back( f ); } else if ( f->isAnd( ) ) { for ( Enode * list = f->getCdr( ) ; list != egraph.enil ; list = list->getCdr( ) ) retrieveConjuncts( list->getCar( ), conjuncts ); } }
// // Check if a formula is a conjunction of clauses // bool Cnfizer::checkConj( Enode * e, set< enodeid_t > & check_cache ) { if ( !e->isAnd( ) ) return false; if ( check_cache.find( e->getId( ) ) != check_cache.end( ) ) // Already visited term return true; Enode * list = e->getCdr( ); for ( ; list != egraph.enil ; list = list->getCdr( ) ) { Enode * arg = list->getCar( ); if( !checkConj( arg, check_cache ) && !checkClause( arg, check_cache ) ) return false; } check_cache.insert( e->getId( ) ); return true; }
cgcolor_t CGraph::colorNodesRec( CNode * c, const uint64_t mask ) { // Already done if ( colored_nodes.find( c ) != colored_nodes.end( ) ) return c->color; // Base case, color variables if ( c->e->getArity( ) == 0 ) { cgcolor_t color = CG_UNDEF; // Belongs to B if ( (egraph.getIPartitions( c->e ) & mask) != 0 ) color |= CG_B; // Belongs to A if ( (egraph.getIPartitions( c->e ) & ~mask) != 0 ) color |= CG_A; c->color = color; } else { // Function symbol: color depending on the arguments // Decide color of term as intersection cgcolor_t color = CG_AB; Enode * args = c->e->getCdr( ); for ( args = c->e->getCdr( ) ; !args->isEnil( ) ; args = args->getCdr( ) ) { Enode * arg = args->getCar( ); // Not necessairly an argument is needed in the graph if ( cnodes_store.find( arg->getId( ) ) != cnodes_store.end( ) ) color &= colorNodesRec( cnodes_store[ arg->getId( ) ], mask ); } c->color = color; } assert( colored_nodes.find( c ) == colored_nodes.end( ) ); colored_nodes.insert( c ); return c->color; }
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"); }
// // 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::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; }
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( ); }
bool CostSolver::assertLitImpl( Enode * e ) { assert( e ); assert( belongsToT( e ) ); assert( e->hasPolarity() ); assert( e->getPolarity() != l_Undef ); #if DEBUG cout << "ct assert " << (e->getPolarity() == l_True ? "" : "!") << e << endl; #endif assert( !conflict_ ); Enode * atom = e; const bool negated = e->getPolarity() == l_False; if ( atom->isCostIncur() ) { assert( nodemap_[ atom ] ); costfun & fun = *nodemap_[ atom ]; #if DEBUG print_status( cout, fun ); #endif incurnode * node = incurmap_[ atom ]; if ( node->prev ) { node->prev->next = node->next; assert( node->prev->cost <= node->cost ); } if ( node->next ) { node->next->prev = node->prev; assert( node->cost <= node->next->cost ); } else { fun.unassigned.last = node->prev; } if ( fun.unassigned.head == node ) { fun.unassigned.head = node->next; } fun.slack -= node->cost; fun.assigned.push_back( node ); if ( negated ) { undo_ops_.push( undo_op( REMOVE_INCUR_NEG, node ) ); if ( !fun.lowerbound.empty() && get_bound( fun.lowerbound.top() ) > fun.incurred + fun.slack ) { assert( explanation.empty() ); conflict_ = atom; codomain potential = fun.incurred + fun.slack; for ( costfun::nodes_t::iterator it = fun.assigned.begin(); it != fun.assigned.end(); ++it ) { incurnode * node = *it; #if ELIM_REDUNDANT if ( node->atom->getPolarity() != l_False ) { continue; } if ( potential + node->cost < get_bound( fun.lowerbound.top() ) ) { potential += node->cost; continue; } #endif if ( node->atom->getPolarity() == l_False ) { explanation.push_back( node->atom ); } } assert( potential < get_bound( fun.lowerbound.top() ) ); assert( find( explanation.begin(), explanation.end(), conflict_ ) != explanation.end() ); explanation.push_back( fun.lowerbound.top() ); #if DEBUG_CONFLICT cout << " " << explanation << " : " << fun.slack << endl; print_status( cout, fun ); cout << endl; #endif #if DEBUG cout << "conflict found " << conflict_ << endl; print_status( cout, fun ); #endif #if DEBUG_CHECK_STATUS check_status(); #endif return false; } } else { fun.incurred += node->cost; undo_ops_.push( undo_op( REMOVE_INCUR_POS, node ) ); if ( !fun.upperbound.empty() && get_bound( fun.upperbound.top() ) <= fun.incurred ) { conflict_ = atom; codomain incurred = fun.incurred; for ( costfun::nodes_t::iterator it = fun.assigned.begin(); it != fun.assigned.end(); ++it ) { incurnode * node = *it; #if ELIM_REDUNDANT if ( node->atom->getPolarity() != l_True ) { continue; } if ( incurred - node->cost >= get_bound( fun.upperbound.top() ) ) { incurred -= node->cost; continue; } #endif if ( node->atom->getPolarity() == l_True ) { explanation.push_back( node->atom ); } } assert( incurred >= get_bound( fun.upperbound.top() ) ); assert( find( explanation.begin(), explanation.end(), conflict_ ) != explanation.end() ); explanation.push_back( fun.upperbound.top() ); #if DEBUG_CONFLICT cout << explanation << " : " << fun.slack << endl; print_status( cout, fun ); cout << endl; #endif #if DEBUG cout << "conflict found " << conflict_ << endl; print_status( cout, fun ); #endif #if DEBUG_CHECK_STATUS check_status(); #endif return false; } } } else if ( atom->isCostBound() ) { #if DEBUG cout << "ct bound asserted " << atom << endl; #endif assert( nodemap_.find( atom ) != nodemap_.end() ); costfun & fun = *nodemap_[ atom ]; Enode * args = atom->getCdr(); Enode * val = args->getCdr()->getCar(); assert( val->isConstant() ); #if DEBUG cout << atom->get2nd() << endl; #endif const codomain & value = atom->get2nd()->getValue(); if ( negated ) { #if DEBUG cout << "ct bound asserted negatively " << atom << endl; #endif if ( ( !fun.lowerbound.empty() && get_bound( fun.lowerbound.top() ) < value ) || fun.lowerbound.empty() ) { #if DEBUG cout << "ct new lower bound " << atom << endl; #endif fun.lowerbound.push( atom ); undo_ops_.push( undo_op( REMOVE_LBOUND, &fun ) ); if ( fun.incurred + fun.slack < get_bound( fun.lowerbound.top() ) ) { conflict_ = atom; codomain potential = fun.incurred + fun.slack; for ( costfun::nodes_t::iterator it = fun.assigned.begin(); it != fun.assigned.end(); ++it ) { incurnode * node = *it; #if ELIM_REDUNDANT if ( node->atom->getPolarity() != l_False ) { continue; } if ( potential + node->cost < get_bound( fun.lowerbound.top() ) ) { potential += node->cost; continue; } #endif if ( node->atom->getPolarity() == l_False ) { explanation.push_back( node->atom ); } } assert( find( explanation.begin(), explanation.end(), conflict_ ) == explanation.end() ); explanation.push_back( conflict_ ); assert( potential < get_bound( fun.lowerbound.top() ) ); assert( find( explanation.begin(), explanation.end(), conflict_ ) != explanation.end() ); #if DEBUG_CONFLICT cout << explanation << " : " << fun.slack << endl; print_status( cout, fun ); cout << endl; #endif #if DEBUG cout << "conflict found " << conflict_ << endl; print_status( cout, fun ); #endif #if DEBUG_CHECK_STATUS check_status(); #endif return false; } } } else { #if DEBUG cout << "ct bound asserted positively " << atom << endl; #endif if ( ( !fun.upperbound.empty() && get_bound( fun.upperbound.top() ) > value ) || fun.upperbound.empty() ) { #if DEBUG cout << "ct new upper bound " << atom << endl; #endif fun.upperbound.push( atom ); undo_ops_.push( undo_op( REMOVE_UBOUND, &fun ) ); if ( fun.incurred >= get_bound( fun.upperbound.top() ) ) { conflict_ = atom; codomain incurred = fun.incurred; for ( costfun::nodes_t::iterator it = fun.assigned.begin(); it != fun.assigned.end(); ++it ) { incurnode * node = *it; #if ELIM_REDUNDANT if ( node->atom->getPolarity() != l_True ) { continue; } if ( incurred - node->cost >= get_bound( fun.upperbound.top() ) ) { incurred -= node->cost; continue; } #endif if ( node->atom->getPolarity() == l_True ) { explanation.push_back( node->atom ); } } assert( find( explanation.begin(), explanation.end(), conflict_ ) == explanation.end() ); assert( incurred >= get_bound( fun.upperbound.top() ) ); explanation.push_back( conflict_ ); assert( find( explanation.begin(), explanation.end(), conflict_ ) != explanation.end() ); #if DEBUG_CONFLICT cout << " " << explanation << " : " << fun.slack << endl; print_status( cout, fun ); cout << endl; #endif #if DEBUG cout << "conflict found " << conflict_ << endl; print_status( cout, fun ); #endif #if DEBUG_CHECK_STATUS check_status(); #endif return false; } } } if ( !fun.lowerbound.empty() && !fun.upperbound.empty() && get_bound( fun.lowerbound.top() ) >= get_bound( fun.upperbound.top() ) ) { conflict_ = atom; explanation.push_back( fun.lowerbound.top() ); explanation.push_back( fun.upperbound.top() ); #if DEBUG_CONFLICT cout << " " << explanation << " : " << fun.slack << endl; print_status( cout, fun ); cout << endl; #endif #if DEBUG cout << "conflict found " << conflict_ << endl; print_status( cout, fun ); #endif #if DEBUG_CHECK_STATUS check_status(); #endif return false; } } else { #if DEBUG cout << "ct unrecognized " << atom << endl; #endif } #if 1 { // Deduction costfun & fun = *nodemap_[ atom ]; if ( !fun.upperbound.empty() && fun.lowerbound.empty() && fun.unassigned.last && get_bound( fun.upperbound.top() ) <= fun.unassigned.last->cost + fun.incurred && !fun.unassigned.last->atom->isDeduced() ) { #if DEBUG cout << "deducing !" << fun.unassigned.last->atom << endl; #endif fun.unassigned.last->atom->setDeduced( l_False, id ); deductions.push_back( fun.unassigned.last->atom ); } else if ( !fun.lowerbound.empty() && fun.upperbound.empty() && fun.unassigned.last && get_bound( fun.lowerbound.top() ) > fun.incurred + fun.slack - fun.unassigned.last->cost && !fun.unassigned.last->atom->isDeduced() ) { #if DEBUG cout << "deducing " << fun.unassigned.last->atom << endl; #endif fun.unassigned.last->atom->setDeduced( l_True, id ); deductions.push_back( fun.unassigned.last->atom ); } else if ( !fun.upperbound.empty() && !fun.lowerbound.empty() && fun.unassigned.last && get_bound( fun.lowerbound.top() ) +1 == get_bound( fun.upperbound.top() ) ) { if ( fun.unassigned.last->cost == fun.slack && fun.incurred + fun.unassigned.last->cost == get_bound( fun.lowerbound.top() ) && fun.incurred + fun.slack - fun.unassigned.last->cost < get_bound( fun.lowerbound.top() ) ) { #if DEBUG cout << "deducing " << fun.unassigned.last->atom << endl; print_status( cout, fun ); #endif fun.unassigned.last->atom->setDeduced( l_True, 0 ); deductions.push_back( fun.unassigned.last->atom ); } } } #endif #if DEBUG_CHECK_STATUS check_status(); #endif return true; }
static double eval(Enode * const e, double const values[], double const params[], unordered_map<Enode *, unsigned> const & value_lookup, unordered_map<Enode *, unsigned> const & param_lookup) { if (e->isNumb()) { return e->getNumb(); } else if (e->isVar()) { auto it = value_lookup.find(e); if (it != value_lookup.end()) { return values[it->second]; } it = param_lookup.find(e); if (it != param_lookup.end()) { return params[it->second]; } DREAL_LOG_FATAL << "Can't find variable " << e; throw runtime_error("GSL eval: unmatched variable"); } else if (e->isTerm()) { switch (e->getCar()->getId()) { case ENODE_ID_PLUS: { Enode * p = e->getCdr(); double ret = eval(p->getCar(), values, params, value_lookup, param_lookup); p = p->getCdr(); while (!p->isEnil()) { ret += eval(p->getCar(), values, params, value_lookup, param_lookup); p = p->getCdr(); } return ret; } case ENODE_ID_MINUS: { Enode * p = e->getCdr(); double ret = eval(p->getCar(), values, params, value_lookup, param_lookup); p = p->getCdr(); while (!p->isEnil()) { ret -= eval(p->getCar(), values, params, value_lookup, param_lookup); p = p->getCdr(); } return ret; } case ENODE_ID_TIMES: { Enode * p = e->getCdr(); double ret = eval(p->getCar(), values, params, value_lookup, param_lookup); p = p->getCdr(); while (!p->isEnil()) { ret *= eval(p->getCar(), values, params, value_lookup, param_lookup); p = p->getCdr(); } return ret; } case ENODE_ID_DIV: { Enode * p = e->getCdr(); double ret = eval(p->getCar(), values, params, value_lookup, param_lookup); p = p->getCdr(); while (!p->isEnil()) { ret /= eval(p->getCar(), values, params, value_lookup, param_lookup); p = p->getCdr(); } return ret; } case ENODE_ID_POW: { if (e->getArity() != 2) { throw runtime_error("GSL eval: pow not implemented"); } double const arg1 = eval(e->get1st(), values, params, value_lookup, param_lookup); double const arg2 = eval(e->get2nd(), values, params, value_lookup, param_lookup); return pow(arg1, arg2); } case ENODE_ID_ATAN2: { double const arg1 = eval(e->get1st(), values, params, value_lookup, param_lookup); double const arg2 = eval(e->get2nd(), values, params, value_lookup, param_lookup); return atan2(arg1, arg2); } case ENODE_ID_UMINUS: { assert(e->getArity() == 1); double const arg = eval(e->get1st(), values, params, value_lookup, param_lookup); return -arg; } case ENODE_ID_SIN: { assert(e->getArity() == 1); double const arg = eval(e->get1st(), values, params, value_lookup, param_lookup); return sin(arg); } case ENODE_ID_COS: { assert(e->getArity() == 1); double const arg = eval(e->get1st(), values, params, value_lookup, param_lookup); return cos(arg); } case ENODE_ID_TAN: { assert(e->getArity() == 1); double const arg = eval(e->get1st(), values, params, value_lookup, param_lookup); return tan(arg); } case ENODE_ID_SQRT: { assert(e->getArity() == 1); double const arg = eval(e->get1st(), values, params, value_lookup, param_lookup); return sqrt(arg); } case ENODE_ID_SAFESQRT: { assert(e->getArity() == 1); double const arg = eval(e->get1st(), values, params, value_lookup, param_lookup); assert(arg >= 0); return sqrt(arg); } case ENODE_ID_EXP: { assert(e->getArity() == 1); double const arg = eval(e->get1st(), values, params, value_lookup, param_lookup); return exp(arg); } case ENODE_ID_LOG: { assert(e->getArity() == 1); double const arg = eval(e->get1st(), values, params, value_lookup, param_lookup); return log(arg); } case ENODE_ID_ASIN: { assert(e->getArity() == 1); double const arg = eval(e->get1st(), values, params, value_lookup, param_lookup); return asin(arg); } case ENODE_ID_ACOS: { assert(e->getArity() == 1); double const arg = eval(e->get1st(), values, params, value_lookup, param_lookup); return acos(arg); } case ENODE_ID_ATAN: { assert(e->getArity() == 1); double const arg = eval(e->get1st(), values, params, value_lookup, param_lookup); return atan(arg); } case ENODE_ID_SINH: { assert(e->getArity() == 1); double const arg = eval(e->get1st(), values, params, value_lookup, param_lookup); return sinh(arg); } case ENODE_ID_COSH: { assert(e->getArity() == 1); double const arg = eval(e->get1st(), values, params, value_lookup, param_lookup); return cosh(arg); } case ENODE_ID_TANH: { assert(e->getArity() == 1); double const arg = eval(e->get1st(), values, params, value_lookup, param_lookup); return tanh(arg); } default: return eval(e->getCar(), values, params, value_lookup, param_lookup); } } else if (e->isList()) { throw runtime_error("GSL eval: list"); } else if (e->isDef()) { throw runtime_error("GSL eval: def"); } else if (e->isEnil()) { throw runtime_error("GSL eval: enil"); } throw runtime_error("GSL eval: unknown"); }
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"); }
void THandler::verifyInterpolantWithExternalTool( vector< Enode * > & expl , Enode * interp_list ) { uint64_t mask = 0xFFFFFFFFFFFFFFFEULL; for ( unsigned in = 1 ; in < core_solver.getNofPartitions( ) ; in ++ ) { Enode * args = interp_list; // Advance in the interpolants list for ( unsigned i = 0 ; i < in - 1 ; i ++ ) args = args->getCdr( ); Enode * interp = args->getCar( ); mask &= ~SETBIT( in ); // Check A -> I, i.e., A & !I // First stage: print declarations const char * name = "/tmp/verifyinterp.smt2"; std::ofstream dump_out( name ); core_solver.dumpHeaderToFile( dump_out ); // Print only A atoms dump_out << "(assert " << endl; dump_out << "(and" << endl; for ( size_t j = 0 ; j < expl.size( ) ; j ++ ) { Enode * e = expl[ j ]; assert( e->isTAtom( ) ); assert( e->getPolarity( ) != l_Undef ); assert( (core_solver.getIPartitions( e ) & mask) != 0 || (core_solver.getIPartitions( e ) & ~mask) != 0 ); if ( (core_solver.getIPartitions( e ) & ~mask) != 0 ) { bool negated = e->getPolarity( ) == l_False; if ( negated ) dump_out << "(not "; e->print( dump_out ); if ( negated ) dump_out << ")"; dump_out << endl; } } dump_out << "(not " << interp << ")" << endl; dump_out << "))" << endl; dump_out << "(check-sat)" << endl; dump_out << "(exit)" << endl; dump_out.close( ); // Check ! bool tool_res; if ( int pid = fork() ) { int status; waitpid(pid, &status, 0); switch ( WEXITSTATUS( status ) ) { case 0: tool_res = false; break; case 1: tool_res = true; break; default: perror( "Tool" ); exit( EXIT_FAILURE ); } } else { execlp( "tool_wrapper.sh", "tool_wrapper.sh", name, 0 ); perror( "Tool" ); exit( 1 ); } if ( tool_res == true ) opensmt_error2( config.certifying_solver, " says A -> I does not hold" ); // Now check B & I dump_out.open( name ); core_solver.dumpHeaderToFile( dump_out ); // Print only B atoms dump_out << "(assert " << endl; dump_out << "(and" << endl; for ( size_t j = 0 ; j < expl.size( ) ; j ++ ) { Enode * e = expl[ j ]; assert( e->isTAtom( ) ); assert( e->getPolarity( ) != l_Undef ); assert( (core_solver.getIPartitions( e ) & mask) != 0 || (core_solver.getIPartitions( e ) & ~mask) != 0 ); if ( (core_solver.getIPartitions( e ) & mask) != 0 ) { bool negated = e->getPolarity( ) == l_False; if ( negated ) dump_out << "(not "; e->print( dump_out ); if ( negated ) dump_out << ")"; dump_out << endl; } } dump_out << interp << endl; dump_out << "))" << endl; dump_out << "(check-sat)" << endl; dump_out << "(exit)" << endl; dump_out.close( ); // Check ! tool_res; if ( int pid = fork() ) { int status; waitpid(pid, &status, 0); switch ( WEXITSTATUS( status ) ) { case 0: tool_res = false; break; case 1: tool_res = true; break; default: perror( "Tool" ); exit( EXIT_FAILURE ); } } else { execlp( "tool_wrapper.sh", "tool_wrapper.sh", name, 0 ); perror( "Tool" ); exit( 1 ); } if ( tool_res == true ) opensmt_error2( config.certifying_solver, " says B & I does not hold" ); } }
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 ) ); }
// // Compute the number of incoming edges for e and children // void Cnfizer::computeIncomingEdges( Enode * e , map< enodeid_t, int > & enodeid_to_incoming_edges ) { assert( e ); vector< Enode * > unprocessed_enodes; // Stack for unprocessed enodes unprocessed_enodes.push_back( e ); // 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 // map< enodeid_t, int >::iterator it = enodeid_to_incoming_edges.find( enode->getId( ) ); if ( it != enodeid_to_incoming_edges.end( ) ) { it->second++; unprocessed_enodes.pop_back( ); continue; } bool unprocessed_children = false; if ( enode->isBooleanOperator( ) ) { for ( Enode * arg_list = enode->getCdr( ) ; !arg_list->isEnil( ) ; arg_list = arg_list->getCdr( ) ) { Enode * arg = arg_list->getCar( ); // // Push only if it is an unprocessed boolean operator // map< enodeid_t, int >::iterator it = enodeid_to_incoming_edges.find( arg->getId( ) ); if ( it == enodeid_to_incoming_edges.end( ) ) { unprocessed_enodes.push_back( arg ); unprocessed_children = true; } else { it->second ++; } } } // // SKip if unprocessed_children // if ( unprocessed_children ) continue; unprocessed_enodes.pop_back( ); // // At this point, every child has been processed // assert ( enode->isBooleanOperator( ) || enode->isAtom( ) ); assert ( enodeid_to_incoming_edges.find( enode->getId( ) ) == enodeid_to_incoming_edges.end( ) ); enodeid_to_incoming_edges[ enode->getId( ) ] = 1; } }
lbool CostSolver::inform( Enode * e ) { assert( e ); assert( belongsToT( e ) ); #if DEBUG cout << "ct inform " << e << endl; #endif if ( e->isCostIncur() ) { assert( e->getArity() == 3 ); Enode * args = e->getCdr(); Enode * var = args->getCar(); Enode * cost = args->getCdr()->getCar(); #if DEBUG cout << "ct inform var = " << var << endl; cout << "ct inform cost = " << cost << endl; #endif assert( var->isVar() ); assert( cost->isConstant() ); nodemap_t::iterator it = nodemap_.find( var ); if ( it != nodemap_.end() ) { costfun & fun = *it->second; nodemap_[ e ] = &fun; add_incur( fun, e, cost ); } else { costfun * fun = new costfun( var ); #if DEBUG cout << "ct new cost fun " << var << endl; #endif nodemap_[ var ] = fun; nodemap_[ e ] = fun; costfuns_.push_back( fun ); add_incur( *fun, e, cost ); } } if ( e->isCostBound() ) { assert( e->getArity() == 2 ); Enode * args = e->getCdr(); Enode * var = args->getCar(); nodemap_t::iterator it = nodemap_.find( var ); if ( it != nodemap_.end() ) { costfun & fun = *it->second; nodemap_[ var ] = &fun; nodemap_[ e ] = &fun; add_bound( fun, e ); } else { costfun * fun = new costfun( var ); #if DEBUG cout << "ct new cost fun " << var << endl; #endif nodemap_[ var ] = fun; nodemap_[ e ] = fun; costfuns_.push_back( fun ); add_bound( *fun, e ); } } #if DEBUG print_status( cout ); #endif return l_Undef; }
// 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"); }