box contractor_int::prune(box b, SMTConfig & config) const { // ======= Proof ======= thread_local static box old_box(b); if (config.nra_proof) { old_box = b; } m_input = ibex::BitSet::empty(b.size()); m_output = ibex::BitSet::empty(b.size()); unsigned i = 0; ibex::IntervalVector & iv = b.get_values(); for (Enode * e : b.get_vars()) { if (e->hasSortInt()) { auto old_iv = iv[i]; iv[i] = ibex::integer(iv[i]); if (old_iv != iv[i]) { m_input.add(i); m_output.add(i); } if (iv[i].is_empty()) { b.set_empty(); break; } } i++; } // ======= Proof ======= if (config.nra_proof) { output_pruning_step(config.nra_proof_out, old_box, b, config.nra_readable_proof, "integer pruning"); } return b; }
contractor_int::contractor_int(box const & b) : contractor_cell(contractor_kind::INT) { m_input = ibex::BitSet::empty(b.size()); auto const & vars = b.get_vars(); for (unsigned i = 0; i < b.size(); ++i) { Enode * const e = vars[i]; if (e->hasSortInt()) { m_input.add(i); } } }
box shrink_for_dop(box b) { for (Enode * e : b.get_vars()) { string const name = e->getCar()->getNameFull(); if (starts_with(name, "forall_")) { string const exist_var_name = name.substr(7); auto exist_var_intv = b[exist_var_name]; b[name] = exist_var_intv; } } return b; }
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; }
contractor default_strategy::build_contractor(box const & box, scoped_vec<shared_ptr<constraint>> const & ctrs, bool const complete, SMTConfig const & config) const { bool const use_cache = true; // 1. Categorize constraints vector<shared_ptr<nonlinear_constraint>> nl_ctrs; vector<shared_ptr<ode_constraint>> ode_ctrs_rev; vector<shared_ptr<generic_forall_constraint>> generic_forall_ctrs; for (shared_ptr<constraint> const ctr : ctrs.get_reverse()) { switch (ctr->get_type()) { case constraint_type::Nonlinear: { auto nl_ctr = dynamic_pointer_cast<nonlinear_constraint>(ctr); nl_ctrs.push_back(nl_ctr); break; } case constraint_type::ODE: { auto ode_ctr = dynamic_pointer_cast<ode_constraint>(ctr); ode_ctrs_rev.push_back(ode_ctr); break; } case constraint_type::GenericForall: { auto gf_ctr = dynamic_pointer_cast<generic_forall_constraint>(ctr); generic_forall_ctrs.push_back(gf_ctr); break; } case constraint_type::ForallT: { // Do nothing break; } default: DREAL_LOG_FATAL << "Unknown Constraint Type: " << ctr->get_type() << " " << *ctr << endl; } } vector<shared_ptr<ode_constraint>> ode_ctrs(ode_ctrs_rev); reverse(ode_ctrs.begin(), ode_ctrs.end()); vector<contractor> ctcs; ctcs.reserve(ctrs.size()); // 2.0 Build Sample Contractor if (config.nra_sample > 0 && complete) { ctcs.push_back(mk_contractor_sample(box, config.nra_sample, ctrs.get_vec())); } // 2.1 Build nonlinear contractors vector<contractor> nl_ctcs; for (auto const & nl_ctr : nl_ctrs) { if (!nl_ctr->is_neq()) { nl_ctcs.push_back(mk_contractor_ibex_fwdbwd(nl_ctr, use_cache)); } else { // Case: != (not equal), do nothing } } contractor nl_ctc = mk_contractor_seq(nl_ctcs); ctcs.insert(ctcs.end(), nl_ctcs.begin(), nl_ctcs.end()); // 2.2. Build Polytope Contractor if (config.nra_polytope) { ctcs.push_back(mk_contractor_ibex_polytope(config.nra_precision, box.get_vars(), nl_ctrs)); } // 2.3. Int Contractor ctcs.push_back(mk_contractor_int(box)); // 2.4. Build generic forall contractors for (auto const & generic_forall_ctr : generic_forall_ctrs) { ctcs.push_back(mk_contractor_generic_forall(box, generic_forall_ctr)); } if (complete && ode_ctrs.size() > 0) { // Add ODE Contractors only for complete check // 2.5. Build GSL Contractors (using CAPD4) vector<contractor> ode_gsl_ctcs; if (config.nra_ODE_sampling) { // 2.5.1 Build Eval contractors vector<contractor> eval_ctcs; for (auto const & nl_ctr : nl_ctrs) { eval_ctcs.push_back(mk_contractor_eval(nl_ctr, false)); } contractor eval_ctc = mk_contractor_seq(eval_ctcs); if (config.nra_parallel) { vector<contractor> nl_ctcs2; for (auto const & nl_ctr : nl_ctrs) { if (!nl_ctr->is_neq()) { nl_ctcs2.push_back(mk_contractor_ibex_fwdbwd(nl_ctr, false)); } else { // Case: != (not equal), do nothing } } contractor nl_ctc2 = mk_contractor_seq(nl_ctcs2); for (auto const & ode_ctr : ode_ctrs) { // Add Forward ODE Pruning (Underapproximation, using GNU GSL) ode_gsl_ctcs.push_back(mk_contractor_gsl(box, ode_ctr, eval_ctc, ode_direction::FWD, false, config.nra_ODE_fwd_timeout)); ode_gsl_ctcs.push_back(nl_ctc2); } } else { for (auto const & ode_ctr : ode_ctrs) { // Add Forward ODE Pruning (Underapproximation, using GNU GSL) ode_gsl_ctcs.push_back(mk_contractor_gsl(box, ode_ctr, eval_ctc, ode_direction::FWD, use_cache, config.nra_ODE_fwd_timeout)); ode_gsl_ctcs.push_back(nl_ctc); } } } // 2.6. Build ODE Contractors (using CAPD4) vector<contractor> ode_capd4_fwd_ctcs; vector<contractor> ode_capd4_bwd_ctcs; for (auto const & ode_ctr : ode_ctrs) { // 2.6.1. Add Forward ODE Pruning (Overapproximation, using CAPD4) if (config.nra_ODE_cache) { ode_capd4_fwd_ctcs.emplace_back( mk_contractor_cache( mk_contractor_try( mk_contractor_seq(mk_contractor_capd_full(box, ode_ctr, ode_direction::FWD, config.nra_ODE_taylor_order, config.nra_ODE_grid_size, use_cache, config.nra_ODE_fwd_timeout), nl_ctc)))); } else { ode_capd4_fwd_ctcs.emplace_back( mk_contractor_try( mk_contractor_seq( mk_contractor_capd_full(box, ode_ctr, ode_direction::FWD, config.nra_ODE_taylor_order, config.nra_ODE_grid_size, use_cache, config.nra_ODE_fwd_timeout), nl_ctc))); } } if (!config.nra_ODE_forward_only) { // 2.6.2. Add Backward ODE Pruning (Overapproximation, using CAPD4) for (auto const & ode_ctr : ode_ctrs_rev) { if (config.nra_ODE_cache) { ode_capd4_bwd_ctcs.emplace_back( mk_contractor_cache( mk_contractor_try( mk_contractor_seq( mk_contractor_capd_full(box, ode_ctr, ode_direction::BWD, config.nra_ODE_taylor_order, config.nra_ODE_grid_size, use_cache, config.nra_ODE_bwd_timeout), nl_ctc)))); } else { ode_capd4_bwd_ctcs.emplace_back( mk_contractor_try( mk_contractor_seq( mk_contractor_capd_full(box, ode_ctr, ode_direction::BWD, config.nra_ODE_taylor_order, config.nra_ODE_grid_size, use_cache, config.nra_ODE_bwd_timeout), nl_ctc))); } } } if (config.nra_ODE_sampling) { if (config.nra_parallel) { ctcs.push_back(mk_contractor_parallel_any( mk_contractor_try_or(mk_contractor_throw_if_empty(mk_contractor_seq(ode_gsl_ctcs)), mk_contractor_empty()), mk_contractor_seq(mk_contractor_seq(ode_capd4_fwd_ctcs), mk_contractor_seq(ode_capd4_bwd_ctcs)))); } else { ctcs.push_back( mk_contractor_try_or( // Try Underapproximation(GSL) if it fails try Overapproximation(CAPD4) mk_contractor_throw_if_empty(mk_contractor_seq(ode_gsl_ctcs)), mk_contractor_seq(mk_contractor_seq(ode_capd4_fwd_ctcs), mk_contractor_seq(ode_capd4_bwd_ctcs)))); } } else { ctcs.insert(ctcs.end(), ode_capd4_fwd_ctcs.begin(), ode_capd4_fwd_ctcs.end()); ctcs.insert(ctcs.end(), ode_capd4_bwd_ctcs.begin(), ode_capd4_bwd_ctcs.end()); } } if (complete) { // 2.7 Build Eval contractors vector<contractor> eval_ctcs; for (auto const & nl_ctr : nl_ctrs) { eval_ctcs.push_back(mk_contractor_eval(nl_ctr, use_cache)); } return mk_contractor_seq(mk_contractor_fixpoint(default_strategy::term_cond, ctcs), mk_contractor_seq(eval_ctcs)); } else { return mk_contractor_fixpoint(default_strategy::term_cond, ctcs); } }
box ncbt_icp::solve(box b, contractor & ctc, SMTConfig & config) { thread_local static unordered_set<shared_ptr<constraint>> used_constraints; used_constraints.clear(); static unsigned prune_count = 0; thread_local static vector<box> box_stack; box_stack.clear(); box_stack.push_back(b); do { // Loop Invariant DREAL_LOG_INFO << "ncbt_icp::solve - loop" << "\t" << "box stack Size = " << box_stack.size(); b = box_stack.back(); try { ctc.prune(b, config); auto const this_used_constraints = ctc.used_constraints(); used_constraints.insert(this_used_constraints.begin(), this_used_constraints.end()); if (config.nra_use_stat) { config.nra_stat.increase_prune(); } } catch (contractor_exception & e) { // Do nothing } prune_count++; box_stack.pop_back(); if (!b.is_empty()) { // SAT tuple<int, box, box> splits = b.bisect(config.nra_precision); if (config.nra_use_stat) { config.nra_stat.increase_branch(); } int const index = get<0>(splits); if (index >= 0) { box const & first = get<1>(splits); box const & second = get<2>(splits); assert(first.get_idx_last_branched() == index); assert(second.get_idx_last_branched() == index); if (second.is_bisectable()) { box_stack.push_back(second); box_stack.push_back(first); } else { box_stack.push_back(first); box_stack.push_back(second); } } else { break; } } else { // UNSAT (b is emptified by pruning operators) // If this bisect_var is not used in all used // constraints, this box is safe to be popped. thread_local static unordered_set<Enode *> used_vars; used_vars.clear(); for (auto used_ctr : used_constraints) { auto this_used_vars = used_ctr->get_vars(); used_vars.insert(this_used_vars.begin(), this_used_vars.end()); } while (box_stack.size() > 0) { int const bisect_var = box_stack.back().get_idx_last_branched(); assert(bisect_var >= 0); // If this bisect_var is not used in all used // constraints, this box is safe to be popped. if (used_vars.find(b.get_vars()[bisect_var]) != used_vars.end()) { // DREAL_LOG_FATAL << b.get_vars()[bisect_var] << " is used in " // << *used_ctr << " and it's not safe to skip"; break; } // DREAL_LOG_FATAL << b.get_vars()[bisect_var] << " is not used and it's safe to skip this box" // << " (" << box_stack.size() << ")"; box_stack.pop_back(); } } } while (box_stack.size() > 0); DREAL_LOG_DEBUG << "prune count = " << prune_count; ctc.set_used_constraints(used_constraints); return b; }
void contractor_generic_forall::handle_disjunction(box & b, vector<Enode *> const &vec, bool const p, SMTConfig & config) { DREAL_LOG_DEBUG << "contractor_generic_forall::handle_disjunction" << endl; unordered_set<Enode *> forall_vars; for (Enode * e : vec) { std::unordered_set<Enode *> const & vars = e->get_forall_vars(); forall_vars.insert(vars.begin(), vars.end()); } unordered_map<Enode*, ibex::Interval> subst; if (!forall_vars.empty()) { // Step 2. Find a counter-example // Solve(¬ l_1 ∧ ¬ l_2 ∧ ... ∧ ¬ l_n) // // Make each ¬ l_i as a contractor ctc_i // Make a fixed_point contractor with ctc_is. // Pass it to icp::solve box counterexample = find_CE(b, forall_vars, vec, p, config); if (counterexample.is_empty()) { // Step 2.1. (NO Counterexample) // Return B. DREAL_LOG_DEBUG << "handle_disjunction: no counterexample found." << endl << "current box = " << endl << b << endl; return; } else { // Step 2.2. (There IS a counterexample C) // // Using C, prune B. // // We've found a counterexample (c1, c2) where ¬ f(c1, c2) holds // Prune X using a point 'y = c2'. (technically, a point in c2, which is an interval) subst = make_subst_from_value(counterexample, forall_vars); } } // Step 3. Compute B_i = prune(B, l_i) // Update B with ∨ B_i // i thread_local static vector<box> boxes; boxes.clear(); auto vars = b.get_vars(); unordered_set<Enode*> const var_set(vars.begin(), vars.end()); for (Enode * e : vec) { if (!e->get_exist_vars().empty()) { lbool polarity = p ? l_True : l_False; if (e->isNot()) { polarity = !polarity; e = e->get1st(); } auto ctr = make_shared<nonlinear_constraint>(e, var_set, polarity, subst); if (ctr->get_var_array().size() == 0) { auto result = ctr->eval(b); if (result.first != false) { boxes.emplace_back(b); } } else { contractor ctc = mk_contractor_ibex_fwdbwd(ctr); box bt(b); ctc.prune(bt, config); m_output.union_with(ctc.output()); unordered_set<shared_ptr<constraint>> const & used_ctrs = ctc.used_constraints(); m_used_constraints.insert(used_ctrs.begin(), used_ctrs.end()); boxes.emplace_back(bt); } } } b = hull(boxes); return; }