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/scripts/mk_project.py b/scripts/mk_project.py index 0ebcfe45d..d11c5fd30 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') @@ -58,8 +59,8 @@ 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'] - add_lib('api', ['portfolio', 'user_plugin', 'smtparser'], + 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') add_exe('test', ['api', 'fuzzing'], exe_name='test-z3', install=False) 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: diff --git a/src/api/api_context.cpp b/src/api/api_context.cpp index 9ebc771f0..6106cb6c7 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); @@ -138,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); } } @@ -391,6 +394,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()); + } + }; @@ -476,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) { 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..6b9826af7 --- /dev/null +++ b/src/api/api_rcf.cpp @@ -0,0 +1,293 @@ +/*++ +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"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" { + + 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); + RESET_ERROR_CODE(); + reset_rcf_cancel(c); + scoped_mpq q(rcfm(c).qm()); + rcfm(c).qm().set(q, val); + rcnumeral r; + rcfm(c).set(r, q); + RETURN_Z3(from_rcnumeral(r)); + 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(); + reset_rcf_cancel(c); + rcnumeral r; + rcfm(c).set(r, val); + RETURN_Z3(from_rcnumeral(r)); + 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(); + reset_rcf_cancel(c); + rcnumeral r; + rcfm(c).mk_pi(r); + RETURN_Z3(from_rcnumeral(r)); + 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(); + reset_rcf_cancel(c); + rcnumeral r; + rcfm(c).mk_e(r); + RETURN_Z3(from_rcnumeral(r)); + 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(); + reset_rcf_cancel(c); + rcnumeral r; + rcfm(c).mk_infinitesimal(name, 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_Z3_rcf_mk_roots num_roots; + 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(); + reset_rcf_cancel(c); + rcnumeral r; + rcfm(c).add(to_rcnumeral(a), to_rcnumeral(b), r); + RETURN_Z3(from_rcnumeral(r)); + 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(); + reset_rcf_cancel(c); + rcnumeral r; + rcfm(c).sub(to_rcnumeral(a), to_rcnumeral(b), r); + RETURN_Z3(from_rcnumeral(r)); + 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(); + reset_rcf_cancel(c); + rcnumeral r; + rcfm(c).mul(to_rcnumeral(a), to_rcnumeral(b), r); + RETURN_Z3(from_rcnumeral(r)); + 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(); + reset_rcf_cancel(c); + rcnumeral r; + rcfm(c).div(to_rcnumeral(a), to_rcnumeral(b), r); + RETURN_Z3(from_rcnumeral(r)); + 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(); + reset_rcf_cancel(c); + rcnumeral r; + rcfm(c).neg(to_rcnumeral(a), r); + RETURN_Z3(from_rcnumeral(r)); + 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(); + reset_rcf_cancel(c); + rcnumeral r; + rcfm(c).inv(to_rcnumeral(a), r); + RETURN_Z3(from_rcnumeral(r)); + 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); + RESET_ERROR_CODE(); + reset_rcf_cancel(c); + return rcfm(c).lt(to_rcnumeral(a), to_rcnumeral(b)); + 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(); + reset_rcf_cancel(c); + return rcfm(c).gt(to_rcnumeral(a), to_rcnumeral(b)); + 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(); + reset_rcf_cancel(c); + return rcfm(c).le(to_rcnumeral(a), to_rcnumeral(b)); + 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(); + 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); + } + + 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(); + reset_rcf_cancel(c); + std::ostringstream buffer; + 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_decimal_string(c, a, prec); + 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/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); 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 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; } /** diff --git a/src/api/python/z3rcf.py b/src/api/python/z3rcf.py new file mode 100644 index 000000000..dd0696594 --- /dev/null +++ b/src/api/python/z3rcf.py @@ -0,0 +1,153 @@ +############################################ +# 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) + +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 + 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)) + + def __del__(self): + Z3_rcf_del(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): + v = _to_rcfnum(other, self.ctx) + return RCFNum(Z3_rcf_add(self.ctx_ref(), self.num, v.num), self.ctx) + + def __radd__(self, other): + v = _to_rcfnum(other, self.ctx) + return RCFNum(Z3_rcf_add(self.ctx_ref(), v.num, self.num), self.ctx) + + def __mul__(self, other): + v = _to_rcfnum(other, self.ctx) + return RCFNum(Z3_rcf_mul(self.ctx_ref(), self.num, v.num), self.ctx) + + def __rmul__(self, other): + v = _to_rcfnum(other, self.ctx) + return RCFNum(Z3_rcf_mul(self.ctx_ref(), v.num, self.num), self.ctx) + + def __sub__(self, other): + v = _to_rcfnum(other, self.ctx) + return RCFNum(Z3_rcf_sub(self.ctx_ref(), self.num, v.num), self.ctx) + + def __rsub__(self, other): + v = _to_rcfnum(other, self.ctx) + return RCFNum(Z3_rcf_sub(self.ctx_ref(), v.num, self.num), self.ctx) + + def __div__(self, other): + v = _to_rcfnum(other, self.ctx) + return RCFNum(Z3_rcf_div(self.ctx_ref(), self.num, v.num), self.ctx) + + def __rdiv__(self, other): + 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) + + 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) + + def __lt__(self, other): + v = _to_rcfnum(other, self.ctx) + return Z3_rcf_lt(self.ctx_ref(), self.num, v.num) + + def __rlt__(self, other): + v = _to_rcfnum(other, self.ctx) + return Z3_rcf_lt(self.ctx_ref(), v.num, self.num) + + def __gt__(self, other): + v = _to_rcfnum(other, self.ctx) + return Z3_rcf_gt(self.ctx_ref(), self.num, v.num) + + def __rgt__(self, other): + v = _to_rcfnum(other, self.ctx) + return Z3_rcf_gt(self.ctx_ref(), v.num, self.num) + + def __le__(self, other): + v = _to_rcfnum(other, self.ctx) + return Z3_rcf_le(self.ctx_ref(), self.num, v.num) + + def __rle__(self, other): + v = _to_rcfnum(other, self.ctx) + return Z3_rcf_le(self.ctx_ref(), v.num, self.num) + + def __ge__(self, other): + v = _to_rcfnum(other, self.ctx) + return Z3_rcf_ge(self.ctx_ref(), self.num, v.num) + + def __rge__(self, other): + v = _to_rcfnum(other, self.ctx) + return Z3_rcf_ge(self.ctx_ref(), v.num, self.num) + + def __eq__(self, other): + v = _to_rcfnum(other, self.ctx) + return Z3_rcf_eq(self.ctx_ref(), self.num, v.num) + + def __ne__(self, other): + v = _to_rcfnum(other, self.ctx) + return Z3_rcf_neq(self.ctx_ref(), self.num, v.num) + 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..87f376117 --- /dev/null +++ b/src/api/z3_rcf.h @@ -0,0 +1,191 @@ +/*++ +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 Delete a RCF numeral created using the RCF API. + + def_API('Z3_rcf_del', VOID, (_in(CONTEXT), _in(RCF_NUM))) + */ + void Z3_API Z3_rcf_del(__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 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. + + 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 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 + + 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 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. + + 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); + + /** + \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 + +#endif diff --git a/src/math/interval/interval.h b/src/math/interval/interval.h index 33f202e06..1fe797861 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; @@ -199,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. */ @@ -350,4 +356,29 @@ 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; + } + interval const * operator->() const { + return &m_interval; + } +}; + #endif 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/mpz_matrix.cpp b/src/math/realclosure/mpz_matrix.cpp new file mode 100644 index 000000000..ba89dfb36 --- /dev/null +++ b/src/math/realclosure/mpz_matrix.cpp @@ -0,0 +1,421 @@ +/*++ +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; + 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) { + 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); +} + +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()); + 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[r_sz] = rows[k1]; + r_sz++; + if (r_sz >= A.n()) + break; + eliminate(A, 0, k1, k2, false); + 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; +} + +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 - static_cast(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..b48585c60 --- /dev/null +++ b/src/math/realclosure/mpz_matrix.h @@ -0,0 +1,156 @@ +/*++ +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. + + \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, mpz_matrix & B); + + // 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/math/realclosure/rcf.pyg b/src/math/realclosure/rcf.pyg new file mode 100644 index 000000000..a394ae32a --- /dev/null +++ b/src/math/realclosure/rcf.pyg @@ -0,0 +1,8 @@ +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 new file mode 100644 index 000000000..394a7102a --- /dev/null +++ b/src/math/realclosure/realclosure.cpp @@ -0,0 +1,5937 @@ +/*++ +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"rcf_params.hpp" +#include"array.h" +#include"mpbq.h" +#include"mpz_matrix.h" +#include"interval_def.h" +#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 +#endif + +#ifndef REALCLOSURE_INI_SEQ_SIZE +#define REALCLOSURE_INI_SEQ_SIZE 256 +#endif + +#ifndef REALCLOSURE_INI_DIV_PRECISION +#define REALCLOSURE_INI_DIV_PRECISION 24 +#endif + +namespace realclosure { + + // --------------------------------- + // + // Intervals with binary rational endpoints + // + // --------------------------------- + + 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; + + 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; + numeral m_upper; + 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); + 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 != 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; } + 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; } + 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.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); } + void set_upper(interval & a, numeral const & n) { m_manager.set(a.m_upper, n); } + 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; } + + // 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; + + void swap(mpbqi & a, mpbqi & b) { + swap(a.m_lower, b.m_lower); + swap(a.m_upper, b.m_upper); + 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); + } + + // --------------------------------- + // + // Values are represented as + // - arbitrary precision rationals (mpq) + // - rational functions on field extensions + // + // --------------------------------- + + 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 + // 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; } + }; + + struct rational_value : public value { + mpq m_value; + rational_value():value(true) {} + }; + + typedef ptr_array polynomial; + + struct extension; + bool rank_lt(extension * r1, extension * r2); + + struct rational_function_value : public value { + polynomial m_numerator; + polynomial m_denominator; + extension * m_ext; + 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; } + polynomial const & den() const { return m_denominator; } + polynomial & den() { return m_denominator; } + + extension * ext() const { return m_ext; } + bool depends_on_infinitesimals() const { return m_depends_on_infinitesimals; } + void set_depends_on_infinitesimals(bool f) { m_depends_on_infinitesimals = f; } + }; + + // --------------------------------- + // + // Field Extensions + // + // --------------------------------- + + typedef int sign; + + typedef std::pair p2s; + + typedef sarray signs; + + struct extension { + enum kind { + TRANSCENDENTAL = 0, + INFINITESIMAL = 1, + ALGEBRAIC = 2 + }; + + unsigned m_ref_count; + 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), m_old_interval(0) {} + + 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; } + mpbqi & interval() { 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); + } + }; + + /** + \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_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) {} + + 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_sc_idx; //!< != UINT_MAX if m_sign_det != 0, in this case m_sc_idx < m_sign_det->m_sign_conditions.size() + 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_depends_on_infinitesimals(false) {} + + polynomial const & p() const { return m_p; } + 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(); } + }; + + 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_k(0), m_proc(p) {} + + void display(std::ostream & out) const { + out << m_name; + } + }; + + struct infinitesimal : public extension { + symbol m_name; + + 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(); + else + out << m_name; + } + }; + + // --------------------------------- + // + // 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; + 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; + 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; + 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 + 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 + 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 { + 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) {} + ~scoped_polynomial_seq() { + } + + /** + \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(); + } + + 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(); + } + 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), + m_qm(qm), + m_mm(m_qm, *m_allocator), + m_bqm(m_qm), + m_qim(m_qm), + 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); + m_pi = 0; + m_e = 0; + + m_exec_depth = 0; + + m_cancel = false; + + updt_params(p); + } + + ~imp() { + restore_saved_intervals(); // to free memory + dec_ref(m_one); + dec_ref(m_pi); + dec_ref(m_e); + if (m_own_allocator) + 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() { + if (m_cancel) + throw exception("canceled"); + cooperate("rcf"); + } + + value * one() const { + 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().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(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); + } + + /** + \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) < -static_cast(m_max_precision); + } + +#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) { + SASSERT(!contains_zero(a)); + SASSERT(!contains_zero(b)); + scoped_set_div_precision set(bqm(), prec); + bqim().div(a, b, c); + 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 or extension. + */ + template + void save_interval(T * v, ptr_vector & to_restore) { + if (v->m_old_interval != 0) + return; // interval was already saved. + to_restore.push_back(v); + inc_ref(v); + 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) { + 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. + */ + 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 Save the current interval (i.e., approximation) of the given value IF it is too small. + */ + 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++) { + 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); + } + 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) { + ptr_vector & exts = m_extensions[k]; + // keep removing unused slots + while (!exts.empty() && exts.back() == 0) { + exts.pop_back(); + } + } + + unsigned next_transcendental_idx() { + cleanup(extension::TRANSCENDENTAL); + return m_extensions[extension::TRANSCENDENTAL].size(); + } + + unsigned next_infinitesimal_idx() { + 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; + } + + void updt_params(params_ref const & _p) { + rcf_params p(_p); + 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); + } + + /** + \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_rational(rational_value * v) { + bqim().del(v->m_interval); + qm().del(v->m_value); + allocator().deallocate(sizeof(rational_value), v); + } + + void del_rational_function(rational_function_value * v) { + bqim().del(v->m_interval); + reset_p(v->num()); + reset_p(v->den()); + dec_ref(v->ext()); + allocator().deallocate(sizeof(rational_function_value), v); + } + + void del_value(value * v) { + if (v->is_rational()) + del_rational(static_cast(v)); + else + 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_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); + } + + 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); + dec_ref_sign_det(a->m_sign_det); + allocator().deallocate(sizeof(algebraic), a); + } + + 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); + } + + void inc_ref(extension * ext) { + SASSERT(ext != 0); + ext->m_ref_count++; + } + + void dec_ref(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[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; + } + } + } + + void inc_ref(value * v) { + if (v) + v->m_ref_count++; + } + + 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) + del_value(v); + } + } + + void dec_ref(unsigned sz, value * const * p) { + for (unsigned i = 0; i < sz; i++) + dec_ref(p[i]); + } + + void del(numeral & a) { + dec_ref(a.m_value); + 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 + */ + 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 represented as rational value one. + */ + 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 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; + */ + bool is_one(value * v) const { + return const_cast(this)->compare(v, one()) == 0; + } + + /** + \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. + */ + 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 (rank_eq(to_rational_function(a)->ext(), to_rational_function(b)->ext())) + return 0; + else + return rank_lt(to_rational_function(a)->ext(), to_rational_function(b)->ext()) ? -1 : 1; + } + + static transcendental * to_transcendental(extension * ext) { + SASSERT(ext->is_transcendental()); + return static_cast(ext); + } + + static infinitesimal * to_infinitesimal(extension * ext) { + SASSERT(ext->is_infinitesimal()); + return static_cast(ext); + } + + static algebraic * to_algebraic(extension * ext) { + SASSERT(ext->is_algebraic()); + return static_cast(ext); + } + + /** + \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 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 depends_on_infinitesimals(extension * ext) { + switch (ext->knd()) { + case extension::TRANSCENDENTAL: return false; + case extension::INFINITESIMAL: return true; + case extension::ALGEBRAIC: return to_algebraic(ext)->depends_on_infinitesimals(); + default: + UNREACHABLE(); + return false; + } + } + + /** + \brief Return true if v is definitely a real value. + */ + bool depends_on_infinitesimals(value * v) const { + if (is_zero(v) || is_nz_rational(v)) + return false; + else + return to_rational_function(v)->depends_on_infinitesimals(); + } + + bool depends_on_infinitesimals(unsigned sz, value * const * p) const { + for (unsigned i = 0; i < sz; i++) + if (depends_on_infinitesimals(p[i])) + return true; + return false; + } + + /** + \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 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. + */ + 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 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 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. + */ + 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.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.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 + */ + 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 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 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) { + void * mem = allocator().allocate(sizeof(sign_condition)); + return new (mem) 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) + */ + 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); + 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)); + return r; + } + + /** + \brief Create a value using the given extension. + */ + rational_function_value * mk_rational_function_value(extension * ext) { + value * num[2] = { 0, one() }; + value * den[1] = { one() }; + rational_function_value * v = mk_rational_function_value_core(ext, 2, num, 1, den); + set_interval(v->interval(), ext->interval()); + 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); + + set_lower(eps->interval(), mpbq(0)); + set_upper(eps->interval(), mpbq(1, m_ini_precision)); + + set(r, mk_rational_function_value(eps)); + + SASSERT(sign(r) > 0); + SASSERT(depends_on_infinitesimals(r)); + } + + 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 refine_transcendental_interval(transcendental * t) { + scoped_mpqi i(qim()); + t->m_k++; + t->m_proc(t->m_k, qim(), i); + int m = magnitude(i); + TRACE("rcf_transcendental", + 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 + bqm().set(l, t->interval().lower()); + mpq_to_mpbqi(i->m_upper, t->interval(), k); + bqm().set(t->interval().lower(), l); + } + + void refine_transcendental_interval(transcendental * t, unsigned prec) { + 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); + } + } + + 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 (contains_zero(t->interval())) { + checkpoint(); + refine_transcendental_interval(t); + } + set(r, mk_rational_function_value(t)); + + SASSERT(!depends_on_infinitesimals(r)); + } + + void mk_transcendental(char const * p, mk_interval & proc, numeral & r) { + mk_transcendental(symbol(p), proc, r); + } + + void mk_transcendental(mk_interval & proc, numeral & r) { + 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); + } + } + + // --------------------------------- + // + // Root isolation + // + // --------------------------------- + + /** + \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, int & 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 = -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)/static_cast(k) + 1 /* compensate imprecision on division */ + 1 /* Knuth's bound is 2 x Max {... } */; + if (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 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_lower_bound(unsigned n, value * const * as, int & 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 = -static_cast(m_ini_precision); + 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)/static_cast(k) + 1 /* compensate imprecision on division */ + 1 /* Knuth's bound is 2 x Max {... } */; + if (C > N) + N = C; + } + } + } + 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 (neg_root_lower_bound(n, q.c_ptr(), N)) { + N = -N; + return true; + } + else { + return false; + } + } + + /** + \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 - 2; i++) { + SASSERT(ds.size() > 0); + 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 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: + - 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 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); + } + + /** + \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 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_s, scoped_polynomial_seq const & prs, int_buffer const & taqrs, scoped_polynomial_seq const & qs, scoped_sign_conditions & scs) { + 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()); + 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 sc_idx) { + unsigned idx = next_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); + set_interval(r->m_interval, interval); + r->m_sign_det = sd; + inc_ref_sign_det(sd); + r->m_sc_idx = sc_idx; + r->m_depends_on_infinitesimals = depends_on_infinitesimals(p_sz, p); + + return r; + } + + /** + \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 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 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 + 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. + 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. + + \pre num_roots is the number of roots in the given interval + */ + void sign_det_isolate_roots(unsigned p_sz, value * const * p, int num_roots, mpbqi const & interval, numeral_vector & roots) { + SASSERT(num_roots >= 2); + scoped_polynomial_seq der_seq(*this); + mk_derivatives(p_sz, p, der_seq); + + 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); + + // 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";); + scoped_mpz_matrix M(mm()); + 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, + // ---> + 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(), 0); + // 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); }); + // 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(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(), M_s); + 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"; + 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]); + 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"; + } + tout << "prs:\n"; + for (unsigned j = 0; j < prs.size(); j++) { + 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, taqrs, qs, scs); + for (unsigned idx = 0; idx < static_cast(num_roots); idx++) { + add_root(p_sz, p, interval, sd, idx, roots); + } + } + + /** + \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 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); + } + } + + 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) + - zero is not a root + - square free + - nonconstant + */ + void nl_nz_sqf_isolate_roots(unsigned n, value * const * p, numeral_vector & roots) { + SASSERT(n > 2); + 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";); + // Compute the number of positive and negative roots + scoped_polynomial_seq seq(*this); + sturm_seq(n, p, seq); + 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";); + 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 { + 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); + } + } + } + + if (num_pos_roots > 0) { + if (num_pos_roots == 1) { + add_root(n, p, pos_interval, 0, UINT_MAX, roots); + } + else { + 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); + } + } + } + } + + /** + \brief Root isolation for polynomials that are + - zero is not a root + - square free + - nonconstant + */ + void nz_sqf_isolate_roots(unsigned n, value * const * p, numeral_vector & roots) { + SASSERT(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(p[0], v); + div(v, p[1], v); + set(r, v); + roots.push_back(r); + } + else { + nl_nz_sqf_isolate_roots(n, p, roots); + } + } + + /** + \brief Root isolation for polynomials where 0 is not a root, and the denominators have been cleaned + when m_clean_denominators == true + + */ + 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; + } + value_ref_buffer sqf(*this); + square_free(n, p, sqf); + 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. + */ + 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) { + // constant polynomial + return; + } + unsigned i = 0; + for (; i < n; i++) { + if (!is_zero(p[i])) + break; + } + SASSERT(i < n); + SASSERT(!is_zero(p[i])); + ptr_buffer nz_p; + for (; i < n; i++) + 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()); + } + } + + // --------------------------------- + // + // Basic operations + // + // --------------------------------- + + void reset(numeral & a) { + del(a); + SASSERT(is_zero(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 { + SASSERT(!contains_zero(a->interval())); + return bqim().is_P(a->interval()) ? 1 : -1; + } + } + + int sign(numeral const & a) { + 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 { + 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 a depends on infinitesimal extensions. + */ + bool depends_on_infinitesimals(numeral const & a) const { + return depends_on_infinitesimals(a.m_value); + } + + 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); + 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 (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()); + } + } + } + + 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, m_ini_precision); + } + + mpbqi & interval(value * a) const { + SASSERT(a != 0); + if (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() { + void * mem = allocator().allocate(sizeof(rational_value)); + return new (mem) rational_value(); + } + + /** + \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); + } + + 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); + reset_interval(a); + } + + template + void update_mpq_value(numeral & a, T & v) { + update_mpq_value(a.m_value, v); + } + + /** + \brief a <- n + */ + void set(numeral & a, int n) { + if (n == 0) { + reset(a); + return; + } + + del(a); + a.m_value = mk_rational(); + inc_ref(a.m_value); + update_mpq_value(a, n); + } + + /** + \brief a <- n + */ + void set(numeral & a, mpz const & n) { + if (qm().is_zero(n)) { + reset(a); + return; + } + + del(a); + a.m_value = mk_rational(); + inc_ref(a.m_value); + update_mpq_value(a, n); + } + + /** + \brief a <- n + */ + void set(numeral & a, mpq const & n) { + if (qm().is_zero(n)) { + reset(a); + return; + } + del(a); + a.m_value = mk_rational(); + inc_ref(a.m_value); + 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; + } + + // --------------------------------- + // + // Polynomial arithmetic in RCF + // + // --------------------------------- + + /** + \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) { + SASSERT(p1 != r.c_ptr()); + SASSERT(p2 != r.c_ptr()); + r.reset(); + value_ref a_i(*this); + unsigned min = std::min(sz1, sz2); + unsigned i = 0; + 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++) + 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(p != r.c_ptr()); + SASSERT(sz > 0); + r.reset(); + 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); + } + + /** + \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); + unsigned i = 0; + 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++) { + neg(p2[i], a_i); + r.push_back(a_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(p != r.c_ptr()); + SASSERT(sz > 0); + r.reset(); + 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); + } + + /** + \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; + value_ref a_i(*this); + for (unsigned i = 0; i < sz; i++) { + mul(a, p[i], a_i); + r.push_back(a_i); + } + } + + /** + \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); + 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] + mul(p1[i], p2[j], tmp); + add(r[i+j], tmp, tmp); + r.set(i+j, tmp); + } + } + adjust_size(r); + } + + /** + \brief p <- p/a + */ + void div(value_ref_buffer & p, value * a) { + 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++) { + div(p[i], a, a_i); + p.set(i, a_i); + } + } + + /** + \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); + value_ref aux(*this); + while (true) { + checkpoint(); + sz1 = r.size(); + if (sz1 < sz2) { + adjust_size(q); + break; + } + unsigned m_n = sz1 - sz2; // m-n + div(r[sz1 - 1], b_n, ratio); + // q[m_n] <- q[m_n] + r[sz1 - 1]/b_n + 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] + 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); + } + } + } + } + + /** + \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 <- 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++) { + div(p[i], a, a_i); + r.push_back(a_i); + } + } + + /** + \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"; + display_poly(tout, sz2, p2); tout << "\n";); + 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); + value_ref new_a(*this); + SASSERT(b_n != 0); + while (true) { + checkpoint(); + sz1 = r.size(); + if (sz1 < sz2) { + TRACE("rcf_rem", tout << "rem result\n"; display_poly(tout, r.size(), r.c_ptr()); tout << "\n";); + return; + } + unsigned m_n = sz1 - sz2; + div(r[sz1 - 1], b_n, ratio); + for (unsigned i = 0; i < sz2 - 1; i++) { + 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); + } + } + + /** + \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); + } + } + + 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 + */ + 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++) { + 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++) { + 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++) { + neg(p[i], a_i); + inc_ref(a_i.get()); + dec_ref(p[i]); + p[i] = a_i.get(); + } + } + + /** + \brief r <- srem(p1, p2) + 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); + } + + /** + \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); + } + + // --------------------------------- + // + // 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(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. + 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(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_sz; 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_sz); + SASSERT(dens.size() == p_sz); + for (unsigned i = 0; i < p_sz; 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_sz; 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_sz; 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_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) { + if (has_clean_denominators(a)) { + p = a; + q = one(); + } + else { + clean_denominators_core(a, p, q); + } + } + + 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(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); + set(p, _p); + 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); + } + } + + // --------------------------------- + // + // GCD + // + // --------------------------------- + + /** + \brief Force the leading coefficient of p to be 1. + */ + 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++) { + div(p[i], p[sz-1], a_i); + p.set(i, a_i); + } + p.set(sz-1, one()); + } + } + } + + /** + \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); + } + 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(*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); + A = B; + B = R; + } + } + } + + 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 + // + // --------------------------------- + + /** + \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 a_i(*this); + a_i = mk_rational_and_swap(i_mpq); + mul(a_i, p[i], a_i); + r.push_back(a_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); + 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); + } + } + } + + /** + \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(); + if (m_use_prem) { + sprem(seq.size(sz-2), seq.coeffs(sz-2), seq.size(sz-1), seq.coeffs(sz-1), r); + 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";); + 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 p1_prime(*this); + value_ref_buffer p1_prime_p2(*this); + seq.push(sz1, p1); + 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); + } + + // --------------------------------- + // + // Sign evaluation for polynomials + // That is, sign of p(x) at b + // + // --------------------------------- + + /** + \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 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 _b(*this); + _b = mk_rational(b); + value_ref pb(*this); + mk_polynomial_value(n, p, _b, pb); + return sign(pb); + } + + /** + \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) { + if (n == 0) + return 0; + else if (n == 1) + return sign(p[0]); + else { + 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); + } + } + } + + // --------------------------------- + // + // Sign variations in polynomial sequences. + // + // --------------------------------- + + enum location { + ZERO, + MINUS_INF, + 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) + 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); + } + + 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()); + } + + // --------------------------------- + // + // 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) = + #{ 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) { + return sign_variations_at_lower(seq, interval) - sign_variations_at_upper(seq, interval); + } + + /** + \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) { + 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); + } + + /** + \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) { + 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); + } + + // --------------------------------- + // + // Interval refinement + // + // --------------------------------- + + 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(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). + */ + 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); + if (!contains_zero(num_i) && !contains_zero(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_transcendental", 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(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; + } + 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) { + 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++; + } + } + + /** + \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) { + 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, prec); + 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); + } + } + + // --------------------------------- + // + // Sign determination + // + // --------------------------------- + + /** + \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())); + } + + /** + \brief Return true if x and q depend on infinitesimal values. + That is, q(x) does not depend on infinitesimal values. + */ + bool depends_on_infinitesimals(polynomial const & q, algebraic * x) { + return x->depends_on_infinitesimals() || depends_on_infinitesimals(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(!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, + // 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 (!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); + } + return true; + } + int num_roots = x->num_roots_inside_interval(); + SASSERT(x->sdt() != 0 || num_roots == 1); + polynomial const & p = x->p(); + 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_p_q == num_roots) { + // q(x) is positive + if (!depends_on_infinitesimals(q, x)) + refine_until_sign_determined(q, x, r); + else + set_lower_zero(r); + SASSERT(!contains_zero(r)); + return true; + } + else if (taq_p_q == -num_roots) { + // q(x) is negative + if (!depends_on_infinitesimals(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); + 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(), 0); + // 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; + } + } + } + } + } + } + + 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) { + 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; + } + } + + /** + \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) { + 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; + } + } + + bool determine_sign(value_ref & r) { + SASSERT(is_rational_function(r.get())); + 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 + - 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_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 + 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 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); + 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); + } + } + + /** + \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() + */ + 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])); + r = num[0]; + } + else { + 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) + */ + 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(); + 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()); + 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) + */ + 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)) { + 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_all(a->ext(), num.size(), num.c_ptr(), ad.size(), ad.c_ptr(), new_num, new_den); + 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); + } + } + } + + /** + \brief Add values 'a' and 'b' of the form n/1 and rank(a) == rank(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); + 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()) + r = 0; + 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); + } + } + + /** + \brief Add values 'a' and 'b' of the form n/d and rank(a) == rank(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)) { + add_p_p(a, b, r); + } + else { + 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_all(a->ext(), num.size(), num.c_ptr(), den.size(), den.c_ptr(), new_num, new_den); + 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); + } + } + } + + 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 v(qm()); + qm().add(to_mpq(a), to_mpq(b), v); + if (qm().is_zero(v)) + r = 0; + else + r = mk_rational_and_swap(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; + 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_and_swap(v); + } + else { + value_ref neg_b(*this); + neg(b, neg_b); + switch (compare_rank(a, neg_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(); + } + } + } + + 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); + 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())); + } + + void neg(value * a, value_ref & r) { + if (a == 0) { + r = 0; + } + else if (is_nz_rational(a)) { + scoped_mpq v(qm()); + qm().set(v, to_mpq(a)); + qm().neg(v); + r = mk_rational_and_swap(v); + } + else { + neg_rf(to_rational_function(a), r); + } + } + + /** + \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() + */ + 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])); + r = num[0]; + } + else { + 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) + */ + 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); + 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()); + 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) + */ + 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)) { + 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_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); + } + } + + /** + \brief Multiply values 'a' and 'b' of the form n/1 and rank(a) == rank(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); + 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()); + 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); + SASSERT(!new_num.empty()); + 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); + } + } + + /** + \brief Multiply values 'a' and 'b' of the form n/d and rank(a) == rank(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)) { + 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_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); + } + } + + 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 v(qm()); + qm().mul(to_mpq(a), to_mpq(b), v); + r = mk_rational_and_swap(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; + case 1: mul_rf_v(to_rational_function(a), b, r); break; + default: UNREACHABLE(); + } + } + } + + 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)) { + 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 v(qm()); + qm().div(to_mpq(a), to_mpq(b), v); + r = mk_rational_and_swap(v); + } + else { + value_ref inv_b(*this); + inv(b, inv_b); + switch (compare_rank(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(); + } + } + } + + void inv_rf(rational_function_value * a, value_ref & r) { + polynomial const & an = a->num(); + polynomial const & ad = a->den(); + 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())); + } + + void inv(value * a, value_ref & r) { + if (a == 0) { + throw exception("division by zero"); + } + if (is_nz_rational(a)) { + scoped_mpq v(qm()); + qm().inv(to_mpq(a), v); + r = mk_rational_and_swap(v); + } + else { + inv_rf(to_rational_function(a), r); + } + } + + void set(numeral & n, value * v) { + inc_ref(v); + dec_ref(n.m_value); + n.m_value = v; + } + + void set(numeral & n, value_ref const & v) { + set(n, v.get()); + } + + void neg(numeral & a) { + value_ref r(*this); + neg(a.m_value, r); + set(a, r); + } + + void neg(numeral const & a, numeral & b) { + value_ref r(*this); + neg(a.m_value, r); + set(b, r); + } + + void inv(numeral & a) { + value_ref r(*this); + inv(a.m_value, r); + set(a, r); + } + + void inv(numeral const & a, numeral & b) { + value_ref r(*this); + inv(a.m_value, r); + set(b, r); + } + + void add(numeral const & a, numeral const & b, numeral & c) { + 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) { + 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) { + 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) { + value_ref r(*this); + div(a.m_value, b.m_value, r); + 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); + else if (b == 0) + return sign(a); + 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))) + return -1; + else if (bqim().before(interval(b), interval(a))) + return 1; + else { + value_ref diff(*this); + sub(a, b, diff); + return sign(diff); + } + } + } + + int compare(numeral const & a, numeral const & b) { + return compare(a.m_value, b.m_value); + } + + // --------------------------------- + // + // "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. + + 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()); + } + } + } + + 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->ext()); + mark(rf->num()); + mark(rf->den()); + } + }; + + 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 num_nz_coeffs(rf->num()) > 1 || !is_rational_one(rf->den()); + } + + template + 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; + while (i > 0) { + --i; + if (p[i] == 0) + continue; + if (first) + first = false; + else + out << " + "; + if (i == 0) + display(out, p[i], compact); + else { + if (!is_rational_one(p[i])) { + 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) + out << "^" << i; + } + } + } + + 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 << "#"; + } + }; + + 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(out, m_ref, 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) { + if (s < 0) + out << " < 0"; + else if (s == 0) + out << " = 0"; + 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 << ", "; + if (a->sdt() != 0) + display_sign_conditions(out, a->sdt()->sc(a->sc_idx()), a->sdt()->qs(), compact); + else + out << "{}"; + 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; + case extension::INFINITESIMAL: to_infinitesimal(r)->display(out); break; + case extension::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 (is_rational_one(rf->den())) { + display_polynomial_expr(out, rf->num(), rf->ext(), compact); + } + else if (is_rational_one(rf->num())) { + out << "1/("; + display_polynomial_expr(out, rf->den(), rf->ext(), compact); + out << ")"; + } + else { + out << "("; + display_polynomial_expr(out, rf->num(), rf->ext(), compact); + out << ")/("; + display_polynomial_expr(out, rf->den(), rf->ext(), compact); + out << ")"; + } + } + } + + void display_compact(std::ostream & out, numeral const & a) const { + 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(), rank_lt_proc()); + out << "["; + display(out, a.m_value, true); + for (unsigned i = 0; i < c.m_found.size(); i++) { + algebraic * ext = c.m_found[i]; + out << ", r!" << ext->idx() << " = "; + display_algebraic_def(out, ext, true); + } + out << "]"; + } + } + + 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 (refine_interval(a.m_value, 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 { + if (sign(a.m_value) > 0) + out << "?"; + else + out << "-?"; + } + } + + void display_decimal(std::ostream & out, numeral const & a, unsigned precision) const { + if (is_zero(a)) { + out << "0"; + } + else if (is_nz_rational(a)) { + qm().display_decimal(out, to_mpq(a), precision); + } + else { + const_cast(this)->display_non_rational_in_decimal(out, a, precision); + } + } + + void display_interval(std::ostream & out, numeral const & a) const { + if (is_zero(a)) + out << "[0, 0]"; + else + bqim().display(out, interval(a.m_value)); + } + }; + + // 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); + } + + manager::~manager() { + dealloc(m_imp); + } + + void manager::get_param_descrs(param_descrs & r) { + rcf_params::collect_param_descrs(r); + } + + 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 * 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 * n, mk_interval & proc, numeral & r) { + m_imp->mk_transcendental(n, proc, r); + } + + void manager::mk_transcendental(mk_interval & proc, numeral & r) { + 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) { + save_interval_ctx ctx(this); + m_imp->isolate_roots(n, as, roots); + } + + void manager::reset(numeral & a) { + m_imp->reset(a); + } + + int manager::sign(numeral const & a) { + save_interval_ctx ctx(this); + 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::depends_on_infinitesimals(numeral const & a) { + return m_imp->depends_on_infinitesimals(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) { + 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); + } + + 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) { + 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::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); + } + + int manager::compare(numeral const & a, numeral const & b) { + save_interval_ctx ctx(this); + 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::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); + } + + 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) { + 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]); +} + +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; +} + +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; +} + +void pp(realclosure::manager::imp * imp, realclosure::extension * x) { + imp->display_ext(std::cout, x, false); + std::cout << std::endl; +} diff --git a/src/math/realclosure/realclosure.h b/src/math/realclosure/realclosure.h new file mode 100644 index 000000000..146299b56 --- /dev/null +++ b/src/math/realclosure/realclosure.h @@ -0,0 +1,406 @@ +/*++ +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" +#include"z3_exception.h" + +namespace realclosure { + class num; + + typedef interval_manager mpqi_manager; + typedef default_exception exception; + + class mk_interval { + public: + virtual void operator()(unsigned k, mpqi_manager & im, mpqi_manager::interval & r) = 0; + }; + + class manager { + 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); + ~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 * name, numeral & r); + void mk_infinitesimal(numeral & 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 * 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. + */ + void isolate_roots(unsigned n, numeral const * as, numeral_vector & 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 the representation of \c a depends on + infinitesimal extensions. + */ + bool depends_on_infinitesimals(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 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 + */ + 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); } + + 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; + + + void display_interval(std::ostream & out, numeral const & a) const; + + void clean_denominators(numeral const & a, numeral & p, numeral & q); + }; + + struct value; + class num { + friend class manager; + friend struct manager::imp; + 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; } + }; +}; + +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; + + +#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 rc_sym_pp { + rcmanager & m; + rcnumeral const & n; + 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 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 rc_decimal_pp { + rcmanager & m; + rcnumeral const & n; + unsigned prec; + 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, rc_decimal_pp const & n) { + n.m.display_decimal(out, n.n, n.prec); + 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; + 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(scoped_rcnumeral const & n) { + return rc_interval_pp(n); +} + +#endif diff --git a/src/muz_qe/dl_bmc_engine.cpp b/src/muz_qe/dl_bmc_engine.cpp index c7e53e581..221c2d2a2 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_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 58255d2a1..000000000 --- a/src/muz_qe/dl_mk_extract_quantifiers2.cpp +++ /dev/null @@ -1,366 +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), - m_quantifiers(m), - m_refs(m) - {} - - mk_extract_quantifiers2::~mk_extract_quantifiers2() { - reset(); - } - - void mk_extract_quantifiers2::set_query(func_decl* q) { - 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); - 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)) { - 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; - } - } - if (!has_quantifier) { - 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()); - } - } - } - -#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_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) { - 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); - } - - 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); - 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; - 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 deleted file mode 100644 index 30c15b313..000000000 --- a/src/muz_qe/dl_mk_extract_quantifiers2.h +++ /dev/null @@ -1,91 +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; - 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; - - 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). - */ - 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; } - - }; - -}; - -#endif /* _DL_MK_EXTRACT_QUANTIFIERS2_H_ */ - 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_dl_interface.cpp b/src/muz_qe/pdr_dl_interface.cpp index 0482ece05..54a40f8b8 100644 --- a/src/muz_qe/pdr_dl_interface.cpp +++ b/src/muz_qe/pdr_dl_interface.cpp @@ -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_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_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/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/muz_qe/proof_utils.cpp b/src/muz_qe/proof_utils.cpp index e5a3ea437..369c3aae6 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].get(); + 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_arg(i) == fml) { + return true; + } + } + return false; + } }; void proof_utils::reduce_hypotheses(proof_ref& pr) { 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); } 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). */ 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)); diff --git a/src/test/algebraic.cpp b/src/test/algebraic.cpp index 064803fe1..7c0fb4a95 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..5bef0dfef --- /dev/null +++ b/src/test/rcf.cpp @@ -0,0 +1,175 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + rcf.cpp + +Abstract: + + Testing RCF module + +Author: + + Leonardo (leonardo) 2013-01-04 + +Notes: + +--*/ +#include"realclosure.h" +#include"mpz_matrix.h" + +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; + 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; + 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) << 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, 10) << std::endl; + std::cout << (eps + 1 > 1) << std::endl; + std::cout << interval_pp((a + eps)/(a - eps)) << std::endl; +} + +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; + r.resize(A.n()); + 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); } + { 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); } +} diff --git a/src/util/array.h b/src/util/array.h index 9dfd2fa94..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,16 +48,16 @@ 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); } - void init() { + void init(T const & v) { iterator it = begin(); iterator e = end(); for (; it != e; ++it) { - new (it) T(); + new (it) T(v); } } @@ -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) @@ -122,39 +122,46 @@ public: if (m_data) { if (CallDestructors) destroy_elements(); - a.deallocate(size(), raw_ptr); + a.deallocate(size(), raw_ptr()); m_data = 0; } } - 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(sz, vs); + init(vs); } - size_t size() const { + template + void set(Allocator & a, unsigned sz, T const & v = T()) { + SASSERT(m_data == 0); + allocate(a, sz); + init(v); + } + + 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]; } @@ -175,31 +182,37 @@ public: return m_data + size(); } + T const * c_ptr() const { return m_data; } T * c_ptr() { return m_data; } + + void swap(array & other) { + std::swap(m_data, other.m_data); + } + }; 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 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/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 diff --git a/src/util/mpz.h b/src/util/mpz.h index 72faea771..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 @@ -374,7 +376,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); @@ -676,10 +678,18 @@ 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; - + void display(std::ostream & out, mpz const & a) const; /** diff --git a/src/util/ref_buffer.h b/src/util/ref_buffer.h index 762113dd6..a539db689 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); } @@ -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]; } @@ -126,6 +122,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 +134,23 @@ 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); + return *this; + } }; /** \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 +166,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: }; 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