From 0a3f95bdaaedb22d7357debd697bbecd9cd44ad2 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 14 Dec 2012 16:54:59 -0800 Subject: [PATCH 01/78] quantifiers Signed-off-by: Nikolaj Bjorner --- src/muz_qe/dl_bmc_engine.cpp | 2 +- src/muz_qe/dl_mk_extract_quantifiers2.cpp | 13 +++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/muz_qe/dl_bmc_engine.cpp b/src/muz_qe/dl_bmc_engine.cpp index b5ffa808a..a01d4c322 100644 --- a/src/muz_qe/dl_bmc_engine.cpp +++ b/src/muz_qe/dl_bmc_engine.cpp @@ -459,7 +459,7 @@ namespace datalog { void setup() { b.m_fparams.m_model = true; b.m_fparams.m_model_compact = true; - b.m_fparams.m_mbqi = true; + // b.m_fparams.m_mbqi = true; b.m_fparams.m_relevancy_lvl = 2; } diff --git a/src/muz_qe/dl_mk_extract_quantifiers2.cpp b/src/muz_qe/dl_mk_extract_quantifiers2.cpp index 97976a6be..33b33f0aa 100644 --- a/src/muz_qe/dl_mk_extract_quantifiers2.cpp +++ b/src/muz_qe/dl_mk_extract_quantifiers2.cpp @@ -307,17 +307,18 @@ namespace datalog { bmc bmc(m_ctx); expr_ref_vector fmls(m); - bmc.compile(source, fmls, 0); // TBD: use cancel_eh to terminate without base-case. - bmc.compile(source, fmls, 1); - bmc.compile(source, fmls, 2); -// bmc.compile(source, fmls, 3); - expr_ref query = bmc.compile_query(m_query_pred, 2); + unsigned depth = 2; + // TBD: use cancel_eh to terminate without base-case. + for (unsigned i = 0; i <= depth; ++i) { + bmc.compile(source, fmls, i); + } + expr_ref query = bmc.compile_query(m_query_pred, depth); fmls.push_back(query); smt_params fparams; fparams.m_relevancy_lvl = 0; fparams.m_model = true; fparams.m_model_compact = true; - fparams.m_mbqi = true; + fparams.m_mbqi = false; smt::kernel solver(m, fparams); TRACE("dl", for (unsigned i = 0; i < fmls.size(); ++i) { From 9f2743309f904e4b78e67d8b2381fb28f3031865 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 28 Dec 2012 16:40:29 -0800 Subject: [PATCH 02/78] fix to proof hypothesis removal facility reported by Arie Gurfinkel Signed-off-by: Nikolaj Bjorner --- src/muz_qe/dl_mk_extract_quantifiers2.cpp | 290 ++-------------------- src/muz_qe/dl_mk_extract_quantifiers2.h | 41 +-- src/muz_qe/pdr_dl_interface.cpp | 4 +- src/muz_qe/proof_utils.cpp | 41 ++- 4 files changed, 65 insertions(+), 311 deletions(-) diff --git a/src/muz_qe/dl_mk_extract_quantifiers2.cpp b/src/muz_qe/dl_mk_extract_quantifiers2.cpp index cd13bb44b..5f320fffe 100644 --- a/src/muz_qe/dl_mk_extract_quantifiers2.cpp +++ b/src/muz_qe/dl_mk_extract_quantifiers2.cpp @@ -31,9 +31,7 @@ namespace datalog { m_ctx(ctx), m(ctx.get_manager()), rm(ctx.get_rule_manager()), - m_query_pred(m), - m_quantifiers(m), - m_refs(m) + m_query_pred(m) {} mk_extract_quantifiers2::~mk_extract_quantifiers2() { @@ -44,135 +42,16 @@ namespace datalog { m_query_pred = q; } - bool mk_extract_quantifiers2::matches_signature(func_decl* head, expr_ref_vector const& binding) { - unsigned sz = head->get_arity(); - if (sz != binding.size()) { - return false; - } - for (unsigned i = 0; i < sz; ++i) { - if (head->get_domain(i) != m.get_sort(binding[sz-i-1])) { - return false; - } - } - return true; - } - - bool mk_extract_quantifiers2::matches_quantifier(quantifier* q, expr_ref_vector const& binding) { - unsigned sz = q->get_num_decls(); - if (sz != binding.size()) { - return false; - } - for (unsigned i = 0; i < sz; ++i) { - if (q->get_decl_sort(i) != m.get_sort(binding[sz-i-1])) { - return false; - } - } - return true; - } - - bool mk_extract_quantifiers2::mk_abstract_expr(expr_ref& term) { - if (!is_app(term)) { - return false; - } - expr* r; - if (m_map.find(term, r)) { - term = r; - return true; - } - if (to_app(term)->get_family_id() == null_family_id) { - return false; - } - expr_ref_vector args(m); - expr_ref tmp(m); - for (unsigned i = 0; i < to_app(term)->get_num_args(); ++i) { - tmp = to_app(term)->get_arg(i); - if (!mk_abstract_expr(tmp)) { - return false; - } - args.push_back(tmp); - } - tmp = m.mk_app(to_app(term)->get_decl(), args.size(), args.c_ptr()); - m_refs.push_back(tmp); - m_map.insert(term, tmp); - term = tmp; - return true; - } - - bool mk_extract_quantifiers2::mk_abstract_binding(expr_ref_vector const& binding, expr_ref_vector& result) { - for (unsigned i = 0; i < binding.size(); ++i) { - expr_ref tmp(m); - tmp = binding[i]; - if (!mk_abstract_expr(tmp)) { - return false; - } - result.push_back(tmp); - } - return true; - } - - void mk_extract_quantifiers2::mk_abstraction_map(rule& r, expr_ref_vector const& binding) { - m_map.reset(); - unsigned sz = binding.size(); - SASSERT(sz == r.get_decl()->get_arity()); - for (unsigned i = 0; i < sz; ++i) { - m_map.insert(binding[sz-i-1], r.get_head()->get_arg(i)); - SASSERT(m.get_sort(binding[sz-i-1]) == m.get_sort(r.get_head()->get_arg(i))); - } - // todo: also make bindings for variables in rule body. - } - - void mk_extract_quantifiers2::match_bindings(unsigned i, unsigned j, unsigned k) { - expr_ref_vector resb(m); - rule* r = m_qrules[i]; - quantifier* q = m_quantifiers[i].get(); - expr_ref_vector const& ruleb = m_rule_bindings[i][j]; - expr_ref_vector const& quantb = m_quantifier_bindings[i][k]; - mk_abstraction_map(*r, ruleb); - if (!mk_abstract_binding(quantb, resb)) { - return; - } - expr_ref inst(m), tmp(m); - var_shifter shift(m); - - for (unsigned l = 0; l < resb.size(); ++l) { - tmp = resb[l].get(); - shift(tmp, q->get_num_decls(), tmp); - resb[l] = tmp; - } - - instantiate(m, q, resb.c_ptr(), inst); - if (!m_seen.contains(r)) { - m_seen.insert(r, alloc(obj_hashtable)); - } - obj_hashtable& seen = *m_seen.find(r); - if (seen.contains(inst)) { - return; - } - seen.insert(inst); - m_refs.push_back(inst); - if (!m_quantifier_instantiations.contains(r, q)) { - m_quantifier_instantiations.insert(r, q, alloc(expr_ref_vector, m)); - } - expr_ref_vector* vec = 0; - VERIFY(m_quantifier_instantiations.find(r, q, vec)); - vec->push_back(inst); - TRACE("dl", tout << "matched: " << mk_pp(q, m) << "\n" << mk_pp(inst, m) << "\n";); - } - - app_ref mk_extract_quantifiers2::ensure_app(expr* e) { - if (is_app(e)) { - return app_ref(to_app(e), m); - } - else { - return app_ref(m.mk_eq(e, m.mk_true()), m); - } - } + /* + * + * + */ void mk_extract_quantifiers2::extract(rule& r, rule_set& new_rules) { unsigned utsz = r.get_uninterpreted_tail_size(); unsigned tsz = r.get_tail_size(); - bool has_quantifier = false; expr_ref_vector conjs(m); + quantifier_ref_vector qs(m); for (unsigned i = utsz; i < tsz; ++i) { conjs.push_back(r.get_tail(i)); } @@ -181,112 +60,27 @@ namespace datalog { expr* e = conjs[j].get(); quantifier* q; if (rule_manager::is_forall(m, e, q)) { - m_quantifiers.push_back(q); - m_qrules.push_back(&r); - m_rule_bindings.push_back(vector()); - m_quantifier_bindings.push_back(vector()); - has_quantifier = true; + qs.push_back(q); } } - if (!has_quantifier) { + if (qs.empty()) { new_rules.add_rule(&r); } - } - - void mk_extract_quantifiers2::apply(rule& r, rule_set& new_rules) { - expr_ref_vector tail(m), conjs(m); - expr_ref fml(m); - unsigned utsz = r.get_uninterpreted_tail_size(); - unsigned tsz = r.get_tail_size(); - for (unsigned i = 0; i < utsz; ++i) { - SASSERT(!r.is_neg_tail(i)); - tail.push_back(r.get_tail(i)); - } - bool has_quantifier = false; - for (unsigned i = utsz; i < tsz; ++i) { - conjs.push_back(r.get_tail(i)); - } - datalog::flatten_and(conjs); - for (unsigned j = 0; j < conjs.size(); ++j) { - expr* e = conjs[j].get(); - quantifier* q; - if (rule_manager::is_forall(m, e, q)) { - expr_ref_vector* ls; - if (m_quantifier_instantiations.find(&r,q,ls)) { - tail.append(*ls); - } - has_quantifier = true; - } - else { - tail.push_back(e); - } - } - if (has_quantifier) { - fml = m.mk_implies(m.mk_and(tail.size(), tail.c_ptr()), r.get_head()); - rule_ref_vector rules(rm); - rm.mk_rule(fml, rules, r.name()); - for (unsigned i = 0; i < rules.size(); ++i) { - new_rules.add_rule(rules[i].get()); - } + else { + m_quantifiers.insert(&r, new quantifier_ref_vector(qs)); + // TODO } } -#if 0 - class mk_extract_quantifiers2::instance_plugin : public smt::quantifier_instance_plugin { - mk_extract_quantifiers2& ex; - ast_manager& m; - expr_ref_vector m_refs; - obj_hashtable m_bindings; - public: - instance_plugin(mk_extract_quantifiers2& ex): ex(ex), m(ex.m), m_refs(m) {} - - virtual void operator()(quantifier* q, unsigned num_bindings, smt::enode*const* bindings) { - expr_ref_vector binding(m); - ptr_vector sorts; - for (unsigned i = 0; i < num_bindings; ++i) { - binding.push_back(bindings[i]->get_owner()); - sorts.push_back(m.get_sort(binding[i].get())); - } - func_decl* f = m.mk_func_decl(symbol("T"), sorts.size(), sorts.c_ptr(), m.mk_bool_sort()); - expr_ref tup(m); - tup = m.mk_app(f, binding.size(), binding.c_ptr()); - if (!m_bindings.contains(tup)) { - m_bindings.insert(tup); - m_refs.push_back(tup); - ex.m_bindings.push_back(binding); - TRACE("dl", tout << "insert\n" << mk_pp(q, m) << "\n" << mk_pp(tup, m) << "\n";); - } - } - }; - -#endif void mk_extract_quantifiers2::reset() { - { - obj_pair_map::iterator - it = m_quantifier_instantiations.begin(), - end = m_quantifier_instantiations.end(); - for (; it != end; ++it) { - dealloc(it->get_value()); - } + obj_map::iterator it = m_quantifiers.begin(), + end = m_quantifiers.end(); + for (; it != end; ++it) { + dealloc(it->m_value); } - { - obj_map*>::iterator - it = m_seen.begin(), - end = m_seen.end(); - for (; it != end; ++it) { - dealloc(it->m_value); - } - } - m_quantifier_instantiations.reset(); - m_seen.reset(); m_has_quantifiers = false; m_quantifiers.reset(); - m_qrules.reset(); - m_bindings.reset(); - m_rule_bindings.reset(); - m_quantifier_bindings.reset(); - m_refs.reset(); } rule_set * mk_extract_quantifiers2::operator()(rule_set const & source, model_converter_ref& mc, proof_converter_ref& pc) { @@ -305,60 +99,6 @@ namespace datalog { extract(**it, *rules); } - bmc bmc(m_ctx); - expr_ref_vector fmls(m); - unsigned depth = 2; - // TBD: use cancel_eh to terminate without base-case. - for (unsigned i = 0; i <= depth; ++i) { - bmc.compile(source, fmls, i); - } - expr_ref query = bmc.compile_query(m_query_pred, depth); - fmls.push_back(query); - smt_params fparams; - fparams.m_relevancy_lvl = 0; - fparams.m_model = true; - fparams.m_model_compact = true; - fparams.m_mbqi = false; - smt::kernel solver(m, fparams); - TRACE("dl", - for (unsigned i = 0; i < fmls.size(); ++i) { - tout << mk_pp(fmls[i].get(), m) << "\n"; - }); - - for (unsigned i = 0; i < fmls.size(); ++i) { - solver.assert_expr(fmls[i].get()); - } -#if 0 - smt::context& ctx = solver.get_context(); - smt::quantifier_manager* qm = ctx.get_quantifier_manager(); - qm->get_plugin()->set_instance_plugin(alloc(instance_plugin, *this)); -#endif - solver.check(); - - for (unsigned i = 0; i < m_bindings.size(); ++i) { - expr_ref_vector& binding = m_bindings[i]; - for (unsigned j = 0; j < m_qrules.size(); ++j) { - rule* r = m_qrules[j]; - if (matches_signature(r->get_decl(), binding)) { - m_rule_bindings[j].push_back(binding); - } - else if (matches_quantifier(m_quantifiers[j].get(), binding)) { - m_quantifier_bindings[j].push_back(binding); - } - } - } - for (unsigned i = 0; i < m_qrules.size(); ++i) { - for (unsigned j = 0; j < m_rule_bindings[i].size(); ++j) { - for (unsigned k = 0; k < m_quantifier_bindings[i].size(); ++k) { - match_bindings(i, j, k); - } - } - } - it = source.begin(); - for (; it != end; ++it) { - apply(**it, *rules); - } - return rules; } diff --git a/src/muz_qe/dl_mk_extract_quantifiers2.h b/src/muz_qe/dl_mk_extract_quantifiers2.h index 30c15b313..320f94c95 100644 --- a/src/muz_qe/dl_mk_extract_quantifiers2.h +++ b/src/muz_qe/dl_mk_extract_quantifiers2.h @@ -31,44 +31,17 @@ namespace datalog { \brief Extract universal quantifiers from rules. */ class mk_extract_quantifiers2 : public rule_transformer::plugin { - context& m_ctx; - ast_manager& m; - rule_manager& rm; - func_decl_ref m_query_pred; - quantifier_ref_vector m_quantifiers; - ptr_vector m_qrules; - vectorm_bindings; - vector > m_rule_bindings; - vector > m_quantifier_bindings; - obj_pair_map m_quantifier_instantiations; - obj_map*> m_seen; - - bool m_has_quantifiers; - obj_map m_map; - expr_ref_vector m_refs; - - class instance_plugin; + context& m_ctx; + ast_manager& m; + rule_manager& rm; + func_decl_ref m_query_pred; + bool m_has_quantifiers; + obj_map m_quantifiers; void reset(); void extract(rule& r, rule_set& new_rules); - void apply(rule& r, rule_set& new_rules); - - app_ref ensure_app(expr* e); - - bool matches_signature(func_decl* head, expr_ref_vector const& binding); - - bool matches_quantifier(quantifier* q, expr_ref_vector const& binding); - - void match_bindings(unsigned i, unsigned j, unsigned k); - - bool mk_abstract_expr(expr_ref& term); - - bool mk_abstract_binding(expr_ref_vector const& binding, expr_ref_vector& result); - - void mk_abstraction_map(rule& r, expr_ref_vector const& binding); - public: /** \brief Create rule transformer that extracts universal quantifiers (over recursive predicates). @@ -83,6 +56,8 @@ namespace datalog { bool has_quantifiers() { return m_has_quantifiers; } + obj_map& quantifiers() { return m_quantifiers; } + }; }; diff --git a/src/muz_qe/pdr_dl_interface.cpp b/src/muz_qe/pdr_dl_interface.cpp index 0482ece05..ba975c8c1 100644 --- a/src/muz_qe/pdr_dl_interface.cpp +++ b/src/muz_qe/pdr_dl_interface.cpp @@ -25,7 +25,7 @@ Revision History: #include "dl_mk_rule_inliner.h" #include "dl_rule.h" #include "dl_rule_transformer.h" -#include "dl_mk_extract_quantifiers.h" +#include "dl_mk_extract_quantifiers2.h" #include "smt2parser.h" #include "pdr_context.h" #include "pdr_dl_interface.h" @@ -146,7 +146,7 @@ lbool dl_interface::query(expr * query) { } } // remove universal quantifiers from body. - datalog::mk_extract_quantifiers* extract_quantifiers = alloc(datalog::mk_extract_quantifiers, m_ctx); + datalog::mk_extract_quantifiers2* extract_quantifiers = alloc(datalog::mk_extract_quantifiers2, m_ctx); datalog::rule_transformer extract_q_tr(m_ctx); extract_q_tr.register_plugin(extract_quantifiers); m_ctx.transform_rules(extract_q_tr, mc, pc); diff --git a/src/muz_qe/proof_utils.cpp b/src/muz_qe/proof_utils.cpp index 1e837b578..0b24d6c8b 100644 --- a/src/muz_qe/proof_utils.cpp +++ b/src/muz_qe/proof_utils.cpp @@ -223,7 +223,7 @@ public: found_false = true; break; } - // SASSERT(m.get_fact(tmp) == m.get_fact(m.get_parent(p, i))); + SASSERT(m.get_fact(tmp) == m.get_fact(m.get_parent(p, i))); parents.push_back(tmp); if (is_closed(tmp) && !m_units.contains(m.get_fact(tmp))) { m_units.insert(m.get_fact(tmp), tmp); @@ -235,6 +235,7 @@ public: break; } tmp = m.get_parent(p, 0); + expr* old_clause = m.get_fact(tmp); elim(tmp); parents[0] = tmp; expr* clause = m.get_fact(tmp); @@ -244,6 +245,31 @@ public: pop(); break; } + // + // case where clause is a literal in the old clause. + // + if (is_literal_in_clause(clause, old_clause)) { + bool found = false; + for (unsigned i = 1; !found && i < parents.size(); ++i) { + if (m.is_complement(clause, m.get_fact(parents[i].get()))) { + parents[1] = parents[i]; + parents.resize(2); + result = m.mk_unit_resolution(parents.size(), parents.c_ptr()); + m_refs.push_back(result); + add_hypotheses(result); + found = true; + } + } + if (!found) { + result = parents[0].get(); + } + pop(); + break; + } + // + // case where new clause is a subset of old clause. + // the literals in clause should be a subset of literals in old_clause. + // get_literals(clause); for (unsigned i = 1; i < parents.size(); ++i) { bool found = false; @@ -309,6 +335,19 @@ public: m_cache.insert(p, result); p = result; } + + bool is_literal_in_clause(expr* fml, expr* clause) { + if (!m.is_or(clause)) { + return false; + } + app* cl = to_app(clause); + for (unsigned i = 0; i < cl->get_num_args(); ++i) { + if (cl->get_argi(i) == fml) { + return true; + } + } + return false; + } }; void proof_utils::reduce_hypotheses(proof_ref& pr) { From d318aab7d1abea500cf47b0fb410ae21b5d6126d Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 2 Jan 2013 09:49:27 -0800 Subject: [PATCH 03/78] experiments wtih QHC Signed-off-by: Nikolaj Bjorner --- src/muz_qe/dl_context.cpp | 4 + src/muz_qe/dl_mk_extract_quantifiers.cpp | 370 +++++++++++++++++----- src/muz_qe/dl_mk_extract_quantifiers.h | 52 ++- src/muz_qe/dl_mk_extract_quantifiers2.cpp | 107 ------- src/muz_qe/dl_mk_extract_quantifiers2.h | 66 ---- src/muz_qe/pdr_dl_interface.cpp | 9 +- src/muz_qe/pdr_quantifiers.cpp | 45 +-- src/muz_qe/pdr_quantifiers.h | 15 +- src/muz_qe/proof_utils.cpp | 2 +- 9 files changed, 379 insertions(+), 291 deletions(-) delete mode 100644 src/muz_qe/dl_mk_extract_quantifiers2.cpp delete mode 100644 src/muz_qe/dl_mk_extract_quantifiers2.h diff --git a/src/muz_qe/dl_context.cpp b/src/muz_qe/dl_context.cpp index 5581732c8..5784c7be2 100644 --- a/src/muz_qe/dl_context.cpp +++ b/src/muz_qe/dl_context.cpp @@ -1206,7 +1206,11 @@ namespace datalog { for (unsigned i = 0; i < m_rule_fmls.size(); ++i) { ptr_vector sorts; get_free_vars(m_rule_fmls[i].get(), sorts); + while (!sorts.empty() && !sorts.back()) { + sorts.pop_back(); + } if (!sorts.empty()) { + std::cout << "has free vars " << mk_pp(m_rule_fmls[i].get(), m) << "\n"; rm.mk_rule(m_rule_fmls[i].get(), rule_refs, m_rule_names[i]); m_rule_fmls[i] = m_rule_fmls.back(); m_rule_names[i] = m_rule_names.back(); diff --git a/src/muz_qe/dl_mk_extract_quantifiers.cpp b/src/muz_qe/dl_mk_extract_quantifiers.cpp index d2b4f0cec..8a5aa52ec 100644 --- a/src/muz_qe/dl_mk_extract_quantifiers.cpp +++ b/src/muz_qe/dl_mk_extract_quantifiers.cpp @@ -19,6 +19,12 @@ Revision History: #include"dl_mk_extract_quantifiers.h" #include"ast_pp.h" +#include"dl_bmc_engine.h" +#include"smt_quantifier.h" +#include"smt_context.h" +#include"for_each_expr.h" +#include "expr_abstract.h" + namespace datalog { @@ -27,24 +33,16 @@ namespace datalog { rule_transformer::plugin(101, false), m_ctx(ctx), m(ctx.get_manager()), - rm(ctx.get_rule_manager()) + rm(ctx.get_rule_manager()), + m_query_pred(m) {} mk_extract_quantifiers::~mk_extract_quantifiers() { - for (unsigned i = 0; i < m_refs.size(); ++i) { - dealloc(m_refs[i]); - } - m_quantifiers.reset(); - m_refs.reset(); + reset(); } - app_ref mk_extract_quantifiers::ensure_app(expr* e) { - if (is_app(e)) { - return app_ref(to_app(e), m); - } - else { - return app_ref(m.mk_eq(e, m.mk_true()), m); - } + void mk_extract_quantifiers::set_query(func_decl* q) { + m_query_pred = q; } void mk_extract_quantifiers::ensure_predicate(expr* e, unsigned& max_var, app_ref_vector& tail) { @@ -67,85 +65,305 @@ namespace datalog { tail.push_back(m.mk_app(a->get_decl(), args.size(), args.c_ptr())); } + class mk_extract_quantifiers::collect_insts { + ast_manager& m; + ptr_vector m_binding; + vector m_bindings; + ptr_vector m_quantifiers; + public: + collect_insts(ast_manager& m): m(m) { } + void operator()(expr* n) { + expr* not_q_or_i, *e1, *e2, *e3; + if (m.is_quant_inst(n, not_q_or_i, m_binding)) { + VERIFY(m.is_or(not_q_or_i, e1, e2)); + VERIFY(m.is_not(e1, e3)); + SASSERT(is_quantifier(e3)); + m_quantifiers.push_back(to_quantifier(e3)); + m_bindings.push_back(expr_ref_vector(m,m_binding.size(), m_binding.c_ptr())); + m_binding.reset(); + } + else if ((m.is_rewrite(n, e1, e2) || + (m.is_rewrite_star(n) && + (e3 = to_app(n)->get_arg(to_app(n)->get_num_args()-1), + e1 = to_app(e3)->get_arg(0), + e2 = to_app(e3)->get_arg(1), + true))) && + is_quantifier(e1) && m.is_false(e2)) { + quantifier* q = to_quantifier(e1); + m_quantifiers.push_back(q); + m_bindings.push_back(expr_ref_vector(m)); + expr_ref_vector& b = m_bindings.back(); + for (unsigned i = 0; i < q->get_num_decls(); ++i) { + b.push_back(m.mk_fresh_const("V", q->get_decl_sort(i))); + } + } + } + + void reset() { + m_quantifiers.reset(); + m_bindings.reset(); + } + + unsigned size() const { return m_quantifiers.size(); } + ptr_vector const& quantifiers() const { return m_quantifiers; } + vector const& bindings() const { return m_bindings; } + }; + + + /* + * forall y . P1(x,y) & + * forall y . P2(x,y) & + * Body[x] & + * ~H[x] + * forall y . y != binding1 => ~ P1(x,y) + * forall y . y != binding2 => ~ P2(x,y) + */ void mk_extract_quantifiers::extract(rule& r, rule_set& new_rules) { - app_ref_vector tail(m); - quantifier_ref_vector quantifiers(m); unsigned utsz = r.get_uninterpreted_tail_size(); - unsigned tsz = r.get_tail_size(); - var_counter vc(true); - unsigned max_var = vc.get_max_var(r); - for (unsigned i = 0; i < utsz; ++i) { - tail.push_back(r.get_tail(i)); - if (r.is_neg_tail(i)) { - new_rules.add_rule(&r); - return; + expr_ref_vector conjs(m); + quantifier_ref_vector qs(m); + for (unsigned i = utsz; i < r.get_tail_size(); ++i) { + conjs.push_back(r.get_tail(i)); + } + datalog::flatten_and(conjs); + for (unsigned j = 0; j < conjs.size(); ++j) { + expr* e = conjs[j].get(); + quantifier* q; + if (rule_manager::is_forall(m, e, q)) { + qs.push_back(q); + conjs[j] = conjs.back(); + conjs.pop_back(); + --j; } } - var_subst vs(m, true); - for (unsigned i = utsz; i < tsz; ++i) { - app* t = r.get_tail(i); - expr_ref_vector conjs(m); - datalog::flatten_and(t, conjs); - expr_ref qe(m); - quantifier* q = 0; - for (unsigned j = 0; j < conjs.size(); ++j) { - expr* e = conjs[j].get(); - if (rule_manager::is_forall(m, e, q)) { - quantifiers.push_back(q); - expr_ref_vector sub(m); - ptr_vector fv; - unsigned num_decls = q->get_num_decls(); - get_free_vars(q, fv); - for (unsigned k = 0; k < fv.size(); ++k) { - unsigned idx = fv.size()-k-1; - if (!fv[idx]) { - fv[idx] = m.mk_bool_sort(); - } - sub.push_back(m.mk_var(idx, fv[idx])); - } - for (unsigned k = 0; k < num_decls; ++k) { - sub.push_back(m.mk_var(num_decls+max_var-k, q->get_decl_sort(k))); - } - max_var += num_decls; - vs(q->get_expr(), sub.size(), sub.c_ptr(), qe); - ensure_predicate(qe, max_var, tail); - } - else { - tail.push_back(ensure_app(e)); - } - } - } - if (quantifiers.empty()) { + if (qs.empty()) { new_rules.add_rule(&r); } else { - rule_ref new_rule(rm); - TRACE("dl", - tout << mk_pp(r.get_head(), m) << " :- \n"; - for (unsigned i = 0; i < tail.size(); ++i) { - tout << " " << mk_pp(tail[i].get(), m) << "\n"; - }); - new_rule = rm.mk(r.get_head(), tail.size(), tail.c_ptr(), 0, r.name(), false); - quantifier_ref_vector* qs = alloc(quantifier_ref_vector, quantifiers); - m_refs.push_back(qs); - new_rules.add_rule(new_rule); - m_quantifiers.insert(new_rule, qs); + expr_ref fml(m); + expr_ref_vector bindings(m); + obj_map insts; + for (unsigned i = 0; i < qs.size(); ++i) { + insts.insert(qs[i].get(), alloc(expr_ref_vector, m)); + } + + unsigned max_inst = 10; // TODO configuration. + + for (unsigned i = 0; i < max_inst; ++i) { + app_ref_vector sub(m); + rule2formula(r, insts, fml, sub); + bool new_binding = find_instantiations_proof_based(fml, sub, insts, bindings); + if (!new_binding) { + break; + } + } + + expr_ref_vector fmls(m); + for (unsigned i = 0; i < utsz; ++i) { + fmls.push_back(r.get_tail(i)); + } + fmls.append(bindings); + fmls.append(conjs); + fml = m.mk_implies(m.mk_and(fmls.size(), fmls.c_ptr()), r.get_head()); + TRACE("dl", tout << "new rule\n" << mk_pp(fml, m) << "\n";); + rule_ref_vector rules(rm); + rm.mk_rule(fml, rules, r.name()); + for (unsigned i = 0; i < rules.size(); ++i) { + new_rules.add_rule(rules[i].get()); + m_quantifiers.insert(rules[i].get(), alloc(quantifier_ref_vector, qs)); + } + obj_map::iterator it = insts.begin(), end = insts.end(); + for (; it != end; ++it) { + dealloc(it->m_value); + } } } + + void mk_extract_quantifiers::rule2formula( + rule& r, + obj_map const& insts, + expr_ref& fml, + app_ref_vector& sub) + { + expr_ref body(m); + expr_ref_vector fmls(m); + ptr_vector sorts; + var_subst vs(m, false); + obj_map::iterator it = insts.begin(), end = insts.end(); + for (; it != end; ++it) { + quantifier* q = it->m_key; + expr_ref_vector& eqs = *it->m_value; + expr_ref_vector disj(m); + disj.append(eqs); + disj.push_back(m.mk_not(q->get_expr())); + body = m.mk_or(disj.size(), disj.c_ptr()); + fml = m.update_quantifier(q, body); + fmls.push_back(fml); + } + fml = m.mk_or(fmls.size(), fmls.c_ptr()); + fmls.reset(); + fmls.push_back(fml); + for (unsigned i = 0; i < r.get_tail_size(); ++i) { + SASSERT(!r.is_neg_tail(i)); + fmls.push_back(r.get_tail(i)); + } + fmls.push_back(m.mk_not(r.get_head())); + fml = m.mk_and(fmls.size(), fmls.c_ptr()); + + get_free_vars(fml, sorts); + for (unsigned i = 0; i < sorts.size(); ++i) { + if (!sorts[i]) { + sorts[i] = m.mk_bool_sort(); + } + sub.push_back(m.mk_const(symbol(i), sorts[i])); + } + vs(fml, sub.size(), (expr*const*)sub.c_ptr(), fml); + } + + bool mk_extract_quantifiers::find_instantiations_proof_based( + expr* fml, + app_ref_vector const& var_inst, + obj_map& insts, + expr_ref_vector& bindings) + { + datalog::scoped_fine_proof _scp(m); + smt_params fparams; + fparams.m_mbqi = true; // false + fparams.m_soft_timeout = 1000; + smt::kernel solver(m, fparams); + solver.assert_expr(fml); + IF_VERBOSE(1, verbose_stream() << "check\n";); + lbool result = solver.check(); + IF_VERBOSE(1, verbose_stream() << "checked\n";); + TRACE("dl", tout << result << "\n";); + if (result != l_false) { + return false; + } + + map qid_map; + quantifier* q; + + obj_map::iterator it = insts.begin(), end = insts.end(); + for (; it != end; ++it) { + q = it->m_key; + qid_map.insert(q->get_qid(), q); + } + + proof* p = solver.get_proof(); + TRACE("dl", tout << mk_pp(p, m) << "\n";); + collect_insts collector(m); + for_each_expr(collector, p); + ptr_vector const& quants = collector.quantifiers(); + + for (unsigned i = 0; i < collector.size(); ++i) { + symbol qid = quants[i]->get_qid(); + if (!qid_map.find(qid, q)) { + TRACE("dl", tout << "Could not find quantifier " << mk_pp(quants[i], m) << "\n";); + continue; + } + expr_ref_vector const& binding = collector.bindings()[i]; + + TRACE("dl", tout << "Instantiating:\n" << mk_pp(quants[i], m) << "\n"; + for (unsigned j = 0; j < binding.size(); ++j) { + tout << mk_pp(binding[j], m) << " "; + } + tout << "\n";); + + expr_ref_vector instantiation(m); + for (unsigned j = 0; j < binding.size(); ++j) { + instantiation.push_back(binding[j]); + } + add_binding(var_inst, bindings, q, instantiation, insts); + } + + return collector.size() > 0; + } + + void mk_extract_quantifiers::add_binding( + app_ref_vector const& var_inst, + expr_ref_vector& bindings, + quantifier* q, + expr_ref_vector const& instantiation, + obj_map& insts) + { + if (instantiation.size() == q->get_num_decls()) { + // Full binding. + apply_binding(var_inst, bindings, q, instantiation, insts); + } + } + + void mk_extract_quantifiers::apply_binding( + app_ref_vector const& var_inst, + expr_ref_vector& bindings, + quantifier* q, + expr_ref_vector const& instantiation, + obj_map& insts) + { + datalog::scoped_no_proof _scp(m); + expr_ref e(m); + expr_ref_vector eqs(m); + var_subst vs(m, false); + inv_var_shifter invsh(m); + vs(q->get_expr(), instantiation.size(), instantiation.c_ptr(), e); + invsh(e, q->get_num_decls(), e); + expr_ref_vector inst(m); + inst.append(var_inst.size(), (expr*const*)var_inst.c_ptr()); + inst.reverse(); + expr_abstract(m, 0, inst.size(), inst.c_ptr(), e, e); + bindings.push_back(e); + for (unsigned i = 0; i < instantiation.size(); ++i) { + e = instantiation[i]; + e = m.mk_eq(m.mk_var(i, q->get_decl_sort(i)), e); + eqs.push_back(e); + } + e = m.mk_and(eqs.size(), eqs.c_ptr()); + insts.find(q)->push_back(e); + + TRACE("dl", tout << mk_pp(q, m) << "\n"; + tout << "instantiation: "; + for (unsigned i = 0; i < instantiation.size(); ++i) { + tout << mk_pp(instantiation[i], m) << " "; + } + tout << "\n"; + tout << "inst: "; + for (unsigned i = 0; i < var_inst.size(); ++i) { + tout << mk_pp(var_inst[i], m) << " "; + } + tout << "\n"; + tout << mk_pp(bindings.back(), m) << "\n"; + tout << "eqs: " << mk_pp(e, m) << "\n"; + ); + } + + + void mk_extract_quantifiers::reset() { + obj_map::iterator it = m_quantifiers.begin(), + end = m_quantifiers.end(); + for (; it != end; ++it) { + dealloc(it->m_value); + } + m_has_quantifiers = false; + m_quantifiers.reset(); + } rule_set * mk_extract_quantifiers::operator()(rule_set const & source, model_converter_ref& mc, proof_converter_ref& pc) { - m_quantifiers.reset(); - rule_set* rules = alloc(rule_set, m_ctx); + reset(); rule_set::iterator it = source.begin(), end = source.end(); + for (; !m_has_quantifiers && it != end; ++it) { + m_has_quantifiers = (*it)->has_quantifiers(); + } + if (!m_has_quantifiers) { + return 0; + } + + rule_set* rules = alloc(rule_set, m_ctx); + it = source.begin(); for (; it != end; ++it) { extract(**it, *rules); } - if (m_quantifiers.empty()) { - dealloc(rules); - rules = 0; - } - return rules; + + return rules; } }; diff --git a/src/muz_qe/dl_mk_extract_quantifiers.h b/src/muz_qe/dl_mk_extract_quantifiers.h index 5da5d59d7..b32dbc32d 100644 --- a/src/muz_qe/dl_mk_extract_quantifiers.h +++ b/src/muz_qe/dl_mk_extract_quantifiers.h @@ -7,7 +7,8 @@ Module Name: Abstract: - Remove universal quantifiers over interpreted predicates in the body. + Replace universal quantifiers over interpreted predicates in the body + by instantiations mined using bounded model checking search. Author: @@ -22,6 +23,7 @@ Revision History: #include"dl_context.h" #include"dl_rule_set.h" #include"dl_rule_transformer.h" +#include"obj_pair_hashtable.h" namespace datalog { @@ -29,15 +31,41 @@ namespace datalog { \brief Extract universal quantifiers from rules. */ class mk_extract_quantifiers : public rule_transformer::plugin { - context& m_ctx; - ast_manager& m; - rule_manager& rm; - ptr_vector m_refs; + + class collect_insts; + + context& m_ctx; + ast_manager& m; + rule_manager& rm; + func_decl_ref m_query_pred; + bool m_has_quantifiers; obj_map m_quantifiers; + void reset(); + void extract(rule& r, rule_set& new_rules); - app_ref ensure_app(expr* e); + void rule2formula( + rule& r, + obj_map const& insts, + expr_ref& fml, + app_ref_vector& sub); + + + void add_binding( + app_ref_vector const& var_inst, + expr_ref_vector& bindings, + quantifier* q, + expr_ref_vector const& instantiation, + obj_map& insts); + + void apply_binding( + app_ref_vector const& var_inst, + expr_ref_vector& bindings, + quantifier* q, + expr_ref_vector const& instantiation, + obj_map& insts); + public: /** @@ -46,15 +74,23 @@ namespace datalog { mk_extract_quantifiers(context & ctx); virtual ~mk_extract_quantifiers(); + + void set_query(func_decl* q); rule_set * operator()(rule_set const & source, model_converter_ref& mc, proof_converter_ref& pc); + bool has_quantifiers() { return m_has_quantifiers; } + obj_map& quantifiers() { return m_quantifiers; } - bool has_quantifiers() const { return !m_quantifiers.empty(); } - void ensure_predicate(expr* e, unsigned& max_var, app_ref_vector& tail); + bool find_instantiations_proof_based( + expr* fml, + app_ref_vector const& var_inst, + obj_map& insts, + expr_ref_vector& bindings); + }; }; diff --git a/src/muz_qe/dl_mk_extract_quantifiers2.cpp b/src/muz_qe/dl_mk_extract_quantifiers2.cpp deleted file mode 100644 index 5f320fffe..000000000 --- a/src/muz_qe/dl_mk_extract_quantifiers2.cpp +++ /dev/null @@ -1,107 +0,0 @@ -/*++ -Copyright (c) 2012 Microsoft Corporation - -Module Name: - - dl_mk_extract_quantifiers2.cpp - -Abstract: - - Remove universal quantifiers over interpreted predicates in the body. - -Author: - - Nikolaj Bjorner (nbjorner) 2012-11-21 - -Revision History: - ---*/ - -#include"dl_mk_extract_quantifiers2.h" -#include"ast_pp.h" -#include"dl_bmc_engine.h" -#include"smt_quantifier.h" -#include"smt_context.h" - -namespace datalog { - - - mk_extract_quantifiers2::mk_extract_quantifiers2(context & ctx) : - rule_transformer::plugin(101, false), - m_ctx(ctx), - m(ctx.get_manager()), - rm(ctx.get_rule_manager()), - m_query_pred(m) - {} - - mk_extract_quantifiers2::~mk_extract_quantifiers2() { - reset(); - } - - void mk_extract_quantifiers2::set_query(func_decl* q) { - m_query_pred = q; - } - - - /* - * - * - */ - void mk_extract_quantifiers2::extract(rule& r, rule_set& new_rules) { - unsigned utsz = r.get_uninterpreted_tail_size(); - unsigned tsz = r.get_tail_size(); - expr_ref_vector conjs(m); - quantifier_ref_vector qs(m); - for (unsigned i = utsz; i < tsz; ++i) { - conjs.push_back(r.get_tail(i)); - } - datalog::flatten_and(conjs); - for (unsigned j = 0; j < conjs.size(); ++j) { - expr* e = conjs[j].get(); - quantifier* q; - if (rule_manager::is_forall(m, e, q)) { - qs.push_back(q); - } - } - if (qs.empty()) { - new_rules.add_rule(&r); - } - else { - m_quantifiers.insert(&r, new quantifier_ref_vector(qs)); - // TODO - } - } - - - void mk_extract_quantifiers2::reset() { - obj_map::iterator it = m_quantifiers.begin(), - end = m_quantifiers.end(); - for (; it != end; ++it) { - dealloc(it->m_value); - } - m_has_quantifiers = false; - m_quantifiers.reset(); - } - - rule_set * mk_extract_quantifiers2::operator()(rule_set const & source, model_converter_ref& mc, proof_converter_ref& pc) { - reset(); - rule_set::iterator it = source.begin(), end = source.end(); - for (; !m_has_quantifiers && it != end; ++it) { - m_has_quantifiers = (*it)->has_quantifiers(); - } - if (!m_has_quantifiers) { - return 0; - } - - rule_set* rules = alloc(rule_set, m_ctx); - it = source.begin(); - for (; it != end; ++it) { - extract(**it, *rules); - } - - return rules; - } - -}; - - diff --git a/src/muz_qe/dl_mk_extract_quantifiers2.h b/src/muz_qe/dl_mk_extract_quantifiers2.h deleted file mode 100644 index 320f94c95..000000000 --- a/src/muz_qe/dl_mk_extract_quantifiers2.h +++ /dev/null @@ -1,66 +0,0 @@ -/*++ -Copyright (c) 2012 Microsoft Corporation - -Module Name: - - dl_mk_extract_quantifiers2.h - -Abstract: - - Replace universal quantifiers over interpreted predicates in the body - by instantiations mined using bounded model checking search. - -Author: - - Nikolaj Bjorner (nbjorner) 2012-11-21 - -Revision History: - ---*/ -#ifndef _DL_MK_EXTRACT_QUANTIFIERS2_H_ -#define _DL_MK_EXTRACT_QUANTIFIERS2_H_ - -#include"dl_context.h" -#include"dl_rule_set.h" -#include"dl_rule_transformer.h" -#include"obj_pair_hashtable.h" - -namespace datalog { - - /** - \brief Extract universal quantifiers from rules. - */ - class mk_extract_quantifiers2 : public rule_transformer::plugin { - context& m_ctx; - ast_manager& m; - rule_manager& rm; - func_decl_ref m_query_pred; - bool m_has_quantifiers; - obj_map m_quantifiers; - - void reset(); - - void extract(rule& r, rule_set& new_rules); - - public: - /** - \brief Create rule transformer that extracts universal quantifiers (over recursive predicates). - */ - mk_extract_quantifiers2(context & ctx); - - virtual ~mk_extract_quantifiers2(); - - void set_query(func_decl* q); - - rule_set * operator()(rule_set const & source, model_converter_ref& mc, proof_converter_ref& pc); - - bool has_quantifiers() { return m_has_quantifiers; } - - obj_map& quantifiers() { return m_quantifiers; } - - }; - -}; - -#endif /* _DL_MK_EXTRACT_QUANTIFIERS2_H_ */ - diff --git a/src/muz_qe/pdr_dl_interface.cpp b/src/muz_qe/pdr_dl_interface.cpp index ba975c8c1..54a40f8b8 100644 --- a/src/muz_qe/pdr_dl_interface.cpp +++ b/src/muz_qe/pdr_dl_interface.cpp @@ -25,7 +25,7 @@ Revision History: #include "dl_mk_rule_inliner.h" #include "dl_rule.h" #include "dl_rule_transformer.h" -#include "dl_mk_extract_quantifiers2.h" +#include "dl_mk_extract_quantifiers.h" #include "smt2parser.h" #include "pdr_context.h" #include "pdr_dl_interface.h" @@ -146,7 +146,7 @@ lbool dl_interface::query(expr * query) { } } // remove universal quantifiers from body. - datalog::mk_extract_quantifiers2* extract_quantifiers = alloc(datalog::mk_extract_quantifiers2, m_ctx); + datalog::mk_extract_quantifiers* extract_quantifiers = alloc(datalog::mk_extract_quantifiers, m_ctx); datalog::rule_transformer extract_q_tr(m_ctx); extract_q_tr.register_plugin(extract_quantifiers); m_ctx.transform_rules(extract_q_tr, mc, pc); @@ -177,8 +177,9 @@ lbool dl_interface::query(expr * query) { while (true) { result = m_context->solve(); if (result == l_true && extract_quantifiers->has_quantifiers()) { - if (quantifier_mc.check()) { - return l_true; + result = quantifier_mc.check(); + if (result != l_false) { + return result; } // else continue } diff --git a/src/muz_qe/pdr_quantifiers.cpp b/src/muz_qe/pdr_quantifiers.cpp index 9233f6314..23c204801 100644 --- a/src/muz_qe/pdr_quantifiers.cpp +++ b/src/muz_qe/pdr_quantifiers.cpp @@ -154,13 +154,10 @@ namespace pdr { // As & not Body_i is satisfiable // then instantiate with model for parameters to Body_i - bool quantifier_model_checker::find_instantiations(quantifier_ref_vector const& qs, unsigned level) { - return - find_instantiations_proof_based(qs, level); // || - // find_instantiations_qe_based(qs, level); + void quantifier_model_checker::find_instantiations(quantifier_ref_vector const& qs, unsigned level) { + find_instantiations_proof_based(qs, level); } - class collect_insts { ast_manager& m; ptr_vector m_binding; @@ -207,7 +204,7 @@ namespace pdr { }; - bool quantifier_model_checker::find_instantiations_proof_based(quantifier_ref_vector const& qs, unsigned level) { + void quantifier_model_checker::find_instantiations_proof_based(quantifier_ref_vector const& qs, unsigned level) { bool found_instance = false; datalog::scoped_fine_proof _scp(m); @@ -233,10 +230,13 @@ namespace pdr { TRACE("pdr", tout << result << "\n";); - if (result != l_false) { - return false; + if (m_rules_model_check != l_false) { + m_rules_model_check = result; + } + + if (result != l_false) { + return; } - m_rules_model_check = false; map qid_map; quantifier* q; @@ -272,7 +272,12 @@ namespace pdr { add_binding(q, new_binding); found_instance = true; } - return found_instance; + if (found_instance) { + m_rules_model_check = l_false; + } + else if (m_rules_model_check != l_false) { + m_rules_model_check = l_undef; + } } @@ -445,7 +450,7 @@ namespace pdr { qe_lite qe(m); r.get_vars(vars); -#if 1 + if (qis) { quantifier_ref_vector const& qi = *qis; for (unsigned i = 0; i < qi.size(); ++i) { @@ -471,7 +476,7 @@ namespace pdr { body.push_back(m.update_quantifier(q, fml)); } } -#endif + a = r.get_head(); for (unsigned i = 0; i < a->get_num_args(); ++i) { v = m.mk_var(vars.size()+i, m.get_sort(a->get_arg(i))); @@ -584,17 +589,17 @@ namespace pdr { find_instantiations(*qis, level); } - bool quantifier_model_checker::model_check(model_node& root) { + lbool quantifier_model_checker::model_check(model_node& root) { m_instantiations.reset(); m_instantiated_rules.reset(); - m_rules_model_check = true; + m_rules_model_check = l_true; ptr_vector nodes; get_nodes(root, nodes); for (unsigned i = nodes.size(); i > 0; ) { --i; model_check_node(*nodes[i]); } - if (!m_rules_model_check) { + if (m_rules_model_check == l_false) { weaken_under_approximation(); } return m_rules_model_check; @@ -644,12 +649,12 @@ namespace pdr { TRACE("pdr", m_rules.display(tout);); } - bool quantifier_model_checker::check() { - if (model_check(m_ctx.get_root())) { - return true; + lbool quantifier_model_checker::check() { + lbool result = model_check(m_ctx.get_root()); + if (result == l_false) { + refine(); } - refine(); - return false; + return result; } }; diff --git a/src/muz_qe/pdr_quantifiers.h b/src/muz_qe/pdr_quantifiers.h index 2a7dcaf2c..941fab3d9 100644 --- a/src/muz_qe/pdr_quantifiers.h +++ b/src/muz_qe/pdr_quantifiers.h @@ -21,6 +21,7 @@ Revision History: #define _PDR_QUANTIFIERS_H_ #include "ast.h" +#include "lbool.h" #include "dl_rule.h" #include "obj_pair_hashtable.h" @@ -46,7 +47,7 @@ namespace pdr { pred_transformer* m_current_pt; datalog::rule const* m_current_rule; model_node* m_current_node; - bool m_rules_model_check; + lbool m_rules_model_check; app_ref_vector m_instantiations; ptr_vector m_instantiated_rules; @@ -54,13 +55,9 @@ namespace pdr { void weaken_under_approximation(); - bool find_instantiations(quantifier_ref_vector const& qs, unsigned level); + void find_instantiations(quantifier_ref_vector const& qs, unsigned level); - bool find_instantiations_model_based(quantifier_ref_vector const& qs, unsigned level); - - bool find_instantiations_proof_based(quantifier_ref_vector const& qs, unsigned level); - - bool find_instantiations_qe_based(quantifier_ref_vector const& qs, unsigned level); + void find_instantiations_proof_based(quantifier_ref_vector const& qs, unsigned level); void add_binding(quantifier* q, expr_ref_vector& binding); @@ -80,7 +77,7 @@ namespace pdr { 'false' and a set of instantiations that contradict the current model. */ - bool model_check(model_node& root); + lbool model_check(model_node& root); void add_over_approximations(quantifier_ref_vector& qis, model_node& n); @@ -113,7 +110,7 @@ namespace pdr { ~quantifier_model_checker(); - bool check(); + lbool check(); }; }; diff --git a/src/muz_qe/proof_utils.cpp b/src/muz_qe/proof_utils.cpp index 038b08c5c..e699ee356 100644 --- a/src/muz_qe/proof_utils.cpp +++ b/src/muz_qe/proof_utils.cpp @@ -342,7 +342,7 @@ public: } app* cl = to_app(clause); for (unsigned i = 0; i < cl->get_num_args(); ++i) { - if (cl->get_argi(i) == fml) { + if (cl->get_arg(i) == fml) { return true; } } From 51a5d22f238818e8166148f0974ae6aa63cae8d2 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 2 Jan 2013 09:50:31 -0800 Subject: [PATCH 04/78] experiments wtih QHC Signed-off-by: Nikolaj Bjorner --- src/muz_qe/dl_context.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/muz_qe/dl_context.cpp b/src/muz_qe/dl_context.cpp index 5784c7be2..5581732c8 100644 --- a/src/muz_qe/dl_context.cpp +++ b/src/muz_qe/dl_context.cpp @@ -1206,11 +1206,7 @@ namespace datalog { for (unsigned i = 0; i < m_rule_fmls.size(); ++i) { ptr_vector sorts; get_free_vars(m_rule_fmls[i].get(), sorts); - while (!sorts.empty() && !sorts.back()) { - sorts.pop_back(); - } if (!sorts.empty()) { - std::cout << "has free vars " << mk_pp(m_rule_fmls[i].get(), m) << "\n"; rm.mk_rule(m_rule_fmls[i].get(), rule_refs, m_rule_names[i]); m_rule_fmls[i] = m_rule_fmls.back(); m_rule_names[i] = m_rule_names.back(); From edf62481e924ac8cfed58a3f66ba9675eb752deb Mon Sep 17 00:00:00 2001 From: Leonardo de Moura Date: Wed, 2 Jan 2013 16:39:08 -0800 Subject: [PATCH 05/78] Add skeleton for the realclosure package Signed-off-by: Leonardo de Moura --- scripts/mk_project.py | 3 +- src/math/interval/interval.h | 3 +- src/math/realclosure/realclosure.cpp | 586 +++++++++++++++++++++++++++ src/math/realclosure/realclosure.h | 265 ++++++++++++ src/util/array.h | 2 +- 5 files changed, 856 insertions(+), 3 deletions(-) create mode 100644 src/math/realclosure/realclosure.cpp create mode 100644 src/math/realclosure/realclosure.h diff --git a/scripts/mk_project.py b/scripts/mk_project.py index 0ebcfe45d..3da30683e 100644 --- a/scripts/mk_project.py +++ b/scripts/mk_project.py @@ -15,6 +15,7 @@ def init_project_def(): add_lib('sat', ['util']) add_lib('nlsat', ['polynomial', 'sat']) add_lib('interval', ['util'], 'math/interval') + add_lib('realclosure', ['interval'], 'math/realclosure') add_lib('subpaving', ['interval'], 'math/subpaving') add_lib('ast', ['util', 'polynomial']) add_lib('rewriter', ['ast', 'polynomial'], 'ast/rewriter') @@ -59,7 +60,7 @@ def init_project_def(): add_lib('portfolio', ['smtlogic_tactics', 'ufbv_tactic', 'fpa', 'aig_tactic', 'muz_qe', 'sls_tactic', 'subpaving_tactic'], 'tactic/portfolio') add_lib('smtparser', ['portfolio'], 'parsers/smt') API_files = ['z3_api.h', 'z3_algebraic.h', 'z3_polynomial.h'] - add_lib('api', ['portfolio', 'user_plugin', 'smtparser'], + add_lib('api', ['portfolio', 'user_plugin', 'smtparser', 'realclosure'], includes2install=['z3.h', 'z3_v1.h', 'z3_macros.h'] + API_files) add_exe('shell', ['api', 'sat', 'extra_cmds'], exe_name='z3') add_exe('test', ['api', 'fuzzing'], exe_name='test-z3', install=False) diff --git a/src/math/interval/interval.h b/src/math/interval/interval.h index 33f202e06..98e76211e 100644 --- a/src/math/interval/interval.h +++ b/src/math/interval/interval.h @@ -105,10 +105,11 @@ struct interval_deps { template class interval_manager { +public: typedef typename C::numeral_manager numeral_manager; typedef typename numeral_manager::numeral numeral; typedef typename C::interval interval; - +private: C m_c; numeral m_result_lower; numeral m_result_upper; diff --git a/src/math/realclosure/realclosure.cpp b/src/math/realclosure/realclosure.cpp new file mode 100644 index 000000000..ee61f923f --- /dev/null +++ b/src/math/realclosure/realclosure.cpp @@ -0,0 +1,586 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + realclosure.cpp + +Abstract: + + Package for computing with elements of the realclosure of a field containing + - all rationals + - extended with computable transcendental real numbers (e.g., pi and e) + - infinitesimals + +Author: + + Leonardo (leonardo) 2013-01-02 + +Notes: + +--*/ +#include"realclosure.h" +#include"array.h" +#include"mpbq.h" +#include"interval_def.h" + +namespace realclosure { + + // --------------------------------- + // + // Binary rational intervals + // + // --------------------------------- + + class mpbq_config { + mpbq_manager & m_manager; + public: + typedef mpbq_manager numeral_manager; + typedef mpbq numeral; + + struct interval { + numeral m_lower; + numeral m_upper; + unsigned m_lower_inf:1; + unsigned m_upper_inf:1; + }; + + void round_to_minus_inf() {} + void round_to_plus_inf() {} + void set_rounding(bool to_plus_inf) {} + + // Getters + numeral const & lower(interval const & a) const { return a.m_lower; } + numeral const & upper(interval const & a) const { return a.m_upper; } + numeral & lower(interval & a) { return a.m_lower; } + numeral & upper(interval & a) { return a.m_upper; } + bool lower_is_open(interval const & a) const { return true; } + bool upper_is_open(interval const & a) const { return true; } + bool lower_is_inf(interval const & a) const { return a.m_lower_inf; } + bool upper_is_inf(interval const & a) const { return a.m_upper_inf; } + + // Setters + void set_lower(interval & a, numeral const & n) { m_manager.set(a.m_lower, n); } + void set_upper(interval & a, numeral const & n) { m_manager.set(a.m_upper, n); } + void set_lower_is_open(interval & a, bool v) { SASSERT(v); } + void set_upper_is_open(interval & a, bool v) { SASSERT(v); } + void set_lower_is_inf(interval & a, bool v) { a.m_lower_inf = v; } + void set_upper_is_inf(interval & a, bool v) { a.m_upper_inf = v; } + + // Reference to numeral manager + numeral_manager & m() const { return m_manager; } + + mpbq_config(numeral_manager & m):m_manager(m) {} + }; + + typedef interval_manager mpbqi_manager; + typedef mpbqi_manager::interval mpbqi; + + // --------------------------------- + // + // Values: rational and composite + // + // --------------------------------- + + struct value { + unsigned m_ref_count; + bool m_rational; + mpbqi m_interval; // approximation as a binary rational + }; + + struct rational_value : public value { + mpq m_value; + }; + + typedef ptr_array polynomial; + + /** + \brief Reference to a field extension element. + The element can be a transcendental real value, an infinitesimal, or an algebraic extension. + */ + struct extension_ref { + enum kind { + TRANSCENDENTAL = 0, + INFINITESIMAL = 1, + ALGEBRAIC = 2 + }; + unsigned m_kind:2; + unsigned m_idx:30; + }; + + bool operator==(extension_ref const & r1, extension_ref const & r2) { + return r1.m_kind == r2.m_kind && r1.m_idx == r2.m_idx; + } + + bool operator<(extension_ref const & r1, extension_ref const & r2) { + return r1.m_kind < r2.m_kind || (r1.m_kind == r2.m_kind && r1.m_idx == r2.m_idx); + } + + struct composite_value : public value { + polynomial m_numerator; + polynomial m_denominator; + extension_ref m_ext_ref; + bool m_real; + }; + + typedef ptr_vector value_vector; + + // --------------------------------- + // + // Root object definition + // + // --------------------------------- + + typedef int sign; + + typedef std::pair p2s; + + typedef sarray signs; + + struct extension { + unsigned m_ref_count; + char const * m_prefix; + extension(char const * p):m_ref_count(0), m_prefix(p) {} + }; + + struct algebraic : public extension { + polynomial m_p; + mpbqi m_interval; + signs m_signs; + bool m_real; + }; + + struct transcendental : public extension { + mk_interval & m_proc; + transcendental(char const * prefix, mk_interval & p):extension(prefix), m_proc(p) {} + }; + + typedef extension infinitesimal; + + struct manager::imp { + small_object_allocator * m_allocator; + bool m_own_allocator; + unsynch_mpq_manager & m_qm; + mpbq_manager m_bqm; + mpqi_manager m_qim; + mpbqi_manager m_bqim; + value_vector m_to_delete; + ptr_vector m_extensions[3]; + volatile bool m_cancel; + + imp(unsynch_mpq_manager & qm, params_ref const & p, small_object_allocator * a): + m_allocator(a == 0 ? alloc(small_object_allocator, "realclosure") : a), + m_own_allocator(a == 0), + m_qm(qm), + m_bqm(m_qm), + m_qim(m_qm), + m_bqim(m_bqm) { + + m_cancel = false; + } + + ~imp() { + if (m_own_allocator) + dealloc(m_allocator); + } + + unsynch_mpq_manager & qm() { return m_qm; } + mpbq_manager & bqm() { return m_bqm; } + mpqi_manager & qim() { return m_qim; } + mpbqi_manager & bqim() { return m_bqim; } + small_object_allocator & allocator() { return *m_allocator; } + + void cleanup(extension_ref::kind k) { + ptr_vector & exts = m_extensions[k]; + // keep removing unused slots + while (!exts.empty() && exts.back() == 0) { + exts.pop_back(); + } + } + + void set_cancel(bool f) { + m_cancel = f; + } + + void updt_params(params_ref const & p) { + // TODO + } + + void del_polynomial(polynomial & p) { + unsigned sz = p.size(); + for (unsigned i = 0; i < sz; i++) { + dec_ref_core(p[i]); + } + p.finalize(allocator()); + } + + void del_rational(rational_value * v) { + qm().del(v->m_value); + allocator().deallocate(sizeof(rational_value), v); + } + + void del_composite(composite_value * v) { + del_polynomial(v->m_numerator); + del_polynomial(v->m_denominator); + dec_ref_ext(v->m_ext_ref); + allocator().deallocate(sizeof(composite_value), v); + } + + void flush_to_delete() { + while (!m_to_delete.empty()) { + value * v = m_to_delete.back(); + m_to_delete.pop_back(); + if (v->m_rational) + del_rational(static_cast(v)); + else + del_composite(static_cast(v)); + } + } + + void del_algebraic(algebraic * a) { + del_polynomial(a->m_p); + bqim().del(a->m_interval); + unsigned sz = a->m_signs.size(); + for (unsigned i = 0; i < sz; i++) { + del_polynomial(a->m_signs[i].first); + } + allocator().deallocate(sizeof(algebraic), a); + } + + void del_transcendental(transcendental * t) { + allocator().deallocate(sizeof(transcendental), t); + } + + void del_infinitesimal(infinitesimal * i) { + allocator().deallocate(sizeof(infinitesimal), i); + } + + void inc_ref_ext(extension_ref x) { + SASSERT(m_extensions[x.m_kind][x.m_idx] != 0); + m_extensions[x.m_kind][x.m_idx]->m_ref_count++; + } + + void dec_ref_ext(extension_ref x) { + SASSERT(m_extensions[x.m_kind][x.m_idx] != 0); + extension * ext = m_extensions[x.m_kind][x.m_idx]; + SASSERT(ext->m_ref_count > 0); + ext->m_ref_count--; + if (ext->m_ref_count == 0) { + m_extensions[x.m_kind][x.m_idx] = 0; + switch (x.m_kind) { + case extension_ref::TRANSCENDENTAL: del_transcendental(static_cast(ext)); break; + case extension_ref::INFINITESIMAL: del_infinitesimal(static_cast(ext)); break; + case extension_ref::ALGEBRAIC: del_algebraic(static_cast(ext)); break; + } + } + } + + void inc_ref(value * v) { + if (v) + v->m_ref_count++; + } + + void dec_ref_core(value * v) { + if (v) { + SASSERT(v->m_ref_count > 0); + v->m_ref_count--; + if (v->m_ref_count == 0) + m_to_delete.push_back(v); + } + } + + void dec_ref(value * v) { + dec_ref_core(v); + flush_to_delete(); + } + + void del(numeral & a) { + dec_ref(a.m_value); + a.m_value = 0; + } + + void mk_infinitesimal(char const * p, numeral & r) { + // TODO + } + + void mk_transcendental(char const * p, mk_interval & proc, numeral & r) { + // TODO + } + + void isolate_roots(char const * p, unsigned n, numeral const * as, numeral_vector & roots) { + // TODO + } + + void reset(numeral & a) { + // TODO + } + + int sign(numeral const & a) { + // TODO + return 0; + } + + bool is_int(numeral const & a) { + // TODO + return false; + } + + bool is_real(numeral const & a) { + // TODO + return false; + } + + void set(numeral & a, int n) { + // TODO + } + + void set(numeral & a, mpz const & n) { + // TODO + } + + void set(numeral & a, mpq const & n) { + // TODO + } + + void set(numeral & a, numeral const & n) { + // TODO + } + + void root(numeral const & a, unsigned k, numeral & b) { + // TODO + } + + void power(numeral const & a, unsigned k, numeral & b) { + // TODO + } + + void add(numeral const & a, numeral const & b, numeral & c) { + // TODO + } + + void sub(numeral const & a, numeral const & b, numeral & c) { + // TODO + } + + void mul(numeral const & a, numeral const & b, numeral & c) { + // TODO + } + + void neg(numeral & a) { + // TODO + } + + void inv(numeral & a) { + // TODO + } + + void div(numeral const & a, numeral const & b, numeral & c) { + // TODO + } + + int compare(numeral const & a, numeral const & b) { + // TODO + return 0; + } + + void select(numeral const & prev, numeral const & next, numeral & result) { + // TODO + } + + void display(std::ostream & out, numeral const & a) const { + // TODO + } + + void display_decimal(std::ostream & out, numeral const & a, unsigned precision) const { + // TODO + } + }; + + + manager::manager(unsynch_mpq_manager & m, params_ref const & p, small_object_allocator * a) { + m_imp = alloc(imp, m, p, a); + } + + manager::~manager() { + dealloc(m_imp); + } + + void manager::get_param_descrs(param_descrs & r) { + // TODO + } + + void manager::set_cancel(bool f) { + m_imp->set_cancel(f); + } + + void manager::updt_params(params_ref const & p) { + m_imp->updt_params(p); + } + + unsynch_mpq_manager & manager::qm() const { + return m_imp->m_qm; + } + + void manager::del(numeral & a) { + m_imp->del(a); + } + + void manager::mk_infinitesimal(char const * p, numeral & r) { + m_imp->mk_infinitesimal(p, r); + } + + void manager::mk_transcendental(char const * p, mk_interval & proc, numeral & r) { + m_imp->mk_transcendental(p, proc, r); + } + + void manager::isolate_roots(char const * p, unsigned n, numeral const * as, numeral_vector & roots) { + m_imp->isolate_roots(p, n, as, roots); + } + + void manager::reset(numeral & a) { + m_imp->reset(a); + } + + int manager::sign(numeral const & a) { + return m_imp->sign(a); + } + + bool manager::is_zero(numeral const & a) { + return sign(a) == 0; + } + + bool manager::is_pos(numeral const & a) { + return sign(a) > 0; + } + + bool manager::is_neg(numeral const & a) { + return sign(a) < 0; + } + + bool manager::is_int(numeral const & a) { + return m_imp->is_int(a); + } + + bool manager::is_real(numeral const & a) { + return m_imp->is_real(a); + } + + void manager::set(numeral & a, int n) { + m_imp->set(a, n); + } + + void manager::set(numeral & a, mpz const & n) { + m_imp->set(a, n); + } + + void manager::set(numeral & a, mpq const & n) { + m_imp->set(a, n); + } + + void manager::set(numeral & a, numeral const & n) { + m_imp->set(a, n); + } + + void manager::swap(numeral & a, numeral & b) { + std::swap(a.m_value, b.m_value); + } + + void manager::root(numeral const & a, unsigned k, numeral & b) { + m_imp->root(a, k, b); + } + + void manager::power(numeral const & a, unsigned k, numeral & b) { + m_imp->power(a, k, b); + } + + void manager::add(numeral const & a, numeral const & b, numeral & c) { + m_imp->add(a, b, c); + } + + void manager::add(numeral const & a, mpz const & b, numeral & c) { + scoped_numeral _b(*this); + set(_b, b); + add(a, _b, c); + } + + void manager::sub(numeral const & a, numeral const & b, numeral & c) { + m_imp->sub(a, b, c); + } + + void manager::mul(numeral const & a, numeral const & b, numeral & c) { + m_imp->mul(a, b, c); + } + + void manager::neg(numeral & a) { + m_imp->neg(a); + } + + void manager::inv(numeral & a) { + m_imp->inv(a); + } + + void manager::div(numeral const & a, numeral const & b, numeral & c) { + m_imp->div(a, b, c); + } + + int manager::compare(numeral const & a, numeral const & b) { + return m_imp->compare(a, b); + } + + bool manager::eq(numeral const & a, numeral const & b) { + return compare(a, b) == 0; + } + + bool manager::eq(numeral const & a, mpq const & b) { + scoped_numeral _b(*this); + set(_b, b); + return eq(a, _b); + } + + bool manager::eq(numeral const & a, mpz const & b) { + scoped_numeral _b(*this); + set(_b, b); + return eq(a, _b); + } + + bool manager::lt(numeral const & a, numeral const & b) { + return compare(a, b) < 0; + } + + bool manager::lt(numeral const & a, mpq const & b) { + scoped_numeral _b(*this); + set(_b, b); + return lt(a, _b); + } + + bool manager::lt(numeral const & a, mpz const & b) { + scoped_numeral _b(*this); + set(_b, b); + return lt(a, _b); + } + + bool manager::gt(numeral const & a, mpq const & b) { + scoped_numeral _b(*this); + set(_b, b); + return gt(a, _b); + } + + bool manager::gt(numeral const & a, mpz const & b) { + scoped_numeral _b(*this); + set(_b, b); + return gt(a, _b); + } + + void manager::select(numeral const & prev, numeral const & next, numeral & result) { + m_imp->select(prev, next, result); + } + + void manager::display(std::ostream & out, numeral const & a) const { + m_imp->display(out, a); + } + + void manager::display_decimal(std::ostream & out, numeral const & a, unsigned precision) const { + m_imp->display_decimal(out, a, precision); + } + +}; diff --git a/src/math/realclosure/realclosure.h b/src/math/realclosure/realclosure.h new file mode 100644 index 000000000..ea7e87512 --- /dev/null +++ b/src/math/realclosure/realclosure.h @@ -0,0 +1,265 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + realclosure.h + +Abstract: + + Package for computing with elements of the realclosure of a field containing + - all rationals + - extended with computable transcendental real numbers (e.g., pi and e) + - infinitesimals + +Author: + + Leonardo (leonardo) 2013-01-02 + +Notes: + +--*/ +#ifndef _REALCLOSURE_H_ +#define _REALCLOSURE_H_ + +#include"mpq.h" +#include"params.h" +#include"scoped_numeral.h" +#include"scoped_numeral_vector.h" +#include"interval.h" + +namespace realclosure { + class num; + + typedef interval_manager mpqi_manager; + + class mk_interval { + public: + virtual void operator()(unsigned k, mpqi_manager & im, mpqi_manager::interval & r) = 0; + }; + + class manager { + public: + struct imp; + private: + imp * m_imp; + public: + manager(unsynch_mpq_manager & m, params_ref const & p = params_ref(), small_object_allocator * a = 0); + ~manager(); + typedef num numeral; + typedef svector numeral_vector; + typedef _scoped_numeral scoped_numeral; + typedef _scoped_numeral_vector scoped_numeral_vector; + + static void get_param_descrs(param_descrs & r); + static void collect_param_descrs(param_descrs & r) { get_param_descrs(r); } + + void set_cancel(bool f); + void cancel() { set_cancel(true); } + void reset_cancel() { set_cancel(false); } + + void updt_params(params_ref const & p); + + unsynch_mpq_manager & qm() const; + + void del(numeral & a); + + /** + \brief Add a new infinitesimal to the current field. The new infinitesimal is smaller than any positive element in the field. + */ + void mk_infinitesimal(char const * prefix_name, numeral & r); + void mk_infinitesimal(numeral & r) { mk_infinitesimal("eps", r); } + + /** + \brief Add a new transcendental real value to the field. + The functor \c mk_interval is used to compute approximations of the transcendental value. + This procedure should be used with care, if the value is not really transcendental with respect to the current + field, computations with the new numeral may not terminate. + Example: we extended the field with Pi. Pi is transcendental with respect to a field that contains only algebraic real numbers. + So, this step is fine. Let us call the resultant field F. + Then, we extend the field F with 1 - Pi. 1 - Pi is transcendental with respect to algebraic real numbers, but it is NOT transcendental + with respect to F, since F contains Pi. + */ + void mk_transcendental(char const * prefix_name, mk_interval & proc, numeral & r); + void mk_transcendental(mk_interval & proc, numeral & r) { mk_transcendental("k", proc, r); } + + /** + \brief Isolate the roots of the univariate polynomial as[0] + as[1]*x + ... + as[n-1]*x^{n-1} + The roots are stored in \c roots. + */ + void isolate_roots(char const * prefix_name, unsigned n, numeral const * as, numeral_vector & roots); + void isolate_roots(unsigned n, numeral const * as, numeral_vector & roots) { isolate_roots("a", n, as, roots); } + + /** + \brief a <- 0 + */ + void reset(numeral & a); + + /** + \brief Return the sign of a. + */ + int sign(numeral const & a); + + /** + \brief Return true if a is zero. + */ + bool is_zero(numeral const & a); + + /** + \brief Return true if a is positive. + */ + bool is_pos(numeral const & a); + + /** + \brief Return true if a is negative. + */ + bool is_neg(numeral const & a); + + /** + \brief Return true if a is an integer. + */ + bool is_int(numeral const & a); + + /** + \brief Return true if a is a real number. + */ + bool is_real(numeral const & a); + + /** + \brief a <- n + */ + void set(numeral & a, int n); + void set(numeral & a, mpz const & n); + void set(numeral & a, mpq const & n); + void set(numeral & a, numeral const & n); + + void swap(numeral & a, numeral & b); + + /** + \brief Return a^{1/k} + + Throws an exception if (a is negative and k is even) or (k is zero). + */ + void root(numeral const & a, unsigned k, numeral & b); + + /** + \brief Return a^k + + Throws an exception if 0^0. + */ + void power(numeral const & a, unsigned k, numeral & b); + + /** + \brief c <- a + b + */ + void add(numeral const & a, numeral const & b, numeral & c); + void add(numeral const & a, mpz const & b, numeral & c); + + /** + \brief c <- a - b + */ + void sub(numeral const & a, numeral const & b, numeral & c); + + /** + \brief c <- a * b + */ + void mul(numeral const & a, numeral const & b, numeral & c); + + /** + \brief a <- -a + */ + void neg(numeral & a); + + /** + \brief a <- 1/a if a != 0 + */ + void inv(numeral & a); + + /** + \brief c <- a/b if b != 0 + */ + void div(numeral const & a, numeral const & b, numeral & c); + + /** + Return -1 if a < b + Return 0 if a == b + Return 1 if a > b + */ + int compare(numeral const & a, numeral const & b); + + /** + \brief a == b + */ + bool eq(numeral const & a, numeral const & b); + bool eq(numeral const & a, mpq const & b); + bool eq(numeral const & a, mpz const & b); + + /** + \brief a != b + */ + bool neq(numeral const & a, numeral const & b) { return !eq(a, b); } + bool neq(numeral const & a, mpq const & b) { return !eq(a, b); } + bool neq(numeral const & a, mpz const & b) { return !eq(a, b); } + + /** + \brief a < b + */ + bool lt(numeral const & a, numeral const & b); + bool lt(numeral const & a, mpq const & b); + bool lt(numeral const & a, mpz const & b); + + /** + \brief a > b + */ + bool gt(numeral const & a, numeral const & b) { return lt(b, a); } + bool gt(numeral const & a, mpq const & b); + bool gt(numeral const & a, mpz const & b); + + /** + \brief a <= b + */ + bool le(numeral const & a, numeral const & b) { return !gt(a, b); } + bool le(numeral const & a, mpq const & b) { return !gt(a, b); } + bool le(numeral const & a, mpz const & b) { return !gt(a, b); } + + /** + \brief a >= b + */ + bool ge(numeral const & a, numeral const & b) { return !lt(a, b); } + bool ge(numeral const & a, mpq const & b) { return !lt(a, b); } + bool ge(numeral const & a, mpz const & b) { return !lt(a, b); } + + /** + \brief Store in result a value in the interval (prev, next) + + \pre lt(pre, next) + */ + void select(numeral const & prev, numeral const & next, numeral & result); + + void display(std::ostream & out, numeral const & a) const; + + /** + \brief Display a real number in decimal notation. + A question mark is added based on the precision requested. + This procedure throws an exception if the \c a is not a real. + */ + void display_decimal(std::ostream & out, numeral const & a, unsigned precision = 10) const; + }; + + class value; + class num { + friend class manager; + friend class manager::imp; + value * m_value; + public: + num():m_value(0) {} + }; +}; + +typedef realclosure::manager rcmanager; +typedef rcmanager::numeral rcnumeral; +typedef rcmanager::numeral_vector rcnumeral_vector; +typedef rcmanager::scoped_numeral scoped_rcnumeral; +typedef rcmanager::scoped_numeral_vector scoped_rcnumeral_vector; + +#endif diff --git a/src/util/array.h b/src/util/array.h index 9dfd2fa94..bcc659ca3 100644 --- a/src/util/array.h +++ b/src/util/array.h @@ -122,7 +122,7 @@ public: if (m_data) { if (CallDestructors) destroy_elements(); - a.deallocate(size(), raw_ptr); + a.deallocate(size(), raw_ptr()); m_data = 0; } } From eee4b1a37b4d2adfda65da38b461c7bdb62116ee Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 2 Jan 2013 20:17:33 -0800 Subject: [PATCH 06/78] fix g++ build Signed-off-by: Nikolaj Bjorner --- src/muz_qe/proof_utils.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/muz_qe/proof_utils.cpp b/src/muz_qe/proof_utils.cpp index e699ee356..369c3aae6 100644 --- a/src/muz_qe/proof_utils.cpp +++ b/src/muz_qe/proof_utils.cpp @@ -252,7 +252,7 @@ public: bool found = false; for (unsigned i = 1; !found && i < parents.size(); ++i) { if (m.is_complement(clause, m.get_fact(parents[i].get()))) { - parents[1] = parents[i]; + parents[1] = parents[i].get(); parents.resize(2); result = m.mk_unit_resolution(parents.size(), parents.c_ptr()); m_refs.push_back(result); From 2cc3e3745e0aaa0ea8875625b94954f0e65b716f Mon Sep 17 00:00:00 2001 From: Leonardo de Moura Date: Wed, 2 Jan 2013 21:14:22 -0800 Subject: [PATCH 07/78] Fix gcc compilation error Signed-off-by: Leonardo de Moura --- src/muz_qe/proof_utils.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/muz_qe/proof_utils.cpp b/src/muz_qe/proof_utils.cpp index e699ee356..afa4604f4 100644 --- a/src/muz_qe/proof_utils.cpp +++ b/src/muz_qe/proof_utils.cpp @@ -252,7 +252,7 @@ public: bool found = false; for (unsigned i = 1; !found && i < parents.size(); ++i) { if (m.is_complement(clause, m.get_fact(parents[i].get()))) { - parents[1] = parents[i]; + parents.set(1, parents.get(i)); parents.resize(2); result = m.mk_unit_resolution(parents.size(), parents.c_ptr()); m_refs.push_back(result); From 70baa3c8c968db7b6740a56e4c61f76f3109e950 Mon Sep 17 00:00:00 2001 From: Leonardo de Moura Date: Wed, 2 Jan 2013 21:18:02 -0800 Subject: [PATCH 08/78] Add nlsat.factor option. This is a workaround for the slow factorization procedure. Signed-off-by: Leonardo de Moura --- src/nlsat/nlsat_explain.cpp | 34 ++++++++++++++++++++-------------- src/nlsat/nlsat_explain.h | 1 + src/nlsat/nlsat_params.pyg | 4 +++- src/nlsat/nlsat_solver.cpp | 1 + 4 files changed, 25 insertions(+), 15 deletions(-) diff --git a/src/nlsat/nlsat_explain.cpp b/src/nlsat/nlsat_explain.cpp index 460e07f58..be60fb052 100644 --- a/src/nlsat/nlsat_explain.cpp +++ b/src/nlsat/nlsat_explain.cpp @@ -42,7 +42,8 @@ namespace nlsat { bool m_simplify_cores; bool m_full_dimensional; bool m_minimize_cores; - + bool m_factor; + struct todo_set { polynomial::cache & m_cache; polynomial_ref_vector m_set; @@ -568,21 +569,22 @@ namespace nlsat { elim_vanishing(p); if (is_const(p)) return; -#if 1 - TRACE("nlsat_explain", tout << "adding factors of\n"; display(tout, p); tout << "\n";); - factor(p, m_factors); - polynomial_ref f(m_pm); - for (unsigned i = 0; i < m_factors.size(); i++) { - f = m_factors.get(i); - elim_vanishing(f); - if (!is_const(f)) { - TRACE("nlsat_explain", tout << "adding factor:\n"; display(tout, f); tout << "\n";); - m_todo.insert(f); + if (m_factor) { + TRACE("nlsat_explain", tout << "adding factors of\n"; display(tout, p); tout << "\n";); + factor(p, m_factors); + polynomial_ref f(m_pm); + for (unsigned i = 0; i < m_factors.size(); i++) { + f = m_factors.get(i); + elim_vanishing(f); + if (!is_const(f)) { + TRACE("nlsat_explain", tout << "adding factor:\n"; display(tout, f); tout << "\n";); + m_todo.insert(f); + } } } -#else - m_todo.insert(normalize(p)); -#endif + else { + m_todo.insert(p); + } } /** @@ -1344,6 +1346,10 @@ namespace nlsat { m_imp->m_minimize_cores = f; } + void explain::set_factor(bool f) { + m_imp->m_factor = f; + } + void explain::operator()(unsigned n, literal const * ls, scoped_literal_vector & result) { (*m_imp)(n, ls, result); } diff --git a/src/nlsat/nlsat_explain.h b/src/nlsat/nlsat_explain.h index 5ecb9d385..85c598f73 100644 --- a/src/nlsat/nlsat_explain.h +++ b/src/nlsat/nlsat_explain.h @@ -40,6 +40,7 @@ namespace nlsat { void set_simplify_cores(bool f); void set_full_dimensional(bool f); void set_minimize_cores(bool f); + void set_factor(bool f); /** \brief Given a set of literals ls[0], ... ls[n-1] s.t. diff --git a/src/nlsat/nlsat_params.pyg b/src/nlsat/nlsat_params.pyg index 4faad3379..6b1577113 100644 --- a/src/nlsat/nlsat_params.pyg +++ b/src/nlsat/nlsat_params.pyg @@ -10,5 +10,7 @@ def_module_params('nlsat', ('randomize', BOOL, True, "randomize selection of a witness in nlsat."), ('max_conflicts', UINT, UINT_MAX, "maximum number of conflicts."), ('shuffle_vars', BOOL, False, "use a random variable order."), - ('seed', UINT, 0, "random seed."))) + ('seed', UINT, 0, "random seed."), + ('factor', BOOL, True, "factor polynomials produced during conflict resolution.") + )) diff --git a/src/nlsat/nlsat_solver.cpp b/src/nlsat/nlsat_solver.cpp index 341431556..368e7eb83 100644 --- a/src/nlsat/nlsat_solver.cpp +++ b/src/nlsat/nlsat_solver.cpp @@ -212,6 +212,7 @@ namespace nlsat { m_ism.set_seed(m_random_seed); m_explain.set_simplify_cores(m_simplify_cores); m_explain.set_minimize_cores(min_cores); + m_explain.set_factor(p.factor()); m_am.updt_params(p.p); } From f8f23382dc9f02a6cb9fdfa821f094df3125edf0 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 3 Jan 2013 17:36:21 -0800 Subject: [PATCH 09/78] bug fix: unsound pruning of assumptions. remove deprecated reduce() feature from smt_kernel Signed-off-by: Nikolaj Bjorner --- src/muz_qe/pdr_context.cpp | 4 +-- src/muz_qe/pdr_farkas_learner.cpp | 5 ++++ src/muz_qe/pdr_prop_solver.cpp | 39 ++++++++++++++++++-------- src/muz_qe/pdr_smt_context_manager.cpp | 20 +++++++++++++ src/smt/smt_kernel.cpp | 7 ----- src/smt/smt_kernel.h | 9 ------ 6 files changed, 55 insertions(+), 29 deletions(-) diff --git a/src/muz_qe/pdr_context.cpp b/src/muz_qe/pdr_context.cpp index c8c1e3b2a..89562fd95 100644 --- a/src/muz_qe/pdr_context.cpp +++ b/src/muz_qe/pdr_context.cpp @@ -33,7 +33,6 @@ Notes: #include "pdr_prop_solver.h" #include "pdr_context.h" #include "pdr_generalizers.h" -#include "datatype_decl_plugin.h" #include "for_each_expr.h" #include "dl_rule_set.h" #include "unit_subsumption_tactic.h" @@ -1682,7 +1681,8 @@ namespace pdr { case l_false: { core_generalizer::cores cores; cores.push_back(std::make_pair(cube, uses_level)); - + TRACE("pdr", tout << "cube:\n"; + for (unsigned j = 0; j < cube.size(); ++j) tout << mk_pp(cube[j].get(), m) << "\n";); for (unsigned i = 0; !cores.empty() && i < m_core_generalizers.size(); ++i) { core_generalizer::cores new_cores; for (unsigned j = 0; j < cores.size(); ++j) { diff --git a/src/muz_qe/pdr_farkas_learner.cpp b/src/muz_qe/pdr_farkas_learner.cpp index 06b8f5eeb..b8e043806 100644 --- a/src/muz_qe/pdr_farkas_learner.cpp +++ b/src/muz_qe/pdr_farkas_learner.cpp @@ -398,6 +398,11 @@ namespace pdr { for (unsigned i = 0; i < r->size(); ++i) { lemmas.push_back(r->form(i)); } + TRACE("farkas_simplify_lemmas", + tout << "simplified:\n"; + for (unsigned i = 0; i < lemmas.size(); ++i) { + tout << mk_pp(lemmas[i].get(), m) << "\n"; + }); } diff --git a/src/muz_qe/pdr_prop_solver.cpp b/src/muz_qe/pdr_prop_solver.cpp index 43d75b7bf..daa52992f 100644 --- a/src/muz_qe/pdr_prop_solver.cpp +++ b/src/muz_qe/pdr_prop_solver.cpp @@ -46,7 +46,6 @@ namespace pdr { expr_ref_vector m_assumptions; obj_map m_proxies2expr; obj_map m_expr2proxies; - obj_hashtable m_implies; unsigned m_num_proxies; app * mk_proxy(expr* literal) { @@ -72,7 +71,6 @@ namespace pdr { expr_ref implies(m.mk_or(m.mk_not(res), literal), m); s.m_ctx->assert_expr(implies); m_assumptions.push_back(implies); - m_implies.insert(implies); TRACE("pdr_verbose", tout << "name asserted " << mk_pp(implies, m) << "\n";); return res; } @@ -92,6 +90,19 @@ namespace pdr { m_assumptions.append(conjs); } + expr* apply_accessor( + ptr_vector const& acc, + unsigned j, + func_decl* f, + expr* c) { + if (is_app(c) && to_app(c)->get_decl() == f) { + return to_app(c)->get_arg(j); + } + else { + return m.mk_app(acc[j], c); + } + } + void expand_literals(expr_ref_vector& conjs) { arith_util arith(m); datatype_util dt(m); @@ -100,6 +111,12 @@ namespace pdr { rational r; unsigned bv_size; + TRACE("pdr", + tout << "begin expand\n"; + for (unsigned i = 0; i < conjs.size(); ++i) { + tout << mk_pp(conjs[i].get(), m) << "\n"; + }); + for (unsigned i = 0; i < conjs.size(); ++i) { expr* e = conjs[i].get(); if (m.is_eq(e, e1, e2) && arith.is_int_real(e1)) { @@ -117,10 +134,10 @@ namespace pdr { (m.is_eq(e, val, c) && is_app(val) && dt.is_constructor(to_app(val)))){ func_decl* f = to_app(val)->get_decl(); func_decl* r = dt.get_constructor_recognizer(f); - conjs[i] = m.mk_app(r,c); + conjs[i] = m.mk_app(r, c); ptr_vector const& acc = *dt.get_constructor_accessors(f); - for (unsigned i = 0; i < acc.size(); ++i) { - conjs.push_back(m.mk_eq(m.mk_app(acc[i], c), to_app(val)->get_arg(i))); + for (unsigned j = 0; j < acc.size(); ++j) { + conjs.push_back(m.mk_eq(apply_accessor(acc, j, f, c), to_app(val)->get_arg(j))); } } else if ((m.is_eq(e, c, val) && bv.is_numeral(val, r, bv_size)) || @@ -142,6 +159,11 @@ namespace pdr { } } } + TRACE("pdr", + tout << "end expand\n"; + for (unsigned i = 0; i < conjs.size(); ++i) { + tout << mk_pp(conjs[i].get(), m) << "\n"; + }); } public: @@ -190,12 +212,7 @@ namespace pdr { expr_ref e(m); for (unsigned i = 0; i < es.size(); ++i) { e = es[i].get(); - if (m_implies.contains(e)) { - e = m.mk_true(); - } - else { - rep(e); - } + rep(e); es[i] = e; if (m.is_true(e)) { es[i] = es.back(); diff --git a/src/muz_qe/pdr_smt_context_manager.cpp b/src/muz_qe/pdr_smt_context_manager.cpp index ca308954c..42d4b4c20 100644 --- a/src/muz_qe/pdr_smt_context_manager.cpp +++ b/src/muz_qe/pdr_smt_context_manager.cpp @@ -20,6 +20,7 @@ Revision History: #include "pdr_smt_context_manager.h" #include "has_free_vars.h" #include "ast_pp.h" +#include "ast_smt_pp.h" #include #include "smt_params.h" @@ -78,6 +79,25 @@ namespace pdr { if (!m.is_true(m_pred)) { assumptions.push_back(m_pred); } + TRACE("pdr_check", + { + ast_smt_pp pp(m); + for (unsigned i = 0; i < m_context.size(); ++i) { + pp.add_assumption(m_context.get_formulas()[i]); + } + for (unsigned i = 0; i < assumptions.size(); ++i) { + pp.add_assumption(assumptions[i].get()); + } + pp.display_smt2(tout, m.mk_true()); + + static unsigned lemma_id = 0; + std::ostringstream strm; + strm << "pdr-lemma-" << lemma_id << ".smt2"; + std::ofstream out(strm.str().c_str()); + pp.display_smt2(out, m.mk_true()); + out.close(); + lemma_id++; + }); lbool result = m_context.check(assumptions.size(), assumptions.c_ptr()); if (!m.is_true(m_pred)) { assumptions.pop_back(); diff --git a/src/smt/smt_kernel.cpp b/src/smt/smt_kernel.cpp index 8ecd079bf..d78b82cc6 100644 --- a/src/smt/smt_kernel.cpp +++ b/src/smt/smt_kernel.cpp @@ -69,10 +69,6 @@ namespace smt { return m_kernel.get_asserted_formulas(); } - bool reduce() { - return m_kernel.reduce_assertions(); - } - void push() { TRACE("smt_kernel", tout << "push()\n";); m_kernel.push(); @@ -221,9 +217,6 @@ namespace smt { return m_imp->get_formulas(); } - bool kernel::reduce() { - return m_imp->reduce(); - } void kernel::push() { m_imp->push(); diff --git a/src/smt/smt_kernel.h b/src/smt/smt_kernel.h index 37b044af1..215f11cbf 100644 --- a/src/smt/smt_kernel.h +++ b/src/smt/smt_kernel.h @@ -84,15 +84,6 @@ namespace smt { */ expr * const * get_formulas() const; - /** - \brief Reduce the set of asserted formulas using preprocessors. - Return true if an inconsistency is detected. - - \remark This is mainly used by dl_smt_relation. This method - seens to be misplaced. This is not the right place. - */ - bool reduce(); - /** \brief Create a backtracking point (aka scope level). */ From f324724abccf1640d9641c27e52420695d535cc5 Mon Sep 17 00:00:00 2001 From: Leonardo de Moura Date: Thu, 3 Jan 2013 17:43:48 -0800 Subject: [PATCH 10/78] Fix typo Signed-off-by: Leonardo de Moura --- src/util/mpz.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/mpz.h b/src/util/mpz.h index 72faea771..c7c1a880f 100644 --- a/src/util/mpz.h +++ b/src/util/mpz.h @@ -374,7 +374,7 @@ public: } - // d <- a + b*c + // d <- a - b*c void submul(mpz const & a, mpz const & b, mpz const & c, mpz & d) { if (is_one(b)) { sub(a, c, d); From ed5b1065746a5656b124554e72155f24c96ee1fb Mon Sep 17 00:00:00 2001 From: Leonardo de Moura Date: Thu, 3 Jan 2013 17:45:28 -0800 Subject: [PATCH 11/78] Add support for ref_buffers with different initial sizes. Add shrink and operator= methods. Signed-off-by: Leonardo de Moura --- src/util/ref_buffer.h | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/src/util/ref_buffer.h b/src/util/ref_buffer.h index 762113dd6..99efe46cc 100644 --- a/src/util/ref_buffer.h +++ b/src/util/ref_buffer.h @@ -30,10 +30,10 @@ Revision History: - void dec_ref(T * obj) - void inc_ref(T * obj) */ -template +template class ref_buffer_core : public Ref { protected: - ptr_buffer m_buffer; + ptr_buffer m_buffer; void inc_ref(T * o) { Ref::inc_ref(o); } void dec_ref(T * o) { Ref::dec_ref(o); } @@ -126,6 +126,11 @@ public: m_buffer.resize(sz, 0); } + void shrink(unsigned sz) { + SASSERT(sz <= m_buffer.size()); + resize(sz); + } + // set pos idx with elem. If idx >= size, then expand. void setx(unsigned idx, T * elem) { if (idx >= size()) { @@ -133,19 +138,22 @@ public: } set(idx, elem); } - -private: - // prevent abuse: - ref_buffer_core& operator=(ref_buffer_core const & other); + + ref_buffer_core & operator=(ref_buffer_core const & other) { + if (this == &other) + return *this; + reset(); + append(other); + } }; /** \brief Buffer of managed references */ -template -class ref_buffer : public ref_buffer_core > { - typedef ref_buffer_core > super; +template +class ref_buffer : public ref_buffer_core, INITIAL_SIZE> { + typedef ref_buffer_core, INITIAL_SIZE> super; public: ref_buffer(TManager & m): super(ref_manager_wrapper(m)) { @@ -161,8 +169,8 @@ public: /** \brief Buffer of unmanaged references */ -template -class sref_buffer : public ref_buffer_core > { +template +class sref_buffer : public ref_buffer_core, INITIAL_SIZE> { public: }; From 6160b2891b4755b9efa84aaa3c3d0c977a41fe17 Mon Sep 17 00:00:00 2001 From: Leonardo de Moura Date: Thu, 3 Jan 2013 17:47:23 -0800 Subject: [PATCH 12/78] Change representation for values in the module for encoding infinitesimals, algebraic extensions and transcendal extensions. Implement basic polynomial operations for polynomials in this field Signed-off-by: Leonardo de Moura --- src/math/realclosure/realclosure.cpp | 1076 ++++++++++++++++++++++++-- src/math/realclosure/realclosure.h | 13 +- 2 files changed, 1025 insertions(+), 64 deletions(-) diff --git a/src/math/realclosure/realclosure.cpp b/src/math/realclosure/realclosure.cpp index ee61f923f..3ad7a834a 100644 --- a/src/math/realclosure/realclosure.cpp +++ b/src/math/realclosure/realclosure.cpp @@ -23,6 +23,17 @@ Notes: #include"array.h" #include"mpbq.h" #include"interval_def.h" +#include"obj_ref.h" +#include"ref_vector.h" +#include"ref_buffer.h" + +#ifndef REALCLOSURE_INI_BUFFER_SIZE +#define REALCLOSURE_INI_BUFFER_SIZE 32 +#endif + +#ifndef REALCLOSURE_INI_SEQ_SIZE +#define REALCLOSURE_INI_SEQ_SIZE 256 +#endif namespace realclosure { @@ -85,11 +96,13 @@ namespace realclosure { struct value { unsigned m_ref_count; bool m_rational; - mpbqi m_interval; // approximation as a binary rational + value():m_ref_count(0), m_rational(false) {} + bool is_rational() const { return m_rational; } }; struct rational_value : public value { mpq m_value; + mpbqi m_interval; // approximation as a binary rational }; typedef ptr_array polynomial; @@ -106,21 +119,67 @@ namespace realclosure { }; unsigned m_kind:2; unsigned m_idx:30; + unsigned idx() const { return m_idx; } + kind knd() const { return static_cast(m_kind); } + bool is_algebraic() const { return knd() == ALGEBRAIC; } + bool is_infinitesimal() const { return knd() == INFINITESIMAL; } + bool is_transcendental() const { return knd() == TRANSCENDENTAL; } }; - + bool operator==(extension_ref const & r1, extension_ref const & r2) { - return r1.m_kind == r2.m_kind && r1.m_idx == r2.m_idx; + return r1.knd() == r2.knd() && r1.idx() == r2.idx(); } bool operator<(extension_ref const & r1, extension_ref const & r2) { - return r1.m_kind < r2.m_kind || (r1.m_kind == r2.m_kind && r1.m_idx == r2.m_idx); + return r1.knd() < r2.knd() || (r1.knd() == r2.knd() && r1.idx() < r2.idx()); } - struct composite_value : public value { - polynomial m_numerator; - polynomial m_denominator; - extension_ref m_ext_ref; - bool m_real; + bool operator<=(extension_ref const & r1, extension_ref const & r2) { + return r1.knd() < r2.knd() || (r1.knd() == r2.knd() && r1.idx() <= r2.idx()); + } + + typedef svector extension_ref_vector; + + struct polynomial_expr { + // The values occurring in this polynomial m_p may only contain extensions that are smaller than m_ext_ref. + // The polynomial m_p must not be the constant polynomial on m_ext_ref. That is, + // it contains a nonzero coefficient at position k > 0. It is not a constant polynomial on m_ext_ref. + polynomial m_p; + extension_ref m_ext_ref; + bool m_real; + mpbqi m_interval; // approximation as a binary rational + + extension_ref const & ext_ref() const { return m_ext_ref; } + bool is_real() const { return m_real; } + polynomial const & p() const { return m_p; } + }; + + struct rational_function_value : public value { + // We assume that a null polynomial_expr denotes 1. + // m_numerator OR m_denominator must be different from NULL + polynomial_expr * m_numerator; + polynomial_expr * m_denominator; + + polynomial_expr * num() const { return m_numerator; } + polynomial_expr * den() const { return m_denominator; } + + extension_ref const & ext_ref() const { + if (den() == 0) + return num()->ext_ref(); + else if (num() == 0 || num()->ext_ref() < den()->ext_ref()) + return den()->ext_ref(); + else + return num()->ext_ref(); + } + + bool is_real() const { + if (den() == 0) + return num()->is_real(); + else if (num() == 0) + return den()->is_real(); + else + return num()->is_real() && den()->is_real(); + } }; typedef ptr_vector value_vector; @@ -139,8 +198,7 @@ namespace realclosure { struct extension { unsigned m_ref_count; - char const * m_prefix; - extension(char const * p):m_ref_count(0), m_prefix(p) {} + extension():m_ref_count(0) {} }; struct algebraic : public extension { @@ -148,16 +206,37 @@ namespace realclosure { mpbqi m_interval; signs m_signs; bool m_real; + + polynomial const & p() const { return m_p; } + mpbqi const & interval() const { return m_interval; } + signs const & s() const { return m_signs; } }; struct transcendental : public extension { + symbol m_name; mk_interval & m_proc; - transcendental(char const * prefix, mk_interval & p):extension(prefix), m_proc(p) {} + transcendental(symbol const & n, mk_interval & p):m_name(n), m_proc(p) {} + void display(std::ostream & out) const { + out << m_name; + } }; - typedef extension infinitesimal; + struct infinitesimal : public extension { + symbol m_name; + infinitesimal(symbol const & n):m_name(n) {} + void display(std::ostream & out) const { + if (m_name.is_numerical()) + out << "eps!" << m_name.get_num(); + else + out << m_name; + } + }; struct manager::imp { + typedef ref_vector value_ref_vector; + typedef ref_buffer value_ref_buffer; + typedef obj_ref value_ref; + small_object_allocator * m_allocator; bool m_own_allocator; unsynch_mpq_manager & m_qm; @@ -166,8 +245,51 @@ namespace realclosure { mpbqi_manager m_bqim; value_vector m_to_delete; ptr_vector m_extensions[3]; + value * m_one; volatile bool m_cancel; + struct scoped_polynomial_seq { + typedef ref_buffer value_seq; + value_seq m_seq_coeffs; + sbuffer m_begins; // start position (in m_seq_coeffs) of each polynomial in the sequence + sbuffer m_szs; // size of each polynomial in the sequence + public: + scoped_polynomial_seq(imp & m):m_seq_coeffs(m) {} + + /** + \brief Add a new polynomial to the sequence. + The contents of p is erased. + */ + void push(unsigned sz, value * const * p) { + m_begins.push_back(m_seq_coeffs.size()); + m_szs.push_back(sz); + m_seq_coeffs.append(sz, p); + } + + /** + \brief Return the number of polynomials in the sequence. + */ + unsigned size() const { return m_szs.size(); } + + /** + \brief Return the vector of coefficients for the i-th polynomial in the sequence. + */ + value * const * coeffs(unsigned i) const { + return m_seq_coeffs.c_ptr() + m_begins[i]; + } + + /** + \brief Return the size of the i-th polynomial in the sequence. + */ + unsigned size(unsigned i) const { return m_szs[i]; } + + void reset() { + m_seq_coeffs.reset(); + m_begins.reset(); + m_szs.reset(); + } + }; + imp(unsynch_mpq_manager & qm, params_ref const & p, small_object_allocator * a): m_allocator(a == 0 ? alloc(small_object_allocator, "realclosure") : a), m_own_allocator(a == 0), @@ -175,21 +297,32 @@ namespace realclosure { m_bqm(m_qm), m_qim(m_qm), m_bqim(m_bqm) { - + mpq one(1); + m_one = mk_rational(one); m_cancel = false; } ~imp() { + dec_ref(m_one); if (m_own_allocator) dealloc(m_allocator); } - unsynch_mpq_manager & qm() { return m_qm; } + unsynch_mpq_manager & qm() const { return m_qm; } mpbq_manager & bqm() { return m_bqm; } mpqi_manager & qim() { return m_qim; } mpbqi_manager & bqim() { return m_bqim; } + mpbqi_manager const & bqim() const { return m_bqim; } small_object_allocator & allocator() { return *m_allocator; } + void checkpoint() { + // TODO + } + + value * one() const { + return m_one; + } + void cleanup(extension_ref::kind k) { ptr_vector & exts = m_extensions[k]; // keep removing unused slots @@ -198,15 +331,25 @@ namespace realclosure { } } + unsigned next_transcendental_idx() { + cleanup(extension_ref::TRANSCENDENTAL); + return m_extensions[extension_ref::TRANSCENDENTAL].size(); + } + + unsigned next_infinitesimal_idx() { + cleanup(extension_ref::INFINITESIMAL); + return m_extensions[extension_ref::INFINITESIMAL].size(); + } + void set_cancel(bool f) { m_cancel = f; } - + void updt_params(params_ref const & p) { // TODO } - void del_polynomial(polynomial & p) { + void finalize_polynomial(polynomial & p) { unsigned sz = p.size(); for (unsigned i = 0; i < sz; i++) { dec_ref_core(p[i]); @@ -214,35 +357,44 @@ namespace realclosure { p.finalize(allocator()); } + void del_polynomial_expr(polynomial_expr * p) { + finalize_polynomial(p->m_p); + bqim().del(p->m_interval); + dec_ref_ext(p->m_ext_ref); + allocator().deallocate(sizeof(polynomial_expr), p); + } + void del_rational(rational_value * v) { + bqim().del(v->m_interval); qm().del(v->m_value); allocator().deallocate(sizeof(rational_value), v); } - - void del_composite(composite_value * v) { - del_polynomial(v->m_numerator); - del_polynomial(v->m_denominator); - dec_ref_ext(v->m_ext_ref); - allocator().deallocate(sizeof(composite_value), v); + + void del_rational_function(rational_function_value * v) { + if (v->num()) + del_polynomial_expr(v->num()); + if (v->den()) + del_polynomial_expr(v->den()); + allocator().deallocate(sizeof(rational_function_value), v); } void flush_to_delete() { while (!m_to_delete.empty()) { value * v = m_to_delete.back(); m_to_delete.pop_back(); - if (v->m_rational) + if (v->is_rational()) del_rational(static_cast(v)); else - del_composite(static_cast(v)); + del_rational_function(static_cast(v)); } } void del_algebraic(algebraic * a) { - del_polynomial(a->m_p); + finalize_polynomial(a->m_p); bqim().del(a->m_interval); unsigned sz = a->m_signs.size(); for (unsigned i = 0; i < sz; i++) { - del_polynomial(a->m_signs[i].first); + finalize_polynomial(a->m_signs[i].first); } allocator().deallocate(sizeof(algebraic), a); } @@ -256,18 +408,18 @@ namespace realclosure { } void inc_ref_ext(extension_ref x) { - SASSERT(m_extensions[x.m_kind][x.m_idx] != 0); - m_extensions[x.m_kind][x.m_idx]->m_ref_count++; + SASSERT(m_extensions[x.knd()][x.idx()] != 0); + m_extensions[x.knd()][x.idx()]->m_ref_count++; } void dec_ref_ext(extension_ref x) { - SASSERT(m_extensions[x.m_kind][x.m_idx] != 0); - extension * ext = m_extensions[x.m_kind][x.m_idx]; + SASSERT(m_extensions[x.knd()][x.idx()] != 0); + extension * ext = m_extensions[x.knd()][x.idx()]; SASSERT(ext->m_ref_count > 0); ext->m_ref_count--; if (ext->m_ref_count == 0) { - m_extensions[x.m_kind][x.m_idx] = 0; - switch (x.m_kind) { + m_extensions[x.knd()][x.idx()] = 0; + switch (x.knd()) { case extension_ref::TRANSCENDENTAL: del_transcendental(static_cast(ext)); break; case extension_ref::INFINITESIMAL: del_infinitesimal(static_cast(ext)); break; case extension_ref::ALGEBRAIC: del_algebraic(static_cast(ext)); break; @@ -299,51 +451,247 @@ namespace realclosure { a.m_value = 0; } - void mk_infinitesimal(char const * p, numeral & r) { + static bool is_zero(value * v) { + return v == 0; + } + + static bool is_nz_rational(value * v) { + SASSERT(v != 0); + return v->is_rational(); + } + + bool is_one(value * v) { + return !is_zero(v) && is_nz_rational(v) && qm().is_one(to_mpq(v)); + } + + static bool is_rational_function(value * v) { + SASSERT(v != 0); + return !(v->is_rational()); + } + + static rational_value * to_nz_rational(value * v) { + SASSERT(is_nz_rational(v)); + return static_cast(v); + } + + static rational_function_value * to_rational_function(value * v) { + SASSERT(!is_nz_rational(v)); + return static_cast(v); + } + + static bool is_zero(numeral const & a) { + return is_zero(a.m_value); + } + + static bool is_nz_rational(numeral const & a) { + SASSERT(!is_zero(a)); + return is_nz_rational(a.m_value); + } + + /** + \brief Return true if v is not a shared value. That is, we can perform + destructive updates. + */ + static bool is_unique(value * v) { + SASSERT(v); + return v->m_ref_count <= 1; + } + + static bool is_unique(numeral const & a) { + return is_unique(a.m_value); + } + + static bool is_unique_nz_rational(value * v) { + return is_nz_rational(v) && is_unique(v); + } + + static bool is_unique_nz_rational(numeral const & a) { + return is_unique_nz_rational(a.m_value); + } + + static rational_value * to_nz_rational(numeral const & a) { + SASSERT(is_nz_rational(a)); + return to_nz_rational(a.m_value); + } + + static bool is_rational_function(numeral const & a) { + return is_rational_function(a.m_value); + } + + static rational_function_value * to_rational_function(numeral const & a) { + SASSERT(is_rational_function(a)); + return to_rational_function(a.m_value); + } + + static mpq & to_mpq(value * v) { + SASSERT(is_nz_rational(v)); + return to_nz_rational(v)->m_value; + } + + static mpq & to_mpq(numeral const & a) { + SASSERT(is_nz_rational(a)); + return to_nz_rational(a)->m_value; + } + + static int compare_rank(value * a, value * b) { + SASSERT(a); SASSERT(b); + if (is_nz_rational(a)) + return is_nz_rational(b) ? 0 : -1; + else if (is_nz_rational(b)) { + SASSERT(is_rational_function(a)); + return 1; + } + else if (to_rational_function(a)->ext_ref() == to_rational_function(b)->ext_ref()) + return 0; + else + return to_rational_function(a)->ext_ref() < to_rational_function(b)->ext_ref() ? -1 : 1; + } + + transcendental * to_transcendental(extension_ref const & r) const { + SASSERT(r.is_transcendental()); + return static_cast(m_extensions[r.knd()][r.idx()]); + } + + infinitesimal * to_infinitesimal(extension_ref const & r) const { + SASSERT(r.is_infinitesimal()); + return static_cast(m_extensions[r.knd()][r.idx()]); + } + + algebraic * to_algebraic(extension_ref const & r) const { + SASSERT(r.is_algebraic()); + return static_cast(m_extensions[r.knd()][r.idx()]); + } + + void mk_infinitesimal(symbol const & n, numeral & r) { + // TODO + } + + void mk_infinitesimal(char const * n, numeral & r) { + mk_infinitesimal(symbol(n), r); + } + + void mk_infinitesimal(numeral & r) { + mk_infinitesimal(symbol(next_infinitesimal_idx()), r); + } + + void mk_transcendental(symbol const & n, mk_interval & proc, numeral & r) { // TODO } void mk_transcendental(char const * p, mk_interval & proc, numeral & r) { - // TODO + mk_transcendental(symbol(p), proc, r); } - void isolate_roots(char const * p, unsigned n, numeral const * as, numeral_vector & roots) { + void mk_transcendental(mk_interval & proc, numeral & r) { + mk_transcendental(symbol(next_transcendental_idx()), proc, r); + } + + void isolate_roots(unsigned n, numeral const * as, numeral_vector & roots) { // TODO } void reset(numeral & a) { - // TODO + del(a); + SASSERT(is_zero(a)); } + int sign(polynomial_expr * p) { + if (p == 0) { + // Remark: The null polynomial expression denotes 1 + return 1; + } + else { + SASSERT(!bqim().contains_zero(p->m_interval)); + return bqim().is_P(p->m_interval) ? 1 : -1; + } + } + int sign(numeral const & a) { - // TODO - return 0; + if (is_zero(a)) + return 0; + else if (is_nz_rational(a)) { + return qm().is_pos(to_mpq(a)) ? 1 : -1; + } + else { + rational_function_value * v = to_rational_function(a); + return sign(v->num()) * sign(v->den()); + } + } + + bool is_int(numeral const & a) { + if (is_zero(a)) + return true; + else if (is_nz_rational(a)) + return qm().is_int(to_mpq(a)); + else { + // TODO + return false; + } + } + + bool is_real(numeral const & a) const { + if (is_zero(a) || is_nz_rational(a)) + return true; + else + return to_rational_function(a)->is_real(); } - bool is_int(numeral const & a) { - // TODO - return false; + rational_value * mk_rational() { + return new (allocator()) rational_value(); } - - bool is_real(numeral const & a) { - // TODO - return false; + + rational_value * mk_rational(mpq & v) { + rational_value * r = mk_rational(); + ::swap(r->m_value, v); + return r; } - + void set(numeral & a, int n) { - // TODO + if (n == 0) { + reset(a); + return; + } + + if (!is_unique_nz_rational(a)) { + del(a); + a.m_value = mk_rational(); + } + SASSERT(is_unique_nz_rational(a)); + qm().set(to_mpq(a), n); } void set(numeral & a, mpz const & n) { - // TODO + if (qm().is_zero(n)) { + reset(a); + return; + } + + if (!is_unique_nz_rational(a)) { + del(a); + a.m_value = mk_rational(); + } + SASSERT(is_unique_nz_rational(a)); + qm().set(to_mpq(a), n); } void set(numeral & a, mpq const & n) { - // TODO + if (qm().is_zero(n)) { + reset(a); + return; + } + + if (!is_unique_nz_rational(a)) { + del(a); + a.m_value = mk_rational(); + } + SASSERT(is_unique_nz_rational(a)); + qm().set(to_mpq(a), n); } void set(numeral & a, numeral const & n) { - // TODO + inc_ref(n.m_value); + dec_ref(a.m_value); + a.m_value = n.m_value; } void root(numeral const & a, unsigned k, numeral & b) { @@ -353,7 +701,414 @@ namespace realclosure { void power(numeral const & a, unsigned k, numeral & b) { // TODO } + + /** + \brief Remove 0s + */ + void adjust_size(value_ref_buffer & r) { + while (!r.empty() && r.back() == 0) { + r.pop_back(); + } + } + + /** + \brief r <- p1 + p2 + */ + void add(unsigned sz1, value * const * p1, unsigned sz2, value * const * p2, value_ref_buffer & r) { + r.reset(); + unsigned min = std::min(sz1, sz2); + for (unsigned i = 0; i < min; i++) + r.push_back(add(p1[i], p2[i])); + for (unsigned i = 0; i < sz1; i++) + r.push_back(p1[i]); + for (unsigned i = 0; i < sz2; i++) + r.push_back(p2[i]); + adjust_size(r); + } + + /** + \brief r <- p1 - p2 + */ + void sub(unsigned sz1, value * const * p1, unsigned sz2, value * const * p2, value_ref_buffer & r) { + r.reset(); + unsigned min = std::min(sz1, sz2); + for (unsigned i = 0; i < min; i++) + r.push_back(sub(p1[i], p2[i])); + for (unsigned i = 0; i < sz1; i++) + r.push_back(p1[i]); + for (unsigned i = 0; i < sz2; i++) + r.push_back(neg(p2[i])); + adjust_size(r); + } + + /** + \brief r <- a * p + */ + void mul(value * a, unsigned sz, value * const * p, value_ref_buffer & r) { + r.reset(); + if (a == 0) + return; + for (unsigned i = 0; i < sz; i++) + r.push_back(mul(a, p[i])); + } + + /** + \brief r <- p1 * p2 + */ + void mul(unsigned sz1, value * const * p1, unsigned sz2, value * const * p2, value_ref_buffer & r) { + r.reset(); + unsigned sz = sz1*sz2; + r.resize(sz); + if (sz1 < sz2) { + std::swap(sz1, sz2); + std::swap(p1, p2); + } + value_ref tmp(*this); + for (unsigned i = 0; i < sz1; i++) { + checkpoint(); + if (p1[i] == 0) + continue; + for (unsigned j = 0; j < sz2; j++) { + // r[i+j] <- r[i+j] + p1[i]*p2[j] + tmp = mul(p1[i], p2[j]); + r.set(i+j, add(r[i+j], tmp)); + } + } + adjust_size(r); + } + + /** + \brief p <- p/a + */ + void div(value_ref_buffer & p, value * a) { + SASSERT(!is_zero(a)); + if (is_one(a)) + return; + unsigned sz = p.size(); + for (unsigned i = 0; i < sz; i++) + p.set(i, div(p[i], a)); + } + + /** + \brief q <- quotient(p1, p2); r <- rem(p1, p2) + */ + void div_rem(unsigned sz1, value * const * p1, unsigned sz2, value * const * p2, + value_ref_buffer & q, value_ref_buffer & r) { + SASSERT(sz2 > 0); + if (sz2 == 1) { + q.reset(); q.append(sz1, p1); + div(q, *p2); + r.reset(); + } + else { + q.reset(); + r.reset(); r.append(sz1, p1); + if (sz1 > 1) { + if (sz1 >= sz2) { + q.resize(sz1 - sz2 + 1); + } + else { + SASSERT(q.empty()); + } + value * b_n = p2[sz2-1]; + SASSERT(!is_zero(b_n)); + value_ref ratio(*this); + while (true) { + checkpoint(); + sz1 = r.size(); + if (sz1 < sz2) { + adjust_size(q); + break; + } + unsigned m_n = sz1 - sz2; // m-n + ratio = div(r[sz1 - 1], b_n); + // q[m_n] <- q[m_n] + r[sz1 - 1]/b_n + q.set(m_n, add(q[m_n], ratio)); + for (unsigned i = 0; i < sz2 - 1; i++) { + // r[i + m_n] <- r[i + m_n] - ratio * p2[i] + ratio = mul(ratio, p2[i]); + r.set(i + m_n, sub(r[i + m_n], ratio)); + } + r.shrink(sz1 - 1); + adjust_size(r); + } + } + } + } + /** + \brief q <- quotient(p1, p2) + */ + void div(unsigned sz1, value * const * p1, unsigned sz2, value * const * p2, value_ref_buffer & q) { + value_ref_buffer r(*this); + div_rem(sz1, p1, sz2, p2, q, r); + } + + /** + \brief r <- rem(p1, p2) + */ + void rem(unsigned sz1, value * const * p1, unsigned sz2, value * const * p2, value_ref_buffer & r) { + r.reset(); + SASSERT(sz2 > 0); + if (sz2 == 1) + return; + r.append(sz1, p1); + if (sz1 <= 1) + return; // r is p1 + value * b_n = p2[sz2 - 1]; + value_ref ratio(*this); + SASSERT(b_n != 0); + while (true) { + checkpoint(); + sz1 = r.size(); + if (sz1 < sz2) { + return; + } + unsigned m_n = sz1 - sz2; + ratio = div(b_n, r[sz1 - 1]); + for (unsigned i = 0; i < sz2 - 1; i++) { + ratio = mul(ratio, p2[i]); + r.set(i + m_n, sub(r[i + m_n], ratio)); + } + r.shrink(sz1 - 1); + adjust_size(r); + } + } + + /** + \brief r <- -r + */ + void neg(value_ref_buffer & r) { + unsigned sz = r.size(); + for (unsigned i = 0; i < sz; i++) + r.set(i, neg(r[i])); + } + + /** + \brief r <- srem(p1, p2) + Signed remainder + */ + void srem(unsigned sz1, value * const * p1, unsigned sz2, value * const * p2, value_ref_buffer & r) { + rem(sz1, p1, sz2, p2, r); + neg(r); + } + + /** + \brief Force the leading coefficient of p to be 1. + */ + void mk_monic(value_ref_buffer & p) { + unsigned sz = p.size(); + if (sz > 0 && !is_one(p[sz-1])) { + SASSERT(p[sz-1] != 0); + for (unsigned i = 0; i < sz - 1; i++) { + p.set(i, div(p[i], p[sz-1])); + } + p.set(0, one()); + } + } + + /** + \brief r <- gcd(p1, p2) + */ + void gcd(unsigned sz1, value * const * p1, unsigned sz2, value * const * p2, value_ref_buffer & r) { + if (sz1 == 0) { + r.append(sz2, p2); + mk_monic(r); + } + else if (sz2 == 0) { + r.append(sz1, p1); + mk_monic(r); + } + else { + value_ref_buffer A(*this); + value_ref_buffer B(*this); + value_ref_buffer & R = r; + A.append(sz1, p1); + B.append(sz2, p2); + while (true) { + if (B.empty()) { + mk_monic(A); + r = A; + return; + } + rem(A.size(), A.c_ptr(), B.size(), B.c_ptr(), R); + A = B; + B = R; + } + } + } + + /** + \brief r <- dp/dx + */ + void derivative(unsigned sz, value * const * p, value_ref_buffer & r) { + r.reset(); + if (sz > 1) { + for (unsigned i = 1; i < sz; i++) { + mpq i_mpq(i); + value_ref i_value(*this); + i_value = mk_rational(i_mpq); + r.push_back(mul(i_value, p[i])); + } + adjust_size(r); + } + } + + /** + \brief r <- squarefree(p) + Store in r the square free factors of p. + */ + void square_free(unsigned sz, value * const * p, value_ref_buffer & r) { + if (sz <= 1) { + r.append(sz, p); + } + else { + value_ref_buffer p_prime(*this); + value_ref_buffer g(*this); + derivative(sz, p, p_prime); + gcd(sz, p, p_prime.size(), p_prime.c_ptr(), g); + if (g.size() <= 1) { + r.append(sz, p); + } + else { + div(sz, p, g.size(), g.c_ptr(), r); + } + } + } + + /** + \brief Keep expanding the Sturm sequence starting at seq + */ + void sturm_seq_core(scoped_polynomial_seq & seq) { + value_ref_buffer r(*this); + while (true) { + unsigned sz = seq.size(); + srem(seq.size(sz-2), seq.coeffs(sz-2), seq.size(sz-1), seq.coeffs(sz-1), r); + if (r.empty()) + return; + seq.push(r.size(), r.c_ptr()); + } + } + + /** + \brief Store in seq the Sturm sequence for (p1; p2) + */ + void sturm_seq(unsigned sz1, value * const * p1, unsigned sz2, value * const * p2, scoped_polynomial_seq & seq) { + seq.reset(); + seq.push(sz1, p1); + seq.push(sz2, p2); + sturm_seq_core(seq); + } + + /** + \brief Store in seq the Sturm sequence for (p; p') + */ + void sturm_seq(unsigned sz, value * const * p, scoped_polynomial_seq & seq) { + seq.reset(); + value_ref_buffer p_prime(*this); + seq.push(sz, p); + derivative(sz, p, p_prime); + seq.push(p_prime.size(), p_prime.c_ptr()); + sturm_seq_core(seq); + } + + /** + \brief Store in seq the Sturm sequence for (p1; p1' * p2) + */ + void sturm_tarski_seq(unsigned sz1, value * const * p1, unsigned sz2, value * const * p2, scoped_polynomial_seq & seq) { + seq.reset(); + value_ref_buffer p1p2(*this); + seq.push(sz1, p1); + derivative(sz1, p1, p1p2); + mul(p1p2.size(), p1p2.c_ptr(), sz2, p2, p1p2); + seq.push(p1p2.size(), p1p2.c_ptr()); + sturm_seq_core(seq); + } + + + value * add(value * a, value * b) { + if (a == 0) + return b; + else if (b == 0) + return a; + else if (is_nz_rational(a) && is_nz_rational(b)) { + scoped_mpq r(qm()); + qm().add(to_mpq(a), to_mpq(b), r); + if (qm().is_zero(r)) + return 0; + else + return mk_rational(r); + } + else { + // TODO + return 0; + } + } + + value * sub(value * a, value * b) { + if (a == 0) + return neg(b); + else if (b == 0) + return a; + else if (is_nz_rational(a) && is_nz_rational(b)) { + scoped_mpq r(qm()); + qm().sub(to_mpq(a), to_mpq(b), r); + if (qm().is_zero(r)) + return 0; + else + return mk_rational(r); + } + else { + // TODO + return 0; + } + } + + value * neg(value * a) { + if (a == 0) + return 0; + else if (is_nz_rational(a)) { + scoped_mpq r(qm()); + qm().set(r, to_mpq(a)); + qm().neg(r); + return mk_rational(r); + } + else { + // TODO + return 0; + } + } + + value * mul(value * a, value * b) { + if (a == 0 || b == 0) + return 0; + else if (is_nz_rational(a) && is_nz_rational(b)) { + scoped_mpq r(qm()); + qm().mul(to_mpq(a), to_mpq(b), r); + return mk_rational(r); + } + else { + // TODO + return 0; + } + } + + value * div(value * a, value * b) { + if (a == 0) + return 0; + if (b == 0) + throw exception("division by zero"); + else if (is_nz_rational(a) && is_nz_rational(b)) { + scoped_mpq r(qm()); + qm().div(to_mpq(a), to_mpq(b), r); + return mk_rational(r); + } + else { + // TODO + return 0; + } + } + void add(numeral const & a, numeral const & b, numeral & c) { // TODO } @@ -371,7 +1126,30 @@ namespace realclosure { } void inv(numeral & a) { - // TODO + if (a.m_value == 0) { + throw exception("division by zero"); + } + else if (is_unique(a)) { + if (is_nz_rational(a)) { + qm().inv(to_mpq(a)); + } + else { + rational_function_value * v = to_rational_function(a); + std::swap(v->m_numerator, v->m_denominator); + } + } + else { + if (is_nz_rational(a)) { + rational_value * v = mk_rational(); + inc_ref(v); + qm().inv(to_mpq(a), to_mpq(v)); + dec_ref(a.m_value); + a.m_value = v; + } + else { + // TODO + } + } } void div(numeral const & a, numeral const & b, numeral & c) { @@ -386,17 +1164,191 @@ namespace realclosure { void select(numeral const & prev, numeral const & next, numeral & result) { // TODO } + + struct collect_algebraic_refs { + imp const & m; + char_vector m_visited; // Set of visited algebraic extensions. + svector m_found; // vector/list of visited algebraic extensions. + + collect_algebraic_refs(imp const & _m):m(_m) {} + + void mark(extension_ref const & r) { + if (r.is_algebraic()) { + m_visited.reserve(r.idx() + 1, false); + if (!m_visited[r.idx()]) { + m_visited[r.idx()] = true; + m_found.push_back(r); + algebraic * a = m.to_algebraic(r); + mark(a->p()); + } + } + } + + void mark(polynomial_expr * p) { + if (p == 0) + return; + mark(p->ext_ref()); + mark(p->p()); + } + + void mark(polynomial const & p) { + for (unsigned i = 0; i < p.size(); i++) { + mark(p[i]); + } + } + + void mark(value * v) { + if (v == 0 || is_nz_rational(v)) + return; + rational_function_value * rf = to_rational_function(v); + mark(rf->num()); + mark(rf->den()); + } + }; + + template + void display_polynomial(std::ostream & out, polynomial const & p, DisplayVar const & display_var, bool compact) const { + unsigned i = p.size(); + bool first = true; + SASSERT(i > 0); + while (i > 0) { + --i; + if (first) + first = false; + else + out << " + "; + if (i == 0) + display(out, p[i], compact); + else { + out << "("; + display(out, p[i], compact); + out << ")*"; + display_var(out, compact); + if (i > 1) + out << "^" << i; + } + } + } + + struct display_free_var_proc { + void operator()(std::ostream & out, bool compact) const { + out << "#"; + } + }; + + struct display_ext_ref_proc { + imp const & m; + extension_ref const & m_ref; + display_ext_ref_proc(imp const & _m, extension_ref const & r):m(_m), m_ref(r) {} + void operator()(std::ostream & out, bool compact) const { + m.display_ext_ref(out, m_ref, compact); + } + }; + + void display_polynomial_expr(std::ostream & out, polynomial_expr const & p, bool compact) const { + display_polynomial(out, p.p(), display_ext_ref_proc(*this, p.ext_ref()), compact); + } + + static void display_poly_sign(std::ostream & out, int s) { + if (s < 0) + out << " < 0"; + else if (s == 0) + out << " = 0"; + else + out << " > 0"; + } + + void display_algebraic_def(std::ostream & out, algebraic * a, bool compact) const { + out << "root("; + display_polynomial(out, a->p(), display_free_var_proc(), compact); + out << ", "; + bqim().display(out, a->interval()); + out << ", {"; + signs const & s = a->s(); + for (unsigned i = 0; i < s.size(); i++) { + if (i > 0) + out << ", "; + display_polynomial(out, s[i].first, display_free_var_proc(), compact); + display_poly_sign(out, s[i].second); + } + out << "})"; + } + + void display_ext_ref(std::ostream & out, extension_ref const & r, bool compact) const { + switch (r.knd()) { + case extension_ref::TRANSCENDENTAL: to_transcendental(r)->display(out); break; + case extension_ref::INFINITESIMAL: to_infinitesimal(r)->display(out); break; + case extension_ref::ALGEBRAIC: + if (compact) + out << "r!" << r.idx(); + else + display_algebraic_def(out, to_algebraic(r), compact); + } + } + + void display(std::ostream & out, value * v, bool compact) const { + if (v == 0) + out << "0"; + else if (is_nz_rational(v)) + qm().display(out, to_mpq(v)); + else { + rational_function_value * rf = to_rational_function(v); + if (rf->den() == 0) { + display_polynomial_expr(out, *rf->num(), compact); + } + else if (rf->num() == 0) { + out << "1/("; + display_polynomial_expr(out, *rf->den(), compact); + out << ")"; + } + else { + out << "("; + display_polynomial_expr(out, *rf->num(), compact); + out << ")/("; + display_polynomial_expr(out, *rf->den(), compact); + out << ")"; + } + } + } + + void display_compact(std::ostream & out, numeral const & a) const { + collect_algebraic_refs c(*this); + c.mark(a.m_value); + if (c.m_found.empty()) { + display(out, a.m_value, true); + } + else { + std::sort(c.m_found.begin(), c.m_found.end()); + out << "["; + display(out, a.m_value, true); + for (unsigned i = 0; i < c.m_found.size(); i++) { + extension_ref const & r = c.m_found[i]; + out << ", r!" << r.idx() << " = "; + display_algebraic_def(out, to_algebraic(r), true); + } + out << "]"; + } + } void display(std::ostream & out, numeral const & a) const { - // TODO + display(out, a.m_value, false); } void display_decimal(std::ostream & out, numeral const & a, unsigned precision) const { - // TODO + if (!is_real(a)) + throw exception("number cannot be printed in decimal notation because it is not a real"); + if (is_zero(a)) { + out << "0"; + } + else if (is_nz_rational(a)) { + qm().display_decimal(out, to_mpq(a), precision); + } + else { + // TODO + } } }; - manager::manager(unsynch_mpq_manager & m, params_ref const & p, small_object_allocator * a) { m_imp = alloc(imp, m, p, a); } @@ -426,15 +1378,23 @@ namespace realclosure { } void manager::mk_infinitesimal(char const * p, numeral & r) { - m_imp->mk_infinitesimal(p, r); + m_imp->mk_infinitesimal(r); + } + + void manager::mk_infinitesimal(numeral & r) { + m_imp->mk_infinitesimal(r); } void manager::mk_transcendental(char const * p, mk_interval & proc, numeral & r) { m_imp->mk_transcendental(p, proc, r); } - void manager::isolate_roots(char const * p, unsigned n, numeral const * as, numeral_vector & roots) { - m_imp->isolate_roots(p, n, as, roots); + void manager::mk_transcendental(mk_interval & proc, numeral & r) { + m_imp->mk_transcendental(proc, r); + } + + void manager::isolate_roots(unsigned n, numeral const * as, numeral_vector & roots) { + m_imp->isolate_roots(n, as, roots); } void manager::reset(numeral & a) { diff --git a/src/math/realclosure/realclosure.h b/src/math/realclosure/realclosure.h index ea7e87512..a90951a5a 100644 --- a/src/math/realclosure/realclosure.h +++ b/src/math/realclosure/realclosure.h @@ -27,11 +27,13 @@ Notes: #include"scoped_numeral.h" #include"scoped_numeral_vector.h" #include"interval.h" +#include"z3_exception.h" namespace realclosure { class num; typedef interval_manager mpqi_manager; + typedef default_exception exception; class mk_interval { public: @@ -67,8 +69,8 @@ namespace realclosure { /** \brief Add a new infinitesimal to the current field. The new infinitesimal is smaller than any positive element in the field. */ - void mk_infinitesimal(char const * prefix_name, numeral & r); - void mk_infinitesimal(numeral & r) { mk_infinitesimal("eps", r); } + void mk_infinitesimal(char const * name, numeral & r); + void mk_infinitesimal(numeral & r); /** \brief Add a new transcendental real value to the field. @@ -80,15 +82,14 @@ namespace realclosure { Then, we extend the field F with 1 - Pi. 1 - Pi is transcendental with respect to algebraic real numbers, but it is NOT transcendental with respect to F, since F contains Pi. */ - void mk_transcendental(char const * prefix_name, mk_interval & proc, numeral & r); - void mk_transcendental(mk_interval & proc, numeral & r) { mk_transcendental("k", proc, r); } + void mk_transcendental(char const * name, mk_interval & proc, numeral & r); + void mk_transcendental(mk_interval & proc, numeral & r); /** \brief Isolate the roots of the univariate polynomial as[0] + as[1]*x + ... + as[n-1]*x^{n-1} The roots are stored in \c roots. */ - void isolate_roots(char const * prefix_name, unsigned n, numeral const * as, numeral_vector & roots); - void isolate_roots(unsigned n, numeral const * as, numeral_vector & roots) { isolate_roots("a", n, as, roots); } + void isolate_roots(unsigned n, numeral const * as, numeral_vector & roots); /** \brief a <- 0 From 1ed275b801dcb16f09a5f22edb8d57d67b89341a Mon Sep 17 00:00:00 2001 From: Leonardo de Moura Date: Thu, 3 Jan 2013 22:08:32 -0800 Subject: [PATCH 13/78] Fix typo Signed-off-by: Leonardo de Moura --- src/util/array.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/array.h b/src/util/array.h index bcc659ca3..edbab2cad 100644 --- a/src/util/array.h +++ b/src/util/array.h @@ -137,7 +137,7 @@ public: void set(Allocator & a, size_t sz, T const * vs) { SASSERT(m_data == 0); allocate(a, sz); - init(sz, vs); + init(vs); } size_t size() const { From 15ed819fbd9a2a8e09c354114b7b125fe15b9e8a Mon Sep 17 00:00:00 2001 From: Leonardo de Moura Date: Thu, 3 Jan 2013 22:09:43 -0800 Subject: [PATCH 14/78] Implement mk_transcendental. Replace extension_ref with extension *. Remove m_to_delete Signed-off-by: Leonardo de Moura --- src/math/realclosure/realclosure.cpp | 312 ++++++++++++++++----------- 1 file changed, 184 insertions(+), 128 deletions(-) diff --git a/src/math/realclosure/realclosure.cpp b/src/math/realclosure/realclosure.cpp index 3ad7a834a..e2424b389 100644 --- a/src/math/realclosure/realclosure.cpp +++ b/src/math/realclosure/realclosure.cpp @@ -39,7 +39,7 @@ namespace realclosure { // --------------------------------- // - // Binary rational intervals + // Intervals with binary rational endpoints // // --------------------------------- @@ -54,6 +54,12 @@ namespace realclosure { numeral m_upper; unsigned m_lower_inf:1; unsigned m_upper_inf:1; + interval():m_lower_inf(true), m_upper_inf(true) {} + interval(numeral & l, numeral & u):m_lower_inf(false), m_upper_inf(false) { + swap(m_lower, l); + swap(m_upper, u); + } + }; void round_to_minus_inf() {} @@ -83,10 +89,18 @@ namespace realclosure { mpbq_config(numeral_manager & m):m_manager(m) {} }; - + typedef interval_manager mpbqi_manager; typedef mpbqi_manager::interval mpbqi; + void swap(mpbqi & a, mpbqi & b) { + swap(a.m_lower, b.m_lower); + swap(a.m_upper, b.m_upper); + unsigned tmp; + tmp = a.m_lower_inf; a.m_lower_inf = b.m_lower_inf; b.m_lower_inf = tmp; + tmp = a.m_upper_inf; a.m_upper_inf = b.m_upper_inf; b.m_upper_inf = tmp; + } + // --------------------------------- // // Values: rational and composite @@ -107,49 +121,19 @@ namespace realclosure { typedef ptr_array polynomial; - /** - \brief Reference to a field extension element. - The element can be a transcendental real value, an infinitesimal, or an algebraic extension. - */ - struct extension_ref { - enum kind { - TRANSCENDENTAL = 0, - INFINITESIMAL = 1, - ALGEBRAIC = 2 - }; - unsigned m_kind:2; - unsigned m_idx:30; - unsigned idx() const { return m_idx; } - kind knd() const { return static_cast(m_kind); } - bool is_algebraic() const { return knd() == ALGEBRAIC; } - bool is_infinitesimal() const { return knd() == INFINITESIMAL; } - bool is_transcendental() const { return knd() == TRANSCENDENTAL; } - }; - - bool operator==(extension_ref const & r1, extension_ref const & r2) { - return r1.knd() == r2.knd() && r1.idx() == r2.idx(); - } + struct extension; + bool rank_lt(extension * r1, extension * r2); - bool operator<(extension_ref const & r1, extension_ref const & r2) { - return r1.knd() < r2.knd() || (r1.knd() == r2.knd() && r1.idx() < r2.idx()); - } - - bool operator<=(extension_ref const & r1, extension_ref const & r2) { - return r1.knd() < r2.knd() || (r1.knd() == r2.knd() && r1.idx() <= r2.idx()); - } - - typedef svector extension_ref_vector; - struct polynomial_expr { - // The values occurring in this polynomial m_p may only contain extensions that are smaller than m_ext_ref. - // The polynomial m_p must not be the constant polynomial on m_ext_ref. That is, - // it contains a nonzero coefficient at position k > 0. It is not a constant polynomial on m_ext_ref. + // The values occurring in this polynomial m_p may only contain extensions that are smaller than m_ext. + // The polynomial m_p must not be the constant polynomial on m_ext. That is, + // it contains a nonzero coefficient at position k > 0. It is not a constant polynomial on m_ext. polynomial m_p; - extension_ref m_ext_ref; + extension * m_ext; bool m_real; mpbqi m_interval; // approximation as a binary rational - - extension_ref const & ext_ref() const { return m_ext_ref; } + polynomial_expr():m_ext(0), m_real(false) {} + extension * ext() const { return m_ext; } bool is_real() const { return m_real; } polynomial const & p() const { return m_p; } }; @@ -162,14 +146,18 @@ namespace realclosure { polynomial_expr * num() const { return m_numerator; } polynomial_expr * den() const { return m_denominator; } + + rational_function_value(polynomial_expr * num, polynomial_expr * den):m_numerator(num), m_denominator(den) { + SASSERT(num != 0 || den != 0); + } - extension_ref const & ext_ref() const { + extension * ext() const { if (den() == 0) - return num()->ext_ref(); - else if (num() == 0 || num()->ext_ref() < den()->ext_ref()) - return den()->ext_ref(); + return num()->ext(); + else if (num() == 0 || rank_lt(num()->ext(), den()->ext())) + return den()->ext(); else - return num()->ext_ref(); + return num()->ext(); } bool is_real() const { @@ -197,25 +185,60 @@ namespace realclosure { typedef sarray signs; struct extension { - unsigned m_ref_count; - extension():m_ref_count(0) {} + enum kind { + TRANSCENDENTAL = 0, + INFINITESIMAL = 1, + ALGEBRAIC = 2 + }; + + unsigned m_ref_count; + unsigned m_kind:2; + unsigned m_idx:30; + mpbqi m_interval; + + extension(kind k, unsigned idx):m_ref_count(0), m_kind(k), m_idx(idx) {} + + unsigned idx() const { return m_idx; } + kind knd() const { return static_cast(m_kind); } + + bool is_algebraic() const { return knd() == ALGEBRAIC; } + bool is_infinitesimal() const { return knd() == INFINITESIMAL; } + bool is_transcendental() const { return knd() == TRANSCENDENTAL; } + + mpbqi const & interval() const { return m_interval; } + }; + + bool rank_lt(extension * r1, extension * r2) { + return r1->knd() < r2->knd() || (r1->knd() == r2->knd() && r1->idx() < r2->idx()); + } + + bool rank_eq(extension * r1, extension * r2) { + return r1->knd() == r2->knd() && r1->idx() == r2->idx(); + } + + struct rank_lt_proc { + bool operator()(extension * r1, extension * r2) const { + return rank_lt(r1, r2); + } }; struct algebraic : public extension { polynomial m_p; - mpbqi m_interval; signs m_signs; bool m_real; + algebraic(unsigned idx):extension(ALGEBRAIC, idx), m_real(false) {} + polynomial const & p() const { return m_p; } - mpbqi const & interval() const { return m_interval; } signs const & s() const { return m_signs; } }; struct transcendental : public extension { symbol m_name; mk_interval & m_proc; - transcendental(symbol const & n, mk_interval & p):m_name(n), m_proc(p) {} + + transcendental(unsigned idx, symbol const & n, mk_interval & p):extension(TRANSCENDENTAL, idx), m_name(n), m_proc(p) {} + void display(std::ostream & out) const { out << m_name; } @@ -223,7 +246,10 @@ namespace realclosure { struct infinitesimal : public extension { symbol m_name; - infinitesimal(symbol const & n):m_name(n) {} + mpbqi m_interval; + + infinitesimal(unsigned idx, symbol const & n):extension(INFINITESIMAL, idx), m_name(n) {} + void display(std::ostream & out) const { if (m_name.is_numerical()) out << "eps!" << m_name.get_num(); @@ -243,9 +269,9 @@ namespace realclosure { mpbq_manager m_bqm; mpqi_manager m_qim; mpbqi_manager m_bqim; - value_vector m_to_delete; ptr_vector m_extensions[3]; value * m_one; + unsigned m_eps_prec; volatile bool m_cancel; struct scoped_polynomial_seq { @@ -323,7 +349,7 @@ namespace realclosure { return m_one; } - void cleanup(extension_ref::kind k) { + void cleanup(extension::kind k) { ptr_vector & exts = m_extensions[k]; // keep removing unused slots while (!exts.empty() && exts.back() == 0) { @@ -332,13 +358,13 @@ namespace realclosure { } unsigned next_transcendental_idx() { - cleanup(extension_ref::TRANSCENDENTAL); - return m_extensions[extension_ref::TRANSCENDENTAL].size(); + cleanup(extension::TRANSCENDENTAL); + return m_extensions[extension::TRANSCENDENTAL].size(); } unsigned next_infinitesimal_idx() { - cleanup(extension_ref::INFINITESIMAL); - return m_extensions[extension_ref::INFINITESIMAL].size(); + cleanup(extension::INFINITESIMAL); + return m_extensions[extension::INFINITESIMAL].size(); } void set_cancel(bool f) { @@ -346,21 +372,19 @@ namespace realclosure { } void updt_params(params_ref const & p) { + m_eps_prec = p.get_uint("eps_prec", 24); // TODO } void finalize_polynomial(polynomial & p) { - unsigned sz = p.size(); - for (unsigned i = 0; i < sz; i++) { - dec_ref_core(p[i]); - } + dec_ref(p.size(), p.c_ptr()); p.finalize(allocator()); } void del_polynomial_expr(polynomial_expr * p) { finalize_polynomial(p->m_p); bqim().del(p->m_interval); - dec_ref_ext(p->m_ext_ref); + dec_ref_ext(p->m_ext); allocator().deallocate(sizeof(polynomial_expr), p); } @@ -378,15 +402,11 @@ namespace realclosure { allocator().deallocate(sizeof(rational_function_value), v); } - void flush_to_delete() { - while (!m_to_delete.empty()) { - value * v = m_to_delete.back(); - m_to_delete.pop_back(); - if (v->is_rational()) - del_rational(static_cast(v)); - else - del_rational_function(static_cast(v)); - } + void del_value(value * v) { + if (v->is_rational()) + del_rational(static_cast(v)); + else + del_rational_function(static_cast(v)); } void del_algebraic(algebraic * a) { @@ -407,22 +427,21 @@ namespace realclosure { allocator().deallocate(sizeof(infinitesimal), i); } - void inc_ref_ext(extension_ref x) { - SASSERT(m_extensions[x.knd()][x.idx()] != 0); - m_extensions[x.knd()][x.idx()]->m_ref_count++; + void inc_ref_ext(extension * ext) { + SASSERT(ext != 0); + ext->m_ref_count++; } - void dec_ref_ext(extension_ref x) { - SASSERT(m_extensions[x.knd()][x.idx()] != 0); - extension * ext = m_extensions[x.knd()][x.idx()]; + void dec_ref_ext(extension * ext) { + SASSERT(m_extensions[ext->knd()][ext->idx()] == ext); SASSERT(ext->m_ref_count > 0); ext->m_ref_count--; if (ext->m_ref_count == 0) { - m_extensions[x.knd()][x.idx()] = 0; - switch (x.knd()) { - case extension_ref::TRANSCENDENTAL: del_transcendental(static_cast(ext)); break; - case extension_ref::INFINITESIMAL: del_infinitesimal(static_cast(ext)); break; - case extension_ref::ALGEBRAIC: del_algebraic(static_cast(ext)); break; + m_extensions[ext->knd()][ext->idx()] = 0; + switch (ext->knd()) { + case extension::TRANSCENDENTAL: del_transcendental(static_cast(ext)); break; + case extension::INFINITESIMAL: del_infinitesimal(static_cast(ext)); break; + case extension::ALGEBRAIC: del_algebraic(static_cast(ext)); break; } } } @@ -432,18 +451,23 @@ namespace realclosure { v->m_ref_count++; } - void dec_ref_core(value * v) { + void inc_ref(unsigned sz, value * const * p) { + for (unsigned i = 0; i < sz; i++) + inc_ref(p[i]); + } + + void dec_ref(value * v) { if (v) { SASSERT(v->m_ref_count > 0); v->m_ref_count--; if (v->m_ref_count == 0) - m_to_delete.push_back(v); + del_value(v); } } - void dec_ref(value * v) { - dec_ref_core(v); - flush_to_delete(); + void dec_ref(unsigned sz, value * const * p) { + for (unsigned i = 0; i < sz; i++) + dec_ref(p[i]); } void del(numeral & a) { @@ -541,29 +565,57 @@ namespace realclosure { SASSERT(is_rational_function(a)); return 1; } - else if (to_rational_function(a)->ext_ref() == to_rational_function(b)->ext_ref()) + else if (rank_eq(to_rational_function(a)->ext(), to_rational_function(b)->ext())) return 0; else - return to_rational_function(a)->ext_ref() < to_rational_function(b)->ext_ref() ? -1 : 1; + return rank_lt(to_rational_function(a)->ext(), to_rational_function(b)->ext()) ? -1 : 1; } - transcendental * to_transcendental(extension_ref const & r) const { - SASSERT(r.is_transcendental()); - return static_cast(m_extensions[r.knd()][r.idx()]); + static transcendental * to_transcendental(extension * ext) { + SASSERT(ext->is_transcendental()); + return static_cast(ext); } - infinitesimal * to_infinitesimal(extension_ref const & r) const { - SASSERT(r.is_infinitesimal()); - return static_cast(m_extensions[r.knd()][r.idx()]); + static infinitesimal * to_infinitesimal(extension * ext) { + SASSERT(ext->is_infinitesimal()); + return static_cast(ext); } - algebraic * to_algebraic(extension_ref const & r) const { - SASSERT(r.is_algebraic()); - return static_cast(m_extensions[r.knd()][r.idx()]); + static algebraic * to_algebraic(extension * ext) { + SASSERT(ext->is_algebraic()); + return static_cast(ext); + } + + polynomial_expr * mk_polynomial_expr(unsigned sz, value * const * p, extension * ext, mpbqi & interval) { + SASSERT(sz > 1); + SASSERT(p[sz-1] != 0); + polynomial_expr * r = new (allocator()) polynomial_expr(); + r->m_p.set(allocator(), sz, p); + inc_ref(sz, p); + inc_ref_ext(ext); + r->m_ext = ext; + realclosure::swap(r->m_interval, interval); + r->m_real = true; + for (unsigned i = 0; i < sz && r->m_real; i++) { + if (!is_real(p[i])) + r->m_real = false; + } + return r; } void mk_infinitesimal(symbol const & n, numeral & r) { - // TODO + unsigned idx = next_infinitesimal_idx(); + infinitesimal * eps = alloc(infinitesimal, idx, n); + m_extensions[extension::INFINITESIMAL].push_back(eps); + value * p[2] = { one(), 0 }; + mpbq zero(0); + mpbq tiny(1, m_eps_prec); + mpbqi interval(zero, tiny); + polynomial_expr * numerator = mk_polynomial_expr(2, p, eps, interval); + r.m_value = alloc(rational_function_value, numerator, 0); + inc_ref(r.m_value); + SASSERT(sign(r) > 0); + SASSERT(!is_real(r)); } void mk_infinitesimal(char const * n, numeral & r) { @@ -628,6 +680,13 @@ namespace realclosure { return false; } } + + bool is_real(value * v) { + if (is_zero(v) || is_nz_rational(v)) + return true; + else + return to_rational_function(v)->is_real(); + } bool is_real(numeral const & a) const { if (is_zero(a) || is_nz_rational(a)) @@ -1166,19 +1225,16 @@ namespace realclosure { } struct collect_algebraic_refs { - imp const & m; char_vector m_visited; // Set of visited algebraic extensions. - svector m_found; // vector/list of visited algebraic extensions. + ptr_vector m_found; // vector/list of visited algebraic extensions. - collect_algebraic_refs(imp const & _m):m(_m) {} - - void mark(extension_ref const & r) { - if (r.is_algebraic()) { - m_visited.reserve(r.idx() + 1, false); - if (!m_visited[r.idx()]) { - m_visited[r.idx()] = true; - m_found.push_back(r); - algebraic * a = m.to_algebraic(r); + void mark(extension * ext) { + if (ext->is_algebraic()) { + m_visited.reserve(ext->idx() + 1, false); + if (!m_visited[ext->idx()]) { + m_visited[ext->idx()] = true; + algebraic * a = to_algebraic(ext); + m_found.push_back(a); mark(a->p()); } } @@ -1187,7 +1243,7 @@ namespace realclosure { void mark(polynomial_expr * p) { if (p == 0) return; - mark(p->ext_ref()); + mark(p->ext()); mark(p->p()); } @@ -1236,17 +1292,17 @@ namespace realclosure { } }; - struct display_ext_ref_proc { - imp const & m; - extension_ref const & m_ref; - display_ext_ref_proc(imp const & _m, extension_ref const & r):m(_m), m_ref(r) {} + struct display_ext_proc { + imp const & m; + extension * m_ref; + display_ext_proc(imp const & _m, extension * r):m(_m), m_ref(r) {} void operator()(std::ostream & out, bool compact) const { - m.display_ext_ref(out, m_ref, compact); + m.display_ext(out, m_ref, compact); } }; void display_polynomial_expr(std::ostream & out, polynomial_expr const & p, bool compact) const { - display_polynomial(out, p.p(), display_ext_ref_proc(*this, p.ext_ref()), compact); + display_polynomial(out, p.p(), display_ext_proc(*this, p.ext()), compact); } static void display_poly_sign(std::ostream & out, int s) { @@ -1274,13 +1330,13 @@ namespace realclosure { out << "})"; } - void display_ext_ref(std::ostream & out, extension_ref const & r, bool compact) const { - switch (r.knd()) { - case extension_ref::TRANSCENDENTAL: to_transcendental(r)->display(out); break; - case extension_ref::INFINITESIMAL: to_infinitesimal(r)->display(out); break; - case extension_ref::ALGEBRAIC: + void display_ext(std::ostream & out, extension * r, bool compact) const { + switch (r->knd()) { + case extension::TRANSCENDENTAL: to_transcendental(r)->display(out); break; + case extension::INFINITESIMAL: to_infinitesimal(r)->display(out); break; + case extension::ALGEBRAIC: if (compact) - out << "r!" << r.idx(); + out << "r!" << r->idx(); else display_algebraic_def(out, to_algebraic(r), compact); } @@ -1312,19 +1368,19 @@ namespace realclosure { } void display_compact(std::ostream & out, numeral const & a) const { - collect_algebraic_refs c(*this); + collect_algebraic_refs c; c.mark(a.m_value); if (c.m_found.empty()) { display(out, a.m_value, true); } else { - std::sort(c.m_found.begin(), c.m_found.end()); + std::sort(c.m_found.begin(), c.m_found.end(), rank_lt_proc()); out << "["; display(out, a.m_value, true); for (unsigned i = 0; i < c.m_found.size(); i++) { - extension_ref const & r = c.m_found[i]; - out << ", r!" << r.idx() << " = "; - display_algebraic_def(out, to_algebraic(r), true); + algebraic * ext = c.m_found[i]; + out << ", r!" << ext->idx() << " = "; + display_algebraic_def(out, ext, true); } out << "]"; } From 9ede98a0291c215c485b2f6a87d1d7b9fcdba8a0 Mon Sep 17 00:00:00 2001 From: Leonardo de Moura Date: Fri, 4 Jan 2013 08:09:20 -0800 Subject: [PATCH 15/78] Fix bugs Signed-off-by: Leonardo de Moura --- src/math/realclosure/realclosure.cpp | 51 ++++++++++----- src/math/realclosure/realclosure.h | 93 ++++++++++++++++++++++++++++ 2 files changed, 129 insertions(+), 15 deletions(-) diff --git a/src/math/realclosure/realclosure.cpp b/src/math/realclosure/realclosure.cpp index e2424b389..cdedaddff 100644 --- a/src/math/realclosure/realclosure.cpp +++ b/src/math/realclosure/realclosure.cpp @@ -110,13 +110,14 @@ namespace realclosure { struct value { unsigned m_ref_count; bool m_rational; - value():m_ref_count(0), m_rational(false) {} + value(bool rat):m_ref_count(0), m_rational(rat) {} bool is_rational() const { return m_rational; } }; struct rational_value : public value { mpq m_value; mpbqi m_interval; // approximation as a binary rational + rational_value():value(true) {} }; typedef ptr_array polynomial; @@ -147,7 +148,7 @@ namespace realclosure { polynomial_expr * num() const { return m_numerator; } polynomial_expr * den() const { return m_denominator; } - rational_function_value(polynomial_expr * num, polynomial_expr * den):m_numerator(num), m_denominator(den) { + rational_function_value(polynomial_expr * num, polynomial_expr * den):value(false), m_numerator(num), m_denominator(den) { SASSERT(num != 0 || den != 0); } @@ -231,6 +232,7 @@ namespace realclosure { polynomial const & p() const { return m_p; } signs const & s() const { return m_signs; } + bool is_real() const { return m_real; } }; struct transcendental : public extension { @@ -325,6 +327,7 @@ namespace realclosure { m_bqim(m_bqm) { mpq one(1); m_one = mk_rational(one); + inc_ref(m_one); m_cancel = false; } @@ -484,7 +487,7 @@ namespace realclosure { return v->is_rational(); } - bool is_one(value * v) { + bool is_one(value * v) const { return !is_zero(v) && is_nz_rational(v) && qm().is_one(to_mpq(v)); } @@ -586,6 +589,17 @@ namespace realclosure { return static_cast(ext); } + bool is_real(extension * ext) { + switch (ext->knd()) { + case extension::TRANSCENDENTAL: return true; + case extension::INFINITESIMAL: return false; + case extension::ALGEBRAIC: return to_algebraic(ext)->is_real(); + default: + UNREACHABLE(); + return false; + } + } + polynomial_expr * mk_polynomial_expr(unsigned sz, value * const * p, extension * ext, mpbqi & interval) { SASSERT(sz > 1); SASSERT(p[sz-1] != 0); @@ -595,7 +609,7 @@ namespace realclosure { inc_ref_ext(ext); r->m_ext = ext; realclosure::swap(r->m_interval, interval); - r->m_real = true; + r->m_real = is_real(ext); for (unsigned i = 0; i < sz && r->m_real; i++) { if (!is_real(p[i])) r->m_real = false; @@ -607,7 +621,7 @@ namespace realclosure { unsigned idx = next_infinitesimal_idx(); infinitesimal * eps = alloc(infinitesimal, idx, n); m_extensions[extension::INFINITESIMAL].push_back(eps); - value * p[2] = { one(), 0 }; + value * p[2] = { 0, one() }; mpbq zero(0); mpbq tiny(1, m_eps_prec); mpbqi interval(zero, tiny); @@ -711,9 +725,10 @@ namespace realclosure { return; } - if (!is_unique_nz_rational(a)) { + if (is_zero(a) || !is_unique_nz_rational(a)) { del(a); a.m_value = mk_rational(); + inc_ref(a.m_value); } SASSERT(is_unique_nz_rational(a)); qm().set(to_mpq(a), n); @@ -725,9 +740,10 @@ namespace realclosure { return; } - if (!is_unique_nz_rational(a)) { + if (is_zero(a) || !is_unique_nz_rational(a)) { del(a); a.m_value = mk_rational(); + inc_ref(a.m_value); } SASSERT(is_unique_nz_rational(a)); qm().set(to_mpq(a), n); @@ -739,9 +755,10 @@ namespace realclosure { return; } - if (!is_unique_nz_rational(a)) { + if (is_zero(a) || !is_unique_nz_rational(a)) { del(a); a.m_value = mk_rational(); + inc_ref(a.m_value); } SASSERT(is_unique_nz_rational(a)); qm().set(to_mpq(a), n); @@ -1269,6 +1286,8 @@ namespace realclosure { SASSERT(i > 0); while (i > 0) { --i; + if (p[i] == 0) + continue; if (first) first = false; else @@ -1276,9 +1295,11 @@ namespace realclosure { if (i == 0) display(out, p[i], compact); else { - out << "("; - display(out, p[i], compact); - out << ")*"; + if (!is_one(p[i])) { + out << "("; + display(out, p[i], compact); + out << ")*"; + } display_var(out, compact); if (i > 1) out << "^" << i; @@ -1433,16 +1454,16 @@ namespace realclosure { m_imp->del(a); } - void manager::mk_infinitesimal(char const * p, numeral & r) { - m_imp->mk_infinitesimal(r); + void manager::mk_infinitesimal(char const * n, numeral & r) { + m_imp->mk_infinitesimal(n, r); } void manager::mk_infinitesimal(numeral & r) { m_imp->mk_infinitesimal(r); } - void manager::mk_transcendental(char const * p, mk_interval & proc, numeral & r) { - m_imp->mk_transcendental(p, proc, r); + void manager::mk_transcendental(char const * n, mk_interval & proc, numeral & r) { + m_imp->mk_transcendental(n, proc, r); } void manager::mk_transcendental(mk_interval & proc, numeral & r) { diff --git a/src/math/realclosure/realclosure.h b/src/math/realclosure/realclosure.h index a90951a5a..8870644b0 100644 --- a/src/math/realclosure/realclosure.h +++ b/src/math/realclosure/realclosure.h @@ -263,4 +263,97 @@ typedef rcmanager::numeral_vector rcnumeral_vector; typedef rcmanager::scoped_numeral scoped_rcnumeral; typedef rcmanager::scoped_numeral_vector scoped_rcnumeral_vector; + +#define RCF_MK_COMPARISON_CORE(EXTERNAL, INTERNAL, TYPE) \ +inline bool EXTERNAL(scoped_rcnumeral const & a, TYPE const & b) { \ + rcmanager & m = a.m(); \ + scoped_rcnumeral _b(m); \ + m.set(_b, b); \ + return m.INTERNAL(a, _b); \ +} + +#define RCF_MK_COMPARISON(EXTERNAL, INTERNAL) \ +RCF_MK_COMPARISON_CORE(EXTERNAL, INTERNAL, int) \ +RCF_MK_COMPARISON_CORE(EXTERNAL, INTERNAL, mpz) \ +RCF_MK_COMPARISON_CORE(EXTERNAL, INTERNAL, mpq) + +RCF_MK_COMPARISON(operator==, eq); +RCF_MK_COMPARISON(operator!=, neq); +RCF_MK_COMPARISON(operator<, lt); +RCF_MK_COMPARISON(operator<=, le); +RCF_MK_COMPARISON(operator>, gt); +RCF_MK_COMPARISON(operator>=, ge); + +#undef RCF_MK_COMPARISON +#undef RCF_MK_COMPARISON_CORE + +#define RCF_MK_BINARY_CORE(EXTERNAL, INTERNAL, TYPE) \ +inline scoped_rcnumeral EXTERNAL(scoped_rcnumeral const & a, TYPE const & b) { \ + rcmanager & m = a.m(); \ + scoped_rcnumeral _b(m); \ + m.set(_b, b); \ + scoped_rcnumeral r(m); \ + m.INTERNAL(a, _b, r); \ + return r; \ +} + +#define RCF_MK_BINARY(EXTERNAL, INTERNAL) \ +RCF_MK_BINARY_CORE(EXTERNAL, INTERNAL, int) \ +RCF_MK_BINARY_CORE(EXTERNAL, INTERNAL, mpz) \ +RCF_MK_BINARY_CORE(EXTERNAL, INTERNAL, mpq) + +RCF_MK_BINARY(operator+, add) +RCF_MK_BINARY(operator-, sub) +RCF_MK_BINARY(operator*, mul) +RCF_MK_BINARY(operator/, div) + +#undef RCF_MK_BINARY +#undef RCF_MK_BINARY_CORE + +inline scoped_rcnumeral root(scoped_rcnumeral const & a, unsigned k) { + scoped_rcnumeral r(a.m()); + a.m().root(a, k, r); + return r; +} + +inline scoped_rcnumeral power(scoped_rcnumeral const & a, unsigned k) { + scoped_rcnumeral r(a.m()); + a.m().power(a, k, r); + return r; +} + +inline scoped_rcnumeral operator^(scoped_rcnumeral const & a, unsigned k) { + return power(a, k); +} + +inline bool is_int(scoped_rcnumeral const & a) { + return a.m().is_int(a); +} + +struct sym_pp { + rcmanager & m; + rcnumeral const & n; + sym_pp(rcmanager & _m, rcnumeral const & _n):m(_m), n(_n) {} + sym_pp(scoped_rcnumeral const & _n):m(_n.m()), n(_n.get()) {} +}; + +inline std::ostream & operator<<(std::ostream & out, sym_pp const & n) { + n.m.display(out, n.n); + return out; +} + +struct decimal_pp { + rcmanager & m; + rcnumeral const & n; + unsigned prec; + decimal_pp(rcmanager & _m, rcnumeral const & _n, unsigned p):m(_m), n(_n), prec(p) {} + decimal_pp(scoped_rcnumeral const & _n, unsigned p):m(_n.m()), n(_n.get()), prec(p) {} +}; + +inline std::ostream & operator<<(std::ostream & out, decimal_pp const & n) { + n.m.display_decimal(out, n.n, n.prec); + return out; +} + + #endif From 0203fa56d28454e4fe861f1c97922ea229e72ed0 Mon Sep 17 00:00:00 2001 From: Leonardo de Moura Date: Fri, 4 Jan 2013 08:11:33 -0800 Subject: [PATCH 16/78] Add tests Signed-off-by: Leonardo de Moura --- src/test/algebraic.cpp | 8 ++++++-- src/test/main.cpp | 1 + src/test/rcf.cpp | 34 ++++++++++++++++++++++++++++++++++ 3 files changed, 41 insertions(+), 2 deletions(-) create mode 100644 src/test/rcf.cpp diff --git a/src/test/algebraic.cpp b/src/test/algebraic.cpp index 064803fe1..d758ee5d8 100644 --- a/src/test/algebraic.cpp +++ b/src/test/algebraic.cpp @@ -176,11 +176,11 @@ static void tst1() { display_anums(std::cout, rs1); } -void tst_refine_mpbq() { +void tst_refine_mpbq(int n, int d) { unsynch_mpq_manager qm; mpbq_manager bqm(qm); scoped_mpq q1(qm); - qm.set(q1, 5, 7); + qm.set(q1, n, d); scoped_mpbq l(bqm); scoped_mpbq u(bqm); std::cout << "using refine upper...\n"; @@ -207,6 +207,10 @@ void tst_refine_mpbq() { } } +void tst_refine_mpbq() { + tst_refine_mpbq(-5, 7); +} + void tst_mpbq_root() { unsynch_mpq_manager qm; mpbq_manager bqm(qm); diff --git a/src/test/main.cpp b/src/test/main.cpp index 13ade7714..31d71226c 100644 --- a/src/test/main.cpp +++ b/src/test/main.cpp @@ -206,6 +206,7 @@ int main(int argc, char ** argv) { TST(mpff); TST(horn_subsume_model_converter); TST(model2expr); + TST(rcf); } void initialize_mam() {} diff --git a/src/test/rcf.cpp b/src/test/rcf.cpp new file mode 100644 index 000000000..8cc9dec19 --- /dev/null +++ b/src/test/rcf.cpp @@ -0,0 +1,34 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + rcf.cpp + +Abstract: + + Testing RCF module + +Author: + + Leonardo (leonardo) 2013-01-04 + +Notes: + +--*/ +#include"realclosure.h" + +static void tst1() { + unsynch_mpq_manager qm; + rcmanager m(qm); + scoped_rcnumeral a(m); + a = 10; + std::cout << sym_pp(a) << std::endl; + scoped_rcnumeral eps(m); + m.mk_infinitesimal("eps", eps); + std::cout << sym_pp(eps) << std::endl; +} + +void tst_rcf() { + tst1(); +} From c430fe26aa306ab8cc4141f1ab55e789a748a225 Mon Sep 17 00:00:00 2001 From: Leonardo de Moura Date: Fri, 4 Jan 2013 08:29:25 -0800 Subject: [PATCH 17/78] Add ite operator to the C++ API Signed-off-by: Leonardo de Moura --- examples/c++/example.cpp | 10 ++++++++++ src/api/c++/z3++.h | 13 +++++++++++++ 2 files changed, 23 insertions(+) diff --git a/examples/c++/example.cpp b/examples/c++/example.cpp index 9e92d99ab..1f127e8e4 100644 --- a/examples/c++/example.cpp +++ b/examples/c++/example.cpp @@ -337,6 +337,15 @@ void ite_example() { std::cout << "term: " << ite << "\n"; } +void ite_example2() { + std::cout << "if-then-else example2\n"; + context c; + expr b = c.bool_const("b"); + expr x = c.int_const("x"); + expr y = c.int_const("y"); + std::cout << (ite(b, x, y) > 0) << "\n"; +} + /** \brief Small example using quantifiers. */ @@ -889,6 +898,7 @@ int main() { error_example(); std::cout << "\n"; numeral_example(); std::cout << "\n"; ite_example(); std::cout << "\n"; + ite_example2(); std::cout << "\n"; quantifier_example(); std::cout << "\n"; unsat_core_example1(); std::cout << "\n"; unsat_core_example2(); std::cout << "\n"; diff --git a/src/api/c++/z3++.h b/src/api/c++/z3++.h index fca009cea..7875e6ef3 100644 --- a/src/api/c++/z3++.h +++ b/src/api/c++/z3++.h @@ -620,6 +620,19 @@ namespace z3 { return expr(a.ctx(), r); } + /** + \brief Create the if-then-else expression ite(c, t, e) + + \pre c.is_bool() + */ + friend expr ite(expr const & c, expr const & t, expr const & e) { + check_context(c, t); check_context(c, e); + assert(c.is_bool()); + Z3_ast r = Z3_mk_ite(c.ctx(), c, t, e); + c.check_error(); + return expr(c.ctx(), r); + } + friend expr operator==(expr const & a, expr const & b) { check_context(a, b); Z3_ast r = Z3_mk_eq(a.ctx(), a, b); From ff62948d90e9ecb94f570106ef0fa6e9ad190339 Mon Sep 17 00:00:00 2001 From: Leonardo de Moura Date: Fri, 4 Jan 2013 12:31:28 -0800 Subject: [PATCH 18/78] Add div and inv for binary rational intervals Signed-off-by: Leonardo de Moura --- src/math/realclosure/realclosure.cpp | 72 +++++++++++++++++++++++----- src/math/realclosure/realclosure.h | 38 +++++++++++---- src/test/rcf.cpp | 1 + 3 files changed, 92 insertions(+), 19 deletions(-) diff --git a/src/math/realclosure/realclosure.cpp b/src/math/realclosure/realclosure.cpp index cdedaddff..e426bc0cf 100644 --- a/src/math/realclosure/realclosure.cpp +++ b/src/math/realclosure/realclosure.cpp @@ -35,6 +35,10 @@ Notes: #define REALCLOSURE_INI_SEQ_SIZE 256 #endif +#ifndef REALCLOSURE_INI_DIV_PRECISION +#define REALCLOSURE_INI_DIV_PRECISION 24 +#endif + namespace realclosure { // --------------------------------- @@ -43,11 +47,29 @@ namespace realclosure { // // --------------------------------- - class mpbq_config { - mpbq_manager & m_manager; - public: - typedef mpbq_manager numeral_manager; + struct mpbq_config { + + struct numeral_manager : public mpbq_manager { + unsigned m_div_precision; + bool m_to_plus_inf; + + numeral_manager(unsynch_mpq_manager & qm):mpbq_manager(qm), m_div_precision(REALCLOSURE_INI_DIV_PRECISION), m_to_plus_inf(true) { + } + + void div(mpbq const & a, mpbq const & b, mpbq & c) { + approx_div(a, b, c, m_div_precision, m_to_plus_inf); + } + + void inv(mpbq & a) { + mpbq one(1); + scoped_mpbq r(*this); + approx_div(one, a, r, m_div_precision, m_to_plus_inf); + swap(a, r); + } + }; + typedef mpbq numeral; + numeral_manager & m_manager; struct interval { numeral m_lower; @@ -59,12 +81,11 @@ namespace realclosure { swap(m_lower, l); swap(m_upper, u); } - }; - void round_to_minus_inf() {} - void round_to_plus_inf() {} - void set_rounding(bool to_plus_inf) {} + void set_rounding(bool to_plus_inf) { m_manager.m_to_plus_inf = to_plus_inf; } + void round_to_minus_inf() { set_rounding(false); } + void round_to_plus_inf() { set_rounding(true); } // Getters numeral const & lower(interval const & a) const { return a.m_lower; } @@ -110,13 +131,14 @@ namespace realclosure { struct value { unsigned m_ref_count; bool m_rational; + mpbqi m_interval; // approximation as a binary rational value(bool rat):m_ref_count(0), m_rational(rat) {} bool is_rational() const { return m_rational; } + mpbqi const & interval() const { return m_interval; } }; struct rational_value : public value { mpq m_value; - mpbqi m_interval; // approximation as a binary rational rational_value():value(true) {} }; @@ -137,6 +159,7 @@ namespace realclosure { extension * ext() const { return m_ext; } bool is_real() const { return m_real; } polynomial const & p() const { return m_p; } + mpbqi const & interval() const { return m_interval; } }; struct rational_function_value : public value { @@ -268,7 +291,7 @@ namespace realclosure { small_object_allocator * m_allocator; bool m_own_allocator; unsynch_mpq_manager & m_qm; - mpbq_manager m_bqm; + mpbq_config::numeral_manager m_bqm; mpqi_manager m_qim; mpbqi_manager m_bqim; ptr_vector m_extensions[3]; @@ -398,6 +421,7 @@ namespace realclosure { } void del_rational_function(rational_function_value * v) { + bqim().del(v->m_interval); if (v->num()) del_polynomial_expr(v->num()); if (v->den()) @@ -617,6 +641,18 @@ namespace realclosure { return r; } + void updt_interval(rational_function_value & v) { + if (v.den() == 0) { + bqim().set(v.m_interval, v.num()->interval()); + } + else if (v.num() == 0) { + bqim().inv(v.den()->interval(), v.m_interval); + } + else { + bqim().div(v.num()->interval(), v.den()->interval(), v.m_interval); + } + } + void mk_infinitesimal(symbol const & n, numeral & r) { unsigned idx = next_infinitesimal_idx(); infinitesimal * eps = alloc(infinitesimal, idx, n); @@ -626,8 +662,10 @@ namespace realclosure { mpbq tiny(1, m_eps_prec); mpbqi interval(zero, tiny); polynomial_expr * numerator = mk_polynomial_expr(2, p, eps, interval); - r.m_value = alloc(rational_function_value, numerator, 0); + rational_function_value * v = alloc(rational_function_value, numerator, 0); + r.m_value = v; inc_ref(r.m_value); + updt_interval(*v); SASSERT(sign(r) > 0); SASSERT(!is_real(r)); } @@ -1421,9 +1459,17 @@ namespace realclosure { qm().display_decimal(out, to_mpq(a), precision); } else { + // TODO } } + + void display_interval(std::ostream & out, numeral const & a) const { + if (is_zero(a)) + out << "[0, 0]"; + else + bqim().display(out, a.m_value->interval()); + } }; manager::manager(unsynch_mpq_manager & m, params_ref const & p, small_object_allocator * a) { @@ -1620,4 +1666,8 @@ namespace realclosure { m_imp->display_decimal(out, a, precision); } + void manager::display_interval(std::ostream & out, numeral const & a) const { + m_imp->display_interval(out, a); + } + }; diff --git a/src/math/realclosure/realclosure.h b/src/math/realclosure/realclosure.h index 8870644b0..6d1907a63 100644 --- a/src/math/realclosure/realclosure.h +++ b/src/math/realclosure/realclosure.h @@ -245,6 +245,9 @@ namespace realclosure { This procedure throws an exception if the \c a is not a real. */ void display_decimal(std::ostream & out, numeral const & a, unsigned precision = 10) const; + + + void display_interval(std::ostream & out, numeral const & a) const; }; class value; @@ -330,30 +333,49 @@ inline bool is_int(scoped_rcnumeral const & a) { return a.m().is_int(a); } -struct sym_pp { +struct rc_sym_pp { rcmanager & m; rcnumeral const & n; - sym_pp(rcmanager & _m, rcnumeral const & _n):m(_m), n(_n) {} - sym_pp(scoped_rcnumeral const & _n):m(_n.m()), n(_n.get()) {} + rc_sym_pp(rcmanager & _m, rcnumeral const & _n):m(_m), n(_n) {} + rc_sym_pp(scoped_rcnumeral const & _n):m(_n.m()), n(_n.get()) {} }; -inline std::ostream & operator<<(std::ostream & out, sym_pp const & n) { +inline rc_sym_pp sym_pp(scoped_rcnumeral const & _n) { + return rc_sym_pp(_n); +} + +inline std::ostream & operator<<(std::ostream & out, rc_sym_pp const & n) { n.m.display(out, n.n); return out; } -struct decimal_pp { +struct rc_decimal_pp { rcmanager & m; rcnumeral const & n; unsigned prec; - decimal_pp(rcmanager & _m, rcnumeral const & _n, unsigned p):m(_m), n(_n), prec(p) {} - decimal_pp(scoped_rcnumeral const & _n, unsigned p):m(_n.m()), n(_n.get()), prec(p) {} + rc_decimal_pp(rcmanager & _m, rcnumeral const & _n, unsigned p):m(_m), n(_n), prec(p) {} + rc_decimal_pp(scoped_rcnumeral const & _n, unsigned p):m(_n.m()), n(_n.get()), prec(p) {} }; -inline std::ostream & operator<<(std::ostream & out, decimal_pp const & n) { +inline std::ostream & operator<<(std::ostream & out, rc_decimal_pp const & n) { n.m.display_decimal(out, n.n, n.prec); return out; } +struct rc_interval_pp { + rcmanager & m; + rcnumeral const & n; + rc_interval_pp(rcmanager & _m, rcnumeral const & _n):m(_m), n(_n) {} + rc_interval_pp(scoped_rcnumeral const & _n):m(_n.m()), n(_n.get()) {} +}; + +inline std::ostream & operator<<(std::ostream & out, rc_interval_pp const & n) { + n.m.display_interval(out, n.n); + return out; +} + +inline rc_interval_pp interval_pp(rc_interval_pp const & n) { + return rc_interval_pp(n); +} #endif diff --git a/src/test/rcf.cpp b/src/test/rcf.cpp index 8cc9dec19..e71a48dc5 100644 --- a/src/test/rcf.cpp +++ b/src/test/rcf.cpp @@ -27,6 +27,7 @@ static void tst1() { scoped_rcnumeral eps(m); m.mk_infinitesimal("eps", eps); std::cout << sym_pp(eps) << std::endl; + std::cout << interval_pp(eps) << std::endl; } void tst_rcf() { From 14827e94f0348712d4a2cc11475737eb6188aec4 Mon Sep 17 00:00:00 2001 From: Leonardo de Moura Date: Fri, 4 Jan 2013 15:01:27 -0800 Subject: [PATCH 19/78] Fix typos and bugs. Add tests. Signed-off-by: Leonardo de Moura --- src/math/realclosure/realclosure.cpp | 159 ++++++++++++++++++++++----- src/math/realclosure/realclosure.h | 6 +- src/test/algebraic.cpp | 2 +- src/test/rcf.cpp | 7 ++ 4 files changed, 144 insertions(+), 30 deletions(-) diff --git a/src/math/realclosure/realclosure.cpp b/src/math/realclosure/realclosure.cpp index e426bc0cf..eae2f6087 100644 --- a/src/math/realclosure/realclosure.cpp +++ b/src/math/realclosure/realclosure.cpp @@ -76,11 +76,23 @@ namespace realclosure { numeral m_upper; unsigned m_lower_inf:1; unsigned m_upper_inf:1; - interval():m_lower_inf(true), m_upper_inf(true) {} - interval(numeral & l, numeral & u):m_lower_inf(false), m_upper_inf(false) { + unsigned m_lower_open:1; + unsigned m_upper_open:1; + interval():m_lower_inf(true), m_upper_inf(true), m_lower_open(true), m_upper_open(true) {} + interval(numeral & l, numeral & u):m_lower_inf(false), m_upper_inf(false), m_lower_open(true), m_upper_open(true) { swap(m_lower, l); swap(m_upper, u); } + numeral & lower() { return m_lower; } + numeral & upper() { return m_upper; } + void set_lower_is_inf(bool f) { m_lower_inf = f; } + void set_upper_is_inf(bool f) { m_upper_inf = f; } + void set_lower_is_open(bool f) { m_lower_open = f; } + void set_upper_is_open(bool f) { m_upper_open = f; } + numeral const & lower() const { return m_lower; } + numeral const & upper() const { return m_upper; } + bool lower_is_inf() const { return m_lower_inf; } + bool upper_is_inf() const { return m_upper_inf; } }; void set_rounding(bool to_plus_inf) { m_manager.m_to_plus_inf = to_plus_inf; } @@ -92,16 +104,16 @@ namespace realclosure { numeral const & upper(interval const & a) const { return a.m_upper; } numeral & lower(interval & a) { return a.m_lower; } numeral & upper(interval & a) { return a.m_upper; } - bool lower_is_open(interval const & a) const { return true; } - bool upper_is_open(interval const & a) const { return true; } + bool lower_is_open(interval const & a) const { return a.m_lower_open; } + bool upper_is_open(interval const & a) const { return a.m_upper_open; } bool lower_is_inf(interval const & a) const { return a.m_lower_inf; } bool upper_is_inf(interval const & a) const { return a.m_upper_inf; } // Setters void set_lower(interval & a, numeral const & n) { m_manager.set(a.m_lower, n); } void set_upper(interval & a, numeral const & n) { m_manager.set(a.m_upper, n); } - void set_lower_is_open(interval & a, bool v) { SASSERT(v); } - void set_upper_is_open(interval & a, bool v) { SASSERT(v); } + void set_lower_is_open(interval & a, bool v) { a.m_lower_open = v; } + void set_upper_is_open(interval & a, bool v) { a.m_upper_open = v; } void set_lower_is_inf(interval & a, bool v) { a.m_lower_inf = v; } void set_upper_is_inf(interval & a, bool v) { a.m_upper_inf = v; } @@ -124,14 +136,16 @@ namespace realclosure { // --------------------------------- // - // Values: rational and composite + // Values are represented as + // - arbitrary precision rationals (mpq) + // - rational functions on field extensions // // --------------------------------- struct value { - unsigned m_ref_count; - bool m_rational; - mpbqi m_interval; // approximation as a binary rational + unsigned m_ref_count; //!< Reference counter + bool m_rational; //!< True if the value is represented as an abitrary precision rational value. + mpbqi m_interval; //!< approximation as an interval with binary rational end-points value(bool rat):m_ref_count(0), m_rational(rat) {} bool is_rational() const { return m_rational; } mpbqi const & interval() const { return m_interval; } @@ -153,8 +167,8 @@ namespace realclosure { // it contains a nonzero coefficient at position k > 0. It is not a constant polynomial on m_ext. polynomial m_p; extension * m_ext; - bool m_real; - mpbqi m_interval; // approximation as a binary rational + bool m_real; //!< True if the polynomial expression does not depend on infinitesimal values + mpbqi m_interval; //!< approximation as an interval with binary rational end-points polynomial_expr():m_ext(0), m_real(false) {} extension * ext() const { return m_ext; } bool is_real() const { return m_real; } @@ -204,7 +218,7 @@ namespace realclosure { typedef int sign; - typedef std::pair p2s; + typedef std::pair p2s; typedef sarray signs; @@ -249,7 +263,7 @@ namespace realclosure { struct algebraic : public extension { polynomial m_p; signs m_signs; - bool m_real; + bool m_real; //!< True if the polynomial p does not depend on infinitesimal extensions. algebraic(unsigned idx):extension(ALGEBRAIC, idx), m_real(false) {} @@ -271,8 +285,7 @@ namespace realclosure { struct infinitesimal : public extension { symbol m_name; - mpbqi m_interval; - + infinitesimal(unsigned idx, symbol const & n):extension(INFINITESIMAL, idx), m_name(n) {} void display(std::ostream & out) const { @@ -296,7 +309,7 @@ namespace realclosure { mpbqi_manager m_bqim; ptr_vector m_extensions[3]; value * m_one; - unsigned m_eps_prec; + unsigned m_ini_precision; //!< initial precision for transcendentals, infinitesimals, etc. volatile bool m_cancel; struct scoped_polynomial_seq { @@ -352,6 +365,8 @@ namespace realclosure { m_one = mk_rational(one); inc_ref(m_one); m_cancel = false; + + updt_params(p); } ~imp() { @@ -398,7 +413,7 @@ namespace realclosure { } void updt_params(params_ref const & p) { - m_eps_prec = p.get_uint("eps_prec", 24); + m_ini_precision = p.get_uint("initial_precision", 24); // TODO } @@ -502,24 +517,48 @@ namespace realclosure { a.m_value = 0; } + /** + \brief Return true if the given interval is smaller than 1/2^k + */ + bool check_precision(mpbqi const & interval, unsigned k) { + if (interval.lower_is_inf() || interval.upper_is_inf()) + return false; + scoped_mpbq w(bqm()); + bqm().sub(interval.upper(), interval.lower(), w); + return bqm().lt_1div2k(w, k); + } + + /** + \brief Return true if v is zero. + */ static bool is_zero(value * v) { return v == 0; } + /** + \brief Return true if v is represented using a nonzero arbitrary precision rational value. + */ static bool is_nz_rational(value * v) { SASSERT(v != 0); return v->is_rational(); } + /** + \brief Return true if v is one. + */ bool is_one(value * v) const { + // TODO: make the check complete return !is_zero(v) && is_nz_rational(v) && qm().is_one(to_mpq(v)); } + /** + \brief Return true if v is a represented as a rational function of the set of field extensions. + */ static bool is_rational_function(value * v) { SASSERT(v != 0); return !(v->is_rational()); } - + static rational_value * to_nz_rational(value * v) { SASSERT(is_nz_rational(v)); return static_cast(v); @@ -659,7 +698,7 @@ namespace realclosure { m_extensions[extension::INFINITESIMAL].push_back(eps); value * p[2] = { 0, one() }; mpbq zero(0); - mpbq tiny(1, m_eps_prec); + mpbq tiny(1, m_ini_precision); mpbqi interval(zero, tiny); polynomial_expr * numerator = mk_polynomial_expr(2, p, eps, interval); rational_function_value * v = alloc(rational_function_value, numerator, 0); @@ -747,6 +786,45 @@ namespace realclosure { return to_rational_function(a)->is_real(); } + void mpq_to_mpbqi(mpq const & q, mpbqi & interval) { + interval.set_lower_is_inf(false); + interval.set_upper_is_inf(false); + if (bqm().to_mpbq(q, interval.lower())) { + bqm().set(interval.upper(), interval.lower()); + interval.set_lower_is_open(false); + interval.set_upper_is_open(false); + } + else { + bqm().set(interval.upper(), interval.lower()); + bqm().mul2(interval.upper()); + interval.set_lower_is_open(true); + interval.set_upper_is_open(true); + if (qm().is_neg(q)) { + ::swap(interval.lower(), interval.upper()); + } + while (bqim().contains_zero(interval) || !check_precision(interval, m_ini_precision)) { + checkpoint(); + bqm().refine_lower(q, interval.lower(), interval.upper()); + bqm().refine_upper(q, interval.lower(), interval.upper()); + } + } + } + + void initialize_rational_value_interval(value * a) { + // For rational values, we only compute the binary intervals if needed. + SASSERT(is_nz_rational(a)); + mpq_to_mpbqi(to_mpq(a), a->m_interval); + } + + mpbqi const & interval(value * a) const { + SASSERT(a != 0); + if (bqim().contains_zero(a->m_interval)) { + SASSERT(is_nz_rational(a)); + const_cast(this)->initialize_rational_value_interval(a); + } + return a->m_interval; + } + rational_value * mk_rational() { return new (allocator()) rational_value(); } @@ -757,6 +835,18 @@ namespace realclosure { return r; } + template + void update_mpq_value(value * a, T & v) { + SASSERT(is_nz_rational(a)); + qm().set(to_mpq(a), v); + bqim().reset(a->m_interval); + } + + template + void update_mpq_value(numeral & a, T & v) { + update_mpq_value(a.m_value, v); + } + void set(numeral & a, int n) { if (n == 0) { reset(a); @@ -769,7 +859,7 @@ namespace realclosure { inc_ref(a.m_value); } SASSERT(is_unique_nz_rational(a)); - qm().set(to_mpq(a), n); + update_mpq_value(a, n); } void set(numeral & a, mpz const & n) { @@ -784,7 +874,7 @@ namespace realclosure { inc_ref(a.m_value); } SASSERT(is_unique_nz_rational(a)); - qm().set(to_mpq(a), n); + update_mpq_value(a, n); } void set(numeral & a, mpq const & n) { @@ -799,7 +889,7 @@ namespace realclosure { inc_ref(a.m_value); } SASSERT(is_unique_nz_rational(a)); - qm().set(to_mpq(a), n); + update_mpq_value(a, n); } void set(numeral & a, numeral const & n) { @@ -1448,10 +1538,24 @@ namespace realclosure { void display(std::ostream & out, numeral const & a) const { display(out, a.m_value, false); } + + void display_non_rational_in_decimal(std::ostream & out, numeral const & a, unsigned precision) { + SASSERT(!is_zero(a)); + SASSERT(!is_nz_rational(a)); + mpbqi const & i = interval(a.m_value); + if (check_precision(i, precision*4)) { + // hack + if (bqm().is_int(i.lower())) + bqm().display_decimal(out, i.upper(), precision); + else + bqm().display_decimal(out, i.lower(), precision); + } + else { + out << "?"; + } + } void display_decimal(std::ostream & out, numeral const & a, unsigned precision) const { - if (!is_real(a)) - throw exception("number cannot be printed in decimal notation because it is not a real"); if (is_zero(a)) { out << "0"; } @@ -1459,8 +1563,7 @@ namespace realclosure { qm().display_decimal(out, to_mpq(a), precision); } else { - - // TODO + const_cast(this)->display_non_rational_in_decimal(out, a, precision); } } @@ -1468,7 +1571,7 @@ namespace realclosure { if (is_zero(a)) out << "[0, 0]"; else - bqim().display(out, a.m_value->interval()); + bqim().display(out, interval(a.m_value)); } }; diff --git a/src/math/realclosure/realclosure.h b/src/math/realclosure/realclosure.h index 6d1907a63..0c2fe8560 100644 --- a/src/math/realclosure/realclosure.h +++ b/src/math/realclosure/realclosure.h @@ -362,6 +362,10 @@ inline std::ostream & operator<<(std::ostream & out, rc_decimal_pp const & n) { return out; } +inline rc_decimal_pp decimal_pp(scoped_rcnumeral const & n, unsigned prec = 10) { + return rc_decimal_pp(n, prec); +} + struct rc_interval_pp { rcmanager & m; rcnumeral const & n; @@ -374,7 +378,7 @@ inline std::ostream & operator<<(std::ostream & out, rc_interval_pp const & n) { return out; } -inline rc_interval_pp interval_pp(rc_interval_pp const & n) { +inline rc_interval_pp interval_pp(scoped_rcnumeral const & n) { return rc_interval_pp(n); } diff --git a/src/test/algebraic.cpp b/src/test/algebraic.cpp index d758ee5d8..7c0fb4a95 100644 --- a/src/test/algebraic.cpp +++ b/src/test/algebraic.cpp @@ -208,7 +208,7 @@ void tst_refine_mpbq(int n, int d) { } void tst_refine_mpbq() { - tst_refine_mpbq(-5, 7); + tst_refine_mpbq(5, 7); } void tst_mpbq_root() { diff --git a/src/test/rcf.cpp b/src/test/rcf.cpp index e71a48dc5..87743e138 100644 --- a/src/test/rcf.cpp +++ b/src/test/rcf.cpp @@ -27,7 +27,14 @@ static void tst1() { scoped_rcnumeral eps(m); m.mk_infinitesimal("eps", eps); std::cout << sym_pp(eps) << std::endl; + std::cout << interval_pp(a) << std::endl; std::cout << interval_pp(eps) << std::endl; + mpq aux; + qm.set(aux, 1, 3); + m.set(a, aux); + std::cout << interval_pp(a) << std::endl; + std::cout << decimal_pp(eps, 4) << std::endl; + std::cout << decimal_pp(a) << std::endl; } void tst_rcf() { From 322d35529091cc5b9a825977e5b4effdca61e5b4 Mon Sep 17 00:00:00 2001 From: Leonardo de Moura Date: Sat, 5 Jan 2013 11:51:58 -0800 Subject: [PATCH 20/78] Simplify data-structures Signed-off-by: Leonardo de Moura --- src/math/realclosure/realclosure.cpp | 321 ++++++++++++++++----------- src/util/array.h | 4 + 2 files changed, 193 insertions(+), 132 deletions(-) diff --git a/src/math/realclosure/realclosure.cpp b/src/math/realclosure/realclosure.cpp index eae2f6087..7943caf53 100644 --- a/src/math/realclosure/realclosure.cpp +++ b/src/math/realclosure/realclosure.cpp @@ -93,6 +93,8 @@ namespace realclosure { numeral const & upper() const { return m_upper; } bool lower_is_inf() const { return m_lower_inf; } bool upper_is_inf() const { return m_upper_inf; } + bool lower_is_open() const { return m_lower_open; } + bool upper_is_open() const { return m_upper_open; } }; void set_rounding(bool to_plus_inf) { m_manager.m_to_plus_inf = to_plus_inf; } @@ -149,6 +151,7 @@ namespace realclosure { value(bool rat):m_ref_count(0), m_rational(rat) {} bool is_rational() const { return m_rational; } mpbqi const & interval() const { return m_interval; } + mpbqi & interval() { return m_interval; } }; struct rational_value : public value { @@ -160,52 +163,22 @@ namespace realclosure { struct extension; bool rank_lt(extension * r1, extension * r2); - - struct polynomial_expr { - // The values occurring in this polynomial m_p may only contain extensions that are smaller than m_ext. - // The polynomial m_p must not be the constant polynomial on m_ext. That is, - // it contains a nonzero coefficient at position k > 0. It is not a constant polynomial on m_ext. - polynomial m_p; - extension * m_ext; - bool m_real; //!< True if the polynomial expression does not depend on infinitesimal values - mpbqi m_interval; //!< approximation as an interval with binary rational end-points - polynomial_expr():m_ext(0), m_real(false) {} - extension * ext() const { return m_ext; } - bool is_real() const { return m_real; } - polynomial const & p() const { return m_p; } - mpbqi const & interval() const { return m_interval; } - }; struct rational_function_value : public value { - // We assume that a null polynomial_expr denotes 1. - // m_numerator OR m_denominator must be different from NULL - polynomial_expr * m_numerator; - polynomial_expr * m_denominator; + polynomial m_numerator; + polynomial m_denominator; + extension * m_ext; + bool m_real; //!< True if the polynomial expression does not depend on infinitesimal values. + rational_function_value(extension * ext):value(false), m_ext(ext), m_real(false) {} - polynomial_expr * num() const { return m_numerator; } - polynomial_expr * den() const { return m_denominator; } - - rational_function_value(polynomial_expr * num, polynomial_expr * den):value(false), m_numerator(num), m_denominator(den) { - SASSERT(num != 0 || den != 0); - } + polynomial const & num() const { return m_numerator; } + polynomial & num() { return m_numerator; } + polynomial const & den() const { return m_denominator; } + polynomial & den() { return m_denominator; } - extension * ext() const { - if (den() == 0) - return num()->ext(); - else if (num() == 0 || rank_lt(num()->ext(), den()->ext())) - return den()->ext(); - else - return num()->ext(); - } - - bool is_real() const { - if (den() == 0) - return num()->is_real(); - else if (num() == 0) - return den()->is_real(); - else - return num()->is_real() && den()->is_real(); - } + extension * ext() const { return m_ext; } + bool is_real() const { return m_real; } + void set_real(bool f) { m_real = f; } }; typedef ptr_vector value_vector; @@ -244,6 +217,7 @@ namespace realclosure { bool is_transcendental() const { return knd() == TRANSCENDENTAL; } mpbqi const & interval() const { return m_interval; } + mpbqi & interval() { return m_interval; } }; bool rank_lt(extension * r1, extension * r2) { @@ -417,18 +391,15 @@ namespace realclosure { // TODO } - void finalize_polynomial(polynomial & p) { + /** + \brief Reset the given polynomial. + That is, after the call p is the 0 polynomial. + */ + void reset_p(polynomial & p) { dec_ref(p.size(), p.c_ptr()); p.finalize(allocator()); } - void del_polynomial_expr(polynomial_expr * p) { - finalize_polynomial(p->m_p); - bqim().del(p->m_interval); - dec_ref_ext(p->m_ext); - allocator().deallocate(sizeof(polynomial_expr), p); - } - void del_rational(rational_value * v) { bqim().del(v->m_interval); qm().del(v->m_value); @@ -437,10 +408,9 @@ namespace realclosure { void del_rational_function(rational_function_value * v) { bqim().del(v->m_interval); - if (v->num()) - del_polynomial_expr(v->num()); - if (v->den()) - del_polynomial_expr(v->den()); + reset_p(v->num()); + reset_p(v->den()); + dec_ref_ext(v->ext()); allocator().deallocate(sizeof(rational_function_value), v); } @@ -452,11 +422,11 @@ namespace realclosure { } void del_algebraic(algebraic * a) { - finalize_polynomial(a->m_p); + reset_p(a->m_p); bqim().del(a->m_interval); unsigned sz = a->m_signs.size(); for (unsigned i = 0; i < sz; i++) { - finalize_polynomial(a->m_signs[i].first); + reset_p(a->m_signs[i].first); } allocator().deallocate(sizeof(algebraic), a); } @@ -544,13 +514,22 @@ namespace realclosure { } /** - \brief Return true if v is one. + \brief Return true if v is represented as rational value one. */ - bool is_one(value * v) const { - // TODO: make the check complete + bool is_rational_one(value * v) const { return !is_zero(v) && is_nz_rational(v) && qm().is_one(to_mpq(v)); } + /** + \brief Return true if v is the value one; + */ + bool is_one(value * v) const { + if (is_rational_one(v)) + return true; + // TODO: check if v is equal to one. + return false; + } + /** \brief Return true if v is a represented as a rational function of the set of field extensions. */ @@ -651,7 +630,22 @@ namespace realclosure { SASSERT(ext->is_algebraic()); return static_cast(ext); } - + + /** + \brief Return True if the given extension is a Real value. + The result is approximate for algebraic extensions. + For algebraic extensions, we have + - true result is always correct (i.e., the extension is really a real value) + - false result is approximate (i.e., the extension may be a real value although it is a root of a polynomial that contains non-real coefficients) + Example: Assume eps is an infinitesimal, and pi is 3.14... . + Assume also that ext is the unique root between (3, 4) of the following polynomial: + x^2 - (pi + eps)*x + pi*ext + Thus, x is pi, but the system will return false, since its defining polynomial has infinitesimal + coefficients. In the future, to make everything precise, we should be able to factor the polynomial + above as + (x - eps)*(x - pi) + and then detect that x is actually the root of (x - pi). + */ bool is_real(extension * ext) { switch (ext->knd()) { case extension::TRANSCENDENTAL: return true; @@ -663,48 +657,113 @@ namespace realclosure { } } - polynomial_expr * mk_polynomial_expr(unsigned sz, value * const * p, extension * ext, mpbqi & interval) { - SASSERT(sz > 1); - SASSERT(p[sz-1] != 0); - polynomial_expr * r = new (allocator()) polynomial_expr(); - r->m_p.set(allocator(), sz, p); - inc_ref(sz, p); - inc_ref_ext(ext); - r->m_ext = ext; - realclosure::swap(r->m_interval, interval); - r->m_real = is_real(ext); - for (unsigned i = 0; i < sz && r->m_real; i++) { + /** + \brief Return true if v is definitely a real value. + */ + bool is_real(value * v) { + if (v->is_rational()) + return true; + else + return to_rational_function(v)->is_real(); + } + + bool is_real(unsigned sz, value * const * p) { + for (unsigned i = 0; i < sz; i++) if (!is_real(p[i])) - r->m_real = false; - } - return r; + return false; + return true; } - void updt_interval(rational_function_value & v) { - if (v.den() == 0) { - bqim().set(v.m_interval, v.num()->interval()); - } - else if (v.num() == 0) { - bqim().inv(v.den()->interval(), v.m_interval); - } - else { - bqim().div(v.num()->interval(), v.den()->interval(), v.m_interval); - } + /** + \brief Set the polynomial p with the given coefficients as[0], ..., as[n-1] + */ + void set_p(polynomial & p, unsigned n, value * const * as) { + SASSERT(n > 0); + SASSERT(!is_zero(as[n - 1])); + reset_p(p); + p.set(allocator(), n, as); + inc_ref(n, as); } + /** + \brief Set the polynomial p as the constant polynomial 1. + */ + void set_p_one(polynomial & p) { + set_p(p, 1, &m_one); + } + + /** + \brief Set the lower bound of the given interval. + */ + void set_lower_core(mpbqi & a, mpbq const & k, bool open, bool inf) { + bqm().set(a.lower(), k); + a.set_lower_is_open(open); + a.set_lower_is_inf(inf); + } + + /** + \brief a.lower <- k + */ + void set_lower(mpbqi & a, mpbq const & k, bool open = true) { + set_lower_core(a, k, open, false); + } + + /** + \brief Set the upper bound of the given interval. + */ + void set_upper_core(mpbqi & a, mpbq const & k, bool open, bool inf) { + bqm().set(a.upper(), k); + a.set_upper_is_open(open); + a.set_upper_is_inf(inf); + } + + /** + \brief a.upper <- k + */ + void set_upper(mpbqi & a, mpbq const & k, bool open = true) { + set_upper_core(a, k, open, false); + } + + /** + \brief a <- b + */ + void set_interval(mpbqi & a, mpbqi const & b) { + set_lower_core(a, b.lower(), b.lower_is_open(), b.lower_is_inf()); + set_upper_core(a, b.upper(), b.upper_is_open(), b.upper_is_inf()); + } + + /** + \brief Create a value using the given extension. + */ + rational_function_value * mk_value(extension * ext) { + rational_function_value * v = alloc(rational_function_value, ext); + inc_ref_ext(ext); + + value * num[2] = { 0, one() }; + set_p(v->num(), 2, num); + set_p_one(v->den()); + + set_interval(v->interval(), ext->interval()); + + v->set_real(is_real(ext)); + + return v; + } + + /** + \brief Create a new infinitesimal. + */ void mk_infinitesimal(symbol const & n, numeral & r) { unsigned idx = next_infinitesimal_idx(); infinitesimal * eps = alloc(infinitesimal, idx, n); m_extensions[extension::INFINITESIMAL].push_back(eps); - value * p[2] = { 0, one() }; - mpbq zero(0); - mpbq tiny(1, m_ini_precision); - mpbqi interval(zero, tiny); - polynomial_expr * numerator = mk_polynomial_expr(2, p, eps, interval); - rational_function_value * v = alloc(rational_function_value, numerator, 0); - r.m_value = v; + + set_lower(eps->interval(), mpbq(0)); + set_upper(eps->interval(), mpbq(1, m_ini_precision)); + + r.m_value = mk_value(eps); inc_ref(r.m_value); - updt_interval(*v); + SASSERT(sign(r) > 0); SASSERT(!is_real(r)); } @@ -738,17 +797,6 @@ namespace realclosure { SASSERT(is_zero(a)); } - int sign(polynomial_expr * p) { - if (p == 0) { - // Remark: The null polynomial expression denotes 1 - return 1; - } - else { - SASSERT(!bqim().contains_zero(p->m_interval)); - return bqim().is_P(p->m_interval) ? 1 : -1; - } - } - int sign(numeral const & a) { if (is_zero(a)) return 0; @@ -756,8 +804,9 @@ namespace realclosure { return qm().is_pos(to_mpq(a)) ? 1 : -1; } else { - rational_function_value * v = to_rational_function(a); - return sign(v->num()) * sign(v->den()); + value * v = a.m_value; + SASSERT(!bqim().contains_zero(v->interval())); + return bqim().is_P(v->interval()) ? 1 : -1; } } @@ -772,7 +821,7 @@ namespace realclosure { } } - bool is_real(value * v) { + bool is_real(value * v) const { if (is_zero(v) || is_nz_rational(v)) return true; else @@ -780,10 +829,7 @@ namespace realclosure { } bool is_real(numeral const & a) const { - if (is_zero(a) || is_nz_rational(a)) - return true; - else - return to_rational_function(a)->is_real(); + return is_real(a.m_value); } void mpq_to_mpbqi(mpq const & q, mpbqi & interval) { @@ -986,7 +1032,7 @@ namespace realclosure { */ void div(value_ref_buffer & p, value * a) { SASSERT(!is_zero(a)); - if (is_one(a)) + if (is_rational_one(a)) return; unsigned sz = p.size(); for (unsigned i = 0; i < sz; i++) @@ -1102,12 +1148,17 @@ namespace realclosure { */ void mk_monic(value_ref_buffer & p) { unsigned sz = p.size(); - if (sz > 0 && !is_one(p[sz-1])) { + if (sz > 0) { SASSERT(p[sz-1] != 0); - for (unsigned i = 0; i < sz - 1; i++) { - p.set(i, div(p[i], p[sz-1])); + if (!is_one(p[sz-1])) { + for (unsigned i = 0; i < sz - 1; i++) { + p.set(i, div(p[i], p[sz-1])); + } } - p.set(0, one()); + // I perform the following assignment even when is_one(p[sz-1]) returns true, + // Reason: the leading coefficient may be equal to one but may not be encoded using + // the rational value 1. + p.set(sz-1, one()); } } @@ -1339,7 +1390,8 @@ namespace realclosure { } else { rational_function_value * v = to_rational_function(a); - std::swap(v->m_numerator, v->m_denominator); + v->num().swap(v->den()); + // TODO: Invert interval } } else { @@ -1385,13 +1437,6 @@ namespace realclosure { } } - void mark(polynomial_expr * p) { - if (p == 0) - return; - mark(p->ext()); - mark(p->p()); - } - void mark(polynomial const & p) { for (unsigned i = 0; i < p.size(); i++) { mark(p[i]); @@ -1402,6 +1447,7 @@ namespace realclosure { if (v == 0 || is_nz_rational(v)) return; rational_function_value * rf = to_rational_function(v); + mark(rf->ext()); mark(rf->num()); mark(rf->den()); } @@ -1423,7 +1469,7 @@ namespace realclosure { if (i == 0) display(out, p[i], compact); else { - if (!is_one(p[i])) { + if (!is_rational_one(p[i])) { out << "("; display(out, p[i], compact); out << ")*"; @@ -1450,8 +1496,8 @@ namespace realclosure { } }; - void display_polynomial_expr(std::ostream & out, polynomial_expr const & p, bool compact) const { - display_polynomial(out, p.p(), display_ext_proc(*this, p.ext()), compact); + void display_polynomial_expr(std::ostream & out, polynomial const & p, extension * ext, bool compact) const { + display_polynomial(out, p, display_ext_proc(*this, ext), compact); } static void display_poly_sign(std::ostream & out, int s) { @@ -1491,6 +1537,17 @@ namespace realclosure { } } + /** + \brief Return true if p is the constant polynomial where the coefficient is + the rational value 1. + + \remark This is NOT checking whether p is actually equal to 1. + That is, it is just checking the representation. + */ + bool is_rational_one(polynomial const & p) const { + return p.size() == 1 && is_one(p[0]); + } + void display(std::ostream & out, value * v, bool compact) const { if (v == 0) out << "0"; @@ -1498,19 +1555,19 @@ namespace realclosure { qm().display(out, to_mpq(v)); else { rational_function_value * rf = to_rational_function(v); - if (rf->den() == 0) { - display_polynomial_expr(out, *rf->num(), compact); + if (is_rational_one(rf->den())) { + display_polynomial_expr(out, rf->num(), rf->ext(), compact); } - else if (rf->num() == 0) { + else if (is_rational_one(rf->num())) { out << "1/("; - display_polynomial_expr(out, *rf->den(), compact); + display_polynomial_expr(out, rf->den(), rf->ext(), compact); out << ")"; } else { out << "("; - display_polynomial_expr(out, *rf->num(), compact); + display_polynomial_expr(out, rf->num(), rf->ext(), compact); out << ")/("; - display_polynomial_expr(out, *rf->den(), compact); + display_polynomial_expr(out, rf->den(), rf->ext(), compact); out << ")"; } } diff --git a/src/util/array.h b/src/util/array.h index edbab2cad..71cfd2be8 100644 --- a/src/util/array.h +++ b/src/util/array.h @@ -176,6 +176,10 @@ public: } T * c_ptr() { return m_data; } + + void swap(array & other) { + std::swap(m_data, other.m_data); + } }; template From 3ffda2535046560fffd59713665d93d9b8c6cb97 Mon Sep 17 00:00:00 2001 From: Leonardo de Moura Date: Sat, 5 Jan 2013 16:32:35 -0800 Subject: [PATCH 21/78] Implement add, sub, mul, div, inv, neg Signed-off-by: Leonardo de Moura --- src/math/realclosure/realclosure.cpp | 637 ++++++++++++++++++++++----- src/test/rcf.cpp | 19 +- src/util/array.h | 1 + src/util/ref_buffer.h | 7 +- 4 files changed, 547 insertions(+), 117 deletions(-) diff --git a/src/math/realclosure/realclosure.cpp b/src/math/realclosure/realclosure.cpp index 7943caf53..d2c12918b 100644 --- a/src/math/realclosure/realclosure.cpp +++ b/src/math/realclosure/realclosure.cpp @@ -50,6 +50,9 @@ namespace realclosure { struct mpbq_config { struct numeral_manager : public mpbq_manager { + // division is not precise + static bool precise() { return false; } + static bool field() { return true; } unsigned m_div_precision; bool m_to_plus_inf; @@ -520,6 +523,13 @@ namespace realclosure { return !is_zero(v) && is_nz_rational(v) && qm().is_one(to_mpq(v)); } + /** + \brief Return true if v is represented as rational value minus one. + */ + bool is_rational_minus_one(value * v) const { + return !is_zero(v) && is_nz_rational(v) && qm().is_minus_one(to_mpq(v)); + } + /** \brief Return true if v is the value one; */ @@ -530,6 +540,25 @@ namespace realclosure { return false; } + /** + \brief Return true if p is the constant polynomial where the coefficient is + the rational value 1. + + \remark This is NOT checking whether p is actually equal to 1. + That is, it is just checking the representation. + */ + bool is_rational_one(polynomial const & p) const { + return p.size() == 1 && is_rational_one(p[0]); + } + bool is_rational_one(value_ref_buffer const & p) const { + return p.size() == 1 && is_rational_one(p[0]); + } + + template + bool is_one(polynomial const & p) const { + return p.size() == 1 && is_one(p[0]); + } + /** \brief Return true if v is a represented as a rational function of the set of field extensions. */ @@ -685,13 +714,6 @@ namespace realclosure { inc_ref(n, as); } - /** - \brief Set the polynomial p as the constant polynomial 1. - */ - void set_p_one(polynomial & p) { - set_p(p, 1, &m_one); - } - /** \brief Set the lower bound of the given interval. */ @@ -732,21 +754,27 @@ namespace realclosure { set_upper_core(a, b.upper(), b.upper_is_open(), b.upper_is_inf()); } + /** + \brief Make a rational_function_value using the given extension, numerator and denominator. + This method does not set the interval. It remains (-oo, oo) + */ + rational_function_value * mk_rational_function_value_core(extension * ext, unsigned num_sz, value * const * num, unsigned den_sz, value * const * den) { + rational_function_value * r = alloc(rational_function_value, ext); + inc_ref_ext(ext); + set_p(r->num(), num_sz, num); + set_p(r->den(), den_sz, den); + r->set_real(is_real(ext) && is_real(num_sz, num) && is_real(den_sz, den)); + return r; + } + /** \brief Create a value using the given extension. */ - rational_function_value * mk_value(extension * ext) { - rational_function_value * v = alloc(rational_function_value, ext); - inc_ref_ext(ext); - + rational_function_value * mk_rational_function_value(extension * ext) { value * num[2] = { 0, one() }; - set_p(v->num(), 2, num); - set_p_one(v->den()); - + value * den[1] = { one() }; + rational_function_value * v = mk_rational_function_value_core(ext, 2, num, 1, den); set_interval(v->interval(), ext->interval()); - - v->set_real(is_real(ext)); - return v; } @@ -761,7 +789,7 @@ namespace realclosure { set_lower(eps->interval(), mpbq(0)); set_upper(eps->interval(), mpbq(1, m_ini_precision)); - r.m_value = mk_value(eps); + r.m_value = mk_rational_function_value(eps); inc_ref(r.m_value); SASSERT(sign(r) > 0); @@ -881,11 +909,15 @@ namespace realclosure { return r; } + void reset_interval(value * a) { + bqim().reset(a->m_interval); + } + template void update_mpq_value(value * a, T & v) { SASSERT(is_nz_rational(a)); qm().set(to_mpq(a), v); - bqim().reset(a->m_interval); + reset_interval(a); } template @@ -899,12 +931,9 @@ namespace realclosure { return; } - if (is_zero(a) || !is_unique_nz_rational(a)) { - del(a); - a.m_value = mk_rational(); - inc_ref(a.m_value); - } - SASSERT(is_unique_nz_rational(a)); + del(a); + a.m_value = mk_rational(); + inc_ref(a.m_value); update_mpq_value(a, n); } @@ -914,12 +943,9 @@ namespace realclosure { return; } - if (is_zero(a) || !is_unique_nz_rational(a)) { - del(a); - a.m_value = mk_rational(); - inc_ref(a.m_value); - } - SASSERT(is_unique_nz_rational(a)); + del(a); + a.m_value = mk_rational(); + inc_ref(a.m_value); update_mpq_value(a, n); } @@ -928,13 +954,9 @@ namespace realclosure { reset(a); return; } - - if (is_zero(a) || !is_unique_nz_rational(a)) { - del(a); - a.m_value = mk_rational(); - inc_ref(a.m_value); - } - SASSERT(is_unique_nz_rational(a)); + del(a); + a.m_value = mk_rational(); + inc_ref(a.m_value); update_mpq_value(a, n); } @@ -967,12 +989,25 @@ namespace realclosure { void add(unsigned sz1, value * const * p1, unsigned sz2, value * const * p2, value_ref_buffer & r) { r.reset(); unsigned min = std::min(sz1, sz2); - for (unsigned i = 0; i < min; i++) + unsigned i = 0; + for (; i < min; i++) r.push_back(add(p1[i], p2[i])); - for (unsigned i = 0; i < sz1; i++) + for (; i < sz1; i++) r.push_back(p1[i]); - for (unsigned i = 0; i < sz2; i++) + for (; i < sz2; i++) r.push_back(p2[i]); + SASSERT(r.size() == std::max(sz1, sz2)); + adjust_size(r); + } + + /** + \brief r <- p + a + */ + void add(unsigned sz, value * const * p, value * a, value_ref_buffer & r) { + SASSERT(sz > 0); + r.reset(); + r.push_back(add(p[0], a)); + r.append(sz - 1, p + 1); adjust_size(r); } @@ -982,12 +1017,25 @@ namespace realclosure { void sub(unsigned sz1, value * const * p1, unsigned sz2, value * const * p2, value_ref_buffer & r) { r.reset(); unsigned min = std::min(sz1, sz2); - for (unsigned i = 0; i < min; i++) + unsigned i = 0; + for (; i < min; i++) r.push_back(sub(p1[i], p2[i])); - for (unsigned i = 0; i < sz1; i++) + for (; i < sz1; i++) r.push_back(p1[i]); - for (unsigned i = 0; i < sz2; i++) + for (; i < sz2; i++) r.push_back(neg(p2[i])); + SASSERT(r.size() == std::max(sz1, sz2)); + adjust_size(r); + } + + /** + \brief r <- p - a + */ + void sub(unsigned sz, value * const * p, value * a, value_ref_buffer & r) { + SASSERT(sz > 0); + r.reset(); + r.push_back(sub(p[0], a)); + r.append(sz - 1, p + 1); adjust_size(r); } @@ -1093,6 +1141,15 @@ namespace realclosure { value_ref_buffer r(*this); div_rem(sz1, p1, sz2, p2, q, r); } + + /** + \brief r <- p/a + */ + void div(unsigned sz, value * const * p, value * a, value_ref_buffer & r) { + for (unsigned i = 0; i < sz; i++) { + r.push_back(div(p[i], a)); + } + } /** \brief r <- rem(p1, p2) @@ -1115,7 +1172,7 @@ namespace realclosure { return; } unsigned m_n = sz1 - sz2; - ratio = div(b_n, r[sz1 - 1]); + ratio = div(r[sz1 - 1], b_n); for (unsigned i = 0; i < sz2 - 1; i++) { ratio = mul(ratio, p2[i]); r.set(i + m_n, sub(r[i + m_n], ratio)); @@ -1125,6 +1182,15 @@ namespace realclosure { } } + /** + \brief r <- -p + */ + void neg(unsigned sz, value * const * p, value_ref_buffer & r) { + r.reset(); + for (unsigned i = 0; i < sz; i++) + r.push_back(neg(p[i])); + } + /** \brief r <- -r */ @@ -1134,6 +1200,19 @@ namespace realclosure { r.set(i, neg(r[i])); } + /** + \brief p <- -p + */ + void neg(polynomial & p) { + unsigned sz = p.size(); + for (unsigned i = 0; i < sz; i++) { + value * v = neg(p[i]); + inc_ref(v); + dec_ref(p[i]); + p[i] = v; + } + } + /** \brief r <- srem(p1, p2) Signed remainder @@ -1150,15 +1229,12 @@ namespace realclosure { unsigned sz = p.size(); if (sz > 0) { SASSERT(p[sz-1] != 0); - if (!is_one(p[sz-1])) { + if (!is_rational_one(p[sz-1])) { for (unsigned i = 0; i < sz - 1; i++) { p.set(i, div(p[i], p[sz-1])); } + p.set(sz-1, one()); } - // I perform the following assignment even when is_one(p[sz-1]) returns true, - // Reason: the leading coefficient may be equal to one but may not be encoded using - // the rational value 1. - p.set(sz-1, one()); } } @@ -1280,7 +1356,178 @@ namespace realclosure { sturm_seq_core(seq); } + /** + \brief Determine the sign of the new rational function value. + The idea is to keep refinining the interval until interval of v does not contain 0. + After a couple of steps we switch to expensive sign determination procedure. + Return false if v is actually zero. + */ + bool determine_sign(rational_function_value * v) { + // TODO + return true; + } + + /** + \brief Set new_p1 and new_p2 using the following normalization rules: + - new_p1 <- p1/p2[0]; new_p2 <- one IF sz2 == 1 + - new_p1 <- one; new_p2 <- p2/p1[0]; IF sz1 == 1 + - new_p1 <- p1/gcd(p1, p2); new_p2 <- p2/gcd(p1, p2); Otherwise + */ + void normalize(unsigned sz1, value * const * p1, unsigned sz2, value * const * p2, value_ref_buffer & new_p1, value_ref_buffer & new_p2) { + SASSERT(sz1 > 0 && sz2 > 0); + if (sz2 == 1) { + // - new_p1 <- p1/p2[0]; new_p2 <- one IF sz2 == 1 + div(sz1, p1, p2[0], new_p1); + new_p2.reset(); new_p2.push_back(one()); + } + else if (sz1 == 1) { + SASSERT(sz2 > 1); + // - new_p1 <- one; new_p2 <- p2/p1[0]; IF sz1 == 1 + new_p1.reset(); new_p1.push_back(one()); + div(sz2, p2, p1[0], new_p2); + } + else { + // - new_p1 <- p1/gcd(p1, p2); new_p2 <- p2/gcd(p1, p2); Otherwise + value_ref_buffer g(*this); + gcd(sz1, p1, sz2, p2, g); + if (is_rational_one(g)) { + new_p1.append(sz1, p1); + new_p2.append(sz2, p2); + } + else if (g.size() == sz1 || g.size() == sz2) { + // After dividing p1 and p2 by g, one of the quotients will have size 1. + // Thus, we have to apply the first two rules again. + value_ref_buffer tmp_p1(*this); + value_ref_buffer tmp_p2(*this); + div(sz1, p1, g.size(), g.c_ptr(), tmp_p1); + div(sz2, p2, g.size(), g.c_ptr(), tmp_p2); + if (tmp_p2.size() == 1) { + div(tmp_p1.size(), tmp_p1.c_ptr(), tmp_p2[0], new_p1); + new_p2.reset(); new_p2.push_back(one()); + } + else if (tmp_p1.size() == 1) { + SASSERT(tmp_p2.size() > 1); + new_p1.reset(); new_p1.push_back(one()); + div(tmp_p2.size(), tmp_p2.c_ptr(), tmp_p1[0], new_p2); + } + else { + UNREACHABLE(); + } + } + else { + div(sz1, p1, g.size(), g.c_ptr(), new_p1); + div(sz2, p2, g.size(), g.c_ptr(), new_p2); + SASSERT(new_p1.size() > 1); + SASSERT(new_p2.size() > 1); + } + } + } + + /** + \brief Create a new value using the a->ext(), and the given numerator and denominator. + Use interval(a) + interval(b) as an initial approximation for the interval of the result, and invoke determine_sign() + */ + value * mk_add_value(rational_function_value * a, value * b, unsigned num_sz, value * const * num, unsigned den_sz, value * const * den) { + SASSERT(num_sz > 0 && den_sz > 0); + if (num_sz == 1 && den_sz == 1) { + // In this case, the normalization rules guarantee that den is one. + SASSERT(is_one(den[0])); + return num[0]; + } + rational_function_value * r = mk_rational_function_value_core(a->ext(), num_sz, num, den_sz, den); + bqim().add(interval(a), interval(b), r->interval()); + if (determine_sign(r)) { + return r; + } + else { + // The new value is 0 + del_rational_function(r); + return 0; + } + } + + /** + \brief Add a value of 'a' the form n/1 with b where rank(a) > rank(b) + */ + value * add_p_v(rational_function_value * a, value * b) { + SASSERT(is_rational_one(a->den())); + SASSERT(compare_rank(a, b) > 0); + polynomial const & an = a->num(); + polynomial const & one = a->den(); + SASSERT(an.size() > 1); + value_ref_buffer new_num(*this); + add(an.size(), an.c_ptr(), b, new_num); + SASSERT(new_num.size() == an.size()); + return mk_add_value(a, b, new_num.size(), new_num.c_ptr(), one.size(), one.c_ptr()); + } + + /** + \brief Add a value 'a' of the form n/d with b where rank(a) > rank(b) + */ + value * add_rf_v(rational_function_value * a, value * b) { + value_ref_buffer b_ad(*this); + value_ref_buffer num(*this); + polynomial const & an = a->num(); + polynomial const & ad = a->den(); + if (is_rational_one(ad)) + return add_p_v(a, b); + // b_ad <- b * ad + mul(b, ad.size(), ad.c_ptr(), b_ad); + // num <- a + b * ad + add(an.size(), an.c_ptr(), b_ad.size(), b_ad.c_ptr(), num); + if (num.empty()) + return 0; + value_ref_buffer new_num(*this); + value_ref_buffer new_den(*this); + normalize(num.size(), num.c_ptr(), ad.size(), ad.c_ptr(), new_num, new_den); + return mk_add_value(a, b, new_num.size(), new_num.c_ptr(), new_den.size(), new_den.c_ptr()); + } + + /** + \brief Add values 'a' and 'b' of the form n/1 and rank(a) == rank(b) + */ + value * add_p_p(rational_function_value * a, rational_function_value * b) { + SASSERT(is_rational_one(a->den())); + SASSERT(is_rational_one(b->den())); + SASSERT(compare_rank(a, b) == 0); + polynomial const & an = a->num(); + polynomial const & one = a->den(); + polynomial const & bn = b->num(); + value_ref_buffer new_num(*this); + add(an.size(), an.c_ptr(), bn.size(), bn.c_ptr(), new_num); + if (new_num.empty()) + return 0; + return mk_add_value(a, b, new_num.size(), new_num.c_ptr(), one.size(), one.c_ptr()); + } + + /** + \brief Add values 'a' and 'b' of the form n/d and rank(a) == rank(b) + */ + value * add_rf_rf(rational_function_value * a, rational_function_value * b) { + SASSERT(compare_rank(a, b) == 0); + polynomial const & an = a->num(); + polynomial const & ad = a->den(); + polynomial const & bn = b->num(); + polynomial const & bd = b->den(); + if (is_rational_one(ad) && is_rational_one(bd)) + return add_p_p(a, b); + value_ref_buffer an_bd(*this); + value_ref_buffer bn_ad(*this); + mul(an.size(), an.c_ptr(), bd.size(), bd.c_ptr(), an_bd); + mul(bn.size(), bn.c_ptr(), ad.size(), ad.c_ptr(), bn_ad); + value_ref_buffer num(*this); + add(an_bd.size(), an_bd.c_ptr(), bn_ad.size(), bn_ad.c_ptr(), num); + if (num.empty()) + return 0; + value_ref_buffer den(*this); + mul(ad.size(), ad.c_ptr(), bd.size(), bd.c_ptr(), den); + value_ref_buffer new_num(*this); + value_ref_buffer new_den(*this); + normalize(num.size(), num.c_ptr(), den.size(), den.c_ptr(), new_num, new_den); + return mk_add_value(a, b, new_num.size(), new_num.c_ptr(), new_den.size(), new_den.c_ptr()); + } + value * add(value * a, value * b) { if (a == 0) return b; @@ -1295,8 +1542,13 @@ namespace realclosure { return mk_rational(r); } else { - // TODO - return 0; + switch (compare_rank(a, b)) { + case -1: return add_rf_v(to_rational_function(b), a); + case 0: return add_rf_rf(to_rational_function(a), to_rational_function(b)); + case 1: return add_rf_v(to_rational_function(a), b); + default: UNREACHABLE(); + return 0; + } } } @@ -1314,11 +1566,28 @@ namespace realclosure { return mk_rational(r); } else { - // TODO - return 0; + value_ref neg_b(*this); + neg_b = neg(b); + switch (compare_rank(a, neg_b)) { + case -1: return add_rf_v(to_rational_function(neg_b), a); + case 0: return add_rf_rf(to_rational_function(a), to_rational_function(neg_b)); + case 1: return add_rf_v(to_rational_function(a), b); + default: UNREACHABLE(); + return 0; + } } } + value * neg_rf(rational_function_value * a) { + polynomial const & an = a->num(); + polynomial const & ad = a->den(); + value_ref_buffer new_num(*this); + neg(an.size(), an.c_ptr(), new_num); + rational_function_value * r = mk_rational_function_value_core(a->ext(), new_num.size(), new_num.c_ptr(), ad.size(), ad.c_ptr()); + bqim().neg(interval(a), r->interval()); + return r; + } + value * neg(value * a) { if (a == 0) return 0; @@ -1329,87 +1598,212 @@ namespace realclosure { return mk_rational(r); } else { - // TODO + return neg_rf(to_rational_function(a)); + } + } + + /** + \brief Create a new value using the a->ext(), and the given numerator and denominator. + Use interval(a) * interval(b) as an initial approximation for the interval of the result, and invoke determine_sign() + */ + value * mk_mul_value(rational_function_value * a, value * b, unsigned num_sz, value * const * num, unsigned den_sz, value * const * den) { + SASSERT(num_sz > 0 && den_sz > 0); + if (num_sz == 1 && den_sz == 1) { + // In this case, the normalization rules guarantee that den is one. + SASSERT(is_one(den[0])); + return num[0]; + } + rational_function_value * r = mk_rational_function_value_core(a->ext(), num_sz, num, den_sz, den); + bqim().mul(interval(a), interval(b), r->interval()); + if (determine_sign(r)) { + return r; + } + else { + // The new value is 0 + del_rational_function(r); return 0; } } + /** + \brief Multiply a value of 'a' the form n/1 with b where rank(a) > rank(b) + */ + value * mul_p_v(rational_function_value * a, value * b) { + SASSERT(is_rational_one(a->den())); + SASSERT(b != 0); + SASSERT(compare_rank(a, b) > 0); + polynomial const & an = a->num(); + polynomial const & one = a->den(); + SASSERT(an.size() > 1); + value_ref_buffer new_num(*this); + mul(b, an.size(), an.c_ptr(), new_num); + SASSERT(new_num.size() == an.size()); + return mk_mul_value(a, b, new_num.size(), new_num.c_ptr(), one.size(), one.c_ptr()); + } + + /** + \brief Multiply a value 'a' of the form n/d with b where rank(a) > rank(b) + */ + value * mul_rf_v(rational_function_value * a, value * b) { + polynomial const & an = a->num(); + polynomial const & ad = a->den(); + if (is_rational_one(ad)) + return mul_p_v(a, b); + value_ref_buffer num(*this); + // num <- b * an + mul(b, an.size(), an.c_ptr(), num); + SASSERT(num.size() == an.size()); + value_ref_buffer new_num(*this); + value_ref_buffer new_den(*this); + normalize(num.size(), num.c_ptr(), ad.size(), ad.c_ptr(), new_num, new_den); + return mk_mul_value(a, b, new_num.size(), new_num.c_ptr(), new_den.size(), new_den.c_ptr()); + } + + /** + \brief Multiply values 'a' and 'b' of the form n/1 and rank(a) == rank(b) + */ + value * mul_p_p(rational_function_value * a, rational_function_value * b) { + SASSERT(is_rational_one(a->den())); + SASSERT(is_rational_one(b->den())); + SASSERT(compare_rank(a, b) == 0); + polynomial const & an = a->num(); + polynomial const & one = a->den(); + polynomial const & bn = b->num(); + value_ref_buffer new_num(*this); + mul(an.size(), an.c_ptr(), bn.size(), bn.c_ptr(), new_num); + SASSERT(!new_num.empty()); + return mk_mul_value(a, b, new_num.size(), new_num.c_ptr(), one.size(), one.c_ptr()); + } + + /** + \brief Multiply values 'a' and 'b' of the form n/d and rank(a) == rank(b) + */ + value * mul_rf_rf(rational_function_value * a, rational_function_value * b) { + SASSERT(compare_rank(a, b) == 0); + polynomial const & an = a->num(); + polynomial const & ad = a->den(); + polynomial const & bn = b->num(); + polynomial const & bd = b->den(); + if (is_rational_one(ad) && is_rational_one(bd)) + return mul_p_p(a, b); + value_ref_buffer num(*this); + value_ref_buffer den(*this); + mul(an.size(), an.c_ptr(), bn.size(), bn.c_ptr(), num); + mul(ad.size(), ad.c_ptr(), bd.size(), bd.c_ptr(), den); + SASSERT(!num.empty()); SASSERT(!den.empty()); + value_ref_buffer new_num(*this); + value_ref_buffer new_den(*this); + normalize(num.size(), num.c_ptr(), den.size(), den.c_ptr(), new_num, new_den); + return mk_mul_value(a, b, new_num.size(), new_num.c_ptr(), new_den.size(), new_den.c_ptr()); + } + value * mul(value * a, value * b) { if (a == 0 || b == 0) return 0; + else if (is_rational_one(a)) + return b; + else if (is_rational_one(b)) + return a; + else if (is_rational_minus_one(a)) + return neg(b); + else if (is_rational_minus_one(b)) + return neg(a); else if (is_nz_rational(a) && is_nz_rational(b)) { scoped_mpq r(qm()); qm().mul(to_mpq(a), to_mpq(b), r); return mk_rational(r); } else { - // TODO - return 0; + switch (compare_rank(a, b)) { + case -1: return mul_rf_v(to_rational_function(b), a); + case 0: return mul_rf_rf(to_rational_function(a), to_rational_function(b)); + case 1: return mul_rf_v(to_rational_function(a), b); + default: UNREACHABLE(); + return 0; + } } } value * div(value * a, value * b) { if (a == 0) return 0; - if (b == 0) + else if (b == 0) throw exception("division by zero"); + else if (is_rational_one(b)) + return a; + else if (is_rational_one(a)) + return inv(b); + else if (is_rational_minus_one(b)) + return neg(a); else if (is_nz_rational(a) && is_nz_rational(b)) { scoped_mpq r(qm()); qm().div(to_mpq(a), to_mpq(b), r); return mk_rational(r); } else { - // TODO - return 0; + value_ref inv_b(*this); + inv_b = inv(b); + switch (compare_rank(a, inv_b)) { + case -1: return mul_rf_v(to_rational_function(inv_b), a); + case 0: return mul_rf_rf(to_rational_function(a), to_rational_function(inv_b)); + case 1: return mul_rf_v(to_rational_function(a), inv_b); + default: UNREACHABLE(); + return 0; + } } } + value * inv_rf(rational_function_value * a) { + polynomial const & an = a->num(); + polynomial const & ad = a->den(); + rational_function_value * r = mk_rational_function_value_core(a->ext(), ad.size(), ad.c_ptr(), an.size(), an.c_ptr()); + bqim().inv(interval(a), r->interval()); + SASSERT(!bqim().contains_zero(r->interval())); + return r; + } + + value * inv(value * a) { + if (a == 0) { + throw exception("division by zero"); + } + if (is_nz_rational(a)) { + scoped_mpq r(qm()); + qm().inv(to_mpq(a), r); + return mk_rational(r); + } + else { + return inv_rf(to_rational_function(a)); + } + } + + void set(numeral & n, value * v) { + inc_ref(v); + dec_ref(n.m_value); + n.m_value = v; + } + + void neg(numeral & a) { + set(a, neg(a.m_value)); + } + + void inv(numeral & a) { + set(a, inv(a.m_value)); + } + void add(numeral const & a, numeral const & b, numeral & c) { - // TODO + set(c, add(a.m_value, b.m_value)); } void sub(numeral const & a, numeral const & b, numeral & c) { - // TODO + set(c, sub(a.m_value, b.m_value)); } void mul(numeral const & a, numeral const & b, numeral & c) { - // TODO + set(c, mul(a.m_value, b.m_value)); } - void neg(numeral & a) { - // TODO - } - - void inv(numeral & a) { - if (a.m_value == 0) { - throw exception("division by zero"); - } - else if (is_unique(a)) { - if (is_nz_rational(a)) { - qm().inv(to_mpq(a)); - } - else { - rational_function_value * v = to_rational_function(a); - v->num().swap(v->den()); - // TODO: Invert interval - } - } - else { - if (is_nz_rational(a)) { - rational_value * v = mk_rational(); - inc_ref(v); - qm().inv(to_mpq(a), to_mpq(v)); - dec_ref(a.m_value); - a.m_value = v; - } - else { - // TODO - } - } - } - void div(numeral const & a, numeral const & b, numeral & c) { - // TODO + set(c, div(a.m_value, b.m_value)); } int compare(numeral const & a, numeral const & b) { @@ -1453,6 +1847,13 @@ namespace realclosure { } }; + bool use_parenthesis(value * v) const { + if (is_zero(v) || is_nz_rational(v)) + return false; + rational_function_value * rf = to_rational_function(v); + return rf->num().size() > 1 || !is_rational_one(rf->den()); + } + template void display_polynomial(std::ostream & out, polynomial const & p, DisplayVar const & display_var, bool compact) const { unsigned i = p.size(); @@ -1470,9 +1871,15 @@ namespace realclosure { display(out, p[i], compact); else { if (!is_rational_one(p[i])) { - out << "("; - display(out, p[i], compact); - out << ")*"; + if (use_parenthesis(p[i])) { + out << "("; + display(out, p[i], compact); + out << ")*"; + } + else { + display(out, p[i], compact); + out << "*"; + } } display_var(out, compact); if (i > 1) @@ -1537,17 +1944,6 @@ namespace realclosure { } } - /** - \brief Return true if p is the constant polynomial where the coefficient is - the rational value 1. - - \remark This is NOT checking whether p is actually equal to 1. - That is, it is just checking the representation. - */ - bool is_rational_one(polynomial const & p) const { - return p.size() == 1 && is_one(p[0]); - } - void display(std::ostream & out, value * v, bool compact) const { if (v == 0) out << "0"; @@ -1831,3 +2227,24 @@ namespace realclosure { } }; + +void pp(realclosure::manager::imp * imp, realclosure::polynomial const & p, realclosure::extension * ext) { + imp->display_polynomial_expr(std::cout, p, ext, false); + std::cout << std::endl; +} + +void pp(realclosure::manager::imp * imp, realclosure::value * v) { + imp->display(std::cout, v, false); + std::cout << std::endl; +} + +void pp(realclosure::manager::imp * imp, unsigned sz, realclosure::value * const * p) { + for (unsigned i = 0; i < sz; i++) + pp(imp, p[i]); +} + +void pp(realclosure::manager::imp * imp, realclosure::manager::imp::value_ref_buffer const & p) { + for (unsigned i = 0; i < p.size(); i++) + pp(imp, p[i]); +} + diff --git a/src/test/rcf.cpp b/src/test/rcf.cpp index 87743e138..f70b9f8a3 100644 --- a/src/test/rcf.cpp +++ b/src/test/rcf.cpp @@ -22,19 +22,34 @@ static void tst1() { unsynch_mpq_manager qm; rcmanager m(qm); scoped_rcnumeral a(m); +#if 0 a = 10; std::cout << sym_pp(a) << std::endl; - scoped_rcnumeral eps(m); - m.mk_infinitesimal("eps", eps); std::cout << sym_pp(eps) << std::endl; std::cout << interval_pp(a) << std::endl; std::cout << interval_pp(eps) << std::endl; +#endif + + scoped_rcnumeral eps(m); + m.mk_infinitesimal("eps", eps); mpq aux; qm.set(aux, 1, 3); m.set(a, aux); + +#if 0 std::cout << interval_pp(a) << std::endl; std::cout << decimal_pp(eps, 4) << std::endl; std::cout << decimal_pp(a) << std::endl; + std::cout << a + eps << std::endl; + std::cout << a * eps << std::endl; + std::cout << (a + eps)*eps - eps << std::endl; +#endif + std::cout << interval_pp(a - eps*2) << std::endl; + std::cout << interval_pp(eps + 1) << std::endl; + scoped_rcnumeral t(m); + t = (a - eps*2) / (eps + 1); + std::cout << t << std::endl; + std::cout << t * (eps + 1) << std::endl; } void tst_rcf() { diff --git a/src/util/array.h b/src/util/array.h index 71cfd2be8..6f179713f 100644 --- a/src/util/array.h +++ b/src/util/array.h @@ -175,6 +175,7 @@ public: return m_data + size(); } + T const * c_ptr() const { return m_data; } T * c_ptr() { return m_data; } void swap(array & other) { diff --git a/src/util/ref_buffer.h b/src/util/ref_buffer.h index 99efe46cc..a539db689 100644 --- a/src/util/ref_buffer.h +++ b/src/util/ref_buffer.h @@ -78,11 +78,7 @@ public: return m_buffer.c_ptr(); } - T const * operator[](unsigned idx) const { - return m_buffer[idx]; - } - - T * operator[](unsigned idx) { + T * operator[](unsigned idx) const { return m_buffer[idx]; } @@ -144,6 +140,7 @@ public: return *this; reset(); append(other); + return *this; } }; From ae1da72cb7062bd2ce190dcdf76765eed297925a Mon Sep 17 00:00:00 2001 From: Leonardo de Moura Date: Sat, 5 Jan 2013 20:21:49 -0800 Subject: [PATCH 22/78] Implement compare Signed-off-by: Leonardo de Moura --- src/math/realclosure/realclosure.cpp | 35 +++++++++++++++++++++++----- src/test/rcf.cpp | 2 ++ 2 files changed, 31 insertions(+), 6 deletions(-) diff --git a/src/math/realclosure/realclosure.cpp b/src/math/realclosure/realclosure.cpp index d2c12918b..46327fc70 100644 --- a/src/math/realclosure/realclosure.cpp +++ b/src/math/realclosure/realclosure.cpp @@ -825,18 +825,21 @@ namespace realclosure { SASSERT(is_zero(a)); } - int sign(numeral const & a) { + int sign(value * a) { if (is_zero(a)) return 0; else if (is_nz_rational(a)) { return qm().is_pos(to_mpq(a)) ? 1 : -1; } else { - value * v = a.m_value; - SASSERT(!bqim().contains_zero(v->interval())); - return bqim().is_P(v->interval()) ? 1 : -1; + SASSERT(!bqim().contains_zero(a->interval())); + return bqim().is_P(a->interval()) ? 1 : -1; } } + + int sign(numeral const & a) { + return sign(a.m_value); + } bool is_int(numeral const & a) { if (is_zero(a)) @@ -1806,9 +1809,29 @@ namespace realclosure { set(c, div(a.m_value, b.m_value)); } + int compare(value * a, value * b) { + if (a == 0) + return -sign(b); + else if (b == 0) + return sign(a); + else if (is_nz_rational(a) && is_nz_rational(b)) + return qm().lt(to_mpq(a), to_mpq(b)) ? -1 : 1; + else { + // TODO: try to refine interval before switching to sub/expensive approach + if (bqm().lt(interval(a).upper(), interval(b).lower())) + return -1; + else if (bqm().lt(interval(b).upper(), interval(a).lower())) + return 1; + else { + value_ref diff(*this); + diff = sub(a, b); + return sign(diff); + } + } + } + int compare(numeral const & a, numeral const & b) { - // TODO - return 0; + return compare(a.m_value, b.m_value); } void select(numeral const & prev, numeral const & next, numeral & result) { diff --git a/src/test/rcf.cpp b/src/test/rcf.cpp index f70b9f8a3..35fd736f4 100644 --- a/src/test/rcf.cpp +++ b/src/test/rcf.cpp @@ -50,6 +50,8 @@ static void tst1() { t = (a - eps*2) / (eps + 1); std::cout << t << std::endl; std::cout << t * (eps + 1) << std::endl; + a = 10; + std::cout << (a + eps > a) << std::endl; } void tst_rcf() { From ecb58091f7fdc1effc0976318af145e63ab92756 Mon Sep 17 00:00:00 2001 From: Leonardo de Moura Date: Sat, 5 Jan 2013 21:26:12 -0800 Subject: [PATCH 23/78] Add support for transcendental values such as pi and e, and the power operator Signed-off-by: Leonardo de Moura --- src/math/interval/interval.h | 22 +++++ src/math/realclosure/realclosure.cpp | 123 ++++++++++++++++++++++++--- src/math/realclosure/realclosure.h | 10 +++ src/test/rcf.cpp | 10 +++ 4 files changed, 153 insertions(+), 12 deletions(-) diff --git a/src/math/interval/interval.h b/src/math/interval/interval.h index 98e76211e..ad0a6fa45 100644 --- a/src/math/interval/interval.h +++ b/src/math/interval/interval.h @@ -351,4 +351,26 @@ public: void e(unsigned k, interval & r); }; +template +class _scoped_interval { +public: + typedef typename Manager::interval interval; +private: + Manager & m_manager; + interval m_interval; +public: + _scoped_interval(Manager & m):m_manager(m) {} + ~_scoped_interval() { m_manager.del(m_interval); } + + Manager & m() const { return m_manager; } + + operator interval const &() const { return m_interval; } + operator interval&() { return m_interval; } + interval const & get() const { return m_interval; } + interval & get() { return m_interval; } + interval * operator->() { + return &m_interval; + } +}; + #endif diff --git a/src/math/realclosure/realclosure.cpp b/src/math/realclosure/realclosure.cpp index 46327fc70..ac0e0cfb0 100644 --- a/src/math/realclosure/realclosure.cpp +++ b/src/math/realclosure/realclosure.cpp @@ -26,6 +26,7 @@ Notes: #include"obj_ref.h" #include"ref_vector.h" #include"ref_buffer.h" +#include"cooperate.h" #ifndef REALCLOSURE_INI_BUFFER_SIZE #define REALCLOSURE_INI_BUFFER_SIZE 32 @@ -188,7 +189,7 @@ namespace realclosure { // --------------------------------- // - // Root object definition + // Field Extensions // // --------------------------------- @@ -251,9 +252,10 @@ namespace realclosure { struct transcendental : public extension { symbol m_name; + unsigned m_k; mk_interval & m_proc; - transcendental(unsigned idx, symbol const & n, mk_interval & p):extension(TRANSCENDENTAL, idx), m_name(n), m_proc(p) {} + transcendental(unsigned idx, symbol const & n, mk_interval & p):extension(TRANSCENDENTAL, idx), m_name(n), m_k(0), m_proc(p) {} void display(std::ostream & out) const { out << m_name; @@ -273,11 +275,36 @@ namespace realclosure { } }; + // --------------------------------- + // + // Predefined transcendental mk_interval procs + // + // --------------------------------- + + struct mk_pi_interval : public mk_interval { + virtual void operator()(unsigned k, mpqi_manager & im, mpqi_manager::interval & r) { + im.pi(k, r); + } + }; + + struct mk_e_interval : public mk_interval { + virtual void operator()(unsigned k, mpqi_manager & im, mpqi_manager::interval & r) { + im.e(k, r); + } + }; + + // --------------------------------- + // + // Manager + // + // --------------------------------- + struct manager::imp { typedef ref_vector value_ref_vector; typedef ref_buffer value_ref_buffer; typedef obj_ref value_ref; - + typedef _scoped_interval scoped_mpqi; + small_object_allocator * m_allocator; bool m_own_allocator; unsynch_mpq_manager & m_qm; @@ -286,6 +313,10 @@ namespace realclosure { mpbqi_manager m_bqim; ptr_vector m_extensions[3]; value * m_one; + mk_pi_interval m_mk_pi_interval; + value * m_pi; + mk_e_interval m_mk_e_interval; + value * m_e; unsigned m_ini_precision; //!< initial precision for transcendentals, infinitesimals, etc. volatile bool m_cancel; @@ -341,6 +372,8 @@ namespace realclosure { mpq one(1); m_one = mk_rational(one); inc_ref(m_one); + m_pi = 0; + m_e = 0; m_cancel = false; updt_params(p); @@ -348,6 +381,8 @@ namespace realclosure { ~imp() { dec_ref(m_one); + dec_ref(m_pi); + dec_ref(m_e); if (m_own_allocator) dealloc(m_allocator); } @@ -360,7 +395,9 @@ namespace realclosure { small_object_allocator & allocator() { return *m_allocator; } void checkpoint() { - // TODO + if (m_cancel) + throw exception("canceled"); + cooperate("rcf"); } value * one() const { @@ -690,7 +727,7 @@ namespace realclosure { \brief Return true if v is definitely a real value. */ bool is_real(value * v) { - if (v->is_rational()) + if (is_zero(v) || is_nz_rational(v)) return true; else return to_rational_function(v)->is_real(); @@ -789,8 +826,7 @@ namespace realclosure { set_lower(eps->interval(), mpbq(0)); set_upper(eps->interval(), mpbq(1, m_ini_precision)); - r.m_value = mk_rational_function_value(eps); - inc_ref(r.m_value); + set(r, mk_rational_function_value(eps)); SASSERT(sign(r) > 0); SASSERT(!is_real(r)); @@ -804,8 +840,31 @@ namespace realclosure { mk_infinitesimal(symbol(next_infinitesimal_idx()), r); } + void refine_transcedental_interval(transcendental * t) { + scoped_mpqi i(qim()); + t->m_k++; + t->m_proc(t->m_k, qim(), i); + unsigned k = std::max(t->m_k, m_ini_precision); + scoped_mpbq l(bqm()); + mpq_to_mpbqi(i->m_lower, t->interval(), k); + // save lower + bqm().set(l, t->interval().lower()); + mpq_to_mpbqi(i->m_upper, t->interval(), k); + bqm().set(t->interval().lower(), l); + } + void mk_transcendental(symbol const & n, mk_interval & proc, numeral & r) { - // TODO + unsigned idx = next_transcendental_idx(); + transcendental * t = alloc(transcendental, idx, n, proc); + m_extensions[extension::TRANSCENDENTAL].push_back(t); + + while (bqim().contains_zero(t->interval())) { + checkpoint(); + refine_transcedental_interval(t); + } + set(r, mk_rational_function_value(t)); + + SASSERT(is_real(r)); } void mk_transcendental(char const * p, mk_interval & proc, numeral & r) { @@ -816,6 +875,28 @@ namespace realclosure { mk_transcendental(symbol(next_transcendental_idx()), proc, r); } + void mk_pi(numeral & r) { + if (m_pi) { + set(r, m_pi); + } + else { + mk_transcendental(symbol("pi"), m_mk_pi_interval, r); + m_pi = r.m_value; + inc_ref(m_pi); + } + } + + void mk_e(numeral & r) { + if (m_e) { + set(r, m_e); + } + else { + mk_transcendental(symbol("e"), m_mk_e_interval, r); + m_e = r.m_value; + inc_ref(m_e); + } + } + void isolate_roots(unsigned n, numeral const * as, numeral_vector & roots) { // TODO } @@ -863,7 +944,7 @@ namespace realclosure { return is_real(a.m_value); } - void mpq_to_mpbqi(mpq const & q, mpbqi & interval) { + void mpq_to_mpbqi(mpq const & q, mpbqi & interval, unsigned k) { interval.set_lower_is_inf(false); interval.set_upper_is_inf(false); if (bqm().to_mpbq(q, interval.lower())) { @@ -879,7 +960,7 @@ namespace realclosure { if (qm().is_neg(q)) { ::swap(interval.lower(), interval.upper()); } - while (bqim().contains_zero(interval) || !check_precision(interval, m_ini_precision)) { + while (bqim().contains_zero(interval) || !check_precision(interval, k)) { checkpoint(); bqm().refine_lower(q, interval.lower(), interval.upper()); bqm().refine_upper(q, interval.lower(), interval.upper()); @@ -890,7 +971,7 @@ namespace realclosure { void initialize_rational_value_interval(value * a) { // For rational values, we only compute the binary intervals if needed. SASSERT(is_nz_rational(a)); - mpq_to_mpbqi(to_mpq(a), a->m_interval); + mpq_to_mpbqi(to_mpq(a), a->m_interval, m_ini_precision); } mpbqi const & interval(value * a) const { @@ -974,7 +1055,17 @@ namespace realclosure { } void power(numeral const & a, unsigned k, numeral & b) { - // TODO + unsigned mask = 1; + value_ref power(*this); + power = a.m_value; + set(b, one()); + while (mask <= k) { + checkpoint(); + if (mask & k) + set(b, mul(b.m_value, power)); + power = mul(power, power); + mask = mask << 1; + } } /** @@ -2095,6 +2186,14 @@ namespace realclosure { m_imp->mk_transcendental(proc, r); } + void manager::mk_pi(numeral & r) { + m_imp->mk_pi(r); + } + + void manager::mk_e(numeral & r) { + m_imp->mk_e(r); + } + void manager::isolate_roots(unsigned n, numeral const * as, numeral_vector & roots) { m_imp->isolate_roots(n, as, roots); } diff --git a/src/math/realclosure/realclosure.h b/src/math/realclosure/realclosure.h index 0c2fe8560..151323886 100644 --- a/src/math/realclosure/realclosure.h +++ b/src/math/realclosure/realclosure.h @@ -85,6 +85,16 @@ namespace realclosure { void mk_transcendental(char const * name, mk_interval & proc, numeral & r); void mk_transcendental(mk_interval & proc, numeral & r); + /** + \brief r <- pi + */ + void mk_pi(numeral & r); + + /** + \brief r <- e (Euler's constant) + */ + void mk_e(numeral & r); + /** \brief Isolate the roots of the univariate polynomial as[0] + as[1]*x + ... + as[n-1]*x^{n-1} The roots are stored in \c roots. diff --git a/src/test/rcf.cpp b/src/test/rcf.cpp index 35fd736f4..7b2445320 100644 --- a/src/test/rcf.cpp +++ b/src/test/rcf.cpp @@ -52,6 +52,16 @@ static void tst1() { std::cout << t * (eps + 1) << std::endl; a = 10; std::cout << (a + eps > a) << std::endl; + scoped_rcnumeral pi(m); + m.mk_pi(pi); + std::cout << pi + 1 << std::endl; + std::cout << decimal_pp(pi + 1, 1) << std::endl; + scoped_rcnumeral e(m); + m.mk_e(e); + t = e + (pi + 1)*2; + std::cout << t << std::endl; + std::cout << decimal_pp(t, 1) << std::endl; + } void tst_rcf() { From 47c6a73e1975311244867df5ab8afbf8d89d505b Mon Sep 17 00:00:00 2001 From: Leonardo de Moura Date: Sat, 5 Jan 2013 22:24:56 -0800 Subject: [PATCH 24/78] Add RCF external API skeletons Signed-off-by: Leonardo de Moura --- scripts/mk_project.py | 2 +- src/api/api_context.cpp | 14 +++ src/api/api_context.h | 19 +++- src/api/api_rcf.cpp | 191 ++++++++++++++++++++++++++++++++++++++ src/api/api_rcf.h | 38 ++++++++ src/api/python/z3types.py | 3 + src/api/z3.h | 1 + src/api/z3_api.h | 2 + src/api/z3_rcf.h | 159 +++++++++++++++++++++++++++++++ 9 files changed, 427 insertions(+), 2 deletions(-) create mode 100644 src/api/api_rcf.cpp create mode 100644 src/api/api_rcf.h create mode 100644 src/api/z3_rcf.h diff --git a/scripts/mk_project.py b/scripts/mk_project.py index 3da30683e..d11c5fd30 100644 --- a/scripts/mk_project.py +++ b/scripts/mk_project.py @@ -59,7 +59,7 @@ def init_project_def(): add_lib('ufbv_tactic', ['normal_forms', 'core_tactics', 'macros', 'smt_tactic', 'rewriter'], 'tactic/ufbv') add_lib('portfolio', ['smtlogic_tactics', 'ufbv_tactic', 'fpa', 'aig_tactic', 'muz_qe', 'sls_tactic', 'subpaving_tactic'], 'tactic/portfolio') add_lib('smtparser', ['portfolio'], 'parsers/smt') - API_files = ['z3_api.h', 'z3_algebraic.h', 'z3_polynomial.h'] + API_files = ['z3_api.h', 'z3_algebraic.h', 'z3_polynomial.h', 'z3_rcf.h'] add_lib('api', ['portfolio', 'user_plugin', 'smtparser', 'realclosure'], includes2install=['z3.h', 'z3_v1.h', 'z3_macros.h'] + API_files) add_exe('shell', ['api', 'sat', 'extra_cmds'], exe_name='z3') diff --git a/src/api/api_context.cpp b/src/api/api_context.cpp index 9ebc771f0..6b3fba6ae 100644 --- a/src/api/api_context.cpp +++ b/src/api/api_context.cpp @@ -25,6 +25,7 @@ Revision History: #include"api_log_macros.h" #include"api_util.h" #include"reg_decl_plugins.h" +#include"realclosure.h" // The install_tactics procedure is automatically generated void install_tactics(tactic_manager & ctx); @@ -391,6 +392,19 @@ namespace api { m_smtlib_parser_sorts.reset(); } } + + // ------------------------ + // + // RCF manager + // + // ----------------------- + realclosure::manager & context::rcfm() { + if (m_rcf_manager.get() == 0) { + m_rcf_manager = alloc(realclosure::manager, m_rcf_qm); + } + return *(m_rcf_manager.get()); + } + }; diff --git a/src/api/api_context.h b/src/api/api_context.h index 896011615..e0c95b07b 100644 --- a/src/api/api_context.h +++ b/src/api/api_context.h @@ -38,6 +38,10 @@ namespace smtlib { class parser; }; +namespace realclosure { + class manager; +}; + namespace api { Z3_search_failure mk_Z3_search_failure(smt::failure f); @@ -83,7 +87,6 @@ namespace api { event_handler * m_interruptable; // Reference to an object that can be interrupted by Z3_interrupt - pmanager m_pmanager; public: // Scoped obj for setting m_interruptable class set_interruptable { @@ -175,8 +178,22 @@ namespace api { // Polynomial manager & caches // // ----------------------- + private: + pmanager m_pmanager; + public: polynomial::manager & pm() { return m_pmanager.pm(); } + // ------------------------ + // + // RCF manager + // + // ----------------------- + private: + unsynch_mpq_manager m_rcf_qm; + scoped_ptr m_rcf_manager; + public: + realclosure::manager & rcfm(); + // ------------------------ // // Solver interface for backward compatibility diff --git a/src/api/api_rcf.cpp b/src/api/api_rcf.cpp new file mode 100644 index 000000000..681cb814e --- /dev/null +++ b/src/api/api_rcf.cpp @@ -0,0 +1,191 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + api_rcf.cpp + +Abstract: + + Additional APIs for handling elements of the Z3 real closed field that contains: + - transcendental extensions + - infinitesimal extensions + - algebraic extensions + +Author: + + Leonardo de Moura (leonardo) 2012-01-05 + +Notes: + +--*/ +#include +#include"z3.h" +#include"api_log_macros.h" +#include"api_context.h" +#include"api_rcf.h" + +extern "C" { + + void Z3_API Z3_rcf_inc_ref(Z3_context c, Z3_rcf_num a) { + Z3_TRY; + LOG_Z3_rcf_inc_ref(c, a); + RESET_ERROR_CODE(); + to_rcf_num(a)->inc_ref(); + Z3_CATCH; + } + + void Z3_API Z3_rcf_dec_ref(Z3_context c, Z3_rcf_num a) { + Z3_TRY; + LOG_Z3_rcf_dec_ref(c, a); + RESET_ERROR_CODE(); + to_rcf_num(a)->dec_ref(); + Z3_CATCH; + } + + Z3_rcf_num Z3_API Z3_rcf_mk_rational(Z3_context c, Z3_string val) { + Z3_TRY; + LOG_Z3_rcf_mk_rational(c, val); + RESET_ERROR_CODE(); + // TODO + RETURN_Z3(0); + Z3_CATCH_RETURN(0); + } + + Z3_rcf_num Z3_API Z3_rcf_mk_small_int(Z3_context c, int val) { + Z3_TRY; + LOG_Z3_rcf_mk_small_int(c, val); + RESET_ERROR_CODE(); + // TODO + RETURN_Z3(0); + Z3_CATCH_RETURN(0); + } + + Z3_rcf_num Z3_API Z3_rcf_mk_pi(Z3_context c) { + Z3_TRY; + LOG_Z3_rcf_mk_pi(c); + RESET_ERROR_CODE(); + // TODO + RETURN_Z3(0); + Z3_CATCH_RETURN(0); + } + + Z3_rcf_num Z3_API Z3_rcf_mk_e(Z3_context c) { + Z3_TRY; + LOG_Z3_rcf_mk_e(c); + RESET_ERROR_CODE(); + // TODO + RETURN_Z3(0); + Z3_CATCH_RETURN(0); + } + + Z3_rcf_num Z3_API Z3_rcf_mk_infinitesimal(Z3_context c, Z3_string name) { + Z3_TRY; + LOG_Z3_rcf_mk_infinitesimal(c, name); + RESET_ERROR_CODE(); + // TODO + RETURN_Z3(0); + Z3_CATCH_RETURN(0); + } + + Z3_rcf_num Z3_API Z3_rcf_add(Z3_context c, Z3_rcf_num a, Z3_rcf_num b) { + Z3_TRY; + LOG_Z3_rcf_add(c, a, b); + RESET_ERROR_CODE(); + // TODO + RETURN_Z3(0); + Z3_CATCH_RETURN(0); + } + + Z3_rcf_num Z3_API Z3_rcf_sub(Z3_context c, Z3_rcf_num a, Z3_rcf_num b) { + Z3_TRY; + LOG_Z3_rcf_sub(c, a, b); + RESET_ERROR_CODE(); + // TODO + RETURN_Z3(0); + Z3_CATCH_RETURN(0); + } + + Z3_rcf_num Z3_API Z3_rcf_mul(Z3_context c, Z3_rcf_num a, Z3_rcf_num b) { + Z3_TRY; + LOG_Z3_rcf_mul(c, a, b); + RESET_ERROR_CODE(); + // TODO + RETURN_Z3(0); + Z3_CATCH_RETURN(0); + } + + Z3_rcf_num Z3_API Z3_rcf_div(Z3_context c, Z3_rcf_num a, Z3_rcf_num b) { + Z3_TRY; + LOG_Z3_rcf_div(c, a, b); + RESET_ERROR_CODE(); + // TODO + RETURN_Z3(0); + Z3_CATCH_RETURN(0); + } + + Z3_rcf_num Z3_API Z3_rcf_neg(Z3_context c, Z3_rcf_num a) { + Z3_TRY; + LOG_Z3_rcf_neg(c, a); + RESET_ERROR_CODE(); + // TODO + RETURN_Z3(0); + Z3_CATCH_RETURN(0); + } + + Z3_rcf_num Z3_API Z3_rcf_inv(Z3_context c, Z3_rcf_num a) { + Z3_TRY; + LOG_Z3_rcf_inv(c, a); + RESET_ERROR_CODE(); + // TODO + RETURN_Z3(0); + Z3_CATCH_RETURN(0); + } + + Z3_bool Z3_API Z3_rcf_lt(Z3_context c, Z3_rcf_num a, Z3_rcf_num b) { + Z3_TRY; + LOG_Z3_rcf_lt(c, a, b); + RESET_ERROR_CODE(); + // TODO + return Z3_FALSE; + Z3_CATCH_RETURN(Z3_FALSE); + } + + Z3_bool Z3_API Z3_rcf_gt(Z3_context c, Z3_rcf_num a, Z3_rcf_num b) { + Z3_TRY; + LOG_Z3_rcf_gt(c, a, b); + RESET_ERROR_CODE(); + // TODO + return Z3_FALSE; + Z3_CATCH_RETURN(Z3_FALSE); + } + + Z3_bool Z3_API Z3_rcf_le(Z3_context c, Z3_rcf_num a, Z3_rcf_num b) { + Z3_TRY; + LOG_Z3_rcf_le(c, a, b); + RESET_ERROR_CODE(); + // TODO + return Z3_FALSE; + Z3_CATCH_RETURN(Z3_FALSE); + } + + Z3_bool Z3_API Z3_rcf_ge(Z3_context c, Z3_rcf_num a, Z3_rcf_num b) { + Z3_TRY; + LOG_Z3_rcf_ge(c, a, b); + RESET_ERROR_CODE(); + // TODO + return Z3_FALSE; + Z3_CATCH_RETURN(Z3_FALSE); + } + + Z3_string Z3_API Z3_rcf_num_to_string(Z3_context c, Z3_rcf_num a) { + Z3_TRY; + LOG_Z3_rcf_num_to_string(c, a); + RESET_ERROR_CODE(); + std::ostringstream buffer; + // TODO + return mk_c(c)->mk_external_string(buffer.str()); + Z3_CATCH_RETURN(""); + } + +}; diff --git a/src/api/api_rcf.h b/src/api/api_rcf.h new file mode 100644 index 000000000..2026fb21e --- /dev/null +++ b/src/api/api_rcf.h @@ -0,0 +1,38 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + api_rcf.h + +Abstract: + + Additional APIs for handling elements of the Z3 real closed field that contains: + - transcendental extensions + - infinitesimal extensions + - algebraic extensions + +Author: + + Leonardo de Moura (leonardo) 2012-01-05 + +Notes: + +--*/ +#ifndef _API_RCF_H_ +#define _API_RCF_H_ + +#include"api_util.h" +#include"realclosure.h" + +struct Z3_rcf_num_ref : public api::object { + rcnumeral m_num; + virtual ~Z3_rcf_num_ref() {} +}; + + +inline Z3_rcf_num_ref * to_rcf_num(Z3_rcf_num n) { return reinterpret_cast(n); } +inline Z3_rcf_num of_rcf_num(Z3_rcf_num_ref * n) { return reinterpret_cast(n); } +inline rcnumeral & to_rcnumeral(Z3_rcf_num n) { SASSERT(n != 0); return to_rcf_num(n)->m_num; } + +#endif diff --git a/src/api/python/z3types.py b/src/api/python/z3types.py index 3a2449203..a26a958c0 100644 --- a/src/api/python/z3types.py +++ b/src/api/python/z3types.py @@ -106,3 +106,6 @@ class FuncEntryObj(ctypes.c_void_p): def __init__(self, e): self._as_parameter_ = e def from_param(obj): return obj +class RCFNumObj(ctypes.c_void_p): + def __init__(self, e): self._as_parameter_ = e + def from_param(obj): return obj diff --git a/src/api/z3.h b/src/api/z3.h index c3e38e3f1..db8becafd 100644 --- a/src/api/z3.h +++ b/src/api/z3.h @@ -26,6 +26,7 @@ Notes: #include"z3_api.h" #include"z3_algebraic.h" #include"z3_polynomial.h" +#include"z3_rcf.h" #undef __in #undef __out diff --git a/src/api/z3_api.h b/src/api/z3_api.h index 98bde7bcf..410945235 100644 --- a/src/api/z3_api.h +++ b/src/api/z3_api.h @@ -47,6 +47,7 @@ DEFINE_TYPE(Z3_func_interp); #define Z3_func_interp_opt Z3_func_interp DEFINE_TYPE(Z3_func_entry); DEFINE_TYPE(Z3_fixedpoint); +DEFINE_TYPE(Z3_rcf_num); DEFINE_VOID(Z3_theory_data); #endif @@ -1190,6 +1191,7 @@ typedef enum def_Type('FUNC_ENTRY', 'Z3_func_entry', 'FuncEntryObj') def_Type('FIXEDPOINT', 'Z3_fixedpoint', 'FixedpointObj') def_Type('PARAM_DESCRS', 'Z3_param_descrs', 'ParamDescrs') + def_Type('RCF_NUM', 'Z3_rcf_num', 'RCFNumObj') */ #ifdef Conly diff --git a/src/api/z3_rcf.h b/src/api/z3_rcf.h new file mode 100644 index 000000000..5b5cc112d --- /dev/null +++ b/src/api/z3_rcf.h @@ -0,0 +1,159 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + z3_rcf.h + +Abstract: + + Additional APIs for handling elements of the Z3 real closed field that contains: + - transcendental extensions + - infinitesimal extensions + - algebraic extensions + +Author: + + Leonardo de Moura (leonardo) 2012-01-05 + +Notes: + +--*/ +#ifndef _Z3_RCF_H_ +#define _Z3_RCF_H_ + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + + /** + \brief Increment the reference counter of a RCF numeral. + + def_API('Z3_rcf_inc_ref', VOID, (_in(CONTEXT), _in(RCF_NUM))) + */ + void Z3_API Z3_rcf_inc_ref(__in Z3_context c, __in Z3_rcf_num a); + + /** + \brief Decrement the reference counter of a RCF numeral. + + def_API('Z3_rcf_dec_ref', VOID, (_in(CONTEXT), _in(RCF_NUM))) + */ + void Z3_API Z3_rcf_dec_ref(__in Z3_context c, __in Z3_rcf_num a); + + /** + \brief Return a RCF rational using the given string. + + def_API('Z3_rcf_mk_rational', RCF_NUM, (_in(CONTEXT), _in(STRING))) + */ + Z3_rcf_num Z3_API Z3_rcf_mk_rational(__in Z3_context c, __in Z3_string val); + + /** + \brief Return a RCF small integer. + + def_API('Z3_rcf_mk_small_int', RCF_NUM, (_in(CONTEXT), _in(INT))) + */ + Z3_rcf_num Z3_API Z3_rcf_mk_small_int(__in Z3_context c, __in int val); + + /** + \brief Return Pi + + def_API('Z3_rcf_mk_pi', RCF_NUM, (_in(CONTEXT),)) + */ + Z3_rcf_num Z3_API Z3_rcf_mk_pi(__in Z3_context c); + + /** + \brief Return e (Euler's constant) + + def_API('Z3_rcf_mk_e', RCF_NUM, (_in(CONTEXT),)) + */ + Z3_rcf_num Z3_API Z3_rcf_mk_e(__in Z3_context c); + + /** + \brief Return a new infinitesimal that is smaller than all elements in the Z3 field. + + def_API('Z3_rcf_mk_infinitesimal', RCF_NUM, (_in(CONTEXT), _in(STRING))) + */ + Z3_rcf_num Z3_API Z3_rcf_mk_infinitesimal(__in Z3_context c, __in Z3_string name); + + /** + \brief Return the value a + b. + + def_API('Z3_rcf_add', RCF_NUM, (_in(CONTEXT), _in(RCF_NUM), _in(RCF_NUM))) + */ + Z3_rcf_num Z3_API Z3_rcf_add(__in Z3_context c, __in Z3_rcf_num a, __in Z3_rcf_num b); + + /** + \brief Return the value a - b. + + def_API('Z3_rcf_sub', RCF_NUM, (_in(CONTEXT), _in(RCF_NUM), _in(RCF_NUM))) + */ + Z3_rcf_num Z3_API Z3_rcf_sub(__in Z3_context c, __in Z3_rcf_num a, __in Z3_rcf_num b); + + /** + \brief Return the value a * b. + + def_API('Z3_rcf_mul', RCF_NUM, (_in(CONTEXT), _in(RCF_NUM), _in(RCF_NUM))) + */ + Z3_rcf_num Z3_API Z3_rcf_mul(__in Z3_context c, __in Z3_rcf_num a, __in Z3_rcf_num b); + + /** + \brief Return the value a / b. + + def_API('Z3_rcf_div', RCF_NUM, (_in(CONTEXT), _in(RCF_NUM), _in(RCF_NUM))) + */ + Z3_rcf_num Z3_API Z3_rcf_div(__in Z3_context c, __in Z3_rcf_num a, __in Z3_rcf_num b); + + /** + \brief Return the value -a + + def_API('Z3_rcf_neg', RCF_NUM, (_in(CONTEXT), _in(RCF_NUM))) + */ + Z3_rcf_num Z3_API Z3_rcf_neg(__in Z3_context c, __in Z3_rcf_num a); + + /** + \brief Return the value 1/a + + def_API('Z3_rcf_inv', RCF_NUM, (_in(CONTEXT), _in(RCF_NUM))) + */ + Z3_rcf_num Z3_API Z3_rcf_inv(__in Z3_context c, __in Z3_rcf_num a); + + /** + \brief Return Z3_TRUE if a < b + + def_API('Z3_rcf_lt', BOOL, (_in(CONTEXT), _in(RCF_NUM), _in(RCF_NUM))) + */ + Z3_bool Z3_API Z3_rcf_lt(__in Z3_context c, __in Z3_rcf_num a, __in Z3_rcf_num b); + + /** + \brief Return Z3_TRUE if a > b + + def_API('Z3_rcf_gt', BOOL, (_in(CONTEXT), _in(RCF_NUM), _in(RCF_NUM))) + */ + Z3_bool Z3_API Z3_rcf_gt(__in Z3_context c, __in Z3_rcf_num a, __in Z3_rcf_num b); + + /** + \brief Return Z3_TRUE if a <= b + + def_API('Z3_rcf_le', BOOL, (_in(CONTEXT), _in(RCF_NUM), _in(RCF_NUM))) + */ + Z3_bool Z3_API Z3_rcf_le(__in Z3_context c, __in Z3_rcf_num a, __in Z3_rcf_num b); + + /** + \brief Return Z3_TRUE if a >= b + + def_API('Z3_rcf_ge', BOOL, (_in(CONTEXT), _in(RCF_NUM), _in(RCF_NUM))) + */ + Z3_bool Z3_API Z3_rcf_ge(__in Z3_context c, __in Z3_rcf_num a, __in Z3_rcf_num b); + + /** + \brief Convert the RCF numeral into a string. + + def_API('Z3_rcf_num_to_string', STRING, (_in(CONTEXT), _in(RCF_NUM))) + */ + Z3_string Z3_API Z3_rcf_num_to_string(__in Z3_context c, __in Z3_rcf_num a); + +#ifdef __cplusplus +}; +#endif // __cplusplus + +#endif From c01f27fe132a762f4ef73ae83e5dbef1dcb88fab Mon Sep 17 00:00:00 2001 From: Leonardo de Moura Date: Sun, 6 Jan 2013 10:46:38 -0800 Subject: [PATCH 25/78] Add small interval caching infrastructure Signed-off-by: Leonardo de Moura --- src/math/realclosure/realclosure.cpp | 119 +++++++++++++++++++++++++-- src/math/realclosure/realclosure.h | 1 + 2 files changed, 112 insertions(+), 8 deletions(-) diff --git a/src/math/realclosure/realclosure.cpp b/src/math/realclosure/realclosure.cpp index ac0e0cfb0..11b05b6d3 100644 --- a/src/math/realclosure/realclosure.cpp +++ b/src/math/realclosure/realclosure.cpp @@ -149,10 +149,16 @@ namespace realclosure { // --------------------------------- struct value { - unsigned m_ref_count; //!< Reference counter - bool m_rational; //!< True if the value is represented as an abitrary precision rational value. - mpbqi m_interval; //!< approximation as an interval with binary rational end-points - value(bool rat):m_ref_count(0), m_rational(rat) {} + unsigned m_ref_count; //!< Reference counter + bool m_rational; //!< True if the value is represented as an abitrary precision rational value. + mpbqi m_interval; //!< approximation as an interval with binary rational end-points + // When performing an operation OP, we may have to make the width (upper - lower) of m_interval very small. + // The precision (i.e., a small interval) needed for executing OP is usually unnecessary for subsequent operations, + // This unnecessary precision will only slowdown the subsequent operations that do not need it. + // To cope with this issue, we cache the value m_interval in m_old_interval whenever the width of m_interval is below + // a give threshold. Then, after finishing OP, we restore the old_interval. + mpbqi * m_old_interval; + value(bool rat):m_ref_count(0), m_rational(rat), m_old_interval(0) {} bool is_rational() const { return m_rational; } mpbqi const & interval() const { return m_interval; } mpbqi & interval() { return m_interval; } @@ -304,7 +310,7 @@ namespace realclosure { typedef ref_buffer value_ref_buffer; typedef obj_ref value_ref; typedef _scoped_interval scoped_mpqi; - + small_object_allocator * m_allocator; bool m_own_allocator; unsynch_mpq_manager & m_qm; @@ -317,7 +323,12 @@ namespace realclosure { value * m_pi; mk_e_interval m_mk_e_interval; value * m_e; + ptr_vector m_to_restore; //!< Set of values v s.t. v->m_old_interval != 0 + + // Parameters unsigned m_ini_precision; //!< initial precision for transcendentals, infinitesimals, etc. + int m_min_magnitude; + volatile bool m_cancel; struct scoped_polynomial_seq { @@ -380,6 +391,7 @@ namespace realclosure { } ~imp() { + restore_saved_intervals(); // to free memory dec_ref(m_one); dec_ref(m_pi); dec_ref(m_e); @@ -404,6 +416,73 @@ namespace realclosure { return m_one; } + /** + \brief Return the magnitude of the given interval. + The magnitude is an approximation of the size of the interval. + */ + int magnitude(mpbq const & l, mpbq const & u) { + SASSERT(bqm().is_nonneg(l) || bqm().is_nonpos(u)); + int l_k = l.k(); + int u_k = u.k(); + if (l_k == u_k) + return bqm().magnitude_ub(l); + if (bqm().is_nonneg(l)) + return qm().log2(u.numerator()) - qm().log2(l.numerator()) - u_k + l_k - u_k; + else + return qm().mlog2(u.numerator()) - qm().mlog2(l.numerator()) - u_k + l_k - u_k; + } + + /** + \brief Return the magnitude of the given interval. + The magnitude is an approximation of the size of the interval. + */ + int magnitude(mpbqi const & i) { + return magnitude(i.lower(), i.upper()); + } + + /** + \brief Return true if the magnitude of the given interval is less than the parameter m_min_magnitude + */ + bool too_small(mpbqi const & i) { + return magnitude(i) < m_min_magnitude; + } + + /** + \brief Save the current interval (i.e., approximation) of the given value. + */ + void save_interval(value * v) { + if (v->m_old_interval != 0) + return; // interval was already saved. + m_to_restore.push_back(v); + inc_ref(v); + v->m_old_interval = new (allocator()) mpbqi(); + set_interval(*(v->m_old_interval), v->m_interval); + } + + /** + \brief Save the current interval (i.e., approximation) of the given value IF it is too small (i.e., too_small(v) == True). + */ + void save_interval_if_too_small(value * v) { + if (too_small(v->m_interval)) + save_interval(v); + } + + /** + \brief Restore the saved intervals (approximations) of RCF values. + */ + void restore_saved_intervals() { + unsigned sz = m_to_restore.size(); + for (unsigned i = 0; i < sz; i++) { + value * v = m_to_restore[i]; + set_interval(v->m_interval, *(v->m_old_interval)); + bqim().del(*(v->m_old_interval)); + allocator().deallocate(sizeof(mpbqi), v->m_old_interval); + v->m_old_interval = 0; + dec_ref(v); + } + m_to_restore.reset(); + } + void cleanup(extension::kind k) { ptr_vector & exts = m_extensions[k]; // keep removing unused slots @@ -427,7 +506,8 @@ namespace realclosure { } void updt_params(params_ref const & p) { - m_ini_precision = p.get_uint("initial_precision", 24); + m_ini_precision = p.get_uint("initial_precision", 24); + m_min_magnitude = -static_cast(p.get_uint("min_mag", 64)); // TODO } @@ -1526,7 +1606,7 @@ namespace realclosure { SASSERT(num_sz > 0 && den_sz > 0); if (num_sz == 1 && den_sz == 1) { // In this case, the normalization rules guarantee that den is one. - SASSERT(is_one(den[0])); + SASSERT(is_rational_one(den[0])); return num[0]; } rational_function_value * r = mk_rational_function_value_core(a->ext(), num_sz, num, den_sz, den); @@ -1704,7 +1784,7 @@ namespace realclosure { SASSERT(num_sz > 0 && den_sz > 0); if (num_sz == 1 && den_sz == 1) { // In this case, the normalization rules guarantee that den is one. - SASSERT(is_one(den[0])); + SASSERT(is_rational_one(den[0])); return num[0]; } rational_function_value * r = mk_rational_function_value_core(a->ext(), num_sz, num, den_sz, den); @@ -2142,6 +2222,14 @@ namespace realclosure { } }; + // Helper object for restoring the value intervals. + class save_interval_ctx { + manager::imp * m; + public: + save_interval_ctx(manager const * _this):m(_this->m_imp) { SASSERT (m); } + ~save_interval_ctx() { m->restore_saved_intervals(); } + }; + manager::manager(unsynch_mpq_manager & m, params_ref const & p, small_object_allocator * a) { m_imp = alloc(imp, m, p, a); } @@ -2195,6 +2283,7 @@ namespace realclosure { } void manager::isolate_roots(unsigned n, numeral const * as, numeral_vector & roots) { + save_interval_ctx ctx(this); m_imp->isolate_roots(n, as, roots); } @@ -2203,6 +2292,7 @@ namespace realclosure { } int manager::sign(numeral const & a) { + save_interval_ctx ctx(this); return m_imp->sign(a); } @@ -2247,14 +2337,17 @@ namespace realclosure { } void manager::root(numeral const & a, unsigned k, numeral & b) { + save_interval_ctx ctx(this); m_imp->root(a, k, b); } void manager::power(numeral const & a, unsigned k, numeral & b) { + save_interval_ctx ctx(this); m_imp->power(a, k, b); } void manager::add(numeral const & a, numeral const & b, numeral & c) { + save_interval_ctx ctx(this); m_imp->add(a, b, c); } @@ -2265,26 +2358,32 @@ namespace realclosure { } void manager::sub(numeral const & a, numeral const & b, numeral & c) { + save_interval_ctx ctx(this); m_imp->sub(a, b, c); } void manager::mul(numeral const & a, numeral const & b, numeral & c) { + save_interval_ctx ctx(this); m_imp->mul(a, b, c); } void manager::neg(numeral & a) { + save_interval_ctx ctx(this); m_imp->neg(a); } void manager::inv(numeral & a) { + save_interval_ctx ctx(this); m_imp->inv(a); } void manager::div(numeral const & a, numeral const & b, numeral & c) { + save_interval_ctx ctx(this); m_imp->div(a, b, c); } int manager::compare(numeral const & a, numeral const & b) { + save_interval_ctx ctx(this); return m_imp->compare(a, b); } @@ -2333,18 +2432,22 @@ namespace realclosure { } void manager::select(numeral const & prev, numeral const & next, numeral & result) { + save_interval_ctx ctx(this); m_imp->select(prev, next, result); } void manager::display(std::ostream & out, numeral const & a) const { + save_interval_ctx ctx(this); m_imp->display(out, a); } void manager::display_decimal(std::ostream & out, numeral const & a, unsigned precision) const { + save_interval_ctx ctx(this); m_imp->display_decimal(out, a, precision); } void manager::display_interval(std::ostream & out, numeral const & a) const { + save_interval_ctx ctx(this); m_imp->display_interval(out, a); } diff --git a/src/math/realclosure/realclosure.h b/src/math/realclosure/realclosure.h index 151323886..72e0da2aa 100644 --- a/src/math/realclosure/realclosure.h +++ b/src/math/realclosure/realclosure.h @@ -44,6 +44,7 @@ namespace realclosure { public: struct imp; private: + friend class save_interval_ctx; imp * m_imp; public: manager(unsynch_mpq_manager & m, params_ref const & p = params_ref(), small_object_allocator * a = 0); From f1d47f35b203ba009fa859904abfc4c978acc049 Mon Sep 17 00:00:00 2001 From: Leonardo de Moura Date: Sun, 6 Jan 2013 18:30:41 -0800 Subject: [PATCH 26/78] Add refine interval infrastructure Signed-off-by: Leonardo de Moura --- src/math/interval/interval.h | 3 + src/math/realclosure/realclosure.cpp | 584 +++++++++++++++++++++++++-- src/test/rcf.cpp | 8 +- 3 files changed, 563 insertions(+), 32 deletions(-) diff --git a/src/math/interval/interval.h b/src/math/interval/interval.h index ad0a6fa45..32c76c87f 100644 --- a/src/math/interval/interval.h +++ b/src/math/interval/interval.h @@ -371,6 +371,9 @@ public: interval * operator->() { return &m_interval; } + interval const * operator->() const { + return &m_interval; + } }; #endif diff --git a/src/math/realclosure/realclosure.cpp b/src/math/realclosure/realclosure.cpp index 11b05b6d3..856a87ec8 100644 --- a/src/math/realclosure/realclosure.cpp +++ b/src/math/realclosure/realclosure.cpp @@ -309,7 +309,8 @@ namespace realclosure { typedef ref_vector value_ref_vector; typedef ref_buffer value_ref_buffer; typedef obj_ref value_ref; - typedef _scoped_interval scoped_mpqi; + typedef _scoped_interval scoped_mpqi; + typedef _scoped_interval scoped_mpbqi; small_object_allocator * m_allocator; bool m_own_allocator; @@ -328,6 +329,9 @@ namespace realclosure { // Parameters unsigned m_ini_precision; //!< initial precision for transcendentals, infinitesimals, etc. int m_min_magnitude; + unsigned m_inf_precision; //!< 2^m_inf_precision is used as the lower bound of oo and -2^m_inf_precision is used as the upper_bound of -oo + scoped_mpbq m_plus_inf_approx; // lower bound for binary rational intervals used to approximate an infinite positive value + scoped_mpbq m_minus_inf_approx; // upper bound for binary rational intervals used to approximate an infinite negative value volatile bool m_cancel; @@ -379,7 +383,9 @@ namespace realclosure { m_qm(qm), m_bqm(m_qm), m_qim(m_qm), - m_bqim(m_bqm) { + m_bqim(m_bqm), + m_plus_inf_approx(m_bqm), + m_minus_inf_approx(m_bqm) { mpq one(1); m_one = mk_rational(one); inc_ref(m_one); @@ -400,7 +406,7 @@ namespace realclosure { } unsynch_mpq_manager & qm() const { return m_qm; } - mpbq_manager & bqm() { return m_bqm; } + mpbq_config::numeral_manager & bqm() { return m_bqm; } mpqi_manager & qim() { return m_qim; } mpbqi_manager & bqim() { return m_bqim; } mpbqi_manager const & bqim() const { return m_bqim; } @@ -421,23 +427,43 @@ namespace realclosure { The magnitude is an approximation of the size of the interval. */ int magnitude(mpbq const & l, mpbq const & u) { - SASSERT(bqm().is_nonneg(l) || bqm().is_nonpos(u)); - int l_k = l.k(); - int u_k = u.k(); - if (l_k == u_k) - return bqm().magnitude_ub(l); - if (bqm().is_nonneg(l)) - return qm().log2(u.numerator()) - qm().log2(l.numerator()) - u_k + l_k - u_k; - else - return qm().mlog2(u.numerator()) - qm().mlog2(l.numerator()) - u_k + l_k - u_k; + SASSERT(bqm().ge(u, l)); + scoped_mpbq w(bqm()); + bqm().sub(u, l, w); + if (bqm().is_zero(w)) + return INT_MIN; + SASSERT(bqm().is_pos(w)); + return bqm().magnitude_ub(w); + } + + /** + \brief Return the magnitude of the given interval. + The magnitude is an approximation of the size of the interval. + */ + int magnitude(mpbqi const & i) { + if (i.lower_is_inf() || i.upper_is_inf()) + return INT_MAX; + else + return magnitude(i.lower(), i.upper()); } /** \brief Return the magnitude of the given interval. The magnitude is an approximation of the size of the interval. */ - int magnitude(mpbqi const & i) { - return magnitude(i.lower(), i.upper()); + int magnitude(mpq const & l, mpq const & u) { + SASSERT(qm().ge(u, l)); + scoped_mpq w(qm()); + qm().sub(u, l, w); + if (qm().is_zero(w)) + return INT_MIN; + SASSERT(qm().is_pos(w)); + return static_cast(qm().log2(w.get().numerator())) + 1 - static_cast(qm().log2(w.get().denominator())); + } + + int magnitude(scoped_mpqi const & i) { + SASSERT(!i->m_lower_inf && !i->m_upper_inf); + return magnitude(i->m_lower, i->m_upper); } /** @@ -447,6 +473,34 @@ namespace realclosure { return magnitude(i) < m_min_magnitude; } +#define SMALL_UNSIGNED 1 << 16 + static unsigned inc_precision(unsigned prec, unsigned inc) { + if (prec < SMALL_UNSIGNED) + return prec + inc; + else + return prec; + } + + struct scoped_set_div_precision { + mpbq_config::numeral_manager & m_bqm; + unsigned m_old_precision; + scoped_set_div_precision(mpbq_config::numeral_manager & bqm, unsigned prec):m_bqm(bqm) { + m_old_precision = m_bqm.m_div_precision; + m_bqm.m_div_precision = prec; + } + ~scoped_set_div_precision() { + m_bqm.m_div_precision = m_old_precision; + } + }; + + /** + \brief c <- a/b with precision prec. + */ + void div(mpbqi const & a, mpbqi const & b, unsigned prec, mpbqi & c) { + scoped_set_div_precision set(bqm(), prec); + bqim().div(a, b, c); + } + /** \brief Save the current interval (i.e., approximation) of the given value. */ @@ -507,8 +561,11 @@ namespace realclosure { void updt_params(params_ref const & p) { m_ini_precision = p.get_uint("initial_precision", 24); + m_inf_precision = p.get_uint("inf_precision", 24); m_min_magnitude = -static_cast(p.get_uint("min_mag", 64)); - // TODO + bqm().power(mpbq(2), m_inf_precision, m_plus_inf_approx); + bqm().set(m_minus_inf_approx, m_plus_inf_approx); + bqm().neg(m_minus_inf_approx); } /** @@ -831,6 +888,20 @@ namespace realclosure { inc_ref(n, as); } + /** + \brief Return true if a is an open interval. + */ + static bool is_open_interval(mpbqi const & a) { + return a.lower_is_inf() && a.upper_is_inf(); + } + + /** + \brief Return true if the interval contains zero. + */ + bool contains_zero(mpbqi const & a) const { + return bqim().contains_zero(a); + } + /** \brief Set the lower bound of the given interval. */ @@ -847,6 +918,15 @@ namespace realclosure { set_lower_core(a, k, open, false); } + /** + \brief a.lower <- -oo + */ + void set_lower_inf(mpbqi & a) { + bqm().reset(a.lower()); + a.set_lower_is_open(true); + a.set_lower_is_inf(true); + } + /** \brief Set the upper bound of the given interval. */ @@ -863,6 +943,15 @@ namespace realclosure { set_upper_core(a, k, open, false); } + /** + \brief a.upper <- oo + */ + void set_upper_inf(mpbqi & a) { + bqm().reset(a.upper()); + a.set_upper_is_open(true); + a.set_upper_is_inf(true); + } + /** \brief a <- b */ @@ -920,11 +1009,19 @@ namespace realclosure { mk_infinitesimal(symbol(next_infinitesimal_idx()), r); } - void refine_transcedental_interval(transcendental * t) { + void refine_transcendental_interval(transcendental * t) { scoped_mpqi i(qim()); t->m_k++; t->m_proc(t->m_k, qim(), i); - unsigned k = std::max(t->m_k, m_ini_precision); + int m = magnitude(i); + TRACE("rcf", + tout << "refine_transcendental_interval rational: " << m << "\nrational interval: "; + qim().display(tout, i); tout << std::endl;); + unsigned k; + if (m >= 0) + k = m_ini_precision; + else + k = inc_precision(-m, 8); scoped_mpbq l(bqm()); mpq_to_mpbqi(i->m_lower, t->interval(), k); // save lower @@ -933,14 +1030,22 @@ namespace realclosure { bqm().set(t->interval().lower(), l); } + void refine_transcendental_interval(transcendental * t, unsigned prec) { + while (!check_precision(t->interval(), prec)) { + TRACE("rcf", tout << "refine_transcendental_interval: " << magnitude(t->interval()) << std::endl;); + checkpoint(); + refine_transcendental_interval(t); + } + } + void mk_transcendental(symbol const & n, mk_interval & proc, numeral & r) { unsigned idx = next_transcendental_idx(); transcendental * t = alloc(transcendental, idx, n, proc); m_extensions[extension::TRANSCENDENTAL].push_back(t); - while (bqim().contains_zero(t->interval())) { + while (contains_zero(t->interval())) { checkpoint(); - refine_transcedental_interval(t); + refine_transcendental_interval(t); } set(r, mk_rational_function_value(t)); @@ -993,7 +1098,7 @@ namespace realclosure { return qm().is_pos(to_mpq(a)) ? 1 : -1; } else { - SASSERT(!bqim().contains_zero(a->interval())); + SASSERT(!contains_zero(a->interval())); return bqim().is_P(a->interval()) ? 1 : -1; } } @@ -1040,7 +1145,7 @@ namespace realclosure { if (qm().is_neg(q)) { ::swap(interval.lower(), interval.upper()); } - while (bqim().contains_zero(interval) || !check_precision(interval, k)) { + while (contains_zero(interval) || !check_precision(interval, k) || bqm().is_zero(interval.lower()) || bqm().is_zero(interval.upper())) { checkpoint(); bqm().refine_lower(q, interval.lower(), interval.upper()); bqm().refine_upper(q, interval.lower(), interval.upper()); @@ -1054,9 +1159,9 @@ namespace realclosure { mpq_to_mpbqi(to_mpq(a), a->m_interval, m_ini_precision); } - mpbqi const & interval(value * a) const { + mpbqi & interval(value * a) const { SASSERT(a != 0); - if (bqim().contains_zero(a->m_interval)) { + if (contains_zero(a->m_interval)) { SASSERT(is_nz_rational(a)); const_cast(this)->initialize_rational_value_interval(a); } @@ -1131,7 +1236,25 @@ namespace realclosure { } void root(numeral const & a, unsigned k, numeral & b) { - // TODO + if (k == 0) + throw exception("0-th root is indeterminate"); + + if (k == 1 || is_zero(a)) { + set(b, a); + return; + } + + if (sign(a) < 0 && k % 2 == 0) + throw exception("even root of negative number"); + + // create the polynomial p of the form x^k - a + value_ref_buffer p(*this); + p.push_back(neg(a.m_value)); + for (unsigned i = 0; i < k - 1; i++) + p.push_back(0); + p.push_back(one()); + + // TODO: invoke isolate_roots } void power(numeral const & a, unsigned k, numeral & b) { @@ -1530,6 +1653,379 @@ namespace realclosure { sturm_seq_core(seq); } + void refine_rational_interval(rational_value * v, unsigned prec) { + mpbqi & i = interval(v); + if (!i.lower_is_open() && !i.lower_is_open()) { + SASSERT(bqm().eq(i.lower(), i.upper())); + return; + } + while (!check_precision(i, prec)) { + checkpoint(); + bqm().refine_lower(to_mpq(v), i.lower(), i.upper()); + bqm().refine_upper(to_mpq(v), i.lower(), i.upper()); + } + } + + /** + \brief Refine the interval for each coefficient of in the polynomial p. + */ + bool refine_coeffs_interval(polynomial const & p, unsigned prec) { + unsigned sz = p.size(); + for (unsigned i = 0; i < sz; i++) { + if (p[i] != 0 && !refine_interval(p[i], prec)) + return false; + } + return true; + } + + /** + \brief Store in r the interval p(v). + */ + void polynomial_interval(polynomial const & p, mpbqi const & v, mpbqi & r) { + // We compute r using the Horner Sequence + // ((a_n * v + a_{n-1})*v + a_{n-2})*v + a_{n-3} ... + // where a_i's are the coefficients of p. + unsigned sz = p.size(); + if (sz == 1) { + bqim().set(r, interval(p[0])); + } + else { + SASSERT(sz > 0); + SASSERT(p[sz - 1] != 0); + // r <- a_n * v + bqim().mul(interval(p[sz-1]), v, r); + unsigned i = sz - 1; + while (i > 0) { + --i; + if (p[i] != 0) + bqim().add(r, interval(p[i]), r); + if (i > 0) + bqim().mul(r, v, r); + } + } + } + + /** + \brief Update the interval of v by using the intervals of + extension and coefficients of the rational function. + */ + void update_rf_interval(rational_function_value * v, unsigned prec) { + if (is_rational_one(v->den())) { + polynomial_interval(v->num(), v->ext()->interval(), v->interval()); + } + else { + scoped_mpbqi num_i(bqim()), den_i(bqim()); + polynomial_interval(v->num(), v->ext()->interval(), num_i); + polynomial_interval(v->den(), v->ext()->interval(), den_i); + div(num_i, den_i, inc_precision(prec, 2), v->interval()); + } + } + + void refine_transcendental_interval(rational_function_value * v, unsigned prec) { + SASSERT(v->ext()->is_transcendental()); + polynomial const & n = v->num(); + polynomial const & d = v->den(); + unsigned _prec = prec; + while (true) { + VERIFY(refine_coeffs_interval(n, _prec)); // must return true because a transcendental never depends on an infinitesimal + VERIFY(refine_coeffs_interval(d, _prec)); // must return true because a transcendental never depends on an infinitesimal + refine_transcendental_interval(to_transcendental(v->ext()), _prec); + update_rf_interval(v, prec); + + TRACE("rcf", tout << "after update_rf_interval: " << magnitude(v->interval()) << " "; + bqim().display(tout, v->interval()); tout << std::endl;); + + if (check_precision(v->interval(), prec)) + return; + _prec++; + } + } + + bool refine_infinitesimal_interval(rational_function_value * v, unsigned prec) { + SASSERT(v->ext()->is_infinitesimal()); + polynomial const & numerator = v->num(); + polynomial const & denominator = v->den(); + unsigned num_idx = first_non_zero(numerator); + unsigned den_idx = first_non_zero(denominator); + if (num_idx == 0 && den_idx == 0) { + unsigned _prec = prec; + while (true) { + refine_interval(numerator[num_idx], _prec); + refine_interval(denominator[num_idx], _prec); + mpbqi const & num_i = interval(numerator[num_idx]); + mpbqi const & den_i = interval(denominator[num_idx]); + SASSERT(!contains_zero(num_i)); + SASSERT(!contains_zero(den_i)); + if (is_open_interval(num_i) && is_open_interval(den_i)) { + // This case is simple because adding/subtracting infinitesimal quantities, will + // not change the interval. + div(num_i, den_i, inc_precision(prec, 2), v->interval()); + } + else { + // The intervals num_i and den_i may not be open. + // Example: numerator[num_idx] or denominator[num_idx] are rationals + // that can be precisely represented as binary rationals. + scoped_mpbqi new_num_i(bqim()); + scoped_mpbqi new_den_i(bqim()); + mpbq tiny_value(1, _prec*2); + if (numerator.size() > 1) + add_infinitesimal(num_i, sign_of_first_non_zero(numerator, 1) > 0, tiny_value, new_num_i); + else + bqim().set(new_num_i, num_i); + if (denominator.size() > 1) + add_infinitesimal(den_i, sign_of_first_non_zero(denominator, 1) > 0, tiny_value, new_den_i); + else + bqim().set(new_den_i, den_i); + div(new_num_i, new_den_i, inc_precision(prec, 2), v->interval()); + } + if (check_precision(v->interval(), prec)) + return true; + _prec++; + } + } + else { + // The following condition must hold because gcd(numerator, denominator) == 1 + // If num_idx > 0 and den_idx > 0, eps^{min(num_idx, den_idx)} is a factor of gcd(numerator, denominator) + SASSERT(num_idx == 0 || den_idx == 0); + int s = sign(numerator[num_idx]) * sign(denominator[den_idx]); + // The following must hold since numerator[num_idx] and denominator[den_idx] are not zero. + SASSERT(s != 0); + if (num_idx == 0) { + SASSERT(den_idx > 0); + // |v| is bigger than any binary rational + // Interval can't be refined. There is no way to isolate an infinity with an interval with binary rational end points. + return false; + } + else { + SASSERT(num_idx > 0); + SASSERT(den_idx == 0); + // |v| is infinitely close to zero. + if (s == 1) { + // it is close from above + set_lower(v->interval(), mpbq(0)); + set_upper(v->interval(), mpbq(1, prec)); + } + else { + // it is close from below + set_lower(v->interval(), mpbq(-1, prec)); + set_upper(v->interval(), mpbq(0)); + } + return true; + } + } + } + + bool refine_algebraic_interval(rational_function_value * v, unsigned prec) { + // TODO + return false; + } + + /** + \brief Refine the interval of v to the desired precision (1/2^k). + Return false in case of failure. A failure can only happen if v depends on infinitesimal values. + */ + bool refine_interval(value * v, unsigned prec) { + checkpoint(); + SASSERT(!is_zero(v)); + int m = magnitude(interval(v)); + if (m == INT_MIN || (m < 0 && static_cast(-m) > prec)) + return true; + save_interval_if_too_small(v); + if (is_nz_rational(v)) { + refine_rational_interval(to_nz_rational(v), prec); + return true; + } + else { + rational_function_value * rf = to_rational_function(v); + if (rf->ext()->is_transcendental()) { + refine_transcendental_interval(rf, prec); + return true; + } + else if (rf->ext()->is_infinitesimal()) + return refine_infinitesimal_interval(rf, prec); + else + return refine_algebraic_interval(rf, prec); + } + } + + /** + \brief Return the position of the first non-zero coefficient of p. + */ + static unsigned first_non_zero(polynomial const & p) { + unsigned sz = p.size(); + for (unsigned i = 0; i < sz; i++) { + if (p[i] != 0) + return i; + } + UNREACHABLE(); + return UINT_MAX; + } + + /** + \brief Return the sign of the first non zero coefficient starting at position start_idx + */ + int sign_of_first_non_zero(polynomial const & p, unsigned start_idx) { + unsigned sz = p.size(); + SASSERT(start_idx < sz); + for (unsigned i = start_idx; i < sz; i++) { + if (p[i] != 0) + return sign(p[i]); + } + UNREACHABLE(); + return 0; + } + + /** + out <- in + infinitesimal (if plus_eps == true) + out <- in - infinitesimal (if plus_eps == false) + + We use the following rules for performing the assignment + + If plus_eps == True + If lower(in) == v (closed or open), then lower(out) == v and open + If upper(in) == v and open, then upper(out) == v and open + If upper(in) == v and closed, then upper(out) == new_v and open + where new_v is v + tiny_value / 2^k, where k is the smallest natural such that sign(new_v) == sign(v) + If plus_eps == False + If lower(in) == v and open, then lower(out) == v and open + If lower(in) == v and closed, then lower(out) == new_v and open + If upper(in) == v (closed or open), then upper(out) == v and open + where new_v is v - tiny_value / 2^k, where k is the smallest natural such that sign(new_v) == sign(v) + */ + void add_infinitesimal(mpbqi const & in, bool plus_eps, mpbq const & tiny_value, mpbqi & out) { + set_interval(out, in); + out.set_lower_is_open(true); + out.set_upper_is_open(true); + if (plus_eps) { + if (!in.upper_is_open()) { + scoped_mpbq tval(bqm()); + tval = tiny_value; + while (true) { + bqm().add(in.upper(), tval, out.upper()); + if (bqm().is_pos(in.upper()) == bqm().is_pos(out.upper())) + return; + bqm().div2(tval); + checkpoint(); + } + } + } + else { + if (!in.lower_is_open()) { + scoped_mpbq tval(bqm()); + tval = tiny_value; + while (true) { + bqm().sub(in.lower(), tval, out.lower()); + if (bqm().is_pos(in.lower()) == bqm().is_pos(out.lower())) + return; + bqm().div2(tval); + checkpoint(); + } + } + } + } + + /** + \brief Determine the sign of an element of Q(trans_0, ..., trans_n) + */ + void determine_transcendental_sign(rational_function_value * v) { + // Remark: the sign of a rational function value on an transcendental is never zero. + // Reason: The transcendental can be the root of a polynomial. + SASSERT(v->ext()->is_transcendental()); + int m = magnitude(v->interval()); + unsigned prec = 1; + if (m < 0) + prec = static_cast(-m) + 1; + while (contains_zero(v->interval())) { + refine_transcendental_interval(v, prec); + prec++; + } + } + + /** + \brief Determine the sign of an element of Q(trans_0, ..., trans_n, eps_0, ..., eps_m) + */ + void determine_infinitesimal_sign(rational_function_value * v) { + // Remark: the sign of a rational function value on an infinitesimal is never zero. + // Reason: An infinitesimal eps is transcendental in any field K. So, it can't be the root + // of a polynomial. + SASSERT(v->ext()->is_infinitesimal()); + polynomial const & numerator = v->num(); + polynomial const & denominator = v->den(); + unsigned num_idx = first_non_zero(numerator); + unsigned den_idx = first_non_zero(denominator); + if (num_idx == 0 && den_idx == 0) { + mpbqi const & num_i = interval(numerator[num_idx]); + mpbqi const & den_i = interval(denominator[num_idx]); + SASSERT(!contains_zero(num_i)); + SASSERT(!contains_zero(den_i)); + if (is_open_interval(num_i) && is_open_interval(den_i)) { + // This case is simple because adding/subtracting infinitesimal quantities, will + // not change the interval. + div(num_i, den_i, m_ini_precision, v->interval()); + } + else { + // The intervals num_i and den_i may not be open. + // Example: numerator[num_idx] or denominator[num_idx] are rationals + // that can be precisely represented as binary rationals. + scoped_mpbqi new_num_i(bqim()); + scoped_mpbqi new_den_i(bqim()); + mpbq tiny_value(1, m_ini_precision); // 1/2^{m_ini_precision} + if (numerator.size() > 1) + add_infinitesimal(num_i, sign_of_first_non_zero(numerator, 1) > 0, tiny_value, new_num_i); + else + bqim().set(new_num_i, num_i); + if (denominator.size() > 1) + add_infinitesimal(den_i, sign_of_first_non_zero(denominator, 1) > 0, tiny_value, new_den_i); + else + bqim().set(new_den_i, den_i); + div(new_num_i, new_den_i, m_ini_precision, v->interval()); + } + } + else { + // The following condition must hold because gcd(numerator, denominator) == 1 + // If num_idx > 0 and den_idx > 0, eps^{min(num_idx, den_idx)} is a factor of gcd(numerator, denominator) + SASSERT(num_idx == 0 || den_idx == 0); + int s = sign(numerator[num_idx]) * sign(denominator[den_idx]); + // The following must hold since numerator[num_idx] and denominator[den_idx] are not zero. + SASSERT(s != 0); + if (num_idx == 0) { + SASSERT(den_idx > 0); + // |v| is bigger than any binary rational + if (s == 1) { + // it is "oo" + set_lower(v->interval(), m_plus_inf_approx); + set_upper_inf(v->interval()); + } + else { + // it is "-oo" + set_lower_inf(v->interval()); + set_upper(v->interval(), m_minus_inf_approx); + } + } + else { + SASSERT(num_idx > 0); + SASSERT(den_idx == 0); + // |v| is infinitely close to zero. + if (s == 1) { + // it is close from above + set_lower(v->interval(), mpbq(0)); + set_upper(v->interval(), mpbq(1, m_ini_precision)); + } + else { + // it is close from below + set_lower(v->interval(), mpbq(-1, m_ini_precision)); + set_upper(v->interval(), mpbq(0)); + } + } + } + SASSERT(!contains_zero(v->interval())); + } + + bool determine_algebraic_sign(rational_function_value * v) { + // TODO + return false; + } + /** \brief Determine the sign of the new rational function value. The idea is to keep refinining the interval until interval of v does not contain 0. @@ -1538,8 +2034,16 @@ namespace realclosure { Return false if v is actually zero. */ bool determine_sign(rational_function_value * v) { - // TODO - return true; + if (!contains_zero(v->interval())) + return true; + switch (v->ext()->knd()) { + case extension::TRANSCENDENTAL: determine_transcendental_sign(v); return true; // it is never zero + case extension::INFINITESIMAL: determine_infinitesimal_sign(v); return true; // it is never zero + case extension::ALGEBRAIC: return determine_algebraic_sign(v); + default: + UNREACHABLE(); + return false; + } } /** @@ -1932,7 +2436,7 @@ namespace realclosure { polynomial const & ad = a->den(); rational_function_value * r = mk_rational_function_value_core(a->ext(), ad.size(), ad.c_ptr(), an.size(), an.c_ptr()); bqim().inv(interval(a), r->interval()); - SASSERT(!bqim().contains_zero(r->interval())); + SASSERT(!contains_zero(r->interval())); return r; } @@ -2190,7 +2694,7 @@ namespace realclosure { SASSERT(!is_zero(a)); SASSERT(!is_nz_rational(a)); mpbqi const & i = interval(a.m_value); - if (check_precision(i, precision*4)) { + if (refine_interval(a.m_value, precision*4)) { // hack if (bqm().is_int(i.lower())) bqm().display_decimal(out, i.upper(), precision); @@ -2198,7 +2702,10 @@ namespace realclosure { bqm().display_decimal(out, i.lower(), precision); } else { - out << "?"; + if (sign(a.m_value) > 0) + out << "?"; + else + out << "-?"; } } @@ -2473,3 +2980,22 @@ void pp(realclosure::manager::imp * imp, realclosure::manager::imp::value_ref_bu pp(imp, p[i]); } +void pp(realclosure::manager::imp * imp, realclosure::mpbqi const & i) { + imp->bqim().display(std::cout, i); + std::cout << std::endl; +} + +void pp(realclosure::manager::imp * imp, realclosure::manager::imp::scoped_mpqi const & i) { + imp->qim().display(std::cout, i); + std::cout << std::endl; +} + +void pp(realclosure::manager::imp * imp, mpbq const & n) { + imp->bqm().display(std::cout, n); + std::cout << std::endl; +} + +void pp(realclosure::manager::imp * imp, mpq const & n) { + imp->qm().display(std::cout, n); + std::cout << std::endl; +} diff --git a/src/test/rcf.cpp b/src/test/rcf.cpp index 7b2445320..b50ebc56c 100644 --- a/src/test/rcf.cpp +++ b/src/test/rcf.cpp @@ -55,13 +55,15 @@ static void tst1() { scoped_rcnumeral pi(m); m.mk_pi(pi); std::cout << pi + 1 << std::endl; - std::cout << decimal_pp(pi + 1, 1) << std::endl; + std::cout << decimal_pp(pi) << std::endl; + std::cout << decimal_pp(pi + 1) << std::endl; scoped_rcnumeral e(m); m.mk_e(e); t = e + (pi + 1)*2; std::cout << t << std::endl; - std::cout << decimal_pp(t, 1) << std::endl; - + std::cout << decimal_pp(t, 10) << std::endl; + std::cout << (eps + 1 > 1) << std::endl; + std::cout << interval_pp((a + eps)/(a - eps)) << std::endl; } void tst_rcf() { From 9fbbdb56e4bb185e39885dec31883f820c46e485 Mon Sep 17 00:00:00 2001 From: Leonardo de Moura Date: Sun, 6 Jan 2013 20:06:27 -0800 Subject: [PATCH 27/78] Implement RCF external C API Signed-off-by: Leonardo de Moura --- src/api/api_context.cpp | 2 + src/api/api_rcf.cpp | 134 ++++++++++++++++++++------- src/api/api_util.h | 1 + src/api/z3_rcf.h | 21 +++++ src/math/realclosure/realclosure.cpp | 18 ++++ src/math/realclosure/realclosure.h | 10 ++ 6 files changed, 155 insertions(+), 31 deletions(-) diff --git a/src/api/api_context.cpp b/src/api/api_context.cpp index 6b3fba6ae..441190ba5 100644 --- a/src/api/api_context.cpp +++ b/src/api/api_context.cpp @@ -139,6 +139,8 @@ namespace api { if (m_interruptable) (*m_interruptable)(); m().set_cancel(true); + if (m_rcf_manager.get() == 0) + m_rcf_manager->set_cancel(true); } } diff --git a/src/api/api_rcf.cpp b/src/api/api_rcf.cpp index 681cb814e..4ba440266 100644 --- a/src/api/api_rcf.cpp +++ b/src/api/api_rcf.cpp @@ -39,16 +39,38 @@ extern "C" { Z3_TRY; LOG_Z3_rcf_dec_ref(c, a); RESET_ERROR_CODE(); + if (to_rcf_num(a)->ref_count() == 1) { + mk_c(c)->rcfm().del(to_rcnumeral(a)); + } to_rcf_num(a)->dec_ref(); Z3_CATCH; } + static rcmanager & rcfm(Z3_context c) { + return mk_c(c)->rcfm(); + } + + static void reset_rcf_cancel(Z3_context c) { + rcfm(c).reset_cancel(); + } + + static Z3_rcf_num mk_rcf_num(Z3_context c, rcnumeral & a) { + Z3_rcf_num_ref * r = alloc(Z3_rcf_num_ref); + rcfm(c).swap(r->m_num, a); + mk_c(c)->save_object(r); + return of_rcf_num(r); + } + Z3_rcf_num Z3_API Z3_rcf_mk_rational(Z3_context c, Z3_string val) { Z3_TRY; LOG_Z3_rcf_mk_rational(c, val); RESET_ERROR_CODE(); - // TODO - RETURN_Z3(0); + reset_rcf_cancel(c); + scoped_mpq q(rcfm(c).qm()); + rcfm(c).qm().set(q, val); + scoped_rcnumeral r(rcfm(c)); + rcfm(c).set(r, q); + RETURN_Z3(mk_rcf_num(c, r)); Z3_CATCH_RETURN(0); } @@ -56,8 +78,10 @@ extern "C" { Z3_TRY; LOG_Z3_rcf_mk_small_int(c, val); RESET_ERROR_CODE(); - // TODO - RETURN_Z3(0); + reset_rcf_cancel(c); + scoped_rcnumeral r(rcfm(c)); + rcfm(c).set(r, val); + RETURN_Z3(mk_rcf_num(c, r)); Z3_CATCH_RETURN(0); } @@ -65,8 +89,10 @@ extern "C" { Z3_TRY; LOG_Z3_rcf_mk_pi(c); RESET_ERROR_CODE(); - // TODO - RETURN_Z3(0); + reset_rcf_cancel(c); + scoped_rcnumeral r(rcfm(c)); + rcfm(c).mk_pi(r); + RETURN_Z3(mk_rcf_num(c, r)); Z3_CATCH_RETURN(0); } @@ -74,8 +100,10 @@ extern "C" { Z3_TRY; LOG_Z3_rcf_mk_e(c); RESET_ERROR_CODE(); - // TODO - RETURN_Z3(0); + reset_rcf_cancel(c); + scoped_rcnumeral r(rcfm(c)); + rcfm(c).mk_e(r); + RETURN_Z3(mk_rcf_num(c, r)); Z3_CATCH_RETURN(0); } @@ -83,8 +111,10 @@ extern "C" { Z3_TRY; LOG_Z3_rcf_mk_infinitesimal(c, name); RESET_ERROR_CODE(); - // TODO - RETURN_Z3(0); + reset_rcf_cancel(c); + scoped_rcnumeral r(rcfm(c)); + rcfm(c).mk_infinitesimal(name, r); + RETURN_Z3(mk_rcf_num(c, r)); Z3_CATCH_RETURN(0); } @@ -92,8 +122,10 @@ extern "C" { Z3_TRY; LOG_Z3_rcf_add(c, a, b); RESET_ERROR_CODE(); - // TODO - RETURN_Z3(0); + reset_rcf_cancel(c); + scoped_rcnumeral r(rcfm(c)); + rcfm(c).add(to_rcnumeral(a), to_rcnumeral(b), r); + RETURN_Z3(mk_rcf_num(c, r)); Z3_CATCH_RETURN(0); } @@ -101,8 +133,10 @@ extern "C" { Z3_TRY; LOG_Z3_rcf_sub(c, a, b); RESET_ERROR_CODE(); - // TODO - RETURN_Z3(0); + reset_rcf_cancel(c); + scoped_rcnumeral r(rcfm(c)); + rcfm(c).sub(to_rcnumeral(a), to_rcnumeral(b), r); + RETURN_Z3(mk_rcf_num(c, r)); Z3_CATCH_RETURN(0); } @@ -110,8 +144,10 @@ extern "C" { Z3_TRY; LOG_Z3_rcf_mul(c, a, b); RESET_ERROR_CODE(); - // TODO - RETURN_Z3(0); + reset_rcf_cancel(c); + scoped_rcnumeral r(rcfm(c)); + rcfm(c).mul(to_rcnumeral(a), to_rcnumeral(b), r); + RETURN_Z3(mk_rcf_num(c, r)); Z3_CATCH_RETURN(0); } @@ -119,8 +155,10 @@ extern "C" { Z3_TRY; LOG_Z3_rcf_div(c, a, b); RESET_ERROR_CODE(); - // TODO - RETURN_Z3(0); + reset_rcf_cancel(c); + scoped_rcnumeral r(rcfm(c)); + rcfm(c).div(to_rcnumeral(a), to_rcnumeral(b), r); + RETURN_Z3(mk_rcf_num(c, r)); Z3_CATCH_RETURN(0); } @@ -128,8 +166,10 @@ extern "C" { Z3_TRY; LOG_Z3_rcf_neg(c, a); RESET_ERROR_CODE(); - // TODO - RETURN_Z3(0); + reset_rcf_cancel(c); + scoped_rcnumeral r(rcfm(c)); + rcfm(c).neg(to_rcnumeral(a), r); + RETURN_Z3(mk_rcf_num(c, r)); Z3_CATCH_RETURN(0); } @@ -137,8 +177,10 @@ extern "C" { Z3_TRY; LOG_Z3_rcf_inv(c, a); RESET_ERROR_CODE(); - // TODO - RETURN_Z3(0); + reset_rcf_cancel(c); + scoped_rcnumeral r(rcfm(c)); + rcfm(c).inv(to_rcnumeral(a), r); + RETURN_Z3(mk_rcf_num(c, r)); Z3_CATCH_RETURN(0); } @@ -146,8 +188,8 @@ extern "C" { Z3_TRY; LOG_Z3_rcf_lt(c, a, b); RESET_ERROR_CODE(); - // TODO - return Z3_FALSE; + reset_rcf_cancel(c); + return rcfm(c).lt(to_rcnumeral(a), to_rcnumeral(b)); Z3_CATCH_RETURN(Z3_FALSE); } @@ -155,8 +197,8 @@ extern "C" { Z3_TRY; LOG_Z3_rcf_gt(c, a, b); RESET_ERROR_CODE(); - // TODO - return Z3_FALSE; + reset_rcf_cancel(c); + return rcfm(c).gt(to_rcnumeral(a), to_rcnumeral(b)); Z3_CATCH_RETURN(Z3_FALSE); } @@ -164,8 +206,8 @@ extern "C" { Z3_TRY; LOG_Z3_rcf_le(c, a, b); RESET_ERROR_CODE(); - // TODO - return Z3_FALSE; + reset_rcf_cancel(c); + return rcfm(c).le(to_rcnumeral(a), to_rcnumeral(b)); Z3_CATCH_RETURN(Z3_FALSE); } @@ -173,8 +215,26 @@ extern "C" { Z3_TRY; LOG_Z3_rcf_ge(c, a, b); RESET_ERROR_CODE(); - // TODO - return Z3_FALSE; + reset_rcf_cancel(c); + return rcfm(c).ge(to_rcnumeral(a), to_rcnumeral(b)); + Z3_CATCH_RETURN(Z3_FALSE); + } + + Z3_bool Z3_API Z3_rcf_eq(Z3_context c, Z3_rcf_num a, Z3_rcf_num b) { + Z3_TRY; + LOG_Z3_rcf_eq(c, a, b); + RESET_ERROR_CODE(); + reset_rcf_cancel(c); + return rcfm(c).eq(to_rcnumeral(a), to_rcnumeral(b)); + Z3_CATCH_RETURN(Z3_FALSE); + } + + Z3_bool Z3_API Z3_rcf_neq(Z3_context c, Z3_rcf_num a, Z3_rcf_num b) { + Z3_TRY; + LOG_Z3_rcf_neq(c, a, b); + RESET_ERROR_CODE(); + reset_rcf_cancel(c); + return rcfm(c).eq(to_rcnumeral(a), to_rcnumeral(b)); Z3_CATCH_RETURN(Z3_FALSE); } @@ -182,8 +242,20 @@ extern "C" { Z3_TRY; LOG_Z3_rcf_num_to_string(c, a); RESET_ERROR_CODE(); + reset_rcf_cancel(c); std::ostringstream buffer; - // TODO + rcfm(c).display(buffer, to_rcnumeral(a)); + return mk_c(c)->mk_external_string(buffer.str()); + Z3_CATCH_RETURN(""); + } + + Z3_string Z3_API Z3_rcf_num_to_decimal_string(Z3_context c, Z3_rcf_num a, unsigned prec) { + Z3_TRY; + LOG_Z3_rcf_num_to_string(c, a); + RESET_ERROR_CODE(); + reset_rcf_cancel(c); + std::ostringstream buffer; + rcfm(c).display_decimal(buffer, to_rcnumeral(a), prec); return mk_c(c)->mk_external_string(buffer.str()); Z3_CATCH_RETURN(""); } diff --git a/src/api/api_util.h b/src/api/api_util.h index b08592cb7..c81384f2f 100644 --- a/src/api/api_util.h +++ b/src/api/api_util.h @@ -37,6 +37,7 @@ namespace api { public: object():m_ref_count(0) {} virtual ~object() {} + unsigned ref_count() const { return m_ref_count; } void inc_ref() { m_ref_count++; } void dec_ref() { SASSERT(m_ref_count > 0); m_ref_count--; if (m_ref_count == 0) dealloc(this); } }; diff --git a/src/api/z3_rcf.h b/src/api/z3_rcf.h index 5b5cc112d..877ae7c64 100644 --- a/src/api/z3_rcf.h +++ b/src/api/z3_rcf.h @@ -145,6 +145,20 @@ extern "C" { */ Z3_bool Z3_API Z3_rcf_ge(__in Z3_context c, __in Z3_rcf_num a, __in Z3_rcf_num b); + /** + \brief Return Z3_TRUE if a == b + + def_API('Z3_rcf_eq', BOOL, (_in(CONTEXT), _in(RCF_NUM), _in(RCF_NUM))) + */ + Z3_bool Z3_API Z3_rcf_eq(__in Z3_context c, __in Z3_rcf_num a, __in Z3_rcf_num b); + + /** + \brief Return Z3_TRUE if a != b + + def_API('Z3_rcf_neq', BOOL, (_in(CONTEXT), _in(RCF_NUM), _in(RCF_NUM))) + */ + Z3_bool Z3_API Z3_rcf_neq(__in Z3_context c, __in Z3_rcf_num a, __in Z3_rcf_num b); + /** \brief Convert the RCF numeral into a string. @@ -152,6 +166,13 @@ extern "C" { */ Z3_string Z3_API Z3_rcf_num_to_string(__in Z3_context c, __in Z3_rcf_num a); + /** + \brief Convert the RCF numeral into a string in decimal notation. + + def_API('Z3_rcf_num_to_decimal_string', STRING, (_in(CONTEXT), _in(RCF_NUM), _in(UINT))) + */ + Z3_string Z3_API Z3_rcf_num_to_decimal_string(__in Z3_context c, __in Z3_rcf_num a, __in unsigned prec); + #ifdef __cplusplus }; #endif // __cplusplus diff --git a/src/math/realclosure/realclosure.cpp b/src/math/realclosure/realclosure.cpp index 856a87ec8..a1b110cc1 100644 --- a/src/math/realclosure/realclosure.cpp +++ b/src/math/realclosure/realclosure.cpp @@ -2464,10 +2464,18 @@ namespace realclosure { set(a, neg(a.m_value)); } + void neg(numeral const & a, numeral & b) { + set(b, neg(a.m_value)); + } + void inv(numeral & a) { set(a, inv(a.m_value)); } + void inv(numeral const & a, numeral & b) { + set(b, inv(a.m_value)); + } + void add(numeral const & a, numeral const & b, numeral & c) { set(c, add(a.m_value, b.m_value)); } @@ -2879,11 +2887,21 @@ namespace realclosure { m_imp->neg(a); } + void manager::neg(numeral const & a, numeral & b) { + save_interval_ctx ctx(this); + m_imp->neg(a, b); + } + void manager::inv(numeral & a) { save_interval_ctx ctx(this); m_imp->inv(a); } + void manager::inv(numeral const & a, numeral & b) { + save_interval_ctx ctx(this); + m_imp->inv(a, b); + } + void manager::div(numeral const & a, numeral const & b, numeral & c) { save_interval_ctx ctx(this); m_imp->div(a, b, c); diff --git a/src/math/realclosure/realclosure.h b/src/math/realclosure/realclosure.h index 72e0da2aa..d3b39fcb5 100644 --- a/src/math/realclosure/realclosure.h +++ b/src/math/realclosure/realclosure.h @@ -181,11 +181,21 @@ namespace realclosure { \brief a <- -a */ void neg(numeral & a); + + /** + \brief b <- -a + */ + void neg(numeral const & a, numeral & b); /** \brief a <- 1/a if a != 0 */ void inv(numeral & a); + + /** + \brief b <- 1/a if a != 0 + */ + void inv(numeral const & a, numeral & b); /** \brief c <- a/b if b != 0 From 1c8101419b132e0954afff90541ff424449182fa Mon Sep 17 00:00:00 2001 From: Leonardo de Moura Date: Sun, 6 Jan 2013 20:59:00 -0800 Subject: [PATCH 28/78] Add Python API for RCF module Signed-off-by: Leonardo de Moura --- src/api/python/z3rcf.py | 114 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 src/api/python/z3rcf.py diff --git a/src/api/python/z3rcf.py b/src/api/python/z3rcf.py new file mode 100644 index 000000000..f85664bd2 --- /dev/null +++ b/src/api/python/z3rcf.py @@ -0,0 +1,114 @@ +############################################ +# Copyright (c) 2013 Microsoft Corporation +# +# Z3 Python interface for Z3 Real Closed Fields +# that may contain +# - computable transcendentals +# - infinitesimals +# - algebraic extensions +# +# Author: Leonardo de Moura (leonardo) +############################################ +from z3 import * +from z3core import * +from z3printer import * +from fractions import Fraction + +def _to_rcfnum(num, ctx=None): + if isinstance(num, RCFNum): + return num + else: + return RCFNum(num, ctx) + +def Pi(ctx=None): + ctx = z3._get_ctx(ctx) + return RCFNum(Z3_rcf_mk_pi(ctx.ref()), ctx) + +def E(ctx=None): + ctx = z3._get_ctx(ctx) + return RCFNum(Z3_rcf_mk_e(ctx.ref()), ctx) + +def MkInfinitesimal(name="eps", ctx=None): + ctx = z3._get_ctx(ctx) + return RCFNum(Z3_rcf_mk_infinitesimal(ctx.ref(), name), ctx) + +class RCFNum: + def __init__(self, num, ctx=None): + # TODO: add support for converting AST numeral values into RCFNum + if isinstance(num, RCFNumObj): + self.num = num + self.ctx = z3._get_ctx(ctx) + else: + self.ctx = z3._get_ctx(ctx) + self.num = Z3_rcf_mk_rational(self.ctx_ref(), str(num)) + Z3_rcf_inc_ref(self.ctx_ref(), self.num) + + def __del__(self): + Z3_rcf_dec_ref(self.ctx_ref(), self.num) + + def ctx_ref(self): + return self.ctx.ref() + + def __repr__(self): + return Z3_rcf_num_to_string(self.ctx_ref(), self.num) + + def __add__(self, other): + return RCFNum(Z3_rcf_add(self.ctx_ref(), self.num, _to_rcfnum(other, self.ctx).num), self.ctx) + + def __radd__(self, other): + return RCFNum(Z3_rcf_add(self.ctx_ref(), _to_rcfnum(other, self.ctx).num, self.num), self.ctx) + + def __mul__(self, other): + return RCFNum(Z3_rcf_mul(self.ctx_ref(), self.num, _to_rcfnum(other, self.ctx).num), self.ctx) + + def __rmul__(self, other): + return RCFNum(Z3_rcf_mul(self.ctx_ref(), _to_rcfnum(other, self.ctx).num, self.num), self.ctx) + + def __sub__(self, other): + return RCFNum(Z3_rcf_sub(self.ctx_ref(), self.num, _to_rcfnum(other, self.ctx).num), self.ctx) + + def __rsub__(self, other): + return RCFNum(Z3_rcf_sub(self.ctx_ref(), _to_rcfnum(other, self.ctx).num, self.num), self.ctx) + + def __div__(self, other): + return RCFNum(Z3_rcf_div(self.ctx_ref(), self.num, _to_rcfnum(other, self.ctx).num), self.ctx) + + def __rdiv__(self, other): + return RCFNum(Z3_rcf_div(self.ctx_ref(), _to_rcfnum(other, self.ctx).num, self.num), self.ctx) + + def __neg__(self): + return self.__rsub__(0) + + def decimal(self, prec=5): + return Z3_rcf_num_to_decimal_string(self.ctx_ref(), self.num, prec) + + def __lt__(self, other): + return Z3_rcf_lt(self.ctx_ref(), self.num, _to_rcfnum(other, self.ctx).num) + + def __rlt__(self, other): + return Z3_rcf_lt(self.ctx_ref(), _to_rcfnum(other, self.ctx).num, self.num) + + def __gt__(self, other): + return Z3_rcf_gt(self.ctx_ref(), self.num, _to_rcfnum(other, self.ctx).num) + + def __rgt__(self, other): + return Z3_rcf_gt(self.ctx_ref(), _to_rcfnum(other, self.ctx).num, self.num) + + def __le__(self, other): + return Z3_rcf_le(self.ctx_ref(), self.num, _to_rcfnum(other, self.ctx).num) + + def __rle__(self, other): + return Z3_rcf_le(self.ctx_ref(), _to_rcfnum(other, self.ctx).num, self.num) + + def __ge__(self, other): + return Z3_rcf_ge(self.ctx_ref(), self.num, _to_rcfnum(other, self.ctx).num) + + def __rge__(self, other): + return Z3_rcf_ge(self.ctx_ref(), _to_rcfnum(other, self.ctx).num, self.num) + + def __eq__(self, other): + return Z3_rcf_eq(self.ctx_ref(), self.num, _to_rcfnum(other, self.ctx).num) + + def __ne__(self, other): + return Z3_rcf_neq(self.ctx_ref(), self.num, _to_rcfnum(other, self.ctx).num) + From 4d578b418ff4d0337787d192ca268ac5a0550641 Mon Sep 17 00:00:00 2001 From: Leonardo de Moura Date: Sun, 6 Jan 2013 21:15:37 -0800 Subject: [PATCH 29/78] Fix bug in approx_div Signed-off-by: Leonardo de Moura --- src/util/mpbq.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/util/mpbq.cpp b/src/util/mpbq.cpp index 9af1c852e..ad2eae770 100644 --- a/src/util/mpbq.cpp +++ b/src/util/mpbq.cpp @@ -855,16 +855,16 @@ void mpbq_manager::approx_div(mpbq const & a, mpbq const & b, mpbq & c, unsigned unsigned k_prime; if (m_manager.is_power_of_two(b.m_num, k_prime)) { // The division is precise, so we ignore k and to_plus_inf - SASSERT(b.m_k == 0); // remark: b.m_num is odd when b.m_k > 0 + SASSERT(b.m_k == 0 || k_prime == 0); // remark: b.m_num is odd when b.m_k > 0, since b.m_num is a power of two we have that b.m_k == 0 or b.m_num == 1. m_manager.set(c.m_num, a.m_num); - if (a.m_k == 0) { - c.m_k = k_prime; - normalize(c); - } - else { - c.m_k = a.m_k + k_prime; - // there is not need to normalize since the least significant bit of a must be 1. + if (b.m_k > 0) { + SASSERT(k_prime == 0); + mpz & pw2 = m_div_tmp1; + m_manager.power(mpz(2), b.m_k, pw2); + m_manager.mul(c.m_num, pw2, c.m_num); } + c.m_k = a.m_k + k_prime; + normalize(c); } else if (m_manager.divides(b.m_num, a.m_num)) { // result is also precise From 3e19df0441ac8199fe9f99e7ac555c09fb88a428 Mon Sep 17 00:00:00 2001 From: Leonardo de Moura Date: Sun, 6 Jan 2013 21:25:46 -0800 Subject: [PATCH 30/78] Fix API logging bug Signed-off-by: Leonardo de Moura --- src/api/api_rcf.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/api_rcf.cpp b/src/api/api_rcf.cpp index 4ba440266..e02522a75 100644 --- a/src/api/api_rcf.cpp +++ b/src/api/api_rcf.cpp @@ -251,7 +251,7 @@ extern "C" { Z3_string Z3_API Z3_rcf_num_to_decimal_string(Z3_context c, Z3_rcf_num a, unsigned prec) { Z3_TRY; - LOG_Z3_rcf_num_to_string(c, a); + LOG_Z3_rcf_num_to_decimal_string(c, a, prec); RESET_ERROR_CODE(); reset_rcf_cancel(c); std::ostringstream buffer; From 3c1f1a3b65cd416038f365aa3b28e7ffb4f69eee Mon Sep 17 00:00:00 2001 From: Leonardo de Moura Date: Sun, 6 Jan 2013 21:50:36 -0800 Subject: [PATCH 31/78] Fix bug in realclosure::compare function Signed-off-by: Leonardo de Moura --- src/math/interval/interval.h | 5 +++++ src/math/interval/interval_def.h | 7 +++++++ src/math/realclosure/realclosure.cpp | 6 +++--- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/math/interval/interval.h b/src/math/interval/interval.h index 32c76c87f..1fe797861 100644 --- a/src/math/interval/interval.h +++ b/src/math/interval/interval.h @@ -200,6 +200,11 @@ public: bool eq(interval const & a, interval const & b) const; + /** + \brief Return true if all values in 'a' are less than all values in 'b'. + */ + bool before(interval const & a, interval const & b) const; + /** \brief Set lower bound to -oo. */ diff --git a/src/math/interval/interval_def.h b/src/math/interval/interval_def.h index c4259fad7..51c1652d7 100644 --- a/src/math/interval/interval_def.h +++ b/src/math/interval/interval_def.h @@ -687,6 +687,13 @@ bool interval_manager::eq(interval const & a, interval const & b) const { upper_is_open(a) == upper_is_open(b); } +template +bool interval_manager::before(interval const & a, interval const & b) const { + if (upper_is_inf(a) || lower_is_inf(b)) + return false; + return m().lt(upper(a), lower(b)) || (upper_is_open(a) && m().eq(upper(a), lower(b))); +} + template void interval_manager::neg_jst(interval const & a, interval_deps & b_deps) { if (lower_is_inf(a)) { diff --git a/src/math/realclosure/realclosure.cpp b/src/math/realclosure/realclosure.cpp index a1b110cc1..0b0483e3c 100644 --- a/src/math/realclosure/realclosure.cpp +++ b/src/math/realclosure/realclosure.cpp @@ -2500,10 +2500,10 @@ namespace realclosure { else if (is_nz_rational(a) && is_nz_rational(b)) return qm().lt(to_mpq(a), to_mpq(b)) ? -1 : 1; else { - // TODO: try to refine interval before switching to sub/expensive approach - if (bqm().lt(interval(a).upper(), interval(b).lower())) + // TODO: try to refine interval before switching to sub+sign approach + if (bqim().before(interval(a), interval(b))) return -1; - else if (bqm().lt(interval(b).upper(), interval(a).lower())) + else if (bqim().before(interval(b), interval(a))) return 1; else { value_ref diff(*this); From 09b5724d828f381b39644175a483ddbb5c077a28 Mon Sep 17 00:00:00 2001 From: Leonardo de Moura Date: Mon, 7 Jan 2013 12:25:28 -0800 Subject: [PATCH 32/78] Simplify RCF C API. Add Z3_rcf_mk_roots (C API) and MkRoots (Python API). Implement basic root isolation support. Signed-off-by: Leonardo de Moura --- src/api/api_rcf.cpp | 117 ++++++++++++++++----------- src/api/api_rcf.h | 38 --------- src/api/python/z3rcf.py | 73 ++++++++++++----- src/api/z3_rcf.h | 24 +++--- src/math/realclosure/realclosure.cpp | 78 +++++++++++++++++- src/math/realclosure/realclosure.h | 4 + 6 files changed, 216 insertions(+), 118 deletions(-) delete mode 100644 src/api/api_rcf.h diff --git a/src/api/api_rcf.cpp b/src/api/api_rcf.cpp index e02522a75..5c76adfad 100644 --- a/src/api/api_rcf.cpp +++ b/src/api/api_rcf.cpp @@ -23,29 +23,10 @@ Notes: #include"z3.h" #include"api_log_macros.h" #include"api_context.h" -#include"api_rcf.h" +#include"realclosure.h" extern "C" { - void Z3_API Z3_rcf_inc_ref(Z3_context c, Z3_rcf_num a) { - Z3_TRY; - LOG_Z3_rcf_inc_ref(c, a); - RESET_ERROR_CODE(); - to_rcf_num(a)->inc_ref(); - Z3_CATCH; - } - - void Z3_API Z3_rcf_dec_ref(Z3_context c, Z3_rcf_num a) { - Z3_TRY; - LOG_Z3_rcf_dec_ref(c, a); - RESET_ERROR_CODE(); - if (to_rcf_num(a)->ref_count() == 1) { - mk_c(c)->rcfm().del(to_rcnumeral(a)); - } - to_rcf_num(a)->dec_ref(); - Z3_CATCH; - } - static rcmanager & rcfm(Z3_context c) { return mk_c(c)->rcfm(); } @@ -53,14 +34,24 @@ extern "C" { static void reset_rcf_cancel(Z3_context c) { rcfm(c).reset_cancel(); } - - static Z3_rcf_num mk_rcf_num(Z3_context c, rcnumeral & a) { - Z3_rcf_num_ref * r = alloc(Z3_rcf_num_ref); - rcfm(c).swap(r->m_num, a); - mk_c(c)->save_object(r); - return of_rcf_num(r); + + static rcnumeral to_rcnumeral(Z3_rcf_num a) { + return rcnumeral::mk(a); } - + + static Z3_rcf_num from_rcnumeral(rcnumeral a) { + return reinterpret_cast(a.c_ptr()); + } + + void Z3_API Z3_rcf_del(Z3_context c, Z3_rcf_num a) { + Z3_TRY; + LOG_Z3_rcf_del(c, a); + RESET_ERROR_CODE(); + rcnumeral _a = to_rcnumeral(a); + rcfm(c).del(_a); + Z3_CATCH; + } + Z3_rcf_num Z3_API Z3_rcf_mk_rational(Z3_context c, Z3_string val) { Z3_TRY; LOG_Z3_rcf_mk_rational(c, val); @@ -68,9 +59,9 @@ extern "C" { reset_rcf_cancel(c); scoped_mpq q(rcfm(c).qm()); rcfm(c).qm().set(q, val); - scoped_rcnumeral r(rcfm(c)); + rcnumeral r; rcfm(c).set(r, q); - RETURN_Z3(mk_rcf_num(c, r)); + RETURN_Z3(from_rcnumeral(r)); Z3_CATCH_RETURN(0); } @@ -79,9 +70,9 @@ extern "C" { LOG_Z3_rcf_mk_small_int(c, val); RESET_ERROR_CODE(); reset_rcf_cancel(c); - scoped_rcnumeral r(rcfm(c)); + rcnumeral r; rcfm(c).set(r, val); - RETURN_Z3(mk_rcf_num(c, r)); + RETURN_Z3(from_rcnumeral(r)); Z3_CATCH_RETURN(0); } @@ -90,9 +81,9 @@ extern "C" { LOG_Z3_rcf_mk_pi(c); RESET_ERROR_CODE(); reset_rcf_cancel(c); - scoped_rcnumeral r(rcfm(c)); + rcnumeral r; rcfm(c).mk_pi(r); - RETURN_Z3(mk_rcf_num(c, r)); + RETURN_Z3(from_rcnumeral(r)); Z3_CATCH_RETURN(0); } @@ -101,9 +92,9 @@ extern "C" { LOG_Z3_rcf_mk_e(c); RESET_ERROR_CODE(); reset_rcf_cancel(c); - scoped_rcnumeral r(rcfm(c)); + rcnumeral r; rcfm(c).mk_e(r); - RETURN_Z3(mk_rcf_num(c, r)); + RETURN_Z3(from_rcnumeral(r)); Z3_CATCH_RETURN(0); } @@ -112,9 +103,37 @@ extern "C" { LOG_Z3_rcf_mk_infinitesimal(c, name); RESET_ERROR_CODE(); reset_rcf_cancel(c); - scoped_rcnumeral r(rcfm(c)); + rcnumeral r; rcfm(c).mk_infinitesimal(name, r); - RETURN_Z3(mk_rcf_num(c, r)); + RETURN_Z3(from_rcnumeral(r)); + Z3_CATCH_RETURN(0); + } + + unsigned Z3_API Z3_rcf_mk_roots(Z3_context c, unsigned n, Z3_rcf_num const a[], Z3_rcf_num roots[]) { + Z3_TRY; + LOG_Z3_rcf_mk_roots(c, n, a, roots); + RESET_ERROR_CODE(); + reset_rcf_cancel(c); + rcnumeral_vector av; + unsigned rz = 0; + for (unsigned i = 0; i < n; i++) { + if (!rcfm(c).is_zero(to_rcnumeral(a[i]))) + rz = i + 1; + av.push_back(to_rcnumeral(a[i])); + } + if (rz == 0) { + // it is the zero polynomial + SET_ERROR_CODE(Z3_INVALID_ARG); + return 0; + } + av.shrink(rz); + rcnumeral_vector rs; + rcfm(c).isolate_roots(av.size(), av.c_ptr(), rs); + unsigned num_roots = rs.size(); + for (unsigned i = 0; i < num_roots; i++) { + roots[i] = from_rcnumeral(rs[i]); + } + return num_roots; Z3_CATCH_RETURN(0); } @@ -123,9 +142,9 @@ extern "C" { LOG_Z3_rcf_add(c, a, b); RESET_ERROR_CODE(); reset_rcf_cancel(c); - scoped_rcnumeral r(rcfm(c)); + rcnumeral r; rcfm(c).add(to_rcnumeral(a), to_rcnumeral(b), r); - RETURN_Z3(mk_rcf_num(c, r)); + RETURN_Z3(from_rcnumeral(r)); Z3_CATCH_RETURN(0); } @@ -134,9 +153,9 @@ extern "C" { LOG_Z3_rcf_sub(c, a, b); RESET_ERROR_CODE(); reset_rcf_cancel(c); - scoped_rcnumeral r(rcfm(c)); + rcnumeral r; rcfm(c).sub(to_rcnumeral(a), to_rcnumeral(b), r); - RETURN_Z3(mk_rcf_num(c, r)); + RETURN_Z3(from_rcnumeral(r)); Z3_CATCH_RETURN(0); } @@ -145,9 +164,9 @@ extern "C" { LOG_Z3_rcf_mul(c, a, b); RESET_ERROR_CODE(); reset_rcf_cancel(c); - scoped_rcnumeral r(rcfm(c)); + rcnumeral r; rcfm(c).mul(to_rcnumeral(a), to_rcnumeral(b), r); - RETURN_Z3(mk_rcf_num(c, r)); + RETURN_Z3(from_rcnumeral(r)); Z3_CATCH_RETURN(0); } @@ -156,9 +175,9 @@ extern "C" { LOG_Z3_rcf_div(c, a, b); RESET_ERROR_CODE(); reset_rcf_cancel(c); - scoped_rcnumeral r(rcfm(c)); + rcnumeral r; rcfm(c).div(to_rcnumeral(a), to_rcnumeral(b), r); - RETURN_Z3(mk_rcf_num(c, r)); + RETURN_Z3(from_rcnumeral(r)); Z3_CATCH_RETURN(0); } @@ -167,9 +186,9 @@ extern "C" { LOG_Z3_rcf_neg(c, a); RESET_ERROR_CODE(); reset_rcf_cancel(c); - scoped_rcnumeral r(rcfm(c)); + rcnumeral r; rcfm(c).neg(to_rcnumeral(a), r); - RETURN_Z3(mk_rcf_num(c, r)); + RETURN_Z3(from_rcnumeral(r)); Z3_CATCH_RETURN(0); } @@ -178,9 +197,9 @@ extern "C" { LOG_Z3_rcf_inv(c, a); RESET_ERROR_CODE(); reset_rcf_cancel(c); - scoped_rcnumeral r(rcfm(c)); + rcnumeral r; rcfm(c).inv(to_rcnumeral(a), r); - RETURN_Z3(mk_rcf_num(c, r)); + RETURN_Z3(from_rcnumeral(r)); Z3_CATCH_RETURN(0); } diff --git a/src/api/api_rcf.h b/src/api/api_rcf.h deleted file mode 100644 index 2026fb21e..000000000 --- a/src/api/api_rcf.h +++ /dev/null @@ -1,38 +0,0 @@ -/*++ -Copyright (c) 2013 Microsoft Corporation - -Module Name: - - api_rcf.h - -Abstract: - - Additional APIs for handling elements of the Z3 real closed field that contains: - - transcendental extensions - - infinitesimal extensions - - algebraic extensions - -Author: - - Leonardo de Moura (leonardo) 2012-01-05 - -Notes: - ---*/ -#ifndef _API_RCF_H_ -#define _API_RCF_H_ - -#include"api_util.h" -#include"realclosure.h" - -struct Z3_rcf_num_ref : public api::object { - rcnumeral m_num; - virtual ~Z3_rcf_num_ref() {} -}; - - -inline Z3_rcf_num_ref * to_rcf_num(Z3_rcf_num n) { return reinterpret_cast(n); } -inline Z3_rcf_num of_rcf_num(Z3_rcf_num_ref * n) { return reinterpret_cast(n); } -inline rcnumeral & to_rcnumeral(Z3_rcf_num n) { SASSERT(n != 0); return to_rcf_num(n)->m_num; } - -#endif diff --git a/src/api/python/z3rcf.py b/src/api/python/z3rcf.py index f85664bd2..2ff29dc9f 100644 --- a/src/api/python/z3rcf.py +++ b/src/api/python/z3rcf.py @@ -32,6 +32,22 @@ def MkInfinitesimal(name="eps", ctx=None): ctx = z3._get_ctx(ctx) return RCFNum(Z3_rcf_mk_infinitesimal(ctx.ref(), name), ctx) +def MkRoots(p, ctx=None): + ctx = z3._get_ctx(ctx) + num = len(p) + _tmp = [] + _as = (RCFNumObj * num)() + _rs = (RCFNumObj * num)() + for i in range(num): + _a = _to_rcfnum(p[i], ctx) + _tmp.append(_a) # prevent GC + _as[i] = _a.num + nr = Z3_rcf_mk_roots(ctx.ref(), num, _as, _rs) + r = [] + for i in range(nr): + r.append(RCFNum(_rs[i], ctx)) + return r + class RCFNum: def __init__(self, num, ctx=None): # TODO: add support for converting AST numeral values into RCFNum @@ -41,10 +57,9 @@ class RCFNum: else: self.ctx = z3._get_ctx(ctx) self.num = Z3_rcf_mk_rational(self.ctx_ref(), str(num)) - Z3_rcf_inc_ref(self.ctx_ref(), self.num) def __del__(self): - Z3_rcf_dec_ref(self.ctx_ref(), self.num) + Z3_rcf_del(self.ctx_ref(), self.num) def ctx_ref(self): return self.ctx.ref() @@ -53,28 +68,36 @@ class RCFNum: return Z3_rcf_num_to_string(self.ctx_ref(), self.num) def __add__(self, other): - return RCFNum(Z3_rcf_add(self.ctx_ref(), self.num, _to_rcfnum(other, self.ctx).num), self.ctx) + v = _to_rcfnum(other, self.ctx) + return RCFNum(Z3_rcf_add(self.ctx_ref(), self.num, v.num), self.ctx) def __radd__(self, other): - return RCFNum(Z3_rcf_add(self.ctx_ref(), _to_rcfnum(other, self.ctx).num, self.num), self.ctx) + v = _to_rcfnum(other, self.ctx) + return RCFNum(Z3_rcf_add(self.ctx_ref(), v.num, self.num), self.ctx) def __mul__(self, other): - return RCFNum(Z3_rcf_mul(self.ctx_ref(), self.num, _to_rcfnum(other, self.ctx).num), self.ctx) + v = _to_rcfnum(other, self.ctx) + return RCFNum(Z3_rcf_mul(self.ctx_ref(), self.num, v.num), self.ctx) def __rmul__(self, other): - return RCFNum(Z3_rcf_mul(self.ctx_ref(), _to_rcfnum(other, self.ctx).num, self.num), self.ctx) + v = _to_rcfnum(other, self.ctx) + return RCFNum(Z3_rcf_mul(self.ctx_ref(), v.num, self.num), self.ctx) def __sub__(self, other): - return RCFNum(Z3_rcf_sub(self.ctx_ref(), self.num, _to_rcfnum(other, self.ctx).num), self.ctx) + v = _to_rcfnum(other, self.ctx) + return RCFNum(Z3_rcf_sub(self.ctx_ref(), self.num, v.num), self.ctx) def __rsub__(self, other): - return RCFNum(Z3_rcf_sub(self.ctx_ref(), _to_rcfnum(other, self.ctx).num, self.num), self.ctx) + v = _to_rcfnum(other, self.ctx) + return RCFNum(Z3_rcf_sub(self.ctx_ref(), v.num, self.num), self.ctx) def __div__(self, other): - return RCFNum(Z3_rcf_div(self.ctx_ref(), self.num, _to_rcfnum(other, self.ctx).num), self.ctx) + v = _to_rcfnum(other, self.ctx) + return RCFNum(Z3_rcf_div(self.ctx_ref(), self.num, v.num), self.ctx) def __rdiv__(self, other): - return RCFNum(Z3_rcf_div(self.ctx_ref(), _to_rcfnum(other, self.ctx).num, self.num), self.ctx) + v = _to_rcfnum(other, self.ctx) + return RCFNum(Z3_rcf_div(self.ctx_ref(), v.num, self.num), self.ctx) def __neg__(self): return self.__rsub__(0) @@ -83,32 +106,42 @@ class RCFNum: return Z3_rcf_num_to_decimal_string(self.ctx_ref(), self.num, prec) def __lt__(self, other): - return Z3_rcf_lt(self.ctx_ref(), self.num, _to_rcfnum(other, self.ctx).num) + v = _to_rcfnum(other, self.ctx) + return Z3_rcf_lt(self.ctx_ref(), self.num, v.num) def __rlt__(self, other): - return Z3_rcf_lt(self.ctx_ref(), _to_rcfnum(other, self.ctx).num, self.num) + v = _to_rcfnum(other, self.ctx) + return Z3_rcf_lt(self.ctx_ref(), v.num, self.num) def __gt__(self, other): - return Z3_rcf_gt(self.ctx_ref(), self.num, _to_rcfnum(other, self.ctx).num) + v = _to_rcfnum(other, self.ctx) + return Z3_rcf_gt(self.ctx_ref(), self.num, v.num) def __rgt__(self, other): - return Z3_rcf_gt(self.ctx_ref(), _to_rcfnum(other, self.ctx).num, self.num) + v = _to_rcfnum(other, self.ctx) + return Z3_rcf_gt(self.ctx_ref(), v.num, self.num) def __le__(self, other): - return Z3_rcf_le(self.ctx_ref(), self.num, _to_rcfnum(other, self.ctx).num) + v = _to_rcfnum(other, self.ctx) + return Z3_rcf_le(self.ctx_ref(), self.num, v.num) def __rle__(self, other): - return Z3_rcf_le(self.ctx_ref(), _to_rcfnum(other, self.ctx).num, self.num) + v = _to_rcfnum(other, self.ctx) + return Z3_rcf_le(self.ctx_ref(), v.num, self.num) def __ge__(self, other): - return Z3_rcf_ge(self.ctx_ref(), self.num, _to_rcfnum(other, self.ctx).num) + v = _to_rcfnum(other, self.ctx) + return Z3_rcf_ge(self.ctx_ref(), self.num, v.num) def __rge__(self, other): - return Z3_rcf_ge(self.ctx_ref(), _to_rcfnum(other, self.ctx).num, self.num) + v = _to_rcfnum(other, self.ctx) + return Z3_rcf_ge(self.ctx_ref(), v.num, self.num) def __eq__(self, other): - return Z3_rcf_eq(self.ctx_ref(), self.num, _to_rcfnum(other, self.ctx).num) + v = _to_rcfnum(other, self.ctx) + return Z3_rcf_eq(self.ctx_ref(), self.num, v.num) def __ne__(self, other): - return Z3_rcf_neq(self.ctx_ref(), self.num, _to_rcfnum(other, self.ctx).num) + v = _to_rcfnum(other, self.ctx) + return Z3_rcf_neq(self.ctx_ref(), self.num, v.num) diff --git a/src/api/z3_rcf.h b/src/api/z3_rcf.h index 877ae7c64..1bca7e391 100644 --- a/src/api/z3_rcf.h +++ b/src/api/z3_rcf.h @@ -27,18 +27,11 @@ extern "C" { #endif // __cplusplus /** - \brief Increment the reference counter of a RCF numeral. - - def_API('Z3_rcf_inc_ref', VOID, (_in(CONTEXT), _in(RCF_NUM))) - */ - void Z3_API Z3_rcf_inc_ref(__in Z3_context c, __in Z3_rcf_num a); + \brief Delete a RCF numeral created using the RCF API. - /** - \brief Decrement the reference counter of a RCF numeral. - - def_API('Z3_rcf_dec_ref', VOID, (_in(CONTEXT), _in(RCF_NUM))) + def_API('Z3_rcf_del', VOID, (_in(CONTEXT), _in(RCF_NUM))) */ - void Z3_API Z3_rcf_dec_ref(__in Z3_context c, __in Z3_rcf_num a); + void Z3_API Z3_rcf_del(__in Z3_context c, __in Z3_rcf_num a); /** \brief Return a RCF rational using the given string. @@ -75,6 +68,17 @@ extern "C" { */ Z3_rcf_num Z3_API Z3_rcf_mk_infinitesimal(__in Z3_context c, __in Z3_string name); + /** + \brief Store in roots the roots of the polynomial a[n-1]*x^{n-1} + ... + a[0]. + The output vector \c roots must have size \c n. + It returns the number of roots of the polynomial. + + \pre The input polynomial is not the zero polynomial. + + def_API('Z3_rcf_mk_roots', UINT, (_in(CONTEXT), _in(UINT), _in_array(1, RCF_NUM), _out_array(1, RCF_NUM))) + */ + unsigned Z3_API Z3_rcf_mk_roots(__in Z3_context c, __in unsigned n, __in_ecount(n) Z3_rcf_num const a[], __out_ecount(n) Z3_rcf_num roots[]); + /** \brief Return the value a + b. diff --git a/src/math/realclosure/realclosure.cpp b/src/math/realclosure/realclosure.cpp index 0b0483e3c..47ebe7f95 100644 --- a/src/math/realclosure/realclosure.cpp +++ b/src/math/realclosure/realclosure.cpp @@ -1082,10 +1082,86 @@ namespace realclosure { } } - void isolate_roots(unsigned n, numeral const * as, numeral_vector & roots) { + /** + \brief Root isolation for polynomials that are + - nonlinear (degree > 2) + - zero is not a root + - square free + - nonconstant + */ + void nl_nz_sqf_isolate_roots(unsigned n, value * const * as, numeral_vector & roots) { + SASSERT(n > 2); + SASSERT(!is_zero(as[0])); + SASSERT(!is_zero(as[n-1])); // TODO } + /** + \brief Root isolation for polynomials that are + - zero is not a root + - square free + - nonconstant + */ + void nz_sqf_isolate_roots(unsigned n, value * const * as, numeral_vector & roots) { + SASSERT(n > 1); + SASSERT(!is_zero(as[0])); + SASSERT(!is_zero(as[n-1])); + if (n == 2) { + // we don't need a field extension for linear polynomials. + numeral r; + value_ref neg_as_0(*this); + neg_as_0 = neg(as[0]); + set(r, div(neg_as_0, as[1])); + roots.push_back(r); + } + else { + nl_nz_sqf_isolate_roots(n, as, roots); + } + } + + /** + \brief Root isolation for polynomials where 0 is not a root. + */ + void nz_isolate_roots(unsigned n, value * const * as, numeral_vector & roots) { + SASSERT(n > 0); + SASSERT(!is_zero(as[0])); + SASSERT(!is_zero(as[n-1])); + if (n == 1) { + // constant polynomial + return; + } + value_ref_buffer sqf(*this); + square_free(n, as, sqf); + nz_sqf_isolate_roots(sqf.size(), sqf.c_ptr(), roots); + } + + /** + \brief Root isolation entry point. + */ + void isolate_roots(unsigned n, numeral const * as, numeral_vector & roots) { + SASSERT(n > 0); + SASSERT(!is_zero(as[n-1])); + if (n == 1) { + // constant polynomial + return; + } + unsigned i = 0; + for (; i < n; i++) { + if (!is_zero(as[i])) + break; + } + SASSERT(i < n); + SASSERT(!is_zero(as[i])); + ptr_buffer as_values; + for (; i < n; i++) + as_values.push_back(as[i].m_value); + nz_isolate_roots(as_values.size(), as_values.c_ptr(), roots); + if (as_values.size() < n) { + // zero is a root + roots.push_back(numeral()); + } + } + void reset(numeral & a) { del(a); SASSERT(is_zero(a)); diff --git a/src/math/realclosure/realclosure.h b/src/math/realclosure/realclosure.h index d3b39fcb5..74d094411 100644 --- a/src/math/realclosure/realclosure.h +++ b/src/math/realclosure/realclosure.h @@ -278,6 +278,10 @@ namespace realclosure { value * m_value; public: num():m_value(0) {} + + // Low level functions for implementing the C API + void * c_ptr() { return m_value; } + static num mk(void * ptr) { num r; r.m_value = reinterpret_cast(ptr); return r; } }; }; From 56db84a0e5a802238c0f43b85eeca8679eafbd55 Mon Sep 17 00:00:00 2001 From: Leonardo de Moura Date: Mon, 7 Jan 2013 15:10:16 -0800 Subject: [PATCH 33/78] Fix RCF API logging bug Signed-off-by: Leonardo de Moura --- src/api/api_rcf.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/api_rcf.cpp b/src/api/api_rcf.cpp index 5c76adfad..c79dfbf20 100644 --- a/src/api/api_rcf.cpp +++ b/src/api/api_rcf.cpp @@ -133,7 +133,7 @@ extern "C" { for (unsigned i = 0; i < num_roots; i++) { roots[i] = from_rcnumeral(rs[i]); } - return num_roots; + RETURN_Z3_rcf_mk_roots num_roots; Z3_CATCH_RETURN(0); } From 4ea06b8040cf307a1b274a7ea242dc1a1475febe Mon Sep 17 00:00:00 2001 From: Leonardo de Moura Date: Mon, 7 Jan 2013 16:22:47 -0800 Subject: [PATCH 34/78] Fix Z3_enable_trace API Signed-off-by: Leonardo de Moura --- src/api/api_context.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/api/api_context.cpp b/src/api/api_context.cpp index 441190ba5..6106cb6c7 100644 --- a/src/api/api_context.cpp +++ b/src/api/api_context.cpp @@ -492,8 +492,11 @@ extern "C" { } void Z3_API Z3_enable_trace(Z3_string tag) { + memory::initialize(UINT_MAX); LOG_Z3_enable_trace(tag); - enable_trace(tag); + // Tag is a string that was probably not allocated by Z3. Create a copy using symbol. + symbol tag_sym(tag); + enable_trace(tag_sym.bare_str()); } void Z3_API Z3_disable_trace(Z3_string tag) { From 5873a59769a6df4ff52d0c077bdf8d8a838c2e63 Mon Sep 17 00:00:00 2001 From: Leonardo de Moura Date: Mon, 7 Jan 2013 16:23:30 -0800 Subject: [PATCH 35/78] Add root upper bounds estimation Signed-off-by: Leonardo de Moura --- src/math/realclosure/realclosure.cpp | 208 +++++++++++++++++++++++++-- 1 file changed, 199 insertions(+), 9 deletions(-) diff --git a/src/math/realclosure/realclosure.cpp b/src/math/realclosure/realclosure.cpp index 47ebe7f95..1c11cc4a2 100644 --- a/src/math/realclosure/realclosure.cpp +++ b/src/math/realclosure/realclosure.cpp @@ -191,8 +191,6 @@ namespace realclosure { void set_real(bool f) { m_real = f; } }; - typedef ptr_vector value_vector; - // --------------------------------- // // Field Extensions @@ -497,8 +495,11 @@ namespace realclosure { \brief c <- a/b with precision prec. */ void div(mpbqi const & a, mpbqi const & b, unsigned prec, mpbqi & c) { + SASSERT(!contains_zero(a)); + SASSERT(!contains_zero(b)); scoped_set_div_precision set(bqm(), prec); bqim().div(a, b, c); + SASSERT(!contains_zero(c)); } /** @@ -1081,6 +1082,161 @@ namespace realclosure { inc_ref(m_e); } } + + /** + \brief r <- magnitude of the lower bound of |i|. + That is, 2^r <= |i|.lower() + Another way to view it is: + 2^r is smaller than the absolute value of any element in the interval i. + + Return true if succeeded, and false if i contains elements that are infinitely close to 0. + + \pre !contains_zero(i) + */ + bool abs_lower_magnitude(mpbqi const & i, int & r) { + SASSERT(!contains_zero(i)); + if (bqim().is_P(i)) { + if (bqm().is_zero(i.lower())) + return false; + r = bqm().magnitude_lb(i.lower()); + return true; + } + else { + SASSERT(bqim().is_N(i)); + if (bqm().is_zero(i.upper())) + return false; + scoped_mpbq tmp(bqm()); + tmp = i.upper(); + bqm().neg(tmp); + r = bqm().magnitude_lb(tmp); + return true; + } + } + + /** + \brief r <- magnitude of the upper bound of |i|. + That is, |i|.upper <= 2^r + Another way to view it is: + 2^r is bigger than the absolute value of any element in the interval i. + + Return true if succeeded, and false if i is unbounded. + + \pre !contains_zero(i) + */ + bool abs_upper_magnitude(mpbqi const & i, int & r) { + SASSERT(!contains_zero(i)); + if (bqim().is_P(i)) { + if (i.upper_is_inf()) + return false; + r = bqm().magnitude_ub(i.upper()); + return true; + } + else { + SASSERT(bqim().is_N(i)); + if (i.lower_is_inf()) + return false; + scoped_mpbq tmp(bqm()); + tmp = i.lower(); + bqm().neg(tmp); + r = bqm().magnitude_ub(tmp); + return true; + } + } + + /** + \brief Find positive root upper bound using Knuth's approach. + + Given p(x) = a_n * x^n + a_{n-1} * x^{n-1} + ... + a_0 + + If a_n is positive, + Let B = max({ (-a_{n-k}/a_n)^{1/k} | 1 <= k <= n, a_{n-k} < 0 }) + Then, 2*B is a bound for the positive roots + + Similarly, if a_n is negative + Let B = max({ (-a_{n-k}/a_n)^{1/k} | 1 <= k <= n, a_{n-k} > 0 }) + Then, 2*B is a bound for the positive roots + + This procedure returns a N s.t. 2*B <= 2^N + + The computation is performed using the intervals associated with the coefficients of + the polynomial. + + The procedure may fail if the interval for a_n is of the form (l, 0) or (0, u). + Similarly, the procedure will fail if one of the a_{n-k} has an interval of the form (l, oo) or (-oo, u). + Both cases can only happen if the values of the coefficients depend on infinitesimal values. + */ + bool pos_root_upper_bound(unsigned n, value * const * p, unsigned & N) { + SASSERT(n > 1); + SASSERT(!is_zero(p[n-1])); + int lc_sign = sign(p[n-1]); + SASSERT(lc_sign != 0); + int lc_mag; + if (!abs_lower_magnitude(interval(p[n-1]), lc_mag)) + return false; + N = 0; + for (unsigned k = 2; k <= n; k++) { + value * a = p[n - k]; + if (!is_zero(a) && sign(a) != lc_sign) { + int a_mag; + if (!abs_upper_magnitude(interval(a), a_mag)) + return false; + int C = (a_mag - lc_mag)/k + 1 /* compensate imprecision on division */ + 1 /* Knuth's bound is 2 x Max {... } */; + if (C > 0 && static_cast(C) > N) + N = C; + } + } + return true; + } + + + /** + \brief Auxiliary method for creating the intervals of the coefficients of the polynomials p(-x) + without actually creating p(-x). + + 'a' is the interval of the i-th coefficient of a polynomial + a_n * x^n + ... + a_0 + */ + void neg_root_adjust(mpbqi const & a, unsigned i, mpbqi & r) { + if (i % 2 == 0) + bqim().neg(a, r); + else + bqim().set(r, a); + } + + /** + \brief Find negative root upper bound using Knuth's approach. + + This is similar to pos_root_upper_bound. In principle, we can use + the same algorithm. We just have to adjust the coefficients by using + the transformation p(-x). + */ + bool neg_root_upper_bound(unsigned n, value * const * as, unsigned & N) { + SASSERT(n > 1); + SASSERT(!is_zero(as[n-1])); + scoped_mpbqi aux(bqim()); + neg_root_adjust(interval(as[n-1]), n-1, aux); + int lc_sign = bqim().is_P(aux) ? 1 : -1; + int lc_mag; + if (!abs_lower_magnitude(aux, lc_mag)) + return false; + N = 0; + for (unsigned k = 2; k <= n; k++) { + value * a = as[n - k]; + if (!is_zero(a)) { + neg_root_adjust(interval(as[n-k]), n-k, aux); + int a_sign = bqim().is_P(aux) ? 1 : -1; + if (a_sign != lc_sign) { + int a_mag; + if (!abs_upper_magnitude(aux, a_mag)) + return false; + int C = (a_mag - lc_mag)/k + 1 /* compensate imprecision on division */ + 1 /* Knuth's bound is 2 x Max {... } */; + if (C > 0 && static_cast(C) > N) + N = C; + } + } + } + return true; + } /** \brief Root isolation for polynomials that are @@ -1093,6 +1249,14 @@ namespace realclosure { SASSERT(n > 2); SASSERT(!is_zero(as[0])); SASSERT(!is_zero(as[n-1])); + unsigned pos_N, neg_N; + bool has_neg_upper = neg_root_upper_bound(n, as, neg_N); + bool has_pos_upper = pos_root_upper_bound(n, as, pos_N); + TRACE("rcf", + display_poly(tout, n, as); tout << "\n"; + tout << "has_neg_upper: " << has_neg_upper << " neg_N: " << neg_N << "\n"; + tout << "has_pos_upper: " << has_pos_upper << " pos_N: " << pos_N << "\n";); + // TODO } @@ -1528,6 +1692,10 @@ namespace realclosure { \brief r <- rem(p1, p2) */ void rem(unsigned sz1, value * const * p1, unsigned sz2, value * const * p2, value_ref_buffer & r) { + TRACE("rcf", + tout << "rem\n"; + display_poly(tout, sz1, p1); tout << "\n"; + display_poly(tout, sz2, p2); tout << "\n";); r.reset(); SASSERT(sz2 > 0); if (sz2 == 1) @@ -1537,18 +1705,20 @@ namespace realclosure { return; // r is p1 value * b_n = p2[sz2 - 1]; value_ref ratio(*this); + value_ref new_a(*this); SASSERT(b_n != 0); while (true) { checkpoint(); sz1 = r.size(); if (sz1 < sz2) { + TRACE("rcf", tout << "rem result\n"; display_poly(tout, r.size(), r.c_ptr()); tout << "\n";); return; } unsigned m_n = sz1 - sz2; ratio = div(r[sz1 - 1], b_n); for (unsigned i = 0; i < sz2 - 1; i++) { - ratio = mul(ratio, p2[i]); - r.set(i + m_n, sub(r[i + m_n], ratio)); + new_a = mul(ratio, p2[i]); + r.set(i + m_n, sub(r[i + m_n], new_a)); } r.shrink(sz1 - 1); adjust_size(r); @@ -1626,13 +1796,18 @@ namespace realclosure { else { value_ref_buffer A(*this); value_ref_buffer B(*this); - value_ref_buffer & R = r; + value_ref_buffer R(*this); A.append(sz1, p1); B.append(sz2, p2); while (true) { + TRACE("rcf_gcd", + tout << "A: "; display_poly(tout, A.size(), A.c_ptr()); tout << "\n"; + tout << "B: "; display_poly(tout, B.size(), B.c_ptr()); tout << "\n";); if (B.empty()) { mk_monic(A); r = A; + TRACE("rcf_gcd", + tout << "gcd result: "; display_poly(tout, r.size(), r.c_ptr()); tout << "\n";); return; } rem(A.size(), A.c_ptr(), B.size(), B.c_ptr(), R); @@ -2192,6 +2367,7 @@ namespace realclosure { rational_function_value * r = mk_rational_function_value_core(a->ext(), num_sz, num, den_sz, den); bqim().add(interval(a), interval(b), r->interval()); if (determine_sign(r)) { + SASSERT(!contains_zero(r->interval())); return r; } else { @@ -2339,6 +2515,7 @@ namespace realclosure { neg(an.size(), an.c_ptr(), new_num); rational_function_value * r = mk_rational_function_value_core(a->ext(), new_num.size(), new_num.c_ptr(), ad.size(), ad.c_ptr()); bqim().neg(interval(a), r->interval()); + SASSERT(!contains_zero(r->interval())); return r; } @@ -2370,6 +2547,7 @@ namespace realclosure { rational_function_value * r = mk_rational_function_value_core(a->ext(), num_sz, num, den_sz, den); bqim().mul(interval(a), interval(b), r->interval()); if (determine_sign(r)) { + SASSERT(!contains_zero(r->interval())); return r; } else { @@ -2637,10 +2815,13 @@ namespace realclosure { } template - void display_polynomial(std::ostream & out, polynomial const & p, DisplayVar const & display_var, bool compact) const { - unsigned i = p.size(); + void display_polynomial(std::ostream & out, unsigned sz, value * const * p, DisplayVar const & display_var, bool compact) const { + if (sz == 0) { + out << "0"; + return; + } + unsigned i = sz; bool first = true; - SASSERT(i > 0); while (i > 0) { --i; if (p[i] == 0) @@ -2670,6 +2851,11 @@ namespace realclosure { } } + template + void display_polynomial(std::ostream & out, polynomial const & p, DisplayVar const & display_var, bool compact) const { + display_polynomial(out, p.size(), p.c_ptr(), display_var, compact); + } + struct display_free_var_proc { void operator()(std::ostream & out, bool compact) const { out << "#"; @@ -2714,6 +2900,10 @@ namespace realclosure { out << "})"; } + void display_poly(std::ostream & out, unsigned n, value * const * p) const { + display_polynomial(out, n, p, display_free_var_proc(), false); + } + void display_ext(std::ostream & out, extension * r, bool compact) const { switch (r->knd()) { case extension::TRANSCENDENTAL: to_transcendental(r)->display(out); break; @@ -2769,7 +2959,7 @@ namespace realclosure { out << "]"; } } - + void display(std::ostream & out, numeral const & a) const { display(out, a.m_value, false); } From e01a7b62686bb5a5ce83da046ea1fa6cd885bda1 Mon Sep 17 00:00:00 2001 From: Leonardo de Moura Date: Mon, 7 Jan 2013 17:31:53 -0800 Subject: [PATCH 36/78] Fix memory management bugs Signed-off-by: Leonardo de Moura --- src/math/realclosure/realclosure.cpp | 554 ++++++++++++++++----------- 1 file changed, 329 insertions(+), 225 deletions(-) diff --git a/src/math/realclosure/realclosure.cpp b/src/math/realclosure/realclosure.cpp index 1c11cc4a2..434f933b6 100644 --- a/src/math/realclosure/realclosure.cpp +++ b/src/math/realclosure/realclosure.cpp @@ -1273,9 +1273,10 @@ namespace realclosure { if (n == 2) { // we don't need a field extension for linear polynomials. numeral r; - value_ref neg_as_0(*this); - neg_as_0 = neg(as[0]); - set(r, div(neg_as_0, as[1])); + value_ref v(*this); + neg(as[0], v); + div(v, as[1], v); + set(r, v); roots.push_back(r); } else { @@ -1369,6 +1370,10 @@ namespace realclosure { return is_real(a.m_value); } + static void swap(mpbqi & a, mpbqi & b) { + realclosure::swap(a, b); + } + void mpq_to_mpbqi(mpq const & q, mpbqi & interval, unsigned k) { interval.set_lower_is_inf(false); interval.set_upper_is_inf(false); @@ -1489,7 +1494,9 @@ namespace realclosure { // create the polynomial p of the form x^k - a value_ref_buffer p(*this); - p.push_back(neg(a.m_value)); + value_ref neg_a(*this); + neg(a.m_value, neg_a); + p.push_back(neg_a); for (unsigned i = 0; i < k - 1; i++) p.push_back(0); p.push_back(one()); @@ -1500,15 +1507,17 @@ namespace realclosure { void power(numeral const & a, unsigned k, numeral & b) { unsigned mask = 1; value_ref power(*this); + value_ref _b(*this); power = a.m_value; - set(b, one()); + _b = one(); while (mask <= k) { checkpoint(); if (mask & k) - set(b, mul(b.m_value, power)); - power = mul(power, power); + mul(_b, power, _b); + mul(power, power, power); mask = mask << 1; } + set(b, _b); } /** @@ -1525,10 +1534,13 @@ namespace realclosure { */ void add(unsigned sz1, value * const * p1, unsigned sz2, value * const * p2, value_ref_buffer & r) { r.reset(); + value_ref a_i(*this); unsigned min = std::min(sz1, sz2); unsigned i = 0; - for (; i < min; i++) - r.push_back(add(p1[i], p2[i])); + for (; i < min; i++) { + add(p1[i], p2[i], a_i); + r.push_back(a_i); + } for (; i < sz1; i++) r.push_back(p1[i]); for (; i < sz2; i++) @@ -1543,7 +1555,9 @@ namespace realclosure { void add(unsigned sz, value * const * p, value * a, value_ref_buffer & r) { SASSERT(sz > 0); r.reset(); - r.push_back(add(p[0], a)); + value_ref a_0(*this); + add(p[0], a, a_0); + r.push_back(a_0); r.append(sz - 1, p + 1); adjust_size(r); } @@ -1553,14 +1567,19 @@ namespace realclosure { */ void sub(unsigned sz1, value * const * p1, unsigned sz2, value * const * p2, value_ref_buffer & r) { r.reset(); + value_ref a_i(*this); unsigned min = std::min(sz1, sz2); unsigned i = 0; - for (; i < min; i++) - r.push_back(sub(p1[i], p2[i])); + for (; i < min; i++) { + sub(p1[i], p2[i], a_i); + r.push_back(a_i); + } for (; i < sz1; i++) r.push_back(p1[i]); - for (; i < sz2; i++) - r.push_back(neg(p2[i])); + for (; i < sz2; i++) { + neg(p2[i], a_i); + r.push_back(a_i); + } SASSERT(r.size() == std::max(sz1, sz2)); adjust_size(r); } @@ -1571,7 +1590,9 @@ namespace realclosure { void sub(unsigned sz, value * const * p, value * a, value_ref_buffer & r) { SASSERT(sz > 0); r.reset(); - r.push_back(sub(p[0], a)); + value_ref a_0(*this); + sub(p[0], a, a_0); + r.push_back(a_0); r.append(sz - 1, p + 1); adjust_size(r); } @@ -1583,8 +1604,11 @@ namespace realclosure { r.reset(); if (a == 0) return; - for (unsigned i = 0; i < sz; i++) - r.push_back(mul(a, p[i])); + value_ref a_i(*this); + for (unsigned i = 0; i < sz; i++) { + mul(a, p[i], a_i); + r.push_back(a_i); + } } /** @@ -1605,8 +1629,9 @@ namespace realclosure { continue; for (unsigned j = 0; j < sz2; j++) { // r[i+j] <- r[i+j] + p1[i]*p2[j] - tmp = mul(p1[i], p2[j]); - r.set(i+j, add(r[i+j], tmp)); + mul(p1[i], p2[j], tmp); + add(r[i+j], tmp, tmp); + r.set(i+j, tmp); } } adjust_size(r); @@ -1619,9 +1644,12 @@ namespace realclosure { SASSERT(!is_zero(a)); if (is_rational_one(a)) return; + value_ref a_i(*this); unsigned sz = p.size(); - for (unsigned i = 0; i < sz; i++) - p.set(i, div(p[i], a)); + for (unsigned i = 0; i < sz; i++) { + div(p[i], a, a_i); + p.set(i, a_i); + } } /** @@ -1648,6 +1676,7 @@ namespace realclosure { value * b_n = p2[sz2-1]; SASSERT(!is_zero(b_n)); value_ref ratio(*this); + value_ref aux(*this); while (true) { checkpoint(); sz1 = r.size(); @@ -1656,13 +1685,15 @@ namespace realclosure { break; } unsigned m_n = sz1 - sz2; // m-n - ratio = div(r[sz1 - 1], b_n); + div(r[sz1 - 1], b_n, ratio); // q[m_n] <- q[m_n] + r[sz1 - 1]/b_n - q.set(m_n, add(q[m_n], ratio)); + add(q[m_n], ratio, aux); + q.set(m_n, aux); for (unsigned i = 0; i < sz2 - 1; i++) { // r[i + m_n] <- r[i + m_n] - ratio * p2[i] - ratio = mul(ratio, p2[i]); - r.set(i + m_n, sub(r[i + m_n], ratio)); + mul(ratio, p2[i], aux); + sub(r[i + m_n], aux, aux); + r.set(i + m_n, aux); } r.shrink(sz1 - 1); adjust_size(r); @@ -1683,8 +1714,10 @@ namespace realclosure { \brief r <- p/a */ void div(unsigned sz, value * const * p, value * a, value_ref_buffer & r) { + value_ref a_i(*this); for (unsigned i = 0; i < sz; i++) { - r.push_back(div(p[i], a)); + div(p[i], a, a_i); + r.push_back(a_i); } } @@ -1715,10 +1748,11 @@ namespace realclosure { return; } unsigned m_n = sz1 - sz2; - ratio = div(r[sz1 - 1], b_n); + div(r[sz1 - 1], b_n, ratio); for (unsigned i = 0; i < sz2 - 1; i++) { - new_a = mul(ratio, p2[i]); - r.set(i + m_n, sub(r[i + m_n], new_a)); + mul(ratio, p2[i], new_a); + sub(r[i + m_n], new_a, new_a); + r.set(i + m_n, new_a); } r.shrink(sz1 - 1); adjust_size(r); @@ -1730,29 +1764,36 @@ namespace realclosure { */ void neg(unsigned sz, value * const * p, value_ref_buffer & r) { r.reset(); - for (unsigned i = 0; i < sz; i++) - r.push_back(neg(p[i])); + value_ref a_i(*this); + for (unsigned i = 0; i < sz; i++) { + neg(p[i], a_i); + r.push_back(a_i); + } } /** \brief r <- -r */ void neg(value_ref_buffer & r) { + value_ref a_i(*this); unsigned sz = r.size(); - for (unsigned i = 0; i < sz; i++) - r.set(i, neg(r[i])); + for (unsigned i = 0; i < sz; i++) { + neg(r[i], a_i); + r.set(i, a_i); + } } /** \brief p <- -p */ void neg(polynomial & p) { + value_ref a_i(*this); unsigned sz = p.size(); for (unsigned i = 0; i < sz; i++) { - value * v = neg(p[i]); - inc_ref(v); + neg(p[i], a_i); + inc_ref(a_i.get()); dec_ref(p[i]); - p[i] = v; + p[i] = a_i.get(); } } @@ -1771,10 +1812,12 @@ namespace realclosure { void mk_monic(value_ref_buffer & p) { unsigned sz = p.size(); if (sz > 0) { + value_ref a_i(*this); SASSERT(p[sz-1] != 0); if (!is_rational_one(p[sz-1])) { for (unsigned i = 0; i < sz - 1; i++) { - p.set(i, div(p[i], p[sz-1])); + div(p[i], p[sz-1], a_i); + p.set(i, a_i); } p.set(sz-1, one()); } @@ -1825,9 +1868,10 @@ namespace realclosure { if (sz > 1) { for (unsigned i = 1; i < sz; i++) { mpq i_mpq(i); - value_ref i_value(*this); - i_value = mk_rational(i_mpq); - r.push_back(mul(i_value, p[i])); + value_ref a_i(*this); + a_i = mk_rational(i_mpq); + mul(a_i, p[i], a_i); + r.push_back(a_i); } adjust_size(r); } @@ -2297,6 +2341,11 @@ namespace realclosure { } } + bool determine_sign(value_ref & r) { + SASSERT(is_rational_function(r.get())); + return determine_sign(to_rational_function(r.get())); + } + /** \brief Set new_p1 and new_p2 using the following normalization rules: - new_p1 <- p1/p2[0]; new_p2 <- one IF sz2 == 1 @@ -2357,30 +2406,32 @@ namespace realclosure { \brief Create a new value using the a->ext(), and the given numerator and denominator. Use interval(a) + interval(b) as an initial approximation for the interval of the result, and invoke determine_sign() */ - value * mk_add_value(rational_function_value * a, value * b, unsigned num_sz, value * const * num, unsigned den_sz, value * const * den) { + void mk_add_value(rational_function_value * a, value * b, unsigned num_sz, value * const * num, unsigned den_sz, value * const * den, value_ref & r) { SASSERT(num_sz > 0 && den_sz > 0); if (num_sz == 1 && den_sz == 1) { // In this case, the normalization rules guarantee that den is one. SASSERT(is_rational_one(den[0])); - return num[0]; - } - rational_function_value * r = mk_rational_function_value_core(a->ext(), num_sz, num, den_sz, den); - bqim().add(interval(a), interval(b), r->interval()); - if (determine_sign(r)) { - SASSERT(!contains_zero(r->interval())); - return r; + r = num[0]; } else { - // The new value is 0 - del_rational_function(r); - return 0; + scoped_mpbqi ri(bqim()); + bqim().add(interval(a), interval(b), ri); + r = mk_rational_function_value_core(a->ext(), num_sz, num, den_sz, den); + swap(r->interval(), ri); + if (determine_sign(r)) { + SASSERT(!contains_zero(r->interval())); + } + else { + // The new value is 0 + r = 0; + } } } /** \brief Add a value of 'a' the form n/1 with b where rank(a) > rank(b) */ - value * add_p_v(rational_function_value * a, value * b) { + void add_p_v(rational_function_value * a, value * b, value_ref & r) { SASSERT(is_rational_one(a->den())); SASSERT(compare_rank(a, b) > 0); polynomial const & an = a->num(); @@ -2389,35 +2440,40 @@ namespace realclosure { value_ref_buffer new_num(*this); add(an.size(), an.c_ptr(), b, new_num); SASSERT(new_num.size() == an.size()); - return mk_add_value(a, b, new_num.size(), new_num.c_ptr(), one.size(), one.c_ptr()); + mk_add_value(a, b, new_num.size(), new_num.c_ptr(), one.size(), one.c_ptr(), r); } /** \brief Add a value 'a' of the form n/d with b where rank(a) > rank(b) */ - value * add_rf_v(rational_function_value * a, value * b) { + void add_rf_v(rational_function_value * a, value * b, value_ref & r) { value_ref_buffer b_ad(*this); value_ref_buffer num(*this); polynomial const & an = a->num(); polynomial const & ad = a->den(); - if (is_rational_one(ad)) - return add_p_v(a, b); - // b_ad <- b * ad - mul(b, ad.size(), ad.c_ptr(), b_ad); - // num <- a + b * ad - add(an.size(), an.c_ptr(), b_ad.size(), b_ad.c_ptr(), num); - if (num.empty()) - return 0; - value_ref_buffer new_num(*this); - value_ref_buffer new_den(*this); - normalize(num.size(), num.c_ptr(), ad.size(), ad.c_ptr(), new_num, new_den); - return mk_add_value(a, b, new_num.size(), new_num.c_ptr(), new_den.size(), new_den.c_ptr()); + if (is_rational_one(ad)) { + add_p_v(a, b, r); + } + else { + // b_ad <- b * ad + mul(b, ad.size(), ad.c_ptr(), b_ad); + // num <- a + b * ad + add(an.size(), an.c_ptr(), b_ad.size(), b_ad.c_ptr(), num); + if (num.empty()) + r = 0; + else { + value_ref_buffer new_num(*this); + value_ref_buffer new_den(*this); + normalize(num.size(), num.c_ptr(), ad.size(), ad.c_ptr(), new_num, new_den); + mk_add_value(a, b, new_num.size(), new_num.c_ptr(), new_den.size(), new_den.c_ptr(), r); + } + } } /** \brief Add values 'a' and 'b' of the form n/1 and rank(a) == rank(b) */ - value * add_p_p(rational_function_value * a, rational_function_value * b) { + void add_p_p(rational_function_value * a, rational_function_value * b, value_ref & r) { SASSERT(is_rational_one(a->den())); SASSERT(is_rational_one(b->den())); SASSERT(compare_rank(a, b) == 0); @@ -2427,109 +2483,120 @@ namespace realclosure { value_ref_buffer new_num(*this); add(an.size(), an.c_ptr(), bn.size(), bn.c_ptr(), new_num); if (new_num.empty()) - return 0; - return mk_add_value(a, b, new_num.size(), new_num.c_ptr(), one.size(), one.c_ptr()); + r = 0; + else + mk_add_value(a, b, new_num.size(), new_num.c_ptr(), one.size(), one.c_ptr(), r); } /** \brief Add values 'a' and 'b' of the form n/d and rank(a) == rank(b) */ - value * add_rf_rf(rational_function_value * a, rational_function_value * b) { + void add_rf_rf(rational_function_value * a, rational_function_value * b, value_ref & r) { SASSERT(compare_rank(a, b) == 0); polynomial const & an = a->num(); polynomial const & ad = a->den(); polynomial const & bn = b->num(); polynomial const & bd = b->den(); - if (is_rational_one(ad) && is_rational_one(bd)) - return add_p_p(a, b); - value_ref_buffer an_bd(*this); - value_ref_buffer bn_ad(*this); - mul(an.size(), an.c_ptr(), bd.size(), bd.c_ptr(), an_bd); - mul(bn.size(), bn.c_ptr(), ad.size(), ad.c_ptr(), bn_ad); - value_ref_buffer num(*this); - add(an_bd.size(), an_bd.c_ptr(), bn_ad.size(), bn_ad.c_ptr(), num); - if (num.empty()) - return 0; - value_ref_buffer den(*this); - mul(ad.size(), ad.c_ptr(), bd.size(), bd.c_ptr(), den); - value_ref_buffer new_num(*this); - value_ref_buffer new_den(*this); - normalize(num.size(), num.c_ptr(), den.size(), den.c_ptr(), new_num, new_den); - return mk_add_value(a, b, new_num.size(), new_num.c_ptr(), new_den.size(), new_den.c_ptr()); - } - - value * add(value * a, value * b) { - if (a == 0) - return b; - else if (b == 0) - return a; - else if (is_nz_rational(a) && is_nz_rational(b)) { - scoped_mpq r(qm()); - qm().add(to_mpq(a), to_mpq(b), r); - if (qm().is_zero(r)) - return 0; - else - return mk_rational(r); + if (is_rational_one(ad) && is_rational_one(bd)) { + add_p_p(a, b, r); } else { - switch (compare_rank(a, b)) { - case -1: return add_rf_v(to_rational_function(b), a); - case 0: return add_rf_rf(to_rational_function(a), to_rational_function(b)); - case 1: return add_rf_v(to_rational_function(a), b); - default: UNREACHABLE(); - return 0; + value_ref_buffer an_bd(*this); + value_ref_buffer bn_ad(*this); + mul(an.size(), an.c_ptr(), bd.size(), bd.c_ptr(), an_bd); + mul(bn.size(), bn.c_ptr(), ad.size(), ad.c_ptr(), bn_ad); + value_ref_buffer num(*this); + add(an_bd.size(), an_bd.c_ptr(), bn_ad.size(), bn_ad.c_ptr(), num); + if (num.empty()) { + r = 0; + } + else { + value_ref_buffer den(*this); + mul(ad.size(), ad.c_ptr(), bd.size(), bd.c_ptr(), den); + value_ref_buffer new_num(*this); + value_ref_buffer new_den(*this); + normalize(num.size(), num.c_ptr(), den.size(), den.c_ptr(), new_num, new_den); + mk_add_value(a, b, new_num.size(), new_num.c_ptr(), new_den.size(), new_den.c_ptr(), r); } } } - value * sub(value * a, value * b) { - if (a == 0) - return neg(b); - else if (b == 0) - return a; + void add(value * a, value * b, value_ref & r) { + if (a == 0) { + r = b; + } + else if (b == 0) { + r = a; + } else if (is_nz_rational(a) && is_nz_rational(b)) { - scoped_mpq r(qm()); - qm().sub(to_mpq(a), to_mpq(b), r); - if (qm().is_zero(r)) - return 0; + scoped_mpq v(qm()); + qm().add(to_mpq(a), to_mpq(b), v); + if (qm().is_zero(v)) + r = 0; else - return mk_rational(r); + r = mk_rational(v); + } + else { + switch (compare_rank(a, b)) { + case -1: add_rf_v(to_rational_function(b), a, r); break; + case 0: add_rf_rf(to_rational_function(a), to_rational_function(b), r); break; + case 1: add_rf_v(to_rational_function(a), b, r); break; + default: UNREACHABLE(); + } + } + } + + void sub(value * a, value * b, value_ref & r) { + if (a == 0) { + neg(b, r); + } + else if (b == 0) { + r = a; + } + else if (is_nz_rational(a) && is_nz_rational(b)) { + scoped_mpq v(qm()); + qm().sub(to_mpq(a), to_mpq(b), v); + if (qm().is_zero(v)) + r = 0; + else + r = mk_rational(v); } else { value_ref neg_b(*this); - neg_b = neg(b); + neg(b, neg_b); switch (compare_rank(a, neg_b)) { - case -1: return add_rf_v(to_rational_function(neg_b), a); - case 0: return add_rf_rf(to_rational_function(a), to_rational_function(neg_b)); - case 1: return add_rf_v(to_rational_function(a), b); + case -1: add_rf_v(to_rational_function(neg_b), a, r); break; + case 0: add_rf_rf(to_rational_function(a), to_rational_function(neg_b), r); break; + case 1: add_rf_v(to_rational_function(a), neg_b, r); break; default: UNREACHABLE(); - return 0; } } } - value * neg_rf(rational_function_value * a) { + void neg_rf(rational_function_value * a, value_ref & r) { polynomial const & an = a->num(); polynomial const & ad = a->den(); value_ref_buffer new_num(*this); neg(an.size(), an.c_ptr(), new_num); - rational_function_value * r = mk_rational_function_value_core(a->ext(), new_num.size(), new_num.c_ptr(), ad.size(), ad.c_ptr()); - bqim().neg(interval(a), r->interval()); + scoped_mpbqi ri(bqim()); + bqim().neg(interval(a), ri); + r = mk_rational_function_value_core(a->ext(), new_num.size(), new_num.c_ptr(), ad.size(), ad.c_ptr()); + swap(r->interval(), ri); SASSERT(!contains_zero(r->interval())); - return r; } - value * neg(value * a) { - if (a == 0) - return 0; + void neg(value * a, value_ref & r) { + if (a == 0) { + r = 0; + } else if (is_nz_rational(a)) { - scoped_mpq r(qm()); - qm().set(r, to_mpq(a)); - qm().neg(r); - return mk_rational(r); + scoped_mpq v(qm()); + qm().set(v, to_mpq(a)); + qm().neg(v); + r = mk_rational(v); } else { - return neg_rf(to_rational_function(a)); + neg_rf(to_rational_function(a), r); } } @@ -2537,30 +2604,32 @@ namespace realclosure { \brief Create a new value using the a->ext(), and the given numerator and denominator. Use interval(a) * interval(b) as an initial approximation for the interval of the result, and invoke determine_sign() */ - value * mk_mul_value(rational_function_value * a, value * b, unsigned num_sz, value * const * num, unsigned den_sz, value * const * den) { + void mk_mul_value(rational_function_value * a, value * b, unsigned num_sz, value * const * num, unsigned den_sz, value * const * den, value_ref & r) { SASSERT(num_sz > 0 && den_sz > 0); if (num_sz == 1 && den_sz == 1) { // In this case, the normalization rules guarantee that den is one. SASSERT(is_rational_one(den[0])); - return num[0]; - } - rational_function_value * r = mk_rational_function_value_core(a->ext(), num_sz, num, den_sz, den); - bqim().mul(interval(a), interval(b), r->interval()); - if (determine_sign(r)) { - SASSERT(!contains_zero(r->interval())); - return r; + r = num[0]; } else { - // The new value is 0 - del_rational_function(r); - return 0; + scoped_mpbqi ri(bqim()); + bqim().mul(interval(a), interval(b), ri); + r = mk_rational_function_value_core(a->ext(), num_sz, num, den_sz, den); + swap(ri, r->interval()); + if (determine_sign(r)) { + SASSERT(!contains_zero(r->interval())); + } + else { + // The new value is 0 + r = 0; + } } } /** \brief Multiply a value of 'a' the form n/1 with b where rank(a) > rank(b) */ - value * mul_p_v(rational_function_value * a, value * b) { + void mul_p_v(rational_function_value * a, value * b, value_ref & r) { SASSERT(is_rational_one(a->den())); SASSERT(b != 0); SASSERT(compare_rank(a, b) > 0); @@ -2570,31 +2639,34 @@ namespace realclosure { value_ref_buffer new_num(*this); mul(b, an.size(), an.c_ptr(), new_num); SASSERT(new_num.size() == an.size()); - return mk_mul_value(a, b, new_num.size(), new_num.c_ptr(), one.size(), one.c_ptr()); + mk_mul_value(a, b, new_num.size(), new_num.c_ptr(), one.size(), one.c_ptr(), r); } /** \brief Multiply a value 'a' of the form n/d with b where rank(a) > rank(b) */ - value * mul_rf_v(rational_function_value * a, value * b) { + void mul_rf_v(rational_function_value * a, value * b, value_ref & r) { polynomial const & an = a->num(); polynomial const & ad = a->den(); - if (is_rational_one(ad)) - return mul_p_v(a, b); - value_ref_buffer num(*this); - // num <- b * an - mul(b, an.size(), an.c_ptr(), num); - SASSERT(num.size() == an.size()); - value_ref_buffer new_num(*this); - value_ref_buffer new_den(*this); - normalize(num.size(), num.c_ptr(), ad.size(), ad.c_ptr(), new_num, new_den); - return mk_mul_value(a, b, new_num.size(), new_num.c_ptr(), new_den.size(), new_den.c_ptr()); + if (is_rational_one(ad)) { + mul_p_v(a, b, r); + } + else { + value_ref_buffer num(*this); + // num <- b * an + mul(b, an.size(), an.c_ptr(), num); + SASSERT(num.size() == an.size()); + value_ref_buffer new_num(*this); + value_ref_buffer new_den(*this); + normalize(num.size(), num.c_ptr(), ad.size(), ad.c_ptr(), new_num, new_den); + mk_mul_value(a, b, new_num.size(), new_num.c_ptr(), new_den.size(), new_den.c_ptr(), r); + } } /** \brief Multiply values 'a' and 'b' of the form n/1 and rank(a) == rank(b) */ - value * mul_p_p(rational_function_value * a, rational_function_value * b) { + void mul_p_p(rational_function_value * a, rational_function_value * b, value_ref & r) { SASSERT(is_rational_one(a->den())); SASSERT(is_rational_one(b->den())); SASSERT(compare_rank(a, b) == 0); @@ -2604,107 +2676,119 @@ namespace realclosure { value_ref_buffer new_num(*this); mul(an.size(), an.c_ptr(), bn.size(), bn.c_ptr(), new_num); SASSERT(!new_num.empty()); - return mk_mul_value(a, b, new_num.size(), new_num.c_ptr(), one.size(), one.c_ptr()); + mk_mul_value(a, b, new_num.size(), new_num.c_ptr(), one.size(), one.c_ptr(), r); } /** \brief Multiply values 'a' and 'b' of the form n/d and rank(a) == rank(b) */ - value * mul_rf_rf(rational_function_value * a, rational_function_value * b) { + void mul_rf_rf(rational_function_value * a, rational_function_value * b, value_ref & r) { SASSERT(compare_rank(a, b) == 0); polynomial const & an = a->num(); polynomial const & ad = a->den(); polynomial const & bn = b->num(); polynomial const & bd = b->den(); - if (is_rational_one(ad) && is_rational_one(bd)) - return mul_p_p(a, b); - value_ref_buffer num(*this); - value_ref_buffer den(*this); - mul(an.size(), an.c_ptr(), bn.size(), bn.c_ptr(), num); - mul(ad.size(), ad.c_ptr(), bd.size(), bd.c_ptr(), den); - SASSERT(!num.empty()); SASSERT(!den.empty()); - value_ref_buffer new_num(*this); - value_ref_buffer new_den(*this); - normalize(num.size(), num.c_ptr(), den.size(), den.c_ptr(), new_num, new_den); - return mk_mul_value(a, b, new_num.size(), new_num.c_ptr(), new_den.size(), new_den.c_ptr()); + if (is_rational_one(ad) && is_rational_one(bd)) { + mul_p_p(a, b, r); + } + else { + value_ref_buffer num(*this); + value_ref_buffer den(*this); + mul(an.size(), an.c_ptr(), bn.size(), bn.c_ptr(), num); + mul(ad.size(), ad.c_ptr(), bd.size(), bd.c_ptr(), den); + SASSERT(!num.empty()); SASSERT(!den.empty()); + value_ref_buffer new_num(*this); + value_ref_buffer new_den(*this); + normalize(num.size(), num.c_ptr(), den.size(), den.c_ptr(), new_num, new_den); + mk_mul_value(a, b, new_num.size(), new_num.c_ptr(), new_den.size(), new_den.c_ptr(), r); + } } - value * mul(value * a, value * b) { - if (a == 0 || b == 0) - return 0; - else if (is_rational_one(a)) - return b; - else if (is_rational_one(b)) - return a; - else if (is_rational_minus_one(a)) - return neg(b); - else if (is_rational_minus_one(b)) - return neg(a); + void mul(value * a, value * b, value_ref & r) { + if (a == 0 || b == 0) { + r = 0; + } + else if (is_rational_one(a)) { + r = b; + } + else if (is_rational_one(b)) { + r = a; + } + else if (is_rational_minus_one(a)) { + neg(b, r); + } + else if (is_rational_minus_one(b)) { + neg(a, r); + } else if (is_nz_rational(a) && is_nz_rational(b)) { - scoped_mpq r(qm()); - qm().mul(to_mpq(a), to_mpq(b), r); - return mk_rational(r); + scoped_mpq v(qm()); + qm().mul(to_mpq(a), to_mpq(b), v); + r = mk_rational(v); } else { switch (compare_rank(a, b)) { - case -1: return mul_rf_v(to_rational_function(b), a); - case 0: return mul_rf_rf(to_rational_function(a), to_rational_function(b)); - case 1: return mul_rf_v(to_rational_function(a), b); + case -1: mul_rf_v(to_rational_function(b), a, r); break; + case 0: mul_rf_rf(to_rational_function(a), to_rational_function(b), r); break; + case 1: mul_rf_v(to_rational_function(a), b, r); break; default: UNREACHABLE(); - return 0; } } } - value * div(value * a, value * b) { - if (a == 0) - return 0; - else if (b == 0) + void div(value * a, value * b, value_ref & r) { + if (a == 0) { + r = 0; + } + else if (b == 0) { throw exception("division by zero"); - else if (is_rational_one(b)) - return a; - else if (is_rational_one(a)) - return inv(b); - else if (is_rational_minus_one(b)) - return neg(a); + } + else if (is_rational_one(b)) { + r = a; + } + else if (is_rational_one(a)) { + inv(b, r); + } + else if (is_rational_minus_one(b)) { + neg(a, r); + } else if (is_nz_rational(a) && is_nz_rational(b)) { - scoped_mpq r(qm()); - qm().div(to_mpq(a), to_mpq(b), r); - return mk_rational(r); + scoped_mpq v(qm()); + qm().div(to_mpq(a), to_mpq(b), v); + r = mk_rational(v); } else { value_ref inv_b(*this); - inv_b = inv(b); + inv(b, inv_b); switch (compare_rank(a, inv_b)) { - case -1: return mul_rf_v(to_rational_function(inv_b), a); - case 0: return mul_rf_rf(to_rational_function(a), to_rational_function(inv_b)); - case 1: return mul_rf_v(to_rational_function(a), inv_b); + case -1: mul_rf_v(to_rational_function(inv_b), a, r); break; + case 0: mul_rf_rf(to_rational_function(a), to_rational_function(inv_b), r); break; + case 1: mul_rf_v(to_rational_function(a), inv_b, r); break; default: UNREACHABLE(); - return 0; } } } - value * inv_rf(rational_function_value * a) { + void inv_rf(rational_function_value * a, value_ref & r) { polynomial const & an = a->num(); polynomial const & ad = a->den(); - rational_function_value * r = mk_rational_function_value_core(a->ext(), ad.size(), ad.c_ptr(), an.size(), an.c_ptr()); - bqim().inv(interval(a), r->interval()); + scoped_mpbqi ri(bqim()); + bqim().inv(interval(a), ri); + r = mk_rational_function_value_core(a->ext(), ad.size(), ad.c_ptr(), an.size(), an.c_ptr()); + swap(r->interval(), ri); SASSERT(!contains_zero(r->interval())); - return r; } - value * inv(value * a) { + void inv(value * a, value_ref & r) { if (a == 0) { throw exception("division by zero"); } if (is_nz_rational(a)) { - scoped_mpq r(qm()); - qm().inv(to_mpq(a), r); - return mk_rational(r); + scoped_mpq v(qm()); + qm().inv(to_mpq(a), v); + r = mk_rational(v); } else { - return inv_rf(to_rational_function(a)); + inv_rf(to_rational_function(a), r); } } @@ -2714,36 +2798,56 @@ namespace realclosure { n.m_value = v; } + void set(numeral & n, value_ref const & v) { + set(n, v.get()); + } + void neg(numeral & a) { - set(a, neg(a.m_value)); + value_ref r(*this); + neg(a.m_value, r); + set(a, r); } void neg(numeral const & a, numeral & b) { - set(b, neg(a.m_value)); + value_ref r(*this); + neg(a.m_value, r); + set(b, r); } void inv(numeral & a) { - set(a, inv(a.m_value)); + value_ref r(*this); + inv(a.m_value, r); + set(a, r); } void inv(numeral const & a, numeral & b) { - set(b, inv(a.m_value)); + value_ref r(*this); + inv(a.m_value, r); + set(b, r); } void add(numeral const & a, numeral const & b, numeral & c) { - set(c, add(a.m_value, b.m_value)); + value_ref r(*this); + add(a.m_value, b.m_value, r); + set(c, r); } void sub(numeral const & a, numeral const & b, numeral & c) { - set(c, sub(a.m_value, b.m_value)); + value_ref r(*this); + sub(a.m_value, b.m_value, r); + set(c, r); } void mul(numeral const & a, numeral const & b, numeral & c) { - set(c, mul(a.m_value, b.m_value)); + value_ref r(*this); + mul(a.m_value, b.m_value, r); + set(c, r); } void div(numeral const & a, numeral const & b, numeral & c) { - set(c, div(a.m_value, b.m_value)); + value_ref r(*this); + div(a.m_value, b.m_value, r); + set(c, r); } int compare(value * a, value * b) { @@ -2761,7 +2865,7 @@ namespace realclosure { return 1; else { value_ref diff(*this); - diff = sub(a, b); + sub(a, b, diff); return sign(diff); } } From ff809db16d1f483efd32c234e0f22f4f14786192 Mon Sep 17 00:00:00 2001 From: Leonardo de Moura Date: Tue, 8 Jan 2013 15:40:19 -0800 Subject: [PATCH 37/78] Add get_int and get_uint to mpz_manager Signed-off-by: Leonardo de Moura --- src/util/mpz.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/util/mpz.h b/src/util/mpz.h index c7c1a880f..3582f0ad1 100644 --- a/src/util/mpz.h +++ b/src/util/mpz.h @@ -676,6 +676,14 @@ public: int64 get_int64(mpz const & a) const; + bool is_uint(mpz const & a) const { return is_uint64(a) && get_uint64(a) < UINT_MAX; } + + unsigned get_uint(mpz const & a) const { SASSERT(is_uint(a)); return static_cast(get_uint64(a)); } + + bool is_int(mpz const & a) const { return is_int64(a) && INT_MIN < get_int64(a) && get_int64(a) < INT_MAX; } + + int get_int(mpz const & a) const { SASSERT(is_int(a)); return static_cast(get_int64(a)); } + double get_double(mpz const & a) const; std::string to_string(mpz const & a) const; From 9c8b428ffbffb984538e3417d26a5a3cbf2dd6a7 Mon Sep 17 00:00:00 2001 From: Leonardo de Moura Date: Tue, 8 Jan 2013 17:58:34 -0800 Subject: [PATCH 38/78] Add matrix operations needed for implementing non-naive sign determination Signed-off-by: Leonardo de Moura --- src/math/realclosure/mpz_matrix.cpp | 409 ++++++++++++++++++++++++++++ src/math/realclosure/mpz_matrix.h | 151 ++++++++++ src/test/rcf.cpp | 86 +++++- 3 files changed, 644 insertions(+), 2 deletions(-) create mode 100644 src/math/realclosure/mpz_matrix.cpp create mode 100644 src/math/realclosure/mpz_matrix.h diff --git a/src/math/realclosure/mpz_matrix.cpp b/src/math/realclosure/mpz_matrix.cpp new file mode 100644 index 000000000..fb9327a9e --- /dev/null +++ b/src/math/realclosure/mpz_matrix.cpp @@ -0,0 +1,409 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + mpz_matrix.h + +Abstract: + + Matrix with integer coefficients. This is not a general purpose + module for handling matrices with integer coefficients. Instead, + it is a custom package that only contains operations needed to + implement Sign Determination (Algorithm 10.11) in the Book: + "Algorithms in real algebraic geometry", Basu, Pollack, Roy + + Design choices: + - Dense representation. The matrices in Alg 10.11 are small and dense. + - Integer coefficients instead of rational coefficients (it only complicates the solver a little bit). + Remark: in Algorithm 10.11, the coefficients of the input matrices are always in {-1, 0, 1}. + During solving, bigger coefficients are produced, but they are usually very small. It may be + an overkill to use mpz instead of int. We use mpz just to be safe. + Remark: We do not use rational arithmetic. The solver is slightly more complicated with integers, but is saves space. + +Author: + + Leonardo (leonardo) 2013-01-07 + +Notes: + +--*/ +#include"mpz_matrix.h" +#include"buffer.h" + +mpz_matrix_manager::mpz_matrix_manager(unsynch_mpz_manager & nm, small_object_allocator & a): + m_nm(nm), + m_allocator(a) { +} + +mpz_matrix_manager::~mpz_matrix_manager() { +} + +void mpz_matrix_manager::mk(unsigned m, unsigned n, mpz_matrix & A) { + SASSERT(m > 0 && n > 0); + del(A); + A.m = m; + A.n = n; + A.a_ij = new (m_allocator) mpz[m*n]; +} + +void mpz_matrix_manager::del(mpz_matrix & A) { + if (A.a_ij != 0) { + for (unsigned i = 0; i < A.m; i++) + for (unsigned j = 0; j < A.n; j++) + nm().del(A(i,j)); + unsigned sz = sizeof(mpz) * A.m * A.n; + m_allocator.deallocate(sz, A.a_ij); + A.m = 0; + A.n = 0; + A.a_ij = 0; + } +} + +void mpz_matrix_manager::set(mpz_matrix & A, mpz_matrix const & B) { + if (&A == &B) + return; + if (A.m != B.m || A.n != B.n) { + del(A); + mk(B.m, B.n, A); + } + SASSERT(A.m == B.m && A.n == B.n); + for (unsigned i = 0; i < B.m; i++) + for (unsigned j = 0; j < B.n; j++) + nm().set(A(i, j), B(i, j)); +} + +void mpz_matrix_manager::tensor_product(mpz_matrix const & A, mpz_matrix const & B, mpz_matrix & C) { + scoped_mpz_matrix _C(*this); + mk(A.m * B.m, A.n * B.n, _C); + for (unsigned i = 0; i < _C.m(); i++) + for (unsigned j = 0; j < _C.n(); j++) + nm().mul(A(i / B.m, j / B.n), + B(i % B.m, j % B.n), + _C(i, j)); + C.swap(_C); +} + +void mpz_matrix_manager::swap_rows(mpz_matrix & A, unsigned i, unsigned j) { + if (i != j) { + for (unsigned k = 0; k < A.n; k++) + ::swap(A(i, k), A(j, k)); + } +} + +// If b_i == 0, then method just divides the given row by its GCD +// If b_i != 0 +// If the GCD of the row divides *b_i +// divide the row and *b_i by the GCD +// Else +// If int_solver == true ==> return false (the system is unsolvable) +bool mpz_matrix_manager::normalize_row(mpz * A_i, unsigned n, mpz * b_i, bool int_solver) { + scoped_mpz g(nm()); + bool first = true; + for (unsigned j = 0; j < n; j++) { + if (nm().is_zero(A_i[j])) + continue; + if (first) { + nm().set(g, A_i[j]); + nm().abs(g); + first = false; + } + else { + nm().gcd(g, A_i[j], g); + } + if (nm().is_one(g)) + return true; + } + if (first) + return true; // zero row + if (!nm().is_one(g)) { + if (b_i) { + if (nm().divides(g, *b_i)) { + for (unsigned j = 0; j < n; j++) { + nm().div(A_i[j], g, A_i[j]); + } + nm().div(*b_i, g, *b_i); + } + else { + if (int_solver) + return false; // system does not have an integer solution + } + } + else { + for (unsigned j = 0; j < n; j++) { + nm().div(A_i[j], g, A_i[j]); + } + } + } + return true; +} + +/* + Given a matrix of the form + + k2 + | + V + X X ... X X ... X + 0 X ... X X ... X + ... ... X X ... X +k1=> 0 0 ... 0 X ... X + 0 0 ... 0 X ... X + ... ... 0 X ... X + 0 0 ... 0 X ... X + + It will "zero" the elements a_{k1+1, k2} ... a_{m, k2} by addining multiples of the row k1 to multiples of the + rows k1+1, ..., m + + The resultant matrix will look like + + k2 + | + V + X X ... X X ... X + 0 X ... X X ... X + ... ... X X ... X +k1=> 0 0 ... 0 X ... X + 0 0 ... 0 0 ... X + ... ... 0 0 ... X + 0 0 ... 0 0 ... X + + + If b != 0, then the transformations are also applied to b. + If int_solver == true and b != 0, then the method returns false if when + performing the transformations it detected that it is impossible to + solve the integer system of equations A x = b. +*/ +bool mpz_matrix_manager::eliminate(mpz_matrix & A, mpz * b, unsigned k1, unsigned k2, bool int_solver) { + // check if first k2-1 positions of row k1 are 0 + DEBUG_CODE(for (unsigned j = 0; j < k2; j++) { SASSERT(nm().is_zero(A(k1, j))); }); + mpz & a_kk = A(k1, k2); + SASSERT(!nm().is_zero(a_kk)); + scoped_mpz t1(nm()), t2(nm()); + scoped_mpz a_ik_prime(nm()), a_kk_prime(nm()), lcm_a_kk_a_ik(nm()); + // for all rows below pivot + for (unsigned i = k1+1; i < A.m; i++) { + // check if first k-1 positions of row k are 0 + DEBUG_CODE(for (unsigned j = 0; j < k2; j++) { SASSERT(nm().is_zero(A(i, j))); }); + mpz & a_ik = A(i, k2); + if (!nm().is_zero(a_ik)) { + // a_ik' = lcm(a_kk, a_ik)/a_kk + // a_kk' = lcm(a_kk, a_ik)/a_ik + nm().lcm(a_kk, a_ik, lcm_a_kk_a_ik); + nm().div(lcm_a_kk_a_ik, a_kk, a_ik_prime); + nm().div(lcm_a_kk_a_ik, a_ik, a_kk_prime); + for (unsigned j = k2+1; j < A.n; j++) { + // a_ij <- a_kk' * a_ij - a_ik' * a_kj + nm().mul(a_ik_prime, A(k1, j), t1); + nm().mul(a_kk_prime, A(i, j), t2); + nm().sub(t2, t1, A(i, j)); + } + if (b) { + // b_i <- a_kk' * b_i - a_ik' * b_k + nm().mul(a_ik_prime, b[k1], t1); + nm().mul(a_kk_prime, b[i], t2); + nm().sub(t2, t1, b[i]); + } + // a_ik <- 0 + nm().set(A(i, k2), 0); + // normalize row i + if (!normalize_row(A.row(i), A.n, b ? &(b[i]) : 0, int_solver)) + return false; + } + SASSERT(nm().is_zero(A(i, k2))); + } + return true; +} + +bool mpz_matrix_manager::solve_core(mpz_matrix const & _A, mpz * b, bool int_solver) { + SASSERT(_A.n == _A.m); + scoped_mpz_matrix A(*this); + set(A, _A); + for (unsigned k = 0; k < A.m(); k++) { + TRACE("mpz_matrix", + tout << "k: " << k << "\n" << A; + tout << "b:"; + for (unsigned i = 0; i < A.m(); i++) { + tout << " "; + nm().display(tout, b[i]); + } + tout << "\n";); + // find pivot + unsigned i = k; + for (; i < A.m(); i++) { + if (!nm().is_zero(A(i, k))) + break; + } + if (i == A.m()) + return false; // matrix is singular + // swap rows k and i + swap_rows(A, k, i); + swap(b[k], b[i]); + // + if (!eliminate(A, b, k, k, int_solver)) + return false; + } + // Back substitution + unsigned k = A.m(); + while (k > 0) { + --k; + DEBUG_CODE(for (unsigned j = 0; j < A.n(); j++) { SASSERT(j == k || nm().is_zero(A(k, j))); }); + SASSERT(!nm().is_zero(A(k, k))); + if (nm().divides(A(k, k), b[k])) { + nm().div(b[k], A(k, k), b[k]); + nm().set(A(k, k), 1); + } + else { + if (int_solver) + return false; // no integer solution + if (nm().is_neg(A(k, k))) { + nm().neg(A(k, k)); + nm().neg(b[k]); + } + } + if (!int_solver) { + // REMARK: + // For the sign determination algorithm, we only use int_solver == true. + // + // TODO: implement backward substitution when int_solver == false + // In this case, A(k, k) may not be 1. + NOT_IMPLEMENTED_YET(); + } + SASSERT(!int_solver || nm().is_one(A(k, k))); + // back substitute + unsigned i = k; + while (i > 0) { + --i; + // Assuming int_solver == true + SASSERT(int_solver); // See comment above + // b_i <- b_i - a_ik * b_k + nm().submul(b[i], A(i, k), b[k], b[i]); + nm().set(A(i, k), 0); + } + } + return true; +} + +bool mpz_matrix_manager::solve(mpz_matrix const & A, mpz * b, mpz const * c) { + for (unsigned i = 0; i < A.n; i++) + nm().set(b[i], c[i]); + return solve_core(A, b, true); +} + +bool mpz_matrix_manager::solve(mpz_matrix const & A, int * b, int const * c) { + scoped_mpz_matrix _b(*this); + mk(A.n, 1, _b); + for (unsigned i = 0; i < A.n; i++) + nm().set(_b(i,0), c[i]); + bool r = solve_core(A, _b.A.a_ij, true); + if (r) { + for (unsigned i = 0; i < A.n; i++) + b[i] = _b.get_int(i, 0); + } + return r; +} + +void mpz_matrix_manager::filter_cols(mpz_matrix const & A, unsigned num_cols, unsigned const * cols, mpz_matrix & B) { + SASSERT(num_cols <= A.n); + // Check pre-condition: + // - All elements in cols are smaller than A.n + // - cols is sorted + // - cols does not contain repeated elements + DEBUG_CODE({ + for (unsigned i = 0; i < num_cols; i ++) { + SASSERT(cols[i] < A.n); + SASSERT(i == 0 || cols[i-1] < cols[i]); + } + }); + if (num_cols == A.n) { + // keep everything + set(B, A); + } + else { + SASSERT(num_cols < A.n); + scoped_mpz_matrix C(*this); + mk(A.m, num_cols, C); + for (unsigned i = 0; i < A.m; i++) + for (unsigned j = 0; j < num_cols; j++) + nm().set(C(i, j), A(i, cols[j])); + B.swap(C); + } +} + +void mpz_matrix_manager::permute_rows(mpz_matrix const & A, unsigned const * p, mpz_matrix & B) { + // Check if p is really a permutation + DEBUG_CODE({ + buffer seen; + seen.resize(A.m, false); + for (unsigned i = 0; i < A.m; i++) { + SASSERT(p[i] < A.m); + SASSERT(!seen[p[i]]); + seen[p[i]] = true; + } + }); + scoped_mpz_matrix C(*this); + mk(A.m, A.n, C); + for (unsigned i = 0; i < A.m; i++) + for (unsigned j = 0; j < A.n; j++) + nm().set(C(i, j), A(p[i], j)); + B.swap(C); +} + +void mpz_matrix_manager::linear_independent_rows(mpz_matrix const & _A, unsigned_vector & r) { + r.reset(); + scoped_mpz_matrix A(*this); + scoped_mpz g(nm()); + scoped_mpz t1(nm()), t2(nm()); + scoped_mpz a_ik_prime(nm()), a_kk_prime(nm()), lcm_a_kk_a_ik(nm()); + set(A, _A); + sbuffer rows; + rows.resize(A.m(), 0); + for (unsigned i = 0; i < A.m(); i++) + rows[i] = i; + for (unsigned k1 = 0, k2 = 0; k1 < A.m(); k1++) { + TRACE("mpz_matrix", tout << "k1: " << k1 << ", k2: " << k2 << "\n" << A;); + // find pivot + unsigned pivot = UINT_MAX; + for (unsigned i = k1; i < A.m(); i++) { + if (!nm().is_zero(A(i, k2))) { + if (pivot == UINT_MAX) { + pivot = i; + } + else { + if (rows[i] < rows[pivot]) + pivot = i; + } + } + } + if (pivot == UINT_MAX) + continue; + // swap rows k and pivot + swap_rows(A, k1, pivot); + std::swap(rows[k1], rows[pivot]); + // + r.push_back(rows[k1]); + if (r.size() >= A.n()) + break; + eliminate(A, 0, k1, k2, false); + k2++; + } + std::sort(r.begin(), r.end()); +} + +void mpz_matrix_manager::display(std::ostream & out, mpz_matrix const & A, unsigned cell_width) const { + out << A.m << " x " << A.n << " Matrix\n"; + for (unsigned i = 0; i < A.m; i++) { + for (unsigned j = 0; j < A.n; j++) { + if (j > 0) + out << " "; + std::string s = nm().to_string(A(i, j)); + if (s.size() < cell_width) { + unsigned space = cell_width - s.size(); + for (unsigned k = 0; k < space; k++) + out << " "; + } + out << s; + } + out << "\n"; + } +} diff --git a/src/math/realclosure/mpz_matrix.h b/src/math/realclosure/mpz_matrix.h new file mode 100644 index 000000000..9ce9aaac7 --- /dev/null +++ b/src/math/realclosure/mpz_matrix.h @@ -0,0 +1,151 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + mpz_matrix.h + +Abstract: + + Matrix with integer coefficients. This is not a general purpose + module for handling matrices with integer coefficients. Instead, + it is a custom package that only contains operations needed to + implement Sign Determination (Algorithm 10.11) in the Book: + "Algorithms in real algebraic geometry", Basu, Pollack, Roy + + Design choices: + - Dense representation. The matrices in Alg 10.11 are small and dense. + - Integer coefficients instead of rational coefficients (it only complicates the solver a little bit). + Remark: in Algorithm 10.11, the coefficients of the input matrices are always in {-1, 0, 1}. + During solving, bigger coefficients are produced, but they are usually very small. It may be + an overkill to use mpz instead of int. We use mpz just to be safe. + Remark: We do not use rational arithmetic. The solver is slightly more complicated with integers, but is saves space. + +Author: + + Leonardo (leonardo) 2013-01-07 + +Notes: + +--*/ +#ifndef _MPZ_MATRIX_H_ +#define _MPZ_MATRIX_H_ + +#include"mpz.h" + +/** + \brief A mxn matrix. + Remark: Algorithm 10.11 only uses square matrices, but supporting + arbitrary matrices does not increase the complexity of this module. +*/ +class mpz_matrix { + friend class mpz_matrix_manager; + friend class scoped_mpz_matrix; + unsigned m; + unsigned n; + mpz * a_ij; +public: + mpz_matrix():m(0), n(0), a_ij(0) {} + mpz const & operator()(unsigned i, unsigned j) const { + SASSERT(i < m); + SASSERT(j < n); + return a_ij[i*n + j]; } + mpz & operator()(unsigned i, unsigned j) { SASSERT(i < m); SASSERT(j < n); return a_ij[i*n + j]; } + void swap(mpz_matrix & B) { std::swap(m, B.m); std::swap(n, B.n); std::swap(a_ij, B.a_ij); } + mpz * row(unsigned i) const { SASSERT(i < m); return a_ij + i*n; } +}; + +class mpz_matrix_manager { + unsynch_mpz_manager & m_nm; + small_object_allocator & m_allocator; + static void swap_rows(mpz_matrix & A, unsigned i, unsigned j); + bool normalize_row(mpz * A_i, unsigned n, mpz * b_i, bool int_solver); + bool eliminate(mpz_matrix & A, mpz * b, unsigned k1, unsigned k2, bool int_solver); + bool solve_core(mpz_matrix const & A, mpz * b, bool int_solver); +public: + mpz_matrix_manager(unsynch_mpz_manager & nm, small_object_allocator & a); + ~mpz_matrix_manager(); + unsynch_mpz_manager & nm() const { return m_nm; } + void mk(unsigned m, unsigned n, mpz_matrix & A); + void del(mpz_matrix & r); + void set(mpz_matrix & A, mpz_matrix const & B); + void tensor_product(mpz_matrix const & A, mpz_matrix const & B, mpz_matrix & C); + /** + \brief Solve A*b = c + + Return false if the system does not have an integer solution. + + \pre A is a square matrix + \pre b and c are vectors of size A.n (== A.m) + */ + bool solve(mpz_matrix const & A, mpz * b, mpz const * c); + /** + \brief Solve A*b = c + + Return false if the system does not have an integer solution. + + \pre A is a square matrix + \pre b and c are vectors of size A.n (== A.m) + */ + bool solve(mpz_matrix const & A, int * b, int const * c); + /** + \brief Store in B that contains the subset cols of columns of A. + + \pre num_cols <= A.n + \pre Forall i < num_cols, cols[i] < A.n + \pre Forall 0 < i < num_cols, cols[i-1] < cols[i] + */ + void filter_cols(mpz_matrix const & A, unsigned num_cols, unsigned const * cols, mpz_matrix & B); + /** + \brief Store in B the matrix obtained after applying the given permutation to the rows of A. + */ + void permute_rows(mpz_matrix const & A, unsigned const * p, mpz_matrix & B); + /** + \brief Store in r the row (ids) of A that are linear independent. + + \remark If there is an option between rows i and j, + this method will give preference to the row that occurs first. + */ + void linear_independent_rows(mpz_matrix const & A, unsigned_vector & r); + + // method for debugging purposes + void display(std::ostream & out, mpz_matrix const & A, unsigned cell_width=4) const; +}; + +class scoped_mpz_matrix { + friend class mpz_matrix_manager; + mpz_matrix_manager & m_manager; + mpz_matrix A; +public: + scoped_mpz_matrix(mpz_matrix_manager & m):m_manager(m) {} + scoped_mpz_matrix(mpz_matrix const & B, mpz_matrix_manager & m):m_manager(m) { m_manager.set(A, B); } + ~scoped_mpz_matrix() { m_manager.del(A); } + mpz_matrix_manager & mm() const { return m_manager; } + unsynch_mpz_manager & nm() const { return mm().nm(); } + + unsigned m() const { return A.m; } + unsigned n() const { return A.n; } + mpz * row(unsigned i) const { return A.row(i); } + + operator mpz_matrix const &() const { return A; } + operator mpz_matrix &() { return A; } + mpz_matrix const & get() const { return A; } + mpz_matrix & get() { return A; } + + void swap(mpz_matrix & B) { A.swap(B); } + + void set(unsigned i, unsigned j, mpz const & v) { nm().set(A(i, j), v); } + void set(unsigned i, unsigned j, int v) { nm().set(A(i, j), v); } + + mpz const & operator()(unsigned i, unsigned j) const { return A(i, j); } + mpz & operator()(unsigned i, unsigned j) { return A(i, j); } + + int get_int(unsigned i, unsigned j) const { SASSERT(nm().is_int(A(i, j))); return nm().get_int(A(i, j)); } +}; + +inline std::ostream & operator<<(std::ostream & out, scoped_mpz_matrix const & m) { + m.mm().display(out, m); + return out; +} + +#endif diff --git a/src/test/rcf.cpp b/src/test/rcf.cpp index b50ebc56c..0583c54a0 100644 --- a/src/test/rcf.cpp +++ b/src/test/rcf.cpp @@ -17,6 +17,7 @@ Notes: --*/ #include"realclosure.h" +#include"mpz_matrix.h" static void tst1() { unsynch_mpq_manager qm; @@ -66,6 +67,87 @@ static void tst1() { std::cout << interval_pp((a + eps)/(a - eps)) << std::endl; } -void tst_rcf() { - tst1(); +static void tst2() { + enable_trace("mpz_matrix"); + unsynch_mpq_manager nm; + small_object_allocator allocator; + mpz_matrix_manager mm(nm, allocator); + scoped_mpz_matrix A(mm); + mm.mk(3, 3, A); + // Matrix + // 1 1 1 + // 0 1 -1 + // 0 1 1 + A.set(0, 0, 1); A.set(0, 1, 1); A.set(0, 2, 1); + A.set(1, 0, 0); A.set(1, 1, 1); A.set(1, 2, -1); + A.set(2, 0, 0); A.set(2, 1, 1); A.set(2, 2, 1); + std::cout << A; + { + int b[3]; + int c[3] = { 10, -2, 8 }; + std::cout << "solve: " << mm.solve(A, b, c) << "\n"; + for (unsigned i = 0; i < 3; i++) std::cout << b[i] << " "; + std::cout << "\n"; + } + scoped_mpz_matrix A2(mm); + mm.tensor_product(A, A, A2); + std::cout << A2; + scoped_mpz_matrix B(mm); + unsigned cols[] = { 1, 3, 7, 8 }; + mm.filter_cols(A2, 4, cols, B); + std::cout << B; + scoped_mpz_matrix C(mm); + unsigned perm[] = { 8, 7, 6, 5, 4, 3, 2, 1, 0 }; + mm.permute_rows(B, perm, C); + std::cout << C; +} + +static void tst_solve(unsigned n, int _A[], int _b[], int _c[], bool solved) { + unsynch_mpq_manager nm; + small_object_allocator allocator; + mpz_matrix_manager mm(nm, allocator); + scoped_mpz_matrix A(mm); + mm.mk(n, n, A); + for (unsigned i = 0; i < n; i++) + for (unsigned j = 0; j < n; j++) + A.set(i, j, _A[i*n + j]); + svector b; + b.resize(n, 0); + if (mm.solve(A, b.c_ptr(), _c)) { + SASSERT(solved); + for (unsigned i = 0; i < n; i++) { + SASSERT(b[i] == _b[i]); + } + } + else { + SASSERT(!solved); + } +} + +static void tst_lin_indep(unsigned m, unsigned n, int _A[], unsigned ex_sz, unsigned ex_r[]) { + unsynch_mpq_manager nm; + small_object_allocator allocator; + mpz_matrix_manager mm(nm, allocator); + scoped_mpz_matrix A(mm); + mm.mk(m, n, A); + for (unsigned i = 0; i < m; i++) + for (unsigned j = 0; j < n; j++) + A.set(i, j, _A[i*n + j]); + unsigned_vector r; + mm.linear_independent_rows(A, r); + SASSERT(r.size() == ex_sz); + for (unsigned i = 0; i < ex_sz; i++) { + SASSERT(r[i] == ex_r[i]); + } +} + + +void tst_rcf() { + // tst1(); + tst2(); + { int A[] = {0, 1, 1, 1, 0, 1, 1, 1, -1}; int c[] = {10, 4, -4}; int b[] = {-2, 4, 6}; tst_solve(3, A, b, c, true); } + { int A[] = {1, 1, 1, 0, 1, 1, 0, 1, 1}; int c[] = {3, 2, 2}; int b[] = {1, 1, 1}; tst_solve(3, A, b, c, false); } + { int A[] = {1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, -1}; unsigned r[] = {0, 1, 4}; tst_lin_indep(5, 3, A, 3, r); } + { int A[] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, -1}; unsigned r[] = {0, 4}; tst_lin_indep(5, 3, A, 2, r); } + { int A[] = {1, 1, 1, 1, 1, 1, 1, 0, 1, 2, 1, 2, 3, 1, 3}; unsigned r[] = {0, 2}; tst_lin_indep(5, 3, A, 2, r); } } From b662bc8dc7996ef47ac30bf52f395c0d45f812f0 Mon Sep 17 00:00:00 2001 From: Leonardo de Moura Date: Wed, 9 Jan 2013 11:16:04 -0800 Subject: [PATCH 39/78] Add lower and upper bounds for negative and positive roots Signed-off-by: Leonardo de Moura --- src/math/realclosure/realclosure.cpp | 140 +++++++++++++++++++-------- src/test/rcf.cpp | 2 +- 2 files changed, 98 insertions(+), 44 deletions(-) diff --git a/src/math/realclosure/realclosure.cpp b/src/math/realclosure/realclosure.cpp index 434f933b6..200a93f58 100644 --- a/src/math/realclosure/realclosure.cpp +++ b/src/math/realclosure/realclosure.cpp @@ -1015,7 +1015,7 @@ namespace realclosure { t->m_k++; t->m_proc(t->m_k, qim(), i); int m = magnitude(i); - TRACE("rcf", + TRACE("rcf_transcendental", tout << "refine_transcendental_interval rational: " << m << "\nrational interval: "; qim().display(tout, i); tout << std::endl;); unsigned k; @@ -1033,7 +1033,7 @@ namespace realclosure { void refine_transcendental_interval(transcendental * t, unsigned prec) { while (!check_precision(t->interval(), prec)) { - TRACE("rcf", tout << "refine_transcendental_interval: " << magnitude(t->interval()) << std::endl;); + TRACE("rcf_transcendental", tout << "refine_transcendental_interval: " << magnitude(t->interval()) << std::endl;); checkpoint(); refine_transcendental_interval(t); } @@ -1165,7 +1165,7 @@ namespace realclosure { Similarly, the procedure will fail if one of the a_{n-k} has an interval of the form (l, oo) or (-oo, u). Both cases can only happen if the values of the coefficients depend on infinitesimal values. */ - bool pos_root_upper_bound(unsigned n, value * const * p, unsigned & N) { + bool pos_root_upper_bound(unsigned n, value * const * p, int & N) { SASSERT(n > 1); SASSERT(!is_zero(p[n-1])); int lc_sign = sign(p[n-1]); @@ -1173,15 +1173,15 @@ namespace realclosure { int lc_mag; if (!abs_lower_magnitude(interval(p[n-1]), lc_mag)) return false; - N = 0; + N = -static_cast(m_ini_precision); for (unsigned k = 2; k <= n; k++) { value * a = p[n - k]; if (!is_zero(a) && sign(a) != lc_sign) { int a_mag; if (!abs_upper_magnitude(interval(a), a_mag)) return false; - int C = (a_mag - lc_mag)/k + 1 /* compensate imprecision on division */ + 1 /* Knuth's bound is 2 x Max {... } */; - if (C > 0 && static_cast(C) > N) + int C = (a_mag - lc_mag)/static_cast(k) + 1 /* compensate imprecision on division */ + 1 /* Knuth's bound is 2 x Max {... } */; + if (C > N) N = C; } } @@ -1204,13 +1204,13 @@ namespace realclosure { } /** - \brief Find negative root upper bound using Knuth's approach. + \brief Find negative root lower bound using Knuth's approach. This is similar to pos_root_upper_bound. In principle, we can use the same algorithm. We just have to adjust the coefficients by using the transformation p(-x). */ - bool neg_root_upper_bound(unsigned n, value * const * as, unsigned & N) { + bool neg_root_lower_bound(unsigned n, value * const * as, int & N) { SASSERT(n > 1); SASSERT(!is_zero(as[n-1])); scoped_mpbqi aux(bqim()); @@ -1219,7 +1219,7 @@ namespace realclosure { int lc_mag; if (!abs_lower_magnitude(aux, lc_mag)) return false; - N = 0; + N = -static_cast(m_ini_precision); for (unsigned k = 2; k <= n; k++) { value * a = as[n - k]; if (!is_zero(a)) { @@ -1229,8 +1229,8 @@ namespace realclosure { int a_mag; if (!abs_upper_magnitude(aux, a_mag)) return false; - int C = (a_mag - lc_mag)/k + 1 /* compensate imprecision on division */ + 1 /* Knuth's bound is 2 x Max {... } */; - if (C > 0 && static_cast(C) > N) + int C = (a_mag - lc_mag)/static_cast(k) + 1 /* compensate imprecision on division */ + 1 /* Knuth's bound is 2 x Max {... } */; + if (C > N) N = C; } } @@ -1238,6 +1238,53 @@ namespace realclosure { return true; } + /** + \brief q <- x^{n-1}*p(1/x) + + Given p(x) a_{n-1} * x^{n-1} + ... + a_0, this method stores + a_0 * x^{n-1} + ... + a_{n-1} into q. + */ + void reverse(unsigned n, value * const * p, value_ref_buffer & q) { + unsigned i = n; + while (i > 0) { + --i; + q.push_back(p[i]); + } + } + + /** + \brief To compute the lower bound for positive roots we computer the upper bound for the polynomial q(x) = x^{n-1}*p(1/x). + Assume U is an upper bound for roots of q(x), i.e., (r > 0 and q(r) = 0) implies r < U. + Note that if r is a root for q(x), then 1/r is a root for p(x) and 1/U is a lower bound for positive roots of p(x). + The polynomial q(x) is just p(x) "reversed". + */ + bool pos_root_lower_bound(unsigned n, value * const * p, int & N) { + value_ref_buffer q(*this); + reverse(n, p, q); + if (pos_root_upper_bound(n, q.c_ptr(), N)) { + N = -N; + return true; + } + else { + return false; + } + } + + /** + \brief See comment on pos_root_lower_bound. + */ + bool neg_root_upper_bound(unsigned n, value * const * p, int & N) { + value_ref_buffer q(*this); + reverse(n, p, q); + if (pos_root_lower_bound(n, q.c_ptr(), N)) { + N = -N; + return true; + } + else { + return false; + } + } + /** \brief Root isolation for polynomials that are - nonlinear (degree > 2) @@ -1245,18 +1292,25 @@ namespace realclosure { - square free - nonconstant */ - void nl_nz_sqf_isolate_roots(unsigned n, value * const * as, numeral_vector & roots) { + void nl_nz_sqf_isolate_roots(unsigned n, value * const * p, numeral_vector & roots) { SASSERT(n > 2); - SASSERT(!is_zero(as[0])); - SASSERT(!is_zero(as[n-1])); - unsigned pos_N, neg_N; - bool has_neg_upper = neg_root_upper_bound(n, as, neg_N); - bool has_pos_upper = pos_root_upper_bound(n, as, pos_N); - TRACE("rcf", - display_poly(tout, n, as); tout << "\n"; - tout << "has_neg_upper: " << has_neg_upper << " neg_N: " << neg_N << "\n"; - tout << "has_pos_upper: " << has_pos_upper << " pos_N: " << pos_N << "\n";); - + SASSERT(!is_zero(p[0])); + SASSERT(!is_zero(p[n-1])); + int pos_lower_N, pos_upper_N, neg_lower_N, neg_upper_N; + bool has_neg_lower = neg_root_lower_bound(n, p, neg_lower_N); + bool has_neg_upper = neg_root_upper_bound(n, p, neg_upper_N); + bool has_pos_lower = pos_root_lower_bound(n, p, pos_lower_N); + bool has_pos_upper = pos_root_upper_bound(n, p, pos_upper_N); + TRACE("rcf_isolate", + display_poly(tout, n, p); tout << "\n"; + if (has_neg_lower) tout << "-2^" << neg_lower_N; else tout << "-oo"; + tout << " < neg-roots < "; + if (has_neg_upper) tout << "-2^" << neg_upper_N; else tout << "0"; + tout << "\n"; + if (has_pos_lower) tout << "2^" << pos_lower_N; else tout << "0"; + tout << " < pos-roots < "; + if (has_pos_upper) tout << "2^" << pos_upper_N; else tout << "oo"; + tout << "\n";); // TODO } @@ -1266,62 +1320,62 @@ namespace realclosure { - square free - nonconstant */ - void nz_sqf_isolate_roots(unsigned n, value * const * as, numeral_vector & roots) { + void nz_sqf_isolate_roots(unsigned n, value * const * p, numeral_vector & roots) { SASSERT(n > 1); - SASSERT(!is_zero(as[0])); - SASSERT(!is_zero(as[n-1])); + SASSERT(!is_zero(p[0])); + SASSERT(!is_zero(p[n-1])); if (n == 2) { // we don't need a field extension for linear polynomials. numeral r; value_ref v(*this); - neg(as[0], v); - div(v, as[1], v); + neg(p[0], v); + div(v, p[1], v); set(r, v); roots.push_back(r); } else { - nl_nz_sqf_isolate_roots(n, as, roots); + nl_nz_sqf_isolate_roots(n, p, roots); } } /** \brief Root isolation for polynomials where 0 is not a root. */ - void nz_isolate_roots(unsigned n, value * const * as, numeral_vector & roots) { + void nz_isolate_roots(unsigned n, value * const * p, numeral_vector & roots) { SASSERT(n > 0); - SASSERT(!is_zero(as[0])); - SASSERT(!is_zero(as[n-1])); + SASSERT(!is_zero(p[0])); + SASSERT(!is_zero(p[n-1])); if (n == 1) { // constant polynomial return; } value_ref_buffer sqf(*this); - square_free(n, as, sqf); + square_free(n, p, sqf); nz_sqf_isolate_roots(sqf.size(), sqf.c_ptr(), roots); } /** \brief Root isolation entry point. */ - void isolate_roots(unsigned n, numeral const * as, numeral_vector & roots) { + void isolate_roots(unsigned n, numeral const * p, numeral_vector & roots) { SASSERT(n > 0); - SASSERT(!is_zero(as[n-1])); + SASSERT(!is_zero(p[n-1])); if (n == 1) { // constant polynomial return; } unsigned i = 0; for (; i < n; i++) { - if (!is_zero(as[i])) + if (!is_zero(p[i])) break; } SASSERT(i < n); - SASSERT(!is_zero(as[i])); - ptr_buffer as_values; + SASSERT(!is_zero(p[i])); + ptr_buffer nz_p; for (; i < n; i++) - as_values.push_back(as[i].m_value); - nz_isolate_roots(as_values.size(), as_values.c_ptr(), roots); - if (as_values.size() < n) { + nz_p.push_back(p[i].m_value); + nz_isolate_roots(nz_p.size(), nz_p.c_ptr(), roots); + if (nz_p.size() < n) { // zero is a root roots.push_back(numeral()); } @@ -1725,7 +1779,7 @@ namespace realclosure { \brief r <- rem(p1, p2) */ void rem(unsigned sz1, value * const * p1, unsigned sz2, value * const * p2, value_ref_buffer & r) { - TRACE("rcf", + TRACE("rcf_rem", tout << "rem\n"; display_poly(tout, sz1, p1); tout << "\n"; display_poly(tout, sz2, p2); tout << "\n";); @@ -1744,7 +1798,7 @@ namespace realclosure { checkpoint(); sz1 = r.size(); if (sz1 < sz2) { - TRACE("rcf", tout << "rem result\n"; display_poly(tout, r.size(), r.c_ptr()); tout << "\n";); + TRACE("rcf_rem", tout << "rem result\n"; display_poly(tout, r.size(), r.c_ptr()); tout << "\n";); return; } unsigned m_n = sz1 - sz2; @@ -2027,7 +2081,7 @@ namespace realclosure { refine_transcendental_interval(to_transcendental(v->ext()), _prec); update_rf_interval(v, prec); - TRACE("rcf", tout << "after update_rf_interval: " << magnitude(v->interval()) << " "; + TRACE("rcf_transcendental", tout << "after update_rf_interval: " << magnitude(v->interval()) << " "; bqim().display(tout, v->interval()); tout << std::endl;); if (check_precision(v->interval(), prec)) diff --git a/src/test/rcf.cpp b/src/test/rcf.cpp index 0583c54a0..40c3e838f 100644 --- a/src/test/rcf.cpp +++ b/src/test/rcf.cpp @@ -143,7 +143,7 @@ static void tst_lin_indep(unsigned m, unsigned n, int _A[], unsigned ex_sz, unsi void tst_rcf() { - // tst1(); + tst1(); tst2(); { int A[] = {0, 1, 1, 1, 0, 1, 1, 1, -1}; int c[] = {10, 4, -4}; int b[] = {-2, 4, 6}; tst_solve(3, A, b, c, true); } { int A[] = {1, 1, 1, 0, 1, 1, 0, 1, 1}; int c[] = {3, 2, 2}; int b[] = {1, 1, 1}; tst_solve(3, A, b, c, false); } From 81807c7001c1c6ded5d0f1c9db8f8ccde715ac58 Mon Sep 17 00:00:00 2001 From: Leonardo de Moura Date: Wed, 9 Jan 2013 13:37:10 -0800 Subject: [PATCH 40/78] Add procedure for computing TaQ(Q, P; a, b) Signed-off-by: Leonardo de Moura --- src/math/realclosure/realclosure.cpp | 337 ++++++++++++++++++++++++++- 1 file changed, 329 insertions(+), 8 deletions(-) diff --git a/src/math/realclosure/realclosure.cpp b/src/math/realclosure/realclosure.cpp index 200a93f58..5a7169fe5 100644 --- a/src/math/realclosure/realclosure.cpp +++ b/src/math/realclosure/realclosure.cpp @@ -326,7 +326,7 @@ namespace realclosure { // Parameters unsigned m_ini_precision; //!< initial precision for transcendentals, infinitesimals, etc. - int m_min_magnitude; + unsigned m_max_precision; //!< Maximum precision for interval arithmetic techniques, it switches to complete methods after that unsigned m_inf_precision; //!< 2^m_inf_precision is used as the lower bound of oo and -2^m_inf_precision is used as the upper_bound of -oo scoped_mpbq m_plus_inf_approx; // lower bound for binary rational intervals used to approximate an infinite positive value scoped_mpbq m_minus_inf_approx; // upper bound for binary rational intervals used to approximate an infinite negative value @@ -465,10 +465,10 @@ namespace realclosure { } /** - \brief Return true if the magnitude of the given interval is less than the parameter m_min_magnitude + \brief Return true if the magnitude of the given interval is less than the parameter m_max_precision. */ bool too_small(mpbqi const & i) { - return magnitude(i) < m_min_magnitude; + return magnitude(i) < -static_cast(m_max_precision); } #define SMALL_UNSIGNED 1 << 16 @@ -563,7 +563,7 @@ namespace realclosure { void updt_params(params_ref const & p) { m_ini_precision = p.get_uint("initial_precision", 24); m_inf_precision = p.get_uint("inf_precision", 24); - m_min_magnitude = -static_cast(p.get_uint("min_mag", 64)); + m_max_precision = p.get_uint("max_precision", 64); // == 1/2^64 for interval arithmetic methods, it switches to complete methods after that. bqm().power(mpbq(2), m_inf_precision, m_plus_inf_approx); bqm().set(m_minus_inf_approx, m_plus_inf_approx); bqm().neg(m_minus_inf_approx); @@ -961,6 +961,14 @@ namespace realclosure { set_upper_core(a, b.upper(), b.upper_is_open(), b.upper_is_inf()); } + /** + \brief a <- [b, b] + */ + void set_interval(mpbqi & a, mpbq const & b) { + set_lower_core(a, b, false, false); + set_upper_core(a, b, false, false); + } + /** \brief Make a rational_function_value using the given extension, numerator and denominator. This method does not set the interval. It remains (-oo, oo) @@ -1285,6 +1293,53 @@ namespace realclosure { } } + /** + \brief Store in ds all (non-constant) derivatives of p. + + \post d.size() == n-2 + */ + void mk_derivatives(unsigned n, value * const * p, scoped_polynomial_seq & ds) { + SASSERT(n >= 3); // p is at least quadratic + SASSERT(!is_zero(p[0])); + SASSERT(!is_zero(p[n-1])); + value_ref_buffer p_prime(*this); + derivative(n, p, p_prime); + ds.push(p_prime.size(), p_prime.c_ptr()); + SASSERT(n >= 3); + for (unsigned i = 0; i < n - 3; i++) { + SASSERT(ds.size() > 1); + unsigned prev = ds.size() - 1; + n = ds.size(prev); + p = ds.coeffs(prev); + derivative(n, p, p_prime); + ds.push(p_prime.size(), p_prime.c_ptr()); + } + } + + /** + \brief Isolate roots of p (first polynomial in TaQ_1) in the given interval using sign conditions to distinguish between them. + We need this method when the polynomial contains roots that are infinitesimally close to each other. + For example, given an infinitesimal eps, the polynomial (x - 1)(x - 1 - eps) == (1 + eps) + (- 2 - eps)*x + x^2 + has two roots 1 and 1+eps, we can't isolate these roots using intervals with binary rational end points. + In this case, we use the signs of (some of the) derivatives in the roots. + By Thom's lemma, we know we can always use the signs of the derivatives to distinguish between different roots. + + Remark: the polynomials do not need to be the derivatives of p. We use derivatives because a simple + sequential search can be used to find the set of polynomials that can be used to distinguish between + the different roots. + */ + void sign_det_isolate_roots(scoped_polynomial_seq & TaQ_1, unsigned num_roots, mpbqi const & interval, numeral_vector & roots) { + SASSERT(num_roots >= 2); + SASSERT(TaQ_1.size() > 0); + unsigned n = TaQ_1.size(0); + value * const * p = TaQ_1.coeffs(0); + + scoped_polynomial_seq ds(*this); + mk_derivatives(n, p, ds); + + // TODO continue + } + /** \brief Root isolation for polynomials that are - nonlinear (degree > 2) @@ -1477,6 +1532,12 @@ namespace realclosure { return r; } + rational_value * mk_rational(mpbq const & v) { + scoped_mpq v_q(qm()); // v as a rational + ::to_mpq(qm(), v, v_q); + return mk_rational(v_q); + } + void reset_interval(value * a) { bqim().reset(a->m_interval); } @@ -2001,6 +2062,260 @@ namespace realclosure { seq.push(p1p2.size(), p1p2.c_ptr()); sturm_seq_core(seq); } + + /** + \brief Return the sign of p(0) + */ + int eval_sign_at_zero(unsigned n, value * const * p) { + if (n == 0) + return 0; + return sign(p[0]); + } + + /** + \brief Return the sign of p(oo) + */ + int eval_sign_at_plus_inf(unsigned n, value * const * p) { + if (n == 0) + return 0; + SASSERT(!is_zero(p[n-1])); // p is well formed + return sign(p[n-1]); + } + + /** + \brief Return the sign of p(-oo) + */ + int eval_sign_at_minus_inf(unsigned n, value * const * p) { + if (n == 0) + return 0; + SASSERT(!is_zero(p[n-1])); // p is well formed + unsigned degree = n - 1; + if (degree % 2 == 0) + return sign(p[n - 1]); + else + return -sign(p[n - 1]); + } + + /** + \brief Store in r an approximation (as an interval) for the interval p(b). + + \pre n >= 2 + */ + void eval_sign_at_approx(unsigned n, value * const * p, mpbq const & b, mpbqi & r) { + SASSERT(n >= 2); + // We compute r using the Horner Sequence + // ((a_{n-1}*b + a_{n-2})*b + a_{n-3})*b + a_{n-4} ... + // where a_i's are the intervals associated with coefficients of p. + SASSERT(n > 0); + SASSERT(p[n - 1] != 0); + scoped_mpbqi bi(bqim()); + set_interval(bi, b); // bi <- [b, b] + // r <- a_n * bi + bqim().mul(interval(p[n - 1]), bi, r); + unsigned i = n - 1; + while (i > 0) { + checkpoint(); + --i; + if (p[i] != 0) + bqim().add(r, interval(p[i]), r); + if (i > 0) + bqim().mul(r, bi, r); + } + } + + /** + \brief We say a polynomial has "refinable" approximated coefficients if the intervals + approximating the coefficients do not have -oo or oo as lower/upper bounds. + */ + bool has_refineable_approx_coeffs(unsigned n, value * const * p) { + for (unsigned i = 0; i < n; i++) { + if (p[i] != 0) { + mpbqi & a_i = interval(p[i]); + if (a_i.lower_is_inf() || a_i.upper_is_inf()) + return false; + } + } + return true; + } + + /** + \brief Evaluate the sign of p(b) by computing a value object. + */ + int expensive_eval_sign_at(unsigned n, value * const * p, mpbq const & b) { + SASSERT(n > 0); + SASSERT(p[n - 1] != 0); + value_ref bv(*this); + bv = mk_rational(b); + // We compute the result using the Horner Sequence + // ((a_{n-1}*bv + a_{n-2})*bv + a_{n-3})*bv + a_{n-4} ... + // where a_i's are the coefficients of p. + value_ref r(*this); + // r <- a_{n-1} * bv + mul(p[n - 1], bv, r); + unsigned i = n - 1; + while (i > 0) { + checkpoint(); + --i; + if (p[i] != 0) + add(r, p[i], r); // r <- r + a_i + if (i > 0) + mul(r, bv, r); // r <- r * bv + } + return sign(r); + } + + /** + \brief Find the magnitude of the biggest interval use to approximate coefficients of p. + + \pre has_refineable_approx_coeffs(n, p) + */ + int find_biggest_interval_magnitude(unsigned n, value * const * p) { + int r = INT_MIN; + for (unsigned i = 0; i < n; i++) { + if (p[i] != 0) { + mpbqi & a_i = interval(p[i]); + SASSERT(!a_i.lower_is_inf() && !a_i.upper_is_inf()); + int m = magnitude(a_i); + if (m > r) + r = m; + } + } + return r; + } + + /** + \brief Return the sign of p(b) + */ + int eval_sign_at(unsigned n, value * const * p, mpbq const & b) { + scoped_mpbqi r(bqim()); + eval_sign_at_approx(n, p, b, r); + if (!bqim().contains_zero(r)) { + // we are done + return bqim().is_P(r) ? 1 : -1; + } + else if (!has_refineable_approx_coeffs(n, p)) { + return expensive_eval_sign_at(n, p, b); + } + else { + int m = find_biggest_interval_magnitude(n, p); + unsigned prec; + if (m >= 0) + prec = 1; + else + prec = -m; + SASSERT(prec >= 1); + while (prec <= m_max_precision) { + if (!refine_coeffs_interval(n, p, prec)) { + // Failed to refine intervals, p must depend on infinitesimal values. + // This can happen even if all intervals of coefficients of p are bounded. + return expensive_eval_sign_at(n, p, b); + } + eval_sign_at_approx(n, p, b, r); + if (!bqim().contains_zero(r)) { + // we are done + return bqim().is_P(r) ? 1 : -1; + } + prec++; // increase precision and try again. + } + return expensive_eval_sign_at(n, p, b); + } + } + + enum location { + ZERO, + MINUS_INF, + PLUS_INF, + MPBQ + }; + + unsigned sign_variations_at_core(scoped_polynomial_seq const & seq, location loc, mpbq const & b) { + unsigned sz = seq.size(); + if (sz <= 1) + return 0; + unsigned r = 0; + int sign, prev_sign; + sign = 0; + prev_sign = 0; + unsigned i = 0; + for (; i < sz; i++) { + // find next nonzero + unsigned psz = seq.size(i); + value * const * p = seq.coeffs(i); + switch (loc) { + case PLUS_INF: + sign = eval_sign_at_plus_inf(psz, p); + break; + case MINUS_INF: + sign = eval_sign_at_minus_inf(psz, p); + break; + case ZERO: + sign = eval_sign_at_zero(psz, p); + break; + case MPBQ: + sign = eval_sign_at(psz, p, b); + break; + default: + UNREACHABLE(); + break; + } + if (sign == 0) + continue; + SASSERT(sign == 1 || sign == -1); + // in the first iteration prev_sign == 0, then r is never incremented. + if (sign != prev_sign && prev_sign != 0) + r++; + // move to the next + prev_sign = sign; + } + return r; + } + + unsigned sign_variations_at_minus_inf(scoped_polynomial_seq const & seq) { + mpbq dummy(0); + return sign_variations_at_core(seq, MINUS_INF, dummy); + } + + unsigned sign_variations_at_plus_inf(scoped_polynomial_seq const & seq) { + mpbq dummy(0); + return sign_variations_at_core(seq, PLUS_INF, dummy); + } + + unsigned sign_variations_at_zero(scoped_polynomial_seq const & seq) { + mpbq dummy(0); + return sign_variations_at_core(seq, ZERO, dummy); + } + + unsigned sign_variations_at(scoped_polynomial_seq const & seq, mpbq const & b) { + return sign_variations_at_core(seq, MPBQ, b); + } + + /** + \brief Given a polynomial Sturm sequence seq for (P; P' * Q) and an interval (a, b], it returns + TaQ(Q, P; a, b) = + #{ x \in (a, b] | P(x) = 0 and Q(x) > 0 } + - + #{ x \in (a, b] | P(x) = 0 and Q(x) < 0 } + + \remark This method ignores whether the interval end-points are closed or open. + */ + int TaQ(scoped_polynomial_seq & seq, mpbqi const & interval) { + int a, b; + if (interval.lower_is_inf()) + a = sign_variations_at_minus_inf(seq); + else if (bqm().is_zero(interval.lower())) + a = sign_variations_at_zero(seq); + else + a = sign_variations_at(seq, interval.lower()); + + if (interval.upper_is_inf()) + b = sign_variations_at_plus_inf(seq); + else if (bqm().is_zero(interval.upper())) + b = sign_variations_at_zero(seq); + else + b = sign_variations_at(seq, interval.upper()); + + return a - b; + } void refine_rational_interval(rational_value * v, unsigned prec) { mpbqi & i = interval(v); @@ -2018,15 +2333,21 @@ namespace realclosure { /** \brief Refine the interval for each coefficient of in the polynomial p. */ - bool refine_coeffs_interval(polynomial const & p, unsigned prec) { - unsigned sz = p.size(); - for (unsigned i = 0; i < sz; i++) { + bool refine_coeffs_interval(unsigned n, value * const * p, unsigned prec) { + for (unsigned i = 0; i < n; i++) { if (p[i] != 0 && !refine_interval(p[i], prec)) return false; } return true; } + /** + \brief Refine the interval for each coefficient of in the polynomial p. + */ + bool refine_coeffs_interval(polynomial const & p, unsigned prec) { + return refine_coeffs_interval(p.size(), p.c_ptr(), prec); + } + /** \brief Store in r the interval p(v). */ @@ -2170,7 +2491,7 @@ namespace realclosure { } /** - \brief Refine the interval of v to the desired precision (1/2^k). + \brief Refine the interval of v to the desired precision (1/2^prec). Return false in case of failure. A failure can only happen if v depends on infinitesimal values. */ bool refine_interval(value * v, unsigned prec) { From 1712f0a33b2eda4c8ac7a1b4ed2b55acd61cfadf Mon Sep 17 00:00:00 2001 From: Leonardo de Moura Date: Wed, 9 Jan 2013 18:43:32 -0800 Subject: [PATCH 41/78] Add goodies Signed-off-by: Leonardo de Moura --- src/util/buffer.h | 9 +++++++-- src/util/util.h | 4 ++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/util/buffer.h b/src/util/buffer.h index 1467e64df..55ab43ec3 100644 --- a/src/util/buffer.h +++ b/src/util/buffer.h @@ -231,8 +231,13 @@ public: SASSERT(size() == nsz); } -private: - buffer& operator=(buffer const&); + buffer & operator=(buffer const & other) { + if (this == &other) + return *this; + reset(); + append(other); + return *this; + } }; template diff --git a/src/util/util.h b/src/util/util.h index 945d259f9..3360c2282 100644 --- a/src/util/util.h +++ b/src/util/util.h @@ -243,6 +243,10 @@ public: m_ptr = 0; return tmp; } + + void swap(scoped_ptr & p) { + std::swap(m_ptr, p.m_ptr); + } }; template From d644b37ac133fd91a32028fb48e50a442ebc2407 Mon Sep 17 00:00:00 2001 From: Leonardo de Moura Date: Wed, 9 Jan 2013 22:35:39 -0800 Subject: [PATCH 42/78] Add non naive sign determination algorithm Signed-off-by: Leonardo de Moura --- src/math/realclosure/mpz_matrix.cpp | 12 +- src/math/realclosure/mpz_matrix.h | 5 +- src/math/realclosure/realclosure.cpp | 733 +++++++++++++++++++++++++-- 3 files changed, 708 insertions(+), 42 deletions(-) diff --git a/src/math/realclosure/mpz_matrix.cpp b/src/math/realclosure/mpz_matrix.cpp index fb9327a9e..fcbad5695 100644 --- a/src/math/realclosure/mpz_matrix.cpp +++ b/src/math/realclosure/mpz_matrix.cpp @@ -349,8 +349,8 @@ void mpz_matrix_manager::permute_rows(mpz_matrix const & A, unsigned const * p, B.swap(C); } -void mpz_matrix_manager::linear_independent_rows(mpz_matrix const & _A, unsigned_vector & r) { - r.reset(); +unsigned mpz_matrix_manager::linear_independent_rows(mpz_matrix const & _A, unsigned * r) { + unsigned r_sz = 0; scoped_mpz_matrix A(*this); scoped_mpz g(nm()); scoped_mpz t1(nm()), t2(nm()); @@ -381,13 +381,15 @@ void mpz_matrix_manager::linear_independent_rows(mpz_matrix const & _A, unsigned swap_rows(A, k1, pivot); std::swap(rows[k1], rows[pivot]); // - r.push_back(rows[k1]); - if (r.size() >= A.n()) + r[r_sz] = rows[k1]; + r_sz++; + if (r_sz >= A.n()) break; eliminate(A, 0, k1, k2, false); k2++; } - std::sort(r.begin(), r.end()); + std::sort(r, r + r_sz); + return r_sz; } void mpz_matrix_manager::display(std::ostream & out, mpz_matrix const & A, unsigned cell_width) const { diff --git a/src/math/realclosure/mpz_matrix.h b/src/math/realclosure/mpz_matrix.h index 9ce9aaac7..5986e3a21 100644 --- a/src/math/realclosure/mpz_matrix.h +++ b/src/math/realclosure/mpz_matrix.h @@ -105,8 +105,11 @@ public: \remark If there is an option between rows i and j, this method will give preference to the row that occurs first. + + \remark The vector r must have at least A.n() capacity + The numer of linear independent rows is returned. */ - void linear_independent_rows(mpz_matrix const & A, unsigned_vector & r); + unsigned linear_independent_rows(mpz_matrix const & A, unsigned * r); // method for debugging purposes void display(std::ostream & out, mpz_matrix const & A, unsigned cell_width=4) const; diff --git a/src/math/realclosure/realclosure.cpp b/src/math/realclosure/realclosure.cpp index 5a7169fe5..30fceede3 100644 --- a/src/math/realclosure/realclosure.cpp +++ b/src/math/realclosure/realclosure.cpp @@ -22,6 +22,7 @@ Notes: #include"realclosure.h" #include"array.h" #include"mpbq.h" +#include"mpz_matrix.h" #include"interval_def.h" #include"obj_ref.h" #include"ref_vector.h" @@ -241,17 +242,48 @@ namespace realclosure { return rank_lt(r1, r2); } }; + + /** + \brief Sign condition object, it encodes one conjunct of a sign assignment. + If has to keep following m_prev to obtain the whole sign condition + */ + struct sign_condition { + unsigned m_q_idx:31; // Sign condition for the polynomial at position m_q_idx in the field m_qs of sign_det structure + unsigned m_mark:1; // auxiliary mark used during deletion + int m_sign; // Sign of the polynomial associated with m_q_idx + sign_condition * m_prev; // Antecedent + sign_condition():m_q_idx(0), m_mark(false), m_sign(0), m_prev(0) {} + sign_condition(unsigned qidx, int sign, sign_condition * prev):m_q_idx(qidx), m_mark(false), m_sign(sign), m_prev(prev) {} + + sign_condition * prev() const { return m_prev; } + unsigned qidx() const { return m_q_idx; } + int sign() const { return m_sign; } + }; + + struct sign_det { + unsigned m_ref_count; // sign_det objects may be shared between different roots of the same polynomial. + mpz_matrix M; // Matrix used in the sign determination + array m_prs; // Polynomials associated with the rows of M + array m_sign_conditions; // Sign conditions associated with the columns of M + array m_qs; // Polynomials used in the sign conditions. + sign_det():m_ref_count(0) {} + + array const & qs() const { return m_qs; } + sign_condition * sc(unsigned idx) const { return m_sign_conditions[idx]; } + }; struct algebraic : public extension { polynomial m_p; - signs m_signs; - bool m_real; //!< True if the polynomial p does not depend on infinitesimal extensions. + sign_det * m_sign_det; //!< != 0 if m_interval constains more than one root of m_p. + unsigned m_idx; //!< != UINT_MAX if m_sign_det != 0, in this case m_idx < m_sign_det->m_sign_conditions.size() + bool m_real; //!< True if the polynomial p does not depend on infinitesimal extensions. - algebraic(unsigned idx):extension(ALGEBRAIC, idx), m_real(false) {} + algebraic(unsigned idx):extension(ALGEBRAIC, idx), m_sign_det(0), m_idx(0), m_real(false) {} polynomial const & p() const { return m_p; } - signs const & s() const { return m_signs; } bool is_real() const { return m_real; } + sign_det * sdt() const { return m_sign_det; } + unsigned sdt_idx() const { return m_idx; } }; struct transcendental : public extension { @@ -309,10 +341,13 @@ namespace realclosure { typedef obj_ref value_ref; typedef _scoped_interval scoped_mpqi; typedef _scoped_interval scoped_mpbqi; - + typedef sbuffer int_buffer; + typedef sbuffer unsigned_buffer; + small_object_allocator * m_allocator; bool m_own_allocator; unsynch_mpq_manager & m_qm; + mpz_matrix_manager m_mm; mpbq_config::numeral_manager m_bqm; mpqi_manager m_qim; mpbqi_manager m_bqim; @@ -340,6 +375,8 @@ namespace realclosure { sbuffer m_szs; // size of each polynomial in the sequence public: scoped_polynomial_seq(imp & m):m_seq_coeffs(m) {} + ~scoped_polynomial_seq() { + } /** \brief Add a new polynomial to the sequence. @@ -373,12 +410,48 @@ namespace realclosure { m_begins.reset(); m_szs.reset(); } + + scoped_polynomial_seq & operator=(scoped_polynomial_seq & s) { + if (this == &s) + return *this; + reset(); + m_seq_coeffs.append(s.m_seq_coeffs); + m_begins.append(s.m_begins); + m_szs.append(s.m_szs); + return *this; + } + }; + + struct scoped_sign_conditions { + imp & m_imp; + ptr_buffer m_scs; + + scoped_sign_conditions(imp & m):m_imp(m) {} + ~scoped_sign_conditions() { + m_imp.del_sign_conditions(m_scs.size(), m_scs.c_ptr()); + } + + sign_condition * & operator[](unsigned idx) { return m_scs[idx]; } + unsigned size() const { return m_scs.size(); } + bool empty() const { return m_scs.empty(); } + void push_back(sign_condition * sc) { m_scs.push_back(sc); } + void release() { + // release ownership + m_scs.reset(); + } + void copy_from(scoped_sign_conditions & scs) { + SASSERT(this != &scs); + release(); + m_scs.append(scs.m_scs.size(), scs.m_scs.c_ptr()); + scs.release(); + } }; imp(unsynch_mpq_manager & qm, params_ref const & p, small_object_allocator * a): m_allocator(a == 0 ? alloc(small_object_allocator, "realclosure") : a), m_own_allocator(a == 0), m_qm(qm), + m_mm(m_qm, *m_allocator), m_bqm(m_qm), m_qim(m_qm), m_bqim(m_bqm), @@ -403,11 +476,22 @@ namespace realclosure { dealloc(m_allocator); } + // Rational number manager unsynch_mpq_manager & qm() const { return m_qm; } + + // Binary rational number manager mpbq_config::numeral_manager & bqm() { return m_bqm; } + + // Rational interval manager mpqi_manager & qim() { return m_qim; } + + // Binary rational interval manager mpbqi_manager & bqim() { return m_bqim; } mpbqi_manager const & bqim() const { return m_bqim; } + + // Integer matrix manager + mpz_matrix_manager & mm() { return m_mm; } + small_object_allocator & allocator() { return *m_allocator; } void checkpoint() { @@ -599,13 +683,58 @@ namespace realclosure { del_rational_function(static_cast(v)); } + void finalize(array & ps) { + for (unsigned i = 0; i < ps.size(); i++) + reset_p(ps[i]); + ps.finalize(allocator()); + } + + void del_sign_condition(sign_condition * sc) { + allocator().deallocate(sizeof(sign_condition), sc); + } + + void del_sign_conditions(unsigned sz, sign_condition * const * to_delete) { + ptr_buffer all_to_delete; + for (unsigned i = 0; i < sz; i++) { + sign_condition * sc = to_delete[i]; + while (sc && sc->m_mark == false) { + sc->m_mark = true; + all_to_delete.push_back(sc); + sc = sc->m_prev; + } + } + for (unsigned i = 0; i < all_to_delete.size(); i++) { + del_sign_condition(all_to_delete[i]); + } + } + + void del_sign_det(sign_det * sd) { + mm().del(sd->M); + del_sign_conditions(sd->m_sign_conditions.size(), sd->m_sign_conditions.c_ptr()); + sd->m_sign_conditions.finalize(allocator()); + finalize(sd->m_prs); + finalize(sd->m_qs); + allocator().deallocate(sizeof(sign_det), sd); + } + + void inc_ref_sign_det(sign_det * sd) { + if (sd != 0) + sd->m_ref_count++; + } + + void dec_ref_sign_det(sign_det * sd) { + if (sd != 0) { + sd->m_ref_count--; + if (sd->m_ref_count == 0) { + del_sign_det(sd); + } + } + } + void del_algebraic(algebraic * a) { reset_p(a->m_p); bqim().del(a->m_interval); - unsigned sz = a->m_signs.size(); - for (unsigned i = 0; i < sz; i++) { - reset_p(a->m_signs[i].first); - } + dec_ref_sign_det(a->m_sign_det); allocator().deallocate(sizeof(algebraic), a); } @@ -928,6 +1057,15 @@ namespace realclosure { a.set_lower_is_inf(true); } + /** + \brief a.lower <- 0 + */ + void set_lower_zero(mpbqi & a) { + bqm().reset(a.lower()); + a.set_lower_is_open(true); + a.set_lower_is_inf(false); + } + /** \brief Set the upper bound of the given interval. */ @@ -953,6 +1091,15 @@ namespace realclosure { a.set_upper_is_inf(true); } + /** + \brief a.upper <- 0 + */ + void set_upper_zero(mpbqi & a) { + bqm().reset(a.upper()); + a.set_upper_is_open(true); + a.set_upper_is_inf(false); + } + /** \brief a <- b */ @@ -969,6 +1116,10 @@ namespace realclosure { set_upper_core(a, b, false, false); } + sign_condition * mk_sign_condition(unsigned qidx, int sign, sign_condition * prev_sc) { + return new (allocator()) sign_condition(qidx, sign, prev_sc); + } + /** \brief Make a rational_function_value using the given extension, numerator and denominator. This method does not set the interval. It remains (-oo, oo) @@ -1306,8 +1457,8 @@ namespace realclosure { derivative(n, p, p_prime); ds.push(p_prime.size(), p_prime.c_ptr()); SASSERT(n >= 3); - for (unsigned i = 0; i < n - 3; i++) { - SASSERT(ds.size() > 1); + for (unsigned i = 0; i < n - 2; i++) { + SASSERT(ds.size() > 0); unsigned prev = ds.size() - 1; n = ds.size(prev); p = ds.coeffs(prev); @@ -1315,9 +1466,162 @@ namespace realclosure { ds.push(p_prime.size(), p_prime.c_ptr()); } } + + /** + \brief Given polynomials p and q, and an interval, compute the number of + roots of p in the interval such that: + - q is zero + - q is positive + - q is negative + + \pre num_roots is the number of roots of p in the given interval. + + \remark num_roots == q_eq_0 + q_gt_0 + q_lt_0 + */ + void count_signs_at_zeros(// Input values + unsigned p_sz, value * const * p, // polynomial p + unsigned q_sz, value * const * q, // polynomial q + mpbqi const & interval, + int num_roots, // number of roots of p in the given interval + // Output values + int & q_eq_0, int & q_gt_0, int & q_lt_0, + value_ref_buffer & q2) { + TRACE("rcf_count_signs", + tout << "p: "; display_poly(tout, p_sz, p); tout << "\n"; + tout << "q: "; display_poly(tout, q_sz, q); tout << "\n";); + SASSERT(num_roots > 0); + int r1 = TaQ(p_sz, p, q_sz, q, interval); + // r1 == q_gt_0 - q_lt_0 + SASSERT(-num_roots <= r1 && r1 <= num_roots); + if (r1 == num_roots) { + // q is positive in all roots of p + q_eq_0 = 0; + q_gt_0 = num_roots; + q_lt_0 = 0; + } + else if (r1 == -num_roots) { + // q is negative in all roots of p + q_eq_0 = 0; + q_gt_0 = 0; + q_lt_0 = num_roots; + } + else if (r1 == num_roots - 1) { + // The following assignment is the only possibility + q_eq_0 = 1; + q_gt_0 = num_roots - 1; + q_lt_0 = 0; + } + else if (r1 == -(num_roots - 1)) { + // The following assignment is the only possibility + q_eq_0 = 1; + q_gt_0 = 0; + q_lt_0 = num_roots - 1; + } + else { + // Expensive case + // q2 <- q^2 + mul(q_sz, q, q_sz, q, q2); + int r2 = TaQ(p_sz, p, q2.size(), q2.c_ptr(), interval); + SASSERT(0 <= r2 && r2 <= num_roots); + // r2 == q_gt_0 + q_lt_0 + SASSERT((r2 + r1) % 2 == 0); + SASSERT((r2 - r1) % 2 == 0); + q_eq_0 = num_roots - r2; + q_gt_0 = (r2 + r1)/2; + q_lt_0 = (r2 - r1)/2; + } + SASSERT(q_eq_0 + q_gt_0 + q_lt_0 == num_roots); + } /** - \brief Isolate roots of p (first polynomial in TaQ_1) in the given interval using sign conditions to distinguish between them. + \brief Expand the set of Tarski Queries used in the sign determination algorithm. + + taqrs contains the results of TaQ(p, prs[i]; interval) + We have that taqrs.size() == prs.size() + + We produce a new_taqrs and new_prs + For each pr in new_prs we have + pr in new_prs, TaQ(p, pr; interval) in new_taqrs + pr*q in new_prs, TaQ(p, pr*q; interval) in new_taqrs + if q2_sz != 0, we also have + pr*q^2 in new_prs, TaQ(p, pr*q^2; interval) in new_taqrs + + */ + void expand_taqrs(// Input values + int_buffer const & taqrs, + scoped_polynomial_seq const & prs, + unsigned p_sz, value * const * p, + unsigned q_sz, value * const * q, + bool use_q2, unsigned q2_sz, value * const * q2, + mpbqi const & interval, + // Output values + int_buffer & new_taqrs, + scoped_polynomial_seq & new_prs + ) { + SASSERT(taqrs.size() == prs.size()); + new_taqrs.reset(); new_prs.reset(); + for (unsigned i = 0; i < taqrs.size(); i++) { + // Add prs * 1 + new_taqrs.push_back(taqrs[i]); + new_prs.push(prs.size(i), prs.coeffs(i)); + // Add prs * q + value_ref_buffer prq(*this); + mul(prs.size(i), prs.coeffs(i), q_sz, q, prq); + new_taqrs.push_back(TaQ(p_sz, p, prq.size(), prq.c_ptr(), interval)); + new_prs.push(prq.size(), prq.c_ptr()); + // If use_q2, + // Add prs * q^2 + if (use_q2) { + value_ref_buffer prq2(*this); + mul(prs.size(i), prs.coeffs(i), q2_sz, q2, prq2); + new_taqrs.push_back(TaQ(p_sz, p, prq2.size(), prq2.c_ptr(), interval)); + new_prs.push(prq2.size(), prq2.c_ptr()); + } + } + SASSERT(new_prs.size() == new_taqrs.size()); + SASSERT(use_q2 || new_prs.size() == 2*prs.size()); + SASSERT(!use_q2 || new_prs.size() == 3*prs.size()); + } + + /** + \brief In the sign determination algorithm main loop, we keep processing polynomials q, + and checking whether they discriminate the roots of the target polynomial. + + The vectors sc_cardinalities contains the cardinalites of the new realizable sign conditions. + That is, we started we a sequence of sign conditions + sc_1, ..., sc_n, + If q2_used is true, then we expanded this sequence as + sc1_1 and q == 0, sc_1 and q > 0, sc_1 and q < 0, ..., sc_n and q == 0, sc_n and q > 0, sc_n and q < 0 + If q2_used is false, then we considered only two possible signs of q. + + Thus, q is useful (i.e., it is a discriminator for the roots of p) IF + If !q2_used, then There is an i s.t. sc_cardinalities[2*i] > 0 && sc_cardinalities[2*i] > 0 + If q2_used, then There is an i s.t. AtLeatTwo(sc_cardinalities[3*i] > 0, sc_cardinalities[3*i+1] > 0, sc_cardinalities[3*i+2] > 0) + */ + bool keep_new_sc_assignment(unsigned sz, int const * sc_cardinalities, bool q2_used) { + SASSERT(q2_used || sz % 2 == 0); + SASSERT(!q2_used || sz % 3 == 0); + if (q2_used) { + for (unsigned i = 0; i < sz; i += 3) { + unsigned c = 0; + if (sc_cardinalities[i] > 0) c++; + if (sc_cardinalities[i+1] > 0) c++; + if (sc_cardinalities[i+2] > 0) c++; + if (c >= 2) + return true; + } + } + else { + for (unsigned i = 0; i < sz; i += 2) { + if (sc_cardinalities[i] > 0 && sc_cardinalities[i+1] > 0) + return true; + } + } + return false; + } + + /** + \brief Isolate roots of p in the given interval using sign conditions to distinguish between them. We need this method when the polynomial contains roots that are infinitesimally close to each other. For example, given an infinitesimal eps, the polynomial (x - 1)(x - 1 - eps) == (1 + eps) + (- 2 - eps)*x + x^2 has two roots 1 and 1+eps, we can't isolate these roots using intervals with binary rational end points. @@ -1327,19 +1631,225 @@ namespace realclosure { Remark: the polynomials do not need to be the derivatives of p. We use derivatives because a simple sequential search can be used to find the set of polynomials that can be used to distinguish between the different roots. + + \pre num_roots is the number of roots in the given interval */ - void sign_det_isolate_roots(scoped_polynomial_seq & TaQ_1, unsigned num_roots, mpbqi const & interval, numeral_vector & roots) { + void sign_det_isolate_roots(unsigned p_sz, value * const * p, int num_roots, mpbqi const & interval, numeral_vector & roots) { SASSERT(num_roots >= 2); - SASSERT(TaQ_1.size() > 0); - unsigned n = TaQ_1.size(0); - value * const * p = TaQ_1.coeffs(0); + scoped_polynomial_seq der_seq(*this); + mk_derivatives(p_sz, p, der_seq); - scoped_polynomial_seq ds(*this); - mk_derivatives(n, p, ds); + CASSERT("rcf_isolate_roots", TaQ_1(p_sz, p, interval) == num_roots); + scoped_mpz_matrix M_s(mm()); + mm().mk(1, 1, M_s); + M_s.set(0, 0, 1); - // TODO continue + // Sequence of polynomials associated with each row of M_s + scoped_polynomial_seq prs(*this); + value * one_p[] = { one() }; + prs.push(1, one_p); // start with the polynomial one + + // For i in [0, poly_rows.size()), taqrs[i] == TaQ(prs[i], p; interval) + int_buffer taqrs; + taqrs.push_back(num_roots); + + // Sequence of polynomials used to discriminate the roots of p + scoped_polynomial_seq qs(*this); + + // Sequence of sign conditions associated with the columns of M_s + // These are sign conditions on the polynomials in qs. + scoped_sign_conditions scs(*this); + scs.push_back(0); + + // Starting configuration + // + // M_s = {{1}} Matrix of size 1x1 containing the value 1 + // prs = [1] Sequence of size 1 containing the constant polynomial 1 (one is always the first element of this sequence) + // taqrs = [num_roots] Sequence of size 1 containing the integer num_roots + // scs = [0] Sequence of size 1 with the empty sign condition (i.e., NULL pointer) + // qs = {} Empty set + // + + scoped_mpz_matrix new_M_s(mm()); + int_buffer new_taqrs; + scoped_polynomial_seq new_prs(*this); + scoped_sign_conditions new_scs(*this); + + int_buffer sc_cardinalities; + unsigned_buffer cols_to_keep; + unsigned_buffer new_row_idxs; + + unsigned i = der_seq.size(); + // We perform the search backwards because the degrees are in decreasing order. + while (i > 0) { + --i; + checkpoint(); + SASSERT(M_s.m() == M_s.n()); + SASSERT(M_s.m() == taqrs.size()); + SASSERT(M_s.m() == scs.size()); + TRACE("rcf_sign_det", + tout << M_s; + for (unsigned j = 0; j < scs.size(); j++) { + display_sign_conditions(tout, scs[j]); + tout << " = " << taqrs[j] << "\n"; + } + tout << "qs:\n"; + for (unsigned j = 0; j < qs.size(); j++) { + display_poly(tout, qs.size(j), qs.coeffs(j)); tout << "\n"; + }); + // We keep executing this loop until we have only one root for each sign condition in scs. + // When this happens the polynomials in qs are the ones used to discriminate the roots of p. + unsigned q_sz = der_seq.size(i); + value * const * q = der_seq.coeffs(i); + // q is a derivative of p. + int q_eq_0, q_gt_0, q_lt_0; + value_ref_buffer q2(*this); + count_signs_at_zeros(p_sz, p, q_sz, q, interval, num_roots, q_eq_0, q_gt_0, q_lt_0, q2); + TRACE("rcf_sign_det", + tout << "q: "; display_poly(tout, q_sz, q); tout << "\n"; + tout << "#(q == 0): " << q_eq_0 << ", #(q > 0): " << q_gt_0 << ", #(q < 0): " << q_lt_0 << "\n";); + bool use_q2; + scoped_mpz_matrix M(mm()); + if (q_eq_0 > 0 && q_gt_0 > 0 && q_lt_0 == 0) { + // M <- {{1, 1}, + // {0, 1}} + mm().mk(2,2,M); + M.set(0,0, 1); M.set(0,1, 1); + M.set(1,0, 0); M.set(1,1, 1); + use_q2 = false; + } + else if (q_eq_0 > 0 && q_gt_0 == 0 && q_lt_0 > 0) { + // M <- {{1, 1}, + // {0, -1}} + mm().mk(2,2,M); + M.set(0,0, 1); M.set(0,1, 1); + M.set(1,0, 0); M.set(1,1, -1); + use_q2 = false; + } + else if (q_eq_0 == 0 && q_gt_0 > 0 && q_lt_0 > 0) { + // M <- {{1, 1}, + // {1, -1}} + mm().mk(2,2,M); + M.set(0,0, 1); M.set(0,1, 1); + M.set(1,0, 1); M.set(1,1, -1); + use_q2 = false; + } + else if (q_eq_0 > 0 && q_gt_0 > 0 && q_lt_0 > 0) { + // M <- {{1, 1, 1}, + // {0, 1, -1}, + // {0, 1, 1}} + mm().mk(3,3,M); + M.set(0,0, 1); M.set(0,1, 1); M.set(0,2, 1); + M.set(1,0, 0); M.set(1,1, 1); M.set(1,2, -1); + M.set(2,0, 0); M.set(2,1, 1); M.set(2,2, 1); + use_q2 = true; + SASSERT(q2.size() > 0); + } + else { + // skip q since its sign does not discriminate the roots of p + continue; + } + mm().tensor_product(M_s, M, new_M_s); + expand_taqrs(taqrs, prs, p_sz, p, q_sz, q, use_q2, q2.size(), q2.c_ptr(), interval, + // ---> + new_taqrs, new_prs); + SASSERT(new_M_s.n() == new_M_s.m()); // it is a square matrix + SASSERT(new_M_s.m() == new_taqrs.size()); + SASSERT(new_M_s.m() == new_prs.size()); + // The system must have a solution + sc_cardinalities.resize(new_taqrs.size()); + // Solve + // new_M_s * sc_cardinalities = new_taqrs + VERIFY(mm().solve(new_M_s, sc_cardinalities.c_ptr(), new_taqrs.c_ptr())); + // The solution must contain only positive values <= num_roots + DEBUG_CODE(for (unsigned j = 0; j < sc_cardinalities.size(); j++) { SASSERT(0 < sc_cardinalities[j] && sc_cardinalities[j] <= num_roots); }); + // We should keep q only if it discriminated something. + // That is, + // If !use_q2, then There is an i s.t. sc_cardinalities[2*i] > 0 && sc_cardinalities[2*i] > 0 + // If use_q2, then There is an i s.t. AtLeatTwo(sc_cardinalities[3*i] > 0, sc_cardinalities[3*i+1] > 0, sc_cardinalities[3*i+2] > 0) + if (!keep_new_sc_assignment(sc_cardinalities.size(), sc_cardinalities.c_ptr(), use_q2)) { + // skip q since it did not reduced the cardinality of the existing sign conditions. + continue; + } + // keep q + unsigned q_idx = qs.size(); + qs.push(q_sz, q); + // We remove the columns associated with sign conditions that have cardinality zero, + // and create new extended sign condition objects for the ones that have cardinality > 0. + cols_to_keep.reset(); + unsigned j = 0; unsigned k = 0; + unsigned step_sz = use_q2 ? 3 : 2; + bool all_one = true; + while (j < sc_cardinalities.size()) { + sign_condition * sc = scs[k]; + k++; + for (unsigned s = 0; s < step_sz; s++) { + // Remark: the second row of M contains the sign for q + if (sc_cardinalities[j] > 0) { + new_scs.push_back(mk_sign_condition(q_idx, M.get_int(1, s), sc)); + cols_to_keep.push_back(j); + } + if (sc_cardinalities[j] > 1) + all_one = false; + j++; + } + } + // Update scs with new_scs + scs.copy_from(new_scs); + SASSERT(new_scs.empty()); + // Update M_s + mm().filter_cols(new_M_s, cols_to_keep.size(), cols_to_keep.c_ptr(), M_s); + SASSERT(new_M_s.n() == cols_to_keep.size()); + new_row_idxs.resize(cols_to_keep.size(), 0); + unsigned new_num_rows = mm().linear_independent_rows(M_s, new_row_idxs.c_ptr()); + SASSERT(new_num_rows == cols_to_keep.size()); + // Update taqrs and prs + prs.reset(); + taqrs.reset(); + for (unsigned j = 0; j < new_num_rows; j++) { + unsigned rid = new_row_idxs[j]; + prs.push(new_prs.size(rid), new_prs.coeffs(rid)); + taqrs.push_back(new_taqrs[rid]); + } + if (all_one) { + // Stop each remaining sign condition in scs has cardinality one + // So, they are discriminating the roots of p. + break; + } + } + TRACE("rcf_sign_det", + tout << "Final state\n"; + tout << M_s; + for (unsigned j = 0; j < scs.size(); j++) { + display_sign_conditions(tout, scs[j]); + tout << " = " << taqrs[j] << "\n"; + } + tout << "qs:\n"; + for (unsigned j = 0; j < qs.size(); j++) { + display_poly(tout, qs.size(j), qs.coeffs(j)); tout << "\n"; + }); + + // TODO: create the extension objects using + // M_s, prs, scs, qs + // Remark: scs and qs are only for pretty printing + // For determining the signs of other polynomials in the new extension objects, + // we only need M_s and prs } + /** + \brief Return true if p is a polynomial of the form a_{n-1}*x^{n-1} + a_0 + */ + bool is_nz_binomial(unsigned n, value * const * p) { + SASSERT(n >= 2); + SASSERT(!is_zero(p[0])); + SASSERT(!is_zero(p[n-1])); + for (unsigned i = 1; i < n - 1; i++) { + if (!is_zero(p[i])) + return false; + } + return true; + } + /** \brief Root isolation for polynomials that are - nonlinear (degree > 2) @@ -1366,7 +1876,27 @@ namespace realclosure { tout << " < pos-roots < "; if (has_pos_upper) tout << "2^" << pos_upper_N; else tout << "oo"; tout << "\n";); + // Compute the number of positive and negative roots + scoped_polynomial_seq seq(*this); + sturm_seq(n, p, seq); + scoped_mpbqi minf_zero(bqim()); + set_lower_inf(minf_zero); + set_upper_zero(minf_zero); + int num_neg_roots = TaQ(seq, minf_zero); + scoped_mpbqi zero_inf(bqim()); + set_lower_zero(zero_inf); + set_upper_inf(zero_inf); + int num_pos_roots = TaQ(seq, zero_inf); + TRACE("rcf_isolate", + tout << "num_neg_roots: " << num_neg_roots << "\n"; + tout << "num_pos_roots: " << num_pos_roots << "\n";); // TODO + + // Test sign_det_isolate_roots + if (num_neg_roots > 1) + sign_det_isolate_roots(n, p, num_neg_roots, minf_zero, roots); + if (num_pos_roots > 1) + sign_det_isolate_roots(n, p, num_pos_roots, zero_inf, roots); } /** @@ -1457,17 +1987,45 @@ namespace realclosure { return sign(a.m_value); } + /** + \brief Return true the given rational function value is actually an integer. + + \pre a is a rational function (algebraic) extension. + + \remark If a is actually an integer, this method is also update its representation. + */ + bool is_algebraic_int(numeral const & a) { + SASSERT(is_rational_function(a)); + SASSERT(to_rational_function(a)->ext()->is_algebraic()); + + // TODO + return false; + } + + /** + \brief Return true if a is an integer + */ bool is_int(numeral const & a) { if (is_zero(a)) return true; else if (is_nz_rational(a)) return qm().is_int(to_mpq(a)); else { - // TODO - return false; + rational_function_value * rf = to_rational_function(a); + switch (rf->ext()->knd()) { + case extension::TRANSCENDENTAL: return false; + case extension::INFINITESIMAL: return false; + case extension::ALGEBRAIC: return is_algebraic_int(a); + default: + UNREACHABLE(); + return false; + } } } + /** + \brief Return true if v does not depend on infinitesimal extensions. + */ bool is_real(value * v) const { if (is_zero(v) || is_nz_rational(v)) return true; @@ -1475,6 +2033,9 @@ namespace realclosure { return to_rational_function(v)->is_real(); } + /** + \brief Return true if a does not depend on infinitesimal extensions. + */ bool is_real(numeral const & a) const { return is_real(a.m_value); } @@ -1482,7 +2043,11 @@ namespace realclosure { static void swap(mpbqi & a, mpbqi & b) { realclosure::swap(a, b); } - + + /** + \brief Store in interval an approximation of the rational number q with precision k. + interval has binary rational end-points and the width is <= 1/2^k + */ void mpq_to_mpbqi(mpq const & q, mpbqi & interval, unsigned k) { interval.set_lower_is_inf(false); interval.set_upper_is_inf(false); @@ -1554,6 +2119,9 @@ namespace realclosure { update_mpq_value(a.m_value, v); } + /** + \brief a <- n + */ void set(numeral & a, int n) { if (n == 0) { reset(a); @@ -1566,6 +2134,9 @@ namespace realclosure { update_mpq_value(a, n); } + /** + \brief a <- n + */ void set(numeral & a, mpz const & n) { if (qm().is_zero(n)) { reset(a); @@ -1578,6 +2149,9 @@ namespace realclosure { update_mpq_value(a, n); } + /** + \brief a <- n + */ void set(numeral & a, mpq const & n) { if (qm().is_zero(n)) { reset(a); @@ -1589,12 +2163,18 @@ namespace realclosure { update_mpq_value(a, n); } + /** + \brief a <- n + */ void set(numeral & a, numeral const & n) { inc_ref(n.m_value); dec_ref(a.m_value); a.m_value = n.m_value; } + /** + \brief a <- b^{1/k} + */ void root(numeral const & a, unsigned k, numeral & b) { if (k == 0) throw exception("0-th root is indeterminate"); @@ -1619,6 +2199,9 @@ namespace realclosure { // TODO: invoke isolate_roots } + /** + \brief a <- b^k + */ void power(numeral const & a, unsigned k, numeral & b) { unsigned mask = 1; value_ref power(*this); @@ -1648,6 +2231,8 @@ namespace realclosure { \brief r <- p1 + p2 */ void add(unsigned sz1, value * const * p1, unsigned sz2, value * const * p2, value_ref_buffer & r) { + SASSERT(p1 != r.c_ptr()); + SASSERT(p2 != r.c_ptr()); r.reset(); value_ref a_i(*this); unsigned min = std::min(sz1, sz2); @@ -1668,6 +2253,7 @@ namespace realclosure { \brief r <- p + a */ void add(unsigned sz, value * const * p, value * a, value_ref_buffer & r) { + SASSERT(p != r.c_ptr()); SASSERT(sz > 0); r.reset(); value_ref a_0(*this); @@ -1681,6 +2267,8 @@ namespace realclosure { \brief r <- p1 - p2 */ void sub(unsigned sz1, value * const * p1, unsigned sz2, value * const * p2, value_ref_buffer & r) { + SASSERT(p1 != r.c_ptr()); + SASSERT(p2 != r.c_ptr()); r.reset(); value_ref a_i(*this); unsigned min = std::min(sz1, sz2); @@ -1703,6 +2291,7 @@ namespace realclosure { \brief r <- p - a */ void sub(unsigned sz, value * const * p, value * a, value_ref_buffer & r) { + SASSERT(p != r.c_ptr()); SASSERT(sz > 0); r.reset(); value_ref a_0(*this); @@ -1716,6 +2305,7 @@ namespace realclosure { \brief r <- a * p */ void mul(value * a, unsigned sz, value * const * p, value_ref_buffer & r) { + SASSERT(p != r.c_ptr()); r.reset(); if (a == 0) return; @@ -1730,6 +2320,8 @@ namespace realclosure { \brief r <- p1 * p2 */ void mul(unsigned sz1, value * const * p1, unsigned sz2, value * const * p2, value_ref_buffer & r) { + SASSERT(p1 != r.c_ptr()); + SASSERT(p2 != r.c_ptr()); r.reset(); unsigned sz = sz1*sz2; r.resize(sz); @@ -1840,6 +2432,8 @@ namespace realclosure { \brief r <- rem(p1, p2) */ void rem(unsigned sz1, value * const * p1, unsigned sz2, value * const * p2, value_ref_buffer & r) { + SASSERT(p1 != r.c_ptr()); + SASSERT(p2 != r.c_ptr()); TRACE("rcf_rem", tout << "rem\n"; display_poly(tout, sz1, p1); tout << "\n"; @@ -1878,6 +2472,7 @@ namespace realclosure { \brief r <- -p */ void neg(unsigned sz, value * const * p, value_ref_buffer & r) { + SASSERT(p != r.c_ptr()); r.reset(); value_ref a_i(*this); for (unsigned i = 0; i < sz; i++) { @@ -1917,6 +2512,8 @@ namespace realclosure { Signed remainder */ void srem(unsigned sz1, value * const * p1, unsigned sz2, value * const * p2, value_ref_buffer & r) { + SASSERT(p1 != r.c_ptr()); + SASSERT(p2 != r.c_ptr()); rem(sz1, p1, sz2, p2, r); neg(r); } @@ -2055,11 +2652,12 @@ namespace realclosure { */ void sturm_tarski_seq(unsigned sz1, value * const * p1, unsigned sz2, value * const * p2, scoped_polynomial_seq & seq) { seq.reset(); - value_ref_buffer p1p2(*this); + value_ref_buffer p1_prime(*this); + value_ref_buffer p1_prime_p2(*this); seq.push(sz1, p1); - derivative(sz1, p1, p1p2); - mul(p1p2.size(), p1p2.c_ptr(), sz2, p2, p1p2); - seq.push(p1p2.size(), p1p2.c_ptr()); + derivative(sz1, p1, p1_prime); + mul(p1_prime.size(), p1_prime.c_ptr(), sz2, p2, p1_prime_p2); + seq.push(p1_prime_p2.size(), p1_prime_p2.c_ptr()); sturm_seq_core(seq); } @@ -2205,6 +2803,7 @@ namespace realclosure { prec = -m; SASSERT(prec >= 1); while (prec <= m_max_precision) { + checkpoint(); if (!refine_coeffs_interval(n, p, prec)) { // Failed to refine intervals, p must depend on infinitesimal values. // This can happen even if all intervals of coefficients of p are bounded. @@ -2227,7 +2826,16 @@ namespace realclosure { PLUS_INF, MPBQ }; - + + /** + \brief Compute the number of sign variations at position (loc, b) in the given polynomial sequence. + The position (loc, b) should be interpreted in the following way: + + - (ZERO, *) -> number of sign variations at 0. + - (MINUS_INF, *) -> number of sign variations at -oo. + - (PLUS_INF, *) -> number of sign variations at oo. + - (MPBQ, b) -> number of sign variations at binary rational b. + */ unsigned sign_variations_at_core(scoped_polynomial_seq const & seq, location loc, mpbq const & b) { unsigned sz = seq.size(); if (sz <= 1) @@ -2316,7 +2924,33 @@ namespace realclosure { return a - b; } + + /** + \brief Return TaQ(Q, P; a, b) = + #{ x \in (a, b] | P(x) = 0 and Q(x) > 0 } + - + #{ x \in (a, b] | P(x) = 0 and Q(x) < 0 } + \remark This method ignores whether the interval end-points are closed or open. + */ + int TaQ(unsigned p_sz, value * const * p, unsigned q_sz, value * const * q, mpbqi const & interval) { + scoped_polynomial_seq seq(*this); + sturm_tarski_seq(p_sz, p, q_sz, q, seq); + return TaQ(seq, interval); + } + + /** + \brief Return TaQ(1, P; a, b) = + #{ x \in (a, b] | P(x) = 0 } + + \remark This method ignores whether the interval end-points are closed or open. + */ + int TaQ_1(unsigned p_sz, value * const * p, mpbqi const & interval) { + scoped_polynomial_seq seq(*this); + sturm_seq(p_sz, p, seq); + return TaQ(seq, interval); + } + void refine_rational_interval(rational_value * v, unsigned prec) { mpbqi & i = interval(v); if (!i.lower_is_open() && !i.lower_is_open()) { @@ -3362,21 +3996,48 @@ namespace realclosure { else out << " > 0"; } + + void display_sign_conditions(std::ostream & out, sign_condition * sc) const { + bool first = true; + out << "{"; + while (sc) { + if (first) + first = false; + else + out << ", "; + out << "q(" << sc->qidx() << ")"; + display_poly_sign(out, sc->sign()); + sc = sc->prev(); + } + out << "}"; + } + + void display_sign_conditions(std::ostream & out, sign_condition * sc, array const & qs, bool compact) const { + bool first = true; + out << "{"; + while (sc) { + if (first) + first = false; + else + out << ", "; + display_polynomial(out, qs[sc->qidx()], display_free_var_proc(), compact); + display_poly_sign(out, sc->sign()); + sc = sc->prev(); + } + out << "}"; + } void display_algebraic_def(std::ostream & out, algebraic * a, bool compact) const { out << "root("; display_polynomial(out, a->p(), display_free_var_proc(), compact); out << ", "; bqim().display(out, a->interval()); - out << ", {"; - signs const & s = a->s(); - for (unsigned i = 0; i < s.size(); i++) { - if (i > 0) - out << ", "; - display_polynomial(out, s[i].first, display_free_var_proc(), compact); - display_poly_sign(out, s[i].second); - } - out << "})"; + out << ", "; + if (a->sdt() != 0) + display_sign_conditions(out, a->sdt()->sc(a->sdt_idx()), a->sdt()->qs(), compact); + else + out << "{}"; + out << ")"; } void display_poly(std::ostream & out, unsigned n, value * const * p) const { From 191de6f7b50a48c93d6535195c4b2422bd504ae5 Mon Sep 17 00:00:00 2001 From: Leonardo de Moura Date: Thu, 10 Jan 2013 08:01:42 -0800 Subject: [PATCH 43/78] Fix test program Signed-off-by: Leonardo de Moura --- src/test/rcf.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/test/rcf.cpp b/src/test/rcf.cpp index 40c3e838f..ac240fa9d 100644 --- a/src/test/rcf.cpp +++ b/src/test/rcf.cpp @@ -134,7 +134,8 @@ static void tst_lin_indep(unsigned m, unsigned n, int _A[], unsigned ex_sz, unsi for (unsigned j = 0; j < n; j++) A.set(i, j, _A[i*n + j]); unsigned_vector r; - mm.linear_independent_rows(A, r); + r.resize(A.n()); + mm.linear_independent_rows(A, r.c_ptr()); SASSERT(r.size() == ex_sz); for (unsigned i = 0; i < ex_sz; i++) { SASSERT(r[i] == ex_r[i]); From eca78aa9c6f70596ba0e9fb8fc509962409b5dcf Mon Sep 17 00:00:00 2001 From: Leonardo de Moura Date: Thu, 10 Jan 2013 08:52:25 -0800 Subject: [PATCH 44/78] Fix incorrect assertions and bug Signed-off-by: Leonardo de Moura --- src/math/realclosure/mpz_matrix.cpp | 11 ++++++++++- src/math/realclosure/mpz_matrix.h | 4 +++- src/math/realclosure/realclosure.cpp | 11 ++++++++--- 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/src/math/realclosure/mpz_matrix.cpp b/src/math/realclosure/mpz_matrix.cpp index fcbad5695..836ac35c2 100644 --- a/src/math/realclosure/mpz_matrix.cpp +++ b/src/math/realclosure/mpz_matrix.cpp @@ -349,7 +349,7 @@ void mpz_matrix_manager::permute_rows(mpz_matrix const & A, unsigned const * p, B.swap(C); } -unsigned mpz_matrix_manager::linear_independent_rows(mpz_matrix const & _A, unsigned * r) { +unsigned mpz_matrix_manager::linear_independent_rows(mpz_matrix const & _A, unsigned * r, mpz_matrix & B) { unsigned r_sz = 0; scoped_mpz_matrix A(*this); scoped_mpz g(nm()); @@ -389,6 +389,15 @@ unsigned mpz_matrix_manager::linear_independent_rows(mpz_matrix const & _A, unsi k2++; } std::sort(r, r + r_sz); + // Copy linear independent rows to B + mpz_matrix & C = A; + mk(r_sz, _A.n, C); + for (unsigned i = 0; i < r_sz; i++ ) { + for (unsigned j = 0; j < _A.n; j++) { + nm().set(C(i, j), _A(r[i], j)); + } + } + B.swap(C); return r_sz; } diff --git a/src/math/realclosure/mpz_matrix.h b/src/math/realclosure/mpz_matrix.h index 5986e3a21..b48585c60 100644 --- a/src/math/realclosure/mpz_matrix.h +++ b/src/math/realclosure/mpz_matrix.h @@ -108,8 +108,10 @@ public: \remark The vector r must have at least A.n() capacity The numer of linear independent rows is returned. + + Store the new matrix in B. */ - unsigned linear_independent_rows(mpz_matrix const & A, unsigned * r); + unsigned linear_independent_rows(mpz_matrix const & A, unsigned * r, mpz_matrix & B); // method for debugging purposes void display(std::ostream & out, mpz_matrix const & A, unsigned cell_width=4) const; diff --git a/src/math/realclosure/realclosure.cpp b/src/math/realclosure/realclosure.cpp index 30fceede3..d970136c0 100644 --- a/src/math/realclosure/realclosure.cpp +++ b/src/math/realclosure/realclosure.cpp @@ -1761,8 +1761,9 @@ namespace realclosure { // Solve // new_M_s * sc_cardinalities = new_taqrs VERIFY(mm().solve(new_M_s, sc_cardinalities.c_ptr(), new_taqrs.c_ptr())); + TRACE("rcf_sign_det", tout << "solution: "; for (unsigned i = 0; i < sc_cardinalities.size(); i++) { tout << sc_cardinalities[i] << " "; } tout << "\n";); // The solution must contain only positive values <= num_roots - DEBUG_CODE(for (unsigned j = 0; j < sc_cardinalities.size(); j++) { SASSERT(0 < sc_cardinalities[j] && sc_cardinalities[j] <= num_roots); }); + DEBUG_CODE(for (unsigned j = 0; j < sc_cardinalities.size(); j++) { SASSERT(0 <= sc_cardinalities[j] && sc_cardinalities[j] <= num_roots); }); // We should keep q only if it discriminated something. // That is, // If !use_q2, then There is an i s.t. sc_cardinalities[2*i] > 0 && sc_cardinalities[2*i] > 0 @@ -1799,9 +1800,9 @@ namespace realclosure { SASSERT(new_scs.empty()); // Update M_s mm().filter_cols(new_M_s, cols_to_keep.size(), cols_to_keep.c_ptr(), M_s); - SASSERT(new_M_s.n() == cols_to_keep.size()); + SASSERT(M_s.n() == cols_to_keep.size()); new_row_idxs.resize(cols_to_keep.size(), 0); - unsigned new_num_rows = mm().linear_independent_rows(M_s, new_row_idxs.c_ptr()); + unsigned new_num_rows = mm().linear_independent_rows(M_s, new_row_idxs.c_ptr(), M_s); SASSERT(new_num_rows == cols_to_keep.size()); // Update taqrs and prs prs.reset(); @@ -1927,6 +1928,9 @@ namespace realclosure { \brief Root isolation for polynomials where 0 is not a root. */ void nz_isolate_roots(unsigned n, value * const * p, numeral_vector & roots) { + TRACE("rcf_isolate", + tout << "nz_isolate_roots\n"; + display_poly(tout, n, p); tout << "\n";); SASSERT(n > 0); SASSERT(!is_zero(p[0])); SASSERT(!is_zero(p[n-1])); @@ -1943,6 +1947,7 @@ namespace realclosure { \brief Root isolation entry point. */ void isolate_roots(unsigned n, numeral const * p, numeral_vector & roots) { + TRACE("rcf_isolate_bug", tout << "isolate_roots: "; for (unsigned i = 0; i < n; i++) { display(tout, p[i]); tout << " "; } tout << "\n";); SASSERT(n > 0); SASSERT(!is_zero(p[n-1])); if (n == 1) { From 872165fa553e49318e267b96d7263bc3f23286e3 Mon Sep 17 00:00:00 2001 From: Leonardo de Moura Date: Thu, 10 Jan 2013 09:17:22 -0800 Subject: [PATCH 45/78] Add more tracing to sign_det_isolate_roots Signed-off-by: Leonardo de Moura --- src/math/realclosure/realclosure.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/math/realclosure/realclosure.cpp b/src/math/realclosure/realclosure.cpp index d970136c0..35a7fcedc 100644 --- a/src/math/realclosure/realclosure.cpp +++ b/src/math/realclosure/realclosure.cpp @@ -1820,6 +1820,7 @@ namespace realclosure { } TRACE("rcf_sign_det", tout << "Final state\n"; + display_poly(tout, p_sz, p); tout << "\n"; tout << M_s; for (unsigned j = 0; j < scs.size(); j++) { display_sign_conditions(tout, scs[j]); @@ -1828,6 +1829,10 @@ namespace realclosure { tout << "qs:\n"; for (unsigned j = 0; j < qs.size(); j++) { display_poly(tout, qs.size(j), qs.coeffs(j)); tout << "\n"; + } + tout << "prs:\n"; + for (unsigned j = 0; j < prs.size(); j++) { + display_poly(tout, prs.size(j), prs.coeffs(j)); tout << "\n"; }); // TODO: create the extension objects using From 3482b8f4f1bcaa6025ca9e731d8cd05b42d91d95 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Thu, 10 Jan 2013 18:08:56 +0000 Subject: [PATCH 46/78] .NET API: bugfix Signed-off-by: Christoph M. Wintersteiger --- src/api/dotnet/AST.cs | 8 ++++++++ src/api/dotnet/Expr.cs | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/api/dotnet/AST.cs b/src/api/dotnet/AST.cs index 2c4c964df..7bc4f6dfb 100644 --- a/src/api/dotnet/AST.cs +++ b/src/api/dotnet/AST.cs @@ -149,6 +149,14 @@ namespace Microsoft.Z3 } } + /// + /// Indicates whether the AST is an application + /// + public bool IsApp + { + get { return this.ASTKind == Z3_ast_kind.Z3_APP_AST; } + } + /// /// Indicates whether the AST is a BoundVariable /// diff --git a/src/api/dotnet/Expr.cs b/src/api/dotnet/Expr.cs index 9f5c04f0b..b2927e2c3 100644 --- a/src/api/dotnet/Expr.cs +++ b/src/api/dotnet/Expr.cs @@ -215,7 +215,7 @@ namespace Microsoft.Z3 /// public bool IsConst { - get { return IsExpr && NumArgs == 0 && FuncDecl.DomainSize == 0; } + get { return IsApp && NumArgs == 0 && FuncDecl.DomainSize == 0; } } #endregion From dd127c2f7194caed03096853050526fb141eeeeb Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Thu, 10 Jan 2013 18:16:29 +0000 Subject: [PATCH 47/78] Java API: bugfix Signed-off-by: Christoph M. Wintersteiger --- src/api/java/AST.java | 8 ++++++++ src/api/java/Expr.java | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/api/java/AST.java b/src/api/java/AST.java index b7d048a1e..60ff48ecb 100644 --- a/src/api/java/AST.java +++ b/src/api/java/AST.java @@ -138,6 +138,14 @@ public class AST extends Z3Object } } + /** + * Indicates whether the AST is an application + **/ + public boolean IsApp() throws Z3Exception + { + return this.ASTKind() == Z3_ast_kind.Z3_APP_AST; + } + /** * Indicates whether the AST is a BoundVariable **/ diff --git a/src/api/java/Expr.java b/src/api/java/Expr.java index a67d94e7e..09456307f 100644 --- a/src/api/java/Expr.java +++ b/src/api/java/Expr.java @@ -204,7 +204,7 @@ public class Expr extends AST **/ public boolean IsConst() throws Z3Exception { - return IsExpr() && NumArgs() == 0 && FuncDecl().DomainSize() == 0; + return IsApp() && NumArgs() == 0 && FuncDecl().DomainSize() == 0; } /** From 4a0b431cf41930e55682eadf5fcc134c1a7d9454 Mon Sep 17 00:00:00 2001 From: Leonardo de Moura Date: Thu, 10 Jan 2013 11:13:21 -0800 Subject: [PATCH 48/78] Add mk_algebraic method Signed-off-by: Leonardo de Moura --- src/math/realclosure/realclosure.cpp | 73 ++++++++++++++++++++++++---- src/util/array.h | 12 ++++- 2 files changed, 74 insertions(+), 11 deletions(-) diff --git a/src/math/realclosure/realclosure.cpp b/src/math/realclosure/realclosure.cpp index 35a7fcedc..1e9b1ee1a 100644 --- a/src/math/realclosure/realclosure.cpp +++ b/src/math/realclosure/realclosure.cpp @@ -275,15 +275,15 @@ namespace realclosure { struct algebraic : public extension { polynomial m_p; sign_det * m_sign_det; //!< != 0 if m_interval constains more than one root of m_p. - unsigned m_idx; //!< != UINT_MAX if m_sign_det != 0, in this case m_idx < m_sign_det->m_sign_conditions.size() + unsigned m_sdt_idx; //!< != UINT_MAX if m_sign_det != 0, in this case m_sdt_idx < m_sign_det->m_sign_conditions.size() bool m_real; //!< True if the polynomial p does not depend on infinitesimal extensions. - algebraic(unsigned idx):extension(ALGEBRAIC, idx), m_sign_det(0), m_idx(0), m_real(false) {} + algebraic(unsigned idx):extension(ALGEBRAIC, idx), m_sign_det(0), m_sdt_idx(0), m_real(false) {} polynomial const & p() const { return m_p; } bool is_real() const { return m_real; } sign_det * sdt() const { return m_sign_det; } - unsigned sdt_idx() const { return m_idx; } + unsigned sdt_idx() const { return m_sdt_idx; } }; struct transcendental : public extension { @@ -445,6 +445,7 @@ namespace realclosure { m_scs.append(scs.m_scs.size(), scs.m_scs.c_ptr()); scs.release(); } + sign_condition * const * c_ptr() { return m_scs.c_ptr(); } }; imp(unsynch_mpq_manager & qm, params_ref const & p, small_object_allocator * a): @@ -639,6 +640,11 @@ namespace realclosure { cleanup(extension::INFINITESIMAL); return m_extensions[extension::INFINITESIMAL].size(); } + + unsigned next_algebraic_idx() { + cleanup(extension::ALGEBRAIC); + return m_extensions[extension::ALGEBRAIC].size(); + } void set_cancel(bool f) { m_cancel = f; @@ -1620,6 +1626,53 @@ namespace realclosure { return false; } + /** + \brief Store the polynomials in prs into the array of polynomials ps. + */ + void set_array_p(array & ps, scoped_polynomial_seq const & prs) { + unsigned sz = prs.size(); + ps.set(allocator(), sz, polynomial()); + for (unsigned i = 0; i < sz; i++) { + unsigned pi_sz = prs.size(i); + value * const * pi = prs.coeffs(i); + set_p(ps[i], pi_sz, pi); + } + } + + /** + \brief Create a "sign determination" data-structure for an algebraic extension. + + The new object will assume the ownership of the elements stored in M and scs. + M and scs will be empty after this operation. + */ + sign_det * mk_sign_det(mpz_matrix & M, scoped_polynomial_seq const & prs, scoped_polynomial_seq const & qs, scoped_sign_conditions & scs) { + sign_det * r = new (allocator()) sign_det(); + r->M.swap(M); + set_array_p(r->m_prs, prs); + set_array_p(r->m_qs, qs); + r->m_sign_conditions.set(allocator(), scs.size(), scs.c_ptr()); + scs.release(); + return r; + } + + /** + \brief Create a new algebraic extension + */ + algebraic * mk_algebraic(unsigned p_sz, value * const * p, mpbqi const & interval, sign_det * sd, unsigned sdt_idx) { + unsigned idx = next_algebraic_idx(); + algebraic * r = new (allocator()) algebraic(idx); + m_extensions[extension::ALGEBRAIC].push_back(r); + + set_p(r->m_p, p_sz, p); + set_interval(r->m_interval, interval); + r->m_sign_det = sd; + inc_ref_sign_det(sd); + r->m_sdt_idx = sdt_idx; + r->m_real = is_real(p_sz, p); + + return r; + } + /** \brief Isolate roots of p in the given interval using sign conditions to distinguish between them. We need this method when the polynomial contains roots that are infinitesimally close to each other. @@ -1834,12 +1887,14 @@ namespace realclosure { for (unsigned j = 0; j < prs.size(); j++) { display_poly(tout, prs.size(j), prs.coeffs(j)); tout << "\n"; }); - - // TODO: create the extension objects using - // M_s, prs, scs, qs - // Remark: scs and qs are only for pretty printing - // For determining the signs of other polynomials in the new extension objects, - // we only need M_s and prs + SASSERT(M_s.n() == M_s.m()); SASSERT(M_s.n() == static_cast(num_roots)); + sign_det * sd = mk_sign_det(M_s, prs, qs, scs); + for (unsigned idx = 0; idx < static_cast(num_roots); idx++) { + algebraic * a = mk_algebraic(p_sz, p, interval, sd, idx); + numeral r; + set(r, mk_rational_function_value(a)); + roots.push_back(r); + } } /** diff --git a/src/util/array.h b/src/util/array.h index 6f179713f..c2358d1a4 100644 --- a/src/util/array.h +++ b/src/util/array.h @@ -53,11 +53,11 @@ private: set_data(mem, sz); } - void init() { + void init(T const & v) { iterator it = begin(); iterator e = end(); for (; it != e; ++it) { - new (it) T(); + new (it) T(v); } } @@ -140,6 +140,13 @@ public: init(vs); } + template + void set(Allocator & a, size_t sz, T const & v = T()) { + SASSERT(m_data == 0); + allocate(a, sz); + init(v); + } + size_t size() const { if (m_data == 0) { return 0; @@ -181,6 +188,7 @@ public: void swap(array & other) { std::swap(m_data, other.m_data); } + }; template From 1a7d39f9a0b6e6146128ddf349e394b250341a09 Mon Sep 17 00:00:00 2001 From: Leonardo de Moura Date: Thu, 10 Jan 2013 12:09:07 -0800 Subject: [PATCH 49/78] Add refine_algebraic_interval Signed-off-by: Leonardo de Moura --- src/math/realclosure/realclosure.cpp | 62 +++++++++++++++++++++++++++- 1 file changed, 60 insertions(+), 2 deletions(-) diff --git a/src/math/realclosure/realclosure.cpp b/src/math/realclosure/realclosure.cpp index 1e9b1ee1a..5dd229fbf 100644 --- a/src/math/realclosure/realclosure.cpp +++ b/src/math/realclosure/realclosure.cpp @@ -3184,9 +3184,67 @@ namespace realclosure { } } + bool refine_algebraic_interval(algebraic * a, unsigned prec) { + if (a->sdt() != 0) { + // we can't bisect the interval, since it contains more than one root. + return false; + } + else { + mpbqi & a_i = a->interval(); + SASSERT(!a_i.lower_is_inf() && !a_i.upper_is_inf()); + int lower_sign = INT_MIN; + while (!check_precision(a_i, prec)) { + checkpoint(); + SASSERT(!bqm().eq(a_i.lower(), a_i.upper())); + scoped_mpbq m(bqm()); + bqm().add(a_i.lower(), a_i.upper(), m); + bqm().div2(m); + int mid_sign = eval_sign_at(a->p().size(), a->p().c_ptr(), m); + if (mid_sign == 0) { + // found the actual root + // set interval [m, m] + set_lower(a_i, m, false); + set_upper(a_i, m, false); + return true; + } + else { + SASSERT(mid_sign == 1 || mid_sign == -1); + if (lower_sign == INT_MIN) { + // initialize lower_sign + lower_sign = eval_sign_at(a->p().size(), a->p().c_ptr(), a_i.lower()); + } + SASSERT(lower_sign == 1 || lower_sign == -1); + if (mid_sign == lower_sign) { + // improved lower bound + set_lower(a_i, m); + } + else { + // improved upper bound + set_upper(a_i, m); + } + } + } + return true; + } + } + bool refine_algebraic_interval(rational_function_value * v, unsigned prec) { - // TODO - return false; + SASSERT(v->ext()->is_algebraic()); + polynomial const & n = v->num(); + polynomial const & d = v->den(); + unsigned _prec = prec; + while (true) { + if (!refine_coeffs_interval(n, _prec) || + !refine_coeffs_interval(d, _prec) || + !refine_algebraic_interval(to_algebraic(v->ext()), _prec)) + return false; + + update_rf_interval(v, prec); + TRACE("rcf_algebraic", tout << "after update_rf_interval: " << magnitude(v->interval()) << " "; bqim().display(tout, v->interval()); tout << std::endl;); + if (check_precision(v->interval(), prec)) + return true; + _prec++; + } } /** From 71ab7759d1a6976b1dca675a891f10b3cd4a5dce Mon Sep 17 00:00:00 2001 From: Leonardo de Moura Date: Thu, 10 Jan 2013 12:23:37 -0800 Subject: [PATCH 50/78] Add root method (syntax sugar for isolate_roots) Signed-off-by: Leonardo de Moura --- src/math/realclosure/realclosure.cpp | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/math/realclosure/realclosure.cpp b/src/math/realclosure/realclosure.cpp index 5dd229fbf..dc15c18bf 100644 --- a/src/math/realclosure/realclosure.cpp +++ b/src/math/realclosure/realclosure.cpp @@ -800,6 +800,11 @@ namespace realclosure { a.m_value = 0; } + void del(numeral_vector & v) { + for (unsigned i = 0; i < v.size(); i++) + del(v[i]); + } + /** \brief Return true if the given interval is smaller than 1/2^k */ @@ -2261,7 +2266,18 @@ namespace realclosure { p.push_back(0); p.push_back(one()); - // TODO: invoke isolate_roots + numeral_vector roots; + nz_isolate_roots(p.size(), p.c_ptr(), roots); + SASSERT(roots.size() == 1 || roots.size() == 2); + if (roots.size() == 1 || sign(roots[0].m_value) > 0) { + set(b, roots[0]); + } + else { + SASSERT(roots.size() == 2); + SASSERT(sign(roots[1].m_value) > 0); + set(b, roots[1]); + } + del(roots); } /** From 191e5034187461ab4402e4891fb0a9d064633467 Mon Sep 17 00:00:00 2001 From: Leonardo de Moura Date: Thu, 10 Jan 2013 12:51:54 -0800 Subject: [PATCH 51/78] Fix bug. Improve nl_nz_sqf_isolate_roots Signed-off-by: Leonardo de Moura --- src/math/realclosure/realclosure.cpp | 104 +++++++++++++++++++++++---- 1 file changed, 92 insertions(+), 12 deletions(-) diff --git a/src/math/realclosure/realclosure.cpp b/src/math/realclosure/realclosure.cpp index dc15c18bf..4fd22eb67 100644 --- a/src/math/realclosure/realclosure.cpp +++ b/src/math/realclosure/realclosure.cpp @@ -1446,7 +1446,7 @@ namespace realclosure { bool neg_root_upper_bound(unsigned n, value * const * p, int & N) { value_ref_buffer q(*this); reverse(n, p, q); - if (pos_root_lower_bound(n, q.c_ptr(), N)) { + if (neg_root_lower_bound(n, q.c_ptr(), N)) { N = -N; return true; } @@ -1678,6 +1678,16 @@ namespace realclosure { return r; } + /** + \brief Add a new root of p that is isolated by (interval, sd, sdt_idx) to roots. + */ + void add_root(unsigned p_sz, value * const * p, mpbqi const & interval, sign_det * sd, unsigned sdt_idx, numeral_vector & roots) { + algebraic * a = mk_algebraic(p_sz, p, interval, sd, sdt_idx); + numeral r; + set(r, mk_rational_function_value(a)); + roots.push_back(r); + } + /** \brief Isolate roots of p in the given interval using sign conditions to distinguish between them. We need this method when the polynomial contains roots that are infinitesimally close to each other. @@ -1895,10 +1905,7 @@ namespace realclosure { SASSERT(M_s.n() == M_s.m()); SASSERT(M_s.n() == static_cast(num_roots)); sign_det * sd = mk_sign_det(M_s, prs, qs, scs); for (unsigned idx = 0; idx < static_cast(num_roots); idx++) { - algebraic * a = mk_algebraic(p_sz, p, interval, sd, idx); - numeral r; - set(r, mk_rational_function_value(a)); - roots.push_back(r); + add_root(p_sz, p, interval, sd, idx, roots); } } @@ -1915,6 +1922,63 @@ namespace realclosure { } return true; } + + /** + \brief magnitude -> mpbq + */ + void magnitude_to_mpbq(int mag, bool sign, mpbq & r) { + if (mag < 0) { + bqm().set(r, mpbq(1, -mag)); + } + else { + bqm().set(r, mpbq(2)); + bqm().power(r, mag); + } + if (sign) + bqm().neg(r); + } + + /** + \brief Convert magnitudes for negative roots lower and upper bounds into an interval. + */ + void mk_neg_interval(bool has_neg_lower, int neg_lower_N, bool has_neg_upper, int neg_upper_N, mpbqi & r) { + scoped_mpbq aux(bqm()); + if (!has_neg_lower) { + set_lower_inf(r); + } + else { + magnitude_to_mpbq(neg_lower_N, true, aux); + set_lower(r, aux); + } + if (!has_neg_upper) { + set_upper_zero(r); + } + else { + magnitude_to_mpbq(neg_upper_N, true, aux); + set_upper(r, aux); + } + } + + /** + \brief Convert magnitudes for negative roots lower and upper bounds into an interval. + */ + void mk_pos_interval(bool has_pos_lower, int pos_lower_N, bool has_pos_upper, int pos_upper_N, mpbqi & r) { + scoped_mpbq aux(bqm()); + if (!has_pos_lower) { + set_lower_zero(r); + } + else { + magnitude_to_mpbq(pos_lower_N, false, aux); + set_lower(r, aux); + } + if (!has_pos_upper) { + set_upper_inf(r); + } + else { + magnitude_to_mpbq(pos_upper_N, false, aux); + set_upper(r, aux); + } + } /** \brief Root isolation for polynomials that are @@ -1956,13 +2020,29 @@ namespace realclosure { TRACE("rcf_isolate", tout << "num_neg_roots: " << num_neg_roots << "\n"; tout << "num_pos_roots: " << num_pos_roots << "\n";); - // TODO - - // Test sign_det_isolate_roots - if (num_neg_roots > 1) - sign_det_isolate_roots(n, p, num_neg_roots, minf_zero, roots); - if (num_pos_roots > 1) - sign_det_isolate_roots(n, p, num_pos_roots, zero_inf, roots); + scoped_mpbqi pos_interval(bqim()); + scoped_mpbqi neg_interval(bqim()); + mk_neg_interval(has_neg_lower, neg_lower_N, has_neg_upper, neg_upper_N, neg_interval); + mk_pos_interval(has_pos_lower, pos_lower_N, has_pos_upper, pos_upper_N, pos_interval); + if (num_neg_roots > 0) { + if (num_neg_roots == 1) { + add_root(n, p, neg_interval, 0, UINT_MAX, roots); + } + else { + // TODO bisection + sign_det_isolate_roots(n, p, num_neg_roots, minf_zero, roots); + } + } + + if (num_pos_roots > 0) { + if (num_pos_roots == 1) { + add_root(n, p, pos_interval, 0, UINT_MAX, roots); + } + else { + // TODO bisection + sign_det_isolate_roots(n, p, num_pos_roots, zero_inf, roots); + } + } } /** From 4cd29987437a9a7ebf989f8e211d73304d4df820 Mon Sep 17 00:00:00 2001 From: Leonardo de Moura Date: Thu, 10 Jan 2013 13:05:47 -0800 Subject: [PATCH 52/78] Add power operator to C and Python RCF APIs Signed-off-by: Leonardo de Moura --- src/api/api_rcf.cpp | 11 +++++++++++ src/api/python/z3rcf.py | 6 ++++++ src/api/z3_rcf.h | 7 +++++++ 3 files changed, 24 insertions(+) diff --git a/src/api/api_rcf.cpp b/src/api/api_rcf.cpp index c79dfbf20..a7feada42 100644 --- a/src/api/api_rcf.cpp +++ b/src/api/api_rcf.cpp @@ -203,6 +203,17 @@ extern "C" { Z3_CATCH_RETURN(0); } + Z3_rcf_num Z3_API Z3_rcf_power(Z3_context c, Z3_rcf_num a, unsigned k) { + Z3_TRY; + LOG_Z3_rcf_power(c, a, k); + RESET_ERROR_CODE(); + reset_rcf_cancel(c); + rcnumeral r; + rcfm(c).power(to_rcnumeral(a), k, r); + RETURN_Z3(from_rcnumeral(r)); + Z3_CATCH_RETURN(0); + } + Z3_bool Z3_API Z3_rcf_lt(Z3_context c, Z3_rcf_num a, Z3_rcf_num b) { Z3_TRY; LOG_Z3_rcf_lt(c, a, b); diff --git a/src/api/python/z3rcf.py b/src/api/python/z3rcf.py index 2ff29dc9f..dd0696594 100644 --- a/src/api/python/z3rcf.py +++ b/src/api/python/z3rcf.py @@ -102,6 +102,12 @@ class RCFNum: def __neg__(self): return self.__rsub__(0) + def power(self, k): + return RCFNum(Z3_rcf_power(self.ctx_ref(), self.num, k), self.ctx) + + def __pow__(self, k): + return self.power(k) + def decimal(self, prec=5): return Z3_rcf_num_to_decimal_string(self.ctx_ref(), self.num, prec) diff --git a/src/api/z3_rcf.h b/src/api/z3_rcf.h index 1bca7e391..87f376117 100644 --- a/src/api/z3_rcf.h +++ b/src/api/z3_rcf.h @@ -121,6 +121,13 @@ extern "C" { */ Z3_rcf_num Z3_API Z3_rcf_inv(__in Z3_context c, __in Z3_rcf_num a); + /** + \brief Return the value a^k + + def_API('Z3_rcf_power', RCF_NUM, (_in(CONTEXT), _in(RCF_NUM), _in(UINT))) + */ + Z3_rcf_num Z3_API Z3_rcf_power(__in Z3_context c, __in Z3_rcf_num a, __in unsigned k); + /** \brief Return Z3_TRUE if a < b From 619e597174957d30bc1efacf8b6f08c88f17f830 Mon Sep 17 00:00:00 2001 From: Leonardo de Moura Date: Thu, 10 Jan 2013 13:38:51 -0800 Subject: [PATCH 53/78] Add normalize_algebraic Signed-off-by: Leonardo de Moura --- src/math/realclosure/realclosure.cpp | 63 ++++++++++++++++++++++++---- 1 file changed, 56 insertions(+), 7 deletions(-) diff --git a/src/math/realclosure/realclosure.cpp b/src/math/realclosure/realclosure.cpp index 4fd22eb67..e837cbed8 100644 --- a/src/math/realclosure/realclosure.cpp +++ b/src/math/realclosure/realclosure.cpp @@ -3580,7 +3580,7 @@ namespace realclosure { - new_p1 <- one; new_p2 <- p2/p1[0]; IF sz1 == 1 - new_p1 <- p1/gcd(p1, p2); new_p2 <- p2/gcd(p1, p2); Otherwise */ - void normalize(unsigned sz1, value * const * p1, unsigned sz2, value * const * p2, value_ref_buffer & new_p1, value_ref_buffer & new_p2) { + void normalize_fraction(unsigned sz1, value * const * p1, unsigned sz2, value * const * p2, value_ref_buffer & new_p1, value_ref_buffer & new_p2) { SASSERT(sz1 > 0 && sz2 > 0); if (sz2 == 1) { // - new_p1 <- p1/p2[0]; new_p2 <- one IF sz2 == 1 @@ -3630,6 +3630,40 @@ namespace realclosure { } } + /** + \brief Simplify p1(x) using x's defining polynomial. + + By definition of polynomial division, we have: + + new_p1(x) == quotient(p1,p)(x) * p(x) + rem(p1,p)(x) + + Since p(x) == 0, we have that + + new_p1(x) = rem(p1,p)(x) + */ + void normalize_algebraic(algebraic * x, unsigned sz1, value * const * p1, value_ref_buffer & new_p1) { + polynomial const & p = x->p(); + rem(sz1, p1, p.size(), p.c_ptr(), new_p1); + } + + /** + \brief Apply normalize_algebraic (if applicable) & normalize_fraction. + */ + void normalize_all(extension * x, unsigned sz1, value * const * p1, unsigned sz2, value * const * p2, value_ref_buffer & new_p1, value_ref_buffer & new_p2) { + if (x->is_algebraic()) { + value_ref_buffer p1_norm(*this); + value_ref_buffer p2_norm(*this); + // FUTURE: we don't need to invoke normalize_algebraic if degree of p1 < degree x->p() + normalize_algebraic(to_algebraic(x), sz1, p1, p1_norm); + // FUTURE: we don't need to invoke normalize_algebraic if degree of p2 < degree x->p() + normalize_algebraic(to_algebraic(x), sz2, p2, p2_norm); + normalize_fraction(p1_norm.size(), p1_norm.c_ptr(), p2_norm.size(), p2_norm.c_ptr(), new_p1, new_p2); + } + else { + normalize_fraction(sz1, p1, sz2, p2, new_p1, new_p2); + } + } + /** \brief Create a new value using the a->ext(), and the given numerator and denominator. Use interval(a) + interval(b) as an initial approximation for the interval of the result, and invoke determine_sign() @@ -3692,7 +3726,7 @@ namespace realclosure { else { value_ref_buffer new_num(*this); value_ref_buffer new_den(*this); - normalize(num.size(), num.c_ptr(), ad.size(), ad.c_ptr(), new_num, new_den); + normalize_all(a->ext(), num.size(), num.c_ptr(), ad.size(), ad.c_ptr(), new_num, new_den); mk_add_value(a, b, new_num.size(), new_num.c_ptr(), new_den.size(), new_den.c_ptr(), r); } } @@ -3712,8 +3746,14 @@ namespace realclosure { add(an.size(), an.c_ptr(), bn.size(), bn.c_ptr(), new_num); if (new_num.empty()) r = 0; - else + else { + // We don't need to invoke normalize_algebraic even if x (== a->ext()) is algebraic. + // Reason: by construction the polynomials a->num() and b->num() are "normalized". + // That is, their degrees are < degree of the polynomial defining x. + // Moreover, when we add polynomials, the degree can only decrease. + // So, degree of new_num must be < degree of x's defining polynomial. mk_add_value(a, b, new_num.size(), new_num.c_ptr(), one.size(), one.c_ptr(), r); + } } /** @@ -3743,7 +3783,7 @@ namespace realclosure { mul(ad.size(), ad.c_ptr(), bd.size(), bd.c_ptr(), den); value_ref_buffer new_num(*this); value_ref_buffer new_den(*this); - normalize(num.size(), num.c_ptr(), den.size(), den.c_ptr(), new_num, new_den); + normalize_all(a->ext(), num.size(), num.c_ptr(), den.size(), den.c_ptr(), new_num, new_den); mk_add_value(a, b, new_num.size(), new_num.c_ptr(), new_den.size(), new_den.c_ptr(), r); } } @@ -3886,7 +3926,7 @@ namespace realclosure { SASSERT(num.size() == an.size()); value_ref_buffer new_num(*this); value_ref_buffer new_den(*this); - normalize(num.size(), num.c_ptr(), ad.size(), ad.c_ptr(), new_num, new_den); + normalize_all(a->ext(), num.size(), num.c_ptr(), ad.size(), ad.c_ptr(), new_num, new_den); mk_mul_value(a, b, new_num.size(), new_num.c_ptr(), new_den.size(), new_den.c_ptr(), r); } } @@ -3904,7 +3944,16 @@ namespace realclosure { value_ref_buffer new_num(*this); mul(an.size(), an.c_ptr(), bn.size(), bn.c_ptr(), new_num); SASSERT(!new_num.empty()); - mk_mul_value(a, b, new_num.size(), new_num.c_ptr(), one.size(), one.c_ptr(), r); + extension * x = a->ext(); + if (x->is_algebraic()) { + // FUTURE: we don't need to invoke normalize_algebraic if degree of new_num < degree x->p() + value_ref_buffer new_num2(*this); + normalize_algebraic(to_algebraic(x), new_num.size(), new_num.c_ptr(), new_num2); + mk_mul_value(a, b, new_num2.size(), new_num2.c_ptr(), one.size(), one.c_ptr(), r); + } + else { + mk_mul_value(a, b, new_num.size(), new_num.c_ptr(), one.size(), one.c_ptr(), r); + } } /** @@ -3927,7 +3976,7 @@ namespace realclosure { SASSERT(!num.empty()); SASSERT(!den.empty()); value_ref_buffer new_num(*this); value_ref_buffer new_den(*this); - normalize(num.size(), num.c_ptr(), den.size(), den.c_ptr(), new_num, new_den); + normalize_all(a->ext(), num.size(), num.c_ptr(), den.size(), den.c_ptr(), new_num, new_den); mk_mul_value(a, b, new_num.size(), new_num.c_ptr(), new_den.size(), new_den.c_ptr(), r); } } From 2f5c7c9ba93d2d9bce97137bdc4af58d4491ee47 Mon Sep 17 00:00:00 2001 From: Leonardo de Moura Date: Thu, 10 Jan 2013 17:07:32 -0800 Subject: [PATCH 54/78] Add determine_algebraic_sign Signed-off-by: Leonardo de Moura --- src/math/realclosure/realclosure.cpp | 233 ++++++++++++++++++++++----- 1 file changed, 195 insertions(+), 38 deletions(-) diff --git a/src/math/realclosure/realclosure.cpp b/src/math/realclosure/realclosure.cpp index e837cbed8..4e9d860b0 100644 --- a/src/math/realclosure/realclosure.cpp +++ b/src/math/realclosure/realclosure.cpp @@ -270,6 +270,7 @@ namespace realclosure { array const & qs() const { return m_qs; } sign_condition * sc(unsigned idx) const { return m_sign_conditions[idx]; } + unsigned num_roots() const { return m_prs.size(); } }; struct algebraic : public extension { @@ -284,6 +285,7 @@ namespace realclosure { bool is_real() const { return m_real; } sign_det * sdt() const { return m_sign_det; } unsigned sdt_idx() const { return m_sdt_idx; } + unsigned num_roots_inside_interval() const { return m_sign_det == 0 ? 1 : m_sign_det->num_roots(); } }; struct transcendental : public extension { @@ -2946,38 +2948,44 @@ namespace realclosure { \brief Return the sign of p(b) */ int eval_sign_at(unsigned n, value * const * p, mpbq const & b) { - scoped_mpbqi r(bqim()); - eval_sign_at_approx(n, p, b, r); - if (!bqim().contains_zero(r)) { - // we are done - return bqim().is_P(r) ? 1 : -1; - } - else if (!has_refineable_approx_coeffs(n, p)) { - return expensive_eval_sign_at(n, p, b); - } + if (n == 0) + return 0; + else if (n == 1) + return sign(p[0]); else { - int m = find_biggest_interval_magnitude(n, p); - unsigned prec; - if (m >= 0) - prec = 1; - else - prec = -m; - SASSERT(prec >= 1); - while (prec <= m_max_precision) { - checkpoint(); - if (!refine_coeffs_interval(n, p, prec)) { - // Failed to refine intervals, p must depend on infinitesimal values. - // This can happen even if all intervals of coefficients of p are bounded. - return expensive_eval_sign_at(n, p, b); - } - eval_sign_at_approx(n, p, b, r); - if (!bqim().contains_zero(r)) { - // we are done - return bqim().is_P(r) ? 1 : -1; - } - prec++; // increase precision and try again. + scoped_mpbqi r(bqim()); + eval_sign_at_approx(n, p, b, r); + if (!contains_zero(r)) { + // we are done + return bqim().is_P(r) ? 1 : -1; + } + else if (!has_refineable_approx_coeffs(n, p)) { + return expensive_eval_sign_at(n, p, b); + } + else { + int m = find_biggest_interval_magnitude(n, p); + unsigned prec; + if (m >= 0) + prec = 1; + else + prec = -m; + SASSERT(prec >= 1); + while (prec <= m_max_precision) { + checkpoint(); + if (!refine_coeffs_interval(n, p, prec)) { + // Failed to refine intervals, p must depend on infinitesimal values. + // This can happen even if all intervals of coefficients of p are bounded. + return expensive_eval_sign_at(n, p, b); + } + eval_sign_at_approx(n, p, b, r); + if (!contains_zero(r)) { + // we are done + return bqim().is_P(r) ? 1 : -1; + } + prec++; // increase precision and try again. + } + return expensive_eval_sign_at(n, p, b); } - return expensive_eval_sign_at(n, p, b); } } @@ -3544,9 +3552,144 @@ namespace realclosure { SASSERT(!contains_zero(v->interval())); } + /** + \brief Return true if x and q do not depend on infinitesimal values. + That is, q(x) does not depend on infinitesimal values. + */ + bool is_real(polynomial const & q, algebraic * x) { + return x->is_real() and is_real(q.size(), q.c_ptr()); + } + + /** + \brief This method is invoked when we know that q(x) is not zero and q(x) does not depend on infinitesimal values. + The procedure will keep refining the intervals associated with x and coefficients of q until + the interval of q(x) is of the form + (l > 0, u) + OR + (l, u < 0) + */ + void refine_until_sign_determined(polynomial const & q, algebraic * x, mpbqi & r) { + SASSERT(is_real(q, x)); + // If x and q do not depend on infinitesimals, must make sure that r satisfies our invariant + // for intervals of polynomial values that do not depend on infinitesimals. + // that is, + // Given r == (l, u), l != 0 and u != 0 + int m = magnitude(r); + unsigned prec; + if (m >= 0) + prec = m_ini_precision; + else + prec = -m; + while (true) { + checkpoint(); + VERIFY(refine_coeffs_interval(q, prec)); // can't fail because q does not depend on infinitesimals + VERIFY(refine_algebraic_interval(x, prec)); // can't fail because x does not depend on infinitesimals + // Update r + polynomial_interval(q, x->interval(), r); + // Since q and r do not depend on infinitesimals -oo and +oo will never be end-points. + SASSERT(!r.lower_is_inf()); + SASSERT(!r.upper_is_inf()); + if (!contains_zero(r) && !bqm().is_zero(r.lower()) && !bqm().is_zero(r.upper())) + return; // the interval r satisfies our requirements. + prec++; + } + } + + /** + \brief If q(x) != 0, return true and store in r an interval that contains the value q(x), but does not contain 0. + If q(x) == 0, return false + */ + bool expensive_algebraic_poly_interval(polynomial const & q, algebraic * x, mpbqi & r) { + polynomial_interval(q, x->interval(), r); + if (!contains_zero(r)) { + if (is_real(q, x) && (bqm().is_zero(r.lower()) || bqm().is_zero(r.upper()))) { + // we don't want intervals of the form (l, 0) and (0, u) when + // q(x) does not depend on infinitesimals. + refine_until_sign_determined(q, x, r); + } + return true; + } + int num_roots = x->num_roots_inside_interval(); + SASSERT(x->sdt() != 0 || num_roots == 1); + scoped_polynomial_seq seq(*this); + polynomial const & p = x->p(); + int taq = TaQ(p.size(), p.c_ptr(), q.size(), q.c_ptr(), x->interval()); + if (num_roots == 1 && taq == 0) + return false; // q(x) is zero + if (taq == num_roots) { + // q(x) is positive + if (is_real(q, x)) + refine_until_sign_determined(q, x, r); + else + set_lower_zero(r); + SASSERT(!contains_zero(r)); + return true; + } + else if (taq == -num_roots) { + // q(x) is negative + if (is_real(q, x)) + refine_until_sign_determined(q, x, r); + else + set_upper_zero(r); + SASSERT(!contains_zero(r)); + return true; + } + else { + SASSERT(num_roots > 1); + SASSERT(x->sdt() != 0); + + // TODO use sign_det data structure + + std::cout << "TODO\n"; + + return false; + } + } + + bool expensive_determine_algebraic_sign(rational_function_value * v) { + SASSERT(contains_zero(v->interval())); + SASSERT(v->ext()->is_algebraic()); + TRACE("rcf_algebraic_sign", + tout << "expensive_determine_algebraic_sign\n"; display(tout, v, false); + tout << "\ninterval: "; bqim().display(tout, v->interval()); tout << "\n";); + algebraic * x = to_algebraic(v->ext()); + scoped_mpbqi num_interval(bqim()); + if (!expensive_algebraic_poly_interval(v->num(), x, num_interval)) + return false; // it is zero + SASSERT(!contains_zero(num_interval)); + scoped_mpbqi den_interval(bqim()); + VERIFY(expensive_algebraic_poly_interval(v->den(), x, den_interval)); + SASSERT(!contains_zero(den_interval)); + div(num_interval, den_interval, m_ini_precision, v->interval()); + SASSERT(!contains_zero(v->interval())); + return true; // it is not zero + } + + /** + \brief Determine the sign of an rational function value + p(x)/q(x) when x is an algebraic extension. + */ bool determine_algebraic_sign(rational_function_value * v) { - // TODO - return false; + SASSERT(v->ext()->is_algebraic()); + mpbqi & interval = v->interval(); + if (interval.lower_is_inf() || interval.upper_is_inf()) { + return expensive_determine_algebraic_sign(v); + } + else { + int m = magnitude(v->interval()); + unsigned prec = 1; + if (m < 0) + prec = static_cast(-m) + 1; + while (contains_zero(v->interval())) { + if (!refine_algebraic_interval(v, prec)) + return expensive_determine_algebraic_sign(v); + prec++; + if (prec > m_max_precision) + return expensive_determine_algebraic_sign(v); + } + SASSERT(!contains_zero(v->interval())); + return true; + } } /** @@ -3655,9 +3798,14 @@ namespace realclosure { value_ref_buffer p2_norm(*this); // FUTURE: we don't need to invoke normalize_algebraic if degree of p1 < degree x->p() normalize_algebraic(to_algebraic(x), sz1, p1, p1_norm); - // FUTURE: we don't need to invoke normalize_algebraic if degree of p2 < degree x->p() - normalize_algebraic(to_algebraic(x), sz2, p2, p2_norm); - normalize_fraction(p1_norm.size(), p1_norm.c_ptr(), p2_norm.size(), p2_norm.c_ptr(), new_p1, new_p2); + if (p1_norm.empty()) { + new_p1.reset(); // result is 0 + } + else { + // FUTURE: we don't need to invoke normalize_algebraic if degree of p2 < degree x->p() + normalize_algebraic(to_algebraic(x), sz2, p2, p2_norm); + normalize_fraction(p1_norm.size(), p1_norm.c_ptr(), p2_norm.size(), p2_norm.c_ptr(), new_p1, new_p2); + } } else { normalize_fraction(sz1, p1, sz2, p2, new_p1, new_p2); @@ -3727,7 +3875,10 @@ namespace realclosure { value_ref_buffer new_num(*this); value_ref_buffer new_den(*this); normalize_all(a->ext(), num.size(), num.c_ptr(), ad.size(), ad.c_ptr(), new_num, new_den); - mk_add_value(a, b, new_num.size(), new_num.c_ptr(), new_den.size(), new_den.c_ptr(), r); + if (new_num.empty()) + r = 0; + else + mk_add_value(a, b, new_num.size(), new_num.c_ptr(), new_den.size(), new_den.c_ptr(), r); } } } @@ -3784,7 +3935,10 @@ namespace realclosure { value_ref_buffer new_num(*this); value_ref_buffer new_den(*this); normalize_all(a->ext(), num.size(), num.c_ptr(), den.size(), den.c_ptr(), new_num, new_den); - mk_add_value(a, b, new_num.size(), new_num.c_ptr(), new_den.size(), new_den.c_ptr(), r); + if (new_num.empty()) + r = 0; + else + mk_add_value(a, b, new_num.size(), new_num.c_ptr(), new_den.size(), new_den.c_ptr(), r); } } } @@ -3927,6 +4081,7 @@ namespace realclosure { value_ref_buffer new_num(*this); value_ref_buffer new_den(*this); normalize_all(a->ext(), num.size(), num.c_ptr(), ad.size(), ad.c_ptr(), new_num, new_den); + SASSERT(!new_num.empty()); mk_mul_value(a, b, new_num.size(), new_num.c_ptr(), new_den.size(), new_den.c_ptr(), r); } } @@ -3949,6 +4104,7 @@ namespace realclosure { // FUTURE: we don't need to invoke normalize_algebraic if degree of new_num < degree x->p() value_ref_buffer new_num2(*this); normalize_algebraic(to_algebraic(x), new_num.size(), new_num.c_ptr(), new_num2); + SASSERT(!new_num.empty()); mk_mul_value(a, b, new_num2.size(), new_num2.c_ptr(), one.size(), one.c_ptr(), r); } else { @@ -3977,6 +4133,7 @@ namespace realclosure { value_ref_buffer new_num(*this); value_ref_buffer new_den(*this); normalize_all(a->ext(), num.size(), num.c_ptr(), den.size(), den.c_ptr(), new_num, new_den); + SASSERT(!new_num.empty()); mk_mul_value(a, b, new_num.size(), new_num.c_ptr(), new_den.size(), new_den.c_ptr(), r); } } @@ -4135,7 +4292,7 @@ namespace realclosure { else if (is_nz_rational(a) && is_nz_rational(b)) return qm().lt(to_mpq(a), to_mpq(b)) ? -1 : 1; else { - // TODO: try to refine interval before switching to sub+sign approach + // FUTURE: try to refine interval before switching to sub+sign approach if (bqim().before(interval(a), interval(b))) return -1; else if (bqim().before(interval(b), interval(a))) From 714167a378123004f40310feeac4ce2b1c660411 Mon Sep 17 00:00:00 2001 From: Leonardo de Moura Date: Thu, 10 Jan 2013 18:36:47 -0800 Subject: [PATCH 55/78] Add more tracing Signed-off-by: Leonardo de Moura --- src/math/realclosure/realclosure.cpp | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/math/realclosure/realclosure.cpp b/src/math/realclosure/realclosure.cpp index 4e9d860b0..48f49f269 100644 --- a/src/math/realclosure/realclosure.cpp +++ b/src/math/realclosure/realclosure.cpp @@ -368,6 +368,9 @@ namespace realclosure { scoped_mpbq m_plus_inf_approx; // lower bound for binary rational intervals used to approximate an infinite positive value scoped_mpbq m_minus_inf_approx; // upper bound for binary rational intervals used to approximate an infinite negative value + // Tracing + unsigned m_exec_depth; + volatile bool m_cancel; struct scoped_polynomial_seq { @@ -450,6 +453,18 @@ namespace realclosure { sign_condition * const * c_ptr() { return m_scs.c_ptr(); } }; + struct scoped_inc_depth { + imp & m_imp; + scoped_inc_depth(imp & m):m_imp(m) { m_imp.m_exec_depth++; } + ~scoped_inc_depth() { m_imp.m_exec_depth--; } + }; + + #ifdef _TRACE + #define INC_DEPTH() scoped_inc_depth __inc(*this) + #else + #define INC_DEPTH() ((void) 0) + #endif + imp(unsynch_mpq_manager & qm, params_ref const & p, small_object_allocator * a): m_allocator(a == 0 ? alloc(small_object_allocator, "realclosure") : a), m_own_allocator(a == 0), @@ -465,6 +480,9 @@ namespace realclosure { inc_ref(m_one); m_pi = 0; m_e = 0; + + m_exec_depth = 0; + m_cancel = false; updt_params(p); @@ -3103,6 +3121,10 @@ namespace realclosure { \remark This method ignores whether the interval end-points are closed or open. */ int TaQ(unsigned p_sz, value * const * p, unsigned q_sz, value * const * q, mpbqi const & interval) { + INC_DEPTH(); + TRACE("rcf_TaQ", tout << "TaQ [" << m_exec_depth << "]\n"; + display_poly(tout, p_sz, p); tout << "\n"; + display_poly(tout, q_sz, q); tout << "\n";); scoped_polynomial_seq seq(*this); sturm_tarski_seq(p_sz, p, q_sz, q, seq); return TaQ(seq, interval); @@ -3115,6 +3137,9 @@ namespace realclosure { \remark This method ignores whether the interval end-points are closed or open. */ int TaQ_1(unsigned p_sz, value * const * p, mpbqi const & interval) { + INC_DEPTH(); + TRACE("rcf_TaQ", tout << "TaQ_1 [" << m_exec_depth << "]\n"; + display_poly(tout, p_sz, p); tout << "\n";); scoped_polynomial_seq seq(*this); sturm_seq(p_sz, p, seq); return TaQ(seq, interval); From 61b686f86f9729a3898fd48064ee4cf350480c6c Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Fri, 11 Jan 2013 11:15:18 +0000 Subject: [PATCH 56/78] FPA: fixes for sbits < ebits Signed-off-by: Christoph M. Wintersteiger --- src/tactic/fpa/fpa2bv_converter.cpp | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/tactic/fpa/fpa2bv_converter.cpp b/src/tactic/fpa/fpa2bv_converter.cpp index b53ad21eb..41871f3e1 100644 --- a/src/tactic/fpa/fpa2bv_converter.cpp +++ b/src/tactic/fpa/fpa2bv_converter.cpp @@ -237,15 +237,14 @@ void fpa2bv_converter::mk_pzero(func_decl *f, expr_ref & result) { void fpa2bv_converter::add_core(unsigned sbits, unsigned ebits, expr_ref & rm, expr_ref & c_sgn, expr_ref & c_sig, expr_ref & c_exp, expr_ref & d_sgn, expr_ref & d_sig, expr_ref & d_exp, expr_ref & res_sgn, expr_ref & res_sig, expr_ref & res_exp) -{ +{ // c/d are now such that c_exp >= d_exp. - expr_ref exp_delta(m); exp_delta = m_bv_util.mk_bv_sub(c_exp, d_exp); dbg_decouple("fpa2bv_add_exp_delta", exp_delta); - // cap the delta + // cap the delta expr_ref cap(m); cap = m_bv_util.mk_numeral(sbits+2, ebits); m_simp.mk_ite(m_bv_util.mk_ule(cap, exp_delta), cap, exp_delta, exp_delta); @@ -404,8 +403,7 @@ void fpa2bv_converter::mk_add(func_decl * f, unsigned num, expr * const * args, // Actual addition. unsigned ebits = m_util.get_ebits(f->get_range()); - unsigned sbits = m_util.get_sbits(f->get_range()); - SASSERT(ebits <= sbits); + unsigned sbits = m_util.get_sbits(f->get_range()); expr_ref a_sgn(m), a_sig(m), a_exp(m), b_sgn(m), b_sig(m), b_exp(m); unpack(x, a_sgn, a_sig, a_exp, true); @@ -1636,7 +1634,6 @@ void fpa2bv_converter::unpack(expr * e, expr_ref & sgn, expr_ref & sig, expr_ref mk_unbias(denormal_exp, denormal_exp); if (normalize) { - SASSERT(ebits <= sbits); expr_ref is_sig_zero(m), shift(m), lz(m); m_simp.mk_eq(m_bv_util.mk_numeral(0, sbits-1), sig, is_sig_zero); mk_leading_zeros(sig, ebits, lz); @@ -1644,9 +1641,14 @@ void fpa2bv_converter::unpack(expr * e, expr_ref & sgn, expr_ref & sig, expr_ref SASSERT(is_well_sorted(m, is_sig_zero)); SASSERT(is_well_sorted(m, lz)); SASSERT(is_well_sorted(m, shift)); - denormal_sig = m_bv_util.mk_bv_shl(denormal_sig, m_bv_util.mk_zero_extend(sbits-ebits, shift)); - // CMW: The book says we don't need this, but it feels wrong not to do that. - //denormal_exp = m_bv_util.mk_bv_sub(denormal_exp, shift); + if (ebits > sbits) { + expr_ref q(m); + q = m_bv_util.mk_zero_extend(sbits-ebits, shift); + denormal_sig = m_bv_util.mk_bv_shl(denormal_sig, q); + } + else { + denormal_sig = m_bv_util.mk_bv_shl(denormal_sig, shift); + } } SASSERT(is_well_sorted(m, normal_sig)); From 0de6b4cc923591b73318d2a1cb168459f9a902ab Mon Sep 17 00:00:00 2001 From: Leonardo de Moura Date: Fri, 11 Jan 2013 10:11:03 -0800 Subject: [PATCH 57/78] Complete the implementation of expensive_algebraic_poly_interval Signed-off-by: Leonardo de Moura --- src/math/realclosure/realclosure.cpp | 421 ++++++++++++++++++++------- 1 file changed, 317 insertions(+), 104 deletions(-) diff --git a/src/math/realclosure/realclosure.cpp b/src/math/realclosure/realclosure.cpp index 48f49f269..2a5db21d4 100644 --- a/src/math/realclosure/realclosure.cpp +++ b/src/math/realclosure/realclosure.cpp @@ -262,8 +262,9 @@ namespace realclosure { struct sign_det { unsigned m_ref_count; // sign_det objects may be shared between different roots of the same polynomial. - mpz_matrix M; // Matrix used in the sign determination + mpz_matrix M_s; // Matrix used in the sign determination array m_prs; // Polynomials associated with the rows of M + array m_taqrs; // Result of the tarski query for each polynomial in m_prs array m_sign_conditions; // Sign conditions associated with the columns of M array m_qs; // Polynomials used in the sign conditions. sign_det():m_ref_count(0) {} @@ -271,20 +272,22 @@ namespace realclosure { array const & qs() const { return m_qs; } sign_condition * sc(unsigned idx) const { return m_sign_conditions[idx]; } unsigned num_roots() const { return m_prs.size(); } + array const & taqrs() const { return m_taqrs; } + array const & prs() const { return m_prs; } }; struct algebraic : public extension { polynomial m_p; sign_det * m_sign_det; //!< != 0 if m_interval constains more than one root of m_p. - unsigned m_sdt_idx; //!< != UINT_MAX if m_sign_det != 0, in this case m_sdt_idx < m_sign_det->m_sign_conditions.size() + unsigned m_sc_idx; //!< != UINT_MAX if m_sign_det != 0, in this case m_sc_idx < m_sign_det->m_sign_conditions.size() bool m_real; //!< True if the polynomial p does not depend on infinitesimal extensions. - algebraic(unsigned idx):extension(ALGEBRAIC, idx), m_sign_det(0), m_sdt_idx(0), m_real(false) {} + algebraic(unsigned idx):extension(ALGEBRAIC, idx), m_sign_det(0), m_sc_idx(0), m_real(false) {} polynomial const & p() const { return m_p; } bool is_real() const { return m_real; } sign_det * sdt() const { return m_sign_det; } - unsigned sdt_idx() const { return m_sdt_idx; } + unsigned sc_idx() const { return m_sc_idx; } unsigned num_roots_inside_interval() const { return m_sign_det == 0 ? 1 : m_sign_det->num_roots(); } }; @@ -735,10 +738,11 @@ namespace realclosure { } void del_sign_det(sign_det * sd) { - mm().del(sd->M); + mm().del(sd->M_s); del_sign_conditions(sd->m_sign_conditions.size(), sd->m_sign_conditions.c_ptr()); sd->m_sign_conditions.finalize(allocator()); finalize(sd->m_prs); + sd->m_taqrs.finalize(allocator()); finalize(sd->m_qs); allocator().deallocate(sizeof(sign_det), sd); } @@ -1497,7 +1501,63 @@ namespace realclosure { ds.push(p_prime.size(), p_prime.c_ptr()); } } - + + + /** + \brief Auxiliary function for count_signs_at_zeros. (See comments at count_signs_at_zeros). + + - taq_p_q contains TaQ(p, q; interval) + */ + void count_signs_at_zeros_core(// Input values + int taq_p_q, + unsigned p_sz, value * const * p, // polynomial p + unsigned q_sz, value * const * q, // polynomial q + mpbqi const & interval, + int num_roots, // number of roots of p in the given interval + // Output values + int & q_eq_0, int & q_gt_0, int & q_lt_0, + value_ref_buffer & q2) { + if (taq_p_q == num_roots) { + // q is positive in all roots of p + q_eq_0 = 0; + q_gt_0 = num_roots; + q_lt_0 = 0; + } + else if (taq_p_q == -num_roots) { + // q is negative in all roots of p + q_eq_0 = 0; + q_gt_0 = 0; + q_lt_0 = num_roots; + } + if (taq_p_q == num_roots - 1) { + // The following assignment is the only possibility + q_eq_0 = 1; + q_gt_0 = num_roots - 1; + q_lt_0 = 0; + } + else if (taq_p_q == -(num_roots - 1)) { + // The following assignment is the only possibility + q_eq_0 = 1; + q_gt_0 = 0; + q_lt_0 = num_roots - 1; + } + else { + // Expensive case + // q2 <- q^2 + mul(q_sz, q, q_sz, q, q2); + int taq_p_q2 = TaQ(p_sz, p, q2.size(), q2.c_ptr(), interval); + SASSERT(0 <= taq_p_q2 && taq_p_q2 <= num_roots); + // taq_p_q2 == q_gt_0 + q_lt_0 + SASSERT((taq_p_q2 + taq_p_q) % 2 == 0); + SASSERT((taq_p_q2 - taq_p_q) % 2 == 0); + q_eq_0 = num_roots - taq_p_q2; + q_gt_0 = (taq_p_q2 + taq_p_q)/2; + q_lt_0 = (taq_p_q2 - taq_p_q)/2; + } + SASSERT(q_eq_0 + q_gt_0 + q_lt_0 == num_roots); + } + + /** \brief Given polynomials p and q, and an interval, compute the number of roots of p in the interval such that: @@ -1521,47 +1581,8 @@ namespace realclosure { tout << "p: "; display_poly(tout, p_sz, p); tout << "\n"; tout << "q: "; display_poly(tout, q_sz, q); tout << "\n";); SASSERT(num_roots > 0); - int r1 = TaQ(p_sz, p, q_sz, q, interval); - // r1 == q_gt_0 - q_lt_0 - SASSERT(-num_roots <= r1 && r1 <= num_roots); - if (r1 == num_roots) { - // q is positive in all roots of p - q_eq_0 = 0; - q_gt_0 = num_roots; - q_lt_0 = 0; - } - else if (r1 == -num_roots) { - // q is negative in all roots of p - q_eq_0 = 0; - q_gt_0 = 0; - q_lt_0 = num_roots; - } - else if (r1 == num_roots - 1) { - // The following assignment is the only possibility - q_eq_0 = 1; - q_gt_0 = num_roots - 1; - q_lt_0 = 0; - } - else if (r1 == -(num_roots - 1)) { - // The following assignment is the only possibility - q_eq_0 = 1; - q_gt_0 = 0; - q_lt_0 = num_roots - 1; - } - else { - // Expensive case - // q2 <- q^2 - mul(q_sz, q, q_sz, q, q2); - int r2 = TaQ(p_sz, p, q2.size(), q2.c_ptr(), interval); - SASSERT(0 <= r2 && r2 <= num_roots); - // r2 == q_gt_0 + q_lt_0 - SASSERT((r2 + r1) % 2 == 0); - SASSERT((r2 - r1) % 2 == 0); - q_eq_0 = num_roots - r2; - q_gt_0 = (r2 + r1)/2; - q_lt_0 = (r2 - r1)/2; - } - SASSERT(q_eq_0 + q_gt_0 + q_lt_0 == num_roots); + int taq_p_q = TaQ(p_sz, p, q_sz, q, interval); + count_signs_at_zeros_core(taq_p_q, p_sz, p, q_sz, q, interval, num_roots, q_eq_0, q_gt_0, q_lt_0, q2); } /** @@ -1670,10 +1691,11 @@ namespace realclosure { The new object will assume the ownership of the elements stored in M and scs. M and scs will be empty after this operation. */ - sign_det * mk_sign_det(mpz_matrix & M, scoped_polynomial_seq const & prs, scoped_polynomial_seq const & qs, scoped_sign_conditions & scs) { + sign_det * mk_sign_det(mpz_matrix & M_s, scoped_polynomial_seq const & prs, int_buffer const & taqrs, scoped_polynomial_seq const & qs, scoped_sign_conditions & scs) { sign_det * r = new (allocator()) sign_det(); - r->M.swap(M); + r->M_s.swap(M_s); set_array_p(r->m_prs, prs); + r->m_taqrs.set(allocator(), taqrs.size(), taqrs.c_ptr()); set_array_p(r->m_qs, qs); r->m_sign_conditions.set(allocator(), scs.size(), scs.c_ptr()); scs.release(); @@ -1683,7 +1705,7 @@ namespace realclosure { /** \brief Create a new algebraic extension */ - algebraic * mk_algebraic(unsigned p_sz, value * const * p, mpbqi const & interval, sign_det * sd, unsigned sdt_idx) { + algebraic * mk_algebraic(unsigned p_sz, value * const * p, mpbqi const & interval, sign_det * sd, unsigned sc_idx) { unsigned idx = next_algebraic_idx(); algebraic * r = new (allocator()) algebraic(idx); m_extensions[extension::ALGEBRAIC].push_back(r); @@ -1692,22 +1714,107 @@ namespace realclosure { set_interval(r->m_interval, interval); r->m_sign_det = sd; inc_ref_sign_det(sd); - r->m_sdt_idx = sdt_idx; + r->m_sc_idx = sc_idx; r->m_real = is_real(p_sz, p); return r; } /** - \brief Add a new root of p that is isolated by (interval, sd, sdt_idx) to roots. + \brief Add a new root of p that is isolated by (interval, sd, sc_idx) to roots. */ - void add_root(unsigned p_sz, value * const * p, mpbqi const & interval, sign_det * sd, unsigned sdt_idx, numeral_vector & roots) { - algebraic * a = mk_algebraic(p_sz, p, interval, sd, sdt_idx); + void add_root(unsigned p_sz, value * const * p, mpbqi const & interval, sign_det * sd, unsigned sc_idx, numeral_vector & roots) { + algebraic * a = mk_algebraic(p_sz, p, interval, sd, sc_idx); numeral r; set(r, mk_rational_function_value(a)); roots.push_back(r); } + /** + \brief Create (the square) matrix for sign determination of q on the roots of p. + It builds matrix based on the number of root of p where + q is == 0, > 0 and < 0. + The resultant matrix is stored in M. + + Return false if the sign of q is already determined, that is + only one of the q_eq_0, q_gt_0, q_lt_0 is greater than zero. + + If the return value is true, then the resultant matrix M has size 2x2 or 3x3 + + - q_eq_0 > 0, q_gt_0 > 0, q_lt_0 == 0 + M <- {{1, 1}, + {0, 1}} + + Meaning: + M . [ #(q == 0), #(q > 0) ]^t == [ TaQ(p, 1), TaQ(p, q) ]^t + + [ ... ]^t represents a column matrix. + + - q_eq_0 > 0, q_gt_0 == 0, q_lt_0 > 0 + M <- {{1, 1}, + {0, -1}} + + Meaning: + M . [ #(q == 0), #(q < 0) ]^t == [ TaQ(p, 1), TaQ(p, q) ]^t + + - q_eq_0 == 0, q_gt_0 > 0, q_lt_0 > 0 + M <- {{1, 1}, + {1, -1}} + + Meaning: + M . [ #(q > 0), #(q < 0) ]^t == [ TaQ(p, 1), TaQ(p, q) ]^t + + - q_eq_0 > 0, q_gt_0 > 0, q_lt_0 > 0 + M <- {{1, 1, 1}, + {0, 1, -1}, + {0, 1, 1}} + + Meaning: + M . [ #(q == 0), #(q > 0), #(q < 0) ]^t == [ TaQ(p, 1), TaQ(p, q), TaQ(p, q^2) ]^t + + */ + bool mk_sign_det_matrix(int q_eq_0, int q_gt_0, int q_lt_0, scoped_mpz_matrix & M) { + if (q_eq_0 > 0 && q_gt_0 > 0 && q_lt_0 == 0) { + // M <- {{1, 1}, + // {0, 1}} + mm().mk(2,2,M); + M.set(0,0, 1); M.set(0,1, 1); + M.set(1,0, 0); M.set(1,1, 1); + return true; + } + else if (q_eq_0 > 0 && q_gt_0 == 0 && q_lt_0 > 0) { + // M <- {{1, 1}, + // {0, -1}} + mm().mk(2,2,M); + M.set(0,0, 1); M.set(0,1, 1); + M.set(1,0, 0); M.set(1,1, -1); + return true; + } + else if (q_eq_0 == 0 && q_gt_0 > 0 && q_lt_0 > 0) { + // M <- {{1, 1}, + // {1, -1}} + mm().mk(2,2,M); + M.set(0,0, 1); M.set(0,1, 1); + M.set(1,0, 1); M.set(1,1, -1); + return true; + } + else if (q_eq_0 > 0 && q_gt_0 > 0 && q_lt_0 > 0) { + // M <- {{1, 1, 1}, + // {0, 1, -1}, + // {0, 1, 1}} + mm().mk(3,3,M); + M.set(0,0, 1); M.set(0,1, 1); M.set(0,2, 1); + M.set(1,0, 0); M.set(1,1, 1); M.set(1,2, -1); + M.set(2,0, 0); M.set(2,1, 1); M.set(2,2, 1); + return true; + } + else { + // Sign of q is already determined. + return false; + } + } + + /** \brief Isolate roots of p in the given interval using sign conditions to distinguish between them. We need this method when the polynomial contains roots that are infinitesimally close to each other. @@ -1796,47 +1903,12 @@ namespace realclosure { TRACE("rcf_sign_det", tout << "q: "; display_poly(tout, q_sz, q); tout << "\n"; tout << "#(q == 0): " << q_eq_0 << ", #(q > 0): " << q_gt_0 << ", #(q < 0): " << q_lt_0 << "\n";); - bool use_q2; scoped_mpz_matrix M(mm()); - if (q_eq_0 > 0 && q_gt_0 > 0 && q_lt_0 == 0) { - // M <- {{1, 1}, - // {0, 1}} - mm().mk(2,2,M); - M.set(0,0, 1); M.set(0,1, 1); - M.set(1,0, 0); M.set(1,1, 1); - use_q2 = false; - } - else if (q_eq_0 > 0 && q_gt_0 == 0 && q_lt_0 > 0) { - // M <- {{1, 1}, - // {0, -1}} - mm().mk(2,2,M); - M.set(0,0, 1); M.set(0,1, 1); - M.set(1,0, 0); M.set(1,1, -1); - use_q2 = false; - } - else if (q_eq_0 == 0 && q_gt_0 > 0 && q_lt_0 > 0) { - // M <- {{1, 1}, - // {1, -1}} - mm().mk(2,2,M); - M.set(0,0, 1); M.set(0,1, 1); - M.set(1,0, 1); M.set(1,1, -1); - use_q2 = false; - } - else if (q_eq_0 > 0 && q_gt_0 > 0 && q_lt_0 > 0) { - // M <- {{1, 1, 1}, - // {0, 1, -1}, - // {0, 1, 1}} - mm().mk(3,3,M); - M.set(0,0, 1); M.set(0,1, 1); M.set(0,2, 1); - M.set(1,0, 0); M.set(1,1, 1); M.set(1,2, -1); - M.set(2,0, 0); M.set(2,1, 1); M.set(2,2, 1); - use_q2 = true; - SASSERT(q2.size() > 0); - } - else { + if (!mk_sign_det_matrix(q_eq_0, q_gt_0, q_lt_0, M)) { // skip q since its sign does not discriminate the roots of p continue; } + bool use_q2 = M.n() == 3; mm().tensor_product(M_s, M, new_M_s); expand_taqrs(taqrs, prs, p_sz, p, q_sz, q, use_q2, q2.size(), q2.c_ptr(), interval, // ---> @@ -1923,7 +1995,7 @@ namespace realclosure { display_poly(tout, prs.size(j), prs.coeffs(j)); tout << "\n"; }); SASSERT(M_s.n() == M_s.m()); SASSERT(M_s.n() == static_cast(num_roots)); - sign_det * sd = mk_sign_det(M_s, prs, qs, scs); + sign_det * sd = mk_sign_det(M_s, prs, taqrs, qs, scs); for (unsigned idx = 0; idx < static_cast(num_roots); idx++) { add_root(p_sz, p, interval, sd, idx, roots); } @@ -2796,10 +2868,20 @@ namespace realclosure { \brief Keep expanding the Sturm sequence starting at seq */ void sturm_seq_core(scoped_polynomial_seq & seq) { + INC_DEPTH(); + SASSERT(seq.size() >= 2); + TRACE("rcf_sturm_seq", + unsigned sz = seq.size(); + tout << "sturm_seq_core [" << m_exec_depth << "]\n"; + display_poly(tout, seq.size(sz-2), seq.coeffs(sz-2)); tout << "\n"; + display_poly(tout, seq.size(sz-1), seq.coeffs(sz-1)); tout << "\n";); value_ref_buffer r(*this); while (true) { unsigned sz = seq.size(); srem(seq.size(sz-2), seq.coeffs(sz-2), seq.size(sz-1), seq.coeffs(sz-1), r); + TRACE("rcf_sturm_seq", + tout << "sturm_seq_core [" << m_exec_depth << "], new polynomial\n"; + display_poly(tout, r.size(), r.c_ptr()); tout << "\n";); if (r.empty()) return; seq.push(r.size(), r.c_ptr()); @@ -3636,12 +3718,11 @@ namespace realclosure { } int num_roots = x->num_roots_inside_interval(); SASSERT(x->sdt() != 0 || num_roots == 1); - scoped_polynomial_seq seq(*this); polynomial const & p = x->p(); - int taq = TaQ(p.size(), p.c_ptr(), q.size(), q.c_ptr(), x->interval()); - if (num_roots == 1 && taq == 0) + int taq_p_q = TaQ(p.size(), p.c_ptr(), q.size(), q.c_ptr(), x->interval()); + if (num_roots == 1 && taq_p_q == 0) return false; // q(x) is zero - if (taq == num_roots) { + if (taq_p_q == num_roots) { // q(x) is positive if (is_real(q, x)) refine_until_sign_determined(q, x, r); @@ -3650,7 +3731,7 @@ namespace realclosure { SASSERT(!contains_zero(r)); return true; } - else if (taq == -num_roots) { + else if (taq_p_q == -num_roots) { // q(x) is negative if (is_real(q, x)) refine_until_sign_determined(q, x, r); @@ -3662,12 +3743,144 @@ namespace realclosure { else { SASSERT(num_roots > 1); SASSERT(x->sdt() != 0); - - // TODO use sign_det data structure - - std::cout << "TODO\n"; - - return false; + int q_eq_0, q_gt_0, q_lt_0; + value_ref_buffer q2(*this); + count_signs_at_zeros_core(taq_p_q, p.size(), p.c_ptr(), q.size(), q.c_ptr(), x->interval(), num_roots, q_eq_0, q_gt_0, q_lt_0, q2); + if (q_eq_0 > 0 && q_gt_0 == 0 && q_lt_0 == 0) { + // q(x) is zero + return false; + } + else if (q_eq_0 == 0 && q_gt_0 > 0 && q_lt_0 == 0) { + // q(x) is positive + set_lower_zero(r); + return true; + } + else if (q_eq_0 == 0 && q_gt_0 == 0 && q_lt_0 > 0) { + // q(x) is negative + set_upper_zero(r); + return true; + } + else { + sign_det & sdt = *(x->sdt()); + // Remark: + // By definition of algebraic and sign_det, we know that + // sdt.M_s * [1, ..., 1]^t = sdt.taqrs()^t + // That is, + // [1, ..., 1]^t = sdt.M_s^-1 * sdt.taqrs()^t + // Moreover the number of roots in x->interval() is equal to the number of rows and columns in sdt.M_s. + // The column j of std.M_s is associated with the sign condition sdt.m_scs[j]. + // The row i of sdt.M_s is associated with the polynomial sdt.prs()[i]. + // + // The extension x is encoded using the sign condition x->sc_idx() of std.m_scs + // + scoped_mpz_matrix M(mm()); + VERIFY(mk_sign_det_matrix(q_eq_0, q_gt_0, q_lt_0, M)); + bool use_q2 = M.n() == 3; + scoped_mpz_matrix new_M_s(mm()); + mm().tensor_product(sdt.M_s, M, new_M_s); + array const & prs = sdt.prs(); // polynomials associated with the rows of M_s + array const & taqrs = sdt.taqrs(); // For each i in [0, taqrs.size()) TaQ(p, prs[i]; x->interval()) == taqrs[i] + SASSERT(prs.size() == taqrs.size()); + int_buffer new_taqrs; + value_ref_buffer prq(*this); + // fill new_taqrs using taqrs and the new tarski queries containing q (and q^2 when use_q2 == true). + for (unsigned i = 0; i < taqrs.size(); i++) { + // Add TaQ(p, prs[i] * 1; x->interval()) + new_taqrs.push_back(taqrs[i]); + // Add TaQ(p, prs[i] * q; x->interval()) + mul(prs[i].size(), prs[i].c_ptr(), q.size(), q.c_ptr(), prq); + new_taqrs.push_back(TaQ(p.size(), p.c_ptr(), prq.size(), prq.c_ptr(), x->interval())); + if (use_q2) { + // Add TaQ(p, prs[i] * q^2; x->interval()) + mul(prs[i].size(), prs[i].c_ptr(), q2.size(), q2.c_ptr(), prq); + new_taqrs.push_back(TaQ(p.size(), p.c_ptr(), prq.size(), prq.c_ptr(), x->interval())); + } + } + int_buffer sc_cardinalities; + sc_cardinalities.resize(new_taqrs.size()); + // Solve + // new_M_s * sc_cardinalities = new_taqrs + VERIFY(mm().solve(new_M_s, sc_cardinalities.c_ptr(), new_taqrs.c_ptr())); + DEBUG_CODE({ + // check if sc_cardinalities has the expected structure + // - contains only 0 or 1 + // - !use_q2 IMPLIES for all i in [0, taqrs.size()) (sc_cardinalities[2*i] == 1) + (sc_cardinalities[2*i + 1] == 1) == 1 + // - use_q2 IMPLIES for all i in [0, taqrs.size()) (sc_cardinalities[3*i] == 1) + (sc_cardinalities[3*i + 1] == 1) + (sc_cardinalities[3*i + 2] == 1) == 1 + for (unsigned i = 0; i < sc_cardinalities.size(); i++) { + SASSERT(sc_cardinalities[i] == 0 || sc_cardinalities[i] == 1); + } + if (!use_q2) { + for (unsigned i = 0; i < taqrs.size(); i++) { + SASSERT((sc_cardinalities[2*i] == 1) + (sc_cardinalities[2*i + 1] == 1) == 1); + } + } + else { + for (unsigned i = 0; i < taqrs.size(); i++) { + SASSERT((sc_cardinalities[3*i] == 1) + (sc_cardinalities[3*i + 1] == 1) + (sc_cardinalities[3*i + 2] == 1) == 1); + } + } + }); + // Remark: + // Note that we found the sign of q for every root of p in the interval x->interval() :) + unsigned sc_idx = x->sc_idx(); + if (use_q2) { + if (sc_cardinalities[3*sc_idx] == 1) { + // q(x) is zero + return false; + } + else if (sc_cardinalities[3*sc_idx + 1] == 1) { + // q(x) is positive + set_lower_zero(r); + return true; + } + else { + SASSERT(sc_cardinalities[3*sc_idx + 2] == 1); + // q(x) is negative + set_upper_zero(r); + return true; + } + } + else { + if (q_eq_0 == 0) { + if (sc_cardinalities[2*sc_idx] == 1) { + // q(x) is positive + set_lower_zero(r); + return true; + } + else { + SASSERT(sc_cardinalities[2*sc_idx + 1] == 1); + // q(x) is negative + set_upper_zero(r); + return true; + } + } + else if (q_gt_0 == 0) { + if (sc_cardinalities[2*sc_idx] == 1) { + // q(x) is zero + return false; + } + else { + SASSERT(sc_cardinalities[2*sc_idx + 1] == 1); + // q(x) is negative + set_upper_zero(r); + return true; + } + } + else { + SASSERT(q_lt_0 == 0); + if (sc_cardinalities[2*sc_idx] == 1) { + // q(x) is zero + return false; + } + else { + SASSERT(sc_cardinalities[2*sc_idx + 1] == 1); + // q(x) is positive + set_lower_zero(r); + return true; + } + } + } + } } } @@ -4484,7 +4697,7 @@ namespace realclosure { bqim().display(out, a->interval()); out << ", "; if (a->sdt() != 0) - display_sign_conditions(out, a->sdt()->sc(a->sdt_idx()), a->sdt()->qs(), compact); + display_sign_conditions(out, a->sdt()->sc(a->sc_idx()), a->sdt()->qs(), compact); else out << "{}"; out << ")"; From 5a9040a247f1a67ccbfaa044b4384c1ae02e1340 Mon Sep 17 00:00:00 2001 From: Leonardo de Moura Date: Fri, 11 Jan 2013 10:35:38 -0800 Subject: [PATCH 58/78] Replace is_real with depends_on_infinitesimals to avoid misunderstandings Signed-off-by: Leonardo de Moura --- src/math/realclosure/realclosure.cpp | 91 ++++++++++++---------------- src/math/realclosure/realclosure.h | 5 +- 2 files changed, 43 insertions(+), 53 deletions(-) diff --git a/src/math/realclosure/realclosure.cpp b/src/math/realclosure/realclosure.cpp index 2a5db21d4..8337fc7ba 100644 --- a/src/math/realclosure/realclosure.cpp +++ b/src/math/realclosure/realclosure.cpp @@ -179,8 +179,8 @@ namespace realclosure { polynomial m_numerator; polynomial m_denominator; extension * m_ext; - bool m_real; //!< True if the polynomial expression does not depend on infinitesimal values. - rational_function_value(extension * ext):value(false), m_ext(ext), m_real(false) {} + bool m_depends_on_infinitesimals; //!< True if the polynomial expression depends on infinitesimal values. + rational_function_value(extension * ext):value(false), m_ext(ext), m_depends_on_infinitesimals(false) {} polynomial const & num() const { return m_numerator; } polynomial & num() { return m_numerator; } @@ -188,8 +188,8 @@ namespace realclosure { polynomial & den() { return m_denominator; } extension * ext() const { return m_ext; } - bool is_real() const { return m_real; } - void set_real(bool f) { m_real = f; } + bool depends_on_infinitesimals() const { return m_depends_on_infinitesimals; } + void set_depends_on_infinitesimals(bool f) { m_depends_on_infinitesimals = f; } }; // --------------------------------- @@ -280,12 +280,12 @@ namespace realclosure { polynomial m_p; sign_det * m_sign_det; //!< != 0 if m_interval constains more than one root of m_p. unsigned m_sc_idx; //!< != UINT_MAX if m_sign_det != 0, in this case m_sc_idx < m_sign_det->m_sign_conditions.size() - bool m_real; //!< True if the polynomial p does not depend on infinitesimal extensions. + bool m_depends_on_infinitesimals; //!< True if the polynomial p depends on infinitesimal extensions. - algebraic(unsigned idx):extension(ALGEBRAIC, idx), m_sign_det(0), m_sc_idx(0), m_real(false) {} + algebraic(unsigned idx):extension(ALGEBRAIC, idx), m_sign_det(0), m_sc_idx(0), m_depends_on_infinitesimals(false) {} polynomial const & p() const { return m_p; } - bool is_real() const { return m_real; } + bool depends_on_infinitesimals() const { return m_depends_on_infinitesimals; } sign_det * sdt() const { return m_sign_det; } unsigned sc_idx() const { return m_sc_idx; } unsigned num_roots_inside_interval() const { return m_sign_det == 0 ? 1 : m_sign_det->num_roots(); } @@ -1000,25 +1000,24 @@ namespace realclosure { } /** - \brief Return True if the given extension is a Real value. - The result is approximate for algebraic extensions. - For algebraic extensions, we have - - true result is always correct (i.e., the extension is really a real value) - - false result is approximate (i.e., the extension may be a real value although it is a root of a polynomial that contains non-real coefficients) + \brief Return True if the given extension depends on infinitesimal extensions. + If it doesn't, then it is definitely a real value. + + If it does, then it may or may not be a real value. Example: Assume eps is an infinitesimal, and pi is 3.14... . Assume also that ext is the unique root between (3, 4) of the following polynomial: x^2 - (pi + eps)*x + pi*ext - Thus, x is pi, but the system will return false, since its defining polynomial has infinitesimal - coefficients. In the future, to make everything precise, we should be able to factor the polynomial + Thus, x is pi, but the system will return true, since its defining polynomial has infinitesimal + coefficients. In the future, we should be able to factor the polynomial above as (x - eps)*(x - pi) and then detect that x is actually the root of (x - pi). */ - bool is_real(extension * ext) { + bool depends_on_infinitesimals(extension * ext) { switch (ext->knd()) { - case extension::TRANSCENDENTAL: return true; - case extension::INFINITESIMAL: return false; - case extension::ALGEBRAIC: return to_algebraic(ext)->is_real(); + case extension::TRANSCENDENTAL: return false; + case extension::INFINITESIMAL: return true; + case extension::ALGEBRAIC: return to_algebraic(ext)->depends_on_infinitesimals(); default: UNREACHABLE(); return false; @@ -1028,18 +1027,18 @@ namespace realclosure { /** \brief Return true if v is definitely a real value. */ - bool is_real(value * v) { + bool depends_on_infinitesimals(value * v) const { if (is_zero(v) || is_nz_rational(v)) - return true; + return false; else - return to_rational_function(v)->is_real(); + return to_rational_function(v)->depends_on_infinitesimals(); } - bool is_real(unsigned sz, value * const * p) { + bool depends_on_infinitesimals(unsigned sz, value * const * p) const { for (unsigned i = 0; i < sz; i++) - if (!is_real(p[i])) - return false; - return true; + if (depends_on_infinitesimals(p[i])) + return true; + return false; } /** @@ -1164,7 +1163,7 @@ namespace realclosure { inc_ref_ext(ext); set_p(r->num(), num_sz, num); set_p(r->den(), den_sz, den); - r->set_real(is_real(ext) && is_real(num_sz, num) && is_real(den_sz, den)); + r->set_depends_on_infinitesimals(depends_on_infinitesimals(ext) || depends_on_infinitesimals(num_sz, num) || depends_on_infinitesimals(den_sz, den)); return r; } @@ -1193,7 +1192,7 @@ namespace realclosure { set(r, mk_rational_function_value(eps)); SASSERT(sign(r) > 0); - SASSERT(!is_real(r)); + SASSERT(depends_on_infinitesimals(r)); } void mk_infinitesimal(char const * n, numeral & r) { @@ -1244,7 +1243,7 @@ namespace realclosure { } set(r, mk_rational_function_value(t)); - SASSERT(is_real(r)); + SASSERT(!depends_on_infinitesimals(r)); } void mk_transcendental(char const * p, mk_interval & proc, numeral & r) { @@ -1715,7 +1714,7 @@ namespace realclosure { r->m_sign_det = sd; inc_ref_sign_det(sd); r->m_sc_idx = sc_idx; - r->m_real = is_real(p_sz, p); + r->m_depends_on_infinitesimals = depends_on_infinitesimals(p_sz, p); return r; } @@ -2266,20 +2265,10 @@ namespace realclosure { } /** - \brief Return true if v does not depend on infinitesimal extensions. + \brief Return true if a depends on infinitesimal extensions. */ - bool is_real(value * v) const { - if (is_zero(v) || is_nz_rational(v)) - return true; - else - return to_rational_function(v)->is_real(); - } - - /** - \brief Return true if a does not depend on infinitesimal extensions. - */ - bool is_real(numeral const & a) const { - return is_real(a.m_value); + bool depends_on_infinitesimals(numeral const & a) const { + return depends_on_infinitesimals(a.m_value); } static void swap(mpbqi & a, mpbqi & b) { @@ -3660,11 +3649,11 @@ namespace realclosure { } /** - \brief Return true if x and q do not depend on infinitesimal values. + \brief Return true if x and q depend on infinitesimal values. That is, q(x) does not depend on infinitesimal values. */ - bool is_real(polynomial const & q, algebraic * x) { - return x->is_real() and is_real(q.size(), q.c_ptr()); + bool depends_on_infinitesimals(polynomial const & q, algebraic * x) { + return x->depends_on_infinitesimals() || depends_on_infinitesimals(q.size(), q.c_ptr()); } /** @@ -3676,7 +3665,7 @@ namespace realclosure { (l, u < 0) */ void refine_until_sign_determined(polynomial const & q, algebraic * x, mpbqi & r) { - SASSERT(is_real(q, x)); + SASSERT(!depends_on_infinitesimals(q, x)); // If x and q do not depend on infinitesimals, must make sure that r satisfies our invariant // for intervals of polynomial values that do not depend on infinitesimals. // that is, @@ -3709,7 +3698,7 @@ namespace realclosure { bool expensive_algebraic_poly_interval(polynomial const & q, algebraic * x, mpbqi & r) { polynomial_interval(q, x->interval(), r); if (!contains_zero(r)) { - if (is_real(q, x) && (bqm().is_zero(r.lower()) || bqm().is_zero(r.upper()))) { + if (!depends_on_infinitesimals(q, x) && (bqm().is_zero(r.lower()) || bqm().is_zero(r.upper()))) { // we don't want intervals of the form (l, 0) and (0, u) when // q(x) does not depend on infinitesimals. refine_until_sign_determined(q, x, r); @@ -3724,7 +3713,7 @@ namespace realclosure { return false; // q(x) is zero if (taq_p_q == num_roots) { // q(x) is positive - if (is_real(q, x)) + if (!depends_on_infinitesimals(q, x)) refine_until_sign_determined(q, x, r); else set_lower_zero(r); @@ -3733,7 +3722,7 @@ namespace realclosure { } else if (taq_p_q == -num_roots) { // q(x) is negative - if (is_real(q, x)) + if (!depends_on_infinitesimals(q, x)) refine_until_sign_determined(q, x, r); else set_upper_zero(r); @@ -4896,8 +4885,8 @@ namespace realclosure { return m_imp->is_int(a); } - bool manager::is_real(numeral const & a) { - return m_imp->is_real(a); + bool manager::depends_on_infinitesimals(numeral const & a) { + return m_imp->depends_on_infinitesimals(a); } void manager::set(numeral & a, int n) { diff --git a/src/math/realclosure/realclosure.h b/src/math/realclosure/realclosure.h index 74d094411..b5e8574b2 100644 --- a/src/math/realclosure/realclosure.h +++ b/src/math/realclosure/realclosure.h @@ -133,9 +133,10 @@ namespace realclosure { bool is_int(numeral const & a); /** - \brief Return true if a is a real number. + \brief Return true if the representation of \c a depends on + infinitesimal extensions. */ - bool is_real(numeral const & a); + bool depends_on_infinitesimals(numeral const & a); /** \brief a <- n From f70de8dd47b5c64968a4342772c1a64f909b8554 Mon Sep 17 00:00:00 2001 From: Leonardo de Moura Date: Fri, 11 Jan 2013 16:28:19 -0800 Subject: [PATCH 59/78] Fix support for gmp Signed-off-by: Leonardo de Moura --- scripts/mk_util.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/scripts/mk_util.py b/scripts/mk_util.py index e4f860a95..5d30ca0fc 100644 --- a/scripts/mk_util.py +++ b/scripts/mk_util.py @@ -1320,11 +1320,13 @@ def mk_config(): check_ar() CXX = find_cxx_compiler() CC = find_c_compiler() + SLIBEXTRAFLAGS = '' if GMP: test_gmp(CXX) ARITH = "gmp" CPPFLAGS = '%s -D_MP_GMP' % CPPFLAGS LDFLAGS = '%s -lgmp' % LDFLAGS + SLIBEXTRAFLAGS = '%s -lgmp' % SLIBEXTRAFLAGS else: CPPFLAGS = '%s -D_MP_INTERNAL' % CPPFLAGS CXXFLAGS = '%s -c' % CXXFLAGS @@ -1332,10 +1334,9 @@ def mk_config(): if HAS_OMP: CXXFLAGS = '%s -fopenmp -mfpmath=sse' % CXXFLAGS LDFLAGS = '%s -fopenmp' % LDFLAGS - SLIBEXTRAFLAGS = '-fopenmp' + SLIBEXTRAFLAGS = '%s -fopenmp' % SLIBEXTRAFLAGS else: CXXFLAGS = '%s -D_NO_OMP_' % CXXFLAGS - SLIBEXTRAFLAGS = '' if DEBUG_MODE: CXXFLAGS = '%s -g -Wall' % CXXFLAGS else: From 3cc072f3a765bfe2b63bba0da40ed94fc8db401a Mon Sep 17 00:00:00 2001 From: Leonardo de Moura Date: Fri, 11 Jan 2013 16:28:39 -0800 Subject: [PATCH 60/78] Add bisect_isolate_roots Signed-off-by: Leonardo de Moura --- src/math/realclosure/realclosure.cpp | 172 +++++++++++++++++++++------ 1 file changed, 137 insertions(+), 35 deletions(-) diff --git a/src/math/realclosure/realclosure.cpp b/src/math/realclosure/realclosure.cpp index 8337fc7ba..3d4bfd3f7 100644 --- a/src/math/realclosure/realclosure.cpp +++ b/src/math/realclosure/realclosure.cpp @@ -79,10 +79,10 @@ namespace realclosure { struct interval { numeral m_lower; numeral m_upper; - unsigned m_lower_inf:1; - unsigned m_upper_inf:1; - unsigned m_lower_open:1; - unsigned m_upper_open:1; + unsigned char m_lower_inf; + unsigned char m_upper_inf; + unsigned char m_lower_open; + unsigned char m_upper_open; interval():m_lower_inf(true), m_upper_inf(true), m_lower_open(true), m_upper_open(true) {} interval(numeral & l, numeral & u):m_lower_inf(false), m_upper_inf(false), m_lower_open(true), m_upper_open(true) { swap(m_lower, l); @@ -136,9 +136,10 @@ namespace realclosure { void swap(mpbqi & a, mpbqi & b) { swap(a.m_lower, b.m_lower); swap(a.m_upper, b.m_upper); - unsigned tmp; - tmp = a.m_lower_inf; a.m_lower_inf = b.m_lower_inf; b.m_lower_inf = tmp; - tmp = a.m_upper_inf; a.m_upper_inf = b.m_upper_inf; b.m_upper_inf = tmp; + std::swap(a.m_lower_inf, b.m_lower_inf); + std::swap(a.m_upper_inf, b.m_upper_inf); + std::swap(a.m_lower_open, b.m_lower_open); + std::swap(a.m_upper_open, b.m_upper_open); } // --------------------------------- @@ -1729,6 +1730,14 @@ namespace realclosure { roots.push_back(r); } + /** + \brief Simpler version of add_root that does not use sign_det data-structure. That is, + interval contains only one root of p. + */ + void add_root(unsigned p_sz, value * const * p, mpbqi const & interval, numeral_vector & roots) { + add_root(p_sz, p, interval, 0, UINT_MAX, roots); + } + /** \brief Create (the square) matrix for sign determination of q on the roots of p. It builds matrix based on the number of root of p where @@ -2070,7 +2079,80 @@ namespace realclosure { set_upper(r, aux); } } + + struct bisect_ctx { + unsigned m_p_sz; + value * const * m_p; + bool m_depends_on_infinitesimals; + scoped_polynomial_seq & m_sturm_seq; + numeral_vector & m_result_roots; + bisect_ctx(unsigned p_sz, value * const * p, bool dinf, scoped_polynomial_seq & seq, numeral_vector & roots): + m_p_sz(p_sz), m_p(p), m_depends_on_infinitesimals(dinf), m_sturm_seq(seq), m_result_roots(roots) {} + }; + void bisect_isolate_roots(mpbqi & interval, int lower_sv, int upper_sv, bisect_ctx & ctx) { + SASSERT(lower_sv >= upper_sv); + int num_roots = lower_sv - upper_sv; + if (num_roots == 0) { + // interval does not contain roots + } + else if (num_roots == 1) { + // Sturm sequences are for half-open intervals (a, b] + // We must check if upper is the root + if (eval_sign_at(ctx.m_p_sz, ctx.m_p, interval.upper()) == 0) { + // found precise root + numeral r; + set(r, mk_rational(interval.upper())); + ctx.m_result_roots.push_back(r); + } + else { + // interval is an isolating interval + add_root(ctx.m_p_sz, ctx.m_p, interval, ctx.m_result_roots); + } + } + else if (ctx.m_depends_on_infinitesimals && check_precision(interval, m_max_precision)) { + // IF + // - The polynomial depends on infinitesimals + // - The interval contains more than one root + // - The size of the interval is less than 1/2^m_max_precision + // THEN + // - We switch to expensive sign determination procedure, since + // the roots may be infinitely close to each other. + // + sign_det_isolate_roots(ctx.m_p_sz, ctx.m_p, num_roots, interval, ctx.m_result_roots); + } + else { + scoped_mpbq mid(bqm()); + bqm().add(interval.lower(), interval.upper(), mid); + bqm().div2(mid); + int mid_sv = sign_variations_at(ctx.m_sturm_seq, mid); + scoped_mpbqi left_interval(bqim()); + scoped_mpbqi right_interval(bqim()); + set_lower(left_interval, interval.lower()); + set_upper(left_interval, mid); + set_lower(right_interval, mid); + set_upper(right_interval, interval.upper()); + bisect_isolate_roots(left_interval, lower_sv, mid_sv, ctx); + bisect_isolate_roots(right_interval, mid_sv, upper_sv, ctx); + } + } + + /** + \brief Entry point for the root isolation procedure based on bisection. + */ + void bisect_isolate_roots(// Input values + unsigned p_sz, value * const * p, mpbqi & interval, + // Extra Input values with already computed information + scoped_polynomial_seq & sturm_seq, // sturm sequence for p + int lower_sv, // number of sign variations at the lower bound of interval + int upper_sv, // number of sign variations at the upper bound of interval + // Output values + numeral_vector & roots) { + bool dinf = depends_on_infinitesimals(p_sz, p); + bisect_ctx ctx(p_sz, p, dinf, sturm_seq, roots); + bisect_isolate_roots(interval, lower_sv, upper_sv, ctx); + } + /** \brief Root isolation for polynomials that are - nonlinear (degree > 2) @@ -2100,14 +2182,11 @@ namespace realclosure { // Compute the number of positive and negative roots scoped_polynomial_seq seq(*this); sturm_seq(n, p, seq); - scoped_mpbqi minf_zero(bqim()); - set_lower_inf(minf_zero); - set_upper_zero(minf_zero); - int num_neg_roots = TaQ(seq, minf_zero); - scoped_mpbqi zero_inf(bqim()); - set_lower_zero(zero_inf); - set_upper_inf(zero_inf); - int num_pos_roots = TaQ(seq, zero_inf); + int num_sv_minus_inf = sign_variations_at_minus_inf(seq); + int num_sv_zero = sign_variations_at_zero(seq); + int num_sv_plus_inf = sign_variations_at_plus_inf(seq); + int num_neg_roots = num_sv_minus_inf - num_sv_zero; + int num_pos_roots = num_sv_zero - num_sv_plus_inf; TRACE("rcf_isolate", tout << "num_neg_roots: " << num_neg_roots << "\n"; tout << "num_pos_roots: " << num_pos_roots << "\n";); @@ -2115,13 +2194,21 @@ namespace realclosure { scoped_mpbqi neg_interval(bqim()); mk_neg_interval(has_neg_lower, neg_lower_N, has_neg_upper, neg_upper_N, neg_interval); mk_pos_interval(has_pos_lower, pos_lower_N, has_pos_upper, pos_upper_N, pos_interval); + if (num_neg_roots > 0) { if (num_neg_roots == 1) { add_root(n, p, neg_interval, 0, UINT_MAX, roots); } else { - // TODO bisection - sign_det_isolate_roots(n, p, num_neg_roots, minf_zero, roots); + if (has_neg_lower) { + bisect_isolate_roots(n, p, neg_interval, seq, num_sv_minus_inf, num_sv_zero, roots); + } + else { + scoped_mpbqi minf_zero(bqim()); + set_lower_inf(minf_zero); + set_upper_zero(minf_zero); + sign_det_isolate_roots(n, p, num_neg_roots, minf_zero, roots); + } } } @@ -2130,8 +2217,15 @@ namespace realclosure { add_root(n, p, pos_interval, 0, UINT_MAX, roots); } else { - // TODO bisection - sign_det_isolate_roots(n, p, num_pos_roots, zero_inf, roots); + if (has_pos_upper) { + bisect_isolate_roots(n, p, pos_interval, seq, num_sv_zero, num_sv_plus_inf, roots); + } + else { + scoped_mpbqi zero_inf(bqim()); + set_lower_zero(zero_inf); + set_upper_inf(zero_inf); + sign_det_isolate_roots(n, p, num_pos_roots, zero_inf, roots); + } } } } @@ -3155,6 +3249,24 @@ namespace realclosure { return sign_variations_at_core(seq, MPBQ, b); } + int sign_variations_at_lower(scoped_polynomial_seq & seq, mpbqi const & interval) { + if (interval.lower_is_inf()) + return sign_variations_at_minus_inf(seq); + else if (bqm().is_zero(interval.lower())) + return sign_variations_at_zero(seq); + else + return sign_variations_at(seq, interval.lower()); + } + + int sign_variations_at_upper(scoped_polynomial_seq & seq, mpbqi const & interval) { + if (interval.upper_is_inf()) + return sign_variations_at_plus_inf(seq); + else if (bqm().is_zero(interval.upper())) + return sign_variations_at_zero(seq); + else + return sign_variations_at(seq, interval.upper()); + } + /** \brief Given a polynomial Sturm sequence seq for (P; P' * Q) and an interval (a, b], it returns TaQ(Q, P; a, b) = @@ -3165,22 +3277,7 @@ namespace realclosure { \remark This method ignores whether the interval end-points are closed or open. */ int TaQ(scoped_polynomial_seq & seq, mpbqi const & interval) { - int a, b; - if (interval.lower_is_inf()) - a = sign_variations_at_minus_inf(seq); - else if (bqm().is_zero(interval.lower())) - a = sign_variations_at_zero(seq); - else - a = sign_variations_at(seq, interval.lower()); - - if (interval.upper_is_inf()) - b = sign_variations_at_plus_inf(seq); - else if (bqm().is_zero(interval.upper())) - b = sign_variations_at_zero(seq); - else - b = sign_variations_at(seq, interval.upper()); - - return a - b; + return sign_variations_at_lower(seq, interval) - sign_variations_at_upper(seq, interval); } /** @@ -5075,3 +5172,8 @@ void pp(realclosure::manager::imp * imp, mpq const & n) { imp->qm().display(std::cout, n); std::cout << std::endl; } + +void pp(realclosure::manager::imp * imp, realclosure::extension * x) { + imp->display_ext(std::cout, x, false); + std::cout << std::endl; +} From 5ce70eb521cfd5bf6dd4f64f9950f0d132851b05 Mon Sep 17 00:00:00 2001 From: Leonardo de Moura Date: Fri, 11 Jan 2013 17:55:03 -0800 Subject: [PATCH 61/78] Fix bug Signed-off-by: Leonardo de Moura --- src/math/realclosure/realclosure.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/math/realclosure/realclosure.cpp b/src/math/realclosure/realclosure.cpp index 3d4bfd3f7..b78e5d5f1 100644 --- a/src/math/realclosure/realclosure.cpp +++ b/src/math/realclosure/realclosure.cpp @@ -1925,7 +1925,7 @@ namespace realclosure { SASSERT(new_M_s.m() == new_taqrs.size()); SASSERT(new_M_s.m() == new_prs.size()); // The system must have a solution - sc_cardinalities.resize(new_taqrs.size()); + sc_cardinalities.resize(new_taqrs.size(), 0); // Solve // new_M_s * sc_cardinalities = new_taqrs VERIFY(mm().solve(new_M_s, sc_cardinalities.c_ptr(), new_taqrs.c_ptr())); @@ -3383,7 +3383,9 @@ namespace realclosure { scoped_mpbqi num_i(bqim()), den_i(bqim()); polynomial_interval(v->num(), v->ext()->interval(), num_i); polynomial_interval(v->den(), v->ext()->interval(), den_i); - div(num_i, den_i, inc_precision(prec, 2), v->interval()); + if (!contains_zero(num_i) && !contains_zero(den_i)) { + div(num_i, den_i, inc_precision(prec, 2), v->interval()); + } } } @@ -3883,7 +3885,7 @@ namespace realclosure { } } int_buffer sc_cardinalities; - sc_cardinalities.resize(new_taqrs.size()); + sc_cardinalities.resize(new_taqrs.size(), 0); // Solve // new_M_s * sc_cardinalities = new_taqrs VERIFY(mm().solve(new_M_s, sc_cardinalities.c_ptr(), new_taqrs.c_ptr())); From ef11ef61b55da1c734c84b117946e8e7dd8a9b84 Mon Sep 17 00:00:00 2001 From: Leonardo de Moura Date: Fri, 11 Jan 2013 17:55:52 -0800 Subject: [PATCH 62/78] Clean m_val field when switching to GMP bignum Signed-off-by: Leonardo de Moura --- src/util/mpz.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/util/mpz.h b/src/util/mpz.h index 3582f0ad1..b5c301d82 100644 --- a/src/util/mpz.h +++ b/src/util/mpz.h @@ -251,8 +251,10 @@ class mpz_manager { } void mk_big(mpz & a) { - if (a.m_ptr == 0) + if (a.m_ptr == 0) { + a.m_val = 0; a.m_ptr = allocate(); + } } #endif @@ -687,7 +689,7 @@ public: double get_double(mpz const & a) const; std::string to_string(mpz const & a) const; - + void display(std::ostream & out, mpz const & a) const; /** From a03a6e9bf61131d4fbbee945ab18025816883cf6 Mon Sep 17 00:00:00 2001 From: Leonardo de Moura Date: Fri, 11 Jan 2013 21:12:51 -0800 Subject: [PATCH 63/78] Add more tracing Signed-off-by: Leonardo de Moura --- src/math/realclosure/realclosure.cpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/math/realclosure/realclosure.cpp b/src/math/realclosure/realclosure.cpp index b78e5d5f1..f124f7e21 100644 --- a/src/math/realclosure/realclosure.cpp +++ b/src/math/realclosure/realclosure.cpp @@ -2876,6 +2876,10 @@ namespace realclosure { \brief r <- gcd(p1, p2) */ void gcd(unsigned sz1, value * const * p1, unsigned sz2, value * const * p2, value_ref_buffer & r) { + INC_DEPTH(); + TRACE("rcf_gcd", tout << "GCD [" << m_exec_depth << "]\n"; + display_poly(tout, sz1, p1); tout << "\n"; + display_poly(tout, sz2, p2); tout << "\n";); if (sz1 == 0) { r.append(sz2, p2); mk_monic(r); @@ -4050,6 +4054,10 @@ namespace realclosure { - new_p1 <- p1/gcd(p1, p2); new_p2 <- p2/gcd(p1, p2); Otherwise */ void normalize_fraction(unsigned sz1, value * const * p1, unsigned sz2, value * const * p2, value_ref_buffer & new_p1, value_ref_buffer & new_p2) { + INC_DEPTH(); + TRACE("rcf_arith", tout << "normalize [" << m_exec_depth << "]\n"; + display_poly(tout, sz1, p1); tout << "\n"; + display_poly(tout, sz2, p2); tout << "\n";); SASSERT(sz1 > 0 && sz2 > 0); if (sz2 == 1) { // - new_p1 <- p1/p2[0]; new_p2 <- one IF sz2 == 1 @@ -4285,6 +4293,10 @@ namespace realclosure { r = mk_rational(v); } else { + INC_DEPTH(); + TRACE("rcf_arith", tout << "add [" << m_exec_depth << "]\n"; + display(tout, a, false); tout << "\n"; + display(tout, b, false); tout << "\n";); switch (compare_rank(a, b)) { case -1: add_rf_v(to_rational_function(b), a, r); break; case 0: add_rf_rf(to_rational_function(a), to_rational_function(b), r); break; @@ -4486,6 +4498,10 @@ namespace realclosure { r = mk_rational(v); } else { + INC_DEPTH(); + TRACE("rcf_arith", tout << "mul [" << m_exec_depth << "]\n"; + display(tout, a, false); tout << "\n"; + display(tout, b, false); tout << "\n";); switch (compare_rank(a, b)) { case -1: mul_rf_v(to_rational_function(b), a, r); break; case 0: mul_rf_rf(to_rational_function(a), to_rational_function(b), r); break; From d60f2db11690082166dd7504be9fa1778066ac7c Mon Sep 17 00:00:00 2001 From: Leonardo de Moura Date: Sat, 12 Jan 2013 09:46:00 -0800 Subject: [PATCH 64/78] Remove select method Signed-off-by: Leonardo de Moura --- src/math/realclosure/realclosure.cpp | 9 --------- src/math/realclosure/realclosure.h | 7 ------- 2 files changed, 16 deletions(-) diff --git a/src/math/realclosure/realclosure.cpp b/src/math/realclosure/realclosure.cpp index f124f7e21..d8babe671 100644 --- a/src/math/realclosure/realclosure.cpp +++ b/src/math/realclosure/realclosure.cpp @@ -4651,10 +4651,6 @@ namespace realclosure { return compare(a.m_value, b.m_value); } - void select(numeral const & prev, numeral const & next, numeral & result) { - // TODO - } - struct collect_algebraic_refs { char_vector m_visited; // Set of visited algebraic extensions. ptr_vector m_found; // vector/list of visited algebraic extensions. @@ -5129,11 +5125,6 @@ namespace realclosure { return gt(a, _b); } - void manager::select(numeral const & prev, numeral const & next, numeral & result) { - save_interval_ctx ctx(this); - m_imp->select(prev, next, result); - } - void manager::display(std::ostream & out, numeral const & a) const { save_interval_ctx ctx(this); m_imp->display(out, a); diff --git a/src/math/realclosure/realclosure.h b/src/math/realclosure/realclosure.h index b5e8574b2..a106ebc26 100644 --- a/src/math/realclosure/realclosure.h +++ b/src/math/realclosure/realclosure.h @@ -251,13 +251,6 @@ namespace realclosure { bool ge(numeral const & a, numeral const & b) { return !lt(a, b); } bool ge(numeral const & a, mpq const & b) { return !lt(a, b); } bool ge(numeral const & a, mpz const & b) { return !lt(a, b); } - - /** - \brief Store in result a value in the interval (prev, next) - - \pre lt(pre, next) - */ - void select(numeral const & prev, numeral const & next, numeral & result); void display(std::ostream & out, numeral const & a) const; From 1d761ea9a599171dfb1c73a83072b023303ba41b Mon Sep 17 00:00:00 2001 From: Leonardo de Moura Date: Sat, 12 Jan 2013 15:45:43 -0800 Subject: [PATCH 65/78] Add clean_denominators procedure Signed-off-by: Leonardo de Moura --- src/math/realclosure/realclosure.cpp | 376 +++++++++++++++++++++++++-- src/math/realclosure/realclosure.h | 2 + src/test/rcf.cpp | 23 +- 3 files changed, 373 insertions(+), 28 deletions(-) diff --git a/src/math/realclosure/realclosure.cpp b/src/math/realclosure/realclosure.cpp index d8babe671..998e5abb2 100644 --- a/src/math/realclosure/realclosure.cpp +++ b/src/math/realclosure/realclosure.cpp @@ -2416,13 +2416,32 @@ namespace realclosure { return new (allocator()) rational_value(); } - rational_value * mk_rational(mpq & v) { + /** + \brief Make a rational and swap its value with v + */ + rational_value * mk_rational_and_swap(mpq & v) { + SASSERT(!qm().is_zero(v)); rational_value * r = mk_rational(); ::swap(r->m_value, v); return r; } + rational_value * mk_rational(mpq const & v) { + SASSERT(!qm().is_zero(v)); + rational_value * r = mk_rational(); + qm().set(r->m_value, v); + return r; + } + + rational_value * mk_rational(mpz const & v) { + SASSERT(!qm().is_zero(v)); + rational_value * r = mk_rational(); + qm().set(r->m_value, v); + return r; + } + rational_value * mk_rational(mpbq const & v) { + SASSERT(!bqm().is_zero(v)); scoped_mpq v_q(qm()); // v as a rational ::to_mpq(qm(), v, v_q); return mk_rational(v_q); @@ -2921,7 +2940,7 @@ namespace realclosure { for (unsigned i = 1; i < sz; i++) { mpq i_mpq(i); value_ref a_i(*this); - a_i = mk_rational(i_mpq); + a_i = mk_rational_and_swap(i_mpq); mul(a_i, p[i], a_i); r.push_back(a_i); } @@ -3086,30 +3105,42 @@ namespace realclosure { return true; } + /** + \brief r <- p(b) + */ + void mk_polynomial_value(unsigned n, value * const * p, value * b, value_ref & r) { + SASSERT(n > 0); + if (n == 1 || b == 0) { + r = p[0]; + } + else { + SASSERT(n >= 2); + // We compute the result using the Horner Sequence + // ((a_{n-1}*b + a_{n-2})*b + a_{n-3})*b + a_{n-4} ... + // where a_i's are the coefficients of p. + mul(p[n - 1], b, r); // r <- a_{n-1} * b + unsigned i = n - 1; + while (i > 0) { + --i; + if (p[i] != 0) + add(r, p[i], r); // r <- r + a_i + if (i > 0) + mul(r, b, r); // r <- r * b + } + } + } + /** \brief Evaluate the sign of p(b) by computing a value object. */ int expensive_eval_sign_at(unsigned n, value * const * p, mpbq const & b) { SASSERT(n > 0); SASSERT(p[n - 1] != 0); - value_ref bv(*this); - bv = mk_rational(b); - // We compute the result using the Horner Sequence - // ((a_{n-1}*bv + a_{n-2})*bv + a_{n-3})*bv + a_{n-4} ... - // where a_i's are the coefficients of p. - value_ref r(*this); - // r <- a_{n-1} * bv - mul(p[n - 1], bv, r); - unsigned i = n - 1; - while (i > 0) { - checkpoint(); - --i; - if (p[i] != 0) - add(r, p[i], r); // r <- r + a_i - if (i > 0) - mul(r, bv, r); // r <- r * bv - } - return sign(r); + value_ref _b(*this); + _b = mk_rational(b); + value_ref pb(*this); + mk_polynomial_value(n, p, _b, pb); + return sign(pb); } /** @@ -4290,7 +4321,7 @@ namespace realclosure { if (qm().is_zero(v)) r = 0; else - r = mk_rational(v); + r = mk_rational_and_swap(v); } else { INC_DEPTH(); @@ -4319,7 +4350,7 @@ namespace realclosure { if (qm().is_zero(v)) r = 0; else - r = mk_rational(v); + r = mk_rational_and_swap(v); } else { value_ref neg_b(*this); @@ -4353,7 +4384,7 @@ namespace realclosure { scoped_mpq v(qm()); qm().set(v, to_mpq(a)); qm().neg(v); - r = mk_rational(v); + r = mk_rational_and_swap(v); } else { neg_rf(to_rational_function(a), r); @@ -4495,7 +4526,7 @@ namespace realclosure { else if (is_nz_rational(a) && is_nz_rational(b)) { scoped_mpq v(qm()); qm().mul(to_mpq(a), to_mpq(b), v); - r = mk_rational(v); + r = mk_rational_and_swap(v); } else { INC_DEPTH(); @@ -4530,7 +4561,7 @@ namespace realclosure { else if (is_nz_rational(a) && is_nz_rational(b)) { scoped_mpq v(qm()); qm().div(to_mpq(a), to_mpq(b), v); - r = mk_rational(v); + r = mk_rational_and_swap(v); } else { value_ref inv_b(*this); @@ -4561,7 +4592,7 @@ namespace realclosure { if (is_nz_rational(a)) { scoped_mpq v(qm()); qm().inv(to_mpq(a), v); - r = mk_rational(v); + r = mk_rational_and_swap(v); } else { inv_rf(to_rational_function(a), r); @@ -4651,6 +4682,280 @@ namespace realclosure { return compare(a.m_value, b.m_value); } + // --------------------------------- + // + // Structural equality + // + // --------------------------------- + + /** + \brief Values a and b are said to be "structurally" equal if: + - a and b are 0. + - a and b are rationals and compare(a, b) == 0 + - a and b are rational function values p_a(x)/q_a(x) and p_b(y)/q_b(y) where x and y are field extensions, and + * x == y (pointer equality, i.e., they are the same field extension object). + * Every coefficient of p_a is structurally equal to every coefficient of p_b + * Every coefficient of q_a is structurally equal to every coefficient of q_b + Clearly structural equality implies equality, but the reverse is not true. + */ + bool struct_eq(value * a, value * b) const { + if (a == b) + return true; + else if (a == 0 || b == 0) + return false; + else if (is_nz_rational(a) && is_nz_rational(b)) + return qm().eq(to_mpq(a), to_mpq(b)); + else if (is_nz_rational(a) || is_nz_rational(b)) + return false; + else { + SASSERT(is_rational_function(a)); + SASSERT(is_rational_function(b)); + rational_function_value * rf_a = to_rational_function(a); + rational_function_value * rf_b = to_rational_function(b); + if (rf_a->ext() != rf_b->ext()) + return false; + return struct_eq(rf_a->num(), rf_b->num()) && struct_eq(rf_a->den(), rf_b->den()); + } + } + + /** + Auxiliary method for + bool struct_eq(value * a, value * b) + */ + bool struct_eq(unsigned sz_a, value * const * p_a, unsigned sz_b, value * const * p_b) const { + if (sz_a != sz_b) + return false; + for (unsigned i = 0; i < sz_a; i++) { + if (!struct_eq(p_a[i], p_b[i])) + return false; + } + return true; + } + + /** + Auxiliary method for + bool struct_eq(value * a, value * b) + */ + bool struct_eq(polynomial const & p_a, polynomial const & p_b) const { + return struct_eq(p_a.size(), p_a.c_ptr(), p_b.size(), p_b.c_ptr()); + } + + // --------------------------------- + // + // Clean denominators + // + // --------------------------------- + + /** + \brief We say 'a' has "clean" denominators if + - a is 0 + - a is a rational_value that is an integer + - a is a rational_function_value of the form p_a(x)/1 where the coefficients of p_a also have clean denominators. + */ + bool has_clean_denominators(value * a) const { + if (a == 0) + return true; + else if (is_nz_rational(a)) + return qm().is_int(to_mpq(a)); + else { + rational_function_value * rf_a = to_rational_function(a); + return is_rational_one(rf_a->den()) && has_clean_denominators(rf_a->num()); + } + } + + /** + \brief See comment at has_clean_denominators(value * a) + */ + bool has_clean_denominators(polynomial const & p) const { + unsigned sz = p.size(); + for (unsigned i = 0; i < sz; i++) { + if (!has_clean_denominators(p[i])) + return false; + } + return true; + } + + /** + \brief "Clean" the denominators of 'a'. That is, return p and q s.t. + a == p/q + and + has_clean_denominators(p) and has_clean_denominators(q) + */ + void clean_denominators_core(value * a, value_ref & p, value_ref & q) { + INC_DEPTH(); + TRACE("rcf_clean", tout << "clean_denominators_core [" << m_exec_depth << "]\na: "; display(tout, a, false); tout << "\n";); + p.reset(); q.reset(); + if (a == 0) { + p = a; + q = one(); + } + else if (is_nz_rational(a)) { + p = mk_rational(to_mpq(a).numerator()); + q = mk_rational(to_mpq(a).denominator()); + } + else { + rational_function_value * rf_a = to_rational_function(a); + value_ref_buffer p_num(*this), p_den(*this); + value_ref d_num(*this), d_den(*this); + clean_denominators_core(rf_a->num(), p_num, d_num); + clean_denominators_core(rf_a->den(), p_den, d_den); + value_ref x(*this); + x = mk_rational_function_value(rf_a->ext()); + mk_polynomial_value(p_num.size(), p_num.c_ptr(), x, p); + mk_polynomial_value(p_den.size(), p_den.c_ptr(), x, q); + if (!struct_eq(d_den, d_num)) { + mul(p, d_den, p); + mul(q, d_num, q); + } + } + } + + /** + \brief Clean the denominators of the polynomial p, it returns clean_p and d s.t. + p = clean_p/d + and has_clean_denominators(clean_p) && has_clean_denominators(d) + */ + void clean_denominators_core(polynomial const & p, value_ref_buffer & norm_p, value_ref & d) { + value_ref_buffer nums(*this), dens(*this); + value_ref a_n(*this), a_d(*this); + bool all_one = true; + for (unsigned i = 0; i < p.size(); i++) { + if (p[i]) { + clean_denominators_core(p[i], a_n, a_d); + nums.push_back(a_n); + if (!is_rational_one(a_d)) + all_one = false; + dens.push_back(a_d); + } + else { + nums.push_back(0); + dens.push_back(0); + } + } + if (all_one) { + norm_p = nums; + d = one(); + } + else { + // Compute lcm of the integer elements in dens. + // This is a little trick to control the coefficient growth. + // We don't compute lcm of the other elements of dens because it is too expensive. + scoped_mpq lcm_z(qm()); + bool found_z = false; + SASSERT(nums.size() == p.size()); + SASSERT(dens.size() == p.size()); + for (unsigned i = 0; i < p.size(); i++) { + if (!dens[i]) + continue; + if (is_nz_rational(dens[i])) { + mpq const & _d = to_mpq(dens[i]); + SASSERT(qm().is_int(_d)); + if (!found_z) { + found_z = true; + qm().set(lcm_z, _d); + } + else { + qm().lcm(lcm_z, _d, lcm_z); + } + } + } + + value_ref lcm(*this); + if (found_z) { + lcm = mk_rational(lcm_z); + } + else { + lcm = one(); + } + + // Compute the multipliers for nums. + // Compute norm_p and d + // + // We do NOT use GCD to compute the LCM of the denominators of non-rational values. + // However, we detect structurally equivalent denominators. + // + // Thus a/(b+1) + c/(b+1) is converted into a*c/(b+1) instead of (a*(b+1) + c*(b+1))/(b+1)^2 + norm_p.reset(); + d = lcm; + value_ref_buffer multipliers(*this); + value_ref m(*this); + for (unsigned i = 0; i < p.size(); i++) { + if (!nums[i]) { + norm_p.push_back(0); + } + else { + SASSERT(dens[i]); + bool is_z; + if (!is_nz_rational(dens[i])) { + m = lcm; + is_z = false; + } + else { + scoped_mpq num_z(qm()); + qm().div(lcm_z, to_mpq(dens[i]), num_z); + SASSERT(qm().is_int(num_z)); + m = mk_rational_and_swap(num_z); + is_z = true; + } + bool found_lt_eq = false; + for (unsigned j = 0; j < p.size(); j++) { + TRACE("rcf_clean_bug", tout << "j: " << j << " "; display(tout, m, false); tout << "\n";); + if (!dens[j]) + continue; + if (i != j && !is_nz_rational(dens[j])) { + if (struct_eq(dens[i], dens[j])) { + if (j < i) + found_lt_eq = true; + } + else { + mul(m, dens[j], m); + } + } + } + if (!is_z && !found_lt_eq) { + mul(dens[i], d, d); + } + mul(m, nums[i], m); + norm_p.push_back(m); + } + } + } + SASSERT(norm_p.size() == p.size()); + } + + void clean_denominators(value * a, value_ref & p, value_ref & q) { + if (has_clean_denominators(a)) { + p = a; + q = one(); + } + else { + clean_denominators_core(a, p, q); + } + } + + void clean_denominators(polynomial const & p, value_ref_buffer & norm_p, value_ref & d) { + if (has_clean_denominators(p)) { + norm_p.append(p.size(), p.c_ptr()); + d = one(); + } + else { + clean_denominators_core(p, norm_p, d); + } + } + + void clean_denominators(numeral const & a, numeral & p, numeral & q) { + value_ref _p(*this), _q(*this); + clean_denominators(a.m_value, _p, _q); + set(p, _p); + set(q, _q); + } + + // --------------------------------- + // + // "Pretty printing" + // + // --------------------------------- + struct collect_algebraic_refs { char_vector m_visited; // Set of visited algebraic extensions. ptr_vector m_found; // vector/list of visited algebraic extensions. @@ -4683,11 +4988,20 @@ namespace realclosure { } }; + static unsigned num_nz_coeffs(polynomial const & p) { + unsigned r = 0; + for (unsigned i = 0; i < p.size(); i++) { + if (p[i]) + r++; + } + return r; + } + bool use_parenthesis(value * v) const { if (is_zero(v) || is_nz_rational(v)) return false; rational_function_value * rf = to_rational_function(v); - return rf->num().size() > 1 || !is_rational_one(rf->den()); + return num_nz_coeffs(rf->num()) > 1 || !is_rational_one(rf->den()); } template @@ -5140,6 +5454,10 @@ namespace realclosure { m_imp->display_interval(out, a); } + void manager::clean_denominators(numeral const & a, numeral & p, numeral & q) { + save_interval_ctx ctx(this); + m_imp->clean_denominators(a, p, q); + } }; void pp(realclosure::manager::imp * imp, realclosure::polynomial const & p, realclosure::extension * ext) { @@ -5162,6 +5480,10 @@ void pp(realclosure::manager::imp * imp, realclosure::manager::imp::value_ref_bu pp(imp, p[i]); } +void pp(realclosure::manager::imp * imp, realclosure::manager::imp::value_ref const & v) { + pp(imp, v.get()); +} + void pp(realclosure::manager::imp * imp, realclosure::mpbqi const & i) { imp->bqim().display(std::cout, i); std::cout << std::endl; diff --git a/src/math/realclosure/realclosure.h b/src/math/realclosure/realclosure.h index a106ebc26..1e0ac4ba5 100644 --- a/src/math/realclosure/realclosure.h +++ b/src/math/realclosure/realclosure.h @@ -263,6 +263,8 @@ namespace realclosure { void display_interval(std::ostream & out, numeral const & a) const; + + void clean_denominators(numeral const & a, numeral & p, numeral & q); }; class value; diff --git a/src/test/rcf.cpp b/src/test/rcf.cpp index ac240fa9d..5bef0dfef 100644 --- a/src/test/rcf.cpp +++ b/src/test/rcf.cpp @@ -135,15 +135,36 @@ static void tst_lin_indep(unsigned m, unsigned n, int _A[], unsigned ex_sz, unsi A.set(i, j, _A[i*n + j]); unsigned_vector r; r.resize(A.n()); - mm.linear_independent_rows(A, r.c_ptr()); + scoped_mpz_matrix B(mm); + mm.linear_independent_rows(A, r.c_ptr(), B); SASSERT(r.size() == ex_sz); for (unsigned i = 0; i < ex_sz; i++) { SASSERT(r[i] == ex_r[i]); } } +static void tst_denominators() { + unsynch_mpq_manager qm; + rcmanager m(qm); + scoped_rcnumeral a(m); + scoped_rcnumeral t(m); + scoped_rcnumeral eps(m); + m.mk_pi(a); + m.inv(a); + m.mk_infinitesimal("eps", eps); + t = (a - eps*2) / (a*eps + 1); + // t = t + a * 2; + scoped_rcnumeral n(m), d(m); + std::cout << t << "\n"; + m.clean_denominators(t, n, d); + std::cout << "---->\n" << n << "\n" << d << "\n"; +} void tst_rcf() { + enable_trace("rcf_clean"); + enable_trace("rcf_clean_bug"); + tst_denominators(); + return; tst1(); tst2(); { int A[] = {0, 1, 1, 1, 0, 1, 1, 1, -1}; int c[] = {10, 4, -4}; int b[] = {-2, 4, 6}; tst_solve(3, A, b, c, true); } From 09d3686d58eba3627bf697db793d654bd12fc3a9 Mon Sep 17 00:00:00 2001 From: Leonardo de Moura Date: Sat, 12 Jan 2013 15:54:07 -0800 Subject: [PATCH 66/78] Fix memory leak in realclosure Signed-off-by: Leonardo de Moura --- src/math/realclosure/realclosure.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/math/realclosure/realclosure.cpp b/src/math/realclosure/realclosure.cpp index 998e5abb2..7f6d4dbe6 100644 --- a/src/math/realclosure/realclosure.cpp +++ b/src/math/realclosure/realclosure.cpp @@ -770,10 +770,12 @@ namespace realclosure { } void del_transcendental(transcendental * t) { + bqim().del(t->m_interval); allocator().deallocate(sizeof(transcendental), t); } void del_infinitesimal(infinitesimal * i) { + bqim().del(i->m_interval); allocator().deallocate(sizeof(infinitesimal), i); } From e6a35c6241b0094917c627c78bc1d7f4c6473d5e Mon Sep 17 00:00:00 2001 From: Leonardo de Moura Date: Sat, 12 Jan 2013 16:27:56 -0800 Subject: [PATCH 67/78] Add prem to avoid rational function values Signed-off-by: Leonardo de Moura --- src/math/realclosure/realclosure.cpp | 79 +++++++++++++++++++++++++++- 1 file changed, 78 insertions(+), 1 deletion(-) diff --git a/src/math/realclosure/realclosure.cpp b/src/math/realclosure/realclosure.cpp index 7f6d4dbe6..9de57b387 100644 --- a/src/math/realclosure/realclosure.cpp +++ b/src/math/realclosure/realclosure.cpp @@ -366,11 +366,13 @@ namespace realclosure { ptr_vector m_to_restore; //!< Set of values v s.t. v->m_old_interval != 0 // Parameters + bool m_use_prem; //!< use pseudo-remainder when computing sturm sequences unsigned m_ini_precision; //!< initial precision for transcendentals, infinitesimals, etc. unsigned m_max_precision; //!< Maximum precision for interval arithmetic techniques, it switches to complete methods after that unsigned m_inf_precision; //!< 2^m_inf_precision is used as the lower bound of oo and -2^m_inf_precision is used as the upper_bound of -oo scoped_mpbq m_plus_inf_approx; // lower bound for binary rational intervals used to approximate an infinite positive value scoped_mpbq m_minus_inf_approx; // upper bound for binary rational intervals used to approximate an infinite negative value + // Tracing unsigned m_exec_depth; @@ -675,6 +677,7 @@ namespace realclosure { } void updt_params(params_ref const & p) { + m_use_prem = p.get_bool("use_prem", true); m_ini_precision = p.get_uint("initial_precision", 24); m_inf_precision = p.get_uint("inf_precision", 24); m_max_precision = p.get_uint("max_precision", 64); // == 1/2^64 for interval arithmetic methods, it switches to complete methods after that. @@ -2825,6 +2828,63 @@ namespace realclosure { } } + /** + \brief r <- prem(p1, p2) Pseudo-remainder + + We are working on a field, but it is useful to use the pseudo-remainder algorithm because + it does not create rational function values. + That is, if has_clean_denominators(p1) and has_clean_denominators(p2) then has_clean_denominators(r). + */ + void prem(unsigned sz1, value * const * p1, unsigned sz2, value * const * p2, unsigned & d, value_ref_buffer & r) { + SASSERT(p1 != r.c_ptr()); + SASSERT(p2 != r.c_ptr()); + TRACE("rcf_prem", + tout << "prem\n"; + display_poly(tout, sz1, p1); tout << "\n"; + display_poly(tout, sz2, p2); tout << "\n";); + d = 0; + r.reset(); + SASSERT(sz2 > 0); + if (sz2 == 1) + return; + r.append(sz1, p1); + if (sz1 <= 1) + return; // r is p1 + value * b_n = p2[sz2 - 1]; + SASSERT(b_n != 0); + value_ref a_m(*this); + value_ref new_a(*this); + while (true) { + checkpoint(); + sz1 = r.size(); + if (sz1 < sz2) { + TRACE("rcf_prem", tout << "prem result\n"; display_poly(tout, r.size(), r.c_ptr()); tout << "\n";); + return; + } + unsigned m_n = sz1 - sz2; + // r: a_m * x^m + a_{m-1} * x^{m-1} + ... + a_0 + // p2: b_n * x^n + b_{n-1} * x^{n-1} + ... + b_0 + d++; + a_m = r[sz1 - 1]; + // don't need to update position sz1 - 1, since it will become 0 + if (!is_rational_one(b_n)) { + for (unsigned i = 0; i < sz1 - 1; i++) { + mul(r[i], b_n, new_a); + r.set(i, new_a); + } + } + // buffer: a_m * x^m + b_n * a_{m-1} * x^{m-1} + ... + b_n * a_0 + // don't need to process i = sz2 - 1, because r[sz1 - 1] will become 0. + for (unsigned i = 0; i < sz2 - 1; i++) { + mul(a_m, p2[i], new_a); + sub(r[i + m_n], new_a, new_a); + r.set(i + m_n, new_a); + } + r.shrink(sz1 - 1); + adjust_size(r); + } + } + /** \brief r <- -p */ @@ -2874,6 +2934,20 @@ namespace realclosure { rem(sz1, p1, sz2, p2, r); neg(r); } + + /** + \brief r <- sprem(p1, p2) + Signed pseudo remainder + */ + void sprem(unsigned sz1, value * const * p1, unsigned sz2, value * const * p2, value_ref_buffer & r) { + SASSERT(p1 != r.c_ptr()); + SASSERT(p2 != r.c_ptr()); + unsigned d; + prem(sz1, p1, sz2, p2, d, r); + // We should not flip the sign if d is odd and leading coefficient of p2 is negative. + if (d % 2 == 0 || (sz2 > 0 && sign(p2[sz2-1]) < 0)) + neg(r); + } /** \brief Force the leading coefficient of p to be 1. @@ -2986,7 +3060,10 @@ namespace realclosure { value_ref_buffer r(*this); while (true) { unsigned sz = seq.size(); - srem(seq.size(sz-2), seq.coeffs(sz-2), seq.size(sz-1), seq.coeffs(sz-1), r); + if (m_use_prem) + sprem(seq.size(sz-2), seq.coeffs(sz-2), seq.size(sz-1), seq.coeffs(sz-1), r); + else + srem(seq.size(sz-2), seq.coeffs(sz-2), seq.size(sz-1), seq.coeffs(sz-1), r); TRACE("rcf_sturm_seq", tout << "sturm_seq_core [" << m_exec_depth << "], new polynomial\n"; display_poly(tout, r.size(), r.c_ptr()); tout << "\n";); From ea9421bb381eadb9fe3d4192f3ed34ae170d258a Mon Sep 17 00:00:00 2001 From: Leonardo de Moura Date: Sat, 12 Jan 2013 16:40:45 -0800 Subject: [PATCH 68/78] Expose rcf module parameters Signed-off-by: Leonardo de Moura --- src/math/realclosure/rcf.pyg | 7 +++++++ src/math/realclosure/realclosure.cpp | 14 ++++++++------ 2 files changed, 15 insertions(+), 6 deletions(-) create mode 100644 src/math/realclosure/rcf.pyg diff --git a/src/math/realclosure/rcf.pyg b/src/math/realclosure/rcf.pyg new file mode 100644 index 000000000..ad24ad530 --- /dev/null +++ b/src/math/realclosure/rcf.pyg @@ -0,0 +1,7 @@ +def_module_params('rcf', + description='real closed fields', + export=True, + params=(('use_prem', BOOL, True, "use pseudo-remainder instead of remainder when computing GCDs and Sturm-Tarski sequences"), + ('initial_precision', UINT, 24, "a value k that is the initial interval size (as 1/2^k) when creating transcendentals and approximated division"), + ('inf_precision', UINT, 24, "a value k that is the initial interval size (i.e., (0, 1/2^l)) used as an approximation for infinitesimal values"), + ('max_precision', UINT, 64, "during sign determination we switch from interval arithmetic to complete methods when the interval size is less than 1/2^k, where k is the max_precision"))) diff --git a/src/math/realclosure/realclosure.cpp b/src/math/realclosure/realclosure.cpp index 9de57b387..ee4a4cc22 100644 --- a/src/math/realclosure/realclosure.cpp +++ b/src/math/realclosure/realclosure.cpp @@ -20,6 +20,7 @@ Notes: --*/ #include"realclosure.h" +#include"rcf_params.hpp" #include"array.h" #include"mpbq.h" #include"mpz_matrix.h" @@ -676,11 +677,12 @@ namespace realclosure { m_cancel = f; } - void updt_params(params_ref const & p) { - m_use_prem = p.get_bool("use_prem", true); - m_ini_precision = p.get_uint("initial_precision", 24); - m_inf_precision = p.get_uint("inf_precision", 24); - m_max_precision = p.get_uint("max_precision", 64); // == 1/2^64 for interval arithmetic methods, it switches to complete methods after that. + void updt_params(params_ref const & _p) { + rcf_params p(_p); + m_use_prem = p.use_prem(); + m_ini_precision = p.initial_precision(); + m_inf_precision = p.inf_precision(); + m_max_precision = p.max_precision(); bqm().power(mpbq(2), m_inf_precision, m_plus_inf_approx); bqm().set(m_minus_inf_approx, m_plus_inf_approx); bqm().neg(m_minus_inf_approx); @@ -5316,7 +5318,7 @@ namespace realclosure { } void manager::get_param_descrs(param_descrs & r) { - // TODO + rcf_params::collect_param_descrs(r); } void manager::set_cancel(bool f) { From a9fa232f1179fd863efedb05aebbd2004309be60 Mon Sep 17 00:00:00 2001 From: Leonardo de Moura Date: Sat, 12 Jan 2013 16:44:39 -0800 Subject: [PATCH 69/78] Fix bug in compare Signed-off-by: Leonardo de Moura --- src/math/realclosure/realclosure.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/math/realclosure/realclosure.cpp b/src/math/realclosure/realclosure.cpp index ee4a4cc22..c28a8065b 100644 --- a/src/math/realclosure/realclosure.cpp +++ b/src/math/realclosure/realclosure.cpp @@ -881,10 +881,7 @@ namespace realclosure { \brief Return true if v is the value one; */ bool is_one(value * v) const { - if (is_rational_one(v)) - return true; - // TODO: check if v is equal to one. - return false; + return const_cast(this)->compare(v, one()) == 0; } /** @@ -4743,8 +4740,12 @@ namespace realclosure { return -sign(b); else if (b == 0) return sign(a); - else if (is_nz_rational(a) && is_nz_rational(b)) - return qm().lt(to_mpq(a), to_mpq(b)) ? -1 : 1; + else if (is_nz_rational(a) && is_nz_rational(b)) { + if (qm().eq(to_mpq(a), to_mpq(b))) + return 0; + else + return qm().lt(to_mpq(a), to_mpq(b)) ? -1 : 1; + } else { // FUTURE: try to refine interval before switching to sub+sign approach if (bqim().before(interval(a), interval(b))) From 1e362e6fec7f667a33ffbb7b7e9756609dfd4927 Mon Sep 17 00:00:00 2001 From: Leonardo de Moura Date: Sat, 12 Jan 2013 17:08:58 -0800 Subject: [PATCH 70/78] Add comments to mark sections Signed-off-by: Leonardo de Moura --- src/math/realclosure/realclosure.cpp | 185 +++++++++++++++++++-------- 1 file changed, 129 insertions(+), 56 deletions(-) diff --git a/src/math/realclosure/realclosure.cpp b/src/math/realclosure/realclosure.cpp index c28a8065b..f9dc76dfb 100644 --- a/src/math/realclosure/realclosure.cpp +++ b/src/math/realclosure/realclosure.cpp @@ -1280,6 +1280,12 @@ namespace realclosure { inc_ref(m_e); } } + + // --------------------------------- + // + // Root isolation + // + // --------------------------------- /** \brief r <- magnitude of the lower bound of |i|. @@ -2305,6 +2311,12 @@ namespace realclosure { } } + // --------------------------------- + // + // Basic operations + // + // --------------------------------- + void reset(numeral & a) { del(a); SASSERT(is_zero(a)); @@ -2520,62 +2532,11 @@ namespace realclosure { a.m_value = n.m_value; } - /** - \brief a <- b^{1/k} - */ - void root(numeral const & a, unsigned k, numeral & b) { - if (k == 0) - throw exception("0-th root is indeterminate"); - - if (k == 1 || is_zero(a)) { - set(b, a); - return; - } - - if (sign(a) < 0 && k % 2 == 0) - throw exception("even root of negative number"); - - // create the polynomial p of the form x^k - a - value_ref_buffer p(*this); - value_ref neg_a(*this); - neg(a.m_value, neg_a); - p.push_back(neg_a); - for (unsigned i = 0; i < k - 1; i++) - p.push_back(0); - p.push_back(one()); - - numeral_vector roots; - nz_isolate_roots(p.size(), p.c_ptr(), roots); - SASSERT(roots.size() == 1 || roots.size() == 2); - if (roots.size() == 1 || sign(roots[0].m_value) > 0) { - set(b, roots[0]); - } - else { - SASSERT(roots.size() == 2); - SASSERT(sign(roots[1].m_value) > 0); - set(b, roots[1]); - } - del(roots); - } - - /** - \brief a <- b^k - */ - void power(numeral const & a, unsigned k, numeral & b) { - unsigned mask = 1; - value_ref power(*this); - value_ref _b(*this); - power = a.m_value; - _b = one(); - while (mask <= k) { - checkpoint(); - if (mask & k) - mul(_b, power, _b); - mul(power, power, power); - mask = mask << 1; - } - set(b, _b); - } + // --------------------------------- + // + // Polynomial arithmetic in RCF + // + // --------------------------------- /** \brief Remove 0s @@ -2947,6 +2908,12 @@ namespace realclosure { if (d % 2 == 0 || (sz2 > 0 && sign(p2[sz2-1]) < 0)) neg(r); } + + // --------------------------------- + // + // GCD + // + // --------------------------------- /** \brief Force the leading coefficient of p to be 1. @@ -3006,6 +2973,12 @@ namespace realclosure { } } + // --------------------------------- + // + // Derivatives and Sturm-Tarski Sequences + // + // --------------------------------- + /** \brief r <- dp/dx */ @@ -3107,6 +3080,13 @@ namespace realclosure { seq.push(p1_prime_p2.size(), p1_prime_p2.c_ptr()); sturm_seq_core(seq); } + + // --------------------------------- + // + // Sign evaluation for polynomials + // That is, sign of p(x) at b + // + // --------------------------------- /** \brief Return the sign of p(0) @@ -3285,6 +3265,12 @@ namespace realclosure { } } + // --------------------------------- + // + // Sign variations in polynomial sequences. + // + // --------------------------------- + enum location { ZERO, MINUS_INF, @@ -3380,6 +3366,12 @@ namespace realclosure { return sign_variations_at(seq, interval.upper()); } + // --------------------------------- + // + // Tarski-Queries (see BPR book) + // + // --------------------------------- + /** \brief Given a polynomial Sturm sequence seq for (P; P' * Q) and an interval (a, b], it returns TaQ(Q, P; a, b) = @@ -3425,6 +3417,12 @@ namespace realclosure { sturm_seq(p_sz, p, seq); return TaQ(seq, interval); } + + // --------------------------------- + // + // Interval refinement + // + // --------------------------------- void refine_rational_interval(rational_value * v, unsigned prec) { mpbqi & i = interval(v); @@ -3687,6 +3685,12 @@ namespace realclosure { } } + // --------------------------------- + // + // Sign determination + // + // --------------------------------- + /** \brief Return the position of the first non-zero coefficient of p. */ @@ -4156,6 +4160,12 @@ namespace realclosure { return determine_sign(to_rational_function(r.get())); } + // --------------------------------- + // + // Arithmetic operations + // + // --------------------------------- + /** \brief Set new_p1 and new_p2 using the following normalization rules: - new_p1 <- p1/p2[0]; new_p2 <- one IF sz2 == 1 @@ -4735,6 +4745,69 @@ namespace realclosure { set(c, r); } + /** + \brief a <- b^{1/k} + */ + void root(numeral const & a, unsigned k, numeral & b) { + if (k == 0) + throw exception("0-th root is indeterminate"); + + if (k == 1 || is_zero(a)) { + set(b, a); + return; + } + + if (sign(a) < 0 && k % 2 == 0) + throw exception("even root of negative number"); + + // create the polynomial p of the form x^k - a + value_ref_buffer p(*this); + value_ref neg_a(*this); + neg(a.m_value, neg_a); + p.push_back(neg_a); + for (unsigned i = 0; i < k - 1; i++) + p.push_back(0); + p.push_back(one()); + + numeral_vector roots; + nz_isolate_roots(p.size(), p.c_ptr(), roots); + SASSERT(roots.size() == 1 || roots.size() == 2); + if (roots.size() == 1 || sign(roots[0].m_value) > 0) { + set(b, roots[0]); + } + else { + SASSERT(roots.size() == 2); + SASSERT(sign(roots[1].m_value) > 0); + set(b, roots[1]); + } + del(roots); + } + + /** + \brief a <- b^k + */ + void power(numeral const & a, unsigned k, numeral & b) { + unsigned mask = 1; + value_ref power(*this); + value_ref _b(*this); + power = a.m_value; + _b = one(); + while (mask <= k) { + checkpoint(); + if (mask & k) + mul(_b, power, _b); + mul(power, power, power); + mask = mask << 1; + } + set(b, _b); + } + + // --------------------------------- + // + // Comparison + // + // --------------------------------- + int compare(value * a, value * b) { if (a == 0) return -sign(b); From e6102a8260d3555a9a1791c225fff78bcbc1aaf1 Mon Sep 17 00:00:00 2001 From: Leonardo de Moura Date: Sat, 12 Jan 2013 17:11:42 -0800 Subject: [PATCH 71/78] Move clean_denominators code to the top Signed-off-by: Leonardo de Moura --- src/math/realclosure/realclosure.cpp | 537 ++++++++++++++------------- 1 file changed, 269 insertions(+), 268 deletions(-) diff --git a/src/math/realclosure/realclosure.cpp b/src/math/realclosure/realclosure.cpp index f9dc76dfb..cd654dae6 100644 --- a/src/math/realclosure/realclosure.cpp +++ b/src/math/realclosure/realclosure.cpp @@ -2909,6 +2909,275 @@ namespace realclosure { neg(r); } + // --------------------------------- + // + // Structural equality + // + // --------------------------------- + + /** + \brief Values a and b are said to be "structurally" equal if: + - a and b are 0. + - a and b are rationals and compare(a, b) == 0 + - a and b are rational function values p_a(x)/q_a(x) and p_b(y)/q_b(y) where x and y are field extensions, and + * x == y (pointer equality, i.e., they are the same field extension object). + * Every coefficient of p_a is structurally equal to every coefficient of p_b + * Every coefficient of q_a is structurally equal to every coefficient of q_b + Clearly structural equality implies equality, but the reverse is not true. + */ + bool struct_eq(value * a, value * b) const { + if (a == b) + return true; + else if (a == 0 || b == 0) + return false; + else if (is_nz_rational(a) && is_nz_rational(b)) + return qm().eq(to_mpq(a), to_mpq(b)); + else if (is_nz_rational(a) || is_nz_rational(b)) + return false; + else { + SASSERT(is_rational_function(a)); + SASSERT(is_rational_function(b)); + rational_function_value * rf_a = to_rational_function(a); + rational_function_value * rf_b = to_rational_function(b); + if (rf_a->ext() != rf_b->ext()) + return false; + return struct_eq(rf_a->num(), rf_b->num()) && struct_eq(rf_a->den(), rf_b->den()); + } + } + + /** + Auxiliary method for + bool struct_eq(value * a, value * b) + */ + bool struct_eq(unsigned sz_a, value * const * p_a, unsigned sz_b, value * const * p_b) const { + if (sz_a != sz_b) + return false; + for (unsigned i = 0; i < sz_a; i++) { + if (!struct_eq(p_a[i], p_b[i])) + return false; + } + return true; + } + + /** + Auxiliary method for + bool struct_eq(value * a, value * b) + */ + bool struct_eq(polynomial const & p_a, polynomial const & p_b) const { + return struct_eq(p_a.size(), p_a.c_ptr(), p_b.size(), p_b.c_ptr()); + } + + // --------------------------------- + // + // Clean denominators + // + // --------------------------------- + + /** + \brief We say 'a' has "clean" denominators if + - a is 0 + - a is a rational_value that is an integer + - a is a rational_function_value of the form p_a(x)/1 where the coefficients of p_a also have clean denominators. + */ + bool has_clean_denominators(value * a) const { + if (a == 0) + return true; + else if (is_nz_rational(a)) + return qm().is_int(to_mpq(a)); + else { + rational_function_value * rf_a = to_rational_function(a); + return is_rational_one(rf_a->den()) && has_clean_denominators(rf_a->num()); + } + } + + /** + \brief See comment at has_clean_denominators(value * a) + */ + bool has_clean_denominators(polynomial const & p) const { + unsigned sz = p.size(); + for (unsigned i = 0; i < sz; i++) { + if (!has_clean_denominators(p[i])) + return false; + } + return true; + } + + /** + \brief "Clean" the denominators of 'a'. That is, return p and q s.t. + a == p/q + and + has_clean_denominators(p) and has_clean_denominators(q) + */ + void clean_denominators_core(value * a, value_ref & p, value_ref & q) { + INC_DEPTH(); + TRACE("rcf_clean", tout << "clean_denominators_core [" << m_exec_depth << "]\na: "; display(tout, a, false); tout << "\n";); + p.reset(); q.reset(); + if (a == 0) { + p = a; + q = one(); + } + else if (is_nz_rational(a)) { + p = mk_rational(to_mpq(a).numerator()); + q = mk_rational(to_mpq(a).denominator()); + } + else { + rational_function_value * rf_a = to_rational_function(a); + value_ref_buffer p_num(*this), p_den(*this); + value_ref d_num(*this), d_den(*this); + clean_denominators_core(rf_a->num(), p_num, d_num); + clean_denominators_core(rf_a->den(), p_den, d_den); + value_ref x(*this); + x = mk_rational_function_value(rf_a->ext()); + mk_polynomial_value(p_num.size(), p_num.c_ptr(), x, p); + mk_polynomial_value(p_den.size(), p_den.c_ptr(), x, q); + if (!struct_eq(d_den, d_num)) { + mul(p, d_den, p); + mul(q, d_num, q); + } + } + } + + /** + \brief Clean the denominators of the polynomial p, it returns clean_p and d s.t. + p = clean_p/d + and has_clean_denominators(clean_p) && has_clean_denominators(d) + */ + void clean_denominators_core(polynomial const & p, value_ref_buffer & norm_p, value_ref & d) { + value_ref_buffer nums(*this), dens(*this); + value_ref a_n(*this), a_d(*this); + bool all_one = true; + for (unsigned i = 0; i < p.size(); i++) { + if (p[i]) { + clean_denominators_core(p[i], a_n, a_d); + nums.push_back(a_n); + if (!is_rational_one(a_d)) + all_one = false; + dens.push_back(a_d); + } + else { + nums.push_back(0); + dens.push_back(0); + } + } + if (all_one) { + norm_p = nums; + d = one(); + } + else { + // Compute lcm of the integer elements in dens. + // This is a little trick to control the coefficient growth. + // We don't compute lcm of the other elements of dens because it is too expensive. + scoped_mpq lcm_z(qm()); + bool found_z = false; + SASSERT(nums.size() == p.size()); + SASSERT(dens.size() == p.size()); + for (unsigned i = 0; i < p.size(); i++) { + if (!dens[i]) + continue; + if (is_nz_rational(dens[i])) { + mpq const & _d = to_mpq(dens[i]); + SASSERT(qm().is_int(_d)); + if (!found_z) { + found_z = true; + qm().set(lcm_z, _d); + } + else { + qm().lcm(lcm_z, _d, lcm_z); + } + } + } + + value_ref lcm(*this); + if (found_z) { + lcm = mk_rational(lcm_z); + } + else { + lcm = one(); + } + + // Compute the multipliers for nums. + // Compute norm_p and d + // + // We do NOT use GCD to compute the LCM of the denominators of non-rational values. + // However, we detect structurally equivalent denominators. + // + // Thus a/(b+1) + c/(b+1) is converted into a*c/(b+1) instead of (a*(b+1) + c*(b+1))/(b+1)^2 + norm_p.reset(); + d = lcm; + value_ref_buffer multipliers(*this); + value_ref m(*this); + for (unsigned i = 0; i < p.size(); i++) { + if (!nums[i]) { + norm_p.push_back(0); + } + else { + SASSERT(dens[i]); + bool is_z; + if (!is_nz_rational(dens[i])) { + m = lcm; + is_z = false; + } + else { + scoped_mpq num_z(qm()); + qm().div(lcm_z, to_mpq(dens[i]), num_z); + SASSERT(qm().is_int(num_z)); + m = mk_rational_and_swap(num_z); + is_z = true; + } + bool found_lt_eq = false; + for (unsigned j = 0; j < p.size(); j++) { + TRACE("rcf_clean_bug", tout << "j: " << j << " "; display(tout, m, false); tout << "\n";); + if (!dens[j]) + continue; + if (i != j && !is_nz_rational(dens[j])) { + if (struct_eq(dens[i], dens[j])) { + if (j < i) + found_lt_eq = true; + } + else { + mul(m, dens[j], m); + } + } + } + if (!is_z && !found_lt_eq) { + mul(dens[i], d, d); + } + mul(m, nums[i], m); + norm_p.push_back(m); + } + } + } + SASSERT(norm_p.size() == p.size()); + } + + void clean_denominators(value * a, value_ref & p, value_ref & q) { + if (has_clean_denominators(a)) { + p = a; + q = one(); + } + else { + clean_denominators_core(a, p, q); + } + } + + void clean_denominators(polynomial const & p, value_ref_buffer & norm_p, value_ref & d) { + if (has_clean_denominators(p)) { + norm_p.append(p.size(), p.c_ptr()); + d = one(); + } + else { + clean_denominators_core(p, norm_p, d); + } + } + + void clean_denominators(numeral const & a, numeral & p, numeral & q) { + value_ref _p(*this), _q(*this); + clean_denominators(a.m_value, _p, _q); + set(p, _p); + set(q, _q); + } + + // --------------------------------- // // GCD @@ -4837,274 +5106,6 @@ namespace realclosure { return compare(a.m_value, b.m_value); } - // --------------------------------- - // - // Structural equality - // - // --------------------------------- - - /** - \brief Values a and b are said to be "structurally" equal if: - - a and b are 0. - - a and b are rationals and compare(a, b) == 0 - - a and b are rational function values p_a(x)/q_a(x) and p_b(y)/q_b(y) where x and y are field extensions, and - * x == y (pointer equality, i.e., they are the same field extension object). - * Every coefficient of p_a is structurally equal to every coefficient of p_b - * Every coefficient of q_a is structurally equal to every coefficient of q_b - Clearly structural equality implies equality, but the reverse is not true. - */ - bool struct_eq(value * a, value * b) const { - if (a == b) - return true; - else if (a == 0 || b == 0) - return false; - else if (is_nz_rational(a) && is_nz_rational(b)) - return qm().eq(to_mpq(a), to_mpq(b)); - else if (is_nz_rational(a) || is_nz_rational(b)) - return false; - else { - SASSERT(is_rational_function(a)); - SASSERT(is_rational_function(b)); - rational_function_value * rf_a = to_rational_function(a); - rational_function_value * rf_b = to_rational_function(b); - if (rf_a->ext() != rf_b->ext()) - return false; - return struct_eq(rf_a->num(), rf_b->num()) && struct_eq(rf_a->den(), rf_b->den()); - } - } - - /** - Auxiliary method for - bool struct_eq(value * a, value * b) - */ - bool struct_eq(unsigned sz_a, value * const * p_a, unsigned sz_b, value * const * p_b) const { - if (sz_a != sz_b) - return false; - for (unsigned i = 0; i < sz_a; i++) { - if (!struct_eq(p_a[i], p_b[i])) - return false; - } - return true; - } - - /** - Auxiliary method for - bool struct_eq(value * a, value * b) - */ - bool struct_eq(polynomial const & p_a, polynomial const & p_b) const { - return struct_eq(p_a.size(), p_a.c_ptr(), p_b.size(), p_b.c_ptr()); - } - - // --------------------------------- - // - // Clean denominators - // - // --------------------------------- - - /** - \brief We say 'a' has "clean" denominators if - - a is 0 - - a is a rational_value that is an integer - - a is a rational_function_value of the form p_a(x)/1 where the coefficients of p_a also have clean denominators. - */ - bool has_clean_denominators(value * a) const { - if (a == 0) - return true; - else if (is_nz_rational(a)) - return qm().is_int(to_mpq(a)); - else { - rational_function_value * rf_a = to_rational_function(a); - return is_rational_one(rf_a->den()) && has_clean_denominators(rf_a->num()); - } - } - - /** - \brief See comment at has_clean_denominators(value * a) - */ - bool has_clean_denominators(polynomial const & p) const { - unsigned sz = p.size(); - for (unsigned i = 0; i < sz; i++) { - if (!has_clean_denominators(p[i])) - return false; - } - return true; - } - - /** - \brief "Clean" the denominators of 'a'. That is, return p and q s.t. - a == p/q - and - has_clean_denominators(p) and has_clean_denominators(q) - */ - void clean_denominators_core(value * a, value_ref & p, value_ref & q) { - INC_DEPTH(); - TRACE("rcf_clean", tout << "clean_denominators_core [" << m_exec_depth << "]\na: "; display(tout, a, false); tout << "\n";); - p.reset(); q.reset(); - if (a == 0) { - p = a; - q = one(); - } - else if (is_nz_rational(a)) { - p = mk_rational(to_mpq(a).numerator()); - q = mk_rational(to_mpq(a).denominator()); - } - else { - rational_function_value * rf_a = to_rational_function(a); - value_ref_buffer p_num(*this), p_den(*this); - value_ref d_num(*this), d_den(*this); - clean_denominators_core(rf_a->num(), p_num, d_num); - clean_denominators_core(rf_a->den(), p_den, d_den); - value_ref x(*this); - x = mk_rational_function_value(rf_a->ext()); - mk_polynomial_value(p_num.size(), p_num.c_ptr(), x, p); - mk_polynomial_value(p_den.size(), p_den.c_ptr(), x, q); - if (!struct_eq(d_den, d_num)) { - mul(p, d_den, p); - mul(q, d_num, q); - } - } - } - - /** - \brief Clean the denominators of the polynomial p, it returns clean_p and d s.t. - p = clean_p/d - and has_clean_denominators(clean_p) && has_clean_denominators(d) - */ - void clean_denominators_core(polynomial const & p, value_ref_buffer & norm_p, value_ref & d) { - value_ref_buffer nums(*this), dens(*this); - value_ref a_n(*this), a_d(*this); - bool all_one = true; - for (unsigned i = 0; i < p.size(); i++) { - if (p[i]) { - clean_denominators_core(p[i], a_n, a_d); - nums.push_back(a_n); - if (!is_rational_one(a_d)) - all_one = false; - dens.push_back(a_d); - } - else { - nums.push_back(0); - dens.push_back(0); - } - } - if (all_one) { - norm_p = nums; - d = one(); - } - else { - // Compute lcm of the integer elements in dens. - // This is a little trick to control the coefficient growth. - // We don't compute lcm of the other elements of dens because it is too expensive. - scoped_mpq lcm_z(qm()); - bool found_z = false; - SASSERT(nums.size() == p.size()); - SASSERT(dens.size() == p.size()); - for (unsigned i = 0; i < p.size(); i++) { - if (!dens[i]) - continue; - if (is_nz_rational(dens[i])) { - mpq const & _d = to_mpq(dens[i]); - SASSERT(qm().is_int(_d)); - if (!found_z) { - found_z = true; - qm().set(lcm_z, _d); - } - else { - qm().lcm(lcm_z, _d, lcm_z); - } - } - } - - value_ref lcm(*this); - if (found_z) { - lcm = mk_rational(lcm_z); - } - else { - lcm = one(); - } - - // Compute the multipliers for nums. - // Compute norm_p and d - // - // We do NOT use GCD to compute the LCM of the denominators of non-rational values. - // However, we detect structurally equivalent denominators. - // - // Thus a/(b+1) + c/(b+1) is converted into a*c/(b+1) instead of (a*(b+1) + c*(b+1))/(b+1)^2 - norm_p.reset(); - d = lcm; - value_ref_buffer multipliers(*this); - value_ref m(*this); - for (unsigned i = 0; i < p.size(); i++) { - if (!nums[i]) { - norm_p.push_back(0); - } - else { - SASSERT(dens[i]); - bool is_z; - if (!is_nz_rational(dens[i])) { - m = lcm; - is_z = false; - } - else { - scoped_mpq num_z(qm()); - qm().div(lcm_z, to_mpq(dens[i]), num_z); - SASSERT(qm().is_int(num_z)); - m = mk_rational_and_swap(num_z); - is_z = true; - } - bool found_lt_eq = false; - for (unsigned j = 0; j < p.size(); j++) { - TRACE("rcf_clean_bug", tout << "j: " << j << " "; display(tout, m, false); tout << "\n";); - if (!dens[j]) - continue; - if (i != j && !is_nz_rational(dens[j])) { - if (struct_eq(dens[i], dens[j])) { - if (j < i) - found_lt_eq = true; - } - else { - mul(m, dens[j], m); - } - } - } - if (!is_z && !found_lt_eq) { - mul(dens[i], d, d); - } - mul(m, nums[i], m); - norm_p.push_back(m); - } - } - } - SASSERT(norm_p.size() == p.size()); - } - - void clean_denominators(value * a, value_ref & p, value_ref & q) { - if (has_clean_denominators(a)) { - p = a; - q = one(); - } - else { - clean_denominators_core(a, p, q); - } - } - - void clean_denominators(polynomial const & p, value_ref_buffer & norm_p, value_ref & d) { - if (has_clean_denominators(p)) { - norm_p.append(p.size(), p.c_ptr()); - d = one(); - } - else { - clean_denominators_core(p, norm_p, d); - } - } - - void clean_denominators(numeral const & a, numeral & p, numeral & q) { - value_ref _p(*this), _q(*this); - clean_denominators(a.m_value, _p, _q); - set(p, _p); - set(q, _q); - } - // --------------------------------- // // "Pretty printing" From 13d5c3e07a3357b44e8613745fa37e5b694c4b55 Mon Sep 17 00:00:00 2001 From: Leonardo de Moura Date: Sat, 12 Jan 2013 18:01:29 -0800 Subject: [PATCH 72/78] Add normalize_int_coeffs to control the coefficient growth in Sturm sequences Signed-off-by: Leonardo de Moura --- src/math/realclosure/realclosure.cpp | 151 ++++++++++++++++++++++++++- 1 file changed, 149 insertions(+), 2 deletions(-) diff --git a/src/math/realclosure/realclosure.cpp b/src/math/realclosure/realclosure.cpp index cd654dae6..9e3019c27 100644 --- a/src/math/realclosure/realclosure.cpp +++ b/src/math/realclosure/realclosure.cpp @@ -614,6 +614,17 @@ namespace realclosure { SASSERT(!contains_zero(c)); } + /** + \brief c <- a/b with precision prec. + */ + void div(mpbqi const & a, mpz const & b, unsigned prec, mpbqi & c) { + SASSERT(!contains_zero(a)); + SASSERT(!qm().is_zero(b)); + scoped_mpbqi bi(bqim()); + set_interval(bi, b); + div(a, bi, prec, c); + } + /** \brief Save the current interval (i.e., approximation) of the given value. */ @@ -1155,6 +1166,16 @@ namespace realclosure { set_upper_core(a, b, false, false); } + /** + \brief a <- [b, b] + */ + void set_interval(mpbqi & a, mpz const & b) { + scoped_mpbq _b(bqm()); + bqm().set(_b, b); + set_lower_core(a, _b, false, false); + set_upper_core(a, _b, false, false); + } + sign_condition * mk_sign_condition(unsigned qidx, int sign, sign_condition * prev_sc) { return new (allocator()) sign_condition(qidx, sign, prev_sc); } @@ -3177,6 +3198,129 @@ namespace realclosure { set(q, _q); } + // --------------------------------- + // + // GCD of integer coefficients + // + // --------------------------------- + + /** + \brief If has_clean_denominators(a), then this method store the gcd of the integer coefficients in g. + If !has_clean_denominators(a) it returns false. + + If g != 0, then it will compute the gcd of g and the coefficients in a. + */ + bool gcd_int_coeffs(value * a, mpz & g) { + if (a == 0) { + return false; + } + else if (is_nz_rational(a)) { + if (!qm().is_int(to_mpq(a))) + return false; + else if (qm().is_zero(g)) { + qm().set(g, to_mpq(a).numerator()); + qm().abs(g); + } + else { + qm().gcd(g, to_mpq(a).numerator(), g); + } + return true; + } + else { + rational_function_value * rf_a = to_rational_function(a); + if (is_rational_one(rf_a->den())) + return false; + else + return gcd_int_coeffs(rf_a->num(), g); + } + } + + /** + \brief See comment in gcd_int_coeffs(value * a, mpz & g) + */ + bool gcd_int_coeffs(unsigned p_sz, value * const * p, mpz & g) { + if (p_sz == 0) { + return false; + } + else { + for (unsigned i = 0; i < p_sz; i++) { + if (p[i]) { + if (!gcd_int_coeffs(p[i], g)) + return false; + if (qm().is_one(g)) + return true; + } + } + return true; + } + } + + /** + \brief See comment in gcd_int_coeffs(value * a, mpz & g) + */ + bool gcd_int_coeffs(polynomial const & p, mpz & g) { + return gcd_int_coeffs(p.size(), p.c_ptr(), g); + } + + /** + \brief Compute gcd_int_coeffs and divide p by it (if applicable). + */ + void normalize_int_coeffs(value_ref_buffer & p) { + scoped_mpz g(qm()); + if (gcd_int_coeffs(p.size(), p.c_ptr(), g) && !qm().is_one(g)) { + SASSERT(qm().is_pos(g)); + value_ref a(*this); + for (unsigned i = 0; i < p.size(); i++) { + if (p[i]) { + a = p[i]; + p.set(i, 0); + exact_div_z(a, g); + p.set(i, a); + } + } + } + } + + /** + \brief a <- a/b where b > 0 + Auxiliary function for normalize_int_coeffs. + It assumes has_clean_denominators(a), and that b divides all integer coefficients. + + FUTURE: perform the operation using destructive updates when a is not shared. + */ + void exact_div_z(value_ref & a, mpz const & b) { + if (a == 0) { + return; + } + else if (is_nz_rational(a)) { + scoped_mpq r(qm()); + SASSERT(qm().is_int(to_mpq(a))); + qm().div(to_mpq(a), b, r); + a = mk_rational_and_swap(r); + } + else { + rational_function_value * rf = to_rational_function(a); + SASSERT(is_rational_one(rf->den())); + value_ref_buffer new_ais(*this); + value_ref ai(*this); + polynomial const & p = rf->num(); + for (unsigned i = 0; i < p.size(); i++) { + if (p[i]) { + ai = p[i]; + exact_div_z(ai, b); + new_ais.push_back(ai); + } + else { + new_ais.push_back(0); + } + } + rational_function_value * r = mk_rational_function_value_core(rf->ext(), new_ais.size(), new_ais.c_ptr(), 1, &m_one); + set_interval(r->m_interval, rf->m_interval); + a = r; + // divide upper and lower by b + div(r->m_interval, b, m_ini_precision, r->m_interval); + } + } // --------------------------------- // @@ -3301,10 +3445,13 @@ namespace realclosure { value_ref_buffer r(*this); while (true) { unsigned sz = seq.size(); - if (m_use_prem) + if (m_use_prem) { sprem(seq.size(sz-2), seq.coeffs(sz-2), seq.size(sz-1), seq.coeffs(sz-1), r); - else + normalize_int_coeffs(r); + } + else { srem(seq.size(sz-2), seq.coeffs(sz-2), seq.size(sz-1), seq.coeffs(sz-1), r); + } TRACE("rcf_sturm_seq", tout << "sturm_seq_core [" << m_exec_depth << "], new polynomial\n"; display_poly(tout, r.size(), r.c_ptr()); tout << "\n";); From 7711146d2311ae6e5640700b56b892299a660bf8 Mon Sep 17 00:00:00 2001 From: Leonardo de Moura Date: Sat, 12 Jan 2013 19:36:07 -0800 Subject: [PATCH 73/78] Add prem_gcd based on pseudo-remainder Signed-off-by: Leonardo de Moura --- src/math/realclosure/realclosure.cpp | 54 ++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/src/math/realclosure/realclosure.cpp b/src/math/realclosure/realclosure.cpp index 9e3019c27..8840fd826 100644 --- a/src/math/realclosure/realclosure.cpp +++ b/src/math/realclosure/realclosure.cpp @@ -2866,6 +2866,11 @@ namespace realclosure { } } + void prem(unsigned sz1, value * const * p1, unsigned sz2, value * const * p2, value_ref_buffer & r) { + unsigned d; + prem(sz1, p1, sz2, p2, d, r); + } + /** \brief r <- -p */ @@ -3386,6 +3391,55 @@ namespace realclosure { } } + void flip_sign_if_lc_neg(value_ref_buffer & r) { + unsigned sz = r.size(); + if (sz == 0) + return; + if (sign(r[sz - 1]) < 0) + neg(r); + } + + void prem_gcd(unsigned sz1, value * const * p1, unsigned sz2, value * const * p2, value_ref_buffer & r) { + INC_DEPTH(); + TRACE("rcf_gcd", tout << "prem-GCD [" << m_exec_depth << "]\n"; + display_poly(tout, sz1, p1); tout << "\n"; + display_poly(tout, sz2, p2); tout << "\n";); + SASSERT(p1 != r.c_ptr()); + SASSERT(p2 != r.c_ptr()); + if (sz1 == 0) { + r.append(sz2, p2); + flip_sign_if_lc_neg(r); + } + else if (sz2 == 0) { + r.append(sz1, p1); + flip_sign_if_lc_neg(r); + } + else { + value_ref_buffer A(*this); + value_ref_buffer B(*this); + value_ref_buffer R(*this); + A.append(sz1, p1); + B.append(sz2, p2); + while (true) { + TRACE("rcf_gcd", + tout << "A: "; display_poly(tout, A.size(), A.c_ptr()); tout << "\n"; + tout << "B: "; display_poly(tout, B.size(), B.c_ptr()); tout << "\n";); + if (B.empty()) { + normalize_int_coeffs(A); + flip_sign_if_lc_neg(A); + r = A; + TRACE("rcf_gcd", + tout << "gcd result: "; display_poly(tout, r.size(), r.c_ptr()); tout << "\n";); + return; + } + prem(A.size(), A.c_ptr(), B.size(), B.c_ptr(), R); + normalize_int_coeffs(R); + A = B; + B = R; + } + } + } + // --------------------------------- // // Derivatives and Sturm-Tarski Sequences From 551d0b7de041c0d6ef40bf1e1cdd54936a56883b Mon Sep 17 00:00:00 2001 From: Leonardo de Moura Date: Sat, 12 Jan 2013 19:44:05 -0800 Subject: [PATCH 74/78] Fix bug in sprem Signed-off-by: Leonardo de Moura --- src/math/realclosure/realclosure.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/math/realclosure/realclosure.cpp b/src/math/realclosure/realclosure.cpp index 8840fd826..37b438136 100644 --- a/src/math/realclosure/realclosure.cpp +++ b/src/math/realclosure/realclosure.cpp @@ -2931,7 +2931,7 @@ namespace realclosure { unsigned d; prem(sz1, p1, sz2, p2, d, r); // We should not flip the sign if d is odd and leading coefficient of p2 is negative. - if (d % 2 == 0 || (sz2 > 0 && sign(p2[sz2-1]) < 0)) + if (d % 2 == 0 || (sz2 > 0 && sign(p2[sz2-1]) > 0)) neg(r); } From 2b5883454c1b50afa76a80799c9cb4782b5f045f Mon Sep 17 00:00:00 2001 From: Leonardo de Moura Date: Sat, 12 Jan 2013 19:49:50 -0800 Subject: [PATCH 75/78] Add support for prem_gcd in square_free Signed-off-by: Leonardo de Moura --- src/math/realclosure/realclosure.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/math/realclosure/realclosure.cpp b/src/math/realclosure/realclosure.cpp index 37b438136..e4e174cc1 100644 --- a/src/math/realclosure/realclosure.cpp +++ b/src/math/realclosure/realclosure.cpp @@ -3475,12 +3475,17 @@ namespace realclosure { value_ref_buffer p_prime(*this); value_ref_buffer g(*this); derivative(sz, p, p_prime); - gcd(sz, p, p_prime.size(), p_prime.c_ptr(), g); + if (m_use_prem) + prem_gcd(sz, p, p_prime.size(), p_prime.c_ptr(), g); + else + gcd(sz, p, p_prime.size(), p_prime.c_ptr(), g); if (g.size() <= 1) { r.append(sz, p); } else { div(sz, p, g.size(), g.c_ptr(), r); + if (m_use_prem) + normalize_int_coeffs(r); } } } From be2bf861c766dda713348cf606b9b2a6fe95c477 Mon Sep 17 00:00:00 2001 From: Leonardo de Moura Date: Sat, 12 Jan 2013 20:43:17 -0800 Subject: [PATCH 76/78] Use clean_denominators before root isolation Signed-off-by: Leonardo de Moura --- src/math/realclosure/rcf.pyg | 1 + src/math/realclosure/realclosure.cpp | 82 ++++++++++++++++++++-------- 2 files changed, 60 insertions(+), 23 deletions(-) diff --git a/src/math/realclosure/rcf.pyg b/src/math/realclosure/rcf.pyg index ad24ad530..a394ae32a 100644 --- a/src/math/realclosure/rcf.pyg +++ b/src/math/realclosure/rcf.pyg @@ -2,6 +2,7 @@ def_module_params('rcf', description='real closed fields', export=True, params=(('use_prem', BOOL, True, "use pseudo-remainder instead of remainder when computing GCDs and Sturm-Tarski sequences"), + ('clean_denominators', BOOL, True, "clean denominators before root isolation"), ('initial_precision', UINT, 24, "a value k that is the initial interval size (as 1/2^k) when creating transcendentals and approximated division"), ('inf_precision', UINT, 24, "a value k that is the initial interval size (i.e., (0, 1/2^l)) used as an approximation for infinitesimal values"), ('max_precision', UINT, 64, "during sign determination we switch from interval arithmetic to complete methods when the interval size is less than 1/2^k, where k is the max_precision"))) diff --git a/src/math/realclosure/realclosure.cpp b/src/math/realclosure/realclosure.cpp index e4e174cc1..246f61b90 100644 --- a/src/math/realclosure/realclosure.cpp +++ b/src/math/realclosure/realclosure.cpp @@ -368,6 +368,7 @@ namespace realclosure { // Parameters bool m_use_prem; //!< use pseudo-remainder when computing sturm sequences + bool m_clean_denominators; unsigned m_ini_precision; //!< initial precision for transcendentals, infinitesimals, etc. unsigned m_max_precision; //!< Maximum precision for interval arithmetic techniques, it switches to complete methods after that unsigned m_inf_precision; //!< 2^m_inf_precision is used as the lower bound of oo and -2^m_inf_precision is used as the upper_bound of -oo @@ -690,10 +691,11 @@ namespace realclosure { void updt_params(params_ref const & _p) { rcf_params p(_p); - m_use_prem = p.use_prem(); - m_ini_precision = p.initial_precision(); - m_inf_precision = p.inf_precision(); - m_max_precision = p.max_precision(); + m_use_prem = p.use_prem(); + m_clean_denominators = p.clean_denominators(); + m_ini_precision = p.initial_precision(); + m_inf_precision = p.inf_precision(); + m_max_precision = p.max_precision(); bqm().power(mpbq(2), m_inf_precision, m_plus_inf_approx); bqm().set(m_minus_inf_approx, m_plus_inf_approx); bqm().neg(m_minus_inf_approx); @@ -2286,15 +2288,15 @@ namespace realclosure { } /** - \brief Root isolation for polynomials where 0 is not a root. + \brief Root isolation for polynomials where 0 is not a root, and the denominators have been cleaned + when m_clean_denominators == true + */ - void nz_isolate_roots(unsigned n, value * const * p, numeral_vector & roots) { - TRACE("rcf_isolate", - tout << "nz_isolate_roots\n"; - display_poly(tout, n, p); tout << "\n";); + void nz_cd_isolate_roots(unsigned n, value * const * p, numeral_vector & roots) { SASSERT(n > 0); SASSERT(!is_zero(p[0])); SASSERT(!is_zero(p[n-1])); + SASSERT(!m_clean_denominators || has_clean_denominators(n, p)); if (n == 1) { // constant polynomial return; @@ -2304,6 +2306,26 @@ namespace realclosure { nz_sqf_isolate_roots(sqf.size(), sqf.c_ptr(), roots); } + /** + \brief Root isolation for polynomials where 0 is not a root. + */ + void nz_isolate_roots(unsigned n, value * const * p, numeral_vector & roots) { + TRACE("rcf_isolate", + tout << "nz_isolate_roots\n"; + display_poly(tout, n, p); tout << "\n";); + if (m_clean_denominators) { + value_ref d(*this); + value_ref_buffer norm_p(*this); + clean_denominators(n, p, norm_p, d); + if (sign(d) < 0) + neg(norm_p); + nz_cd_isolate_roots(norm_p.size(), norm_p.c_ptr(), roots); + } + else { + nz_cd_isolate_roots(n, p, roots); + } + } + /** \brief Root isolation entry point. */ @@ -3019,14 +3041,20 @@ namespace realclosure { /** \brief See comment at has_clean_denominators(value * a) */ - bool has_clean_denominators(polynomial const & p) const { - unsigned sz = p.size(); + bool has_clean_denominators(unsigned sz, value * const * p) const { for (unsigned i = 0; i < sz; i++) { if (!has_clean_denominators(p[i])) return false; } return true; } + + /** + \brief See comment at has_clean_denominators(value * a) + */ + bool has_clean_denominators(polynomial const & p) const { + return has_clean_denominators(p.size(), p.c_ptr()); + } /** \brief "Clean" the denominators of 'a'. That is, return p and q s.t. @@ -3068,11 +3096,11 @@ namespace realclosure { p = clean_p/d and has_clean_denominators(clean_p) && has_clean_denominators(d) */ - void clean_denominators_core(polynomial const & p, value_ref_buffer & norm_p, value_ref & d) { + void clean_denominators_core(unsigned p_sz, value * const * p, value_ref_buffer & norm_p, value_ref & d) { value_ref_buffer nums(*this), dens(*this); value_ref a_n(*this), a_d(*this); bool all_one = true; - for (unsigned i = 0; i < p.size(); i++) { + for (unsigned i = 0; i < p_sz; i++) { if (p[i]) { clean_denominators_core(p[i], a_n, a_d); nums.push_back(a_n); @@ -3095,9 +3123,9 @@ namespace realclosure { // We don't compute lcm of the other elements of dens because it is too expensive. scoped_mpq lcm_z(qm()); bool found_z = false; - SASSERT(nums.size() == p.size()); - SASSERT(dens.size() == p.size()); - for (unsigned i = 0; i < p.size(); i++) { + SASSERT(nums.size() == p_sz); + SASSERT(dens.size() == p_sz); + for (unsigned i = 0; i < p_sz; i++) { if (!dens[i]) continue; if (is_nz_rational(dens[i])) { @@ -3132,7 +3160,7 @@ namespace realclosure { d = lcm; value_ref_buffer multipliers(*this); value_ref m(*this); - for (unsigned i = 0; i < p.size(); i++) { + for (unsigned i = 0; i < p_sz; i++) { if (!nums[i]) { norm_p.push_back(0); } @@ -3151,7 +3179,7 @@ namespace realclosure { is_z = true; } bool found_lt_eq = false; - for (unsigned j = 0; j < p.size(); j++) { + for (unsigned j = 0; j < p_sz; j++) { TRACE("rcf_clean_bug", tout << "j: " << j << " "; display(tout, m, false); tout << "\n";); if (!dens[j]) continue; @@ -3173,7 +3201,11 @@ namespace realclosure { } } } - SASSERT(norm_p.size() == p.size()); + SASSERT(norm_p.size() == p_sz); + } + + void clean_denominators_core(polynomial const & p, value_ref_buffer & norm_p, value_ref & d) { + clean_denominators_core(p.size(), p.c_ptr(), norm_p, d); } void clean_denominators(value * a, value_ref & p, value_ref & q) { @@ -3186,16 +3218,20 @@ namespace realclosure { } } - void clean_denominators(polynomial const & p, value_ref_buffer & norm_p, value_ref & d) { - if (has_clean_denominators(p)) { - norm_p.append(p.size(), p.c_ptr()); + void clean_denominators(unsigned sz, value * const * p, value_ref_buffer & norm_p, value_ref & d) { + if (has_clean_denominators(sz, p)) { + norm_p.append(sz, p); d = one(); } else { - clean_denominators_core(p, norm_p, d); + clean_denominators_core(sz, p, norm_p, d); } } + void clean_denominators(polynomial const & p, value_ref_buffer & norm_p, value_ref & d) { + clean_denominators(p.size(), p.c_ptr(), norm_p, d); + } + void clean_denominators(numeral const & a, numeral & p, numeral & q) { value_ref _p(*this), _q(*this); clean_denominators(a.m_value, _p, _q); From f747bde54895c3a1ba74a528a6bdfee646a19852 Mon Sep 17 00:00:00 2001 From: Leonardo de Moura Date: Sat, 12 Jan 2013 21:59:41 -0800 Subject: [PATCH 77/78] Add restore_interval for extensions Signed-off-by: Leonardo de Moura --- src/math/realclosure/realclosure.cpp | 58 ++++++++++++++++++++-------- 1 file changed, 41 insertions(+), 17 deletions(-) diff --git a/src/math/realclosure/realclosure.cpp b/src/math/realclosure/realclosure.cpp index 246f61b90..09e722f23 100644 --- a/src/math/realclosure/realclosure.cpp +++ b/src/math/realclosure/realclosure.cpp @@ -217,8 +217,9 @@ namespace realclosure { unsigned m_kind:2; unsigned m_idx:30; mpbqi m_interval; + mpbqi * m_old_interval; - extension(kind k, unsigned idx):m_ref_count(0), m_kind(k), m_idx(idx) {} + extension(kind k, unsigned idx):m_ref_count(0), m_kind(k), m_idx(idx), m_old_interval(0) {} unsigned idx() const { return m_idx; } kind knd() const { return static_cast(m_kind); } @@ -365,6 +366,7 @@ namespace realclosure { mk_e_interval m_mk_e_interval; value * m_e; ptr_vector m_to_restore; //!< Set of values v s.t. v->m_old_interval != 0 + ptr_vector m_ex_to_restore; // Parameters bool m_use_prem; //!< use pseudo-remainder when computing sturm sequences @@ -627,39 +629,59 @@ namespace realclosure { } /** - \brief Save the current interval (i.e., approximation) of the given value. + \brief Save the current interval (i.e., approximation) of the given value or extension. */ - void save_interval(value * v) { + template + void save_interval(T * v, ptr_vector & to_restore) { if (v->m_old_interval != 0) return; // interval was already saved. - m_to_restore.push_back(v); + to_restore.push_back(v); inc_ref(v); v->m_old_interval = new (allocator()) mpbqi(); set_interval(*(v->m_old_interval), v->m_interval); } + void save_interval(value * v) { + save_interval(v, m_to_restore); + } + void save_interval(extension * x) { + save_interval(x, m_ex_to_restore); + } /** - \brief Save the current interval (i.e., approximation) of the given value IF it is too small (i.e., too_small(v) == True). + \brief Save the current interval (i.e., approximation) of the given value IF it is too small. */ - void save_interval_if_too_small(value * v) { - if (too_small(v->m_interval)) + void save_interval_if_too_small(value * v, unsigned new_prec) { + if (new_prec > m_max_precision && !contains_zero(interval(v))) save_interval(v); } /** - \brief Restore the saved intervals (approximations) of RCF values. + \brief Save the current interval (i.e., approximation) of the given value IF it is too small. */ - void restore_saved_intervals() { - unsigned sz = m_to_restore.size(); + void save_interval_if_too_small(extension * x, unsigned new_prec) { + if (new_prec > m_max_precision && !contains_zero(x->m_interval)) + save_interval(x); + } + + /** + \brief Restore the saved intervals (approximations) of RCF values and extensions + */ + template + void restore_saved_intervals(ptr_vector & to_restore) { + unsigned sz = to_restore.size(); for (unsigned i = 0; i < sz; i++) { - value * v = m_to_restore[i]; + T * v = to_restore[i]; set_interval(v->m_interval, *(v->m_old_interval)); bqim().del(*(v->m_old_interval)); allocator().deallocate(sizeof(mpbqi), v->m_old_interval); v->m_old_interval = 0; dec_ref(v); } - m_to_restore.reset(); + to_restore.reset(); + } + void restore_saved_intervals() { + restore_saved_intervals(m_to_restore); + restore_saved_intervals(m_ex_to_restore); } void cleanup(extension::kind k) { @@ -720,7 +742,7 @@ namespace realclosure { bqim().del(v->m_interval); reset_p(v->num()); reset_p(v->den()); - dec_ref_ext(v->ext()); + dec_ref(v->ext()); allocator().deallocate(sizeof(rational_function_value), v); } @@ -797,12 +819,12 @@ namespace realclosure { allocator().deallocate(sizeof(infinitesimal), i); } - void inc_ref_ext(extension * ext) { + void inc_ref(extension * ext) { SASSERT(ext != 0); ext->m_ref_count++; } - void dec_ref_ext(extension * ext) { + void dec_ref(extension * ext) { SASSERT(m_extensions[ext->knd()][ext->idx()] == ext); SASSERT(ext->m_ref_count > 0); ext->m_ref_count--; @@ -1188,7 +1210,7 @@ namespace realclosure { */ rational_function_value * mk_rational_function_value_core(extension * ext, unsigned num_sz, value * const * num, unsigned den_sz, value * const * den) { rational_function_value * r = alloc(rational_function_value, ext); - inc_ref_ext(ext); + inc_ref(ext); set_p(r->num(), num_sz, num); set_p(r->den(), den_sz, den); r->set_depends_on_infinitesimals(depends_on_infinitesimals(ext) || depends_on_infinitesimals(num_sz, num) || depends_on_infinitesimals(den_sz, den)); @@ -1256,6 +1278,7 @@ namespace realclosure { while (!check_precision(t->interval(), prec)) { TRACE("rcf_transcendental", tout << "refine_transcendental_interval: " << magnitude(t->interval()) << std::endl;); checkpoint(); + save_interval_if_too_small(t, prec); refine_transcendental_interval(t); } } @@ -4106,6 +4129,7 @@ namespace realclosure { } bool refine_algebraic_interval(algebraic * a, unsigned prec) { + save_interval_if_too_small(a, prec); if (a->sdt() != 0) { // we can't bisect the interval, since it contains more than one root. return false; @@ -4178,7 +4202,7 @@ namespace realclosure { int m = magnitude(interval(v)); if (m == INT_MIN || (m < 0 && static_cast(-m) > prec)) return true; - save_interval_if_too_small(v); + save_interval_if_too_small(v, prec); if (is_nz_rational(v)) { refine_rational_interval(to_nz_rational(v), prec); return true; From 7312f49f888e753306625d975efa30236747fca7 Mon Sep 17 00:00:00 2001 From: Leonardo de Moura Date: Sun, 13 Jan 2013 09:06:07 -0800 Subject: [PATCH 78/78] Fix Visual Studio warnings Signed-off-by: Leonardo de Moura --- src/api/api_rcf.cpp | 32 ++++++++++----------- src/math/realclosure/mpz_matrix.cpp | 5 ++-- src/math/realclosure/realclosure.cpp | 31 +++++++++++--------- src/math/realclosure/realclosure.h | 4 +-- src/util/array.h | 42 ++++++++++++++-------------- 5 files changed, 60 insertions(+), 54 deletions(-) diff --git a/src/api/api_rcf.cpp b/src/api/api_rcf.cpp index a7feada42..6b9826af7 100644 --- a/src/api/api_rcf.cpp +++ b/src/api/api_rcf.cpp @@ -25,24 +25,24 @@ Notes: #include"api_context.h" #include"realclosure.h" +static rcmanager & rcfm(Z3_context c) { + return mk_c(c)->rcfm(); +} + +static void reset_rcf_cancel(Z3_context c) { + rcfm(c).reset_cancel(); +} + +static Z3_rcf_num from_rcnumeral(rcnumeral a) { + return reinterpret_cast(a.c_ptr()); +} + +static rcnumeral to_rcnumeral(Z3_rcf_num a) { + return rcnumeral::mk(a); +} + extern "C" { - static rcmanager & rcfm(Z3_context c) { - return mk_c(c)->rcfm(); - } - - static void reset_rcf_cancel(Z3_context c) { - rcfm(c).reset_cancel(); - } - - static rcnumeral to_rcnumeral(Z3_rcf_num a) { - return rcnumeral::mk(a); - } - - static Z3_rcf_num from_rcnumeral(rcnumeral a) { - return reinterpret_cast(a.c_ptr()); - } - void Z3_API Z3_rcf_del(Z3_context c, Z3_rcf_num a) { Z3_TRY; LOG_Z3_rcf_del(c, a); diff --git a/src/math/realclosure/mpz_matrix.cpp b/src/math/realclosure/mpz_matrix.cpp index 836ac35c2..ba89dfb36 100644 --- a/src/math/realclosure/mpz_matrix.cpp +++ b/src/math/realclosure/mpz_matrix.cpp @@ -44,7 +44,8 @@ void mpz_matrix_manager::mk(unsigned m, unsigned n, mpz_matrix & A) { del(A); A.m = m; A.n = n; - A.a_ij = new (m_allocator) mpz[m*n]; + void * mem = m_allocator.allocate(sizeof(mpz)*m*n); + A.a_ij = new (mem) mpz[m*n]; } void mpz_matrix_manager::del(mpz_matrix & A) { @@ -409,7 +410,7 @@ void mpz_matrix_manager::display(std::ostream & out, mpz_matrix const & A, unsig out << " "; std::string s = nm().to_string(A(i, j)); if (s.size() < cell_width) { - unsigned space = cell_width - s.size(); + unsigned space = cell_width - static_cast(s.size()); for (unsigned k = 0; k < space; k++) out << " "; } diff --git a/src/math/realclosure/realclosure.cpp b/src/math/realclosure/realclosure.cpp index 09e722f23..394a7102a 100644 --- a/src/math/realclosure/realclosure.cpp +++ b/src/math/realclosure/realclosure.cpp @@ -97,10 +97,10 @@ namespace realclosure { void set_upper_is_open(bool f) { m_upper_open = f; } numeral const & lower() const { return m_lower; } numeral const & upper() const { return m_upper; } - bool lower_is_inf() const { return m_lower_inf; } - bool upper_is_inf() const { return m_upper_inf; } - bool lower_is_open() const { return m_lower_open; } - bool upper_is_open() const { return m_upper_open; } + bool lower_is_inf() const { return m_lower_inf != 0; } + bool upper_is_inf() const { return m_upper_inf != 0; } + bool lower_is_open() const { return m_lower_open != 0; } + bool upper_is_open() const { return m_upper_open != 0; } }; void set_rounding(bool to_plus_inf) { m_manager.m_to_plus_inf = to_plus_inf; } @@ -112,10 +112,10 @@ namespace realclosure { numeral const & upper(interval const & a) const { return a.m_upper; } numeral & lower(interval & a) { return a.m_lower; } numeral & upper(interval & a) { return a.m_upper; } - bool lower_is_open(interval const & a) const { return a.m_lower_open; } - bool upper_is_open(interval const & a) const { return a.m_upper_open; } - bool lower_is_inf(interval const & a) const { return a.m_lower_inf; } - bool upper_is_inf(interval const & a) const { return a.m_upper_inf; } + bool lower_is_open(interval const & a) const { return a.lower_is_open(); } + bool upper_is_open(interval const & a) const { return a.upper_is_open(); } + bool lower_is_inf(interval const & a) const { return a.lower_is_inf(); } + bool upper_is_inf(interval const & a) const { return a.upper_is_inf(); } // Setters void set_lower(interval & a, numeral const & n) { m_manager.set(a.m_lower, n); } @@ -637,7 +637,8 @@ namespace realclosure { return; // interval was already saved. to_restore.push_back(v); inc_ref(v); - v->m_old_interval = new (allocator()) mpbqi(); + void * mem = allocator().allocate(sizeof(mpbqi)); + v->m_old_interval = new (mem) mpbqi(); set_interval(*(v->m_old_interval), v->m_interval); } void save_interval(value * v) { @@ -1201,7 +1202,8 @@ namespace realclosure { } sign_condition * mk_sign_condition(unsigned qidx, int sign, sign_condition * prev_sc) { - return new (allocator()) sign_condition(qidx, sign, prev_sc); + void * mem = allocator().allocate(sizeof(sign_condition)); + return new (mem) sign_condition(qidx, sign, prev_sc); } /** @@ -1748,7 +1750,8 @@ namespace realclosure { M and scs will be empty after this operation. */ sign_det * mk_sign_det(mpz_matrix & M_s, scoped_polynomial_seq const & prs, int_buffer const & taqrs, scoped_polynomial_seq const & qs, scoped_sign_conditions & scs) { - sign_det * r = new (allocator()) sign_det(); + void * mem = allocator().allocate(sizeof(sign_det)); + sign_det * r = new (mem) sign_det(); r->M_s.swap(M_s); set_array_p(r->m_prs, prs); r->m_taqrs.set(allocator(), taqrs.size(), taqrs.c_ptr()); @@ -1763,7 +1766,8 @@ namespace realclosure { */ algebraic * mk_algebraic(unsigned p_sz, value * const * p, mpbqi const & interval, sign_det * sd, unsigned sc_idx) { unsigned idx = next_algebraic_idx(); - algebraic * r = new (allocator()) algebraic(idx); + void * mem = allocator().allocate(sizeof(algebraic)); + algebraic * r = new (mem) algebraic(idx); m_extensions[extension::ALGEBRAIC].push_back(r); set_p(r->m_p, p_sz, p); @@ -2495,7 +2499,8 @@ namespace realclosure { } rational_value * mk_rational() { - return new (allocator()) rational_value(); + void * mem = allocator().allocate(sizeof(rational_value)); + return new (mem) rational_value(); } /** diff --git a/src/math/realclosure/realclosure.h b/src/math/realclosure/realclosure.h index 1e0ac4ba5..146299b56 100644 --- a/src/math/realclosure/realclosure.h +++ b/src/math/realclosure/realclosure.h @@ -267,10 +267,10 @@ namespace realclosure { void clean_denominators(numeral const & a, numeral & p, numeral & q); }; - class value; + struct value; class num { friend class manager; - friend class manager::imp; + friend struct manager::imp; value * m_value; public: num():m_value(0) {} diff --git a/src/util/array.h b/src/util/array.h index c2358d1a4..a3658c354 100644 --- a/src/util/array.h +++ b/src/util/array.h @@ -40,7 +40,7 @@ private: array & operator=(array const & source); - void set_data(void * mem, size_t sz) { + void set_data(void * mem, unsigned sz) { size_t * _mem = static_cast(mem); *_mem = sz; _mem ++; @@ -48,7 +48,7 @@ private: } template - void allocate(Allocator & a, size_t sz) { + void allocate(Allocator & a, unsigned sz) { size_t * mem = reinterpret_cast(a.allocate(space(sz))); set_data(mem, sz); } @@ -80,13 +80,13 @@ public: \brief Store the array in the given chunk of memory (mem). This chunck should be big enough to store space(sz) bytes. */ - array(void * mem, size_t sz, T const * vs) { + array(void * mem, unsigned sz, T const * vs) { DEBUG_CODE(m_data = 0;); set(mem, sz, vs); } // WARNING: the memory allocated will not be automatically freed. - array(void * mem, size_t sz, bool init_mem) { + array(void * mem, unsigned sz, bool init_mem) { DEBUG_CODE(m_data = 0;); set_data(mem, sz); if (init_mem) @@ -95,14 +95,14 @@ public: // WARNING: the memory allocated will not be automatically freed. template - array(Allocator & a, size_t sz, T const * vs) { + array(Allocator & a, unsigned sz, T const * vs) { DEBUG_CODE(m_data = 0;); set(a, sz, vs); } // WARNING: the memory allocated will not be automatically freed. template - array(Allocator & a, size_t sz, bool init_mem) { + array(Allocator & a, unsigned sz, bool init_mem) { DEBUG_CODE(m_data = 0;); allocate(a, sz); if (init_mem) @@ -127,41 +127,41 @@ public: } } - void set(void * mem, size_t sz, T const * vs) { + void set(void * mem, unsigned sz, T const * vs) { SASSERT(m_data == 0); set_data(mem, sz); init(vs); } template - void set(Allocator & a, size_t sz, T const * vs) { + void set(Allocator & a, unsigned sz, T const * vs) { SASSERT(m_data == 0); allocate(a, sz); init(vs); } template - void set(Allocator & a, size_t sz, T const & v = T()) { + void set(Allocator & a, unsigned sz, T const & v = T()) { SASSERT(m_data == 0); allocate(a, sz); init(v); } - size_t size() const { + unsigned size() const { if (m_data == 0) { return 0; } - return reinterpret_cast(m_data)[SIZE_IDX]; + return static_cast(reinterpret_cast(m_data)[SIZE_IDX]); } bool empty() const { return m_data == 0; } - T & operator[](size_t idx) { + T & operator[](unsigned idx) { SASSERT(idx < size()); return m_data[idx]; } - T const & operator[](size_t idx) const { + T const & operator[](unsigned idx) const { SASSERT(idx < size()); return m_data[idx]; } @@ -195,24 +195,24 @@ template class ptr_array : public array { public: ptr_array() {} - ptr_array(void * mem, size_t sz, T * const * vs):array(mem, sz, vs) {} + ptr_array(void * mem, unsigned sz, T * const * vs):array(mem, sz, vs) {} template - ptr_array(Allocator & a, size_t sz, T * const * vs):array(a, sz, vs) {} - ptr_array(void * mem, size_t sz, bool init_mem):array(mem, sz, init_mem) {} + ptr_array(Allocator & a, unsigned sz, T * const * vs):array(a, sz, vs) {} + ptr_array(void * mem, unsigned sz, bool init_mem):array(mem, sz, init_mem) {} template - ptr_array(Allocator & a, size_t sz, bool init_mem):array(a, sz, init_mem) {} + ptr_array(Allocator & a, unsigned sz, bool init_mem):array(a, sz, init_mem) {} }; template class sarray : public array { public: sarray() {} - sarray(void * mem, size_t sz, T const * vs):array(mem, sz, vs) {} + sarray(void * mem, unsigned sz, T const * vs):array(mem, sz, vs) {} template - sarray(Allocator & a, size_t sz, T const * vs):array(a, sz, vs) {} - sarray(void * mem, size_t sz, bool init_mem):array(mem, sz, init_mem) {} + sarray(Allocator & a, unsigned sz, T const * vs):array(a, sz, vs) {} + sarray(void * mem, unsigned sz, bool init_mem):array(mem, sz, init_mem) {} template - sarray(Allocator & a, size_t sz, bool init_mem):array(a, sz, init_mem) {} + sarray(Allocator & a, unsigned sz, bool init_mem):array(a, sz, init_mem) {} }; #endif